[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71FZ4B9M004Y1XT69APR1HE":3,"skill-same:01M71FZ4B9M004Y1XT69APR1HE":77},{"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":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":14,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":50,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M71FZ4B9M004Y1XT69APR1HE","sync-base",null,"ALWAYS invoke this skill to bring a branch behind its base current — before reading product truth, before verifying, and before every merge push. NEVER rebase a behind-base branch by hand or bring it current with git reset.","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\u002Fsync-base\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"sync","base","sync base","ask","git merge","invoke","use","files","rev","continue","tool","bring","parse","diff","never","push","python3","show","branch","ls","2026-11-10 10:12:30",false,"---\nname: sync-base\ndescription: >-\n  ALWAYS invoke this skill to bring a branch behind its base current — before reading product truth, before verifying, and before every merge push. NEVER rebase a behind-base branch by hand or bring it current with git reset.\nallowed-tools: Read, Edit, {{! tool('use_skill') !}}, {{! tool('ask_user') !}}, Bash(python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Fsync_base.py\":*), Bash(git status:*), Bash(git rev-parse:*), Bash(git symbolic-ref:*), Bash(git merge-base:*), Bash(git rev-list:*), Bash(git diff:*), Bash(git ls-files:*), Bash(git show:*), Bash(git add:*), Bash(git rebase --continue), Bash(git rebase --abort)\n---\n\n\u003Cobjective>\nThe current checkout brought current with its fetched base, with authorized dirty work that blocks base movement checkpointed on its owning branch and detached-head safety preserved.\n\u003C\u002Fobjective>\n\n\u003Cworkflow>\n\nRecord the absolute checkout root and selected base, then run the synchronization primitive against that working tree (default: the current directory). Retain the same checkout and base through checkpoint recovery and retry:\n\n```bash\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Fsync_base.py\" [repo] [--base \u003Cbranch>] [--no-fetch]\n```\n\nIt resolves the base ref and `origin\u002F\u003Cbase>` through the shared changeset-scope primitives and fetches the base. When an attached branch is behind, it rebases the branch onto the fetched base. When a clean detached HEAD is an ancestor of the fetched base, it advances the worktree with `git switch --detach origin\u002F\u003Cbase>`; a detached HEAD carrying commits absent from the base fails without moving. The base defaults to `origin\u002FHEAD`; pass `--base \u003Cbranch>` when the changeset tracks a non-default base (a stacked pull request whose base is another feature branch). It prints a JSON result (`status`, `base_ref`, `remote_ref`, `branch`, `detail`, `preservation` on a clean outcome, and `conflict` on an active rebase conflict) and exits:\n\n| `status`          | exit | meaning                                                                                                                                                                                                        | how Claude acts                                                                                                                                                            |\n| ----------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `already_current` | 0    | the branch is not behind the base                                                                                                                                                                              | proceed                                                                                                                                                                    |\n| `rebased`         | 0    | the branch was rebased onto `origin\u002F\u003Cbase>`                                                                                                                                                                    | proceed; use `\u003Creadiness_preservation>` to identify which verification and review evidence the base movement invalidated                                                   |\n| `conflict`        | 3    | the rebase stopped with active conflict state; the result's `conflict` object names the conflicted paths, git facts, git conflict text, and options                                                            | finish the rebase per `\u003Cconflict_reconciliation>`; when another route is cheaper or evidence cannot decide, ask through the structured-question tool and act on the answer |\n| `dirty_tree`      | 4    | the branch is behind, but uncommitted changes to tracked files block the rebase; no rebase is attempted and the tree is left untouched                                                                         | classify ownership per `\u003Cdirty_tree_resolution>`; commit authorized changes to the right branch, leave operator-owned work untouched, and re-run                           |\n| `git_failure`     | 1    | a diverged detached HEAD carrying its own commits, an unresolved base, a failed fetch, or an absent or unloadable sibling `scope-changeset` script — a clean behind-base detached HEAD is advanced, not failed | report `detail`; do not rebase                                                                                                                                             |\n\nThese statuses and exit codes belong to the primitive. Complete this workflow only after `already_current` or `rebased` establishes currency for the recorded checkout and base. A checkpoint alone establishes no currency, and a successful synchronization establishes neither working-tree cleanliness nor verification readiness. In particular, an already-current checkout may carry pending edits; those edits alone require no recovery checkpoint.\n\nResolve authorized `dirty_tree` state end to end through `\u003Cdirty_tree_resolution>`. The bundled synchronizer never commits or stashes; `\u002Fcommit-changes` owns checkpoint policy, hooks, and proof of success. Use skill `product-tree:commit-changes`. Create every recovery checkpoint through it, never through a direct `git commit`. Preserve the primitive result alongside any unresolved authority, checkpoint, Git, or conflict condition; a stopped recovery is no successful synchronization result.\n\nPass `--no-fetch` only when the remote-tracking ref is already current and a fetch would be redundant.\n\n\u003C\u002Fworkflow>\n\n\u003Cdirty_tree_resolution>\n\nA `dirty_tree` outcome means uncommitted tracked changes block base movement. Inspect the exact tracked paths, establish ownership, and apply existing authorization to those paths before mutation. Existing authorization remains effective within its stated scope until the operator changes it. An analysis or interview label neither grants nor revokes that authority and never waives an unfinished prerequisite. Stash remains forbidden; committing clears the tree.\n\n1. **Authorized session-owned changes.** Classify changes Claude made during the active objective, respect explicit operator limits, and clear the precondition within the same invocation. Record the checked-out branch, or the full HEAD OID when detached, as the objective checkout. When objective paths and an unrelated note are dirty together, checkpoint the objective paths first:\n   - **Related to the objective** → when the worktree is detached or sitting on the default branch, run `git switch -c work\u002F\u003Cobjective-slug>` from the current commit and record that branch as the objective checkout; use skill `product-tree:commit-changes` immediately on that branch, staging only the objective paths, regardless of whether verification is passing, failing, or not run.\n   - **An unrelated note** — a `PLAN.md` \u002F `ISSUES.md` recording future work that is not part of the objective → once the note is the only remaining tracked change, run `git switch -c work\u002F\u003Cnote-slug> origin\u002F\u003Cbase>`, so the uncommitted note moves onto a branch cut from the fetched base, and commit it there through `\u002Fcommit-changes`. Return with `git switch \u003Cobjective-branch>`, or `git switch --detach \u003Cobjective-head-oid>` when the objective checkout was detached, before the re-run in step 4. When `git switch -c` refuses because the note's path differs between the objective head and `origin\u002F\u003Cbase>`, stop with that blocked branch move and the note path. Name the note branch and its full head identity in the result as a separate changeset not yet on the default branch.\n   - Every `git switch` in this step runs through the harness's per-call approval, as `\u003Cgit_command_policy>` states.\n2. **Operator-owned or unknown work.** When existing authorization covers committing the exact paths, use skill `product-tree:commit-changes` on their owning branch and continue. Otherwise preserve those files and their index state, report the blocked commit and exact paths, and request authority with a recommended commit option and a pause-and-inspect option. Apply the same boundary when an explicit operator limit withholds authority over session-owned paths. Never classify absent authority as a rebase conflict.\n3. **Confirm the checkpoint.** Require `\u002Fcommit-changes` to report a zero commit exit, changed full HEAD identity, committed and remaining paths, and verification state (`passing`, `failing`, or `not-run`). All three verification states permit preservation; later gates decide eligibility. A rejected hook, failed commit, unchanged HEAD, or incomplete result leaves recovery blocked. Preserve its exact diagnostics and repair through the owning workflow before retrying; never bypass hooks or infer success from a commit attempt. A decision about mutation authority cannot resolve a hook failure.\n4. **Re-run the primitive.** After a successful checkpoint, run `sync_base.py` again for the recorded checkout and base within the same invocation. Resolve any remaining authorized tracked changes through this recovery protocol. Finish only with `already_current` or `rebased`, or report the specific unresolved condition. Never report an intermediate authorized `dirty_tree` as completed synchronization.\n\n\u003C\u002Fdirty_tree_resolution>\n\n\u003Cconflict_reconciliation>\n\nA `conflict` outcome means a rebase is active. Finish it. Never abort it without a decision, and never leave it waiting for someone to notice.\n\nEvery decision this section asks for goes through the structured-question tool, the harness single question channel.\n\n**Check the cost first.** `git status` names the commit the rebase stopped at and how many remain. Before reconciling, and again whenever a conflict resists, compare finishing the rebase with the cheapest alternative: cherry-picking only the commits that still matter onto `origin\u002F\u003Cbase>`, or redoing the change from `origin\u002F\u003Cbase>` using the old branch as reference. When an alternative takes clearly less work, stop and ask through the structured-question tool, stating:\n\n- where the rebase stands: the commit it stopped at, the commits still to replay, and the conflicts so far by kind;\n- the suggested route and why it takes less work than finishing the rebase;\n- what each route keeps and loses, and the branch or commit that keeps the old work reachable.\n\nLeave the rebase active while the decision is pending. Then do what the answer decides: continue the rebase, or run `git rebase --abort` and take the route it chose.\n\n**Otherwise, reconcile.** Read the `conflict` object, inspect the repository state, and reconcile every conflict that deterministic evidence can decide:\n\n1. Inspect:\n   - `git status`\n   - `git diff`\n   - `git ls-files -u`\n   - `git show :1:\u003Cpath>`, `git show :2:\u003Cpath>`, and `git show :3:\u003Cpath>` for conflicted paths when stage contents are needed.\n2. Classify each conflicted path by portable role, never by repository-local path names:\n   - **source of truth** — the project-declared authoritative source for a behavior or derived artifact.\n   - **generated artifact** — a file produced from source by a project-declared regeneration command.\n   - **note** — `PLAN.md`, `ISSUES.md`, session notes, or the project's declared equivalents.\n   - **governance surface** — specs, decisions, review policy, merge policy, or project-declared control files.\n   - **ordinary implementation or test** — code or evidence governed by the loaded spec node and decisions.\n3. Resolve autonomously when evidence decides:\n   - Version or manifest bumps choose the monotonic\u002Flatest valid value, then include the exact project-declared version or manifest validation command in the result.\n   - Generated artifacts are resolved by resolving their source of truth first. Never hand-merge generated output when regeneration is available. Include the exact project-declared regeneration command in the result and require re-entry after regeneration; this is a mechanical continuation, not an operator decision.\n   - Redundant edits keep the change that supersedes the other by product truth, branch chronology, or source contract; remove the redundant text rather than preserving both.\n   - Nearby independent edits are combined when both are compatible with the loaded specs, decisions, and tests.\n   - Notes are reconciled in place to still-true facts: keep every entry either side adds that remains true, and delete a stale, duplicate, or superseded entry from the resolved file.\n4. After resolving a file, run `git add \u003Cresolved-paths>`.\n5. Continue with `git rebase --continue`.\n6. Return the resolved-path scope and the narrowest deterministic verification command the project's merge overlay, `spx\u002Flocal\u002Fmerging.md`, declares. Run that command through its governing workflow after the rebase completes; when the overlay cannot classify the paths, return the full deterministic gate command.\n\nWhen a remaining conflict is a product-intent conflict — specs, decisions, tests, newer session state, and git facts do not choose which behavior should survive — ask through the structured-question tool. The report says `Base sync stopped: rebase conflict requires reconciliation`, lists the conflicted paths, summarizes every reconciliation attempted, explains why evidence did not decide, gives the options from the `conflict.operator_options` list, and recommends one. Leave the rebase active while the decision is pending, then do what the answer decides.\n\nNever stop with a rebase active and no decision asked for. A rebase nobody owns stays in the worktree until someone finds it.\n\n\u003C\u002Fconflict_reconciliation>\n\n\u003Cgit_command_policy>\n\nGranted commands, which run without a per-call prompt:\n\n- Read state: `git status`, `git rev-parse`, `git symbolic-ref --short HEAD`, `git merge-base`, `git rev-list`, `git diff --name-only`, `git diff`, `git ls-files -u`, `git show :1:\u003Cpath>`, `git show :2:\u003Cpath>`, `git show :3:\u003Cpath>`.\n- Resolve: edit files, `git add \u003Cresolved-paths>`, `git rebase --continue`.\n- Take the decided route: `git rebase --abort`, run only on that decision under `\u003Cconflict_reconciliation>`.\n\nUngranted commands, which run through the harness's per-call approval because a prefix grant for them would also admit a form this policy forbids:\n\n- Resolve one classified path: `git checkout --ours -- \u003Cpath>` or `git checkout --theirs -- \u003Cpath>`.\n- Recover a dirty tree: `git switch -c work\u002F\u003Cobjective-slug>`, `git switch -c work\u002F\u003Cnote-slug> origin\u002F\u003Cbase>`, `git switch \u003Cobjective-branch>`, and `git switch --detach \u003Cobjective-head-oid>`, exactly as `\u003Cdirty_tree_resolution>` sequences them; the checkpoint commit itself goes through `\u002Fcommit-changes`.\n- Continue the decided route after `git rebase --abort`: the cherry-pick or redo commands that route names, as `\u003Cconflict_reconciliation>` sequences them.\n\nA per-call approval prompt is the harness's permission check on one command, never an operator question about the route; the route stays decided by evidence or by the answer to the structured question.\n\nThe synchronizer script owns base movement. It runs `git fetch origin \u003Cbase>` and either `git rebase origin\u002F\u003Cbase>` for an attached branch or `git switch --detach origin\u002F\u003Cbase>` for a clean detached HEAD that is an ancestor of the fetched base. Do not substitute direct sync commands for the script.\n\nExplicitly disallowed:\n\n- `git reset --hard`, `git reset --merge`, or any `git reset` as a base-sync mechanism.\n- `git stash`.\n- `git switch` with `-f`, `--force`, `--discard-changes`, `-C`, `--force-create`, or `-m`\u002F`--merge`, and any `git switch` that moves the checkout away from uncommitted objective paths.\n- `git checkout .` or `git restore .` to wipe conflict state.\n- Blanket `git checkout --ours .` or `git checkout --theirs .`.\n- Creating another worktree to escape the assigned one.\n- `git rebase --abort`, unless an answer to the structured question chose it under `\u003Cconflict_reconciliation>`.\n\nUse `--ours` or `--theirs` only for a specific path after classification has already decided the product result. The checkout flag is the mechanical file update, never the decision.\n\n\u003C\u002Fgit_command_policy>\n\n\u003Creadiness_preservation>\n\nOn `rebased` or `already_current`, the result carries a `preservation` object that identifies pre-push readiness work the base movement did not invalidate:\n\n- `old_base_oid`, `new_base_oid`, `old_head_oid`, `new_head_oid` — full OIDs before and after the sync.\n- `base_delta_paths` — the files the base advanced over.\n- `branch_paths_before`, `branch_paths_after` — the branch's own changed paths against the old and new base.\n- `path_overlap` — base-delta paths the branch also changed.\n- `branch_patch_changed` — whether the branch's patch identity differs across the sync.\n- `branch_diff_unchanged` — the git-only reuse signal: the branch patch is unchanged and the base delta does not overlap the branch.\n\nRead `branch_diff_unchanged` to consider a prior local review reusable — and **also** confirm, against the project's merge overlay `spx\u002Flocal\u002Fmerging.md`, that no `base_delta_paths` entry is a governance surface the reviewer judges against. Run that overlay's narrowest deterministic lane covering `base_delta_paths`, falling back to the full gate when the overlay is absent, any path is unclassified, or `path_overlap` is non-empty. The proof carries no lane name — lane mapping is the overlay's.\n\nThe proof scopes pre-push local work only. It never satisfies a merge gate: current-head pull-request checks and the current-head CI review still decide `MERGE_READINESS` after the push.\n\n\u003C\u002Freadiness_preservation>\n\n\u003Ctesting>\n\nThe bundled synchronizer is covered before release by this real-git test matrix:\n\n| Input                                                       | Expected result                                                                     |\n| ----------------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| attached branch at the fetched base tip                     | exit 0; `status=already_current`; non-null `preservation`                           |\n| attached branch behind the fetched base                     | exit 0; `status=rebased`; branch commit preserved; non-null `preservation`          |\n| attached branch behind the fetched base with a tracked edit | exit 4; `status=dirty_tree`; HEAD and working tree unchanged; no `conflict`         |\n| attached branch with conflicting commits                    | exit 3; `status=conflict`; active rebase state and structured `conflict`            |\n| clean detached HEAD behind the fetched base                 | exit 0; `status=rebased`; HEAD advanced to `origin\u002F\u003Cbase>`; non-null `preservation` |\n| detached HEAD behind the fetched base with a tracked edit   | exit 4; `status=dirty_tree`; HEAD and working tree unchanged; no `conflict`         |\n| clean detached HEAD at the fetched base tip                 | exit 0; `status=already_current`; HEAD unchanged; non-null `preservation`           |\n| explicit `--base` naming a non-default branch               | exit 0; `status=rebased` onto that branch's `origin\u002F\u003Cbase>`, not `origin\u002FHEAD`      |\n| branch diff holding bytes that are not valid UTF-8          | exit 0; `status=rebased`; `preservation` reports an unchanged branch patch identity |\n| `--no-fetch` with a remote base that does not resolve       | exit 1; `status=git_failure`; detached HEAD unchanged                               |\n| detached HEAD carrying a commit absent from the base        | exit 1; `status=git_failure`; HEAD unchanged                                        |\n| missing `origin` during fetch                               | exit 1; `status=git_failure`; actionable `detail`                                   |\n\nEvery fixture uses an invocation-unique temporary directory owned and removed by pytest's `tmp_path` fixture.\n\n\u003C\u002Ftesting>\n\n\u003Cfailure_modes>\n\n**Failure 1: Dirty-tree recovery stopped at an intermediate result.**\n\nWhat happened: Claude returned session-owned `dirty_tree` before branch creation, commit authorization, and retry completed.\n\nWhy it failed: The public sync capability exposed an internal precondition instead of completing authorized recovery itself.\n\nHow to avoid: Keep `dirty_tree` as the bundled script's deterministic result, resolve authorized work through `\u002Fcommit-changes` inside this workflow, and return only after rerunning the synchronizer.\n\n**Failure 2: An interview label suspended an authorized prerequisite.**\n\nWhat happened: Claude said base synchronization was paused during an interview even though existing authorization covered the checkpoint needed to continue.\n\nWhy it failed: A conversation label was treated as a change in authority, leaving the prerequisite unresolved.\n\nHow to avoid: Apply the operator's actual path-scoped authority and explicit limits, complete authorized recovery, and report any remaining blocked action with its evidence.\n\n**Failure 3: A rebase was left active with no decision asked for.**\n\nWhat happened: Claude stopped a rebase of a long-lived branch at commit 2 of 21, reported the first conflict, and ended its work with the rebase still active. The worktree stayed blocked for sixteen days, and the work was redone from the base anyway.\n\nWhy it failed: The stop waited for someone to inspect it. No question was asked, and nobody weighed finishing the rebase against cherry-picking or redoing the change from the base.\n\nHow to avoid: Check the cost before reconciling, ask a structured question with an assessment and a recommended route when another route takes less work, and act on the answer before stopping.\n\n\u003C\u002Ffailure_modes>\n\n\u003Csuccess_criteria>\n\n- The final primitive result is `already_current` or `rebased` for the recorded checkout and selected base, with the full identities and preservation facts `\u003Creadiness_preservation>` lists.\n- Every recovery checkpoint carries `\u002Fcommit-changes` proof of success and recorded verification state, and precedes a successful retry for that checkout and base.\n- Existing path-scoped authority and explicit operator limits govern mutations independently of interview labels and verification state.\n- An unresolved authority, checkpoint, Git, or product-intent condition reports the exact blocked action and evidence, with no claim of completed synchronization, clean working-tree state, or verification readiness.\n\n\u003C\u002Fsuccess_criteria>\n","\u003Cobjective>\nThe current checkout brought current with its fetched base, with authorized dirty work that blocks base movement checkpointed on its owning branch and detached-head safety preserved.\n\u003C\u002Fobjective>\n\n\u003Cworkflow>\n\nRecord the absolute checkout root and selected base, then run the synchronization primitive against that working tree (default: the current directory). Retain the same checkout and base through checkpoint recovery and retry:\n\n```bash\npython3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Fsync_base.py\" [repo] [--base \u003Cbranch>] [--no-fetch]\n```\n\nIt resolves the base ref and `origin\u002F\u003Cbase>` through the shared changeset-scope primitives and fetches the base. When an attached branch is behind, it rebases the branch onto the fetched base. When a clean detached HEAD is an ancestor of the fetched base, it advances the worktree with `git switch --detach origin\u002F\u003Cbase>`; a detached HEAD carrying commits absent from the base fails without moving. The base defaults to `origin\u002FHEAD`; pass `--base \u003Cbranch>` when the changeset tracks a non-default base (a stacked pull request whose base is another feature branch). It prints a JSON result (`status`, `base_ref`, `remote_ref`, `branch`, `detail`, `preservation` on a clean outcome, and `conflict` on an active rebase conflict) and exits:\n\n| `status`          | exit | meaning                                                                                                                                                                                                        | how Claude acts                                                                                                                                                            |\n| ----------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `already_current` | 0    | the branch is not behind the base                                                                                                                                                                              | proceed                                                                                                                                                                    |\n| `rebased`         | 0    | the branch was rebased onto `origin\u002F\u003Cbase>`                                                                                                                                                                    | proceed; use `\u003Creadiness_preservation>` to identify which verification and review evidence the base movement invalidated                                                   |\n| `conflict`        | 3    | the rebase stopped with active conflict state; the result's `conflict` object names the conflicted paths, git facts, git conflict text, and options                                                            | finish the rebase per `\u003Cconflict_reconciliation>`; when another route is cheaper or evidence cannot decide, ask through the structured-question tool and act on the answer |\n| `dirty_tree`      | 4    | the branch is behind, but uncommitted changes to tracked files block the rebase; no rebase is attempted and the tree is left untouched                                                                         | classify ownership per `\u003Cdirty_tree_resolution>`; commit authorized changes to the right branch, leave operator-owned work untouched, and re-run                           |\n| `git_failure`     | 1    | a diverged detached HEAD carrying its own commits, an unresolved base, a failed fetch, or an absent or unloadable sibling `scope-changeset` script — a clean behind-base detached HEAD is advanced, not failed | report `detail`; do not rebase                                                                                                                                             |\n\nThese statuses and exit codes belong to the primitive. Complete this workflow only after `already_current` or `rebased` establishes currency for the recorded checkout and base. A checkpoint alone establishes no currency, and a successful synchronization establishes neither working-tree cleanliness nor verification readiness. In particular, an already-current checkout may carry pending edits; those edits alone require no recovery checkpoint.\n\nResolve authorized `dirty_tree` state end to end through `\u003Cdirty_tree_resolution>`. The bundled synchronizer never commits or stashes; `\u002Fcommit-changes` owns checkpoint policy, hooks, and proof of success. Use skill `product-tree:commit-changes`. Create every recovery checkpoint through it, never through a direct `git commit`. Preserve the primitive result alongside any unresolved authority, checkpoint, Git, or conflict condition; a stopped recovery is no successful synchronization result.\n\nPass `--no-fetch` only when the remote-tracking ref is already current and a fetch would be redundant.\n\n\u003C\u002Fworkflow>\n\n\u003Cdirty_tree_resolution>\n\nA `dirty_tree` outcome means uncommitted tracked changes block base movement. Inspect the exact tracked paths, establish ownership, and apply existing authorization to those paths before mutation. Existing authorization remains effective within its stated scope until the operator changes it. An analysis or interview label neither grants nor revokes that authority and never waives an unfinished prerequisite. Stash remains forbidden; committing clears the tree.\n\n1. **Authorized session-owned changes.** Classify changes Claude made during the active objective, respect explicit operator limits, and clear the precondition within the same invocation. Record the checked-out branch, or the full HEAD OID when detached, as the objective checkout. When objective paths and an unrelated note are dirty together, checkpoint the objective paths first:\n   - **Related to the objective** → when the worktree is detached or sitting on the default branch, run `git switch -c work\u002F\u003Cobjective-slug>` from the current commit and record that branch as the objective checkout; use skill `product-tree:commit-changes` immediately on that branch, staging only the objective paths, regardless of whether verification is passing, failing, or not run.\n   - **An unrelated note** — a `PLAN.md` \u002F `ISSUES.md` recording future work that is not part of the objective → once the note is the only remaining tracked change, run `git switch -c work\u002F\u003Cnote-slug> origin\u002F\u003Cbase>`, so the uncommitted note moves onto a branch cut from the fetched base, and commit it there through `\u002Fcommit-changes`. Return with `git switch \u003Cobjective-branch>`, or `git switch --detach \u003Cobjective-head-oid>` when the objective checkout was detached, before the re-run in step 4. When `git switch -c` refuses because the note's path differs between the objective head and `origin\u002F\u003Cbase>`, stop with that blocked branch move and the note path. Name the note branch and its full head identity in the result as a separate changeset not yet on the default branch.\n   - Every `git switch` in this step runs through the harness's per-call approval, as `\u003Cgit_command_policy>` states.\n2. **Operator-owned or unknown work.** When existing authorization covers committing the exact paths, use skill `product-tree:commit-changes` on their owning branch and continue. Otherwise preserve those files and their index state, report the blocked commit and exact paths, and request authority with a recommended commit option and a pause-and-inspect option. Apply the same boundary when an explicit operator limit withholds authority over session-owned paths. Never classify absent authority as a rebase conflict.\n3. **Confirm the checkpoint.** Require `\u002Fcommit-changes` to report a zero commit exit, changed full HEAD identity, committed and remaining paths, and verification state (`passing`, `failing`, or `not-run`). All three verification states permit preservation; later gates decide eligibility. A rejected hook, failed commit, unchanged HEAD, or incomplete result leaves recovery blocked. Preserve its exact diagnostics and repair through the owning workflow before retrying; never bypass hooks or infer success from a commit attempt. A decision about mutation authority cannot resolve a hook failure.\n4. **Re-run the primitive.** After a successful checkpoint, run `sync_base.py` again for the recorded checkout and base within the same invocation. Resolve any remaining authorized tracked changes through this recovery protocol. Finish only with `already_current` or `rebased`, or report the specific unresolved condition. Never report an intermediate authorized `dirty_tree` as completed synchronization.\n\n\u003C\u002Fdirty_tree_resolution>\n\n\u003Cconflict_reconciliation>\n\nA `conflict` outcome means a rebase is active. Finish it. Never abort it without a decision, and never leave it waiting for someone to notice.\n\nEvery decision this section asks for goes through the structured-question tool, the harness single question channel.\n\n**Check the cost first.** `git status` names the commit the rebase stopped at and how many remain. Before reconciling, and again whenever a conflict resists, compare finishing the rebase with the cheapest alternative: cherry-picking only the commits that still matter onto `origin\u002F\u003Cbase>`, or redoing the change from `origin\u002F\u003Cbase>` using the old branch as reference. When an alternative takes clearly less work, stop and ask through the structured-question tool, stating:\n\n- where the rebase stands: the commit it stopped at, the commits still to replay, and the conflicts so far by kind;\n- the suggested route and why it takes less work than finishing the rebase;\n- what each route keeps and loses, and the branch or commit that keeps the old work reachable.\n\nLeave the rebase active while the decision is pending. Then do what the answer decides: continue the rebase, or run `git rebase --abort` and take the route it chose.\n\n**Otherwise, reconcile.** Read the `conflict` object, inspect the repository state, and reconcile every conflict that deterministic evidence can decide:\n\n1. Inspect:\n   - `git status`\n   - `git diff`\n   - `git ls-files -u`\n   - `git show :1:\u003Cpath>`, `git show :2:\u003Cpath>`, and `git show :3:\u003Cpath>` for conflicted paths when stage contents are needed.\n2. Classify each conflicted path by portable role, never by repository-local path names:\n   - **source of truth** — the project-declared authoritative source for a behavior or derived artifact.\n   - **generated artifact** — a file produced from source by a project-declared regeneration command.\n   - **note** — `PLAN.md`, `ISSUES.md`, session notes, or the project's declared equivalents.\n   - **governance surface** — specs, decisions, review policy, merge policy, or project-declared control files.\n   - **ordinary implementation or test** — code or evidence governed by the loaded spec node and decisions.\n3. Resolve autonomously when evidence decides:\n   - Version or manifest bumps choose the monotonic\u002Flatest valid value, then include the exact project-declared version or manifest validation command in the result.\n   - Generated artifacts are resolved by resolving their source of truth first. Never hand-merge generated output when regeneration is available. Include the exact project-declared regeneration command in the result and require re-entry after regeneration; this is a mechanical continuation, not an operator decision.\n   - Redundant edits keep the change that supersedes the other by product truth, branch chronology, or source contract; remove the redundant text rather than preserving both.\n   - Nearby independent edits are combined when both are compatible with the loaded specs, decisions, and tests.\n   - Notes are reconciled in place to still-true facts: keep every entry either side adds that remains true, and delete a stale, duplicate, or superseded entry from the resolved file.\n4. After resolving a file, run `git add \u003Cresolved-paths>`.\n5. Continue with `git rebase --continue`.\n6. Return the resolved-path scope and the narrowest deterministic verification command the project's merge overlay, `spx\u002Flocal\u002Fmerging.md`, declares. Run that command through its governing workflow after the rebase completes; when the overlay cannot classify the paths, return the full deterministic gate command.\n\nWhen a remaining conflict is a product-intent conflict — specs, decisions, tests, newer session state, and git facts do not choose which behavior should survive — ask through the structured-question tool. The report says `Base sync stopped: rebase conflict requires reconciliation`, lists the conflicted paths, summarizes every reconciliation attempted, explains why evidence did not decide, gives the options from the `conflict.operator_options` list, and recommends one. Leave the rebase active while the decision is pending, then do what the answer decides.\n\nNever stop with a rebase active and no decision asked for. A rebase nobody owns stays in the worktree until someone finds it.\n\n\u003C\u002Fconflict_reconciliation>\n\n\u003Cgit_command_policy>\n\nGranted commands, which run without a per-call prompt:\n\n- Read state: `git status`, `git rev-parse`, `git symbolic-ref --short HEAD`, `git merge-base`, `git rev-list`, `git diff --name-only`, `git diff`, `git ls-files -u`, `git show :1:\u003Cpath>`, `git show :2:\u003Cpath>`, `git show :3:\u003Cpath>`.\n- Resolve: edit files, `git add \u003Cresolved-paths>`, `git rebase --continue`.\n- Take the decided route: `git rebase --abort`, run only on that decision under `\u003Cconflict_reconciliation>`.\n\nUngranted commands, which run through the harness's per-call approval because a prefix grant for them would also admit a form this policy forbids:\n\n- Resolve one classified path: `git checkout --ours -- \u003Cpath>` or `git checkout --theirs -- \u003Cpath>`.\n- Recover a dirty tree: `git switch -c work\u002F\u003Cobjective-slug>`, `git switch -c work\u002F\u003Cnote-slug> origin\u002F\u003Cbase>`, `git switch \u003Cobjective-branch>`, and `git switch --detach \u003Cobjective-head-oid>`, exactly as `\u003Cdirty_tree_resolution>` sequences them; the checkpoint commit itself goes through `\u002Fcommit-changes`.\n- Continue the decided route after `git rebase --abort`: the cherry-pick or redo commands that route names, as `\u003Cconflict_reconciliation>` sequences them.\n\nA per-call approval prompt is the harness's permission check on one command, never an operator question about the route; the route stays decided by evidence or by the answer to the structured question.\n\nThe synchronizer script owns base movement. It runs `git fetch origin \u003Cbase>` and either `git rebase origin\u002F\u003Cbase>` for an attached branch or `git switch --detach origin\u002F\u003Cbase>` for a clean detached HEAD that is an ancestor of the fetched base. Do not substitute direct sync commands for the script.\n\nExplicitly disallowed:\n\n- `git reset --hard`, `git reset --merge`, or any `git reset` as a base-sync mechanism.\n- `git stash`.\n- `git switch` with `-f`, `--force`, `--discard-changes`, `-C`, `--force-create`, or `-m`\u002F`--merge`, and any `git switch` that moves the checkout away from uncommitted objective paths.\n- `git checkout .` or `git restore .` to wipe conflict state.\n- Blanket `git checkout --ours .` or `git checkout --theirs .`.\n- Creating another worktree to escape the assigned one.\n- `git rebase --abort`, unless an answer to the structured question chose it under `\u003Cconflict_reconciliation>`.\n\nUse `--ours` or `--theirs` only for a specific path after classification has already decided the product result. The checkout flag is the mechanical file update, never the decision.\n\n\u003C\u002Fgit_command_policy>\n\n\u003Creadiness_preservation>\n\nOn `rebased` or `already_current`, the result carries a `preservation` object that identifies pre-push readiness work the base movement did not invalidate:\n\n- `old_base_oid`, `new_base_oid`, `old_head_oid`, `new_head_oid` — full OIDs before and after the sync.\n- `base_delta_paths` — the files the base advanced over.\n- `branch_paths_before`, `branch_paths_after` — the branch's own changed paths against the old and new base.\n- `path_overlap` — base-delta paths the branch also changed.\n- `branch_patch_changed` — whether the branch's patch identity differs across the sync.\n- `branch_diff_unchanged` — the git-only reuse signal: the branch patch is unchanged and the base delta does not overlap the branch.\n\nRead `branch_diff_unchanged` to consider a prior local review reusable — and **also** confirm, against the project's merge overlay `spx\u002Flocal\u002Fmerging.md`, that no `base_delta_paths` entry is a governance surface the reviewer judges against. Run that overlay's narrowest deterministic lane covering `base_delta_paths`, falling back to the full gate when the overlay is absent, any path is unclassified, or `path_overlap` is non-empty. The proof carries no lane name — lane mapping is the overlay's.\n\nThe proof scopes pre-push local work only. It never satisfies a merge gate: current-head pull-request checks and the current-head CI review still decide `MERGE_READINESS` after the push.\n\n\u003C\u002Freadiness_preservation>\n\n\u003Ctesting>\n\nThe bundled synchronizer is covered before release by this real-git test matrix:\n\n| Input                                                       | Expected result                                                                     |\n| ----------------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| attached branch at the fetched base tip                     | exit 0; `status=already_current`; non-null `preservation`                           |\n| attached branch behind the fetched base                     | exit 0; `status=rebased`; branch commit preserved; non-null `preservation`          |\n| attached branch behind the fetched base with a tracked edit | exit 4; `status=dirty_tree`; HEAD and working tree unchanged; no `conflict`         |\n| attached branch with conflicting commits                    | exit 3; `status=conflict`; active rebase state and structured `conflict`            |\n| clean detached HEAD behind the fetched base                 | exit 0; `status=rebased`; HEAD advanced to `origin\u002F\u003Cbase>`; non-null `preservation` |\n| detached HEAD behind the fetched base with a tracked edit   | exit 4; `status=dirty_tree`; HEAD and working tree unchanged; no `conflict`         |\n| clean detached HEAD at the fetched base tip                 | exit 0; `status=already_current`; HEAD unchanged; non-null `preservation`           |\n| explicit `--base` naming a non-default branch               | exit 0; `status=rebased` onto that branch's `origin\u002F\u003Cbase>`, not `origin\u002FHEAD`      |\n| branch diff holding bytes that are not valid UTF-8          | exit 0; `status=rebased`; `preservation` reports an unchanged branch patch identity |\n| `--no-fetch` with a remote base that does not resolve       | exit 1; `status=git_failure`; detached HEAD unchanged                               |\n| detached HEAD carrying a commit absent from the base        | exit 1; `status=git_failure`; HEAD unchanged                                        |\n| missing `origin` during fetch                               | exit 1; `status=git_failure`; actionable `detail`                                   |\n\nEvery fixture uses an invocation-unique temporary directory owned and removed by pytest's `tmp_path` fixture.\n\n\u003C\u002Ftesting>\n\n\u003Cfailure_modes>\n\n**Failure 1: Dirty-tree recovery stopped at an intermediate result.**\n\nWhat happened: Claude returned session-owned `dirty_tree` before branch creation, commit authorization, and retry completed.\n\nWhy it failed: The public sync capability exposed an internal precondition instead of completing authorized recovery itself.\n\nHow to avoid: Keep `dirty_tree` as the bundled script's deterministic result, resolve authorized work through `\u002Fcommit-changes` inside this workflow, and return only after rerunning the synchronizer.\n\n**Failure 2: An interview label suspended an authorized prerequisite.**\n\nWhat happened: Claude said base synchronization was paused during an interview even though existing authorization covered the checkpoint needed to continue.\n\nWhy it failed: A conversation label was treated as a change in authority, leaving the prerequisite unresolved.\n\nHow to avoid: Apply the operator's actual path-scoped authority and explicit limits, complete authorized recovery, and report any remaining blocked action with its evidence.\n\n**Failure 3: A rebase was left active with no decision asked for.**\n\nWhat happened: Claude stopped a rebase of a long-lived branch at commit 2 of 21, reported the first conflict, and ended its work with the rebase still active. The worktree stayed blocked for sixteen days, and the work was redone from the base anyway.\n\nWhy it failed: The stop waited for someone to inspect it. No question was asked, and nobody weighed finishing the rebase against cherry-picking or redoing the change from the base.\n\nHow to avoid: Check the cost before reconciling, ask a structured question with an assessment and a recommended route when another route takes less work, and act on the answer before stopping.\n\n\u003C\u002Ffailure_modes>\n\n\u003Csuccess_criteria>\n\n- The final primitive result is `already_current` or `rebased` for the recorded checkout and selected base, with the full identities and preservation facts `\u003Creadiness_preservation>` lists.\n- Every recovery checkpoint carries `\u002Fcommit-changes` proof of success and recorded verification state, and precedes a successful retry for that checkout and base.\n- Existing path-scoped authority and explicit operator limits govern mutations independently of interview labels and verification state.\n- An unresolved authority, checkpoint, Git, or product-intent condition reports the exact blocked action and evidence, with no claim of completed synchronization, clean working-tree state, or verification readiness.\n\n\u003C\u002Fsuccess_criteria>\n",{"name":5,"description":7,"allowed-tools":44},"Read, Edit, {{! tool('use_skill') !}}, {{! tool('ask_user') !}}, Bash(python3 \"${CLAUDE_SKILL_DIR}\u002Fscripts\u002Fsync_base.py\":*), Bash(git status:*), Bash(git rev-parse:*), Bash(git symbolic-ref:*), Bash(git merge-base:*), Bash(git rev-list:*), Bash(git diff:*), Bash(git ls-files:*), Bash(git show:*), Bash(git add:*), Bash(git rebase --continue), Bash(git rebase --abort)","main","f1f479d9550ca2349a637d3ad24fb421d15046ac","src\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Fsync-base","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins","https:\u002F\u002Fgithub.com\u002Foutcomeeng","https:\u002F\u002Foutcome.engineering","Spec-driven development for AI-assisted engineering","2026-11-13 22:49:15",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add outcomeeng\u002Fplugins --skill sync-base",{"tool":59,"command":60},"bunx","bunx skills add outcomeeng\u002Fplugins --skill sync-base",{"tool":62,"command":63},"pnpm","pnpm dlx skills add outcomeeng\u002Fplugins --skill sync-base","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Fsync-base\u002FSKILL.md",true,[67,73],{"path":68,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":71,"sourceHtmlUrl":72},"scripts\u002F__init__.py","SCRIPT","脚本",0,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Fsync-base\u002Fscripts\u002F__init__.py",{"path":74,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":75,"sourceHtmlUrl":76},"scripts\u002Fsync_base.py",33746,"https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fproduct-tree\u002Fskills\u002Fsync-base\u002Fscripts\u002Fsync_base.py",[78,84,119,135,152,165],{"skillNo":79,"name":80,"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":81,"categories":82,"keywords":83,"publishedAt":39},"01M71G25RZTPQG9KJ00JCHC1F7","plugin","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Ftemplates\u002Fplugin\u002FSKILL.md",[],[80],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":100,"publishedAt":39},"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",[90,95],{"categoryNo":91,"categoryName":92,"taxonomyType":93,"taxonomyTypeDesc":94},"01JBSEEDCAT000000000000003","演示文稿","SCENARIO","使用场景",{"categoryNo":96,"categoryName":97,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[101,102,103,104,105,106,107,33,108,109,110,111,112,113,114,115,116,117,24,118],"sanitize powerpoint","powerpoint","sanitize","fonts","type","cleaning","structure","attributes","theme","pptx","naming","layout","deck","up","edit","xml","master","when",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":39},"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",[],[126,127,128,129,130,33,24,131,132,118,133,134],"draw excalidraw","excalidraw","draw","diagrams","architectures","generate","workflows","concepts","json",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":144,"publishedAt":39},"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",[141],{"categoryNo":142,"categoryName":143,"taxonomyType":93,"taxonomyTypeDesc":94},"01JBSEEDCAT000000000000008","测试与质量",[145,146,147,148,149,150,151],"typescript","typescript test standards","test","standards","false","read","across",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":161,"publishedAt":39},"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",[158],{"categoryNo":159,"categoryName":160,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000030","开发工程师",[145,162,163,164,148,149,150,151],"code","typescript code standards","code standards",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":39},"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",[],[172,148,173,174,145,175,176,151,150,177,149,178],"architecture standards","typescript architecture standards","architecture","adr","architect","auditor","conventions"]