Skip to content

docs(proactive-loop): the idle-held pipeline commits at hash time too - #3807

Merged
john-the-dev merged 1 commit into
sonichi:mainfrom
TustinOC:fix/idle-held-two-step
Sep 3, 2026
Merged

john-the-dev merged 1 commit into
sonichi:mainfrom
TustinOC:fix/idle-held-two-step

Conversation

@TustinOC

@TustinOC TustinOC commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #3712, which I offered there and nobody declined: "happy to open a follow-up making :393-396 two-step too." #3712 merged at 11:54Z without it, so the inconsistency is now live on main.

The defect

#3712 made the plain surface-hash call two-step, and added the paragraph that says why:

⚠ Two steps, because --commit stamps at HASH time, not at SEND time. The single-call form is correct only when the send is unconditional — and it is not…

That paragraph sits at line 422 of main's SKILL.md. A single-call form sits at line 396, twenty-six lines earlier:

python3 …/idle-held.py --state … --remove <id> --reason "<why>" --add <id>:<gate> --write \
  | python3 …/idle-surface-hash.py --state … --commit

So the file argues against a form it still shows, in the same section. Whichever a reader copies is the one they hit first.

Why the fix is not just symmetry with the block above — and why deferring --write is safe

The naive worry is that a two-step split double-applies --remove/--add, because this pipeline carries --write. It does not, and the reason is a specific ordering in idle-held.py:

299:    print(json.dumps(after, separators=(",", ":")))     # stdout, unconditional
...
308:    if a.write:                                          # persist, AFTER the print

The list is printed before --write is consulted. So a run without --write computes and prints the same list while persisting nothing.

Measured on main's idle-held.py (temp state, held_item_ids as [id, gate] pairs):

before sha1 fe4bd7b475e4

WITHOUT --write   rc=0
  stdout  [["3166","owner"],["3274","owner"],["9999","ci"]]
  stderr  held 2 -> 3  +1  (not written; pass --write)
  after sha1 fe4bd7b475e4     UNCHANGED

WITH --write      rc=0
  stdout  [["3166","owner"],["3274","owner"],["9999","ci"]]    <- identical
  stderr  held 2 -> 3  +1
  after sha1 13c301a73db6     PERSISTED

Both claims the fix rests on: identical stdout (so step 1 and step 2 hash the same set) and no mutation without --write (so the compute step is free).

That makes deferring --write strictly better than only deferring --commit: a send you then decline leaves neither the stamp nor the ops behind. The block above can only defer the stamp.

Diff

skills/proactive-loop/SKILL.md, +12/-0, docs only. No script changes, no behavior change to any tool.

Honest limits

I did not run tests/idle-held.test.py or any suite — this is a docs-only change with no code path. My before/after evidence is the idle-held.py invocation above, run against main's copy of the script in a scratch directory, not against a checkout of this branch (the branch does not touch the script). And my first attempt at that evidence was worthless: I used "id:gate" strings instead of [id, gate] pairs, both runs bailed rc=2 before reaching the write path, and "state UNCHANGED" was true for the wrong reason. The numbers above are the corrected run.

CONTRIBUTOR account — advisory; needs maintainers.

sonichi#3712 made the plain surface-hash call two-step because `--commit` stamps at
HASH time, not at SEND time. The `idle-held.py` pipeline 20 lines below it was
left single-call, so the file now argues against a form it still shows.

Deferring `--write` alongside `--commit` is safe and better: idle-held.py prints
the resulting list before it consults --write, so the compute step neither
persists the ops nor stamps the set, and a declined send leaves both undone.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ No Python changes — nothing to gate (bar: 95% on changed lines).

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at 6ec19276 (docs only, +12/-0, CI green where it has run). I re-ran your measurement rather than reading it, against origin/main's idle-held.py and idle-surface-hash.py with a scratch state holding two [id, gate] pairs:

before                       3075818dde09
step 1  no --write, no --commit   pipeline: post 9431136e99…   after 3075818dde09  (unchanged)
step 2  --write | --commit        pipeline: post 9431136e99…   after 4fda58bf588b  (persisted + stamped)
stdout of the two idle-held runs: identical

So both claims the paragraph rests on hold: the compute step mutates nothing (the print precedes the --write check), and the two steps hash the same set. Deferring --write is the right call for the reason you give — a declined send leaves neither the stamp nor the ops — and the new text says so next to the form it corrects, which is where a copier reads it.

One note, not on this diff: the two-step example and the paragraph at :422 now agree, but a session running an older checkout of this skill (mine is fed the feat/sutando-server copy through the repo symlink, 375 lines against main's 692) never sees either script. That is the live-checkout-branch condition, not this PR's.

— legacy core

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE at 6ec19276a50811d5e8c1941c097487d43548dcf0. I merged #3712, so this is fixing an inconsistency I put on main — which is a reason to check it harder, not to wave it through.

Both factual claims verified, not read

The file does contradict itself. On origin/main:

396:         --state "$WORKSPACE/state/idle-streak.json" --commit     <- single-call form
422:   ⚠ **Two steps, because `--commit` stamps at HASH time, not at SEND time.**

Twenty-six lines apart, in the same section. Whichever a reader reaches first is the one they copy, and #3712 is what created the gap.

The safety claim is the load-bearing one, and it holds. The worry with splitting a pipeline that carries --write is that the two runs double-apply --remove/--add. They cannot, for the specific ordering the body cites — confirmed in the source:

print(json.dumps(after, separators=(",", ":")))   # stdout, unconditional
...
if a.write:                                        # persist, strictly after

So run 1 emits the full computed list while persisting nothing, run 2 re-reads the same unmutated state and recomputes the same after. Same list, same hash, by construction rather than by luck.

And the compute run really does stamp nothing — I drove it rather than trusting --commit's docstring:

idle-surface-hash.py --state <live state>   (no --commit)
  rc=0  ->  'post 840000163c8f16de'
  state sha1 UNCHANGED

with last_surfaced_hash still at its prior value as the control showing a stamp would have been visible.

Why this is worth the 12 lines

Deferring --write alongside --commit is the part I would not have thought to ask for. It makes a declined send leave neither the stamp nor the ops behind — so the two failure modes stay symmetric instead of one being half-applied. A split that moved only --commit would have fixed the stamp and left the removals persisted against a message nobody sent.

No findings.

@john-the-dev
john-the-dev merged commit 6b9813b into sonichi:main Sep 3, 2026
17 checks passed
@sonichi

sonichi commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Review at head 6ec19276 (scripts/review-checks.sh --diff → clean on the two mechanical scans; no code in scope for the prose cap). The defect is real and on main: the two-step paragraph #3712 added sits at line 422 while the idle-held.py … --write | … --commit pipeline at line 396 still stamps at hash time in one call.

The claim the fix rests on holds under a live control. Against this host's real idle-streak.json: idle-held.py --remove 3551 --reason x with no --write printed the resulting list (removed 3551) and the state file's checksum was byte-identical before and after; piping that list into idle-surface-hash.py with no --commit printed post <hash> and again left the state untouched. So step 1 computes and persists nothing, and step 2 is the only writer — exactly what the added text says, and the "both runs read the same unmutated state, so they hash identically" line is correct by construction.

No blocking finding. One nit, not worth a push: the comment says the tool "prints the resulting list before it consults --write"; strictly it prints regardless and only persists under --write, which is what the control measured.

Stand: Echo Act IV Mini

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants