Skip to content

Lane A complete (A.19-A.21): record emission, the exit gate, and one decoder - #14

Open
tom-snyder wants to merge 3 commits into
mainfrom
feat/phase3-lane-a-emit
Open

Lane A complete (A.19-A.21): record emission, the exit gate, and one decoder#14
tom-snyder wants to merge 3 commits into
mainfrom
feat/phase3-lane-a-emit

Conversation

@tom-snyder

Copy link
Copy Markdown
Member

Summary

The last three packets of Lane A. A.19 populates the canonical record, A.20 reviewed it and returned FAIL, A.21 is the area exit gate and owns ruling G11.

This completes Lane A — A.1 through A.21.

A.19 got the hard half right and the easy half wrong

It correctly refused to read a clock — the package contains none and a test enforces it. Then it copied advisory.staleness_seconds out of the cache, where the frozen contract defines that field as record-assembly time minus AsOf. Right source, wrong quantity, and the effect was precisely what the packet existed to prevent: a twenty-one-day-old cache reported as one hour old and inside its SLO.

The test standing in for that property couldn't have caught it — it asserted an input value reappeared on the output, so its corpus was its own assertion. Same circularity that let an rpm corpus be truncated at the point of failure one packet earlier.

AssembledAt is now required, with the zero value refused rather than defaulted, because a zero default silently reinstates the bug. The replacement test states a watermark and an assembly instant 21 days later and asserts 1814400 — arithmetic it does itself. The fixture's publisher-lag column is now a decoy constant resembling the contract quantity in no way, so any surviving copy-through fails loudly: four tests go red when the copy is reinstated. And emit_test.go carries a new header section — NO TEST HERE MAY USE AN INPUT FIELD AS ITS OWN EXPECTED VALUE — naming both circularities.

A real hole in the authorization guarantee

The remediable_by_agent allowlist covered collector, detector kind, evidence class, fixed version and parse_degradedbut not ecosystem. A repo-SCA finding with Ecosystem=deb emitted remediableByAgent=true. An OS package isn't agent-fixable no matter which collector noticed it: S7 is about the thing, not about who found it.

The exhaustive enumeration grew from 1,512 points to 12,096, with an assertion that the walked count equals the product so the space can't be silently narrowed later.

And Emission.RemediableByAgent()'s doc claimed a re-clamp the crossing bytes didn't perform. Made true rather than qualified — tested on the serialised bytes of Results that did not come from Emit: hand-built, mutated after emission, and round-tripped through JSON then mutated.

What went right, and it's substantial

No second fingerprint is proved against the frozen conformance corpus and its independently-generated goldens — and that proof caught a real detail: the corpus hashes the Debian locator as apt:openssl:amd64, so spelling the package manager dpkg fails the golden. R.16's harness doing its job across an area boundary, long after it was built.

anvil/trust defaults to untrusted unconditionally, and the one anvil_generated string is trustworthy by construction: the reasoning is joined from a closed vocabulary of eleven constant fragments plus integers Anvil computed, with anything else refused and no record emitted.

G11 closed structurally

internal/ingest/decode is now the single implementation of every wire format Lane A reads, and bootstrap.advisoryRecord / delta.Record are Go type aliases for decode.Record — so no conversion function exists that could drop a field on one side.

A.21 correctly declined to merge dispatch, with a stated reason: the two callers legitimately disagree about what an unreadable document means, since A.8 skips an archive member while A.14 must error because a change was dropped.

The exit gate reports what it could not prove

"7 of 12 links proven end to end in this run. An UNPROVEN link is not a passing link. Read the list above before quoting."

It names each gap: no dpkg/rpm/apk on a Windows host, so the host collector's refusal is proven but its output is not; the accelerator isn't in the chain at all, because pulling a warm-start artifact needs a registry the harness must not contact; no production caller wires the chain together yet.

All 25 area exit criteria are reported individually, several with explicit split verdicts — satisfied at package level, unverifiable-here end to end — rather than aggregated into one green tick.

Testing

gofmt / vet / build   clean
go test -count=1      green across 24 packages
conformance harness   byte-identical across two runs

Every fix verified RED against the pre-fix behaviour first.

Two record-contract gaps, reported not decided

