Skip to content

fix(coord): make Blocked by mutations set-safe - #2936

Merged
EHotwagner merged 10 commits into
mainfrom
item/2907-blocked-by-set-mutations-recovery
Aug 25, 2026
Merged

EHotwagner merged 10 commits into
mainfrom
item/2907-blocked-by-set-mutations-recovery

Conversation

@EHotwagner

@EHotwagner EHotwagner commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2907

Summary

  • adds explicit --add, --remove, --replace, and --clear intents for the set-valued Blocked by field
  • serializes every authoritative Blocked by writer behind one server-ordered issue-comment lease: explicit intents, batch, release, intake, reconcile, and deferred flush replay all join the same boundary
  • refuses legacy bare replacement and adds diagnostic-only BLOCKED-BY-BODY-INERT linting for empty, divergent, duplicate, or invalid body projections
  • migrates the production parity route to explicit replacement/clear syntax

Verification

  • dotnet build src/FS.GG.Coord.Cli/FS.GG.Coord.Cli.fsproj -c Release --no-restore: 0 warnings, 0 errors on the rebased recovery head
  • BoardOps tests: 259 passed; GitHub tests: 671 passed; parity corpus: 616/616; production write corpus: 186/186
  • focused controls prove one derived first attempt is fenced, retry preserves both edges, maximum concurrent actions remains one, and a lower-ID contender prevents batch/release/flush bypass with zero board mutations and exactly one retained deferred entry
  • scripts/generate-projections --check: current
  • fsgg-sdd analyze: implementationReady; verify: verificationReady with 14/14 observed obligations; ship: shipReady
  • the earlier full branch verification also covered Kernel, Lifecycle, CLI, Core, parity, identity-harness, signature-doc mutation sweep, and deterministic package checks
  • eight bounded inversions observed red: add preservation, remove preservation, stale refusal, inert-body lint, mutation-lease election, direct-writer lease bypass, deferred-flush lease bypass, and the paired intake/reconcile lease-wire ordering inversion (184/186, exactly those two routes red)

Verification: exact commands, outcomes, controls, and inversion evidence are recorded in work/2907-blocked-by-set-mutations/verification/verification-evidence.md; the durable 259-test TRX is readiness/2907-blocked-by-set-mutations/test-results/boardops.trx.

Recovery

This supersedes #2916 in the same recovery lineage. Its expired, unconsumed review wait was bound to the dead claim generation, and the typed force-claim route correctly recovered the item while the known #2853 ledger defect prevented that old PR from consuming or replacing the wait. A fresh PR is the documented safe recovery because review-wait authority is scoped to PR comments. Successor-critic findings on this PR were repaired on the exact current head.

The branch was rebased onto current origin/main, and its SDD generated views were refreshed with fsgg-sdd 1.2.5. The prior #2912 dependency and coherent-set 0.75.2 release are already landed and verified; this PR itself declares no delivery obligation.

@EHotwagner

EHotwagner commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor Author

No description provided.

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@github-actions

Copy link
Copy Markdown

