Repository navigation
Widen the deployed-tree exclusions to the host-state prefixes rsync was shipping - #8522
Conversation
…as shipping An operator deploy of srv1 refused at admission with CandidateCheckoutNotClean, naming ten ignored paths it would otherwise have installed: .claude/settings.local.json and nine stale .claude/worktrees/ directories. The refusal is correct and fired before any mutation, exactly as live_deploy_candidate_observed_at_launcher_note promises. The working-tree leg of the source sync is rsync, which does not read .gitignore, so an ignored path outside deployed_tree_excluded_prefixes is copied to the host under a revision that does not name it. candidate.dag already names the two remedies for that arm -- widen the exclusions, or remove the file -- and already listed .claude/ as a known member of the class. Four prefixes take the first remedy because they are host state by construction: agent session state and local tool/build caches that no revision ever names, so shipping one is never the intent and refusing on one is pure friction. The .env family deliberately stays refused. The repository ignores .env, .env.local and .env.*.local; the third is a wildcard, and both path_is_deployed and rsync's --exclude /<p> match a literal segment. Listing the two spellable forms would silence admission on them while .env.*.local kept shipping -- partial cover on the one class where the shipped bytes are secrets, which is strictly worse than the present loud refusal on all three. src/v2/test/claim/manual/sg2_scratch_probe.dag stays refused for a different reason: it sits inside a source root the graph reads, so excluding it would change what the host compiles rather than merely what it stores. Not derived from gunbc.gitignore_authority, on purpose: that authority answers "must not be tracked" and this one answers "must not be shipped", and the sets genuinely differ -- a tracked file ships and is not ignored. Deriving would also let an ordinary .gitignore edit silently change what a deploy installs, and would put an import edge into a module held leaf for the acyclicity reason its own header states. Witnesses: the existing one-home pin moves to the six-prefix list, and three claims are added -- the observed blocker as a claim, a control asserting the deliberately-excluded residue still reads as deployed (so a later widening cannot silence it unnoticed), and segment-matching over the appended dotted prefixes, where .claudex/ and target-codexish/ are the easy regression into a character run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
the_ignored_scan_keeps_deployed_paths_and_drops_excluded_ones asserted `.gunbc/ledger.json` on the KEPT side, which was correct while the exclusion set was ["target", ".git"] and is wrong now that local tool state is excluded. Executed against the widened set it was the one red in the module, which is the pin doing its job. The claim's purpose is unchanged and slightly sharpened: the kept side is now exactly the residue the exclusion set deliberately does not cover -- the .env family, whose wildcard spelling no prefix can express, and the probe inside a read source root -- and .claude/settings.local.json joins the dropped side so the observed blocker appears in the both-directions claim too, not only in its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Validated by execution before flipping out of draft: The first run at Provenance of the subject: the refusal quoted in the description is from a real operator deploy attempt against srv1, not a constructed fixture — it fired before any mutation and the host was left untouched. |
…me the claim it falsifies The required floor caught the real consequence this PR has outside its own module: deployed_tree_rsync_exclude_flags() renders one `--exclude /<p>` per excluded prefix directly into gunbc-srv1-green-tree-sync.service's first ExecStart, so widening the list from two prefixes to six changes emitted unit bytes that live_deploy_unit_emission_oracle_witness pins. That pin firing is the oracle doing its job -- the emitted units are exactly what a change to the exclusion set must not move silently. The expectation is updated, and so is the claim's NAME, because after this change `..._matches_the_deployed_bytes` is literally false: the host still carries the two-prefix spelling and will carry the six-prefix one only after the next deploy of that slot. A claim asserting agreement with deployed bytes while deliberately disagreeing with them is the named-for-X-claims-Y shape, so it is now `..._matches_the_deployed_bytes_plus_the_exclusion_widening` with the delta, its derivation and its closing condition recorded on the row. The delta is derived rather than transcribed, which is the distinction that keeps this a declared divergence instead of a blind regeneration: the base bytes remain the 2026-08-19 srv1 transcription, and the one field that moves is the one the exclusion list computes. The second ExecStart is deliberately untouched -- the .git leg carries its own include/exclude set and does not read the list -- so its bytes staying identical is the control proving the widening reached the leg it should and no other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
CI red fixed at What the floor caught. Two fixes, because the name was also wrong. After this change Why this is a declared divergence, not a blind re-pin. The base bytes remain the independent 2026-08-19 srv1 transcription; the one field that moves is the one the exclusion list computes. The second Validated by execution on srv1 (aarch64) at — sent from nimble-fox-671 |
…8567) main is red on test.claim.live_deploy.emit witness_scripts_stay_within_ci_runner_grants, and the cause is mine. That claim pins the tree-sync ExecStart to the exact two-prefix spelling `--exclude /target --exclude /.git`, which #8522 widened to six prefixes. WHY #8522 WAS GREEN ANYWAY, WHICH IS THE PART WORTH RECORDING. Two witnesses assert those same emitted bytes: live_deploy_unit_emission_oracle_witness, which #8522 updated because the floor caught it, and this one, which was NOT enrolled at the time and therefore could not be caught. #8503 restored 14 modules and 226 witness identities, this claim among them, and it landed straight onto the already-merged change. So the red is not #8503's defect -- the restoration did exactly what restoring evidence is for, which is to make a previously invisible breakage observable. The lesson is the one about denominators: I fixed the pin the floor showed me and inferred from a green run that no other pin existed, when the correct question was which OTHER claims assert these bytes. A dormant witness is not a passing witness, and a green floor says nothing about the claims it never ran. Fix is the same delta as the oracle row: the pattern gains the four host-state prefixes. The surrounding claim is unchanged and still does its real job -- apply and retract scripts must stay inside the ci-runner grants (no chown, no rmdir, no rsync outside the unit) -- and that half never depended on the exclusion list. Not fixed here, and not mine: v2.test.lens_inert_carrier.inert_carrier_test inert_carrier_no_unrostered_or_stale is the other red on main. It fails on main without this branch's content, and a rebased run carrying an added prose row reported the same two failures rather than three, so that one is independent of both #8522 and #8562. Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d literal #8567 fixed the same red on the same line by hand-transcribing #8522's six prefixes into the pattern. That restores the parallel representation this branch removes: the literal is a second copy of gunbc.live_deploy.deployed_tree_scope deployed_tree_excluded_prefixes, and it rotted once already — that rot is exactly the red both PRs are answering. Resolved to this branch's side, which derives the flags from that authority, so the seventh prefix cannot desync the witness at all. The conjunct's real job is unchanged and every other conjunct stays literal.
…8571) Both failures are latent defects the quarantine was hiding, not defects in #8503: the two witnesses sat under long/ while main moved out from under their assertions, and un-quarantining them made the divergence executable. 1. test.claim.live_deploy.emit.witness_scripts_stay_within_ci_runner_grants pinned the tree-sync ExecStart with the two-prefix exclude list. #8522 widened deployed_tree_excluded_prefixes to six prefixes and updated the two witnesses that were visible at the time (candidate_checkout_witness, live_deploy_unit_emission_oracle_witness); this one was in long/ and was not. The pin now transcribes the current six-prefix ExecStart, matching its two siblings. 2. v2.test.lens_inert_carrier.inert_carrier_test.inert_carrier_no_unrostered_or_stale #8413 landed the compute-fabric carriers; two of them — Attempt (product.fabric.execution) and Offer (product.fabric.supply) — are referenced only from fabric_terminal_contract_witness_test, so the live inert set went to 14 against a 12-row roster. Both get a roster row naming the consumer they await, under the existing row dissolution. Measured, not inferred: an independent reimplementation of compute_inert_carrier_data over the same corpus reproduces the 12 rostered names exactly and adds precisely Attempt and Offer. Verified by execution (claim_batch, both entries, this tree): src/v2/lens/inert_carrier_test.dag 4 PASS, 0 FAIL dag/test/claim/live_deploy/emit_test.dag 47 PASS, 0 FAIL The RED control for both is main itself: run 32290602208 fails exactly these two identities and nothing else (planned=9683 executed=9683 failed=2). Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#8569) `witness_scripts_stay_within_ci_runner_grants` transcribed the rendered ExecStart line by hand, including `--exclude /target --exclude /.git`. #8522 widened `deployed_tree_excluded_prefixes` to six entries while this witness was quarantined to `long/`, so the literal went false the moment the witness resumed executing — main is red on it at 43693a8. Re-transcribing today's six flags would green it and re-arm the identical rot. The conjunct now reads `deployed_tree_rsync_exclude_flags()`, the same authority the emitter interpolates, so the exclusion set stops being a second representation of a fact the model already carries. The grant property keeps its teeth: no chown, no rmdir, no `rsync -a`, no `--include=/config`, and the apply script must contain the unit verbatim — all still asserted literally, because those are what the witness forbids rather than what the authority derives. Two executed controls: - planting the emitter to drop the exclude flags reds this witness (FAIL witness_scripts_stay_within_ci_runner_grants), so the derived conjunct still discriminates; - planting a seventh prefix on the authority reds `the_rsync_flags_derive_from_the_same_exclusion_set_the_filter_reads` in candidate_checkout_witness_test, which pins the exclusion set's content literally and is not quarantined — so deriving here removes a duplicate representation without removing coverage of the list itself. Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
What happened
An operator deploy of srv1 (
live_deploy_apply_srv1_operator_wet) refused at admission:The refusal is correct and fired before any mutation, exactly as
live_deploy_candidate_observed_at_launcher_notepromises. Nothing was touched.Why the fix is a widening rather than a cleanup
The working-tree leg of the source sync is rsync, which does not read
.gitignore. An ignoredpath outside
deployed_tree_excluded_prefixesis therefore copied to the host under a revisionthat does not name it.
candidate.dagalready names the two remedies for that arm — widen theexclusions, or remove the file — and already listed
.claude/as a known member of the class.Four prefixes take the first remedy because they are host state by construction: agent
session state and local tool/build caches that no revision ever names. Shipping one is never the
intent, so refusing on one is pure friction.
deployed_tree_excluded_prefixesgoes["target", ".git"]→+ .claude, .gunbc, target-codex, .cargo-home.What deliberately stays refused
The
.envfamily. The repo ignores.env,.env.local,.env.*.local. The third is awildcard, and both
path_is_deployedand rsync's--exclude /<p>match a literal segment.Listing the two spellable forms would silence admission on them while
.env.*.localkeptshipping — partial cover on the one class where the shipped bytes are secrets, strictly worse
than the present loud refusal on all three.
src/v2/test/claim/manual/sg2_scratch_probe.dag, for a different reason: it sits inside asource root the graph reads, so excluding it would change what the host compiles, not merely
what it stores.
Not derived from
gunbc.gitignore_authority, on purposeThat authority answers must not be tracked; this one answers must not be shipped, and the
sets genuinely differ — a tracked file ships and is not ignored. Deriving would also let an
ordinary
.gitignoreedit silently change what a deploy installs, and would put an import edgeinto a module held leaf for the acyclicity reason its own header states. Flagging it as a
question rather than acting on it.
Witnesses
dag/test/claim/live_deploy/candidate_checkout_witness_test.dag, 21 → 24 claims:== 2/"target,.git").claudepaths that refused.env,.env.local,.env.production.localandthe scratch probe must still read as deployed, so a later widening cannot silence them unnoticed
.claudex/,dag/.claude/,target-codexish/are the easy regression into a character runScope
Prose in
candidate.dagthat read "the two prefixes deployed_tree_scope names" is updated in thesame commit; leaving it would be the stale-citation class §3 names.