---
name: deft-directive-sync
description: >-
  Session-start framework sync and upgrade handoff. Refreshes deposit via npm
  + directive/deft update, validates xBRIEF lifecycle, detects stale origins
  (RFC D12), and finishes SCM release handoff in released | pr-open |
  blocked:<reason> (#1604).
triggers:
  - good morning
  - update deft
  - update directive
  - upgrade framework
  - update xbrief
  - sync frameworks
---
<!-- AUTO-GENERATED by task packs:render -- DO NOT EDIT MANUALLY -->
<!-- Purpose: rendered skill -->
<!-- Source of truth: packs/skills/skills-pack-0.1.json -->
<!-- Regenerate with: task packs:render -->
<!-- Edit the source, not this file. Slice instead of loading every SKILL.md: task packs:slice skills by-trigger --trigger <kw> (or list) -->

# Deft Directive Sync

Session-start framework sync and upgrade handoff -- refresh the framework deposit via npm + `directive update` / `deft update`, validate xBRIEF lifecycle structure, detect stale origins (RFC D12), then finish SCM release handoff in a named terminal state (#1604).

> **Canonical bootstrap / update path (#761 / #1604):** Install and upgrade via npm: `npm i -g @deftai/directive` (install) or `npm i -g @deftai/directive@latest` (upgrade); Node >= 20 is required. Then from the project root run `directive update` (or `deft update`) to refresh `.deft/core/`, optionally `deft migrate` for npm provenance, and `directive doctor` / `deft doctor` / `task doctor` to confirm deposit health. For machines without Node, the frozen legacy Go installer (`deft-install` / platform-specific `install-*` from GitHub Releases) is a no-Node bootstrap bridge (#1912) -- migrate to npm once Node is available. Legacy Python-launcher upgrade / `task upgrade` are metadata-only acknowledgment (they do NOT replace the payload). **Git-submodule / `task framework:doctor` paths are back-compat only** -- Phases 1-2 below are the legacy update flow, de-emphasized in UPGRADING.md / README. Deposit success is not upgrade released; Phase 8 records one terminal state: `released` | `pr-open` | `blocked:<reason>`. See UPGRADING.md and #761 / #1912 / #1604.

Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.

## Platform Requirements

! This skill requires **GitHub** as the SCM platform and the **GitHub CLI (`gh`)** to be installed and authenticated for origin freshness (Phase 5) and for the PR path of Phase 8 SCM release handoff. Origin freshness checks fetch issue data via REST-equivalent `gh` surfaces (prefer `gh api repos/<owner>/<repo>/issues/<N>` over GraphQL-heavy forms when under rate pressure; #954).

## When to Use

- User says "good morning", "update deft", "update directive", "upgrade framework", "update xbrief", or "sync frameworks"
- Beginning of a new session where framework updates may be available
- After a known upstream deft / `@deftai/directive` release
- After a successful local deposit when the default branch still lacks the framework update

## Missing CLI / PATH remediation

! When `directive`, `deft`, or the global npm bin is missing from PATH (or `npm i -g @deftai/directive@latest` has not been run), surface actionable install remediation first:

```bash
npm i -g @deftai/directive@latest
```

(or the pnpm equivalent: `pnpm add -g @deftai/directive@latest`). Then re-run `directive update` / `deft update` and `directive doctor`.

⊗ Send the operator to manual GitHub release-asset archaeology or Go installer discovery as the happy path when Node is available -- npm is the primary remediation (#761 / #1604).

## Session orientation — unmanaged header (#2065)

! The region of AGENTS.md **above** the `<!-- deft:managed-section ... -->` marker is project-owned, preserved verbatim on refresh, and **not** freshness-checked by `deft doctor`.

- ! Do NOT treat that unmanaged header as the work queue — consult `deft triage:queue`, `xbrief/` lifecycle, GitHub issues, and `PROJECT-DEFINITION.xbrief.json` instead (#1149, #2065 Option A).
- ⊗ Do NOT add or revive `Status`, `Next:`, or `Known Issues` blocks in AGENTS.md — they duplicate authoritative sources and rot silently while the managed section stays current.

## Framework Events Emitted Here

! When this skill responds to a context-window shift or an explicit "are you using Deft?" probe (per AGENTS.md Deft Alignment Confirmation), emit the paired `session:interrupted` -> `session:resumed` framework events via `task lifecycle:event` so observability of agent-runtime state transitions is structural, not prose-only:

- ! Before re-confirming alignment: `task lifecycle:event -- emit session:interrupted --session-id <id> --reason <enum>`
- ! Immediately after the alignment confirmation line: `task lifecycle:event -- emit session:resumed --session-id <id> --interrupted-id <id-from-prior-emit>`
- ⊗ Emit a `session:resumed` whose `interrupted_id` does not reference a prior `session:interrupted` -- such records are orphan and rejected by `task lifecycle:event -- validate-pairing` (#635 events behavioral wiring)

## Pre-Cutover Detection Guard

! Before proceeding with sync, detect whether the project uses the pre-v0.20 document model and report model state.

### Detection Criteria

A project is **pre-cutover** if ANY of the following are true. This prose mirrors the executable helper in `task migrate:preflight`; when in doubt, the helper is canonical.

1. `SPECIFICATION.md` exists and is neither a deprecation redirect nor a current generated spec export. A current generated spec export contains `<!-- Purpose: rendered specification -->` and `<!-- Source of truth: xbrief/specification.xbrief.json -->`, and `xbrief/specification.xbrief.json` plus all five lifecycle folders exist.
2. `PROJECT.md` exists and contains neither the legacy `<!-- deft:deprecated-redirect -->` sentinel NOR the current `Purpose: deprecation redirect` canonical-banner marker (real content, not a deprecation redirect)
3. `xbrief/specification.xbrief.json` exists but the lifecycle folders (`xbrief/proposed/`, `xbrief/pending/`, `xbrief/active/`, `xbrief/completed/`, `xbrief/cancelled/`) do NOT exist

### Action on Detection

! If pre-cutover state is detected, display the actionable migration message, then **skip Phases 0-6** and proceed directly to Phase 7 with the Document Model line set to "pre-v0.20 (legacy)". Phase 8 still applies if a local migration/framework change must land on the default branch after the frozen migrator path:

> "This project uses the pre-v0.20 document model. Current npm releases no longer ship in-product `task migrate:vbrief` (#2068). Follow UPGRADING.md § Frozen pre-v0.20 document-model migration: pin framework v0.59.0, install Python 3.11+ and uv, run `task migrate:vbrief` once from that payload, then upgrade to current npm."

! Include specific details about what was detected:

- Missing lifecycle folders: "Create lifecycle folders via the frozen v0.59.0 migrator (#2068), or manually add `xbrief/{proposed,pending,active,completed,cancelled}/` after migrating narratives"
- `SPECIFICATION.md` with real content: "SPECIFICATION.md contains non-redirect content -- this file is deprecated; use scope xBRIEFs in `xbrief/` instead"
- `PROJECT.md` with real content: "PROJECT.md contains non-redirect content -- this file is deprecated; use `PROJECT-DEFINITION.xbrief.json` instead"
- Missing `PROJECT-DEFINITION.xbrief.json`: "Run `task project:render` to generate the project definition"
- Scope xBRIEF in wrong folder: "Status is '{status}' but file is in {folder}/ -- run `task scope:activate <file>` to fix"

### Model State in Sync Output

! Include a **Document Model** line in the Phase 7 summary:

- Pre-cutover detected: "**Document Model**: pre-v0.20 (legacy) -- follow UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068)"
- Post-cutover (lifecycle folders present, no stale artifacts): "**Document Model**: v0.20+ (xBRIEF-centric) -- OK"
- Post-cutover with tampered placeholders: "**Document Model**: v0.20+ with warnings -- SPECIFICATION.md or PROJECT.md contains non-redirect content"

⊗ Skip model state detection during sync -- always report the document model state.
⊗ Silently ignore pre-cutover artifacts -- the user must be informed with an actionable command to fix the state.

## Phase 0 -- Primary upgrade (npm deposit) (#761 / #1604)

! Treat npm + `directive update` / `deft update` as the **primary** consumer upgrade path when an upgrade is authorized. Do not lead with submodule update when Node is available.

### 0a: When mutation is authorized

! **Mutating** engine install / deposit refresh is authorized only when at least one of:

1. The operator used an **explicit upgrade** trigger: `update deft`, `update directive`, `upgrade framework`, or equivalent ("upgrade Directive", "run update").
2. Doctor / payload-staleness already reports the deposit is **behind** and the operator confirmed the upgrade (or autonomous upgrade was pre-approved).
3. CLI is **missing** from PATH -- then Missing CLI remediation may install the global package so doctor/update can run.

! For routine session orientation triggers (`good morning`, `sync frameworks`, plain `update xbrief` without upgrade language):

1. ! Run **read-only** checks: CLI present?, `directive doctor` / `deft doctor` / `task doctor` when available, structure validation (Phases 3+).
2. ! If doctor reports a stale deposit or available upgrade, **report** the recommended command and ask for consent before mutating.
3. ⊗ Run `npm i -g @deftai/directive@latest` or `directive update` / `deft update` on a routine "good morning" without staleness evidence **and** operator consent (or pre-approved autonomous upgrade).

### 0b: Consumer worktree isolation before deposit

! Before any mutating deposit that will be handed off in Phase 8:

1. ! Run `git status --porcelain` at the **project root** (not only the legacy submodule).
2. ! If the worktree or index has non-framework product changes (or any unexpected staged paths), **stop** and either:
   - ask the operator to stash / commit product work first, or
   - record `blocked:dirty-worktree` and skip deposit mutation,
   - or, with explicit consent, isolate product changes (stash including index) so the deposit cannot mix with them.
3. ! After deposit, stage **only** framework-managed paths for the upgrade commit (e.g. `.deft/core/`, managed AGENTS section, hooks, VERSION / marker files). Reconstruct a clean index if needed rather than `git add -A`.
4. ⊗ Create a framework-only commit or PR from a mixed worktree/index that still carries product feature paths.

### 0c: Engine + deposit (when authorized)

1. ! Confirm the global CLI is available (`directive --version` or `deft --version`). If missing, run Missing CLI / PATH remediation above, then continue.
2. ! Upgrade the global engine only when mutation is authorized (0a):

```bash
npm i -g @deftai/directive@latest
```

3. ! From the **project root**, after worktree isolation (0b), refresh the deposit:

```bash
directive update
# or: deft update
```

4. ~ Optionally stamp npm provenance (idempotent): `directive migrate` / `deft migrate`.
5. ! Verify deposit health:

```bash
directive doctor
# or: deft doctor / task doctor
```

6. ! Record whether the working tree now has **framework-only** changes under `.deft/core/`, managed AGENTS section, hooks, or related managed files that need SCM release.

### 0d: Framework-only change-set discipline

! Keep upgrade commits/PRs **framework-only** -- do not mix product feature work into the same commit or PR as the deposit refresh.

⊗ Treat a successful deposit alone as `released` -- the default branch must carry the update (or a PR must be open) before the operator goal is complete (#1604).
⊗ Force a global CLI upgrade or deposit mutation on routine session sync without consent or staleness evidence.

## Phase 1 -- Pre-flight (legacy submodule path)

! **Legacy / back-compat only.** Run Phases 1-2 only when the consumer still uses a `deft/` git submodule layout and cannot use the npm deposit path. Prefer Phase 0 for all npm-managed installs.

! Check that the deft/ submodule working tree is clean before attempting any update.

1. ! Run `git -C deft status --porcelain`
2. ! If output is non-empty (dirty working tree): **stop** and ask user whether to stash (`git -C deft stash`) or abort the sync entirely. Do NOT proceed with a dirty submodule. Record terminal state `blocked:dirty-submodule` if the operator aborts.
3. ! Record the current DEFT commit for later comparison:
   ```
   git -C deft log --oneline -1
   ```
4. ! Present the current state to the user:
   - Current DEFT commit (hash + subject)
   - Clean/dirty status
   - Confirmation that pre-flight passed (or the blocker if dirty)

## Phase 2 -- Update DEFT Submodule (legacy / back-compat)

! **Legacy / back-compat only** -- not the primary consumer upgrade path (#1604). Submodule update does not replace Phase 0 for npm installs.

1. ! Run the submodule update:
   ```
   git submodule update --remote --merge deft
   ```
2. ! Show what changed by comparing before/after:
   ```
   git -C deft log --oneline <old-hash>..HEAD
   ```
3. ~ If no new commits, report "deft submodule already up to date" and proceed to Phase 3.

## Phase 3 -- Structure Validation

! Validate the xBRIEF lifecycle folder structure and project files.

### 3a: Lifecycle Folder Structure

! Verify all required lifecycle folders exist:

1. ! Check that the following directories exist under `./xbrief/`:
   - `proposed/`
   - `pending/`
   - `active/`
   - `completed/`
   - `cancelled/`
2. ! Report any missing folders with a clear warning:
   - "WARNING: xbrief/{folder}/ does not exist -- lifecycle structure is incomplete"
3. ~ If folders are missing, suggest `task migrate:preflight` and the frozen v0.59.0 migrator path (#2068), or creating them manually after migration

### 3b: PROJECT-DEFINITION.xbrief.json Validation

! Validate the project identity gestalt file:

1. ! Check that `./xbrief/PROJECT-DEFINITION.xbrief.json` exists
   - If missing: "WARNING: PROJECT-DEFINITION.xbrief.json not found -- run `task project:render` to create"
2. ! If the file exists, validate it is well-formed:
   - Valid JSON (`python3 -m json.tool` or equivalent)
   - Top-level `xBRIEFInfo` envelope with `version` field equal to `"0.8"` (legacy `"0.6"` is read-accepted until `deft migrate:xbrief`)
   - `plan` object with `title`, `status`, and `items` fields present
   - `plan.narratives` values are plain strings (not objects or arrays)
3. ! **Freshness check**: Compare `xBRIEFInfo.updated` (or `xBRIEFInfo.created` if no `updated`) against recent scope completions:
   - Scan `xbrief/completed/` for xBRIEFs with `xBRIEFInfo.updated` timestamps newer than the PROJECT-DEFINITION timestamp
   - If stale: "WARNING: PROJECT-DEFINITION.xbrief.json may be stale -- {N} scopes completed since last update. Run `task project:render` to refresh."

### 3c: Validate Root-Level xBRIEF Files

! Validate all `./xbrief/*.xbrief.json` files at the xbrief root:

1. ! Check each file is valid JSON
2. ! Verify structural conformance:
   - Top-level `xBRIEFInfo` envelope with `version` field present
   - `plan` object with `title`, `status`, and `items` fields present
   - `plan.status` values from valid enum: draft, proposed, approved, pending, running, completed, blocked, cancelled
3. ~ Use `task xbrief:validate` for deeper validation
4. ! Report any validation failures with file name and specific violation

⊗ Overwrite or modify project-level `./xbrief/*.xbrief.json` files -- those are project data, not framework files. Report issues and let the user decide how to fix them.

## Phase 4 -- Lifecycle Consistency Check

! Verify that each scope xBRIEF's `plan.status` matches its folder location.

1. ! Scan all scope xBRIEFs in lifecycle folders (`proposed/`, `pending/`, `active/`, `completed/`, `cancelled/`)
2. ! For each xBRIEF, check `plan.status` against the expected statuses for its folder:
   - `proposed/`: status should be `draft` or `proposed`
   - `pending/`: status should be `approved` or `pending`
   - `active/`: status should be `running` or `blocked`
   - `completed/`: status should be `completed`
   - `cancelled/`: status should be `cancelled`
3. ! Report any mismatches:
   - "MISMATCH: {filename} in {folder}/ has status '{status}' -- expected one of [{expected_statuses}]"
4. ~ Per `vbrief/vbrief.md` convention, trust the status field and suggest correcting the folder location:
   - "Suggested fix: move {filename} to {correct_folder}/ (status '{status}' is authoritative)"

⊗ Auto-move xBRIEFs to fix folder/status mismatches -- report only; user decides during refinement or ad-hoc

## Phase 5 -- Origin Freshness (RFC D12)

! For xBRIEFs with external origin references, detect externally-closed origins. `task reconcile:issues` reports linked / unlinked / closed / completed-status drift -- it does **not** have a stale-content (`updatedAt`) bucket. Implementation-intent fail-closed for a newer origin is `task xbrief:preflight` (#3363).

### Step 1: Scan Origins

1. ! Run `task reconcile:issues` for linked / unlinked / externally-closed / completed-status drift.
2. ! For each xBRIEF in `proposed/` and `pending/` with a `github-issue` reference in `plan.references` or top-level `references`, an advisory scan MAY fetch the issue (`gh api repos/{owner}/{repo}/issues/{N}`) and compare `updatedAt` / `updated_at` against `xBRIEFInfo.updated` (or `xBRIEFInfo.created` if no `updated` field). This is report-only; it is not the engine verb.

### Step 2: Categorize and Report

1. ! **Externally closed origins** -- issue state is `CLOSED` (from `task reconcile:issues`):
   - "{N} xBRIEFs have origins that were closed externally"
   - List each: "{filename}: Issue #{N} is closed ({close_reason})"
2. ! **Advisory newer origin** -- issue `updatedAt` is newer than xBRIEF `updated` timestamp (manual scan or later `task xbrief:preflight`):
   - "{N} xBRIEFs have origins updated since last sync"
   - List each: "{filename}: Issue #{N} updated {time_delta} ago"
3. ~ **Current origins** -- no changes detected (report count only)

### Step 3: Recommendation

- ! Report only -- never auto-update xBRIEFs based on origin changes
- ~ If newer or externally-closed origins are found, suggest: "Run a refinement session (`skills/deft-directive-refinement/SKILL.md`) to walk origin changes with user approval. Implementation will fail closed at `task xbrief:preflight` until the brief is refreshed or intentional divergence is recorded."

⊗ Auto-update xBRIEFs based on origin freshness checks -- report only; user decides during refinement

## Phase 6 -- Framework Sync

After structure validation, sync framework-level assets.

### 6a: Check AGENTS.md freshness

~ Compare the project's `AGENTS.md` against the deft template (if a template exists in the updated deposit / `.deft/core/` or legacy `deft/` submodule):

1. ~ Diff the structure (section headings, key rules) rather than expecting byte-identical content
2. ~ Report any new sections or rules added upstream that are missing locally
3. ~ Do NOT auto-overwrite -- present differences and let the user decide
4. ~ If the unmanaged header still carries `Status`, `Next:`, or `Known Issues`, recommend replacing them with the **Session orientation** pointer at `xbrief/` + triage + issues (#2065 Option A) -- do NOT treat that header prose as the work queue

### 6b: Check codebase MAP freshness

~ If `./.planning/codebase/MAP.md` exists, or `PROJECT-DEFINITION.xbrief.json` declares a `projectionManifest[]` entry with `kind: "codebase-map"`, run `task verify:codebase-map-fresh` when the command resolves. If it reports drift, recommend `task codebase:map` and note that the generated MAP is advisory unless the operator asked to refresh projections.

- ! Keep `plan.architecture.codeStructure` and configured provider artifacts authoritative; the MAP is a generated projection.
- ⊗ Auto-edit canonical xBRIEF metadata to make the MAP fresh during sync -- report drift and let the operator choose a follow-up.

### 6c: List new skills

! Compare the `skills/` directory (or deposited `.deft/core/` skills) before and after the update:

1. ! List any new skill directories added in the update
2. ~ For each new skill, read its frontmatter `description` field and present a one-liner
3. ~ Mention if any existing skills were updated (changed files)

## Phase 6d -- Legacy Artifact Review (post-migration, one-time)

! If `xbrief/migration/LEGACY-REPORT.md` exists (and has NOT been renamed to `LEGACY-REPORT.reviewed.md`), walk the operator through each captured legacy section and record their disposition inline in the same file. This phase surfaces the non-canonical content that `task migrate:xbrief` preserved via the `LegacyArtifacts` narrative mechanism (#505).

### Detection

1. ! Check for `xbrief/migration/LEGACY-REPORT.md` in the project root.
2. ! If the file is absent or `LEGACY-REPORT.reviewed.md` exists (reviewed form), skip Phase 6d silently and proceed to Phase 7.
3. ! If `LEGACY-REPORT.md` is present and has NOT been renamed, begin the review loop below.

### Review loop

1. ! Present the report summary (sources + per-bucket section counts) to the user.
2. ! For each captured section listed under `## specification.xbrief.json -> LegacyArtifacts`, `## PROJECT-DEFINITION.xbrief.json -> LegacyArtifacts`, and `## PRD.md content (flagged: hand-edited)`:
   - Restate the section title, source file + line range, and size.
   - Offer exactly three disposition options: **Keep** (leave inside `LegacyArtifacts`), **Fold into {suggested narrative}** (move into a canonical narrative key), or **Drop** (remove from `LegacyArtifacts`, with explicit user confirmation).
   - ~ If a sidecar pointer is present (`xbrief/legacy/{stem}-{slug}.md`), open the sidecar for the user before offering options so the full content is visible.
3. ! Record each disposition inline in the same `LEGACY-REPORT.md` file under a new `## Reviewed` section with one entry per legacy item: original section, user's decision, target location (if folded) or confirmation note (if kept/dropped), and the reviewer's timestamp.
4. ! For a **Fold** decision, the agent updates the target xBRIEF's narrative key AND deletes only the corresponding section from the `LegacyArtifacts` narrative -- never the file.
5. ! For a **Drop** decision, the agent removes only the corresponding section from the `LegacyArtifacts` narrative.
6. ! Once all sections carry a recorded disposition, rename the file to `LEGACY-REPORT.reviewed.md`. The file is kept so the audit trail remains -- ⊗ MUST NOT delete either form.

### Anti-patterns

- ⊗ Delete `LEGACY-REPORT.md` or `LEGACY-REPORT.reviewed.md` -- these are the migration audit trail and MUST persist.
- ⊗ Auto-dispose of legacy artifacts without user input -- every section requires an explicit decision.
- ⊗ Rename to `.reviewed.md` before every captured section has a recorded disposition in the `## Reviewed` section.
- ⊗ Drop a legacy section without explicit user confirmation (even if the section looks obviously stale).
- ⊗ Silently delete sidecar files under `xbrief/legacy/` -- they are referenced from `LegacyArtifacts` and are part of the audit trail.

## Phase 6e -- Doc-sprawl awareness (advisory, #647)

Doc sprawl is a project-health concern, not just a human-experience one: a lean, well-written AGENTS.md sitting on top of a large reachable doc corpus does **not** stop agents from discovering and loading those docs (Augment Code study, `content/docs/good-agents-md.md`). It degrades agent quality silently until measured. This step **surfaces** the risk; it is ADVISORY and MUST NOT block or fail the sync.

1. ~ Scan the project's reachable documentation footprint: the top-level `docs/` / `_docs/` directories, nested `README.md` files, and any `architecture` / `design` docs.
2. ~ Flag, as a non-blocking nudge, when any of these appear:
   - A large `docs/` (or `_docs/`) directory that is **not referenced** from the AGENTS.md reference chain (orphan docs are discovered <10% of the time yet still cost context when found -- the reference-chain contract, #644).
   - Deeply nested READMEs that duplicate guidance the reference chain already carries.
   - Architecture / design docs that restate what the codebase already shows (a measured overexploration trigger).
3. ~ Point the operator at `content/docs/agent-docs.md` (the empirically-grounded structure pattern) and the `REFERENCES.md` reference-chain contract for remediation. For directive's own always-loaded file, the `verify:agents-md-budget` ratchet (#645) and the consumer advisory (`agentsMdAdvisory`, #2155) are the size guards.
4. ⊗ Do NOT convert this into a hard gate or auto-delete any doc -- the value is the nudge at the right moment; thresholds are a judgment call the operator owns.

## Phase 7 -- Summary

! Present a consolidated summary to the user covering:

1. **Upgrade path used**: npm deposit (Phase 0) / legacy submodule (Phases 1-2) / already up to date
2. **DEFT / deposit version change**: old version or commit -> new (or "already up to date")
3. **Structure validation**: lifecycle folders status (all present / missing folders listed)
4. **PROJECT-DEFINITION status**: valid / missing / stale (with freshness details)
5. **xBRIEF validation results**: pass/fail per file, with details on any failures
6. **Lifecycle consistency**: all consistent / N mismatches found (with details)
7. **Origin freshness**: N stale / N externally-closed / N current (with details)
8. **Document Model**: pre-v0.20 (legacy) / v0.20+ (xBRIEF-centric) OK / v0.20+ with warnings (see Pre-Cutover Detection Guard)
9. **AGENTS.md status**: current / has upstream changes / needs review
10. **Codebase MAP status**: current / stale / absent / not configured (advisory)
11. **New skills**: list any newly added skills with descriptions
12. **Pending SCM release**: whether framework-only changes still need commit/push/PR/merge (Phase 8)

! After a successful deposit (or submodule bump), **do not stop at the local change set**. Proceed to Phase 8 SCM release handoff. If there is nothing to release (already on default branch / no local framework delta), record terminal state `released` when the default branch already carries the current framework update; otherwise name the next release step.

? For the legacy submodule path only, if a bump is ready and the operator has not yet approved a commit, ask: "Shall I commit the submodule update and continue SCM release handoff?" -- do NOT auto-commit without approval. Prefer message:

```
chore(deft): update deft submodule to <short-hash>
```

For npm deposit changes, prefer a framework-only commit message such as:

```
chore(deft): refresh framework deposit to <version>
```

## Phase 8 -- SCM release handoff (#1604)

! After a successful framework deposit (Phase 0) or legacy submodule bump (Phases 1-2), carry the update through consumer SCM release. Installer/update success is not upgrade released.

! Record **exactly one** terminal state when this skill exits after a deposit-related run:

| Terminal state | Meaning |
| --- | --- |
| `released` | Default branch carries the framework update (merged or committed directly under policy). |
| `pr-open` | Framework-only branch pushed and PR opened; awaiting review/merge per policy. |
| `blocked:<reason>` | Explicit blocker (examples: `blocked:dirty-worktree`, `blocked:auth`, `blocked:branch-protection`, `blocked:human-merge-gate`, `blocked:ci`, `blocked:operator-declined`). |

### 8a: Policy detection

1. ! Read consumer branch policy: `plan.policy.allowDirectCommitsToMaster`, `plan.policy.requireHumanMerge`, and any known branch-protection / default-branch rules (`deft policy:show` when available).
2. ! Classify the path:
   - **PR path (default when protected or human-merge):** branch protection enabled, or `requireHumanMerge` is true, or direct commits to default branch are disallowed.
   - **Direct-commit path:** `allowDirectCommitsToMaster` (or equivalent) is true **and** human-merge is not required **and** branch protection does not forbid the commit.

### 8b: PR-oriented path (branch-protected / human-merge)

1. ! With operator consent (or autonomous consent when the operator already approved upgrade handoff), create a **framework-only** branch (e.g. `chore/deft-update-<version>`), commit framework deposit files only, push, and open a PR targeting the default branch.
2. ! Stop at terminal state **`pr-open`** once the PR URL is available -- unless policy explicitly allows bot merge **and** the operator directed merge.
3. ⊗ Auto-merge past `requireHumanMerge` / the human merge gate (#1193). Agents may open PRs; they do not merge when policy forbids it.
4. ⊗ Claim `released` when the PR is only open or only locally committed.

### 8c: Direct-commit-enabled path

1. ! Present an **explicit** default-branch path with confirmation (do not silently commit to master/main).
2. ! On confirmation, commit framework-only changes on the default branch (or merge the framework branch) and push so the default branch carries the update.
3. ! Record terminal state **`released`** only after the default branch ref includes the framework update.

### 8d: Stop-after-commit is a failure mode

! A local framework-only commit without push/PR/merge **and** without naming the next release step is a **failure mode** of this skill (#1604 historical class).

- ! If the workflow must pause after a local commit, name the next step explicitly (push + open PR, or confirm direct-commit + push) and record `blocked:<reason>` or continue until `pr-open` / `released`.
- ⊗ End the skill at "local framework-only commit done" without a terminal state line.
- ⊗ Report `released` when only a local commit exists and the default branch does not yet carry the update.

### 8e: Terminal state line

! End the skill output with a single machine-readable line operators and agents can grep:

```text
upgrade-handoff: released
upgrade-handoff: pr-open
upgrade-handoff: blocked:<reason>
```

Include the PR URL when state is `pr-open`, and the merge/default-branch SHA when state is `released`.

### 8f: Framework-only path allowlist

! When committing for handoff, include only framework deposit artifacts (managed `.deft/core/`, AGENTS managed section, hooks, VERSION/marker files, and other paths the deposit itself refreshed). Exclude product source, tests, and unrelated staged files. If isolation was used in 0b, restore the operator's product stash only **after** the framework commit is complete (or on a separate branch).

## Anti-Patterns

- ⊗ Auto-commit submodule changes without user approval
- ⊗ Auto-commit or auto-push framework deposit changes without the Phase 8 policy path and consent rules
- ⊗ Stop after a local framework-only commit without naming the next release step or recording a terminal state (#1604)
- ⊗ Claim terminal state `released` when the update is only local or only on an open PR
- ⊗ Auto-merge when `requireHumanMerge` is true or branch protection forbids bot merge (#1193)
- ⊗ Treat git submodule update as the primary consumer upgrade path when npm + `directive update` / `deft update` is available
- ⊗ Send operators to manual GitHub release-asset archaeology when `npm i -g @deftai/directive@latest` is the correct remediation
- ⊗ Overwrite project-level `./xbrief/*.xbrief.json` files -- those are project data
- ⊗ Skip the pre-flight dirty check -- a dirty submodule can cause merge conflicts or data loss
- ⊗ Include a separate fetch of the xBRIEF schema from upstream deftai/xBRIEF -- that is a CI concern (see #128), not a user sync task
- ⊗ Auto-move xBRIEFs to fix folder/status mismatches -- report only; never auto-fix
- ⊗ Auto-update xBRIEFs based on origin freshness -- report only; user decides during refinement
- ⊗ Mix product feature work into a framework-only upgrade commit or PR
- ⊗ Force `npm i -g @deftai/directive@latest` or deposit mutation on routine session sync without staleness evidence and operator consent
- ⊗ Run Phase 8 framework-only commit from a mixed product+framework worktree/index without isolation