✅ This PR stays inside the touch-set its issue declared.

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"at":"2026-08-25T06:23:31.0000000\u002B00:00","event":"cancel","evidenceRef":"hosted parity-fixture repair before critic record","reviewGeneration":"ded5b50850568537153813fac65c175535ddd3d4:initial-review:0","schema":"fsgg.coord.review-wait/v1"}

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"acceptedExceptions":[],"baseSha":null,"claimGeneration":null,"critic":"shrike-4035","diffAuditReceipts":[],"diffAuditRequired":false,"digest":"7eb47e320071417f4fc6ecd78f38b8e66b6d1bcdd71f356ae6d0ecbd904d320e","headSha":"02d5a50ba8cf14192655bd40f5179d8baad3f9ee","initialReview":null,"kind":"initial","policyVersion":"structured-decisions/1","precedingReview":null,"previousDigest":null,"repairPhaseReceipt":null,"revision":1,"round":0,"routeApplicability":"meaningful","routeEvidence":["Built artifact: exact-head Release build of src/FS.GG.Coord.Cli/bin/Release/net10.0/fsgg-coord-engine at 02d5a50; launcher SHA-256 f7dc79059479edcdaacaa79d6bcb0f96bef311a7f52331f51c29e8041e526153 and DLL SHA-256 1f334640469ceb00b8f8198c88043e8a264b66c4a4eb7b1cffa0f35d91338406. Verification: dotnet build src/FS.GG.Coord.Cli/FS.GG.Coord.Cli.fsproj -c Release completed with 0 warnings and 0 errors in the detached exact-head worktree.","Executed command: against the production HTTP adapter and the instrumented blockedby parity server, the compiled engine ran set-field --worker critic-route FS.GG.SDD#42 \u0027Blocked by\u0027 --replace \u0027#290\u0027, then set-field --batch --worker critic-route FS.GG.SDD#42 \u0027Blocked by=#299\u0027. The first command posted one REST mutation-lease comment; the second mutated Blocked by while the cumulative lease-post count remained one. Verification: both commands exited 0; the server recorded the first set to FS-GG/FS.GG.SDD#290 and the second aliased field mutation; temporary observation-only instrumentation was removed and git diff --exit-code was clean afterward.","Compared routes: explicit set-field Blocked-by mutations enter Writes.withBlockedByMutationLease at Handlers.fs:575, while the production batch replacement route proceeds through setFieldBatchCmd and boardWriteBatch at Handlers.fs:593-690 without entering that lease; release --blocked-by is a second production bypass through Board.boardWrite at Client.fs:5484-5525. Verification: rg -n \u0027withBlockedByMutationLease\u0027 over src and tests found the production call only at Handlers.fs:575, alongside the declaration/signature and one test call; exact-head source inspection found the named batch and release writers.","Observed result: CHANGES REQUIRED. Material finding 1: the new comment lease serializes only callers using the explicit set-field mutation route, not every production writer of the same authoritative Blocked by field. A batch or release replacement can therefore mutate after boardWriteGuarded\u0027s re-read at Board.fs:1463-1473 and before its unconditional Projects-v2 mutation; the derived add/remove can then overwrite that intervening value, so no serial order explains the result and issue acceptance for atomic add/remove/concurrent-change safety remains unmet. Route all authoritative Blocked-by writers through the same serialization boundary or provide real server-side conditional authority, and add a discriminating interleaving test against at least one bypass route. Material finding 2: the required SDD front-half still specifies the refuted read-then-write design: clarifications.md DEC-002 and plan.md PD-003 call the immediate re-read the solution and do not describe the subsequently added lease or its all-writer invariant. The generated verify receipt also carries VF001 because evidence.yml pins analysis.json as 3ae6df877929bd095ea483ed27cced27a3a43bcfe0acd2c4ada8c1b5411888ad while the exact file is 38a2c866a5b712987a139f889e06969751a270028812b7fa4e61bf9b6a9c805e. Update the SDD decisions/plan/tasks and regenerate evidence/analyze/verify/ship so the governing design and source snapshots describe the repaired mechanism. Other exact-head checks passed: BoardOps 257/257, GitHub 671/671, parity 616/616 with 0 not measured, check-parity-fixtures 46/46, verify-paths OK, effective base 712f025, obligations-none comment 5406348970 binds the head, PR body has zero edits, and issue body edits all predate current claim generation 5406278215. The two current CI failures are the expected pre-authorization claim-generation/claim-fence gates; at census time 42 checks passed, 2 were in progress, 2 expected failures, and 1 skipped across all 47 check-runs. No mechanically discovered semantic rename requires diff audit. This decision does not accept, merge, authorize, publish, or fulfill delivery."],"schema":"fsgg.coord.review-decision/v2","subject":"#2907/pr/2936","succession":null,"timestamp":"2026-08-25T06:35:01Z","verdict":"changes-required"}

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"acceptedExceptions":[],"baseSha":null,"claimGeneration":null,"critic":"curlew-5907","diffAuditReceipts":[],"diffAuditRequired":false,"digest":"32cbc237c3f5d125b0cb2602c1ec84009a9c7f8e71875fe17c4633bba6844d48","headSha":"6cd87934b3dfc239da337e5b6f469b377323fc86","initialReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406502532","kind":"confirmation","policyVersion":"structured-decisions/1","precedingReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406502532","previousDigest":"7eb47e320071417f4fc6ecd78f38b8e66b6d1bcdd71f356ae6d0ecbd904d320e","repairPhaseReceipt":null,"revision":2,"round":1,"routeApplicability":"meaningful","routeEvidence":["Built artifact: exact-head Release build of src/FS.GG.Coord.Cli/bin/Release/net10.0/fsgg-coord-engine at 6cd8793; launcher SHA-256 f7dc79059479edcdaacaa79d6bcb0f96bef311a7f52331f51c29e8041e526153 and DLL SHA-256 f6ad2c4e3e6713a65fd40a5e392ac19717e867fb2b234c72008f4a6b7473ca88. Verification: dotnet test tests/FS.GG.Coord.Cli.BoardOps.Tests/FS.GG.Coord.Cli.BoardOps.Tests.fsproj -c Release built the exact detached head and passed 258/258 tests; git diff --exit-code remained clean outside build outputs.","Executed command: against tests/coord-engine-parity/blockedby_server.py, I installed live lower-ID mutation-lease comment 1000, placed one production-shaped deferred queue entry for FS-GG/FS.GG.SDD#42 field \u0027Blocked by\u0027, then ran the compiled exact-head engine\u0027s flush command with that cache. It exited 0 and reported \u0027replayed 1 of 1 queued write(s)\u0027. A direct set-field --batch Blocked-by replacement against the same active lease exited 1 with \u0027lease is held by comment 1000; no board mutation was sent\u0027. Verification: GET /_writes after both commands returned exactly one mutation, the flush replay setting Blocked by to FS-GG/FS.GG.SDD#299, while the active contender comment remained present.","Compared routes: the repaired direct routes are wrapped by withBlockedByLeaseForWrites/Writes.withBlockedByMutationLease in Handlers.fs:483-490, 718-719, 2269-2279 and Client.fs:43-50, 2986-2990, 5540-5541. The deferred route is created by Board.boardWrite/boardWriteBatch at Board.fs:1398-1433 and 1513-1534, but Board.flush replays each entry directly through private attempt at Board.fs:1662-1678 without acquiring or checking the Blocked-by mutation lease. Verification: rg -n \u0027Board\.boardWriteBatch|Board\.boardWrite|Blocked by\u0027 over src plus exact-head source inspection; the production-route execution above discriminated the direct and deferred paths under the same live contender.","Observed result: CHANGES REQUIRED. Material finding 1: the repair still does not serialize every authoritative Blocked-by writer. Any explicit replace/clear, batch, release, or reconcile write that is rate-limited is queued while the lease is held, then the lease is released; a later flush replays that unconditional set/clear through Board.attempt without the lease. The executed comparison proved flush mutated under an active lower-ID contender while the direct batch route correctly refused. A flush can therefore land between a derived writer\u0027s observation and mutation and be overwritten, violating AC-003/FR-003 and the PR body\u0027s \u0027every authoritative writer\u0027 claim. Route Blocked-by replay through the same lease (with queue behavior that cannot duplicate/drop entries), or make Blocked-by writes non-deferrable and require re-derivation; add a named flush-vs-derived interleaving inversion and update/regenerate the SDD fixed point to include this authority route. Material finding 2: the current PR body carries fsgg:pr-authorization grant=5406718363/opkey=d163d676... for this exact head even though live review remains state awaitingSuccessorReview/action dispatchSuccessor and no confirmation or host acceptance exists. body-edits reports the authorization edit at 2026-08-25T07:01:05Z. pnext-item section 6 permits the live delivery authorization call only immediately after host acceptance; the premature current-head grant causes the edited-trigger claim-generation/claim-fence producers to evaluate an otherwise unreviewed head as authorized. Remove the marker so the edited workflows revoke it, and create a fresh authorization only through the documented post-acceptance delivery call. The repair otherwise closes the initial direct-writer and stale-SDD findings: current spec/clarifications/plan/tasks hashes agree with the generated views, verificationReady/shipReady are current, FSGG-PATHS is OK, obligations-none comment 5406348970 binds this head, and no semantic rename requires diff audit. This decision does not accept, merge, authorize, publish, or fulfill delivery."],"schema":"fsgg.coord.review-decision/v2","subject":"#2907/pr/2936","succession":null,"timestamp":"2026-08-25T07:05:42Z","verdict":"changes-required"}

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"acceptedExceptions":[],"baseSha":null,"claimGeneration":null,"critic":"snipe-8da1","diffAuditReceipts":[],"diffAuditRequired":false,"digest":"e669cf93646719c47b6e448a644cf17ca1f63fc0da02ecc783fa48d0ef830059","headSha":"9f3149e286879a8da9fbff0671afdbf385c70f82","initialReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406502532","kind":"confirmation","policyVersion":"structured-decisions/1","precedingReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406768001","previousDigest":"32cbc237c3f5d125b0cb2602c1ec84009a9c7f8e71875fe17c4633bba6844d48","repairPhaseReceipt":null,"revision":3,"round":2,"routeApplicability":"meaningful","routeEvidence":["Built artifact: exact-head Release fsgg-coord-engine and BoardOps test assembly at 9f3149e. Verification: dotnet test tests/FS.GG.Coord.Cli.BoardOps.Tests/FS.GG.Coord.Cli.BoardOps.Tests.fsproj -c Release rebuilt the detached head and passed 259/259; the focused repaired flush test passed after restoration, and git diff --exit-code was clean.","Executed command: against tests/coord-engine-parity/blockedby_server.py, I posted live lower-ID mutation-lease comment 1000, installed one production-shaped deferred Blocked by entry, then ran the compiled engine\u0027s set-field --batch replacement and flush commands with the same cache. Both exited 1 under contention. Verification: GET /_writes returned an empty writes array; pending.jsonl retained exactly one line; flush reported replayed 0 of 1 and no duplicate appended.","Compared routes: every authoritative direct Blocked by route is wrapped through withBlockedByLeaseForWrites or Writes.withBlockedByMutationLease in Handlers.fs and Client.fs, while Board.flush now reacquires Writes.withBlockedByMutationLease before replaying a queued Blocked by entry through attempt. Verification: exact-head rg/source census of every Board.boardWrite, Board.boardWriteBatch, and Board.boardWriteGuarded call plus the executed direct-batch versus deferred-flush comparison; FSGG-PATHS reported OK.","Observed result: PASS. The flush repair closes round 1 comment 5406768001: lower-ID contention causes zero field mutations, the unlanded entry remains queued once, and disabling only the replay lease predicate makes the named flush interleaving test fail Expected 0, Actual 1; restoration returns it green. The corrected SDD fixed point is current: fsgg-sdd analyze/verify/ship --dry-run each reported coherent true, noChange, no diagnostics, with implementationReady, verificationReady, and shipReady. The premature authorization is durably absent from the live PR body: body-edits records removal at 2026-08-25T07:08:21Z, live marker count is zero, and check-claim-generation against current head returns [missing]. Election 5406718363 alone authorizes no head; a later documented post-acceptance delivery call will intentionally reuse that append-only election as grounding but must create a new exact-head PR authorization marker, so no claim-generation rotation or election deletion is required. No semantic rename requires diff audit, and no unresolved material finding remains. This decision does not accept, merge, authorize, publish, or fulfill delivery."],"schema":"fsgg.coord.review-decision/v2","subject":"#2907/pr/2936","succession":null,"timestamp":"2026-08-25T07:28:28Z","verdict":"pass"}

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

