# Argus extraction v1 — checkpoint round 2, CLOSURE (2026-09-08)

Reviewer: independent reviewer, round 2, **closure angle** — does each round-one finding actually close, and does
it close at the cause. Branch `ops/argus-extraction-v1-red`, head `632dfbb`, reviewed in its **committed** state.
The working tree's `src/Sibyla.Web/**` and `tests/Sibyla.Tests.Browser/**` are another session's and out of scope.
Round-one reports: `docs/apollo-argus-extraction-v1-checkpoint-review-contract-260908.md` (C-n) and
`docs/apollo-argus-extraction-v1-checkpoint-review-evidence-260908.md` (E-n, M-n, L-n). Closure commits: `ed2616f`,
`29e1bfc`, `21097cc`, `632dfbb`. I did not read the other round-2 reviewer's work.

## Verdict

**REVISE — Critical 0 / High 1 / Medium 6 / Low 4.**

**Closure tally: 32 CLOSED, 8 PARTLY, 1 NOT CLOSED, 1 NOT APPLICABLE (42 findings).**

The engineering is strong. Every reproduction round one left behind now behaves the other way, I ran them all, and
the fixes reach causes rather than symptoms — the validator refuses instead of throwing *and* the worker grew a net
under it; the kind tally was corrected and the test that had enshrined the defect was corrected with it; the
register comparison went through the seam §4.7 names instead of being copied. I re-scored the 40-document sample
from the kept answers with the committed bench and reproduced the combined report **byte-for-byte apart from its
timestamp**. What fails is not the code but the record: one section of the spec still carries the sentence the
Critical ruling exists to retract, six spec amendments are written in a form no markdown renderer displays, the
half of the work that fixed twelve findings recorded no RED run at all, and the four superseded sitting reports
still headline "Gate (S-10): PASSED".

---

## Closure table

Evidence column: `H` = harness reproduction (`D:\fileStorage\tmp\closure-harness`, a console project referencing
the built `Sibyla.Platform.Infrastructure.dll`, nothing written in the repository); `T` = suite run; `R` = an
independent re-score of the committed answers; `C` = code/diff read.

