[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71FYPC05EB36Q7JZMSJM0H2":3,"skill-same:01M71FYPC05EB36Q7JZMSJM0H2":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":14,"repositoryHtmlUrl":52,"repositoryHomepageUrl":6,"ownerHtmlUrl":53,"ownerBlogUrl":54,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M71FYPC05EB36Q7JZMSJM0H2","refactor",null,"ALWAYS invoke this skill when moving nodes, re-scoping content, or extracting shared providers. NEVER restructure the Product Tree without this skill.","outcomeeng\u002Fplugins","outcomeeng","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F257335685?v=4","01M71FRVRF36XYR3HRSCGFHK2R","Outcome Engineering","01M71FRVS4BZG17DWANFJ7EPNY",1,"MIT","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Frefactor\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查","SCENARIO","使用场景",[5,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"Bash","edit","write","spec","read","rm","markdown","product","mv","Nodes","when","invoke","use","tool","re","extracting","never","tree","shared","2026-11-10 10:12:30",false,"---\nname: refactor\ndescription: ALWAYS invoke this skill when moving nodes, re-scoping content, or extracting shared providers. NEVER restructure the Product Tree without this skill.\nallowed-tools: Read, Glob, Grep, Write, Edit, Bash(git status:*), Bash(git mv:*), Bash(git rm -r:*), Bash(spx validation markdown:*), Bash(spx spec status:*), {{! tool('use_skill') !}}\n---\n\n\u003Cobjective>\n\nA restructured Product Tree — nodes moved between parents, content re-scoped, shared providers extracted, nodes consolidated — with impact analyzed and modifications reported.\n\n\u003C\u002Fobjective>\n\n\u003Ccontext>\n\n**PREREQUISITE**: Check for a live `\u003CPRODUCT_TREE_FOUNDATION>` marker. When it is absent, load the foundation first. Use skill `product-tree:understand`.\n\n\u003C\u002Fcontext>\n\n\u003Creference_guides>\n\n- Live `\u002Funderstand` `\u003Cartifact_placement>` — content taxonomy and placement rules\n- Live `\u002Funderstand` `\u003Cidentity_and_kinds>` — the seven kinds, their order, and containment\n- `product-tree:decompose` — structural composition, shared provider extraction, consolidation boundaries, ordering evidence, and index assignment\n\n\u003C\u002Freference_guides>\n\n\u003Coperations>\n\nThis skill handles four structural operations:\n\n| Operation            | Input                           | Output                                                                                      |\n| -------------------- | ------------------------------- | ------------------------------------------------------------------------------------------- |\n| **Move**             | Node + new parent               | Node relocated, paths updated                                                               |\n| **Re-scope**         | Two+ nodes + assertions to move | Assertions redistributed, specs updated                                                     |\n| **Extract provider** | Two+ nodes sharing a behavior   | `\u002Fdecompose` defines the provider node, its kind, and indices, refactoring applies the move |\n| **Consolidate**      | Two+ nodes to merge             | Single node with combined content, old nodes removed                                        |\n\n\u003C\u002Foperations>\n\n\u003Cworkflow>\n\n\u003Cstep name=\"intake\">\n\n**Step 1: Identify the operation**\n\nDetermine which operation from the user's request:\n\n- \"Move X under Y\" → **Move**\n- \"These assertions belong in the other node\" \u002F \"The boundary is wrong\" → **Re-scope**\n- \"Both nodes need the same thing\" \u002F \"Extract shared X\" → **Extract provider**\n- \"These two nodes are really the same thing\" → **Consolidate**\n\nNormalize every referenced node, ADR, and PDR to its full path from `spx\u002F` before analyzing impact. A bare node name, decision filename, or numeric prefix is not enough to identify a file.\n\nIf the request is ambiguous, ask.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"context\">\n\n**Step 2: Load context**\n\nA live `\u003CPRODUCT_TREE_CONTEXT target=\"...\">` marker for the exact path of every node the operation touches — the moved, re-scoped, extracted, or consolidated nodes and every parent that gains or loses a child, `spx\u002F` when the root is that parent — is Step 3's input. Use skill `product-tree:contextualize` once per such node whose marker the live conversation lacks. Each invocation loads:\n\n- The node's spec and assertions\n- Parent and ancestor specs\n- ADRs\u002FPDRs in scope\n- Sibling nodes, with the index each carries\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"impact\">\n\n**Step 3: Analyze impact**\n\nBefore applying changes, determine what will be affected:\n\n**For Move:**\n\n- Does the target parent exist?\n- Will the node's existing index conflict with children at the target?\n- Does the move change the node's parent or its index? If yes, the index comes from `\u002Fdecompose` on the target parent, and Step 4a does not start until the matrix file at the path its result names exists. Use skill `product-tree:decompose` with the target parent as its only argument.\n- Do any ADRs\u002FPDRs at the source location govern this node? Will it leave their scope?\n- Do any ancestor specs have cross-cutting assertions that reference this node?\n- Are there test links in other specs that point into this node's `tests\u002F` directory?\n\n**For Re-scope:**\n\n- Which assertions move from which node to which?\n- Do the assertions' test links need updating (different `tests\u002F` directory)?\n- After redistribution, does any node end up with zero assertions? (If so, Step 4b consolidates or removes it.)\n- Do the remaining assertions in each node still form a coherent concern?\n\n**For Extract provider:**\n\n- What exactly is shared? (Infrastructure, utility, foundation)\n- Which siblings need it? (Must be 2+)\n- Has `product-tree:decompose` defined the shared provider, ordering evidence, and index placement?\n- Which assertions describe what the provider provides?\n- Do dependent specs need updating to remove the shared content?\n\n**For Consolidate:**\n\n- Are the nodes truly the same concern, or just similar?\n- Which node's opening survives?\n- How do the combined assertions fit together?\n- Which node's directory survives based on durable scope identity and evidence links?\n- Does consolidation alter sibling ordering or child composition? If yes, compose the parent before applying. Use skill `product-tree:decompose` with the parent as its only argument.\n- What happens to the removed node's test files?\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_move\">\n\n**Step 4a: Apply — Move**\n\nA move that changes the node's parent or its index starts only when the matrix file at the path `\u002Fdecompose` named in its result for the target parent exists and names the moved node's disposition against every existing sibling there; read the new index from that file. A `git mv` with no such file does not run. A slug rename that keeps parent and index needs no matrix.\n\n1. Move the node directory to the new location with the index the matrix file names, using `git mv \u003Cold-node-directory> \u003Cnew-node-directory>`. The spec, the `tests\u002F` directory, any ISSUES.md or prior-form PLAN.md, and every child node move with it, and no old directory remains.\n2. When the slug changes, rename the spec file with `git mv` so it repeats the new slug.\n3. Update cross-cutting assertion links in ancestor specs that pointed to the old path.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_rescope\">\n\n**Step 4b: Apply — Re-scope**\n\n1. Remove the assertions from the source node's spec.\n2. Add the assertions to the target node's spec under the correct assertion type heading.\n3. If test files exist for the moved assertions:\n   - Move each test file from source `tests\u002F` to target `tests\u002F` with `git mv \u003Cold> \u003Cnew>`.\n   - Update the test links in the assertions.\n4. If the source node now has zero assertions, consolidate it into the node that owns its remaining concern (Step 4d), or, when no concern remains, remove its directory with `git rm -r \u003Cnode-directory>`.\n5. Verify both specs still have coherent concerns.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_extract\">\n\n**Step 4c: Apply — Extract provider**\n\n1. Compose the parent containing the affected siblings. Use skill `product-tree:decompose` with that parent as its only argument.\n2. Apply the resulting structure: the decomposition wrote the provider directory and spec, so this step creates neither again.\n3. Move assertions for the shared concern into the provider, and move their test files with `git mv \u003Cold> \u003Cnew>`.\n4. Remove the shared content from each dependent node's spec.\n5. Update evidence links that moved with the assertions.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_consolidate\">\n\n**Step 4d: Apply — Consolidate**\n\n1. Choose the surviving node by durable scope identity, evidence links, and governing decisions.\n2. Merge assertions from the removed node into the surviving node:\n   - Group by assertion type\n   - Deduplicate identical assertions\n   - Resolve conflicting assertions (ask user if unclear)\n3. Move each test file from the removed node's `tests\u002F` into the surviving node's `tests\u002F` with `git mv \u003Cold> \u003Cnew>`, before Step 6 removes the emptied node.\n4. Update the surviving node's opening to cover the merged scope.\n5. Update any cross-cutting assertion links in ancestor specs that pointed to the removed node.\n6. Remove the old node's directory with `git rm -r \u003Cold-node-directory>` once its assertions and tests live in the surviving node, so Git records the removal in the changeset.\n7. If the surviving node now exceeds ~7 assertions or mixes independent concerns, decompose the surviving node. Use skill `product-tree:decompose` with the surviving node as its only argument.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"validate\">\n\n**Step 5: Validate**\n\nAfter applying any operation, run the product's verify command for the spec lane against the restructured tree: `spx validation markdown` and `spx spec status --format json`, and every further command the root guide names for a spec-only change, which runs through per-call approval where the grants above omit it. A nonzero exit stops the run with the command, its exit code, and its failing output. Then check each criterion:\n\n- [ ] No broken evidence links — every `([test](...))` in affected specs resolves to an existing file\n- [ ] No orphaned test files — every test file in affected `tests\u002F` directories is linked from an assertion\n- [ ] Coordination-note files (ISSUES.md, and a prior-form PLAN.md) moved with their node — they are node-local, not shared (do not need evidence links)\n- [ ] No empty nodes — every node has at least one assertion\n- [ ] Any new or changed index came from the matrix file at the path `product-tree:decompose` named in its result for the target parent\n- [ ] ADR\u002FPDR scope correct — nodes are governed by the decisions in their ancestry\n- [ ] Cross-cutting assertions in ancestors still reference valid paths\n- [ ] Every node, ADR, and PDR reference uses a full path from `spx\u002F`\n- [ ] Atemporal voice maintained — no temporal language introduced\n- [ ] No content misplacement (per the live `\u002Funderstand` `\u003Cartifact_placement>` and the placements it rules out)\n- [ ] The product's verify command for the spec lane passed on the restructured tree\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"report\">\n\n**Step 6: Report**\n\nSummarize what changed:\n\n```text\nRefactoring: {operation type}\n\nFiles created:\n  - {path}\n\nFiles modified:\n  - {path}: {what changed}\n\nFiles moved:\n  - {old path} → {new path}\n\nFiles removed:\n  - {path}\n\nAssertions redistributed: {count}\nTest files moved: {count}\nCross-cutting links updated: {count}\n```\n\nIf the refactoring revealed further issues (nodes with too many assertions, orphaned providers, scope ambiguity), note them as recommended follow-ups.\n\n\u003C\u002Fstep>\n\n\u003C\u002Fworkflow>\n\n\u003Cfailure_modes>\n\n**Failure 1: ADR scope silently lost after move**\n\nClaude moved a node from directory A to directory B. Directory A contained an ADR at index 15 that governed the moved node. After the move, the node was no longer a descendant of that ADR's scope — the architectural constraint silently disappeared. Tests continued to pass because they tested behavior, not ADR compliance.\n\nHow to avoid: In the impact analysis step, glob for all ADRs\u002FPDRs in the source ancestry. For each one, check whether the constraint still applies at the destination. If it does, either the ADR needs to move too or a new ADR must be created at the destination's scope.\n\n**Failure 2: Test links broken after move, not caught**\n\nClaude moved a node and its `tests\u002F` directory but didn't update the assertion evidence links in ancestor specs that referenced `([test](old-path\u002Ftests\u002F...))`. The assertions still claimed coverage, but the links pointed to nonexistent files.\n\nHow to avoid: After any move, grep the entire `spx\u002F` tree for the old path. Every match is a broken reference that must be updated. The validation step checks \"every `([test](...))` resolves to an existing file\" — run it.\n\n**Failure 3: Bare decision reference survived the move**\n\nClaude reported that the moved node still followed `15-build.adr.md`, but another directory also contained `15-build.adr.md`. The reference was impossible to resolve from the report.\n\nHow to avoid: Use full paths from `spx\u002F` for every node, ADR, and PDR before and after the move. A correct report says `spx\u002F...\u002F15-build.adr.md`, never just `15-build.adr.md`.\n\n**Failure 4: Consolidated nodes with different contracts**\n\nClaude merged two \"parsing\" outcomes because they sounded similar. One parsed user input for validation; the other parsed API responses for data extraction. Different contracts, different users, different failure modes. The merged node's opening became a vague compromise that fit neither concern well.\n\nHow to avoid: Before consolidating, compare the openings — the contract each node states and the consumption context or audience it serves. If they own different contracts or serve different contexts, they are distinct nodes regardless of implementation similarity.\n\n**Failure 5: Used `mv` instead of `git mv` for tracked files**\n\nClaude used Bash `mv` to relocate a node directory. Git saw this as a deletion plus an unrelated new file. The file's history was lost, and `git blame` showed the move as the original author of all lines.\n\nHow to avoid: Always use `git mv` for files tracked by git. This preserves rename detection and history. Check `git status` first — if the file shows as tracked, use `git mv`.\n\n**Failure 6: Temporal language introduced during re-scope**\n\nClaude moved assertions between nodes and rewrote the source node's opening to explain what happened: \"After extracting the validation concerns into the sibling node, this outcome focuses on data transformation.\" This narrates a refactoring history — it's temporal. The atemporal version: \"This outcome transforms raw input into normalized records.\"\n\nHow to avoid: When rewriting specs after structural changes, treat the rewrite as if the spec was always this way. The Product Tree is a durable map — it states product truth, not a changelog. Apply the read-aloud test: if the sentence would sound strange to someone who never saw the old structure, it's temporal.\n\n**Failure 7: Blanket-re-pointed cited assertions to a generalized parent**\n\nClaude generalized a node into a new parent node with multiple children and re-pointed every assertion that cited an ADR\u002FPDR onto the new parent. Some of those assertions held only for the original child's surface or capability, not for every child. An agentic review then surfaced them one per round — each as \"the new sibling does not realize this now-universal invariant\" — producing a long fix cascade (~10 rounds in one generalization: plan-level approval, per-integration behaviors, persistence, structural discovery, back-link suppression, mount mode), each round correcting one over-universalized assertion.\n\nHow to avoid: Before re-pointing, classify each citing assertion. Universal — holds for every child of the generalized parent — cites the parent. Node-specific — holds only for the child with that surface or capability — cites the realizing child. Route each citation to parent or child by that classification; never blanket-re-point every citation to the new parent.\n\n**Failure 8: Moved the node before the matrix existed**\n\nClaude moved a top-level node to the next free index above the node it consumes, then did it again one index higher when the first number was rejected. No ordering-evidence matrix existed for the root either time, and the operator reset both moves.\n\nHow to avoid: Step 4a reads the index from the matrix file at the path `\u002Fdecompose` named in its result for the target parent, and a `git mv` with no such file does not run.\n\n\u003C\u002Ffailure_modes>\n\n\u003Canti_patterns>\n\n**Leaving empty nodes after re-scope.** If all assertions move out of a node, the node is now empty. Either remove it or consolidate it — don't leave a spec with no assertions.\n\n**Treating `spx\u002Flocal\u002F` as a node directory.** `spx\u002Flocal\u002F` holds skill overlays, not spec nodes. It has no kind suffix and its files have no spec structure. Do not move, archive, or validate it as part of tree surgery.\n\n\u003C\u002Fanti_patterns>\n\n\u003Csuccess_criteria>\n\nRefactoring is complete when the restructured tree shows:\n\n- [ ] Every node the operation touched, the parent that gained or lost a child included, had a live `\u003CPRODUCT_TREE_CONTEXT>` marker before the first read\n- [ ] Every evidence link in an affected spec resolves to an existing file, and every test file in an affected `tests\u002F` directory is linked from an assertion\n- [ ] Every affected node carries at least one assertion, and no empty node directory remains\n- [ ] Every moved or surviving node is governed by the decisions in its new ancestry\n- [ ] Every new or changed index came from the matrix file at the path `product-tree:decompose` named in its result, and no `git mv` ran before that file existed\n- [ ] Every moved tracked file kept its history through `git mv`\n- [ ] The product's verify command for the spec lane passed on the restructured tree\n- [ ] The report names every file created, modified, moved, and removed, and every follow-up issue\n\n\u003C\u002Fsuccess_criteria>\n","\u003Cobjective>\n\nA restructured Product Tree — nodes moved between parents, content re-scoped, shared providers extracted, nodes consolidated — with impact analyzed and modifications reported.\n\n\u003C\u002Fobjective>\n\n\u003Ccontext>\n\n**PREREQUISITE**: Check for a live `\u003CPRODUCT_TREE_FOUNDATION>` marker. When it is absent, load the foundation first. Use skill `product-tree:understand`.\n\n\u003C\u002Fcontext>\n\n\u003Creference_guides>\n\n- Live `\u002Funderstand` `\u003Cartifact_placement>` — content taxonomy and placement rules\n- Live `\u002Funderstand` `\u003Cidentity_and_kinds>` — the seven kinds, their order, and containment\n- `product-tree:decompose` — structural composition, shared provider extraction, consolidation boundaries, ordering evidence, and index assignment\n\n\u003C\u002Freference_guides>\n\n\u003Coperations>\n\nThis skill handles four structural operations:\n\n| Operation            | Input                           | Output                                                                                      |\n| -------------------- | ------------------------------- | ------------------------------------------------------------------------------------------- |\n| **Move**             | Node + new parent               | Node relocated, paths updated                                                               |\n| **Re-scope**         | Two+ nodes + assertions to move | Assertions redistributed, specs updated                                                     |\n| **Extract provider** | Two+ nodes sharing a behavior   | `\u002Fdecompose` defines the provider node, its kind, and indices, refactoring applies the move |\n| **Consolidate**      | Two+ nodes to merge             | Single node with combined content, old nodes removed                                        |\n\n\u003C\u002Foperations>\n\n\u003Cworkflow>\n\n\u003Cstep name=\"intake\">\n\n**Step 1: Identify the operation**\n\nDetermine which operation from the user's request:\n\n- \"Move X under Y\" → **Move**\n- \"These assertions belong in the other node\" \u002F \"The boundary is wrong\" → **Re-scope**\n- \"Both nodes need the same thing\" \u002F \"Extract shared X\" → **Extract provider**\n- \"These two nodes are really the same thing\" → **Consolidate**\n\nNormalize every referenced node, ADR, and PDR to its full path from `spx\u002F` before analyzing impact. A bare node name, decision filename, or numeric prefix is not enough to identify a file.\n\nIf the request is ambiguous, ask.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"context\">\n\n**Step 2: Load context**\n\nA live `\u003CPRODUCT_TREE_CONTEXT target=\"...\">` marker for the exact path of every node the operation touches — the moved, re-scoped, extracted, or consolidated nodes and every parent that gains or loses a child, `spx\u002F` when the root is that parent — is Step 3's input. Use skill `product-tree:contextualize` once per such node whose marker the live conversation lacks. Each invocation loads:\n\n- The node's spec and assertions\n- Parent and ancestor specs\n- ADRs\u002FPDRs in scope\n- Sibling nodes, with the index each carries\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"impact\">\n\n**Step 3: Analyze impact**\n\nBefore applying changes, determine what will be affected:\n\n**For Move:**\n\n- Does the target parent exist?\n- Will the node's existing index conflict with children at the target?\n- Does the move change the node's parent or its index? If yes, the index comes from `\u002Fdecompose` on the target parent, and Step 4a does not start until the matrix file at the path its result names exists. Use skill `product-tree:decompose` with the target parent as its only argument.\n- Do any ADRs\u002FPDRs at the source location govern this node? Will it leave their scope?\n- Do any ancestor specs have cross-cutting assertions that reference this node?\n- Are there test links in other specs that point into this node's `tests\u002F` directory?\n\n**For Re-scope:**\n\n- Which assertions move from which node to which?\n- Do the assertions' test links need updating (different `tests\u002F` directory)?\n- After redistribution, does any node end up with zero assertions? (If so, Step 4b consolidates or removes it.)\n- Do the remaining assertions in each node still form a coherent concern?\n\n**For Extract provider:**\n\n- What exactly is shared? (Infrastructure, utility, foundation)\n- Which siblings need it? (Must be 2+)\n- Has `product-tree:decompose` defined the shared provider, ordering evidence, and index placement?\n- Which assertions describe what the provider provides?\n- Do dependent specs need updating to remove the shared content?\n\n**For Consolidate:**\n\n- Are the nodes truly the same concern, or just similar?\n- Which node's opening survives?\n- How do the combined assertions fit together?\n- Which node's directory survives based on durable scope identity and evidence links?\n- Does consolidation alter sibling ordering or child composition? If yes, compose the parent before applying. Use skill `product-tree:decompose` with the parent as its only argument.\n- What happens to the removed node's test files?\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_move\">\n\n**Step 4a: Apply — Move**\n\nA move that changes the node's parent or its index starts only when the matrix file at the path `\u002Fdecompose` named in its result for the target parent exists and names the moved node's disposition against every existing sibling there; read the new index from that file. A `git mv` with no such file does not run. A slug rename that keeps parent and index needs no matrix.\n\n1. Move the node directory to the new location with the index the matrix file names, using `git mv \u003Cold-node-directory> \u003Cnew-node-directory>`. The spec, the `tests\u002F` directory, any ISSUES.md or prior-form PLAN.md, and every child node move with it, and no old directory remains.\n2. When the slug changes, rename the spec file with `git mv` so it repeats the new slug.\n3. Update cross-cutting assertion links in ancestor specs that pointed to the old path.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_rescope\">\n\n**Step 4b: Apply — Re-scope**\n\n1. Remove the assertions from the source node's spec.\n2. Add the assertions to the target node's spec under the correct assertion type heading.\n3. If test files exist for the moved assertions:\n   - Move each test file from source `tests\u002F` to target `tests\u002F` with `git mv \u003Cold> \u003Cnew>`.\n   - Update the test links in the assertions.\n4. If the source node now has zero assertions, consolidate it into the node that owns its remaining concern (Step 4d), or, when no concern remains, remove its directory with `git rm -r \u003Cnode-directory>`.\n5. Verify both specs still have coherent concerns.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_extract\">\n\n**Step 4c: Apply — Extract provider**\n\n1. Compose the parent containing the affected siblings. Use skill `product-tree:decompose` with that parent as its only argument.\n2. Apply the resulting structure: the decomposition wrote the provider directory and spec, so this step creates neither again.\n3. Move assertions for the shared concern into the provider, and move their test files with `git mv \u003Cold> \u003Cnew>`.\n4. Remove the shared content from each dependent node's spec.\n5. Update evidence links that moved with the assertions.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"apply_consolidate\">\n\n**Step 4d: Apply — Consolidate**\n\n1. Choose the surviving node by durable scope identity, evidence links, and governing decisions.\n2. Merge assertions from the removed node into the surviving node:\n   - Group by assertion type\n   - Deduplicate identical assertions\n   - Resolve conflicting assertions (ask user if unclear)\n3. Move each test file from the removed node's `tests\u002F` into the surviving node's `tests\u002F` with `git mv \u003Cold> \u003Cnew>`, before Step 6 removes the emptied node.\n4. Update the surviving node's opening to cover the merged scope.\n5. Update any cross-cutting assertion links in ancestor specs that pointed to the removed node.\n6. Remove the old node's directory with `git rm -r \u003Cold-node-directory>` once its assertions and tests live in the surviving node, so Git records the removal in the changeset.\n7. If the surviving node now exceeds ~7 assertions or mixes independent concerns, decompose the surviving node. Use skill `product-tree:decompose` with the surviving node as its only argument.\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"validate\">\n\n**Step 5: Validate**\n\nAfter applying any operation, run the product's verify command for the spec lane against the restructured tree: `spx validation markdown` and `spx spec status --format json`, and every further command the root guide names for a spec-only change, which runs through per-call approval where the grants above omit it. A nonzero exit stops the run with the command, its exit code, and its failing output. Then check each criterion:\n\n- [ ] No broken evidence links — every `([test](...))` in affected specs resolves to an existing file\n- [ ] No orphaned test files — every test file in affected `tests\u002F` directories is linked from an assertion\n- [ ] Coordination-note files (ISSUES.md, and a prior-form PLAN.md) moved with their node — they are node-local, not shared (do not need evidence links)\n- [ ] No empty nodes — every node has at least one assertion\n- [ ] Any new or changed index came from the matrix file at the path `product-tree:decompose` named in its result for the target parent\n- [ ] ADR\u002FPDR scope correct — nodes are governed by the decisions in their ancestry\n- [ ] Cross-cutting assertions in ancestors still reference valid paths\n- [ ] Every node, ADR, and PDR reference uses a full path from `spx\u002F`\n- [ ] Atemporal voice maintained — no temporal language introduced\n- [ ] No content misplacement (per the live `\u002Funderstand` `\u003Cartifact_placement>` and the placements it rules out)\n- [ ] The product's verify command for the spec lane passed on the restructured tree\n\n\u003C\u002Fstep>\n\n\u003Cstep name=\"report\">\n\n**Step 6: Report**\n\nSummarize what changed:\n\n```text\nRefactoring: {operation type}\n\nFiles created:\n  - {path}\n\nFiles modified:\n  - {path}: {what changed}\n\nFiles moved:\n  - {old path} → {new path}\n\nFiles removed:\n  - {path}\n\nAssertions redistributed: {count}\nTest files moved: {count}\nCross-cutting links updated: {count}\n```\n\nIf the refactoring revealed further issues (nodes with too many assertions, orphaned providers, scope ambiguity), note them as recommended follow-ups.\n\n\u003C\u002Fstep>\n\n\u003C\u002Fworkflow>\n\n\u003Cfailure_modes>\n\n**Failure 1: ADR scope silently lost after move**\n\nClaude moved a node from directory A to directory B. Directory A contained an ADR at index 15 that governed the moved node. After the move, the node was no longer a descendant of that ADR's scope — the architectural constraint silently disappeared. Tests continued to pass because they tested behavior, not ADR compliance.\n\nHow to avoid: In the impact analysis step, glob for all ADRs\u002FPDRs in the source ancestry. For each one, check whether the constraint still applies at the destination. If it does, either the ADR needs to move too or a new ADR must be created at the destination's scope.\n\n**Failure 2: Test links broken after move, not caught**\n\nClaude moved a node and its `tests\u002F` directory but didn't update the assertion evidence links in ancestor specs that referenced `([test](old-path\u002Ftests\u002F...))`. The assertions still claimed coverage, but the links pointed to nonexistent files.\n\nHow to avoid: After any move, grep the entire `spx\u002F` tree for the old path. Every match is a broken reference that must be updated. The validation step checks \"every `([test](...))` resolves to an existing file\" — run it.\n\n**Failure 3: Bare decision reference survived the move**\n\nClaude reported that the moved node still followed `15-build.adr.md`, but another directory also contained `15-build.adr.md`. The reference was impossible to resolve from the report.\n\nHow to avoid: Use full paths from `spx\u002F` for every node, ADR, and PDR before and after the move. A correct report says `spx\u002F...\u002F15-build.adr.md`, never just `15-build.adr.md`.\n\n**Failure 4: Consolidated nodes with different contracts**\n\nClaude merged two \"parsing\" outcomes because they sounded similar. One parsed user input for validation; the other parsed API responses for data extraction. Different contracts, different users, different failure modes. The merged node's opening became a vague compromise that fit neither concern well.\n\nHow to avoid: Before consolidating, compare the openings — the contract each node states and the consumption context or audience it serves. If they own different contracts or serve different contexts, they are distinct nodes regardless of implementation similarity.\n\n**Failure 5: Used `mv` instead of `git mv` for tracked files**\n\nClaude used Bash `mv` to relocate a node directory. Git saw this as a deletion plus an unrelated new file. The file's history was lost, and `git blame` showed the move as the original author of all lines.\n\nHow to avoid: Always use `git mv` for files tracked by git. This preserves rename detection and history. Check `git status` first — if the file shows as tracked, use `git mv`.\n\n**Failure 6: Temporal language introduced during re-scope**\n\nClaude moved assertions between nodes and rewrote the source node's opening to explain what happened: \"After extracting the validation concerns into the sibling node, this outcome focuses on data transformation.\" This narrates a refactoring history — it's temporal. The atemporal version: \"This outcome transforms raw input into normalized records.\"\n\nHow to avoid: When rewriting specs after structural changes, treat the rewrite as if the spec was always this way. The Product Tree is a durable map — it states product truth, not a changelog. Apply the read-aloud test: if the sentence would sound strange to someone who never saw the old structure, it's temporal.\n\n**Failure 7: Blanket-re-pointed cited assertions to a generalized parent**\n\nClaude generalized a node into a new parent node with multiple children and re-pointed every assertion that cited an ADR\u002FPDR onto the new parent. Some of those assertions held only for the original child's surface or capability, not for every child. An agentic review then surfaced them one per round — each as \"the new sibling does not realize this now-universal invariant\" — producing a long fix cascade (~10 rounds in one generalization: plan-level approval, per-integration behaviors, persistence, structural discovery, back-link suppression, mount mode), each round correcting one over-universalized assertion.\n\nHow to avoid: Before re-pointing, classify each citing assertion. Universal — holds for every child of the generalized parent — cites the parent. Node-specific — holds only for the child with that surface or capability — cites the realizing child. Route each citation to parent or child by that classification; never blanket-re-point every citation to the new parent.\n\n**Failure 8: Moved the node before the matrix existed**\n\nClaude moved a top-level node to the next free index above the node it consumes, then did it again one index higher when the first number was rejected. No ordering-evidence matrix existed for the root either time, and the operator reset both moves.\n\nHow to avoid: Step 4a reads the index from the matrix file at the path `\u002Fdecompose` named in its result for the target parent, and a `git mv` with no such file does not run.\n\n\u003C\u002Ffailure_modes>\n\n\u003Canti_patterns>\n\n**Leaving empty nodes after re-scope.** If all assertions move out of a node, the node is now empty. Either remove it or consolidate it — don't leave a spec with no assertions.\n\n**Treating `spx\u002Flocal\u002F` as a node directory.** `spx\u002Flocal\u002F` holds skill overlays, not spec nodes. It has no kind suffix and its files have no spec structure. Do not move, archive, or validate it as part of tree surgery.\n\n\u003C\u002Fanti_patterns>\n\n\u003Csuccess_criteria>\n\nRefactoring is complete when the restructured tree shows:\n\n- [ ] Every node the operation touched, the parent that gained or lost a child included, had a live `\u003CPRODUCT_TREE_CONTEXT>` marker before the first read\n- [ ] Every evidence link in an affected spec resolves to an existing file, and every test file in an affected `tests\u002F` directory is linked from an assertion\n- [ ] Every affected node carries at least one assertion, and no empty node directory remains\n- [ ] Every moved or surviving node is governed by the decisions in its new ancestry\n- [ ] Every new or changed index came from the matrix file at the path `product-tree:decompose` named in its result, and no `git mv` ran before that file existed\n- [ ] Every moved tracked file kept its history through `git mv`\n- [ ] The product's verify command for the spec lane passed on the restructured tree\n- [ ] The report names every file created, modified, moved, and removed, and every follow-up issue\n\n\u003C\u002Fsuccess_criteria>\n",{"name":5,"description":7,"allowed-tools":48},"Read, Glob, Grep, Write, Edit, Bash(git status:*), Bash(git mv:*), Bash(git rm -r:*), Bash(spx validation markdown:*), Bash(spx spec status:*), {{! tool('use_skill') !}}","main","978d51c51d68c99cffb2ba48f7cad9d2f07e5738","src\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Frefactor","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins","https:\u002F\u002Fgithub.com\u002Foutcomeeng","https:\u002F\u002Foutcome.engineering","Spec-driven development for AI-assisted engineering","2026-11-10 01:06:33",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add outcomeeng\u002Fplugins --skill refactor",{"tool":63,"command":64},"bunx","bunx skills add outcomeeng\u002Fplugins --skill refactor",{"tool":66,"command":67},"pnpm","pnpm dlx skills add outcomeeng\u002Fplugins --skill refactor","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Frefactor\u002FSKILL.md",true,[],[72,78,109,125,141,154],{"skillNo":73,"name":74,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":43},"01M71G25RZTPQG9KJ00JCHC1F7","plugin","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Ftemplates\u002Fplugin\u002FSKILL.md",[],[74],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":92,"publishedAt":43},"01M71G23DCX3SV60H63HBBKQ6J","sanitize-powerpoint","ALWAYS invoke this skill when sanitizing, cleaning up, auditing, or aligning a PowerPoint (.pptx) deck — slide-master and layout structure, layout type attributes, stray fonts, non-theme colors, or layout naming. NEVER hand-edit pptx XML without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fsanitize-powerpoint\u002FSKILL.md",[84,87],{"categoryNo":85,"categoryName":86,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[93,94,95,96,97,98,99,40,100,101,102,103,104,105,106,25,107,108,35,34],"sanitize powerpoint","powerpoint","sanitize","fonts","type","cleaning","structure","attributes","theme","pptx","naming","layout","deck","up","xml","master",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":115,"publishedAt":43},"01M71G21PQKBCDECHGVYSKPJW5","draw-excalidraw","ALWAYS invoke this skill when creating Excalidraw diagrams, visualizing workflows, architectures, or concepts. NEVER generate Excalidraw JSON without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fdraw-excalidraw\u002FSKILL.md",[],[116,117,118,119,120,40,35,121,122,34,123,124],"draw excalidraw","excalidraw","draw","diagrams","architectures","generate","workflows","concepts","json",{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":134,"publishedAt":43},"01M71G1ZMNGFD37RZYM620XM2C","typescript-test-standards","TypeScript test standards enforced across all skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-test-standards\u002FSKILL.md",[131],{"categoryNo":132,"categoryName":133,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[135,136,137,138,139,28,140],"typescript","typescript test standards","test","standards","false","across",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":150,"publishedAt":43},"01M71G1XBH0X1VCTR7269MDGSY","typescript-code-standards","TypeScript code standards enforced across all skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-code-standards\u002FSKILL.md",[147],{"categoryNo":148,"categoryName":149,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000030","开发工程师",[135,151,152,153,138,139,28,140],"code","typescript code standards","code standards",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":160,"publishedAt":43},"01M71G1TQ3VN45SJ75DFMJDB9W","typescript-architecture-standards","TypeScript ADR conventions enforced across architect and auditor skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-architecture-standards\u002FSKILL.md",[],[161,138,162,163,135,164,165,140,28,166,139,167],"architecture standards","typescript architecture standards","architecture","adr","architect","auditor","conventions"]