diff --git a/docs/memory/_shared/context-loading.md b/docs/memory/_shared/context-loading.md index c8e34e02..81389ca2 100644 --- a/docs/memory/_shared/context-loading.md +++ b/docs/memory/_shared/context-loading.md @@ -1,5 +1,5 @@ --- -description: "Smart context loading convention — descriptive 7-file always-load layer (skill file wins; exception set rule-derived from skill files, never enumerated in the preamble — d9rs), opt-in skill helpers (6-value allowlist incl. `_srad`/`_pipeline`) + stage-conditional loading, standard subagent context (orchestrators incl. `/fab-proceed`), per-stage model resolution at the dispatch seam (`fab resolve-agent `, provider-neutral, Claude-Code adapter named, review-resolves-once — l3ja; applies on every post-intake stage since the single-dispatch collapse, advisory only for a genuinely no-dispatch run — fgxx; the two halves dispatch through two seams — model via the Agent `model` param (short alias opus/sonnet/haiku/fable, id→alias mapped at the seam), effort via an imperative subagent-prompt instruction (no Agent effort param; omit when empty), plus a compliance-visibility expectation that each site surface the resolved `model=/effort=` — m3d4; residual = a per-sub-agent effort param on the Agent tool, a harness ask), selective domain loading, SRAD protocol pointer, scoped Next Steps Convention, generic fab-command failure rule (unconditional non-zero exit → STOP; `fab log command` exits 0 by contract)" +description: "Smart context loading convention — descriptive 7-file always-load layer (skill file wins; exception set rule-derived from skill files, never enumerated in the preamble — d9rs), opt-in skill helpers (7-value allowlist incl. `_srad`/`_pipeline`/`_intake`) + stage-conditional loading, standard subagent context (orchestrators incl. `/fab-proceed`), per-stage model resolution at the dispatch seam (`fab resolve-agent `, provider-neutral, Claude-Code adapter named, review-resolves-once — l3ja; applies on every post-intake stage since the single-dispatch collapse, advisory only for a genuinely no-dispatch run — fgxx; the two halves dispatch through two seams — model via the Agent `model` param (short alias opus/sonnet/haiku/fable, id→alias mapped at the seam), effort via an imperative subagent-prompt instruction (no Agent effort param; omit when empty), plus a compliance-visibility expectation that each site surface the resolved `model=/effort=` — m3d4; residual = a per-sub-agent effort param on the Agent tool, a harness ask), selective domain loading, SRAD protocol pointer, scoped Next Steps Convention, generic fab-command failure rule (unconditional non-zero exit → STOP; `fab log command` exits 0 by contract)" --- # Context Loading @@ -33,22 +33,33 @@ The only universal helper beyond the 7 project files is `_preamble.md`. Addition ### Skill Helper Declaration (Opt-In) -Skills declare additional helper files via the `helpers:` frontmatter list. Allowed values (six since 260611-szxd, which added `_pipeline`): `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`. The agent MUST read `.claude/skills/{helper}/SKILL.md` for each declared helper after reading `_preamble` and before executing the skill body. +Skills declare additional helper files via the `helpers:` frontmatter list. Allowed values (seven since 260613-3xaj, which added `_intake`; six since 260611-szxd, which added `_pipeline`): `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake`. The agent MUST read `.claude/skills/{helper}/SKILL.md` for each declared helper after reading `_preamble` and before executing the skill body. **Stage-conditional loading** (260611-zc9m): a skill MAY instead load a helper at its point of use via an explicit in-body read instruction (e.g., "read `.claude/skills/_review/SKILL.md` before entering Review Behavior"). Frontmatter `helpers:` declares unconditional pre-body loads; in-body read instructions declare conditional ones — a helper loaded this way is intentionally absent from the frontmatter list, so the frontmatter contract stays honest. `/fab-continue` is the sole current user: `_generation` at apply entry / intake-`active` regeneration, `_review` at Review Behavior entry (see [pipeline/execution-skills.md](../pipeline/execution-skills.md)). -Current mapping (post-260611-szxd): +Current mapping (post-260613-3xaj): | Skill(s) | `helpers:` | |----------|------------| -| `fab-new`, `fab-draft` | `[_generation, _srad]` | +| `fab-new`, `fab-draft` | `[_generation, _srad, _intake]` (3xaj added `_intake`; both keep `_generation`/`_srad` declared directly — the `_pipeline` precedent, consumers declare underlying helpers rather than inheriting transitively) | | `fab-continue` | `[_srad]` (+ point-of-use in-body reads of `_generation`/`_review`) | | `fab-ff`, `fab-fff` | `[_generation, _review, _srad, _pipeline]` (orchestrator-level rework edits `plan.md` sections directly, so `_generation` stays unconditional — finding f074 refuted; `_pipeline` is the shared ff/fff pipeline bracket and constitutes the wrappers' entire body, so its load is unconditional by construction — szxd) | | `fab-clarify` | `[_srad]` | | `fab-operator` | `[_cli-fab, _cli-external]` | -| All others (17 skills) | omitted / `[]` (load only `_preamble`) | +| All others (16 skills) | omitted / `[]` (load only `_preamble`) | -`_naming` and `_cli-rk` are NOT allowed values — their content is inlined into `_preamble`. `_preamble` itself is implicit and never listed. +`_naming` and `_cli-rk` are NOT allowed values — their content is inlined into `_preamble`. `_preamble` itself is implicit and never listed. `/fab-proceed` declares **no** `helpers:` (it dispatches `_intake` as a subagent prompt — the subagent reads the helper — exactly as it dispatched `/fab-new` before 3xaj). The internal helpers `_generation`, `_review`, `_pipeline`, and `_intake` themselves carry no `helpers:` frontmatter — they reference what they need in-body and rely on the consumer (or dispatched subagent) having loaded it. + +**One shared helper per pipeline phase (completed by 3xaj).** The four internal orchestration/mechanics helpers decompose the workflow symmetrically — each is a shared body parameterized by call-site-specific knobs, with call-site tails staying in the consumer files: + +| Phase | Helper | Knob(s) | Consumers | +|-------|--------|---------|-----------| +| artifact mechanics | `_generation` | — | `fab-new`, `fab-draft`, `fab-continue`, `fab-ff`, `fab-fff` | +| review mechanics | `_review` | — | `fab-continue`, `fab-ff`, `fab-fff` | +| **pre-intake orchestration** | **`_intake`** | `{questioning-mode}` | `fab-new`, `fab-draft`, `fab-proceed` | +| post-intake orchestration | `_pipeline` | `{driver}`, `{terminal}` | `fab-ff`, `fab-fff` | + +`_intake` (3xaj) is the **pre-boundary** counterpart to the **post-boundary** `_pipeline` (szxd): intake is the single context-bearing boundary in the pipeline; everything up to and including intake creation runs in the main session context (pre-boundary: `_intake`), everything after runs as dispatched subagents over the intake artifact (post-boundary: `_pipeline`). Both extractions mirror the same shape (shared body + one-or-two knobs + call-site tails). See [pipeline/planning-skills.md](../pipeline/planning-skills.md) § The `_intake` Shared Create-Intake Procedure for the full pre-boundary decomposition. ### Preflight Script for Change Context @@ -83,7 +94,7 @@ This applies to all skills operating on an active change, not just spec-writing ### Standard Subagent Context -When orchestrator skills (`/fab-ff`, `/fab-fff`, and the prefix-step orchestrator `/fab-proceed` — added to the preamble's § Subagent Dispatch orchestrator list in d9rs; it dispatches `/fab-new`, `/fab-switch`, and `/git-branch` as prefix steps before delegating) or middle agents (`/fab-continue`) dispatch subagents via the Agent tool, the subagent prompt MUST instruct the subagent to read a standard set of project files **before** executing its task. This is defined in `_preamble.md` § Standard Subagent Context and is distinct from the Always Load layer (which is for the parent agent itself). +When orchestrator skills (`/fab-ff`, `/fab-fff`, and the prefix-step orchestrator `/fab-proceed` — added to the preamble's § Subagent Dispatch orchestrator list in d9rs; it dispatches the `_intake` Create-Intake Procedure (3xaj — was the full `/fab-new` skill), `/fab-switch`, and `/git-branch` as prefix steps before delegating) or middle agents (`/fab-continue`) dispatch subagents via the Agent tool, the subagent prompt MUST instruct the subagent to read a standard set of project files **before** executing its task. This is defined in `_preamble.md` § Standard Subagent Context and is distinct from the Always Load layer (which is for the parent agent itself). The standard subagent context includes: @@ -218,6 +229,7 @@ The exception set is **declared by the skill files themselves** (the preamble ne | Change | Date | Summary | |--------|------|---------| +| 260613-3xaj-extract-intake-helper | 2026-06-13 | **Helper allowlist grows to seven values**: `_intake` added to the `_preamble.md` § Skill Helper Declaration allowed values (`_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake`). The new `_intake.md` internal helper holds the shared pre-boundary Create-Intake Procedure (fab-new Steps 0–9, parameterized by `{questioning-mode}`); `fab-new`/`fab-draft` declare `helpers: [_generation, _srad, _intake]` (keep declaring `_generation`/`_srad` directly per the `_pipeline` precedent), `_intake` itself carries no `helpers:`, and `/fab-proceed` keeps none (dispatches `_intake` as a subagent prompt). Helper mapping table updated (fab-new/fab-draft row; "all others" 17→16). Added the **one-shared-helper-per-pipeline-phase symmetry table** (`_generation`/`_review`/`_intake`/`_pipeline`) — `_intake` is the pre-boundary counterpart to the post-boundary `_pipeline`, both mirroring the shared-body + knob + call-site-tail shape. Standard Subagent Context prefix-step list updated (`/fab-proceed` dispatches `_intake`, not full `/fab-new`). See [pipeline/planning-skills.md](../pipeline/planning-skills.md) § The `_intake` Shared Create-Intake Procedure. | | 260613-m3d4-uniform-stage-model-tier | 2026-06-13 | **Per-Stage Model Resolution gains the two-seam dispatch contract + compliance visibility** (Change C — skills/docs prose only, no Go; gated on `260613-fgxx`). The resolved tier's **two halves now dispatch through two seams**: the **model** rides the Agent tool's `model` param (unchanged), the **effort** is injected as an imperative instruction in the subagent prompt (``Operate at `` reasoning effort for this task.``, omitted when empty) — closing Gap 2's effort half, which fgxx had left as a deferred follow-up (the Agent tool has no `effort` param). Added a **compliance-visibility** expectation (Gap 1b): every dispatch site surfaces the resolved `model=/effort=` (into the dispatch prompt and/or step output) so a skipped/mis-resolved `fab resolve-agent` call is visible rather than silent, plus a prose all-empty-resolution surface/assert note (no Go guard — dispatch is harness-internal). Harness-adapter boundary now records the **id→alias** detail (the `model` param takes `opus`/`sonnet`/`haiku`/`fable`, not the full `claude-*` id the resolver emits — the orchestrator maps at the seam) and names the prompt as the effort adapter. The lone residual is the **harness ask** — a first-class per-sub-agent `effort` param on the Agent tool, out of fab's control, not built. Mirrors `_preamble.md` § Per-Stage Model Resolution; the `_pipeline.md`/`fab-ff`/`fab-fff`/`fab-continue` dispatch sites and their SPEC mirrors carry the same two-seam phrasing (see [pipeline/execution-skills.md](../pipeline/execution-skills.md)); `docs/findings/per-stage-model-tier-application.md` and `docs/specs/stage-models.md` reconciled to this reality. | | 260613-fgxx-collapse-post-intake-dual-mode | 2026-06-13 | **Per-Stage Model Resolution reconciled to the single post-intake dispatch mode** (minimal reconcile — full uniform-tier closure deferred to a follow-up). The "Foreground is advisory-only" paragraph (and its `description:` frontmatter mention) is rewritten: since fgxx collapsed the post-intake dual execution mode, **every** post-intake stage dispatches a sub-agent — including plain `/fab-continue`, now a one-stage sequencer that resolves `fab resolve-agent ` and dispatches its stage's block — so per-stage selection applies uniformly across apply/review/hydrate regardless of caller, and the residual advisory case narrows to a stage skill genuinely run with no dispatch at all. Intake stays pre-boundary (not tiered). Does NOT write the full uniform-tiering / per-sub-agent-effort-knob story. Mirrors `_preamble.md` § Per-Stage Model Resolution (see [pipeline/execution-skills.md](../pipeline/execution-skills.md) for the fab-continue rewrite). | | 260613-l3ja-per-stage-model-tiers | 2026-06-13 | New **Per-Stage Model Resolution** requirements section documenting the sub-agent dispatch-seam wiring (`_preamble.md` § Subagent Dispatch → Per-Stage Model Resolution): the orchestrators (`/fab-ff`, `/fab-fff`, `/fab-proceed`) and `/fab-continue`'s dispatch run `fab resolve-agent ` immediately before each stage's sub-agent and pass the resolved `model=`/`effort=` (empty model ⇒ omit/inherit; empty effort ⇒ omit). The resolution is **provider-neutral**; the Claude Code adapter (Agent tool `model` param) is named as harness-specific, not universal — and the coupling is not new (fab's dispatch is already Claude-Code-shaped). `review` resolves **once** for both reviewers + merge; foreground runs are advisory-only (MAY note mismatch, MUST NOT switch). The config schema + rationale live in [configuration.md](configuration.md). | diff --git a/docs/memory/_shared/index.md b/docs/memory/_shared/index.md index ab306cf2..b8d8b454 100644 --- a/docs/memory/_shared/index.md +++ b/docs/memory/_shared/index.md @@ -8,4 +8,4 @@ description: "Cross-cutting concerns spanning all domains — config.yaml/consti | File | Description | Last Updated | |------|-------------|-------------| | [configuration](configuration.md) | `config.yaml` schema (incl. `fab_version`, `review_tools`, `true_impact_exclude`, `test_paths`, `stage_hooks`, `agent.tiers` per-stage-model override — sole surface, per-field merge over fab-kit defaults, NO validation/provider-neutral, fixed non-overridable stage→tier mapping, l3ja; `stage_directives` + `model_tiers` removed in c5tr via migration 2.1.6-to-2.2.0), single fab-module parser `internal/config` + nil-safe accessors incl. the coupled-Unmarshal fallback caveat (ye8r), companion files (`context.md`, `code-quality.md`, `code-review.md` incl. `## Parsimony Pass` toggle and the wired `## Rework Budget` Max-cycles knob), `constitution.md` governance, 5 Cs of Quality, lifecycle management | 2026-06-13 | -| [context-loading](context-loading.md) | Smart context loading convention — descriptive 7-file always-load layer (skill file wins; exception set rule-derived from skill files, never enumerated in the preamble — d9rs), opt-in skill helpers (6-value allowlist incl. `_srad`/`_pipeline`) + stage-conditional loading, standard subagent context (orchestrators incl. `/fab-proceed`), per-stage model resolution at the dispatch seam (`fab resolve-agent `, provider-neutral, Claude-Code adapter named, review-resolves-once — l3ja; applies on every post-intake stage since the single-dispatch collapse, advisory only for a genuinely no-dispatch run — fgxx; the two halves dispatch through two seams — model via the Agent `model` param (short alias opus/sonnet/haiku/fable, id→alias mapped at the seam), effort via an imperative subagent-prompt instruction (no Agent effort param; omit when empty), plus a compliance-visibility expectation that each site surface the resolved `model=/effort=` — m3d4; residual = a per-sub-agent effort param on the Agent tool, a harness ask), selective domain loading, SRAD protocol pointer, scoped Next Steps Convention, generic fab-command failure rule (unconditional non-zero exit → STOP; `fab log command` exits 0 by contract) | 2026-06-13 | +| [context-loading](context-loading.md) | Smart context loading convention — descriptive 7-file always-load layer (skill file wins; exception set rule-derived from skill files, never enumerated in the preamble — d9rs), opt-in skill helpers (7-value allowlist incl. `_srad`/`_pipeline`/`_intake`) + stage-conditional loading, standard subagent context (orchestrators incl. `/fab-proceed`), per-stage model resolution at the dispatch seam (`fab resolve-agent `, provider-neutral, Claude-Code adapter named, review-resolves-once — l3ja; applies on every post-intake stage since the single-dispatch collapse, advisory only for a genuinely no-dispatch run — fgxx; the two halves dispatch through two seams — model via the Agent `model` param (short alias opus/sonnet/haiku/fable, id→alias mapped at the seam), effort via an imperative subagent-prompt instruction (no Agent effort param; omit when empty), plus a compliance-visibility expectation that each site surface the resolved `model=/effort=` — m3d4; residual = a per-sub-agent effort param on the Agent tool, a harness ask), selective domain loading, SRAD protocol pointer, scoped Next Steps Convention, generic fab-command failure rule (unconditional non-zero exit → STOP; `fab log command` exits 0 by contract) | 2026-06-13 | diff --git a/docs/memory/memory-docs/index.md b/docs/memory/memory-docs/index.md index 2b3cb81f..616bb066 100644 --- a/docs/memory/memory-docs/index.md +++ b/docs/memory/memory-docs/index.md @@ -11,4 +11,4 @@ description: "Authoring docs/memory & docs/specs — the hydrate skills (ingest | [hydrate-generate](hydrate-generate.md) | `/docs-hydrate-memory` generate mode — codebase scanning, gap detection, interactive scoping, memory file generation + placement rules (target path, domain/sub-domain index stubs, shape bounds — d9rs) | 2026-06-12 | | [hydrate-specs](hydrate-specs.md) | `/docs-hydrate-specs` skill — structural gap detection between memory and specs, interactive propose-then-apply (incl. the no-target new-spec-file branch and aligned prompt/handler tokens — d9rs) | 2026-06-12 | | [specs-index](specs-index.md) | `docs/specs/` directory — pre-implementation specs, distinction from memory, bootstrap and context integration, per-skill SPEC mirror coverage + naming policy (`SPEC-{source-filename}.md`; `_cli-fab`/`_cli-external` excluded — uliv); mirrors are reserved paths for `docs-reorg-specs` (d9rs) | 2026-06-12 | -| [templates](templates.md) | Artifact templates (intake, plan), skill frontmatter, and memory file format (incl. `description:` frontmatter + generated domains-only index, tciy). `plan.md` (`## Requirements` + `## Tasks` + `## Acceptance` + optional review-owned `## Deletion Candidates`) absorbs the former `spec.md` (j6cs) and the prior `tasks.md` + `checklist.md` pair; acceptance R# mandate scoped to requirement-derived categories, plan Assumptions = three grades, no Status header lines (uliv) | 2026-06-12 | +| [templates](templates.md) | Artifact templates (intake, plan), skill frontmatter (incl. `helpers:` + the one-shared-helper-per-phase decomposition `_generation`/`_review`/`_intake`/`_pipeline`, symmetry completed by 3xaj), and memory file format (incl. `description:` frontmatter + generated domains-only index, tciy). `plan.md` (`## Requirements` + `## Tasks` + `## Acceptance` + optional review-owned `## Deletion Candidates`) absorbs the former `spec.md` (j6cs) and the prior `tasks.md` + `checklist.md` pair; acceptance R# mandate scoped to requirement-derived categories, plan Assumptions = three grades, no Status header lines (uliv) | 2026-06-12 | diff --git a/docs/memory/memory-docs/templates.md b/docs/memory/memory-docs/templates.md index 6a840856..09429673 100644 --- a/docs/memory/memory-docs/templates.md +++ b/docs/memory/memory-docs/templates.md @@ -1,5 +1,5 @@ --- -description: "Artifact templates (intake, plan), skill frontmatter, and memory file format (incl. `description:` frontmatter + generated domains-only index, tciy). `plan.md` (`## Requirements` + `## Tasks` + `## Acceptance` + optional review-owned `## Deletion Candidates`) absorbs the former `spec.md` (j6cs) and the prior `tasks.md` + `checklist.md` pair; acceptance R# mandate scoped to requirement-derived categories, plan Assumptions = three grades, no Status header lines (uliv)" +description: "Artifact templates (intake, plan), skill frontmatter (incl. `helpers:` + the one-shared-helper-per-phase decomposition `_generation`/`_review`/`_intake`/`_pipeline`, symmetry completed by 3xaj), and memory file format (incl. `description:` frontmatter + generated domains-only index, tciy). `plan.md` (`## Requirements` + `## Tasks` + `## Acceptance` + optional review-owned `## Deletion Candidates`) absorbs the former `spec.md` (j6cs) and the prior `tasks.md` + `checklist.md` pair; acceptance R# mandate scoped to requirement-derived categories, plan Assumptions = three grades, no Status header lines (uliv)" --- # Templates @@ -100,6 +100,19 @@ Skill files in `$(fab kit-path)/skills/` use YAML frontmatter with these fields: The `model_tier` field is used by `sync/2-sync-workspace.sh` during deployment to generate agent files with provider-specific `model:` fields. (Note: the model-tier system was later eliminated — all skills now run on the session's default model and no `model_tier` frontmatter is consumed. This documents historical behavior; the former `model-tiers.md` memory file was removed in the 260608 domain restructure.) +#### `helpers:` and the One-Shared-Helper-Per-Phase Decomposition + +A skill MAY declare additional helper files via a `helpers:` frontmatter list. The mechanism, the allowed-values allowlist (seven as of 3xaj), and the per-skill mapping are authoritatively documented in [_shared/context-loading.md](../_shared/context-loading.md) § Skill Helper Declaration — not duplicated here. What matters for **skill-file structure** is the decomposition principle that mechanism enables: the workflow factors into **one shared internal helper per pipeline phase**, each a shared body parameterized by call-site-specific knobs, with call-site tails staying in the consumer files: + +| Phase | Helper | Knob(s) | Consumers | +|-------|--------|---------|-----------| +| artifact mechanics | `_generation` | — | `fab-new`, `fab-draft`, `fab-continue`, `fab-ff`, `fab-fff` | +| review mechanics | `_review` | — | `fab-continue`, `fab-ff`, `fab-fff` | +| pre-intake orchestration | `_intake` | `{questioning-mode}` | `fab-new`, `fab-draft`, `fab-proceed` | +| post-intake orchestration | `_pipeline` | `{driver}`, `{terminal}` | `fab-ff`, `fab-fff` | + +The symmetry was **completed by 3xaj**: `_intake` (pre-boundary intake creation) is the counterpart to `_pipeline` (post-boundary orchestration), pivoting on the single context-bearing boundary — intake. Each helper file follows the established internal-helper frontmatter shape (`user-invocable: false`, `disable-model-invocation: true`, `metadata: internal: true`) and is a **flat** `src/kit/skills/_*.md` canonical source (the directory-per-skill `_{name}/SKILL.md` form is the *deployed* copy `fab sync` writes under `.claude/skills/`). The internal helpers carry no `helpers:` of their own (consumer-declared model). See [pipeline/planning-skills.md](../pipeline/planning-skills.md) § The `_intake` Shared Create-Intake Procedure for the full decomposition. + ### Memory File Format (`docs/memory/`) Memory files are the source of truth for system behavior and design decisions. Structure: @@ -223,6 +236,7 @@ When `/fab-continue` (hydrate) hydrates into memory files: | Change | Date | Summary | |--------|------|---------| +| 260613-3xaj-extract-intake-helper | 2026-06-13 | **Skill Frontmatter gains the `helpers:` decomposition note** (Change B of the skill-architecture refactor trio): added a `#### helpers:` and the One-Shared-Helper-Per-Phase Decomposition subsection recording the **symmetry table** (`_generation` artifact mechanics / `_review` review mechanics / `_intake` pre-intake orchestration / `_pipeline` post-intake orchestration), completed by 3xaj when `_intake` (the pre-boundary counterpart to the post-boundary `_pipeline`) was extracted. Notes the internal-helper frontmatter shape, the flat canonical-source-vs-deployed-`SKILL.md` layout distinction, and the consumer-declared (no helper-side `helpers:`) model. The allowlist + per-skill mapping stay authoritative in [_shared/context-loading.md](../_shared/context-loading.md) (cross-referenced, not duplicated). `description:` frontmatter updated to surface the decomposition. | | 260612-d9rs-docs-reality-sweep | 2026-06-12 | **Depth bound reconciled** (skills-audit batch 5/5, Theme 8): the Memory Tree Shape depth bullet now states both countings as equivalent — ≤3 *path segments* under `docs/memory/` ≡ *folder depth* ≤2 (domain = 1, sub-domain = 2) — matching `docs-reorg-memory`'s reconciled Shape Report (`⚠ over depth` fires for folders deeper than 2). **Rebalancer apply path updated**: each new sub-domain's `description:`-only index stub is created BEFORE `fab memory-index` runs (Index Ownership model — resolves the former Step 5.3/5.4 edit-vs-forbid contradiction), and the no-dangling-link hard block gained an abort/rollback escape (unrewritable link → roll back that migration, re-run `fab memory-index`, continue with the rest). Hydration Rule 4 names all three index tiers. | | 260612-pw3k-operator-pane-perf-error-surfacing | 2026-06-12 | Index Hierarchy date-sourcing description corrected (binary-review B5, F34): `fab memory-index` dates come from ONE batched `git log --date=short --name-only -- docs/memory` pass (newest-first, first date per path wins), with the per-file `git log -1` spawn retained solely as fallback when the batched call fails — replacing the stale "`git log -1 --date=short` dates" wording. Rendered output byte-identical; mechanism description only. | | 260611-uliv-skills-staleness-sweep-frontmatter-fixes | 2026-06-11 | **Acceptance R# exemption (D, behavior-flagged)**: the R# trace mandate is scoped to requirement-derived categories — Code Quality and `checklist.extra_categories` items use `A-{NNN}: {outcome}` with no R# (matching the template's own A-007/A-008 shape); `_generation.md` steps 4+6 and the plan template's TRACEABILITY/ACCEPTANCE-FORMAT comments state the split. New design decision "Acceptance R# Mandate Scoped to Requirement-Derived Categories". **Template comment rewrites (C)**: the intake template's four spec-stage comments rewritten to plan/apply-entry vocabulary ("primary input for plan generation (apply entry)", "apply-entry agent (which co-generates plan.md)"); the plan template's `## Assumptions` comment now says **three grades only** (Unresolved is intake-only; `_preamble.md`'s four-grades rule scoped to intake artifacts — the intake template keeps all four). **Dead `**Status**:` lines deleted**: `**Status**: Draft` (intake) and `**Status**: In Progress` (plan) removed — no skill read or flipped them; `.status.yaml` is the state of record. `docs/specs/templates.md` mirror updated in the same change. | diff --git a/docs/memory/pipeline/execution-skills.md b/docs/memory/pipeline/execution-skills.md index 7cc868fc..76f621b6 100644 --- a/docs/memory/pipeline/execution-skills.md +++ b/docs/memory/pipeline/execution-skills.md @@ -31,23 +31,23 @@ Ship and review-pr are the exception: `/git-pr` and `/git-pr-review` manage thei |---|---|---|---|---|---| | Yes | Yes | — | — | — | `/fab-fff` only | | Yes | No | — | — | — | `/git-branch` → `/fab-fff` | -| No | — | Substantive | None | — | `/fab-new` → `/fab-fff` | +| No | — | Substantive | None | — | `_intake` → `/fab-switch` → `/git-branch` → `/fab-fff` | | No | — | Substantive | ≥1 | Clearly relevant | `/fab-switch` → `/git-branch` → `/fab-fff` | -| No | — | Substantive | ≥1 | Not clearly relevant | `/fab-new` → `/fab-fff` (emit bypass notes) | +| No | — | Substantive | ≥1 | Not clearly relevant | `_intake` → `/fab-switch` → `/git-branch` → `/fab-fff` (emit bypass notes) | | No | — | Empty/thin | ≥1 | — | `/fab-switch` → `/git-branch` → `/fab-fff` (pick by date-recency) | | No | — | Empty/thin | None | — | Error: "Nothing to proceed with — start a discussion or run /fab-new (or /fab-draft) first." | -The `/fab-new`-prefixed rows no longer chain `/git-branch` (w7dp): `/fab-new` Step 11 creates or checks out the matching branch inline (since #322), so the trailing dispatch was a guaranteed no-op. The `/fab-switch`-prefixed rows and the branch-mismatch row keep it — switching activates a change but creates no branch; the git-branch Dispatch section's "runs when" claim matches the table. +As of 3xaj the create-new rows dispatch the shared **`_intake` Create-Intake Procedure** (`{questioning-mode} = promptless-defer`) — not the full `/fab-new` skill — and then chain `/fab-switch` → `/git-branch`. This **inverts the w7dp claim** that the create-new rows could drop `/git-branch` as a no-op: that held only while the rows invoked the full `/fab-new` skill, whose Step 11 branched inline. `_intake` stops at intake `ready` and does NOT activate or branch (activate + branch are `/fab-new`'s Steps 10–11 tail, kept at the call site per the EXTRACTION BOUNDARY), so `/fab-switch` (activate) + `/git-branch` (branch) are now **required** to reach the same active-and-branched end state — parity preserved, not regressed. The `/fab-switch`-prefixed relevant-intake rows and the branch-mismatch row keep `/git-branch` for the same reason they always did (switching activates a change but creates no branch); the git-branch Dispatch section's "runs when" claim matches the table. **Relevance assessment**: When conversation is substantive AND ≥1 unactivated intake exists, the skill scores each candidate's topical relevance. Relevance is judged by reading each candidate's title, `## Origin`, `## Why`, and `## What Changes` sections — not the slug alone (slugs are terse and routinely misrepresent content). Clearly relevant requires shared topic + overlapping terminology + consistent scope; partial or vague overlap does NOT qualify. If multiple candidates are equally clearly relevant, date-descending prefix (`sort -t- -k1,1r | head -1`) is used as the tiebreak — this is the ONLY use of date-recency in the substantive branch. In the empty/thin + ≥1 intake branch, date-recency is the primary pick (no relevance check runs), preserving the "resume yesterday's draft" flow; this is safe because an empty/thin conversation carries no competing signal. **Asymmetric-bias rule**: When a candidate's relevance is genuinely ambiguous (neither clearly relevant nor clearly unrelated), it MUST be classified as *not clearly relevant*, falling through to `/fab-new`. Failure modes are asymmetric: false-positive (activating an unrelated draft) corrupts the draft and conflates features in pipeline output — recovery requires manual rollback; false-negative (creating a new intake when the draft was relevant) leaves the draft intact — the user sees the bypass note and can run `/fab-switch {name}` to recover. Biasing toward the recoverable failure is the design intent. Relevance judgment is inline LLM judgment — no new `fab` subcommand, classifier, or embedding index. -**Bypass notes**: When the skill dispatches `/fab-new` despite ≥1 unactivated intake being present (substantive + not clearly relevant row), each scanned draft is surfaced as a line in the output using the exact wording `Note: unactivated draft {name} exists — not relevant to current conversation, left untouched.` Multiple notes appear in date-descending order and are emitted BEFORE any step reports (e.g., `Created intake:`, `Branch:`), so the reader sees context before action. No notes are emitted on the activation paths (clearly-relevant row, empty/thin + ≥1 intake row) or when no drafts were scanned. +**Bypass notes**: When the skill dispatches the create-new path (`_intake`, since 3xaj — was the full `/fab-new` skill) despite ≥1 unactivated intake being present (substantive + not clearly relevant row), each scanned draft is surfaced as a line in the output using the exact wording `Note: unactivated draft {name} exists — not relevant to current conversation, left untouched.` Multiple notes appear in date-descending order and are emitted BEFORE any step reports (e.g., `Created intake:`, `Branch:`), so the reader sees context before action. No notes are emitted on the activation paths (clearly-relevant row, empty/thin + ≥1 intake row) or when no drafts were scanned. -Each prefix step (`/fab-new`, `/fab-switch`, `/git-branch`) is dispatched as a subagent via the Agent tool per `_preamble.md` § Subagent Dispatch. The final `/fab-fff` delegation is invoked via the Skill tool in the current context (not as a subagent), since it is the terminal operation and its output should be visible to the user. `/fab-proceed` does not pass `--force` or any flags to `/fab-fff`. The skill is idempotent — re-running detects which steps are already complete and skips them. The skill remains zero-prompt: ambiguous relevance is resolved by the asymmetric-bias rule, never by asking the user. The fab-new dispatch is **promptless** and carries the defer-and-surface contract (w7dp): the subagent prompt instructs it to ask NO questions — would-be-asked SRAD Unresolved decisions are recorded in the intake's `## Assumptions` table as Unresolved rows with Rationale `Deferred — promptless dispatch`, returned in the subagent result, and surfaced by `/fab-proceed` as informational lines before the `/fab-fff` delegation; the intake gate is the structural backstop (a single Unresolved row zeroes `fab score`, failing the gate) — see [planning-skills.md](planning-skills.md) § Promptless Dispatch and `_srad.md`'s Critical-Rule carve-out. When conversation context synthesis feeds `/fab-new`, the skill extracts decisions, rejected alternatives, constraints, and specific values from the *live conversation only* — bypassed drafts are NOT mined for synthesis content. Skill file: `$(fab kit-path)/skills/fab-proceed.md`. +Each prefix step (the `_intake` Create-Intake Procedure, `/fab-switch`, `/git-branch`) is dispatched as a subagent via the Agent tool per `_preamble.md` § Subagent Dispatch. The final `/fab-fff` delegation is invoked via the Skill tool in the current context (not as a subagent), since it is the terminal operation and its output should be visible to the user. `/fab-proceed` does not pass `--force` or any flags to `/fab-fff`. The skill is idempotent — re-running detects which steps are already complete and skips them. The skill remains zero-prompt: ambiguous relevance is resolved by the asymmetric-bias rule, never by asking the user. As of 3xaj the create-an-intake sub-operation dispatches the shared **`_intake` Create-Intake Procedure with `{questioning-mode} = promptless-defer`** (the subagent reads `.claude/skills/_intake/SKILL.md`) rather than the full `/fab-new` skill — `/fab-proceed`'s state-detection decides *whether* to create an intake; `_intake` performs it; the activate/branch that the full `/fab-new` once did inline is now the separate `/fab-switch` → `/git-branch` chain on the create-new rows (see the dispatch table above). The dispatch is **promptless** and `promptless-defer` *is* the defer-and-surface contract (w7dp), now encoded in the called helper rather than as a per-dispatch prompt contract over `/fab-new`: the procedure asks NO questions — would-be-asked SRAD Unresolved decisions are recorded in the intake's `## Assumptions` table as Unresolved rows with Rationale `Deferred — promptless dispatch`, returned in the subagent result, and surfaced by `/fab-proceed` as informational lines before the `/fab-fff` delegation; the intake gate is the structural backstop (a single Unresolved row zeroes `fab score`, failing the gate) — see [planning-skills.md](planning-skills.md) § Promptless Dispatch and § The `_intake` Shared Create-Intake Procedure, and `_srad.md`'s Critical-Rule carve-out. When conversation context synthesis feeds the `_intake` dispatch, the skill extracts decisions, rejected alternatives, constraints, and specific values from the *live conversation only* — bypassed drafts are NOT mined for synthesis content. Skill file: `$(fab kit-path)/skills/fab-proceed.md`. -**Skill inventory — change creation**: Two skills handle change creation: `/fab-new` (create + activate, description: "Start a new change — creates the intake and activates it", skill file: `$(fab kit-path)/skills/fab-new.md`) and `/fab-draft` (create without activating, description: "Create a change intake without activating it.", skill file: `$(fab kit-path)/skills/fab-draft.md`). `/fab-new` calls `fab change switch "{name}"` after advancing intake to ready, so the change is immediately active and `.fab-status.yaml` is created as a side effect. `/fab-draft` performs fab-new's identical Steps 0–9 (create folder, initialize `.status.yaml`, generate intake, advance to ready) but does NOT call `fab change switch` and does NOT create the `.fab-status.yaml` symlink — since szxd, `fab-draft.md` no longer carries its own copy of those steps: it is a **thin delta over fab-new** that reads the deployed `fab-new` SKILL.md, executes its Steps 0–9, and prominently SKIPS Steps 10–11 (no activation, no git) — see [planning-skills.md](planning-skills.md). The `Next:` line for `/fab-new` reads `/fab-continue, /fab-fff, /fab-ff, or /fab-clarify` (no activation preamble); the `Next:` line for `/fab-draft` includes the activation preamble: `/fab-switch {name} to make it active, then /fab-continue, /fab-fff, /fab-ff, or /fab-clarify`. Both skills appear under the "Start & Navigate" group in `fab help`. Both carry a Key Properties `Idempotent?` declaration: re-running with an already-used backlog/Linear ID routes to resume instead of erroring, while a natural-language re-run intentionally creates a new change — see [planning-skills.md](planning-skills.md) § Re-Run Semantics (Idempotency). +**Skill inventory — change creation**: Two skills handle change creation: `/fab-new` (create + activate, description: "Start a new change — creates the intake and activates it", skill file: `$(fab kit-path)/skills/fab-new.md`) and `/fab-draft` (create without activating, description: "Create a change intake without activating it.", skill file: `$(fab kit-path)/skills/fab-draft.md`). `/fab-new` calls `fab change switch "{name}"` after advancing intake to ready, so the change is immediately active and `.fab-status.yaml` is created as a side effect. `/fab-draft` performs the identical Steps 0–9 (create folder, initialize `.status.yaml`, generate intake, advance to ready) but does NOT call `fab change switch` and does NOT create the `.fab-status.yaml` symlink — as of 3xaj **both** `/fab-new` and `/fab-draft` are thin call-sites over the shared `_intake` Create-Intake Procedure (`{questioning-mode} = interactive`): `/fab-new` adds its Steps 10–11 activate/branch tail, `/fab-draft` stops at `ready`. (This supersedes szxd's "thin delta over fab-new" form, where `/fab-draft` read `fab-new`'s SKILL.md, executed its Steps 0–9, and prominently SKIPPED Steps 10–11 with a momentum warning — that warning is retired in 3xaj because the not-to-run steps no longer live in any body `/fab-draft` executes.) See [planning-skills.md](planning-skills.md) § The `_intake` Shared Create-Intake Procedure. The `Next:` line for `/fab-new` reads `/fab-continue, /fab-fff, /fab-ff, or /fab-clarify` (no activation preamble); the `Next:` line for `/fab-draft` includes the activation preamble: `/fab-switch {name} to make it active, then /fab-continue, /fab-fff, /fab-ff, or /fab-clarify`. Both skills appear under the "Start & Navigate" group in `fab help`. Both carry a Key Properties `Idempotent?` declaration: re-running with an already-used backlog/Linear ID routes to resume instead of erroring, while a natural-language re-run intentionally creates a new change — see [planning-skills.md](planning-skills.md) § Re-Run Semantics (Idempotency). **Status confidence display**: `/fab-status` shows confidence read from the persisted `.status.yaml` `confidence` block (intake is the sole scoring source as of j6cs). It displays `Confidence: {score} of 5.0 (...)`; the former ` (indicative)` qualifier was removed when the `confidence.indicative` flag was retired. When no confidence data exists, it falls back to `Confidence: not yet scored`. `fab score --check-gate --stage intake` emits count fields (`certain`, `confident`, `tentative`, `unresolved`) and compares against the flat 3.0 intake gate (no separate spec branch remains). @@ -439,6 +439,7 @@ Steps execute 1→3 for safety. If interrupted, re-run detects folder already in | Change | Date | Summary | |--------|------|---------| +| 260613-3xaj-extract-intake-helper | 2026-06-13 | **`/fab-proceed` create-intake dispatch rerouted to `_intake(promptless-defer)`** (Change B of the skill-architecture refactor trio — markdown/skill only, no Go changes). The `/fab-proceed` dispatch table's two **create-new rows** now read `_intake → /fab-switch → /git-branch → /fab-fff` (was `/fab-new → /fab-fff`): the create-an-intake sub-operation dispatches the shared `_intake` Create-Intake Procedure (`{questioning-mode} = promptless-defer`) instead of the full `/fab-new` skill, and because `_intake` stops at intake `ready` without activating or branching, the `/fab-switch` (activate) + `/git-branch` (branch) chain is now **required** for parity — **inverting** the w7dp claim that the create-new rows could drop `/git-branch` as a no-op (that held only while they invoked full `/fab-new`, whose Step 11 branched inline). `/fab-proceed`'s state-detection, relevance assessment, asymmetric-bias rule, bypass notes, Conversation Context Synthesis, and terminal `/fab-fff` delegation are all unchanged — only the create-an-intake leaf is rerouted. `/fab-draft` reconciled to a thin `_intake(interactive)` call-site (superseding the szxd "thin delta over fab-new" description; momentum warning retired). The `/fab-proceed.md` § heading renamed `fab-new Dispatch` → `Create-Intake Dispatch`. Stale prose swept (bypass-notes, prefix-step list, change-creation inventory, dispatch table + rationale). Full `_intake` extraction recorded in [planning-skills.md](planning-skills.md) § The `_intake` Shared Create-Intake Procedure. | | 260613-m3d4-uniform-stage-model-tier | 2026-06-13 | **Per-stage dispatch sites surface the resolved tier + inject effort via the subagent prompt (two-seam dispatch contract)** (Change C — skills/docs prose only, no Go; gated on `260613-fgxx`). Every per-stage dispatch site in the `fab-ff`/`fab-fff` orchestrator family — the shared `_pipeline.md` bracket (Behavior "Per-stage model resolution" note + Steps 1 apply / 2 review / 3 hydrate + Auto-Rework re-apply/re-review items 3/4) and `fab-fff.md` Steps 4–5 (ship/review-pr) — now (1) **surfaces** the resolved `model=/effort=` after `fab resolve-agent ` (carried into the dispatch prompt and/or echoed in step output, so a skipped/mis-resolved tier is **visible rather than silent** — Gap 1b compliance visibility), and (2) applies the tier through **two seams**: the **model** half via the Agent tool's `model` param (unchanged) and the **effort** half via an imperative instruction in the subagent prompt (``Operate at `` reasoning effort for this task.``, omitted when empty — Gap 2's effort half, previously left as fgxx's deferred follow-up; the Agent tool has no `effort` param). `review` resolves **once** for both reviewers + merge (same model + same effort instruction). `fab-continue.md`'s one-stage-sequencer header note + post-intake dispatch contract + nested-reviewers note carry the same two-seam phrasing; `fab-ff.md`'s sibling note (structural twin of fab-fff's) reconciled to match. The harness-adapter boundary records the **id→alias** detail (the `model` param takes `opus`/`sonnet`/`haiku`/`fable`, not the full `claude-*` id the resolver emits). The lone residual is the **harness ask** — a first-class per-sub-agent `effort` param on the Agent tool, out of fab's control, not built. SPEC mirrors updated same-PR: `SPEC-_pipeline.md`, `SPEC-_preamble.md`, `SPEC-fab-fff.md`, `SPEC-fab-ff.md`, `SPEC-fab-continue.md`. `docs/findings/per-stage-model-tier-application.md` (Gap 1a closed by fgxx, Gap 1b + Gap 2-effort addressed here, item 4 = residual) and `docs/specs/stage-models.md` reconciled. Canonical contract: [_shared/context-loading.md](../_shared/context-loading.md) § Per-Stage Model Resolution. | | 260613-fgxx-collapse-post-intake-dual-mode | 2026-06-13 | **Collapsed the post-intake dual execution mode — one execution mode, orchestrators as pure sequencers** (skills+specs prose refactor + one Go test; no production Go change, no new CLI command). Guiding principle (newly named): **intake is the sole context boundary** — the main session runs up to and including intake; after intake the intake artifact IS the context and every post-intake stage (apply/review/hydrate) runs as a dispatched sub-agent fed by `intake.md` + `plan.md` + `docs/memory/`. `fab-continue.md`: the three caller-aware "When invoked as a subagent: do NOT run `fab status`" blockquote conditionals (Apply/Review/Hydrate Behavior) are **removed** — with one execution mode there is no second branch; plain `/fab-continue` is now a **one-stage sequencer** that runs `fab resolve-agent ` then dispatches the stage's block exactly as the orchestrators do, and owns the `finish`/`fail`/`reset` transition after it returns. The "do NOT run `fab status`; return results only" prompt instruction is now the **universal block contract** (carried in the dispatch prompt), not a per-caller skip rule in the block; it is NOT re-encoded as any "skip §Verdict when subagent" flag. The interactive § Verdict rework menu (Path A) and `_pipeline.md`'s autonomous Auto-Rework Loop (Paths B/C/D) **remain** as the invocation-level failure-policy fork. Header note reconciled from "foreground = advisory-only" to the one-stage-sequencer framing. `_pipeline.md` Behavior note affirmed as **pure sequencer** (dispatch → read → proceed/loop/stop) with the universal-contract framing; per-cycle rework choreography (fail+reset pair) and the history-shape invariant **unchanged in semantics**. `_preamble.md` § Per-Stage Model Resolution "Foreground is advisory-only" minimally reconciled (residual advisory only for a genuinely no-dispatch run; closes Gap 1a — uniform tiering across apply/review/hydrate; full uniform-tier closure deferred to a follow-up). New Go test `TestHistoryShape_IdenticalRegardlessOfDriver` in `internal/status/mutators_test.go` pins that the manual (`driver=""`) and dispatched (`driver="fab-fff"`) paths leave `.history.jsonl` entries of identical shape — agreeing on every caller-blind field (stage/action/from/reason), equal modulo the per-run `ts` timestamp and the optional `driver` field (see [schemas.md](schemas.md)). SPEC mirrors updated same-PR: `SPEC-fab-continue.md` (f006 → universal block contract), `SPEC-_pipeline.md`, `SPEC-_preamble.md`, `docs/specs/stage-models.md` § Foreground limitation. `runtime/operator.md` verified — no foreground-post-intake assumption (no-op). | | 260612-w7dp-orchestrator-dispatch-review-pr-recovery | 2026-06-12 | **Orchestrator dispatch targets + review-pr failure recovery** (skills-audit batch 2/5, Themes 2+4 — markdown only, no Go changes). `/git-pr` + `/git-pr-review`: optional explicit `` argument (Step 0 transient resolution; **hard STOP** on explicit-arg resolution failure; value-based classification vs git-pr's 7 type tokens) and a **branch-matches-change guard** before any status mutation/commit/push (two-form match — exact folder equality or folder-as-substring; STOP with `/git-branch`//`/fab-switch`/pass-the-change guidance, no autonomous checkout); git-pr's detached-HEAD STOP moved before Step 0a's start (verify-before-mutate parity); the Step 1b mismatch nudge **removed** as superseded. `fab-fff.md` Steps 4–5 dispatch `/git-pr {name}` / `/git-pr-review {name}` (folder name — collision-proof vs type tokens). git-pr-review's Step 6/6.5/phase gates key on "a change resolved in Step 0 (active or explicit)"; push-failure + Phase-2 timeout re-run guidance conditionally name the explicit change. `fab-continue.md`: new **`review-pr`/`failed` dispatch row** (re-execute `/git-pr-review {name}` — `start` owns failed→active; never `reset`, whose From-set excludes `failed`; a failed PR review no longer reads "complete"); ship/review-pr rows pass `{name}` explicitly and cite `active` only (`ready` unreachable per AllowedStates); **idempotent Reset Flow** (target already `active` → skip the CLI call; `failed` → the failed dispatch rows; `pending` → error with guidance); reset event line `done/ready/skipped → active`; `intake.md`-missing error points at `/fab-continue intake`. `_pipeline.md`: the five unexecutable `/fab-clarify intake` recovery pointers replaced by the executable, **override-aware** `/fab-continue intake` → `/fab-clarify ` route (+ delete-`plan.md` note); stop template, gate-fail, and task-fail guidance name the change; `{driver}` claim scoped (fail/recovery commands deliberately driver-less); decision heuristics made **disjoint** (code-fails-correct-requirement → Fix code; requirement-itself-wrong → Revise requirements). fab-proceed: `/git-branch` dropped from the `/fab-new`-prefixed dispatch rows (Step 11 creates the branch inline) and the promptless fab-new dispatch defined as defer-and-surface (see [planning-skills.md](planning-skills.md)). New design decisions "Ship-Time Skills Take an Explicit Change…" and "Recovery Guidance Is Override-Aware". 13 SPEC mirrors + `docs/specs/skills.md`/`glossary.md`/`architecture.md` § Git Integration updated same-PR. | diff --git a/docs/memory/pipeline/index.md b/docs/memory/pipeline/index.md index e59ef295..7f572351 100644 --- a/docs/memory/pipeline/index.md +++ b/docs/memory/pipeline/index.md @@ -10,6 +10,6 @@ description: "The change pipeline — stage lifecycle & state machine, planning/ | [change-lifecycle](change-lifecycle.md) | Change naming, folder structure, `.status.yaml` (6-stage pipeline + `plan:` block, flock-serialized writes + fsync durability + sparse-key insertion), `.fab-status.yaml` symlink (atomic temp+rename swap, dangling-target validation), `stage_hooks` pre/post commands around start/finish (documented in c5tr), git integration (bookkeeping decoupled; ship-time branch↔change hard guard since w7dp), `/fab-status` (six-glyph progress legend incl. `⏭` skipped), `/fab-switch` (six-state `{state}` qualifier), backlog scanning | 2026-06-12 | | [clarify](clarify.md) | `/fab-clarify` skill — intake-only (j6cs), dual modes (suggest/auto), intake taxonomy scan, structured questions, coverage reports, audit trail, grade reclassification, always-recompute intake score; hosts the [AUTO-MODE] Skill Invocation Protocol and is sole bulk-confirm authority (zc9m); bulk confirm evaluated before the zero-gaps exit, confirmations graded by recomputed composite (no fiat Certain), unified audit-trail placement rules (c5tr) | 2026-06-12 | | [execution-skills](execution-skills.md) | Apply (unified `plan.md` `## Requirements`+`## Tasks`+`## Acceptance` generated at entry), review, hydrate, archive, and orchestrator behavior — `/fab-continue` for pipeline stages, `/fab-archive` for housekeeping, `/fab-proceed` for context-aware pipeline orchestration; `/git-pr` ship behavior with the mechanically-rendered `## Meta` block via `fab pr-meta`, a unified Step 0 resolution (szxd), and git-state hardening — detached-HEAD STOP, expected-area staging guard, PR-state branching, resolved default-branch guard (g8st); `/git-pr-review` split commit/push failure semantics + unpushed-commit re-run gate (g8st); fab-continue loads `_generation`/`_review` stage-conditionally at point of use (zc9m); the shared ff/fff bracket lives in `_pipeline.md` with once-stated rework choreography (cycle cap = the `{max_cycles}` knob from code-review.md § Rework Budget, default 3 — c5tr), exhaustion terminal state review:failed, and fab-continue's review-failed rework-menu dispatch row (szxd); re-archiving a genuinely archived change soft-skips (exit 0) and restore `--switch` surfaces `pointer: failed` (k4ge); archive/restore index writes are atomic with honest `index: failed` reporting on both paths (hv7t); `/git-pr`/`/git-pr-review` accept an optional explicit `` argument and guard branch↔change correspondence before any mutation, fab-continue gained the review-pr/failed dispatch row + idempotent reset, and recovery guidance is override-aware (w7dp). Operator coordination moved to runtime/operator.md. | 2026-06-13 | -| [planning-skills](planning-skills.md) | `/fab-new`, `/fab-continue`, `/fab-ff`, `/fab-clarify` — the planning stage (intake only) and the shared `_generation.md` partial (Intake + unified Plan procedures); requirement capture + plan generation live at apply entry (spec stage removed in j6cs); fab-new/fab-draft re-run/resume semantics (ID collisions route to resume, 9u91); change_type is hook-owned — skills verify via .status.yaml and override only if wrong (uliv); SRAD framework lives in the `_srad` helper, declared by the 6 planning skills (zc9m); fab-draft is a thin delta over fab-new Steps 0–9, fab-ff/fab-fff are thin wrappers over the shared `_pipeline` bracket, fab-new Step 11 is a single first-match-wins branch table (szxd; 6 rows since g8st — remote-only `--track` checkout, dirty-tree carried-over note excluding the change's own artifacts, same-change rename); SRAD grades by half-open thresholds with one `R<25 AND A<25` Critical-Rule definition, the plan walk emits `## Assumptions` explicitly, artifacts always carry the section (omit-when-zero is display-only), fab-new output puts the Assumptions block last (c5tr); fab-new's backlog-ID collision pre-check is exact-ID anchored (`fab resolve --id` equality) and fab-proceed's promptless fab-new dispatch carries the defer-and-surface contract with the matching `_srad` Critical-Rule carve-out (w7dp); fab-new/fab-draft `Next:` lines derive at runtime per the _preamble Lookup Procedure and `_generation` names fab-continue in both consumer groups (d9rs) | 2026-06-12 | +| [planning-skills](planning-skills.md) | `/fab-new`, `/fab-continue`, `/fab-ff`, `/fab-clarify` — the planning stage (intake only) and the shared `_generation.md` partial (Intake + unified Plan procedures); requirement capture + plan generation live at apply entry (spec stage removed in j6cs); fab-new/fab-draft re-run/resume semantics (ID collisions route to resume, 9u91); change_type is hook-owned — skills verify via .status.yaml and override only if wrong (uliv); SRAD framework lives in the `_srad` helper, declared by the 6 planning skills (zc9m); pre-boundary intake creation (fab-new Steps 0–9) lives in the shared `_intake` helper parameterized by `{questioning-mode}` (interactive | promptless-defer) — fab-new/fab-draft/fab-proceed are thin call-sites, completing the one-shared-helper-per-phase symmetry with `_pipeline`; fab-new keeps the activate/branch tail, fab-draft stops at ready (momentum warning retired), fab-proceed dispatches promptless-defer + chains /fab-switch→/git-branch (3xaj); fab-ff/fab-fff are thin wrappers over the shared `_pipeline` bracket, fab-new Step 11 is a single first-match-wins branch table (szxd; 6 rows since g8st — remote-only `--track` checkout, dirty-tree carried-over note excluding the change's own artifacts, same-change rename); SRAD grades by half-open thresholds with one `R<25 AND A<25` Critical-Rule definition, the plan walk emits `## Assumptions` explicitly, artifacts always carry the section (omit-when-zero is display-only), fab-new output puts the Assumptions block last (c5tr); fab-new's backlog-ID collision pre-check is exact-ID anchored (`fab resolve --id` equality) and fab-proceed's promptless `_intake` dispatch carries the defer-and-surface contract with the matching `_srad` Critical-Rule carve-out (w7dp); fab-new/fab-draft `Next:` lines derive at runtime per the _preamble Lookup Procedure and `_generation` names fab-continue in both consumer groups (d9rs) | 2026-06-12 | | [preflight](preflight.md) | `lib/preflight.sh` script — validation, accessor-based architecture, structured YAML output, skill integration | 2026-06-12 | | [schemas](schemas.md) | Workflow schema authority — the Go state machine (`internal/status` transitions + `internal/statusfile` stage order/progress; declarative `workflow.yaml` retired in c5tr): 6-stage pipeline, states, transitions, validation rules; `.status.yaml` `plan:` (`## Requirements`-aware), `confidence:` (indicative retired), and lazy `true_impact:` block schemas (incl. the `tests` sub-block + render-time `impl` residual, 7t5a); `fab impact` and `fab pr-meta` helper subcommands (rj31); allowed-states-enforced transition targets, `fab score --check-gate` non-zero gate-fail exit, iterations-preserving reset cascade (k4ge); `fab score` normal-mode hard-fail on load/persist/read errors (hv7t); per-stage allowed-states + transition-event tables enumerated, pinned by the exhaustive 216-cell matrix test (tb6f) | 2026-06-13 | diff --git a/docs/memory/pipeline/planning-skills.md b/docs/memory/pipeline/planning-skills.md index 20396f07..e2616b08 100644 --- a/docs/memory/pipeline/planning-skills.md +++ b/docs/memory/pipeline/planning-skills.md @@ -1,5 +1,5 @@ --- -description: "`/fab-new`, `/fab-continue`, `/fab-ff`, `/fab-clarify` — the planning stage (intake only) and the shared `_generation.md` partial (Intake + unified Plan procedures); requirement capture + plan generation live at apply entry (spec stage removed in j6cs); fab-new/fab-draft re-run/resume semantics (ID collisions route to resume, 9u91); change_type is hook-owned — skills verify via .status.yaml and override only if wrong (uliv); SRAD framework lives in the `_srad` helper, declared by the 6 planning skills (zc9m); fab-draft is a thin delta over fab-new Steps 0–9, fab-ff/fab-fff are thin wrappers over the shared `_pipeline` bracket, fab-new Step 11 is a single first-match-wins branch table (szxd; 6 rows since g8st — remote-only `--track` checkout, dirty-tree carried-over note excluding the change's own artifacts, same-change rename); SRAD grades by half-open thresholds with one `R<25 AND A<25` Critical-Rule definition, the plan walk emits `## Assumptions` explicitly, artifacts always carry the section (omit-when-zero is display-only), fab-new output puts the Assumptions block last (c5tr); fab-new's backlog-ID collision pre-check is exact-ID anchored (`fab resolve --id` equality) and fab-proceed's promptless fab-new dispatch carries the defer-and-surface contract with the matching `_srad` Critical-Rule carve-out (w7dp); fab-new/fab-draft `Next:` lines derive at runtime per the _preamble Lookup Procedure and `_generation` names fab-continue in both consumer groups (d9rs)" +description: "`/fab-new`, `/fab-continue`, `/fab-ff`, `/fab-clarify` — the planning stage (intake only) and the shared `_generation.md` partial (Intake + unified Plan procedures); requirement capture + plan generation live at apply entry (spec stage removed in j6cs); fab-new/fab-draft re-run/resume semantics (ID collisions route to resume, 9u91); change_type is hook-owned — skills verify via .status.yaml and override only if wrong (uliv); SRAD framework lives in the `_srad` helper, declared by the 6 planning skills (zc9m); pre-boundary intake creation (fab-new Steps 0–9) lives in the shared `_intake` helper parameterized by `{questioning-mode}` (interactive | promptless-defer) — fab-new/fab-draft/fab-proceed are thin call-sites, completing the one-shared-helper-per-phase symmetry with `_pipeline`; fab-new keeps the activate/branch tail, fab-draft stops at ready (momentum warning retired), fab-proceed dispatches promptless-defer + chains /fab-switch→/git-branch (3xaj); fab-ff/fab-fff are thin wrappers over the shared `_pipeline` bracket, fab-new Step 11 is a single first-match-wins branch table (szxd; 6 rows since g8st — remote-only `--track` checkout, dirty-tree carried-over note excluding the change's own artifacts, same-change rename); SRAD grades by half-open thresholds with one `R<25 AND A<25` Critical-Rule definition, the plan walk emits `## Assumptions` explicitly, artifacts always carry the section (omit-when-zero is display-only), fab-new output puts the Assumptions block last (c5tr); fab-new's backlog-ID collision pre-check is exact-ID anchored (`fab resolve --id` equality) and fab-proceed's promptless `_intake` dispatch carries the defer-and-surface contract with the matching `_srad` Critical-Rule carve-out (w7dp); fab-new/fab-draft `Next:` lines derive at runtime per the _preamble Lookup Procedure and `_generation` names fab-continue in both consumer groups (d9rs)" --- # Planning Skills @@ -41,9 +41,11 @@ The agent generates a 2-6 word slug (lowercase, hyphen-joined, no articles/prepo 1. **Clear input** — SRAD scoring identifies few or no Unresolved decisions. The skill generates the intake with up to 3 targeted questions (highest blast radius), assumes all Confident/Tentative decisions, and completes quickly. 2. **Vague input** — SRAD scoring identifies many Unresolved decisions. The skill enters **conversational mode**: back-and-forth exploration with no fixed question cap, starting with the highest-impact decisions (lowest Reversibility + lowest Agent Competence). Each question builds on previous answers. The conversation ends when the confidence score reaches >= 3.0 and the user signals satisfaction, or the user terminates early. -#### Promptless Dispatch Under `/fab-proceed` (Defer-and-Surface, w7dp) +#### Promptless Dispatch Under `/fab-proceed` (Defer-and-Surface, w7dp; rerouted through `_intake` in 3xaj) -When `/fab-proceed` dispatches `/fab-new` as a promptless subagent, there is no user to ask and no `[AUTO-MODE]` prefix is sent. The dispatch prompt carries the **defer-and-surface contract**: the subagent asks NO questions — each decision SRAD would normally ask (Unresolved, including Critical-Rule hits) is instead recorded in the intake's `## Assumptions` table as an Unresolved row with Rationale `Deferred — promptless dispatch` and returned in the subagent result; `/fab-proceed` surfaces the deferred decisions as informational lines (staying zero-prompt) before delegating to `/fab-fff`. The intake gate is the **structural backstop**: `fab score` returns 0.0 whenever **any** Unresolved row exists, so even a single deferral fails the gate and the pipeline stops normally for the user to resolve via `/fab-clarify`. `_srad.md`'s Critical Rule carries the matching **promptless-dispatch carve-out** (cross-referencing `fab-proceed.md` § fab-new Dispatch): the MUST-ask is satisfied by deferring and surfacing, never by silently assuming — everywhere a user is reachable, the MUST-ask applies unchanged. (Interactive relay and `[AUTO-MODE]` adoption were rejected as protocol surface the audit didn't ask for.) In the same change, `/fab-proceed`'s two `/fab-new`-prefixed dispatch rows dropped the trailing `/git-branch` — a guaranteed no-op since #322, because fab-new Step 11 creates or checks out the branch inline; the `/fab-switch`-prefixed rows and the branch-mismatch row keep it. See [execution-skills.md](execution-skills.md) for the full fab-proceed dispatch table. +When `/fab-proceed` runs the create-new path, it dispatches the **`_intake` Create-Intake Procedure with `{questioning-mode} = promptless-defer`** as a promptless subagent (since 3xaj — previously it dispatched the full `/fab-new` skill). There is no user to ask and no `[AUTO-MODE]` prefix is sent. The dispatch prompt carries the **defer-and-surface contract**, which `promptless-defer` now *encodes in the called helper* rather than as a per-dispatch prompt contract over `/fab-new`: the subagent asks NO questions — each decision SRAD would normally ask (Unresolved, including Critical-Rule hits) is instead recorded in the intake's `## Assumptions` table as an Unresolved row with Rationale `Deferred — promptless dispatch` and returned in the subagent result; `/fab-proceed` surfaces the deferred decisions as informational lines (staying zero-prompt) before delegating to `/fab-fff`. The intake gate is the **structural backstop**: `fab score` returns 0.0 whenever **any** Unresolved row exists, so even a single deferral fails the gate and the pipeline stops normally for the user to resolve via `/fab-clarify`. `_srad.md`'s Critical Rule carries the matching **promptless-dispatch carve-out** (cross-referencing `fab-proceed.md` § Create-Intake Dispatch): the MUST-ask is satisfied by deferring and surfacing, never by silently assuming — everywhere a user is reachable, the MUST-ask applies unchanged. (Interactive relay and `[AUTO-MODE]` adoption were rejected as protocol surface the audit didn't ask for.) + +**`/git-branch` is now REQUIRED on the create-new rows (3xaj inverted the w7dp claim).** The prior w7dp rationale — "the two `/fab-new`-prefixed dispatch rows dropped the trailing `/git-branch` as a no-op since #322 because fab-new Step 11 branches inline" — no longer holds: `_intake(promptless-defer)` stops at intake `ready` and does NOT activate or branch (activate + branch are `/fab-new`'s Steps 10–11 tail, which the EXTRACTION BOUNDARY keeps at the call site and which `_intake` never runs). So the create-new rows now chain **`_intake` → `/fab-switch` → `/git-branch`** precisely BECAUSE `_intake` stops short of branching — `/fab-switch` activates and `/git-branch` creates the matching branch, reaching the same active-and-branched end state the old full-`/fab-new` dispatch produced inline (parity is preserved, not regressed). The `/fab-switch`-prefixed relevant-intake rows and the branch-mismatch row keep `/git-branch` for the same reason they always did. See [execution-skills.md](execution-skills.md) for the full fab-proceed dispatch table. #### Gap Analysis @@ -62,7 +64,9 @@ The skill SHALL: 2. Call `fab change new` with `--slug`, optional `--change-id` (backlog ID), and `--log-args` (description). The command handles: directory creation, `created_by` detection (`gh api user` → `git config user.name` → `"unknown"`, silent fallback), `.status.yaml` initialization from the kit template, and status integration (`start intake fab-new`; command logging via `--log-args`) 3. Generate `intake.md` from the template (including Origin section), loading `fab/project/constitution.md` and `fab/project/config.yaml` as context -After generating the intake, `/fab-new` advances intake to `ready` — signaling the artifact exists and is open for `/fab-clarify` refinement. It then auto-activates the change via `fab change switch` (Step 10) and creates the matching git branch inline (Step 11). Branch creation applies the same branch-case logic as the standalone `/git-branch` skill — as of szxd, Step 11 states it as a single condition/command/report table annotated "evaluate in order, first match wins", preceded by the context commands (current branch, dirty-tree `{dirty_count}`, local target-exists check, remote `origin/{name}` check, upstream check, `fab change resolve` on the current branch) and a keep-in-sync comment referencing `git-branch.md` Step 4. As of g8st the table has **six rows**: (1) already active (no-op), (2) target exists locally (checkout), (3) **target exists only on the remote** → `git checkout --track "origin/{name}"` — never recreating a divergent local with `checkout -b` (report: `checked out, tracking origin/{name}`), (4) on main/master (create), (5) local-only branch passing the rename guard (rename), (6) local-only branch belonging to a different change OR pushed branch (create, leaving the old branch intact). The **rename guard** (row 5) matches `git-branch.md` Step 4 — the two twins are kept in sync via the in-file comment, deliberately NOT delegated to `/git-branch` at runtime (inline wins on runtime token economy): rename when the current branch resolves to no change (`fab change resolve "$(git branch --show-current)"` fails — e.g., a disposable `wt create` name) **or to the SAME change being branched** (g8st — e.g., a worktree placeholder named with the change's own ID; previously this fell through to the create row and left a stray placeholder branch); when it matches a *different* change's branch (e.g., after `/fab-switch` away from an unpushed change), create a new branch via `git checkout -b` instead, leaving the other change's branch intact (known caveat: the new branch inherits the old change's HEAD). A **dirty-tree note** (g8st) is appended to the report line whenever `{dirty_count}` > 0 and the matched row runs `git checkout -b` or `git branch -m`: ` — note: {dirty_count} uncommitted change(s) carried over from {old_branch}` — non-blocking (warn, never stash-prompt: the step runs inside no-questions/orchestrated flows). The twins' one deliberate divergence lives outside the shared rows: fab-new derives `{dirty_count}` **excluding `fab/changes/{name}/`** — the change's own just-created artifacts (`intake.md`, `.status.yaml`, `.history.jsonl`) always exist uncommitted by Step 11 and would fire the note on every run; git-branch counts the full porcelain output. The git step is non-fatal — if not in a git repo, it warns and skips; if a git operation fails, it reports the error and the change remains activated. For create-without-activate behavior, use `/fab-draft` instead — since szxd, `fab-draft.md` is a **thin delta over fab-new**: its body instructs reading the deployed `fab-new` SKILL.md and executing its Pre-flight/Arguments/Steps 0–9 with deltas (Step 9 tail = change NOT activated, user must `/fab-switch`; a prominent SKIP of Steps 10–11 — no activation, no git; Output minus the Activated/Branch lines, ending with the Activation Preamble `Next:` line; activation/git error rows dropped). The formerly byte-identical ~120-line copy of Steps 0–9 is gone; the shared steps were deliberately NOT moved into `_generation.md` (fab-continue/ff/fff also load it and would pay the context tax). +As of 3xaj, the intake-creation work above — Steps 0–9 (parse input, slug, gap analysis, create change, conversation context mining, generate `intake.md`, verify change type, confidence, SRAD question selection, advance to `ready`) — no longer lives inline in `fab-new.md`. It lives once in the shared **`_intake` Create-Intake Procedure** (`src/kit/skills/_intake.md`), which `fab-new.md` invokes as a thin call-site with `{questioning-mode} = interactive`. `fab-new.md` retains only the **activate + branch tail** (Steps 10–11) below — see § The `_intake` Shared Create-Intake Procedure. + +After the Create-Intake Procedure advances intake to `ready` — signaling the artifact exists and is open for `/fab-clarify` refinement — `/fab-new` auto-activates the change via `fab change switch` (Step 10) and creates the matching git branch inline (Step 11). Branch creation applies the same branch-case logic as the standalone `/git-branch` skill — as of szxd, Step 11 states it as a single condition/command/report table annotated "evaluate in order, first match wins", preceded by the context commands (current branch, dirty-tree `{dirty_count}`, local target-exists check, remote `origin/{name}` check, upstream check, `fab change resolve` on the current branch) and a keep-in-sync comment referencing `git-branch.md` Step 4. As of g8st the table has **six rows**: (1) already active (no-op), (2) target exists locally (checkout), (3) **target exists only on the remote** → `git checkout --track "origin/{name}"` — never recreating a divergent local with `checkout -b` (report: `checked out, tracking origin/{name}`), (4) on main/master (create), (5) local-only branch passing the rename guard (rename), (6) local-only branch belonging to a different change OR pushed branch (create, leaving the old branch intact). The **rename guard** (row 5) matches `git-branch.md` Step 4 — the two twins are kept in sync via the in-file comment, deliberately NOT delegated to `/git-branch` at runtime (inline wins on runtime token economy): rename when the current branch resolves to no change (`fab change resolve "$(git branch --show-current)"` fails — e.g., a disposable `wt create` name) **or to the SAME change being branched** (g8st — e.g., a worktree placeholder named with the change's own ID; previously this fell through to the create row and left a stray placeholder branch); when it matches a *different* change's branch (e.g., after `/fab-switch` away from an unpushed change), create a new branch via `git checkout -b` instead, leaving the other change's branch intact (known caveat: the new branch inherits the old change's HEAD). A **dirty-tree note** (g8st) is appended to the report line whenever `{dirty_count}` > 0 and the matched row runs `git checkout -b` or `git branch -m`: ` — note: {dirty_count} uncommitted change(s) carried over from {old_branch}` — non-blocking (warn, never stash-prompt: the step runs inside no-questions/orchestrated flows). The twins' one deliberate divergence lives outside the shared rows: fab-new derives `{dirty_count}` **excluding `fab/changes/{name}/`** — the change's own just-created artifacts (`intake.md`, `.status.yaml`, `.history.jsonl`) always exist uncommitted by Step 11 and would fire the note on every run; git-branch counts the full porcelain output. The git step is non-fatal — if not in a git repo, it warns and skips; if a git operation fails, it reports the error and the change remains activated. For create-without-activate behavior, use `/fab-draft` instead — as of 3xaj, `fab-draft.md` is also a **thin call-site over `_intake`**: it reads `_intake.md` and executes the Create-Intake Procedure with `{questioning-mode} = interactive`, then stops at `ready` (does NOT activate, does NOT create a git branch). Its Output is fab-new's minus the `Activated:`/`Branch:` lines, ending with the Activation Preamble `Next:` line; it adds no activation/git error rows. **The szxd-era "thin delta over fab-new" form (read `fab-new`'s SKILL.md, execute its Steps 0–9, prominently SKIP Steps 10–11) is retired by 3xaj** — and with it the "running activation/branch by momentum" warning that delta carried: those steps no longer live in any body `/fab-draft` executes (Steps 10–11 are `fab-new.md`'s tail, which `/fab-draft` never reads), so the hazard is structurally gone. The shared Steps 0–9 live in `_intake.md` (deliberately NOT in `_generation.md`, which holds only the artifact-generation mechanics `_intake` Step 5 delegates to). #### Re-Run Semantics (Idempotency) @@ -89,6 +93,31 @@ After generating `intake.md` and verifying the change type, `/fab-new` persists Loads: config, constitution, `docs/memory/index.md` (to understand the existing memory landscape). +### The `_intake` Shared Create-Intake Procedure (3xaj) + +As of 3xaj, the **pre-boundary intake-creation procedure** — `/fab-new` Steps 0–9 — lives once in the internal helper `src/kit/skills/_intake.md` (deployed to `.claude/skills/_intake/SKILL.md` by `fab sync`). It is the symmetric counterpart to `_pipeline.md`: where `_pipeline.md` is the shared *post*-intake orchestration bracket, `_intake.md` is the shared *pre*-intake orchestration body. Both follow the proven shape — a shared body parameterized by one knob, with call-site-specific tails staying in the call-site files. + +**The one knob is `{questioning-mode}`** (`interactive` | `promptless-defer`), applied only at Step 8 (SRAD-based question selection). Every other step (0–7, 9) is mode-invariant: + +- **`interactive`** — used by `/fab-new` and `/fab-draft`. Step 8 asks via SRAD (SRAD-driven question selection, no fixed cap, conversational mode when 5+ Unresolved). Byte-identical to the pre-3xaj inline fab-new Step 8. +- **`promptless-defer`** — used by `/fab-proceed`'s dispatch. Step 8 records each would-be-asked Unresolved decision as a deferred Unresolved row (Rationale `Deferred — promptless dispatch`) instead of asking, quoting the `_srad.md` § Critical Rule promptless-dispatch carve-out verbatim. Preserves `/fab-proceed`'s defer-and-surface contract exactly (see § Promptless Dispatch above). + +The fork is legitimately **invocation-level** (who resolves ambiguity: human-now vs. defer-and-surface) — exactly parallel to the post-boundary autonomy fork (interactive rework menu vs. autonomous auto-rework). + +**The three consumers became thin call-sites**, replacing the prior two inconsistent reuse mechanisms (both of which reached into `fab-new.md`): + +| Consumer | Pre-3xaj reuse | Post-3xaj | +|----------|----------------|-----------| +| `/fab-new` | inline owner of Steps 0–9 | `_intake(interactive)` + its Steps 10–11 activate/branch **tail** (stays at the call site) | +| `/fab-draft` | prose delta over fab-new's body ("execute its Steps 0–9, SKIP 10–11"; carried a momentum warning) | `_intake(interactive)`, stop at `ready` (no activate, no branch); **momentum warning gone** — the not-to-run steps no longer live in any body draft executes | +| `/fab-proceed` | dispatched the full `/fab-new` skill as a promptless subagent | dispatches `_intake(promptless-defer)`; create-new dispatch rows now chain `_intake` → `/fab-switch` → `/git-branch` for activate/branch parity (see § Promptless Dispatch) | + +**What stays at the call site (the EXTRACTION BOUNDARY — deliberately NOT over-extracted):** (1) `/fab-new`'s activate (Step 10) + branch (Step 11) tail — a *different responsibility* (make active + checked out vs. queue it), not a questioning-mode parameter; (2) `/fab-proceed`'s state-detection + relevance-assessment, which decides *whether* to call `_intake` at all. `_intake.md` is purely "given I've decided to create an intake, do it (Steps 0–9)." + +**Helper-declaration mechanics**: `_intake` is added to the `_preamble.md` § Skill Helper Declaration **Allowed-values allowlist** (now 7 values: `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake` — see [_shared/context-loading.md](../_shared/context-loading.md) § Skill Helper Declaration). `fab-new.md` and `fab-draft.md` declare `helpers: [_generation, _srad, _intake]` (they keep `_generation`/`_srad` declared directly — the `_pipeline` precedent, where consumers declare the underlying helpers rather than inheriting transitively). `_intake.md` itself carries **no** `helpers:` frontmatter (matching `_pipeline`/`_review`/`_generation`), referencing `_generation` (Step 5) and `_srad` (Steps 4, 8) in-body and relying on the consumer having loaded them. `/fab-proceed` keeps **no** `helpers:` — it dispatches `_intake` as a subagent prompt (the subagent reads the helper), exactly as it dispatched `/fab-new` before. Step 5 of `_intake` delegates to `_generation.md` § Intake Generation Procedure rather than inlining artifact generation; Step 4's lifted text is genericized ("the invoking skill" rather than "this `/fab-new` invocation"), structurally retiring `fab-draft`'s former per-consumer self-name instruction. + +> **Source-vs-deployed layout**: the canonical source is the **flat** `src/kit/skills/_intake.md` (every existing internal helper — `_pipeline.md`, `_generation.md`, `_srad.md`, `_review.md`, `_preamble.md` — is a flat `.md` in `src/kit/skills/`). The directory-per-skill `_intake/SKILL.md` form is the *deployed* copy `fab sync` writes under `.claude/skills/` (gitignored); skill *bodies* reference the deployed path (`.claude/skills/_intake/SKILL.md`) per the established `_preamble` convention. (This corrected the intake's Assumption #1, which had named the `_intake/SKILL.md` directory layout for the source tree — the canonical-source *intent* "never edit `.claude/skills/`" was preserved; only the layout detail was corrected at apply.) + ### `/fab-continue [] []` `/fab-continue` advances to the next pipeline stage — planning, implementation, review, or hydrate — and either generates the artifact or executes the stage's behavior. When called with a stage argument, it resets to that stage. When called with a change-name argument, it targets that change instead of the active one in `.fab-status.yaml` (transient — `.fab-status.yaml` is not modified). Both arguments can coexist; stage names are disambiguated first (fixed set of 6: `intake`, `apply`, `review`, `hydrate`, `ship`, `review-pr`), all other arguments are treated as change-name overrides. The pipeline flows intake → apply → review → hydrate → ship → review-pr. @@ -301,6 +330,12 @@ Calling `/fab-clarify` multiple times is safe — it refines the intake further **Rejected**: Keeping inline duplication — inevitable drift between the copies. Keeping a separate Spec Generation Procedure and `spec.md` — reintroduces the seam the merge removes and leaves an unread file (nothing reads `spec.md` programmatically once the gate moves to intake). *Introduced by*: 260210-wpay-extract-shared-generation-logic; *Updated by*: 260423-qszh-merge-tasks-checklist (Tasks + Checklist procedures → unified Plan Generation Procedure); 260601-j6cs-merge-spec-into-apply (Spec Generation Procedure folded into Plan Generation; `## Requirements` co-generated at apply entry) +### Pre-Boundary Intake-Creation Extracted to `_intake` (3xaj) +**Decision**: `/fab-new` Steps 0–9 (the "create an intake" procedure) are lifted into a single internal helper `_intake.md`, parameterized by one knob `{questioning-mode}` (`interactive` | `promptless-defer`) applied only at Step 8. The three consumers (`/fab-new`, `/fab-draft`, `/fab-proceed`) become thin call-sites. The activate/branch tail (`/fab-new` Steps 10–11) and `/fab-proceed`'s state-detection stay at the call site (the EXTRACTION BOUNDARY — do NOT over-extract). This is a pure restructure: behavioral parity is the bar, zero Go code touched. +**Why**: Steps 0–9 were duplicated across the pre-boundary family via **two inconsistent reuse mechanisms both pointing at `fab-new.md`** — `/fab-draft` was a fragile prose delta (carrying a "don't activate/branch by momentum" warning precisely because the steps it must NOT run shared the body it executed), and `/fab-proceed` dispatched the full `/fab-new` skill as a promptless subagent. So `fab-new.md` was simultaneously a skill AND the de-facto shared library two others reached into. Extraction mirrors the proven `_pipeline.md` shape (shared body + one knob + call-site tails) and completes the symmetry: one shared helper per pipeline phase — `_generation` (artifact mechanics), `_review` (review mechanics), `_pipeline` (post-intake orchestration), `_intake` (pre-intake orchestration). The momentum warning **evaporates** structurally (the not-to-run steps no longer live in any body `/fab-draft` executes), and Step 4's self-name references are genericized, retiring `/fab-draft`'s per-consumer self-name instruction. +**Rejected**: Transitive inheritance of `_generation`/`_srad` through `_intake` (the `_pipeline` precedent has consumers declare underlying helpers directly — kept). A `{self-name}` parameter for Step 4 (the text only needs to be invocation-agnostic, not invocation-named — user-endorsed genericize-over-parameterize). Over-extracting activate/branch or proceed's state-detection into `_intake` (recreates the dual-mode problem on the intake side). The flat canonical-source layout `src/kit/skills/_intake.md` was chosen over `_intake/SKILL.md` (the latter is the *deployed* form `fab sync` writes; every existing canonical helper is a flat `.md`). +*Introduced by*: 260613-3xaj-extract-intake-helper + ### Plan Generation Lives at Apply Entry, Not in a Separate Stage (qszh) **Decision**: The `tasks` stage is removed from the pipeline. Plan generation (writing `plan.md` with `## Tasks` + `## Acceptance`) is an entry sub-step of the apply skill, not a stage gate. Pipeline goes from 8 stages (intake → spec → tasks → apply → review → hydrate → ship → review-pr) to 7 stages (intake → spec → apply → review → hydrate → ship → review-pr). `progress.tasks` is dropped from `.status.yaml` entirely — no rename to `progress.plan`, since with no separate stage there is no key to populate. **Why**: The `tasks` stage was a no-decision gate. `/fab-continue` advanced spec → tasks → apply back-to-back; users never stopped at tasks. Every change paid the wall-time, token, and `.status.yaml` cost of a transition that had no decision content. Folding generation into apply makes drift between Tasks and Acceptance mechanically impossible (single skill call, single context window, single LLM, single file). The qszh collapse stopped at `tasks` and preserved the spec stage; j6cs later folded the spec stage in too (see "Spec Stage Merged into Apply" below), so apply entry now co-generates `## Requirements` alongside `## Tasks` + `## Acceptance`. @@ -359,6 +394,7 @@ Calling `/fab-clarify` multiple times is safe — it refines the intake further | Change | Date | Summary | |--------|------|---------| +| 260613-3xaj-extract-intake-helper | 2026-06-13 | **Pre-boundary intake-creation extracted to `_intake`** (Change B of the skill-architecture refactor trio): `/fab-new` Steps 0–9 lifted into the internal helper `src/kit/skills/_intake.md` (flat canonical source; deployed `.claude/skills/_intake/SKILL.md`), parameterized by one knob `{questioning-mode}` (`interactive` \| `promptless-defer`) applied only at Step 8. The three consumers became thin call-sites: `/fab-new` = `_intake(interactive)` + its Steps 10–11 activate/branch **tail**; `/fab-draft` = `_intake(interactive)` stop-at-`ready` (its szxd "thin delta over fab-new" form and the "don't activate/branch by momentum" warning **retired** — the not-to-run steps no longer live in any body draft executes); `/fab-proceed` dispatches `_intake(promptless-defer)` and its create-new rows now chain `_intake → /fab-switch → /git-branch` (the w7dp "drop `/git-branch` — fab-new Step 11 branches inline" claim **inverted**: `_intake` stops at `ready` without branching, so the chain is REQUIRED for parity). `fab-new`/`fab-draft` declare `helpers: [_generation, _srad, _intake]`; `_intake` carries no `helpers:` (references `_generation` Step 5, `_srad` Steps 4/8 in-body); `/fab-proceed` keeps no `helpers:` (dispatches the helper to a subagent). Step 4 genericized ("the invoking skill" not "this `/fab-new` invocation"); Step 5 delegates to `_generation.md` § Intake Generation Procedure. `_preamble.md` allowlist 6→7 values (+`_intake`). New § The `_intake` Shared Create-Intake Procedure + § Promptless Dispatch rewrite; new design decision "Pre-Boundary Intake-Creation Extracted to `_intake`". The `/fab-proceed.md` heading renamed `§ fab-new Dispatch` → `§ Create-Intake Dispatch`; line-46 cross-ref reconciled in lockstep. SPEC mirrors: new `SPEC-_intake.md` + `SPEC-fab-new`/`SPEC-fab-draft`/`SPEC-fab-proceed`/`SPEC-_preamble` updated. Pure skill restructure — zero Go code; behavioral parity is the bar. | | 260612-d9rs-docs-reality-sweep | 2026-06-12 | **`Next:` lines rule-derived** (skills-audit batch 5/5, root-cause guard): `fab-new.md` and `fab-draft.md` closing `Next:` lines now derive at runtime per `_preamble.md` § Lookup Procedure (intake-state row, default first — including `/fab-proceed`, which the hardcoded enumerations had drifted to omit); behavior unchanged, the derivation rule replaces the literal list (`fab-setup.md` already derived — verified in place). **`_generation` consumer groups corrected**: the partial's header/intro now names `/fab-continue` in **both** consumer groups (Intake Generation via its intake-`active` regeneration row + Plan Generation at apply entry) and drops the stale "auto-clarify" orchestration residue (removed in j6cs). | | 260612-w7dp-orchestrator-dispatch-review-pr-recovery | 2026-06-12 | **fab-new backlog-ID collision pre-check exact-ID anchored** (skills-audit batch 2/5, Themes 2+4): Step 3 now runs `fab resolve --id {id}` and compares the canonical 4-char ID for **equality** with `{id}` — a substring hit inside another change's slug (which resolves with a different canonical ID) no longer routes to resume and silently skips creation; the CLI `Change ID already in use` error stays the backlog-only safety net. **fab-proceed's promptless fab-new dispatch defined** (defer-and-surface): the dispatch prompt instructs no questions — would-be-asked SRAD Unresolved decisions are recorded as `Deferred — promptless dispatch` Unresolved rows, returned in the subagent result, and surfaced as informational lines before the `/fab-fff` delegation; the intake gate is the structural backstop (any Unresolved row scores 0.0 per `score.go`). `_srad.md`'s Critical Rule gained the matching **promptless-dispatch carve-out** (cross-referencing fab-proceed; wherever a user is reachable, MUST-ask applies unchanged). fab-proceed's two `/fab-new`-prefixed dispatch rows dropped the stale `/git-branch` chain (no-op since #322 — fab-new Step 11 creates the branch inline); `/fab-switch`-prefixed rows keep it. The ff/fff post-bail stop guidance is now the executable, override-aware `/fab-continue intake` → `/fab-clarify ` route (see [execution-skills.md](execution-skills.md)). New § Promptless Dispatch subsection; "SRAD Autonomy Framework" and "ID-Collision Re-Runs Route to Resume" design decisions updated. SPEC mirrors (SPEC-fab-new, SPEC-fab-proceed, SPEC-_srad, SPEC-fab-ff, SPEC-fab-fff) updated same-PR. | | 260612-g8st-git-state-hardening | 2026-06-12 | **fab-new Step 11 branch table hardened** (twin of `git-branch.md` Step 4, behavior-flagged): the first-match-wins table grew from 5 to **6 rows** — new row 3 checks out **remote-only** branches with `git checkout --track "origin/{name}"` instead of recreating a divergent local; the rename guard (row 5) now also renames when the current local-only branch resolves to the **same** change being branched (worktree-placeholder scenario — no more stray placeholder branch); a non-blocking **dirty-tree note** (` — note: {dirty_count} uncommitted change(s) carried over from {old_branch}`) is appended on `checkout -b`/`branch -m` rows (warn, never stash-prompt). fab-new-specific divergence: `{dirty_count}` excludes `fab/changes/{name}/` so the change's own just-created artifacts don't fire the note on every run (git-branch counts the full porcelain). Context commands extended (porcelain count, `origin/{name}` verify); keep-in-sync comments updated in both twins. git-pr's "already shipped" no-op re-run is now conditioned on an OPEN PR. `SPEC-fab-new.md` mirror updated same-PR. | diff --git a/docs/specs/skills.md b/docs/specs/skills.md index c375d6c0..2124578f 100644 --- a/docs/specs/skills.md +++ b/docs/specs/skills.md @@ -21,7 +21,7 @@ As of 1.10.0 the `spec` stage and the separate `spec.md` artifact are removed. R Every skill MAY declare additional helper files it needs to load via a `helpers:` frontmatter list. The agent reads each declared helper's `.claude/skills/{helper}/SKILL.md` after reading `_preamble` and before executing the skill body. -**Allowed values** (6): `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`. +**Allowed values** (7): `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake`. **Default**: omitted (or `[]`) — the skill loads only `_preamble`. @@ -45,7 +45,7 @@ helpers: [_generation, _review, _srad, _pipeline] | Skill | `helpers:` | |-------|------------| -| `fab-new`, `fab-draft` | `[_generation, _srad]` | +| `fab-new`, `fab-draft` | `[_generation, _srad, _intake]` | | `fab-ff`, `fab-fff` | `[_generation, _review, _srad, _pipeline]` (the shared bracket lives in `_pipeline.md`) | | `fab-continue` | `[_srad]` (+ `_generation`/`_review` stage-conditionally, in-body) | | `fab-clarify` | `[_srad]` | @@ -122,7 +122,7 @@ Adding a skill to the kit touches eight integration points. Work through all of 3. **`helpers:` declaration** — list any additional partials the skill needs (`_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`) in frontmatter; skills without the list load only `_preamble`. See § Skill Helpers. 4. **`Next:` line** — the skill's output ends with a state-derived `Next:` line per `_preamble.md` § Next Steps Convention (or documents an explicit opt-out, as `fab-discuss` and `fab-operator` do). 5. **Error Handling + Key Properties tables** — the body closes with the two standard tables (skill-specific errors only; idempotency, write surface, stage effects). -6. **SPEC mirror file** — create `docs/specs/skills/SPEC-{name}.md` (Summary + Flow + tool/sub-agent/bookkeeping tables). Partials keep their leading underscore in the SPEC filename (`SPEC-_review.md`, `SPEC-_preamble.md`, `SPEC-_generation.md`, `SPEC-_srad.md`, `SPEC-_pipeline.md`). **Exclusion policy**: the pure-reference partials `_cli-fab.md` and `_cli-external.md` carry no SPEC — their content mirrors the CLI surface rather than defining behavior, and the constitution already forces `_cli-fab.md` updates on every CLI change; a SPEC would be a third copy of the same tables. Every other skill file and behavioral partial gets a SPEC, and the constitution requires updating it on every skill edit. +6. **SPEC mirror file** — create `docs/specs/skills/SPEC-{name}.md` (Summary + Flow + tool/sub-agent/bookkeeping tables). Partials keep their leading underscore in the SPEC filename (`SPEC-_review.md`, `SPEC-_preamble.md`, `SPEC-_generation.md`, `SPEC-_srad.md`, `SPEC-_pipeline.md`, `SPEC-_intake.md`). **Exclusion policy**: the pure-reference partials `_cli-fab.md` and `_cli-external.md` carry no SPEC — their content mirrors the CLI surface rather than defining behavior, and the constitution already forces `_cli-fab.md` updates on every CLI change; a SPEC would be a third copy of the same tables. Every other skill file and behavioral partial gets a SPEC, and the constitution requires updating it on every skill edit. 7. **skills.md row** — add the skill's section to this file (and its `helpers:` row to § Skill Helpers when it declares any). 8. **Help grouping** — add the skill to `skillToGroupMap` in `src/go/fab/cmd/fab/fabhelp.go` so `/fab-help` lists it under the right group (unmapped skills fall into the "Other" bucket). diff --git a/docs/specs/skills/SPEC-_intake.md b/docs/specs/skills/SPEC-_intake.md new file mode 100644 index 00000000..3bb8868c --- /dev/null +++ b/docs/specs/skills/SPEC-_intake.md @@ -0,0 +1,94 @@ +# _intake + +## Summary + +Shared pre-boundary **Create-Intake Procedure** (fab-new Steps 0–9) used by three skills: `/fab-new`, `/fab-draft`, and `/fab-proceed`'s create-new dispatch (added in 260613-3xaj — extract-intake-helper). It completes the helper symmetry: the pre-boundary skill family (intake creation, which runs in the main session context because it needs the live conversation) now has a single shared body, mirroring the post-boundary `_pipeline.md`. Single authoritative source for Steps 0 (parse input) · 1 (generate slug) · 2 (gap analysis) · 3 (create change, incl. backlog/Linear collision pre-checks and `fab change new` flags) · 4 (conversation context mining — the load-bearing context-flush at the boundary) · 5 (generate `intake.md`, delegating to `_generation.md` § Intake Generation Procedure) · 6 (verify hook-owned `change_type`) · 7 (confidence, `fab score --stage intake`) · 8 (SRAD-based question selection — *the parameterized step*) · 9 (advance intake to `ready`). + +**Parameter** (bound by each consumer's own file): + +| Parameter | `/fab-new` | `/fab-draft` | `/fab-proceed` dispatch | +|-----------|-----------|--------------|-------------------------| +| `{questioning-mode}` — how Step 8 resolves ambiguity | `interactive` | `interactive` | `promptless-defer` | + +- **`interactive`** — Step 8 asks the user via SRAD (no fixed cap; conversational mode when 5+ Unresolved). The existing `/fab-new`/`/fab-draft` behavior. +- **`promptless-defer`** — Step 8 records each would-be-asked Unresolved decision as an Unresolved row with Rationale `Deferred — promptless dispatch` instead of asking, per the `_srad.md` § Critical Rule promptless-dispatch carve-out (quoted verbatim in the helper). The intake gate (`fab score` → 0.0 on any Unresolved row) is the structural backstop. + +This is the **only** behavioral fork in intake creation, and it is legitimately invocation-level (who resolves ambiguity: human-now vs. defer-and-surface) — exactly parallel to the post-boundary autonomy fork. + +**Extraction boundary** (do NOT over-extract — the procedure is purely "given I've decided to create an intake, do it, Steps 0–9"): +- **Activate (Step 10) + branch (Step 11)** stay as a tail in `fab-new.md` — a different responsibility (make the change active + checked out vs. queue it), not a questioning-mode parameter. +- **`/fab-proceed`'s state detection + relevance assessment** stay in `fab-proceed.md` — they decide *whether* to call `_intake`, not how to create one. + +**Self-name genericization** (260613-3xaj): the lifted Step 4 refers to "the invoking skill" / "this invocation" rather than "this `/fab-new` invocation", structurally retiring `fab-draft`'s former "read self-name mentions as `/fab-draft`" prose instruction. No `{self-name}` parameter — the text is invocation-agnostic, not invocation-named. + +**Helpers**: carries NO `helpers:` frontmatter. It references `_generation` (Step 5) and `_srad` (Step 8) in-body and relies on the consumer having loaded them — the consumer-declared model, matching `_pipeline`/`_review`/`_generation` (none of which carry `helpers:`). `/fab-new` and `/fab-draft` declare `helpers: [_generation, _srad, _intake]`; `/fab-proceed` declares none and dispatches `_intake` to a subagent that loads them. + +This is an internal partial (`user-invocable: false`, `disable-model-invocation: true`, `metadata: internal: true`) — never invoked directly. Canonical source is the flat `src/kit/skills/_intake.md`; `fab sync` deploys it to `.claude/skills/_intake/SKILL.md`. + +## Flow + +``` +Consumer (fab-new / fab-draft / fab-proceed dispatch) reads _intake.md with {questioning-mode} bound +│ +├─ Step 0: Parse Input +│ ├─ Linear ID? ──► MCP: mcp__claude_ai_Linear__get_issue +│ ├─ Backlog ID? ──► Read: fab/backlog.md (optional [ISSUE_ID] bracket) +│ └─ Natural language ──► use as-is +│ +├─ Step 1: Generate Slug (2-6 word kebab; SHALL NOT include Linear issue ID) +│ +├─ Step 2: Gap Analysis (existing mechanisms / scope concerns) +│ +├─ Step 3: Create Change +│ ├─ [backlog ID] collision pre-check: fab resolve --id {id} → EQUALITY with {id}; +│ │ on match, fab resolve --folder {id} names the existing change → route to resume +│ ├─ [Linear ID] collision pre-check: grep -lw "{ISSUE_ID}" fab/changes/*/.status.yaml +│ ├─ [existing non-archived change] → route to resume (/fab-switch + /fab-continue), STOP +│ │ (NL re-run intentionally creates a new change each run) +│ ├─ Bash: fab change new --slug --log-args [--change-id <4char> if backlog] +│ └─ [if Linear] Bash: fab status add-issue +│ +├─ Step 4: Conversation Context Mining (context-flush at the boundary) +│ └─ Extract decisions / rejected alternatives / constraints / specific values +│ → encode as Certain/Confident assumptions in the intake table +│ (genericized: "the invoking skill", not "this /fab-new invocation") +│ +├─ Step 5: Generate intake.md +│ └─ Delegate to _generation.md § Intake Generation Procedure ◄── HOOK CANDIDATE (intake write) +│ +├─ Step 6: Verify Change Type (hook-owned — the intake-write hook set it in Step 5) +│ ├─ Bash: grep '^change_type:' fab/changes/{name}/.status.yaml +│ └─ [only if wrong] Bash: fab status set-change-type +│ +├─ Step 7: Confidence (authoritative — intake is the sole scoring source) +│ └─ Bash: fab score --stage intake ◄── bookkeeping +│ +├─ Step 8: SRAD-Based Question Selection *(THE PARAMETERIZED STEP)* +│ ├─ {questioning-mode} = interactive → ask via SRAD (no cap; conversational at 5+ Unresolved) +│ └─ {questioning-mode} = promptless-defer → record each Unresolved as +│ "Deferred — promptless dispatch" row; return them for the dispatcher to surface +│ +└─ Step 9: Advance Intake to Ready + └─ Bash: fab status advance intake (then control returns to the call site) +``` + +### Tools used + +| Tool | Purpose | +|------|---------| +| Read | `_generation.md` (Step 5), `_srad.md` (Step 8 — both questioning modes), templates, backlog, project files | +| Write | `intake.md` (via the Intake Generation Procedure) | +| Bash | `fab change new`, `fab resolve --id`/`--folder` (collision pre-check), `fab status set-change-type` (override only), `fab score`, `fab status advance`, `fab status add-issue` | +| MCP (Linear) | Fetch issue details (optional path) | + +### Sub-agents + +None — the procedure runs inside the consuming skill's (or dispatched subagent's) context. Under `/fab-proceed` the *procedure itself* is what is dispatched as a subagent (per `fab-proceed.md` § Create-Intake Dispatch); it spawns no further sub-agents. + +### Bookkeeping commands (hook candidates) + +| Step | Command | Trigger | +|------|---------|---------| +| 6 | `fab status set-change-type` | Only if the hook-inferred type is wrong (the intake-write hook owns `change_type`) | +| 7 | `fab score --stage intake` | After intake.md write | +| 9 | `fab status advance` | After all intake work complete | diff --git a/docs/specs/skills/SPEC-_preamble.md b/docs/specs/skills/SPEC-_preamble.md index d1cf551e..24314fa3 100644 --- a/docs/specs/skills/SPEC-_preamble.md +++ b/docs/specs/skills/SPEC-_preamble.md @@ -12,7 +12,7 @@ Post-260418-or0o, `_preamble.md` contains four additional subsections inlined fr | Subsection | Purpose | Canonical source | |------------|---------|------------------| -| `## Skill Helper Declaration` | Documents the per-skill `helpers:` frontmatter field, its 6 allowed values (`_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`), semantics (read each helper after `_preamble`, before body), stage-conditional in-body loading (point-of-use reads — used by `fab-continue` for `_generation`/`_review`), and default (empty → load only `_preamble`). Explicitly states that `_naming` and `_cli-rk` are inlined (not allowed as values) and that `_preamble` is implicit. | `_preamble.md` itself | +| `## Skill Helper Declaration` | Documents the per-skill `helpers:` frontmatter field, its 7 allowed values (`_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake` — `_intake` added in 260613-3xaj for the pre-boundary Create-Intake Procedure consumed by `fab-new`/`fab-draft`), semantics (read each helper after `_preamble`, before body), stage-conditional in-body loading (point-of-use reads — used by `fab-continue` for `_generation`/`_review`), and default (empty → load only `_preamble`). Explicitly states that `_naming` and `_cli-rk` are inlined (not allowed as values) and that `_preamble` is implicit. | `_preamble.md` itself | | `## Naming Conventions` | Change folder pattern (`{YYMMDD}-{XXXX}-{slug}`), git branch naming (matches folder name), worktree directory naming (`{adjective}-{noun}`). The operator spawning rules moved to `_cli-external.md`'s wt section (260611-zc9m). | `_preamble.md` (inlined from the deleted `_naming.md`) | | `## Run-Kit (rk) Reference` | Silent-fail detection (`command -v rk`), iframe window creation, proxy URL pattern, server URL discovery at use-time, 4-step visual display recipe. | `_preamble.md` (inlined from the deleted `_cli-rk.md`) | | `## Common fab Commands` | Headline table of 6 most-used fab command families (`preflight`, `score`, `log command`, `change`, `resolve`, `status`) with purpose and canonical invocation form. Cross-references `_cli-fab` for exhaustive flag documentation. Its "Key behaviors" list includes the generic failure rule: any fab command that exits non-zero → STOP and surface stderr (deferring to explicit per-skill handling where a skill intentionally branches on a non-zero exit; `fab log command` can never trip the rule through internal failure — given valid usage it always exits 0, surfacing internal failures as a stderr warning only (cobra arg-count errors exit non-zero before RunE), so the former `2>/dev/null \|\| true` guard boilerplate is retired as of 260612-ye8r). The `fab change` row's canonical form is `fab resolve --folder` — the query flags exist only on top-level `fab resolve`; `fab change resolve` takes a bare `[]` (the former `fab change resolve --folder` canonical form was an invalid command, fixed in 260612-k4ge). | `_preamble.md` | @@ -56,7 +56,7 @@ Skill reads _preamble.md ├─ Skill Helper Declaration │ (defines the `helpers:` frontmatter field — │ allowed: _generation, _review, _cli-fab, -│ _cli-external, _srad, _pipeline; +│ _cli-external, _srad, _pipeline, _intake; │ plus stage-conditional in-body loading) │ ├─ Naming Conventions (inlined from _naming) diff --git a/docs/specs/skills/SPEC-fab-draft.md b/docs/specs/skills/SPEC-fab-draft.md index 8f4d2a76..c13ff341 100644 --- a/docs/specs/skills/SPEC-fab-draft.md +++ b/docs/specs/skills/SPEC-fab-draft.md @@ -2,20 +2,27 @@ ## Summary -Creates a new change intake without activating the change. Since 260611-szxd (f031) the skill file is a **thin delta over `/fab-new`**: its body instructs the agent to read `.claude/skills/fab-new/SKILL.md` and execute its Pre-flight, Arguments, and Steps 0–9 exactly as written there (self-name mentions read as `/fab-draft`), with four deltas — there is no duplicated copy of the shared steps. Used to queue changes for later without switching the active context. After creation, run `/fab-switch {name}` to activate. +Creates a new change intake without activating the change. Since 260613-3xaj (extract-intake-helper) the skill is a **thin call-site** over the shared `_intake` Create-Intake Procedure: its body reads `.claude/skills/_intake/SKILL.md` and executes the **Create-Intake Procedure** (Steps 0–9) with `{questioning-mode} = interactive`, then **stops at intake `ready`** — no activation (no Step 10), no git branch (no Step 11). Used to queue changes for later without switching the active context. After creation, run `/fab-switch {name}` to activate. -**Re-run contract** (Constitution III): inherited from fab-new Steps 0–9 — a backlog/Linear-ID re-run detects the existing non-archived change and routes to resume (`/fab-switch {name}` + `/fab-continue`) instead of erroring; a natural-language re-run intentionally creates a new change each run. Declared in the skill's Key Properties section (kept locally — it IS the delta). +Before 260613-3xaj, `fab-draft` was a thin *delta over `/fab-new`* (read `fab-new/SKILL.md`, execute its Steps 0–9, skip 10–11). That form carried a **momentum warning** — "running activation/branch by momentum is the known failure mode of this delta" — precisely because the steps it must NOT run (activate/branch) lived in the same `fab-new.md` body it executed. With Steps 0–9 lifted into `_intake.md`, the warning **evaporates**: `fab-draft` now reads `_intake.md` (Steps 0–9 only), and Steps 10–11 live solely in `fab-new.md`'s tail, which `fab-draft` never reads. There is no longer any body containing the not-to-run steps, so there is no momentum hazard. -**Helpers**: Declares `helpers: [_generation, _srad]` in frontmatter per `docs/specs/skills.md § Skill Helpers` (the executed fab-new steps need both). +**Re-run contract** (Constitution III): inherited from the shared procedure's Step 3 — a backlog/Linear-ID re-run detects the existing non-archived change and routes to resume (`/fab-switch {name}` + `/fab-continue`) instead of erroring; a natural-language re-run intentionally creates a new change each run. Declared in the skill's Key Properties section. -## Delta over /fab-new +**Helpers**: Declares `helpers: [_generation, _srad, _intake]` in frontmatter per `docs/specs/skills.md § Skill Helpers` (`_intake` added in 260613-3xaj; the executed Create-Intake Procedure references `_generation`/`_srad` in-body, so the consumer keeps declaring both directly — the `_pipeline` precedent). -| # | Delta | -|---|-------| -| 1 | **Step 9 tail**: after `fab status advance {name} intake`, the change is NOT activated — the user must run `/fab-switch {name}` (replaces fab-new's Step 9 closing sentence about Step 10) | -| 2 | **Skip Steps 10–11 entirely** — no `fab change switch`, no `git` command. Stated explicitly and prominently in the skill body (the known failure mode of the delta form is an agent running activation by momentum; the body instructs a re-check before any `fab change switch`/`git` invocation) | -| 3 | **Output**: fab-new's Output block minus the `Activated:` and `Branch:` lines; `Next:` per the Activation Preamble convention (`_preamble.md` § Activation Preamble — names `/fab-draft`; the intake-state command list is derived per § Lookup Procedure and includes `/fab-proceed`) | -| 4 | **Error Handling**: fab-new's table minus the activation/git rows | +## Difference from /fab-new + +`/fab-draft` and `/fab-new` run the **same** shared Create-Intake Procedure with the **same** `{questioning-mode} = interactive`. The only difference is the tail: + +| | `/fab-new` | `/fab-draft` | +|---|-----------|--------------| +| Steps 0–9 | `_intake(interactive)` | `_intake(interactive)` | +| Step 10 (activate) | Yes (`fab change switch`) | **No** — `.fab-status.yaml` symlink not created | +| Step 11 (git branch) | Yes | **No** | +| Output | with `Activated:`/`Branch:` lines | minus those lines; `Next:` per Activation Preamble | +| Error Handling | + activation/git rows | shared-procedure rows only (no activation/git — those steps never run) | + +The output `Next:` line uses the activation preamble: `/fab-switch {name} to make it active, then /fab-continue, /fab-ff, /fab-fff, /fab-proceed, or /fab-clarify` — the command list after "then" is the state table's intake row, derived per `_preamble.md` § Lookup Procedure (default first, not hardcoded). ## Flow @@ -23,21 +30,21 @@ Creates a new change intake without activating the change. Since 260611-szxd (f0 User invokes /fab-draft │ ├─ Read: _preamble.md (always-load layer: 7 project files) -├─ Read: .claude/skills/fab-new/SKILL.md ◄── the delta indirection +├─ Read: .claude/skills/_intake/SKILL.md (helpers: declaration — also _generation, _srad) │ -├─ Execute fab-new Pre-flight, Arguments, Steps 0–9 +├─ Steps 0–9: Create-Intake Procedure (_intake.md, {questioning-mode} = interactive) │ (parse input → slug → gap analysis → create change [collision check] │ → conversation mining → intake.md write ◄── HOOK → verify change type -│ → confidence score → SRAD questions → advance intake to ready) -│ — see SPEC-fab-new.md for the per-step tool detail +│ → confidence score → SRAD questions [interactive] → advance intake to ready) +│ — see SPEC-_intake.md for the per-step tool detail │ -└─ STOP after Step 9 (deltas 1–2: no activation, no git branch; +└─ STOP after Step 9 (no activation, no git branch; .fab-status.yaml symlink is NOT created) ``` ### Tools used -Same as `/fab-new` Steps 0–9 (see `SPEC-fab-new.md`): Read, Write (`intake.md`), Bash (`fab change new`, `fab status set-change-type` override-only, `fab score`, `fab status advance`, `fab status add-issue`), MCP (Linear, optional). No `fab change switch`, no git commands. +Same as the shared Create-Intake Procedure Steps 0–9 (see `SPEC-_intake.md`): Read, Write (`intake.md`), Bash (`fab change new`, `fab status set-change-type` override-only, `fab score`, `fab status advance`, `fab status add-issue`), MCP (Linear, optional). No `fab change switch`, no git commands. ### Sub-agents @@ -45,16 +52,4 @@ None. ### Bookkeeping commands (hook candidates) -| Step | Command | Trigger | -|------|---------|---------| -| 6 | `fab status set-change-type` | Only if the hook-inferred type is wrong (the intake-write hook owns `change_type`) | -| 7 | `fab score --stage intake` | After intake.md write | -| 9 | `fab status advance` | After all intake work complete | - -### Difference from /fab-new - -`/fab-draft` omits Steps 10 and 11 from `/fab-new`: -- **No Step 10** — change is not activated (`.fab-status.yaml` symlink is not created) -- **No Step 11** — git branch is not created - -The output `Next:` line uses the activation preamble: `/fab-switch {name} to make it active, then /fab-continue, /fab-ff, /fab-fff, /fab-proceed, or /fab-clarify` — the command list after "then" is the state table's intake row, derived per `_preamble.md` § Lookup Procedure (default first, not hardcoded). +All bookkeeping belongs to the shared procedure — see `SPEC-_intake.md` (Step 6 `fab status set-change-type` override-only, Step 7 `fab score --stage intake`, Step 9 `fab status advance`). `fab-draft` adds none (no Step 10/11). diff --git a/docs/specs/skills/SPEC-fab-new.md b/docs/specs/skills/SPEC-fab-new.md index 1426a1d2..4f17b1be 100644 --- a/docs/specs/skills/SPEC-fab-new.md +++ b/docs/specs/skills/SPEC-fab-new.md @@ -2,13 +2,15 @@ ## Summary -Creates a new change from a natural language description, Linear ticket, or backlog ID. Generates the change folder, writes `intake.md`, verifies the hook-inferred change type (the PostToolUse intake-write hook owns `change_type`; the skill overrides via `set-change-type` only if wrong), computes the authoritative intake confidence (no `indicative` flag — 1.10.0), advances intake to `ready`, activates the change, and creates the matching git branch. +Creates a new change from a natural language description, Linear ticket, or backlog ID. Since 260613-3xaj (extract-intake-helper) the skill is a **thin call-site** over the shared `_intake` Create-Intake Procedure: its body reads `.claude/skills/_intake/SKILL.md` and executes the **Create-Intake Procedure** (Steps 0–9) with `{questioning-mode} = interactive`, then runs its own **Steps 10–11 tail** (activate + git branch). The procedure generates the change folder, writes `intake.md`, verifies the hook-inferred change type (the PostToolUse intake-write hook owns `change_type`; the skill overrides via `set-change-type` only if wrong), computes the authoritative intake confidence (no `indicative` flag — 1.10.0), and advances intake to `ready`; `/fab-new`'s tail then activates the change and creates the matching git branch. -**Re-run contract** (Constitution III): a backlog/Linear-ID re-run detects the existing non-archived change and routes to resume (`/fab-switch {name}` + `/fab-continue`) instead of erroring; a natural-language re-run intentionally creates a new change each run. Declared in the skill's Key Properties section. +**Extraction boundary** (260613-3xaj): Steps 0–9 are NOT inlined here — they live in `_intake.md` (see `SPEC-_intake.md`). Only the activate (Step 10) + branch (Step 11) tail, Output block, and activation/git error rows stay in `fab-new.md` — a different responsibility (make the change active + checked out) that is NOT a questioning-mode parameter. + +**Re-run contract** (Constitution III): a backlog/Linear-ID re-run detects the existing non-archived change and routes to resume (`/fab-switch {name}` + `/fab-continue`) instead of erroring; a natural-language re-run intentionally creates a new change each run. Implemented in the shared procedure's Step 3; declared in the skill's Key Properties section. **Output ordering** (260612-c5tr): the Output template ends with the Assumptions summary as the final content block immediately before the `Next:` line, per `_srad.md` § Assumptions Summary Block (order: intake → Confidence → Activated → Branch → Assumptions → `Next:`); the block is omitted from output only when 0 assumptions were made. -**Helpers**: Declares `helpers: [_generation, _srad]` in frontmatter per `docs/specs/skills.md § Skill Helpers`. +**Helpers**: Declares `helpers: [_generation, _srad, _intake]` in frontmatter per `docs/specs/skills.md § Skill Helpers` (`_intake` added in 260613-3xaj; `_generation`/`_srad` kept declared directly, mirroring the `_pipeline` precedent where consumers declare underlying helpers alongside the orchestration helper). ## Flow @@ -16,65 +18,13 @@ Creates a new change from a natural language description, Linear ticket, or back User invokes /fab-new │ ├─ Read: _preamble.md (always-load layer: 7 project files) +├─ Read: .claude/skills/_intake/SKILL.md (helpers: declaration — also _generation, _srad) │ -├─ Step 0: Parse Input -│ ├─ Linear ID? ──► MCP: mcp__claude_ai_Linear__get_issue -│ ├─ Backlog ID? ──► Read: fab/backlog.md -│ └─ Natural language ──► use as-is -│ -├─ Step 1: Generate Slug -│ └─ (agent reasoning — no tools) -│ -├─ Step 2: Gap Analysis -│ └─ Read/Grep: existing skills, specs, memory -│ -├─ Step 3: Create Change -│ ├─ [backlog ID detected] collision check first: -│ │ Bash: fab resolve --id {id} → compare stdout for EQUALITY -│ │ with {id} (260612-w7dp — resolution is substring-based, so a -│ │ hit inside another change's slug resolves with a DIFFERENT -│ │ canonical ID and must NOT route to resume); on an exact match, -│ │ fab resolve --folder {id} names the existing change -│ ├─ [Linear ID detected] collision check first: -│ │ Bash: grep -lw "{ISSUE_ID}" fab/changes/*/.status.yaml -│ │ (-w word-anchors: DEV-123 won't match DEV-1234) -│ │ (Linear IDs never appear in folder names — they live in -│ │ .status.yaml issues arrays; the single-level glob -│ │ naturally excludes archive/) -│ ├─ [existing non-archived change found by either check] -│ │ → route to resume: report it + point to -│ │ /fab-switch {name} then /fab-continue — STOP -│ │ (no duplicate created; `Change ID already in use` -│ │ stays as safety net for backlog IDs only — Linear -│ │ re-runs pass no --change-id, so the scan is the -│ │ only collision guard) -│ │ (NL re-run intentionally creates a new change each run) -│ └─ Bash: fab change new --slug --log-args -│ │ [--change-id <4char> — only when a backlog ID -│ │ was detected in Step 0] -│ └─ (creates folder, .status.yaml from template) -│ └─ [if Linear] Bash: fab status add-issue -│ -├─ Step 4: Conversation Context Mining -│ └─ (agent reasoning — scans conversation history) -│ -├─ Step 5: Generate intake.md -│ ├─ Read: $(fab kit-path)/templates/intake.md -│ └─ Write: fab/changes/{name}/intake.md ◄── HOOK CANDIDATE -│ -├─ Step 6: Verify Change Type (hook-owned — the intake-write -│ │ hook already set it in Step 5's Write) -│ ├─ Bash: grep '^change_type:' fab/changes/{name}/.status.yaml -│ └─ [only if wrong] Bash: fab status set-change-type -│ -├─ Step 7: Confidence (authoritative — intake is the sole scoring source) -│ └─ Bash: fab score --stage intake ◄── bookkeeping (no indicative flag, 1.10.0) -│ -├─ Step 8: SRAD Questions -│ └─ (agent reasoning, possible user interaction) -│ -├─ Step 9: Advance Intake to Ready -│ └─ Bash: fab status advance intake +├─ Steps 0–9: Create-Intake Procedure (_intake.md, {questioning-mode} = interactive) +│ │ (parse input → slug → gap analysis → create change [collision check] +│ │ → conversation mining → intake.md write ◄── HOOK → verify change type +│ │ → confidence score → SRAD questions [interactive] → advance to ready) +│ └─ see SPEC-_intake.md for the per-step tool detail │ ├─ Step 10: Activate Change │ └─ Bash: fab change switch "{name}" @@ -108,13 +58,13 @@ User invokes /fab-new ### Tools used +Steps 0–9 tool usage now lives in the shared procedure — see `SPEC-_intake.md` § Tools used (Read templates/backlog/project files, Write `intake.md`, Bash `fab change new`/`fab resolve`/`fab status set-change-type`/`fab score`/`fab status advance`/`fab status add-issue`, MCP Linear). `fab-new`'s own tail (Steps 10–11) uses: + | Tool | Purpose | |------|---------| -| Read | Load preamble, templates, backlog, project files | -| Write | Write `intake.md` | -| Bash | `fab change new`, `fab resolve --id`/`--folder` (backlog-ID collision pre-check), `fab status set-change-type` (override only), `fab score`, `fab status advance`, `fab status add-issue`, `fab change switch` | -| Bash (git) | `git rev-parse --is-inside-work-tree`, `git branch --show-current`, `git status --porcelain` (dirty count, excluding `fab/changes/{name}/`), `git rev-parse --verify` (local + `origin/{name}`), `git config branch.{current}.remote`, `git checkout -b`, `git checkout`, `git checkout --track`, `git branch -m` | -| MCP (Linear) | Fetch issue details (optional path) | +| Read | `.claude/skills/_intake/SKILL.md` and the `helpers:` files (`_generation`, `_srad`); always-load layer | +| Bash | `fab change switch` (Step 10) | +| Bash (git) | `git rev-parse --is-inside-work-tree`, `git branch --show-current`, `git status --porcelain` (dirty count, excluding `fab/changes/{name}/`), `git rev-parse --verify` (local + `origin/{name}`), `git config branch.{current}.remote`, `git checkout -b`, `git checkout`, `git checkout --track`, `git branch -m` (Step 11) | ### Sub-agents @@ -122,10 +72,9 @@ None. ### Bookkeeping commands (hook candidates) +Steps 6/7/9 bookkeeping (`fab status set-change-type` override-only, `fab score --stage intake`, `fab status advance`) now belong to the shared procedure — see `SPEC-_intake.md`. `fab-new`'s tail: + | Step | Command | Trigger | |------|---------|---------| -| 6 | `fab status set-change-type` | Only if the hook-inferred type is wrong (the intake-write hook owns `change_type`) | -| 7 | `fab score --stage intake` | After intake.md write | -| 9 | `fab status advance` | After all intake work complete | -| 10 | `fab change switch` | After intake advanced to ready | +| 10 | `fab change switch` | After the Create-Intake Procedure advanced intake to ready | | 11 | `git checkout -b` / `git checkout` / `git checkout --track` / `git branch -m` | After change activated | diff --git a/docs/specs/skills/SPEC-fab-proceed.md b/docs/specs/skills/SPEC-fab-proceed.md index 9a2b9615..e460a5cf 100644 --- a/docs/specs/skills/SPEC-fab-proceed.md +++ b/docs/specs/skills/SPEC-fab-proceed.md @@ -2,7 +2,9 @@ ## Summary -Context-aware orchestrator — detects pipeline state via a 5-step detection pipeline, runs prefix steps (fab-new, fab-switch, git-branch) as subagents, then delegates to `/fab-fff` via the Skill tool. No arguments, no flags — infers everything from context. Idempotent — re-running detects completed steps and skips them. Reads `_preamble.md` (per skill convention) but skips running preflight and defers project-context loading to `/fab-fff`. **Per-stage model** (260613-l3ja): the prefix steps are NOT pipeline stages and take no `fab resolve-agent` resolution (they dispatch at the inherited model); per-stage model selection belongs to the delegated `/fab-fff`, which resolves each of its own stages per `_preamble.md` § Subagent Dispatch → Per-Stage Model Resolution. +Context-aware orchestrator — detects pipeline state via a 5-step detection pipeline, runs prefix steps (create-intake via `_intake`, fab-switch, git-branch) as subagents, then delegates to `/fab-fff` via the Skill tool. No arguments, no flags — infers everything from context. Idempotent — re-running detects completed steps and skips them. Reads `_preamble.md` (per skill convention) but skips running preflight and defers project-context loading to `/fab-fff`. **Per-stage model** (260613-l3ja): the prefix steps are NOT pipeline stages and take no `fab resolve-agent` resolution (they dispatch at the inherited model); per-stage model selection belongs to the delegated `/fab-fff`, which resolves each of its own stages per `_preamble.md` § Subagent Dispatch → Per-Stage Model Resolution. + +**Create-intake dispatch via `_intake` (260613-3xaj)**: the create-new path no longer dispatches the full `/fab-new` skill. It dispatches the shared `_intake` Create-Intake Procedure (read `.claude/skills/_intake/SKILL.md`) with `{questioning-mode} = promptless-defer` — `promptless-defer` IS the defer-and-surface contract (Unresolved decisions → `Deferred — promptless dispatch` rows, surfaced before `/fab-fff`; the intake gate is the structural backstop). `/fab-proceed`'s state-detection + relevance-assessment logic — *whether* to create an intake vs. activate an existing draft — STAYS in `fab-proceed.md` (it decides whether to call `_intake`, not how to create one). Because `_intake` stops at intake `ready` and does NOT activate or branch (those are `/fab-new`'s call-site tail, omitted here), the create-new dispatch-table rows now chain `_intake` → `/fab-switch` → `/git-branch` to reach the same end state (active change + matching branch) the prior full-`/fab-new` dispatch produced inline — a parity-preserving consequence of the extraction. Conversation context is the interpretive lens for any unactivated intakes: an unactivated intake is only resumed when it is clearly relevant to the current conversation or there is no competing conversation signal. An unrelated draft never hijacks the pipeline when the current conversation is about a different topic. @@ -50,14 +52,19 @@ User invokes /fab-proceed │ ├─ Prefix Dispatch (subagents) │ ├─ ┌──────────────────────────────────────────┐ -│ │ │ SUB-AGENT: /fab-new (if dispatched) │ -│ │ │ Read: .claude/skills/fab-new/SKILL.md │ +│ │ │ SUB-AGENT: _intake (if create-new path) │ +│ │ │ Read: .claude/skills/_intake/SKILL.md │ +│ │ │ Procedure: Create-Intake Steps 0–9 │ +│ │ │ {questioning-mode} = promptless-defer │ │ │ │ Input: synthesized description │ │ │ │ (from conversation ONLY — │ │ │ │ never from bypassed drafts) │ -│ │ │ Prompt: defer-and-surface — ask NO │ -│ │ │ questions; Unresolved → intake row │ +│ │ │ promptless-defer = ask NO questions; │ +│ │ │ Unresolved → intake row │ │ │ │ "Deferred — promptless dispatch" │ +│ │ │ Stops at intake ready (no activate/ │ +│ │ │ branch — chained as /fab-switch + │ +│ │ │ /git-branch below) │ │ │ │ Returns: created change folder name │ │ │ │ + deferred Unresolved decisions │ │ │ │ (surfaced before /fab-fff) │ @@ -85,13 +92,13 @@ User invokes /fab-proceed |----------------|-----------------|--------------|---------------------|-----------|--------------|----------| | Yes | Yes | — | — | — | (none) | /fab-fff | | Yes | No | — | — | — | /git-branch | /fab-fff | -| No | — | Substantive | None | — | /fab-new | /fab-fff | +| No | — | Substantive | None | — | _intake → /fab-switch → /git-branch | /fab-fff | | No | — | Substantive | ≥1 | Clearly relevant | /fab-switch → /git-branch | /fab-fff | -| No | — | Substantive | ≥1 | Not clearly relevant | /fab-new (emit bypass notes) | /fab-fff | +| No | — | Substantive | ≥1 | Not clearly relevant | _intake → /fab-switch → /git-branch (emit bypass notes) | /fab-fff | | No | — | Empty/thin | ≥1 | — | /fab-switch → /git-branch (pick by date-recency) | /fab-fff | | No | — | Empty/thin | None | — | (error — stop) | — | -The `/fab-new`-prefixed rows stopped chaining `/git-branch` in 260612-w7dp — `/fab-new` Step 11 creates or checks out the matching branch inline, so the trailing dispatch was a guaranteed no-op. `/fab-switch` rows keep it (switching activates but creates no branch). +**Create-new chaining (260613-3xaj)**: the create-new rows chain `_intake` → `/fab-switch` → `/git-branch`. Before 3xaj they dispatched the full `/fab-new` skill, whose Steps 10–11 activated + branched inline (so 260612-w7dp dropped the redundant trailing `/git-branch`). Now `_intake` stops at intake `ready` without activating or branching (the EXTRACTION BOUNDARY keeps activate/branch as `fab-new.md`'s tail), so `/fab-proceed` runs the dedicated `/fab-switch` (activate) + `/git-branch` prefix steps — which it already has — to reach the same end state. This makes the create-new rows symmetric with the relevant-intake rows. ### Asymmetric-Bias Rule @@ -106,9 +113,9 @@ Biasing toward the recoverable failure is the design intent. | Agent | When | Purpose | |-------|------|---------| -| /fab-new | Substantive + no intake, OR substantive + ≥1 intake but none clearly relevant | Create change from synthesized description (conversation only — never bypassed drafts). Promptless **defer-and-surface contract** (260612-w7dp): the prompt forbids questions; would-be-asked Unresolved decisions land in the intake's `## Assumptions` as `Deferred — promptless dispatch` rows, are returned in the result, and `/fab-proceed` surfaces them before delegating to `/fab-fff` (whose intake gate is the structural backstop — `fab score` returns 0.0 whenever any Unresolved row exists, so even a single deferral fails the gate). This is the `_srad.md` § Critical Rule promptless-dispatch carve-out — defer-and-surface satisfies the MUST-ask when no user is reachable | -| /fab-switch | Substantive + clearly relevant intake, OR empty/thin + ≥1 intake | Activate the selected change | -| /git-branch | Branch-mismatch row (active change, branch doesn't match) and the /fab-switch-prefixed rows — NOT the /fab-new rows (fab-new Step 11 creates the branch inline, 260612-w7dp) | Create or checkout the matching branch | +| _intake (Create-Intake Procedure) | Substantive + no intake, OR substantive + ≥1 intake but none clearly relevant | Create change from synthesized description (conversation only — never bypassed drafts) via the shared procedure (read `.claude/skills/_intake/SKILL.md`) with `{questioning-mode} = promptless-defer` (260613-3xaj — replaces the prior full-`/fab-new` dispatch). `promptless-defer` IS the **defer-and-surface contract** (260612-w7dp): asks no questions; would-be-asked Unresolved decisions land in the intake's `## Assumptions` as `Deferred — promptless dispatch` rows, are returned in the result, and `/fab-proceed` surfaces them before delegating to `/fab-fff` (whose intake gate is the structural backstop — `fab score` returns 0.0 whenever any Unresolved row exists, so even a single deferral fails the gate). This is the `_srad.md` § Critical Rule promptless-dispatch carve-out — defer-and-surface satisfies the MUST-ask when no user is reachable. The procedure stops at intake `ready` (no activate/branch), so the create-new rows chain `/fab-switch` + `/git-branch` after it | +| /fab-switch | Substantive + clearly relevant intake, OR empty/thin + ≥1 intake, OR after `_intake` on the create-new rows (activate the just-created change) | Activate the selected change | +| /git-branch | Branch-mismatch row (active change, branch doesn't match), the /fab-switch-prefixed relevant-intake rows, AND the `_intake`-prefixed create-new rows (since `_intake` stops at `ready` without branching — 260613-3xaj; before 3xaj the create-new path used full `/fab-new` whose Step 11 branched inline, so 260612-w7dp had dropped the trailing /git-branch there) | Create or checkout the matching branch | ### Bypass Notes diff --git a/fab/changes/260613-3xaj-extract-intake-helper/.history.jsonl b/fab/changes/260613-3xaj-extract-intake-helper/.history.jsonl index 6f4fa2fb..28b6c47d 100644 --- a/fab/changes/260613-3xaj-extract-intake-helper/.history.jsonl +++ b/fab/changes/260613-3xaj-extract-intake-helper/.history.jsonl @@ -8,3 +8,14 @@ {"delta":"+0.0","event":"confidence","score":3.2,"trigger":"calc-score","ts":"2026-06-13T12:21:29Z"} {"delta":"+0.0","event":"confidence","score":3.2,"trigger":"calc-score","ts":"2026-06-13T12:21:34Z"} {"delta":"+0.0","event":"confidence","score":3.2,"trigger":"calc-score","ts":"2026-06-13T12:21:34Z"} +{"cmd":"fab-fff","event":"command","ts":"2026-06-13T12:34:53Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"apply","ts":"2026-06-13T12:35:10Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"review","ts":"2026-06-13T12:48:42Z"} +{"event":"review","result":"failed","ts":"2026-06-13T12:57:48Z"} +{"action":"re-entry","driver":"fab-fff","event":"stage-transition","stage":"apply","ts":"2026-06-13T12:57:48Z"} +{"action":"re-entry","driver":"fab-fff","event":"stage-transition","stage":"review","ts":"2026-06-13T13:01:17Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"hydrate","ts":"2026-06-13T13:07:37Z"} +{"event":"review","result":"passed","ts":"2026-06-13T13:07:37Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"ship","ts":"2026-06-13T13:16:01Z"} +{"action":"enter","driver":"git-pr","event":"stage-transition","stage":"review-pr","ts":"2026-06-13T13:17:30Z"} +{"event":"review","result":"passed","ts":"2026-06-13T13:23:34Z"} diff --git a/fab/changes/260613-3xaj-extract-intake-helper/.status.yaml b/fab/changes/260613-3xaj-extract-intake-helper/.status.yaml index ef871806..75f78a0a 100644 --- a/fab/changes/260613-3xaj-extract-intake-helper/.status.yaml +++ b/fab/changes/260613-3xaj-extract-intake-helper/.status.yaml @@ -5,17 +5,17 @@ created_by: sahil-noon change_type: fix issues: [] progress: - intake: ready - apply: pending - review: pending - hydrate: pending - ship: pending - review-pr: pending + intake: done + apply: done + review: done + hydrate: done + ship: done + review-pr: done plan: - generated: false - task_count: 0 - acceptance_count: 0 - acceptance_completed: 0 + generated: true + task_count: 11 + acceptance_count: 17 + acceptance_completed: 17 confidence: certain: 3 confident: 6 @@ -29,7 +29,27 @@ confidence: competence: 85.6 disambiguation: 83.1 stage_metrics: - intake: {started_at: "2026-06-13T11:24:43Z", driver: fab-new, iterations: 1} -prs: [] + intake: {started_at: "2026-06-13T11:24:43Z", driver: fab-new, iterations: 1, completed_at: "2026-06-13T12:35:10Z"} + apply: {started_at: "2026-06-13T12:57:48Z", driver: fab-fff, iterations: 2, completed_at: "2026-06-13T13:01:17Z"} + review: {started_at: "2026-06-13T13:01:17Z", driver: fab-fff, iterations: 2, completed_at: "2026-06-13T13:07:37Z"} + hydrate: {started_at: "2026-06-13T13:07:37Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-13T13:16:01Z"} + ship: {started_at: "2026-06-13T13:16:01Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-13T13:17:30Z"} + review-pr: {started_at: "2026-06-13T13:17:30Z", driver: git-pr, iterations: 1, completed_at: "2026-06-13T13:23:34Z"} +prs: + - https://github.com/sahil87/fab-kit/pull/412 +true_impact: + added: 0 + deleted: 0 + net: 0 + excluding: + added: 0 + deleted: 0 + net: 0 + tests: + added: 0 + deleted: 0 + net: 0 + computed_at: "2026-06-13T13:16:01Z" + computed_at_stage: hydrate # true_impact: lazily created on first apply-finish (no placeholder here). -last_updated: 2026-06-13T12:21:34Z +last_updated: 2026-06-13T13:23:34Z diff --git a/fab/changes/260613-3xaj-extract-intake-helper/plan.md b/fab/changes/260613-3xaj-extract-intake-helper/plan.md new file mode 100644 index 00000000..78032f68 --- /dev/null +++ b/fab/changes/260613-3xaj-extract-intake-helper/plan.md @@ -0,0 +1,218 @@ +# Plan: Extract `_intake.md` Shared Helper for Pre-Boundary Intake Creation + +**Change**: 260613-3xaj-extract-intake-helper +**Intake**: `intake.md` + +## Requirements + + + +### Helper: `_intake` Create-Intake Procedure + +#### R1: New `_intake` internal helper exists at the canonical source path +A new internal helper SHALL be created at `src/kit/skills/_intake.md` (flat canonical-source layout — see Design Decisions / Assumptions row 1), carrying frontmatter `name: _intake`, `user-invocable: false`, `disable-model-invocation: true`, `metadata: internal: true`, and a `description:` describing the Create-Intake Procedure (Steps 0–9, parameterized by `{questioning-mode}`). It MUST NOT be created or edited under `.claude/skills/` (gitignored `fab sync` output). + +- **GIVEN** the constitution names `src/kit/` canonical and every existing internal helper is a flat `src/kit/skills/_*.md` file +- **WHEN** the helper is authored +- **THEN** `src/kit/skills/_intake.md` exists with the internal-helper frontmatter matching `_pipeline.md`/`_review.md`/`_generation.md`/`_srad.md` +- **AND** no file is written under `.claude/skills/` + +#### R2: `_intake` body defines Steps 0–9 parameterized by the single `{questioning-mode}` knob +The helper body SHALL define the "Create-Intake Procedure" = fab-new Steps 0–9, with exactly ONE parameter `{questioning-mode}` (`interactive` | `promptless-defer`) applied only at Step 8. Steps 0–7 and 9 SHALL be mode-invariant. The lifted text MUST be behaviorally identical to current fab-new Steps 0–9. + +- **GIVEN** the intake's emphatic EXTRACTION BOUNDARY ("do NOT over-extract"; `{questioning-mode}` is the SOLE fork) +- **WHEN** the body is written +- **THEN** Steps 0, 1, 2, 3, 4, 5, 6, 7, 9 are reproduced from fab-new with no behavior change +- **AND** Step 8 branches: `interactive` → SRAD-driven question selection (no fixed cap, conversational mode when 5+ Unresolved); `promptless-defer` → record each would-be-asked Unresolved decision as a deferred Unresolved row per the `_srad.md` § Critical Rule promptless-dispatch carve-out (quoted verbatim) +- **AND** Step 5 references the already-extracted `_generation.md` § Intake Generation Procedure rather than inlining it + +#### R3: Step 4 self-name references are genericized in the lifted body +The lifted Step 4 (Conversation Context Mining) SHALL refer to the invoking skill generically (e.g., "the invoking skill" / "this invocation") rather than "this `/fab-new` invocation". No `{self-name}` parameter SHALL be introduced. Step 4 SHALL also carry the finding's framing as the load-bearing context-flush at the boundary. + +- **GIVEN** Assumption #9 (user-endorsed: genericize over parameterize) and `fab-draft`'s current "read self-name as `/fab-draft`" instruction +- **WHEN** Step 4 is lifted +- **THEN** the text reads invocation-agnostically and `fab-draft`'s per-consumer self-name instruction is structurally retired +- **AND** no `{self-name}` parameter exists in the helper + +#### R4: `_intake` carries no `helpers:` frontmatter +`_intake.md` SHALL NOT declare a `helpers:` frontmatter list; it references `_generation` and `_srad` in-body and relies on the consumer having loaded them (the consumer-declared model, matching every existing internal helper). + +- **GIVEN** Assumption #5 and that `_pipeline`/`_review`/`_generation` carry no `helpers:` +- **WHEN** the frontmatter is authored +- **THEN** `_intake.md` has only `name`/`description`/`user-invocable`/`disable-model-invocation`/`metadata` + +### Consumer: `fab-new` thin call-site + retained tail + +#### R5: `fab-new.md` becomes `_intake(interactive)` + activate/branch tail +`fab-new.md` SHALL replace its inline Steps 0–9 with a reference to read `_intake.md` and execute the Create-Intake Procedure with `{questioning-mode} = interactive`. Step 10 (activate), Step 11 (the full git-branch table incl. verify-in-repo guard, the 6-row evaluate-in-order table, the fab-new-specific `{dirty_count}` derivation excluding `fab/changes/{name}/`, the dirty-tree note, the keep-in-sync-with-git-branch.md comment), the Output block (with `Activated:`/`Branch:` lines), and the activation/git Error Handling rows SHALL STAY in `fab-new.md`. `fab-new.md`'s `helpers:` SHALL add `_intake` while keeping `_generation` and `_srad`. + +- **GIVEN** the EXTRACTION BOUNDARY (activate/branch is a different responsibility, stays at the call site) and Assumption #4 +- **WHEN** `fab-new.md` is rewired +- **THEN** Steps 0–9 are replaced by an `_intake(interactive)` call-site reference +- **AND** Steps 10–11, Output, and activation/git error rows remain verbatim +- **AND** frontmatter declares `helpers: [_generation, _srad, _intake]` + +### Consumer: `fab-draft` thin call-site, momentum warning evaporates + +#### R6: `fab-draft.md` becomes `_intake(interactive)`, stop at ready, momentum warning removed +`fab-draft.md` SHALL replace its prose-delta body with a reference to read `_intake.md` and execute the Create-Intake Procedure with `{questioning-mode} = interactive`; do NOT activate; do NOT create a git branch; stop after Step 9. The delta #2 "don't run Steps 10–11 by momentum" warning SHALL be removed (those steps no longer live in the body draft executes). `fab-draft` SHALL keep its own Output block (fab-new's minus `Activated:`/`Branch:`, ending with the Activation Preamble `Next:` line), Key Properties table, and Error Handling (no activation/git rows). Its `helpers:` SHALL add `_intake` while keeping `_generation` and `_srad`. + +- **GIVEN** delta #2's warning exists only because the not-to-run steps shared the executed body +- **WHEN** `fab-draft.md` is rewired to call `_intake(interactive)` +- **THEN** the momentum warning is gone and the body references `_intake` instead of `fab-new.md`'s steps +- **AND** the Activation-Preamble `Next:` line and no-activation/git error posture are preserved +- **AND** frontmatter declares `helpers: [_generation, _srad, _intake]` + +### Consumer: `fab-proceed` dispatch reroute, state-detection stays + +#### R7: `fab-proceed.md`'s fab-new subagent dispatch reroutes to `_intake(promptless-defer)` +The subagent today dispatched as "`/fab-new` with a promptless defer-and-surface contract" SHALL instead dispatch the `_intake` Create-Intake Procedure with `{questioning-mode} = promptless-defer`. `/fab-proceed`'s state-detection (Steps 1–5, dispatch table), Relevance Assessment, asymmetric-bias rule, bypass notes, fab-switch/git-branch dispatch, Conversation Context Synthesis, and the terminal `/fab-fff` delegation SHALL all STAY unchanged. The defer-and-surface behavior is preserved (it is now `{questioning-mode}`-encoded in the called helper rather than a per-dispatch prompt contract over `/fab-new`). + +- **GIVEN** the EXTRACTION BOUNDARY (proceed's state-detection decides *whether* to call `_intake`, stays at the call site) +- **WHEN** the fab-new Dispatch subsection is rewired +- **THEN** it dispatches `_intake(promptless-defer)` for the create-an-intake sub-operation +- **AND** the deferred-Unresolved surfacing + intake-gate backstop contract is preserved verbatim +- **AND** all state-detection / relevance / synthesis logic is unchanged + +### Allowlist + Specs + +#### R8: `_preamble.md` helpers Allowed-values allowlist gains `_intake` +The canonical `src/kit/skills/_preamble.md` § Skill Helper Declaration "Allowed values" line SHALL be updated from `_generation, _review, _cli-fab, _cli-external, _srad, _pipeline` to additionally include `_intake`. + +- **GIVEN** Assumption #7 and that adding a new declared helper requires allowlist membership +- **WHEN** `_preamble.md` is edited +- **THEN** the Allowed-values line lists `_intake` + +#### R9: All five constitution-mandated SPEC-* files are reconciled with the skill edits +Per the constitution ("Changes to skill files MUST update the corresponding `docs/specs/skills/SPEC-*.md`"): a new `docs/specs/skills/SPEC-_intake.md` SHALL be created (mirroring `SPEC-_pipeline.md`/`SPEC-_generation.md` format), and `SPEC-fab-new.md`, `SPEC-fab-draft.md`, `SPEC-fab-proceed.md`, `SPEC-_preamble.md` SHALL be modified to reflect the rewires. + +- **GIVEN** the constitution's SPEC-update mandate and Assumption #6 +- **WHEN** the skill edits land +- **THEN** the five SPEC files mirror the skill-source changes and cross-references resolve + +### Non-Goals + +- Editing or generating any file under `.claude/skills/` — those are `fab sync` deployed copies (out of scope; the diff touches canonical sources + specs only). +- Running `fab sync` — explicitly out of scope for this diff. +- Touching any Go source or test — zero Go code (the finding confirms the state machine is already caller-agnostic). +- Changing intake-creation *behavior* — this is a pure restructure; behavioral parity is the bar. +- Hydrating `docs/memory/` — that is the hydrate stage's job (Affected Memory is resolved at hydrate). + +### Design Decisions + +1. **Canonical-source path is flat `src/kit/skills/_intake.md`, NOT `src/kit/skills/_intake/SKILL.md`**: *Why*: every existing canonical internal helper (`_pipeline.md`, `_generation.md`, `_srad.md`, `_review.md`, `_preamble.md`) is a flat `.md` file in `src/kit/skills/`; the `{name}/SKILL.md` directory-per-skill layout is the *deployed* form produced by `fab sync` under `.claude/skills/`. *Rejected*: `src/kit/skills/_intake/SKILL.md` (the intake's Assumption #1 path) — it would be inconsistent with the actual canonical source tree and would not deploy correctly. The canonical-source *intent* of Assumption #1 (never edit `.claude/skills/`) is fully honored; only the layout detail is corrected per apply-time evidence (Assumptions row 1). +2. **Mirror the `_pipeline.md` shape exactly**: shared body parameterized by one knob (`{questioning-mode}`, parallel to `_pipeline`'s `{driver}`/`{terminal}`); call-site-specific tails stay in the call-site files; consumers declare the helper via `helpers:` frontmatter AND keep declaring the underlying helpers (`_generation`, `_srad`) directly. *Why*: proven symmetry; low blast radius. *Rejected*: transitive inheritance of `_generation`/`_srad` through `_intake` — `_pipeline` precedent has consumers declare underlying helpers directly (Assumptions rows 4, 5). +3. **`fab-proceed` keeps declaring no `helpers:`**: it dispatches `_intake` as a subagent prompt (the subagent reads the helper), not as a frontmatter pre-load — same as today where it dispatched `/fab-new`. *Why*: proceed is an orchestrator that loads nothing for itself; the dispatched subagent loads what it needs. (Assumptions row 10.) + +## Tasks + +### Phase 1: Create the helper + +- [x] T001 Create `src/kit/skills/_intake.md` with internal-helper frontmatter (`name: _intake`, `user-invocable: false`, `disable-model-invocation: true`, `metadata: internal: true`, descriptive `description:`) and a body defining the Create-Intake Procedure = Steps 0–9, parameterized by `{questioning-mode}` at Step 8, Step 4 genericized, Step 5 referencing `_generation.md`, the `_srad.md` carve-out quoted verbatim for `promptless-defer`. No `helpers:` frontmatter. + +### Phase 2: Rewire consumers + +- [x] T002 Rewire `src/kit/skills/fab-new.md`: replace inline Steps 0–9 with an `_intake(interactive)` call-site reference; keep Steps 10–11, Output, activation/git Error Handling, Key Properties, trailing `Next:`; add `_intake` to `helpers:` (now `[_generation, _srad, _intake]`). +- [x] T003 Rewire `src/kit/skills/fab-draft.md`: replace the prose-delta body with an `_intake(interactive)` call-site reference (do not activate, no git branch, stop after Step 9); remove delta #2's momentum warning; keep Output (Activation-Preamble `Next:`), Key Properties, Error Handling; add `_intake` to `helpers:` (now `[_generation, _srad, _intake]`). +- [x] T004 Rewire `src/kit/skills/fab-proceed.md` § fab-new Dispatch: reroute the subagent dispatch to `_intake(promptless-defer)`; keep all state-detection / relevance / synthesis / terminal-delegation logic unchanged. Dispatch-table create-new rows chain `_intake` → `/fab-switch` → `/git-branch` for activate/branch parity (Assumption 11). + +### Phase 3: Allowlist + +- [x] T005 Update `src/kit/skills/_preamble.md` § Skill Helper Declaration "Allowed values" line to include `_intake` (and update the inline `helpers: [...]` example comment if it would otherwise drift). + +### Phase 4: Specs (constitution-mandated) + +- [x] T006 [P] Create `docs/specs/skills/SPEC-_intake.md` mirroring `SPEC-_pipeline.md`/`SPEC-_generation.md` format (Summary, parameter, Flow, sub-agents, bookkeeping). +- [x] T007 [P] Modify `docs/specs/skills/SPEC-fab-new.md` to reflect the thin-call-site rewire + retained activate/branch tail + `_intake` in helpers. +- [x] T008 [P] Modify `docs/specs/skills/SPEC-fab-draft.md` to reflect the `_intake(interactive)` rewire + removed momentum warning + `_intake` in helpers. +- [x] T009 [P] Modify `docs/specs/skills/SPEC-fab-proceed.md` to reflect the `_intake(promptless-defer)` dispatch reroute + dispatch-table chaining. +- [x] T010 [P] Modify `docs/specs/skills/SPEC-_preamble.md` to reflect the updated 7-value helpers allowlist. + +### Phase 5: Verification + +- [x] T011 Re-read all edited/created files; confirm cross-reference integrity (`_intake` references resolve, allowlist matches, the five specs mirror the skill edits), the EXTRACTION BOUNDARY held (no over-extraction of activate/branch or proceed state-detection), and behavioral parity vs. current fab-new Steps 0–9 (byte-level diff of mode-invariant Steps 0/1/2/3/6/7 = identical; Steps 4/8/9 differ only as intended). + +## Execution Order + +- T001 blocks T002, T003, T004 (consumers reference the helper). +- T005 is independent of T001–T004. +- T006–T010 are [P] (independent spec files) but should follow T001–T005 so they mirror the final skill text. +- T011 runs last. + +## Acceptance + +### Functional Completeness + +- [x] A-001 R1: `src/kit/skills/_intake.md` exists with correct internal-helper frontmatter; nothing was written under `.claude/skills/`. (Frontmatter verified: `name: _intake`, `user-invocable: false`, `disable-model-invocation: true`, `metadata.internal: true`, descriptive `description:`. `git status` shows no `.claude/skills/` entries.) +- [x] A-002 R2: `_intake.md` body defines Steps 0–9 with `{questioning-mode}` as the sole parameter applied at Step 8; Steps 0–7 and 9 are mode-invariant; Step 5 references `_generation.md`. (Diff vs HEAD fab-new confirms Steps 0/1/2/3/5/6/7 byte-identical; Step 8 is the only branched step; Steps 4/9 differ only by intended genericization. Step 5 reads "Follow the Intake Generation Procedure (`_generation.md`)".) +- [x] A-003 R3: Step 4 in `_intake.md` is genericized (no "this `/fab-new` invocation"); no `{self-name}` parameter exists. (Step 4 reads "preceded the invoking skill's invocation" and "identical to a cold invocation"; the context-flush framing paragraph is present; no `{self-name}` token anywhere in the file.) +- [x] A-004 R4: `_intake.md` declares no `helpers:` frontmatter. (Frontmatter has only name/description/user-invocable/disable-model-invocation/metadata.) +- [x] A-005 R5: `fab-new.md` references `_intake(interactive)`, retains Steps 10–11 + Output + activation/git error rows, and declares `helpers: [_generation, _srad, _intake]`. (Step 11 git-branch table verbatim incl. the verify-in-repo guard, 6-row evaluate-in-order table, `{dirty_count}` derivation excluding `fab/changes/{name}/`, dirty-tree note, keep-in-sync comment; Output has Activated:/Branch: lines; error table keeps `fab change switch`/not-in-git-repo/`git checkout` rows.) +- [x] A-006 R6: `fab-draft.md` references `_intake(interactive)`, stops at ready, has NO momentum warning, retains its Activation-Preamble `Next:` and no-git/activation error posture, and declares `helpers: [_generation, _srad, _intake]`. (HEAD delta #2's "running activation or branch creation by momentum is the known failure mode" string is gone; line 47 explains why no hazard exists; Error Handling explicitly adds no activation/git rows.) +- [x] A-007 R7: `fab-proceed.md` dispatches `_intake(promptless-defer)`; state-detection / relevance / synthesis / terminal delegation are unchanged. (Steps 1–5 detection, Relevance Assessment, asymmetric-bias, Conversation Context Synthesis, terminal `/fab-fff` Skill-tool delegation all preserved; create-new rows chain `_intake → /fab-switch → /git-branch` per Assumption 11 — verified necessary: HEAD `/fab-new` rows activated+branched inline via Steps 10–11, and `/fab-switch` runs the same `fab change switch` (fab-switch.md L41) as fab-new Step 10, so the chain reaches the identical end state.) +- [x] A-008 R8: `_preamble.md` Allowed-values line lists `_intake` (7 values total). (`_preamble.md` L105: `_generation, _review, _cli-fab, _cli-external, _srad, _pipeline, _intake`.) +- [x] A-009 R9: All five SPEC files exist and mirror the skill edits. (`SPEC-_intake.md` new + `SPEC-fab-new`/`SPEC-fab-draft`/`SPEC-fab-proceed`/`SPEC-_preamble` modified; each mirrors its skill's rewire and cross-references resolve to `SPEC-_intake.md`.) + +### Behavioral Correctness + +- [x] A-010 R2: `interactive` mode reproduces current fab-new/fab-draft Step 8 behavior (SRAD-driven, no cap, conversational at 5+ Unresolved); `promptless-defer` reproduces fab-proceed's defer-and-surface contract (the `_srad.md` carve-out is quoted, not redefined). (`_intake.md` Step 8 interactive bullet is verbatim-equivalent to HEAD fab-new Step 8; the promptless-defer blockquote is a verbatim substring of `_srad.md` § Critical Rule "Promptless-dispatch carve-out" — confirmed by direct comparison.) +- [x] A-011 R5 R6 R7: Diffing the lifted Steps 0–9 against current fab-new.md shows zero intended behavior change for interactive consumers; fab-proceed's promptless contract is byte-equivalent in effect. (`diff` of HEAD fab-new Steps 0–9 vs `_intake.md` Steps 0–9: only Step 4 framing+genericization, Step 8 parameterization, and Step 9 call-site-agnostic closing differ — all intended. Steps 0/1/2/3/5/6/7 identical.) + +### Scenario Coverage + +- [x] A-012 R6: `fab-draft`'s removed momentum warning is justified — Steps 10–11 are not present in the body draft executes (they live only in fab-new.md's tail, which draft never reads). (fab-draft.md L38 reads `_intake.md` which contains Steps 0–9 only; L47 states the hazard is gone because the not-to-run steps live solely in fab-new.md's tail. Confirmed Steps 10–11 are absent from `_intake.md`.) + +### Edge Cases & Error Handling + +- [x] A-013 R5 R6: fab-new keeps activation/git error rows; fab-draft keeps the activation/git rows removed — error-handling tables match each consumer's retained responsibilities. (fab-new.md error table retains `fab change switch` (Step 10), not-in-git-repo (Step 11), and `git checkout`/`git branch` (Step 11) rows; fab-draft.md L61 explicitly states it adds no activation/git rows because those steps never run.) + +### Code Quality + +- [x] A-014 Pattern consistency: `_intake.md` and `SPEC-_intake.md` follow the established `_pipeline`/`_review`/`_generation` helper and spec conventions (frontmatter shape, parameter note, Flow/sub-agents/bookkeeping sections). (`_intake.md` frontmatter matches the internal-helper shape; intro blockquote documents the `{questioning-mode}` knob and helper model like `_pipeline.md`. `SPEC-_intake.md` has Summary + parameter table + Flow diagram + Tools used + Sub-agents + Bookkeeping commands — same structure as `SPEC-_pipeline.md`/`SPEC-_generation.md`.) +- [x] A-015 No unnecessary duplication: Steps 0–9 now live in exactly one place (`_intake.md`); consumers reference it rather than re-stating the steps; Step 5 references `_generation.md` rather than inlining intake generation. (Inline Steps 0–9 removed from fab-new.md (-89 lines net structure) and fab-draft's delta body replaced with a call-site reference; the lifted steps exist only in `_intake.md`. Step 5 delegates to `_generation.md` § Intake Generation Procedure, not inlined.) + +### Documentation Accuracy + + + +- [x] A-016: Skill prose accurately describes the new structure (no stale references to "fab-new's Steps 0–9" from draft/proceed; no claim that the helper lives at a `.claude/skills/` or `_intake/SKILL.md` path in the canonical source). (Within the edited skill files this holds: fab-draft no longer says "execute fab-new's Steps 0–9"; the only `fab-new` mentions in draft/proceed are correct relationship descriptions; the authored canonical source `src/kit/skills/_intake.md` contains no `.claude/skills/` self-path. Rework cycle 1 RESOLVED the previously-flagged `docs/specs/skills.md` staleness: allowlist count `(6)`→`(7)` with `_intake` appended (L24), the `fab-new, fab-draft` helpers row → `[_generation, _srad, _intake]` (L48), and the partial-SPEC enumeration gained `SPEC-_intake.md` (L125). Now self-consistent with `_preamble.md`/`SPEC-_preamble.md`.) + +### Cross-References + + + +- [x] A-017: Every cross-reference resolves — `_intake` deployed-path reference (`.claude/skills/_intake/SKILL.md`) in consumer bodies, `_generation`/`_srad` in-body references in `_intake.md`, the `_preamble.md` allowlist, and the five SPEC cross-references are mutually consistent. (All three consumers reference `.claude/skills/_intake/SKILL.md`; `_intake.md` Step 5 → `_generation.md`, Steps 4/8 → `_srad.md`; `_preamble.md` allowlist includes `_intake`; SPEC-fab-new/draft/proceed each cross-reference `SPEC-_intake.md`. The enumerated set is mutually consistent. Rework cycle 1 additionally RESOLVED two dangling back-references to the renamed `fab-proceed.md` heading — `_srad.md` and `docs/memory/pipeline/planning-skills.md` both updated `§ fab-new Dispatch` → `§ Create-Intake Dispatch` (Must-fix #1 + directly-caused fallout, Assumption #12) — and reconciled `docs/specs/skills.md`'s allowlist (Should-fix #2). Post-fix repo-wide grep of `src/kit/`+`docs/` for `fab-new Dispatch` returns zero.) + +## Notes + +- Check items as you review: `- [x]` +- All acceptance items must pass before `/fab-continue` (hydrate) +- Skill *bodies* reference the deployed helper path (`.claude/skills/_intake/SKILL.md`) per the established `_preamble` "Read the `_preamble` skill ... deployed to `.claude/skills/`" convention; the *file we author/edit* is the canonical flat source `src/kit/skills/_intake.md`. These are not in conflict — `fab sync` maps `src/kit/skills/_intake.md` → `.claude/skills/_intake/SKILL.md`. + +## Assumptions + + + +| # | Grade | Decision | Rationale | Scores | +|---|-------|----------|-----------|--------| +| 1 | Certain | Canonical-source path is the flat `src/kit/skills/_intake.md`, NOT `src/kit/skills/_intake/SKILL.md`. The canonical-source intent of intake Assumption #1 (never edit `.claude/skills/`) is honored; the directory-per-skill layout it named is the *deployed* `.claude/skills/{name}/SKILL.md` form, not the source tree. | Apply-time evidence: every existing canonical helper (`_pipeline.md`, `_generation.md`, `_srad.md`, `_review.md`, `_preamble.md`) is a flat `.md` in `src/kit/skills/`; `.claude/skills/_pipeline/SKILL.md` is the deployed copy. Constitution: `src/kit/` canonical, `.claude/skills/` is `fab sync` output. Corrects intake row 1's path detail while preserving its intent. | S:100 R:90 A:100 D:100 | +| 2 | Certain | `_intake` frontmatter is `user-invocable: false`, `disable-model-invocation: true`, `metadata: internal: true`. | Matches `_pipeline`/`_review`/`_generation`/`_srad` verbatim (verified by reading them). Intake row 2. | S:100 R:85 A:100 D:100 | +| 3 | Certain | `{questioning-mode}` (`interactive` \| `promptless-defer`) is the SOLE parameter; activate/branch and proceed's state-detection stay at the call site. | Intake's EXTRACTION BOUNDARY is explicit and emphatic. Mirrors `_pipeline`'s one-knob shape. Intake row 3. | S:100 R:80 A:95 D:100 | +| 4 | Confident | `fab-new.md` and `fab-draft.md` keep declaring `_generation` and `_srad` directly in `helpers:` AND add `_intake` (now `[_generation, _srad, _intake]`). | `_pipeline` precedent: `fab-ff`/`fab-fff` declare underlying helpers (`_generation`, `_review`, `_srad`) directly alongside `_pipeline`. Frontmatter-only, trivially reversible. Intake row 4. | S:55 R:90 A:70 D:65 | +| 5 | Confident | `_intake.md` carries NO `helpers:` frontmatter; references `_generation`/`_srad` in-body, relies on consumer having loaded them. | Matches `_pipeline`/`_review`/`_generation` (none carry `helpers:`). Consumer-declared model. Intake row 5. | S:60 R:85 A:75 D:70 | +| 6 | Confident | Spec updates: new `SPEC-_intake.md` + modify `SPEC-fab-new.md`, `SPEC-fab-draft.md`, `SPEC-fab-proceed.md`, `SPEC-_preamble.md`. | Constitution mandates SPEC-* updates for skill-file changes. Five edited skills map 1:1 (verified all four mods exist; `SPEC-_intake.md` is new). Intake row 6. | S:80 R:85 A:90 D:85 | +| 7 | Confident | `_preamble.md` Allowed-values line gains `_intake` (now `_generation, _review, _cli-fab, _cli-external, _srad, _pipeline, _intake`). | Current allowlist verbatim at `_preamble.md` line 105 (verified). Intake row 7. | S:90 R:90 A:95 D:90 | +| 8 | Confident | Behavioral parity is the acceptance bar: `interactive` reproduces current fab-new/fab-draft Steps 0–9 exactly; `promptless-defer` preserves `/fab-proceed`'s current contract exactly. | Finding frames de-duplication, not behavior change. The carve-out exists verbatim in `_srad.md` § Critical Rule; referenced not redefined. Intake row 8. | S:75 R:70 A:80 D:75 | +| 9 | Confident | Lifted Step 4 genericized ("the invoking skill" not "this `/fab-new` invocation"); `{self-name}` parameter rejected; structurally retires fab-draft's self-name instruction. | User-endorsed (2026-06-13): genericize over parameterize. Ordinary apply-time prose. Intake row 9. | S:80 R:80 A:75 D:80 | +| 10 | Confident | `fab-proceed.md` keeps declaring NO `helpers:` frontmatter; it dispatches `_intake(promptless-defer)` as a subagent prompt (the subagent reads the helper), exactly as it dispatched `/fab-new` today. | NEW apply-time decision. fab-proceed is an orchestrator that loads no helpers for itself (verified: no `helpers:` in its current frontmatter) and delegates loading to dispatched subagents. Adding `_intake` to its frontmatter would be incorrect — it never executes `_intake` in its own context. Frontmatter-only, trivially reversible. | S:75 R:90 A:80 D:80 | +| 11 | Confident | The `/fab-proceed` create-new dispatch-table rows now chain `_intake` → `/fab-switch` → `/git-branch` (previously `/fab-new` alone). REQUIRED for behavioral parity: the old dispatch invoked the FULL `/fab-new` skill, which activated (Step 10) + branched (Step 11) inline; `_intake` stops at `ready` and does neither (the EXTRACTION BOUNDARY keeps activate/branch as fab-new's call-site tail), so proceed must run the dedicated `/fab-switch`+`/git-branch` prefix steps (which it already has) to reach the same end state before `/fab-fff`. Without this, proceed's create-new path would hand `/fab-fff` an inactive, unbranched change — a parity regression. | NEW apply-time decision surfaced by the reroute. Directly serves Assumption #8 (preserve proceed's contract exactly). The `/fab-switch`+`/git-branch` steps already exist in proceed (used by the relevant-intake rows); this makes the create-new rows symmetric. Reversible (skill prose). The obsolete "fab-new rows skip /git-branch because Step 11 branches inline" rationale (lines 95/154) is updated accordingly. | S:80 R:85 A:80 D:70 | +| 12 | Certain | The dangling back-reference fix to the renamed `fab-proceed.md` heading (`§ fab-new Dispatch` → `§ Create-Intake Dispatch`) is applied not only in `src/kit/skills/_srad.md` but also in `docs/memory/pipeline/planning-skills.md:46`, which carried the identical parenthetical cross-reference. | Rework-cycle finding: this change's T004 renamed the `fab-proceed.md` heading, so EVERY back-reference to `§ fab-new Dispatch` dangles. A repo-wide grep of `src/kit/`+`docs/` found exactly two (`_srad.md` = Must-fix #1; `planning-skills.md` = directly-caused rename fallout). Both fixed; post-fix grep returns zero. Section-name-only edits — no surrounding prose changed; the memory file's editable-canonical status (not gitignored, not `.claude/skills/`) permits the edit. | S:100 R:95 A:100 D:100 | + +12 assumptions (4 certain, 8 confident, 0 tentative). + +## Deletion Candidates + +None — this change IS a deletion (de-duplication). The redundant blocks the extraction targets — the inline Steps 0–9 in `fab-new.md` and `fab-draft.md`'s old prose-delta body (incl. delta #2's momentum warning) — were already removed by apply (modified-file diff: +121 / −223 lines; the lifted content now lives once in `_intake.md`). No leftover redundant block remains for the reviewer to additionally delete. The new `_intake.md` (137 lines) and `SPEC-_intake.md` (94 lines) are the single authoritative home for the lifted procedure, not new duplication. The `fab-new` mentions remaining in `fab-draft.md`/`fab-proceed.md` are correct relationship descriptions (e.g., "Steps 10–11 live only in `fab-new.md`'s tail"), not stale indirection, and must stay. diff --git a/src/kit/skills/_intake.md b/src/kit/skills/_intake.md new file mode 100644 index 00000000..afb78487 --- /dev/null +++ b/src/kit/skills/_intake.md @@ -0,0 +1,138 @@ +--- +name: _intake +description: "Shared pre-boundary Create-Intake Procedure (fab-new Steps 0–9) used by fab-new, fab-draft, and fab-proceed — parse input, slug, gap analysis, create change, conversation context mining, generate intake.md, verify change type, confidence, question selection, advance to ready. Parameterized by a single {questioning-mode} knob (interactive | promptless-defer)." +user-invocable: false +disable-model-invocation: true +metadata: + internal: true +--- +# Shared Create-Intake Procedure + +> This file defines the shared **pre-boundary** intake-creation logic used by three skills: +> `/fab-new`, `/fab-draft`, and `/fab-proceed` (its create-new subagent dispatch — `/fab-proceed` +> dispatches `_intake` directly, not `/fab-new`). The calling skill +> (the **consumer**) binds one parameter before executing this procedure — read it from the +> consumer's own file: +> +> - **`{questioning-mode}`** — how Step 8 resolves ambiguity: +> - **`interactive`** — used by `/fab-new` and `/fab-draft`. Step 8 asks the user via SRAD +> (SRAD-driven question selection, no fixed cap, conversational mode when 5+ Unresolved). +> - **`promptless-defer`** — used by `/fab-proceed`'s dispatch. Step 8 records each would-be-asked +> Unresolved decision as a deferred Unresolved row instead of asking, per the `_srad.md` +> promptless-dispatch carve-out (quoted at Step 8). +> +> This is the **only** behavioral fork in intake creation, and it is legitimately +> **invocation-level** (who resolves ambiguity: human-now vs. defer-and-surface). It is exactly +> parallel to the post-boundary autonomy fork (interactive rework menu vs. autonomous auto-rework). +> +> **What stays at the call site** (do NOT do it here): deciding *whether* to create an intake +> (`/fab-proceed`'s state-detection + relevance assessment), and what to do *after* (activate + +> git branch — `/fab-new`'s Steps 10–11 tail). This procedure is purely "given I've decided to +> create an intake, do it (Steps 0–9)." +> +> This procedure references `_generation.md` (Step 5) and `_srad.md` (Step 8) in-body; it carries +> no `helpers:` frontmatter of its own — every consumer already declares both helpers (the +> consumer-declared model), or, for `/fab-proceed`, dispatches this procedure to a subagent that loads +> them. Mirror the proven `_pipeline.md` shape: shared body parameterized by one knob, call-site +> tails stay in the call-site files. + +--- + +## Create-Intake Procedure (Steps 0–9) + +### Step 0: Parse Input + +Detect input type (check in order): + +1. **Linear ticket ID** (`[A-Z]+-\d+`) — fetch via `mcp__claude_ai_Linear__get_issue`; extract title, description, state, labels, branchName. On failure, fall back to natural language. +2. **Backlog ID** (`[a-z0-9]{4}`) — read `fab/backlog.md`, search for `\[{id}\]`. Check for an optional `[ISSUE_ID]` bracket immediately after (e.g., `[ni3o] [DEV-1011]`); if found, extract and fetch per #1. Store backlog ID for folder name. +3. **Natural language** — use as-is + +### Step 1: Generate Slug + +Generate a 2-6 word slug (lowercase, hyphen-joined, no articles/prepositions) from the description. The slug SHALL NOT include the Linear issue ID — it contains only the descriptive portion (e.g., `add-oauth`). This slug is passed to `fab change new` as the `--slug` value. + +### Step 2: Gap Analysis + +Check for existing mechanisms or scope concerns covering the idea. If covered: present findings, let user decide. If not: proceed. + +### Step 3: Create Change + +**Re-run / collision check** (only when a backlog or Linear ID was detected in Step 0): before creating, check whether a non-archived change already exists for that ID. The mechanism differs by ID type: + +- **Backlog ID** (4-char — embedded in the folder-name prefix): `fab resolve --id {id} 2>/dev/null` — then compare its stdout (the canonical 4-char ID of the matched change) for **equality** with `{id}`. Only an exact ID match names an existing change for this ID; resolution is substring-based, so `{id}` occurring inside another change's *slug* also resolves — with a different canonical ID — and MUST NOT route to resume. On an exact match, get the folder name via `fab resolve --folder {id}`. +- **Linear ID** (never in folder names — slugs exclude issue IDs; the ID is recorded only in `.status.yaml` `issues` arrays): `grep -lw "{ISSUE_ID}" fab/changes/*/.status.yaml 2>/dev/null` — `-w` anchors on word boundaries so `DEV-123` does not match `DEV-1234`; the single-level glob naturally excludes `fab/changes/archive/`; a match's parent folder is the existing change. + +If a check finds an existing change, do NOT create a duplicate — **route to resume**: report `Change {name} already exists for [{id}].` and point the user to `/fab-switch {name}` then `/fab-continue` (whose intake-`active` dispatch row regenerates a missing intake, recovering an interrupted creation). STOP. (For backlog IDs, `fab change new`'s `Change ID already in use` error remains the safety net if this check is skipped; Linear re-runs have no CLI safety net — no `--change-id` is passed — so this scan is the only collision guard.) + +**Natural-language re-run semantics**: a natural-language description intentionally creates a **new change on every run** (fresh random ID) — there is no dedup for NL input. + +Run `fab change new` with appropriate flags: +- `--slug ` — the slug from Step 1 (descriptive only, no issue ID) +- `--change-id <4char>` — only if a backlog ID was detected in Step 0 (the 4-char backlog ID becomes the change ID) +- `--log-args ` — the original description text + +Capture the folder name from stdout. The command handles date generation, random ID generation (if no `--change-id`), collision detection, directory creation, `created_by` detection, `.status.yaml` initialization, and command logging (when `--log-args` is provided). + +If a Linear ticket was detected in Step 0, record the issue ID via `fab status`: +`fab status add-issue {name} DEV-988` (using the actual detected ID). + +### Step 4: Conversation Context Mining + +This step is the load-bearing **context-flush at the boundary**: intake is the single context-bearing boundary in the pipeline (everything after it runs as a dispatched subagent reading only the intake artifact). Capturing the live conversation's decisions here is what transfers that context across the boundary — it is not merely a scoring optimization. + +Before generating the intake, scan the current conversation for prior discussion of this change's topic — whether from `/fab-discuss`, free-form exploration, or any conversation that preceded the invoking skill's invocation. Extract: + +- **Decisions made** — specific choices with rationale (e.g., "OAuth2 over SAML because no enterprise requirement") +- **Alternatives rejected** — options considered and why they were ruled out +- **Constraints identified** — boundaries or requirements surfaced during discussion +- **Specific values agreed upon** — config structures, API shapes, exact behaviors + +Encode extracted decisions as Certain or Confident assumptions in the intake's Assumptions table with rationale referencing the discussion (e.g., "Discussed — user chose X over Y"). These feed directly into SRAD scoring and reduce downstream ambiguity. + +If no prior discussion exists in the conversation, skip this step — behavior is identical to a cold invocation. + +### Step 5: Generate `intake.md` + +Follow the **Intake Generation Procedure** (`_generation.md`). Load context per `_preamble.md` Layer 1 and generate from `$(fab kit-path)/templates/intake.md`. Incorporate any decisions extracted in Step 4. + +### Step 6: Verify Change Type + +The PostToolUse intake-write hook owns `change_type`: it infers and writes the type to `.status.yaml` on **every** `intake.md` write, using word-boundary keyword regexes evaluated in order — `fix` → `refactor` (incl. "redesign") → `docs` → `test` → `ci` → `chore` — defaulting to `feat`. Do NOT run a manual keyword inference or an unconditional `set-change-type`: any later intake write (e.g., `/fab-clarify`) re-fires the hook and silently overwrites a skill-set value. + +1. **Verify** the hook's result by reading `change_type` from the change's `.status.yaml` (e.g., `grep '^change_type:' fab/changes/{name}/.status.yaml`) — `fab preflight` does not emit this field +2. **Override only if wrong**: `fab status set-change-type {name} ` — and note that any subsequent intake edit re-fires the hook and overwrites the override, so re-verify after later intake writes + +### Step 7: Confidence + +After generating `intake.md` and verifying the change type, persist and display the confidence score: + +1. Call `fab score --stage intake ` (normal mode, **not** `--check-gate`) +2. This writes the score to `.status.yaml` (no `indicative` flag is written — retired in 1.10.0; intake scoring is authoritative) +3. Display the result from stdout (score and breakdown) + +Output format: `Confidence: {score} / 5.0 ({N} decisions)` + +The score is persisted to `.status.yaml` so that consumers (`/fab-switch`, `/fab-status`, `fab change list`) can display it without recomputation. It is the authoritative confidence — intake is the sole scoring source, and the single intake gate (flat 3.0) reads it. + +### Step 8: SRAD-Based Question Selection *(the parameterized step)* + +This is the sole step that varies by `{questioning-mode}`. + +- **`{questioning-mode} = interactive`** (used by `/fab-new`, `/fab-draft`): Apply SRAD (`_srad.md`). No fixed question cap — SRAD scoring determines count. Zero questions for clear inputs. **Conversational mode**: when 5+ Unresolved, ask one at a time until resolved or user signals done. + +- **`{questioning-mode} = promptless-defer`** (used by `/fab-proceed`'s dispatch): there is no user to ask. Apply SRAD, but instead of asking, record each would-be-asked Unresolved decision as a deferred Unresolved row, per the `_srad.md` § Critical Rule **promptless-dispatch carve-out** (verbatim): + + > each would-be-asked Unresolved decision is recorded as an Unresolved row with Rationale `Deferred — promptless dispatch` and surfaced to the user by the dispatcher. The intake gate is the structural backstop — `fab score` returns 0.0 whenever any Unresolved row exists, so a deferred decision always blocks the automated bracket until resolved via `/fab-clarify`. + + Return the deferred Unresolved decisions in the subagent result so the dispatcher (`/fab-proceed`) can surface them. The MUST-ask is satisfied by deferring and surfacing, never by silently assuming. + +### Step 9: Advance Intake to Ready + +After all intake work is complete (generation, type verification, confidence, questions), advance intake to `ready`: + +```bash +fab status advance {name} intake +``` + +This signals that the intake artifact exists and is open for `/fab-clarify` refinement. What happens next is the **call site's** responsibility (activate + branch, or stop at ready, or hand the deferred decisions to the dispatcher) — see the consumer's own tail. diff --git a/src/kit/skills/_preamble.md b/src/kit/skills/_preamble.md index 44867c18..cc21a4d9 100644 --- a/src/kit/skills/_preamble.md +++ b/src/kit/skills/_preamble.md @@ -102,7 +102,7 @@ helpers: [_generation, _review, _srad, _pipeline] --- ``` -**Allowed values**: `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`. +**Allowed values**: `_generation`, `_review`, `_cli-fab`, `_cli-external`, `_srad`, `_pipeline`, `_intake`. **Not allowed** (inlined into this preamble): `_naming`, `_cli-rk`. diff --git a/src/kit/skills/_srad.md b/src/kit/skills/_srad.md index dd5f23b7..c6c54437 100644 --- a/src/kit/skills/_srad.md +++ b/src/kit/skills/_srad.md @@ -46,7 +46,7 @@ Each decision produces an assumption graded on a 4-level scale: **Decisions with R < 25 AND A < 25 (the override in the aggregation rule above — the Critical Rule's single numeric definition) are always Unresolved and MUST always be asked** — even in `/fab-new` and `/fab-continue`. These count toward the skill's question budget (max ~3). The existence of `/fab-clarify` as an escape valve does NOT justify silently assuming high-blast-radius decisions. `/fab-clarify` is for Tentative assumptions, not for Unresolved ones. -**Promptless-dispatch carve-out**: when a planning skill runs as a promptless subagent under `/fab-proceed`'s defer-and-surface contract (`fab-proceed.md` § fab-new Dispatch), there is no user to ask. The MUST-ask is satisfied by **deferring and surfacing**, never by silently assuming: each would-be-asked Unresolved decision is recorded as an Unresolved row with Rationale `Deferred — promptless dispatch` and surfaced to the user by the dispatcher. The intake gate is the structural backstop — `fab score` returns 0.0 whenever any Unresolved row exists, so a deferred decision always blocks the automated bracket until resolved via `/fab-clarify`. Everywhere a user is reachable, the MUST-ask applies unchanged. +**Promptless-dispatch carve-out**: when a planning skill runs as a promptless subagent under `/fab-proceed`'s defer-and-surface contract (`fab-proceed.md` § Create-Intake Dispatch), there is no user to ask. The MUST-ask is satisfied by **deferring and surfacing**, never by silently assuming: each would-be-asked Unresolved decision is recorded as an Unresolved row with Rationale `Deferred — promptless dispatch` and surfaced to the user by the dispatcher. The intake gate is the structural backstop — `fab score` returns 0.0 whenever any Unresolved row exists, so a deferred decision always blocks the automated bracket until resolved via `/fab-clarify`. Everywhere a user is reachable, the MUST-ask applies unchanged. ## Skill-Specific Autonomy Levels diff --git a/src/kit/skills/fab-draft.md b/src/kit/skills/fab-draft.md index 2e4a2618..830a2c0f 100644 --- a/src/kit/skills/fab-draft.md +++ b/src/kit/skills/fab-draft.md @@ -1,7 +1,7 @@ --- name: fab-draft description: "Create a change intake without activating it." -helpers: [_generation, _srad] +helpers: [_generation, _srad, _intake] --- # /fab-draft @@ -16,23 +16,49 @@ helpers: [_generation, _srad] --- -## Behavior (delta over /fab-new) +## Pre-flight -`/fab-draft` is a **thin delta over `/fab-new`**. Read `.claude/skills/fab-new/SKILL.md` and execute its **Pre-flight**, **Arguments**, and **Steps 0–9** exactly as written there (self-name mentions read as `/fab-draft` — e.g., Step 4's "preceded this `/fab-new` invocation"), with these deltas: +1. Verify `fab/project/config.yaml` and `fab/project/constitution.md` exist +2. **If either missing, STOP**: `fab/ is not initialized. Run /fab-setup first to bootstrap the project.` -1. **Step 9 tail**: after `fab status advance {name} intake`, the change is **NOT activated** — the user must run `/fab-switch {name}` to make it active before proceeding. (This replaces fab-new's Step 9 closing sentence about Step 10 activating the change.) +--- + +## Arguments + +- **``** *(required)* — natural language, Linear ticket ID (`DEV-988`), or backlog ID (`90g5`) + +If no description: ask *"What change do you want to make?"* + +--- + +## Behavior + +### Steps 0–9: Create the Intake + +Read `.claude/skills/_intake/SKILL.md` and execute the **Create-Intake Procedure** (Steps 0–9) with: + +- **`{questioning-mode} = interactive`** — Step 8 asks the user via SRAD (no fixed cap; conversational mode when 5+ Unresolved). Same intake-creation behavior as `/fab-new`. + +Then **STOP after the procedure's Step 9** (intake at `ready`): + +- Do **NOT** activate the change — there is no Step 10. The `.fab-status.yaml` symlink is NOT created; the user must run `/fab-switch {name}` to make it active before proceeding. +- Do **NOT** create a git branch — there is no Step 11. Run no `fab change switch` and no `git` command. + +This is the only difference between `/fab-draft` and `/fab-new`: `/fab-draft` calls the shared Create-Intake Procedure and stops at `ready`, while `/fab-new` follows the same procedure with its own Steps 10–11 tail (activate + branch). Because those steps live only in `fab-new.md`'s tail — which `/fab-draft` never reads — there is no "run activation by momentum" hazard here. + +### Output -2. **SKIP Steps 10–11 ENTIRELY — no activation, no git branch.** Do NOT run `fab change switch`. Do NOT run any `git` command. Stop after Step 9. This is the defining difference from `/fab-new`; running activation or branch creation by momentum is the known failure mode of this delta form — before any `fab change switch` or `git` invocation, re-check that you are executing `/fab-draft`. +Use fab-new's Output block **minus** the `Activated: {name}` and `Branch: ...` lines, ending with the Activation Preamble `Next:` line (`_preamble.md` § Activation Preamble — `/fab-draft` always uses it): -3. **Output**: fab-new's Output block **minus** the `Activated: {name}` and `Branch: ...` lines, ending with the Activation Preamble `Next:` line (`_preamble.md` § Activation Preamble — `/fab-draft` always uses it): +``` +Next: /fab-switch {name} to make it active, then /fab-continue, /fab-ff, /fab-fff, /fab-proceed, or /fab-clarify +``` - ``` - Next: /fab-switch {name} to make it active, then /fab-continue, /fab-ff, /fab-fff, /fab-proceed, or /fab-clarify - ``` +(The command list after "then" is the state table's intake row, derived per `_preamble.md` § Lookup Procedure — default first, not hardcoded.) - (The command list after "then" is the state table's intake row, derived per `_preamble.md` § Lookup Procedure — default first, not hardcoded.) +### Error Handling -4. **Error Handling**: fab-new's table **minus** the activation/git rows (`fab change switch` failure, not-in-git-repo, `git checkout`/`git branch` failure) — those steps never run here. +The Create-Intake Procedure's own error conditions apply (config/constitution missing, no description, intake template missing, `fab change new` collision/failure, Linear/backlog lookup failures). `/fab-draft` adds **no** activation/git rows — `fab change switch`, not-in-git-repo, and `git checkout`/`git branch` failures cannot occur here because those steps never run. --- diff --git a/src/kit/skills/fab-new.md b/src/kit/skills/fab-new.md index f8ee605b..f170f5a1 100644 --- a/src/kit/skills/fab-new.md +++ b/src/kit/skills/fab-new.md @@ -1,7 +1,7 @@ --- name: fab-new description: "Start a new change — creates the intake, activates it, and creates the git branch." -helpers: [_generation, _srad] +helpers: [_generation, _srad, _intake] --- # /fab-new @@ -27,92 +27,13 @@ If no description: ask *"What change do you want to make?"* ## Behavior -### Step 0: Parse Input +### Steps 0–9: Create the Intake -Detect input type (check in order): +Read `.claude/skills/_intake/SKILL.md` and execute the **Create-Intake Procedure** (Steps 0–9 — parse input, slug, gap analysis, create change, conversation context mining, generate `intake.md`, verify change type, confidence, SRAD question selection, advance to ready) with: -1. **Linear ticket ID** (`[A-Z]+-\d+`) — fetch via `mcp__claude_ai_Linear__get_issue`; extract title, description, state, labels, branchName. On failure, fall back to natural language. -2. **Backlog ID** (`[a-z0-9]{4}`) — read `fab/backlog.md`, search for `\[{id}\]`. Check for an optional `[ISSUE_ID]` bracket immediately after (e.g., `[ni3o] [DEV-1011]`); if found, extract and fetch per #1. Store backlog ID for folder name. -3. **Natural language** — use as-is +- **`{questioning-mode} = interactive`** — Step 8 asks the user via SRAD (no fixed cap; conversational mode when 5+ Unresolved). This is `/fab-new`'s existing intake-creation behavior. -### Step 1: Generate Slug - -Generate a 2-6 word slug (lowercase, hyphen-joined, no articles/prepositions) from the description. The slug SHALL NOT include the Linear issue ID — it contains only the descriptive portion (e.g., `add-oauth`). This slug is passed to `fab change new` as the `--slug` value. - -### Step 2: Gap Analysis - -Check for existing mechanisms or scope concerns covering the idea. If covered: present findings, let user decide. If not: proceed. - -### Step 3: Create Change - -**Re-run / collision check** (only when a backlog or Linear ID was detected in Step 0): before creating, check whether a non-archived change already exists for that ID. The mechanism differs by ID type: - -- **Backlog ID** (4-char — embedded in the folder-name prefix): `fab resolve --id {id} 2>/dev/null` — then compare its stdout (the canonical 4-char ID of the matched change) for **equality** with `{id}`. Only an exact ID match names an existing change for this ID; resolution is substring-based, so `{id}` occurring inside another change's *slug* also resolves — with a different canonical ID — and MUST NOT route to resume. On an exact match, get the folder name via `fab resolve --folder {id}`. -- **Linear ID** (never in folder names — slugs exclude issue IDs; the ID is recorded only in `.status.yaml` `issues` arrays): `grep -lw "{ISSUE_ID}" fab/changes/*/.status.yaml 2>/dev/null` — `-w` anchors on word boundaries so `DEV-123` does not match `DEV-1234`; the single-level glob naturally excludes `fab/changes/archive/`; a match's parent folder is the existing change. - -If a check finds an existing change, do NOT create a duplicate — **route to resume**: report `Change {name} already exists for [{id}].` and point the user to `/fab-switch {name}` then `/fab-continue` (whose intake-`active` dispatch row regenerates a missing intake, recovering an interrupted creation). STOP. (For backlog IDs, `fab change new`'s `Change ID already in use` error remains the safety net if this check is skipped; Linear re-runs have no CLI safety net — no `--change-id` is passed — so this scan is the only collision guard.) - -**Natural-language re-run semantics**: a natural-language description intentionally creates a **new change on every run** (fresh random ID) — there is no dedup for NL input. - -Run `fab change new` with appropriate flags: -- `--slug ` — the slug from Step 1 (descriptive only, no issue ID) -- `--change-id <4char>` — only if a backlog ID was detected in Step 0 (the 4-char backlog ID becomes the change ID) -- `--log-args ` — the original description text - -Capture the folder name from stdout. The command handles date generation, random ID generation (if no `--change-id`), collision detection, directory creation, `created_by` detection, `.status.yaml` initialization, and command logging (when `--log-args` is provided). - -If a Linear ticket was detected in Step 0, record the issue ID via `fab status`: -`fab status add-issue {name} DEV-988` (using the actual detected ID). - -### Step 4: Conversation Context Mining - -Before generating the intake, scan the current conversation for prior discussion of this change's topic — whether from `/fab-discuss`, free-form exploration, or any conversation that preceded this `/fab-new` invocation. Extract: - -- **Decisions made** — specific choices with rationale (e.g., "OAuth2 over SAML because no enterprise requirement") -- **Alternatives rejected** — options considered and why they were ruled out -- **Constraints identified** — boundaries or requirements surfaced during discussion -- **Specific values agreed upon** — config structures, API shapes, exact behaviors - -Encode extracted decisions as Certain or Confident assumptions in the intake's Assumptions table with rationale referencing the discussion (e.g., "Discussed — user chose X over Y"). These feed directly into SRAD scoring and reduce downstream ambiguity. - -If no prior discussion exists in the conversation, skip this step — behavior is identical to a cold `/fab-new`. - -### Step 5: Generate `intake.md` - -Follow the **Intake Generation Procedure** (`_generation.md`). Load context per `_preamble.md` Layer 1 and generate from `$(fab kit-path)/templates/intake.md`. Incorporate any decisions extracted in Step 4. - -### Step 6: Verify Change Type - -The PostToolUse intake-write hook owns `change_type`: it infers and writes the type to `.status.yaml` on **every** `intake.md` write, using word-boundary keyword regexes evaluated in order — `fix` → `refactor` (incl. "redesign") → `docs` → `test` → `ci` → `chore` — defaulting to `feat`. Do NOT run a manual keyword inference or an unconditional `set-change-type`: any later intake write (e.g., `/fab-clarify`) re-fires the hook and silently overwrites a skill-set value. - -1. **Verify** the hook's result by reading `change_type` from the change's `.status.yaml` (e.g., `grep '^change_type:' fab/changes/{name}/.status.yaml`) — `fab preflight` does not emit this field -2. **Override only if wrong**: `fab status set-change-type {name} ` — and note that any subsequent intake edit re-fires the hook and overwrites the override, so re-verify after later intake writes - -### Step 7: Confidence - -After generating `intake.md` and verifying the change type, persist and display the confidence score: - -1. Call `fab score --stage intake ` (normal mode, **not** `--check-gate`) -2. This writes the score to `.status.yaml` (no `indicative` flag is written — retired in 1.10.0; intake scoring is authoritative) -3. Display the result from stdout (score and breakdown) - -Output format: `Confidence: {score} / 5.0 ({N} decisions)` - -The score is persisted to `.status.yaml` so that consumers (`/fab-switch`, `/fab-status`, `fab change list`) can display it without recomputation. It is the authoritative confidence — intake is the sole scoring source, and the single intake gate (flat 3.0) reads it. - -### Step 8: SRAD-Based Question Selection - -Apply SRAD (`_srad.md`, loaded via `helpers:`). No fixed question cap — SRAD scoring determines count. Zero questions for clear inputs. **Conversational mode**: when 5+ Unresolved, ask one at a time until resolved or user signals done. - -### Step 9: Advance Intake to Ready - -After all intake work is complete (generation, type verification, confidence, questions), advance intake to `ready`: - -```bash -fab status advance {name} intake -``` - -This signals that the intake artifact exists and is open for `/fab-clarify` refinement. After Step 10 activates the change, the user can run `/fab-continue` immediately to proceed to apply (which co-generates `plan.md` — requirements + tasks + acceptance). +The procedure advances intake to `ready` at its Step 9 and stops. `/fab-new`'s own tail (Steps 10–11 below) then activates the change and creates the git branch — these are `/fab-new`-specific and stay here; they are NOT part of the shared procedure. ### Step 10: Activate Change diff --git a/src/kit/skills/fab-proceed.md b/src/kit/skills/fab-proceed.md index 43bfc073..5512ab3c 100644 --- a/src/kit/skills/fab-proceed.md +++ b/src/kit/skills/fab-proceed.md @@ -86,13 +86,13 @@ Combine the signals from Steps 1–4 per the dispatch table below. |----------------|-----------------|--------------|---------------------|-----------|--------------| | Yes | Yes | — | — | — | `/fab-fff` only | | Yes | No | — | — | — | `/git-branch` → `/fab-fff` | -| No | — | Substantive | None | — | `/fab-new` → `/fab-fff` | +| No | — | Substantive | None | — | `_intake` → `/fab-switch` → `/git-branch` → `/fab-fff` | | No | — | Substantive | ≥1 | Clearly relevant | `/fab-switch` → `/git-branch` → `/fab-fff` | -| No | — | Substantive | ≥1 | Not clearly relevant | `/fab-new` → `/fab-fff` (emit bypass notes) | +| No | — | Substantive | ≥1 | Not clearly relevant | `_intake` → `/fab-switch` → `/git-branch` → `/fab-fff` (emit bypass notes) | | No | — | Empty/thin | ≥1 | — | `/fab-switch` → `/git-branch` → `/fab-fff` (pick by date-recency) | | No | — | Empty/thin | None | — | Error — stop | -The `/fab-new`-prefixed rows do NOT chain `/git-branch`: `/fab-new` Step 11 creates or checks out the matching branch inline, so a trailing `/git-branch` would be a guaranteed no-op. The `/fab-switch`-prefixed rows keep it — switching activates a change but creates no branch. +The `_intake`-prefixed rows chain `/fab-switch` → `/git-branch`: the shared Create-Intake Procedure stops at intake `ready` and does NOT activate or branch (activate/branch is `/fab-new`'s call-site tail, not part of the procedure). So `/fab-proceed` runs the dedicated `/fab-switch` (activate) and `/git-branch` prefix steps to reach the same end state — active change + matching branch — that the prior full-`/fab-new` dispatch produced inline. The `/fab-switch`-relevant-intake rows already chained both; this makes the create-new rows symmetric. The `Relevant?` column is evaluated only when Conversation is Substantive AND Unactivated intake is ≥1. In the Empty/thin + ≥1 intake row, no relevance check runs — pick the most-recent by `YYMMDD` prefix. This preserves the "resume yesterday's draft" flow, and is safe because an empty/thin conversation carries no competing signal that could conflict with the intake's content. @@ -108,7 +108,7 @@ For each candidate intake, score its topical relevance to the current conversati 2. Judge topical overlap between each intake and the conversation. "Clearly relevant" requires shared topic, overlapping terminology, and consistent scope. Partial, vague, or tangential overlap MUST NOT qualify. 3. Classify each candidate as **clearly relevant** or **not clearly relevant**. 4. If ≥1 candidate is clearly relevant: select the best match. If multiple candidates are equally clearly relevant, use the date-descending full-folder-name tiebreak (`sort -r | head -1` — deterministic even among same-day changes). -5. If no candidate is clearly relevant: fall through to `/fab-new`, and surface every scanned draft as a bypass note (see Bypass Notes). +5. If no candidate is clearly relevant: fall through to the create-new path (`_intake` → `/fab-switch` → `/git-branch`), and surface every scanned draft as a bypass note (see Bypass Notes). ### Asymmetric-Bias Rule @@ -129,16 +129,16 @@ Relevance judgment is performed by the invoking agent inline — no external cla ### Subagent Dispatch (Prefix Steps) -Each prefix step (`/fab-new`, `/fab-switch`, `/git-branch`) SHALL be dispatched as a subagent using the Agent tool (`subagent_type: "general-purpose"`) per `_preamble.md` § Subagent Dispatch. Each subagent prompt MUST instruct the subagent to read the standard subagent context files per `_preamble.md` § Standard Subagent Context. +Each prefix step (the `_intake` Create-Intake Procedure, `/fab-switch`, `/git-branch`) SHALL be dispatched as a subagent using the Agent tool (`subagent_type: "general-purpose"`) per `_preamble.md` § Subagent Dispatch. Each subagent prompt MUST instruct the subagent to read the standard subagent context files per `_preamble.md` § Standard Subagent Context. > **Per-stage model**: the prefix steps are NOT pipeline stages, so they take **no** `fab resolve-agent` resolution — they dispatch at the inherited model. Per-stage model selection belongs to the pipeline `/fab-proceed` delegates to: the final `/fab-fff` invocation (run via the Skill tool in the current context) resolves `fab resolve-agent ` for each of its own stages per `_preamble.md` § Subagent Dispatch → Per-Stage Model Resolution. -#### fab-new Dispatch +#### Create-Intake Dispatch -Runs when the dispatch table selects `/fab-new`: either substantive conversation + no intake, or substantive conversation + ≥1 intake but none clearly relevant. +Runs when the dispatch table selects the create-new path (`_intake`): either substantive conversation + no intake, or substantive conversation + ≥1 intake but none clearly relevant. The create-an-intake sub-operation is routed through the shared `_intake` Create-Intake Procedure (the same Steps 0–9 `/fab-new` runs) in its `promptless-defer` mode — `/fab-proceed` decides *whether* to create an intake; `_intake` performs it. After it returns (intake at `ready`, not activated), the dispatch table chains `/fab-switch` → `/git-branch` to activate and branch. 1. Synthesize a description from the conversation (see Conversation Context Synthesis below). The synthesis MUST NOT pull from bypassed drafts — only the live conversation is the source. -2. Dispatch subagent: read `.claude/skills/fab-new/SKILL.md`, invoke `/fab-new` with the synthesized description. The dispatch is promptless — there is no interactive relay — so the prompt MUST include the **defer-and-surface contract**: ask NO questions; any decision SRAD would normally ask (Unresolved) is instead recorded in the intake's `## Assumptions` table as an Unresolved row with Rationale `Deferred — promptless dispatch`, and listed in the subagent result. (This is the `_srad.md` § Critical Rule **promptless-dispatch carve-out** — the MUST-ask is satisfied by deferring and surfacing, not by silently assuming.) +2. Dispatch subagent: read `.claude/skills/_intake/SKILL.md`, execute the **Create-Intake Procedure** with `{questioning-mode} = promptless-defer` and the synthesized description. The dispatch is promptless — there is no interactive relay — and `promptless-defer` is exactly the **defer-and-surface contract**: the procedure asks NO questions; any decision SRAD would normally ask (Unresolved) is instead recorded in the intake's `## Assumptions` table as an Unresolved row with Rationale `Deferred — promptless dispatch`, and listed in the subagent result. (This is the `_srad.md` § Critical Rule **promptless-dispatch carve-out** — the MUST-ask is satisfied by deferring and surfacing, not by silently assuming.) The procedure stops at intake `ready`; it does NOT activate or branch (those are `/fab-new`'s tail, not part of the shared procedure) — the `/fab-switch`/`/git-branch` prefix steps are dispatched separately per the dispatch table when needed. 3. Capture the created change folder name **and any deferred Unresolved decisions** from the subagent result 4. **Surface deferred decisions**: before delegating to `/fab-fff`, emit one line per deferred decision (informational — `/fab-proceed` stays zero-prompt). The intake gate is the structural backstop: `fab score` returns 0.0 whenever **any** Unresolved row exists, so even a single deferral fails the `/fab-fff` gate and the pipeline stops normally for the user to resolve via `/fab-clarify`. @@ -151,21 +151,21 @@ Runs when the dispatch table selects `/fab-switch` (substantive + clearly releva #### git-branch Dispatch -Runs when the dispatch table selects `/git-branch`: the branch-mismatch row (active change, branch doesn't match) and the `/fab-switch`-prefixed rows. The `/fab-new`-prefixed rows do NOT include it — `/fab-new` Step 11 creates or checks out the matching branch inline. +Runs when the dispatch table selects `/git-branch`: the branch-mismatch row (active change, branch doesn't match), the `/fab-switch`-prefixed relevant-intake rows, and the `_intake`-prefixed create-new rows (since `_intake` stops at `ready` without branching, `/git-branch` creates the matching branch after `/fab-switch` activates). 1. Dispatch subagent: read `.claude/skills/git-branch/SKILL.md`, follow its behavior for the active change 2. Capture the branch creation/checkout result from the subagent result ### Conversation Context Synthesis -When `/fab-proceed` dispatches `/fab-new`, it SHALL synthesize a description from the conversation by extracting: +When `/fab-proceed` dispatches the `_intake` Create-Intake Procedure (the create-new path), it SHALL synthesize a description from the conversation by extracting: - **Decisions made** — specific choices with rationale - **Alternatives rejected** — options considered and why they were ruled out - **Constraints identified** — boundaries or requirements surfaced - **Specific values agreed upon** — config structures, API shapes, exact behaviors -The synthesized description MUST be substantive enough for `/fab-new` to generate a complete intake without prompting. Do not fabricate details — capture what was said. Do not mix in content from bypassed drafts; if a bypassed draft contains overlapping details, ignore them during synthesis — the bypassed draft is left untouched for the user to reconcile later. +The synthesized description MUST be substantive enough for the Create-Intake Procedure to generate a complete intake without prompting. Do not fabricate details — capture what was said. Do not mix in content from bypassed drafts; if a bypassed draft contains overlapping details, ignore them during synthesis — the bypassed draft is left untouched for the user to reconcile later. ### fab-fff Terminal Delegation @@ -180,7 +180,7 @@ The skill SHALL NOT pass `--force` or any other flags to `/fab-fff`. If `/fab-ff | Condition | Action | |-----------|--------| | Empty/thin conversation and no intake | Output: `Nothing to proceed with — start a discussion or run /fab-new (or /fab-draft) first.` Stop. | -| fab-new subagent fails | Surface the error from fab-new and stop. Do not proceed to further steps. | +| `_intake` (create-intake) subagent fails | Surface the error from the Create-Intake Procedure and stop. Do not proceed to further steps. | | fab-switch subagent fails | Surface the error from fab-switch and stop. | | git-branch subagent fails | Surface the error from git-branch and stop. | | fab-fff gate failure | `/fab-fff` stops normally with its own gate failure message. `/fab-proceed` does not retry or bypass the gate. | @@ -204,7 +204,7 @@ Handing off to /fab-fff... ### Bypass Notes -Emitted only when the dispatch table selected `/fab-new` despite ≥1 unactivated intake being present (the "Substantive + ≥1 intake + Not clearly relevant" row). For each scanned unactivated intake, emit one line using this exact wording: +Emitted only when the dispatch table selected the create-new path (`_intake`) despite ≥1 unactivated intake being present (the "Substantive + ≥1 intake + Not clearly relevant" row). For each scanned unactivated intake, emit one line using this exact wording: ``` Note: unactivated draft {name} exists — not relevant to current conversation, left untouched. @@ -218,10 +218,10 @@ When the skill activates an existing intake (the "clearly relevant" row or the " Only for steps actually executed: -- `Created intake: {change-name}` (when `/fab-new` ran) +- `Created intake: {change-name}` (when the `_intake` Create-Intake Procedure ran) - `Activated: {change-name}` (when `/fab-switch` ran) - `Branch: {branch-name} ({action})` (when `/git-branch` ran; action = created / checked out / already active) -- `Deferred: {decision summary}` (one line per Unresolved decision the fab-new subagent recorded as `Deferred — promptless dispatch` — emitted after the step reports, before the handoff line) +- `Deferred: {decision summary}` (one line per Unresolved decision the `_intake` subagent recorded as `Deferred — promptless dispatch` — emitted after the step reports, before the handoff line) When only `/fab-fff` is needed (active change + matching branch), output shows only the detecting-state line and the handoff line before `/fab-fff` output.