license_manual_note has no slot anywhere in internal/record, though S8's compliance mechanics require the manual-override field carrying the quoted operative sentence. The named trust-validation step has none either. Lane A carries both on its own Emission type meanwhile, and says so — but the frozen contract is where they belong, and that's mine to rule on.

…decoder

The last three packets of Lane A. A.19 populates the canonical record, A.20
reviewed it and returned FAIL, A.21 is the area exit gate and owns ruling G11.

A.19 got the hard half right and the easy half wrong

It correctly refused to read a clock -- the package contains none and a test
enforces it. Then it copied advisory.staleness_seconds out of the cache, where
the frozen contract defines that field as RECORD-ASSEMBLY TIME MINUS AsOf. Right
source, wrong quantity, and the effect was the one the packet existed to
prevent: a twenty-one-day-old cache reported as one hour old and inside its SLO.

The test standing in for that property could not have caught it -- it asserted
that an input value reappeared on the output, so its corpus was its own
assertion. That is the same circularity that let an rpm corpus be truncated at
the point of failure one packet earlier.

Now AssembledAt is a REQUIRED parameter whose zero value is REFUSED rather than
defaulted, because a zero default silently reinstates the bug. The replacement
test states a watermark and an assembly instant twenty-one days later and
asserts 1814400 -- arithmetic it does itself. The fixture's publisher-lag column
is now a decoy constant resembling the contract quantity in no way, so any
surviving copy-through fails loudly across the suite; four tests go red when the
copy is reinstated. emit_test.go carries a new header section, NO TEST HERE MAY
USE AN INPUT FIELD AS ITS OWN EXPECTED VALUE, naming both circularities.

A real hole in the authorization guarantee

The remediable_by_agent allowlist covered collector, detector kind, evidence
class, fixed version and parse_degraded -- but not ECOSYSTEM. A repo-SCA finding
with Ecosystem=deb emitted remediableByAgent=true. An OS package is not
agent-fixable no matter which collector noticed it: S7 is about the thing, not
about who found it. The exhaustive enumeration grew from 1512 points to 12096,
with an assertion that the walked count equals the product so the space cannot
be silently narrowed later.

And Emission.RemediableByAgent()'s doc claimed a re-clamp the crossing bytes did
not perform. Made true rather than qualified: the clamp now travels on a copy
through MarshalJSON and Results(), tested on the SERIALISED bytes of Results
that did not come from Emit -- hand-built, mutated after emission, and
round-tripped through JSON then mutated.

What went right, and it is substantial

No second fingerprint is proved against the FROZEN conformance corpus and its
independently-generated goldens -- and that proof caught a real detail: the
corpus hashes the Debian locator as apt:openssl:amd64, so spelling the package
manager "dpkg" fails the golden. R.16's harness doing its job across an area
boundary, months after it was built.

anvil/trust defaults to untrusted unconditionally, and the single
anvil_generated string is trustworthy BY CONSTRUCTION rather than by assertion:
the reasoning is joined from a closed vocabulary of eleven constant fragments
plus integers Anvil computed, and any other part is refused with no record
emitted.

G11 closed, and closed structurally

internal/ingest/decode is now the single implementation of every wire format
Lane A reads, and bootstrap.advisoryRecord and delta.Record are Go type ALIASES
for decode.Record -- so no conversion function exists that could drop a field on
one side. A.21 correctly declined to merge DISPATCH, with a stated reason: the
two callers legitimately disagree about what an unreadable document means, since
A.8 skips an archive member while A.14 must error because a change was dropped.

The exit gate reports what it could not prove

The harness ends with "7 of 12 links proven end to end in this run" and "An
UNPROVEN link is not a passing link. Read the list above before quoting" -- and
names each gap: no dpkg/rpm/apk on a Windows host, so the host collector's
REFUSAL is proven but its output is not; the accelerator is not in the chain at
all because pulling a warm-start artifact needs a registry the harness must not
contact; no production caller wires the chain together yet.

All 25 area exit criteria are reported individually, several with explicit
split verdicts -- satisfied at package level, unverifiable-here end to end --
rather than aggregated into one green tick. That was the instruction and it is
the right shape: the report is what gets believed.

Evidence: gofmt, vet, build clean; go test -count=1 ./... green across 24
packages; conformance harness byte-identical across two runs. Every fix verified
RED against the pre-fix behaviour first.

