feat: enforce artifact names that never cite their work unit#111
Merged
Conversation
levifig
force-pushed
the
feat/parameterized-capability-smokes
branch
from
July 24, 2026 22:41
8509ffa to
91f30e1
Compare
levifig
force-pushed
the
chore/cleanup-planning-vocabulary
branch
from
July 25, 2026 00:26
9bfe378 to
f750ba4
Compare
Living project documentation and distributed guidance describe Change-first work in permanent, semantic language. Implementation-unit names, numbered development stages, volatile inventory counts, and completed-migration narration no longer leak into guidance that outlives the work that produced it. Historical Change, SPEC, and ADR citations remain where they serve as decision provenance, and compatibility commands, schema identities, and domain terminology are untouched. Reconciled onto current main, where several of the original assumptions had moved: - Canonical guidance is root `AGENTS.md`; the retired `.agents/AGENTS.md` copy is gone, so the transitional-note replacement and its hygiene assertion both retarget to the real file. - `STRATEGY.md` keeps the CLI-protocol-layer, continuity, diagnosis/repair and personality principles with their work-identifier citations stripped, rather than deleting them. Deleting the CLI principle would have dangled the `ARCHITECTURE.md` reference that cites it by name, and the hook-constraint list was dropped only because `ARCHITECTURE.md` already carries a strictly larger version. - `ARCHITECTURE.md` keeps main's newer root-`AGENTS.md` install and doctor descriptions, with the volatile check count removed. - Two content assertions pinned snapshots rather than invariants: the routing-eval test now requires the loaded and expected skill counts to agree instead of naming a number, and the journal-context assertion no longer depends on gofmt alignment padding. - Capability evidence claims follow the receipts that exist: superseded 2.1.209 and 0.144.4 receipts are removed and the surviving claims name Claude Code 2.1.218, Codex 0.145.0, and OpenCode 1.18.4.
An artifact is named for what it is, never for the work unit that produced it. Reference runs one way — a Change, spec, task, or issue points at its artifacts, and an artifact never points back — because the containing directory already supplies that provenance. A work identity in a filename is therefore both redundant and doomed: it has to be renamed to stay true, and the number outlives everyone's memory of what it meant. `loaf check --hook artifact-names` enforces this at commit time, gated on `Bash(git commit:*)` and fail-closed, and it ships to every installed target that projects enforcement hooks. Three deliberate boundaries keep it a gate rather than an obstruction: - Artifact directories are matched by basename against the existing artifact registry, not by hardcoded path. Relocating reports, plans, or handoffs — including out of `.agents/` entirely — needs no change here. - Only tracked paths are judged. An untracked scratch file is nobody's published artifact, and failing a commit over one the author never staged would obstruct rather than gate. - Artifacts whose front matter records `final` or `archived` are grandfathered. This principle governs how new artifacts are named, not whether closed ones are rewritten, and a durable render's filename is bound to a state record with no rename path today. Only the leading front-matter block counts, so a status line in the body cannot silence the guard. Versions and timestamps are identity rather than citation and remain legal, as does a numbered record living in the directory that owns it. The one violation free to correct is renamed; its `source:` front matter already recorded the provenance the right way, in a field. Guidance lands in the `foundations` skill and root `AGENTS.md`, with pointers in the four skills that create named artifacts. Every target was rebuilt, and the three capability receipts were re-recorded against the new binary with genuinely observed markers.
levifig
changed the base branch from
feat/parameterized-capability-smokes
to
main
July 25, 2026 01:26
levifig
force-pushed
the
chore/cleanup-planning-vocabulary
branch
from
July 25, 2026 01:26
f750ba4 to
a98831c
Compare
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.
Stacked on #110.
Summary
Two commits. The first converges living guidance onto permanent, semantic language. The second turns the underlying principle into something the framework enforces instead of something we remember.
refactor: converge permanent workflow vocabulary— implementation-unit names, numbered development stages, volatile inventory counts, and completed-migration narration no longer leak into guidance that outlives the work that produced them. Historical Change, SPEC, and ADR citations stay where they serve as decision provenance; compatibility commands, schema identities, and domain terminology are untouched.feat: enforce artifact names that never cite their work unit— a newloaf check --hook artifact-namesgate, fail-closed onBash(git commit:*), shipped to every target that projects enforcement hooks.The principle
Reference runs one way: a Change, spec, task, or issue points at its artifacts, and an artifact never points back. The containing directory already supplies that provenance, so a work identity in a filename is both redundant and doomed — it has to be renamed to stay true, and the number outlives everyone's memory of what it meant. Provenance belongs in a front-matter field such as
source:, where it is readable and updatable.Identity is not citation, and these stay legal: a version (
claude-code-2.1.218-plugin-startup-smoke.json), a timestamp (20260620-214448-skills-audit.md), and a numbered record inside the directory that owns it (.agents/specs/SPEC-042-slug.md,docs/decisions/ADR-007-slug.md).Three boundaries that keep it a gate, not an obstruction
.agents/entirely — needs no change to the guard. A test pins this withdocs/reports/andworkspace/nested/handoffs/.finalorarchivedis skipped: this principle governs how new artifacts are named, not whether closed ones get rewritten, and a durable render's filename is bound to a state record that has no rename path today. Only the leading front-matter block counts, so a status line in the body cannot silence the guard.Reconciliation against current main
The original branch predated several assumptions:
AGENTS.md; the retired.agents/AGENTS.mdis gone, so both the transitional-note replacement and its hygiene assertion retarget to the real file.STRATEGY.mdkeeps the CLI-protocol-layer, continuity, diagnosis/repair, and personality principles with their work-identifier citations stripped, rather than deleting them. Deleting the CLI principle would have dangled theARCHITECTURE.mdreference that cites it by name. The hook-constraint list was dropped only becauseARCHITECTURE.mdalready carries a strictly larger version.ARCHITECTURE.mdkeeps main's newer root-AGENTS.mdinstall and doctor descriptions, with the volatile check count removed.installed-ownership doctor sliceevidence bullet is not claimed here. That work lives in feat: diagnose target adapter ownership drift #109 and has not landed.Sweep and guidance
TASK-074-sidecar-audit-report.mdis renamed tosidecar-audit.md; itssource: TASK-074front matter already recorded provenance the right way.report-spec-053-taxonomy-signoff.mdis a finalized durable render and is grandfathered.Guidance lands in the
foundationsskill and rootAGENTS.md, with pointers inshape,handoff,research, andhousekeeping— the four skills that create named artifacts.Verification
LOAF_VALIDATE_TYPESCRIPT=1 npm run build— every target rebuilt from reconciled sourcesnpm run test— all four packagesnpm run test:capability-runners— 21 passedloaf check --hookforcheck-secrets,security-audit,render-drift,ephemeral-provenance, andartifact-namesgofmt,git diff --checkHook projection confirmed in
plugins/loaf/hooks/hooks.json,dist/cursor/hooks.json,dist/opencode/plugins/hooks.ts, anddist/amp/.amp/plugins/loaf.ts. Codex'shooks.jsonstays SessionStart-only by design; its enforcement path is asserted throughcodexEnforcementHooks.Note
The guard is inert until the installed binary is upgraded — an older
loafonPATHreportsUnknown hook: artifact-names.Follow-up
A
loaf report renameverb is deliberately out of scope. Grandfathering is correct on its own merits, and moving a durable render's slug,id, filename and state linkage atomically deserves its own review. Two report titles carry the same problem inside DB records, where no filename guard can reach them.