📝 docs(audit): pre-tag remediation — dead URLs, ADR truth, and the recorded deferrals - #42
Merged
Conversation
…th (DOC-02) The repo is named `Assent`. GitHub repo URLs are case-insensitive; GitHub Pages paths are not — so every in-tree `platformrelay.github.io/assent/...` reference 404s. Measured live: `/assent/` -> 404, `/Assent/` -> 200. Corrected in `cmd/assent/main.go` (the URL the released `assent --help` prints), the five README links, `docs/usage/cli.md` (byte-pinned against the help text, so both halves move together), `mkdocs.yml`'s `site_url` — which drives every page's canonical link and all 63 `<loc>` entries in `sitemap.xml` — plus D-044 and the E9 spec. `internal/catalogue`'s DocsBase casing goes with them. New pin in `hack/docs/truthlag_pins_test.sh` (already wired into `task check`): every `platformrelay.github.io` occurrence repo-wide, `.go` files included, must carry `mkdocs.yml`'s `site_url` prefix. It is a prefix check, not a liveness check; the comment says so and names the known dead path. D-044's URL is corrected in place rather than superseded: a dead link inside a record is a factual defect, not a decision to reverse.
…read (DOC-03) `run_render.go` attached `catalogue.DocsBase + "/" + pack + "/" + rule` to every rule on the run path, rendered as a `📖 [Full documentation](…)` link in the contributor's thread. No `rules/` space exists on the docs site — measured, `<site>/rules` and `<site>/rules/` both 404 against a 63-page sitemap — so the one affordance a blocked contributor clicks was dead on every finding. Casing does not fix it; the URL space does not exist in either case. The run path now carries only the rule's authored `docs.url`, and `renderDocsSection` already omits the line when that is empty: no link beats a broken one. Today the fallback is dormant BY SCHEMA, not merely unused — the frozen v1alpha1 merge-policy schema is `additionalProperties: false` over eight keys, so an authored rule-level `docs:` is rejected at load (audit ARCH-08). Both polarities pinned, since "no link" is also what deleting the feature would produce: absent -> no link and no docs-site URL anywhere in the body; authored -> that URL verbatim. Mutation-proven — restoring the minting reds both subtests. Building the `/rules/` pipeline stays out of scope. `assent catalogue`'s JSON still carries DocsURL (D-017 B10 field set, an openspec-level change); the constant now documents the dead space instead of implying it resolves.
…mit (SEC-01) This release hardens `--checkout` extensively (D-133 refuses any symlink anywhere) and the page already names "run without `--checkout`" as the first remedy — so it reads as though the flag has been made sound. It has not. Measured, not transcribed: `cmd/assent/checkout.go` contains zero occurrences of `SHA`/`Sha`, `run.go` has no step comparing the checkout to `pins.sourceSha`, and the pins and the compare-and-swap merge come from `info.SourceSHA` — the forge's view. So assent judges the tree it is handed and the forge merges the commit it knows about, with nothing relating the two. Stated as a property of how the checkout is CONSTRUCTED, which is what it is: an operator who builds `head/` from the merge-request head SHA and cancels superseded pipelines is unaffected. Deliberately claims no mitigation on assent's part — it probes no such project setting, and citing one it never reads is the pattern the audit found three times.
ADR-0013 Amendment 1 told authors that ordering raw text "graduates to the Rego escape hatch (ADR-0002)". There is no Rego backend: zero `opa`/`rego` module dependencies, no `rego` property in any frozen v1alpha1 policy schema, no backend selector. The pointer is replaced by the coercion idioms the same section already gives (`int(...)`, `double(...)`, `timestamp(...)`), the design-taste sentence is kept, and Rego is named as planned (E11, D-012) with no implementation today. `internal/core/aggregate/evaluate.go` carried the same false pointer in a comment — a second copy the finding did not name — and is corrected with it. This matters now because D-131's compatibility note ships in the v0.2.0 Release body addressed to anyone whose policy orders text on purpose: the tag manufactures the readership that clicks through. ADR-0002's Status read a plain "Accepted (v2)" while README calls the Rego backend "Locked — E11" and the C4 page "PLANNED — E11" — the ADR was the only surface claiming it exists. Annotated. ADR-0006's Status asserted "CI e2e default: GitLab CE testcontainer". No CI job boots one: `test/e2e/**` is behind `//go:build e2e` and `verify.yaml`'s only e2e touch is `go vet -tags e2e ./...`, which compiles and runs nothing. ADR-0019 §"Multi-replica HA" carries the ADR set's only unmet MUST: `assent doctor` MUST emit `duplicate_prevention:`. The value is computed (`doctor_forge.go:19`) and dropped by `emitDoctorReport`. Annotated as unmet rather than emitted: this is one instance of audit ARCH-11 (doctor prints essentially none of its typed report), so emitting one field would half-close it and leave the report inconsistent — and a user-visible CLI output change does not belong in a docs-truth lane. The ADR index rows for 0002/0006/0009/0019 are updated too. D-135 claimed its Status extension made the ADR-0009 gap visible on the index; it did not, because the index is a separate file with its own column — a false claim inside a correction about a false claim. Leaving 0002/0006/0019's rows plain would have reproduced exactly that.
…is not shipped D-134's "Corrected surfaces" clause recommended staying advisory by leaving a precondition unmet "or `phase: observe`" — advice the same row retracts ~2,000 words later as ARM-08, "false in the unsafe direction, twice over". A reader who stops at the natural stopping point left with the retracted advice. The clause now carries the retraction inline. Factual-record correction, not a decision reversal. D-041 gets a `Superseded by D-134` pointer appended; its content is byte-unchanged per GUIDELINES (supersede, don't edit). `vision.md` listed a "local dry-run" among the SHIPPED modes. Measured in this worktree: `assent run --dry-run` prints `flag provided but not defined: -dry-run` and exits 2. Corrected to one shipped mode and four planned. Note that the commit which introduced this (f2f59ed) was itself a docs-truth fix — it split the paragraph into shipped vs planned and promoted dry-run into the shipped half, making the false claim stronger and load-bearing.
…-31); OQ-29 to P1 AGENTS.md rule 6 — the audit's reliability and security lenses ruled six P1/P2 findings not blocking for v0.2.0, so the deferral gets written down rather than carried in a session. D-138 (RELI-01/02/03): all pre-existing at v0.1.0, verified at 1d8aa60; the machine direction holds — no probed path merges anything unevaluated — and the failure is confined to the human signal channel. Records that D-130's compensating control does NOT reach the GUARD-1 self-edit path, because no thread is posted there, so nothing holds the merge and no later run repairs the banner; that RELI-01's fix reopens a frozen acceptance criterion and so is out of release scope by construction; and that RELI-02 is unrecoverable without a human, with the race as the declared default posture. Also folds in the ADR-0019 `duplicate_prevention:` annotate-don't-emit call, same human-signal shape. D-139 (SEC-01/04/05) with the named fixes so the next lane is unambiguous, the discharged condition that the release must not read as though `--checkout` has been made safe, and the SEC-05 x SEC-03 composition recorded explicitly AS A HYPOTHESIS — no evidence real GitLab omits source_project_id. The lens withdrew its SEC-08 as non-novel (D-130 covers it); not written up. OQ-31: may the GUARD-1 BLOCK path write a summary or supersession note, and if not, what channel carries BLOCK to a human given no thread and exit 0? Undecided. RELI-03 gets no OQ — D11 already decided it; it is unbuilt, not undecided. OQ-29 raised to P1: its escalation condition ("if docs/architecture/ ever enters the nav") was already true when written. MkDocs publishes every file in docs_dir regardless of nav — measured, architecture/policy-profiles/ returns 200, the sitemap carries 63 locs against ~10 nav entries, and the false invariant is in search_index.json. GUIDELINES 57-58 is read as a publication boundary but creates only a nav boundary. Plus the RELI-03 comment at internal/forge/forge.go, corrected in place: it cited reset_approvals_on_push as its safety argument, a setting that appears in zero Go files, and deferred to P4-E1-S10, a slice that shipped. Text only, no behaviour change — an asserted-and-unbuilt mitigation terminates the search, which is how this survived two prior audits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Remediation lane for the pre-tag items of the 2026-08-09 project audit. Text and records
only, with two deliberate exceptions noted below. Rebased onto
mainafter #41 merged.Every claim landed here was executed, not reasoned — the audit's root finding is that claims
in this repo get written from another agent's prose.
Group 1 — the dead-URL family (DOC-02, P1)
The repo is named
Assent. GitHub repo URLs are case-insensitive; Pages paths are not.Fixed in
cmd/assent/main.go(the URL the released binary prints), the five README links,docs/usage/cli.md,mkdocs.yml'ssite_url(which drives every canonical link and all 63sitemap.xml<loc>entries — all of them currently dead), D-044, the E9 spec, andinternal/catalogue'sDocsBase. Swept, not worked from a list;CHANGELOG.mdand commitmessages contain zero occurrences, so the gate is safe repo-wide.
cli.md:58sits in the byte-pinned help block:TestCLIDocCoversSubcommandswas observedred with only
main.gochanged, then green with both halves.New gate in
hack/docs/truthlag_pins_test.sh(already intask check): everyplatformrelay.github.iooccurrence repo-wide,.goincluded, must carrysite_url's prefix.Four proofs: a lowercase flip reds naming the file; mutating
site_urlreds 11; a missingsite_urlreds rather than passing; a needle matching nothing reds as vacuous.It is a prefix check, not a liveness check, and the comment says so and names the known
dead path — a gate that greenlights a URL we know 404s is the pattern the audit indicts.
Group 2 — the dead rule-docs URL (DOC-03, P1)
/Assent/rulesand/Assent/rules/both 404 against a 63-page sitemap. Casing cannot fix it.The run path no longer mints; it carries the rule's authored
docs.url, andrenderDocsSectionalready omits the line when empty.Nothing red'd when the minting was removed — a test that could not fail — so both polarities
are now pinned and mutation-proven against the restored minting. Render goldens are
unaffected (none contained the URL);
internal/renderandexamples/renderpass.Honest limitation: the fallback is dormant by schema, not merely unused — the frozen
merge-policy schema is
additionalProperties: falseover eight keys, so an authored rule-leveldocs:is rejected at load (ARCH-08). Present-day behaviour is "no documentation link", whichis the point.
assent catalogue's JSON still carriesDocsURL(D-017 B10 field set — anopenspec change); the constant documents the dead space rather than implying it resolves.
Group 3 — ADR truth
ADR-0013 Amendment 1's Rego pointer is dropped: zero
opa/regodeps, noregoproperty inany frozen schema. The same false pointer existed in production Go (
evaluate.go) and iscorrected with it — a copy the finding did not name. ADR-0002 and ADR-0006 annotated (CI's only
e2e touch is
go vet -tags e2e, which runs nothing). ADR-0009's index row corrected — and0002/0006/0019's too, since leaving them plain would reproduce D-135's own error.
Judgment call — ADR-0019's unmet MUST: annotated, not emitted. It is one instance of
ARCH-11 (doctor prints essentially none of its typed report), so emitting one field would
half-close it and leave the report inconsistent with itself; and it is a user-visible CLI
output change, which does not belong in a docs lane before a tag. Recorded in D-138 and queued
for the v0.2.1 ARCH-11 slice.
Group 4 — decision-log truth
D-134's "Corrected surfaces" carried
phase: observeadvice the same row retracts ~2,000 wordslater as ARM-08; the retraction now sits at the natural stopping point. D-041 gets a supersede
pointer, content byte-unchanged.
vision.mdcorrected — measured here:assent run --dry-run->flag provided but not defined: -dry-run, exit 2. The commit thatintroduced that claim was itself a docs-truth fix that promoted dry-run into the shipped half.
Group 5 — recorded deferrals
D-138 (RELI-01/02/03) and D-139 (SEC-01/04/05) — two rows, not one, because a single
3,000-word cell would reproduce Group 4.1's exact defect. OQ-31 records the GUARD-1
communication question, undecided. OQ-29 raised to P1: its escalation condition was already
true — MkDocs publishes every file in
docs_dirregardless ofnav;architecture/policy-profiles/returns 200, the sitemap has 63 entries against ~10 naventries, and the false invariant is in
search_index.json(420 sections indexed).Also
internal/forge/forge.go's RELI-03 comment, corrected in place: it citedreset_approvals_on_push(zero Go files) and deferred to P4-E1-S10, a slice that shipped.cli.mdgains the--checkoutknown-limitation section required by the security lens —framed as how the checkout is constructed, claiming no mitigation the tool performs.
Deliberate exceptions to "no behaviour change"
factual defect, not a decision to reverse.
Judgment call for the operator: rename the repo?
Renaming
Assent->assentwould fix this at the source and is the better permanent answer —everything else already spells it lowercase (module path,
repo_url,site_name).I do not think this PR is a workaround pending that. It is correct today, and it makes the
rename cheaper: with the gate in place a rename becomes a one-line
site_urlchange plus amechanical sweep the gate itself verifies. Note the operator's
homepageUrlis already/Assent/, so the live config agrees with the capital-A world, and a rename inverts everyreference here — it is not a drop-in.
Gates
task check— all 14 stage banners, EXIT=0, coverage 90.4%, clean tree, withgolangci-lint cache cleanand isolatedGOCACHE/GOLANGCI_LINT_CACHE:fmt · vet · lint · test · coverage · build · dogfood-comparison · compare-exitgate-test · changelog-verify · release-changelog-gate-test · release-verify-tag-gate-test · docs-gates · lint-depguard-test · lint-workflow-pins-testAn intermediate run reported EXIT=201 at
changelog-verify(the changelog-last step), sothe exit code was read from the log, not inferred.
Rebased with
rereredisabled; verified by set difference and row content that no decisionrow was dropped — D-136/D-137 are byte-identical to
main, and the only rows that differ areD-041, D-044 and D-134.
Do not merge without review.
Found but deliberately NOT fixed here
Four decision rows contain unescaped
|inside inline code, so they render with phantomcolumns on the published site (
decisions/decisions/is in the nav):D-135 is the one that matters. It renders as 8 cells, and D-135 is both the row
ADR-0009's Status line sends readers to and a row whose entire subject is a docs-truth
correction. So the correction about a false claim is itself mangled on the published page —
a further instance of this audit's root finding, not a formatting nitpick.
Pre-existing, not raised by the audit, found by a column-count scan while checking my own rows
(mine are correct: D-138/D-139 = 3 cells, OQ-31 = 4).
Sweep boundary, stated: the scan covered
docs/decisions/decisions.mdanddocs/planning/open-questions.mdonly. It did not coverdocs/adr/README.mdor the ADRStatus rows, which are also tables and which this PR edits — those edits add no pipes, but they
are unscanned.
Not fixed in this lane on purpose: it edits four decision rows including two very recent ones,
which invites rebase conflicts with other lanes, and the durable answer is a gate rather than a
one-off escape. Suggested follow-up: escape the pipes and add a ~6-line column-count pin to
hack/docs/truthlag_pins_test.sh, the same cheapest-gate shape as the DOC-02 pin in this PR.