UNPKG

paqad-ai

Version:

Spec-driven development framework — AI agents that think before they type

87 lines (63 loc) • 5.51 kB
--- name: module-attribution-extractor description: 'Runs the deterministic module-attribution extractor over a prompt and surfaces MD-XXXX drafts through the Decision Pause Contract, as the first half of the Attribution Gate in feature-development planning. Use when planning starts, when a pasted ticket needs a first pass before the inferencer, or when a prompt names a module.' metadata: model_tier: fast cacheable: 'false' output_format: markdown paqad-meta: skill.meta.yaml --- ## What It Does Applies the framework-owned pattern set (`Module:` / `Component:` / `Area:` / `Subsystem:` ticket headers; `module: <slug>`; `new module <Name>`; `in the <name> module`) to the prompt. Emits one candidate per distinct slug, classified as `exact-match`, `near-collision` (Levenshtein ≤ 2 against an existing slug), or `unknown`. Multiple modules in a single prompt produce one MD-XXXX draft each (spec AC #9). ## Use This When - Running the Attribution Gate at the start of `feature-development.planning`. - A user pastes a ticket and you need a deterministic first pass before invoking the inferencer. - A user prompt explicitly names a module (`module: billing`, `Subsystem: Reporting`). ## Inputs - `prompt`: required. The user-supplied text (typically a ticket body or feature request). - `project_root`: optional. Used to resolve `module-map.yml` for collision detection. Defaults to `cwd`. - The pattern set itself is closed and lives in `references/pattern-set.md`. ## Procedure 1. Resolve the project root (default `cwd`). 2. Invoke the TS engine via the bundled wrapper: ``` bash scripts/extract.sh <prompt-file> [project-root] ``` The wrapper shells out to `paqad-ai module-decisions extract --project-root <root> --prompt-file <tmp>`. Prefer `--prompt-file` (the wrapper always uses it) so multiline ticket text survives the shell. 3. Parse the emitted JSON. Each entry in `candidates` has: `slug`, `display_name`, `kind`, `collision_with`, `pattern`, `excerpt`. 4. If `needs_decision` is empty, the extractor either found nothing or every hit was an `exact-match`: exit with the literal status `extractor: no-decision-needed` so the caller can fall through to the inferencer (the empty case) or continue planning (the exact-match case). 5. Otherwise, for each candidate in `needs_decision`, surface a Decision Pause packet via `AskUserQuestion` (or the active adapter's Decision Pause Contract entry point). ## Decision Pause Packet Shape For each candidate that needs a decision, the packet must include: - **Question**: the user-visible question. For `unknown`: `Confirm new module "<display_name>" (slug: <slug>)?`. For `near-collision`: `"<slug>" is suspiciously close to existing module "<collision_with>"; confirm which one this is.` - **Header**: short tag, e.g. `New module`, `Slug collision`. - **Options** (2–4): - **Accept new module `<slug>`**: proceed to atomic apply via `src/module-decisions/apply.ts`. - For near-collision: **Use existing `<collision_with>`**, resolve to the existing slug; mark the draft `superseded`. - **Rename**: collect a new slug from the user; loop back through `extract`. - **Reject**: mark the draft `rejected`; no module-map mutation. The packet's selected option drives the state transition. Only an explicit `Accept` response promotes the MD-XXXX from `draft` → `proposed` → `accepted`. No silent acceptance. ## Output Contract - Zero or more MD-XXXX YAML files written under `.paqad/decisions/module-decisions/<id>.yml` (one per candidate that needs a decision). - A `Workflow Status` block listing what was extracted and what is awaiting the user. - On accepted decisions only: `apply.ts` performs the snapshot → temp-write → rename → record update → events append. **Do not write `module-map.yml` from this skill directly.** - When the extractor returns no candidates, emit the literal status `extractor: no-decision-needed` so the caller can fall through to `module-attribution-inferencer`. ## Escalate / Stop Conditions - Stop and surface the packet if `kind === 'near-collision'`: never auto-resolve to either side. - Stop if the same slug appears in two different patterns with conflicting display names: ask which name to use. - Refuse to write any MD-XXXX file if the project has no `.paqad/` directory (project is not onboarded). ## Scripts Deterministic plumbing: do **not** re-derive these in the LLM layer. - `scripts/extract.sh <prompt-file> [project-root]`: invoke the extractor; prints the JSON report. - `scripts/needs-decision.sh [report.json|-]`: exit 0 if a Decision Pause packet is needed, exit 1 otherwise (with `extractor: no-decision-needed` on stderr). - `scripts/filter-by-kind.sh <exact-match|near-collision|unknown> [report.json|-]`: filter `candidates` by kind; prints a JSON array. ## Assets - `assets/templates/packet-unknown.md`: Decision Pause packet template for an `unknown` candidate. - `assets/templates/packet-collision.md`: Decision Pause packet template for a `near-collision` candidate. ## Resources - `references/pattern-set.md`: bundled pattern reference. - `runtime/base/skills/module-attribution-extractor/agents/openai.yaml`: agent interface metadata. - `src/module-decisions/extractor.ts`: pattern set + classification. - `src/module-decisions/schema.ts`: MD-XXXX state machine. - `src/module-decisions/apply.ts`: atomic apply path (only writer of `module-map.yml`). - the Decision Pause Contract (in the framework bootstrap): packet semantics.