ci: pin the provenance filters on the writer that actually runs - #848
Conversation
G008 carried twelve pins on `scripts/stage_coss_group_payroll_readiness.sql` and none on `roster.rs`. That was right while the script was the ONLY writer of `payroll_draft_lines`. It stopped being right at #846: `roster::materialise_roster_in_tx` now runs on every `payroll.create_run`, and the pinned file is no longer on the production path. So the mechanical proof of "what may become a payroll roster" sat entirely on one encoding, while a second encoding did the actual work. Nothing stopped the two drifting, and G008 would have stayed green while the executed one weakened. The tests added with #846 do bind those predicates — but they bind them from the outside, and a text pin is what makes a REVIEWER see the divergence in the diff. Six pins on the writer, each mutation-proven against it: drop `run.status = 'APPLIED'` -> exit 1 drop `r.row_status <> 'ERROR'` -> exit 1 equality -> overlap on the period -> exit 1 weaken ONE of the four non-blank source-material flags -> exit 1 (counted, not `includes`: a plain includes passes while three of the four are weakened) reintroduce `?|array` -> exit 1 add a reconciliation DELETE -> exit 1 restored -> exit 0, 29 checks DELIBERATELY NOT RE-POINTED FROM THE SCRIPT. Two of the script's pins would be actively harmful on the writer. `requireMatches(/raw_row\?\|array\[/)` asserts the key-presence idiom — the exact fabrication vector where a blank `출근` cell counts as attendance material — so re-pointing it would make CI REQUIRE the bug. The writer is pinned on the non-blank form instead, and on the ABSENCE of `?|array`. The DELETE pin is not stylistic: 0222 revoked DELETE on this table from `console_rt` and asserts the revocation, so a reconciliation delete raises 42501 at PLAN time and kills every `payroll.create_run` — not just the re-stage that introduced it. The script keeps its own twelve pins. It is still the operational hand-run path, and retiring it is a separate change: reducing it to a read-only query would leave all twelve passing over a file that writes nothing, which is a false green rather than a removal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anvil Adversarial Pull Request Review — PR #848Verdict: Executive OverviewPR #848 addresses a real and critical architectural gap: following PR #846, However, while the intent of pinning
16-Lens Canonical Reasoning Matrix
Critical Risks & Required Remediation
Findings not addressable in the diff
Pinning the literal parameter Recommendation: requireMatches(
rosterWriter,
/run\.pay_period_start\s*=\s*\$\d+/,
"production roster writer scopes by the declared pay period, by equality",
);
Because Recommendation: requireNotIncludes(
rosterWriter,
"?|",
"production roster writer never uses key-presence operator ?|",
);Or (if requireNotMatches(
rosterWriter,
/\?\|\s*array/i,
"production roster writer never treats a merely PRESENT column as material",
);
Checking for the exact substring Since Recommendation: requireNotMatches(
rosterWriter,
/delete\s+from\s+payroll_draft_lines/i,
"production roster writer never deletes: console_rt holds no DELETE on this table",
);🤖 [Reviewed] by Oyatie Anvil |
|
❌ Blocked — 7 finding(s) across 68 gates; 6 gate(s) produced no measurement.
🤖 [Blocked] by Oyatie Anvil |
G008 carried twelve pins on the staging script and none on
roster.rs. That was right while the script was the only writer ofpayroll_draft_lines. It stopped being right at #846 —roster::materialise_roster_in_txnow runs on everypayroll.create_run, and the pinned file is no longer on the production path.So the mechanical proof of "what may become a payroll roster" sat entirely on one encoding while a second encoding did the actual work. Nothing stopped the two drifting, and G008 would have stayed green while the executed one weakened.
The tests from #846 do bind these predicates — but from the outside. A text pin is what makes a reviewer see the divergence in the diff.
Six pins, each mutation-proven against the writer
run.status = 'APPLIED'r.row_status <> 'ERROR'The non-blank pin is counted, not
includes— a plainincludespasses while three of the four flags are weakened. That's the same hole I shipped once already in #836 and had to fix.Deliberately not re-pointed from the script
Two of the script's pins would be actively harmful here.
requireMatches(/raw_row\?\|array\[/)asserts the key-presence idiom — the exact fabrication vector where a blank출근cell counts as attendance material. Re-pointing it at the writer would make CI require the bug. The writer is pinned on the non-blank form instead, plus the absence of?|array.The DELETE pin isn't stylistic: 0222 revoked DELETE on this table from
console_rtand asserts the revocation, so a reconciliation delete raises 42501 at plan time — killing everypayroll.create_run, not just the re-stage that introduced it.The script keeps its twelve pins
It's still the operational hand-run path. Retiring it is a separate change, and not a trivial one: reducing it to a read-only query would leave all twelve pins passing over a file that writes nothing — a false green rather than a removal.
🤖 Generated with Claude Code