<!-- Generated by gzkit — do not edit manually -->
# .gzkit/skills Agent Instructions

> Shared subtree rules. See root `AGENTS.md` for project-wide governance.

<!-- Generated by gz agent sync — do not edit -->

# Model Selection (gzkit)

<!-- rule-version: 0.3.0 -->

> **Rule version:** `0.3.0` — renamed prohibited headings; lifted Rationale to expansion doc (OBPI-0.0.54-04 shape conformance pass).

## Operative claims (binding)

1. **Model tier is determined by decision complexity, not task size.** Large read-only tasks use Haiku; small design decisions use Opus.
2. **Default to the lowest tier that closes the decision space.** Each surface must name what "closing the decision" means.
3. **Skill SKILL.md files carry explicit `model:` frontmatter.** No inference; declaration is the contract.
4. **Subagent prompts specify effort level, not model name.** The Agent tool maps effort to model; agents do not hardcode model IDs.

## Routing matrix

| Work Type | Model | Decision space | Example |
|-----------|-------|-----------------|---------|
| **Read-only search/lookup** | Haiku | None—pattern matching only | Explore agent finding files by grep, Bash read operations |
| **Mechanical validation** | Haiku | Binary (pass/fail); no tradeoff analysis | `gz register-adrs` checking ADR frontmatter, `gz validate` gates |
| **Routine code review** | Haiku | Bounded checklist; style/naming only | Linting output, pre-commit hook errors, simple ruff/typecheck fixes |
| **Format normalization** | Haiku | Deterministic transformation | YAML/JSON reformatting, table rendering, template instantiation |
| **Architectural design, planning** | Opus | Unbounded; constraints compete; novel decisions | ADR authoring, `/gz-design`, brief writing, cross-surface coherence checks |
| **Complex refactoring** | Opus | Dataflow reasoning across N≥3 files | Multi-file renames, schema migrations, coupled-surface updates |
| **Gate-5 attestation, narrative synthesis** | Opus | Operator-facing quality; tone and completeness matter | Closing OBPIs with evidence, writing release notes, operator-facing prose |
| **Subagent multi-step work** | Varies (see below) | Depends on subtask type | Agent spawned for parallel exploration, verification, or specialized review |

## Skill frontmatter (`model:` directive)

Every skill SKILL.md MUST declare the model tier used when invoking the skill runtime:

```yaml
---
skill-version: 0.1.0
model: haiku  # or: sonnet, opus
---
# Skill Name

...
```

**Valid values:** `haiku`, `sonnet`, `opus`. No inference; no runtime detection; declared value is the contract.

**Mapping to Claude models:**
- `haiku` → `claude-haiku-4-5-20251001`
- `sonnet` → `claude-sonnet-4-6`
- `opus` → `claude-opus-4-7`

## Subagent effort levels

Subagents use effort directives, not model names. The Agent tool maps effort → model:

- `effort: light` → Haiku (fast, bounded tasks)
- `effort: high` → Sonnet (moderate complexity, structured output)
- `effort: xhigh` → Opus (hard problems, novel design, unbounded reasoning)
- `effort: max` → Opus + extended thinking (genuinely hard; use sparingly)

**Rule:** Prompt the subagent with the effort level needed to close the decision, not the effort level that would be "nice to have."

## Do Not

- Declaring `model: opus` for a read-only task (use Haiku)
- Defaulting subagents to `effort: xhigh` when `effort: light` would suffice
- Hardcoding model IDs in subagent prompts (`claude-opus-4-7`) instead of using effort levels
- Omitting the `model:` directive from a skill and inferring it at runtime
- Using Sonnet as a "middle ground" between Haiku and Opus without naming what decision complexity requires it

See [Model-Selection Rationale](../../docs/governance/model-selection-rationale.md) for context and token economy reasoning.

<!-- Generated by gz agent sync — do not edit -->

<!-- rule-version: 0.9.0 -->

# Skill & Surface Sync (gzkit)

> **Rule version:** `0.9.0` — renamed prohibited headings; lifted Rationale to expansion doc (OBPI-0.0.54-04 shape conformance pass).

