Skip to content

feat: enforce artifact names that never cite their work unit#111

Merged
levifig merged 2 commits into
mainfrom
chore/cleanup-planning-vocabulary
Jul 25, 2026
Merged

feat: enforce artifact names that never cite their work unit#111
levifig merged 2 commits into
mainfrom
chore/cleanup-planning-vocabulary

Conversation

@levifig

@levifig levifig commented Jul 14, 2026

Copy link
Copy Markdown
Owner

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 new loaf check --hook artifact-names gate, fail-closed on Bash(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

  • Artifact directories are matched by basename, not by hardcoded path, against the existing artifact registry. Relocating reports, plans, or handoffs — including out of .agents/ entirely — needs no change to the guard. A test pins this with docs/reports/ and workspace/nested/handoffs/.
  • 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. Outside a Git repository a filesystem walk stands in.
  • Closed artifacts are grandfathered. Front matter recording final or archived is 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:

  • Canonical guidance is now root AGENTS.md; the retired .agents/AGENTS.md is gone, so both the transitional-note replacement and its hygiene assertion 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. 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 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 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.
  • The installed-ownership doctor slice evidence 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.md is renamed to sidecar-audit.md; its source: TASK-074 front matter already recorded provenance the right way. report-spec-053-taxonomy-signoff.md is a finalized durable render and is grandfathered.

Guidance lands in the foundations skill and root AGENTS.md, with pointers in shape, handoff, research, and housekeeping — the four skills that create named artifacts.

Verification

  • LOAF_VALIDATE_TYPESCRIPT=1 npm run build — every target rebuilt from reconciled sources
  • npm run test — all four packages
  • npm run test:capability-runners — 21 passed
  • loaf check --hook for check-secrets, security-audit, render-drift, ephemeral-provenance, and artifact-names
  • gofmt, git diff --check
  • Three capability receipts re-recorded against the rebuilt binary with genuinely observed markers

Hook projection confirmed in plugins/loaf/hooks/hooks.json, dist/cursor/hooks.json, dist/opencode/plugins/hooks.ts, and dist/amp/.amp/plugins/loaf.ts. Codex's hooks.json stays SessionStart-only by design; its enforcement path is asserted through codexEnforcementHooks.

Note

The guard is inert until the installed binary is upgraded — an older loaf on PATH reports Unknown hook: artifact-names.

Follow-up

A loaf report rename verb 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.

@levifig
levifig force-pushed the feat/parameterized-capability-smokes branch from 8509ffa to 91f30e1 Compare July 24, 2026 22:41
@levifig
levifig force-pushed the chore/cleanup-planning-vocabulary branch from 9bfe378 to f750ba4 Compare July 25, 2026 00:26
@levifig levifig changed the title refactor: converge permanent workflow vocabulary feat: enforce artifact names that never cite their work unit Jul 25, 2026
levifig added 2 commits July 25, 2026 02:25
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
levifig changed the base branch from feat/parameterized-capability-smokes to main July 25, 2026 01:26
@levifig
levifig force-pushed the chore/cleanup-planning-vocabulary branch from f750ba4 to a98831c Compare July 25, 2026 01:26
@levifig
levifig merged commit c85551d into main Jul 25, 2026
1 check passed
@levifig
levifig deleted the chore/cleanup-planning-vocabulary branch July 25, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant