Skip to content

loop: a claim, because two sessions took the same three tasks - #3332

Merged
gHashTag merged 2 commits into
masterfrom
w118-loop-claim
Sep 6, 2026
Merged

loop: a claim, because two sessions took the same three tasks#3332
gHashTag merged 2 commits into
masterfrom
w118-loop-claim

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Two sessions of this loop ran concurrently and picked the same three tasks
from the same list of recommendations, opening PRs for all of them (#3314,
#3317). Nothing in the flow says who is working on what.

tri loop claim <name>

Git gives exactly one atomic operation over a shared remote: creating a ref that
does not exist.

tri loop claim pass-118            # 0 = yours, 1 = someone has it
tri loop claim pass-118 --who      # ask without taking
tri loop claim pass-118 --release  # give it back

Exit codes follow the repo's vocabulary: 0 taken · 1 refused, and the
line names the holder · 2 the attempt could not be made — which is not the
same as being refused.

The obvious version does not lock

Pushing origin/master to the claim tag succeeds for the second claimant
too
— git treats re-pushing the same value to an existing tag as a no-op:

first push  exit 0
second push exit 0      <- both sessions now believe they hold it

Only a different value is rejected, and two sessions of this loop sit on the
same origin/master almost by definition. That lock would have been worse than
none, because it reports success.

So the claim is a commit no other claimant can produce — an empty tree with a
message naming host, pid and HEAD:

ALPHA claims: exit 0
BETA claims:  exit 1   already held -- loop claim `probe-118` by ... pid 76989

A structural test pins that the claim is built, not borrowed; mutating it
back to rev-parse origin/master fails the suite. A second pins that a refused
push with no holder exits 2 — a broken push is not a lost race, and
reporting it as HELD sends the next session away from work nobody is doing.

That second test could not kill anything until it was fixed: it sliced from the
first match holder to end of file and asserted contains("exit(2)"), which
four unrelated exit-2 sites satisfy.

Census: three moved, and two are not mine

census was → now whose
fetches files read 43 → 44 mine — loopclaim.rs
quiet named a path but not quiet 128 → 127 already moved on master
shell run: steps 235 → 234 already moved on master

A clean origin/master worktree fails census pin --gate with the last two
before any change of mine. This carries that bless and says so rather than
passing it on.

Correction, measured after this merged. The clause that stood here — "the
pre-commit hook has been failing for every author since a workflow edit landed
unblessed" — is false. I generalised it from a single observation and published
it without measuring. Replaying census pin --gate over the last twelve
first-parent commits of master, with the tool itself unchanged across the
window (cli/tri/src/census.rs: 0 commits), exactly one commit fails:
3429f9c6. The next commit blesses it nineteen minutes later. The red window
was one commit, not an era.

The replay showed two things worth more than the claim they replace.
cli-tri.yml, where census pin --gate runs, is not one of the four
required contexts (check, check-linked-issue, check-now-freshness,
validate), so a red census cannot block a merge; and a GitHub squash-merge
never runs a local hook, so the pre-commit census guard binds local commits
only. 3429f9c6 merged red for both reasons at once — which is also why the
pin oscillated 235 → 234 → 235 in under an hour, one commit blessing a
reduction that the next reverted.

Worth recording how long that took: census pin without --gate exits 0
whatever moved.
I read that non-verdict as "nothing moved" twice, once as the
control that supposedly cleared master, and spent several commands chasing a
phantom the hook's own preamble had already explained.

791 tests pass. Claimed pass-118 with the tool while writing this.

Refs #3331

Two sessions of this loop ran concurrently and picked the same three tasks from
the same list of recommendations, opening PRs for all of them (#3314, #3317).
Nothing in the flow says who is working on what.

`tri loop claim <name>` uses the one atomic operation git gives over a shared
remote: creating a ref that does not exist. Exit 0 it is yours, 1 someone has
it and the line names them, 2 the attempt could not be made -- which is not the
same as being refused.

THE OBVIOUS VERSION DOES NOT LOCK, and I measured it before writing this one.
Pushing `origin/master` to the claim tag succeeds for the SECOND claimant too:
git treats re-pushing the same value to an existing tag as a no-op and exits 0,
so both sessions believe they hold it. Two sessions of this loop sit on the same
`origin/master` almost by definition, so that lock would have been worse than
none -- it reports success.

The claim is therefore a commit no other claimant can produce: an empty tree
with a message naming host, pid and HEAD. Measured: ALPHA exit 0, BETA exit 1
with the holder named.

A refused push with NO holder exits 2, not 1. That is a broken push rather than
a lost race, and reporting it as HELD would send the next session away from work
nobody is doing.

Two mutants killed -- borrowing the claim value from an existing ref, and
reporting a broken push as held. The second test could not kill anything until
it was fixed: it sliced from the first `match holder` to end of file and
asserted `contains("exit(2)")`, which four unrelated exit-2 sites satisfy.

CENSUS. Three moved and they are not all mine:

  fetches  files read                 43 -> 44   MINE: cli/tri/src/loopclaim.rs
  quiet    named a path but not quiet 128 -> 127  already moved on master
  shell    run: steps                 235 -> 234  already moved on master

A clean `origin/master` worktree fails `census pin --gate` with the last two
before any change of mine, so the pre-commit hook has been failing for every
author since a workflow edit landed unblessed. This commit carries the bless it
inherited and says so rather than passing it on.

Getting there took four wrong turns worth recording: `census pin` WITHOUT
`--gate` exits 0 whatever moved, and I read that non-verdict as "nothing moved"
twice -- once as the control that supposedly cleared master.

Refs #3331

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) September 5, 2026 19:15
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-05 19:19:07 UTC

Summary

Status Count
Total Open PRs 16
PRs with Failing Checks 12
PRs with All Checks Green 4
READY 3
FAILING 12
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=ffccfa1a71cf != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

The branch was green with auto-merge armed and BEHIND by nine commits, and
`allow_update_branch` is false, so nothing but a session could move it. The
claim mechanism it carries is the one both concurrent sessions need: this pass
alone duplicated an issue already open (#3323 against #3333) and nearly retook a
task a neighbour had finished.

Two censuses moved and are re-blessed in this same commit, as the gate demands:
`quiet` 127 -> 128 (one more path named but not quiet) and `shell`
`run: steps` 234 -> 235, both from the nine commits being merged in, not from
this branch's own work.
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-06 03:42:30 UTC

Summary

Status Count
Total Open PRs 16
PRs with Failing Checks 10
PRs with All Checks Green 6
READY 5
FAILING 10
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=8c144e6dfce9 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@gHashTag
gHashTag merged commit f175107 into master Sep 6, 2026
30 checks passed
gHashTag added a commit that referenced this pull request Sep 6, 2026
…3357) (#3358)

Two sessions ran concurrently on 2026-09-06 sharing one checkout and one
`target/`, and neither had a way to find out.

Every symptom was attributed to the tool being measured: a commit that "did not
form" while `git push` reported success (it pushed an unchanged HEAD), a built
binary that lost a subcommand it had a minute earlier, census readings that
flip-flopped between runs, and a HEAD sitting on a branch this session never
created. That is the broken-ruler error with a second session as the ruler.

R17 writes down what `tri loop claim` (#3332) made possible: own the tree, take
the claim first, and do not diagnose a shared tree through the tree.

It also records the trap found while following it. A private `CARGO_TARGET_DIR`
-- the thing that makes the tree private -- hides the binary from
`.githooks/pre-commit:52` and `.githooks/commit-msg:14`, which probe
`$ROOT/target/{debug,release}/tri`. They report "not built", refuse with exit 2,
and the commit silently does not form while the next push reports success. The
fix is a symlink where the hooks look, and checking that `git rev-parse HEAD`
moved rather than trusting what `git commit` printed.

Resealed: the rule file changed, so `LOOP-RULES.sha256` moved with it in this
commit. R6 -- the seal certifies identity, not correctness.

Co-authored-by: lab <lab@example.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Sep 6, 2026
* skill: spool five lessons from pass 119 (Refs #3236)

Written with `tri skill add` and left UNNUMBERED, deliberately. A branch that
also runs `fold` picks its number against its own base and collides exactly like
a direct append; folding belongs after the merge.

The five:

- A refusal recorded only in prose is invisible to the tool that advises against
  it. `gates unmeasured` printed `dispatch: NO` beside "add `workflow_dispatch:`
  first" for the workflow #3325 had just deliberately removed one from -- that
  is, it advised putting a dispatch back in front of `cargo publish` on a live
  registry. A two-state predicate over a three-state domain is the shape.
- Two date fields render in two timezones. `%ad` and `%cd` each print in their
  OWN recorded offset, which made one commit read as a seven-hour clock skew.
  Compared as epochs: 0 of 20. The tell was that the date order ran against
  topology.
- Assert that the edit moved, not that the anchor exists. Two scripted edits
  reported success and changed nothing; the second because `find()` returned -1,
  `s[-1:j]` is the empty string, and `replace("", new)` inserts at position 0.
- A private target dir hides the binary from the hooks. `.githooks/pre-commit:52`
  probes `$ROOT/target/{debug,release}/tri`; with `CARGO_TARGET_DIR` elsewhere the
  commit silently does not form while the next push reports success.
- A gate whose workflow is not a required context cannot block a merge. This one
  refutes a claim I had already published in #3332: replayed over twelve
  first-parent commits with the tool unchanged, exactly one fails, blessed
  nineteen minutes later.

`tri skill check`: 556 sections, no number used twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* skill: a quoted diagnostic is not a command invocation (Refs #3236)

`Documented t27c subcommands exist` went red on this branch alone: dead `tri`
mentions rose 141 -> 142. The offender was mine, and it was a QUOTATION.

The lesson about stale binaries quoted the hook's own warning inside one code
span -- `target/debug/tri is older than cli/tri/src`. The gate extracts
`tri <word>` from code spans, so it read a subcommand `is`, which resolves on
none of the four surfaces. Correctly: the gate is right, the text was wrong.

Split into two spans, so the prose reads the same and the code spans name only
paths: `target/debug/tri` is older than `cli/tri/src`.

Measured rather than assumed, with both controls run first because the obvious
probe was itself broken -- `$B $sub --help` does not word-split in zsh, so both
mentions reported DEAD until the probe used `${=sub}`. With a must-resolve and a
must-not-resolve control behaving correctly, the two `tri` mentions my files
introduce (`tri census pin`, `tri gates unmeasured`) both resolve, and `tri is`
is gone. The ratchet returns to 141.

Not amended: the branch is pushed, and this repository never force-pushes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: lab <lab@example.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant