# Close

> Use to formally close a slice once its phases are complete, audited, and reconciled — confirm the rollup, verify spec-coherence, harvest durable findings, reconcile lifecycle status, and land a clean final commit. Routed to from /reconcile.

- Skill: `davidlee/close` (Agent Skill)
- Install (CLI): `npx skillmds@latest add davidlee/close`
- Raw SKILL.md: https://api.skillmd.com/api/skills/davidlee/close/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: davidlee (https://skillmd.com/u/davidlee)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/davidlee/close

---


# Close

You are executing formal closure, not just marking work done.

Inputs:

- completed, audited, reconciled implementation phases
- the reconciliation review (RV) with every finding terminal and the
  `## Reconciliation Outcome` recorded (see `/reconcile`)
- the governing slice id

## Process

1. **Pre-check:**
   - Phase exit criteria (`EX-`) and verification (`VT-`) are met. Confirm the
     rollup: `doctrine slice list` should show the slice as `X/X complete` with
     no `!N` blocked, no `?N` anomalous, no `—` untracked.
   - `/reconcile` is done (via `/audit → /reconcile`): the RV ledger is resolved,
     every governance/spec finding is dispositioned, and the RV carries a
     `## Reconciliation Outcome` section. If the reconciliation brief was empty
     (no-op), the outcome confirms that explicitly.
   - Durable findings from the slice are harvested per `harvest.md` before
     closure — or **consciously rejected**. A clean slice may harvest nothing;
     the conscious-rejection gate is what closure checks.
   - `doctrine check gate` is green.
2. **Spec-coherence gate — confirm reconciliation is complete before `done`:**
   Before the terminal transition, verify every item from the audit's
   reconciliation brief is resolved through one of four paths:
   * **REV done** — governance/spec items covered by a `done` REV
     (`revision status REV-N done`). The REV rationale (`revision-NNN.md`)
     carries the reconcile narrative.
   * **Withdrawn** — finding withdrawn in the RV with rationale.
   * **Tolerated** — finding tolerated in the RV with rationale.
   * **Escalated to design** — slice transitioned back to `design` via the
     ADR-009 §1 back-edge (`reconcile → design`).
   Additionally:
   * Every per-slice direct-edit item is applied to `design.md` /
     `slice-NNN.md` and recorded in the `## Reconciliation Outcome`.
   * The RV ledger is resolved (`done · await=none`). **A zero-finding review
    is terminal for close purposes whatever its derived status string says —
    the absence of unresolved blockers is what gates the transition (ADR-007
    D-C9b), never the status string (D7: a display summary, never a gate).
    The string itself is unsettled for the empty ledger — D-C8 fixes it at
    `active`, the engine currently answers `done`, and `ISS-314` tracks the
    gap. Do not raise a token finding to force it terminal.**
   * The reconcile outcome is recorded (REV rationale and/or RV
     `## Reconciliation Outcome`).
   No free-floating "rejected" disposition is permitted — every finding
   must land in one of the terminal states above.
   If any item is unresolved, **refuse close** and return to `/reconcile`.
   (The structural closure seam is the mechanical backstop; this gate is the
   substantive check — the verb enforces; the skill verifies.)
3. **Commit cleanly:** land `.doctrine/**` workflow artefacts in small, clean
   conventional commits scoped with the slice id, rather than letting them
   accumulate. Code and workflow edits go together or separately, whichever
   commits cleanly first.
   > **Stage the slug symlink.** `*/new` verbs lay out `<kind>/NNN/` **and** a
   > sibling slug symlink `<kind>/NNN-slug -> NNN` (slice, review, revision, rec, …).
   > A path-scoped `git add <kind>/NNN/` stages the dir but misses the symlink,
   > stranding it uncommitted. Stage both — `git add <kind>/NNN*` — or add the
   > symlink explicitly. Check `git status` for a dangling `NNN-slug` before the
   > final commit.
