Spec 007 created this skill from scratch. The deterministic readiness checks + report generation live in
land.py; this SKILL.md drives the judgment layer (when to invoke, how to interpret blockers, what mode to pick).
What this skill does
Closes the worktree-drift gap: every slice in jig today commits to a worktree branch and stays there until a human remembers to merge. The skill provides a deterministic landing path:
- Verify the slice is actually done — STATUS=DONE in spec.md, full test suite green, deviation log section present, DoD checkboxes all ticked.
- Emit a structured markdown report with five readiness checks
(STATUS=DONE, tests green, deviation log present, DoD ticked, and the
Class-A cross-ref backstop — spec 112: refuse when the slice/ADR is already
integrated on
origin/main,JIG_CROSSREF_GATE=0to bypass) and (in--mode director--mode pr) a Next-steps section of suggested git commands.
prepare is non-destructive on git state: it may read branch state and
fetch origin/main to surface a branch-freshness warning, but it never
switches branches, merges, pushes, or removes worktrees. execute --mode direct runs the destructive merge sequence by pushing the current branch to
origin/main, then fast-forwards the canonical local main worktree to
origin/main or reports why local sync was skipped (slice 007-02 + 081-01);
execute --mode pr runs the destructive push + PR-open sequence (slice
007-03). git worktree remove and gh pr merge are never executed — both
stay user-driven post-landing suggestions.
How to use
Run the readiness check
python3 "${CLAUDE_PLUGIN_ROOT}/skills/slice-land/land.py" prepare \
<path-to-spec.md> <slice-fragment> [--mode {direct,pr}]
spec.md— path to the spec file (e.g.docs/specs/007-slice-land/spec.md).<slice-fragment>— case-insensitive substring against## Slice X — Yheadings. Same lenient match asworkflow.py transition.--mode— optional. Without it, only the readiness check runs.direct— for solo / merge-to-main projects. Suggested commands: push the branch toorigin/main, then fast-forward localmaintoorigin/mainwhen a clean canonicalmainworktree exists.pr— for team / PR-shaped flows. Suggested commands:git push -u origin <branch> && gh pr create --body-file <path>. A PR body is written to/tmp/jig-slice-<NNN-NN>-pr-body.mdcontaining the slice's ACs and a deviation-log excerpt.
Run the merge sequence (execute --mode direct)
After prepare confirms the slice is ready, execute --mode direct
runs the direct landing sequence:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/slice-land/land.py" execute \
--mode direct <path-to-spec.md> <slice-fragment> [--dry-run]
--dry-run— print the git commands that would run without executing them. Use this for a final sanity check before committing.- Without
--dry-run— runsgit push origin <branch>:mainfrom the current worktree after the fast-forward guard passes, then fetches and fast-forwards the canonical local worktree checked out atrefs/heads/maintoorigin/mainwhen that worktree exists and is clean. The caller's feature/detached worktree is not switched tomain. Stops and reports if the authoritative push fails. If the post-push local sync cannot run becausemainis missing, dirty, locked, diverged, or unavailable, the report sayslocal main sync skipped: <reason>while preserving the successful authoritative push. Never runsgit worktree remove(printed as a post-landing suggestion only).
Safety guards (checked before any git mutation):
- Refuses if current branch is
mainormaster. - Refuses if
mainhas diverged (fast-forward not possible).
Open the PR (execute --mode pr)
For PR-shaped flows, execute --mode pr pushes the branch and opens
the PR via the GitHub CLI:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/slice-land/land.py" execute \
--mode pr <path-to-spec.md> <slice-fragment> [--dry-run]
--dry-run— print the commands that would run, write the PR body file for inspection, but do NOT push or open the PR.- Without
--dry-run— runsgit push -u origin <branch>followed bygh pr create --title "<title>" --body-file <body-path>. Stops if push fails (gh is NOT called after push failure). Title shape isfeat(<scope>): <subject>: scope comes from a singleskill:frontmatter value or falls back to the spec directory slug, and subject drops the numeric slice prefix so the PR-title workflow accepts it. - Push runs from the current worktree (not the main worktree root, since the feature branch is checked out here).
- Never runs
gh pr merge(printed as a post-landing suggestion only). - Local
mainsync is pending until the PR actually merges. PR mode never claims localmainwas updated beforeorigin/mainmoves.
Safety guards (checked before any subprocess mutation):
- Refuses if current branch is
mainormaster. - Refuses if
ghCLI is not on PATH (install instructions printed). - Refuses if
origindoes not point at github.com (HTTPS or SSH form).
Exit codes
0— all five readiness checks pass; the slice is ready to land (forprepare) or was merged successfully (forexecute).1— at least one check failed, a safety guard fired, or a git command failed (the report still emits; the user sees what's wrong).2— user error (missing spec, ambiguous fragment, invalid--mode).
Test-check warnings
If tdd.py run returns exit 2 (no test runner detected at the target),
the readiness report marks the Tests row as a [?] warning rather
than a [ ] blocker. Rationale: some slices are doc-only and have no
executable tests — those slices should still be landable. Exit 1 from
tdd.py run (red tests) IS a blocker.
Close-out (post-DONE) subsection
Slices often include items that can only be completed AFTER the
RECONCILED → DONE transition — e.g. workflow.py status-board regen
(which reads the updated STATUS marker) or CLAUDE.md skills-table
promotion (which advertises the slice as done). These items create a
chicken-and-egg if they live in the DoD: slice-land requires DoD =
N/N before blessing landing, but the user uses slice-land's blessing as
the cue to commit + flip to DONE.
Convention (spec 009 / slice 009-01): put post-DONE items in a
subsection headed ### Close-out (post-DONE) inside the slice, after
the DoD checklist and before the slice's --- separator. slice-land's
check_dod recognizes the heading and excludes its checkboxes from the
count. Anything between **DoD:** and ### Close-out is DoD-counted;
anything after isn't.
Example:
## Slice 009-01 — close-out-section-recognition
**STATUS: DONE**
...
**DoD:**
- [x] All ACs pass; full suite green.
- [x] Reviewed by `reviewer` subagent.
- [x] Deviation log produced.
- [x] Reconciliation review pass.
### Close-out (post-DONE)
- [ ] `docs/specs/README.md` regenerated by `workflow.py status-board`
AFTER the DONE transition.
- [ ] `CLAUDE.md` skills-table update.
The heading is case-insensitive (matches Close-out, Closeout, close out, etc.) and requires H3 (###) — H2/H4 don't delimit, to avoid
accidentally matching unrelated content.
If no ### Close-out heading exists in the slice, check_dod
behavior is unchanged (counts all boxes in the slice section).
When to invoke
Auto-trigger phrases: "land this slice", "merge back to main", "ready to ship", "create a PR for this slice", "close out the slice", "slice is done — what now".
Typical session flow:
- Slice transitions to
DONEviaworkflow.py transition. - Deviation log is written under the slice heading.
- DoD checkboxes get ticked.
- Run
land.py prepare ... --mode direct(or--mode pr). - Copy-paste the suggested commands.
End-to-end example
# 1. Verify readiness — no merge command yet.
python3 .../land.py prepare docs/specs/007-slice-land/spec.md "007-01"
# 2. Get the direct-merge recipe.
python3 .../land.py prepare docs/specs/007-slice-land/spec.md "007-01" --mode direct
# Expected output (when all five checks pass):
#
# # Landing readiness — slice 007-01 — land-prepare
#
# ## Readiness checks
#
# - [x] Status: DONE
# - [x] Tests: green (`tdd.py run` exit 0)
# - [x] Deviation log: present
# - [x] DoD: 9/9 boxes ticked
# - [x] Cross-ref state: not already integrated on origin/main
#
# ## Next steps (mode: direct)
#
# Run via `execute --mode direct` so `origin/main` is updated from
# this branch, then the canonical local `main` worktree is
# fast-forwarded as housekeeping when it is available and clean:
#
# git push origin claude/eager-zhukovsky-34ebb0:main
# git fetch origin main
# git merge --ff-only origin/main
# git worktree remove .claude/worktrees/eager-zhukovsky-34ebb0
Gotchas
prepareis non-destructive;executeIS destructive.prepareonly writes the PR body file (mode=pr); it never runsgit checkout,git merge,git push,git worktree remove, orgh pr create.execute --mode directruns the push/sync sequence as an authoritative push toorigin/mainand reports the post-push local sync result (007-02 / 081-01);execute --mode prruns push + gh pr create (007-03) and reports local sync as pending on merge. Use--dry-runfor a non-destructive preview of either execute mode.- Test-check target =
land.py's cwd. The helper invokestdd.py runagainst the current working directory by default. If the slice changes a deep subdir (e.g.skills/foo/), runland.pyfromskills/foo/(or pass it as cwd) to keep the test run focused. Running from the project root re-tests the whole suite — slower but also more honest. - Substring fragment matching is identical to
workflow.py. A fragment like007-01matches## Slice 007-01 — land-prepare. Ambiguous fragments (multiple matches) refuse with exit 2. - PR body file path is predictable (
/tmp/jig-slice-NNN-NN-pr-body.md) so callers can read / edit the body beforegh pr create. Re-running--mode proverwrites the same file — idempotent. - The Test plan section in the PR body uses generic
[x]lines. The helper does not detect per-AC test counts — that would require the AC-coverage mapping deferred to slice 006-02. The generated checkboxes are placeholders; tighten the PR body manually beforegh pr createif you want specific counts. - Branch detection requires being inside a git repo. Outside one
(e.g. running against a synthetic spec in
/tmp), the helper degrades to the literal placeholder<BRANCH>in the suggested commands. Edit by hand before running. - Deviation log detection is heading-based. The helper looks for
### Deviation log(or a variant like### Deviation log (after reconciliation)) within the slice section. Missing heading → blocker, even if the slice has reconciliation content under another heading. Convention: every Done slice gets the explicit subsection.
Relationship to other skills
spec-workflowowns the lifecycle state transitions (workflow.py transition).slice-landruns AFTER the final DONE transition; the two skills cleanly compose.independent-reviewruns BEFOREslice-land— review verdicts must bepassand reconciliation review must be done before the slice goes DONE. Once the slice is DONE,slice-landchecks readiness and emits the landing recipe.tdd-loopis the helperslice-landshells out to for the test check. The test-check normalization (green / red / warn) maps directly totdd.py run's exit code 0 / 1 / 2.adr-workflowis orthogonal — ADRs may or may not be written during reconciliation.slice-landdoesn't gate on ADR presence.
Out of scope for slice 007-03
scaffold.jsonintegration: "direct" | "pr"field (slice 007-04 remains deferred — the--modeflag is sufficient for manual invocations).- Multi-slice batch landing (single slice at a time is the right audit-trail granularity).
- Interactive confirm-before-push prompt —
--dry-runis the preview mechanism instead. Use--dry-run, inspect the PR body, then re-run without--dry-run. - JIRA / Linear ticketing integration, Slack notifications, auto-drafting ADRs from the deviation log.
- GitHub Enterprise (self-hosted) — the
_check_github_remotesubstring match requiresgithub.comliterally. Self-hosted GHE users will need a future extension or local override.