You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Safety: decision log for rejected options (tried but not used) #396
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
A Bot can record a short decision entry: option considered, why rejected (or chosen), related task and tool.
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).
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.
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).
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.
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.
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.
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
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).
Read-only: the Timeline renders what the host wrote; it never edits or deletes records and never shows a held message's text.
With no records, or with config.decisions off, no line and no extra request appear.
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.
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.
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.
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.
Closing as completed — the functionality in this issue has landed.
Implemented by Record the decisions a Bot considered and rejected #566 (abaf938b, merged 2026-10-21T09:50:40Z): record_decision({ option, choice, why }) appends one JSON line per entry to <home>/decisions.jsonl, host-written so a Bot cannot edit or delete a past entry; opt-in (config.decisions, off by default — not registered, nothing written, no event); a key/token/password in the text is refused.
Activity
Birfy commentedon Oct 19, 2026
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).Birfy commentedon Oct 19, 2026
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_decisionthat 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.
Add an opt-in state journal and a dev replay script (#527)
Record the decisions a Bot considered and rejected (ThinkFlowLab#396)
Record the decisions a Bot considered and rejected (ThinkFlowLab#396)
Birfy commentedon Oct 21, 2026
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.decision, so a Timeline or audit view can render it; nothing is registered and nothing is written while the switch is off.core/test/decisions.test.js(reverting the host module and its registration leaves both new tests red).Still open from this issue:
decisionentries in the Timeline and the audit trail (@flybot/timeline (team-activity): swimlane timeline of Bot turns, tool runs and exchanges #241, [Team] Add a human-readable authorization and task audit trail #57), tagged with Bot, harness/model (RFC: harness × provider (Magpie-inspired gateway, routing groups, profiles) #366) and task.Dropping
needs-human(the Chief released this batch); addingagent-readyfor the remainder, which is self-contained in the Timeline plugin plus the filters above.Birfy commentedon Oct 21, 2026
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 adecisionactivity event; opt-in withconfig.decisionsoff 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 matchingpatches/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.Birfy commentedon Oct 21, 2026
Phase 1 merged in
abaf938b(PR #566). Remaining slice for a PR —agent-readyafter #356: show the decision log on the turn in the Timeline plugin.Acceptance
decisions.jsonlrecords shows one collapsedDecisions (n)line; expanding lists each entry (option / choice / why, up to 5 shown, the cap noted).config.decisionsoff, no line and no extra request appear.Scope:
plugins/timeline/src/timeline.js, newplugins/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.
Birfy commentedon Oct 21, 2026
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.jsatabaf938b:KEPT = 2_000(line 20) is applied only inlist():readFilethe whole file, split,slice(-KEPT)(around lines 42–45), then parse.record()appends withappendFile(around lines 35–37) and never trims, rotates, or checks size.So
decisions.jsonlgrows without bound, and everylist()reads and JSON-parses the entire file before discarding all but the newest 2000 lines. Withrecord_decisionavailable 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
KEPTlines (temp file + rename, likepersist()insecrets.js); or write one file per day and havelist()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.
Birfy commentedon Oct 21, 2026
#566 收口(
abaf938b,head7cc4c568)—— 已落地、默认关,不 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 mergedabaf938b。actor 是共享的 Birfy 身份,所以只能看时序;这条正说明「只要 PASS 落在 tip 上就可能被排队合掉」,因此要挂住的 PR 必须打hold且不在 tip 上发 PASS。日志轮转那条(另见本 issue 后续评论):
decisions.jsonl只追加、读取时截 2000 条,文件本身会一直长;按 cap 读不等于文件被裁剪。属于后续优化,不进准入线。Birfy commentedon Oct 21, 2026
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.jsonlhas no storage cap (KEPTtrims the read only), a real defect candidate in the merged code, to be triaged once the keep/close answer is in.Birfy commentedon Oct 21, 2026
范围归属(2026-10-21):本条保持
agent-ready,但剩余范围归 Maintainer 本轮认领跟进(Timeline 把decision条目渲染在该 turn 上;Bot/task 筛选与 Needs-you 信号随后),Dev 不要来抓这条。另记一个已知上限,不另开 issue:决策日志(
<home>/decisions.jsonl)只追加、不轮转,读取时截最近 2000 条 —— 超出后最早的条目不可见(文件仍在),这是有意的上限;要看更早的记录走审计导出(#57)。Birfy commentedon Oct 21, 2026
Chief decision (this round): #566 is accepted, no revert — it is on main as
abaf938band stays. The remaining Timelinedecisionview (with the Bot/task filter and the personal/team split) is within the approved scope, so this returns toagent-readyfor Dev. Phase 1 is done; only the view remains.Birfy commentedon Oct 21, 2026
Closing as completed — the functionality in this issue has landed.
abaf938b, merged 2026-10-21T09:50:40Z):record_decision({ option, choice, why })appends one JSON line per entry to<home>/decisions.jsonl, host-written so a Bot cannot edit or delete a past entry; opt-in (config.decisions, off by default — not registered, nothing written, no event); a key/token/password in the text is refused.decisions.jsonlnever trims: the 2000-line cap is read-only, the file grows without bound #603 / fix: bound the decisions log on disk and on read (#603) #606 (7df78bae): the log was capped to 2000 lines only on read whilerecord()appended forever. It is now bounded on disk too (newest 2000 lines, at most 1 MB, via temp file + rename), andlist()reads at most the last 1 MB.Removing
agent-ready: there is no work left to claim here.Record the decisions a Bot considered and rejected (ThinkFlowLab#566)