## Non-negotiable rules

1. **Edit `.gzkit/` first.** The canonical source for skills is `.gzkit/skills/` and for rules is `.gzkit/rules/`. Always make changes there.
2. **Bump the version on every edit.** Increment the version marker before saving. The marker differs by surface:
   - **Skills** carry `skill-version:` in YAML frontmatter (validated by the skill schema).
   - **Rules** carry a body-level `<!-- rule-version: X.Y.Z -->` HTML comment immediately after the frontmatter, plus a visible `> **Rule version:** \`X.Y.Z\`` block quote with a one-sentence rationale. The rule frontmatter schema (`RuleFrontmatter` in `src/gzkit/rules.py`) is `extra="forbid"` and rejects a `skill-version:` key on rule files — that is intentional. The "skill-version" name on a non-skill artifact was a doctrine smell; the body-level marker resolves it.
3. **Run sync after every edit.** `uv run gz agent sync control-surfaces` propagates canonical state to ALL derived surfaces. One invocation covers BOTH the wheel-shipping byte-parity copy at `src/gzkit/<surface>/` AND vendor mirrors at `.[vendor]/<surface>/`. No separate copy step is needed (OBPI-0.0.32-08).
4. **Never edit vendor mirrors directly.** `.claude/skills/`, `.claude/rules/`, `.github/skills/`, and `.github/instructions/` are generated outputs.
5. **Never edit `src/gzkit/<surface>/` directly.** `src/gzkit/skills/`, `src/gzkit/rules/`, `src/gzkit/personas/`, and `src/gzkit/templates/` are derived outputs regenerated by sync. Only the sync mechanism (`sync_pkg_surfaces`) writes to them.
6. **`skill-version` bumps require `last_reviewed` bumps in the same edit (skills only).** Any commit that increments `skill-version:` in a skill's frontmatter MUST also set `last_reviewed:` to today's date (YYYY-MM-DD). Rationale: if the change is substantive enough to bump the version, the author has just re-examined the surface — the review-date field exists to record exactly that act. The 90-day staleness audit (`DEFAULT_MAX_REVIEW_AGE_DAYS` in `src/gzkit/skills_audit.py`) is the downstream consumer; decoupling the two fields produces fresh code with stale review metadata, which silently disables the staleness signal. Rules do not carry `last_reviewed:` (no schema field) — this clause applies to skills only.

## Surface layout

| Canonical (edit here) | Pkg copy (wheel-shipping) | Vendor mirrors (generated, do not edit) | Version marker |
|-----------------------|---------------------------|-----------------------------------------|----------------|
| `.gzkit/skills/`      | `src/gzkit/skills/`       | `.claude/skills/`, `.github/skills/`    | Frontmatter `skill-version:` |
| `.gzkit/rules/`       | `src/gzkit/rules/`        | `.claude/rules/`, `.github/instructions/` | Body `<!-- rule-version: X.Y.Z -->` + visible block quote |
| `.gzkit/personas/`    | `src/gzkit/personas/`     | `.claude/personas/`, `.agents/personas/`, `.github/personas/` | (no version marker) |
| `.gzkit/templates/`   | `src/gzkit/templates/`    | (none — consumed at scaffold-time only) | (no version marker) |
| `.gzkit/chores/<slug>/` | `src/gzkit/chores/<slug>/` (canonical class only) | (none) | (no version marker) |

`gz agent sync control-surfaces` propagates ALL rows in one invocation: first pkg copies, then vendor mirrors.

## Procedure

1. Edit the canonical file under `.gzkit/skills/` or `.gzkit/rules/`
2. Bump the version marker for the surface:
   - Skill: increment `skill-version:` in frontmatter
   - Rule: increment both the `<!-- rule-version: X.Y.Z -->` HTML comment and the visible `> **Rule version:** \`X.Y.Z\`` block quote
3. **Skills only:** update `last_reviewed:` to today's date (YYYY-MM-DD) in the same edit as the `skill-version` bump (non-negotiable rule #6)
4. Run `uv run gz agent sync control-surfaces`
5. Verify sync output shows no stale or divergent mirrors
6. If sync reports stale mirror-only paths, follow the recovery in `/gz-agent-sync` skill documentation

## Version discipline

| Change type | Bump | Example |
|-------------|------|---------|
| GovZero framework major release | Major | 6.0.0 -> 7.0.0 |
| Governance rule or procedure change | Minor | 6.0.0 -> 6.1.0 |
| Tooling, template, or wording fix | Patch | 6.0.0 -> 6.0.1 |

The same bump table applies to both skills (frontmatter version) and rules (body-level version). The marker location differs; the semver semantics do not.

## Conflict resolution

When sync detects a version mismatch between canonical and a mirror, resolve via:

| Signal | What it tells you | How to check |
|--------|-------------------|--------------|
| Version marker (semver) | Intentional edit sequence | Skill: parse frontmatter `skill-version`. Rule: parse body `<!-- rule-version: ... -->` |
| Git commit hash / timestamp | Physical edit recency | `git log -1 --format=%H -- <path>` |

**Resolution rules:**

- **Mirror version > canonical version:** An agent edited the mirror directly. Promote the mirror content to canonical, then sync. The higher version wins.
- **Mirror version == canonical version, content differs:** An agent edited the mirror without bumping the version. Use git commit timestamp to determine recency; flag for human review.
- **Canonical version > mirror version:** Normal state — sync propagates canonical to mirrors.

Version is the primary signal (intentional semantic ordering). Commit hash is the tiebreaker (physical recency when versions match but content diverges).

## Do Not

- Do not edit a skill without bumping its `skill-version` frontmatter
- Do not edit a rule without bumping its body-level `<!-- rule-version: ... -->` marker (and the visible block quote)
- Do not add `skill-version:` to rule frontmatter — the schema rejects it (GHI #307); use the body-level marker instead
- Do not edit `.claude/rules/` directly — sync overwrites it from `.gzkit/rules/`
- Do not edit `.claude/skills/` directly — edit `.gzkit/skills/` and sync
- Do not manually copy skill files between surfaces — use the sync command
- Do not skip sync because "both files look the same" — sync also updates manifests, registrations, and vendor-specific rendering

## Bootstrap semantics (`gz init`)

On first init in an adopter project, `gz init` populates
`.gzkit/skills/<slug>/SKILL.md` by copying canonical content from the wheel's
package surface (`importlib.resources.files("gzkit.skills")`). The package
surface is the *one-time bootstrap source*: after init, the adopter's
`.gzkit/skills/` is **the project canonical source-of-truth** for that
project, and the "Edit `.gzkit/` first" rule binds from that point forward.

Repair mode (re-running `gz init`) is idempotent: it adds new canonical
slugs delivered by the installed gzkit version without overwriting
operator-edited files (`skip_existing=True`). Use `--force` to wipe and
re-copy every canonical SKILL.md from the wheel.

`scaffold_core_skills` filters skills whose canonical SKILL.md declares
`lifecycle_state: retired` — retired slugs are not re-introduced on `gz init`
(hard cutover invariant, enforced by `tests/commands/test_skills.py::TestSkillCommands::test_init_scaffolds_adr_create_and_removes_adr_manager`).

As of OBPI-0.0.32-04, `gz init` also populates `.gzkit/rules/<slug>.md` by
copying canonical rule content from the wheel's package surface
(`importlib.resources.files("gzkit.rules")`). The same editing invariant
applies: after init, `.gzkit/rules/` is **the project canonical
source-of-truth** for rules in that adopter project. Rules scaffolding runs
after `sync_all` in the fresh init path so the initial control-surface sync
uses the instruction-sync path; subsequent `gz agent sync control-surfaces`
invocations render canonical rules to `.github/instructions/`. The
`AGENTS.md` file in the rules package is excluded from scaffolding (it is a
package-internal agent contract, not an operator-facing rule).

`gz init --update` (OBPI-0.0.32-05, not yet landed) will provide
version-aware refresh semantics for the adopter's `.gzkit/<surface>/` from
the wheel, with three-state IDENTICAL/STALE/EDITED detection so operator
edits are preserved across gzkit upgrades.

## Retirement policy (delete-on-retire, binding)

When a skill is superseded or consolidated, its directory is **deleted**
from every surface root — `.gzkit/skills/`, `src/gzkit/skills/`,
`.claude/skills/`, `.github/skills/`, and `.agents/skills/`. Do not
preserve a `lifecycle_state: retired` SKILL.md stub.

**Why delete:** the `package_only` classifier carve-out exists for
*non-md package-machinery* (e.g. `__init__.py`, `__pycache__/**`).
Routing retired SKILL.md tombstones through `package_only` is classifier
scope creep that inflates the shipped wheel with `archived_into`
frontmatter stubs and muddies the canonical/package_only distinction.
Operator judgment (recorded under GHI #464 / ADR-0.0.32 closeout):
*"tombstones not worth keeping if they are stubs."*

**Redirect UX is acceptably "skill not found."** A retired-name
invocation returns the missing-skill response rather than a tombstone
pointer to the successor. The discoverability cost is small enough that
indefinite wheel inflation is not justified.

**Defensive backstop preserved.** The retired-frontmatter branch in
`_classify_skill_file` (lines ~45–52 of `src/gzkit/skills/__init__.py`)
remains as a defensive net: if a tombstone leaks back in violation of
this doctrine, it is still classified `package_only` so
`gz validate --distribution` does not flag it. The doctrine is the
authored rule; the classifier branch is the runtime fallback.

**Promotion of new-skill scaffolding inherits this rule.** Any future
skill consolidation lands as a delete-on-retire pass in the same
commit/OBPI that introduces the successor, not as a follow-up cleanup
chore.

## Canonical surface class-classifier

This section codifies the three-class content model for every canonical surface (OBPI-0.0.32-13 for chores; extended to all surfaces under OBPI-0.0.32-15). Every file under any canonical surface (`src/gzkit/{chores,rules,skills,personas,templates}/`) falls into exactly one class:

| Class | Definition | Byte-parity | Sync | Validator |
|---|---|---|---|---|
| **canonical** | Operator-authored content present at `.gzkit/<surface>/` — the source of truth | Required | `.gzkit/ → src/gzkit/` | Included in wheel; present in baseline manifest |
| **package_only** | Python modules and supporting files with no `.gzkit/` counterpart (e.g. `__init__.py`, `_scaffolder.py`) | Exempt | NEVER propagate to `.gzkit/` side | Exempt from `ON_DISK_NOT_INCLUDED` |
| **runtime_state** | Per-run logs, receipts, proofs — written by agents at runtime | Exempt | NEVER sync in either direction | Exempt from both error classes |

### Default rules by surface

| Surface | canonical | package_only | runtime_state |
|---------|-----------|--------------|---------------|
| **chores** | `CHORE.md`, `AGENTS.md`, `*.md` outside `proofs/`, `acceptance.json`, `registry.json`, `.py` with `.gzkit/` counterpart | `__init__.py`, `__pycache__/**`, `.py` with no `.gzkit/` counterpart | `CHORE-LOG.md`, `proofs/**`, `.gitkeep` |
| **rules** | `*.md`, `*.json` with `.gzkit/rules/` counterpart, `.py` with `.gzkit/rules/` counterpart | `__init__.py`, `__pycache__/**`, `_scaffolder.py`, `.py` or `.json` with no `.gzkit/rules/` counterpart | (none) |
| **skills** | `SKILL.md` (all) | `__init__.py`, `__pycache__/**` | (none) |
| **personas** | `*.md` (all) | `__init__.py`, `__pycache__/**` | (none) |
| **templates** | `*.md` (all) | `__init__.py`, `__pycache__/**` | (none) |

**Python helpers (all signature-compatible with `_classify_chore_file`):**

| Surface | Helper | Module | Tests |
|---------|--------|--------|-------|
| chores | `_classify_chore_file(path, *, project_root=None)` | `gzkit.chores` | `tests/test_chores.py::TestChoresLayoutDualSurface` |
| rules | `_classify_rule_file(path, *, project_root=None)` | `gzkit.rules` | `tests/test_rules.py::TestClassifyRuleFile` |
| skills | `_classify_skill_file(path, *, project_root=None)` | `gzkit.skills` | `tests/test_skills.py::TestClassifySkillFile` |
| personas | `_classify_persona_file(path, *, project_root=None)` | `gzkit.personas` | `tests/test_personas.py::TestClassifyPersonaFile` |
| templates | `_classify_template_file(path, *, project_root=None)` | `gzkit.templates` | `tests/test_templates.py::TestClassifyTemplateFile` |

The `gz validate --distribution` validator consults these classifiers to exempt `package_only` files from `ON_DISK_NOT_INCLUDED` errors. The `sync_pkg_surfaces` function consults them to skip non-canonical files when propagating `.gzkit/ → src/gzkit/`.

**Long-term note (chores):** The chores classifier is a **temporary accommodation** for runtime-state co-location with canonical instructions. When `ADR-pool.canonical-vs-runtime-separation` promotes, runtime-state moves to a separate location and the chores classifier shrinks to a single class (canonical only).

See [Skill-Surface-Sync Rationale](../../docs/governance/skill-surface-sync-rationale.md) for the version-marker split rationale and conflict-resolution design.

<!-- Generated by gz agent sync — do not edit -->

<!-- rule-version: 0.2.0 -->

# Tool / Skill / Runbook Alignment

> **Rule version:** `0.2.0` — lifted pedagogy, canonical violations, and enforcement details to rationale doc under GHI #327.

gzkit's operator surface is a three-layer hierarchy: **tools** (CLI verbs), **skills** (operator-facing value chains composing tools toward an intent), and **runbooks** (documentation preserving operator intent across iteration). These three invariants are the mechanical test for layer alignment; apply them whenever you author or modify any of the three surfaces.

## Invariants

### Invariant 1 — Every CLI tool has at least one skill that wields it

Every CLI verb registered in `src/gzkit/cli/` must be invoked by at least one skill under `.gzkit/skills/` — either via the skill's frontmatter `gz_command:` field or in the skill's body instructions. An orphaned tool (a live CLI verb with no skill pointing at it) is either dead code or hidden drift — either way, it is a defect signal.

### Invariant 2 — Every skill's `gz_command` matches a runbook-prescribed tool for the same operator moment

Every skill's declared `gz_command:` (frontmatter or body) must resolve to a CLI verb that the runbook prescribes for the same operator moment. If the skill name and its actual CLI target describe different operator moments — or if the skill invokes a verb the runbook does not prescribe for that moment — the skill is drifted from the runbook's preserved intent.

### Invariant 3 — Destination verb's default output form must honor the routing skill's Output Contract

When a skill's Output Contract declares a required rendering form ("table", "JSON", "tree", "plain text", etc.), the CLI verb in the skill's `gz_command` must produce that form as its default human-readable output. If the skill promises a table and the verb emits prose, the skill is drifted from its destination regardless of whether the verb name matches (Invariant 2). Invariant 3 closes the gap between "right verb routed" and "right rendering produced."

## When to apply

- **Authoring a new CLI verb** — confirm at least one skill will wield it before merging; author the skill in the same patch or file a follow-up GHI
- **Renaming a CLI verb** — audit every referencing skill (frontmatter `gz_command:` + body invocations); update in same patch
- **Authoring a new skill** — confirm `gz_command:` target matches the runbook-prescribed verb for that operator moment, and run the target once to verify output form matches Output Contract
- **Renaming a skill** — confirm `gz_command:` still aligns with the new name's implied operator moment
- **Re-routing a skill's `gz_command`** — run the new target and observe default output before committing; fix destination verb or skill contract if form disagrees
- **Editing the runbook** — confirm prescribed verbs still match skill-layer routing; add missing skills or update prescriptions
- **Editing a CLI verb's rendering** — check every skill whose `gz_command` points at that verb; preserve any Output Contract-declared form

> See [`docs/governance/tool-skill-runbook-rationale.md`](../../docs/governance/tool-skill-runbook-rationale.md) for canonical violation examples, enforcement details, commit-message discipline, and rationale.
