Skip to content

T-RIOC-assert-claude-md-import-resolves

Status: closed/superseded · Impact: medium · Complexity: small

AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-04 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.

Docs-extraction tasks that move content out of CLAUDE.md into an @-imported file leave a standing manual human spot-check, because /sdlc:task-work cannot verify — from inside a worktree — that Claude Code’s @path CLAUDE.md import actually resurfaces the moved text in agent context. This follow-up, originating from T-NCC9-conventions-md-extraction in git@github.com:sksizer/markdown-contract.git, asks this repo (the sdlc plugin) to add an affordance to task-work (or a helper it invokes) that asserts @path CLAUDE.md imports resolve, so @import-based extractions no longer leave a standing manual verification step.

Docs-extraction tasks that move content out of CLAUDE.md into an @-imported file (e.g. CONVENTIONS.md) leave a standing manual human spot-check because task-work cannot verify, from inside a worktree, that Claude Code’s @path CLAUDE.md import actually resurfaces the moved text in agent context — a live claude /memory resolution is not reachable from a worktree. Add an affordance to task-work (or a helper it invokes) that asserts @path CLAUDE.md imports resolve, so @import-based extractions no longer leave a standing manual verification step.

T-NCC9-conventions-md-extraction

LocationRole today
plugin/skills/task-work/preflight_permissions.tsA self-contained bun pre-flight helper task-work already invokes (Step 3b). Establishes the pattern the new assertion follows: a top-of-file docstring, argv in, findings on stdout, documented exit codes, no shared state.
plugin/skills/task-work/SKILL.mdtask-work’s ordered steps. Step 7 runs the project’s declared quality checks; no step asserts that the @path imports in a CLAUDE.md the branch modified still resolve to existing files.
plugin/skills/task-work/tests/Peer bun:test suites for the task-work helpers (preflight_permissions.test.ts, start_task.test.ts, and siblings). None covers CLAUDE.md import resolution.

Add a small, self-contained assertion helper beside the other task-work helpers — assert_claude_md_imports.ts — that parses the @path memory imports out of one or more CLAUDE.md files and checks that each resolves to an existing file on disk, then wire task-work’s quality phase to run it against any CLAUDE.md the branch’s diff touched. A docs-extraction task that moves prose out of CLAUDE.md into an @-imported file (e.g. CONVENTIONS.md) then gets a deterministic, worktree-local assertion that the import still points at the moved file — replacing the manual claude /memory spot-check that a worktree cannot run.

  1. Add plugin/skills/task-work/assert_claude_md_imports.ts, a #!/usr/bin/env bun helper modeled on preflight_permissions.ts — argv in, findings on stdout, documented exit codes, no shared state. Argv is one or more CLAUDE.md file paths.
  2. Parse each input file for @path memory imports using Claude Code’s rule: an @-token whose path is NOT inside a fenced code block or an inline-code span (reuse the codeBlockLines and inline-code masking already used by the claim resolvers under plugin/lib/model/entities/task/claims/, so a discussed @example in prose is not treated as an import), and that carries a real path (skip bare @word / email-shaped tokens with no path separator or file extension).
  3. Resolve each parsed import against the filesystem: a leading ~/ expands to the home directory, an absolute path is used as-is, and any other path resolves relative to the directory of the importing CLAUDE.md. An import passes when its resolved target exists and is a readable file.
  4. Emit one finding line per UNresolved import on stdout in the shape path:line: unresolved import @target, and exit 1; exit 0 with no stdout when every parsed import resolves; exit 2 on bad arguments (an argv entry that is not a readable file). Print the resolved-import count to stderr so a passing run stays observable.
  5. Add a peer test plugin/skills/task-work/tests/assert_claude_md_imports.test.ts covering: a CLAUDE.md whose @present.md target exists (exit 0); a CLAUDE.md whose @absent.md target is missing (exit 1 plus the finding line); an @-token inside a fenced code block (ignored); and a ~/-prefixed import.
  6. Wire the assertion into plugin/skills/task-work/SKILL.md Step 7: compute the set of CLAUDE.md files the branch changed via git diff --name-only origin/main...HEAD filtered to a CLAUDE.md basename; when that set is non-empty, run the helper against it and treat a non-zero exit as a gate failure (same disposition as a failing quality-check verb). An empty set is a silent no-op.
LocationKindChange
plugin/skills/task-work/assert_claude_md_imports.tsnewThe assertion helper: parse @path imports from the given CLAUDE.md files, resolve each on disk, exit non-zero on any unresolved import.
plugin/skills/task-work/tests/assert_claude_md_imports.test.tsnewPeer bun:test coverage: resolves-existing (exit 0), missing-target (exit 1 plus finding), fenced-@ ignored, ~/ expansion.
plugin/skills/task-work/SKILL.mdmodifyAdd the Step 7 sub-step that runs the helper against CLAUDE.md files the branch diff touched and gates on its exit code.
  • AC-1: plugin/skills/task-work/assert_claude_md_imports.ts exists, runs under bun run, and its top-of-file docstring names exit 0 (imports resolve), exit 1 (an unresolved import), and exit 2 (bad arguments).
  • AC-2: Run against a CLAUDE.md containing @present.md where present.md exists in the same directory: the helper exits 0 and writes nothing to stdout.
  • AC-3: Run against a CLAUDE.md containing @absent.md where absent.md does not exist: the helper exits 1 and stdout carries a line ending unresolved import @absent.md.
  • AC-4: An @ignored.md token inside a fenced code block in the CLAUDE.md is not reported, even when ignored.md is absent (exit 0).
  • AC-5: plugin/skills/task-work/tests/assert_claude_md_imports.test.ts exists and its cases pass under bun test.
  • AC-6: plugin/skills/task-work/SKILL.md Step 7 documents the CLAUDE.md import assertion — it runs the helper against the CLAUDE.md files reported by git diff --name-only origin/main...HEAD, and a non-zero exit fails the quality gate.
  • Running a live claude /memory resolution or reproducing Claude Code’s full recursive import expansion (depth limits, transitive @ chains) — the helper checks one-hop on-disk existence of directly-cited imports, not the assembled memory tree.
  • Asserting the content moved correctly (that the imported file carries the extracted prose) — the helper checks that the import target exists and is readable, not what it contains.
  • Auto-fixing or rewriting broken @path imports — the helper reports; it does not edit CLAUDE.md.
  • Scanning CLAUDE.md files the branch did not modify, or walking the whole repo on every task-work run — the wiring is scoped to CLAUDE.md files in the branch diff.
  • Originates from T-NCC9-conventions-md-extraction in git@github.com:sksizer/markdown-contract.git (recorded in related:). That task motivates the affordance but does not block it — this task can land independently and be adopted by future docs-extraction tasks.
  • No blocking depends_on. The helper reuses the bun runtime and the fenced-code / inline-code masking already present in the task claim resolvers; no new runtime dependency is introduced.

Spawned by /sdlc:spawn-task-pr on 2026-07-04 UTC from T-NCC9-conventions-md-extraction in git@github.com:sksizer/markdown-contract.git.


← Back to Tasks