Files
ss-tools/.opencode/command/speckit.plan.md
busya d874a4dca6 feat(speckit): workflow architecture upgrades + rework 038 spec per new flow
Add prototype/openapi/validate/resume commands, wire edge-failure matrix into UX,
enforce traceability + validation gates, mandate C4/C5 belief-runtime verification.
Rework 038-dashboard-scenario-model artifacts: applicability, structured edge cases,
24-class UX state matrix, interactive HTML prototype, standardized OpenAPI 3.1 (7 ops),
full RTM with coverage gate, 56-task backlog, and PASS validation report.
2026-07-31 11:25:43 +03:00

443 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: Execute the implementation planning workflow for superset-tools (Python backend + Svelte frontend) and generate research, design, contracts, traceability, and quickstart artifacts.
handoffs:
- label: Create Tasks
agent: speckit.tasks
prompt: Break the plan into executable tasks for Python/Svelte implementation
send: true
- label: Create Checklist
agent: speckit.checklist
prompt: Create a requirements-quality checklist for the active feature
---
## User Input
```text
$ARGUMENTS
```
You **MUST** consider the user input before proceeding (if not empty).
## Outline
1. **Setup**: Run `.specify/scripts/bash/setup-plan.sh --json` from repo root and parse `FEATURE_SPEC`, `IMPL_PLAN`, `SPECS_DIR`, and `BRANCH`.
- `IMPL_PLAN` is the authoritative path for `plan.md` inside `specs/<feature>/`.
- Derive `FEATURE_DIR` from `IMPL_PLAN` and write every planning artifact there.
- Never treat `.kilo/plans/*` as workflow output for `/speckit.plan`.
2. **Load canonical planning context**:
- `README.md`
- `requirements.txt` (backend dependencies)
- `frontend/package.json` (frontend dependencies)
- `.specify/memory/constitution.md`
- `.opencode/skills/semantics-core/SKILL.md`
- `.opencode/skills/semantics-contracts/SKILL.md`
- `.opencode/skills/semantics-python/SKILL.md`
- `.opencode/skills/semantics-svelte/SKILL.md`
- `.opencode/skills/semantics-testing/SKILL.md`
- `.specify/templates/plan-template.md`
- `FEATURE_DIR/contracts/ux/screen-models.md` (if `/speckit.ux` was run)
- `FEATURE_DIR/contracts/ux/api-ux.md` (if `/speckit.ux` was run)
- `FEATURE_DIR/contracts/ux/*-ux.md` (per-screen UX contracts)
- `FEATURE_DIR/prototype/manifest.md` (if `/speckit.prototype` was run)
- `FEATURE_DIR/contracts/openapi.yaml` (if `/speckit.openapi` was run)
- `FEATURE_DIR/contracts/openapi-traceability.md` (if `/speckit.openapi` was run)
- relevant `docs/adr/*.md`
3. **Execute the planning workflow** using the template structure:
- Fill `Technical Context` for the current repository reality: Python 3.9+/FastAPI backend, SvelteKit 5/Tailwind frontend, PostgreSQL, Docker, semantic contracts, belief runtime.
- Fill `Constitution Check` using the local constitution.
- ERROR if a blocking constitutional or semantic conflict is discovered and cannot be justified.
- Phase 0: generate `research.md` in `FEATURE_DIR`, resolving all material unknowns.
- Phase 1: generate `data-model.md`, `contracts/modules.md`, optional machine-readable contract artifacts, `quickstart.md`, and `traceability.md` in `FEATURE_DIR`.
- Phase 1: `traceability.md` is REQUIRED for every feature — a requirements traceability matrix mapping Story/Requirement → UX screen+state → Screen Model → API operationId → contract → task → test. Every row carries explicit rationale for N/A cells. Include a coverage gate.
- Materialize blocking ADR references and planning decisions inside the plan and downstream contracts.
- Run `.specify/scripts/bash/update-agent-context.sh kilocode` after planning artifacts are written.
4. **Stop and report** after planning artifacts are complete. Report branch, `plan.md` path, generated artifacts (including `traceability.md` with coverage gate status), prototype/openapi artifact references (if generated upstream), and blocking ADR/decision-memory outcomes.
## Phase 0: Research
Research must resolve only implementation-shaping unknowns that matter for this repository, such as:
- module placement under `backend/src/` or `frontend/src/`
- **Screen Model topology**: which screens need a `[TYPE Model]` (`.svelte.ts`), which atoms each model declares, which invariants cross widget boundaries
- API endpoint design (REST routes, WebSocket channels)
- database schema changes (SQLAlchemy models, migrations)
- Svelte component hierarchy and store topology
- async task orchestration patterns
- **TypeScript DTO alignment**: frontend `types/` matching backend Pydantic schemas
- test strategy (pytest + vitest; L1 model invariants without render + L2 UX contracts with render)
- belief runtime instrumentation for C4/C5 flows
- semantic validation boundaries and static verification workflow
**If `/speckit.ux` was run before plan:**
- `screen-models.md` defines Model inventory → use directly, don't re-discover
- `api-ux.md` defines API shapes → use as @DATA_CONTRACT source for backend Pydantic schemas
- `<screen>-ux.md` defines UX contracts → use as @UX_STATE/@UX_FEEDBACK source for component contracts
- Generated `.svelte.ts` model files in `frontend/src/lib/models/` → DO NOT regenerate; reference them via `@RELATION BINDS_TO` from component contracts
Write `research.md` with concise sections:
- Decision
- Rationale
- Alternatives Considered
- Impact On Contracts / Tasks
Use `[NEED_CONTEXT: target]` instead of inventing relation targets, DTO names, or module boundaries that cannot be grounded in repo context.
## Phase 1: Design, ADR Continuity, and Contracts
### Frontend Model & Component Reuse Scan (MANDATORY — before contract generation)
Before designing any new screen, execute a **model-first inventory scan** followed by a **component inventory scan** of the existing codebase to maximise reuse and prevent duplicate primitives.
**Step 1: Screen Model scan** (use a subagent with `subagent_type: "explore"`):
- Search `frontend/src/lib/models/` for existing `[TYPE Model]` contracts
- Use `axiom_semantic_discovery search_contracts type="Model" query="<domain>"` for structured search
- Check model atoms, actions, and invariants — reuse if the screen state maps to an existing model
- New models use `.svelte.ts` extension, `[TYPE Model]` contract, `@STATE`/`@ACTION`/`@INVARIANT` tags
**Step 2: Component scan** (priority order):
**Scan targets** (priority order):
1. `frontend/src/lib/ui/` — design-system atoms: `Button.svelte`, `Select.svelte`, `Input.svelte`, `Card.svelte`
2. `frontend/src/lib/components/ui/` — composite UI widgets: `SearchableMultiSelect.svelte`, `MultiSelect.svelte`
3. `frontend/src/lib/components/` — feature components that may be adaptable
4. Inline patterns in existing pages (`frontend/src/routes/`) — badges, skeletons, empty states, collapsibles
**For each found component, the scan MUST return:**
- Exact file path
- Props interface (what it accepts)
- Whether it's a direct fit, adaptable, or pattern-only
**Reuse decision tree:**
| Situation | Action |
|-----------|--------|
| Component exists and fits | `@RELATION DEPENDS_ON -> [ExistingComponent]` — zero new code |
| Pattern exists (badge, skeleton, tooltip) | Document the Tailwind classes to replicate; no component extraction |
| No reusable asset exists | Create new component only then |
**Output:** The `contracts/modules.md` for every frontend contract MUST include `@RELATION` edges to reused components/models and a `@RATIONALE` noting WHY the asset is reused rather than rebuilt. For pattern-only reuse, the contract MUST reference the source page/file where the pattern was observed. Components that bind to a Screen Model declare `@RELATION BINDS_TO -> [ModelId]`.
**Forbidden patterns:**
- Creating a new `<Modal>` when `confirm()` suffices
- Building a custom `<Select>` when `$lib/ui/Select.svelte` exists
- Inventing a `<Toast>` system when `addToast()` from `$lib/toasts.js` is already wired
### UX / Interaction Validation
Validate the proposed design against `ux_reference.md` as an **interaction reference** for operators, API callers, CLI/operator flows, result envelopes, warnings, recovery guidance, and (when applicable) browser-based UI flows.
If the planned architecture degrades the promised interaction model, deterministic recovery path, or context-budget behavior, stop and warn the user.
### Attention Compliance Gate (MANDATORY — before generating contracts)
Every contract in `contracts/modules.md` MUST pass these checks. Contracts that fail are invisible to the model after context compression (per `semantics-core` §VIII):
| Rule | Check | Failure Consequence |
|------|-------|---------------------|
| **ATTN_1** | First anchor line: `#region Domain.Sub.Name [C:N] [TYPE Type] [SEMANTICS tag1,tag2]` — all on ONE line | CSA 4× pooling loses detail from multi-line anchors |
| **ATTN_2** | IDs are hierarchical: `Core.Auth.Login`, not `login_handler` | HCA 128× makes flat IDs indistinguishable from noise |
| **ATTN_3** | All contracts in a domain share primary `@SEMANTICS` keyword (e.g., all auth contracts use `[SEMANTICS auth, ...]`) | DSA Lightning Indexer fails to group domain contracts |
| **ATTN_4** | Contract ≤150 lines, module ≤400 lines | Contracts exceeding the sliding window are partially invisible |
**Cross-stack compliance (fullstack features only):**
- Backend Pydantic schema contract and frontend TypeScript DTO contract MUST have matching `@RELATION` edges crossing the stack boundary.
- Both MUST share at least one `@SEMANTICS` keyword so the DSA Indexer can link them.
### Data Model Output
Generate `data-model.md` for superset-tools domain entities such as:
- Pydantic request/response schemas
- SQLAlchemy models and relationships
- WebSocket message formats
- Task state transitions
- Git operation entities
- Plugin configuration schemas
- **Frontend TypeScript DTOs** in `frontend/src/types/` — MUST match backend Pydantic schemas across the stack boundary
- **Screen Model interfaces** — typed atoms, FSM state unions, action payloads for `.svelte.ts` models
### Global ADR Continuity
Before task decomposition, planning must identify any repo-shaping decisions this feature depends on or extends:
- Python module layout and decomposition
- FastAPI route organization
- SvelteKit routing and component hierarchy
- **Screen Model topology**: which screens need a model, model-atom boundaries, invariant scope
- belief-state runtime behavior (JSON structured logging / console markers)
- semantic comment-anchor rules
- **TypeScript-first frontend architecture** (`.svelte.ts` models, typed props, typed API boundaries)
- payload/schema stability decisions
### Contract Design Output
Generate `contracts/modules.md` as the primary design contract for implementation. Contracts must:
- use short hierarchical semantic IDs with 2-3 levels: `Domain.Name` (e.g., `Core.Auth.Login`, `Api.Dashboards`, `Users.ListModel`, `Test.Migration.RunTask`). NOT flat IDs like `login_handler` or `UserListModel`.
- classify each planned module/component/model with `[C:N]` complexity in the `#region` anchor (NOT `@COMPLEXITY N`)
- use canonical anchor syntax: `#region Id [C:N] [TYPE TypeName] [SEMANTICS tags]` / `#endregion Id`
- use canonical relation syntax `@RELATION PREDICATE -> TARGET_ID`
- preserve accepted-path and rejected-path memory via `@RATIONALE` and `@REJECTED` where needed
- describe Python modules, FastAPI routes, Svelte components, **Screen Models** (`.svelte.ts`), stores, and services instead of inventing MCP/backend layers
Complexity guidance for this repository:
- **C1**: anchors only (DTOs, simple Pydantic schemas, pure constants)
- **C2**: typically adds `@BRIEF` (pure functions, utility helpers)
- **C3**: typically adds `@RELATION` (service modules, route handlers); Svelte components also `@UX_STATE`
- **C4**: typically adds `@PRE`, `@POST`, `@SIDE_EFFECT`; **Screen Models** also `@STATE`, `@ACTION`, `@INVARIANT`; orchestration paths should account for belief runtime markers
- **C5**: C4 + `@DATA_CONTRACT`, `@INVARIANT`, and explicit decision-memory continuity (`@RATIONALE`/`@REJECTED`)
### Function-Level Contracts for C3+ (MANDATORY for cross-stack and orchestration)
For every C3+ function, method, or Screen Model action that is:
- An API endpoint (FastAPI route handler)
- A Screen Model action with `@SIDE_EFFECT`
- A C4/C5 orchestration function (migration runner, task executor, auth flow)
Generate its full `#region` header in `contracts/modules.md` under its parent module. This header becomes the implementation contract that the coding agent MUST satisfy.
**Minimal header for C3 API endpoints:**
```
#region Domain.Resource.Action [C:3] [TYPE Function] [SEMANTICS domain,action]
# @ingroup Domain
# @BRIEF One-line purpose.
# @RELATION DEPENDS_ON -> [DependencyService]
# @RELATION DEPENDS_ON -> [DTO:RequestSchema]
# #endregion Domain.Resource.Action
```
**Full header for C4/C5 orchestration & cross-stack functions:**
```
#region Domain.Resource.Action [C:4] [TYPE Function] [SEMANTICS domain,action]
# @ingroup Domain
# @BRIEF One-line purpose.
# @PRE Precondition 1 (verifiable by guard clause).
# @POST Output guarantee 1 (testable assertion).
# @SIDE_EFFECT State mutation, I/O, or external call.
# @SIDE_EFFECT Logging (REASON/REFLECT/EXPLORE markers required).
# @RELATION DEPENDS_ON -> [ServiceDependency]
# @RELATION DEPENDS_ON -> [DTO:InputSchema]
# @DATA_CONTRACT InputDTO -> OutputDTO
# @RATIONALE Why this implementation approach.
# @REJECTED What alternative was considered and forbidden.
# @TEST_EDGE: scenario_name -> Expected failure behavior.
# #endregion Domain.Resource.Action
```
**Screen Model actions (Svelte `.svelte.ts`):**
```
// #region ScreenModel.actionName [C:4] [TYPE Function] [SEMANTICS domain,action]
// @BRIEF What this action does.
// @ACTION Public action — callable from components.
// @PRE Guards before execution.
// @POST State guarantees after completion.
// @SIDE_EFFECT API call, store mutation, model state update.
// @RELATION CALLS -> [apiClient]
// @TEST_EDGE: network_failure -> ScreenState = "error"
// #endregion ScreenModel.actionName
```
**Rules:**
- Function contract headers are **NOT implementation** — they are design contracts. The coding agent implements the body.
- C1/C2 functions do NOT need pre-generated contracts — only C3+.
- `@TEST_EDGE` declarations enable qa-tester to write tests BEFORE implementation (true TDD).
- `@DATA_CONTRACT` on API endpoints enables fullstack-coder to align frontend TypeScript DTOs.
- `@SIDE_EFFECT` with belief runtime markers ensures molecular CoT logging is wired from day one.
- Cross-stack functions MUST have matching `@DATA_CONTRACT` on both backend and frontend sides.
- All contracts MUST pass the Attention Compliance Gate (ATTN_1-4) above.
If a planned contract depends on unknown schema, relation target, or ADR identity, emit `[NEED_CONTEXT: target]` instead of fabricating placeholders.
### Fixture Generation (MANDATORY for C3+ contracts with @TEST_EDGE)
For every C3+ contract that declares `@TEST_EDGE`, `@POST`, or `@REJECTED` guardrails, generate **canonical test fixtures** in `FEATURE_DIR/fixtures/`. Canonical fixtures live beside the spec — they are the design-time source of truth. Executable fixtures are materialized into `tests/` later by `/speckit.tasks`.
**Output structure:**
```text
specs/<feature>/fixtures/
├── manifest.md # Fixture index with GRACE contracts
├── api/
│ ├── <contract>_valid.json
│ ├── <contract>_missing_field.json
│ ├── <contract>_invalid_type.json
│ ├── <contract>_external_fail.json
│ └── <contract>_rejected_path.json
└── model/
├── <model>_valid.json
├── <model>_edge_case.json
└── <model>_invariant.json
```
**`manifest.md` — fixture index with GRACE contracts:**
```markdown
#region Example.Fixturemanifest [C:3] [TYPE ADR] [SEMANTICS test,fixture,[DOMAIN]]
@defgroup Fixtures Canonical test fixtures for [FEATURE].
## @{ Fixture FX_Auth.Login.Valid [C:2] [TYPE Block] [SEMANTICS test,auth,fixture]
@BRIEF Valid login request/response pair.
@RELATION VERIFIES -> [Api.Auth.Login]
@TEST_FIXTURE: valid_login -> fixtures/api/auth_login_valid.json
@TEST_INVARIANT: TokenIssued -> VERIFIED_BY: [Test.Api.Auth]
## @} Fixture FX_Auth.Login.Valid
## @{ Fixture FX_Auth.Login.MissingPassword [C:2] [TYPE Block] [SEMANTICS test,auth,fixture]
@BRIEF Missing password field — @TEST_EDGE: missing_field.
@RELATION VERIFIES -> [Api.Auth.Login]
@TEST_EDGE: missing_field -> 422 VALIDATION_ERROR
@TEST_FIXTURE: missing_password -> fixtures/api/auth_login_missing_field.json
## @} Fixture FX_Auth.Login.MissingPassword
## @{ Fixture FX_Migration.EnvReset [C:2] [TYPE Block] [SEMANTICS test,migration,fixture]
@BRIEF Model invariant: changing source env resets selection.
@RELATION VERIFIES -> [Migration.Model]
@TEST_INVARIANT: env_reset_selection -> VERIFIED_BY: [Test.Migration.Model]
@TEST_FIXTURE: env_reset -> fixtures/model/migration_env_reset.json
## @} Fixture FX_Migration.EnvReset
## @} FX_Migration.EnvReset
## @} FX_Auth.Login.MissingPassword
## @} FX_Auth.Login.Valid
# #endregion Example.Fixturemanifest
```
**JSON fixture format:**
```json
{
"fixture_id": "FX_Auth.Login.MissingPassword",
"verifies": "Api.Auth.Login",
"edge": "missing_field",
"input": {
"username": "admin"
},
"expected": {
"status": 422,
"error_code": "VALIDATION_ERROR",
"error_detail": "Field 'password' is required"
}
}
```
**Generation rules:**
- **One JSON file per fixture** — named `<contract_snake>_<edge>.json`
- **Minimum 5 per C3+ contract**: valid, missing_field, invalid_type, external_fail, rejected_path
- **Expected values ALWAYS hardcoded** — never derived from implementation (anti-tautology)
- **Input values are concrete** — real strings, numbers, objects, not pseudocode
- **Fixture ID format**: `FX_<Domain>.<Name>` — hierarchical, matches contract hierarchy
- **@TEST_FIXTURE in manifest** points to the JSON file path
- **@RELATION VERIFIES** links fixture to production contract
- For `@REJECTED` paths: expected MUST include error/failure, proving the path is unreachable
- For model invariants: input = state before action, expected = state after action
- Do NOT generate executable test files here — only canonical JSON fixtures
### Fixture Traceability
Extend `traceability.md` with a Fixture column:
| Story | Model | Fixture | Task | Test |
|-------|-------|---------|------|------|
| US1 | Api.Auth.Login | FX_Auth.Login.Valid | T017 | Test.Api.Auth
### Quickstart Output
Generate `quickstart.md` using real repository verification paths via the root Makefile (timeout-protected, tiered):
```bash
# Tier 1: Fast unit tests (<120s, no Docker)
make test # backend + frontend unit tests
make test-unit # backend SQLite tests only
make test-frontend # frontend vitest tests only
# Tier 2: Smart selection
make test-related F=backend/src/path/to/file.py # only tests linked via @RELATION BINDS_TO
# Tier 3: Integration tests (Docker required, <600s)
make test-integration
# Coverage
make coverage # backend pytest-cov + frontend vitest v8
# Linting
make lint # ruff + eslint
# Docker
docker compose up --build
```
### Traceability Matrix Output
Generate `traceability.md` — a requirements traceability matrix (RTM) for EVERY feature, mapping every user story through its implementation chain. Use the format below. Every cell with N/A MUST include a brief rationale (e.g., "N/A — backend-only, no UI surface"). Include a coverage gate at the end.
```markdown
#region Std.Opencode.Traceability [C:3] [TYPE ADR] [SEMANTICS traceability,rtm,[DOMAIN]]
@defgroup Trace Matrix Requirements → Screen+State → Model → API → Contract → Task → Test for [FEATURE].
## Applicability
- **Feature type**: [Backend-only / Frontend-only / Fullstack]
- **UI surface**: [Yes / No — if No, UX and prototype columns are N/A throughout]
- **API surface**: [Yes / No — if No, API and OpenAPI columns are N/A throughout]
## Traceability Matrix
| Story / Req | UX Screen + State | Screen Model | API operationId | Contract | Backend Task | Frontend Task | Test |
|------------|-------------------|-------------|-----------------|----------|-------------|--------------|------|
| US1: [Title] | /route (loaded) | Domain.Model | listResources | Api.Resources.List | T017 | T015 | Test.Api.Resources |
| US1: [Title] | /route (error) | Domain.Model | listResources | Api.Resources.List | T017 | T016 | Test.Api.Resources.Edge |
| [DOMAIN]-FR-001 | N/A — infra, no UI | N/A — infra | N/A — no API | Core.Config | T004 | N/A — backend-only | Test.Core.Config |
| US2: [Title] | /migration (idle) | Migration.Model | startMigration | Api.Migration.Start | T020 | T022 | Test.Migration |
| US2: [Title] | /migration (NET_02 timeout) | Migration.Model | startMigration | Api.Migration.Start | T021 | T023 | Test.Migration.Timeout |
### N/A Rationale Key
- **N/A — backend-only**: Feature has no UI surface
- **N/A — frontend-only**: Feature has no API changes
- **N/A — infra**: Shared infrastructure, not user-facing
- **N/A — no API**: Purely internal module, no HTTP endpoint
- **N/A — imported**: Uses existing model/component without changes
- **N/A — reuse**: Extends existing contract, no new contract needed
## Impact Analysis Quick Reference
| If you change... | These fixtures verify it | These tests verify it | These screens depend |
|-----------------|------------------------|----------------------|---------------------|
| `GET /api/dashboards` | FX_Dashboards.Hub.* | Test.Dashboards.Hub | /dashboards, /migration |
| `Dashboards.Hub` model | FX_Dashboards.EnvReset | Test.Dashboards.Hub | /dashboards |
## Coverage Gate
- [ ] Every user story has at least one row
- [ ] Every functional requirement (FR-xxx) has at least one row OR explicit N/A rationale
- [ ] Every API endpoint has at least one row for success AND at least one row for an error state
- [ ] Every Screen Model has at least one row for loaded AND at least one row for an error state
- [ ] Every N/A cell carries a rationale from the key above (not just "N/A")
- [ ] Every contract referenced appears in `contracts/modules.md`
- [ ] Every task ID (Txxx) appears in `tasks.md` (or is marked T??? if tasks not yet generated)
- [ ] Impact table covers every contract with downstream dependents
#endregion Std.Opencode.Traceability
```
**Generation rules:**
- One row per unique (Story/Requirement, UX State, API Endpoint) tuple — happy path AND error states each get rows
- UX Screen+State column: format `route/name (state)` — e.g., `/dashboards (loaded)`, `/migration (NET_02 timeout)`
- Model column: `[TYPE Model]` contract ID from `screen-models.md`, or N/A with rationale
- API column: `operationId` from OpenAPI spec (if generated), otherwise endpoint path. Or N/A with rationale.
- Contract column: contract ID from `contracts/modules.md`
- Task columns: task IDs from `tasks.md` (to be filled after `/speckit.tasks` — leave as `T???` if tasks not yet generated)
- Test column: test contract ID pattern `Test.<Domain>.<Name>` or N/A with rationale
- Impact table: derived from `@RELATION` edges in contracts — invert the dependency graph
- Grep-friendly: `grep "Dashboards.Hub" traceability.md` → all rows for that model
- Agent zombie mode: without MCP tools, `grep "<contract>" traceability.md` replaces `impact_analysis`
- **N/A discipline**: Every N/A cell MUST include a brief rationale from the key, never just "N/A"
- **Coverage gate**: Must be completed and checked before `plan.md` is considered final
- **Backend-only features**: UX Screen, Screen Model, Frontend Task columns are N/A — backend-only. API and contract columns are filled normally.
- **Frontend-only features**: API operationId column is N/A — frontend-only (unless calling existing APIs)
## Key Rules
- Use absolute paths in workflow execution.
- Planning must reflect the current repository structure (`backend/src/**/*.py`, `frontend/src/**/*.svelte`, `backend/tests/`, `docs/adr/*`).
- Do not reference `.ai/*` or `.kilocode/*` paths (use `.opencode/` for skills).
- Do not write any feature planning artifact outside `specs/<feature>/...`.
- Do not hand off to `speckit.tasks` until blocking ADR continuity and rejected-path guardrails are explicit.