Two record-contract gaps reported, not decided, both mine to rule on:
license_manual_note has no slot anywhere in internal/record though S8's
compliance mechanics require it, and the named trust-validation step has none
either. Lane A carries both on its own Emission type meanwhile, and says so.
refusals that arrive by the purl route

CI went red on the Lane A exit gate, and it was red because the gate works. On
ubuntu-latest dpkg exists, so host.Collect ran for real -- 1204 packages across
three families -- and the harness then FAILED ITSELF: "a supported package
manager is present on this host, so the fixture inventory is no longer the best
available evidence."

That is a tripwire firing when BETTER EVIDENCE BECOMES AVAILABLE, the same
design as the sanitiser's importer tripwire. It is honoured here by wiring, not
quietened.

The fixture was two things wearing one name

It was evidence for the host-inventory link AND the only host-shaped packages
the synthetic corpus can decide. The tripwire correctly rejected the first;
deleting the file outright would have destroyed the second, because no real
machine anywhere has openssl 3.1.3-r0 with CVE-2026-1001 against it, and the
comparator and emission links would have gone vacuous.

So it is split: host.Collect supplies the host-inventory link, and
corpus-probe-packages.json keeps the four synthetic rows under a name that says
what they are, with a header stating it is NOT a recording of the collector.

The branch is host.Collect's own error return, not a build tag or an env var,
and the ledger line says WHICH inventory a run used -- because a run that proved
the collector and a run that proved only a fixture are different claims, and
that distinction is the entire point of the link. New tripwires point the other
way too: a present package manager that enumerates ZERO packages fails the run
(the silent-clean reading exit criterion 20 forbids), and so does collecting
packages and then not submitting them all.

Determinism was strengthened rather than preserved. chainOutput now compares the
host source label and the collected rows, and the reason is not cosmetic: real
packages match nothing in a synthetic corpus, so two DIFFERENT collections would
have left every previously-compared field identical and the determinism claim
would have been true about a chain that never saw the data. What CI now asserts
is that host.Collect read a live package database twice and returned the same
bytes.

Measured rather than assumed: the probe fixture was temporarily grown to 1204
rows with realistic Ubuntu version strings -- epochs, tildes, plus-forms, "++"
in names, empty arch -- and the whole chain ran TWICE in 0.12s with zero deltas
and all 1204 identified, then the file was restored.

The finding the harness reported instead of swallowing

It logged, as a FINDING against internal/match rather than a failure of its own:
a package refused for an unimplemented scheme did not appear in
CoverageReport.EcosystemsRefused, because the refusal arrived by the purl route
and only the ecosystem route fed that list. "The count is visible; the thing to
implement next is not."

EcosystemsRefused exists so an operator can see which ecosystems Anvil cannot
yet handle. Dropping half its inputs made it quietly wrong in the direction that
looks better. Both routes feed it now, through one function that decides what
the identity token is.

PurlType is a separate field rather than a reuse of Ecosystem, and that choice
is load-bearing: {Ecosystem: "deb", Purl: "pkg:npm/..."} is refused on the purl
type, so writing the type into Ecosystem would put "deb" -- an IMPLEMENTED
scheme -- on the operator's implement-next list. That is worse than the empty
list it replaced. A negative control asserts an implemented ecosystem never
enters the list.

The harness's logged finding is now an assertion, and it cannot be satisfied by
the ecosystem route alone, because this corpus's refusal arrives by the purl one.

Evidence: gofmt, vet, build clean; go test -count=1 ./... green across 24
packages here; no t.Skip added. On this host the ledger still ends "7 of 12
links proven end to end" with host inventory UNPROVEN naming the corpus probe --
which is correct, because Windows has no package manager. CI is where the real
path exists and CI is the proof; it already caught this once.
The exit gate ends with a ledger naming which of its twelve links it PROVED and
which it could not, plus "An UNPROVEN link is not a passing link. Read the list
above before quoting." That ledger is the deliverable of A.21.

Go only shows test log output on failure, so on a green CI run it was invisible
-- a reader saw "ok" and had no way to know that seven of twelve links were
proven and five were not. A gate whose verdict nobody can read is a gate that
reports pass.

This step prints it on the machine where it says the most: ubuntu-latest has
dpkg, so host.Collect runs for real and the host-inventory link is PROVEN there,
where on the Windows dev host it is UNPROVEN and names the corpus probe instead.
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