3a. **Dispatched slice — integrate the admitted OID (post-audit only).** If the
   slice was driven by `/dispatch`, project the audited units now:

   ```bash
   # 0. Resolve the trunk delivery ref once (config-driven; default refs/heads/main):
   trunk=$(doctrine dispatch deliver-to)

   # 1. Admit a close_target candidate (if not already done during /reconcile):
   doctrine dispatch candidate create --slice <N> --label close-001 \
     --role close_target --payload code --base "$trunk" \
     --source refs/heads/review/<N>
   doctrine dispatch candidate admit --slice <N> --role close_target \
     --candidate refs/heads/candidate/<N>/close-001 --review RV-NNN

   # 2. Project onto trunk (--trunk is REQUIRED; omitting it is a dry run):
   doctrine dispatch sync --slice <N> --integrate --trunk "$trunk"
   ```

   When a candidate workflow is active this targets the immutable **admitted
   `close_target` OID** (and `--edge` the admitted `review_surface` OID) under a
   fast-forward-only CAS row — **never a raw `phase/*`/`review/*` tip or the
   mutable candidate ref**, and never a close-time merge. A moved trunk refuses
   (admit a superseding close-target candidate on the new base). This is the
   **only** place `--integrate` runs — never at `/dispatch` conclude, only here,
   post-audit.

   **Verify (tree-true, ISS-030).** After `--integrate --trunk`, both checks
   below must pass — do **not** proceed to step 4 unless they do:
   ```bash
   # (a) No phantom reverse-diff: the tracked working tree matches HEAD. A nonzero
   #     exit means integrate advanced the ref but desynced the live checkout — STOP.
   git diff --quiet HEAD

   # (b) The projected delta genuinely landed (not a silent dry-run): the committed
   #     journal's trunk row holds the planned tip; it must equal the trunk ref.
   trunk=$(doctrine dispatch deliver-to)
   planned=$(doctrine dispatch sync --slice <N> --show-journal-trunk-oid \
     --trunk "$trunk")
   git diff --quiet "$planned" "$trunk"
   ```
   (a) is the ISS-030 detector — the **whole tracked tree**, not path-limited (a
   phantom reverse-diff can span any file the slice projected, not just `src/`).
   (b) reads the trunk row's `planned_new_oid` from the committed `dispatch/<N>`
   journal — a tree-read, stable from this checkout (SL-121) — and diffs it against
   the resolved trunk delivery ref (`doctrine dispatch deliver-to`, default
   `refs/heads/main`); a difference means trunk does not hold the projected tip, so
   integration did not land.

3b. **Split lineage — the payload reached trunk out-of-band
   (`--record-integration`).** If the reviewed code already sits on trunk by a
   sanctioned route the dispatch journal never recorded — an operator direct-land,
   a manual `--no-ff` merge of the payload, an external integration — then
   `--integrate` (step 3a) **cannot** run: it advances trunk fast-forward-only, but
   a landed payload is an *ancestor*, not a descendant, of the trunk merge tip
   (non-ff → refused). The close gate still refuses `done` ("dispatched but no
   trunk row"). Record the earned row instead of hand-editing `journal.toml`:

   ```bash
   trunk=$(doctrine dispatch deliver-to)
   doctrine dispatch sync --slice <N> --record-integration --trunk "$trunk"
   ```

   It resolves the earned trunk payload (phase-chain tip, or the admitted
   `close_target` when a candidate workflow is active — **never** `review/<N>`, a
   review surface SPEC-022 forbids as a trunk payload), asserts it is **already an
   ancestor** of trunk, and commits a single **Verified** trunk row — mutating no
   ref. Then `slice status <N> done` reads that row and passes. This is the
   sanctioned replacement for the SL-190 hand-written journal row (SL-211 /
   IMP-236); land the payload out-of-band as `git merge --no-ff phase/<N>-NN` (or
   the admitted candidate), not `review/<N>`. Deep recovery detail:
   `doctrine memory show mem.pattern.dispatch.split-lineage-close-conflict-direct-land`.

4. **Transition lifecycle:** confirm the slice is in `reconcile` (flip it with
   `doctrine slice status <id> reconcile` if `/audit` didn't), then
   `doctrine slice status <id> done` (`<id>` is the bare number, e.g. `40`).
   The closure seam enforces the order and refuses while an RV targeting the
   slice carries an unresolved blocker. Re-run `doctrine slice list` and confirm
   the `⚠` divergence marker is gone — authored status now agrees with the
   rollup. (The terminal set is `{done, abandoned}`; closure here is the `done`
   path — abandoning a slice is a separate decision, not this skill.)
5. **Close the originating backlog item:** if a backlog item (ISS/IMP/CHR/RSK)
   spawned this slice, transition it too — `doctrine backlog edit <ID>
   --status resolved --resolution fixed` (or the resolution that fits). A closed
   slice with its origin still open is hygiene debt.
6. If a blocking finding remains unresolved, do **not** close — return to
   `/reconcile`. If closure depends on tolerated drift with material
   tradeoffs, `/consult` before normalising around it.

### Drift discharge recipe

When `slice status <id> done` refuses with *undischarged residual drift*, each
flagged requirement needs an `accept` REC **owned by the closing slice** that
satisfies all three clauses:

- **(a)** `move = accept` — an affirm, not `revise`/`redesign`.
- **(b)** a `[[status_delta]]` naming the requirement whose `to` equals its
  current authored status (the status the refusal prints).
- **(c)** the REC's `[[evidence_ref]]` set ⊇ every distinct coverage key feeding
  that requirement's composite, so fresh contradictory evidence cannot be excused
  by a stale REC.

Author it, then hand-edit the two tables:

```bash
doctrine rec new --move accept --owning-slice SL-NNN --title "accept REQ-NNN"
# then hand-author the [[status_delta]] and [[evidence_ref]] tables
```

Full predicate, rationale, and a worked example:
`doctrine memory show mem.pattern.doctrine.close-drift-discharge-rec`.

## Outcomes

- The slice is `done`, with the rollup in agreement.
- The originating backlog item (if any) is resolved.
- Durable guidance is captured in memory or consciously rejected.
- The tree is clean and the workflow artefacts are committed.

