--- name: nacl-tl-plan description: | Create NaCl TL implementation plans from graph SA specifications, including waves, backend and frontend tasks, technical tasks, API contracts, and TL tracking files. Use when planning implementation from graph scope, generating tasks, or when the user says `/nacl-tl-plan`. --- # NaCl TL Plan For Codex Read `../nacl-tl-core/SKILL.md` and `../nacl-tl-core/references/tl-codex-contract.md` before executing this workflow. Planning creates implementation artifacts from SA graph data. Read `../nacl-core/SKILL.md` and `../nacl-tl-core/SKILL.md` first. ## Workflow 1. Resolve planning scope: full graph, module, use case list, feature request, or wave start. 2. Check that SA graph data exists and has enough module, use case, entity, form, and dependency context. 3. **External Contracts Gate** (W6 consumer-side, primary-owner exception): for every UC in scope, refuse to generate tasks when the UC references an external provider/protocol whose `.tl/external-contracts/.md` is absent on disk OR has empty required sections. See **External Contracts Gate** below. 4. Build an execution wave plan from dependencies and priorities. 5. Present the planned files, graph writes, and wave structure. 6. Stop for confirmation before writing `.tl/` files or graph TL nodes. 7. Generate self-sufficient task files, API contracts, master plan, status, and changelog entries from templates. 8. Verify file structure and graph task counts when possible. ## External Contracts Gate This is a consumer-side read of the artifact written by `nacl-sa-architect` during its External Contracts phase (W6 plan brief; declared primary-owner exception). Strict-only — no inline `--skip-external-contract` flag, no `gate_mode: legacy`, no `project_kind: prototype` carve-out. ### Why this gate exists 13 of ~60 postmortem signals across two NaCl projects were external-API or wire-protocol gaps (kie.ai in both projects, TUS upload, base_url divergence, reverse-proxy URL scheme, ffmpeg/ffprobe runtime — see `docs/retrospectives/project-beta-runtime-baseline.md`). Local tests passed; the product did not work. `nacl-tl-sync`'s Wire-Evidence Gate (W2) already downgrades sync to `UNVERIFIED` when wire-evidence is absent. W6 makes the artifact concrete upstream so the gate has something to point at. ### Steps 1. **Discover.** Use only a packaged named external-contract closure read for every UC in scope. The current pilot does not expose that read, so graph- backed discovery returns `Status: BLOCKED` with code `TL_NAMED_QUERY_UNAVAILABLE`; do not execute an inline graph statement. A user-provided confirmed file-only contract list may support planning, but it cannot be reported as graph-complete. 2. **Existence check.** For each confirmed contract row, the file at `ec.file_path` must exist on disk. - Absent → record `external-contract-missing` for `(uc_id, contract_id)`. 3. **Stub check.** For each contract row, the file's required sections must be filled with non-stub content. Required sections are 1–8 and 10–11 of `.tl/external-contracts/_template.md`. Section 9 (Model namespace) is required when `ec.kind == 'provider'`. Section 7 (File URL reachability) must be filled OR explicitly marked `N/A — no file URLs`. - Any required section missing or "TBD" → record `external-contract-stub` for `(uc_id, contract_id, missing_sections)`. 4. **Refuse.** If any violation was recorded, surface every violation, refuse to write any TL graph node, any `.tl/tasks/*` file, or any `.tl/status.json` / `.tl/master-plan.md` / `.tl/changelog.md` entry. Emit: - `Status: BLOCKED` workflow detail `external-contract-missing` (when any file is absent), OR - `Status: BLOCKED` workflow detail `external-contract-stub` (when all files exist but at least one has missing required sections). 5. **Remedy.** Either complete the contract via `/nacl-sa-architect` External Contracts phase, OR file a signed exception under the W4 schema covering every `(uc_id, contract_id)` tuple. There is no inline override. ### Example logic ```text violations = [] for uc in scope: for contract in graph_query(uc.id): if not file_exists(contract.file_path): violations.append((uc, contract, "external-contract-missing")) continue missing = sections_unfilled(contract.file_path, contract.kind) if missing: violations.append((uc, contract, "external-contract-stub", missing)) if violations: report_every(violations) return Status.BLOCKED # workflow detail per most-severe violation kind ``` ### Worked example — UC-300 referencing kie.ai - Graph: `UC-300 -[:REQUIRES_EXTERNAL]-> (ExternalContract {id: 'ext-kie', name: 'kie.ai', kind: 'provider', file_path: '.tl/external-contracts/kie.md'})`. - File present, all required sections filled → gate PASSES; UC-300 tasks generated. - File absent → `Status: BLOCKED workflow-detail external-contract-missing`; remedy: `/nacl-sa-architect` External Contracts phase OR W4 signed exception. - File present, Section 9 (Model namespace) is "TBD" → `Status: BLOCKED workflow-detail external-contract-stub`; remedy: complete Section 9 OR W4 signed exception. ## Source-Parity Requirements - Use `graph-infra/schema/tl-schema.cypher` and `graph-infra/queries/tl-queries.cypher` as the source for TL labels, relationships, properties, and named query expectations. - Preserve the SA-to-TL handoff: each generated `Task` must trace back from a `UseCase -[:GENERATES]-> Task` edge when graph writes are in scope. - Preserve `Task`, `Wave`, and `APIEndpoint` creation semantics, including `IN_WAVE`, `DEPENDS_ON`, `IMPLEMENTS`, `CONSUMES`, and `PRODUCES`. - Re-planning is idempotent and incremental by default (full overwrite only on explicit `--overwrite`). When Task/Wave nodes already exist, detect the narrow changed set from **two drift-confirmed signals only**: (1) version drift — `t.planned_from_version IS NOT NULL AND coalesce(uc.spec_version,0) > t.planned_from_version`; (2) explicit stale stamp — `review_status='stale'` on a UC or its Tasks. Do NOT add an arm keyed only on `INCLUDES_UC {kind:'modified'}` + `status='spec-complete'`: `spec-complete` is sticky and would re-flag already-current UCs every run, resetting in-progress work. Regenerate only the changed set; `MERGE` Task nodes by stable id (`UC###-BE/FE`) — no duplicates. Tasks whose status is `done`/`verified-pending` keep status, phase progress, commit and verification evidence on regen — only their files regenerate (with a delta section); the delta code is carried by a new task of the feature, and reopening a shipped task is an explicit operator decision, never a MERGE side effect. All other stale tasks reset to `pending`. On each regen set `Task.planned_from_version = uc.spec_version` and clear `review_status`/`stale_*` on BOTH the Task and its source UC (sanctioned clears — plan regen and the narrow fix/reconcile self-sync — must advance `planned_from_version` in the same write; forgetting the UC keeps L8 red). `INCLUDES_UC {kind}` is consumed for explicit `--feature FR-NNN` scoping only. On a project just upgraded to Фаза 0, baseline once (`SET t.planned_from_version = coalesce(uc.spec_version,0) WHERE t.planned_from_version IS NULL`) so the first run does not over-regenerate. - Resolve `--feature FR-NNN` scope from the graph (`(:FeatureRequest)-[:INCLUDES_UC]->`, with `kind` splitting new vs modified), falling back to the markdown file only if the FR node is absent. - Consume screen state machines: when a planned UC has `(:UseCase)-[:HAS_SCREEN]->(:Screen)` machines (authored by `nacl-sa-ui state-machine`; read via the `sa_uc_screen_machine` named query), embed a "Screen State Machine" section into `task-fe.md` — per screen: route and rendered form, then one transition row per `from_state × event [guard] → to_state` with effects and their targets (load/mutate → endpoint, navigate → screen, analytics → event). The machine is the deterministic UI contract; `task-be.md` does not carry it, `acceptance.md` references machine elements through slice `covers` ids, and `test-spec-fe.md` exercises each transition at least once. No new edges are written (the machine layer carries no TL overlay). Null-filter the effects map-collect (unmatched OPTIONAL yields an all-null map, not []). - Consume behavior slices: when a planned UC has `(:UseCase)-[:HAS_SLICE]->(:Slice)` nodes (authored by `nacl-sa-uc slices`), (a) embed a "Behavior Slices" section (kind, Given/When/Then, covered machine elements, called endpoints) into `task-be.md` / `task-fe.md` / `acceptance.md` — self-sufficiency holds, dev agents never query the graph; (b) MERGE `(:Slice)-[:VERIFIED_BY]->(:Task)` for every slice of the UC — default rule: all of the UC's `GENERATES` tasks; refine to the FE task for `COVERS`-anchored slices and the BE task for `CALLS`-anchored slices only when task ids carry the canonical `-BE`/`-FE` suffixes, then default-link any slice still unlinked. This is the consumer half of the L11.4 verification-closure gate — a re-plan must never leave a slice without a verifying task. - Consume domain errors: when a planned UC's `EXPOSES` endpoints carry `MAY_RAISE` edges (authored by `nacl-sa-uc errors`), embed a "Domain Errors" section — `task-be.md` gets the envelope contract (code / error_kind / http_status / retryable / raising endpoints; the code is emitted verbatim), `task-fe.md` gets the handling table (code / machine state / presentation kind / user-facing message, with unhandled errors listed as an L12.7 advisory), `acceptance.md` gets both halves, and `test-spec*.md` covers each error code at least once. No new edges are written (errors carry no TL overlay — verification belongs to slices). Null-filter the handled_by map-collect (unmatched OPTIONAL yields an all-null map, not []). - Consume the resilience layer: when a planned UC has cache policies on its `EXPOSES` surfaces (`(cp:CachePolicy)-[:CACHES]->`) or `(:UseCase)-[:HAS_DEGRADATION]->(:DegradationRule)` rules (authored by `nacl-sa-uc resilience`; read via the `sa_uc_resilience` named query), embed a "Cache & Degradation" section — `task-be.md` gets the cache contract (storage / invalidation kind + ttl seconds or invalidation event / serves_stale / cached endpoints) plus the backend rules (trigger / fallback / verbatim `behavior`: skip_unit, backoff with Retry-After, provider switches), `task-fe.md` gets the degradation handling table (trigger / degraded state / fallback / behavior) plus the client-side caches with their invalidation events, `acceptance.md` gets both halves, `test-spec*.md` covers each rule at least once, and unjoined cached_data rules / uncovered cached-surface errors are listed as L13.8 / L13.7 advisories. No new edges are written (the resilience layer carries no TL overlay either). Null-filter both map-collects. - Read `ExternalContract` graph nodes and `REQUIRES_EXTERNAL` / `DEPENDS_ON_EXTERNAL` edges to drive the External Contracts Gate. - Write `.tl/tasks//` files, `.tl/master-plan.md`, `.tl/status.json`, or graph nodes only after a presented plan, explicit user confirmation, and a passing External Contracts Gate. - After any confirmed write, read back the generated task files and graph nodes; if read-back cannot run, report `Status: UNVERIFIED` or `Status: BLOCKED`. - In fallback-only mode, do not claim graph completeness or SA coverage. ## Capabilities ### May Do - Read SA graph context for planning. - Generate `.tl/tasks/`, `.tl/master-plan.md`, `.tl/status.json`, and related planning artifacts. - Create TL Wave and Task graph nodes when confirmed and graph tooling exists. - Preserve self-sufficient task file contracts for downstream dev skills. ### Must Not Do - Plan from incomplete SA graph data without reporting the gap. - Write files or graph nodes without confirmation. - Leave task files dependent on external SA docs for required implementation context. - Modify root-level source skill folders. - Select or constrain the runtime model. ### Conditional Tools And Actions - Graph reads and writes require available graph tooling. - File generation requires writable workspace access and confirmation. - Feature request scope requires readable feature request artifacts. - Validation requires access to generated files and optional graph readback. ### Blocked Or Unverified Reporting - Use `BLOCKED` when SA graph data, templates, permissions, or confirmation are missing. - Use `BLOCKED` workflow detail `external-contract-missing` when the External Contracts Gate fails because a referenced `.tl/external-contracts/.md` is absent on disk. - Use `BLOCKED` workflow detail `external-contract-stub` when the External Contracts Gate fails because a referenced file exists but has empty or "TBD" required sections. - Use `FAILED` when generated artifacts violate required structure. - Use `PARTIALLY_VERIFIED` when files are generated but graph readback is unavailable, or the reverse. - Use `NOT_RUN` for dry-run writes. - Use `UNVERIFIED` when dependency ordering or generated scope cannot be checked. ## Source Comparison - Source Claude skill path: `../../nacl-tl-plan/SKILL.md` ### Preserved Methodology - Graph-based planning from SA specifications. - Wave planning by dependencies and priority. - Self-sufficient backend, frontend, technical, and API contract task files. - TL graph Task and Wave awareness. - Behavior-slice consumption: task-file "Behavior Slices" section + `VERIFIED_BY` re-linking on every (re-)plan of a slice-adopting UC. - Domain-error consumption: task-file "Domain Errors" section (BE envelope contract + FE handling table) on every (re-)plan of a taxonomy-adopting UC. - Resilience consumption: task-file "Cache & Degradation" section (BE cache contract + backend rules, FE handling table + client caches) on every (re-)plan of a resilience-adopting UC. ### Removed Claude Mechanics - Guaranteed runtime-specific graph tool names. - Source status labels outside the closed vocabulary. - Self-referential old/new comparison wording. - Model routing fields. ### Codex Replacement Behavior - Treat graph and file writes as conditional and confirmed. - Use TL core templates as artifact contracts. - Verify generated artifacts when possible. - Report scope or dependency uncertainty explicitly.