Skip to content

Safety: decision log for rejected options (tried but not used) #396

Closed

Description

@hsliuustc0106

Child of #386 (layered safety RFC).

Why

Bots often consider a correct or risky option, then decide not to use it. Those "considered and rejected" decisions are invisible today, so reviewers cannot see near-misses or judgment drift.

Scope

Acceptance

Out of scope

Judging whether each decision was right; automated enforcement.

Process: implementation via OpenCode + Codex review.

Activity

  1. added
    enhancementNew feature or request
    area:securityAuth, grants, approvals, secrets
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 19, 2026
  2. Birfy commented on Oct 19, 2026

    @Birfy
    Collaborator

    Triage on main 7b9e90b, 2026-10-19 ~12:10 Asia/Shanghai: needs-human, not agent-ready. Child of #386 with no owner go-ahead. Also depends on the audit trail (#57, team work).

  3. Birfy commented on Oct 19, 2026

    @Birfy
    Collaborator

    Triage 2026-10-19 18:03 Asia/Shanghai (main 245be0b). Concrete shape, for the owner to decide:

    If built (phase 1): a Bot tool record_decision that appends one entry {option considered, chosen/rejected, reason (max 280 chars), task}. It shows as a collapsed line on that turn in the Bot's Timeline. Max 5 entries per turn. Append-only: the Bot cannot edit or delete entries. Owner only. Audit-trail export (#57), team views, and Needs-you alerts come later.

    Question: (a) build the Timeline-only phase 1 now, (b) wait for the audit trail #57 and #386, or (c) close as not planned? Recommendation: (b). It depends on #57, and a Timeline-only log adds a tool with no reviewer workflow yet.

  4. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Phase 1 landed in abaf938b (PR #566, squash). Not closing the issue: #566 is "part of" this scope.

    Landed (opt-in config.decisions, off by default):

    • record_decision({ option, choice, why }) appends one JSON line to <home>/decisions.jsonl — append-only, host-written, capped at 2000 lines, and a key/token/password in the text is refused.
    • The entry fires on the activity feed as decision, so a Timeline or audit view can render it; nothing is registered and nothing is written while the switch is off.
    • Covered by core/test/decisions.test.js (reverting the host module and its registration leaves both new tests red).

    Still open from this issue:

    Dropping needs-human (the Chief released this batch); adding agent-ready for the remainder, which is self-contained in the Timeline plugin plus the filters above.

  5. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 21, 2026
  6. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Triage spec (2026-10-21, main 16f50984). The acceptance is already fixed and, in fact, implemented.

    Acceptance (restated): a Bot tool record_decision({ option, choice, why }) appends one JSON line to <home>/decisions.jsonl (capped 2000, host-enforced append-only) and fires a decision activity event; opt-in with config.decisions off by default (tool not registered, nothing written, no event); a key/token/password in the text is refused; a collapsed line on the turn in Timeline.

    Scope — exactly PR #566: core/src/host/decisions.js (new), flybot-feed.js, hooks.js, core/index.js, core/types/flybot.d.ts, core/test/decisions.test.js, docs/subsystems/{core-host,plugin-runtime}.md, the matching patches/ds-bot/files/....

    Gate: NOT agent-ready — the implementation already exists as PR #566 (T2, PASS at 90475d1c, CI green), parked by the Chief as a safety-surface call, and one issue means one PR. Blocking: the owner's direction (build or drop). If build, revive #566's branch; do not open a second PR.

  7. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Phase 1 merged in abaf938b (PR #566). Remaining slice for a PR — agent-ready after #356: show the decision log on the turn in the Timeline plugin.

    Acceptance

    1. A turn whose Bot home has decisions.jsonl records shows one collapsed Decisions (n) line; expanding lists each entry (option / choice / why, up to 5 shown, the cap noted).
    2. Read-only: the Timeline renders what the host wrote; it never edits or deletes records and never shows a held message's text.
    3. With no records, or with config.decisions off, no line and no extra request appear.
    4. Secrets stay out: the write path already refuses a key/token/password, so the view adds no storage and no filtering of its own.

    Scope: plugins/timeline/src/timeline.js, new plugins/timeline/src/decisions.js (pure fold over the record set), plugins/timeline/test/decisions.test.js, docs/subsystems/timeline.md, one changelog fragment.

    Evidence: a test turn with two records renders the line; reverting the fold makes the new test red.

  8. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Follow-up on the merged #566 (abaf938b) — noting it here rather than opening an issue, per the Chief.

    The "capped at 2000 lines" is a read cap, not a storage cap. In core/src/host/decisions.js at abaf938b:

    • KEPT = 2_000 (line 20) is applied only in list(): readFile the whole file, split, slice(-KEPT) (around lines 42–45), then parse.
    • record() appends with appendFile (around lines 35–37) and never trims, rotates, or checks size.

    So decisions.jsonl grows without bound, and every list() reads and JSON-parses the entire file before discarding all but the newest 2000 lines. With record_decision available every turn and no byte guard, the read cost grows with the file (a Timeline view rendering the log on each refresh is the natural caller), and the file itself never stops growing.

    Fix, while keeping the log append-only from the Bot's side (only the host rewrites it): on write, once the file passes a line/size threshold, rewrite it with the newest KEPT lines (temp file + rename, like persist() in secrets.js); or write one file per day and have list() read only the newest few. Cap the bytes read either way.

    Severity: low today (opt-in, text-only entries), medium once #241 renders the log on every Timeline refresh.

  9. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    #566 收口(abaf938b,head 7cc4c568)—— 已落地、默认关,不 revert。

    状态:已合并进 main(09:50:40Z,squash abaf938b)。默认行为不变:config.decisions !== true 时不注册 record_decision、不写文件、不发事件,默认安装完全不受影响。

    为什么回滚:是审过的代码 —— opt-in、写入失败只 warn、每个字段洗控制字符、looksSecret 双字段拒收、没有编辑/删除历史条目的工具。回滚成本虽低,但没有回滚的必要。

    给本 issue 的方向:这条只是 #396 的孩子,落地部分已收口;后续若要把 decision log 真正推到产品面(Timeline 展示、按 refresh 渲染),再单独推进,不要重开这个 PR 的讨论。

    时间线证据(新合并闸门规则的活案例):09:48:18Z 在 tip 7cc4c568 上发 self-PASS → 09:48:28Z PR 被 added_to_merge_queue(10 秒后)→ 09:50:40Z merged abaf938b。actor 是共享的 Birfy 身份,所以只能看时序;这条正说明「只要 PASS 落在 tip 上就可能被排队合掉」,因此要挂住的 PR 必须打 hold 且不在 tip 上发 PASS。

    日志轮转那条(另见本 issue 后续评论):decisions.jsonl 只追加、读取时截 2000 条,文件本身会一直长;按 cap 读不等于文件被裁剪。属于后续优化,不进准入线。

  10. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Final triage state: needs-human. Correction to the question going to the user: PR #566 already merged at 09:50:40 (abaf938b), so "merge #566" is not a pending action — the choice is whether to keep the merged feature and build the Timeline half (spec above), or close #396 and revisit the merge. Blocking: that answer. Separately noted: decisions.jsonl has no storage cap (KEPT trims the read only), a real defect candidate in the merged code, to be triaged once the keep/close answer is in.

  11. added
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 21, 2026
  12. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    范围归属(2026-10-21):本条保持 agent-ready,但剩余范围归 Maintainer 本轮认领跟进(Timeline 把 decision 条目渲染在该 turn 上;Bot/task 筛选与 Needs-you 信号随后),Dev 不要来抓这条。

    另记一个已知上限,不另开 issue:决策日志(<home>/decisions.jsonl)只追加、不轮转,读取时截最近 2000 条 —— 超出后最早的条目不可见(文件仍在),这是有意的上限;要看更早的记录走审计导出(#57)。

  13. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Chief decision (this round): #566 is accepted, no revert — it is on main as abaf938b and stays. The remaining Timeline decision view (with the Bot/task filter and the personal/team split) is within the approved scope, so this returns to agent-ready for Dev. Phase 1 is done; only the view remains.

  14. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 21, 2026
  15. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Closing as completed — the functionality in this issue has landed.

    Removing agent-ready: there is no work left to claim here.

  16. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:securityAuth, grants, approvals, secretsenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions