Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/memory/_shared/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ When absent, `fab-sync.sh` falls back to `haiku` for the fast tier. (Note: the m
Optional top-level field. A YAML sequence of pathspec exclusion patterns — typically directory prefixes ending in `/` (e.g., `fab/`, `docs/`, `vendor/`), but any pattern accepted by `git diff` `:(exclude)<pattern>` syntax is valid. Scaffold default: `[fab/, docs/]` so new fab-kit projects emit the impact block out of the box.

Semantics:
- **Present and non-empty** — the `**Impact**:` line of the PR `## Meta` block (rendered by `fab pr-meta` as of rj31; previously inlined in `/git-pr`) annotates the excluded scope, the parenthetical/annotation reflecting the config values verbatim (each per-element backtick-wrapped, never hardcoded). Computed via `git diff --shortstat "$BASE...HEAD"` against the merge-base with and without the `:(exclude)<pattern>` pathspec args (canonical math in `internal/impact`).
- **Present and non-empty** — the Impact table of the PR `## Meta` block (rendered by `fab pr-meta` as of rj31; previously inlined in `/git-pr`) names the excluded scope in its `<sub>` provenance caption (`<sub>excludes `fab/`, `docs/` · generated by fab-kit vX.Y.Z</sub>`, since the pnao normalization + 260625 layout revision — no separate `**Impact**:` lead-in line), the caption reflecting the config values verbatim (each per-element backtick-wrapped, never hardcoded). Computed via `git diff --shortstat "$BASE...HEAD"` against the merge-base with and without the `:(exclude)<pattern>` pathspec args (canonical math in `internal/impact`).
- **Absent, `null`, or `[]`** — the impact block is omitted entirely; the rest of the PR body matches the no-block output byte-for-byte.
- **No fab context** (`fab/project/config.yaml` does not exist) — the block is omitted silently, preserving `/git-pr`'s fab-optional behavior.
- **True-impact pass returns zero** (every modified file falls inside an excluded path) — the entire block is omitted to avoid a misleading `+0 / −0` line.
Expand All @@ -88,7 +88,7 @@ Optional top-level field (7t5a). A YAML sequence of glob/pathspec patterns ident

**Graceful collapse.** When `test_paths` is absent, `null`, or `[]`, the impact engine skips the test pass entirely (`Result.Tests` stays nil), no `tests` sub-block is written to `.status.yaml`, and impl/tests rendering collapses to today's single-number display — matching the existing lazy-omit posture of the `excluding` sub-block.

Consumed by the impact engine (`internal/impact/`, via `ComputeForRepo`) and rendered by the `/git-pr` PR body (three-row impl/tests/total breakdown) and `fab change list --show-stats` (compact `{impl}i+{tests}t={total}` column). See the `true_impact` block in [schemas.md](/pipeline/schemas.md) for the `tests` sub-block schema and the render-time `impl = max(0, total − tests)` residual.
Consumed by the impact engine (`internal/impact/`, via `ComputeForRepo`) and rendered by the `/git-pr` PR body (the `fab pr-meta` Meta-block Impact table — nested `└ impl`/`└ tests` rows under the bold `true` row, normalized in pnao) and `fab change list --show-stats` (compact `{impl}i+{tests}t={total}` column). See the `true_impact` block in [schemas.md](/pipeline/schemas.md) for the `tests` sub-block schema and the render-time `impl = max(0, total − tests)` residual.

#### `stage_directives` (removed in c5tr)
Removed from the config surface in 260612-c5tr. The key promised per-stage artifact-generation directives but had **zero readers, ever** — the scaffold seeded it, `/fab-setup` edited it, three migrations preserved/relocated it (j6cs's `1.9.7-to-1.10.0` relocated `spec` directives into `apply`), yet no skill or binary consumed it: directives placed there were silently ignored. Worse, `fab init` stamps `fab_version` past all migrations, so freshly scaffolded projects kept the zombie block permanently — including a `[NEEDS CLARIFICATION]` directive that contradicted the intake-only marker rule (post-1.10.0) and a GIVEN/WHEN/THEN directive redundant with `_generation.md`'s mandated scenarios. Disposition: remove-dead (the wire alternative — having `_generation`/`_review` consume `stage_directives.{stage}` — would have added prompt surface to every generation for a feature nobody had ever successfully used). The scaffold no longer seeds it, `/fab-setup config` no longer offers it, the always-load descriptor no longer mentions it, and migration `2.1.6-to-2.2.0.md` drops it (plus a defensive `model_tiers`) from existing user configs — see [migrations.md](/distribution/migrations.md). No directive was relocated; nothing replaces the key.
Expand Down
2 changes: 1 addition & 1 deletion docs/memory/_shared/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ 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-16 |
| [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-25 |
| [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 <stage>`, 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 enum opus/sonnet/haiku/fable, resolved with `fab resolve-agent <stage> --alias` so the alias is emitted directly — the deterministic adapter that superseded m3d4's prompt-side id→alias hand-map; yky7), 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-19 |
7 changes: 4 additions & 3 deletions docs/memory/pipeline/execution-skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ Each prefix step (the `_intake` Create-Intake Procedure, `/fab-switch`, `/git-br

**PR type system**: `/git-pr` supports 7 PR types (feat, fix, refactor, docs, test, ci, chore) derived from Conventional Commits. Types are resolved via a four-step chain: explicit argument → read from `.status.yaml` → infer from fab change intake → infer from diff. The type controls the PR title prefix (`{type}: {title}`). The PR body has two layers: an agent-generated `## Summary` + optional `## Changes` (prose synthesized from `intake.md`), and a mechanically-rendered `## Meta` block. Title derivation uses intake heading when available, commit subject otherwise, regardless of type.

**Mechanical `## Meta` block via `fab pr-meta`** (rj31): As of rj31, `/git-pr` Step 3c no longer assembles the `## Meta` block from natural-language formatting prose. The entire block — the 5-column table (`ID | Type | Confidence | Plan | Review`), the `**Pipeline**:` line (six fixed-order stages with ` ✓` per `done` stage and hyperlinked intake/apply labels), the optional `**Issues**:` line, and the multi-form `**Impact**:` line — is rendered deterministically by the `fab pr-meta <change> --type <type> [--issues "<space-joined IDs>"]` subcommand, whose stdout the skill pastes verbatim (omitting the block on non-zero exit / empty stdout, matching the legacy `{has_fab} = false` path). Type resolution (Step 0b), issue gathering (Step 1), and the agent-generated Summary/Changes stay skill-side; the progress token remains `✓ body — meta + summary + changes`. `fab pr-meta` is self-contained — it reads `.status.yaml`, parses `plan.md` task checkboxes, reads config (`true_impact_exclude`, `test_paths`, `project.linear_workspace`), computes impact via `internal/impact`, and resolves git/`gh` context (branch, owner/repo, merge-base) itself; the skill passes only `<change>`, `--type`, and optional `--issues`. This moves the Meta block's determinism into Go (tested via golden-output tests), eliminating per-run drift (e.g., the dropped exclude-list backticks the prose previously produced). `gh` failure degrades to plain-text Pipeline labels; a missing merge-base drops only the Impact line — never a hard `/git-pr` failure. `/git-pr` no longer calls `fab impact` directly (the subcommand remains public for other consumers). Blob URLs use `https://github.com/{owner}/{repo}/blob/{branch}/...` to resolve against the feature branch instead of main. See [schemas.md](/pipeline/schemas.md) for the `true_impact` render-time `impl` residual and [the `fab pr-meta` CLI reference](../../../src/kit/skills/_cli-fab.md) for the full signature, output contract, and exit codes.
**Mechanical `## Meta` block via `fab pr-meta`** (rj31): As of rj31, `/git-pr` Step 3c no longer assembles the `## Meta` block from natural-language formatting prose. The entire block — the 5-column top table (`Change ID | Type | Confidence | Plan | Review`), the `**Impact**:` table + caption, the optional `**Issues**:` line, and the `**Pipeline:**` line (six fixed-order stages with ` ✓` per `done` stage and hyperlinked intake/apply labels) — is rendered deterministically by the `fab pr-meta <change> --type <type> [--issues "<space-joined IDs>"]` subcommand, whose stdout the skill pastes verbatim (omitting the block on non-zero exit / empty stdout, matching the legacy `{has_fab} = false` path). **Element order (260625-pnao layout revision):** heading → top table → Impact table + caption → optional Issues → **Pipeline (LAST)** — Pipeline moved from before Impact to the final element, with the optional Issues line sitting just above it. Each table/paragraph is blank-line separated so GitHub renders them distinctly. The top table's first header is **`Change ID`** (was `ID`); a present id is backtick-wrapped (`` `pnao` ``), the empty-fallback `—` stays bare. `**Pipeline:**` carries the colon **inside** the bold span. **Impact is one normalized, self-labeling table** (pnao, replacing the prior multi-form rendering): a single `Impact | +/− | Net` table — the compact `+/−` is the *column header* only, while the data rows carry spaced `+A / −B` figures; the first column header is `Impact` (was `Scope`), so the table needs **no separate `**Impact**:` lead-in line**. Numeric columns are right-aligned. It carries the fixed taxonomy `raw / true / impl / tests / excluded` where `raw = true + excluded` and `true = impl + tests`. `true` is ALWAYS the post-exclude diff (the bold row, always present) — fixing the prior "total flips meaning" bug where the same label denoted the raw diff in one form and the post-exclude diff in another. The table adapts by DROPPING rows, never reshaping: the `raw` row appears only when it differs from `true` (excludes engaged), and nested `└ impl` / `└ tests` rows appear only when a tests pair is present. Below the table a `<sub>` (small-text, NOT italic) provenance caption co-locates the excludes note and the fab-kit **binary** version stamp — `<sub>excludes `fab/`, `docs/` · generated by fab-kit vX.Y.Z</sub>` (excludes clause omitted when none configured; `vdev` rendered honestly on dev builds). Emphasis stays bold-only and the caption uses `<sub>` because GitHub's Markdown sanitizer strips `style`/`class` (so row backgrounds / colored numbers are impossible) but keeps `<sub>` on its HTML allowlist. Type resolution (Step 0b), issue gathering (Step 1), and the agent-generated Summary/Changes stay skill-side; the progress token remains `✓ body — meta + summary + changes`. `fab pr-meta` is self-contained — it reads `.status.yaml`, parses `plan.md` task checkboxes, reads config (`true_impact_exclude`, `test_paths`, `project.linear_workspace`), computes impact via `internal/impact`, and resolves git/`gh` context (branch, owner/repo, merge-base) itself; the skill passes only `<change>`, `--type`, and optional `--issues`. This moves the Meta block's determinism into Go (tested via golden-output tests), eliminating per-run drift (e.g., the dropped exclude-list backticks the prose previously produced). `gh` failure degrades to plain-text Pipeline labels; a missing merge-base drops only the Impact block — never a hard `/git-pr` failure. `/git-pr` no longer calls `fab impact` directly (the subcommand remains public for other consumers). Blob URLs use `https://github.com/{owner}/{repo}/blob/{branch}/...` to resolve against the feature branch instead of main. See [schemas.md](/pipeline/schemas.md) for the `true_impact` render-time `impl` residual and [the `fab pr-meta` CLI reference](../../../src/kit/skills/_cli-fab.md) for the full signature, output contract, and exit codes.

**PR change metadata**: Change identity and linked issues are part of the mechanically-rendered `## Meta` block (see "Mechanical `## Meta` block via `fab pr-meta`" above). The Meta table's `ID` cell carries the 4-char change ID from `.status.yaml`; the optional `**Issues**:` line (rendered only when the skill passes a non-empty `--issues`, gathered via `fab status get-issues`) renders each ID as a Linear hyperlink `https://linear.app/{linear_workspace}/issue/{ISSUE_ID}` when `project.linear_workspace` is configured, bare comma-joined IDs otherwise, positioned below Pipeline and above Impact. Missing fields show `—`. When the change cannot be resolved, `fab pr-meta` exits non-zero and the entire Meta block is omitted.
**PR change metadata**: Change identity and linked issues are part of the mechanically-rendered `## Meta` block (see "Mechanical `## Meta` block via `fab pr-meta`" above). The Meta table's `Change ID` cell carries the 4-char change ID from `.status.yaml` (backtick-wrapped when present; bare `—` when absent); the optional `**Issues**:` line (rendered only when the skill passes a non-empty `--issues`, gathered via `fab status get-issues`) renders each ID as a Linear hyperlink `https://linear.app/{linear_workspace}/issue/{ISSUE_ID}` when `project.linear_workspace` is configured, bare comma-joined IDs otherwise, positioned below the Impact table + caption and above Pipeline (the pnao layout revision made Pipeline the last element). Missing fields show `—`. When the change cannot be resolved, `fab pr-meta` exits non-zero and the entire Meta block is omitted.

## Requirements

Expand Down Expand Up @@ -366,12 +366,13 @@ Steps execute 1→3 for safety. If interrupted, re-run detects folder already in
*Introduced by*: 260213-jc0u-split-archive-hydrate

### Unified PR Template with Conditional Field Population
**Decision**: `/git-pr` uses a single unified PR body template for all PR types. Fab-linked fields are conditionally populated based on artifact availability — whether the change resolves and artifacts exist — not on PR type. As of rj31 these fields live in a mechanically-rendered `## Meta` block (5-column table `ID | Type | Confidence | Plan | Review`, `**Pipeline**:` line, optional `**Issues**:` line, multi-form `**Impact**:` line) produced by `fab pr-meta`, with `—` for unavailable cells; the whole block is omitted when the change can't be resolved. The agent-generated `## Summary` / `## Changes` prose stays type-agnostic too.
**Decision**: `/git-pr` uses a single unified PR body template for all PR types. Fab-linked fields are conditionally populated based on artifact availability — whether the change resolves and artifacts exist — not on PR type. As of rj31 these fields live in a mechanically-rendered `## Meta` block (5-column top table `Change ID | Type | Confidence | Plan | Review`, a self-labeling single-table `Impact | +/− | Net` block + `<sub>` caption, an optional `**Issues**:` line, and the `**Pipeline:**` line last — normalized in pnao, with the 260625 layout revision making Pipeline the final element) produced by `fab pr-meta`, with `—` for unavailable cells; the whole block is omitted when the change can't be resolved. The agent-generated `## Summary` / `## Changes` prose stays type-agnostic too.
**Why**: A `test` or `docs` change that went through the full fab pipeline deserves the same quality signals as a `feat` change. Gating template richness on type hid real planning work and reduced reviewer confidence. The unified template always shows the same structure, reducing cognitive overhead. Mechanizing the Meta block (rj31) further guarantees the structure is byte-for-byte identical across runs.
**Rejected**: Two-tier templates gated on type (hides work for non-feat types), keep type-gating but extend Tier 1 to all types (still requires two code paths), omit columns when empty (inconsistent table shape across PRs). For the rj31 mechanization: pushing the *whole* PR body into Go (would force Summary/Changes into mechanical text extraction, degrading prose quality), and a flags-only formatter (leaves the skill re-deriving git/gh inputs each run — partial drift remains).
*Introduced by*: 260305-b0xs-unified-pr-template
*Supersedes*: 260225-54vl-smart-git-pr-category-taxonomy (Two-Tier PR Templates with Type Resolution)
*Updated by*: 260604-rj31-mechanical-pr-meta (the `## Meta` block — table, Pipeline, Issues, Impact — is now rendered by `fab pr-meta`, replacing the inlined Step 3c formatting prose; the former "Stats table" five columns are now `ID | Type | Confidence | Plan | Review`)
*Updated by*: 260625-pnao-normalize-pr-meta-impact-block (the Meta Impact block is now a single self-labeling `Impact | +/− | Net` table with the `raw / true / impl / tests / excluded` taxonomy and a `<sub>` provenance caption stamping the fab-kit binary version, replacing the prior three render shapes; `true` is always the post-exclude diff, fixing the "total flips meaning" bug. The layout revision in the same change renamed the top-table header `ID` → `Change ID` (id backtick-wrapped), dropped the `**Impact**:` lead-in, and reordered the block to heading → top table → Impact + caption → optional Issues → `**Pipeline:**` last)

### Execution Stage Reset Preserves Artifacts and Checkboxes
**Decision**: `/fab-continue apply` re-runs apply behavior starting from the first unchecked task. It does NOT uncheck all tasks. After qszh, `fab status reset apply` also preserves `plan.md` on disk — reset modifies `.status.yaml` state only; artifact files persist. The apply entry's plan-generation sub-step is idempotent on `plan.md` presence and is skipped when the file exists. To force plan regeneration the user MUST delete `plan.md` before re-running.
Expand Down
Loading
Loading