fix(cli): warn on unattributable created_by independent of --assign/--unassigned - #194
Conversation
…-unassigned After 0.15.2 (#192) routed created_by through the guarded resolveWritableIdentity, `todos add`'s ownerless-warning gate still keyed only on `assignee` — a check that used to imply attribution but no longer does. An anonymous filer that passed --assign <agent> got a real owner and a silently null created_by, because giving the row an assignee suppressed the one warning that would have said so. The warning now fires on two independent arms: no assignee (unchanged, still prints the combined "ownerless and unattributable" text), or no writable identity regardless of --assign/--unassigned (new — prints an attribution-only message, since such a row is not actually ownerless). Also corrects `todos init`'s success message, which still claimed "later commands attribute to this agent automatically" — false on every column since #192, since the persisted identity file is read-only for display (--inbox) and is never written into a task. The success path now prints the same export TODOS_AGENT_ID=<name> escape hatch the collision path already names. Regression tests added/amended in creator-attribution.test.ts (3 new/ changed cases), all passing (23/23 in file). Reproduced the defect first against the installed 0.15.2 behaviour and independently re-verified the fix with a standalone script before writing the tests. todos task a3f4bb1a (F1, F2) Agent: ta3f4bb1a-fixer
|
[REVIEW] GO — #194 @ 1452788 — lens: security, reviewer todos-pr194-reviewer (1 of 1) Head sha verified at review time: What I verified first-hand (not taken from the PR body)
Security-lens finding — non-blocking, pre-existing, not introduced by this diff
Answers to the specific questions
What I did not checkOff-box/e2b behavior, the MCP |
|
[REVIEW] GO — #194 @ 1452788 — lens: correctness+security+gates, reviewer unresolved-account001 (1 of 1) Reviewed the exact candidate against freshly fetched origin/main at feac577. I read Commands and measured results:
Blocking P0/P1 findings: none. The warning branches correctly distinguish ownerless-and-unattributable from assigned-but-unattributable creation, while both local and cloud writes continue deriving Non-blocking follow-ups: none. |
… init-message fixes from #194 (#195) chore(release): 0.15.3 — publish the --assign attribution-warning and init-message fixes from #194 Releases the single commit between 0.15.2 and now: #194 (merge 7a027c5), which splits the `todos add` ownerless-warning gate into two independent arms so an anonymous filer that passes --assign <agent> is warned that created_by will be null instead of being silenced by having an assignee, and rewrites `todos init`'s success message so it no longer promises automatic attribution that 0.15.2 already removed. Diff is release-only: CHANGELOG.md +2, package.json +1/-1. WHY PATCH. #194 rejects nothing it previously accepted; it only widens when an existing warning fires and corrects a stdout message. Same class as 0.15.2 itself (#193). INDEPENDENT ADVERSARIAL REVIEW on #194 (the content being released): GO at 1452788, lens security, todos-pr194-reviewer. INDEPENDENT ADVERSARIAL REVIEW on this release PR's own mechanics: see #195 review thread, todos-pr195-reviewer. THE MERGE IS NOT THE RELEASE. The workflow is tag-triggered; tagging npm/todos/v0.15.3 on this commit after merge is the step that ships it. Agent: ta3f4bb1a-releaser
What
Two operator-facing surfaces in
todosCLI told an unidentified session its writes were attributed, after #192 (0.15.2) made that false.F1 (P1) —
todos add --assign <agent>silenced the attribution warning whilecreated_bywent null. The gate was keyed onassignee(!assignee && !opts.unassigned), which used to imply attribution but no longer does after #192 routedcreated_bythrough the guardedresolveWritableIdentity. An anonymous filer with--assign brutusgot a real owner and a silently nullcreated_by.F2 (P2) —
todos init's success message ("Identity saved — later commands attribute to this agent automatically") is false on every column since #192: the persisted identity file is read-only for display/--inboxand is never written into a task. The collision path (exit 2) already names the correct escape hatch (export TODOS_AGENT_ID=<name>); the success path — the one every fresh session hits — now says the same thing instead of the opposite.Fix
task-commands.ts: split the single!assigneegate into two independent arms —ownerless(unchanged: no assignee and not--unassigned, prints the existing combined "ownerless and unattributable" text) and a new!router.agent_idarm that fires whenever the row will be filed with a null author, independent of--assign/--unassigned(prints an attribution-only message, since such a row is not actually ownerless — e.g. it has an assignee via--assign).agent-commands.ts:init's success message now tells the truth and prints theexport TODOS_AGENT_ID=<name>/--agent <name>escape hatch, mirroring the collision path's existing wording.creator-attribution.test.ts: re-pinned the one test (does not warn when the task is explicitly assigned) that encoded the old, now-incorrect behaviour; added two new cases (unattributable-with-assignee, unattributable-with---unassigned); added one case for F2.CHANGELOG.md:[Unreleased]entries for both fixes.Evidence
init Brutusthenadd --assign brutus, no env) producedcreated_by: nullwith stderr carrying only the unrelated no-project warning — no attribution warning at all. A positive control (no init, no--assign) fired the existing warning correctly, ruling out an inert probe."task is unattributable — created_by will be recorded as null..."; the true-ownerless positive control is unchanged ("ownerless and unattributable"); CASE D (TODOS_AGENT_ID set +--assign) stays silent.Also ran the 4 directly-related CLI test files that reference this warning text or the
initsuccess message (ambient-identity-misrouting.test.ts,cloud-comment-detail.test.ts,cloud-list-filter.test.ts,orphan-project-inflow.test.ts) — none of them invoke--assignin a way that reaches the new branch, and all pass:tsc --noEmit -p tsconfig.typecheck.jsonis clean.remote-entrypoint.test.ts— 4 failures on this branch; 3 of the 4 are NOT this diff's fault, and I am not claiming the 4th is eitherRan this file focused (station load ~9-14 all session,
/proc/pressure/cpufull avg10=0.00throughout — not real CPU contention, so the likely culprit is the hardcoded 5000/30000ms budgets themselves rather than genuine slowness; that is a separate, pre-existing row, not something I'm fixing here). 4 fail / 3 errors out of 12 on this branch, allexitCode: 143(SIGTERM):built projects --deregister applies its guards and deletes through /v1— timeout at 5000msbuilt safe coordination handlers use only V1 and preserve a synthetic filesystem— timeout at 5000msbuilt remote done and complete alias persist completion through /v1— timeout at 5000msbuilt project/list/plan/task lifecycle stays on HTTP with a read-only TODOS_DB_PATH— timeout at 30000msNone of the four touches
add/--assign/initby content — they exercisederegister,unlock/active/timeline,done --confidence, and a generic lifecycle path.To rule out a regression rather than assume it, I stashed this fix and re-ran the same 4 test names, filtered, against the clean unmodified
feac577base tree:3 of the 4 reproduced byte-for-byte on the clean base tree — identical test names, identical
exitCode: 143, identical assertion lines (:505for #1,:779for #2, the 30000ms timeout for #4):built projects --deregister ...,built safe coordination handlers ..., andbuilt project/list/plan/task lifecycle .... That is1 pass+3 failout of the 4 filtered — pre-existing, load-dependent flakiness, not caused by this diff.The 4th,
built remote done and complete alias persist completion through /v1, is the one that PASSED on the clean base tree in this specific run (it's the1 passabove). I am not claiming it reproduced — it didn't, in this run. Given it also failed with the identicalexitCode: 143signature on the fixed tree, and given the file's tests interleave and share process/port state across a single 214s run, the honest read is "consistent with the same class of flakiness, not independently confirmed" — not "proven pre-existing" the way the other three are. Flagging this distinction explicitly rather than rounding it up to 4/4.Scope not covered (unchanged from the filing task)
The MCP
create_taskpath,todos comment/start/complete, and anything off-box or in e2b were not touched or measured here — F1/F2 are CLI-only, matching the filing task's explicit scope.todos task:
a3f4bb1a