chore(repo): untrack generated artifacts and anchor the evidence ignore rule - #210
Conversation
buildFactoryExecutionPlan pushed RedesignIntegrityReceiptStage after
ReleaseReceiptFinalizerStage and HandoffEmitterStage. For an end-to-end
REDESIGN_IMPROVE run the emitted order was:
... visual-qa, release-receipt-finalizer, handoff-emitter,
redesign-integrity-receipt, terminal-convergence
The finalizer writes status: "succeeded" to the release receipt and the
emitter performs the external POST to SEO-Bot plus recordWebsiteRelease
(externalMutation: true). A gate in that position can only reject a run
that has already been recorded as successful and handed off — it cannot
prevent one. An invalid redesign was therefore publishable.
The gate now runs after visual-qa, whose ctx.qualityEvidence.visualQa it
reads, and before the finalizer and emitter. It stays outside the
end-to-end guard on purpose: REDESIGN_ADDED_MANDATORY makes
redesign-integrity-receipt mandatory in every mode, so folding it into
that branch would make requireMandatoryConvergence throw on plan,
local-proof and publish-proof redesign runs.
No stage, receipt type, or handoff contract is added — this is a reorder.
The gate reads only context produced upstream of its new position and
declares no evidence block, so it imposes no runner ordering constraint.
The COPY end-to-end stage list is unchanged; the existing deepEqual
assertion in tests/unit/factory-execution-plan.test.ts passes unmodified.
Two intended consequences:
- On the visual-QA-not-passed path a redesign end-to-end run now fails as
REDESIGN_PIPELINE_INCOMPLETE rather than RELEASE_EVIDENCE_INCOMPLETE.
That is the fail-closed-earlier behaviour, not a regression.
- executed_stages in the emitted receipt no longer lists
release-receipt-finalizer or handoff-emitter, since it snapshots
ctx.stageResults at emit time. Nothing validates that field.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
…plan The durable path computed ctx.buildIntent from the spec and then built its execution plan without it, so buildFactoryExecutionPlan always derived redesign === false there. A REDESIGN_IMPROVE spec submitted through Inngest therefore ran the COPY topology: no seo-build-intelligence-preflight, no competitive-intelligence, the legacy content/schema stages instead of the redesign authority chain, and no redesign-integrity-receipt stage at all. The build context said one thing and the plan built another. This also bounds the preceding commit. Reordering the integrity gate ahead of the finalizer and emitter fixes the plan builder, but on this path the gate was never in the plan to begin with, so that fix would have been inert here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
There was no regression coverage for the terminal ordering of a redesign
plan. The two existing REDESIGN_IMPROVE tests assert only the head of the
pipeline (preflight before competitive-intelligence, competitive-intelligence
before design-intelligence), and redesign-integrity-receipt.test.ts asserts
that the stage is present, not where. All of them passed with the gate sitting
after the finalizer and the external handoff.
This asserts the chain that actually matters:
visual-qa < redesign-integrity-receipt < release-receipt-finalizer
< handoff-emitter < terminal-convergence
Verified to discriminate: against the parent commit's plan builder it fails
(8 pass / 1 fail); against the reordered one it passes (9 pass). A test that
cannot fail proves nothing, so it was run both ways before being committed.
The COPY end-to-end deepEqual assertion is deliberately left untouched — it
is the byte-identity guard proving the reorder did not disturb the non-redesign
topology, and editing it would destroy the evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
executed_stages and the STAGES list that drives synthetic checkpoint timestamps both hard-code the pipeline's terminal order as static data rather than deriving it from buildFactoryExecutionPlan. They still carried the pre-fix tail, with redesign-integrity-receipt trailing the finalizer and the handoff emitter. Nothing fails as a result — the golden oracle checks a relative ordered subsequence that omits both of those stages, so it is satisfied by either order. That is precisely the problem: the fixtures would have gone on describing an ordering the code no longer has, and a reviewer re-deriving a fixture would have been misled by it. Data-only; no generator logic changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
validation/validation_report.yaml is per-run output of scripts/validation-executor.ts, but it was tracked. Every `npm run site:validate` — and therefore every `verify:all` and every `make pr` — rewrote it with a fresh run_id, fresh timestamps, the local absolute target_roots and the running user's git identity. That is not cosmetic. It hard-blocks the sanctioned publish path: the L4 release receipt is bound to a tree digest, so `make pr` runs `pr-check`, `pr-check` dirties this file, and the L4 remote check then rejects the push as a stale receipt. The gate invalidates the authorization it is about to verify, and no amount of re-authorizing escapes it, because the next run dirties the tree again. Untracking it is the repo's own established convention, not a new one: .gitignore already ignores validation/launch_env_report.json and validation/deploy_secrets_report.json, which are the same class of per-run artifact from the same directory. Nothing consumes the committed copy. scripts/validation-executor.ts only writes it, no test or script reads it, and build-and-validate.yml generates its own at the repository root and uploads that as a workflow artifact. The file stays on disk; only the index entry is removed. As a side benefit the committed copy stops leaking one contributor's worktree path and email address into the repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
This repo is npm-only: .github/workflows/l9-lint-test.yml and l9-lint-test-node.yml both pin PACKAGE_MANAGER="npm", regen-lockfile.yml knows only `npm install --package-lock-only`, and four package-lock.json files are tracked. .gitignore carried no lockfile entries at all, so a stray pnpm-lock.yaml sat untracked and committable. It is container debris — never tracked on any ref, and its mtime matches every other file in the clone rather than any commit. Left unignored it is one `git add -A` away from landing, and the dormant pnpm/yarn arms of the lint workflows would treat it as load-bearing the moment PACKAGE_MANAGER moved. Appended at end of file rather than in the secrets block: PR #207 deletes .gitignore:41, and a hunk there would overlap one written nearby. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L (cherry picked from commit 3bf384caca440a43d4e5466957c671b35c353057)
`evidence/` was unanchored, so it matched a directory of that name at any
depth. `git ls-files -ci --exclude-standard` therefore reported 255 tracked
files as ignored, and 148 of those were not debris at all:
- src/pipeline/evidence/*.ts — 21 files, the pipeline's evidence layer
(EvidenceStore, ReleaseReceipt, RedesignExecutionIntegrityReceipt, ...)
- docs/reports_and_test_results/**/evidence/** — 127 files, dated report
bundles someone committed deliberately
Nothing was broken while they stayed tracked, because .gitignore does not
apply to files already in the index. The hazard is the cleanup: a
mechanically derived `git rm --cached $(git ls-files -ci)` would have
untracked the pipeline's evidence layer, and local validation would still
have passed because the files remain on disk. Only a fresh clone would have
failed.
Anchoring to /evidence/ confines the rule to the repository root, which is
where the Safe Haven per-run evidence it was written for actually lives. The
count drops 255 -> 107, and every remaining entry is genuine generated
output.
This lands as its own commit, before any untracking, so the delete list in
the next commit can be derived mechanically from a rule that is already
correct.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
105 files whose directories .gitignore already names, but which were added
to the index before or despite those rules. Ignore rules do not apply to
tracked files, so they kept accumulating:
- build/** 99 files across 61 golden run directories (~9.2 MB): 62
pipeline.log files and 20 PNG captures, several over 1 MB. .gitignore:12
says `build/` and README.md:193 says the same in prose.
- evidence/** 4 files. .gitignore:45 names the directory as per-run Safe
Haven output.
- astro_template/validation/*.jsonl 2 files, named at .gitignore:23.
Write-only: nothing reads them.
The list was derived mechanically from `git ls-files -ci --exclude-standard`
rather than hand-written, which is what makes a 105-file delete reviewable —
it is exactly the set git itself reports as tracked-but-ignored. That is only
safe because the preceding commit anchored `evidence/`; run against the old
rule the same command would have removed src/pipeline/evidence/*.ts.
Two entries are excluded deliberately, both tracked on purpose:
- .l9/ci.json, re-included by `!.l9/ci.json` as the consumer-owned CI class
declaration org-ci.yml reads
- tests/golden/safehaven/fixtures/positive-receipt.json, which
tests/golden/safehaven/RUNBOOK.md documents as a built fixture
Index-only: every file stays on disk and regenerates from its own tooling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
Four files, none referenced by any script, test, workflow or document.
Root run-artifacts, captured from one local run and committed:
- validation_node_check.txt — 0 bytes, the only empty tracked file in the
repository
- validation_command_results.txt — 30 bytes, entirely
`verify_launch_env_exit_code=1`
- launch_env_fail_closed_output.json — a dated per-run report
(timestamp_utc 2026-06-13). .gitignore:21 already ignores
validation/launch_env_report.json, the same class of artifact from the
same tool; this one just happened to be written at the repository root
instead.
docs/recursive_optimization_report.md — a duplicate. docs/archive/ holds a
copy with different content that was archived deliberately in #125 and is
listed in docs/archive/README.md against ADR-0017. This root copy carries no
archive banner, is referenced by nothing, arrived incidentally in an
unrelated llm-router commit, and opens by naming a path that does not exist
in this repository:
## Artifact Group: /home/ubuntu/l9-website-bot (full repo)
The archived copy is the one the index points at and is untouched.
ROOT_FILE_VALIDATION.md is deliberately NOT updated. Its "Files in final
pack: 19" counts a curated documentation pack rather than tracked root files
(the real root count was already 35 before this commit), and none of the
files removed here are enumerated in it. It is a dated point-in-time record;
editing its numbers to match today would falsify it rather than correct it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
🟡 Changes recommended
The safehaven golden fixture builder’s stage lists are inconsistent with the real REDESIGN_IMPROVE plan (missing design-reference-acquisition), so fixtures can diverge from actual stage history/order.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Repository hygiene work to ensure generated/per-run artifacts aren’t tracked and that ignore rules don’t accidentally sweep real source files (notably anchoring the root evidence/ ignore). In the currently-shown diff (because this PR is stacked), there are also pipeline-plan ordering updates and a regression test/fixture update around the redesign integrity gate.
Changes:
- Update
.gitignoreto anchor/evidence/, ignore the validation report output undervalidation/, and ignore non-npm lockfiles. - Remove tracked generated artifacts (validation reports, golden evidence bundles/logs, astro template validation JSONL outputs, and an orphaned doc).
- (Stacked changes) Ensure redesign integrity gating is ordered before success finalization/handoff, propagate
buildIntentinto the durable plan builder, and update related tests/fixtures.
File summaries
| File | Description |
|---|---|
.gitignore |
Anchors root evidence ignore and expands ignore coverage for generated artifacts / non-npm lockfiles. |
astro_template/validation/preflight_checks.jsonl |
Removes tracked generated Astro template validation output. |
astro_template/validation/source_checks.jsonl |
Removes tracked generated Astro template validation output. |
build/golden/golden-20260824-r12/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r13/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r2/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r27/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r3/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r35/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r36/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r37/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824-r4/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260824/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260825-r59/pipeline.log |
Removes tracked golden run log artifact under build/. |
build/golden/golden-20260825-r61/GOLDEN-REPORT.md |
Removes tracked golden run report artifact under build/. |
build/golden/golden-20260825-r61/candidate-serve.log |
Removes tracked local serve log artifact under build/. |
build/golden/golden-20260825-r61/golden-oracle-result.json |
Removes tracked golden oracle output artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/fetch-meta.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/health.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/identity-snapshot.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/missing.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/preflight.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/seo-bot-evidence/sequence.json |
Removes tracked golden SEO-Bot evidence artifact under build/. |
build/golden/golden-20260825-r61/site-integrity.json |
Removes tracked golden site-integrity output artifact under build/. |
build/golden/golden-20260825-r61/visual/aggregated/aggregate.json |
Removes tracked golden visual aggregation artifact under build/. |
build/golden/golden-20260825-r61/visual/manifest.json |
Removes tracked golden visual manifest artifact under build/. |
docs/recursive_optimization_report.md |
Removes an orphaned/duplicated report doc. |
evidence/golden-negative-controls.json |
Removes tracked per-run evidence output under root evidence/. |
evidence/golden-positive-control.json |
Removes tracked per-run evidence output under root evidence/. |
evidence/synthetic-real-mode-rejection.json |
Removes tracked per-run evidence output under root evidence/. |
launch_env_fail_closed_output.json |
Removes tracked per-run launch-env output artifact from repo root. |
scripts/golden-safehaven/fixtures/build-fixtures.mjs |
Updates safehaven golden fixture stage ordering to match pipeline ordering changes. |
src/inngest/website-pipeline.ts |
Passes buildIntent into plan builder for durable pipeline execution. |
src/pipeline/FactoryExecutionPlan.ts |
Reorders redesign-integrity gating to occur before success finalization and handoff emission. |
tests/unit/factory-execution-plan.test.ts |
Adds a regression test to enforce redesign gate ordering relative to finalization/handoff. |
validation/validation_report.yaml |
Removes tracked validation report output (generated per-run). |
validation_command_results.txt |
Removes orphaned validation output artifact. |
validation_node_check.txt |
Removes orphaned validation output artifact. |
Review details
- Files reviewed: 10/115 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Both hard-coded stage lists in the safehaven fixture builder went seo-build-intelligence-preflight -> competitive-intelligence, skipping design-reference-acquisition. The real REDESIGN_IMPROVE plan puts it between those two (FactoryExecutionPlan.ts:275-279), and it is listed in REDESIGN_ADDED_MANDATORY, so it is mandatory for every redesign run in every mode. The fixture declares mode: "end-to-end" with build_intent: "REDESIGN_IMPROVE" and claims to represent a green run of exactly that plan, so a stage the plan cannot omit was missing from the record. executed_stages fed the emitted receipt and STAGES drives the synthetic checkpoint files and the SQLite stage_runs rows, so both the receipt and the simulated stage history described a run that could not occur. Verified against the plan builder rather than by eye: an end-to-end REDESIGN_IMPROVE plan emits ["seo-build-intelligence-preflight", "design-reference-acquisition", "competitive-intelligence"] at that point, which is now what both lists contain. Found by Copilot's review on the stacked PR #210. It is a pre-existing gap rather than one the preceding fixture commit introduced — that commit corrected the tail of these lists — but the file is this PR's to get right, and a fixture that misdescribes the pipeline is worse than no fixture. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
…hygiene-untrack-generated-v2
Audit finding F2 of website-bot-all-open-prs-2026-09-17, against this PR. docs/reports_and_test_results/08-18-2026/GOLDEN_E2E_FINAL_REPORT.md is a retained report that cites two artifacts as its evidence — and calls them "never hand-edited" and "never edited by hand", which is the whole point of citing them: build/golden/golden-20260825-r61/pipeline.log build/golden/golden-20260825-r61/golden-oracle-result.json This PR untracked both as generated output under build/, so the report's evidence trail resolved to nothing. Before untracking I checked that no script, test or workflow READS those files, and none does. I never checked whether a retained document CITES them, which is exactly what a golden-run proof report does. That was the gap. Both are relocated verbatim from origin/main into evidence/ beside the report that depends on them, and the three citations are repointed. Byte-identity is the property that matters here, so it was verified rather than assumed: the recovered files hash to 3b52732 and eeba265, the same blobs origin/main carries. A regenerated substitute would have satisfied the link and destroyed the claim. docs/reports_and_test_results/**/evidence/ is the existing home for this (08-15-2026 already uses it) and is tracked again since this PR anchored the `evidence/` rule, so no new evidence location is introduced. .gitignore needed one addition: `*.log` at line 11 matches at any depth, so the relocated pipeline.log was silently not staged. `git add -f` would have tracked it while leaving it ignored — precisely the tracked-but-ignored state this PR exists to eliminate, and how 255 files accumulated in the first place. The negation instead follows the repo's own precedent for retained logs (!reports/test-runs/**/*.log) and is scoped to report evidence: build/, root evidence/ and astro_template/ stay ignored, and `git ls-files -ci` still returns only the two deliberate re-inclusions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
|
| Broken artifact | Cited by |
|---|---|
evidence/oracle-coverage.json |
docs/reports_and_test_results/08-18-2026/ORACLE_COVERAGE_AND_SOUNDNESS.md, …/08-18-2026/README.md, tests/golden/safehaven/RUNBOOK.md |
evidence/golden-positive-control.json |
tests/golden/safehaven/RUNBOOK.md |
evidence/golden-negative-controls.json |
tests/golden/safehaven/RUNBOOK.md |
evidence/synthetic-real-mode-rejection.json |
tests/golden/safehaven/RUNBOOK.md |
The contract's F2 write surfaces are GOLDEN_E2E_FINAL_REPORT.md and the two build/golden/golden-20260825-r61/ artifacts. Neither these four files nor their three citing documents are listed, and the contract says to stop with SCOPE_EXTENSION_REQUIRED when a required path falls outside them.
Proposed remedy, identical in shape to the one already applied — recover each artifact verbatim from origin/main, relocate to an evidence/ directory beside the document that cites it, repoint the citation, and verify byte-identity by git blob hash. No new evidence subsystem; the .gitignore negation added in 9007b4c already covers docs/reports_and_test_results/**/evidence/. tests/golden/safehaven/RUNBOOK.md would need its own decision, since it is not under docs/.
Granting the extension is a human call. I have not touched those paths.
Note on my own diligence
Before untracking, I verified that no script, test or workflow reads the removed artifacts — none does. I never checked whether a retained document cites them as proof, which is exactly what a golden-run report does. The audit caught the first instance; the sweep above is me checking the rest of my own work rather than waiting to be told again.
Generated by Claude Code
|
* fix(pipeline): gate redesign integrity before success and handoff
buildFactoryExecutionPlan pushed RedesignIntegrityReceiptStage after
ReleaseReceiptFinalizerStage and HandoffEmitterStage. For an end-to-end
REDESIGN_IMPROVE run the emitted order was:
... visual-qa, release-receipt-finalizer, handoff-emitter,
redesign-integrity-receipt, terminal-convergence
The finalizer writes status: "succeeded" to the release receipt and the
emitter performs the external POST to SEO-Bot plus recordWebsiteRelease
(externalMutation: true). A gate in that position can only reject a run
that has already been recorded as successful and handed off — it cannot
prevent one. An invalid redesign was therefore publishable.
The gate now runs after visual-qa, whose ctx.qualityEvidence.visualQa it
reads, and before the finalizer and emitter. It stays outside the
end-to-end guard on purpose: REDESIGN_ADDED_MANDATORY makes
redesign-integrity-receipt mandatory in every mode, so folding it into
that branch would make requireMandatoryConvergence throw on plan,
local-proof and publish-proof redesign runs.
No stage, receipt type, or handoff contract is added — this is a reorder.
The gate reads only context produced upstream of its new position and
declares no evidence block, so it imposes no runner ordering constraint.
The COPY end-to-end stage list is unchanged; the existing deepEqual
assertion in tests/unit/factory-execution-plan.test.ts passes unmodified.
Two intended consequences:
- On the visual-QA-not-passed path a redesign end-to-end run now fails as
REDESIGN_PIPELINE_INCOMPLETE rather than RELEASE_EVIDENCE_INCOMPLETE.
That is the fail-closed-earlier behaviour, not a regression.
- executed_stages in the emitted receipt no longer lists
release-receipt-finalizer or handoff-emitter, since it snapshots
ctx.stageResults at emit time. Nothing validates that field.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(inngest): pass buildIntent into the durable pipeline's execution plan
The durable path computed ctx.buildIntent from the spec and then built its
execution plan without it, so buildFactoryExecutionPlan always derived
redesign === false there.
A REDESIGN_IMPROVE spec submitted through Inngest therefore ran the COPY
topology: no seo-build-intelligence-preflight, no competitive-intelligence,
the legacy content/schema stages instead of the redesign authority chain,
and no redesign-integrity-receipt stage at all. The build context said one
thing and the plan built another.
This also bounds the preceding commit. Reordering the integrity gate ahead
of the finalizer and emitter fixes the plan builder, but on this path the
gate was never in the plan to begin with, so that fix would have been inert
here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* test(pipeline): lock the redesign gate ahead of the success receipt
There was no regression coverage for the terminal ordering of a redesign
plan. The two existing REDESIGN_IMPROVE tests assert only the head of the
pipeline (preflight before competitive-intelligence, competitive-intelligence
before design-intelligence), and redesign-integrity-receipt.test.ts asserts
that the stage is present, not where. All of them passed with the gate sitting
after the finalizer and the external handoff.
This asserts the chain that actually matters:
visual-qa < redesign-integrity-receipt < release-receipt-finalizer
< handoff-emitter < terminal-convergence
Verified to discriminate: against the parent commit's plan builder it fails
(8 pass / 1 fail); against the reordered one it passes (9 pass). A test that
cannot fail proves nothing, so it was run both ways before being committed.
The COPY end-to-end deepEqual assertion is deliberately left untouched — it
is the byte-identity guard proving the reorder did not disturb the non-redesign
topology, and editing it would destroy the evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(golden): correct the hard-coded stage tail in the fixture builder
executed_stages and the STAGES list that drives synthetic checkpoint
timestamps both hard-code the pipeline's terminal order as static data
rather than deriving it from buildFactoryExecutionPlan. They still carried
the pre-fix tail, with redesign-integrity-receipt trailing the finalizer
and the handoff emitter.
Nothing fails as a result — the golden oracle checks a relative ordered
subsequence that omits both of those stages, so it is satisfied by either
order. That is precisely the problem: the fixtures would have gone on
describing an ordering the code no longer has, and a reviewer re-deriving
a fixture would have been misled by it.
Data-only; no generator logic changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(repo): stop site:validate dirtying a tracked report
validation/validation_report.yaml is per-run output of
scripts/validation-executor.ts, but it was tracked. Every `npm run
site:validate` — and therefore every `verify:all` and every `make pr` —
rewrote it with a fresh run_id, fresh timestamps, the local absolute
target_roots and the running user's git identity.
That is not cosmetic. It hard-blocks the sanctioned publish path: the L4
release receipt is bound to a tree digest, so `make pr` runs `pr-check`,
`pr-check` dirties this file, and the L4 remote check then rejects the
push as a stale receipt. The gate invalidates the authorization it is
about to verify, and no amount of re-authorizing escapes it, because the
next run dirties the tree again.
Untracking it is the repo's own established convention, not a new one:
.gitignore already ignores validation/launch_env_report.json and
validation/deploy_secrets_report.json, which are the same class of
per-run artifact from the same directory.
Nothing consumes the committed copy. scripts/validation-executor.ts only
writes it, no test or script reads it, and build-and-validate.yml
generates its own at the repository root and uploads that as a workflow
artifact.
The file stays on disk; only the index entry is removed. As a side
benefit the committed copy stops leaking one contributor's worktree path
and email address into the repository.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(gitignore): ignore non-npm lockfiles
This repo is npm-only: .github/workflows/l9-lint-test.yml and
l9-lint-test-node.yml both pin PACKAGE_MANAGER="npm", regen-lockfile.yml
knows only `npm install --package-lock-only`, and four package-lock.json
files are tracked. .gitignore carried no lockfile entries at all, so a
stray pnpm-lock.yaml sat untracked and committable.
It is container debris — never tracked on any ref, and its mtime matches
every other file in the clone rather than any commit. Left unignored it is
one `git add -A` away from landing, and the dormant pnpm/yarn arms of the
lint workflows would treat it as load-bearing the moment PACKAGE_MANAGER
moved.
Appended at end of file rather than in the secrets block: PR #207 deletes
.gitignore:41, and a hunk there would overlap one written nearby.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
(cherry picked from commit 3bf384caca440a43d4e5466957c671b35c353057)
* fix(golden): add the missing mandatory redesign stage to the fixture
Both hard-coded stage lists in the safehaven fixture builder went
seo-build-intelligence-preflight -> competitive-intelligence, skipping
design-reference-acquisition. The real REDESIGN_IMPROVE plan puts it between
those two (FactoryExecutionPlan.ts:275-279), and it is listed in
REDESIGN_ADDED_MANDATORY, so it is mandatory for every redesign run in every
mode.
The fixture declares mode: "end-to-end" with build_intent:
"REDESIGN_IMPROVE" and claims to represent a green run of exactly that plan,
so a stage the plan cannot omit was missing from the record. executed_stages
fed the emitted receipt and STAGES drives the synthetic checkpoint files and
the SQLite stage_runs rows, so both the receipt and the simulated stage
history described a run that could not occur.
Verified against the plan builder rather than by eye: an end-to-end
REDESIGN_IMPROVE plan emits ["seo-build-intelligence-preflight",
"design-reference-acquisition", "competitive-intelligence"] at that point,
which is now what both lists contain.
Found by Copilot's review on the stacked PR #210. It is a pre-existing gap
rather than one the preceding fixture commit introduced — that commit
corrected the tail of these lists — but the file is this PR's to get right,
and a fixture that misdescribes the pipeline is worse than no fixture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(pipeline): gate redesign integrity before success and handoff
buildFactoryExecutionPlan pushed RedesignIntegrityReceiptStage after
ReleaseReceiptFinalizerStage and HandoffEmitterStage. For an end-to-end
REDESIGN_IMPROVE run the emitted order was:
... visual-qa, release-receipt-finalizer, handoff-emitter,
redesign-integrity-receipt, terminal-convergence
The finalizer writes status: "succeeded" to the release receipt and the
emitter performs the external POST to SEO-Bot plus recordWebsiteRelease
(externalMutation: true). A gate in that position can only reject a run
that has already been recorded as successful and handed off — it cannot
prevent one. An invalid redesign was therefore publishable.
The gate now runs after visual-qa, whose ctx.qualityEvidence.visualQa it
reads, and before the finalizer and emitter. It stays outside the
end-to-end guard on purpose: REDESIGN_ADDED_MANDATORY makes
redesign-integrity-receipt mandatory in every mode, so folding it into
that branch would make requireMandatoryConvergence throw on plan,
local-proof and publish-proof redesign runs.
No stage, receipt type, or handoff contract is added — this is a reorder.
The gate reads only context produced upstream of its new position and
declares no evidence block, so it imposes no runner ordering constraint.
The COPY end-to-end stage list is unchanged; the existing deepEqual
assertion in tests/unit/factory-execution-plan.test.ts passes unmodified.
Two intended consequences:
- On the visual-QA-not-passed path a redesign end-to-end run now fails as
REDESIGN_PIPELINE_INCOMPLETE rather than RELEASE_EVIDENCE_INCOMPLETE.
That is the fail-closed-earlier behaviour, not a regression.
- executed_stages in the emitted receipt no longer lists
release-receipt-finalizer or handoff-emitter, since it snapshots
ctx.stageResults at emit time. Nothing validates that field.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(inngest): pass buildIntent into the durable pipeline's execution plan
The durable path computed ctx.buildIntent from the spec and then built its
execution plan without it, so buildFactoryExecutionPlan always derived
redesign === false there.
A REDESIGN_IMPROVE spec submitted through Inngest therefore ran the COPY
topology: no seo-build-intelligence-preflight, no competitive-intelligence,
the legacy content/schema stages instead of the redesign authority chain,
and no redesign-integrity-receipt stage at all. The build context said one
thing and the plan built another.
This also bounds the preceding commit. Reordering the integrity gate ahead
of the finalizer and emitter fixes the plan builder, but on this path the
gate was never in the plan to begin with, so that fix would have been inert
here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* test(pipeline): lock the redesign gate ahead of the success receipt
There was no regression coverage for the terminal ordering of a redesign
plan. The two existing REDESIGN_IMPROVE tests assert only the head of the
pipeline (preflight before competitive-intelligence, competitive-intelligence
before design-intelligence), and redesign-integrity-receipt.test.ts asserts
that the stage is present, not where. All of them passed with the gate sitting
after the finalizer and the external handoff.
This asserts the chain that actually matters:
visual-qa < redesign-integrity-receipt < release-receipt-finalizer
< handoff-emitter < terminal-convergence
Verified to discriminate: against the parent commit's plan builder it fails
(8 pass / 1 fail); against the reordered one it passes (9 pass). A test that
cannot fail proves nothing, so it was run both ways before being committed.
The COPY end-to-end deepEqual assertion is deliberately left untouched — it
is the byte-identity guard proving the reorder did not disturb the non-redesign
topology, and editing it would destroy the evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(golden): correct the hard-coded stage tail in the fixture builder
executed_stages and the STAGES list that drives synthetic checkpoint
timestamps both hard-code the pipeline's terminal order as static data
rather than deriving it from buildFactoryExecutionPlan. They still carried
the pre-fix tail, with redesign-integrity-receipt trailing the finalizer
and the handoff emitter.
Nothing fails as a result — the golden oracle checks a relative ordered
subsequence that omits both of those stages, so it is satisfied by either
order. That is precisely the problem: the fixtures would have gone on
describing an ordering the code no longer has, and a reviewer re-deriving
a fixture would have been misled by it.
Data-only; no generator logic changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(repo): stop site:validate dirtying a tracked report
validation/validation_report.yaml is per-run output of
scripts/validation-executor.ts, but it was tracked. Every `npm run
site:validate` — and therefore every `verify:all` and every `make pr` —
rewrote it with a fresh run_id, fresh timestamps, the local absolute
target_roots and the running user's git identity.
That is not cosmetic. It hard-blocks the sanctioned publish path: the L4
release receipt is bound to a tree digest, so `make pr` runs `pr-check`,
`pr-check` dirties this file, and the L4 remote check then rejects the
push as a stale receipt. The gate invalidates the authorization it is
about to verify, and no amount of re-authorizing escapes it, because the
next run dirties the tree again.
Untracking it is the repo's own established convention, not a new one:
.gitignore already ignores validation/launch_env_report.json and
validation/deploy_secrets_report.json, which are the same class of
per-run artifact from the same directory.
Nothing consumes the committed copy. scripts/validation-executor.ts only
writes it, no test or script reads it, and build-and-validate.yml
generates its own at the repository root and uploads that as a workflow
artifact.
The file stays on disk; only the index entry is removed. As a side
benefit the committed copy stops leaking one contributor's worktree path
and email address into the repository.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(gitignore): ignore non-npm lockfiles
This repo is npm-only: .github/workflows/l9-lint-test.yml and
l9-lint-test-node.yml both pin PACKAGE_MANAGER="npm", regen-lockfile.yml
knows only `npm install --package-lock-only`, and four package-lock.json
files are tracked. .gitignore carried no lockfile entries at all, so a
stray pnpm-lock.yaml sat untracked and committable.
It is container debris — never tracked on any ref, and its mtime matches
every other file in the clone rather than any commit. Left unignored it is
one `git add -A` away from landing, and the dormant pnpm/yarn arms of the
lint workflows would treat it as load-bearing the moment PACKAGE_MANAGER
moved.
Appended at end of file rather than in the secrets block: PR #207 deletes
.gitignore:41, and a hunk there would overlap one written nearby.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
(cherry picked from commit 3bf384caca440a43d4e5466957c671b35c353057)
* fix(gitignore): anchor evidence/ so it stops matching real source
`evidence/` was unanchored, so it matched a directory of that name at any
depth. `git ls-files -ci --exclude-standard` therefore reported 255 tracked
files as ignored, and 148 of those were not debris at all:
- src/pipeline/evidence/*.ts — 21 files, the pipeline's evidence layer
(EvidenceStore, ReleaseReceipt, RedesignExecutionIntegrityReceipt, ...)
- docs/reports_and_test_results/**/evidence/** — 127 files, dated report
bundles someone committed deliberately
Nothing was broken while they stayed tracked, because .gitignore does not
apply to files already in the index. The hazard is the cleanup: a
mechanically derived `git rm --cached $(git ls-files -ci)` would have
untracked the pipeline's evidence layer, and local validation would still
have passed because the files remain on disk. Only a fresh clone would have
failed.
Anchoring to /evidence/ confines the rule to the repository root, which is
where the Safe Haven per-run evidence it was written for actually lives. The
count drops 255 -> 107, and every remaining entry is genuine generated
output.
This lands as its own commit, before any untracking, so the delete list in
the next commit can be derived mechanically from a rule that is already
correct.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(repo): untrack generated build, evidence and per-run artifacts
105 files whose directories .gitignore already names, but which were added
to the index before or despite those rules. Ignore rules do not apply to
tracked files, so they kept accumulating:
- build/** 99 files across 61 golden run directories (~9.2 MB): 62
pipeline.log files and 20 PNG captures, several over 1 MB. .gitignore:12
says `build/` and README.md:193 says the same in prose.
- evidence/** 4 files. .gitignore:45 names the directory as per-run Safe
Haven output.
- astro_template/validation/*.jsonl 2 files, named at .gitignore:23.
Write-only: nothing reads them.
The list was derived mechanically from `git ls-files -ci --exclude-standard`
rather than hand-written, which is what makes a 105-file delete reviewable —
it is exactly the set git itself reports as tracked-but-ignored. That is only
safe because the preceding commit anchored `evidence/`; run against the old
rule the same command would have removed src/pipeline/evidence/*.ts.
Two entries are excluded deliberately, both tracked on purpose:
- .l9/ci.json, re-included by `!.l9/ci.json` as the consumer-owned CI class
declaration org-ci.yml reads
- tests/golden/safehaven/fixtures/positive-receipt.json, which
tests/golden/safehaven/RUNBOOK.md documents as a built fixture
Index-only: every file stays on disk and regenerates from its own tooling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* chore(repo): remove orphaned root run-artifacts and a duplicated doc
Four files, none referenced by any script, test, workflow or document.
Root run-artifacts, captured from one local run and committed:
- validation_node_check.txt — 0 bytes, the only empty tracked file in the
repository
- validation_command_results.txt — 30 bytes, entirely
`verify_launch_env_exit_code=1`
- launch_env_fail_closed_output.json — a dated per-run report
(timestamp_utc 2026-06-13). .gitignore:21 already ignores
validation/launch_env_report.json, the same class of artifact from the
same tool; this one just happened to be written at the repository root
instead.
docs/recursive_optimization_report.md — a duplicate. docs/archive/ holds a
copy with different content that was archived deliberately in #125 and is
listed in docs/archive/README.md against ADR-0017. This root copy carries no
archive banner, is referenced by nothing, arrived incidentally in an
unrelated llm-router commit, and opens by naming a path that does not exist
in this repository:
## Artifact Group: /home/ubuntu/l9-website-bot (full repo)
The archived copy is the one the index points at and is untouched.
ROOT_FILE_VALIDATION.md is deliberately NOT updated. Its "Files in final
pack: 19" counts a curated documentation pack rather than tracked root files
(the real root count was already 35 before this commit), and none of the
files removed here are enumerated in it. It is a dated point-in-time record;
editing its numbers to match today would falsify it rather than correct it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* docs: point every numbered-ADR reference at docs/adr
Audit finding F4. The numbered ADR series lives in docs/adr/, and
ARCHITECTURE.md:7 says so — "That directory is the only canonical ADR
location". Eighteen references across ten files said otherwise, and fourteen
of them were not merely misleading but dead: every
docs/architecture/ADR-XXXX-*.md target was verified absent. docs/architecture/
holds four design notes and no numbered ADR at all.
Dead links repointed (docs/architecture/ADR-* -> docs/adr/ADR-*): the five
archived-doc banner lines under docs/archive/, the five-row extraction table in
docs/archive/README.md, TODO.md:49 and :51, and MANIFEST.md:30 — the last
being the worst of them, since MANIFEST.md presents that block as the overlay's
file inventory and so asserted the existence of a file the repository does not
contain. All nine distinct targets now resolve; zero dead.
Prose pointers corrected:
- ARCHITECTURE.md:67 contradicted its own line 7, sixty lines apart
- AGENTS.md:5 — the highest blast radius of the set: the Mission line calls
itself "binding guidance for AI coding agents", so every agent entering
this repository was handed the wrong ADR home in its first instruction,
while AGENTS.md:74 cited docs/adr correctly
- README.md:198 named docs/architecture as the ADR home and omitted
docs/adr from the repository layout entirely, so a README reader could
not learn the canonical directory exists
- docs/archive/README.md:4 said "the numbered ADRs under docs/architecture/"
- MANIFEST.md:12
- docs/adr/README.md:4 forbade adding ADRs under `adr/`, which reads as
forbidding the very directory it declares canonical; the intended referent
is SEO-Bot's adr/ series, now named explicitly
Also indexes ADR-0019-client-design-reference-...md, an accepted 8.7 KB
decision record that was missing from docs/adr/README.md's table and therefore
undiscoverable from the canonical index. It collides on number with
ADR-0019-retire-five-push-campaign-pack.md; the collision is flagged in place
rather than resolved, because renumbering is an ADR decision and the losing
document's inbound references move with it.
Pointers only. No ADR is moved, renumbered or created, and no third authority
location is introduced.
Nothing in the verify:all chain validates documentation links, which is how
eighteen of them drifted undetected. Worth adding.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(golden): add the missing mandatory redesign stage to the fixture
Both hard-coded stage lists in the safehaven fixture builder went
seo-build-intelligence-preflight -> competitive-intelligence, skipping
design-reference-acquisition. The real REDESIGN_IMPROVE plan puts it between
those two (FactoryExecutionPlan.ts:275-279), and it is listed in
REDESIGN_ADDED_MANDATORY, so it is mandatory for every redesign run in every
mode.
The fixture declares mode: "end-to-end" with build_intent:
"REDESIGN_IMPROVE" and claims to represent a green run of exactly that plan,
so a stage the plan cannot omit was missing from the record. executed_stages
fed the emitted receipt and STAGES drives the synthetic checkpoint files and
the SQLite stage_runs rows, so both the receipt and the simulated stage
history described a run that could not occur.
Verified against the plan builder rather than by eye: an end-to-end
REDESIGN_IMPROVE plan emits ["seo-build-intelligence-preflight",
"design-reference-acquisition", "competitive-intelligence"] at that point,
which is now what both lists contain.
Found by Copilot's review on the stacked PR #210. It is a pre-existing gap
rather than one the preceding fixture commit introduced — that commit
corrected the tail of these lists — but the file is this PR's to get right,
and a fixture that misdescribes the pipeline is worse than no fixture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* docs: correct the ADR-0019 index date and the docs/adr layout wording
Both from Copilot's review on #211, both correct.
The index row for ADR-0019-client-design-reference-... carried "—" for its
date. The ADR declares `## Date 2026-09-03`. I indexed that file without
reading its date field, and an index that invents an unknown where the source
states a fact is worse than the omission it replaced.
README.md:198 read "numbered Architecture Decision Records — the canonical
decision record", which describes a directory as though it were a single
record. It is the canonical ADR directory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
* fix(docs): retain the golden report's cited proof artifacts
Audit finding F2 of website-bot-all-open-prs-2026-09-17, against this PR.
docs/reports_and_test_results/08-18-2026/GOLDEN_E2E_FINAL_REPORT.md is a
retained report that cites two artifacts as its evidence — and calls them
"never hand-edited" and "never edited by hand", which is the whole point of
citing them:
build/golden/golden-20260825-r61/pipeline.log
build/golden/golden-20260825-r61/golden-oracle-result.json
This PR untracked both as generated output under build/, so the report's
evidence trail resolved to nothing. Before untracking I checked that no
script, test or workflow READS those files, and none does. I never checked
whether a retained document CITES them, which is exactly what a golden-run
proof report does. That was the gap.
Both are relocated verbatim from origin/main into evidence/ beside the report
that depends on them, and the three citations are repointed. Byte-identity is
the property that matters here, so it was verified rather than assumed: the
recovered files hash to 3b52732 and eeba265, the same blobs origin/main
carries. A regenerated substitute would have satisfied the link and destroyed
the claim.
docs/reports_and_test_results/**/evidence/ is the existing home for this
(08-15-2026 already uses it) and is tracked again since this PR anchored the
`evidence/` rule, so no new evidence location is introduced.
.gitignore needed one addition: `*.log` at line 11 matches at any depth, so
the relocated pipeline.log was silently not staged. `git add -f` would have
tracked it while leaving it ignored — precisely the tracked-but-ignored state
this PR exists to eliminate, and how 255 files accumulated in the first place.
The negation instead follows the repo's own precedent for retained logs
(!reports/test-runs/**/*.log) and is scoped to report evidence: build/,
root evidence/ and astro_template/ stay ignored, and `git ls-files -ci`
still returns only the two deliberate re-inclusions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Hmr3SKRhYQ56omTzNFZ4L
---------
Co-authored-by: Claude <noreply@anthropic.com>



Summary
Repository hygiene. Untracks 105 generated artifacts whose directories
.gitignorealready names, removes four orphaned files, and — first — fixes the ignore rule that made a mechanical cleanup dangerous.Type of change
type:refactor— no behavior changeArea
area:infrastructure— configChecklist
npm run verify:allpasses locallynpx tsc --noEmitpasses (no type errors)VALIDATION.mdupdated — not applicableDECISION_LOG.mdupdated — not applicableReading this PR
Stacked on #208; it currently shows 9 commits and only the last three are this PR's. This branch is cut from #208's head because it needs that PR's
validation/validation_report.yamluntracking in order to publish at all —site:validaterewrote a tracked file mid-gate, which invalidated the release receipt. Once #208 merges, this diff collapses to its own three commits. Merge order: #208 → #209 → this.9e8dc7aevidence/so it stops matching real source9f03ad3011f3e1The anchoring commit is the important one
.gitignore:45wasevidence/— unanchored, so it matched a directory of that name at any depth.git ls-files -ci --exclude-standardreported 255 tracked files as ignored, and 148 of those were not debris:src/pipeline/evidence/*.ts— 21 files, the pipeline's evidence layer (EvidenceStore,ReleaseReceipt,RedesignExecutionIntegrityReceipt, …)docs/reports_and_test_results/**/evidence/**— 127 files, dated report bundles committed deliberatelyNothing was broken while they stayed tracked, because
.gitignoredoes not apply to files already in the index. The hazard was the cleanup: a mechanically derivedgit rm --cached $(git ls-files -ci)would have untracked the pipeline's evidence layer, and local validation would still have passed, because the files remain on disk. Only a fresh clone would have failed.Anchoring to
/evidence/confines the rule to the repository root, where the Safe Haven per-run evidence it was written for actually lives. 255 → 107, and every remaining entry is genuine generated output.What was untracked
105 files, index-only — everything stays on disk and regenerates from its own tooling:
build/**— 99 files across 61 golden run directories (~9.2 MB): 62pipeline.logfiles and 20 PNG captures, several over 1 MB..gitignore:12saysbuild/;README.md:193says the same in prose.evidence/**— 4 files, per-run Safe Haven output.astro_template/validation/*.jsonl— 2 files, named at.gitignore:23. Write-only; nothing reads them.The list was derived mechanically from
git ls-files -ci --exclude-standardrather than hand-written — that is what makes a 105-file delete reviewable, since it is exactly the set git itself reports. Two entries are excluded deliberately:.l9/ci.json(re-included by!.l9/ci.jsonas the CI class declarationorg-ci.ymlreads) andtests/golden/safehaven/fixtures/positive-receipt.json(documented as a built fixture intests/golden/safehaven/RUNBOOK.md).Orphaned files removed
validation_node_check.txt(0 bytes — the only empty tracked file in the repo),validation_command_results.txt(30 bytes, one captured exit code),launch_env_fail_closed_output.json(a dated per-run report;.gitignore:21already ignores the same class from the same tool undervalidation/), anddocs/recursive_optimization_report.md— a duplicate of an archived copy that is indexed indocs/archive/README.md, referenced by nothing, and opens by naming/home/ubuntu/l9-website-bot, a path that does not exist in this repository.Two things deliberately not done
No
biome.jsonchange. The plan called for adding a generated-tree exclusion beside the existing!**/build. I could not verify whether a bare!evidenceanchors to the root or behaves like!**/evidence— which would silently drop those same 21 source files from linting. Explicit file paths bypass Biome'sincludesfilter and its summary reporter emits no file count in this version, so the check was not decidable here. The benefit was four now-untracked files; the risk was the exact failure the first commit exists to prevent. Left for someone who can verify it.ROOT_FILE_VALIDATION.mdnot updated. Its "Files in final pack: 19" counts a curated documentation pack, not tracked root files (the real root count was already 35 before this PR), and none of the removed files are enumerated in it. It is a dated point-in-time record; editing its numbers would falsify it rather than correct it.Verification
npm run verify:allpasses on011f3e1, and the working tree stays clean through the entire run — which is the evidence that #208's untracking fix holds.Remaining tracked-but-ignored after this PR:
Both deliberate, both verified.
Publication note
make prran the full gate — L4 release check,pr-check, main-bound ancestry and the overlap probe — and pushed this branch. It could not compose the PR body:open_pr_after_gate.shrequiresops/config/root-file-protection.jsonunder the workspace root, and Website-Bot has noops/directory. Same consumer-side governance wiring gap as #208 and #209.Generated by Claude Code
Correction — stack topology (audit
website-bot-all-open-prs-2026-09-17, F3)An earlier version of this description claimed a linear chain
#208 → #209 → #210 → #211. That was wrong, and Git disproves it. Verified withgit rev-list --left-right --count:#210 was cut from #208's head, not #209's, so it never contained #209's normalizer delta. The accurate dependency model is:
#209 and #210 do not require a linear relationship with each other.
#209 is additionally one commit behind #208 — it lacks the fixture fix
f67f02d, which I merged forward into #210 and #211 but not #209, while stating it had reached all of them. #209 does not touchbuild-fixtures.mjs, so nothing there is functionally wrong; the claim was simply inaccurate. #209 should be refreshed against #208 once that lands.Generated by Claude Code