From c4cb12414c7ea0284f3548bfcbb2f5b33a18b8b9 Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:05:52 -0400 Subject: [PATCH 1/6] Shims: delete internal/buildpin The package existed only to keep modernc.org/sqlite in go.mod until the store imported it. The store, the cache and the host collector all import the driver now, and go mod tidy stays a no-op without the pin. --- internal/buildpin/pin.go | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 internal/buildpin/pin.go diff --git a/internal/buildpin/pin.go b/internal/buildpin/pin.go deleted file mode 100644 index a971b2b..0000000 --- a/internal/buildpin/pin.go +++ /dev/null @@ -1,25 +0,0 @@ -// Package buildpin holds Phase 0 dependency pins alive in the module graph -// until the packages that actually use them exist. -// -// The spine's Go control-plane decision mandates modernc.org/sqlite, and -// plan/design/spine-language.md's source table pins it at v1.56.0. plan/design/first-plan.md -// Phase 0 requires that dependency be added at bootstrap. But nothing imports -// it yet -- neither the store schema nor the migration runner's FTS5 startup guard existed -- -// and `go mod tidy` removes a module that no package in the module imports. -// Without this file, CI's tidy check would silently delete the pin the -// bootstrap was supposed to establish. -// -// DELETE THIS PACKAGE when internal/store lands. It has no runtime -// purpose and must never be imported by anything other than itself. -package buildpin - -import ( - // modernc.org/sqlite: the cgo-free SQLite driver the spine's Go - // control-plane decision mandates. Do NOT substitute mattn/go-sqlite3 -- it - // needs cgo and a C toolchain, which breaks the single static binary and - // the cross-compilation matrix. Verified at bootstrap against this exact - // version: SQLite 3.53.3, FTS5 virtual tables and MATCH queries both work. - // Licence read as a file body per the spine's licence section: - // BSD-3-Clause. - _ "modernc.org/sqlite" -) From 79d78c78727facdb6f13503a82fea60391cdc743 Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:05:52 -0400 Subject: [PATCH 2/6] Ignore the owner's branding designs assets/branding/ holds the logo designs. They have never been committed and stay local, like plan/ and research/. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9c9557d..a0c5f40 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ research/ plan/ tools/ +# The owner's logo and branding designs -- kept local, never distributed. +assets/branding/ + # Python __pycache__/ *.py[cod] From 9a6f01a0b5b4ef4f2bef8f1f368025050ecc39d3 Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:05:52 -0400 Subject: [PATCH 3/6] Review notes: name the reviewer's model family, not its vendor Four review files disclose that the critic came from the same model family as the code it reviewed. The disclosure stays; the vendor name goes. --- internal/mirror/accelerator/REVIEW-accelerator.md | 2 +- internal/record/REVIEW-contract-and-fingerprint.md | 2 +- internal/record/REVIEW-queue-and-read-path.md | 2 +- internal/record/lanea/REVIEW-emission.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/mirror/accelerator/REVIEW-accelerator.md b/internal/mirror/accelerator/REVIEW-accelerator.md index c642990..68813b5 100644 --- a/internal/mirror/accelerator/REVIEW-accelerator.md +++ b/internal/mirror/accelerator/REVIEW-accelerator.md @@ -4,7 +4,7 @@ **This was a SAME-FAMILY critic.** The packet routes this review to an OpenCode `openai/gpt-5.5-fast` route; that route is withdrawn (plan/design/routing.md OWNER -DECISION, 2026-08-07). This review was performed by a Claude subagent — the same +DECISION, 2026-08-07). This review was performed by an automated review agent from the same model family that produced the accelerator. Correlated blind spots are therefore *not* excluded, and this verdict must never be recorded as "cross-family critic: PASS". Compensation applied: every claim was checked against the file, the suite was diff --git a/internal/record/REVIEW-contract-and-fingerprint.md b/internal/record/REVIEW-contract-and-fingerprint.md index b35d52e..597f026 100644 --- a/internal/record/REVIEW-contract-and-fingerprint.md +++ b/internal/record/REVIEW-contract-and-fingerprint.md @@ -12,7 +12,7 @@ the spine's corrected-requirements, record, safety and Go control-plane sections ## 0. WHICH GUARANTEE THIS DOCUMENT ACTUALLY PROVIDES — READ THIS FIRST -**This was a SAME-FAMILY critic.** The critic and both implementers (the record contract, the fingerprint) are Anthropic models. +**This was a SAME-FAMILY critic.** The critic and both implementers (the record contract, the fingerprint) are models from the same family. `plan/design/routing.md` originally required a **different model family** here precisely so that a shared blind spot could not survive review; the owner withdrew external routes on 2026-08-07 because running them means copying `plan/` — deliberately private — to a third-party provider. See the OWNER DECISION diff --git a/internal/record/REVIEW-queue-and-read-path.md b/internal/record/REVIEW-queue-and-read-path.md index b4da0c9..68a8b83 100644 --- a/internal/record/REVIEW-queue-and-read-path.md +++ b/internal/record/REVIEW-queue-and-read-path.md @@ -19,7 +19,7 @@ block dated 2026-08-07 at the top of `routing.md` withdrew all OpenCode/OpenRout because running one copies private repository contents to a third-party provider; this review's packet still says `OpenCode route (openai/gpt-5.5)` and that route is dead. -The replacement is an Anthropic subagent given an explicit instruction to **refute rather than +The replacement is a same-family review agent given an explicit instruction to **refute rather than assess**, with named failure modes to hunt. That narrows the shared-blind-spot gap; it does not close it. **Do not read this file as "cross-family critic: PASS."** It is a same-family critic, and a later reader deciding whether the cross-family gate has been met should treat it as diff --git a/internal/record/lanea/REVIEW-emission.md b/internal/record/lanea/REVIEW-emission.md index 0ff1f3f..df510b8 100644 --- a/internal/record/lanea/REVIEW-emission.md +++ b/internal/record/lanea/REVIEW-emission.md @@ -3,8 +3,8 @@ **Verdict: FAIL.** One blocker, two majors, three minors. **This was a SAME-FAMILY critic.** The packet routes this review to OpenCode `openai/gpt-5.5`; that route is -withdrawn per the OWNER DECISION block atop `plan/design/routing.md`. This review was performed by a Claude -subagent — the same family that wrote record emission — and therefore carries none of the independence the +withdrawn per the OWNER DECISION block atop `plan/design/routing.md`. This review was performed by an automated +review agent from the same model family that wrote record emission, and therefore carries none of the independence the cross-family guard was written to buy. Do **not** record this as "cross-family critic: PASS". To compensate, every claim below is backed by a probe I wrote and ran against the committed code, not by reading record emission's own tests. Nine probes were written; six passed, three failed. The probes were deleted From 1a83393d02650e828c96c2b6a2df52de5ffde8e8 Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:15:00 -0400 Subject: [PATCH 4/6] Package docs: one doc.go per package, and a guard that keeps it so go doc concatenates every comment attached to a package clause in file order, so several packages were introduced by whichever file sorted first: the authorization kernel by one of its phases, the attack-surface inventory by its authentication helper, the comparator by Alpine's version ordering. Each package's own summary paragraph moves, verbatim, into doc.go; every other file comment stays where it was, separated from the package line by a blank line, and references to "the package comment" now point at the file that holds the text. test/hygiene's TestEveryPackageSummaryIsItsOwn requires a doc.go per package whose summary starts "Package " ("Command " for a main package) and refuses a comment attached to any other file's package clause; TestThePackageDocGuardFails is its negative control, and the guard was seen failing on a reattached comment before it was trusted. --- cmd/anvil-dast/doc.go | 2 + cmd/anvil-dast/main.go | 5 +- cmd/anvil-dast/pin-templates.go | 1 + cmd/anvil-host-collector/doc.go | 3 + cmd/anvil-host-collector/main.go | 8 +- cmd/anvil/doc.go | 2 + cmd/anvil/main.go | 3 +- internal/collector/host/collect.go | 10 +- internal/collector/host/doc.go | 3 + internal/collector/host/rpm.go | 2 +- internal/collector/repo/doc.go | 4 + internal/collector/repo/trivy.go | 5 +- internal/dast/authz/doc.go | 3 + internal/dast/authz/phase1_run.go | 1 + internal/dast/authz/phase2_admission.go | 1 + internal/dast/authz/phase3_enforcement.go | 1 + internal/dast/authz/phase4_disclosure.go | 1 + internal/dast/authz/types.go | 4 +- internal/dast/containment/doc.go | 5 + internal/dast/containment/netns.go | 7 +- internal/dast/containment/provision.go | 4 +- internal/dast/containment/reset.go | 1 + internal/dast/engines/doc.go | 4 + internal/dast/engines/nuclei.go | 4 +- internal/dast/engines/zap.go | 1 + internal/dast/inventory/auth_helper.go | 1 + internal/dast/inventory/doc.go | 4 + internal/dast/inventory/tier0_runtime.go | 7 +- internal/dast/inventory/tier1_repospec.go | 1 + internal/dast/inventory/tier2_confirm.go | 1 + internal/dast/inventory/tier2_go_extract.go | 1 + .../dast/inventory/tier2_other_extract.go | 1 + internal/dast/inventory/tier3_crawl.go | 1 + internal/dast/record/confirm_gate.go | 1 + internal/dast/record/coverage.go | 14 +- internal/dast/record/doc.go | 7 + internal/dast/target/doc.go | 4 + internal/dast/target/manifest.go | 5 +- internal/handoff/doc.go | 3 + internal/handoff/state_machine.go | 4 +- internal/ingest/bootstrap/bootstrap.go | 4 +- internal/ingest/bootstrap/doc.go | 3 + internal/ingest/cache/doc.go | 4 + internal/ingest/cache/schema.go | 5 +- internal/ingest/config/doc.go | 2 + internal/ingest/config/feeds.go | 3 +- internal/ingest/decode/decode.go | 5 +- internal/ingest/decode/doc.go | 4 + internal/ingest/delta/delta.go | 11 +- internal/ingest/delta/doc.go | 2 + internal/ingest/delta/upsert.go | 3 +- internal/ingest/drift/doc.go | 3 + internal/ingest/drift/drift.go | 8 +- internal/ingest/drift/tombstone.go | 1 + internal/ingest/invisible/doc.go | 2 + internal/ingest/invisible/invisible.go | 3 +- internal/ingest/license/doc.go | 3 + internal/ingest/license/tiers.go | 6 +- internal/ingest/poller/doc.go | 2 + internal/ingest/poller/poller.go | 5 +- internal/ingest/reconcile/baseline.go | 3 +- internal/ingest/reconcile/doc.go | 2 + internal/ingest/sanitize/doc.go | 3 + internal/ingest/sanitize/sanitize.go | 14 +- internal/match/apk_compare.go | 1 + internal/match/comparator.go | 13 +- internal/match/comparator_test.go | 6 +- internal/match/doc.go | 4 + internal/match/dpkg_compare.go | 1 + internal/match/purl.go | 3 +- internal/match/rpm_compare.go | 1 + internal/mirror/accelerator/doc.go | 3 + internal/mirror/accelerator/trivydb.go | 4 +- internal/policy/doc.go | 9 + internal/policy/locate.go | 10 +- internal/record/contract.go | 6 +- internal/record/correlation.go | 3 +- internal/record/doc.go | 5 + internal/record/lanea/doc.go | 3 + internal/record/lanea/emit.go | 4 +- internal/record/readpath.go | 1 + internal/record/sealing.go | 2 +- internal/record/taskcard.go | 1 + internal/scanctl/deadlines.go | 16 +- internal/scanctl/doc.go | 8 + internal/scanctl/handoff.go | 2 +- internal/scanctl/statemachine.go | 2 +- internal/store/ddl.go | 3 +- internal/store/doc.go | 2 + test/hygiene/package_docs_test.go | 170 ++++++++++++++++++ 90 files changed, 375 insertions(+), 149 deletions(-) create mode 100644 cmd/anvil-dast/doc.go create mode 100644 cmd/anvil-host-collector/doc.go create mode 100644 cmd/anvil/doc.go create mode 100644 internal/collector/host/doc.go create mode 100644 internal/collector/repo/doc.go create mode 100644 internal/dast/authz/doc.go create mode 100644 internal/dast/containment/doc.go create mode 100644 internal/dast/engines/doc.go create mode 100644 internal/dast/inventory/doc.go create mode 100644 internal/dast/record/doc.go create mode 100644 internal/dast/target/doc.go create mode 100644 internal/handoff/doc.go create mode 100644 internal/ingest/bootstrap/doc.go create mode 100644 internal/ingest/cache/doc.go create mode 100644 internal/ingest/config/doc.go create mode 100644 internal/ingest/decode/doc.go create mode 100644 internal/ingest/delta/doc.go create mode 100644 internal/ingest/drift/doc.go create mode 100644 internal/ingest/invisible/doc.go create mode 100644 internal/ingest/license/doc.go create mode 100644 internal/ingest/poller/doc.go create mode 100644 internal/ingest/reconcile/doc.go create mode 100644 internal/ingest/sanitize/doc.go create mode 100644 internal/match/doc.go create mode 100644 internal/mirror/accelerator/doc.go create mode 100644 internal/policy/doc.go create mode 100644 internal/record/doc.go create mode 100644 internal/record/lanea/doc.go create mode 100644 internal/scanctl/doc.go create mode 100644 internal/store/doc.go create mode 100644 test/hygiene/package_docs_test.go diff --git a/cmd/anvil-dast/doc.go b/cmd/anvil-dast/doc.go new file mode 100644 index 0000000..c177046 --- /dev/null +++ b/cmd/anvil-dast/doc.go @@ -0,0 +1,2 @@ +// Command anvil-dast is the Anvil dynamic-analysis (DAST) binary. +package main diff --git a/cmd/anvil-dast/main.go b/cmd/anvil-dast/main.go index dc2fc7a..1f6e654 100644 --- a/cmd/anvil-dast/main.go +++ b/cmd/anvil-dast/main.go @@ -1,6 +1,4 @@ -// Command anvil-dast is the Anvil dynamic-analysis (DAST) binary. -// -// It ships as a SEPARATE distribution artifact from the core anvil binary, +// anvil-dast ships as a SEPARATE distribution artifact from the core anvil binary, // requires separate installation, and refuses to probe anything without an // explicit attestation. See the two-artifact split for why this is a // separate artifact rather than a configuration flag: a boolean inside a @@ -12,6 +10,7 @@ // // Bootstrap placeholder. The general entrypoint arrives with the live dynamic // tier (plan node live); every request it makes routes through internal/dast/authz. + package main import ( diff --git a/cmd/anvil-dast/pin-templates.go b/cmd/anvil-dast/pin-templates.go index ab31f3d..c4a8a46 100644 --- a/cmd/anvil-dast/pin-templates.go +++ b/cmd/anvil-dast/pin-templates.go @@ -46,6 +46,7 @@ // `code:` template is refused at load time. This job calls it on BOTH trees // and diffs the results, which means the definition of "dangerous" lives in // exactly one place and this file cannot drift from it. + package main import ( diff --git a/cmd/anvil-host-collector/doc.go b/cmd/anvil-host-collector/doc.go new file mode 100644 index 0000000..454be27 --- /dev/null +++ b/cmd/anvil-host-collector/doc.go @@ -0,0 +1,3 @@ +// Command anvil-host-collector runs Anvil's read-only host package collector +// once, writes the resulting inventory to stdout as JSON, and exits. +package main diff --git a/cmd/anvil-host-collector/main.go b/cmd/anvil-host-collector/main.go index be77cc7..f3aa310 100644 --- a/cmd/anvil-host-collector/main.go +++ b/cmd/anvil-host-collector/main.go @@ -1,6 +1,3 @@ -// Command anvil-host-collector runs Anvil's read-only host package collector -// once, writes the resulting inventory to stdout as JSON, and exits. -// // # Why this main package exists // // deploy/systemd/anvil-host-collector.service has always declared @@ -55,7 +52,7 @@ // // RUN IT UNDER THE SHIPPED UNIT, OR REPRODUCE WHAT THE UNIT DOES. Running this // binary as root outside the unit re-opens the rpmdb write side effect -// documented in internal/collector/host's package comment: on a +// documented in the comment at the top of internal/collector/host/collect.go: on a // Berkeley-DB-backed rpmdb, `rpm -qa` creates /var/lib/rpm/__db.001..003 when // the caller can write that directory. The binary cannot prevent that — no rpm // flag suppresses it, and branching on the effective uid is forbidden — so the @@ -72,6 +69,7 @@ // is that a run never reports a silent "clean", and "we could not look" has // to be distinguishable from "there is nothing here" by a caller reading // the exit status alone. + package main import ( @@ -85,7 +83,7 @@ import ( "github.com/Susquehanna-Syntax/Anvil/internal/collector/host" ) -// Exit statuses, documented in the package comment above. +// Exit statuses, documented in the comment at the top of this file. const ( exitCollected = 0 exitFailed = 1 diff --git a/cmd/anvil/doc.go b/cmd/anvil/doc.go new file mode 100644 index 0000000..707c955 --- /dev/null +++ b/cmd/anvil/doc.go @@ -0,0 +1,2 @@ +// Command anvil is the Anvil core binary. +package main diff --git a/cmd/anvil/main.go b/cmd/anvil/main.go index 7a27616..50069d8 100644 --- a/cmd/anvil/main.go +++ b/cmd/anvil/main.go @@ -1,5 +1,3 @@ -// Command anvil is the Anvil core binary. -// // Anvil finds vulnerabilities in Linux servers and code repositories and // proposes fixes. This binary is the core artifact: Lane A (deterministic // SBOM/host package matching), Lane B (first-party source detection), the @@ -15,6 +13,7 @@ // Bootstrap placeholder. The real entrypoint — the anvil scan and // anvil daemon --loop subcommands wired to internal/scanctl and // internal/queue — is plan node cli (plan/anvil.html). + package main import ( diff --git a/internal/collector/host/collect.go b/internal/collector/host/collect.go index 274aa40..a727586 100644 --- a/internal/collector/host/collect.go +++ b/internal/collector/host/collect.go @@ -1,7 +1,4 @@ -// Package host is Anvil's READ-ONLY host package collector (plan node -// hostcollector; design in plan/design/lane-a.md). -// -// It enumerates the packages a Linux host has installed by asking the native +// The collector enumerates the packages a Linux host has installed by asking the native // package database — `dpkg-query -W`, `rpm -qa`, `apk list --installed` / // `apk info -v` — reads /etc/os-release, and returns an Inventory. It then // exits. That is the whole product. @@ -164,6 +161,7 @@ // carrying a SQL driver. The `finding`-column vocabulary it must agree with is // asserted against cache's DDL by a TEST-ONLY import instead, so drift is // caught without the dependency. + package host import ( @@ -201,7 +199,7 @@ import ( // and must not try. const RemediableByAgent = false -// ReadOnly records, in the emitted provenance, the claim the package comment's +// ReadOnly records, in the emitted provenance, the claim the file comment's // "WHAT READ-ONLY CLAIMS HERE" section makes and no more: NO MUTATING PACKAGE // MANAGER COMMAND LINE IS EXPRESSIBLE BY THIS COLLECTOR. It does not claim // that a run changes no byte on the host — see RPMDB WRITE SIDE EFFECT in that @@ -219,7 +217,7 @@ const InventorySchemaVersion = 1 // Collector is the `finding.collector` value for rows derived from this // collector. It duplicates internal/ingest/cache's CollectorHost by VALUE -// rather than by import — see the package comment on why the SQLite-linking +// rather than by import — see the comment at the top of this file on why the SQLite-linking // cache package is not a dependency of a production-server collector — and // TestCollectorValueMatchesTheCacheSchema imports cache from the test binary // to prove the two have not drifted. diff --git a/internal/collector/host/doc.go b/internal/collector/host/doc.go new file mode 100644 index 0000000..2b9341b --- /dev/null +++ b/internal/collector/host/doc.go @@ -0,0 +1,3 @@ +// Package host is Anvil's READ-ONLY host package collector (plan node +// hostcollector; design in plan/design/lane-a.md). +package host diff --git a/internal/collector/host/rpm.go b/internal/collector/host/rpm.go index b3de0fb..ae813b2 100644 --- a/internal/collector/host/rpm.go +++ b/internal/collector/host/rpm.go @@ -20,7 +20,7 @@ import "strings" // database creates and updates /var/lib/rpm/__db.001..__db.003 when the caller // can write that directory, which in practice means when it runs as root. rpm // offers no flag that suppresses this, and this package will not branch on the -// effective uid to avoid it. collect.go's package comment carries the full +// effective uid to avoid it. the comment at the top of collect.go carries the full // statement, its limits, and the deployment-layer mitigation that is the only // place the behaviour can actually be prevented. // diff --git a/internal/collector/repo/doc.go b/internal/collector/repo/doc.go new file mode 100644 index 0000000..f2ab49b --- /dev/null +++ b/internal/collector/repo/doc.go @@ -0,0 +1,4 @@ +// Package repo is Lane A's repository SCA collector (plan node repocollector; +// design in plan/design/lane-a.md): it runs Trivy over a repository that is already on disk and +// turns Trivy's JSON report into Lane A findings. +package repo diff --git a/internal/collector/repo/trivy.go b/internal/collector/repo/trivy.go index e1dc45f..b7b9c84 100644 --- a/internal/collector/repo/trivy.go +++ b/internal/collector/repo/trivy.go @@ -1,7 +1,3 @@ -// Package repo is Lane A's repository SCA collector (plan node repocollector; -// design in plan/design/lane-a.md): it runs Trivy over a repository that is already on disk and -// turns Trivy's JSON report into Lane A findings. -// // # No model, ever // // The spine's corrected-requirements table: Lane A is "deterministic, zero inference — SBOM/host @@ -48,6 +44,7 @@ // attaches when a release artifact bakes the binary in — that is the container image's // call, not this package's, and this comment exists so the container image finds the fact // rather than re-deriving it. + package repo import ( diff --git a/internal/dast/authz/doc.go b/internal/dast/authz/doc.go new file mode 100644 index 0000000..033c26e --- /dev/null +++ b/internal/dast/authz/doc.go @@ -0,0 +1,3 @@ +// Package authz is Anvil's authorization kernel: the only thing in the system +// that may turn a proposed network request into a permitted one. +package authz diff --git a/internal/dast/authz/phase1_run.go b/internal/dast/authz/phase1_run.go index 5acdfdb..6a5e9a5 100644 --- a/internal/dast/authz/phase1_run.go +++ b/internal/dast/authz/phase1_run.go @@ -111,6 +111,7 @@ // operator genuinely needs to see what was rejected, the value goes into // Evidence through redactUntrusted, which keeps an allowlisted charset and // bounds the length. + package authz import ( diff --git a/internal/dast/authz/phase2_admission.go b/internal/dast/authz/phase2_admission.go index b48ca99..d592d8e 100644 --- a/internal/dast/authz/phase2_admission.go +++ b/internal/dast/authz/phase2_admission.go @@ -89,6 +89,7 @@ // goes through redactUntrusted (phase1_run.go), which keeps an allowlisted // charset and bounds the length, and every parsed value is separately bounded // and charset-restricted at parse time. + package authz import ( diff --git a/internal/dast/authz/phase3_enforcement.go b/internal/dast/authz/phase3_enforcement.go index ffd4b4b..6e4b697 100644 --- a/internal/dast/authz/phase3_enforcement.go +++ b/internal/dast/authz/phase3_enforcement.go @@ -71,6 +71,7 @@ // CapOverrides / ThresholdOverrides, which carry plain numbers, and the only // operation on them is Lower. A config value therefore cannot BE a floor; it // can only be compared against one. + package authz import ( diff --git a/internal/dast/authz/phase4_disclosure.go b/internal/dast/authz/phase4_disclosure.go index 0b9617d..b801fb5 100644 --- a/internal/dast/authz/phase4_disclosure.go +++ b/internal/dast/authz/phase4_disclosure.go @@ -139,6 +139,7 @@ // more visible act than passing a different time.Time to the next call, and it // is as far as a kernel with no trusted clock can go. Recorded in // internal/SKIPPED-CONTROLS.md as G18-2. + package authz import ( diff --git a/internal/dast/authz/types.go b/internal/dast/authz/types.go index 900f8e5..a1d9471 100644 --- a/internal/dast/authz/types.go +++ b/internal/dast/authz/types.go @@ -1,6 +1,3 @@ -// Package authz is Anvil's authorization kernel: the only thing in the system -// that may turn a proposed network request into a permitted one. -// // The decision flow — Decide, Adjudicate, Authorization, RequireAuthorization — // is documented at the top of kernel.go. This file holds the VOCABULARY: the // types every one of the 21 gates in plan/design/dynamic-tier.md's Authorization Gate @@ -28,6 +25,7 @@ // No net.Dial, no http.Client, no socket construction of any kind — the kernel core's // forbidden actions, and gate 3's whole point. This file imports stdlib only, // and gate 2 (phase0_build.go) is the machine check that keeps it that way. + package authz import ( diff --git a/internal/dast/containment/doc.go b/internal/dast/containment/doc.go new file mode 100644 index 0000000..47154a3 --- /dev/null +++ b/internal/dast/containment/doc.go @@ -0,0 +1,5 @@ +// Package containment holds the DAST half's network containment: a dedicated +// Linux network namespace whose egress is default-deny under nftables, and an +// assertion probe that re-proves the containment ON EVERY RUN. It also provisions, +// holds and resets the ephemeral target a DAST scan runs against. +package containment diff --git a/internal/dast/containment/netns.go b/internal/dast/containment/netns.go index a7b86db..0d722a0 100644 --- a/internal/dast/containment/netns.go +++ b/internal/dast/containment/netns.go @@ -1,7 +1,3 @@ -// Package containment holds the DAST half's network containment: a dedicated -// Linux network namespace whose egress is default-deny under nftables, and an -// assertion probe that re-proves the containment ON EVERY RUN. -// // # The design spike the plan asked for // // plan/design/dynamic-tier.md's Open Questions (the network containment entry) leaves the assertion @@ -96,6 +92,7 @@ // declaration for the full reasoning; the short version is that the gate was // right, the first draft of this file was wrong, and the seam moved rather // than the gate. + package containment import ( @@ -1092,7 +1089,7 @@ const CanarySubcommand = "__anvil-dast-netns-canary" // DefaultCanaryDialTimeout bounds one connect attempt. It is the contract a // ConnectProbe implementation honours. // -// It is short on purpose, and the asymmetry in the package doc is why that is +// It is short on purpose, and the asymmetry in the comment at the top of this file is why that is // safe rather than sloppy: a REACHABLE metadata endpoint is link-local and // answers in well under a millisecond, so a timeout in this range can only // convert a slow "blocked" into a "blocked" -- it can never convert a diff --git a/internal/dast/containment/provision.go b/internal/dast/containment/provision.go index 969e473..89309b0 100644 --- a/internal/dast/containment/provision.go +++ b/internal/dast/containment/provision.go @@ -1,6 +1,3 @@ -// Package containment provisions and holds the ephemeral target a DAST scan -// runs against. -// // This file is target provisioning: given the DECLARED manifest that // internal/dast/target already parsed, bring up the operator's Compose project // under gVisor `runsc`, wait on the declared healthcheck with a hard timeout, @@ -87,6 +84,7 @@ // asserts the runner was never called. There is no flag, option or environment // variable in this package that relaxes any assertion in it; widening one is a // visible edit to this file. + package containment import ( diff --git a/internal/dast/containment/reset.go b/internal/dast/containment/reset.go index c1b12dc..3a19d30 100644 --- a/internal/dast/containment/reset.go +++ b/internal/dast/containment/reset.go @@ -82,6 +82,7 @@ // record.HalfStatusFailed. Its DastStatus() derives through // record.DeriveDastStatus, so this file writes no record enum literal and the // derived status can never be DastStatusCompletedClean. + package containment import ( diff --git a/internal/dast/engines/doc.go b/internal/dast/engines/doc.go new file mode 100644 index 0000000..ecfd8e5 --- /dev/null +++ b/internal/dast/engines/doc.go @@ -0,0 +1,4 @@ +// Package engines holds Anvil's probe-engine drivers: one for Nuclei (nuclei.go) +// and one for ZAP (zap.go). A target reaches either engine only by way of an +// authz.Authorization. +package engines diff --git a/internal/dast/engines/nuclei.go b/internal/dast/engines/nuclei.go index c56f9b0..28c9330 100644 --- a/internal/dast/engines/nuclei.go +++ b/internal/dast/engines/nuclei.go @@ -1,5 +1,4 @@ -// Package engines holds Anvil's probe-engine drivers. This file is the nuclei driver: the -// Nuclei driver. +// This file is the Nuclei driver. // // # What this driver is for, stated as the failure it prevents // @@ -91,6 +90,7 @@ // directory cannot reach a host the kernel did not admit, because it does not // get to name a host at all. It can, at most, cause a request the kernel then // gates like every other request. + package engines import ( diff --git a/internal/dast/engines/zap.go b/internal/dast/engines/zap.go index bcc5195..7191aec 100644 --- a/internal/dast/engines/zap.go +++ b/internal/dast/engines/zap.go @@ -171,6 +171,7 @@ // here would be fabricated — and a fabricated figure is worse than the // acknowledged gap, because tier-M sizing would then be documented against it. // internal/SKIPPED-CONTROLS.md U5 records what measuring it takes. + package engines import ( diff --git a/internal/dast/inventory/auth_helper.go b/internal/dast/inventory/auth_helper.go index 3c8282a..eb088fb 100644 --- a/internal/dast/inventory/auth_helper.go +++ b/internal/dast/inventory/auth_helper.go @@ -214,6 +214,7 @@ // are stated on that interface and enforced nowhere here — the same shape // ClientSpider and engines.ZapRunner have, and the same integration lane owes // the proof. + package inventory import ( diff --git a/internal/dast/inventory/doc.go b/internal/dast/inventory/doc.go new file mode 100644 index 0000000..65af06d --- /dev/null +++ b/internal/dast/inventory/doc.go @@ -0,0 +1,4 @@ +// Package inventory builds Anvil's attack-surface inventory: the list of +// endpoints the DAST tier believes a target exposes, and — for every one of +// them — WHERE THAT BELIEF CAME FROM and WHETHER ANVIL CONFIRMED IT. +package inventory diff --git a/internal/dast/inventory/tier0_runtime.go b/internal/dast/inventory/tier0_runtime.go index 0f52cbe..e822a9f 100644 --- a/internal/dast/inventory/tier0_runtime.go +++ b/internal/dast/inventory/tier0_runtime.go @@ -1,7 +1,3 @@ -// Package inventory builds Anvil's attack-surface inventory: the list of -// endpoints the DAST tier believes a target exposes, and — for every one of -// them — WHERE THAT BELIEF CAME FROM and WHETHER ANVIL CONFIRMED IT. -// // This file is the runtime spec probe: Tier 0, the runtime spec route. // // =========================================================================== @@ -152,6 +148,7 @@ // Contract (lines 1142-1160); research/22-attack-surface-discovery.md lines // 319-323; internal/record/contract.go (InventoryProvenance, Trust, // DastCoverage). + package inventory import ( @@ -229,7 +226,7 @@ const ( // // # Why this is declared here and not in internal/record // -// internal/record owns every shared enum (its package doc: "The record area owns every +// internal/record owns every shared enum (contract.go's header comment: "The record area owns every // shared enum, because it owns the record contract, and no other area may // declare one"). record.InventoryProvenance is there for exactly that reason. // The confirmed/candidate axis is NOT — record carries it as two aggregate diff --git a/internal/dast/inventory/tier1_repospec.go b/internal/dast/inventory/tier1_repospec.go index 3344d65..8464c41 100644 --- a/internal/dast/inventory/tier1_repospec.go +++ b/internal/dast/inventory/tier1_repospec.go @@ -145,6 +145,7 @@ // Contract (lines 1142-1160); research/22-attack-surface-discovery.md lines // 325-328; internal/record/contract.go (InventoryProvenance, Trust, // ArtifactLocation, ArtifactContent, DastCoverage). + package inventory import ( diff --git a/internal/dast/inventory/tier2_confirm.go b/internal/dast/inventory/tier2_confirm.go index 9399bef..ea098b8 100644 --- a/internal/dast/inventory/tier2_confirm.go +++ b/internal/dast/inventory/tier2_confirm.go @@ -67,6 +67,7 @@ // ("promote to confirmed only on a non-404 response") and Risk #4 ("more // endpoints can mean less coverage" — the phpBB regression from timeout // exhaustion), which is what ProbeBudget and AssertBudgetSufficed exist for. + package inventory import ( diff --git a/internal/dast/inventory/tier2_go_extract.go b/internal/dast/inventory/tier2_go_extract.go index 46187b8..a6622a8 100644 --- a/internal/dast/inventory/tier2_go_extract.go +++ b/internal/dast/inventory/tier2_go_extract.go @@ -200,6 +200,7 @@ // Sources: Go route extraction's design (lines 644-676) and the Coverage Reporting // Contract (lines 1142-1160); research/22-attack-surface-discovery.md lines // 330-341; third_party/go-apispec/PIN.md; internal/record/contract.go. + package inventory import ( diff --git a/internal/dast/inventory/tier2_other_extract.go b/internal/dast/inventory/tier2_other_extract.go index f312d24..e3a6b60 100644 --- a/internal/dast/inventory/tier2_other_extract.go +++ b/internal/dast/inventory/tier2_other_extract.go @@ -112,6 +112,7 @@ // Sources: non-Go route extraction's design (lines 677-708) and the Coverage Reporting // Contract (lines 1142-1160); research/22-attack-surface-discovery.md lines // 330-341 and its Risk #2; internal/dast/inventory/tier2_go_extract.go (Go route extraction). + package inventory import ( diff --git a/internal/dast/inventory/tier3_crawl.go b/internal/dast/inventory/tier3_crawl.go index 6c85207..2b220fc 100644 --- a/internal/dast/inventory/tier3_crawl.go +++ b/internal/dast/inventory/tier3_crawl.go @@ -111,6 +111,7 @@ // exclusion list bounds nothing but waste. So the list failing open costs // coverage, never containment, and every miss is a recorded visit an operator // can read back. + package inventory import ( diff --git a/internal/dast/record/confirm_gate.go b/internal/dast/record/confirm_gate.go index 7e87ba7..dc7285f 100644 --- a/internal/dast/record/confirm_gate.go +++ b/internal/dast/record/confirm_gate.go @@ -256,6 +256,7 @@ // string into a tree and a tree into a program. Every package here that imports // `regexp` already links it transitively; gate 3 attributes by import line // rather than transitively, which is why the line was needed at all. + package record import ( diff --git a/internal/dast/record/coverage.go b/internal/dast/record/coverage.go index 4ea3a90..6d038e2 100644 --- a/internal/dast/record/coverage.go +++ b/internal/dast/record/coverage.go @@ -1,10 +1,3 @@ -// Package record aggregates the dynamic tier's per-tier inventory facts into the -// record-level coverage fields internal/record freezes: `dast_coverage` -// (record.DastCoverage), `endpoint_coverage`, `server_line_coverage` and -// `inventory_provenance`, plus the two target fields `target_provenance` -// (record.TargetProvenance) and `target.provisioning` -// (record.TargetProvisioning). -// // This is coverage reporting (plan node coverage). It produces THE NUMBER AN OPERATOR TRUSTS, so // every design decision below is about the number looking better than the // evidence behind it. @@ -107,6 +100,7 @@ // (DastCoverage, InventoryProvenance, TargetProvenance, TargetProvisioning); // research/22-attack-surface-discovery.md lines 364-374 and Risk #4; // research/23-dast-signal-sources.md Risk #1. + package record import ( @@ -314,7 +308,7 @@ const ( QualifierTierFactsAbsent QualifierReason = "tier_facts_absent" // QualifierOperationsCollapsed: more operations than addresses. See the - // package doc, decision 4. + // comment at the top of this file, decision 4. QualifierOperationsCollapsed QualifierReason = "operations_collapsed_onto_addresses" // QualifierProbeBudgetExhausted: endpoints in the union were never @@ -499,7 +493,7 @@ type ProvenanceRow struct { // Operations are the operation names this endpoint carries, sorted. It // is non-empty mainly for GraphQL, where many operations share one - // address; see the package doc, decision 4. + // address; see the comment at the top of this file, decision 4. Operations []string } @@ -735,7 +729,7 @@ func (s Summary) CandidateCount() int { return s.InventoryUnionCount() - s.confi // OperationCount is route confirmation's second number: distinct (endpoint, operation) // pairs. Reported BESIDE the endpoint count, never substituted into the -// denominator. See the package doc, decision 4. +// denominator. See the comment at the top of this file, decision 4. func (s Summary) OperationCount() int { return s.operations } // RequestsIssued is how many requests actually left through the seam. diff --git a/internal/dast/record/doc.go b/internal/dast/record/doc.go new file mode 100644 index 0000000..303ceee --- /dev/null +++ b/internal/dast/record/doc.go @@ -0,0 +1,7 @@ +// Package record aggregates the dynamic tier's per-tier inventory facts into the +// record-level coverage fields internal/record freezes: `dast_coverage` +// (record.DastCoverage), `endpoint_coverage`, `server_line_coverage` and +// `inventory_provenance`, plus the two target fields `target_provenance` +// (record.TargetProvenance) and `target.provisioning` +// (record.TargetProvisioning). +package record diff --git a/internal/dast/target/doc.go b/internal/dast/target/doc.go new file mode 100644 index 0000000..1b5cd83 --- /dev/null +++ b/internal/dast/target/doc.go @@ -0,0 +1,4 @@ +// Package target parses `.anvil/target.yaml`, the DECLARED description of a +// repository's runtime target, and decides -- once, in one place -- whether the +// DAST half has a target to work with at all. +package target diff --git a/internal/dast/target/manifest.go b/internal/dast/target/manifest.go index 1237443..c973506 100644 --- a/internal/dast/target/manifest.go +++ b/internal/dast/target/manifest.go @@ -1,7 +1,3 @@ -// Package target parses `.anvil/target.yaml`, the DECLARED description of a -// repository's runtime target, and decides -- once, in one place -- whether the -// DAST half has a target to work with at all. -// // The one sentence that governs every decision in this file, from // plan/design/dynamic-tier.md's "Target Manifest Schema" section: // @@ -49,6 +45,7 @@ // scalars, multi-document streams, flow mappings, nested collections inside // sequences, tabs). Refusing an unsupported construct is safe; guessing at one // is not. + package target import ( diff --git a/internal/handoff/doc.go b/internal/handoff/doc.go new file mode 100644 index 0000000..d8c8afe --- /dev/null +++ b/internal/handoff/doc.go @@ -0,0 +1,3 @@ +// Package handoff implements the claim/lease protocol for the single +// `handoff` table defined by internal/store/schema.sql. +package handoff diff --git a/internal/handoff/state_machine.go b/internal/handoff/state_machine.go index 50e4fdc..3d1c042 100644 --- a/internal/handoff/state_machine.go +++ b/internal/handoff/state_machine.go @@ -1,6 +1,3 @@ -// Package handoff implements the claim/lease protocol for the single -// `handoff` table defined by internal/store/schema.sql. -// // WHAT THIS PACKAGE IS, AND WHY THERE IS ONLY ONE OF IT. // The handoff-table ruling: "The record area owns the table and the // claim/lease protocol." The handoff adapter no longer writes a migration and no longer @@ -73,6 +70,7 @@ // The tmpfs packet is still written by research/08 §C's durable recipe // (exclusive temp file in the same directory, fsync, rename, fsync parent) and // never relies on ext4's auto_da_alloc; see WritePacket. + package handoff import ( diff --git a/internal/ingest/bootstrap/bootstrap.go b/internal/ingest/bootstrap/bootstrap.go index a0cac23..feec18e 100644 --- a/internal/ingest/bootstrap/bootstrap.go +++ b/internal/ingest/bootstrap/bootstrap.go @@ -1,6 +1,3 @@ -// Package bootstrap fills a feed's ingestion cache ONCE, from a bulk artifact, -// so that the conditional-GET poller only ever has to carry deltas. -// // This is the bulk bootstrap (plan node bootstrap). Lane A is the // zero-inference half of Anvil (the spine's corrected-requirements table): CVE/OSV/GHSA describe // vulnerable PACKAGE VERSIONS and a version comparator answers that exactly and @@ -124,6 +121,7 @@ // - It does not name a feed. There is no feed id, URL, cadence or format // mapping compiled into this file. Which feeds exist comes from the feed // table; what an artifact CONTAINS is decided by looking at the bytes. + package bootstrap import ( diff --git a/internal/ingest/bootstrap/doc.go b/internal/ingest/bootstrap/doc.go new file mode 100644 index 0000000..f583d24 --- /dev/null +++ b/internal/ingest/bootstrap/doc.go @@ -0,0 +1,3 @@ +// Package bootstrap fills a feed's ingestion cache ONCE, from a bulk artifact, +// so that the conditional-GET poller only ever has to carry deltas. +package bootstrap diff --git a/internal/ingest/cache/doc.go b/internal/ingest/cache/doc.go new file mode 100644 index 0000000..8e1fde3 --- /dev/null +++ b/internal/ingest/cache/doc.go @@ -0,0 +1,4 @@ +// Package cache owns the Lane A ingestion cache: a SECOND SQLite file, +// `anvil-cache.sqlite`, holding advisory feed content (plan node cache; design in +// plan/design/lane-a.md). +package cache diff --git a/internal/ingest/cache/schema.go b/internal/ingest/cache/schema.go index ec3a62a..f8ff4b3 100644 --- a/internal/ingest/cache/schema.go +++ b/internal/ingest/cache/schema.go @@ -1,7 +1,3 @@ -// Package cache owns the Lane A ingestion cache: a SECOND SQLite file, -// `anvil-cache.sqlite`, holding advisory feed content (plan node cache; design in -// plan/design/lane-a.md). -// // # This is not the store of record // // internal/store is Anvil's audit store of record and it is a frozen @@ -56,6 +52,7 @@ // directories by reading checked-in LICENSE file bodies. This schema only // RECORDS the outcome, and refuses a row that records nothing at all. // - It does not fetch. The poller polls and the bulk bootstrap bootstraps. + package cache import ( diff --git a/internal/ingest/config/doc.go b/internal/ingest/config/doc.go new file mode 100644 index 0000000..c1a82de --- /dev/null +++ b/internal/ingest/config/doc.go @@ -0,0 +1,2 @@ +// Package config loads Anvil's advisory-feed table from DATA. +package config diff --git a/internal/ingest/config/feeds.go b/internal/ingest/config/feeds.go index 3f36460..e205242 100644 --- a/internal/ingest/config/feeds.go +++ b/internal/ingest/config/feeds.go @@ -1,5 +1,3 @@ -// Package config loads Anvil's advisory-feed table from DATA. -// // This is the feed table (plan node feeds). Lane A is the // zero-inference half of Anvil (the spine's corrected-requirements table): a tiered conditional-GET // poller filling one SQLite+FTS5 cache, plus two collectors feeding a version @@ -59,6 +57,7 @@ // and no SPDX identifier, and that "attribution is requested" is a request, // not a grant. Such a feed is legal here only at LicenseTier3 — optional, // opt-in, risk-accepted — and Anvil must never describe it as open licensed. + package config import ( diff --git a/internal/ingest/decode/decode.go b/internal/ingest/decode/decode.go index e757df2..408ee4a 100644 --- a/internal/ingest/decode/decode.go +++ b/internal/ingest/decode/decode.go @@ -1,7 +1,3 @@ -// Package decode turns a publisher's advisory document into the row shape -// internal/ingest/cache stores. It is the ONE implementation of every wire -// format Lane A reads. -// // =========================================================================== // WHY THIS PACKAGE EXISTS — THE ONE-DECODER RULING // =========================================================================== @@ -67,6 +63,7 @@ // The callers re-prove it at the write site with sanitize.AssertAllSanitized // on the exact values about to be bound. This package's guarantee is not // trusted there; it is checked. + package decode import ( diff --git a/internal/ingest/decode/doc.go b/internal/ingest/decode/doc.go new file mode 100644 index 0000000..9a30848 --- /dev/null +++ b/internal/ingest/decode/doc.go @@ -0,0 +1,4 @@ +// Package decode turns a publisher's advisory document into the row shape +// internal/ingest/cache stores. It is the ONE implementation of every wire +// format Lane A reads. +package decode diff --git a/internal/ingest/delta/delta.go b/internal/ingest/delta/delta.go index 309d3b9..afe8d58 100644 --- a/internal/ingest/delta/delta.go +++ b/internal/ingest/delta/delta.go @@ -1,5 +1,3 @@ -// Package delta is Lane A's steady-state delta ingestion. -// // =========================================================================== // WHAT THIS PACKAGE IS FOR // =========================================================================== @@ -103,6 +101,7 @@ // // A refusal is loud, typed and counted. It is not a silent no-op, and it is // not a t.Skip. + package delta import ( @@ -159,7 +158,7 @@ var ( ErrNoDeltaLog = fmt.Errorf("%w: feed has no delta log", ErrSyncRefused) // ErrNoReconciler is RouteReconcile with nothing wired to run it. See the - // package comment: defaulting it to the bulk bootstrap's bulk importer would cost 570 MB + // header comment: defaulting it to the bulk bootstrap's bulk importer would cost 570 MB // a day. ErrNoReconciler = fmt.Errorf("%w: no reconciler", ErrSyncRefused) @@ -241,7 +240,7 @@ const ( RouteFeedBody Route = "feed_body" // RouteReconcile is the periodic wider-window pass on - // reconcile_interval_seconds. See the package comment for why it is + // reconcile_interval_seconds. See the comment at the top of this file for why it is // planned here and refused unless delegated. RouteReconcile Route = "reconcile" @@ -307,7 +306,7 @@ type Plan struct { LastOK time.Time // ReconcileDue and BaselineDue are the two wider passes. They are - // reported rather than run; see the package comment. + // reported rather than run; see the comment at the top of this file. // // Both are WINDOW-BOUNDARY tests, not elapsed-time tests: reconcile is due // when `now` and `LastOK` fall in different reconcile windows. For the @@ -596,7 +595,7 @@ type Source interface { Record(ctx context.Context, feed config.FeedConfig, id string) ([]byte, error) } -// Reconciler runs RouteReconcile. See the package comment for why this package +// Reconciler runs RouteReconcile. See the comment at the top of this file for why this package // refuses rather than defaulting the route to the bulk bootstrap's bulk importer. type Reconciler interface { Reconcile(ctx context.Context, feed config.FeedConfig) (BatchStats, error) diff --git a/internal/ingest/delta/doc.go b/internal/ingest/delta/doc.go new file mode 100644 index 0000000..fc52483 --- /dev/null +++ b/internal/ingest/delta/doc.go @@ -0,0 +1,2 @@ +// Package delta is Lane A's steady-state delta ingestion. +package delta diff --git a/internal/ingest/delta/upsert.go b/internal/ingest/delta/upsert.go index 1ff73bb..427be9d 100644 --- a/internal/ingest/delta/upsert.go +++ b/internal/ingest/delta/upsert.go @@ -9,7 +9,7 @@ // CREATE, no DELETE FROM advisory_fts that is not scoped to a single rowid, no // `INSERT INTO advisory_fts(advisory_fts) VALUES('rebuild')`. // -// internal/ingest/cache's own package comment gives the reason: "FTS5 accepts +// the comment at the top of internal/ingest/cache/schema.go gives the reason: "FTS5 accepts // incremental INSERT/DELETE, so an hourly delta touching 200 records costs 200 // row upserts and NOT a rebuild. That is why no code path in Anvil may DROP or // rebuild `advisory_fts`." delta ingestion's design repeats it as a forbidden action, and @@ -55,6 +55,7 @@ // call. internal/ingest/sanitize's writer guard sees the assertion in the same // function as the bind, which is what it can check; the assertion is what makes // the claim true rather than merely visible. + package delta import ( diff --git a/internal/ingest/drift/doc.go b/internal/ingest/drift/doc.go new file mode 100644 index 0000000..7feea57 --- /dev/null +++ b/internal/ingest/drift/doc.go @@ -0,0 +1,3 @@ +// Package drift is drift handling: what Anvil does when a feed changes shape underneath +// it, and what it does when a publisher takes an advisory back. +package drift diff --git a/internal/ingest/drift/drift.go b/internal/ingest/drift/drift.go index a479b35..97facf5 100644 --- a/internal/ingest/drift/drift.go +++ b/internal/ingest/drift/drift.go @@ -1,6 +1,3 @@ -// Package drift is drift handling: what Anvil does when a feed changes shape underneath -// it, and what it does when a publisher takes an advisory back. -// // =========================================================================== // THE ONE RULE THIS PACKAGE EXISTS TO KEEP // =========================================================================== @@ -27,7 +24,7 @@ // =========================================================================== // // It does NOT mean a second CVE decoder. internal/ingest/delta already has one -// and internal/ingest/bootstrap has another, and delta's own package comment +// and internal/ingest/bootstrap has another, and the comment at the top of delta.go // records that the second one is a real cross-area hazard which its // conformance test exists to contain. A THIRD would be the same defect with a // third name on it, so this package extracts NOTHING itself: it decides which @@ -83,6 +80,7 @@ // cache on the tombstone path). // - It does not compute a fingerprint, derive one, or compare against one. // anvil-fp/v1 is internal/record's and is the only one (the spine's record section). + package drift import ( @@ -623,7 +621,7 @@ func decodeOne(feedID string, raw []byte) (Record, error) { // primary key and the publisher's bytes. // // It is deliberately minimal. Reconstructing severity or version ranges here -// would be the third decoder the package comment refuses to write, and a +// would be the third decoder the comment at the top of this file refuses to write, and a // half-reconstructed record is exactly the "looks complete and is not" outcome // drift handling exists to prevent. What survives is enough for the record to be found // again and re-parsed by a later build that understands the shape. diff --git a/internal/ingest/drift/tombstone.go b/internal/ingest/drift/tombstone.go index 8d3ac3d..476fd5b 100644 --- a/internal/ingest/drift/tombstone.go +++ b/internal/ingest/drift/tombstone.go @@ -52,6 +52,7 @@ // INSERT OR REPLACE would delete and re-insert under a NEW rowid and // orphan the FTS entry silently — the exact defect cache/schema.go // documents at the `advisory` table. + package drift import ( diff --git a/internal/ingest/invisible/doc.go b/internal/ingest/invisible/doc.go new file mode 100644 index 0000000..09346bf --- /dev/null +++ b/internal/ingest/invisible/doc.go @@ -0,0 +1,2 @@ +// Package invisible is THE definition of "renders as nothing" for Lane A. +package invisible diff --git a/internal/ingest/invisible/invisible.go b/internal/ingest/invisible/invisible.go index e67da68..fdefde2 100644 --- a/internal/ingest/invisible/invisible.go +++ b/internal/ingest/invisible/invisible.go @@ -1,5 +1,3 @@ -// Package invisible is THE definition of "renders as nothing" for Lane A. -// // =========================================================================== // WHY THIS PACKAGE EXISTS AT ALL // =========================================================================== @@ -94,6 +92,7 @@ // that a renderer must draw nothing for them, which is the class's question // answered from a table. An unassigned code point with no such property draws a // .notdef box, which is visible residue, and stays out. + package invisible import "unicode" diff --git a/internal/ingest/license/doc.go b/internal/ingest/license/doc.go new file mode 100644 index 0000000..4310d59 --- /dev/null +++ b/internal/ingest/license/doc.go @@ -0,0 +1,3 @@ +// Package license is Lane A's licence gate and the segregated mirror layout it +// enforces. This is the licence gate (plan node licencegate). +package license diff --git a/internal/ingest/license/tiers.go b/internal/ingest/license/tiers.go index 3289d21..9563753 100644 --- a/internal/ingest/license/tiers.go +++ b/internal/ingest/license/tiers.go @@ -1,6 +1,3 @@ -// Package license is Lane A's licence gate and the segregated mirror layout it -// enforces. This is the licence gate (plan node licencegate). -// // # NO FEED IS ADMITTED BY A FRESH CLONE. THAT IS THE DESIGN. // // Clone this repository, run the gate, and every feed is refused. Nothing is @@ -149,6 +146,7 @@ // are never compared against a canonical fingerprint. // - It does not carry CIS Benchmark content, or text derived from reading one // (the spine's hard exclusion). + package license import ( @@ -181,7 +179,7 @@ const ( // obligation on each other. Each feed's record is a delimited block inside // it — see BodyBeginMarker. // - // It is a record, not evidence. See the package doc. + // It is a record, not evidence. See the comment at the top of this file. NotesFileName = "LICENSE-NOTES.md" // LicenseFileName is the record each TIER 2 source directory carries on diff --git a/internal/ingest/poller/doc.go b/internal/ingest/poller/doc.go new file mode 100644 index 0000000..c303a62 --- /dev/null +++ b/internal/ingest/poller/doc.go @@ -0,0 +1,2 @@ +// Package poller is Lane A's authenticated conditional-GET poller. +package poller diff --git a/internal/ingest/poller/poller.go b/internal/ingest/poller/poller.go index f9c8d9a..3233c7e 100644 --- a/internal/ingest/poller/poller.go +++ b/internal/ingest/poller/poller.go @@ -1,5 +1,3 @@ -// Package poller is Lane A's authenticated conditional-GET poller. -// // =========================================================================== // WHAT THIS PACKAGE IS FOR // =========================================================================== @@ -104,6 +102,7 @@ // in that declaration and nowhere else. They are a fact about a PROVIDER's rate // limiter that several feeds happen to share, not a fact about any feed — see // the declaration. + package poller import ( @@ -738,7 +737,7 @@ func (p *Poller) Poll(ctx context.Context, feed config.FeedConfig) (PollResult, return res, err } - // The licence gate BEFORE THE NETWORK. See the package comment: a feed whose licence + // The licence gate BEFORE THE NETWORK. See the comment at the top of this file: a feed whose licence // evidence is absent is not fetched, because the bytes could not be kept // and the request would still have cost budget. decision, err := p.resolveLicense(feed) diff --git a/internal/ingest/reconcile/baseline.go b/internal/ingest/reconcile/baseline.go index cb725f8..f0e1a1e 100644 --- a/internal/ingest/reconcile/baseline.go +++ b/internal/ingest/reconcile/baseline.go @@ -1,5 +1,3 @@ -// Package reconcile owns the WEEKLY FULL-BASELINE SELF-HEAL. -// // This is the weekly self-heal (plan node upkeep). Lane A is the // zero-inference half of Anvil (the spine's corrected-requirements table): CVE/OSV/GHSA describe // vulnerable PACKAGE VERSIONS and a version comparator answers that exactly @@ -102,6 +100,7 @@ // imported zero records all increment that counter in the LIVE cache and are // named in the report. See recordFailure for the one case that deliberately // does not increment it, and why. + package reconcile import ( diff --git a/internal/ingest/reconcile/doc.go b/internal/ingest/reconcile/doc.go new file mode 100644 index 0000000..581c319 --- /dev/null +++ b/internal/ingest/reconcile/doc.go @@ -0,0 +1,2 @@ +// Package reconcile owns the WEEKLY FULL-BASELINE SELF-HEAL. +package reconcile diff --git a/internal/ingest/sanitize/doc.go b/internal/ingest/sanitize/doc.go new file mode 100644 index 0000000..3ddb5a5 --- /dev/null +++ b/internal/ingest/sanitize/doc.go @@ -0,0 +1,3 @@ +// Package sanitize is Lane A's sanitizer: ingest-time neutralisation of hostile +// text, plus the `anvil/trust` stamp that says where the bytes came from. +package sanitize diff --git a/internal/ingest/sanitize/sanitize.go b/internal/ingest/sanitize/sanitize.go index d83783f..0066c08 100644 --- a/internal/ingest/sanitize/sanitize.go +++ b/internal/ingest/sanitize/sanitize.go @@ -1,6 +1,3 @@ -// Package sanitize is Lane A's sanitizer: ingest-time neutralisation of hostile -// text, plus the `anvil/trust` stamp that says where the bytes came from. -// // =========================================================================== // WHY THIS RUNS AT INGEST AND NOWHERE ELSE // =========================================================================== @@ -465,6 +462,7 @@ // deleting text a reader might have seen is the survivable failure. The other // direction — keeping a span a reader will NOT see — is the one that puts // agent instructions in front of a repo-credentialed agent. + package sanitize import ( @@ -679,7 +677,7 @@ type SanitizeStats struct { // HiddenTagText counts HTML TAG tokens carrying text beyond their own tag // name — attributes, junk after an end tag name, and the content of the // raw-text elements. NOTHING WAS REMOVED FOR THIS COUNT. Tags are out of - // scope for removal (see the ruling in the package comment); this counter + // scope for removal (see the ruling in the comment at the top of this file); this counter // exists so that "stats=clean" stops being the answer for a string that a // renderer would partly hide. HiddenTagText int @@ -997,7 +995,7 @@ func IngestSlice(raw []string) ([]record.TrustedString, SanitizeStats) { } // stripRunes runs the rune classification. It is the first of Sanitize's two -// passes and it must stay first: see the package comment on ordering. +// passes and it must stay first: see the header comment on ordering. // // An invalid UTF-8 byte is DROPPED rather than replaced with U+FFFD. Replacing // preserves the position of the damage, which is friendlier to a human reading @@ -1096,7 +1094,7 @@ func (s *SanitizeStats) count(cat category) { // --------------------------------------------------------------------------- // Hidden markup: comment spans and the four other productions that render as -// nothing. See "WHICH HTML PRODUCTIONS ARE IN SCOPE" in the package comment +// nothing. See "WHICH HTML PRODUCTIONS ARE IN SCOPE" in the comment at the top of this file // for the scope decision and for what is deliberately left alone. // --------------------------------------------------------------------------- @@ -1482,7 +1480,7 @@ func indexCloseTag(s string, from int, name string) int { // IT IS NOT PART OF THE SANITIZE POST-CONDITION, and AssertSanitized does not // call it. Sanitize does not remove these spans, so a string that satisfies // AssertSanitized may still fail this; that asymmetry is the ruling in the -// package comment, made checkable. The remedy for a failure is to escape or +// header comment, made checkable. The remedy for a failure is to escape or // fence the text at the display site, never to delete it here. func AssertNoHiddenTagText(s string) error { tags, runes, offset := scanHiddenTagText(s) @@ -1521,7 +1519,7 @@ func AssertNoHiddenTagText(s string) error { // DOCTYPE opener. // nil DOES NOT mean every character in s is visible to a reader. HTML // MEAN TAGS are out of scope for removal (see the ruling in the -// package comment) and a tag can hide text. The nearest +// header comment) and a tag can hide text. The nearest // available check is AssertNoHiddenTagText; the actual // control is rendering s as plain text. func AssertSanitized(s string) error { diff --git a/internal/match/apk_compare.go b/internal/match/apk_compare.go index bfee52e..c9042a4 100644 --- a/internal/match/apk_compare.go +++ b/internal/match/apk_compare.go @@ -226,6 +226,7 @@ // line 732 `6.0_p1 > 6.0`), and the three R8 shapes, which appear in no row of // it at all. Those vectors are tagged AUTHORED and name the rule they come // from; they may not name a file, because there is no line to name. + package match import ( diff --git a/internal/match/comparator.go b/internal/match/comparator.go index 4a3693c..c1a4221 100644 --- a/internal/match/comparator.go +++ b/internal/match/comparator.go @@ -1,7 +1,3 @@ -// Package match is Lane A's deterministic version comparator: the comparator of -// plan/design/lane-a.md, and the component every other Lane A step -// feeds. -// // =========================================================================== // WHAT THIS PACKAGE IS // =========================================================================== @@ -178,6 +174,7 @@ // - There is no clock. `as_of` and `detected_at` belong to the collector // and to record emission's record emitter; a second time source here would be a // second answer to a question already owned elsewhere. + package match import ( @@ -816,8 +813,8 @@ func epochSpelling(scheme Scheme, v string) (present bool, value int, ok bool) { // advisory endpoint spelled `2.34-100.el9` gives `2 > 0`, so the installed // version sorts ABOVE the fixed endpoint, the range does not contain it, and // the run reports zero findings, zero refusals, Complete=true and a clean -// verdict on a vulnerable host. That is the false negative the package doc's -// first paragraph names as the worst output this lane can produce, on one of +// verdict on a vulnerable host. That is the false negative the header comment's +// first section names as the worst output this lane can produce, on one of // the most common shapes in the RPM world. // // =========================================================================== @@ -1139,7 +1136,7 @@ func (d Defence) sortKey() string { // UpstreamOnlyAdvisory is the package-level residue of the vendor-first // policy: an advisory that was decided by an upstream range for a package // which HAS vendor coverage for some other advisory. It is REPORTED, not -// suppressed — see the package doc's "SCOPE OF THE PRECEDENCE". +// suppressed — see the header comment's "SCOPE OF THE PRECEDENCE". type UpstreamOnlyAdvisory struct { Ecosystem string Package string @@ -1174,7 +1171,7 @@ func (u UpstreamOnlyAdvisory) sortKey() string { // this package cannot fix it — a vendor row and an upstream row that share no // identifier cannot be shown to be about the same flaw, and guessing that // they are (by package name, say) is the package-scoped suppression the -// package doc rejects as an unbounded false-negative generator. +// header comment rejects as an unbounded false-negative generator. // // What it CAN do is stop the dependence being invisible. Every vendor range // that arrives without an alias is listed here, so "the defence did not fire" diff --git a/internal/match/comparator_test.go b/internal/match/comparator_test.go index c18dcc9..bc561f5 100644 --- a/internal/match/comparator_test.go +++ b/internal/match/comparator_test.go @@ -3642,6 +3642,8 @@ func TestNoSourceFileReachesForAClockOrARandomSource(t *testing.T) { expected := map[string]bool{ "comparator.go": true, "dpkg_compare.go": true, "rpm_compare.go": true, "apk_compare.go": true, "purl.go": true, "comparator_test.go": true, + // The package summary, and nothing else (test/hygiene's package-doc guard). + "doc.go": true, // The transcribed corpus. It is a separate file because it is // GENERATED from the three published suites rather than written, // and mixing a generated table into a hand-written test file is @@ -3683,8 +3685,8 @@ func TestNoSourceFileReachesForAClockOrARandomSource(t *testing.T) { return true }) } - if seen != 5 { - t.Errorf("scanned %d non-test files, expected 5", seen) + if seen != 6 { + t.Errorf("scanned %d non-test files, expected 6", seen) } } diff --git a/internal/match/doc.go b/internal/match/doc.go new file mode 100644 index 0000000..c105e86 --- /dev/null +++ b/internal/match/doc.go @@ -0,0 +1,4 @@ +// Package match is Lane A's deterministic version comparator: the comparator of +// plan/design/lane-a.md, and the component every other Lane A step +// feeds. +package match diff --git a/internal/match/dpkg_compare.go b/internal/match/dpkg_compare.go index fd42c2d..5941ec9 100644 --- a/internal/match/dpkg_compare.go +++ b/internal/match/dpkg_compare.go @@ -64,6 +64,7 @@ // any published comparison table can contain it. dpkgValidity in // corpus_transcribed_test.go transcribes the nine is_valid() assertions from // Dpkg_Version.t for exactly this reason. + package match import ( diff --git a/internal/match/purl.go b/internal/match/purl.go index 7d7a8c0..722d516 100644 --- a/internal/match/purl.go +++ b/internal/match/purl.go @@ -31,11 +31,12 @@ // for three times over: a denylist loses, because the string nobody listed is // the one that walks through. // -// The practical consequence is stated plainly in the package doc: this +// The practical consequence is stated plainly in comparator.go's header comment: this // comparator covers `deb`, `rpm` and `apk`, and refuses `npm`, `pypi`, // `golang`, `maven`, `nuget`, `cargo`, `gem`, `composer` and everything else. // A refusal is a visible gap. A fallback to semver, or to a lexical compare, // would be an invisible wrong answer. + package match import ( diff --git a/internal/match/rpm_compare.go b/internal/match/rpm_compare.go index 4285393..72ee8ab 100644 --- a/internal/match/rpm_compare.go +++ b/internal/match/rpm_compare.go @@ -74,6 +74,7 @@ // the published suite minus the rows the implementation fails is a corpus // filtered by the implementation, and that circularity is what this project's // licence-marker table already paid for once. + package match import ( diff --git a/internal/mirror/accelerator/doc.go b/internal/mirror/accelerator/doc.go new file mode 100644 index 0000000..e8e51cf --- /dev/null +++ b/internal/mirror/accelerator/doc.go @@ -0,0 +1,3 @@ +// Package accelerator is Anvil's OPTIONAL warm-start cache for package-range +// matching. This is the accelerator (plan node accelerator). +package accelerator diff --git a/internal/mirror/accelerator/trivydb.go b/internal/mirror/accelerator/trivydb.go index 9631527..8ac9359 100644 --- a/internal/mirror/accelerator/trivydb.go +++ b/internal/mirror/accelerator/trivydb.go @@ -1,6 +1,3 @@ -// Package accelerator is Anvil's OPTIONAL warm-start cache for package-range -// matching. This is the accelerator (plan node accelerator). -// // # ANVIL MUST WORK CORRECTLY WITHOUT THIS PACKAGE. THAT IS THE DESIGN. // // Everything here is a performance optimisation. If the accelerator is absent, @@ -137,6 +134,7 @@ // what RegistryBase is for. It is stated here because no test can observe // it: the spine's safety section forbids the network at test time, so the // mock registry does not model the redirect the real one issues. + package accelerator import ( diff --git a/internal/policy/doc.go b/internal/policy/doc.go new file mode 100644 index 0000000..71efcd8 --- /dev/null +++ b/internal/policy/doc.go @@ -0,0 +1,9 @@ +// Package policy resolves Anvil's trigger policy from the repository it is +// scanning. Trigger policy is DATA: which events fire a scan, which refs and +// paths they apply to, which semver bumps gate a full scan, and on what +// cadence the daemon re-scans are all read from a file in the repository, never +// compiled into Anvil. The spine's corrected-requirements table makes that a hard constraint, and +// plan/design/control-plane.md restates the review rule it implies: a literal +// such as "push" or "major" used as a match condition anywhere outside the +// parser is a defect. +package policy diff --git a/internal/policy/locate.go b/internal/policy/locate.go index 0fff674..7310a72 100644 --- a/internal/policy/locate.go +++ b/internal/policy/locate.go @@ -1,12 +1,3 @@ -// Package policy resolves Anvil's trigger policy from the repository it is -// scanning. Trigger policy is DATA: which events fire a scan, which refs and -// paths they apply to, which semver bumps gate a full scan, and on what -// cadence the daemon re-scans are all read from a file in the repository, never -// compiled into Anvil. The spine's corrected-requirements table makes that a hard constraint, and -// plan/design/control-plane.md restates the review rule it implies: a literal -// such as "push" or "major" used as a match condition anywhere outside the -// parser is a defect. -// // This file is the policy schema's half of that: FINDING the policy file. Parsing it, // evaluating its rules (the policy engine), and computing the semver bump its rules match // against (semver bump classification) are separate steps in this same package. @@ -18,6 +9,7 @@ // side -- so a second schema for this one file, in the dynamic tier or in the GitHub // Action (the GitHub Action), would be the eleventh. Consumers validate against that file and // extend it there. + package policy import ( diff --git a/internal/record/contract.go b/internal/record/contract.go index 0e28aea..1ad9cae 100644 --- a/internal/record/contract.go +++ b/internal/record/contract.go @@ -1,8 +1,3 @@ -// Package record defines Anvil's single frozen audit-record contract: the -// SARIF 2.1.0 wire shape Anvil produces, the typed `anvil/*` property-bag -// extension that carries what stock SARIF cannot, and — since the -// 2026-08-07 orchestrator ruling — every enum shared across Anvil's areas. -// // # Authority // // This file is the single point where shared cross-area vocabulary is fixed. @@ -51,6 +46,7 @@ // Specification", "Store Schema"); research/18-unified-audit-record.md // ("Recommendation For Anvil", the annotated record, Risks); // research/24-coding-agent-consumption.md ("What the audit record must carry"). + package record import ( diff --git a/internal/record/correlation.go b/internal/record/correlation.go index dca5ade..658ee0f 100644 --- a/internal/record/correlation.go +++ b/internal/record/correlation.go @@ -80,7 +80,8 @@ // the annotated record's anvil/correlation block); the spine's safety section; // plan/design/record-and-store.md (Open Questions #1). // -// (Free-floating file comment: contract.go carries the package doc.) +// (Free-floating file comment: doc.go carries the package doc.) + package record import ( diff --git a/internal/record/doc.go b/internal/record/doc.go new file mode 100644 index 0000000..5617de7 --- /dev/null +++ b/internal/record/doc.go @@ -0,0 +1,5 @@ +// Package record defines Anvil's single frozen audit-record contract: the +// SARIF 2.1.0 wire shape Anvil produces, the typed `anvil/*` property-bag +// extension that carries what stock SARIF cannot, and — since the +// 2026-08-07 orchestrator ruling — every enum shared across Anvil's areas. +package record diff --git a/internal/record/lanea/doc.go b/internal/record/lanea/doc.go new file mode 100644 index 0000000..0bbe64a --- /dev/null +++ b/internal/record/lanea/doc.go @@ -0,0 +1,3 @@ +// Package lanea populates the Lane-A-owned fields of Anvil's canonical audit +// record: record emission (plan node emission). +package lanea diff --git a/internal/record/lanea/emit.go b/internal/record/lanea/emit.go index b878992..3dab995 100644 --- a/internal/record/lanea/emit.go +++ b/internal/record/lanea/emit.go @@ -1,6 +1,3 @@ -// Package lanea populates the Lane-A-owned fields of Anvil's canonical audit -// record: record emission (plan node emission). -// // =========================================================================== // WHAT THIS PACKAGE IS, AND THE ONE THING IT IS NOT // =========================================================================== @@ -194,6 +191,7 @@ // (RefusalTrustValidationStep), and the named step travels on // Emission.TrustValidationStep — the same treatment, and the same reported // record-contract gap, as license_manual_note in deviation 1. + package lanea import ( diff --git a/internal/record/readpath.go b/internal/record/readpath.go index b7abf56..500703f 100644 --- a/internal/record/readpath.go +++ b/internal/record/readpath.go @@ -87,6 +87,7 @@ // path", the annotated Tier-1 task card); research/24-coding-agent-consumption // .md ("What the audit record must carry"); plan/design/record-and-store.md // (the read path); the spine's corrected-requirements, record and safety sections. + package record import ( diff --git a/internal/record/sealing.go b/internal/record/sealing.go index f9f15aa..9c1817f 100644 --- a/internal/record/sealing.go +++ b/internal/record/sealing.go @@ -58,7 +58,7 @@ // onto those columns. Every literal it produces comes from contract.go, which // is the same source ddl_test.go checks the SQL against. // -// (Free-floating file comment: contract.go carries the package doc.) +// (Free-floating file comment: doc.go carries the package doc.) package record diff --git a/internal/record/taskcard.go b/internal/record/taskcard.go index 65cc0f5..7c98a6a 100644 --- a/internal/record/taskcard.go +++ b/internal/record/taskcard.go @@ -71,6 +71,7 @@ // `evidence_class`, `dast.reproduction`, `risk.*`, `locus.*`, // `advisory_excerpt` (<=800 tokens) and `group_id`. Every one has a field // below, and TestCardCarriesTheNonNegotiableFields asserts it. + package record import ( diff --git a/internal/scanctl/deadlines.go b/internal/scanctl/deadlines.go index 94dbbf1..64396a4 100644 --- a/internal/scanctl/deadlines.go +++ b/internal/scanctl/deadlines.go @@ -1,15 +1,6 @@ -// Package scanctl is `anvil-scanctl`: the ONE named scan controller -// the spine's one-controller rule requires, holding ONE state machine with ONE owner. -// The spine's one-controller rule exists because four research branches each specified part -// of an orchestrator (a consumption protocol with leases and ledgers, a -// correlator process, sixteen validation gates, a target-lifecycle harness), -// and "implement it as one named scan controller with one state machine and -// one owner, or it will be re-implemented inconsistently in four places." -// -// This file (the deadline design) carries the package doc because it is the package's -// first file and the one every other file in it depends on. Later files in -// this package — statemachine.go (the controller's state wiring), handoff.go -// (the handoff adapter) — must NOT add a second package comment. +// This file is the deadline design, the one every other file in this package +// depends on. The package doc lives in doc.go; no other file may attach a +// comment to its package clause (test/hygiene's package-doc guard checks). // // scanctl OWNS NO VOCABULARY. Every enum token it handles is a Go constant // from internal/record, which the shared-vocabulary review makes the single @@ -19,6 +10,7 @@ // separate authors each defining the shared vocabulary from their own side. // A bare string literal for an enum value in this package is a second // definition and is how that recurs. + package scanctl import ( diff --git a/internal/scanctl/doc.go b/internal/scanctl/doc.go new file mode 100644 index 0000000..3cf4ad5 --- /dev/null +++ b/internal/scanctl/doc.go @@ -0,0 +1,8 @@ +// Package scanctl is `anvil-scanctl`: the ONE named scan controller +// the spine's one-controller rule requires, holding ONE state machine with ONE owner. +// The spine's one-controller rule exists because four research branches each specified part +// of an orchestrator (a consumption protocol with leases and ledgers, a +// correlator process, sixteen validation gates, a target-lifecycle harness), +// and "implement it as one named scan controller with one state machine and +// one owner, or it will be re-implemented inconsistently in four places." +package scanctl diff --git a/internal/scanctl/handoff.go b/internal/scanctl/handoff.go index d73dd25..ac0b84b 100644 --- a/internal/scanctl/handoff.go +++ b/internal/scanctl/handoff.go @@ -141,7 +141,7 @@ // with the grant. A Go re-check here would be a second definition that // could disagree, and it could not be atomic with anything. // -// (Free-floating file comment: deadlines.go carries the package doc.) +// (Free-floating file comment: doc.go carries the package doc.) package scanctl diff --git a/internal/scanctl/statemachine.go b/internal/scanctl/statemachine.go index bdb76d6..4d9be16 100644 --- a/internal/scanctl/statemachine.go +++ b/internal/scanctl/statemachine.go @@ -102,7 +102,7 @@ // "two areas meaning different things by the same field name" class the // shared-vocabulary review was convened over. // -// (Free-floating file comment: deadlines.go carries the package doc.) +// (Free-floating file comment: doc.go carries the package doc.) package scanctl diff --git a/internal/store/ddl.go b/internal/store/ddl.go index c7845ac..079e13e 100644 --- a/internal/store/ddl.go +++ b/internal/store/ddl.go @@ -1,5 +1,3 @@ -// Package store owns Anvil's single SQLite store of record. -// // The spine's corrected-requirements table collapsed the originally-specified "8-hour buffer file" // into one SQLite database, a `handoff` table, and a regenerable tmpfs packet // that is never a source of truth. This package holds the DDL for that @@ -12,6 +10,7 @@ // enough read-only introspection for the migration runner's migration ledger and for the test // that proves the SQL vocabularies and internal/record's Go enums have not // drifted apart. It opens no database and executes no statement. + package store import ( diff --git a/internal/store/doc.go b/internal/store/doc.go new file mode 100644 index 0000000..b7ce4a4 --- /dev/null +++ b/internal/store/doc.go @@ -0,0 +1,2 @@ +// Package store owns Anvil's single SQLite store of record. +package store diff --git a/test/hygiene/package_docs_test.go b/test/hygiene/package_docs_test.go new file mode 100644 index 0000000..6557405 --- /dev/null +++ b/test/hygiene/package_docs_test.go @@ -0,0 +1,170 @@ +package hygiene_test + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "path/filepath" + "sort" + "strings" + "testing" +) + +// go doc concatenates every comment attached to a package clause, in file +// order, and shows the first sentence as the package summary. Before plan node +// packagedocs, several packages attached file comments to their package +// clause, so go doc introduced the authorization kernel with one of its phases, +// the attack-surface inventory with its authentication helper and the +// comparator with Alpine's version ordering. The rule this guard keeps: +// +// - every package with non-test Go source has a doc.go; +// - doc.go holds the package summary and nothing else (no imports, no +// declarations), starting "Package " or, for a command, +// "Command "; +// - no other non-test file attaches a comment to its package clause. File +// comments stay, separated from the package line by a blank line. + +// docProblems checks one set of Go source files, keyed by slash-separated +// path relative to the module root. It is a pure function so the negative +// control can run the shipping check on a synthetic tree. +func docProblems(files map[string][]byte) []string { + byDir := map[string][]string{} + for p := range files { + if strings.HasSuffix(p, ".go") && !strings.HasSuffix(p, "_test.go") { + byDir[path.Dir(p)] = append(byDir[path.Dir(p)], p) + } + } + dirs := make([]string, 0, len(byDir)) + for d := range byDir { + dirs = append(dirs, d) + } + sort.Strings(dirs) + + var problems []string + for _, dir := range dirs { + paths := byDir[dir] + sort.Strings(paths) + hasDocGo := false + for _, p := range paths { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, p, files[p], parser.ParseComments) + if err != nil { + problems = append(problems, fmt.Sprintf("%s: does not parse: %v", p, err)) + continue + } + if path.Base(p) != "doc.go" { + if f.Doc != nil { + problems = append(problems, fmt.Sprintf("%s: a comment is attached to the package "+ + "clause, so go doc shows it as part of the package summary. Put a blank line "+ + "between it and the package line; the summary belongs in %s/doc.go", p, dir)) + } + continue + } + hasDocGo = true + want := "Package " + f.Name.Name + " " + if f.Name.Name == "main" { + want = "Command " + path.Base(dir) + " " + } + if f.Doc == nil || !strings.HasPrefix(f.Doc.Text(), want) { + problems = append(problems, fmt.Sprintf("%s: the package summary must start %q", p, want)) + } + if len(f.Imports) > 0 || len(f.Decls) > 0 { + problems = append(problems, fmt.Sprintf("%s: holds code; doc.go holds the package "+ + "summary and nothing else", p)) + } + for _, cg := range f.Comments { + if cg != f.Doc && !isBuildConstraint(cg) { + problems = append(problems, fmt.Sprintf("%s: carries a comment other than the "+ + "package summary", p)) + break + } + } + } + if !hasDocGo { + problems = append(problems, fmt.Sprintf("%s: no doc.go; every package states what it is "+ + "in one place", dir)) + } + } + return problems +} + +func isBuildConstraint(cg *ast.CommentGroup) bool { + return len(cg.List) == 1 && strings.HasPrefix(cg.List[0].Text, "//go:build ") +} + +func TestEveryPackageSummaryIsItsOwn(t *testing.T) { + root, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + listed, how := listTree(t, root) + files := map[string][]byte{} + for _, rel := range listed { + top := strings.SplitN(rel, "/", 2)[0] + if !strings.HasSuffix(rel, ".go") || strings.Contains("/"+rel, "/testdata/") || top == "eval" { + continue + } + data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(rel))) + if err != nil { + continue // listed by git but deleted in the working tree + } + files[rel] = data + } + // The module has dozens of packages; a handful of files means the listing + // broke, and an empty set passes every rule. + if len(files) < 100 { + t.Fatalf("only %d Go files listed (%s); that is a broken listing, not a small module", len(files), how) + } + for _, p := range docProblems(files) { + t.Error(p) + } +} + +// TestThePackageDocGuardFails is the negative control: each synthetic package +// breaks one rule, and the shipping check must name every one of them. +func TestThePackageDocGuardFails(t *testing.T) { + good := []byte("// Package good is fine.\npackage good\n") + cases := map[string]struct { + files map[string][]byte + want string + }{ + "attached file comment": {map[string][]byte{ + "a/doc.go": []byte("// Package a is fine.\npackage a\n"), + "a/b.go": []byte("// Phase 1 of something.\npackage a\n"), + }, "attached to the package clause"}, + "no doc.go": {map[string][]byte{ + "b/b.go": []byte("// File comment.\n\npackage b\n"), + }, "no doc.go"}, + "wrong summary": {map[string][]byte{ + "c/doc.go": []byte("// This package does things.\npackage c\n"), + }, `must start "Package c "`}, + "command summary": {map[string][]byte{ + "cmd/tool/doc.go": []byte("// Package main is a tool.\npackage main\n"), + }, `must start "Command tool "`}, + "code in doc.go": {map[string][]byte{ + "d/doc.go": []byte("// Package d is fine.\npackage d\n\nconst X = 1\n"), + }, "holds code"}, + "second comment in doc.go": {map[string][]byte{ + "e/doc.go": []byte("// Package e is fine.\npackage e\n\n// stray\n"), + }, "other than the package summary"}, + } + for name, c := range cases { + c.files["good/doc.go"] = good + got := strings.Join(docProblems(c.files), "\n") + if !strings.Contains(got, c.want) { + t.Errorf("%s: the guard did not report %q; it said:\n%s", name, c.want, got) + } + if strings.Contains(got, "good/") { + t.Errorf("%s: the guard refused the well-formed package: %s", name, got) + } + } + // Test files are not part of go doc and may carry their own package comment. + tests := map[string][]byte{"f/doc.go": []byte("// Package f is fine.\npackage f\n"), + "f/f_test.go": []byte("// Package f_test is external.\npackage f_test\n")} + if p := docProblems(tests); len(p) > 0 { + t.Errorf("a test file's package comment was refused: %v", p) + } +} From 866decb2f2e65f3d3154d364ee32a61261c63854 Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:18:39 -0400 Subject: [PATCH 5/6] Review notes: move the reviews to docs/reviews/ and the controls ledger to docs/controls.md A package folder should read as the package. The nine adversarial review reports move out of internal/** into docs/reviews/, named after what they review, and the controls ledger moves from internal/SKIPPED-CONTROLS.md to docs/controls.md. Every citation in code, tests, CI and the fingerprint specification now uses the new path; the containment test that resolves ledger entry IDs reads docs/controls.md and still resolves all 15 pointers. CONTRACT.md and FINGERPRINT-SPEC.md stay beside their code: they are specifications, not history. --- .github/workflows/ci.yml | 4 ++-- .../SKIPPED-CONTROLS.md => docs/controls.md | 0 .../reviews/accelerator.md | 0 .../reviews/comparator.md | 2 +- .../reviews/contract-and-fingerprint.md | 0 .../reviews/controller-core.md | 0 .../reviews/emission.md | 0 .../reviews/queue-and-read-path.md | 0 .../reviews/read-only-boundary.md | 0 .../reviews/sanitizer.md | 0 .../reviews/sealing-claims-masking.md | 0 internal/collector/repo/trivy_test.go | 2 +- internal/dast/authz/egress_chokepoint_test.go | 2 +- internal/dast/authz/kernel.go | 2 +- internal/dast/authz/kernel_depgraph_test.go | 4 ++-- internal/dast/authz/kernel_test.go | 2 +- internal/dast/authz/phase0_build.go | 4 ++-- internal/dast/authz/phase0_build_test.go | 2 +- internal/dast/authz/phase1_run.go | 4 ++-- internal/dast/authz/phase1_run_test.go | 2 +- .../dast/authz/phase1_scopebytes_race_test.go | 2 +- internal/dast/authz/phase2_admission.go | 6 +++--- internal/dast/authz/phase3_enforcement.go | 2 +- internal/dast/authz/phase4_disclosure.go | 6 +++--- internal/dast/authz/phase4_disclosure_test.go | 2 +- internal/dast/authz/types.go | 4 ++-- internal/dast/containment/netns.go | 8 ++++---- internal/dast/containment/netns_test.go | 10 +++++----- internal/dast/containment/provision.go | 8 ++++---- internal/dast/containment/provision_test.go | 18 +++++++++--------- internal/dast/containment/reset.go | 4 ++-- internal/dast/containment/reset_test.go | 2 +- internal/dast/engines/nuclei.go | 6 +++--- internal/dast/engines/nuclei_test.go | 8 ++++---- internal/dast/engines/zap.go | 10 +++++----- internal/dast/engines/zap_test.go | 12 ++++++------ internal/dast/inventory/auth_helper.go | 6 +++--- internal/dast/inventory/auth_helper_test.go | 2 +- internal/dast/inventory/tier0_runtime.go | 4 ++-- internal/dast/inventory/tier0_runtime_test.go | 10 +++++----- internal/dast/inventory/tier1_repospec.go | 2 +- internal/dast/inventory/tier1_repospec_test.go | 6 +++--- internal/dast/inventory/tier2_go_extract.go | 2 +- .../dast/inventory/tier2_go_extract_test.go | 2 +- internal/dast/inventory/tier3_crawl.go | 10 +++++----- internal/dast/inventory/tier3_crawl_test.go | 4 ++-- internal/ingest/cache/cache_test.go | 2 +- internal/ingest/invisible/invisible_test.go | 2 +- internal/ingest/sanitize/sanitize_test.go | 2 +- .../mirror/accelerator/accelerator_test.go | 2 +- internal/record/FINGERPRINT-SPEC.md | 2 +- internal/record/fingerprint.go | 2 +- .../record/fingerprint_conformance_test.go | 2 +- internal/record/fingerprint_spec_test.go | 2 +- internal/record/sarif_github.go | 2 +- test/hygiene/plan_codes_test.go | 2 +- 56 files changed, 103 insertions(+), 103 deletions(-) rename internal/SKIPPED-CONTROLS.md => docs/controls.md (100%) rename internal/mirror/accelerator/REVIEW-accelerator.md => docs/reviews/accelerator.md (100%) rename internal/match/REVIEW-comparator.md => docs/reviews/comparator.md (99%) rename internal/record/REVIEW-contract-and-fingerprint.md => docs/reviews/contract-and-fingerprint.md (100%) rename internal/scanctl/REVIEW-controller-core.md => docs/reviews/controller-core.md (100%) rename internal/record/lanea/REVIEW-emission.md => docs/reviews/emission.md (100%) rename internal/record/REVIEW-queue-and-read-path.md => docs/reviews/queue-and-read-path.md (100%) rename internal/collector/host/REVIEW-read-only-boundary.md => docs/reviews/read-only-boundary.md (100%) rename internal/ingest/sanitize/REVIEW-sanitizer.md => docs/reviews/sanitizer.md (100%) rename internal/handoff/REVIEW-sealing-claims-masking.md => docs/reviews/sealing-claims-masking.md (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61feecc..a3d6991 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: # detector reports it against the FIX on a correct tree. What was not # legitimate is that this workflow ran only the -race lane, so # TestScopeBytesAreReadOnceUnderAConcurrentWriter -- the behavioural - # control whose measured numbers are in internal/SKIPPED-CONTROLS.md + # control whose measured numbers are in docs/controls.md # under G4-1 (0 of 200 constructed Scopes mismatched on the shipped tree, # 89 of 200 with the copy deleted, 102 of 200 with the copy replaced by # `rawAlias := raw[:]`) -- was compiled in ZERO CI lanes. Recorded as N3; @@ -318,7 +318,7 @@ jobs: # there" are indistinguishable to a caller, and for a security scanner that is # the worst possible failure -- an empty result reads as a clean repository. # - # internal/SKIPPED-CONTROLS.md item N1 recorded this as OPEN: the test was + # docs/controls.md item N1 recorded this as OPEN: the test was # correctly made fatal-when-opted-in rather than skip-when-opted-in, which # made it honest, but no machine anywhere set the opt-in, so the control was # proven on NOTHING. This job is what closes it. If it is ever removed, that diff --git a/internal/SKIPPED-CONTROLS.md b/docs/controls.md similarity index 100% rename from internal/SKIPPED-CONTROLS.md rename to docs/controls.md diff --git a/internal/mirror/accelerator/REVIEW-accelerator.md b/docs/reviews/accelerator.md similarity index 100% rename from internal/mirror/accelerator/REVIEW-accelerator.md rename to docs/reviews/accelerator.md diff --git a/internal/match/REVIEW-comparator.md b/docs/reviews/comparator.md similarity index 99% rename from internal/match/REVIEW-comparator.md rename to docs/reviews/comparator.md index 4820c95..354196e 100644 --- a/internal/match/REVIEW-comparator.md +++ b/docs/reviews/comparator.md @@ -423,7 +423,7 @@ the package's own guards: returns nothing (`TestNoSourceFileReachesForAClockOrARandomSource`, re-run and independently reproduced). - `grep -rn "t.Skip" internal/match/` — **none**. No new entry is owed to - `internal/SKIPPED-CONTROLS.md`. + `docs/controls.md`. - Both of the package's own guards (G1 import allowlist, G5 dependency graph) carry working RED controls, and G5's negative control genuinely observes `modernc.org/…` under `internal/ingest/cache`. These are real guards, not decorative ones. diff --git a/internal/record/REVIEW-contract-and-fingerprint.md b/docs/reviews/contract-and-fingerprint.md similarity index 100% rename from internal/record/REVIEW-contract-and-fingerprint.md rename to docs/reviews/contract-and-fingerprint.md diff --git a/internal/scanctl/REVIEW-controller-core.md b/docs/reviews/controller-core.md similarity index 100% rename from internal/scanctl/REVIEW-controller-core.md rename to docs/reviews/controller-core.md diff --git a/internal/record/lanea/REVIEW-emission.md b/docs/reviews/emission.md similarity index 100% rename from internal/record/lanea/REVIEW-emission.md rename to docs/reviews/emission.md diff --git a/internal/record/REVIEW-queue-and-read-path.md b/docs/reviews/queue-and-read-path.md similarity index 100% rename from internal/record/REVIEW-queue-and-read-path.md rename to docs/reviews/queue-and-read-path.md diff --git a/internal/collector/host/REVIEW-read-only-boundary.md b/docs/reviews/read-only-boundary.md similarity index 100% rename from internal/collector/host/REVIEW-read-only-boundary.md rename to docs/reviews/read-only-boundary.md diff --git a/internal/ingest/sanitize/REVIEW-sanitizer.md b/docs/reviews/sanitizer.md similarity index 100% rename from internal/ingest/sanitize/REVIEW-sanitizer.md rename to docs/reviews/sanitizer.md diff --git a/internal/handoff/REVIEW-sealing-claims-masking.md b/docs/reviews/sealing-claims-masking.md similarity index 100% rename from internal/handoff/REVIEW-sealing-claims-masking.md rename to docs/reviews/sealing-claims-masking.md diff --git a/internal/collector/repo/trivy_test.go b/internal/collector/repo/trivy_test.go index bd24ade..ffa0767 100644 --- a/internal/collector/repo/trivy_test.go +++ b/internal/collector/repo/trivy_test.go @@ -1011,7 +1011,7 @@ func TestRealTrivyScansAFixtureRepo(t *testing.T) { // no binary .............. somebody DID ask, and the install step that was // supposed to provide it did not. This used to // skip, which is the pattern internal/ - // SKIPPED-CONTROLS.md exists for: the one test + // docs/controls.md exists for: the one test // that proves a real scanner does not report a // vulnerable fixture clean would have reported // SUCCESS in exactly the run that was configured diff --git a/internal/dast/authz/egress_chokepoint_test.go b/internal/dast/authz/egress_chokepoint_test.go index cd892b7..a8f26e7 100644 --- a/internal/dast/authz/egress_chokepoint_test.go +++ b/internal/dast/authz/egress_chokepoint_test.go @@ -430,7 +430,7 @@ func scanRepoForEgress(t *testing.T, root string) EgressScan { // repoRootForGuards returns the module root, or fails. // -// It does not skip when it cannot find the root. internal/SKIPPED-CONTROLS.md +// It does not skip when it cannot find the root. docs/controls.md // records this repository shipping two guards that vanished silently when they // could not run; a build-time invariant that cannot locate the tree it guards // has not passed, it has not run. diff --git a/internal/dast/authz/kernel.go b/internal/dast/authz/kernel.go index 9c3c6da..e4d702c 100644 --- a/internal/dast/authz/kernel.go +++ b/internal/dast/authz/kernel.go @@ -47,7 +47,7 @@ // chain REFUSES at that gate. It does not skip it, log a warning, or treat the // absence as a pass. // -// That is not defensiveness for its own sake. internal/SKIPPED-CONTROLS.md +// That is not defensiveness for its own sake. docs/controls.md // records two separate incidents in this repository where a control that could // not run reported success, and names the shape precisely: "a guard that // vanishes silently when it cannot run is worse than no guard, because the diff --git a/internal/dast/authz/kernel_depgraph_test.go b/internal/dast/authz/kernel_depgraph_test.go index d57780b..531cd27 100644 --- a/internal/dast/authz/kernel_depgraph_test.go +++ b/internal/dast/authz/kernel_depgraph_test.go @@ -55,7 +55,7 @@ // half it PROVED and which it could not, and which becomes a real two-sided // measurement the moment cmd/anvil-dast links the kernel. It is not fixed by // fabricating a consumer: a consumer invented to make a gate green is the -// exact shape internal/SKIPPED-CONTROLS.md records this repository shipping +// exact shape docs/controls.md records this repository shipping // twice. package authz @@ -73,7 +73,7 @@ import ( // records it as an ImportGraph. // // It fails rather than skips when the toolchain is unavailable. -// internal/SKIPPED-CONTROLS.md names the failure this avoids: a dependency +// docs/controls.md names the failure this avoids: a dependency // shape guard in this repository skipped whenever `go list` could not run, // "i.e. in exactly the hermetic environments it mattered in". // diff --git a/internal/dast/authz/kernel_test.go b/internal/dast/authz/kernel_test.go index cdca7c9..4e4495b 100644 --- a/internal/dast/authz/kernel_test.go +++ b/internal/dast/authz/kernel_test.go @@ -412,7 +412,7 @@ func TestChainRefusesEveryMissingGateIndividually(t *testing.T) { if r.Permits() { t.Fatalf("the chain permitted with %s missing. A gate with no "+ "implementation compiled in is a REFUSAL, never a skipped step: "+ - "internal/SKIPPED-CONTROLS.md records two incidents in this "+ + "docs/controls.md records two incidents in this "+ "repository where a control that could not run reported success", missing) } diff --git a/internal/dast/authz/phase0_build.go b/internal/dast/authz/phase0_build.go index 707a2d6..eab760e 100644 --- a/internal/dast/authz/phase0_build.go +++ b/internal/dast/authz/phase0_build.go @@ -21,7 +21,7 @@ // THE FACT TYPES REFUSE TO BE FABRICATED, AND REFUSE TO BE EMPTY // =========================================================================== // -// internal/SKIPPED-CONTROLS.md opens with two incidents in this repository +// docs/controls.md opens with two incidents in this repository // where a control that could not run reported success — a symlink test that // skipped on Windows while a directory junction walked through the guard it // protected, and a dependency-shape guard that skipped whenever `go list` @@ -372,7 +372,7 @@ func isStdlibPackage(p string) bool { // // That is a vacuous positive, and it is written down rather than papered over. // The gate is not fabricated a consumer to satisfy it — a consumer invented to -// make a gate green is the exact shape internal/SKIPPED-CONTROLS.md records +// make a gate green is the exact shape docs/controls.md records // this repository shipping twice. The kernel's build-time guard owns the real // positive control: once cmd/anvil-dast links the kernel, gate 1 needs BOTH // graphs — anvil-dast MUST contain internal/dast/authz and anvil MUST NOT — so diff --git a/internal/dast/authz/phase0_build_test.go b/internal/dast/authz/phase0_build_test.go index aa158a4..280e047 100644 --- a/internal/dast/authz/phase0_build_test.go +++ b/internal/dast/authz/phase0_build_test.go @@ -70,7 +70,7 @@ func mustScan(t *testing.T, files int, sites ...EgressCallSite) EgressScan { // THE FACT TYPES REFUSE AN UNMEASURED MEASUREMENT // =========================================================================== -// TestUnmeasuredFactsAreRefused is the guard internal/SKIPPED-CONTROLS.md was +// TestUnmeasuredFactsAreRefused is the guard docs/controls.md was // written about: "a guard that vanishes silently when it cannot run is worse // than no guard, because the green tick is read as an answer." func TestUnmeasuredFactsAreRefused(t *testing.T) { diff --git a/internal/dast/authz/phase1_run.go b/internal/dast/authz/phase1_run.go index 6a5e9a5..bc14aab 100644 --- a/internal/dast/authz/phase1_run.go +++ b/internal/dast/authz/phase1_run.go @@ -66,7 +66,7 @@ // behaviour depend on test execution order. // 3. Register a gate 7 that permits whenever the four inputs are well formed. // That is a gate that has never refused anything — the exact shape -// internal/SKIPPED-CONTROLS.md records this repository shipping twice, and +// docs/controls.md records this repository shipping twice, and // the reason the engineering standard "a guard that has never failed has // not been tested" exists. // @@ -524,7 +524,7 @@ func LoadScopeFile(path string, decl ModeDeclaration) (Scope, GateResult) { // it differ from another, so the harness is a data race by construction, and // `go test -race` reports it against THIS FILE'S COPY on a correct build. It // therefore cannot run in the -race lane without failing a correct tree. -// Recorded in internal/SKIPPED-CONTROLS.md as G4-1, with the literal race +// Recorded in docs/controls.md as G4-1, with the literal race // report. func CheckGate4ScopeFile(raw []byte, decl ModeDeclaration) (Scope, GateResult) { // ONE guard, not two. An earlier draft checked decl.Declared() first and diff --git a/internal/dast/authz/phase1_run_test.go b/internal/dast/authz/phase1_run_test.go index 9d6b0d0..414e285 100644 --- a/internal/dast/authz/phase1_run_test.go +++ b/internal/dast/authz/phase1_run_test.go @@ -2381,7 +2381,7 @@ func TestGate7CannotBeRegisteredAsAGateFunc(t *testing.T) { t.Fatal("registerInto accepted a gate 7 implementation. A vacuous gate 7 — one " + "that permits whenever the four inputs are well formed — is a gate that " + "has never refused anything, which is the exact shape " + - "internal/SKIPPED-CONTROLS.md records this repository shipping twice") + "docs/controls.md records this repository shipping twice") } if !errors.Is(err, ErrRefused) { t.Fatalf("the refusal does not unwrap to ErrRefused: %v", err) diff --git a/internal/dast/authz/phase1_scopebytes_race_test.go b/internal/dast/authz/phase1_scopebytes_race_test.go index 133d734..11845ee 100644 --- a/internal/dast/authz/phase1_scopebytes_race_test.go +++ b/internal/dast/authz/phase1_scopebytes_race_test.go @@ -35,7 +35,7 @@ // So it runs in the ordinary `go test ./...` lane, where it is a real control // with real numbers, and TestGate4ReadsTheCallersScopeBytesExactlyOnce — a // source-level assertion, no goroutines — is what runs in every lane. The -// exclusion is recorded in internal/SKIPPED-CONTROLS.md as G4-1 rather than +// exclusion is recorded in docs/controls.md as G4-1 rather than // hidden behind a t.Skip. // // =========================================================================== diff --git a/internal/dast/authz/phase2_admission.go b/internal/dast/authz/phase2_admission.go index d592d8e..70eba2b 100644 --- a/internal/dast/authz/phase2_admission.go +++ b/internal/dast/authz/phase2_admission.go @@ -49,7 +49,7 @@ // is a safety-section violation wearing a hat. // - Registering a gate 11 that permits whenever the four inputs are well // formed would be a gate that has never refused anything — the exact shape -// internal/SKIPPED-CONTROLS.md records this repository shipping twice. +// docs/controls.md records this repository shipping twice. // // GATE 11 IS ENFORCED IN TWO PLACES INSTEAD, and neither is a chain position. // @@ -1305,7 +1305,7 @@ func gate10ReservedRanges(target Target, scope Scope, _ Attestation, _ Clock) Ru // // The distinction between "we looked and there was none" and "we did not // look" is the entire reason this type exists rather than a bool. -// internal/SKIPPED-CONTROLS.md names the shape: "a guard that vanishes +// docs/controls.md names the shape: "a guard that vanishes // silently when it cannot run is worse than no guard, because the green tick is // read as an answer." A policy nobody determined permits no path. type RobotsDetermination string @@ -1678,7 +1678,7 @@ func CheckGate11RobotsDeny(policy RobotsPolicy, target Target, path string) Gate return gateFailed(g, ReasonRobotsNotDetermined, "no robots.txt determination was made for this origin. \"We did not look\" and "+ "\"we looked and there was nothing\" produce the same silence and opposite "+ - "conclusions; internal/SKIPPED-CONTROLS.md records two incidents in this "+ + "conclusions; docs/controls.md records two incidents in this "+ "repository where the first was read as the second.") } if policy.Determination() == RobotsUnavailable { diff --git a/internal/dast/authz/phase3_enforcement.go b/internal/dast/authz/phase3_enforcement.go index 6e4b697..cbe6413 100644 --- a/internal/dast/authz/phase3_enforcement.go +++ b/internal/dast/authz/phase3_enforcement.go @@ -1867,7 +1867,7 @@ func CheckGate16CircuitBreaker(m *HealthMonitor) GateResult { if !m.Constructed() { return gateFailed(g, ReasonMonitorUnconstructed, "there is no health monitor for this target. A target nobody is monitoring is "+ - "not a target known to be healthy — internal/SKIPPED-CONTROLS.md records "+ + "not a target known to be healthy — docs/controls.md records "+ "two incidents in this repository where \"we did not look\" was read as "+ "\"we looked and it was fine\".") } diff --git a/internal/dast/authz/phase4_disclosure.go b/internal/dast/authz/phase4_disclosure.go index b801fb5..85d8313 100644 --- a/internal/dast/authz/phase4_disclosure.go +++ b/internal/dast/authz/phase4_disclosure.go @@ -99,7 +99,7 @@ // accepts a "now", so the one instant the caller chose is the one every // comparison in the run is made against. A lie is still spellable; it is no // longer spellable INCONSISTENTLY, and the cost of telling it consistently is -// in this file's closing section and in SKIPPED-CONTROLS G18-2. +// in this file's closing section and in docs/controls.md G18-2. // // types.go's RunClock is sealed at run initiation by an unexported // constructor, exactly as Scope and Attestation are, and @@ -138,7 +138,7 @@ // runs are keyed to different attestation IDs. That is a materially larger and // more visible act than passing a different time.Time to the next call, and it // is as far as a kernel with no trusted clock can go. Recorded in -// internal/SKIPPED-CONTROLS.md as G18-2. +// docs/controls.md as G18-2. package authz @@ -1582,7 +1582,7 @@ func (r DisclosureRecord) ScopeHash() ScopeHash { return r.key.ScopeHash() } // handoff buffer, and in this tree disclosure state lives NOWHERE. Every // refusal in this section is real and tested; the ALLOW has never been taken // by a caller that actually persisted anything. Recorded in -// internal/SKIPPED-CONTROLS.md as G19-1, with what would settle it. +// docs/controls.md as G19-1, with what would settle it. // // # The residual the declaration leaves even once one exists // diff --git a/internal/dast/authz/phase4_disclosure_test.go b/internal/dast/authz/phase4_disclosure_test.go index d3c87e1..c56f5da 100644 --- a/internal/dast/authz/phase4_disclosure_test.go +++ b/internal/dast/authz/phase4_disclosure_test.go @@ -3111,7 +3111,7 @@ func TestPhase4EmbargoConstantsPinTheRelationsNotOnlyTheValues(t *testing.T) { // NOT a constant-interaction defect and is deliberately not asserted // here: an embargo outliving the attestation that authorised the scan // is expected, and it is what makes the two-run divergent-clock - // residual visible in the audit log at all. See SKIPPED-CONTROLS + // residual visible in the audit log at all. See docs/controls.md // G18-2. } diff --git a/internal/dast/authz/types.go b/internal/dast/authz/types.go index a1d9471..d3662b2 100644 --- a/internal/dast/authz/types.go +++ b/internal/dast/authz/types.go @@ -76,7 +76,7 @@ var ( ErrCapRaise = fmt.Errorf("%w: configuration may only LOWER a coded cap, never raise it", ErrRefused) // ErrNotMeasured is the Phase 0 refusal: a build-time gate was handed - // facts that were never actually measured. internal/SKIPPED-CONTROLS.md + // facts that were never actually measured. docs/controls.md // records two separate incidents in this repository where a guard that // could not run reported success; a gate that cannot measure must fail, // not pass. @@ -1186,7 +1186,7 @@ func (s Scope) removePaths(host string, port uint16, policy RobotsPolicy, rec Sc // - PermitsPath implies Permits. Everything Permits refuses, this refuses. // - A SCOPE THAT WAS NEVER NARROWED PERMITS NO PATH. "Nobody determined this // origin's robots.txt" and "robots.txt permitted this path" produce the -// same silence and opposite conclusions, and internal/SKIPPED-CONTROLS.md +// same silence and opposite conclusions, and docs/controls.md // records two incidents in this repository where the first was read as the // second. So a path needs a determination that COVERS ITS ORIGIN before it // can be permitted here, and the zero Scope permits nothing at all. diff --git a/internal/dast/containment/netns.go b/internal/dast/containment/netns.go index 0d722a0..2494eb6 100644 --- a/internal/dast/containment/netns.go +++ b/internal/dast/containment/netns.go @@ -80,7 +80,7 @@ // // SystemCommander REFUSES on any non-Linux GOOS -- it does not return a // no-op that would let AssertContainment return nil. There is no t.Skip in -// this package. See internal/SKIPPED-CONTROLS.md entry U1 for what remains +// this package. See docs/controls.md entry U1 for what remains // unexecuted and exactly what would settle it. // // # Why this package holds no socket @@ -187,7 +187,7 @@ const ChainName = "egress" // WHAT THIS STILL DOES NOT PROVE: the canary runs under `ip netns exec`, so it // holds a socket IN the namespace and its own dials traverse `output`, not // `forward`. It therefore exercises the chain the TARGET DOES NOT USE. Recorded -// in internal/SKIPPED-CONTROLS.md as U1a, with what would settle it. +// in docs/controls.md as U1a, with what would settle it. // // WHY THERE IS NO `input` CHAIN, stated rather than left as an omission. The // property asserted here is what the target can REACH. `input` is inbound: the @@ -982,7 +982,7 @@ func splitAllowEntries(m *target.Manifest) ([]netip.Prefix, []string, error) { // package's own tests. So booted_clean today means "the container is contained // by gVisor" and does NOT mean "its egress is default-deny". Wiring that is the // integration packet's, not this one's, and it is recorded in -// internal/SKIPPED-CONTROLS.md as U1c so it cannot be forgotten -- including +// docs/controls.md as U1c so it cannot be forgotten -- including // the containment review's criterion that AssertContainment must run BEFORE any probe engine // starts. // @@ -1110,7 +1110,7 @@ const CanarySubcommand = "__anvil-dast-netns-canary" // The canary remains the UNTRUSTED half and this package cannot authenticate // it: a substituted binary can write any number it likes. What the check buys // is that the ordinary way this control rots -- an honest probe with too short -// a dialer -- now fails loudly. The rest is internal/SKIPPED-CONTROLS.md U1b. +// a dialer -- now fails loudly. The rest is docs/controls.md U1b. const DefaultCanaryDialTimeout = 2 * time.Second // canaryTimingTolerance is how much short of DefaultCanaryDialTimeout a diff --git a/internal/dast/containment/netns_test.go b/internal/dast/containment/netns_test.go index b24e734..4c1cce2 100644 --- a/internal/dast/containment/netns_test.go +++ b/internal/dast/containment/netns_test.go @@ -30,7 +30,7 @@ import ( // kernel drops the packet, or that a real canary in a real namespace reports // what this suite's fake canaries report. // -// That gap is written down, not implied: internal/SKIPPED-CONTROLS.md entry U1 +// That gap is written down, not implied: docs/controls.md entry U1 // names it and names exactly what would settle it. // // There is NO t.Skip in this file. Every test asserts something on every @@ -1595,7 +1595,7 @@ func TestAssertContainmentRefusesANilCommanderAndAnEmptyCanaryPath(t *testing.T) // This test is the clause after the semicolon, and it is met. THE CLAUSE // BEFORE IT IS NOT MET AND CANNOT BE MET ON THIS HOST: there is no real target // fixture, no kernel, no nftables, and nothing here installs a ruleset -// anywhere. internal/SKIPPED-CONTROLS.md entry U1 is the standing record of +// anywhere. docs/controls.md entry U1 is the standing record of // that half, and a privileged Linux CI lane is what would settle it. // // The distinction is not pedantry. An exit criterion recorded as met when half @@ -1763,7 +1763,7 @@ func (p recordingProbe) Attempt(_ context.Context, network, address string) (boo // evidence -- is that a Linux kernel delivers the errno a ConnectProbe would // turn into each of these names. That mapping lives with the implementation, // which holds the socket; nothing on a Windows host can prove it. See -// internal/SKIPPED-CONTROLS.md U1. +// docs/controls.md U1. func TestClassifyDialFailureTable(t *testing.T) { cases := []struct { f DialFailure @@ -1958,7 +1958,7 @@ func TestTimeoutIsBoundedAndNonZero(t *testing.T) { // that cannot run on the development host, which is exactly the condition // under which a skip is reached for -- and a skipped containment assertion // lets the package print `ok` while proving nothing about the sandbox. The gap -// goes in internal/SKIPPED-CONTROLS.md (entry U1) instead, where it is read +// goes in docs/controls.md (entry U1) instead, where it is read // rather than passed over. func TestThisFileSkipsNothing(t *testing.T) { src, err := parser.ParseFile(token.NewFileSet(), "netns_test.go", nil, parser.SkipObjectResolution) @@ -1980,7 +1980,7 @@ func TestThisFileSkipsNothing(t *testing.T) { } if strings.HasPrefix(sel.Sel.Name, tok[2:]) { t.Errorf("netns_test.go calls t.%s. If a control genuinely cannot run here, it "+ - "belongs in internal/SKIPPED-CONTROLS.md with what would settle it -- not "+ + "belongs in docs/controls.md with what would settle it -- not "+ "behind a green tick", sel.Sel.Name) } return true diff --git a/internal/dast/containment/provision.go b/internal/dast/containment/provision.go index 89309b0..a1f7272 100644 --- a/internal/dast/containment/provision.go +++ b/internal/dast/containment/provision.go @@ -35,7 +35,7 @@ // // Docker is not installed on every host that builds Anvil -- it is not // installed on the host this packet was written on, which is recorded in -// internal/SKIPPED-CONTROLS.md as entry U2, along with the three things that +// docs/controls.md as entry U2, along with the three things that // would settle it in order of decreasing cost. The Docker interface below is // the ONE boundary between // this logic and the container runtime, so the decision logic -- the @@ -171,7 +171,7 @@ const ( // NOTHING ENFORCES THE SPLIT AT THE PHASE BOUNDARY, because the boundary // is inside `docker compose up` and no implementation of the Docker seam // exists. The two budgets are a contract stated in that interface's doc, - // and internal/SKIPPED-CONTROLS.md U2a records that nobody has honoured + // and docs/controls.md U2a records that nobody has honoured // it yet, along with the fixture that would settle it. // // It is a compiled-in constant and not a manifest field on purpose: @@ -576,7 +576,7 @@ func (e *ProvisionError) Provisioning() (record.TargetProvisioning, error) { // // Nothing in this package shells out, opens a socket, or imports a client. A // real implementation of this interface is a separate concern and is NOT -// provided here -- see internal/SKIPPED-CONTROLS.md entry U2 for what remains +// provided here -- see docs/controls.md entry U2 for what remains // unproven without one and exactly what would settle it. // // THE CONTRACT AN IMPLEMENTATION OWES, stated here because the compiler cannot @@ -1060,7 +1060,7 @@ func NewProvisioner(d Docker, repoRoot string) (*Provisioner, error) { // SetupNetns and AssertContainment are separate exported functions and nothing // in the tree calls either. A caller that fires probes on the strength of // booted_clean alone is asserting a network containment nobody established. -// Recorded in internal/SKIPPED-CONTROLS.md as U1c; wiring it belongs to the +// Recorded in docs/controls.md as U1c; wiring it belongs to the // integration packet. // // The order below is the contract, and it is ordered so that nothing starts diff --git a/internal/dast/containment/provision_test.go b/internal/dast/containment/provision_test.go index 15eb7d9..8d4a837 100644 --- a/internal/dast/containment/provision_test.go +++ b/internal/dast/containment/provision_test.go @@ -25,7 +25,7 @@ import ( // Every test in this file drives provisioning through RECORDED SHAPES of what // `docker info`, `docker compose up` and `docker inspect` return. Docker is // not installed on the host this packet was written on -- verified, and -// recorded in internal/SKIPPED-CONTROLS.md as entry U2 with what would settle +// recorded in docs/controls.md as entry U2 with what would settle // it -- so the decision logic is proven here in full and the fidelity of a // real implementation's shapes is not. // @@ -1908,7 +1908,7 @@ func TestTheSealedSnapshotIsTakenByValue(t *testing.T) { // --------------------------------------------------------------------------- // TestEverySkippedControlsPointerResolves: this package defers its unproven -// half to internal/SKIPPED-CONTROLS.md by ENTRY ID, in doc comments a reader is +// half to docs/controls.md by ENTRY ID, in doc comments a reader is // expected to follow. Three of those pointers named an id numbered after the old // plan step -- one per line of provisioning's doc -- that was never filed under // that name; the entry exists as U2. So the one document holding @@ -1917,7 +1917,7 @@ func TestTheSealedSnapshotIsTakenByValue(t *testing.T) { // A pointer nobody follows is a pointer nobody notices is broken, so this // resolves every one of them against the document's actual headings. func TestEverySkippedControlsPointerResolves(t *testing.T) { - const doc = "../../SKIPPED-CONTROLS.md" + const doc = "../../../docs/controls.md" body, err := os.ReadFile(doc) if err != nil { t.Fatalf("this package's doc comments defer to %s and it cannot be read: %v", doc, err) @@ -1932,7 +1932,7 @@ func TestEverySkippedControlsPointerResolves(t *testing.T) { t.Fatalf("no entry headings were found in %s, so every assertion below is vacuous", doc) } - // The id shapes SKIPPED-CONTROLS.md actually uses, as an ALLOWLIST of + // The id shapes docs/controls.md actually uses, as an ALLOWLIST of // prefixes rather than a denylist of everything else a nearby sentence // might contain. Other letter-and-number shapes are deliberately not in it. idIn := regexp.MustCompile(`\b([DUNGHL][0-9]+(?:-[0-9]+)?[a-z]?)\b`) @@ -1947,7 +1947,7 @@ func TestEverySkippedControlsPointerResolves(t *testing.T) { t.Fatalf("reading %s: %v", f, err) } text := string(src) - const marker = "SKIPPED-CONTROLS.md" + const marker = "docs/controls.md" for i := 0; ; { j := strings.Index(text[i:], marker) if j < 0 { @@ -1971,11 +1971,11 @@ func TestEverySkippedControlsPointerResolves(t *testing.T) { } } if checked == 0 { - t.Fatal("no SKIPPED-CONTROLS.md entry pointers were found in this package's sources. " + + t.Fatal("no docs/controls.md entry pointers were found in this package's sources. " + "Either the deferral comments were deleted -- in which case the unproven half is now " + "undocumented -- or this test's pattern no longer matches them and it is a no-op") } - t.Logf("resolved %d SKIPPED-CONTROLS.md entry pointers against %d headings", checked, len(headings)) + t.Logf("resolved %d docs/controls.md entry pointers against %d headings", checked, len(headings)) } // TestNothingInThisPackageReadsConfiguration: plan/design/dynamic-tier.md forbids a @@ -1997,7 +1997,7 @@ func TestNothingInThisPackageReadsConfiguration(t *testing.T) { } // TestThisPackageSkipsNothing: a skipped test still lets the package print ok. -// internal/SKIPPED-CONTROLS.md exists because that has already cost this +// docs/controls.md exists because that has already cost this // repository twice. func TestThisPackageSkipsNothing(t *testing.T) { src, err := os.ReadFile("provision_test.go") @@ -2011,7 +2011,7 @@ func TestThisPackageSkipsNothing(t *testing.T) { for _, forbidden := range []string{tok + "(", tok + "f(", tok + "Now("} { if strings.Contains(string(src), forbidden) { t.Errorf("provision_test.go contains %q. If a control genuinely cannot run "+ - "here, it belongs in internal/SKIPPED-CONTROLS.md with what would "+ + "here, it belongs in docs/controls.md with what would "+ "settle it -- not behind a green tick", forbidden) } } diff --git a/internal/dast/containment/reset.go b/internal/dast/containment/reset.go index 3a19d30..73995b3 100644 --- a/internal/dast/containment/reset.go +++ b/internal/dast/containment/reset.go @@ -66,7 +66,7 @@ // a destroy-and-recreate discards the seed's effects and nothing in this // tree can replay them. Returning an unseeded target and calling it "the // declared initial state" is the silent lie this packet is about. Named, -// with what would settle it, in internal/SKIPPED-CONTROLS.md U3. +// with what would settle it, in docs/controls.md U3. // // # A failed reset stops the run for that target, structurally // @@ -549,7 +549,7 @@ func NewResetter(p *Provisioner, m *target.Manifest) (*Resetter, error) { "destroy-and-recreate discards its effects and nothing in this tree can "+ "replay it, so the target this would hand back is NOT the declared initial "+ "state -- it is an unseeded one. Refusing loudly beats returning it and "+ - "calling the next probe independent. internal/SKIPPED-CONTROLS.md U3 names "+ + "calling the next probe independent. docs/controls.md U3 names "+ "what would settle this", ErrRefused, ErrResetSeedNotReplayable, m.Seed.Command) } diff --git a/internal/dast/containment/reset_test.go b/internal/dast/containment/reset_test.go index 7e444d4..6826b50 100644 --- a/internal/dast/containment/reset_test.go +++ b/internal/dast/containment/reset_test.go @@ -1392,7 +1392,7 @@ func TestResetFileSkipsNothing(t *testing.T) { for _, forbidden := range []string{tok + "(", tok + "f(", tok + "Now("} { if strings.Contains(string(src), forbidden) { t.Errorf("reset_test.go contains %q. If a control genuinely cannot run here "+ - "it belongs in internal/SKIPPED-CONTROLS.md with what would settle it -- "+ + "it belongs in docs/controls.md with what would settle it -- "+ "not behind a green tick", forbidden) } } diff --git a/internal/dast/engines/nuclei.go b/internal/dast/engines/nuclei.go index 28c9330..9732e81 100644 --- a/internal/dast/engines/nuclei.go +++ b/internal/dast/engines/nuclei.go @@ -60,7 +60,7 @@ // available failure, so they are separated here by type, by exit code, and by // a coverage predicate the caller must consult before reporting clean. // -// internal/SKIPPED-CONTROLS.md entry U4 records exactly what remains +// docs/controls.md entry U4 records exactly what remains // unexecuted and what would settle it. There is no t.Skip in this package. // // # What this driver assumes about template provenance: NOTHING @@ -446,7 +446,7 @@ func (e *EngineUnavailableError) ExitCode() int { return ExitCodeArtefactAbsent // does not control, and an engine that re-resolves reopens DNS rebinding. // // That obligation is STATED HERE AND ENFORCED NOWHERE IN THIS FILE. It is a -// contract on the implementer, recorded in internal/SKIPPED-CONTROLS.md U4 as +// contract on the implementer, recorded in docs/controls.md U4 as // the specific thing an integration lane must prove. type Engine interface { // ExecuteCallbackWithCtx runs the plan and calls cb once per result. @@ -1019,7 +1019,7 @@ func LoadTemplates(dir string) ([]Template, []RejectedTemplate, error) { // — os.ModeIrregular, with the symlink bit CLEAR and IsDir false. A // `d.Type()&os.ModeSymlink != 0` check sees nothing there. That is // the same primitive that walked through internal/mirror/accelerator's - // quarantine guard (internal/SKIPPED-CONTROLS.md H1), and it is why + // quarantine guard (docs/controls.md H1), and it is why // the question here is "is this one of the two shapes a template tree // is made of" rather than "is this one of the link kinds I have heard // of". diff --git a/internal/dast/engines/nuclei_test.go b/internal/dast/engines/nuclei_test.go index 299003f..bc3883b 100644 --- a/internal/dast/engines/nuclei_test.go +++ b/internal/dast/engines/nuclei_test.go @@ -33,7 +33,7 @@ // gate stack only ever shown to refuse could be refusing for the wrong reason, // so the positive control is what makes the negative ones mean anything. // -// internal/SKIPPED-CONTROLS.md entry U4 is the standing record of what is +// docs/controls.md entry U4 is the standing record of what is // still open: the engine, which is (a) and is about this host. // // =========================================================================== @@ -41,7 +41,7 @@ // =========================================================================== // // TestThisFileSkipsNothing reads this file's own syntax tree and fails if one -// appears. internal/SKIPPED-CONTROLS.md's opening section records two +// appears. docs/controls.md's opening section records two // separate occasions on which a skip retired a live security control behind a // green tick. package engines @@ -464,7 +464,7 @@ func TestTemplateDigestIsOverTheExactBytes(t *testing.T) { } } -// TestALinkInTheTemplateTreeIsRejected. internal/SKIPPED-CONTROLS.md H1 +// TestALinkInTheTemplateTreeIsRejected. docs/controls.md H1 // records this repository shipping a guard that a Windows DIRECTORY JUNCTION // walked straight through, because the test only ever created the privileged // kind of link. This one creates the strongest link the host permits and @@ -2415,7 +2415,7 @@ func TestScrubRemovesEveryHiddenClassAndCountsIt(t *testing.T) { // Structural guards over this package's own source // --------------------------------------------------------------------------- -// TestThisFileSkipsNothing. internal/SKIPPED-CONTROLS.md's opening section +// TestThisFileSkipsNothing. docs/controls.md's opening section // records two occasions on which a skip retired a live security control // behind a green tick. func TestThisFileSkipsNothing(t *testing.T) { diff --git a/internal/dast/engines/zap.go b/internal/dast/engines/zap.go index 7191aec..fe448c3 100644 --- a/internal/dast/engines/zap.go +++ b/internal/dast/engines/zap.go @@ -63,7 +63,7 @@ // - If ZAP is ever run WITHOUT the proxy, none of the above applies and // nothing in this file can make it apply. That is why the proxy is // required at construction rather than defaulted, and why -// internal/SKIPPED-CONTROLS.md U5 records that no run on this host has +// docs/controls.md U5 records that no run on this host has // ever demonstrated the proxied path end to end. // // # WHAT IS NOT ENFORCED — read this before relying on the hop bound @@ -170,7 +170,7 @@ // this host to run and no Docker to run `docker stats` against, so any figure // here would be fabricated — and a fabricated figure is worse than the // acknowledged gap, because tier-M sizing would then be documented against it. -// internal/SKIPPED-CONTROLS.md U5 records what measuring it takes. +// docs/controls.md U5 records what measuring it takes. package engines @@ -317,7 +317,7 @@ type ZapRunOutcome struct { // // Obligations 1 and 3 are STATED HERE AND ENFORCED NOWHERE IN THIS FILE. // They are contracts on the implementer, recorded in -// internal/SKIPPED-CONTROLS.md U5 as the specific things an integration lane +// docs/controls.md U5 as the specific things an integration lane // must prove. type ZapRunner interface { Autorun(ctx context.Context, inv ZapInvocation) (ZapRunOutcome, error) @@ -353,7 +353,7 @@ type ZapReportTemplate string // THESE TWO STRINGS ARE UNVERIFIED AGAINST AN INSTALLED ZAP. There is no ZAP // on this host to enumerate `zap.sh -cmd -autorun` report templates against, // so they are transcribed from the report add-on's documented template ids and -// recorded in internal/SKIPPED-CONTROLS.md U5 as a thing a real lane settles +// recorded in docs/controls.md U5 as a thing a real lane settles // in one command. What IS enforced here is that both are declared, both are // rendered into the plan, and a run that fails to write either one is a // refusal rather than a clean report. @@ -1656,7 +1656,7 @@ type ZapConfig struct { // // So there is no trigger check here and no field for one. WHERE THAT // SCHEDULED-ONLY RULE IS ENFORCED: nowhere in this package. That is stated -// rather than implied, and internal/SKIPPED-CONTROLS.md U5 records it as an +// rather than implied, and docs/controls.md U5 records it as an // unenforced contract with the name of the check that must exist elsewhere. type ZapDriver struct { mu sync.Mutex diff --git a/internal/dast/engines/zap_test.go b/internal/dast/engines/zap_test.go index bdc8a52..188b958 100644 --- a/internal/dast/engines/zap_test.go +++ b/internal/dast/engines/zap_test.go @@ -43,7 +43,7 @@ // and the tests that exercise the authorization boundary itself now use the // production route in both directions. // -// internal/SKIPPED-CONTROLS.md U5 records what remains unexecuted, including +// docs/controls.md U5 records what remains unexecuted, including // the one plan/design/dynamic-tier.md explicitly asked this step to answer — ZAP's JVM // memory footprint — which is NOT answered here, because there is no ZAP to // measure and a fabricated number would become tier-M sizing documentation. @@ -1798,7 +1798,7 @@ func TestNewZapDriverRefusesEveryUnauthorizedRoute(t *testing.T) { // The two tests here are the ones that could not be written: a ZapDriver built // by NewZapDriver from a real token, fired end to end, and the same driver // refusing a real kernel refusal without issuing anything. They are the ZAP -// counterparts of nuclei_test.go's items (2) and (3), and the SKIPPED-CONTROLS +// counterparts of nuclei_test.go's items (2) and (3), and the docs/controls.md // entry they close is U5(c), which recorded this blocker as shared with U4(b). // mustZapDriver assembles a ZapDriver against the real kernel: a real @@ -2166,7 +2166,7 @@ func TestTheAutomationPlanCarriesNoReferenceAWriterCouldReach(t *testing.T) { // reconcile with the ZAP driver's instruction rather than to discover it. // // WHERE THE SCHEDULED-ONLY RULE IS ENFORCED: nowhere in this package. -// internal/SKIPPED-CONTROLS.md U5 records it as an unenforced contract. +// docs/controls.md U5 records it as an unenforced contract. func TestThisDriverIsTriggerAgnosticByInstruction(t *testing.T) { v := reflect.TypeOf(ZapConfig{}) for i := 0; i < v.NumField(); i++ { @@ -2175,7 +2175,7 @@ func TestThisDriverIsTriggerAgnosticByInstruction(t *testing.T) { t.Fatalf("ZapConfig.%s exists. The ZAP driver's design requires this driver to be "+ "trigger-agnostic and puts the scheduled-only gate in the caller's "+ "trigger-policy check. If that instruction has changed, change "+ - "internal/SKIPPED-CONTROLS.md U5 in the same commit", v.Field(i).Name) + "docs/controls.md U5 in the same commit", v.Field(i).Name) } } f := reflect.TypeOf(ZapPlanFacts{}) @@ -2219,7 +2219,7 @@ func TestTheJVMFootprintIsNotFabricatedAnywhereInThisPackage(t *testing.T) { body := string(raw) if !strings.Contains(body, "unquantified") { t.Fatal("zap.go no longer records ZAP's JVM footprint as unquantified. If it was " + - "MEASURED, this test and internal/SKIPPED-CONTROLS.md U5 both change in the " + + "MEASURED, this test and docs/controls.md U5 both change in the " + "same commit as the measurement") } for _, f := range forbidden { @@ -2229,7 +2229,7 @@ func TestTheJVMFootprintIsNotFabricatedAnywhereInThisPackage(t *testing.T) { "ZAP and no Docker (both measured 2026-08-22, PowerShell). A figure here "+ "becomes tier-M sizing documentation. Settle it with a `docker stats` run "+ "during a representative scheduled scan and record the measurement in "+ - "internal/SKIPPED-CONTROLS.md U5", f) + "docs/controls.md U5", f) } } } diff --git a/internal/dast/inventory/auth_helper.go b/internal/dast/inventory/auth_helper.go index eb088fb..6d53de5 100644 --- a/internal/dast/inventory/auth_helper.go +++ b/internal/dast/inventory/auth_helper.go @@ -131,7 +131,7 @@ // inertImports; adding it is a one-line edit in // internal/dast/authz/egress_chokepoint_test.go, is reported to the // orchestrator rather than made here, and is recorded as U10 in -// internal/SKIPPED-CONTROLS.md. +// docs/controls.md. // // a compressed artifact. compress/gzip IS allowlisted, so this one is // reachable; it is not done because an artifact sink that stores compressed @@ -930,7 +930,7 @@ type AuthDriver interface { // Helper either. // // MEASURED 2026-08-22, PowerShell, on the development host (recorded in -// internal/dast/engines/zap.go's header and in SKIPPED-CONTROLS U5): no zap.sh, +// internal/dast/engines/zap.go's header and in docs/controls.md U5): no zap.sh, // no zap, no zap.bat, no docker. A JVM is present; ZAP is not. // // It never returns (nil, nil). @@ -2971,7 +2971,7 @@ func (s *Session) stepKind(oneBased int) AuthStepKind { // is NOT on gate 3's inertImports; adding it is a one-line edit in // internal/dast/authz/egress_chokepoint_test.go, is reported to the // orchestrator rather than made here, and is U10 in -// internal/SKIPPED-CONTROLS.md. It is the spelling the backstop's own +// docs/controls.md. It is the spelling the backstop's own // positive-control test uses. // // a compressed artifact, and A CREDENTIAL RENDERED AS PIXELS. Both are in diff --git a/internal/dast/inventory/auth_helper_test.go b/internal/dast/inventory/auth_helper_test.go index e89fa09..39f645f 100644 --- a/internal/dast/inventory/auth_helper_test.go +++ b/internal/dast/inventory/auth_helper_test.go @@ -1690,7 +1690,7 @@ func TestTheSweepIsABackstopAndTheProvenanceRuleIsTheControl(t *testing.T) { // import it — which is also why the encoder below is written out by hand // here. Adding the import is a one-line widening of the egress allowlist in // internal/dast/authz/egress_chokepoint_test.go, is reported to the - // orchestrator, and is recorded in internal/SKIPPED-CONTROLS.md (U10). + // orchestrator, and is recorded in docs/controls.md (U10). beyond := authBase64(authPassword) if _, hit := credentialIn([]byte(beyond), secrets); hit { t.Fatal("the sweep decoded base64, so this test no longer demonstrates the " + diff --git a/internal/dast/inventory/tier0_runtime.go b/internal/dast/inventory/tier0_runtime.go index e822a9f..1654ff0 100644 --- a/internal/dast/inventory/tier0_runtime.go +++ b/internal/dast/inventory/tier0_runtime.go @@ -99,7 +99,7 @@ // authz.Adjudicate is the only mint for an authz.Authorization, the admission // chain contains Gate11RobotsDeny, and nothing is registered for it — so the // chain refuses every target there and NO Authorization can be constructed -// from outside package authz. internal/SKIPPED-CONTROLS.md U4 records this. +// from outside package authz. docs/controls.md U4 records this. // The consequence for the runtime spec probe is exact: Probe's admit-and-fetch path cannot reach // a fetcher today, and every test of it asserts a refusal. // @@ -1093,7 +1093,7 @@ func (p ParseResult) AssertAccountedFor() error { // // It is a PURE function of (target, servedAt, contentType, body) and it opens // nothing. That is what makes Tier 0's extraction fully testable while gate 11 -// keeps the fetch half unreachable (internal/SKIPPED-CONTROLS.md U4): a +// keeps the fetch half unreachable (docs/controls.md U4): a // kernel Target is authz.NewTarget's output and needs no authorization, // because it is gate 8/9's product and not a permission. // diff --git a/internal/dast/inventory/tier0_runtime_test.go b/internal/dast/inventory/tier0_runtime_test.go index 5e609ee..daf4ad2 100644 --- a/internal/dast/inventory/tier0_runtime_test.go +++ b/internal/dast/inventory/tier0_runtime_test.go @@ -22,7 +22,7 @@ // authz.Adjudicate is the only mint for an authz.Authorization, the admission // chain contains Gate11RobotsDeny, and nothing is registered for it — so no // Authorization can be constructed from outside package authz at all -// (internal/SKIPPED-CONTROLS.md U4). Every test below that would need one +// (docs/controls.md U4). Every test below that would need one // asserts a refusal instead, and // TestNoAuthorizationCanBeMintedUntilGate11IsRegistered FAILS on the day that // changes, listing what must then be written. There is no t.Skip in this file. @@ -1854,7 +1854,7 @@ func TestTheHardCodedPathScannerCanSeeAViolation(t *testing.T) { // =========================================================================== // THE PROBE PATH, END TO END // -// internal/SKIPPED-CONTROLS.md U4 records that no authz.Authorization could be +// docs/controls.md U4 records that no authz.Authorization could be // minted from outside package authz, because Gate11RobotsDeny sat in the // admission chain with no implementation. That is NO LONGER TRUE in this tree: // gate 11 has moved to the Governor's per-request chain @@ -1892,7 +1892,7 @@ func mintAuthorization(t *testing.T) (authz.Authorization, *countingSink) { Tier 0 cannot issue a spec fetch without an authz.Authorization, and Adjudicate is the only mint. If gate 11 has been put back into kernel.go's admissionChain, this whole file's fetch half becomes unreachable again and -internal/SKIPPED-CONTROLS.md U4 needs reopening for Tier 0 as well as for the nuclei driver.`, +docs/controls.md U4 needs reopening for Tier 0 as well as for the nuclei driver.`, dec.Gate(), dec.Reason(), dec.Err()) } auth, err := dec.Authorization() @@ -2723,7 +2723,7 @@ func TestJSONNumbersAndBooleansInPlaceOfStringsDoNotBecomeRoutes(t *testing.T) { // TestThisPackageSkipsNothing. // -// internal/SKIPPED-CONTROLS.md exists because "a guard that vanishes silently +// docs/controls.md exists because "a guard that vanishes silently // when it cannot run is worse than no guard, because the green tick is read as // an answer" — and it happened twice in this repository before anyone noticed. // U6's entry claims this package has zero skip sites; this is what makes the @@ -2775,7 +2775,7 @@ func TestThisPackageSkipsNothing(t *testing.T) { } t.Errorf(`%s calls %s. -internal/SKIPPED-CONTROLS.md U6 records that this package has ZERO skip sites, +docs/controls.md U6 records that this package has ZERO skip sites, and a skip here would let the package print "ok" while the control it guards went unrun. If the skip is genuinely platform-specific, it has to be listed in that document with what would settle it — and this test updated in the same diff --git a/internal/dast/inventory/tier1_repospec.go b/internal/dast/inventory/tier1_repospec.go index 8464c41..8a16f3b 100644 --- a/internal/dast/inventory/tier1_repospec.go +++ b/internal/dast/inventory/tier1_repospec.go @@ -919,7 +919,7 @@ func retagAll(target authz.Target, pr ParseResult) ParseResult { // and cannot: Tier 1 issues no request at all, which is why this whole packet // is exercisable today while the runtime spec probe's fetch half depends on the kernel // admitting. There is no t.Skip anywhere in this tier and nothing in -// internal/SKIPPED-CONTROLS.md belongs to it. +// docs/controls.md belongs to it. func IngestSpecFile(cfg IngestConfig, f SpecFile) (FileResult, error) { if !cfg.Target.Constructed() { return FileResult{}, fmt.Errorf("inventory: %w: IngestSpecFile was handed a Target "+ diff --git a/internal/dast/inventory/tier1_repospec_test.go b/internal/dast/inventory/tier1_repospec_test.go index 3b3494a..293b234 100644 --- a/internal/dast/inventory/tier1_repospec_test.go +++ b/internal/dast/inventory/tier1_repospec_test.go @@ -6,7 +6,7 @@ // // EVERYTHING IN THIS PACKET IS PROVEN END TO END. Tier 1 issues no request, so // none of it depends on the kernel admitting anything, none of it needs a -// SpecFetcher, and nothing in it belongs in internal/SKIPPED-CONTROLS.md. +// SpecFetcher, and nothing in it belongs in docs/controls.md. // There is no t.Skip in this file. // // all five input formats the packet names parse to Route lists — OpenAPI @@ -1955,7 +1955,7 @@ func TestTheTier1StructuralScannersCanSeeAViolation(t *testing.T) { } // TestTier1ContainsNoSkip. The packet's standing rule is that an unprovable -// control is written down in internal/SKIPPED-CONTROLS.md, never turned into a +// control is written down in docs/controls.md, never turned into a // t.Skip. Tier 1 issues no request and has nothing unprovable in it, so the // rule reduces to a scan. func TestTier1ContainsNoSkip(t *testing.T) { @@ -1973,7 +1973,7 @@ func TestTier1ContainsNoSkip(t *testing.T) { switch sel.Sel.Name { case "Skip", "Skipf", "SkipNow": t.Errorf("%s calls t.%s at %s. An unprovable control is written down in "+ - "internal/SKIPPED-CONTROLS.md naming what would settle it, never "+ + "docs/controls.md naming what would settle it, never "+ "skipped", file, sel.Sel.Name, fset.Position(sel.Pos())) } return true diff --git a/internal/dast/inventory/tier2_go_extract.go b/internal/dast/inventory/tier2_go_extract.go index a6622a8..b3d6391 100644 --- a/internal/dast/inventory/tier2_go_extract.go +++ b/internal/dast/inventory/tier2_go_extract.go @@ -702,7 +702,7 @@ type ExtractedRoute struct { // with network and toolchain reach that belongs behind network containment. // // Nothing implements this today. third_party/go-apispec/PIN.md section 5 -// records the two measured blockers. internal/SKIPPED-CONTROLS.md is where the +// records the two measured blockers. docs/controls.md is where the // unprovable half is named; there is no t.Skip in this package. type TypeCheckedExtractor interface { // ExtractTypeChecked returns one ExtractedRoute per registration the diff --git a/internal/dast/inventory/tier2_go_extract_test.go b/internal/dast/inventory/tier2_go_extract_test.go index 3acb9f7..53b06f9 100644 --- a/internal/dast/inventory/tier2_go_extract_test.go +++ b/internal/dast/inventory/tier2_go_extract_test.go @@ -1489,7 +1489,7 @@ const ( // repoRootForLicenceGuard walks up to the module root, and FAILS rather than // skipping when it cannot find it. // -// internal/SKIPPED-CONTROLS.md records this repository shipping two guards that +// docs/controls.md records this repository shipping two guards that // vanished silently when they could not run. A compliance check that cannot // locate the tree it guards has not passed; it has not run. func repoRootForLicenceGuard(t *testing.T) string { diff --git a/internal/dast/inventory/tier3_crawl.go b/internal/dast/inventory/tier3_crawl.go index 2b220fc..bd62bda 100644 --- a/internal/dast/inventory/tier3_crawl.go +++ b/internal/dast/inventory/tier3_crawl.go @@ -742,7 +742,7 @@ type CrawlPage struct { // implementation declares which of these it is, and validateCrawlConfig // refuses anything that is not FetchDisciplineSingleRequest. A refusal Anvil // makes before the first request beats an obligation no implementation can -// keep, and the gap is recorded in internal/SKIPPED-CONTROLS.md rather than +// keep, and the gap is recorded in docs/controls.md rather than // left as a control that exists only in prose. type FetchDiscipline string @@ -802,7 +802,7 @@ func (d FetchDiscipline) PermitsCrawl() bool { return admittedDisciplines()[d] } // pass Discipline() and Anvil refuses to drive it. That is not a decision // against ZAP; it is the honest consequence of obligation 1 below. What would // settle it is a seam that hands each sub-request back for admission, and it -// is written up in internal/SKIPPED-CONTROLS.md rather than implied by a +// is written up in docs/controls.md rather than implied by a // promise nobody can keep. // // # The obligations an implementation takes on @@ -846,7 +846,7 @@ type ClientSpider interface { // silent-clean failure this whole tier is written against. // // MEASURED 2026-08-22, PowerShell, on the development host (recorded in -// internal/dast/engines/zap.go's header and in SKIPPED-CONTROLS U5): no zap.sh, +// internal/dast/engines/zap.go's header and in docs/controls.md U5): no zap.sh, // no zap, no zap.bat, no docker. A JVM is present; ZAP is not. // // It never returns (nil, nil). @@ -1363,7 +1363,7 @@ func checkFetchDiscipline(d FetchDiscipline) error { "kernel and not a style question. CrawlPage has no field in which the seam "+ "could report them, so Anvil cannot admit them after the fact either. This "+ "is refused rather than driven, and the gap is recorded in "+ - "internal/SKIPPED-CONTROLS.md with what would close it", + "docs/controls.md with what would close it", ErrRefused, string(d)) } return fmt.Errorf("inventory: %w: the wired ClientSpider declares fetch discipline %q, "+ @@ -1494,7 +1494,7 @@ func (s *crawlState) crawlOne(ctx context.Context, item frontierItem, now authz. // would mean resolving an attacker-chosen name — which is the DNS lookup // gate 9 exists to bound, performed on behalf of the party the crawl is // pointed at. Refusing the link without resolving it is the cheaper and - // safer order. internal/SKIPPED-CONTROLS.md records what would change it. + // safer order. docs/controls.md records what would change it. intent, err := authz.NewRequestIntent(authz.RequestFacts{ Origin: item.origin, Admitted: s.cfg.Target, diff --git a/internal/dast/inventory/tier3_crawl_test.go b/internal/dast/inventory/tier3_crawl_test.go index a9ce575..c5cbadd 100644 --- a/internal/dast/inventory/tier3_crawl_test.go +++ b/internal/dast/inventory/tier3_crawl_test.go @@ -2151,7 +2151,7 @@ func TestNoCrawlRequestCanCarryASession(t *testing.T) { // // Obligation 1 was therefore a contract THE CHOSEN IMPLEMENTATION CANNOT KEEP. // This is the refusal that replaces it. The gap is recorded in -// internal/SKIPPED-CONTROLS.md with what would close it. +// docs/controls.md with what would close it. func TestARenderingSpiderIsRefusedRatherThanDriven(t *testing.T) { cases := []struct { name string @@ -2252,7 +2252,7 @@ func TestTheDisciplineAllowlistIsAnAllowlist(t *testing.T) { // The refusal must NAME the sub-request problem rather than saying "bad // value": an operator who wired a browser needs to know why. err := checkFetchDiscipline(FetchDisciplineRendering) - for _, want := range []string{"SUB-REQUESTS", "gate-14", "gate-21", "SKIPPED-CONTROLS"} { + for _, want := range []string{"SUB-REQUESTS", "gate-14", "gate-21", "docs/controls.md"} { if !strings.Contains(err.Error(), want) { t.Fatalf("the rendering refusal does not mention %q: %v", want, err) } diff --git a/internal/ingest/cache/cache_test.go b/internal/ingest/cache/cache_test.go index fde4f9c..01e76f9 100644 --- a/internal/ingest/cache/cache_test.go +++ b/internal/ingest/cache/cache_test.go @@ -969,7 +969,7 @@ func TestFeedStateAcceptsEveryFeedIDInTheShippedConfig(t *testing.T) { // skip here reports "the produce/consume edge between the feed table // and the ingestion cache was verified" whenever the file moves, is // renamed, or stops parsing -- which is the exact failure mode - // internal/SKIPPED-CONTROLS.md is about. + // docs/controls.md is about. t.Fatalf("The feed table's example config could not be loaded, so the feed_id domain shared by the feed table "+ "and the ingestion cache was NOT cross-checked: %v", err) } diff --git a/internal/ingest/invisible/invisible_test.go b/internal/ingest/invisible/invisible_test.go index f833ce6..17f7655 100644 --- a/internal/ingest/invisible/invisible_test.go +++ b/internal/ingest/invisible/invisible_test.go @@ -575,7 +575,7 @@ func TestTheReservedDefaultIgnorablesAreInTheClass(t *testing.T) { // split a share-alike marker -- would come back invisibly. t.Fatal("this toolchain does not ship Other_Default_Ignorable_Code_Point, so the reserved " + "half of the invisible class was NOT checked. This fails rather than skips: " + - "see internal/SKIPPED-CONTROLS.md.") + "see docs/controls.md.") } reserved := 0 for r := rune(0); r <= unicode.MaxRune; r++ { diff --git a/internal/ingest/sanitize/sanitize_test.go b/internal/ingest/sanitize/sanitize_test.go index f949ea3..d9b2941 100644 --- a/internal/ingest/sanitize/sanitize_test.go +++ b/internal/ingest/sanitize/sanitize_test.go @@ -955,7 +955,7 @@ func TestNoDefaultIgnorableCodePointSurvives(t *testing.T) { // text past AssertSanitized -- silently, on a toolchain bump. t.Fatalf("this Go toolchain ships Other_Default_Ignorable_Code_Point=%v and "+ "Variation_Selector=%v, so the default-ignorable sweep was NOT checked. This fails "+ - "rather than skips: see internal/SKIPPED-CONTROLS.md.", odi != nil, vs != nil) + "rather than skips: see docs/controls.md.", odi != nil, vs != nil) } n := 0 for r := rune(0); r <= unicode.MaxRune; r++ { diff --git a/internal/mirror/accelerator/accelerator_test.go b/internal/mirror/accelerator/accelerator_test.go index cc1b09f..aa458f7 100644 --- a/internal/mirror/accelerator/accelerator_test.go +++ b/internal/mirror/accelerator/accelerator_test.go @@ -1119,7 +1119,7 @@ func TestAFullWarmStartLeavesEveryTierDirectoryUntouched(t *testing.T) { // a directory that passes every string check and resolves elsewhere. // // THIS TEST USED TO SKIP ON WINDOWS, AND THAT SKIP IS THE FIRST OF THE TWO -// INCIDENTS internal/SKIPPED-CONTROLS.md exists for. os.Symlink needs +// INCIDENTS docs/controls.md exists for. os.Symlink needs // SeCreateSymbolicLinkPrivilege (Developer Mode or an elevated shell), so on an // ordinary Windows host the whole guard reported SUCCESS while checking // nothing, and the package still printed ok. A Windows DIRECTORY JUNCTION then diff --git a/internal/record/FINGERPRINT-SPEC.md b/internal/record/FINGERPRINT-SPEC.md index 59eb766..b67875c 100644 --- a/internal/record/FINGERPRINT-SPEC.md +++ b/internal/record/FINGERPRINT-SPEC.md @@ -8,7 +8,7 @@ be able to read the complete algorithm here and nowhere else, and emit byte-iden `plan/design/record-and-store.md`'s "Fingerprint Specification" section remains the record of *why* the research-branch conflict was resolved the way it was. It is a **summary**, not the definition. -`internal/record/REVIEW-contract-and-fingerprint.md` (finding 1) proved the summary insufficient by re-implementing its +`docs/reviews/contract-and-fingerprint.md` (finding 1) proved the summary insufficient by re-implementing its four-clause `normalized_match` text in Python and obtaining `55e27b07…` where the committed golden for `sast-01-go-sql-string-concat` is `13c60ccf…`. The orchestrator ruled on 2026-08-08 that the implementation was right and the specification incomplete, and that the fix was to write the diff --git a/internal/record/fingerprint.go b/internal/record/fingerprint.go index 4cc72cf..ae87617 100644 --- a/internal/record/fingerprint.go +++ b/internal/record/fingerprint.go @@ -143,7 +143,7 @@ package record // THE AUTHORITATIVE SPECIFICATION IS internal/record/FINGERPRINT-SPEC.md // =========================================================================== // -// The contract-and-fingerprint review (REVIEW-contract-and-fingerprint.md) +// The contract-and-fingerprint review (docs/reviews/contract-and-fingerprint.md) // proved that the four-clause `normalized_match` text in // plan/design/record-and-store.md is NOT sufficient to reproduce this file's // digests: a re-implementation written from that text alone emits 55e27b07... diff --git a/internal/record/fingerprint_conformance_test.go b/internal/record/fingerprint_conformance_test.go index e52a690..bf07ef6 100644 --- a/internal/record/fingerprint_conformance_test.go +++ b/internal/record/fingerprint_conformance_test.go @@ -81,7 +81,7 @@ package record // // Sources: the spine's record section; the fingerprint conformance harness' design; // internal/record/FINGERPRINT-SPEC.md (the algorithm, and Appendix Z); -// internal/record/REVIEW-contract-and-fingerprint.md finding 1 (why the specification had to be +// docs/reviews/contract-and-fingerprint.md finding 1 (why the specification had to be // written down completely before this test could exist at all). import ( diff --git a/internal/record/fingerprint_spec_test.go b/internal/record/fingerprint_spec_test.go index 02868a7..8a3d7da 100644 --- a/internal/record/fingerprint_spec_test.go +++ b/internal/record/fingerprint_spec_test.go @@ -2,7 +2,7 @@ package record // fingerprint_spec_test.go — the test that keeps FINGERPRINT-SPEC.md honest. // -// The contract-and-fingerprint review (REVIEW-contract-and-fingerprint.md), +// The contract-and-fingerprint review (docs/reviews/contract-and-fingerprint.md), // blocker 1: `normalized_match` was defined only in Go. The critic // re-implemented plan/design/record-and-store.md's four-clause spec text in // Python and got 55e27b07… where the committed golden for sast-01 is 13c60ccf…. diff --git a/internal/record/sarif_github.go b/internal/record/sarif_github.go index 7c0df87..82fa2b2 100644 --- a/internal/record/sarif_github.go +++ b/internal/record/sarif_github.go @@ -136,7 +136,7 @@ package record // visible rather than absorbed. fingerprint.go says the key "is owned by the // GitHub projection (the GitHub projection)"; plan/design/record-and-store.md's Record Field // Contract names the producer as the fingerprint engine; and -// internal/record/REVIEW-contract-and-fingerprint.md's MAJOR 3 records that the disagreement is +// docs/reviews/contract-and-fingerprint.md's MAJOR 3 records that the disagreement is // unruled and that no code in the tree produces the value today. The GitHub projection's // packet scopes this file to the projection and says nothing about producing // a fingerprint. diff --git a/test/hygiene/plan_codes_test.go b/test/hygiene/plan_codes_test.go index bf36f48..4a7cbd0 100644 --- a/test/hygiene/plan_codes_test.go +++ b/test/hygiene/plan_codes_test.go @@ -169,7 +169,7 @@ func TestThePatternsRefuseTheOldShapesAndNothingElse(t *testing.T) { "gates 4, 5, 8, 9 and 10", "Phase 4: close the confirmation gate", "the read-only-boundary review's finding M1(a)", - "U7 is recorded in internal/SKIPPED-CONTROLS.md", + "U7 is recorded in docs/controls.md", "SARIF 2.1.0 §3.27.4", "research/10-prior-art-and-landscape.md", "the confirmation gate's rules R1, R2 and R3", From 2b4ed4e0e3eafa6f18aa8d724c131521a5c3f2ef Mon Sep 17 00:00:00 2001 From: "Tom D. Snyder" Date: Sat, 3 Oct 2026 09:22:58 -0400 Subject: [PATCH 6/6] Develop on Linux: replace Windows-host statements with what the Debian machine measures Development moved from a Windows 11 machine to Debian 13 in September 2026. Comments, tests and the controls ledger that described Windows as the development host now state what was measured on Debian on 2026-10-03: go1.26.5 linux/amd64 as uid 1000, dpkg-query listing 3,467 packages, gcc present so -race runs locally, an unprivileged user and network namespace available, and nft, docker, podman, runsc, nuclei, ZAP, Java, Trivy and opengrep absent. The ledger's skip column was re-measured: TestCollectAgainstTheRealHost no longer skips, leaving 4 tests and 14 SKIP lines, all classified legitimate. Dated Windows measurements stay as history, and the Windows CI job stays, because it is the only lane that takes containment's refusal branch. --- .github/workflows/ci.yml | 2 +- cmd/anvil-host-collector/main_test.go | 2 +- docs/controls.md | 56 +++++++++++++-------- internal/collector/host/collect.go | 3 +- internal/collector/host/collect_test.go | 6 +-- internal/dast/containment/netns.go | 10 ++-- internal/dast/containment/netns_test.go | 8 +-- internal/dast/engines/nuclei.go | 2 +- internal/dast/engines/nuclei_test.go | 4 +- internal/dast/engines/zap_test.go | 3 +- internal/handoff/claim.go | 4 +- internal/handoff/reaper.go | 2 +- internal/ingest/reconcile/reconcile_test.go | 6 +-- internal/mirror/accelerator/reparse.go | 2 +- internal/mirror/accelerator/reparse_test.go | 2 +- internal/mirror/accelerator/trivydb.go | 4 +- internal/record/readpath.go | 3 +- internal/scanctl/controller_review_test.go | 6 +-- internal/scanctl/statemachine.go | 4 +- test/conformance/lanea/lanea_test.go | 15 +++--- 20 files changed, 82 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3d6991..f35722e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: # # This step exists so the ledger is READ on the machine where it says the # most: ubuntu-latest has dpkg, so host.Collect runs for real here and the - # host-inventory link is PROVEN, where on the Windows dev host it is not. + # host-inventory link is PROVEN, where on a host with no package manager it is not. - name: Lane A chain ledger (printed, not just passed) run: go test -count=1 -v -run TestLaneAChain ./test/conformance/lanea/ diff --git a/cmd/anvil-host-collector/main_test.go b/cmd/anvil-host-collector/main_test.go index 84c6edd..03584a2 100644 --- a/cmd/anvil-host-collector/main_test.go +++ b/cmd/anvil-host-collector/main_test.go @@ -40,7 +40,7 @@ func TestTheBinaryRefusesEveryArgument(t *testing.T) { // On a Linux host with a package manager this is the host collector's stop condition and // the read-only-boundary review's "confirmation the binary runs successfully as // a non-root user in the test fixture" — the thing that could not be given -// before, because there was no binary. Everywhere else (a Windows development +// before, because there was no binary. Everywhere else (a non-Linux // host, a Linux container with no package manager) the collector reports no // package manager, which is exit status 3 WITH an inventory: the coverage // report is the point, and this asserts it is emitted rather than swallowed. diff --git a/docs/controls.md b/docs/controls.md index 56a346f..fc25ba2 100644 --- a/docs/controls.md +++ b/docs/controls.md @@ -41,14 +41,22 @@ be listed here. ## Method The **"skips here"** column is **measured, not reasoned**: `go test -count=1 -v -./...` was run on the Windows dev host and the `--- SKIP` lines were read out of -the output. The **"skips in CI"** column *is* reasoned, from the condition plus +./...` was run on the development machine and the `--- SKIP` lines were read out +of the output. Development moved from a Windows 11 machine to a Debian machine in +September 2026; the column was re-measured on Debian on 2026-10-03, and where a +row differs between the two the entry says which. The **"skips in CI"** column *is* reasoned, from the condition plus `.github/workflows/ci.yml` (`runs-on: ubuntu-latest`, `go test -race -count=1 ./...`, no Trivy install, no `ANVIL_TRIVY_E2E`, no acquired licence bodies — CI is always a fresh clone). Where the reasoning does not settle it, the column says so rather than guessing. -- Host measured: `go1.26.5 windows/amd64`, Windows 11, non-elevated, +- Host measured on 2026-10-03: `go1.26.5 linux/amd64`, Debian 13, kernel + 6.12.107, uid 1000 (not root). `dpkg-query` is present and lists 3,467 + packages; `rpm`, `apk`, `nft`, `docker`, `podman`, `runsc`, `nuclei`, `zap.sh`, + `java`, `trivy` and `opengrep` are absent. `gcc` is present, so `-race` runs + locally. An unprivileged user and network namespace can be created + (`unshare -rn true` succeeds). +- Host first measured: `go1.26.5 windows/amd64`, Windows 11, non-elevated, Developer Mode **off**. - Suite state after the changes below: `gofmt` clean, `go vet` clean, `go build ./...` clean, `go test -count=1 ./...` **green** (17 packages). @@ -667,12 +675,13 @@ and all of it is tested: passes, so the first half is not passing because the function refuses everything. **The stop condition's FIRST clause — "Default-deny ruleset installs correctly on a real target fixture" — is NOT met and cannot be met - on this host**: nothing here installs a ruleset anywhere, and item (1) below + on the development machine** (Debian, measured 2026-10-03: no `nft` binary, uid + 1000): nothing here installs a ruleset anywhere, and item (1) below is what would settle it. This entry previously claimed the whole stop condition was met, which is how a gap ships. - `SystemCommander` **refuses** on any non-Linux GOOS and - `TestSystemCommanderRefusesOffLinux` asserts that refusal on this host. There - is no no-op Commander, so there is no path by which Windows returns "contained". + `TestSystemCommanderRefusesOffLinux` asserts that refusal wherever GOOS is not + linux. There is no no-op Commander, so there is no path by which Windows returns "contained". **That guard now runs in a CI lane**: `containment-non-linux` in `.github/workflows/ci.yml` runs the package on `windows-latest`, asserts the named test actually executed and passed, then DELETES the @@ -684,9 +693,12 @@ and all of it is tested: ### What it does not prove, and exactly what would settle it Everything above is a statement about Anvil's decision logic. None of it is a -statement about a kernel. Windows has no network namespaces and no nftables; -WSL2 is present on the dev host and is **not** the target runtime, so it does -not settle this either. +statement about a kernel. The Debian development machine (measured 2026-10-03) +has network namespaces and lets an unprivileged process create a user and +network namespace, but `nft` is not installed and the suite runs as uid 1000, so +nothing installs a ruleset and nothing here settles this either. Installing +nftables is the owner's decision; with it, item (1) below may be runnable inside +an unprivileged user namespace on this machine, which has not been tried. What would settle it, in order of decreasing cost: @@ -822,7 +834,8 @@ against. **Docker is not installed on the host this packet was written on.** Measured, not assumed: `Get-Command docker` and `Get-Command runsc` both return nothing -on `go1.26.5 windows/amd64`, Windows 11. +on `go1.26.5 windows/amd64`, Windows 11. Re-measured 2026-10-03 on the Debian +development machine: `docker`, `podman` and `runsc` are still absent. **Nothing in the tree implements the `Docker` interface.** A repository-wide grep for `ComposeUp` and `EngineInfo(` finds the interface, its call sites in @@ -1046,7 +1059,8 @@ container engine**, and this entry is the standing record of that. ### (a) nuclei is not installed, and no adapter exists -**Measured** on the development host: `Get-Command nuclei` finds nothing, and +**Measured** on the Windows development machine, and again on 2026-10-03 on the +Debian one (`command -v nuclei` finds nothing): no engine binary, and `go list -m all` contains no `projectdiscovery` module. `SystemEngine()` therefore returns `*EngineUnavailableError` on **every** host — it never returns a no-op engine — and `ScanResult.AssertNotSilentlyEmpty` refuses to let @@ -1256,7 +1270,8 @@ the same commit. **What would settle it, and what stops it here.** The plan's own suggestion is one `docker stats` run during a representative scheduled scan. -**MEASURED 2026-08-22, PowerShell, on the development host:** +**MEASURED 2026-08-22, PowerShell, on the Windows development machine** (re-measured +2026-10-03 on the Debian one: `docker`, `zap.sh` and `java` are all absent there): ``` Get-Command docker -> NOT FOUND @@ -1999,7 +2014,7 @@ consequence. |---|---| | **File** | `internal/collector/host/collect_test.go:3967` | | **Trigger** | `runtime.GOOS != "linux"` | -| **Skipped here?** | **YES — measured.** `no native package manager on windows` | +| **Skipped here?** | **No — measured 2026-10-03 on Debian**: `dpkg-query` is present and the test enumerates the host. It skipped on the Windows machine (`no native package manager on windows`) | | **Skips in CI?** | No. `ubuntu-latest` is Linux | | **Property unverified** | Real `dpkg-query`/`rpm`/`apk` enumeration against a live host | | **Security control?** | No — a collector coverage claim | @@ -2011,7 +2026,7 @@ consequence. |---|---| | **File** | `internal/collector/host/collect_test.go:3976` | | **Trigger** | Linux, but none of `dpkg-query`/`rpm`/`apk` resolves | -| **Skipped here?** | No — unreachable on Windows (L1 returns first) | +| **Skipped here?** | No — measured 2026-10-03: `dpkg-query` resolves on the Debian machine | | **Skips in CI?** | No. `ubuntu-latest` ships `dpkg-query` | | **Property unverified** | As L1 | | **Security control?** | No | @@ -2023,7 +2038,7 @@ consequence. |---|---| | **File** | `internal/collector/host/collect_test.go:3462` | | **Trigger** | `runtime.GOOS != "windows" && os.Geteuid() == 0` | -| **Skipped here?** | No — measured. `os.Geteuid()` is `-1` on Windows, so the condition is short-circuited | +| **Skipped here?** | No — measured 2026-10-03: the suite runs as uid 1000, so the condition is false (on Windows `os.Geteuid()` is `-1` and the GOOS check short-circuits it) | | **Skips in CI?** | No. GitHub's `ubuntu-latest` runs as the non-root `runner` user. **Would** skip in a root container | | **Property unverified** | Successful enumeration under a non-root UID | | **Security control?** | Partly — the root-free-by-design claim | @@ -2154,11 +2169,11 @@ missing artefact and the command that produces it. | Hazards closed by a test-only change | 9 (7 sites removed, 2 narrowed) | | Hazards needing a non-test change | 4 (N1 closed by CI, N2 closed elsewhere, N3 closed, N4 closed) | | Legitimate skips, left in place | 11 | -| Skips that fire on the Windows dev host | 4 tests / 15 subtest lines | +| Skips that fire on the development machine (Debian, 2026-10-03) | 4 tests / 14 SKIP lines (L4, L5, L7 ×11, L11) | | `t.SkipNow` sites | 0 | -Skips still firing on this host, all classified LEGITIMATE above: -`TestCollectAgainstTheRealHost` (L1), `TestRealTrivyScansAFixtureRepo` (L4), +Skips still firing on the development machine (Debian, measured 2026-10-03), all +classified LEGITIMATE above: `TestRealTrivyScansAFixtureRepo` (L4), `TestBothConsumersAgree` (L5), `TestPinnedLicenceBodiesMatchTheirPins` ×11 (L7), `TestXVM3RelatedLocationsAreCapped/loc0_rel3000` (L11). @@ -2213,8 +2228,9 @@ What remains open in U4 and U5 is the **tooling**: no Nuclei engine and no ZAP on any host or CI lane here, and ZAP's JVM footprint still unmeasured. U1 (`internal/dast/containment`, network containment) is the -highest-stakes of them: the package is green on Windows and has never run -against a Linux kernel. See its entry for the privileged Linux CI lane that +highest-stakes of them: the package is green on Windows and on the Debian +development machine and has never installed a ruleset into a kernel (no `nft` +binary there, measured 2026-10-03). See its entry for the privileged Linux CI lane that would close it, and U1a/U1b for the two things that lane would still not settle on its own. diff --git a/internal/collector/host/collect.go b/internal/collector/host/collect.go index a727586..4132df2 100644 --- a/internal/collector/host/collect.go +++ b/internal/collector/host/collect.go @@ -118,7 +118,8 @@ // LIMITS OF THIS STATEMENT, so it is not over-read in the other direction: // this is a documented property of rpm's BDB backend, recorded because the // read-only-boundary review's review found the unconditional claim and could -// not reproduce the behaviour on a Windows development host. It has NOT been +// not reproduce the behaviour on the Windows machine it ran on, and the Debian +// development machine (measured 2026-10-03) has no rpm at all. It has NOT been // reproduced by this repository on a BDB host, and the sqlite and ndb backends' // sidecar behaviour (`rpmdb.sqlite-wal`, `-shm`) has not been examined at all. // Nothing is asserted about them in either direction. diff --git a/internal/collector/host/collect_test.go b/internal/collector/host/collect_test.go index 4f9829e..a33e425 100644 --- a/internal/collector/host/collect_test.go +++ b/internal/collector/host/collect_test.go @@ -4142,7 +4142,7 @@ var permittedGraphEdges = map[graphEdge]string{ {"path/filepath", "syscall"}: "filepath consults the platform's path rules; resolveBinary joins the constant search directories with it", {"internal/syscall/unix", "syscall"}: "the standard library's per-family syscall shims on linux and darwin", {"internal/syscall/execenv", "syscall"}: "the standard library's environment plumbing for a child process, pulled in by os on every platform", - {"internal/syscall/windows", "syscall"}: "the standard library's Win32 shims; present only on the Windows development host, never on a target", + {"internal/syscall/windows", "syscall"}: "the standard library's Win32 shims; present only in a Windows build, never on a target", {"internal/syscall/windows/registry", "syscall"}: "time's Windows time-zone lookup reads the registry; Windows-only", {"internal/filepathlite", "syscall"}: "filepath's allocation-free core on Windows; Windows-only", {"crypto/internal/sysrand", "syscall"}: "the kernel entropy source, reached by map iteration seeding; linux-only in this graph", @@ -4348,8 +4348,8 @@ func TestTheImportGraphGuardCatchesASpawningDependency(t *testing.T) { // TestCollectAgainstTheRealHost runs the actual exec path when there is a // package manager to run it against. It is SKIPPED on any host without one — -// including the Windows development host — so the evidence it produces belongs -// to CI on Linux and to the fixture-container run, not to a developer laptop. +// including any non-Linux host. On the Debian development machine it runs +// against dpkg (measured 2026-10-03: 3,467 packages) and in CI on ubuntu-latest. func TestCollectAgainstTheRealHost(t *testing.T) { if runtime.GOOS != "linux" { t.Skipf("no native package manager on %s", runtime.GOOS) diff --git a/internal/dast/containment/netns.go b/internal/dast/containment/netns.go index 2494eb6..65f5c2f 100644 --- a/internal/dast/containment/netns.go +++ b/internal/dast/containment/netns.go @@ -63,11 +63,13 @@ // that reads the inode through the same `ip netns exec` it later fails to apply // would agree with itself and report contained. // -// # This host cannot prove any of it +// # The development machine cannot prove any of it // -// The development host for this packet is WINDOWS. Linux network namespaces -// and nftables do not exist here, so nothing in this file that touches the -// kernel can be executed, and no test in this package claims otherwise. The +// This packet was written on Windows, where Linux network namespaces and +// nftables do not exist. The Debian machine development moved to has network +// namespaces but no nft binary, and the suite runs as uid 1000 (measured +// 2026-10-03). Nothing in this file that touches the kernel has been executed, +// and no test in this package claims otherwise. The // package is therefore split so that the honest part is testable everywhere: // // - The ruleset is BUILT by a pure function (BuildRuleset) and pinned by diff --git a/internal/dast/containment/netns_test.go b/internal/dast/containment/netns_test.go index 4c1cce2..8aa341a 100644 --- a/internal/dast/containment/netns_test.go +++ b/internal/dast/containment/netns_test.go @@ -21,8 +21,10 @@ import ( // What this file can and cannot prove // --------------------------------------------------------------------------- // -// This suite runs on the Windows development host, where Linux network -// namespaces and nftables DO NOT EXIST. It therefore proves the DECISION LOGIC +// This suite was written on Windows, where Linux network namespaces and +// nftables do not exist, and runs on the Debian development machine as uid +// 1000 with no nft binary (measured 2026-10-03). Neither can install a ruleset, +// so it proves the DECISION LOGIC // -- ruleset construction, rule ordering, the deny-set relation with the // authorization kernel's gate 10, and every branch of the canary-report // evaluation -- and it proves that the platform path REFUSES rather than @@ -1762,7 +1764,7 @@ func (p recordingProbe) Attempt(_ context.Context, network, address string) (boo // a verdict. What it does not prove -- stated so the claim matches the // evidence -- is that a Linux kernel delivers the errno a ConnectProbe would // turn into each of these names. That mapping lives with the implementation, -// which holds the socket; nothing on a Windows host can prove it. See +// which holds the socket; nothing on a host without that implementation can prove it. See // docs/controls.md U1. func TestClassifyDialFailureTable(t *testing.T) { cases := []struct { diff --git a/internal/dast/engines/nuclei.go b/internal/dast/engines/nuclei.go index 9732e81..8775155 100644 --- a/internal/dast/engines/nuclei.go +++ b/internal/dast/engines/nuclei.go @@ -1010,7 +1010,7 @@ func LoadTemplates(dir string) ([]Template, []RejectedTemplate, error) { // // This is an ALLOWLIST of two shapes rather than a check for // os.ModeSymlink, and the reason is measured rather than - // theoretical. On this Windows host a DIRECTORY JUNCTION (`mklink + // theoretical. On the Windows machine this was written on, a DIRECTORY JUNCTION (`mklink // /J`, which needs no privilege at all, unlike `mklink /D`) is // reported by filepath.WalkDir as: // diff --git a/internal/dast/engines/nuclei_test.go b/internal/dast/engines/nuclei_test.go index bc3883b..a4e760b 100644 --- a/internal/dast/engines/nuclei_test.go +++ b/internal/dast/engines/nuclei_test.go @@ -5,8 +5,8 @@ // =========================================================================== // // Nuclei is NOT INSTALLED here (`Get-Command nuclei` finds nothing; `go list -// -m all` contains no projectdiscovery module — both measured on the -// development host, Windows 11, go1.26.5 windows/amd64). So no test here runs +// -m all` contains no projectdiscovery module — measured on the +// Windows 11 development machine, and again on 2026-10-03 on the Debian one). So no test here runs // the engine, and none pretends to: the engine is behind an interface, driven // by recorded shapes, and SystemEngine refuses on every host rather than // returning a no-op that would let a scan come back clean. diff --git a/internal/dast/engines/zap_test.go b/internal/dast/engines/zap_test.go index 188b958..35111e1 100644 --- a/internal/dast/engines/zap_test.go +++ b/internal/dast/engines/zap_test.go @@ -5,7 +5,8 @@ // =========================================================================== // // ZAP is NOT INSTALLED here. MEASURED 2026-08-22 from PowerShell on the -// development host (Windows 11, go1.26.5 windows/amd64): +// Windows 11 development machine (re-measured 2026-10-03 on the Debian one: +// zap.sh, docker and java are all absent there): // // Get-Command zap.sh -> NOT FOUND // Get-Command zap -> NOT FOUND diff --git a/internal/handoff/claim.go b/internal/handoff/claim.go index 37078a0..efe8019 100644 --- a/internal/handoff/claim.go +++ b/internal/handoff/claim.go @@ -801,8 +801,8 @@ func (q *Queue) packetGate(ctx context.Context, h Handle) error { // data=writeback. This code does not depend on that heuristic. // // Windows has no fsync-able directory handle, so the parent fsync is skipped -// there and only there. Anvil's packet lives on tmpfs on Linux; Windows is a -// development host. +// there and only there. Anvil's packet lives on tmpfs on Linux; Windows is +// only ever a build and test platform. func (q *Queue) WritePacket(h Handle, data []byte) (string, error) { return q.WritePacketContext(context.Background(), h, data) } diff --git a/internal/handoff/reaper.go b/internal/handoff/reaper.go index ca080e9..bdb5b9a 100644 --- a/internal/handoff/reaper.go +++ b/internal/handoff/reaper.go @@ -410,7 +410,7 @@ func (q *Queue) ReapContext(ctx context.Context) (ReapReport, error) { // // Found by CI, not locally: the race detector slows execution enough to widen // the cancel-during-sweep window from rare to reliable. It reproduced on -// ubuntu-latest under -race while passing every run on the Windows dev host, +// ubuntu-latest under -race while passing every run on the Windows machine development then used, // which is precisely why -race is a required check rather than an optional one. func (q *Queue) Run(ctx context.Context, interval time.Duration, observe func(ReapReport, error)) error { if interval <= 0 { diff --git a/internal/ingest/reconcile/reconcile_test.go b/internal/ingest/reconcile/reconcile_test.go index d159458..4ad0bb5 100644 --- a/internal/ingest/reconcile/reconcile_test.go +++ b/internal/ingest/reconcile/reconcile_test.go @@ -52,9 +52,8 @@ // httptest. NO TEST USES A REAL CREDENTIAL. // // A green run does NOT prove the self-heal is affordable against a 300,000 -// record cvelistV5 baseline; the fixtures here are tens of records. Nor does -// it prove anything about `go test -race`, which cannot run on the Windows dev -// host this was written on. +// record cvelistV5 baseline; the fixtures here are tens of records. `go test -race` covers +// this file on the Debian development machine (measured 2026-10-03) and in CI. package reconcile import ( @@ -1779,7 +1778,6 @@ func TestIntegrationNotesForTheManualRun(t *testing.T) { " - affordability. The fixtures are tens of records; a real cvelistV5 baseline is ~300,000", " records and ~570 MB, and neither the wall time of the merge join nor the disk cost of the", " scratch database has been measured against one.", - " - go test -race. It cannot run on the Windows dev host (cgo.exe exit 2); CI runs it on Linux.", " - that the bulk bootstrap's decoder and delta ingestion's decoder agree about every REAL publisher document. They agree", " about the synthetic CVE 5.1 documents in this file, which is what makes the 'matched' count", " meaningful here; a disagreement on a real corpus would surface as a wall of 'divergent' rows", diff --git a/internal/mirror/accelerator/reparse.go b/internal/mirror/accelerator/reparse.go index 3afb619..047b73e 100644 --- a/internal/mirror/accelerator/reparse.go +++ b/internal/mirror/accelerator/reparse.go @@ -37,7 +37,7 @@ package accelerator // understand IO_REPARSE_TAG_MOUNT_POINT. It is written in portable Go against // os and path/filepath only — no cgo, no golang.org/x/sys, no syscall, no build // tags — so on Linux and macOS it behaves exactly as symlink resolution always -// did, and the CI run and the Windows dev host execute the same code on the same +// did, and a Linux run and a Windows run execute the same code on the same // question. (Only the test that BUILDS a junction is platform-specific, in // reparse_windows_test.go, and it reaches package syscall rather than adding a // dependency.) diff --git a/internal/mirror/accelerator/reparse_test.go b/internal/mirror/accelerator/reparse_test.go index 4e9be76..9e5d640 100644 --- a/internal/mirror/accelerator/reparse_test.go +++ b/internal/mirror/accelerator/reparse_test.go @@ -258,7 +258,7 @@ func assertSamePath(t *testing.T, got, want string) { // A-1 itself. // // TestSymlinkedCacheRootIntoTheQuarantineIsRefused reported SKIP four times on -// the Windows dev host and the package still printed +// the Windows development machine and the package still printed // // ok github.com/Susquehanna-Syntax/Anvil/internal/mirror/accelerator // diff --git a/internal/mirror/accelerator/trivydb.go b/internal/mirror/accelerator/trivydb.go index 8ac9359..e64971d 100644 --- a/internal/mirror/accelerator/trivydb.go +++ b/internal/mirror/accelerator/trivydb.go @@ -617,8 +617,8 @@ const caseInsensitivePaths = runtime.GOOS == "windows" || runtime.GOOS == "darwi // onto the tier form, so it can only ever refuse more — and refusing a cache // root that spells itself "Tier2" costs nothing, because no legitimate cache // root distinguishes Tier2 from tier2. Making it conditional would mean the -// Linux CI run tests different code from the Windows dev host, on exactly the -// question the Windows host got wrong. +// Linux run tests different code from a Windows run, on exactly the question +// the Windows build got wrong. func foldPathComponent(c string) string { if c == "" || c == "." || c == ".." { return c diff --git a/internal/record/readpath.go b/internal/record/readpath.go index 500703f..197392b 100644 --- a/internal/record/readpath.go +++ b/internal/record/readpath.go @@ -1211,8 +1211,7 @@ func formatTime(t time.Time) string { // cardPath is the Tier-1 path for a finding id. // // Finding ids are `sast:8c1e4b0f…` in research/18's own example, and a colon -// is not a legal path character on Windows — where this project is being -// developed. The sanitisation is a total, deterministic function so the same +// is not a legal path character on Windows, where the test suite also runs. The sanitisation is a total, deterministic function so the same // finding always lands at the same path on every platform. func (rd *Reader) cardPath(findingID string) string { return rd.cardPrefix() + SanitizeCardFilename(findingID) + ".json" diff --git a/internal/scanctl/controller_review_test.go b/internal/scanctl/controller_review_test.go index 15ade3b..4956c32 100644 --- a/internal/scanctl/controller_review_test.go +++ b/internal/scanctl/controller_review_test.go @@ -369,9 +369,9 @@ func TestWriteGuardsConsultTheSealerNotTheCallersSnapshot(t *testing.T) { // saved the old design, which is why the fix was to move the buffers onto the // Controller rather than to write a warning. // -// -race cannot run on the Windows dev host; CI runs this on ubuntu-latest, where -// this repository has already had one concurrency bug that passed every local -// run (internal/handoff/reaper.go:415). +// Run it under -race: locally on Linux and in CI on ubuntu-latest. This +// repository has already had one concurrency bug that only -race found +// (internal/handoff/reaper.go's Run). func TestConcurrentFanInLosesNoFindings(t *testing.T) { const ( workers = 8 diff --git a/internal/scanctl/statemachine.go b/internal/scanctl/statemachine.go index 4d9be16..b5d6595 100644 --- a/internal/scanctl/statemachine.go +++ b/internal/scanctl/statemachine.go @@ -820,8 +820,8 @@ func NewController(policy DeadlinePolicy, marks WatermarkPolicy) (*Controller, e // IT TAKES THE MUTEX, for the same reason record.Sealer.SetClock takes its own // (controller-core finding m4). Construction-time use was always safe; a daemon that re-clocks at // runtime raced four readers — Transition, applyTick, publish and NextWake — -// and the race detector cannot run on the Windows dev host, so only CI would -// ever have seen it. +// and only the race detector sees that, so it must run (locally on Linux and in +// CI) for this to stay checked. func (c *Controller) SetClock(now func() time.Time) { if now == nil { now = time.Now diff --git a/test/conformance/lanea/lanea_test.go b/test/conformance/lanea/lanea_test.go index e1da89c..fc76c5b 100644 --- a/test/conformance/lanea/lanea_test.go +++ b/test/conformance/lanea/lanea_test.go @@ -107,9 +107,10 @@ // on this machine, and the corpus probe alone when none is. // // Both branches run the full chain. What differs is what the ledger may claim: -// on a Linux CI host the host-inventory link is PROVEN and names the collected -// package count, and on a developer's Windows machine it stays UNPROVEN and -// says so. THE LEDGER LINE NAMES WHICH — a run that proved the collector and a +// on a Linux host with a package manager (CI, and the Debian development +// machine since 2026-10-03) the host-inventory link is PROVEN and names the +// collected package count, and on a host with none it stays UNPROVEN and says +// so. THE LEDGER LINE NAMES WHICH — a run that proved the collector and a // run that proved only a file must not render the same, because telling those // two apart is the entire point of that link. // @@ -2009,8 +2010,8 @@ func hostSourceProblems(src hostInventorySource, collectedSubmitted int) []strin // TestTheHostLedgerLineNamesWhichInventoryItUsed is the control for the one // thing the host link exists to communicate. // -// THE REAL PATH CANNOT RUN ON A WINDOWS DEVELOPMENT HOST — there is no dpkg, -// rpm or apk, so host.Collect refuses and the chain above takes the probe +// THE REAL PATH CANNOT RUN ON A HOST WITHOUT dpkg, rpm OR apk (any non-Linux +// host, a minimal container), so host.Collect refuses and the chain above takes the probe // branch every time. Everything DOWNSTREAM of that call is host-independent // and is exercised here against two synthetic sources: a run that collected // real packages and a run that did not. They must not render the same, in the @@ -2168,8 +2169,8 @@ func TestTheHostTripwiresFireOnDroppedEvidence(t *testing.T) { // not the best thing this machine can offer. // // It is deliberately not a statement about which branch is correct HERE: on a -// Windows development host it asserts the probe branch, on a Linux CI host it -// asserts the collected branch, and either way it asserts that the chain and +// host with no package manager it asserts the probe branch, on a Linux host +// with one it asserts the collected branch, and either way it asserts that the chain and // the machine agree. func TestTheChainUsesTheBestAvailableHostEvidence(t *testing.T) { _, err := host.Collect(context.Background(), host.Options{