Host critic-succession grant

The round-2 record passed at the prior head, then hosted validation exposed two production-fixture regressions that required a genuine implementation repair and a moved head. The SDD review contract requires a fresh independent critic for that repair-confirmation; this grant accounts for the identity change and is bound to the exact candidate head. It is not host acceptance, delivery authorization, or a repair-phase grant.

{
  "originalCriticIdentity": "snipe-8da1",
  "successorCriticIdentity": "teal-9f95",
  "grantedBy": "teal-682a",
  "reason": "round-2 pass followed by hosted write-harness regression repair requiring fresh independent confirmation",
  "candidateHeadSha": "d0b493569229dd749d3803c0670773932c80e192"
}

The successor must record its own fresh verdict against this exact head and bind this grant URL in the structured decision's succession object.

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"acceptedExceptions":[],"baseSha":null,"claimGeneration":null,"critic":"teal-9f95","diffAuditReceipts":[],"diffAuditRequired":false,"digest":"bb4ca0ff2f7785092093103d3f074678712c0a6537007a818643fd2ee11597f0","headSha":"d0b493569229dd749d3803c0670773932c80e192","initialReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406502532","kind":"confirmation","policyVersion":"structured-decisions/1","precedingReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406997564","previousDigest":"e669cf93646719c47b6e448a644cf17ca1f63fc0da02ecc783fa48d0ef830059","repairPhaseReceipt":null,"revision":4,"round":3,"routeApplicability":"meaningful","routeEvidence":["Built artifact: exact-head Release fsgg-coord-engine at d0b4935; launcher SHA-256 f7dc79059479edcdaacaa79d6bcb0f96bef311a7f52331f51c29e8041e526153 and DLL SHA-256 c88e6184cf82c09664c8d4e2dd3f2228d0daaa79fd75ede7b912e135f4bb85a8. Verification: dotnet build src/FS.GG.Coord.Cli -c Release completed with 0 warnings and 0 errors in the detached exact-head review worktree; the durable exact-head BoardOps TRX reports total=259, executed=259, passed=259, failed=0.","Executed command: PATH=/tmp/fsgg-sdd-1.0.m625FK:$PATH tests/coord-engine-e2e/writes.sh exercised the production HTTP adapter with the explicitly vetted fsgg-sdd 1.0.0 validator and passed 186/186. The repaired #2134 intake leg observed issue POST, board-add GraphQL, lease POST, atomic dependency GraphQL, lease DELETE; the repaired #2157 reconcile leg observed lease POST, atomic Status-plus-Blocked-by GraphQL, lease DELETE, then durable lifecycle receipt POST. Verification: both named legs printed PASS and the final corpus summary was 186 passed, 0 failed.","Compared routes: restoring only tests/coord-engine-e2e/writes.sh from prior passed implementation head 9f3149e while retaining the exact d0b4935 production artifact made the vetted full corpus 184/186: only #2134 Blocked intake and #2157 lifecycle receipt/atomic batch were red on the new five-request and four-request lease traffic. Restoring the d0b4935 assertions returned 186/186. The exact-head parity production comparison independently passed 616/616 with 0 not measured, so the assertions discriminate the repaired wire ordering without regressing the broader command routes.","Observed result: PASS. Round 3\u0027s hosted failures are repaired: the exact order assertions preserve the semantic projectionFresh, projected-field, fresh-observation, coupled-write, and durable-receipt checks while adding the missing lease-wire boundary. fsgg-sdd 1.2.5 analyze, verify, and ship dry-runs each returned coherent=true, outcome=noChange, zero diagnostics, and respectively implementationReady, verificationReady, and shipReady; generated Claude/Codex command, skill, and normalized guidance projections are equivalent. Effective review base remains 712f025; current main b84f7db changes no path touched by this PR, and git merge-tree produced conflict-free tree 9a58941718b84b217ec4cbe271bbcd1ecae9cee0, so #2906 introduces no semantic overlap. Live PR head is exact and MERGEABLE, current obligations-none comment 5406348970 binds d0b4935, the PR body contains no fsgg:pr-authorization marker, and the structured ledger contains no acceptance record. The 47 hosted check-run census currently has 42 successes, one skip, two expected pre-authorization failures, and two still-running jobs; checks and host acceptance remain host-owned next gates. No semantic rename requires diff audit, and no unresolved material finding remains. This decision does not accept, merge, authorize, publish, or fulfill delivery."],"schema":"fsgg.coord.review-decision/v2","subject":"#2907/pr/2936","succession":{"grantUrl":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5407445734","grantedBy":"teal-682a","originalCritic":"snipe-8da1"},"timestamp":"2026-08-25T08:15:00Z","verdict":"pass"}

@EHotwagner

Copy link
Copy Markdown
Contributor Author

@EHotwagner

Copy link
Copy Markdown
Contributor Author

{"acceptedExceptions":[],"baseSha":"b84f7db7e325a73e6d03b25103a2acf8a193c273","claimGeneration":"5406278215","critic":"teal-9f95","diffAuditReceipts":[],"diffAuditRequired":false,"digest":"2d9f57beeffd5ae34471c55b6aa60e6dc809c49aa24aae33d55d1666b883086d","headSha":"d0b493569229dd749d3803c0670773932c80e192","initialReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5406502532","kind":"acceptance","policyVersion":"structured-decisions/1","precedingReview":"https://github.com/FS-GG/.github/pull/2936#issuecomment-5407448928","previousDigest":"bb4ca0ff2f7785092093103d3f074678712c0a6537007a818643fd2ee11597f0","repairPhaseReceipt":null,"revision":5,"round":3,"routeApplicability":"meaningful","routeEvidence":["Built artifact: host acceptance binds the exact reviewed Release implementation head d0b4935 and the critic\u0027s independently built engine, BoardOps assembly, generated skill projections, and SDD readiness views at that head; no host rebuild is represented as critic evidence.","Executed command: the independent round-3 record ran the vetted production writes corpus at 186/186, parity at 616/616, BoardOps at 259/259, and SDD analyze/evidence/verify/ship to a coherent no-change fixed point; the host re-read all 47 settled GitHub checks and the live typed review ledger after wait completion.","Compared routes: the critic\u0027s dual lease-acquire inversion produced exactly 184/186 with only #2134 and #2157 red, restoration returned 186/186, equivalent Codex and Claude skill projections exposed all six required skills, and a synthetic merge against current main b84f7db was conflict-free with no semantic overlap from #2906.","Observed result: ACCEPTED. Fresh successor critic teal-9f95 passed with no unresolved material findings under exact-head host grant 5407445734. Hosted checks are settled with 42 successes, one skip, and only the expected pre-authorization claim-generation and claim-fence failures. The PR body has no authorization marker; this acceptance does not merge or authorize delivery."],"schema":"fsgg.coord.review-decision/v2","subject":"#2907/pr/2936","succession":null,"timestamp":"2026-08-25T08:16:13Z","verdict":"accepted"}

@EHotwagner
EHotwagner merged commit a300406 into main Aug 25, 2026
56 of 58 checks passed
@EHotwagner
EHotwagner deleted the item/2907-blocked-by-set-mutations-recovery branch August 25, 2026 16:50
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.

[coord] Give the set-valued Blocked by field atomic add/remove semantics and lint inert body declarations

1 participant