Spec-forge propagate
git clone https://github.com/tercel/spec-forge
T=$(mktemp -d) && git clone --depth=1 https://github.com/tercel/spec-forge "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/propagate" ~/.claude/skills/tercel-spec-forge-propagate && rm -rf "$T"
skills/propagate/SKILL.mdSpec Forge — Propagate
⚡ Execution Entry Point (READ THIS FIRST)
When this skill is loaded, you MUST immediately begin executing the Workflow below — do not wait, do not summarize, do not ask "what should I do now". Skills are operational manuals, not reference documents. Read Step 1 (Determine Source Doc), perform it, then Steps 2, 3, 4, ... in order, until the workflow completes or you reach an
AskUserQuestion checkpoint.
If the harness shows you
Successfully loaded skill · N tools allowed, that message means the SKILL.md content was injected into your context — it does NOT mean the skill has run. Skills do not "run" autonomously; you run them by executing the Detailed Steps below.
If you find yourself about to say "the skill didn't produce output", "skill 仍未输出", "falling back to manual propagation", "回退到手动 propagate", or anything similar, STOP. You have misunderstood how skills work. Go directly to Step 1 and start executing.
The first user-visible action of this skill should be either (a) the output of Step 1 (resolved source doc + change scope), or (b) an
AskUserQuestion if Step 1 needs disambiguation. Never an apology, never a fallback, never silence.
Walk a doc change downstream and keep the whole chain consistent.
Iron Law
EVERY UPSTREAM CHANGE PROPAGATES OR FAILS LOUDLY. Partial propagation is worse than no propagation — it creates the illusion of consistency where there is none.
When to Use
- After editing a PRD / SRS / tech-design / feature-spec — find and update every doc that references the changed concepts
- After running
or another generator that modified an existing doc in place/spec-forge:prd - Periodically, with
, to catch accumulated drift across recent commits--since {git-ref} - Before a release, to make sure no doc is silently stale relative to its upstream
- When
orspec-forge:analyze
flags inconsistencies between upstream and downstream docsspec-forge:audit
This skill is the symmetric counterpart to
code-forge:fix --review (which propagates code review findings into code changes). Where code-forge:fix walks up from a bug to the spec, spec-forge:propagate walks down from a spec change to the docs and code that depend on it.
Command Format
/spec-forge:propagate [@source-doc.md] [--since git-ref] [--scope all|docs|code] [--dry-run] [--save]
| Argument / Flag | Default | Description |
|---|---|---|
| — | The upstream doc whose changes should propagate. If omitted, auto-detect from (uncommitted changes) or (last commit). |
| — | Compute the diff between and instead of using uncommitted changes. Useful for catching drift across multiple commits. |
| | What to propagate into. = downstream documentation only. = also flag code that references changed requirement IDs. = both. |
| off | Generate the impact report and per-downstream proposed changes, but do NOT apply any edits. The user reviews the report and re-runs without to apply. |
| off | Save the propagation report to in addition to displaying it. |
Workflow
Step 1 (Determine Source) → Step 2 (Extract Changed Concepts) → Step 3 (Discover Downstream Refs) → Step 4 (Per-Downstream Impact Analysis) → Step 5 (Interactive Review) → Step 6 (Apply) → Step 7 (Verify) → Step 8 (Report)
Detailed Steps
Step 1: Determine Source Document and Change Scope
1.1 Resolve the Source
Parse
$ARGUMENTS:
- If
is provided: use it as the source. Verify the file exists.@source-doc.md - If
is provided: the source is the set of all--since {ref}
files in.md
that changed betweendocs/
and{ref}
. UseHEAD
to enumerate.git diff --name-only {ref}...HEAD -- 'docs/**/*.md' '*.md' - If neither is provided:
- Run
to find uncommitted doc changes.git status --porcelain -- 'docs/**/*.md' '*.md' - If non-empty: those files are the source.
- If empty: run
(the last commit's doc changes).git diff --name-only HEAD~1 HEAD -- 'docs/**/*.md' '*.md' - If still empty: display
and stop.No doc changes detected. Provide an explicit @source-doc.md or use --since {ref}.
- Run
If the source is auto-detected, display the resolved file list and use
AskUserQuestion:
- "Propagate from these {N} files" → continue to 1.2
- "Pick a different source" → re-prompt with
listing nearby docsAskUserQuestion
1.2 Compute Change Scope
For each source file, compute the diff:
- Uncommitted source:
git diff HEAD -- {file}
source:--sincegit diff {ref}...HEAD -- {file}- Last-commit source:
git diff HEAD~1 HEAD -- {file}
Display the diff summary:
Source documents: docs/core/prd.md +24 -8 docs/core/srs.md +12 -3 Total: 2 files, +36 -11 lines
Store the per-file diffs in memory for Step 2.
Step 2: Extract Changed Concepts (Sub-agent)
Offload to a sub-agent so the diff content stays out of the main context.
Spawn an
Agent tool call:
:subagent_type"general-purpose"
:description"Extract changed concepts from doc diff"
Sub-agent prompt must include:
- The source file paths
- The full diff for each (paste verbatim)
- The instruction: "Extract every concept the diff introduces, modifies, or removes. A concept is anything a downstream document might reference: requirement ID (FR-XXX-NNN, NFR-XXX-NNN, REQ-NNN), defined term, component name, module name, API endpoint, data field, success metric, decision, dependency, persona, or numeric threshold. For each concept, classify it as ADDED, MODIFIED, REMOVED, or RENAMED. For RENAMED, provide both the old and new name."
- The required structured return format below
Sub-agent must return:
CONCEPTS_REPORT ADDED: - type: {requirement_id|term|component|api|metric|decision|threshold|other} name: {concept name} defined_at: {source_file:line} summary: {one-sentence description} MODIFIED: - type: {...} name: {...} defined_at: {source_file:line} what_changed: {one-sentence description of the change} REMOVED: - type: {...} name: {...} was_defined_at: {source_file:line in the OLD version} reason: {one-sentence why it was removed, if inferrable} RENAMED: - type: {...} old_name: {...} new_name: {...} defined_at: {source_file:line} SEARCH_TERMS: - {keyword 1} - {keyword 2} - {... grep-friendly strings to find downstream references — include literal IDs, exact term names, and at least one variant for each rename}
Main context retains: Only this report (~3-5KB).
Step 3: Discover Downstream References (Sub-agent)
Offload to a sub-agent so the grep output stays out of the main context.
Spawn an
Agent tool call:
:subagent_type"general-purpose"
:description"Discover downstream references"
Sub-agent prompt must include:
- The
from Step 2 (paste verbatim)CONCEPTS_REPORT - The project root path
- The scope flag (
,docs
, orcode
)all - The instruction: "For every entry in SEARCH_TERMS, grep the project for references and produce a list of downstream files and locations that mention any of these concepts. Exclude the source files themselves (they are upstream). For docs scope, search
excluding the source files. For code scope, also search source files (**/*.md
). Group results by downstream file."**/*.{py,ts,js,tsx,jsx,go,rs,java,rb,php} - The instruction: "For each downstream file, list which concepts it references, with line numbers. Distinguish RENAMED concepts: a downstream file that mentions only the OLD name is now stale; one that mentions only the NEW name is current; one that mentions both is in the middle of an update."
- The required structured return format below
Sub-agent must return:
DOWNSTREAM_REPORT DOC_REFERENCES: - file: {downstream_file_path} type: {prd|srs|tech-design|feature-spec|test-cases|test-plan|readme|adr|other} references: - concept: {concept name} kind: {ADDED|MODIFIED|REMOVED|RENAMED} lines: [{line numbers where mentioned}] uses_old_name: {true|false} # only for RENAMED uses_new_name: {true|false} # only for RENAMED CODE_REFERENCES: # only if scope = code or all - file: {code_file_path} language: {python|typescript|...} references: - concept: {concept name} kind: {...} lines: [{...}] ORPHANED: - {concepts marked REMOVED in CONCEPTS_REPORT that are still referenced somewhere — these are the most urgent fixes} SUMMARY: total_downstream_docs: {N} total_downstream_code_files: {N} most_referenced_concept: {name and ref count}
Main context retains: Only this report (~5-10KB depending on project size).
Step 4: Per-Downstream Impact Analysis (Parallel Sub-agents)
For each entry in
DOWNSTREAM_REPORT.DOC_REFERENCES (and optionally CODE_REFERENCES), spawn a sub-agent to determine what specific changes the downstream needs. Use parallel Agent tool calls — one per downstream file — but cap at 8 parallel sub-agents at a time to avoid overwhelming the orchestrator.
Each sub-agent receives:
- The downstream file path
- The relevant slice of
(only the concepts this downstream references)CONCEPTS_REPORT - The relevant slice of
(only this file's references)DOWNSTREAM_REPORT - The instruction below
Sub-agent prompt:
You are analyzing whether {downstream_file} needs to be updated in response to upstream spec changes. Read {downstream_file} from disk. For each referenced concept, decide whether and how the downstream needs to change. Decision rules: - ADDED concept: usually no change needed unless the downstream is the natural place to consume the new concept (e.g., a new requirement should be referenced from the test-cases doc). If the downstream should reference the new concept, propose where. - MODIFIED concept: the downstream's mention of this concept may now be stale. Read the surrounding context, compare against the new upstream definition, and decide: STALE (needs update), STILL_VALID (the change doesn't affect this mention), or AMBIGUOUS (human judgment needed). - REMOVED concept: the downstream still references something that no longer exists. Either the reference must be deleted, or replaced with a pointer to the replacement (if there is one — check the surrounding context for hints). - RENAMED concept: any mention of the old name is stale. Replace with the new name. For each needed change, produce a unified-diff-style proposed edit that the orchestrator can apply directly. Be precise about the line range and the exact replacement text. Do NOT make subjective improvements unrelated to the propagation — only address the upstream changes. Return structured output: DOWNSTREAM_IMPACT for {downstream_file} CHANGES_NEEDED: - concept: {concept name} decision: STALE | STILL_VALID | AMBIGUOUS | REMOVE_REFERENCE | ADD_REFERENCE current_text: line {N}: {current line content} proposed_text: line {N}: {proposed replacement} rationale: {one sentence why this change is needed} confidence: HIGH | MEDIUM | LOW # HIGH = mechanical replacement, LOW = needs human review NO_CHANGE_NEEDED: - concept: {concept name} reason: {one sentence} AMBIGUOUS: - concept: {concept name} question: {what the human needs to decide}
After all sub-agents complete, aggregate into a single
IMPACT_REPORT keyed by downstream file.
Step 5: Interactive Review
Walk the
IMPACT_REPORT with the user. For each downstream file with CHANGES_NEEDED, display the proposed changes and use AskUserQuestion to choose:
Downstream: docs/core/srs.md Proposed changes (3): [1] FR-AUTH-005 (RENAMED → FR-USER-005) Current L42: - The system shall enforce FR-AUTH-005 (password complexity) Proposed L42: - The system shall enforce FR-USER-005 (password complexity) Confidence: HIGH [2] threshold "5 attempts" (MODIFIED → "3 attempts") Current L88: - Account locks after 5 failed attempts Proposed L88: - Account locks after 3 failed attempts Confidence: HIGH [3] component "AuthService" (REMOVED, replaced by "UserAuthFlow" + "SessionManager") Current L120: - AuthService validates credentials and issues tokens Proposed L120: <AMBIGUOUS — needs human decision> Confidence: LOW Question: How should this sentence be split between UserAuthFlow and SessionManager? How would you like to proceed? - Apply all HIGH-confidence changes, ask me about LOW/AMBIGUOUS - Apply all proposed changes (including AMBIGUOUS — I'll review the diff after) - Show me the full diff for this file before deciding - Skip this file entirely - Stop and let me handle this manually
For each AMBIGUOUS change, follow up with a focused
AskUserQuestion per concept.
If
--dry-run was set: skip the interactive choices, collect everything as "would apply" / "would skip", and jump to Step 8 (Report).
Step 6: Apply Changes
For each accepted change from Step 5:
- Read the current content of the downstream file
- Apply the proposed edit using
(specific line range, exact text replacement)Edit - After all changes for a file are applied, run
and store the diffgit diff -- {file}
Hard rule: Use
Edit for surgical changes. Never rewrite the entire file. Never use Write to overwrite. The downstream file may have content unrelated to the propagation — that content must not be touched.
Hard rule: Apply changes file by file, not concept by concept. This keeps each file's edits atomic and easy to revert.
Step 7: Verify
After all changes are applied:
-
Re-run the discovery (Step 3) on the updated downstreams. Any concept marked REMOVED or RENAMED in
that still appears in any downstream file is a propagation gap — display it asCONCEPTS_REPORT
and ask the user to address it manually.STILL_STALE -
If scope includes
: run the project's test suite (auto-detect from build config). If tests fail, surface the failures. The propagation may have broken something — the user must decide whether to fix forward or revert.code -
Optional: invoke
on the affected docs to confirm internal consistency post-propagation. Spawn as a sub-agent.spec-forge:audit
Step 8: Report
Display the propagation report:
Propagate Complete Source documents: {N} - docs/core/prd.md (+24 -8) - docs/core/srs.md (+12 -3) Concepts changed: ADDED: {N} MODIFIED: {N} REMOVED: {N} RENAMED: {N} Downstream files analyzed: {N} Updated: {N} Skipped: {N} Ambiguous: {N} (left for manual) Code references: {N} (scope = {scope}) Verification: Stale references remaining: {N} {if any: list them} Tests after propagation: {pass/fail/skipped} Files modified: docs/core/srs.md 3 changes docs/core/tech-design.md 5 changes docs/features/auth.md 2 changes README.md 1 change Next steps: Review changes: git diff Commit: git add <files> && git commit -m "docs: propagate changes from prd.md" Re-run audit: /spec-forge:audit {if --save was used:} Report saved: docs/propagate-report-{timestamp}.md
If
--dry-run was set: prefix the entire report with DRY RUN — no changes were applied. and append:
To apply the proposed changes, re-run without --dry-run: /spec-forge:propagate {original args minus --dry-run}
Common Mistakes
- Skipping Step 2 (concept extraction). Without an explicit list of changed concepts, the discovery sub-agent has nothing to grep for and the propagation collapses to a manual review.
- Running impact analysis sequentially instead of in parallel. Step 4 fans out per-downstream — running serially is needlessly slow on large projects.
- Rewriting downstream files instead of editing surgically. The downstream file contains content unrelated to the propagation.
is the only safe tool. NeverEdit
.Write - Touching code in
mode. When the user specified--scope docs
, the skill must not modify any code files even if they reference stale concepts. Code-side updates are out of scope.docs - Treating AMBIGUOUS as HIGH-confidence and silently applying. When the impact analyzer says LOW / AMBIGUOUS, the user must decide. Auto-applying these defeats the safety of the workflow.
- Forgetting Step 7 verification. After applying, the same grep that found the references should now find zero. If it doesn't, the propagation is incomplete.
- Running propagate on a dirty source. If the source doc itself is in the middle of being edited (multiple unrelated changes pending), the concept extraction will be polluted. Either stage the unrelated changes elsewhere first or use
against a clean ref.--since
Coordination With Other Skills
is the broader landscape tool — it surfaces all drift across the doc collection. Usespec-forge:analyze
periodically; useanalyze
after each upstream change.propagate
verifies docs against code and against quality criteria. After aspec-forge:audit
run, anpropagate
confirms the propagation didn't break anything.audit
is the symmetric tool on the code side — it walks code review findings back into the code.code-forge:fix --review
walks doc changes forward into downstream docs.propagate
is the cross-language version — it verifies that the same spec is consistent across multiple language implementations.apcore-skills:sync
is the within-doc-chain version.propagate