| id | sev | state | evidence | what remains |
|---|---|---|---|---|
| **C-1** | High | **CLOSED** | H: 18 wrong-kind shapes now refuse. `header` = `5` / `[]` / `"nope"`, `header.issuer` scalar, `header.recipient` `[]`, `payment_proof` `[]`, `service_period` `3`, `lines[0].service_period` `[]`, `lines[0].sub_issuer` `[]`, `evidence` `5`, `statement` `5`/`[]`, `lines` `5`, `lines[0]` `5`, `movements` `7`, `movements[0]` `5` — every one `V-5: <path> is not an object/array`; root scalar/array → `V-1`. Cause fix: `Read` guards at entry (`ExtractionContractV2.Schema.cs:246-250`), `ReadValue` guards `Obj`/`StrArray`/`ObjArray`. Defence in depth: `Validate` now also catches `OverflowException`, and `ExtractionRunner.cs:208-222` catches **any** non-cancellation exception out of the validator and returns a failed validation *with* its evidence element — which is the round-one consequence (five burned attempts, no evidence, no rule) killed at its own cause. Four reject fixtures + `expected.txt` rows; `ExtractionRunnerTests.AValidatorThatThrowsIsAFailedValidationWithItsEvidence` | — |
| **C-2** | High | **CLOSED** | R: re-ran `score` over the two sittings' kept answers into a temp evidence root — Header 445/447 = 99.55 %, Lines 342/347 = 98.56 %, Movements 1349/1419 = 95.07 %, gate PASSED; the per-field table now sums exactly to the per-kind table (447/347/1419), which round one showed it did not. `Scorer.cs` tallies each kind over all its cells; `FloorApplies` is the floor's alone. The test that *enshrined* the defect (`TheGateIsPerKindAtNinetyFivePercentAndTheFloorNeedsTwentyCells`) was corrected in place: it now asserts `False(nineteen.GatePassed)` and `lines 109/119` where it used to assert the gate passed | — |
| **C-3** | High | **CLOSED** | C: `ON CONFLICT (owner_id, entry_code, source_filename) DO UPDATE` plus a `replacedPriorDeclaration` flag in the audit detail (`IngestionService.cs:624-651`). T: `ASecondRulingOnTheSameFileAndEntryReplacesTheDeclarationInsteadOfRaising23505` rules twice on the same (entry, filename) against a real database and asserts the second verdict wins and the audit says so on the second and not on the first | — |
| C-4 | Med | **CLOSED** | H: 200 `A` + trailing space **ACCEPTED**; 99 `B` + ten spaces + 100 `C` (normalises to 200) **ACCEPTED**; 201 `A` refused `V-4 … (201)`; `doc_type_printed` 60 + space accepted, 61 refused. Control characters are still measured on the wire text before N-1, which is right | — |
| C-5 | Med | **PARTLY** | H: `not_printed` naming an object of three nulls is still accepted (unchanged, and correct); the spec was amended instead. But the V-17 amendment is a **third cell in a two-column table** and is dropped by every GFM renderer — see finding **N-2** | render the amendment: fold it into the V-17 cell |
| C-6 | Med | **CLOSED** | C: the summarised branch computes `exception = answerDocType != "credit_note" && (l.Kind == StampKind \|\| l.Net < 0m)` — the same expression the per-line branch uses (`Scorer.cs:271-292` vs `:306`). RED recorded with the number: expected `260.41`, actual `270.41` | — |
| C-7 | Med | **CLOSED** | C: the summarised sums read the key through `CorrectedDecimal(code, $"lines[{lineNo}].{keyName}", …)` — the identical key format the per-line branch uses (`lines[{no}].netAmount`) — and the correction notes reach the cell, so the count sees them. RED: expected `250.41`, actual `260.41` | — |
| C-8 | Med | **CLOSED** | C: `PartyTaxId` is `Str(p,"tax_id")` only, matching `ExtractionResult.PartyTaxId`. RED: `Assert.Null` failed with `"Company no. 12465777"` | — |
| C-9 | Med | **CLOSED** | C: the unpaired-answer branch emits `posting_date, doc_date, amount, currency, description, [running_balance], period` — cell-for-cell the unpaired-key branch. R: the reproduced combined report shows seven cells on both `movements[BM000905]` and `movements[seq=2]`; movements fell from 96.04 % to 95.07 %, i.e. the fix costs points rather than earning them | — |
| C-10 | Med | **CLOSED** | C: `CompanyGate.cs:162` calls `CompanyMatcher.SameTaxId`; `InternalsVisibleTo("Sibyla.Worker.Documents")` added. T: `ALabelledRegisterFiscalNumberStillMatchesTheAnswersCounterparty` seeds `entmst.fiscal_no = "NIPC: 500 940 231"` and asserts the match — a fixture that fails under `Match` | — |
| C-11 | Med | **CLOSED** | C: `SameNormalized` returns false when both sides carry a prefix. T: `AnEntryWhoseFiscalNumberCarriesAnotherCountryPrefixIsNotAMatch` seeds `ES500940231` against an answer of `PT500940231` and asserts Processed with no register reference | — |
| C-12 | Med | **CLOSED** | C: `!Within(sumAllLines, printedNet)` — the default ± 0.02, no zero argument; the spec's §2.6 sentence carries the amendment inline (renders) | — |
| C-13 | Med | **PARTLY** | C: V-15's either-direction reading is unchanged in code (correct) and amended in the spec — but as a dropped third cell, **N-2** | render the amendment |
| C-14 | Med | **CLOSED** | C: the SELECT fetches `hold_reason` and the audit detail carries it. T: the old unfailable `Contains("held", …)` became `Assert.Equal("timeouts:2", …)`, and a second test asserts `movements_truncated:87/60` — both would fail on the old constant | — |
| C-15 | Med | **CLOSED** | C: `MatchedFiscalKeyAsync` decides the side from the entry's own `entmst.fiscal_no` with `SameTaxId`, falling back to `flow_type`; the declaration names the number and the side. T: `TheDeclarationNamesTheFiscalKeyTheMatchWasMadeOnNotIssuerBlindly` asserts `IE3559150RH` present, `PT513362061` **absent**, `recipient` named | — |
| C-16 | Med | **CLOSED** | C: `if (registerEntry is null) intake.DuplicateOfId = null;`. T: both directions — a both-flagged row keeps its checksum reference, a plain checksum duplicate still clears it | note that the released row reaches `Processed` with the checksum question open and nothing re-raises it; that follows the finding's own ask |
| C-17 | Med | **CLOSED (as a stated gap)** | The gap is described accurately in §2.6's gate paragraph ("the lookup runs only for a row the gate has just assigned … an upload made *into* a company … gets no register check in this slice") and in owner guide §8.3, which puts the extension to the owner as a decision. T: `APreAssignedRowGetsNoRegisterLookupInThisSlice_KnownGapUntilThePersistenceGuard` seeds an intake **with** a company and the same answer that matches `I26010021` in the (a) fixture, and asserts Processed with no reference — it does exactly what its name says and fails the moment the gate is extended | — |
| C-18 | Med | **CLOSED** | R: re-scored the reserve set with `--reserve` — the report prints "ruled flags applied: **3**; … 10/268 = **3.73 %**", so the mechanism fires on the real flags file (whose three D-EX-6 rows carry `"ruling"`). The 5 % breach is a gate reason. Scope limit → **N-6** | — |
| C-19 | Med | **CLOSED** | R: `Score-Combined.ps1` / the bench's `score` verb reproduced `combined-20260908-fiscal-statements-a558523.md` byte-for-byte apart from the "Scored on" timestamp; 40 documents, one set, no CLI. Spec §5.7 records that the R-EX-3 gate is that report's | the owner guide does not — **N-4** |
| C-20 | Med | **CLOSED** | C: the five release facts are in `src/Sibyla.Worker.Documents/appsettings.json` (inside the published tree); `publish-release.ps1` refuses a release missing any of them or whose hash/commit disagrees with `skill-package.manifest.json` (whose keys I checked: `treeSha256`, `skillCommit` — the gate is live, not dead). Owner guide §8.2 names the `appsettings.Production.json` override trap | the fingerprint that is supposed to bind it has not been re-taken — see **M-4** |
| C-21 | Low | **CLOSED** | H: `ZZZ` and `XYZ` refused `V-13 … is not a three-letter ISO 4217 code`; `USD` and `HRK` accepted; `eur` refused | — |
| C-22 | Low | **CLOSED** | H: `vat_rate 23.123456` and `0.001` refused "has more than 2 decimals"; `quantity 1.234567` still accepted at 6 dp, so the exemption was narrowed and not moved | — |
| C-23 | Low | **CLOSED** | H: the new fixture refuses `V-10: the 2 stamp_tax lines' net 2.00 and header.stamp_duty_amount 1.00 disagree`; `stampPrinted` replaces `stampLine is not null` in V-8 and V-9 too | — |
| C-24 | Low | **PARTLY** | H: N-3 is now applied by the reader — `"NIPC: 500 940 231"`/PT → `PT500940231`, `"19.628.811/0001-60"`/BR → `BR19628811000160`, `GB12465777` left alone. All six accept goldens still canonicalise byte-equal | the spec amendment is a dropped third cell (**N-2**); and the doc comment claims "a label before the number dropped", which N-3 does only for a colon form — `NIPC504615947` comes back unchanged (`SameTaxId` covers it; N-3 does not) |
| C-25 | Low | **CLOSED** | C: `PromptHash(prompt, documentPath, packageDir)` tokenises the prompt that ran; the evidence uses it. T: `TheEvidenceHashesThePromptThatRanNotTheFixedLine` and `ThePromptHashDistinguishesOnePromptFromAnother` — the parity oracle can now fail | — |
| C-26 | Low | **CLOSED** | C: `PrefixShare = 0.5m`; `if (shorter.Count < longer.Count * PrefixShare) return false`. RED recorded ("TRF" matched a seven-token key). Spec row dropped (**N-2**) | — |
| C-27 | Low | **CLOSED** | C: a non-zero header-minus-Σ-lines difference is a finding in **every** branch, with the branch named in the evidence string | — |
| C-28 | Low | **CLOSED** | C: migration `20260908014913_ExtractionV2RegisterEntryCodeWidth` (varchar(20) → text) and `HasMaxLength(20)` removed. T: `ARegisterEntryCodeLongerThanTwentyCharactersIsStoredWhole` with a 24-character code | Main not migrated (owner guide §8.1 says so) |
| C-29 | Low | **NOT APPLICABLE** | Recorded by round one as "noted; no fix" | — |
| **E-1** | **Critical** | **PARTLY** | D-EX-7 is recorded in §1.1, §5.7's F-22 plan, S-10, the appeals sheet (a dated event table, the blind rates and the destroyed evidence) and owner guide §4 (struck-through step). The wording does **not** soften: "three honest measured sittings and no held-out result", "the reserve set is spent", the false PASSED named. **But §5.6 was never touched** — see **N-1** | one sentence in §5.6 |
| **E-2** | High | **CLOSED** | Same evidence as C-2; the two reports reconcile and the correction moved the movements figure **down** | — |
| **E-3** | High | **PARTLY** | The blind rates (76/80 = 95.00 %, 27/51 = 52.94 %, 158/158) now appear in D-EX-7, the appeals sheet, the owner guide and the commit record. R: I confirmed the post-ruling reserve is 80/80, 30/30, 158/158 under the corrected tally | the second half was not done: a reserve report still headlines "**Gate (S-10): PASSED**" (`--reserve` only widens the key loader), and the two committed reserve reports still print "Gate PASSED" over "Header — 0 not gated / Lines — 0 not gated" — **N-5** |
| **E-4** | High | **CLOSED** | Cause fixed: `BenchRun.ReadRawStdout` joins the run folder before `File.Exists` (the path is documented relative to the evidence root). R: my own re-score read **48 attempt traces and 188 file reads**, every one of the 40 documents non-empty, `numTurns` on all 40. The usage-limit stop got a test (`AUsageLimitSignalInTheTraceStopsTheRunAtThatDocument`) after the loop was factored into a testable seam | the four sitting reports still print "Files read: 0"; the combined report discloses this on its face |
| **E-5** | High | **CLOSED** | `ee87b15` replaces the paragraph: names `rm -rf "$R"/*/`, says the whole run folder went (`actual/` **and** the ten per-document evidence folders), says "'Nothing is lost' was wrong", and states what survives (a 289-cell matrix, zero evidence elements). The original text is left above the correction | no mechanical guard against glob-deleting under `evidence/extract-v2/` was added |
| **M-1** | Med | **PARTLY** | Cause fixed: `BenchDocumentRecord.RestoreFrom(prior)` carries a resumed document's attempts, durations, turns, files read and evidence forward, and a wholly-resumed run reports the **original** window; two RED-recorded tests | the claim "M-1's damage to the committed fiscal report cannot be repaired without re-running the CLI" is **false** — see **N-5**. `git show a50e5d5:…score-20260907-1321….json` holds 35 records, **60 evidence elements, 60 attempts, 5,050,898 ms**, window 13:21 → 14:45, and the run folder is still on disk with the traces my re-score read. HEAD's copy has 0/0/0 and a 0.2 s window |
| **M-2** | Med | **CLOSED** | C: `CorrectedCellShare = 0.05m`, breach is a gate reason. R: exercised live — the reserve reports 3.73 %, the sample 1.08 % | — |
| **M-3** | Med | **CLOSED** | `RED-green.txt` gains a dated appendix that says plainly what is and is not evidenced: for the R-EX-3 cap amendment "there is no RED run recorded for them"; for D-EX-6 "recorded in the commit message and in the chat, not in this file". That is the honest version of the claim | the D-EX-6 RED remains an assertion; and the discipline lapsed again in this very round — **N-3** |
| **M-4** | Med | **NOT CLOSED** | `New-ExtractionFingerprint.ps1` is untouched since RED (`aca37be`): its roots are still `tests/Sibyla.Tests.{Platform,Argus,Browser}`, `src`, `tools`, `Sibyla.slnx` — **no `docs/`**. No third fingerprint at the reviewed head is recorded (`RED-green.txt`'s last is G3 `aba0ad5`, `60660617…`). No commit message, spec line or evidence line mentions M-4 | add `docs/apollo-argus-extraction-v1-spec.md` to the roots and take a fingerprint at the checkpoint head — which matters more now than in round one, because C-20's whole argument is that the release tuple is "inside the clean-export fingerprint" and the last fingerprint predates it |
| **M-5** | Med | **CLOSED** | C: `**\local\**` replaces the bare `local\**`, and the record says plainly that `--restricted` and the two `--add-dir` roots are what actually hold it. T: a **general** guard, `EveryDenyRuleIsAbsoluteOrAProvenWildcardForm`, asserts every emitted deny rule is an absolute drive path or a `**/` form — stronger than the finding asked for and it can fail | the bare relative form was not added to the probe matrix (the matrix is not re-run this round) |
| **M-6** | Med | **PARTLY** | §8's R-EX-3 row now splits pre-release evidence from the two rows that cannot exist before the release, and says a reviewer reading it as one list "will find two of its own gate items unmet and be right" | the finding named **two** unmet rows. The other — the §4.5 hostile-document run — is still not named as outstanding anywhere the owner looks (`grep -n hostile docs/apollo-argus-extraction-v1-owner-guide-260907.md` → nothing; `evidence/extract-v2/hostile-documents-run.md` does not exist), and the amendment now positively classes it as "evidence the reviewers read *before* the release", which they did not |
| **L-1** | Low | **CLOSED** | The appeals sheet reads "22 of the reserve's **28** missed cells" with a dated correction note; no stale "27 missed" remains in `docs/` or the evidence `.md` files | — |
| **L-2** | Low | **CLOSED** | `RED-green.txt`'s appendix names the superseding `083cbd79…c53c5` with `SkillCommit`, `CliVersion`, `Model`, `Effort`, `ClaudeTimeoutSeconds`, and explains that a stale value stops a release rather than corrupting one | — |

---

## New findings

| id | sev | file:line | what is wrong | why it matters |
|---|---|---|---|---|
| **N-1** | **High** | `docs/apollo-argus-extraction-v1-spec.md:476` (§5.6), against `:11` (the D-EX-7 amendment header) and `:57` (the D-EX-7 row) | §5.6 still ends: *"never opened while the package, `EXTRACT.md`, model or effort is being tuned — the reserve is the **third, single sitting** of §5.7 under the frozen tuple at R-EX-3."* That is the clause D-EX-7 quotes as false. `git log -L 476,476` shows the line last changed at revision 4 (`7f86af2`); none of `ed2616f`, `29e1bfc`, `21097cc`, `632dfbb` touched it. Meanwhile the amendment header at `:11` says the ruling is recorded in "§1.1 D-EX-7, **§5.6**, §5.7, S-10" | E-1 was the round's only Critical and its entire remedy is wording. The section that *defines* the reserve still asserts the seal, so the spec contradicts itself (S-10 now says the set "**was** run once, and then … a second time"), and the change log claims an edit that was not made. It is a one-sentence fix, but until it lands the Critical is not closed where its own ruling says it is |
| **N-2** | Medium | `spec.md:253` (N-3), `:267` (V-5), `:275` (V-13), `:277` (V-15), `:279` (V-17), `:450` (S-11) | Six amendments made by this closure work are written as a **third cell in a two-column table** (the delimiter rows at `:250` and `:262` are `\|---\|---\|`; `:439` likewise). Per the GFM tables spec, "if a row has more cells than the header row, the excess is ignored" — so all six amendment texts render as nothing. Detected mechanically; the only other over-wide row in the file (`:508`) is pre-existing from revision 1 | These six cells *are* the fix for C-5, C-13, C-24 and C-26, and they carry half of C-1's and C-21's record. A reader of the rendered spec — which is how a spec is read on GitLab, on GitHub or in a preview pane — sees V-17 as "resolves and is `null`", V-15 as one-directional, N-3 as the model's job and V-13 as bare "ISO 4217", i.e. exactly the text round one said was wrong. The raw file is right, so this is one pipe character per row |
| **N-3** | Medium | commit `21097cc`, message line "Every behaviour change here was observed RED for the reason the reviewer states before its fix"; `tests/Sibyla.Tests.Argus/evidence/extract-v2/RED-green.txt` | `RED-green.txt` has RED sections for the bench-and-scorer group (`632dfbb`, batch A eleven failures with expected/actual, batch B four, and an honest note that batch B's RED was produced by reverting after the fact) and for the M-3/L-2 corrections. It has **nothing** for `21097cc` — the group that closed C-1 (High), C-3 (High), C-4, C-10, C-11, C-12, C-14–C-17, C-20–C-25, C-27, C-28 and M-5. `grep` for any of its new test names (`ALabelledRegisterFiscalNumber…`, `EveryDenyRuleIsAbsolute…`, `TheReleaseCarriesTheTuple…`) returns nothing in that file, and the commit adds no evidence file | This is M-3 recurring inside the commit series that closes M-3, on the larger half of the work and on both High findings. The appendix written three commits earlier says the file should be "the whole record rather than most of it, which is what a checkpoint reviewer is entitled to assume it is". Either record the RED run for that group — reproducible by reverting each guard locally, as batch B did — or drop the claim from the commit record |
| **N-4** | Medium | `docs/apollo-argus-extraction-v1-owner-guide-260907.md:104-140` (step 4) | C-2 and C-19 changed how the bench behaves and where the gate is read: a kind with zero scored cells is now a **gate failure**, so every single-set sitting (`--set fiscal`, `--set statements`) will print "gate: FAILED — movements: no cell was scored…" and exit 1; the R-EX-3 gate is read off `Score-Combined.ps1`. Spec §5.7:486 records this. The owner guide does not: step 4 still says "Expect exit 0", "Read the `.md`", "the gate per kind at 95 %", and never mentions the `score` verb or the combined report | Q-EX-14 makes any CLI, model, effort or package change re-run the bench, so the owner *will* run a sitting again. The operative document will then tell them a red gate is a failure when it is the designed behaviour, and will not tell them how to produce the artifact R-EX-3 actually needs |
| **N-5** | Medium | `tests/Sibyla.Tests.Argus/evidence/extract-v2/score-20260907-{1321,1546,1613,2250}-*.md` | All four superseded reports still headline "**Gate (S-10): PASSED**" under the defective tally, with no banner, no supersession note and no pointer to the combined report. The two reserve reports print "Header — 0 not gated / Lines — 0 not gated" beside PASSED — the exact false green E-3 asked be stopped. The fiscal report additionally prints a 22:50 → 22:50 window with 0 attempts and 0 evidence elements. `632dfbb` says that "cannot be repaired without re-running the CLI"; it can: `git show a50e5d5:…json` carries 60 evidence elements, 60 attempts and the real 13:21 → 14:45 window, the run folder is still on disk (my re-score read 48 traces out of it), and the combined report's own header says so — "the run folder and the git history of that report hold when" | A checkpoint reader opening `evidence/extract-v2/` meets four PASSED headlines computed by a rule the spec has since repudiated, and one correct report that nothing in the other four points to. Two of the three post-ruling reserve/statement reports are re-scorable today from their kept `actual/` answers (I did it); the 1613 report is not, but a one-line supersession banner costs nothing on any of them |
| **N-6** | Medium | `tools/extraction-bench/Sibyla.Tools.ExtractionBench/Scorer.cs:136, 259, 295` | §5.5's 5 % guard counts a cell as "adjusted" only where `RulingOf(flags)` is attached, and that happens at exactly three sites: the `date_due` cell under `DateDuePrinted == false`, `line_count` under `KeyLinesSummarised`, and the three summarised line sums. `DocumentFlags` has ten score-bearing members. A ruled flag on `PrintsRunningBalance`, `PrintedOpeningBalance`, `PrintedClosingBalance`, `PrintOrder` or `HasTransactionDateColumn` would move scored cells with no `ruling:` note and so fall outside the guard entirely; `PrintsIssuerTaxId` / `PrintsRecipientTaxId` are worse — flipping one *removes* the `fiscal_no` cell, which no share-of-cells guard can see | C-18's point was that a flag moves the score exactly as a correction does and must be inside §5.5's control. The fix covers the three shapes that already happened, not the mechanism, so the next post-sitting ruling on any other flag repeats the finding. The guard is the only quantitative control on moving the goalposts after reading answers |
| **N-7** | Low | commit `21097cc`, "ONE THING THIS COMMIT CARRIES THAT IS NOT MINE" | The *fact* of entanglement is accurate and I verified it: `src/Sibyla.Platform.Domain/Entities/DocumentIntake.cs` gains `Discarded = 10`, `IngestionService.cs` gains `DiscardAsync`, `LockIntakeAsync`, `ListFilteredAsync`, `IntakeListFilter`/`IntakeListPage` and four call-site changes, and `IngestionServiceHoldTests.cs` gains five Discard/list tests — none of it this slice's. The stated **reason** does not hold: "our code calls that session's new `LockIntakeAsync`, whose definition is in their hunks, so the split would not compile." No register-fix hunk calls it. The `LockIntakeAsync` call in `RuleOnDuplicateAsync` is its own 7-line hunk (`@@ -561,7 +601,7 @@`), separate from the register hunk (`@@ -581,13 +621,28 @@`) twenty lines away, and `SeedAsync`'s `sha`/`duplicateOf`/`holdReason` parameters the new register tests use all pre-date the commit | Disclosing entanglement is right and committing whole is defensible; stating a compile dependency that does not exist is not, and it is the kind of claim a later reader takes on trust. Worth noting to the owner separately: merging this branch carries another session's `Discarded` feature — production code and its tests — into `main` through a review that was told the feature is out of scope |
| **N-8** | Low | commit `21097cc` ("Platform extraction suites: 314 passed"), `632dfbb` ("Argus 218, Platform extraction 331") | T: the filter both round-one reviewers and `RED-green.txt` use — `Extraction\|ClaudeCli\|SkillPackage\|CliProcessRunner` — returns **228 passed, 0 failed** at HEAD, which is exactly what `RED-green.txt` records. Argus is 218/218, matching. Neither 314 nor 331 is reproducible from any filter named anywhere; my widest reasonable filter (adding CompanyMatcher, WorkerStartup, QueueWorkerGate, IngestionServiceHold) gave 308 | The evidence file is right and the suites are green, so nothing is hidden — but two commit messages state a headline count that no documented command reproduces, in a record whose value is that its numbers can be re-run |
| **N-9** | Low | `tests/Sibyla.Tests.Platform/IngestionServiceHoldTests.cs`, the `/// <summary>The register entry I26010021 … (AccountPeriodTests' seeding shape).</summary>` comment | The other session's `[Theory] DiscardPreservesEvidenceAndAuditsOnce` was inserted **between** that XML doc comment and the `SeedRegisterEntryAsync` method it documents, so the comment now describes a discard test | Committed noise from the entanglement of N-7; two lines to move. Not the other session's feature, which is out of scope — the placement is what this commit chose to carry |
| **N-10** | Low | `src/Sibyla.Platform.Infrastructure/Ingestion/ExtractionContractV2.Schema.cs:257` (`NormalizeTaxId`'s summary) | The comment says N-3 drops "a label before the number". H: it drops a label before a **colon** (`"NIPC: 500 940 231"` → `PT500940231`) but not one glued to the digits — `NIPC504615947` comes back unchanged, because `hasLetterPrefix` is true for `NI`. `CompanyMatcher.Printed()` does strip that form, so the gate still matches; the canonical `tax_id` does not carry the N-3 shape the comment promises | Small, but §2.7's amendment leans on "the canonical form … carries the N-3 shape whatever the answer wrote", and for the glued-label form it does not. Either extend N-3 or say which form it handles. (Related, and not a finding: C-24 + C-11 together mean a wrong `country` in the answer now produces a prefixed `tax_id` that no longer matches the company — a false negative, so it fails safe into a person's hands) |

---

## What I verified as sound

- **Every round-one reproduction reverses.** I built a harness outside the repository against the committed
  `Sibyla.Platform.Infrastructure.dll` and ran all of them: the wrong-kind mutations (18 shapes, including the six
  the report named and array/scalar cases at every level), 200-characters-plus-a-space, the whitespace-run string,
  `ZZZ`, `vat_rate: 23.123456`, the two-stamp-line answer, `not_printed` on an object of nulls, and N-3's four
  printed forms. Nothing in the repository was written.
- **No regression in the contract.** All 55 committed reject fixtures still land on their declared outcome (49
  refuse, 6 are the declared ACCEPT / ACCEPT-WITH-FINDING rows of `expected.txt`), and all six accept goldens still
  validate **and canonicalise byte-equal** to their `.canonical.json`.
- **The combined report is real and reproducible.** I copied the two run folders to a temp evidence root and ran
  the committed `score` verb: 40 documents, Header 445/447 = 99.55 %, Lines 342/347 = 98.56 %, Movements
  1349/1419 = 95.07 %, 48 traces, 188 file reads, corrections 24, adjusted 24/2213 = 1.08 %, gate PASSED — and the
  `.md` diffs against the committed one on the timestamp line alone. `SameTaxId`, the register lookup, the gate
  tally, the trace join and the 5 % guard are therefore all exercised end to end on real data, not only in unit
  fixtures.
- **The suites.** `Sibyla.Tests.Argus` **218/218**; `Sibyla.Tests.Platform` on the documented extraction filter
  **228/228**; on a wider filter including `CompanyMatcher`, `WorkerStartup`, `QueueWorkerGate` and
  `IngestionServiceHold`, **308/308**. Build lock taken and released on every run.
- **The oracles can fail.** I checked the ones round one called unfailable: `Contains("held", "held for…")` became
  `Assert.Equal("timeouts:2", …)`; `Assert.Equal(hash, PromptHash())` became a hash over the prompt that ran, with
  a divergence seam; `TheGateIsPerKind…` had its enshrining assertion inverted; the new kind-tally oracle asserts
  106/103, 108/104, 115/115, which the old code produced as 100/100. `EveryDenyRuleIsAbsoluteOrAProvenWildcardForm`
  is a general rule rather than a restatement of the one fixed string.
- **The wording of D-EX-7 does not soften.** It names the two reads with times, the keys amended in between, the
  false PASSED, the destroyed evidence, and states the conclusion in the strongest available form — "three honest
  measured sittings and no held-out result", "the reserve set is spent". The owner guide strikes the reserve step
  through rather than deleting it and says why. E-5's account is corrected the same way: the original paragraph is
  left standing above the correction.
- **C-17 is a real gap, honestly described.** §2.6 and owner guide §8.3 state it in plain words and put the
  extension to the owner as a vocabulary decision, and the characterisation test does what its name says: it uses
  the same answer that matches `I26010021` in the neighbouring fixture and differs only in that the intake already
  carries a company.

## What I could not verify, and why

- **§4.5's hostile-document run.** Known and out of scope for this round; the corpus and
  `tools/skill-package/Invoke-HostileDocumentRun.ps1` exist, the evidence file does not. Recorded only under M-6,
  as a gate row that is still not named as outstanding where the owner would see it.
- **The blind reserve run's answers.** Destroyed (E-5); only the 289-cell matrix survives. I recomputed nothing
  from it beyond checking that the rates D-EX-7 publishes are the ones round one derived.
- **Anything needing the production database.** `PreviewParityTests` is red pending three v2 migrations on Main
  (known, not my finding); I did not query `gott_sibyla`. C-10's real-world premise — that `entmst.fiscal_no` is
  stored labelled in the live register — still rests on `Documents.cs`'s "as captured" and on the fixture.
- **The owner's rulings D-EX-5, D-EX-6, D-EX-7.** Attributed and dated in the record; I have no independent
  channel.
- **The published worker tree.** `publish-release.ps1`'s new checks are asserted by `WorkerStartupTests` against
  the golden manifest; I ran no publish and no deployment.
- **The other session's `Discarded` feature** carried whole by `21097cc`. Out of scope by instruction; I verified
  only that the entanglement disclosure describes the right files (N-7).

## Restrictions honoured

No secrets path, key ring, worker CLI home or production database was read. `D:\fileStorage\repos\invoice-skill-build`
was not touched (`git status --porcelain` there is empty). The Claude CLI was never called and nothing was deployed.
The build lock `D:/fileStorage/tmp/apollo-extraction/build.lock` was taken and released around every build, test and
re-score. The only file I wrote inside the repository is this report; the harness, the temp evidence root and the
reproduced reports live under `D:\fileStorage\tmp\closure-harness`.
