diff --git a/.claude/commands/validate.md b/.claude/commands/validate.md index a93d849e..5e0d1c26 100644 --- a/.claude/commands/validate.md +++ b/.claude/commands/validate.md @@ -1,5 +1,5 @@ --- -description: "Validate one completed SCE plan and synchronize its durable context" +description: "Validate one completed SCE plan and record final validation evidence" argument-hint: "" allowed-tools: Task, Read, Glob, Grep, Edit, Write, Question, Skill, Bash --- diff --git a/.claude/settings.json b/.claude/settings.json index 87ee9495..75cda4c6 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,55 +1,55 @@ { "$schema": "https://json.schemastore.org/claude-code-settings.json", "hooks": { - "PostToolUse": [ + "PreToolUse": [ { + "matcher": "Bash", "hooks": [ { - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks diff-trace", - "type": "command" + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce policy bash" } - ], - "matcher": "Write|Edit|MultiEdit|NotebookEdit" - }, + ] + } + ], + "PostToolUse": [ { + "matcher": "Write|Edit|MultiEdit|NotebookEdit", "hooks": [ { - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace", - "type": "command" + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks diff-trace" } ] - } - ], - "PreToolUse": [ + }, { "hooks": [ { - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce policy bash", - "type": "command" + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace" } - ], - "matcher": "Bash" + ] } ], - "Stop": [ + "UserPromptSubmit": [ { "hooks": [ { - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace", - "type": "command" + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace" } ] } ], - "UserPromptSubmit": [ + "Stop": [ { "hooks": [ { - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace", - "type": "command" + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-sce-or-show-install-guidance.sh\" sce hooks conversation-trace" } ] } ] } -} +} \ No newline at end of file diff --git a/.claude/skills/sce-change-to-plan/SKILL.md b/.claude/skills/sce-change-to-plan/SKILL.md index fe97c838..36744080 100644 --- a/.claude/skills/sce-change-to-plan/SKILL.md +++ b/.claude/skills/sce-change-to-plan/SKILL.md @@ -45,8 +45,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -119,7 +119,12 @@ Render the `plan_ready` result as the summary defined by the **Plan authoring ph Take the next task from `next_task`. A `plan_ready` result always names one. Do not evaluate its dependencies; the **Plan review phase** checks them when the emitted command runs and returns `blocked` if they are unmet. -The continuation invites revision. The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. +The workflow carries one of two explicit continuation shapes across a same-session wait: + +- **Initial-clarification continuation:** `original_request`, `clarification_answers`, and `loaded_context_brief`. `original_request` is the unchanged request from step 1; preserve it with the answers and never ask the user to provide it again. +- **Existing-plan revision continuation:** `plan_path`, `correction`, and `loaded_context_brief`. `plan_path` identifies the plan already written, and `correction` contains the user's requested revision. + +The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. Write `task` rather than `tasks` when `total_tasks` is 1. @@ -131,13 +136,13 @@ Then stop and wait. Do not implement, and do not run the handoff yourself. ### 4. Revise the plan on request -When the user answers clarification questions from step 2, answers open questions listed in the summary, or answers with changes to the plan, revise it in this session. Do not ask them to rerun `/change-to-plan`, and do not ask for the original change request again. +When the user answers clarification questions from step 2, resume the **Initial-clarification continuation** with `original_request`, `clarification_answers`, and the same `loaded_context_brief` from step 1. Preserve `original_request` unchanged and never ask the user for the original change request again. When the user answers open questions listed in the summary or requests changes to an already-written plan, resume the **Existing-plan revision continuation** with `plan_path`, `correction`, and the same `loaded_context_brief`. Do not ask them to rerun `/change-to-plan`. -Run the **Plan authoring phase** with their answer or correction and the same `loaded` brief from step 1. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. +Run the **Plan authoring phase** with the applicable continuation fields. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. An answer that resolves a doubt removes that open question. An answer that does not resolve it leaves the question standing; do not drop it because the user replied to it. If the reply raises a new doubt, the revised plan carries a new open question. -Pass the correction as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. +Pass `clarification_answers` or `correction` as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. Branch on `status` exactly as in step 2. A revision may legitimately return `needs_clarification` or `blocked`. diff --git a/.claude/skills/sce-change-to-plan/references/plan-template.md b/.claude/skills/sce-change-to-plan/references/plan-template.md index ac36a72c..19e8a12a 100644 --- a/.claude/skills/sce-change-to-plan/references/plan-template.md +++ b/.claude/skills/sce-change-to-plan/references/plan-template.md @@ -41,6 +41,17 @@ which criterion they map to. - {Durable context files that must describe the change once implemented.} +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + ## Constraints and non-goals - **In scope:** {files, modules, and surfaces this plan may touch} @@ -59,19 +70,19 @@ recorded. Remove the section otherwise.} - [ ] T01: `{single intent title}` (status:todo) - Task ID: T01 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: {task IDs, or none} - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending - [ ] T02: `{single intent title}` (status:todo) - Task ID: T02 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: T01 - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending ## Open questions @@ -92,11 +103,11 @@ invent one: `None.` is the expected answer for a well-specified change.} ```markdown - [ ] T02: `Add /auth/refresh endpoint` (status:todo) - Task ID: T02 - - Goal: Implement a POST `/auth/refresh` endpoint that exchanges a valid refresh token for a new access token. - - Boundaries (in/out of scope): In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. + - Scope: In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. - Dependencies: T01 - Done when: `POST /auth/refresh` returns a signed JWT on valid input and 401 on expired or invalid token; targeted tests pass; OpenAPI spec updated. - - Verification notes (commands or checks): `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Verify: `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Context synchronization: pending ``` ## Acceptance criteria rules @@ -109,7 +120,7 @@ invent one: `None.` is the expected answer for a well-specified change.} automated check exists, and say exactly what to look at. - List repository-wide checks once under `Full validation` instead of repeating them per criterion. -- Task-level `Verification notes` prove one task. Acceptance criteria prove the +- Task-level `Verify` proves one task. Acceptance criteria prove the plan. Keep them distinct: a task's checks are narrow and local, a criterion's check is end-to-end. - The union of the acceptance criteria must cover every success signal in the @@ -149,11 +160,14 @@ checkbox and status: ```markdown - [x] T01: `{title}` (status:done) - - {authored fields, unchanged} + - {authored fields, unchanged: Task ID, Scope, Dependencies, Done when} + - Verify: {each planned check, updated with its actual outcome} - Completed: {YYYY-MM-DD} - Files changed: {paths} - - Evidence: {commands run and their outcomes} - - Notes: {material deviations or approved assumptions} + - Result: {concise factual outcome, not a prose diff} + - Context impact: {durable context this change affects, or none} + - Context synchronization: pending | synced | blocked + - Context synchronization blocker: {present only when status is blocked} Blocker: {problem}; Required action: {action}; Retry condition: {condition} ``` `/validate` appends a `## Validation Report` section at the end of the plan. diff --git a/.claude/skills/sce-commit/SKILL.md b/.claude/skills/sce-commit/SKILL.md index bfbc2b9d..2987bef1 100644 --- a/.claude/skills/sce-commit/SKILL.md +++ b/.claude/skills/sce-commit/SKILL.md @@ -37,8 +37,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -135,15 +135,25 @@ a contract violation: report it and stop without committing. #### 3. Execute exactly one commit -Run `git commit` once with the returned message. +Follow the **Bypass execution handoff** in `references/atomic-commit.md`: + +1. Create the commit-message temp file outside the repository working tree, and + write the returned `message` verbatim to it using a file-writing operation. Do + not interpolate the multiline message into shell source or a shell command. +2. Run `git commit -F ` exactly once. +3. Only after that command succeeds, retrieve the commit hash explicitly with + `git rev-parse --verify HEAD^{commit}`. Do not parse Git's human-readable + output. +4. Delete the temp file after the commit attempt, including on failure, where + practical. On success, render the **Bypass success** layout from `references/output.md` and stop. On failure, render the **Bypass Git failure** layout from the same file and stop. -Do not retry, do not amend, do not stage additional files, and do not invent a -fallback commit. +Do not retry, do not amend, do not stage additional files, and do not fabricate a +commit hash. ## Rules @@ -153,7 +163,9 @@ fallback commit. They are behaviorally identical. - Read `references/atomic-commit.md` before running the phase. - Do not duplicate the internal instructions of the **Atomic commit phase**. -- Do not stage, unstage, restore, or otherwise modify files. +- Do not stage, unstage, restore, or otherwise modify repository or worktree + files. The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Do not amend, reset, revert, rebase, or push. - Do not read unstaged or untracked changes as commit input. - Do not infer success when the **Atomic commit phase** returns a non-success status. diff --git a/.claude/skills/sce-commit/references/atomic-commit.md b/.claude/skills/sce-commit/references/atomic-commit.md index a2404c4c..b41d362a 100644 --- a/.claude/skills/sce-commit/references/atomic-commit.md +++ b/.claude/skills/sce-commit/references/atomic-commit.md @@ -8,10 +8,6 @@ Write messages matching: `references/commit-message-style.md` -Return a result matching: - -`references/commit-contract.yaml` - Committing is not this skill's job. The invoking `/commit` workflow decides whether a returned message is committed, and it is the only thing that runs `git commit`. @@ -108,22 +104,35 @@ Confirm before returning that: - No plan slug or task ID appears that the staged diff does not support. - The mode's own constraints hold. -### 9. Return YAML +## Bypass execution handoff -Return exactly one YAML document matching `references/commit-contract.yaml`: +This phase returns the message; the invoking `/commit` workflow performs the +bypass commit. When the mode is `bypass`, the invoking workflow must: -- `proposal` in `regular` mode, with one or more messages. -- `bypass_message` in `bypass` mode, with exactly one message. -- `blocked` when messages cannot be written faithfully. +1. Create the commit-message temp file outside the repository working tree, + and write the returned `message` verbatim to it using a file-writing + operation. Never interpolate a multiline message into shell source or a + shell command. +2. Run `git commit -F ` exactly once. +3. After and only after a successful commit, run + `git rev-parse --verify HEAD^{commit}` and use that explicit `HEAD` value as + the reported hash. Never parse Git's human-readable commit output. +4. On any commit failure, report Git's failure and stop. Never retry, amend, + stage more files, or fabricate a hash. +5. Delete the temp file after the commit attempt, including on failure, where + practical. -Return only the YAML document. Do not add explanatory prose before or after it. +`oneshot` and `skip` select this same bypass behavior; they differ only in the +trigger token. ## Atomic commit boundaries Do not: - Run `git commit`, or any command that writes to the repository or its index. -- Stage, unstage, or modify files. +- Stage, unstage, restore, or otherwise modify repository or worktree files. + The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Ask the user to stage or confirm staging. - Analyze unstaged or untracked changes. - Return more than one message in `bypass` mode. @@ -133,13 +142,13 @@ Do not: - Mention `context/` synchronization activity in a commit message. - Claim a message was committed. - Run plan, task, or validation workflows. + + ## Completion The skill is complete after: - The staged diff was read, or reading it failed and was reported. - Messages were written for every staged file, or a blocker prevented it. -- One valid terminal YAML result matching `references/commit-contract.yaml` was - returned. diff --git a/.claude/skills/sce-commit/references/commit-message-style.md b/.claude/skills/sce-commit/references/commit-message-style.md new file mode 100644 index 00000000..056382ff --- /dev/null +++ b/.claude/skills/sce-commit/references/commit-message-style.md @@ -0,0 +1,44 @@ +# Commit Message Guide + +Use this repository style when writing new commits. + +## Core rules + +- Start with `scope: Subject` for most code changes. + - Common scopes: `runtime`, `language`, `objects`, `tests`, `CI`, `README`. + - Combined scopes are fine when needed (for example `language+runtime`). +- Use an imperative verb in the subject: `Fix`, `Add`, `Refactor`, `Remove`, `Implement`, `Update`, `Rewrite`, `Use`, `Allow`. +- Keep the subject specific and technical (name the subsystem and actual change). +- Keep the subject to one line and do not end it with a period. +- Add a body when the change is non-trivial. + - Explain why the change is needed. + - Explain how it works at a high level. + - Include impact/tradeoffs/follow-ups when relevant. +- For performance-related commits, include concrete measurements and benchmark context. + - Include regressions as well as improvements. +- Add issue references when relevant on their own lines. + - Example: `Fixes #123` + - Example: `Ref: https://...` + +## Practical template + +```text +: + + + + +Fixes # (optional) +``` + +## Size-based defaults + +1. Small fix: subject + 1 short reason line. +2. Medium refactor: subject + short why + short what changed. +3. Large architectural change: subject + context + bullets for major changes + impact/tradeoffs. + +## Anti-patterns to avoid + +- Vague subjects like "misc updates" or "cleanup". +- Bodies that only repeat the subject without explaining why or impact. +- Overly playful tone in serious bug-fix or architectural change. diff --git a/.claude/skills/sce-decision/SKILL.md b/.claude/skills/sce-decision/SKILL.md index 17e9d470..8b5d378e 100644 --- a/.claude/skills/sce-decision/SKILL.md +++ b/.claude/skills/sce-decision/SKILL.md @@ -10,18 +10,18 @@ compatibility: claude ## Purpose Write exactly one architecture decision record for one qualifying system-wide -important decision during successful task or plan context synchronization. Return +important decision during successful task context synchronization. Return a deterministic internal handoff to the invoking synchronization phase. Do not render an independent user-visible response. ## Input -Accept one structured decision request from `sce-next-task` or `sce-validate` -context synchronization. It must identify: +Accept one structured decision request from `sce-next-task` task context +synchronization. It must identify: - One decision stated as a single durable choice. - Why it qualifies under the decision gate. -- The implementation or validation evidence establishing the decision. +- The implementation / task-verification evidence establishing the decision. - The resolved plan path and relevant task IDs, when applicable. - Related current-state context and existing ADR paths. - An optional requested status. @@ -45,8 +45,12 @@ constraint involving at least one of: Routine implementation details, local refactors, naming and formatting choices, temporary experiments, and easily reversible choices do not qualify. When the -request does not demonstrate the threshold, return `blocked`; do not create an -ADR merely because context synchronization occurred. +request does not demonstrate the threshold, return `not_qualified` (or +`skipped` when the caller deliberately skips the gate); do not create an ADR +merely because context synchronization occurred. A nonqualifying or skipped +result is non-blocking, so the invoking synchronization phase continues +normally. Reserve `blocked` for missing, contradictory, or otherwise unsafe +decision input or history. ## Workflow @@ -57,20 +61,24 @@ path, references sufficient to make the record traceable, and no unresolved material contradiction. If it contains several decisions, require the caller to submit one request per decision. -Allowed statuses are exactly `Proposed`, `Accepted`, `Rejected`, `Deprecated`, -and `Superseded`. Use the explicitly requested allowed status; otherwise default -to `Accepted`. Reject any other status rather than guessing. +Allowed statuses for a newly written ADR are exactly `Proposed`, `Accepted`, +`Rejected`, `Deprecated`, and `Superseded`. Use the explicitly requested allowed +status; otherwise default to `Accepted`. `Deprecated` and `Superseded` remain +distinct creation-time-only statuses: use them to describe the record when it is +created, but never mutate an existing ADR into or out of either status. Reject any +other status rather than guessing. ### 2. Inspect existing decision history Read `context/decisions/` and the supplied related ADR paths before writing. -- Reuse an existing ADR when it already records the same decision; return its - path without creating a duplicate. -- Never edit an ADR whose status is `Accepted`. -- A correction, reversal, or changed decision creates a new dated ADR that - references and supersedes the accepted record. -- Do not overwrite any existing ADR or silently change its status. +- Reuse an existing ADR only when it records an equivalent decision and has an + active status: `Proposed` or `Accepted`. Return its path without creating a + duplicate. Never reuse a `Rejected`, `Deprecated`, or `Superseded` ADR. +- Existing ADRs are immutable regardless of status. Never edit an ADR whose status is `Accepted`; do not edit, overwrite, or silently change the status of any existing record. +- A correction, reversal, or any changed decision always creates a new dated ADR; + it references and supersedes the prior record when applicable, rather than + modifying that record. If `context/` or `context/decisions/` is absent, or history cannot be interpreted without inventing facts, return `blocked` without creating directories. @@ -118,8 +126,11 @@ Return exactly one internal handoff: - `written`: include `status`, `adr_path`, `decision`, `decision_status`, `created` (`true` for a new ADR and `false` for reuse), `supersedes`, and concise verification evidence. +- `not_qualified` or `skipped`: include `status`, the reason the decision gate + did not produce an ADR, and concise supporting evidence. These results are + non-blocking; the invoking synchronization phase continues normally. - `blocked`: include `status`, the specific `problem`, its `impact`, and the - `required_action`. + `required_action`. Use this only when decision writing cannot proceed safely. Use stable field names and repository-relative paths. Return no prose before or after the handoff. The invoking synchronization phase owns all user-visible @@ -130,7 +141,7 @@ reporting. Do not: - Write more than one ADR per request. -- Run outside successful task or plan context synchronization. +- Run outside successful task context synchronization. - Create a command, prompt, context root, or decisions directory. - Modify application code, tests, plans, current-state context, or existing accepted ADRs. diff --git a/.claude/skills/sce-handover/SKILL.md b/.claude/skills/sce-handover/SKILL.md index 8ce56bb8..e9e3d601 100644 --- a/.claude/skills/sce-handover/SKILL.md +++ b/.claude/skills/sce-handover/SKILL.md @@ -26,8 +26,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -62,7 +62,8 @@ blockers. Ground those facts against repository state: -- `git status` and `git diff` for uncommitted work. +- `git status`, `git diff`, and `git diff --cached` for uncommitted work, + including both unstaged and staged changes. - `context/plans/*.md` for the active plan and task, when one is being worked. - Recent commits, when they clarify what just landed. @@ -85,8 +86,9 @@ timestamp segment, rather than overwriting it. #### 3. Compose the handover document -Use the **Persisted-document format: Handover document** section embedded in -this file. Populate all four required sections: +Read `references/handover-template.md` before composing. It defines the +persisted-document format and is the only template authority. Populate all +four required sections: - `Current Task State` - `Decisions Made` @@ -136,10 +138,15 @@ and stop. Read the file and confirm it contains all four required sections: `Current Task State`, `Decisions Made`, `Open Questions / Blockers`, and -`Next Recommended Step`. +`Next Recommended Step`. For each section, inspect the content up to the +next required heading (or the end of the file): it must contain non-whitespace +content, and it must not consist only of an empty list marker, a template +placeholder such as `{What is being worked on...}`, or other unreplaced +`{...}` scaffolding. Explicit statements such as `None identified.` are real +content and are valid. -When any required section is missing, render the **Loader blocked** layout -(invalid handover) and stop. +When any required section is missing, empty, or placeholder-only, render the +**Loader blocked** layout (invalid handover) and stop. #### 3. Present for continuation @@ -164,53 +171,3 @@ guidance is the entire loader contract. loadable handover. - Never create the `context/` root; `sce setup --bootstrap-context` owns that. - Do not begin, plan, or automate the loaded handover's recommended next step. - -## Internal persisted-document format: Handover document - -The Markdown document writer mode creates under -`context/handovers/{name}.md`. This is the persisted file's content, distinct -from the terminal response defined in `references/output.md`. - -### Layout - -```markdown -# Handover: {plan name or short session topic} - -Date: {YYYY-MM-DD} -Plan: `{context/plans/plan-name.md}` (omit when no plan applies) -Task: `{task-id}` (omit when no single task applies) - -## Current Task State - -{What is being worked on, what is complete, what is in progress. Cite files, -commands, or plan/task references where they ground the statement.} - -## Decisions Made - -- {Decision and its rationale, or `None made this session.`} - -## Open Questions / Blockers - -- {Unresolved question or blocker, or `None identified.`} - -## Next Recommended Step - -{The single most useful next action for the following session, concrete -enough to act on directly.} - -## Assumptions - -- {Any detail above that was inferred rather than directly evidenced, or - `None.`} -``` - -### Rules - -- Include `Plan` and `Task` only when the session was working one identifiable - plan task; omit them rather than guessing. -- Every one of the four required sections must appear, in this order, even - when its content is `None identified.` or an equivalent. -- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere - in the document; do not duplicate confirmed facts here. -- Describe durable state useful to a future session, not a transcript of this - one. diff --git a/.claude/skills/sce-handover/references/handover-template.md b/.claude/skills/sce-handover/references/handover-template.md new file mode 100644 index 00000000..51edfefb --- /dev/null +++ b/.claude/skills/sce-handover/references/handover-template.md @@ -0,0 +1,47 @@ +The Markdown document writer mode creates under +`context/handovers/{name}.md`. This is the persisted file's content, distinct +from the terminal response defined in `references/output.md`. + +### Layout + +```markdown +# Handover: {plan name or short session topic} + +Date: {YYYY-MM-DD} +Plan: `{context/plans/plan-name.md}` (omit when no plan applies) +Task: `{task-id}` (omit when no single task applies) + +## Current Task State + +{What is being worked on, what is complete, what is in progress. Cite files, +commands, or plan/task references where they ground the statement.} + +## Decisions Made + +- {Decision and its rationale, or `None made this session.`} + +## Open Questions / Blockers + +- {Unresolved question or blocker, or `None identified.`} + +## Next Recommended Step + +{The single most useful next action for the following session, concrete +enough to act on directly.} + +## Assumptions + +- {Any detail above that was inferred rather than directly evidenced, or + `None.`} +``` + +### Rules + +- Include `Plan` and `Task` only when the session was working one identifiable + plan task; omit them rather than guessing. +- Every one of the four required sections must appear, in this order, even + when its content is `None identified.` or an equivalent. +- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere + in the document; do not duplicate confirmed facts here. +- Describe durable state useful to a future session, not a transcript of this + one. diff --git a/.claude/skills/sce-handover/references/output.md b/.claude/skills/sce-handover/references/output.md index 82928975..afe30b5f 100644 --- a/.claude/skills/sce-handover/references/output.md +++ b/.claude/skills/sce-handover/references/output.md @@ -32,28 +32,6 @@ No file was written. **Path:** `{written path}` -## Current Task State - -{same content written to the file} - -## Decisions Made - -- {as written} - -## Open Questions / Blockers - -- {as written} - -## Next Recommended Step - -{as written} - -## Assumptions - -- {as written, or `None.`} - ---- - To continue from this handover in another session: `/handover {written path}` diff --git a/.claude/skills/sce-next-task/SKILL.md b/.claude/skills/sce-next-task/SKILL.md index 85450eef..5c96a865 100644 --- a/.claude/skills/sce-next-task/SKILL.md +++ b/.claude/skills/sce-next-task/SKILL.md @@ -46,9 +46,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -86,12 +85,31 @@ Branch on `status`: `blocked` -> Do not run implementation. Render the **Review blocked** layout from `references/output.md`. When `candidates` is present the plan could not be resolved, and each entry is a candidate path for `/next-task {candidate-path}`. `executable_tasks_remaining` true means another task remains executable and `/next-task {plan-path} {task-id}` selects one; false means no task in the plan can proceed until the plan is updated. Do not print the raw result. Stop. +`sync_debt` -> Read `references/context-sync.md`, then run the **Task context synchronization phase** using the debt task's persisted `Context synchronization handoff` — and, when present, its persisted `Context synchronization blocker` — named by the **Plan review phase**. Do not reconstruct a missing handoff from conversation history. + +Write the debt task's lifecycle to the plan: `synced`, clearing its blocker, required action, and retry condition, for `synced` or `no_context_change`; a refreshed `blocked` state with the report's blocker, required action, and retry condition for `blocked`. If that lifecycle write fails, treat the outcome as `blocked`. + +Branch on the outcome: + +`blocked` -> Render the **Context synchronization blocked** layout from `references/output.md`, distinct from the **Review blocked** layout above. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select or start a new task. Stop. + +`synced` | `no_context_change` -> Re-invoke the **Plan review phase** with the same `plan-name-or-path` and, when present, `task-id` to resume normal task selection. + `plan_complete` -> Render the **Plan already complete** layout from `references/output.md`. Stop. `ready` -> Pass the complete readiness result to the **Task execution phase**. Do not reconstruct, summarize, or reinterpret the reviewed task before passing it. +The review inspects every completed task's `Context synchronization` field in +the plan, in plan order, regardless of its position relative to the task being +selected or resumed, before allowing a new implementation task to start. A +missing field, or any value other than `synced`, is unresolved synchronization +debt. Never infer `synced` from conversation history. When the debt-carrying +task has no durable `Context synchronization handoff` subsection, the **Plan +review phase** returns `blocked` directly with a legacy-migration required +action; otherwise it returns `sync_debt`, resolved by the branch above. + ### 2. Execute the task Read `references/task-execution.md`, then run the **Task execution phase** with @@ -133,9 +151,14 @@ Do not restate, summarize, or reconstruct any part of the execution result. This phase verifies the five root context files on every invocation, whatever the change's reported impact, so it is never correct to skip it as unnecessary. +Before branching on the synchronization result, write the completed task's +lifecycle to the plan file: `synced` for `synced` or `no_context_change`, and +`blocked` with the report's blocker, required action, and retry condition for +`blocked`. If that lifecycle write fails, treat synchronization as `blocked`. + Branch on the synchronization result. -`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. Nothing records the skipped synchronization, so it is lost once this session ends. +`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select another task. Stop. diff --git a/.claude/skills/sce-next-task/references/context-sync.md b/.claude/skills/sce-next-task/references/context-sync.md index 3c5cb8b4..30ec3eae 100644 --- a/.claude/skills/sce-next-task/references/context-sync.md +++ b/.claude/skills/sce-next-task/references/context-sync.md @@ -5,46 +5,70 @@ Run this phase for step 3 of the workflow, and only when task execution returned session inherits what this task established. It never touches code, tests, or plan state. -Input: the complete `complete` result from the task execution phase, passed -verbatim. It is the authoritative handoff, and this phase owns reading the plan, -task, changed files, verification evidence, and reported context impact out of -it. +Input: either the complete `complete` result from the task execution phase +(same-session), passed verbatim, or the plan path and task ID a plan-review +recovery step resolved for a `blocked` task, together with that task's own +completed record — read directly from the plan — and its persisted `Context +synchronization blocker` when present (cross-session retry). Whichever was +supplied is the authoritative source, and this phase owns reading the plan, +task, changed files, verification evidence, and reported context impact out +of it. -Do not restate, summarize, or reconstruct any part of the execution result. Do -not reconstruct a missing execution result from conversation history. +Do not restate, summarize, or reconstruct any part of it. Do not reconstruct a +missing execution result or completed task record from conversation history. -The execution result must have: +A live execution result must have: ```text status: complete ``` -Treat the execution result as the authoritative handoff for: +A cross-session retry has no separate `status` field to check; the completed +task record's presence in the plan, identified by plan path and task ID, is +itself the authoritative signal. + +Use the report format in: + +`references/sync-report.md` + +Treat whichever source was supplied — the live execution result, or the +completed task record read directly from the plan — as the authoritative +source for: - The resolved plan and completed task. +- `changes.files_changed`, or the completed task record's own `Files changed` + field on retry, already attributed relative to the pre-edit Git baseline. - Files changed by implementation. -- Implementation summary. -- Verification evidence. +- The task's `Result` (or implementation summary, for a live result). +- `Verify` outcomes (or verification evidence, for a live result). - Done-check evidence. - Reported context impact. This phase must not be run for `declined`, `blocked`, or `incomplete` execution results. -## 3.1 Validate the execution handoff +## 3.1 Validate the handoff Confirm that: -- `status` is exactly `complete`. -- A `plan` object with a `path` is present. -- Exactly one completed task is identified. -- Changed files and an implementation summary are present. -- Verification evidence is present. +- A live execution result has `status` exactly `complete`; a cross-session + retry has no `status` field to check and is authoritative by the completed + task record's presence in the plan. +- A resolved plan path and task ID are present; a live execution result + carries them in its `plan` and `task` objects, and a cross-session retry + receives them directly from the caller that resolved the debt task. +- Exactly one completed task is identified, and — on retry — its record is + read directly from the plan by that plan path and task ID rather than + reconstructed in-band. +- Changed files and a `Result` (an implementation summary, for a live result) + are present. +- `Verify` outcomes (verification evidence, for a live result) are present. - Done-check evidence is present. - A context-impact classification is present. -If the handoff is missing required information or is internally contradictory, do -not modify context. Return a `blocked` Markdown report. +If the required information is missing, the completed task record cannot be +read from the plan, or either is internally contradictory, do not modify +context. Return a `blocked` Markdown report. ## 3.2 Confirm the context root @@ -289,6 +313,13 @@ Set exactly one report status: existing context was checked and no edit was warranted. `blocked` means context could not be synchronized safely. +A `blocked` report always writes the plan path and task ID/title as identity, +plus a `Context synchronization blocker` section (blocker, required action, +retry condition), using the same field names the plan's completion record +uses, so the plan-review recovery step can persist the blocker verbatim and a +future retry can read the completed task record directly from the plan by +plan path and task ID. + Record only the Markdown report. Do not add explanatory prose before or after it. Do not determine whether the plan is complete. The `/next-task` workflow owns diff --git a/.claude/skills/sce-next-task/references/output.md b/.claude/skills/sce-next-task/references/output.md index d0ade404..b6e772bd 100644 --- a/.claude/skills/sce-next-task/references/output.md +++ b/.claude/skills/sce-next-task/references/output.md @@ -138,145 +138,3 @@ When the `approve` flag is supplied, omit the question and end after wait. - If the handoff is stale or incomplete, show the known task information and identify the problem under **Risks or trade-offs**. - -# Context Sync Report - -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. - -The `Status` value must be exactly one of: - -- `synced` -- `no_context_change` -- `blocked` - -The input execution status is always `complete` and does not need to be repeated -as a separate workflow state. - -## Synced variant - -# Context Sync Report - -**Status:** synced -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Updated context - -- `{context file}` — {concise description of the durable truth updated} - -## Architecture decisions - -- `{written or reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}` - -## Verification - -- {How the edited context was checked against implementation and execution evidence.} -- {File hygiene: line counts, relative links, diagrams where structure is complex.} -- {Documentation, link, or formatting checks that were run, when applicable.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## No-context-change variant - -# Context Sync Report - -**Status:** no_context_change -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Synchronization result - -{Explain why the completed implementation did not introduce durable, -non-obvious repository knowledge requiring an update.} - -## Context reviewed - -- `{context file or area}` — {what was checked and why it remains accurate} - -## Architecture decisions - -- `{reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}`, already present. - -## Verification - -- {How existing context was compared with implementation and execution evidence.} - ---- - -## Blocked variant - -# Context Sync Report - -**Status:** blocked -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Blocker - -**Problem:** {specific synchronization blocker} -**Impact:** {why context cannot be made authoritative safely} -**Required action:** {decision or correction required} - -## Context changes - -- {List safe context edits preserved, or state `No context files were changed.`} - -## Architecture decisions - -- `{ADR path written or reused before the blocker}` — {decision and status} -- None written or reused before the blocker. - -## Retry condition - -{State the concrete condition under which context synchronization should run -again.} - -## Report rules - -- Name exact context files when they were changed or reviewed. -- Under **Architecture decisions**, list every ADR path written or reused during - the decision gate. In a successful report, state `None qualified.` when the - gate skipped invocation. In a blocked report, state - `None written or reused before the blocker.` when applicable. -- Under **Updated files**, list every changed file from the execution handoff - except paths under `context/`. -- Report the missing context root as `blocked`, with `sce setup - --bootstrap-context` as the required action and the existence of `context/` as - the retry condition. -- Omit **Feature existence** only when the task implemented no feature. -- Describe durable truth, not implementation-session chronology. -- Keep evidence concise and factual. -- Do not claim final validation passed. -- Do not determine whether the plan is complete. -- Do not recommend a next implementation task. diff --git a/.claude/skills/sce-next-task/references/plan-review.md b/.claude/skills/sce-next-task/references/plan-review.md index d08975e3..89e378f3 100644 --- a/.claude/skills/sce-next-task/references/plan-review.md +++ b/.claude/skills/sce-next-task/references/plan-review.md @@ -1,8 +1,8 @@ # Plan review phase Run this phase for step 1 of the workflow. It resolves one plan, selects one -task, and decides whether that task can be implemented right now. It reads; it -never writes. +task, and decides whether that task can be implemented right now. It reads; +it never writes. Inputs: the parsed `plan-name-or-path`, and `task-id` when present. The `auto-approve` token is not passed here and has no meaning in this phase. @@ -20,7 +20,30 @@ Read the selected plan before exploring the repository. ## 1.2 Resolve one task -When a task ID is supplied, select that task. +Before selecting or starting a task, inspect every completed task's +`Context synchronization` field in the plan, in plan order, regardless of its +position relative to the task being selected or resumed. A missing field, or +any value other than `synced`, is unresolved synchronization debt. Never infer +`synced` from chat history. + +For the first task carrying debt: + +- When the task has no durable completed-task record (no `Files changed`, + `Result`, `Verify`, or `Context impact` recorded — a legacy plan predating + that structure, or an incomplete write), do not attempt a reconstructed retry. + Set internal status `blocked` with a required action to migrate the plan + (backfill the completion record, or resolve the debt manually) and a retry + condition of the plan carrying that structure. Stop. +- Otherwise, set internal status `sync_debt`, naming the debt task (its ID and + title) and its own completed record — read directly from the plan by plan + path and task ID — including, when its field is `blocked`, its persisted + `Context synchronization blocker`. Do not run or cite the Task context + synchronization phase. Stop. Do not select or start a new task. + +Only after every completed task is `synced` does task selection proceed. + +When a task ID is supplied, select that task only after the same synchronization- +debt check passes. Otherwise, select the first incomplete task in plan order whose declared dependencies are complete. @@ -90,6 +113,7 @@ Set exactly one internal state: - `ready` - `blocked` - `plan_complete` +- `sync_debt` Record only the internal state. Do not add explanatory prose before or after it. @@ -103,8 +127,14 @@ A `ready` result must identify: - Relevant files and context. - Review assumptions. -Step 2 consumes this result verbatim, so anything the execution phase needs has -to be present here. +A `sync_debt` result must identify: + +- The debt-carrying task's ID and title. +- Its own completed record, read directly from the plan by plan path and task ID. +- Its persisted `Context synchronization blocker`, when present. + +Step 2 consumes a `ready` result verbatim, so anything the execution phase +needs has to be present here. ## Plan review boundaries @@ -113,7 +143,7 @@ Do not: - Modify application code. - Modify tests. - Update the plan. -- Mark the task complete. +- Mark a task complete. - Request implementation confirmation. - Run task execution. - Synchronize context. diff --git a/.claude/skills/sce-next-task/references/sync-report.md b/.claude/skills/sce-next-task/references/sync-report.md new file mode 100644 index 00000000..0211740c --- /dev/null +++ b/.claude/skills/sce-next-task/references/sync-report.md @@ -0,0 +1,137 @@ +# Context Sync Report + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `synced` +- `no_context_change` +- `blocked` + +The input execution status is always `complete` and does not need to be repeated +as a separate workflow state. + +## Synced variant + +# Context Sync Report + +**Status:** synced +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Updated context + +- `{context file}` — {concise description of the durable truth updated} + +## Architecture decisions + +- `{written or reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}` + +## Verification + +- {How the edited context was checked against implementation and execution evidence.} +- {File hygiene: line counts, relative links, diagrams where structure is complex.} +- {Documentation, link, or formatting checks that were run, when applicable.} + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## No-context-change variant + +# Context Sync Report + +**Status:** no_context_change +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Synchronization result + +{Explain why the completed implementation did not introduce durable, +non-obvious repository knowledge requiring an update.} + +## Context reviewed + +- `{context file or area}` — {what was checked and why it remains accurate} + +## Architecture decisions + +- `{reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}`, already present. + +## Verification + +- {How existing context was compared with implementation and execution evidence.} + +--- + +## Blocked variant + +# Context Sync Report + +**Status:** blocked +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Context synchronization blocker + +- Blocker: {specific synchronization blocker} +- Required action: {decision or correction required} +- Retry condition: {concrete condition under which context synchronization + should run again} + +## Context changes + +- {List safe context edits preserved, or state `No context files were changed.`} + +## Architecture decisions + +- `{ADR path written or reused before the blocker}` — {decision and status} +- None written or reused before the blocker. + +## Report rules + +- Name exact context files when they were changed or reviewed. +- Under **Architecture decisions**, list every ADR path written or reused during + the decision gate. In a successful report, state `None qualified.` when the + gate skipped invocation. In a blocked report, state + `None written or reused before the blocker.` when applicable. +- Under **Updated files** (synced and no-context-change reports), list every + changed file from the execution handoff except paths under `context/`. A + blocked report does not repeat that list — it is already on the plan's + completed task record. +- Report the missing context root as `blocked`, with `sce setup + --bootstrap-context` as the required action and the existence of `context/` as + the retry condition. +- In a blocked report, write the `Context synchronization blocker` + subsection using the same field names the plan's completion record + uses, so plan review can persist it verbatim. +- Omit **Feature existence** only when the task implemented no feature. +- Describe durable truth, not implementation-session chronology. +- Keep evidence concise and factual. +- Do not claim final validation passed. +- Do not determine whether the plan is complete. +- Do not recommend a next implementation task. diff --git a/.claude/skills/sce-next-task/references/task-execution.md b/.claude/skills/sce-next-task/references/task-execution.md index 7b049492..fe01b530 100644 --- a/.claude/skills/sce-next-task/references/task-execution.md +++ b/.claude/skills/sce-next-task/references/task-execution.md @@ -21,9 +21,26 @@ workflow. It suppresses the approval question and the wait. It never suppresses the gate. Only the workflow entrypoint may set it, and only from an explicit user-supplied approval token. Never infer it. -If required handoff information is absent or stale, still show the gate using -what is known, clearly identify the handoff problem, and do not edit files. After -the user responds, set internal status `blocked`. +If required handoff information is absent, stale, or contradictory, still show the +gate using what is known, clearly identify the handoff problem, and do not edit +files. With the `approve` flag supplied, do not treat pre-approval as permission +to repair or reinterpret the handoff: after showing the gate, set internal status +`blocked` deterministically. Without the flag, wait for the user's response and +then set internal status `blocked`; do not retry the handoff in the same phase. + +A successful `complete` handoff must explicitly contain all of these fields: + +- The resolved `plan` object, including its path and completion counts. +- The selected `task` identity, including its ID and title. +- `changes.files_changed`, the implementation's baseline-relative changed-file list. +- `changes.summary`, a concise implementation summary. +- `verification`, with every reported outcome marked `passed` and its evidence. +- `done_checks`, pairing every done check with evidence. +- `plan_update`, proving the selected task was marked complete and evidence recorded. +- `context_impact`, including classification, affected areas, and reason. + +Do not omit, invent, or reconstruct any of these fields when handing off to context +synchronization. ## 2.1 Validate the handoff without editing @@ -80,7 +97,21 @@ If those constraints materially contradict the reviewed task, set internal statu ## 2.4 Prepare the implementation -Before editing: +Before editing, capture a Git baseline. Record the current `HEAD` commit, the +staged and unstaged patch/content state, and every untracked path/content state +using equivalent `git status`, `git diff`, and `git diff --cached` views. If the +baseline cannot be captured reliably, stop before editing and set internal status +`blocked`. + +After implementation, capture the same views again. Compute +`changes.files_changed` by comparing the post-edit snapshot with the pre-edit +baseline, not by listing the whole working tree or by diffing only against +`HEAD`. Include each path whose state or content changed during this task once; +exclude paths unchanged from the baseline, including unrelated pre-existing +staged, unstaged, and untracked changes. A path already dirty at baseline is +included only when this task changed its state or content. + +Then: - Read the relevant files supplied by plan review. - Inspect nearby code and tests when needed. @@ -145,8 +176,12 @@ Never report a check as passed unless it ran successfully. Only after successful implementation and task-level verification: - Mark only the selected task complete. -- Record concise implementation evidence. -- Record verification commands and outcomes. +- Record directly on the completed task: `Completed` (the date), the + baseline-relative `Files changed` list, a concise factual `Result`, the + actual outcome of every planned `Verify` check, and `Context impact`. +- Set that task's `Context synchronization` field to `pending` in the plan file + before returning `complete`; this write must happen after the execution + facts above and before the synchronization phase is invoked. - Record material deviations or approved assumptions. - Preserve the plan's existing structure and terminology. @@ -173,6 +208,14 @@ Set internal status `blocked` for every other non-successful outcome, including: Do not determine whether the plan is complete. The `/next-task` workflow owns that decision after context synchronization. +Before determining terminal status for a `complete` result, verify that the +handoff contains the resolved plan, task identity, baseline-relative changed +files, implementation summary, verification evidence, done-check evidence, plan +update, and context-impact classification listed above. The mandatory five-root- +file context pass remains required for every completed task, regardless of the +reported context-impact classification, because it is cheap, deterministic, and +load-bearing for context accuracy; `context_impact` must not be used to waive it. + ## 2.9 Return internal state After the phase reaches a terminal state, set exactly one internal state. diff --git a/.claude/skills/sce-validate/SKILL.md b/.claude/skills/sce-validate/SKILL.md index 95d91248..5d45fbb2 100644 --- a/.claude/skills/sce-validate/SKILL.md +++ b/.claude/skills/sce-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: sce-validate description: > - Validate one completed SCE plan and synchronize its durable context + Validate one completed SCE plan and record final validation evidence compatibility: claude --- @@ -12,8 +12,7 @@ compatibility: claude Own this workflow from input parsing through its terminal user-visible response. Execute the phases below directly and in order. Phase statuses are internal state, not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE -package, or SCE workflow command except `sce-decision`, and invoke `sce-decision` -only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates, +package, or SCE workflow command. Follow the canonical workflow's steps, gates, and stops exactly as written: never invent, skip, reorder, or merge a step. ## Phase references @@ -25,15 +24,12 @@ receives, and how its result branches — and each reference holds the phase its | Step | Read before running the phase | |---|---| | 1 | `references/validation.md` | -| 2 | `references/context-sync.md` | `references/validation-report.md` defines the `## Validation Report` section written into the plan file. Step 1 points to it at the moment it is needed, on a `validated` or `failed` outcome only. -Read a step's reference before taking any action for that step, not after. Read -only the reference for the step you have reached: a run that stops at a `blocked` -or `failed` validation never enters step 2, which is why they are separate files. +Read the reference before taking any action for step 1, not after. ## User-visible output @@ -46,9 +42,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -90,66 +85,25 @@ Do not write the Validation Report yourself. Branch on the report's `Status:`. -`blocked` -> Do not run context synchronization. Print the blocked Markdown -report as returned. Do not rephrase it into a different layout. Stop. +`blocked` -> Print the blocked Markdown report as returned. Do not rephrase it +into a different layout. Stop. -`failed` -> Do not run context synchronization. Print the failed Markdown -report as returned. It is already a session handoff: self-contained, actionable, -and ending with `/validate {plan-path}` after repairs. +`failed` -> Print the failed Markdown report as returned. It is already a session +handoff: self-contained, actionable, and ending with `/validate {plan-path}` after +repairs. Do not rewrite it into a shorter summary. Do not drop the retry command. Do not -add an alternate continuation that replaces `/validate`. +add an alternate continuation that replaces `/validate`. Stop. -Stop. Do not mark the plan finished. Do not continue to context synchronization. -Do not start the repair work in this workflow unless the user explicitly asks -to continue here; the default is that the handoff can leave this session. - -`validated` -> Pass the complete validated Markdown result to the **Plan context synchronization phase**. - -Do not reconstruct, summarize, or reinterpret the validation result before -passing it. - -### 2. Synchronize plan context - -Read `references/context-sync.md`, then run the **Plan context synchronization -phase** with the `Status: validated` Markdown result from the **Validation -phase**. - -Do not run the **Plan context synchronization phase** for `failed` or `blocked`. Those are not -success states. - -Pass the validated result verbatim. It is the authoritative handoff, and the **Plan context synchronization phase** -owns reading the plan path, required context paths, validation evidence, and -reported context impact out of it. - -Do not restate, summarize, or reconstruct any part of the validation result. - -This phase verifies the five root context files on every invocation, whatever the -reported impact, and must account for every path in the plan's `Context sync` -section, so it is never correct to skip it as unnecessary. - -Branch on the synchronization result. - -`blocked` -> Validation itself succeeded and is already recorded in the plan. -Render the **Context synchronization blocked** layout from -`references/output.md`. Nothing records the skipped synchronization, so it is -lost once this session ends. - -Stop. - -`synced` | `no_context_change` -> Print out the report returned by the **Plan context synchronization phase**. +`validated` -> Print the complete validated Markdown result as returned. Continue to the next step. -### 3. Report completion +### 2. Report completion Return exactly one completion block. Do not start another workflow. Render the **Completion** layout from `references/output.md`. -When the synchronization status was `no_context_change`, keep the same -completion block. "Synchronized" here means the final context pass finished -successfully, including the case where no edit was warranted. - Stop. ## Rules @@ -157,14 +111,10 @@ Stop. - Validate at most one plan per invocation. - Read each phase's reference before running that phase. - Do not duplicate the internal instructions of embedded phases. -- The only permitted sibling-skill invocation is `sce-decision`, and only the - successful context-synchronization decision gate may invoke it. - Do not run final validation when implementation tasks remain; the **Validation phase** returns `blocked`, and this workflow stops. -- Run the **Plan context synchronization phase** only when the **Validation phase** returned - `Status: validated`. Do not run it for `failed` or `blocked`. - On `failed`, print the handoff Markdown as returned and stop. Preserve the - retry `/validate {plan-path}` instruction. Do not synchronize context. + retry `/validate {plan-path}` instruction. - Do not implement remaining plan tasks from this workflow unless the user explicitly continues in-session after a failed handoff. - Do not create a Git commit or push changes. @@ -172,5 +122,3 @@ Stop. - Do not execute a follow-up `/next-task`, `/change-to-plan`, or `/validate` yourself. - Do not infer success when an embedded phase returns a non-success status. -- Preserve validation evidence already written to the plan when context - synchronization fails. diff --git a/.claude/skills/sce-validate/references/context-sync.md b/.claude/skills/sce-validate/references/context-sync.md deleted file mode 100644 index bdd0c223..00000000 --- a/.claude/skills/sce-validate/references/context-sync.md +++ /dev/null @@ -1,350 +0,0 @@ -# SCE Plan Context Sync - -## Purpose - -Reconcile one fully validated plan with the repository's durable context and -return a Markdown report. - -This phase owns: - -- Validating the validation handoff. -- Confirming the context root exists. -- Discovering the context required by the finished plan. -- Deciding whether durable context changed. -- Editing and verifying the affected context files. -- Returning one Markdown synchronization report. - -Use the report format in: - -`references/sync-report.md` - -Task-level context sync may already have run after individual tasks. This phase -is the plan-level final pass: it starts from the plan's `Context sync` -requirements and the validated implementation, and closes gaps that remain. - - - -## Input - -The complete Markdown result returned by the validation phase. - -The validation result must report: - -```markdown -**Status:** validated -**Plan:** {plan path} -``` - -Treat that Markdown as the authoritative handoff for: - -- The resolved plan path. -- Validation commands and outcomes. -- Acceptance-criteria evidence. -- Scaffolding removals. -- Reported context impact, required context paths, and affected areas. - -This phase must not be invoked for `failed` or `blocked` validation results. -Those are not success states. Same rule as `sce-task-context-sync`: context sync -runs only after a successful prior phase. - -Do not reconstruct a missing validation result from conversation history. - -## Workflow - -### 1. Validate the validation handoff - -Confirm that: - -- `Status:` is exactly `validated`. -- `Plan:` names an existing plan path. -- Acceptance-criteria evidence is present and every criterion is met. -- Commands run are present. -- A context-impact classification is present. - -If the handoff is missing required information or is internally contradictory, -do not modify context. Return a `blocked` Markdown report. - -### 2. Confirm the context root - -When `context/` does not exist, there is no durable memory to synchronize. -Do not create it, and do not write context files outside it. - -Return a `blocked` report whose required action is: - -`sce setup --bootstrap-context` - -State that validation itself succeeded and is recorded in the plan, and that -plan context synchronization should run again once the context root exists. - -Bootstrapping is the user's action, not this phase's. - -### 3. Discover applicable context - -Start with the validated Markdown result: - -- **Context impact** classification, required context, and affected areas. -- Acceptance-criteria evidence. -- Commands run. - -Then read the plan's `Context sync` section and inspect existing repository -context in this order when present: - -1. Paths named by the plan's `Context sync` section -2. `context/context-map.md` -3. Context files for the affected domain or subsystem -4. `context/overview.md` -5. `context/architecture.md` -6. `context/glossary.md` -7. `context/patterns.md` -8. Operational, product, or decision records directly related to the finished - change - -Use the context map and existing links to locate authoritative files. - -Do not scan or rewrite the entire `context/` tree by default. - -Do not create a new context file when an existing authoritative file can be -updated coherently. - -#### The mandatory root pass - -Every invocation verifies these five files against code truth, whatever the -reported classification is: - -- `context/overview.md` -- `context/architecture.md` -- `context/glossary.md` -- `context/patterns.md` -- `context/context-map.md` - -Verifying is not editing. A classification that warrants no root edit still -requires reading each of these and confirming it is not contradicted by the -finished implementation. A file that is absent is a gap; record it in the -report rather than creating it to satisfy the pass. - -Report each of the five as verified or edited. Never declare synchronization -done while one of them is unchecked. - -#### Plan context requirements - -Every path or statement listed under the plan's `Context sync` section must be -accounted for in the report as already accurate or updated. A requirement the -finished code still does not satisfy is a blocker, not a note. - -### 4. Determine whether durable context changed - -Use the reported context impact as a strong hint, then verify it against the -finished implementation and existing context. - -Durable context includes non-obvious repository knowledge such as: - -- User-visible or externally observable behavior. -- Architecture, boundaries, ownership, and dependency direction. -- Public interfaces, data contracts, and persistence behavior. -- Operational procedures and important failure modes. -- Security or privacy behavior. -- Shared terminology. -- Intentional limitations and meaningful design decisions. - -Do not document: - -- Details already obvious from the implementation. -- Temporary debugging information. -- A file-by-file narration of the change. -- Test output that belongs only in validation evidence. -- Speculation or future work not established by the finished plan. -- Generic engineering practices. - -Interpret impact classifications as follows. Each governs which files are -*edited*; none of them waives the mandatory root pass or the plan's Context -sync requirements. - -- `none`: Make no edits beyond any correction the root pass or unmet plan - context requirement turns up. -- `local`: Update the nearest existing authoritative context only when the new - behavior is not reliably discoverable from code. -- `domain`: Update affected domain context and the context map when its links or - summaries changed. -- `root`: Update the relevant root context and any affected domain context. - -If the reported classification is inconsistent with the actual change, use the -verified classification and explain the difference in the report. - -### 5. Record qualifying architecture decisions - -During this successful synchronization, determine whether the completed change -establishes or changes a system-wide important constraint involving one or more -of: - -- System boundaries or ownership. -- Public or cross-domain interfaces. -- Data models or persistence. -- Compatibility contracts. -- Security posture. -- Deployment or distribution strategy. -- A major dependency. -- A similarly durable constraint that is costly or risky to reverse. - -Routine implementation details, local refactors, naming and formatting choices, -temporary experiments, and easily reversible choices do not qualify. Do not -invoke a decision skill for them. - -Use the discovered context, existing decision records, and this evidence: - -- acceptance-criteria and validation evidence. - -Identify each qualifying decision, then handle qualifying decisions in -deterministic order: - -1. Reuse a written ADR path already returned during this plan when it records the - same decision. -2. Otherwise invoke `sce-decision` once with exactly one structured decision - request containing the decision, qualifying evidence, plan and task references, - related context and ADR paths, and any user-requested status. -3. On `written`, retain the returned `adr_path` as synchronization evidence and - make it available for current-state context links before synchronization - completes. Reuse is valid evidence; do not create a duplicate ADR. -4. On `blocked`, stop before current-state context edits and return a `blocked` - synchronization report carrying the decision-writing problem, impact, required - action, and retry condition. - -Invoke `sce-decision` only here, after a successful execution or validation -handoff and during context synchronization. Do not invoke it from a non-success -branch or for any non-decision purpose. When no decision qualifies, continue -without invoking it and record that outcome in synchronization evidence. - -### 6. Synchronize context - -Make the smallest coherent documentation change that preserves repository truth. - -When editing context: - -- Describe the resulting behavior, not the validation session. -- Preserve repository terminology and document structure. -- Remove or correct statements contradicted by the finished implementation. -- Update cross-references when files are added, moved, renamed, or superseded. -- Keep one authoritative statement for each durable fact. -- Avoid copying the validation result verbatim into context files. -- Do not change application code, tests, or plan validation evidence. - -Create a new context file only when: - -- The knowledge is durable and non-obvious. -- No existing file owns it coherently. -- The new file has a clear place in the context map. - -#### Feature existence - -Every feature the finished plan implemented must have at least one durable -canonical description discoverable from `context/`, in a domain file under -`context/{domain}/` or in `context/overview.md` for a cross-cutting feature. - -When the plan delivered a feature no context file describes, add that -description. Prefer a small, precise domain file over overloading -`overview.md` with detail. - -This is not license to narrate the diff: describe what the feature is and how -it behaves, not what was edited during the plan. - -#### Glossary - -Add a `context/glossary.md` entry for any domain language the plan introduced. -New terminology is durable knowledge whatever the classification is. - -#### File hygiene - -Every context file this phase writes must satisfy: - -- One topic per file. -- At most 250 lines. When an edit would push a file past 250 lines, split it - into focused files and link them rather than letting it grow. -- Relative paths in every link to another context file. -- A Mermaid diagram where structure, boundaries, or flows are complex enough - that prose alone would not carry them. -- Concrete code examples only where they clarify non-trivial behavior. - -When detail outgrows a shared file, migrate it into `context/{domain}/`, leave a -concise pointer behind, and link the new file from `context/context-map.md`. - -### 7. Verify synchronization - -After edits, verify: - -- Every changed context file accurately reflects the finished implementation. -- No edited statement contradicts the code, plan, or validation evidence. -- Every qualifying decision has one written or reused ADR path in the report, - and the report states when no decision qualified. -- Every file in the mandatory root pass was read and confirmed against code - truth, whether or not it was edited. -- Every plan `Context sync` requirement is met. -- Each feature implemented by the plan has a durable canonical description - reachable from `context/`. -- Every changed file is at or below 250 lines, covers one topic, and links other - context files by relative path. -- Diagrams are present where structure, boundaries, or flows are complex. -- Links and referenced paths resolve when practical to check. -- New context files are reachable from the context map or another authoritative - index. -- Root context remains concise and delegates details to domain files. -- Unrelated context was not changed. - -Use focused documentation, link, or formatting checks when available. - -Do not rerun full-plan validation. - -If synchronization cannot be completed without inventing facts or resolving a -material contradiction, preserve safe edits when appropriate and return a -`blocked` report. - -### 8. Return the Markdown report - -Return exactly one report status: - -- `synced` -- `no_context_change` -- `blocked` - -`synced` means context files were updated and verified. `no_context_change` -means existing context was checked and no edit was warranted. `blocked` means -context could not be synchronized safely. - -Return only the Markdown report. Do not add explanatory prose before or after -it. - -## Plan context synchronization boundaries - -Do not: - -- Accept a validation result whose status is not `validated`. -- Accept `failed` or `blocked` validation results. -- Implement or modify application code. -- Modify tests. -- Change task completion status, acceptance-criteria marks, or the Validation - Report. -- Rerun full-plan validation. -- Select or execute an implementation task. -- Create a Git commit or push changes. -- Create the context root. `sce setup --bootstrap-context` owns that. -- Narrate changed files as documentation. Feature existence is the only reason - to document a change that introduced no other durable knowledge. -- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command - except `sce-decision`, or invoke `sce-decision` outside the decision gate in - successful context synchronization. -- Delete a context file that has uncommitted changes. -- Return YAML. - -## Completion - -The phase is complete after: - -- The context root was confirmed, or a `blocked` report named - `sce setup --bootstrap-context` as the required action. -- The mandatory root pass was run. -- Plan context requirements were checked. -- The decision gate recorded every qualifying ADR path, found no qualifying - decision, or returned a synchronization blocker. -- Applicable durable context was synchronized and verified, no context change - was warranted, or a synchronization blocker was reported. -- One Markdown report matching `references/sync-report.md` was returned. - - diff --git a/.claude/skills/sce-validate/references/output.md b/.claude/skills/sce-validate/references/output.md index 9cc8a19b..8b811d79 100644 --- a/.claude/skills/sce-validate/references/output.md +++ b/.claude/skills/sce-validate/references/output.md @@ -1,186 +1,16 @@ -# Validation Result +# Validate output layouts -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. +Use only the applicable layout. Values come from internal workflow state. -The `Status` value must be exactly one of: +## Completion -- `validated` -- `failed` -- `blocked` +```markdown +------------------------------------- -The plan-file `## Validation Report` section is written separately using -`references/validation-report.md`. This file is the skill's return value to the -invoking workflow. +# Plan {plan-name} validated. -## Validated variant +All implementation tasks were already complete. +Final validation passed. -# Validation Report - -**Status:** validated -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` - -## Commands run - -- `{command}` -> {passed} — {concise outcome summary} - -## Acceptance criteria - -- [x] AC1: {criterion statement} — {evidence} -- [x] AC2: {criterion statement} — {evidence} - -## Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - -## Residual risks - -- {risk} -- None identified. - -## Context impact - -**Classification:** `{none | local | domain | root}` -**Affected areas:** `{comma-separated areas}` -**Required context:** - -- `{path or statement from the plan Context sync section}` - -{One or two sentences on why this classification fits the finished plan.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## Failed variant - -This variant is a session handoff. Another agent or a later session must be -able to act from it alone. Write it as a prompt the user can paste forward, not -as a summary of the validation run. - -# Validation failed — handoff - -**Status:** failed -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` -**Validation report:** written to `{plan path}` - -## Goal for the next session - -Repair the unfinished validation so every acceptance criterion and full -validation command passes. Do not modify tests or product code inside a -`/validate` run to force green results; fix the implementation (or the plan) in -a normal work session, then rerun validation. - -## What failed - -- `{check or AC id}`: {problem} - - Evidence: {command output, exit summary, or inspection finding} - - Required action: {concrete repair or decision} - -## Acceptance criteria - -- [x] AC1: {criterion} — {evidence} -- [ ] AC2: {criterion} — {why unmet} - -## Commands run - -- `{command}` -> {passed | failed | not_run} — {concise outcome summary} - -## Constraints - -- All implementation tasks were already complete when validation ran. -- Validation did not modify tests, application code, or configuration to clear - failures. -- Durable context was not synchronized; plan context sync runs only after - validation succeeds. -- Prefer the plan at `{plan path}` and its Validation Report as the source of - recorded evidence. - -## Residual risks - -- {risk} -- None identified. - -## Recommended work - -1. {First concrete fix, with files or areas when known} -2. {Second concrete fix, or decision the user must make} -3. Rerun final validation after the fixes land: - -`/validate {plan path}` - -Do not stop after the repair. The plan is not finished until `/validate` -returns `validated` and plan context sync completes. - ---- - -## Blocked variant - -# Validation blocked - -**Status:** blocked -**Plan:** `{plan path when resolved}` -**Name:** `{plan name when resolved}` - -## Issues - -- **{issue id}** ({category}): {problem} - - Impact: {impact} - - Required: {decision or action} - -## Incomplete tasks - -- `{task id}` — {title} -- Omit this section when no incomplete tasks apply. - -## Candidates - -- `{candidate plan path}` -- Omit this section when plan resolution was not ambiguous. - -## Next step - -{Exactly one continuation, matching the blocker:} - -- Incomplete tasks: - -`/next-task {plan path}` - -- Ambiguous plan: - -`/validate {candidate path}` - -- Missing plan content or other blocker: state the decision required. Do not - invent a command. - ---- - -## Report rules - -- Name the exact `Plan:` path so every emitted command is runnable. -- Use **Status:** exactly `validated`, `failed`, or `blocked`. -- Never claim a check passed unless it ran successfully or the authorized - inspection confirmed it. -- Do not modify tests or product code to clear a failure; record it under - **What failed**. -- The failed variant must always end its **Recommended work** with - `/validate {plan path}` as the final step after repairs. -- The failed variant must be self-contained enough to hand to another session - without the original chat. -- Include **Context impact** only on `validated`. Omit it on `failed` and - `blocked`; plan context sync is not invoked for non-success states. -- Do not include context synchronization results in this report. The invoking - workflow runs `sce-plan-context-sync` only after `validated`. -- Do not select or describe an unrelated next implementation task when status is - `validated`. -- Omit empty optional sections rather than writing placeholders. +Validation report: {plan-path} +``` diff --git a/.claude/skills/sce-validate/references/validation-report.md b/.claude/skills/sce-validate/references/validation-report.md index c99ad85e..3121e1a4 100644 --- a/.claude/skills/sce-validate/references/validation-report.md +++ b/.claude/skills/sce-validate/references/validation-report.md @@ -5,7 +5,7 @@ The Markdown section `sce-validation` appends to the plan file when returning under exactly one `## Validation Report` heading. This is plan-file content. The result returned to the workflow is defined -separately in `references/validation-result.md`. +separately in `references/validation.md`. Do not author this section while planning. Only `/validate` through `sce-validation` writes it. @@ -23,11 +23,6 @@ writes it. - `{command}` -> exit {code} ({concise outcome summary}) - `{command}` -> exit {code} ({concise outcome summary}) -### Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - ### Success-criteria verification - [x] AC1: {criterion statement} -> {evidence} @@ -66,19 +61,16 @@ After repairs, rerun: - Mark each acceptance criterion checkbox in the plan's `## Acceptance criteria` section to match the evidence. Do not mark a criterion met unless the check ran successfully or the inspection named by `Validate:` confirms it. -- Under **Scaffolding removed**, list only temporary debug code, intermediate - artifacts, or throwaway files introduced during the change. Write `None.` when - nothing temporary remained. -- Under **Failed checks and follow-ups**, record the failing check and its - evidence only. Do not describe code or test edits made during validation; +- Under **Failed checks and follow-ups**, record every failing check and its + evidence, including leftover debug-only flags, temporary artifacts, or local + scaffolding. Do not describe code or test edits made during validation; validation does not modify tests or product code to clear failures. Write `None.` when status is `validated`. - When status is `failed`, always include **Retry** with the exact `/validate {plan path}` command. Omit **Retry** when status is `validated`. - Keep evidence concise and factual. Do not narrate the whole implementation history. -- Do not claim context synchronization completed. Plan context sync is a later - workflow step and runs only after `validated`. +- Do not claim durable context synchronization as part of validation. - Do not rewrite task evidence or reopen completed tasks. - When a previous `## Validation Report` already exists, replace it with the new one rather than stacking duplicates. diff --git a/.claude/skills/sce-validate/references/validation.md b/.claude/skills/sce-validate/references/validation.md index 08d7f5a8..1816eaca 100644 --- a/.claude/skills/sce-validate/references/validation.md +++ b/.claude/skills/sce-validate/references/validation.md @@ -12,22 +12,18 @@ This phase owns: - Confirming every implementation task is complete. - Running the plan's full validation commands and each acceptance criterion check. -- Removing temporary scaffolding introduced by the change. - Writing the Validation Report into the plan. - Marking acceptance criteria against the evidence. - Returning one Markdown validation result. Return a result matching: -`references/validation-result.md` +the **Validation Result** section below in this file Write plan-file evidence matching: `references/validation-report.md` -Context synchronization is not this phase's job. The invoking `/validate` -workflow runs `sce-plan-context-sync` only after a `validated` result. - ## Input A plan name or path. @@ -60,7 +56,6 @@ From the plan, collect: - Every acceptance criterion and its `Validate:` check. - The `Full validation` command list. -- The `Context sync` requirements, for the context-impact handoff only. Return `blocked` when the plan has no usable acceptance criteria, or when no validation commands can be determined from the plan or repository conventions. @@ -69,20 +64,7 @@ Prefer the plan's authored checks. Fall back to repository-primary test, lint, and format commands only when `Full validation` is absent, and record that fallback under notes on a `validated` or `failed` result. -### 4. Remove temporary scaffolding - -Before or while running checks, remove temporary scaffolding introduced during -the change when it is clearly throwaway: - -- Debug-only patches or flags left enabled. -- Temporary files or intermediate artifacts not part of the delivered design. -- Local scaffolding the plan or task notes mark as temporary. - -Do not delete durable product code, tests, configuration, or context files. - -Record every removed path. When nothing temporary remains, report `None.` - -### 5. Run full validation and acceptance checks +### 4. Run full validation and acceptance checks Run the plan's `Full validation` commands. @@ -90,6 +72,11 @@ Then verify each acceptance criterion using its `Validate:` line. Prefer a runnable command. Use a named inspection only when the criterion authorizes it, and say exactly what was inspected. +Treat leftover debug-only flags, temporary files, intermediate artifacts, or +local scaffolding as a failed validation check. Record the path and evidence +under **Failed checks and follow-ups**; never delete or repair it during +validation. + When a check fails, record the failure and continue gathering evidence. Do not modify tests, application code, or configuration to make a check pass. Final validation measures the finished work; repair belongs to a later work session, @@ -101,7 +88,7 @@ inspection confirmed the criterion. Do not run task-by-task implementation work for incomplete tasks. That belongs to `/next-task`. -### 6. Update the plan +### 5. Update the plan For `validated` and `failed` outcomes: @@ -115,22 +102,7 @@ Do not reopen completed tasks, rewrite task evidence, or change the task stack. For `blocked`, leave the plan file unchanged. -### 7. Determine context impact for the handoff - -On `validated` only, classify the durable context impact of the finished plan -so `sce-plan-context-sync` can start from the plan's own requirements: - -- Start from the plan's `Context sync` section. -- Inspect what the completed implementation actually changed when needed. -- Report required context paths and affected areas. -- Use `none`, `local`, `domain`, or `root` with the same meanings as task-level - context sync. - -Do not edit context files here. - -On `failed` or `blocked`, omit context impact; context sync will not run. - -### 8. Return the Markdown result +### 6. Return the Markdown result Return exactly one Markdown result: @@ -138,7 +110,7 @@ Return exactly one Markdown result: passed, and the Validation Report was written. - `failed` when evidence was captured but required checks or criteria remain unsatisfied. Shape it as a session handoff per - `references/validation-result.md`, ending recommended work with + the **Validation Result** section below in this file, ending recommended work with `/validate {plan path}`. - `blocked` when validation cannot proceed safely. @@ -154,14 +126,14 @@ Do not: - Modify tests, application code, or configuration to make a failing check pass. - Apply lint or format auto-fixes that change product or test files as part of making validation green. -- Synchronize durable context under `context/` outside the plan file. +- Synchronize durable context under `context/`. - Create the context root. - Mark the plan archived or delete the plan. - Create a Git commit or push changes. - Invent acceptance criteria the plan does not state. - Claim verification that was not performed. - Return a YAML result. -- Invoke plan context sync. The workflow owns that step. + ## Completion The phase is complete after: @@ -169,7 +141,175 @@ The phase is complete after: - One plan was resolved, or resolution failed and was reported. - Implementation completeness was checked. - Validation ran to a terminal state, or a blocker prevented it. -- One valid Markdown result matching `references/validation-result.md` was +- One valid Markdown result matching the **Validation Result** section below in this file was returned. + +# Validation Result + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `validated` +- `failed` +- `blocked` + +The plan-file `## Validation Report` section is written separately using +`references/validation-report.md`. This file is the skill's return value to the +invoking workflow. + +## Validated variant + +# Validation Report + +**Status:** validated +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` + +## Commands run + +- `{command}` -> {passed} — {concise outcome summary} + +## Acceptance criteria + +- [x] AC1: {criterion statement} — {evidence} +- [x] AC2: {criterion statement} — {evidence} + +## Residual risks + +- {risk} +- None identified. + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## Failed variant + +This variant is a session handoff. Another agent or a later session must be +able to act from it alone. Write it as a prompt the user can paste forward, not +as a summary of the validation run. + +# Validation failed — handoff + +**Status:** failed +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` +**Validation report:** written to `{plan path}` + +## Goal for the next session + +Repair the unfinished validation so every acceptance criterion and full +validation command passes. Do not modify tests or product code inside a +`/validate` run to force green results; fix the implementation (or the plan) in +a normal work session, then rerun validation. + +## What failed + +- `{check or AC id}`: {problem} + - Evidence: {command output, exit summary, or inspection finding} + - Required action: {concrete repair or decision} + +## Acceptance criteria + +- [x] AC1: {criterion} — {evidence} +- [ ] AC2: {criterion} — {why unmet} + +## Commands run + +- `{command}` -> {passed | failed | not_run} — {concise outcome summary} + +## Constraints + +- All implementation tasks were already complete when validation ran. +- Validation did not modify tests, application code, or configuration to clear + failures. +- Validation does not synchronize durable context. +- Prefer the plan at `{plan path}` and its Validation Report as the source of + recorded evidence. + +## Residual risks + +- {risk} +- None identified. + +## Recommended work + +1. {First concrete fix, with files or areas when known} +2. {Second concrete fix, or decision the user must make} +3. Rerun final validation after the fixes land: + +`/validate {plan path}` + +Do not stop after the repair. The plan is not finished until `/validate` +returns `validated`. + +--- + +## Blocked variant + +# Validation blocked + +**Status:** blocked +**Plan:** `{plan path when resolved}` +**Name:** `{plan name when resolved}` + +## Issues + +- **{issue id}** ({category}): {problem} + - Impact: {impact} + - Required: {decision or action} + +## Incomplete tasks + +- `{task id}` — {title} +- Omit this section when no incomplete tasks apply. + +## Candidates + +- `{candidate plan path}` +- Omit this section when plan resolution was not ambiguous. + +## Next step + +{Exactly one continuation, matching the blocker:} + +- Incomplete tasks: + +`/next-task {plan path}` + +- Ambiguous plan: + +`/validate {candidate path}` + +- Missing plan content or other blocker: state the decision required. Do not + invent a command. + +--- + +## Report rules + +- Name the exact `Plan:` path so every emitted command is runnable. +- Use **Status:** exactly `validated`, `failed`, or `blocked`. +- Never claim a check passed unless it ran successfully or the authorized + inspection confirmed it. +- Do not modify tests or product code to clear a failure; record it under + **What failed**. +- The failed variant must always end its **Recommended work** with + `/validate {plan path}` as the final step after repairs. +- The failed variant must be self-contained enough to hand to another session + without the original chat. +- Do not include durable context synchronization results in this report. +- Do not select or describe an unrelated next implementation task when status is + `validated`. +- Omit empty optional sections rather than writing placeholders. diff --git a/.opencode/command/validate.md b/.opencode/command/validate.md index bdd82071..bd329e25 100644 --- a/.opencode/command/validate.md +++ b/.opencode/command/validate.md @@ -1,5 +1,5 @@ --- -description: "Validate one completed SCE plan and synchronize its durable context" +description: "Validate one completed SCE plan and record final validation evidence" argument-hint: "" agent: "Shared Context Code" entry-skill: "sce-validate" diff --git a/.opencode/skills/sce-change-to-plan/SKILL.md b/.opencode/skills/sce-change-to-plan/SKILL.md index 510f1c47..f57b7878 100644 --- a/.opencode/skills/sce-change-to-plan/SKILL.md +++ b/.opencode/skills/sce-change-to-plan/SKILL.md @@ -45,8 +45,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -119,7 +119,12 @@ Render the `plan_ready` result as the summary defined by the **Plan authoring ph Take the next task from `next_task`. A `plan_ready` result always names one. Do not evaluate its dependencies; the **Plan review phase** checks them when the emitted command runs and returns `blocked` if they are unmet. -The continuation invites revision. The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. +The workflow carries one of two explicit continuation shapes across a same-session wait: + +- **Initial-clarification continuation:** `original_request`, `clarification_answers`, and `loaded_context_brief`. `original_request` is the unchanged request from step 1; preserve it with the answers and never ask the user to provide it again. +- **Existing-plan revision continuation:** `plan_path`, `correction`, and `loaded_context_brief`. `plan_path` identifies the plan already written, and `correction` contains the user's requested revision. + +The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. Write `task` rather than `tasks` when `total_tasks` is 1. @@ -131,13 +136,13 @@ Then stop and wait. Do not implement, and do not run the handoff yourself. ### 4. Revise the plan on request -When the user answers clarification questions from step 2, answers open questions listed in the summary, or answers with changes to the plan, revise it in this session. Do not ask them to rerun `/change-to-plan`, and do not ask for the original change request again. +When the user answers clarification questions from step 2, resume the **Initial-clarification continuation** with `original_request`, `clarification_answers`, and the same `loaded_context_brief` from step 1. Preserve `original_request` unchanged and never ask the user for the original change request again. When the user answers open questions listed in the summary or requests changes to an already-written plan, resume the **Existing-plan revision continuation** with `plan_path`, `correction`, and the same `loaded_context_brief`. Do not ask them to rerun `/change-to-plan`. -Run the **Plan authoring phase** with their answer or correction and the same `loaded` brief from step 1. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. +Run the **Plan authoring phase** with the applicable continuation fields. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. An answer that resolves a doubt removes that open question. An answer that does not resolve it leaves the question standing; do not drop it because the user replied to it. If the reply raises a new doubt, the revised plan carries a new open question. -Pass the correction as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. +Pass `clarification_answers` or `correction` as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. Branch on `status` exactly as in step 2. A revision may legitimately return `needs_clarification` or `blocked`. diff --git a/.opencode/skills/sce-change-to-plan/references/plan-template.md b/.opencode/skills/sce-change-to-plan/references/plan-template.md index ac36a72c..19e8a12a 100644 --- a/.opencode/skills/sce-change-to-plan/references/plan-template.md +++ b/.opencode/skills/sce-change-to-plan/references/plan-template.md @@ -41,6 +41,17 @@ which criterion they map to. - {Durable context files that must describe the change once implemented.} +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + ## Constraints and non-goals - **In scope:** {files, modules, and surfaces this plan may touch} @@ -59,19 +70,19 @@ recorded. Remove the section otherwise.} - [ ] T01: `{single intent title}` (status:todo) - Task ID: T01 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: {task IDs, or none} - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending - [ ] T02: `{single intent title}` (status:todo) - Task ID: T02 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: T01 - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending ## Open questions @@ -92,11 +103,11 @@ invent one: `None.` is the expected answer for a well-specified change.} ```markdown - [ ] T02: `Add /auth/refresh endpoint` (status:todo) - Task ID: T02 - - Goal: Implement a POST `/auth/refresh` endpoint that exchanges a valid refresh token for a new access token. - - Boundaries (in/out of scope): In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. + - Scope: In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. - Dependencies: T01 - Done when: `POST /auth/refresh` returns a signed JWT on valid input and 401 on expired or invalid token; targeted tests pass; OpenAPI spec updated. - - Verification notes (commands or checks): `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Verify: `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Context synchronization: pending ``` ## Acceptance criteria rules @@ -109,7 +120,7 @@ invent one: `None.` is the expected answer for a well-specified change.} automated check exists, and say exactly what to look at. - List repository-wide checks once under `Full validation` instead of repeating them per criterion. -- Task-level `Verification notes` prove one task. Acceptance criteria prove the +- Task-level `Verify` proves one task. Acceptance criteria prove the plan. Keep them distinct: a task's checks are narrow and local, a criterion's check is end-to-end. - The union of the acceptance criteria must cover every success signal in the @@ -149,11 +160,14 @@ checkbox and status: ```markdown - [x] T01: `{title}` (status:done) - - {authored fields, unchanged} + - {authored fields, unchanged: Task ID, Scope, Dependencies, Done when} + - Verify: {each planned check, updated with its actual outcome} - Completed: {YYYY-MM-DD} - Files changed: {paths} - - Evidence: {commands run and their outcomes} - - Notes: {material deviations or approved assumptions} + - Result: {concise factual outcome, not a prose diff} + - Context impact: {durable context this change affects, or none} + - Context synchronization: pending | synced | blocked + - Context synchronization blocker: {present only when status is blocked} Blocker: {problem}; Required action: {action}; Retry condition: {condition} ``` `/validate` appends a `## Validation Report` section at the end of the plan. diff --git a/.opencode/skills/sce-commit/SKILL.md b/.opencode/skills/sce-commit/SKILL.md index 1abbe352..0300fa6e 100644 --- a/.opencode/skills/sce-commit/SKILL.md +++ b/.opencode/skills/sce-commit/SKILL.md @@ -37,8 +37,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -135,15 +135,25 @@ a contract violation: report it and stop without committing. #### 3. Execute exactly one commit -Run `git commit` once with the returned message. +Follow the **Bypass execution handoff** in `references/atomic-commit.md`: + +1. Create the commit-message temp file outside the repository working tree, and + write the returned `message` verbatim to it using a file-writing operation. Do + not interpolate the multiline message into shell source or a shell command. +2. Run `git commit -F ` exactly once. +3. Only after that command succeeds, retrieve the commit hash explicitly with + `git rev-parse --verify HEAD^{commit}`. Do not parse Git's human-readable + output. +4. Delete the temp file after the commit attempt, including on failure, where + practical. On success, render the **Bypass success** layout from `references/output.md` and stop. On failure, render the **Bypass Git failure** layout from the same file and stop. -Do not retry, do not amend, do not stage additional files, and do not invent a -fallback commit. +Do not retry, do not amend, do not stage additional files, and do not fabricate a +commit hash. ## Rules @@ -153,7 +163,9 @@ fallback commit. They are behaviorally identical. - Read `references/atomic-commit.md` before running the phase. - Do not duplicate the internal instructions of the **Atomic commit phase**. -- Do not stage, unstage, restore, or otherwise modify files. +- Do not stage, unstage, restore, or otherwise modify repository or worktree + files. The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Do not amend, reset, revert, rebase, or push. - Do not read unstaged or untracked changes as commit input. - Do not infer success when the **Atomic commit phase** returns a non-success status. diff --git a/.opencode/skills/sce-commit/references/atomic-commit.md b/.opencode/skills/sce-commit/references/atomic-commit.md index a2404c4c..b41d362a 100644 --- a/.opencode/skills/sce-commit/references/atomic-commit.md +++ b/.opencode/skills/sce-commit/references/atomic-commit.md @@ -8,10 +8,6 @@ Write messages matching: `references/commit-message-style.md` -Return a result matching: - -`references/commit-contract.yaml` - Committing is not this skill's job. The invoking `/commit` workflow decides whether a returned message is committed, and it is the only thing that runs `git commit`. @@ -108,22 +104,35 @@ Confirm before returning that: - No plan slug or task ID appears that the staged diff does not support. - The mode's own constraints hold. -### 9. Return YAML +## Bypass execution handoff -Return exactly one YAML document matching `references/commit-contract.yaml`: +This phase returns the message; the invoking `/commit` workflow performs the +bypass commit. When the mode is `bypass`, the invoking workflow must: -- `proposal` in `regular` mode, with one or more messages. -- `bypass_message` in `bypass` mode, with exactly one message. -- `blocked` when messages cannot be written faithfully. +1. Create the commit-message temp file outside the repository working tree, + and write the returned `message` verbatim to it using a file-writing + operation. Never interpolate a multiline message into shell source or a + shell command. +2. Run `git commit -F ` exactly once. +3. After and only after a successful commit, run + `git rev-parse --verify HEAD^{commit}` and use that explicit `HEAD` value as + the reported hash. Never parse Git's human-readable commit output. +4. On any commit failure, report Git's failure and stop. Never retry, amend, + stage more files, or fabricate a hash. +5. Delete the temp file after the commit attempt, including on failure, where + practical. -Return only the YAML document. Do not add explanatory prose before or after it. +`oneshot` and `skip` select this same bypass behavior; they differ only in the +trigger token. ## Atomic commit boundaries Do not: - Run `git commit`, or any command that writes to the repository or its index. -- Stage, unstage, or modify files. +- Stage, unstage, restore, or otherwise modify repository or worktree files. + The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Ask the user to stage or confirm staging. - Analyze unstaged or untracked changes. - Return more than one message in `bypass` mode. @@ -133,13 +142,13 @@ Do not: - Mention `context/` synchronization activity in a commit message. - Claim a message was committed. - Run plan, task, or validation workflows. + + ## Completion The skill is complete after: - The staged diff was read, or reading it failed and was reported. - Messages were written for every staged file, or a blocker prevented it. -- One valid terminal YAML result matching `references/commit-contract.yaml` was - returned. diff --git a/.opencode/skills/sce-commit/references/commit-message-style.md b/.opencode/skills/sce-commit/references/commit-message-style.md new file mode 100644 index 00000000..056382ff --- /dev/null +++ b/.opencode/skills/sce-commit/references/commit-message-style.md @@ -0,0 +1,44 @@ +# Commit Message Guide + +Use this repository style when writing new commits. + +## Core rules + +- Start with `scope: Subject` for most code changes. + - Common scopes: `runtime`, `language`, `objects`, `tests`, `CI`, `README`. + - Combined scopes are fine when needed (for example `language+runtime`). +- Use an imperative verb in the subject: `Fix`, `Add`, `Refactor`, `Remove`, `Implement`, `Update`, `Rewrite`, `Use`, `Allow`. +- Keep the subject specific and technical (name the subsystem and actual change). +- Keep the subject to one line and do not end it with a period. +- Add a body when the change is non-trivial. + - Explain why the change is needed. + - Explain how it works at a high level. + - Include impact/tradeoffs/follow-ups when relevant. +- For performance-related commits, include concrete measurements and benchmark context. + - Include regressions as well as improvements. +- Add issue references when relevant on their own lines. + - Example: `Fixes #123` + - Example: `Ref: https://...` + +## Practical template + +```text +: + + + + +Fixes # (optional) +``` + +## Size-based defaults + +1. Small fix: subject + 1 short reason line. +2. Medium refactor: subject + short why + short what changed. +3. Large architectural change: subject + context + bullets for major changes + impact/tradeoffs. + +## Anti-patterns to avoid + +- Vague subjects like "misc updates" or "cleanup". +- Bodies that only repeat the subject without explaining why or impact. +- Overly playful tone in serious bug-fix or architectural change. diff --git a/.opencode/skills/sce-decision/SKILL.md b/.opencode/skills/sce-decision/SKILL.md index 78a61e61..b545be96 100644 --- a/.opencode/skills/sce-decision/SKILL.md +++ b/.opencode/skills/sce-decision/SKILL.md @@ -10,18 +10,18 @@ compatibility: opencode ## Purpose Write exactly one architecture decision record for one qualifying system-wide -important decision during successful task or plan context synchronization. Return +important decision during successful task context synchronization. Return a deterministic internal handoff to the invoking synchronization phase. Do not render an independent user-visible response. ## Input -Accept one structured decision request from `sce-next-task` or `sce-validate` -context synchronization. It must identify: +Accept one structured decision request from `sce-next-task` task context +synchronization. It must identify: - One decision stated as a single durable choice. - Why it qualifies under the decision gate. -- The implementation or validation evidence establishing the decision. +- The implementation / task-verification evidence establishing the decision. - The resolved plan path and relevant task IDs, when applicable. - Related current-state context and existing ADR paths. - An optional requested status. @@ -45,8 +45,12 @@ constraint involving at least one of: Routine implementation details, local refactors, naming and formatting choices, temporary experiments, and easily reversible choices do not qualify. When the -request does not demonstrate the threshold, return `blocked`; do not create an -ADR merely because context synchronization occurred. +request does not demonstrate the threshold, return `not_qualified` (or +`skipped` when the caller deliberately skips the gate); do not create an ADR +merely because context synchronization occurred. A nonqualifying or skipped +result is non-blocking, so the invoking synchronization phase continues +normally. Reserve `blocked` for missing, contradictory, or otherwise unsafe +decision input or history. ## Workflow @@ -57,20 +61,24 @@ path, references sufficient to make the record traceable, and no unresolved material contradiction. If it contains several decisions, require the caller to submit one request per decision. -Allowed statuses are exactly `Proposed`, `Accepted`, `Rejected`, `Deprecated`, -and `Superseded`. Use the explicitly requested allowed status; otherwise default -to `Accepted`. Reject any other status rather than guessing. +Allowed statuses for a newly written ADR are exactly `Proposed`, `Accepted`, +`Rejected`, `Deprecated`, and `Superseded`. Use the explicitly requested allowed +status; otherwise default to `Accepted`. `Deprecated` and `Superseded` remain +distinct creation-time-only statuses: use them to describe the record when it is +created, but never mutate an existing ADR into or out of either status. Reject any +other status rather than guessing. ### 2. Inspect existing decision history Read `context/decisions/` and the supplied related ADR paths before writing. -- Reuse an existing ADR when it already records the same decision; return its - path without creating a duplicate. -- Never edit an ADR whose status is `Accepted`. -- A correction, reversal, or changed decision creates a new dated ADR that - references and supersedes the accepted record. -- Do not overwrite any existing ADR or silently change its status. +- Reuse an existing ADR only when it records an equivalent decision and has an + active status: `Proposed` or `Accepted`. Return its path without creating a + duplicate. Never reuse a `Rejected`, `Deprecated`, or `Superseded` ADR. +- Existing ADRs are immutable regardless of status. Never edit an ADR whose status is `Accepted`; do not edit, overwrite, or silently change the status of any existing record. +- A correction, reversal, or any changed decision always creates a new dated ADR; + it references and supersedes the prior record when applicable, rather than + modifying that record. If `context/` or `context/decisions/` is absent, or history cannot be interpreted without inventing facts, return `blocked` without creating directories. @@ -118,8 +126,11 @@ Return exactly one internal handoff: - `written`: include `status`, `adr_path`, `decision`, `decision_status`, `created` (`true` for a new ADR and `false` for reuse), `supersedes`, and concise verification evidence. +- `not_qualified` or `skipped`: include `status`, the reason the decision gate + did not produce an ADR, and concise supporting evidence. These results are + non-blocking; the invoking synchronization phase continues normally. - `blocked`: include `status`, the specific `problem`, its `impact`, and the - `required_action`. + `required_action`. Use this only when decision writing cannot proceed safely. Use stable field names and repository-relative paths. Return no prose before or after the handoff. The invoking synchronization phase owns all user-visible @@ -130,7 +141,7 @@ reporting. Do not: - Write more than one ADR per request. -- Run outside successful task or plan context synchronization. +- Run outside successful task context synchronization. - Create a command, prompt, context root, or decisions directory. - Modify application code, tests, plans, current-state context, or existing accepted ADRs. diff --git a/.opencode/skills/sce-handover/SKILL.md b/.opencode/skills/sce-handover/SKILL.md index a512ff8b..edff1a72 100644 --- a/.opencode/skills/sce-handover/SKILL.md +++ b/.opencode/skills/sce-handover/SKILL.md @@ -26,8 +26,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -62,7 +62,8 @@ blockers. Ground those facts against repository state: -- `git status` and `git diff` for uncommitted work. +- `git status`, `git diff`, and `git diff --cached` for uncommitted work, + including both unstaged and staged changes. - `context/plans/*.md` for the active plan and task, when one is being worked. - Recent commits, when they clarify what just landed. @@ -85,8 +86,9 @@ timestamp segment, rather than overwriting it. #### 3. Compose the handover document -Use the **Persisted-document format: Handover document** section embedded in -this file. Populate all four required sections: +Read `references/handover-template.md` before composing. It defines the +persisted-document format and is the only template authority. Populate all +four required sections: - `Current Task State` - `Decisions Made` @@ -136,10 +138,15 @@ and stop. Read the file and confirm it contains all four required sections: `Current Task State`, `Decisions Made`, `Open Questions / Blockers`, and -`Next Recommended Step`. +`Next Recommended Step`. For each section, inspect the content up to the +next required heading (or the end of the file): it must contain non-whitespace +content, and it must not consist only of an empty list marker, a template +placeholder such as `{What is being worked on...}`, or other unreplaced +`{...}` scaffolding. Explicit statements such as `None identified.` are real +content and are valid. -When any required section is missing, render the **Loader blocked** layout -(invalid handover) and stop. +When any required section is missing, empty, or placeholder-only, render the +**Loader blocked** layout (invalid handover) and stop. #### 3. Present for continuation @@ -164,53 +171,3 @@ guidance is the entire loader contract. loadable handover. - Never create the `context/` root; `sce setup --bootstrap-context` owns that. - Do not begin, plan, or automate the loaded handover's recommended next step. - -## Internal persisted-document format: Handover document - -The Markdown document writer mode creates under -`context/handovers/{name}.md`. This is the persisted file's content, distinct -from the terminal response defined in `references/output.md`. - -### Layout - -```markdown -# Handover: {plan name or short session topic} - -Date: {YYYY-MM-DD} -Plan: `{context/plans/plan-name.md}` (omit when no plan applies) -Task: `{task-id}` (omit when no single task applies) - -## Current Task State - -{What is being worked on, what is complete, what is in progress. Cite files, -commands, or plan/task references where they ground the statement.} - -## Decisions Made - -- {Decision and its rationale, or `None made this session.`} - -## Open Questions / Blockers - -- {Unresolved question or blocker, or `None identified.`} - -## Next Recommended Step - -{The single most useful next action for the following session, concrete -enough to act on directly.} - -## Assumptions - -- {Any detail above that was inferred rather than directly evidenced, or - `None.`} -``` - -### Rules - -- Include `Plan` and `Task` only when the session was working one identifiable - plan task; omit them rather than guessing. -- Every one of the four required sections must appear, in this order, even - when its content is `None identified.` or an equivalent. -- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere - in the document; do not duplicate confirmed facts here. -- Describe durable state useful to a future session, not a transcript of this - one. diff --git a/.opencode/skills/sce-handover/references/handover-template.md b/.opencode/skills/sce-handover/references/handover-template.md new file mode 100644 index 00000000..51edfefb --- /dev/null +++ b/.opencode/skills/sce-handover/references/handover-template.md @@ -0,0 +1,47 @@ +The Markdown document writer mode creates under +`context/handovers/{name}.md`. This is the persisted file's content, distinct +from the terminal response defined in `references/output.md`. + +### Layout + +```markdown +# Handover: {plan name or short session topic} + +Date: {YYYY-MM-DD} +Plan: `{context/plans/plan-name.md}` (omit when no plan applies) +Task: `{task-id}` (omit when no single task applies) + +## Current Task State + +{What is being worked on, what is complete, what is in progress. Cite files, +commands, or plan/task references where they ground the statement.} + +## Decisions Made + +- {Decision and its rationale, or `None made this session.`} + +## Open Questions / Blockers + +- {Unresolved question or blocker, or `None identified.`} + +## Next Recommended Step + +{The single most useful next action for the following session, concrete +enough to act on directly.} + +## Assumptions + +- {Any detail above that was inferred rather than directly evidenced, or + `None.`} +``` + +### Rules + +- Include `Plan` and `Task` only when the session was working one identifiable + plan task; omit them rather than guessing. +- Every one of the four required sections must appear, in this order, even + when its content is `None identified.` or an equivalent. +- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere + in the document; do not duplicate confirmed facts here. +- Describe durable state useful to a future session, not a transcript of this + one. diff --git a/.opencode/skills/sce-handover/references/output.md b/.opencode/skills/sce-handover/references/output.md index 82928975..afe30b5f 100644 --- a/.opencode/skills/sce-handover/references/output.md +++ b/.opencode/skills/sce-handover/references/output.md @@ -32,28 +32,6 @@ No file was written. **Path:** `{written path}` -## Current Task State - -{same content written to the file} - -## Decisions Made - -- {as written} - -## Open Questions / Blockers - -- {as written} - -## Next Recommended Step - -{as written} - -## Assumptions - -- {as written, or `None.`} - ---- - To continue from this handover in another session: `/handover {written path}` diff --git a/.opencode/skills/sce-next-task/SKILL.md b/.opencode/skills/sce-next-task/SKILL.md index 384550a8..d892a9a8 100644 --- a/.opencode/skills/sce-next-task/SKILL.md +++ b/.opencode/skills/sce-next-task/SKILL.md @@ -46,9 +46,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -86,12 +85,31 @@ Branch on `status`: `blocked` -> Do not run implementation. Render the **Review blocked** layout from `references/output.md`. When `candidates` is present the plan could not be resolved, and each entry is a candidate path for `/next-task {candidate-path}`. `executable_tasks_remaining` true means another task remains executable and `/next-task {plan-path} {task-id}` selects one; false means no task in the plan can proceed until the plan is updated. Do not print the raw result. Stop. +`sync_debt` -> Read `references/context-sync.md`, then run the **Task context synchronization phase** using the debt task's persisted `Context synchronization handoff` — and, when present, its persisted `Context synchronization blocker` — named by the **Plan review phase**. Do not reconstruct a missing handoff from conversation history. + +Write the debt task's lifecycle to the plan: `synced`, clearing its blocker, required action, and retry condition, for `synced` or `no_context_change`; a refreshed `blocked` state with the report's blocker, required action, and retry condition for `blocked`. If that lifecycle write fails, treat the outcome as `blocked`. + +Branch on the outcome: + +`blocked` -> Render the **Context synchronization blocked** layout from `references/output.md`, distinct from the **Review blocked** layout above. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select or start a new task. Stop. + +`synced` | `no_context_change` -> Re-invoke the **Plan review phase** with the same `plan-name-or-path` and, when present, `task-id` to resume normal task selection. + `plan_complete` -> Render the **Plan already complete** layout from `references/output.md`. Stop. `ready` -> Pass the complete readiness result to the **Task execution phase**. Do not reconstruct, summarize, or reinterpret the reviewed task before passing it. +The review inspects every completed task's `Context synchronization` field in +the plan, in plan order, regardless of its position relative to the task being +selected or resumed, before allowing a new implementation task to start. A +missing field, or any value other than `synced`, is unresolved synchronization +debt. Never infer `synced` from conversation history. When the debt-carrying +task has no durable `Context synchronization handoff` subsection, the **Plan +review phase** returns `blocked` directly with a legacy-migration required +action; otherwise it returns `sync_debt`, resolved by the branch above. + ### 2. Execute the task Read `references/task-execution.md`, then run the **Task execution phase** with @@ -133,9 +151,14 @@ Do not restate, summarize, or reconstruct any part of the execution result. This phase verifies the five root context files on every invocation, whatever the change's reported impact, so it is never correct to skip it as unnecessary. +Before branching on the synchronization result, write the completed task's +lifecycle to the plan file: `synced` for `synced` or `no_context_change`, and +`blocked` with the report's blocker, required action, and retry condition for +`blocked`. If that lifecycle write fails, treat synchronization as `blocked`. + Branch on the synchronization result. -`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. Nothing records the skipped synchronization, so it is lost once this session ends. +`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select another task. Stop. diff --git a/.opencode/skills/sce-next-task/references/context-sync.md b/.opencode/skills/sce-next-task/references/context-sync.md index 3c5cb8b4..30ec3eae 100644 --- a/.opencode/skills/sce-next-task/references/context-sync.md +++ b/.opencode/skills/sce-next-task/references/context-sync.md @@ -5,46 +5,70 @@ Run this phase for step 3 of the workflow, and only when task execution returned session inherits what this task established. It never touches code, tests, or plan state. -Input: the complete `complete` result from the task execution phase, passed -verbatim. It is the authoritative handoff, and this phase owns reading the plan, -task, changed files, verification evidence, and reported context impact out of -it. +Input: either the complete `complete` result from the task execution phase +(same-session), passed verbatim, or the plan path and task ID a plan-review +recovery step resolved for a `blocked` task, together with that task's own +completed record — read directly from the plan — and its persisted `Context +synchronization blocker` when present (cross-session retry). Whichever was +supplied is the authoritative source, and this phase owns reading the plan, +task, changed files, verification evidence, and reported context impact out +of it. -Do not restate, summarize, or reconstruct any part of the execution result. Do -not reconstruct a missing execution result from conversation history. +Do not restate, summarize, or reconstruct any part of it. Do not reconstruct a +missing execution result or completed task record from conversation history. -The execution result must have: +A live execution result must have: ```text status: complete ``` -Treat the execution result as the authoritative handoff for: +A cross-session retry has no separate `status` field to check; the completed +task record's presence in the plan, identified by plan path and task ID, is +itself the authoritative signal. + +Use the report format in: + +`references/sync-report.md` + +Treat whichever source was supplied — the live execution result, or the +completed task record read directly from the plan — as the authoritative +source for: - The resolved plan and completed task. +- `changes.files_changed`, or the completed task record's own `Files changed` + field on retry, already attributed relative to the pre-edit Git baseline. - Files changed by implementation. -- Implementation summary. -- Verification evidence. +- The task's `Result` (or implementation summary, for a live result). +- `Verify` outcomes (or verification evidence, for a live result). - Done-check evidence. - Reported context impact. This phase must not be run for `declined`, `blocked`, or `incomplete` execution results. -## 3.1 Validate the execution handoff +## 3.1 Validate the handoff Confirm that: -- `status` is exactly `complete`. -- A `plan` object with a `path` is present. -- Exactly one completed task is identified. -- Changed files and an implementation summary are present. -- Verification evidence is present. +- A live execution result has `status` exactly `complete`; a cross-session + retry has no `status` field to check and is authoritative by the completed + task record's presence in the plan. +- A resolved plan path and task ID are present; a live execution result + carries them in its `plan` and `task` objects, and a cross-session retry + receives them directly from the caller that resolved the debt task. +- Exactly one completed task is identified, and — on retry — its record is + read directly from the plan by that plan path and task ID rather than + reconstructed in-band. +- Changed files and a `Result` (an implementation summary, for a live result) + are present. +- `Verify` outcomes (verification evidence, for a live result) are present. - Done-check evidence is present. - A context-impact classification is present. -If the handoff is missing required information or is internally contradictory, do -not modify context. Return a `blocked` Markdown report. +If the required information is missing, the completed task record cannot be +read from the plan, or either is internally contradictory, do not modify +context. Return a `blocked` Markdown report. ## 3.2 Confirm the context root @@ -289,6 +313,13 @@ Set exactly one report status: existing context was checked and no edit was warranted. `blocked` means context could not be synchronized safely. +A `blocked` report always writes the plan path and task ID/title as identity, +plus a `Context synchronization blocker` section (blocker, required action, +retry condition), using the same field names the plan's completion record +uses, so the plan-review recovery step can persist the blocker verbatim and a +future retry can read the completed task record directly from the plan by +plan path and task ID. + Record only the Markdown report. Do not add explanatory prose before or after it. Do not determine whether the plan is complete. The `/next-task` workflow owns diff --git a/.opencode/skills/sce-next-task/references/output.md b/.opencode/skills/sce-next-task/references/output.md index d0ade404..b6e772bd 100644 --- a/.opencode/skills/sce-next-task/references/output.md +++ b/.opencode/skills/sce-next-task/references/output.md @@ -138,145 +138,3 @@ When the `approve` flag is supplied, omit the question and end after wait. - If the handoff is stale or incomplete, show the known task information and identify the problem under **Risks or trade-offs**. - -# Context Sync Report - -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. - -The `Status` value must be exactly one of: - -- `synced` -- `no_context_change` -- `blocked` - -The input execution status is always `complete` and does not need to be repeated -as a separate workflow state. - -## Synced variant - -# Context Sync Report - -**Status:** synced -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Updated context - -- `{context file}` — {concise description of the durable truth updated} - -## Architecture decisions - -- `{written or reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}` - -## Verification - -- {How the edited context was checked against implementation and execution evidence.} -- {File hygiene: line counts, relative links, diagrams where structure is complex.} -- {Documentation, link, or formatting checks that were run, when applicable.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## No-context-change variant - -# Context Sync Report - -**Status:** no_context_change -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Synchronization result - -{Explain why the completed implementation did not introduce durable, -non-obvious repository knowledge requiring an update.} - -## Context reviewed - -- `{context file or area}` — {what was checked and why it remains accurate} - -## Architecture decisions - -- `{reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}`, already present. - -## Verification - -- {How existing context was compared with implementation and execution evidence.} - ---- - -## Blocked variant - -# Context Sync Report - -**Status:** blocked -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Blocker - -**Problem:** {specific synchronization blocker} -**Impact:** {why context cannot be made authoritative safely} -**Required action:** {decision or correction required} - -## Context changes - -- {List safe context edits preserved, or state `No context files were changed.`} - -## Architecture decisions - -- `{ADR path written or reused before the blocker}` — {decision and status} -- None written or reused before the blocker. - -## Retry condition - -{State the concrete condition under which context synchronization should run -again.} - -## Report rules - -- Name exact context files when they were changed or reviewed. -- Under **Architecture decisions**, list every ADR path written or reused during - the decision gate. In a successful report, state `None qualified.` when the - gate skipped invocation. In a blocked report, state - `None written or reused before the blocker.` when applicable. -- Under **Updated files**, list every changed file from the execution handoff - except paths under `context/`. -- Report the missing context root as `blocked`, with `sce setup - --bootstrap-context` as the required action and the existence of `context/` as - the retry condition. -- Omit **Feature existence** only when the task implemented no feature. -- Describe durable truth, not implementation-session chronology. -- Keep evidence concise and factual. -- Do not claim final validation passed. -- Do not determine whether the plan is complete. -- Do not recommend a next implementation task. diff --git a/.opencode/skills/sce-next-task/references/plan-review.md b/.opencode/skills/sce-next-task/references/plan-review.md index d08975e3..89e378f3 100644 --- a/.opencode/skills/sce-next-task/references/plan-review.md +++ b/.opencode/skills/sce-next-task/references/plan-review.md @@ -1,8 +1,8 @@ # Plan review phase Run this phase for step 1 of the workflow. It resolves one plan, selects one -task, and decides whether that task can be implemented right now. It reads; it -never writes. +task, and decides whether that task can be implemented right now. It reads; +it never writes. Inputs: the parsed `plan-name-or-path`, and `task-id` when present. The `auto-approve` token is not passed here and has no meaning in this phase. @@ -20,7 +20,30 @@ Read the selected plan before exploring the repository. ## 1.2 Resolve one task -When a task ID is supplied, select that task. +Before selecting or starting a task, inspect every completed task's +`Context synchronization` field in the plan, in plan order, regardless of its +position relative to the task being selected or resumed. A missing field, or +any value other than `synced`, is unresolved synchronization debt. Never infer +`synced` from chat history. + +For the first task carrying debt: + +- When the task has no durable completed-task record (no `Files changed`, + `Result`, `Verify`, or `Context impact` recorded — a legacy plan predating + that structure, or an incomplete write), do not attempt a reconstructed retry. + Set internal status `blocked` with a required action to migrate the plan + (backfill the completion record, or resolve the debt manually) and a retry + condition of the plan carrying that structure. Stop. +- Otherwise, set internal status `sync_debt`, naming the debt task (its ID and + title) and its own completed record — read directly from the plan by plan + path and task ID — including, when its field is `blocked`, its persisted + `Context synchronization blocker`. Do not run or cite the Task context + synchronization phase. Stop. Do not select or start a new task. + +Only after every completed task is `synced` does task selection proceed. + +When a task ID is supplied, select that task only after the same synchronization- +debt check passes. Otherwise, select the first incomplete task in plan order whose declared dependencies are complete. @@ -90,6 +113,7 @@ Set exactly one internal state: - `ready` - `blocked` - `plan_complete` +- `sync_debt` Record only the internal state. Do not add explanatory prose before or after it. @@ -103,8 +127,14 @@ A `ready` result must identify: - Relevant files and context. - Review assumptions. -Step 2 consumes this result verbatim, so anything the execution phase needs has -to be present here. +A `sync_debt` result must identify: + +- The debt-carrying task's ID and title. +- Its own completed record, read directly from the plan by plan path and task ID. +- Its persisted `Context synchronization blocker`, when present. + +Step 2 consumes a `ready` result verbatim, so anything the execution phase +needs has to be present here. ## Plan review boundaries @@ -113,7 +143,7 @@ Do not: - Modify application code. - Modify tests. - Update the plan. -- Mark the task complete. +- Mark a task complete. - Request implementation confirmation. - Run task execution. - Synchronize context. diff --git a/.opencode/skills/sce-next-task/references/sync-report.md b/.opencode/skills/sce-next-task/references/sync-report.md new file mode 100644 index 00000000..0211740c --- /dev/null +++ b/.opencode/skills/sce-next-task/references/sync-report.md @@ -0,0 +1,137 @@ +# Context Sync Report + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `synced` +- `no_context_change` +- `blocked` + +The input execution status is always `complete` and does not need to be repeated +as a separate workflow state. + +## Synced variant + +# Context Sync Report + +**Status:** synced +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Updated context + +- `{context file}` — {concise description of the durable truth updated} + +## Architecture decisions + +- `{written or reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}` + +## Verification + +- {How the edited context was checked against implementation and execution evidence.} +- {File hygiene: line counts, relative links, diagrams where structure is complex.} +- {Documentation, link, or formatting checks that were run, when applicable.} + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## No-context-change variant + +# Context Sync Report + +**Status:** no_context_change +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Synchronization result + +{Explain why the completed implementation did not introduce durable, +non-obvious repository knowledge requiring an update.} + +## Context reviewed + +- `{context file or area}` — {what was checked and why it remains accurate} + +## Architecture decisions + +- `{reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}`, already present. + +## Verification + +- {How existing context was compared with implementation and execution evidence.} + +--- + +## Blocked variant + +# Context Sync Report + +**Status:** blocked +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Context synchronization blocker + +- Blocker: {specific synchronization blocker} +- Required action: {decision or correction required} +- Retry condition: {concrete condition under which context synchronization + should run again} + +## Context changes + +- {List safe context edits preserved, or state `No context files were changed.`} + +## Architecture decisions + +- `{ADR path written or reused before the blocker}` — {decision and status} +- None written or reused before the blocker. + +## Report rules + +- Name exact context files when they were changed or reviewed. +- Under **Architecture decisions**, list every ADR path written or reused during + the decision gate. In a successful report, state `None qualified.` when the + gate skipped invocation. In a blocked report, state + `None written or reused before the blocker.` when applicable. +- Under **Updated files** (synced and no-context-change reports), list every + changed file from the execution handoff except paths under `context/`. A + blocked report does not repeat that list — it is already on the plan's + completed task record. +- Report the missing context root as `blocked`, with `sce setup + --bootstrap-context` as the required action and the existence of `context/` as + the retry condition. +- In a blocked report, write the `Context synchronization blocker` + subsection using the same field names the plan's completion record + uses, so plan review can persist it verbatim. +- Omit **Feature existence** only when the task implemented no feature. +- Describe durable truth, not implementation-session chronology. +- Keep evidence concise and factual. +- Do not claim final validation passed. +- Do not determine whether the plan is complete. +- Do not recommend a next implementation task. diff --git a/.opencode/skills/sce-next-task/references/task-execution.md b/.opencode/skills/sce-next-task/references/task-execution.md index 7b049492..fe01b530 100644 --- a/.opencode/skills/sce-next-task/references/task-execution.md +++ b/.opencode/skills/sce-next-task/references/task-execution.md @@ -21,9 +21,26 @@ workflow. It suppresses the approval question and the wait. It never suppresses the gate. Only the workflow entrypoint may set it, and only from an explicit user-supplied approval token. Never infer it. -If required handoff information is absent or stale, still show the gate using -what is known, clearly identify the handoff problem, and do not edit files. After -the user responds, set internal status `blocked`. +If required handoff information is absent, stale, or contradictory, still show the +gate using what is known, clearly identify the handoff problem, and do not edit +files. With the `approve` flag supplied, do not treat pre-approval as permission +to repair or reinterpret the handoff: after showing the gate, set internal status +`blocked` deterministically. Without the flag, wait for the user's response and +then set internal status `blocked`; do not retry the handoff in the same phase. + +A successful `complete` handoff must explicitly contain all of these fields: + +- The resolved `plan` object, including its path and completion counts. +- The selected `task` identity, including its ID and title. +- `changes.files_changed`, the implementation's baseline-relative changed-file list. +- `changes.summary`, a concise implementation summary. +- `verification`, with every reported outcome marked `passed` and its evidence. +- `done_checks`, pairing every done check with evidence. +- `plan_update`, proving the selected task was marked complete and evidence recorded. +- `context_impact`, including classification, affected areas, and reason. + +Do not omit, invent, or reconstruct any of these fields when handing off to context +synchronization. ## 2.1 Validate the handoff without editing @@ -80,7 +97,21 @@ If those constraints materially contradict the reviewed task, set internal statu ## 2.4 Prepare the implementation -Before editing: +Before editing, capture a Git baseline. Record the current `HEAD` commit, the +staged and unstaged patch/content state, and every untracked path/content state +using equivalent `git status`, `git diff`, and `git diff --cached` views. If the +baseline cannot be captured reliably, stop before editing and set internal status +`blocked`. + +After implementation, capture the same views again. Compute +`changes.files_changed` by comparing the post-edit snapshot with the pre-edit +baseline, not by listing the whole working tree or by diffing only against +`HEAD`. Include each path whose state or content changed during this task once; +exclude paths unchanged from the baseline, including unrelated pre-existing +staged, unstaged, and untracked changes. A path already dirty at baseline is +included only when this task changed its state or content. + +Then: - Read the relevant files supplied by plan review. - Inspect nearby code and tests when needed. @@ -145,8 +176,12 @@ Never report a check as passed unless it ran successfully. Only after successful implementation and task-level verification: - Mark only the selected task complete. -- Record concise implementation evidence. -- Record verification commands and outcomes. +- Record directly on the completed task: `Completed` (the date), the + baseline-relative `Files changed` list, a concise factual `Result`, the + actual outcome of every planned `Verify` check, and `Context impact`. +- Set that task's `Context synchronization` field to `pending` in the plan file + before returning `complete`; this write must happen after the execution + facts above and before the synchronization phase is invoked. - Record material deviations or approved assumptions. - Preserve the plan's existing structure and terminology. @@ -173,6 +208,14 @@ Set internal status `blocked` for every other non-successful outcome, including: Do not determine whether the plan is complete. The `/next-task` workflow owns that decision after context synchronization. +Before determining terminal status for a `complete` result, verify that the +handoff contains the resolved plan, task identity, baseline-relative changed +files, implementation summary, verification evidence, done-check evidence, plan +update, and context-impact classification listed above. The mandatory five-root- +file context pass remains required for every completed task, regardless of the +reported context-impact classification, because it is cheap, deterministic, and +load-bearing for context accuracy; `context_impact` must not be used to waive it. + ## 2.9 Return internal state After the phase reaches a terminal state, set exactly one internal state. diff --git a/.opencode/skills/sce-validate/SKILL.md b/.opencode/skills/sce-validate/SKILL.md index 7b8f68fa..3484a180 100644 --- a/.opencode/skills/sce-validate/SKILL.md +++ b/.opencode/skills/sce-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: sce-validate description: > - Validate one completed SCE plan and synchronize its durable context + Validate one completed SCE plan and record final validation evidence compatibility: opencode --- @@ -12,8 +12,7 @@ compatibility: opencode Own this workflow from input parsing through its terminal user-visible response. Execute the phases below directly and in order. Phase statuses are internal state, not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE -package, or SCE workflow command except `sce-decision`, and invoke `sce-decision` -only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates, +package, or SCE workflow command. Follow the canonical workflow's steps, gates, and stops exactly as written: never invent, skip, reorder, or merge a step. ## Phase references @@ -25,15 +24,12 @@ receives, and how its result branches — and each reference holds the phase its | Step | Read before running the phase | |---|---| | 1 | `references/validation.md` | -| 2 | `references/context-sync.md` | `references/validation-report.md` defines the `## Validation Report` section written into the plan file. Step 1 points to it at the moment it is needed, on a `validated` or `failed` outcome only. -Read a step's reference before taking any action for that step, not after. Read -only the reference for the step you have reached: a run that stops at a `blocked` -or `failed` validation never enters step 2, which is why they are separate files. +Read the reference before taking any action for step 1, not after. ## User-visible output @@ -46,9 +42,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -90,66 +85,25 @@ Do not write the Validation Report yourself. Branch on the report's `Status:`. -`blocked` -> Do not run context synchronization. Print the blocked Markdown -report as returned. Do not rephrase it into a different layout. Stop. +`blocked` -> Print the blocked Markdown report as returned. Do not rephrase it +into a different layout. Stop. -`failed` -> Do not run context synchronization. Print the failed Markdown -report as returned. It is already a session handoff: self-contained, actionable, -and ending with `/validate {plan-path}` after repairs. +`failed` -> Print the failed Markdown report as returned. It is already a session +handoff: self-contained, actionable, and ending with `/validate {plan-path}` after +repairs. Do not rewrite it into a shorter summary. Do not drop the retry command. Do not -add an alternate continuation that replaces `/validate`. +add an alternate continuation that replaces `/validate`. Stop. -Stop. Do not mark the plan finished. Do not continue to context synchronization. -Do not start the repair work in this workflow unless the user explicitly asks -to continue here; the default is that the handoff can leave this session. - -`validated` -> Pass the complete validated Markdown result to the **Plan context synchronization phase**. - -Do not reconstruct, summarize, or reinterpret the validation result before -passing it. - -### 2. Synchronize plan context - -Read `references/context-sync.md`, then run the **Plan context synchronization -phase** with the `Status: validated` Markdown result from the **Validation -phase**. - -Do not run the **Plan context synchronization phase** for `failed` or `blocked`. Those are not -success states. - -Pass the validated result verbatim. It is the authoritative handoff, and the **Plan context synchronization phase** -owns reading the plan path, required context paths, validation evidence, and -reported context impact out of it. - -Do not restate, summarize, or reconstruct any part of the validation result. - -This phase verifies the five root context files on every invocation, whatever the -reported impact, and must account for every path in the plan's `Context sync` -section, so it is never correct to skip it as unnecessary. - -Branch on the synchronization result. - -`blocked` -> Validation itself succeeded and is already recorded in the plan. -Render the **Context synchronization blocked** layout from -`references/output.md`. Nothing records the skipped synchronization, so it is -lost once this session ends. - -Stop. - -`synced` | `no_context_change` -> Print out the report returned by the **Plan context synchronization phase**. +`validated` -> Print the complete validated Markdown result as returned. Continue to the next step. -### 3. Report completion +### 2. Report completion Return exactly one completion block. Do not start another workflow. Render the **Completion** layout from `references/output.md`. -When the synchronization status was `no_context_change`, keep the same -completion block. "Synchronized" here means the final context pass finished -successfully, including the case where no edit was warranted. - Stop. ## Rules @@ -157,14 +111,10 @@ Stop. - Validate at most one plan per invocation. - Read each phase's reference before running that phase. - Do not duplicate the internal instructions of embedded phases. -- The only permitted sibling-skill invocation is `sce-decision`, and only the - successful context-synchronization decision gate may invoke it. - Do not run final validation when implementation tasks remain; the **Validation phase** returns `blocked`, and this workflow stops. -- Run the **Plan context synchronization phase** only when the **Validation phase** returned - `Status: validated`. Do not run it for `failed` or `blocked`. - On `failed`, print the handoff Markdown as returned and stop. Preserve the - retry `/validate {plan-path}` instruction. Do not synchronize context. + retry `/validate {plan-path}` instruction. - Do not implement remaining plan tasks from this workflow unless the user explicitly continues in-session after a failed handoff. - Do not create a Git commit or push changes. @@ -172,5 +122,3 @@ Stop. - Do not execute a follow-up `/next-task`, `/change-to-plan`, or `/validate` yourself. - Do not infer success when an embedded phase returns a non-success status. -- Preserve validation evidence already written to the plan when context - synchronization fails. diff --git a/.opencode/skills/sce-validate/references/context-sync.md b/.opencode/skills/sce-validate/references/context-sync.md deleted file mode 100644 index bdd0c223..00000000 --- a/.opencode/skills/sce-validate/references/context-sync.md +++ /dev/null @@ -1,350 +0,0 @@ -# SCE Plan Context Sync - -## Purpose - -Reconcile one fully validated plan with the repository's durable context and -return a Markdown report. - -This phase owns: - -- Validating the validation handoff. -- Confirming the context root exists. -- Discovering the context required by the finished plan. -- Deciding whether durable context changed. -- Editing and verifying the affected context files. -- Returning one Markdown synchronization report. - -Use the report format in: - -`references/sync-report.md` - -Task-level context sync may already have run after individual tasks. This phase -is the plan-level final pass: it starts from the plan's `Context sync` -requirements and the validated implementation, and closes gaps that remain. - - - -## Input - -The complete Markdown result returned by the validation phase. - -The validation result must report: - -```markdown -**Status:** validated -**Plan:** {plan path} -``` - -Treat that Markdown as the authoritative handoff for: - -- The resolved plan path. -- Validation commands and outcomes. -- Acceptance-criteria evidence. -- Scaffolding removals. -- Reported context impact, required context paths, and affected areas. - -This phase must not be invoked for `failed` or `blocked` validation results. -Those are not success states. Same rule as `sce-task-context-sync`: context sync -runs only after a successful prior phase. - -Do not reconstruct a missing validation result from conversation history. - -## Workflow - -### 1. Validate the validation handoff - -Confirm that: - -- `Status:` is exactly `validated`. -- `Plan:` names an existing plan path. -- Acceptance-criteria evidence is present and every criterion is met. -- Commands run are present. -- A context-impact classification is present. - -If the handoff is missing required information or is internally contradictory, -do not modify context. Return a `blocked` Markdown report. - -### 2. Confirm the context root - -When `context/` does not exist, there is no durable memory to synchronize. -Do not create it, and do not write context files outside it. - -Return a `blocked` report whose required action is: - -`sce setup --bootstrap-context` - -State that validation itself succeeded and is recorded in the plan, and that -plan context synchronization should run again once the context root exists. - -Bootstrapping is the user's action, not this phase's. - -### 3. Discover applicable context - -Start with the validated Markdown result: - -- **Context impact** classification, required context, and affected areas. -- Acceptance-criteria evidence. -- Commands run. - -Then read the plan's `Context sync` section and inspect existing repository -context in this order when present: - -1. Paths named by the plan's `Context sync` section -2. `context/context-map.md` -3. Context files for the affected domain or subsystem -4. `context/overview.md` -5. `context/architecture.md` -6. `context/glossary.md` -7. `context/patterns.md` -8. Operational, product, or decision records directly related to the finished - change - -Use the context map and existing links to locate authoritative files. - -Do not scan or rewrite the entire `context/` tree by default. - -Do not create a new context file when an existing authoritative file can be -updated coherently. - -#### The mandatory root pass - -Every invocation verifies these five files against code truth, whatever the -reported classification is: - -- `context/overview.md` -- `context/architecture.md` -- `context/glossary.md` -- `context/patterns.md` -- `context/context-map.md` - -Verifying is not editing. A classification that warrants no root edit still -requires reading each of these and confirming it is not contradicted by the -finished implementation. A file that is absent is a gap; record it in the -report rather than creating it to satisfy the pass. - -Report each of the five as verified or edited. Never declare synchronization -done while one of them is unchecked. - -#### Plan context requirements - -Every path or statement listed under the plan's `Context sync` section must be -accounted for in the report as already accurate or updated. A requirement the -finished code still does not satisfy is a blocker, not a note. - -### 4. Determine whether durable context changed - -Use the reported context impact as a strong hint, then verify it against the -finished implementation and existing context. - -Durable context includes non-obvious repository knowledge such as: - -- User-visible or externally observable behavior. -- Architecture, boundaries, ownership, and dependency direction. -- Public interfaces, data contracts, and persistence behavior. -- Operational procedures and important failure modes. -- Security or privacy behavior. -- Shared terminology. -- Intentional limitations and meaningful design decisions. - -Do not document: - -- Details already obvious from the implementation. -- Temporary debugging information. -- A file-by-file narration of the change. -- Test output that belongs only in validation evidence. -- Speculation or future work not established by the finished plan. -- Generic engineering practices. - -Interpret impact classifications as follows. Each governs which files are -*edited*; none of them waives the mandatory root pass or the plan's Context -sync requirements. - -- `none`: Make no edits beyond any correction the root pass or unmet plan - context requirement turns up. -- `local`: Update the nearest existing authoritative context only when the new - behavior is not reliably discoverable from code. -- `domain`: Update affected domain context and the context map when its links or - summaries changed. -- `root`: Update the relevant root context and any affected domain context. - -If the reported classification is inconsistent with the actual change, use the -verified classification and explain the difference in the report. - -### 5. Record qualifying architecture decisions - -During this successful synchronization, determine whether the completed change -establishes or changes a system-wide important constraint involving one or more -of: - -- System boundaries or ownership. -- Public or cross-domain interfaces. -- Data models or persistence. -- Compatibility contracts. -- Security posture. -- Deployment or distribution strategy. -- A major dependency. -- A similarly durable constraint that is costly or risky to reverse. - -Routine implementation details, local refactors, naming and formatting choices, -temporary experiments, and easily reversible choices do not qualify. Do not -invoke a decision skill for them. - -Use the discovered context, existing decision records, and this evidence: - -- acceptance-criteria and validation evidence. - -Identify each qualifying decision, then handle qualifying decisions in -deterministic order: - -1. Reuse a written ADR path already returned during this plan when it records the - same decision. -2. Otherwise invoke `sce-decision` once with exactly one structured decision - request containing the decision, qualifying evidence, plan and task references, - related context and ADR paths, and any user-requested status. -3. On `written`, retain the returned `adr_path` as synchronization evidence and - make it available for current-state context links before synchronization - completes. Reuse is valid evidence; do not create a duplicate ADR. -4. On `blocked`, stop before current-state context edits and return a `blocked` - synchronization report carrying the decision-writing problem, impact, required - action, and retry condition. - -Invoke `sce-decision` only here, after a successful execution or validation -handoff and during context synchronization. Do not invoke it from a non-success -branch or for any non-decision purpose. When no decision qualifies, continue -without invoking it and record that outcome in synchronization evidence. - -### 6. Synchronize context - -Make the smallest coherent documentation change that preserves repository truth. - -When editing context: - -- Describe the resulting behavior, not the validation session. -- Preserve repository terminology and document structure. -- Remove or correct statements contradicted by the finished implementation. -- Update cross-references when files are added, moved, renamed, or superseded. -- Keep one authoritative statement for each durable fact. -- Avoid copying the validation result verbatim into context files. -- Do not change application code, tests, or plan validation evidence. - -Create a new context file only when: - -- The knowledge is durable and non-obvious. -- No existing file owns it coherently. -- The new file has a clear place in the context map. - -#### Feature existence - -Every feature the finished plan implemented must have at least one durable -canonical description discoverable from `context/`, in a domain file under -`context/{domain}/` or in `context/overview.md` for a cross-cutting feature. - -When the plan delivered a feature no context file describes, add that -description. Prefer a small, precise domain file over overloading -`overview.md` with detail. - -This is not license to narrate the diff: describe what the feature is and how -it behaves, not what was edited during the plan. - -#### Glossary - -Add a `context/glossary.md` entry for any domain language the plan introduced. -New terminology is durable knowledge whatever the classification is. - -#### File hygiene - -Every context file this phase writes must satisfy: - -- One topic per file. -- At most 250 lines. When an edit would push a file past 250 lines, split it - into focused files and link them rather than letting it grow. -- Relative paths in every link to another context file. -- A Mermaid diagram where structure, boundaries, or flows are complex enough - that prose alone would not carry them. -- Concrete code examples only where they clarify non-trivial behavior. - -When detail outgrows a shared file, migrate it into `context/{domain}/`, leave a -concise pointer behind, and link the new file from `context/context-map.md`. - -### 7. Verify synchronization - -After edits, verify: - -- Every changed context file accurately reflects the finished implementation. -- No edited statement contradicts the code, plan, or validation evidence. -- Every qualifying decision has one written or reused ADR path in the report, - and the report states when no decision qualified. -- Every file in the mandatory root pass was read and confirmed against code - truth, whether or not it was edited. -- Every plan `Context sync` requirement is met. -- Each feature implemented by the plan has a durable canonical description - reachable from `context/`. -- Every changed file is at or below 250 lines, covers one topic, and links other - context files by relative path. -- Diagrams are present where structure, boundaries, or flows are complex. -- Links and referenced paths resolve when practical to check. -- New context files are reachable from the context map or another authoritative - index. -- Root context remains concise and delegates details to domain files. -- Unrelated context was not changed. - -Use focused documentation, link, or formatting checks when available. - -Do not rerun full-plan validation. - -If synchronization cannot be completed without inventing facts or resolving a -material contradiction, preserve safe edits when appropriate and return a -`blocked` report. - -### 8. Return the Markdown report - -Return exactly one report status: - -- `synced` -- `no_context_change` -- `blocked` - -`synced` means context files were updated and verified. `no_context_change` -means existing context was checked and no edit was warranted. `blocked` means -context could not be synchronized safely. - -Return only the Markdown report. Do not add explanatory prose before or after -it. - -## Plan context synchronization boundaries - -Do not: - -- Accept a validation result whose status is not `validated`. -- Accept `failed` or `blocked` validation results. -- Implement or modify application code. -- Modify tests. -- Change task completion status, acceptance-criteria marks, or the Validation - Report. -- Rerun full-plan validation. -- Select or execute an implementation task. -- Create a Git commit or push changes. -- Create the context root. `sce setup --bootstrap-context` owns that. -- Narrate changed files as documentation. Feature existence is the only reason - to document a change that introduced no other durable knowledge. -- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command - except `sce-decision`, or invoke `sce-decision` outside the decision gate in - successful context synchronization. -- Delete a context file that has uncommitted changes. -- Return YAML. - -## Completion - -The phase is complete after: - -- The context root was confirmed, or a `blocked` report named - `sce setup --bootstrap-context` as the required action. -- The mandatory root pass was run. -- Plan context requirements were checked. -- The decision gate recorded every qualifying ADR path, found no qualifying - decision, or returned a synchronization blocker. -- Applicable durable context was synchronized and verified, no context change - was warranted, or a synchronization blocker was reported. -- One Markdown report matching `references/sync-report.md` was returned. - - diff --git a/.opencode/skills/sce-validate/references/output.md b/.opencode/skills/sce-validate/references/output.md index 9cc8a19b..8b811d79 100644 --- a/.opencode/skills/sce-validate/references/output.md +++ b/.opencode/skills/sce-validate/references/output.md @@ -1,186 +1,16 @@ -# Validation Result +# Validate output layouts -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. +Use only the applicable layout. Values come from internal workflow state. -The `Status` value must be exactly one of: +## Completion -- `validated` -- `failed` -- `blocked` +```markdown +------------------------------------- -The plan-file `## Validation Report` section is written separately using -`references/validation-report.md`. This file is the skill's return value to the -invoking workflow. +# Plan {plan-name} validated. -## Validated variant +All implementation tasks were already complete. +Final validation passed. -# Validation Report - -**Status:** validated -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` - -## Commands run - -- `{command}` -> {passed} — {concise outcome summary} - -## Acceptance criteria - -- [x] AC1: {criterion statement} — {evidence} -- [x] AC2: {criterion statement} — {evidence} - -## Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - -## Residual risks - -- {risk} -- None identified. - -## Context impact - -**Classification:** `{none | local | domain | root}` -**Affected areas:** `{comma-separated areas}` -**Required context:** - -- `{path or statement from the plan Context sync section}` - -{One or two sentences on why this classification fits the finished plan.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## Failed variant - -This variant is a session handoff. Another agent or a later session must be -able to act from it alone. Write it as a prompt the user can paste forward, not -as a summary of the validation run. - -# Validation failed — handoff - -**Status:** failed -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` -**Validation report:** written to `{plan path}` - -## Goal for the next session - -Repair the unfinished validation so every acceptance criterion and full -validation command passes. Do not modify tests or product code inside a -`/validate` run to force green results; fix the implementation (or the plan) in -a normal work session, then rerun validation. - -## What failed - -- `{check or AC id}`: {problem} - - Evidence: {command output, exit summary, or inspection finding} - - Required action: {concrete repair or decision} - -## Acceptance criteria - -- [x] AC1: {criterion} — {evidence} -- [ ] AC2: {criterion} — {why unmet} - -## Commands run - -- `{command}` -> {passed | failed | not_run} — {concise outcome summary} - -## Constraints - -- All implementation tasks were already complete when validation ran. -- Validation did not modify tests, application code, or configuration to clear - failures. -- Durable context was not synchronized; plan context sync runs only after - validation succeeds. -- Prefer the plan at `{plan path}` and its Validation Report as the source of - recorded evidence. - -## Residual risks - -- {risk} -- None identified. - -## Recommended work - -1. {First concrete fix, with files or areas when known} -2. {Second concrete fix, or decision the user must make} -3. Rerun final validation after the fixes land: - -`/validate {plan path}` - -Do not stop after the repair. The plan is not finished until `/validate` -returns `validated` and plan context sync completes. - ---- - -## Blocked variant - -# Validation blocked - -**Status:** blocked -**Plan:** `{plan path when resolved}` -**Name:** `{plan name when resolved}` - -## Issues - -- **{issue id}** ({category}): {problem} - - Impact: {impact} - - Required: {decision or action} - -## Incomplete tasks - -- `{task id}` — {title} -- Omit this section when no incomplete tasks apply. - -## Candidates - -- `{candidate plan path}` -- Omit this section when plan resolution was not ambiguous. - -## Next step - -{Exactly one continuation, matching the blocker:} - -- Incomplete tasks: - -`/next-task {plan path}` - -- Ambiguous plan: - -`/validate {candidate path}` - -- Missing plan content or other blocker: state the decision required. Do not - invent a command. - ---- - -## Report rules - -- Name the exact `Plan:` path so every emitted command is runnable. -- Use **Status:** exactly `validated`, `failed`, or `blocked`. -- Never claim a check passed unless it ran successfully or the authorized - inspection confirmed it. -- Do not modify tests or product code to clear a failure; record it under - **What failed**. -- The failed variant must always end its **Recommended work** with - `/validate {plan path}` as the final step after repairs. -- The failed variant must be self-contained enough to hand to another session - without the original chat. -- Include **Context impact** only on `validated`. Omit it on `failed` and - `blocked`; plan context sync is not invoked for non-success states. -- Do not include context synchronization results in this report. The invoking - workflow runs `sce-plan-context-sync` only after `validated`. -- Do not select or describe an unrelated next implementation task when status is - `validated`. -- Omit empty optional sections rather than writing placeholders. +Validation report: {plan-path} +``` diff --git a/.opencode/skills/sce-validate/references/validation-report.md b/.opencode/skills/sce-validate/references/validation-report.md index c99ad85e..3121e1a4 100644 --- a/.opencode/skills/sce-validate/references/validation-report.md +++ b/.opencode/skills/sce-validate/references/validation-report.md @@ -5,7 +5,7 @@ The Markdown section `sce-validation` appends to the plan file when returning under exactly one `## Validation Report` heading. This is plan-file content. The result returned to the workflow is defined -separately in `references/validation-result.md`. +separately in `references/validation.md`. Do not author this section while planning. Only `/validate` through `sce-validation` writes it. @@ -23,11 +23,6 @@ writes it. - `{command}` -> exit {code} ({concise outcome summary}) - `{command}` -> exit {code} ({concise outcome summary}) -### Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - ### Success-criteria verification - [x] AC1: {criterion statement} -> {evidence} @@ -66,19 +61,16 @@ After repairs, rerun: - Mark each acceptance criterion checkbox in the plan's `## Acceptance criteria` section to match the evidence. Do not mark a criterion met unless the check ran successfully or the inspection named by `Validate:` confirms it. -- Under **Scaffolding removed**, list only temporary debug code, intermediate - artifacts, or throwaway files introduced during the change. Write `None.` when - nothing temporary remained. -- Under **Failed checks and follow-ups**, record the failing check and its - evidence only. Do not describe code or test edits made during validation; +- Under **Failed checks and follow-ups**, record every failing check and its + evidence, including leftover debug-only flags, temporary artifacts, or local + scaffolding. Do not describe code or test edits made during validation; validation does not modify tests or product code to clear failures. Write `None.` when status is `validated`. - When status is `failed`, always include **Retry** with the exact `/validate {plan path}` command. Omit **Retry** when status is `validated`. - Keep evidence concise and factual. Do not narrate the whole implementation history. -- Do not claim context synchronization completed. Plan context sync is a later - workflow step and runs only after `validated`. +- Do not claim durable context synchronization as part of validation. - Do not rewrite task evidence or reopen completed tasks. - When a previous `## Validation Report` already exists, replace it with the new one rather than stacking duplicates. diff --git a/.opencode/skills/sce-validate/references/validation.md b/.opencode/skills/sce-validate/references/validation.md index 08d7f5a8..1816eaca 100644 --- a/.opencode/skills/sce-validate/references/validation.md +++ b/.opencode/skills/sce-validate/references/validation.md @@ -12,22 +12,18 @@ This phase owns: - Confirming every implementation task is complete. - Running the plan's full validation commands and each acceptance criterion check. -- Removing temporary scaffolding introduced by the change. - Writing the Validation Report into the plan. - Marking acceptance criteria against the evidence. - Returning one Markdown validation result. Return a result matching: -`references/validation-result.md` +the **Validation Result** section below in this file Write plan-file evidence matching: `references/validation-report.md` -Context synchronization is not this phase's job. The invoking `/validate` -workflow runs `sce-plan-context-sync` only after a `validated` result. - ## Input A plan name or path. @@ -60,7 +56,6 @@ From the plan, collect: - Every acceptance criterion and its `Validate:` check. - The `Full validation` command list. -- The `Context sync` requirements, for the context-impact handoff only. Return `blocked` when the plan has no usable acceptance criteria, or when no validation commands can be determined from the plan or repository conventions. @@ -69,20 +64,7 @@ Prefer the plan's authored checks. Fall back to repository-primary test, lint, and format commands only when `Full validation` is absent, and record that fallback under notes on a `validated` or `failed` result. -### 4. Remove temporary scaffolding - -Before or while running checks, remove temporary scaffolding introduced during -the change when it is clearly throwaway: - -- Debug-only patches or flags left enabled. -- Temporary files or intermediate artifacts not part of the delivered design. -- Local scaffolding the plan or task notes mark as temporary. - -Do not delete durable product code, tests, configuration, or context files. - -Record every removed path. When nothing temporary remains, report `None.` - -### 5. Run full validation and acceptance checks +### 4. Run full validation and acceptance checks Run the plan's `Full validation` commands. @@ -90,6 +72,11 @@ Then verify each acceptance criterion using its `Validate:` line. Prefer a runnable command. Use a named inspection only when the criterion authorizes it, and say exactly what was inspected. +Treat leftover debug-only flags, temporary files, intermediate artifacts, or +local scaffolding as a failed validation check. Record the path and evidence +under **Failed checks and follow-ups**; never delete or repair it during +validation. + When a check fails, record the failure and continue gathering evidence. Do not modify tests, application code, or configuration to make a check pass. Final validation measures the finished work; repair belongs to a later work session, @@ -101,7 +88,7 @@ inspection confirmed the criterion. Do not run task-by-task implementation work for incomplete tasks. That belongs to `/next-task`. -### 6. Update the plan +### 5. Update the plan For `validated` and `failed` outcomes: @@ -115,22 +102,7 @@ Do not reopen completed tasks, rewrite task evidence, or change the task stack. For `blocked`, leave the plan file unchanged. -### 7. Determine context impact for the handoff - -On `validated` only, classify the durable context impact of the finished plan -so `sce-plan-context-sync` can start from the plan's own requirements: - -- Start from the plan's `Context sync` section. -- Inspect what the completed implementation actually changed when needed. -- Report required context paths and affected areas. -- Use `none`, `local`, `domain`, or `root` with the same meanings as task-level - context sync. - -Do not edit context files here. - -On `failed` or `blocked`, omit context impact; context sync will not run. - -### 8. Return the Markdown result +### 6. Return the Markdown result Return exactly one Markdown result: @@ -138,7 +110,7 @@ Return exactly one Markdown result: passed, and the Validation Report was written. - `failed` when evidence was captured but required checks or criteria remain unsatisfied. Shape it as a session handoff per - `references/validation-result.md`, ending recommended work with + the **Validation Result** section below in this file, ending recommended work with `/validate {plan path}`. - `blocked` when validation cannot proceed safely. @@ -154,14 +126,14 @@ Do not: - Modify tests, application code, or configuration to make a failing check pass. - Apply lint or format auto-fixes that change product or test files as part of making validation green. -- Synchronize durable context under `context/` outside the plan file. +- Synchronize durable context under `context/`. - Create the context root. - Mark the plan archived or delete the plan. - Create a Git commit or push changes. - Invent acceptance criteria the plan does not state. - Claim verification that was not performed. - Return a YAML result. -- Invoke plan context sync. The workflow owns that step. + ## Completion The phase is complete after: @@ -169,7 +141,175 @@ The phase is complete after: - One plan was resolved, or resolution failed and was reported. - Implementation completeness was checked. - Validation ran to a terminal state, or a blocker prevented it. -- One valid Markdown result matching `references/validation-result.md` was +- One valid Markdown result matching the **Validation Result** section below in this file was returned. + +# Validation Result + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `validated` +- `failed` +- `blocked` + +The plan-file `## Validation Report` section is written separately using +`references/validation-report.md`. This file is the skill's return value to the +invoking workflow. + +## Validated variant + +# Validation Report + +**Status:** validated +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` + +## Commands run + +- `{command}` -> {passed} — {concise outcome summary} + +## Acceptance criteria + +- [x] AC1: {criterion statement} — {evidence} +- [x] AC2: {criterion statement} — {evidence} + +## Residual risks + +- {risk} +- None identified. + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## Failed variant + +This variant is a session handoff. Another agent or a later session must be +able to act from it alone. Write it as a prompt the user can paste forward, not +as a summary of the validation run. + +# Validation failed — handoff + +**Status:** failed +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` +**Validation report:** written to `{plan path}` + +## Goal for the next session + +Repair the unfinished validation so every acceptance criterion and full +validation command passes. Do not modify tests or product code inside a +`/validate` run to force green results; fix the implementation (or the plan) in +a normal work session, then rerun validation. + +## What failed + +- `{check or AC id}`: {problem} + - Evidence: {command output, exit summary, or inspection finding} + - Required action: {concrete repair or decision} + +## Acceptance criteria + +- [x] AC1: {criterion} — {evidence} +- [ ] AC2: {criterion} — {why unmet} + +## Commands run + +- `{command}` -> {passed | failed | not_run} — {concise outcome summary} + +## Constraints + +- All implementation tasks were already complete when validation ran. +- Validation did not modify tests, application code, or configuration to clear + failures. +- Validation does not synchronize durable context. +- Prefer the plan at `{plan path}` and its Validation Report as the source of + recorded evidence. + +## Residual risks + +- {risk} +- None identified. + +## Recommended work + +1. {First concrete fix, with files or areas when known} +2. {Second concrete fix, or decision the user must make} +3. Rerun final validation after the fixes land: + +`/validate {plan path}` + +Do not stop after the repair. The plan is not finished until `/validate` +returns `validated`. + +--- + +## Blocked variant + +# Validation blocked + +**Status:** blocked +**Plan:** `{plan path when resolved}` +**Name:** `{plan name when resolved}` + +## Issues + +- **{issue id}** ({category}): {problem} + - Impact: {impact} + - Required: {decision or action} + +## Incomplete tasks + +- `{task id}` — {title} +- Omit this section when no incomplete tasks apply. + +## Candidates + +- `{candidate plan path}` +- Omit this section when plan resolution was not ambiguous. + +## Next step + +{Exactly one continuation, matching the blocker:} + +- Incomplete tasks: + +`/next-task {plan path}` + +- Ambiguous plan: + +`/validate {candidate path}` + +- Missing plan content or other blocker: state the decision required. Do not + invent a command. + +--- + +## Report rules + +- Name the exact `Plan:` path so every emitted command is runnable. +- Use **Status:** exactly `validated`, `failed`, or `blocked`. +- Never claim a check passed unless it ran successfully or the authorized + inspection confirmed it. +- Do not modify tests or product code to clear a failure; record it under + **What failed**. +- The failed variant must always end its **Recommended work** with + `/validate {plan path}` as the final step after repairs. +- The failed variant must be self-contained enough to hand to another session + without the original chat. +- Do not include durable context synchronization results in this report. +- Do not select or describe an unrelated next implementation task when status is + `validated`. +- Omit empty optional sections rather than writing placeholders. diff --git a/.pi/prompts/validate.md b/.pi/prompts/validate.md index 543484be..690a65d4 100644 --- a/.pi/prompts/validate.md +++ b/.pi/prompts/validate.md @@ -1,5 +1,5 @@ --- -description: "Validate one completed SCE plan and synchronize its durable context" +description: "Validate one completed SCE plan and record final validation evidence" argument-hint: "" --- diff --git a/.pi/skills/sce-change-to-plan/SKILL.md b/.pi/skills/sce-change-to-plan/SKILL.md index a96a1b71..5a97185f 100644 --- a/.pi/skills/sce-change-to-plan/SKILL.md +++ b/.pi/skills/sce-change-to-plan/SKILL.md @@ -44,8 +44,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -118,7 +118,12 @@ Render the `plan_ready` result as the summary defined by the **Plan authoring ph Take the next task from `next_task`. A `plan_ready` result always names one. Do not evaluate its dependencies; the **Plan review phase** checks them when the emitted command runs and returns `blocked` if they are unmet. -The continuation invites revision. The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. +The workflow carries one of two explicit continuation shapes across a same-session wait: + +- **Initial-clarification continuation:** `original_request`, `clarification_answers`, and `loaded_context_brief`. `original_request` is the unchanged request from step 1; preserve it with the answers and never ask the user to provide it again. +- **Existing-plan revision continuation:** `plan_path`, `correction`, and `loaded_context_brief`. `plan_path` identifies the plan already written, and `correction` contains the user's requested revision. + +The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. Write `task` rather than `tasks` when `total_tasks` is 1. @@ -130,13 +135,13 @@ Then stop and wait. Do not implement, and do not run the handoff yourself. ### 4. Revise the plan on request -When the user answers clarification questions from step 2, answers open questions listed in the summary, or answers with changes to the plan, revise it in this session. Do not ask them to rerun `/change-to-plan`, and do not ask for the original change request again. +When the user answers clarification questions from step 2, resume the **Initial-clarification continuation** with `original_request`, `clarification_answers`, and the same `loaded_context_brief` from step 1. Preserve `original_request` unchanged and never ask the user for the original change request again. When the user answers open questions listed in the summary or requests changes to an already-written plan, resume the **Existing-plan revision continuation** with `plan_path`, `correction`, and the same `loaded_context_brief`. Do not ask them to rerun `/change-to-plan`. -Run the **Plan authoring phase** with their answer or correction and the same `loaded` brief from step 1. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. +Run the **Plan authoring phase** with the applicable continuation fields. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. An answer that resolves a doubt removes that open question. An answer that does not resolve it leaves the question standing; do not drop it because the user replied to it. If the reply raises a new doubt, the revised plan carries a new open question. -Pass the correction as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. +Pass `clarification_answers` or `correction` as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. Branch on `status` exactly as in step 2. A revision may legitimately return `needs_clarification` or `blocked`. diff --git a/.pi/skills/sce-change-to-plan/references/plan-template.md b/.pi/skills/sce-change-to-plan/references/plan-template.md index ac36a72c..19e8a12a 100644 --- a/.pi/skills/sce-change-to-plan/references/plan-template.md +++ b/.pi/skills/sce-change-to-plan/references/plan-template.md @@ -41,6 +41,17 @@ which criterion they map to. - {Durable context files that must describe the change once implemented.} +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + ## Constraints and non-goals - **In scope:** {files, modules, and surfaces this plan may touch} @@ -59,19 +70,19 @@ recorded. Remove the section otherwise.} - [ ] T01: `{single intent title}` (status:todo) - Task ID: T01 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: {task IDs, or none} - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending - [ ] T02: `{single intent title}` (status:todo) - Task ID: T02 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: T01 - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending ## Open questions @@ -92,11 +103,11 @@ invent one: `None.` is the expected answer for a well-specified change.} ```markdown - [ ] T02: `Add /auth/refresh endpoint` (status:todo) - Task ID: T02 - - Goal: Implement a POST `/auth/refresh` endpoint that exchanges a valid refresh token for a new access token. - - Boundaries (in/out of scope): In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. + - Scope: In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. - Dependencies: T01 - Done when: `POST /auth/refresh` returns a signed JWT on valid input and 401 on expired or invalid token; targeted tests pass; OpenAPI spec updated. - - Verification notes (commands or checks): `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Verify: `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Context synchronization: pending ``` ## Acceptance criteria rules @@ -109,7 +120,7 @@ invent one: `None.` is the expected answer for a well-specified change.} automated check exists, and say exactly what to look at. - List repository-wide checks once under `Full validation` instead of repeating them per criterion. -- Task-level `Verification notes` prove one task. Acceptance criteria prove the +- Task-level `Verify` proves one task. Acceptance criteria prove the plan. Keep them distinct: a task's checks are narrow and local, a criterion's check is end-to-end. - The union of the acceptance criteria must cover every success signal in the @@ -149,11 +160,14 @@ checkbox and status: ```markdown - [x] T01: `{title}` (status:done) - - {authored fields, unchanged} + - {authored fields, unchanged: Task ID, Scope, Dependencies, Done when} + - Verify: {each planned check, updated with its actual outcome} - Completed: {YYYY-MM-DD} - Files changed: {paths} - - Evidence: {commands run and their outcomes} - - Notes: {material deviations or approved assumptions} + - Result: {concise factual outcome, not a prose diff} + - Context impact: {durable context this change affects, or none} + - Context synchronization: pending | synced | blocked + - Context synchronization blocker: {present only when status is blocked} Blocker: {problem}; Required action: {action}; Retry condition: {condition} ``` `/validate` appends a `## Validation Report` section at the end of the plan. diff --git a/.pi/skills/sce-commit/SKILL.md b/.pi/skills/sce-commit/SKILL.md index 07059111..262b2a35 100644 --- a/.pi/skills/sce-commit/SKILL.md +++ b/.pi/skills/sce-commit/SKILL.md @@ -36,8 +36,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -134,15 +134,25 @@ a contract violation: report it and stop without committing. #### 3. Execute exactly one commit -Run `git commit` once with the returned message. +Follow the **Bypass execution handoff** in `references/atomic-commit.md`: + +1. Create the commit-message temp file outside the repository working tree, and + write the returned `message` verbatim to it using a file-writing operation. Do + not interpolate the multiline message into shell source or a shell command. +2. Run `git commit -F ` exactly once. +3. Only after that command succeeds, retrieve the commit hash explicitly with + `git rev-parse --verify HEAD^{commit}`. Do not parse Git's human-readable + output. +4. Delete the temp file after the commit attempt, including on failure, where + practical. On success, render the **Bypass success** layout from `references/output.md` and stop. On failure, render the **Bypass Git failure** layout from the same file and stop. -Do not retry, do not amend, do not stage additional files, and do not invent a -fallback commit. +Do not retry, do not amend, do not stage additional files, and do not fabricate a +commit hash. ## Rules @@ -152,7 +162,9 @@ fallback commit. They are behaviorally identical. - Read `references/atomic-commit.md` before running the phase. - Do not duplicate the internal instructions of the **Atomic commit phase**. -- Do not stage, unstage, restore, or otherwise modify files. +- Do not stage, unstage, restore, or otherwise modify repository or worktree + files. The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Do not amend, reset, revert, rebase, or push. - Do not read unstaged or untracked changes as commit input. - Do not infer success when the **Atomic commit phase** returns a non-success status. diff --git a/.pi/skills/sce-commit/references/atomic-commit.md b/.pi/skills/sce-commit/references/atomic-commit.md index a2404c4c..b41d362a 100644 --- a/.pi/skills/sce-commit/references/atomic-commit.md +++ b/.pi/skills/sce-commit/references/atomic-commit.md @@ -8,10 +8,6 @@ Write messages matching: `references/commit-message-style.md` -Return a result matching: - -`references/commit-contract.yaml` - Committing is not this skill's job. The invoking `/commit` workflow decides whether a returned message is committed, and it is the only thing that runs `git commit`. @@ -108,22 +104,35 @@ Confirm before returning that: - No plan slug or task ID appears that the staged diff does not support. - The mode's own constraints hold. -### 9. Return YAML +## Bypass execution handoff -Return exactly one YAML document matching `references/commit-contract.yaml`: +This phase returns the message; the invoking `/commit` workflow performs the +bypass commit. When the mode is `bypass`, the invoking workflow must: -- `proposal` in `regular` mode, with one or more messages. -- `bypass_message` in `bypass` mode, with exactly one message. -- `blocked` when messages cannot be written faithfully. +1. Create the commit-message temp file outside the repository working tree, + and write the returned `message` verbatim to it using a file-writing + operation. Never interpolate a multiline message into shell source or a + shell command. +2. Run `git commit -F ` exactly once. +3. After and only after a successful commit, run + `git rev-parse --verify HEAD^{commit}` and use that explicit `HEAD` value as + the reported hash. Never parse Git's human-readable commit output. +4. On any commit failure, report Git's failure and stop. Never retry, amend, + stage more files, or fabricate a hash. +5. Delete the temp file after the commit attempt, including on failure, where + practical. -Return only the YAML document. Do not add explanatory prose before or after it. +`oneshot` and `skip` select this same bypass behavior; they differ only in the +trigger token. ## Atomic commit boundaries Do not: - Run `git commit`, or any command that writes to the repository or its index. -- Stage, unstage, or modify files. +- Stage, unstage, restore, or otherwise modify repository or worktree files. + The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Ask the user to stage or confirm staging. - Analyze unstaged or untracked changes. - Return more than one message in `bypass` mode. @@ -133,13 +142,13 @@ Do not: - Mention `context/` synchronization activity in a commit message. - Claim a message was committed. - Run plan, task, or validation workflows. + + ## Completion The skill is complete after: - The staged diff was read, or reading it failed and was reported. - Messages were written for every staged file, or a blocker prevented it. -- One valid terminal YAML result matching `references/commit-contract.yaml` was - returned. diff --git a/.pi/skills/sce-commit/references/commit-message-style.md b/.pi/skills/sce-commit/references/commit-message-style.md new file mode 100644 index 00000000..056382ff --- /dev/null +++ b/.pi/skills/sce-commit/references/commit-message-style.md @@ -0,0 +1,44 @@ +# Commit Message Guide + +Use this repository style when writing new commits. + +## Core rules + +- Start with `scope: Subject` for most code changes. + - Common scopes: `runtime`, `language`, `objects`, `tests`, `CI`, `README`. + - Combined scopes are fine when needed (for example `language+runtime`). +- Use an imperative verb in the subject: `Fix`, `Add`, `Refactor`, `Remove`, `Implement`, `Update`, `Rewrite`, `Use`, `Allow`. +- Keep the subject specific and technical (name the subsystem and actual change). +- Keep the subject to one line and do not end it with a period. +- Add a body when the change is non-trivial. + - Explain why the change is needed. + - Explain how it works at a high level. + - Include impact/tradeoffs/follow-ups when relevant. +- For performance-related commits, include concrete measurements and benchmark context. + - Include regressions as well as improvements. +- Add issue references when relevant on their own lines. + - Example: `Fixes #123` + - Example: `Ref: https://...` + +## Practical template + +```text +: + + + + +Fixes # (optional) +``` + +## Size-based defaults + +1. Small fix: subject + 1 short reason line. +2. Medium refactor: subject + short why + short what changed. +3. Large architectural change: subject + context + bullets for major changes + impact/tradeoffs. + +## Anti-patterns to avoid + +- Vague subjects like "misc updates" or "cleanup". +- Bodies that only repeat the subject without explaining why or impact. +- Overly playful tone in serious bug-fix or architectural change. diff --git a/.pi/skills/sce-decision/SKILL.md b/.pi/skills/sce-decision/SKILL.md index d28f32a9..543044c4 100644 --- a/.pi/skills/sce-decision/SKILL.md +++ b/.pi/skills/sce-decision/SKILL.md @@ -9,18 +9,18 @@ description: > ## Purpose Write exactly one architecture decision record for one qualifying system-wide -important decision during successful task or plan context synchronization. Return +important decision during successful task context synchronization. Return a deterministic internal handoff to the invoking synchronization phase. Do not render an independent user-visible response. ## Input -Accept one structured decision request from `sce-next-task` or `sce-validate` -context synchronization. It must identify: +Accept one structured decision request from `sce-next-task` task context +synchronization. It must identify: - One decision stated as a single durable choice. - Why it qualifies under the decision gate. -- The implementation or validation evidence establishing the decision. +- The implementation / task-verification evidence establishing the decision. - The resolved plan path and relevant task IDs, when applicable. - Related current-state context and existing ADR paths. - An optional requested status. @@ -44,8 +44,12 @@ constraint involving at least one of: Routine implementation details, local refactors, naming and formatting choices, temporary experiments, and easily reversible choices do not qualify. When the -request does not demonstrate the threshold, return `blocked`; do not create an -ADR merely because context synchronization occurred. +request does not demonstrate the threshold, return `not_qualified` (or +`skipped` when the caller deliberately skips the gate); do not create an ADR +merely because context synchronization occurred. A nonqualifying or skipped +result is non-blocking, so the invoking synchronization phase continues +normally. Reserve `blocked` for missing, contradictory, or otherwise unsafe +decision input or history. ## Workflow @@ -56,20 +60,24 @@ path, references sufficient to make the record traceable, and no unresolved material contradiction. If it contains several decisions, require the caller to submit one request per decision. -Allowed statuses are exactly `Proposed`, `Accepted`, `Rejected`, `Deprecated`, -and `Superseded`. Use the explicitly requested allowed status; otherwise default -to `Accepted`. Reject any other status rather than guessing. +Allowed statuses for a newly written ADR are exactly `Proposed`, `Accepted`, +`Rejected`, `Deprecated`, and `Superseded`. Use the explicitly requested allowed +status; otherwise default to `Accepted`. `Deprecated` and `Superseded` remain +distinct creation-time-only statuses: use them to describe the record when it is +created, but never mutate an existing ADR into or out of either status. Reject any +other status rather than guessing. ### 2. Inspect existing decision history Read `context/decisions/` and the supplied related ADR paths before writing. -- Reuse an existing ADR when it already records the same decision; return its - path without creating a duplicate. -- Never edit an ADR whose status is `Accepted`. -- A correction, reversal, or changed decision creates a new dated ADR that - references and supersedes the accepted record. -- Do not overwrite any existing ADR or silently change its status. +- Reuse an existing ADR only when it records an equivalent decision and has an + active status: `Proposed` or `Accepted`. Return its path without creating a + duplicate. Never reuse a `Rejected`, `Deprecated`, or `Superseded` ADR. +- Existing ADRs are immutable regardless of status. Never edit an ADR whose status is `Accepted`; do not edit, overwrite, or silently change the status of any existing record. +- A correction, reversal, or any changed decision always creates a new dated ADR; + it references and supersedes the prior record when applicable, rather than + modifying that record. If `context/` or `context/decisions/` is absent, or history cannot be interpreted without inventing facts, return `blocked` without creating directories. @@ -117,8 +125,11 @@ Return exactly one internal handoff: - `written`: include `status`, `adr_path`, `decision`, `decision_status`, `created` (`true` for a new ADR and `false` for reuse), `supersedes`, and concise verification evidence. +- `not_qualified` or `skipped`: include `status`, the reason the decision gate + did not produce an ADR, and concise supporting evidence. These results are + non-blocking; the invoking synchronization phase continues normally. - `blocked`: include `status`, the specific `problem`, its `impact`, and the - `required_action`. + `required_action`. Use this only when decision writing cannot proceed safely. Use stable field names and repository-relative paths. Return no prose before or after the handoff. The invoking synchronization phase owns all user-visible @@ -129,7 +140,7 @@ reporting. Do not: - Write more than one ADR per request. -- Run outside successful task or plan context synchronization. +- Run outside successful task context synchronization. - Create a command, prompt, context root, or decisions directory. - Modify application code, tests, plans, current-state context, or existing accepted ADRs. diff --git a/.pi/skills/sce-handover/SKILL.md b/.pi/skills/sce-handover/SKILL.md index 80daabab..8e5d325e 100644 --- a/.pi/skills/sce-handover/SKILL.md +++ b/.pi/skills/sce-handover/SKILL.md @@ -25,8 +25,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. @@ -61,7 +61,8 @@ blockers. Ground those facts against repository state: -- `git status` and `git diff` for uncommitted work. +- `git status`, `git diff`, and `git diff --cached` for uncommitted work, + including both unstaged and staged changes. - `context/plans/*.md` for the active plan and task, when one is being worked. - Recent commits, when they clarify what just landed. @@ -84,8 +85,9 @@ timestamp segment, rather than overwriting it. #### 3. Compose the handover document -Use the **Persisted-document format: Handover document** section embedded in -this file. Populate all four required sections: +Read `references/handover-template.md` before composing. It defines the +persisted-document format and is the only template authority. Populate all +four required sections: - `Current Task State` - `Decisions Made` @@ -135,10 +137,15 @@ and stop. Read the file and confirm it contains all four required sections: `Current Task State`, `Decisions Made`, `Open Questions / Blockers`, and -`Next Recommended Step`. +`Next Recommended Step`. For each section, inspect the content up to the +next required heading (or the end of the file): it must contain non-whitespace +content, and it must not consist only of an empty list marker, a template +placeholder such as `{What is being worked on...}`, or other unreplaced +`{...}` scaffolding. Explicit statements such as `None identified.` are real +content and are valid. -When any required section is missing, render the **Loader blocked** layout -(invalid handover) and stop. +When any required section is missing, empty, or placeholder-only, render the +**Loader blocked** layout (invalid handover) and stop. #### 3. Present for continuation @@ -163,53 +170,3 @@ guidance is the entire loader contract. loadable handover. - Never create the `context/` root; `sce setup --bootstrap-context` owns that. - Do not begin, plan, or automate the loaded handover's recommended next step. - -## Internal persisted-document format: Handover document - -The Markdown document writer mode creates under -`context/handovers/{name}.md`. This is the persisted file's content, distinct -from the terminal response defined in `references/output.md`. - -### Layout - -```markdown -# Handover: {plan name or short session topic} - -Date: {YYYY-MM-DD} -Plan: `{context/plans/plan-name.md}` (omit when no plan applies) -Task: `{task-id}` (omit when no single task applies) - -## Current Task State - -{What is being worked on, what is complete, what is in progress. Cite files, -commands, or plan/task references where they ground the statement.} - -## Decisions Made - -- {Decision and its rationale, or `None made this session.`} - -## Open Questions / Blockers - -- {Unresolved question or blocker, or `None identified.`} - -## Next Recommended Step - -{The single most useful next action for the following session, concrete -enough to act on directly.} - -## Assumptions - -- {Any detail above that was inferred rather than directly evidenced, or - `None.`} -``` - -### Rules - -- Include `Plan` and `Task` only when the session was working one identifiable - plan task; omit them rather than guessing. -- Every one of the four required sections must appear, in this order, even - when its content is `None identified.` or an equivalent. -- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere - in the document; do not duplicate confirmed facts here. -- Describe durable state useful to a future session, not a transcript of this - one. diff --git a/.pi/skills/sce-handover/references/handover-template.md b/.pi/skills/sce-handover/references/handover-template.md new file mode 100644 index 00000000..51edfefb --- /dev/null +++ b/.pi/skills/sce-handover/references/handover-template.md @@ -0,0 +1,47 @@ +The Markdown document writer mode creates under +`context/handovers/{name}.md`. This is the persisted file's content, distinct +from the terminal response defined in `references/output.md`. + +### Layout + +```markdown +# Handover: {plan name or short session topic} + +Date: {YYYY-MM-DD} +Plan: `{context/plans/plan-name.md}` (omit when no plan applies) +Task: `{task-id}` (omit when no single task applies) + +## Current Task State + +{What is being worked on, what is complete, what is in progress. Cite files, +commands, or plan/task references where they ground the statement.} + +## Decisions Made + +- {Decision and its rationale, or `None made this session.`} + +## Open Questions / Blockers + +- {Unresolved question or blocker, or `None identified.`} + +## Next Recommended Step + +{The single most useful next action for the following session, concrete +enough to act on directly.} + +## Assumptions + +- {Any detail above that was inferred rather than directly evidenced, or + `None.`} +``` + +### Rules + +- Include `Plan` and `Task` only when the session was working one identifiable + plan task; omit them rather than guessing. +- Every one of the four required sections must appear, in this order, even + when its content is `None identified.` or an equivalent. +- Keep `Assumptions` scoped to details actually labeled as inferred elsewhere + in the document; do not duplicate confirmed facts here. +- Describe durable state useful to a future session, not a transcript of this + one. diff --git a/.pi/skills/sce-handover/references/output.md b/.pi/skills/sce-handover/references/output.md index 82928975..afe30b5f 100644 --- a/.pi/skills/sce-handover/references/output.md +++ b/.pi/skills/sce-handover/references/output.md @@ -32,28 +32,6 @@ No file was written. **Path:** `{written path}` -## Current Task State - -{same content written to the file} - -## Decisions Made - -- {as written} - -## Open Questions / Blockers - -- {as written} - -## Next Recommended Step - -{as written} - -## Assumptions - -- {as written, or `None.`} - ---- - To continue from this handover in another session: `/handover {written path}` diff --git a/.pi/skills/sce-next-task/SKILL.md b/.pi/skills/sce-next-task/SKILL.md index 6bc2e8a4..e4b4a3d2 100644 --- a/.pi/skills/sce-next-task/SKILL.md +++ b/.pi/skills/sce-next-task/SKILL.md @@ -45,9 +45,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -85,12 +84,31 @@ Branch on `status`: `blocked` -> Do not run implementation. Render the **Review blocked** layout from `references/output.md`. When `candidates` is present the plan could not be resolved, and each entry is a candidate path for `/next-task {candidate-path}`. `executable_tasks_remaining` true means another task remains executable and `/next-task {plan-path} {task-id}` selects one; false means no task in the plan can proceed until the plan is updated. Do not print the raw result. Stop. +`sync_debt` -> Read `references/context-sync.md`, then run the **Task context synchronization phase** using the debt task's persisted `Context synchronization handoff` — and, when present, its persisted `Context synchronization blocker` — named by the **Plan review phase**. Do not reconstruct a missing handoff from conversation history. + +Write the debt task's lifecycle to the plan: `synced`, clearing its blocker, required action, and retry condition, for `synced` or `no_context_change`; a refreshed `blocked` state with the report's blocker, required action, and retry condition for `blocked`. If that lifecycle write fails, treat the outcome as `blocked`. + +Branch on the outcome: + +`blocked` -> Render the **Context synchronization blocked** layout from `references/output.md`, distinct from the **Review blocked** layout above. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select or start a new task. Stop. + +`synced` | `no_context_change` -> Re-invoke the **Plan review phase** with the same `plan-name-or-path` and, when present, `task-id` to resume normal task selection. + `plan_complete` -> Render the **Plan already complete** layout from `references/output.md`. Stop. `ready` -> Pass the complete readiness result to the **Task execution phase**. Do not reconstruct, summarize, or reinterpret the reviewed task before passing it. +The review inspects every completed task's `Context synchronization` field in +the plan, in plan order, regardless of its position relative to the task being +selected or resumed, before allowing a new implementation task to start. A +missing field, or any value other than `synced`, is unresolved synchronization +debt. Never infer `synced` from conversation history. When the debt-carrying +task has no durable `Context synchronization handoff` subsection, the **Plan +review phase** returns `blocked` directly with a legacy-migration required +action; otherwise it returns `sync_debt`, resolved by the branch above. + ### 2. Execute the task Read `references/task-execution.md`, then run the **Task execution phase** with @@ -132,9 +150,14 @@ Do not restate, summarize, or reconstruct any part of the execution result. This phase verifies the five root context files on every invocation, whatever the change's reported impact, so it is never correct to skip it as unnecessary. +Before branching on the synchronization result, write the completed task's +lifecycle to the plan file: `synced` for `synced` or `no_context_change`, and +`blocked` with the report's blocker, required action, and retry condition for +`blocked`. If that lifecycle write fails, treat synchronization as `blocked`. + Branch on the synchronization result. -`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. Nothing records the skipped synchronization, so it is lost once this session ends. +`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select another task. Stop. diff --git a/.pi/skills/sce-next-task/references/context-sync.md b/.pi/skills/sce-next-task/references/context-sync.md index 3c5cb8b4..30ec3eae 100644 --- a/.pi/skills/sce-next-task/references/context-sync.md +++ b/.pi/skills/sce-next-task/references/context-sync.md @@ -5,46 +5,70 @@ Run this phase for step 3 of the workflow, and only when task execution returned session inherits what this task established. It never touches code, tests, or plan state. -Input: the complete `complete` result from the task execution phase, passed -verbatim. It is the authoritative handoff, and this phase owns reading the plan, -task, changed files, verification evidence, and reported context impact out of -it. +Input: either the complete `complete` result from the task execution phase +(same-session), passed verbatim, or the plan path and task ID a plan-review +recovery step resolved for a `blocked` task, together with that task's own +completed record — read directly from the plan — and its persisted `Context +synchronization blocker` when present (cross-session retry). Whichever was +supplied is the authoritative source, and this phase owns reading the plan, +task, changed files, verification evidence, and reported context impact out +of it. -Do not restate, summarize, or reconstruct any part of the execution result. Do -not reconstruct a missing execution result from conversation history. +Do not restate, summarize, or reconstruct any part of it. Do not reconstruct a +missing execution result or completed task record from conversation history. -The execution result must have: +A live execution result must have: ```text status: complete ``` -Treat the execution result as the authoritative handoff for: +A cross-session retry has no separate `status` field to check; the completed +task record's presence in the plan, identified by plan path and task ID, is +itself the authoritative signal. + +Use the report format in: + +`references/sync-report.md` + +Treat whichever source was supplied — the live execution result, or the +completed task record read directly from the plan — as the authoritative +source for: - The resolved plan and completed task. +- `changes.files_changed`, or the completed task record's own `Files changed` + field on retry, already attributed relative to the pre-edit Git baseline. - Files changed by implementation. -- Implementation summary. -- Verification evidence. +- The task's `Result` (or implementation summary, for a live result). +- `Verify` outcomes (or verification evidence, for a live result). - Done-check evidence. - Reported context impact. This phase must not be run for `declined`, `blocked`, or `incomplete` execution results. -## 3.1 Validate the execution handoff +## 3.1 Validate the handoff Confirm that: -- `status` is exactly `complete`. -- A `plan` object with a `path` is present. -- Exactly one completed task is identified. -- Changed files and an implementation summary are present. -- Verification evidence is present. +- A live execution result has `status` exactly `complete`; a cross-session + retry has no `status` field to check and is authoritative by the completed + task record's presence in the plan. +- A resolved plan path and task ID are present; a live execution result + carries them in its `plan` and `task` objects, and a cross-session retry + receives them directly from the caller that resolved the debt task. +- Exactly one completed task is identified, and — on retry — its record is + read directly from the plan by that plan path and task ID rather than + reconstructed in-band. +- Changed files and a `Result` (an implementation summary, for a live result) + are present. +- `Verify` outcomes (verification evidence, for a live result) are present. - Done-check evidence is present. - A context-impact classification is present. -If the handoff is missing required information or is internally contradictory, do -not modify context. Return a `blocked` Markdown report. +If the required information is missing, the completed task record cannot be +read from the plan, or either is internally contradictory, do not modify +context. Return a `blocked` Markdown report. ## 3.2 Confirm the context root @@ -289,6 +313,13 @@ Set exactly one report status: existing context was checked and no edit was warranted. `blocked` means context could not be synchronized safely. +A `blocked` report always writes the plan path and task ID/title as identity, +plus a `Context synchronization blocker` section (blocker, required action, +retry condition), using the same field names the plan's completion record +uses, so the plan-review recovery step can persist the blocker verbatim and a +future retry can read the completed task record directly from the plan by +plan path and task ID. + Record only the Markdown report. Do not add explanatory prose before or after it. Do not determine whether the plan is complete. The `/next-task` workflow owns diff --git a/.pi/skills/sce-next-task/references/output.md b/.pi/skills/sce-next-task/references/output.md index d0ade404..b6e772bd 100644 --- a/.pi/skills/sce-next-task/references/output.md +++ b/.pi/skills/sce-next-task/references/output.md @@ -138,145 +138,3 @@ When the `approve` flag is supplied, omit the question and end after wait. - If the handoff is stale or incomplete, show the known task information and identify the problem under **Risks or trade-offs**. - -# Context Sync Report - -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. - -The `Status` value must be exactly one of: - -- `synced` -- `no_context_change` -- `blocked` - -The input execution status is always `complete` and does not need to be repeated -as a separate workflow state. - -## Synced variant - -# Context Sync Report - -**Status:** synced -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Updated context - -- `{context file}` — {concise description of the durable truth updated} - -## Architecture decisions - -- `{written or reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}` - -## Verification - -- {How the edited context was checked against implementation and execution evidence.} -- {File hygiene: line counts, relative links, diagrams where structure is complex.} -- {Documentation, link, or formatting checks that were run, when applicable.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## No-context-change variant - -# Context Sync Report - -**Status:** no_context_change -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Synchronization result - -{Explain why the completed implementation did not introduce durable, -non-obvious repository knowledge requiring an update.} - -## Context reviewed - -- `{context file or area}` — {what was checked and why it remains accurate} - -## Architecture decisions - -- `{reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}`, already present. - -## Verification - -- {How existing context was compared with implementation and execution evidence.} - ---- - -## Blocked variant - -# Context Sync Report - -**Status:** blocked -**Plan:** `{plan path}` -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Blocker - -**Problem:** {specific synchronization blocker} -**Impact:** {why context cannot be made authoritative safely} -**Required action:** {decision or correction required} - -## Context changes - -- {List safe context edits preserved, or state `No context files were changed.`} - -## Architecture decisions - -- `{ADR path written or reused before the blocker}` — {decision and status} -- None written or reused before the blocker. - -## Retry condition - -{State the concrete condition under which context synchronization should run -again.} - -## Report rules - -- Name exact context files when they were changed or reviewed. -- Under **Architecture decisions**, list every ADR path written or reused during - the decision gate. In a successful report, state `None qualified.` when the - gate skipped invocation. In a blocked report, state - `None written or reused before the blocker.` when applicable. -- Under **Updated files**, list every changed file from the execution handoff - except paths under `context/`. -- Report the missing context root as `blocked`, with `sce setup - --bootstrap-context` as the required action and the existence of `context/` as - the retry condition. -- Omit **Feature existence** only when the task implemented no feature. -- Describe durable truth, not implementation-session chronology. -- Keep evidence concise and factual. -- Do not claim final validation passed. -- Do not determine whether the plan is complete. -- Do not recommend a next implementation task. diff --git a/.pi/skills/sce-next-task/references/plan-review.md b/.pi/skills/sce-next-task/references/plan-review.md index d08975e3..89e378f3 100644 --- a/.pi/skills/sce-next-task/references/plan-review.md +++ b/.pi/skills/sce-next-task/references/plan-review.md @@ -1,8 +1,8 @@ # Plan review phase Run this phase for step 1 of the workflow. It resolves one plan, selects one -task, and decides whether that task can be implemented right now. It reads; it -never writes. +task, and decides whether that task can be implemented right now. It reads; +it never writes. Inputs: the parsed `plan-name-or-path`, and `task-id` when present. The `auto-approve` token is not passed here and has no meaning in this phase. @@ -20,7 +20,30 @@ Read the selected plan before exploring the repository. ## 1.2 Resolve one task -When a task ID is supplied, select that task. +Before selecting or starting a task, inspect every completed task's +`Context synchronization` field in the plan, in plan order, regardless of its +position relative to the task being selected or resumed. A missing field, or +any value other than `synced`, is unresolved synchronization debt. Never infer +`synced` from chat history. + +For the first task carrying debt: + +- When the task has no durable completed-task record (no `Files changed`, + `Result`, `Verify`, or `Context impact` recorded — a legacy plan predating + that structure, or an incomplete write), do not attempt a reconstructed retry. + Set internal status `blocked` with a required action to migrate the plan + (backfill the completion record, or resolve the debt manually) and a retry + condition of the plan carrying that structure. Stop. +- Otherwise, set internal status `sync_debt`, naming the debt task (its ID and + title) and its own completed record — read directly from the plan by plan + path and task ID — including, when its field is `blocked`, its persisted + `Context synchronization blocker`. Do not run or cite the Task context + synchronization phase. Stop. Do not select or start a new task. + +Only after every completed task is `synced` does task selection proceed. + +When a task ID is supplied, select that task only after the same synchronization- +debt check passes. Otherwise, select the first incomplete task in plan order whose declared dependencies are complete. @@ -90,6 +113,7 @@ Set exactly one internal state: - `ready` - `blocked` - `plan_complete` +- `sync_debt` Record only the internal state. Do not add explanatory prose before or after it. @@ -103,8 +127,14 @@ A `ready` result must identify: - Relevant files and context. - Review assumptions. -Step 2 consumes this result verbatim, so anything the execution phase needs has -to be present here. +A `sync_debt` result must identify: + +- The debt-carrying task's ID and title. +- Its own completed record, read directly from the plan by plan path and task ID. +- Its persisted `Context synchronization blocker`, when present. + +Step 2 consumes a `ready` result verbatim, so anything the execution phase +needs has to be present here. ## Plan review boundaries @@ -113,7 +143,7 @@ Do not: - Modify application code. - Modify tests. - Update the plan. -- Mark the task complete. +- Mark a task complete. - Request implementation confirmation. - Run task execution. - Synchronize context. diff --git a/.pi/skills/sce-next-task/references/sync-report.md b/.pi/skills/sce-next-task/references/sync-report.md new file mode 100644 index 00000000..0211740c --- /dev/null +++ b/.pi/skills/sce-next-task/references/sync-report.md @@ -0,0 +1,137 @@ +# Context Sync Report + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `synced` +- `no_context_change` +- `blocked` + +The input execution status is always `complete` and does not need to be repeated +as a separate workflow state. + +## Synced variant + +# Context Sync Report + +**Status:** synced +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Updated context + +- `{context file}` — {concise description of the durable truth updated} + +## Architecture decisions + +- `{written or reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}` + +## Verification + +- {How the edited context was checked against implementation and execution evidence.} +- {File hygiene: line counts, relative links, diagrams where structure is complex.} +- {Documentation, link, or formatting checks that were run, when applicable.} + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## No-context-change variant + +# Context Sync Report + +**Status:** no_context_change +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Updated files + +- {List each changed file from the execution handoff except paths under + `context/`; state `None.` when no files remain.} + +## Synchronization result + +{Explain why the completed implementation did not introduce durable, +non-obvious repository knowledge requiring an update.} + +## Context reviewed + +- `{context file or area}` — {what was checked and why it remains accurate} + +## Architecture decisions + +- `{reused ADR path}` — {decision and status} +- None qualified. + +## Feature existence + +- `{feature}` — `{context file that canonically describes it}`, already present. + +## Verification + +- {How existing context was compared with implementation and execution evidence.} + +--- + +## Blocked variant + +# Context Sync Report + +**Status:** blocked +**Plan:** `{plan path}` +**Task:** `{task id} — {task title}` + +## Context synchronization blocker + +- Blocker: {specific synchronization blocker} +- Required action: {decision or correction required} +- Retry condition: {concrete condition under which context synchronization + should run again} + +## Context changes + +- {List safe context edits preserved, or state `No context files were changed.`} + +## Architecture decisions + +- `{ADR path written or reused before the blocker}` — {decision and status} +- None written or reused before the blocker. + +## Report rules + +- Name exact context files when they were changed or reviewed. +- Under **Architecture decisions**, list every ADR path written or reused during + the decision gate. In a successful report, state `None qualified.` when the + gate skipped invocation. In a blocked report, state + `None written or reused before the blocker.` when applicable. +- Under **Updated files** (synced and no-context-change reports), list every + changed file from the execution handoff except paths under `context/`. A + blocked report does not repeat that list — it is already on the plan's + completed task record. +- Report the missing context root as `blocked`, with `sce setup + --bootstrap-context` as the required action and the existence of `context/` as + the retry condition. +- In a blocked report, write the `Context synchronization blocker` + subsection using the same field names the plan's completion record + uses, so plan review can persist it verbatim. +- Omit **Feature existence** only when the task implemented no feature. +- Describe durable truth, not implementation-session chronology. +- Keep evidence concise and factual. +- Do not claim final validation passed. +- Do not determine whether the plan is complete. +- Do not recommend a next implementation task. diff --git a/.pi/skills/sce-next-task/references/task-execution.md b/.pi/skills/sce-next-task/references/task-execution.md index 7b049492..fe01b530 100644 --- a/.pi/skills/sce-next-task/references/task-execution.md +++ b/.pi/skills/sce-next-task/references/task-execution.md @@ -21,9 +21,26 @@ workflow. It suppresses the approval question and the wait. It never suppresses the gate. Only the workflow entrypoint may set it, and only from an explicit user-supplied approval token. Never infer it. -If required handoff information is absent or stale, still show the gate using -what is known, clearly identify the handoff problem, and do not edit files. After -the user responds, set internal status `blocked`. +If required handoff information is absent, stale, or contradictory, still show the +gate using what is known, clearly identify the handoff problem, and do not edit +files. With the `approve` flag supplied, do not treat pre-approval as permission +to repair or reinterpret the handoff: after showing the gate, set internal status +`blocked` deterministically. Without the flag, wait for the user's response and +then set internal status `blocked`; do not retry the handoff in the same phase. + +A successful `complete` handoff must explicitly contain all of these fields: + +- The resolved `plan` object, including its path and completion counts. +- The selected `task` identity, including its ID and title. +- `changes.files_changed`, the implementation's baseline-relative changed-file list. +- `changes.summary`, a concise implementation summary. +- `verification`, with every reported outcome marked `passed` and its evidence. +- `done_checks`, pairing every done check with evidence. +- `plan_update`, proving the selected task was marked complete and evidence recorded. +- `context_impact`, including classification, affected areas, and reason. + +Do not omit, invent, or reconstruct any of these fields when handing off to context +synchronization. ## 2.1 Validate the handoff without editing @@ -80,7 +97,21 @@ If those constraints materially contradict the reviewed task, set internal statu ## 2.4 Prepare the implementation -Before editing: +Before editing, capture a Git baseline. Record the current `HEAD` commit, the +staged and unstaged patch/content state, and every untracked path/content state +using equivalent `git status`, `git diff`, and `git diff --cached` views. If the +baseline cannot be captured reliably, stop before editing and set internal status +`blocked`. + +After implementation, capture the same views again. Compute +`changes.files_changed` by comparing the post-edit snapshot with the pre-edit +baseline, not by listing the whole working tree or by diffing only against +`HEAD`. Include each path whose state or content changed during this task once; +exclude paths unchanged from the baseline, including unrelated pre-existing +staged, unstaged, and untracked changes. A path already dirty at baseline is +included only when this task changed its state or content. + +Then: - Read the relevant files supplied by plan review. - Inspect nearby code and tests when needed. @@ -145,8 +176,12 @@ Never report a check as passed unless it ran successfully. Only after successful implementation and task-level verification: - Mark only the selected task complete. -- Record concise implementation evidence. -- Record verification commands and outcomes. +- Record directly on the completed task: `Completed` (the date), the + baseline-relative `Files changed` list, a concise factual `Result`, the + actual outcome of every planned `Verify` check, and `Context impact`. +- Set that task's `Context synchronization` field to `pending` in the plan file + before returning `complete`; this write must happen after the execution + facts above and before the synchronization phase is invoked. - Record material deviations or approved assumptions. - Preserve the plan's existing structure and terminology. @@ -173,6 +208,14 @@ Set internal status `blocked` for every other non-successful outcome, including: Do not determine whether the plan is complete. The `/next-task` workflow owns that decision after context synchronization. +Before determining terminal status for a `complete` result, verify that the +handoff contains the resolved plan, task identity, baseline-relative changed +files, implementation summary, verification evidence, done-check evidence, plan +update, and context-impact classification listed above. The mandatory five-root- +file context pass remains required for every completed task, regardless of the +reported context-impact classification, because it is cheap, deterministic, and +load-bearing for context accuracy; `context_impact` must not be used to waive it. + ## 2.9 Return internal state After the phase reaches a terminal state, set exactly one internal state. diff --git a/.pi/skills/sce-validate/SKILL.md b/.pi/skills/sce-validate/SKILL.md index 8572da99..b8c3864e 100644 --- a/.pi/skills/sce-validate/SKILL.md +++ b/.pi/skills/sce-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: sce-validate description: > - Validate one completed SCE plan and synchronize its durable context + Validate one completed SCE plan and record final validation evidence --- # SCE Validate @@ -11,8 +11,7 @@ description: > Own this workflow from input parsing through its terminal user-visible response. Execute the phases below directly and in order. Phase statuses are internal state, not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE -package, or SCE workflow command except `sce-decision`, and invoke `sce-decision` -only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates, +package, or SCE workflow command. Follow the canonical workflow's steps, gates, and stops exactly as written: never invent, skip, reorder, or merge a step. ## Phase references @@ -24,15 +23,12 @@ receives, and how its result branches — and each reference holds the phase its | Step | Read before running the phase | |---|---| | 1 | `references/validation.md` | -| 2 | `references/context-sync.md` | `references/validation-report.md` defines the `## Validation Report` section written into the plan file. Step 1 points to it at the moment it is needed, on a `validated` or `failed` outcome only. -Read a step's reference before taking any action for that step, not after. Read -only the reference for the step you have reached: a run that stops at a `blocked` -or `failed` validation never enters step 2, which is why they are separate files. +Read the reference before taking any action for step 1, not after. ## User-visible output @@ -45,9 +41,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. Relevant non-SCE skills may be used as helper capabilities during the active step. They are not workflow handoffs: when a helper returns, control returns to the active @@ -89,66 +84,25 @@ Do not write the Validation Report yourself. Branch on the report's `Status:`. -`blocked` -> Do not run context synchronization. Print the blocked Markdown -report as returned. Do not rephrase it into a different layout. Stop. +`blocked` -> Print the blocked Markdown report as returned. Do not rephrase it +into a different layout. Stop. -`failed` -> Do not run context synchronization. Print the failed Markdown -report as returned. It is already a session handoff: self-contained, actionable, -and ending with `/validate {plan-path}` after repairs. +`failed` -> Print the failed Markdown report as returned. It is already a session +handoff: self-contained, actionable, and ending with `/validate {plan-path}` after +repairs. Do not rewrite it into a shorter summary. Do not drop the retry command. Do not -add an alternate continuation that replaces `/validate`. +add an alternate continuation that replaces `/validate`. Stop. -Stop. Do not mark the plan finished. Do not continue to context synchronization. -Do not start the repair work in this workflow unless the user explicitly asks -to continue here; the default is that the handoff can leave this session. - -`validated` -> Pass the complete validated Markdown result to the **Plan context synchronization phase**. - -Do not reconstruct, summarize, or reinterpret the validation result before -passing it. - -### 2. Synchronize plan context - -Read `references/context-sync.md`, then run the **Plan context synchronization -phase** with the `Status: validated` Markdown result from the **Validation -phase**. - -Do not run the **Plan context synchronization phase** for `failed` or `blocked`. Those are not -success states. - -Pass the validated result verbatim. It is the authoritative handoff, and the **Plan context synchronization phase** -owns reading the plan path, required context paths, validation evidence, and -reported context impact out of it. - -Do not restate, summarize, or reconstruct any part of the validation result. - -This phase verifies the five root context files on every invocation, whatever the -reported impact, and must account for every path in the plan's `Context sync` -section, so it is never correct to skip it as unnecessary. - -Branch on the synchronization result. - -`blocked` -> Validation itself succeeded and is already recorded in the plan. -Render the **Context synchronization blocked** layout from -`references/output.md`. Nothing records the skipped synchronization, so it is -lost once this session ends. - -Stop. - -`synced` | `no_context_change` -> Print out the report returned by the **Plan context synchronization phase**. +`validated` -> Print the complete validated Markdown result as returned. Continue to the next step. -### 3. Report completion +### 2. Report completion Return exactly one completion block. Do not start another workflow. Render the **Completion** layout from `references/output.md`. -When the synchronization status was `no_context_change`, keep the same -completion block. "Synchronized" here means the final context pass finished -successfully, including the case where no edit was warranted. - Stop. ## Rules @@ -156,14 +110,10 @@ Stop. - Validate at most one plan per invocation. - Read each phase's reference before running that phase. - Do not duplicate the internal instructions of embedded phases. -- The only permitted sibling-skill invocation is `sce-decision`, and only the - successful context-synchronization decision gate may invoke it. - Do not run final validation when implementation tasks remain; the **Validation phase** returns `blocked`, and this workflow stops. -- Run the **Plan context synchronization phase** only when the **Validation phase** returned - `Status: validated`. Do not run it for `failed` or `blocked`. - On `failed`, print the handoff Markdown as returned and stop. Preserve the - retry `/validate {plan-path}` instruction. Do not synchronize context. + retry `/validate {plan-path}` instruction. - Do not implement remaining plan tasks from this workflow unless the user explicitly continues in-session after a failed handoff. - Do not create a Git commit or push changes. @@ -171,5 +121,3 @@ Stop. - Do not execute a follow-up `/next-task`, `/change-to-plan`, or `/validate` yourself. - Do not infer success when an embedded phase returns a non-success status. -- Preserve validation evidence already written to the plan when context - synchronization fails. diff --git a/.pi/skills/sce-validate/references/context-sync.md b/.pi/skills/sce-validate/references/context-sync.md deleted file mode 100644 index bdd0c223..00000000 --- a/.pi/skills/sce-validate/references/context-sync.md +++ /dev/null @@ -1,350 +0,0 @@ -# SCE Plan Context Sync - -## Purpose - -Reconcile one fully validated plan with the repository's durable context and -return a Markdown report. - -This phase owns: - -- Validating the validation handoff. -- Confirming the context root exists. -- Discovering the context required by the finished plan. -- Deciding whether durable context changed. -- Editing and verifying the affected context files. -- Returning one Markdown synchronization report. - -Use the report format in: - -`references/sync-report.md` - -Task-level context sync may already have run after individual tasks. This phase -is the plan-level final pass: it starts from the plan's `Context sync` -requirements and the validated implementation, and closes gaps that remain. - - - -## Input - -The complete Markdown result returned by the validation phase. - -The validation result must report: - -```markdown -**Status:** validated -**Plan:** {plan path} -``` - -Treat that Markdown as the authoritative handoff for: - -- The resolved plan path. -- Validation commands and outcomes. -- Acceptance-criteria evidence. -- Scaffolding removals. -- Reported context impact, required context paths, and affected areas. - -This phase must not be invoked for `failed` or `blocked` validation results. -Those are not success states. Same rule as `sce-task-context-sync`: context sync -runs only after a successful prior phase. - -Do not reconstruct a missing validation result from conversation history. - -## Workflow - -### 1. Validate the validation handoff - -Confirm that: - -- `Status:` is exactly `validated`. -- `Plan:` names an existing plan path. -- Acceptance-criteria evidence is present and every criterion is met. -- Commands run are present. -- A context-impact classification is present. - -If the handoff is missing required information or is internally contradictory, -do not modify context. Return a `blocked` Markdown report. - -### 2. Confirm the context root - -When `context/` does not exist, there is no durable memory to synchronize. -Do not create it, and do not write context files outside it. - -Return a `blocked` report whose required action is: - -`sce setup --bootstrap-context` - -State that validation itself succeeded and is recorded in the plan, and that -plan context synchronization should run again once the context root exists. - -Bootstrapping is the user's action, not this phase's. - -### 3. Discover applicable context - -Start with the validated Markdown result: - -- **Context impact** classification, required context, and affected areas. -- Acceptance-criteria evidence. -- Commands run. - -Then read the plan's `Context sync` section and inspect existing repository -context in this order when present: - -1. Paths named by the plan's `Context sync` section -2. `context/context-map.md` -3. Context files for the affected domain or subsystem -4. `context/overview.md` -5. `context/architecture.md` -6. `context/glossary.md` -7. `context/patterns.md` -8. Operational, product, or decision records directly related to the finished - change - -Use the context map and existing links to locate authoritative files. - -Do not scan or rewrite the entire `context/` tree by default. - -Do not create a new context file when an existing authoritative file can be -updated coherently. - -#### The mandatory root pass - -Every invocation verifies these five files against code truth, whatever the -reported classification is: - -- `context/overview.md` -- `context/architecture.md` -- `context/glossary.md` -- `context/patterns.md` -- `context/context-map.md` - -Verifying is not editing. A classification that warrants no root edit still -requires reading each of these and confirming it is not contradicted by the -finished implementation. A file that is absent is a gap; record it in the -report rather than creating it to satisfy the pass. - -Report each of the five as verified or edited. Never declare synchronization -done while one of them is unchecked. - -#### Plan context requirements - -Every path or statement listed under the plan's `Context sync` section must be -accounted for in the report as already accurate or updated. A requirement the -finished code still does not satisfy is a blocker, not a note. - -### 4. Determine whether durable context changed - -Use the reported context impact as a strong hint, then verify it against the -finished implementation and existing context. - -Durable context includes non-obvious repository knowledge such as: - -- User-visible or externally observable behavior. -- Architecture, boundaries, ownership, and dependency direction. -- Public interfaces, data contracts, and persistence behavior. -- Operational procedures and important failure modes. -- Security or privacy behavior. -- Shared terminology. -- Intentional limitations and meaningful design decisions. - -Do not document: - -- Details already obvious from the implementation. -- Temporary debugging information. -- A file-by-file narration of the change. -- Test output that belongs only in validation evidence. -- Speculation or future work not established by the finished plan. -- Generic engineering practices. - -Interpret impact classifications as follows. Each governs which files are -*edited*; none of them waives the mandatory root pass or the plan's Context -sync requirements. - -- `none`: Make no edits beyond any correction the root pass or unmet plan - context requirement turns up. -- `local`: Update the nearest existing authoritative context only when the new - behavior is not reliably discoverable from code. -- `domain`: Update affected domain context and the context map when its links or - summaries changed. -- `root`: Update the relevant root context and any affected domain context. - -If the reported classification is inconsistent with the actual change, use the -verified classification and explain the difference in the report. - -### 5. Record qualifying architecture decisions - -During this successful synchronization, determine whether the completed change -establishes or changes a system-wide important constraint involving one or more -of: - -- System boundaries or ownership. -- Public or cross-domain interfaces. -- Data models or persistence. -- Compatibility contracts. -- Security posture. -- Deployment or distribution strategy. -- A major dependency. -- A similarly durable constraint that is costly or risky to reverse. - -Routine implementation details, local refactors, naming and formatting choices, -temporary experiments, and easily reversible choices do not qualify. Do not -invoke a decision skill for them. - -Use the discovered context, existing decision records, and this evidence: - -- acceptance-criteria and validation evidence. - -Identify each qualifying decision, then handle qualifying decisions in -deterministic order: - -1. Reuse a written ADR path already returned during this plan when it records the - same decision. -2. Otherwise invoke `sce-decision` once with exactly one structured decision - request containing the decision, qualifying evidence, plan and task references, - related context and ADR paths, and any user-requested status. -3. On `written`, retain the returned `adr_path` as synchronization evidence and - make it available for current-state context links before synchronization - completes. Reuse is valid evidence; do not create a duplicate ADR. -4. On `blocked`, stop before current-state context edits and return a `blocked` - synchronization report carrying the decision-writing problem, impact, required - action, and retry condition. - -Invoke `sce-decision` only here, after a successful execution or validation -handoff and during context synchronization. Do not invoke it from a non-success -branch or for any non-decision purpose. When no decision qualifies, continue -without invoking it and record that outcome in synchronization evidence. - -### 6. Synchronize context - -Make the smallest coherent documentation change that preserves repository truth. - -When editing context: - -- Describe the resulting behavior, not the validation session. -- Preserve repository terminology and document structure. -- Remove or correct statements contradicted by the finished implementation. -- Update cross-references when files are added, moved, renamed, or superseded. -- Keep one authoritative statement for each durable fact. -- Avoid copying the validation result verbatim into context files. -- Do not change application code, tests, or plan validation evidence. - -Create a new context file only when: - -- The knowledge is durable and non-obvious. -- No existing file owns it coherently. -- The new file has a clear place in the context map. - -#### Feature existence - -Every feature the finished plan implemented must have at least one durable -canonical description discoverable from `context/`, in a domain file under -`context/{domain}/` or in `context/overview.md` for a cross-cutting feature. - -When the plan delivered a feature no context file describes, add that -description. Prefer a small, precise domain file over overloading -`overview.md` with detail. - -This is not license to narrate the diff: describe what the feature is and how -it behaves, not what was edited during the plan. - -#### Glossary - -Add a `context/glossary.md` entry for any domain language the plan introduced. -New terminology is durable knowledge whatever the classification is. - -#### File hygiene - -Every context file this phase writes must satisfy: - -- One topic per file. -- At most 250 lines. When an edit would push a file past 250 lines, split it - into focused files and link them rather than letting it grow. -- Relative paths in every link to another context file. -- A Mermaid diagram where structure, boundaries, or flows are complex enough - that prose alone would not carry them. -- Concrete code examples only where they clarify non-trivial behavior. - -When detail outgrows a shared file, migrate it into `context/{domain}/`, leave a -concise pointer behind, and link the new file from `context/context-map.md`. - -### 7. Verify synchronization - -After edits, verify: - -- Every changed context file accurately reflects the finished implementation. -- No edited statement contradicts the code, plan, or validation evidence. -- Every qualifying decision has one written or reused ADR path in the report, - and the report states when no decision qualified. -- Every file in the mandatory root pass was read and confirmed against code - truth, whether or not it was edited. -- Every plan `Context sync` requirement is met. -- Each feature implemented by the plan has a durable canonical description - reachable from `context/`. -- Every changed file is at or below 250 lines, covers one topic, and links other - context files by relative path. -- Diagrams are present where structure, boundaries, or flows are complex. -- Links and referenced paths resolve when practical to check. -- New context files are reachable from the context map or another authoritative - index. -- Root context remains concise and delegates details to domain files. -- Unrelated context was not changed. - -Use focused documentation, link, or formatting checks when available. - -Do not rerun full-plan validation. - -If synchronization cannot be completed without inventing facts or resolving a -material contradiction, preserve safe edits when appropriate and return a -`blocked` report. - -### 8. Return the Markdown report - -Return exactly one report status: - -- `synced` -- `no_context_change` -- `blocked` - -`synced` means context files were updated and verified. `no_context_change` -means existing context was checked and no edit was warranted. `blocked` means -context could not be synchronized safely. - -Return only the Markdown report. Do not add explanatory prose before or after -it. - -## Plan context synchronization boundaries - -Do not: - -- Accept a validation result whose status is not `validated`. -- Accept `failed` or `blocked` validation results. -- Implement or modify application code. -- Modify tests. -- Change task completion status, acceptance-criteria marks, or the Validation - Report. -- Rerun full-plan validation. -- Select or execute an implementation task. -- Create a Git commit or push changes. -- Create the context root. `sce setup --bootstrap-context` owns that. -- Narrate changed files as documentation. Feature existence is the only reason - to document a change that introduced no other durable knowledge. -- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command - except `sce-decision`, or invoke `sce-decision` outside the decision gate in - successful context synchronization. -- Delete a context file that has uncommitted changes. -- Return YAML. - -## Completion - -The phase is complete after: - -- The context root was confirmed, or a `blocked` report named - `sce setup --bootstrap-context` as the required action. -- The mandatory root pass was run. -- Plan context requirements were checked. -- The decision gate recorded every qualifying ADR path, found no qualifying - decision, or returned a synchronization blocker. -- Applicable durable context was synchronized and verified, no context change - was warranted, or a synchronization blocker was reported. -- One Markdown report matching `references/sync-report.md` was returned. - - diff --git a/.pi/skills/sce-validate/references/output.md b/.pi/skills/sce-validate/references/output.md index 9cc8a19b..8b811d79 100644 --- a/.pi/skills/sce-validate/references/output.md +++ b/.pi/skills/sce-validate/references/output.md @@ -1,186 +1,16 @@ -# Validation Result +# Validate output layouts -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. +Use only the applicable layout. Values come from internal workflow state. -The `Status` value must be exactly one of: +## Completion -- `validated` -- `failed` -- `blocked` +```markdown +------------------------------------- -The plan-file `## Validation Report` section is written separately using -`references/validation-report.md`. This file is the skill's return value to the -invoking workflow. +# Plan {plan-name} validated. -## Validated variant +All implementation tasks were already complete. +Final validation passed. -# Validation Report - -**Status:** validated -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` - -## Commands run - -- `{command}` -> {passed} — {concise outcome summary} - -## Acceptance criteria - -- [x] AC1: {criterion statement} — {evidence} -- [x] AC2: {criterion statement} — {evidence} - -## Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - -## Residual risks - -- {risk} -- None identified. - -## Context impact - -**Classification:** `{none | local | domain | root}` -**Affected areas:** `{comma-separated areas}` -**Required context:** - -- `{path or statement from the plan Context sync section}` - -{One or two sentences on why this classification fits the finished plan.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## Failed variant - -This variant is a session handoff. Another agent or a later session must be -able to act from it alone. Write it as a prompt the user can paste forward, not -as a summary of the validation run. - -# Validation failed — handoff - -**Status:** failed -**Plan:** `{plan path}` -**Name:** `{plan name}` -**Tasks:** `{completed}/{total} complete` -**Date:** `{YYYY-MM-DD}` -**Validation report:** written to `{plan path}` - -## Goal for the next session - -Repair the unfinished validation so every acceptance criterion and full -validation command passes. Do not modify tests or product code inside a -`/validate` run to force green results; fix the implementation (or the plan) in -a normal work session, then rerun validation. - -## What failed - -- `{check or AC id}`: {problem} - - Evidence: {command output, exit summary, or inspection finding} - - Required action: {concrete repair or decision} - -## Acceptance criteria - -- [x] AC1: {criterion} — {evidence} -- [ ] AC2: {criterion} — {why unmet} - -## Commands run - -- `{command}` -> {passed | failed | not_run} — {concise outcome summary} - -## Constraints - -- All implementation tasks were already complete when validation ran. -- Validation did not modify tests, application code, or configuration to clear - failures. -- Durable context was not synchronized; plan context sync runs only after - validation succeeds. -- Prefer the plan at `{plan path}` and its Validation Report as the source of - recorded evidence. - -## Residual risks - -- {risk} -- None identified. - -## Recommended work - -1. {First concrete fix, with files or areas when known} -2. {Second concrete fix, or decision the user must make} -3. Rerun final validation after the fixes land: - -`/validate {plan path}` - -Do not stop after the repair. The plan is not finished until `/validate` -returns `validated` and plan context sync completes. - ---- - -## Blocked variant - -# Validation blocked - -**Status:** blocked -**Plan:** `{plan path when resolved}` -**Name:** `{plan name when resolved}` - -## Issues - -- **{issue id}** ({category}): {problem} - - Impact: {impact} - - Required: {decision or action} - -## Incomplete tasks - -- `{task id}` — {title} -- Omit this section when no incomplete tasks apply. - -## Candidates - -- `{candidate plan path}` -- Omit this section when plan resolution was not ambiguous. - -## Next step - -{Exactly one continuation, matching the blocker:} - -- Incomplete tasks: - -`/next-task {plan path}` - -- Ambiguous plan: - -`/validate {candidate path}` - -- Missing plan content or other blocker: state the decision required. Do not - invent a command. - ---- - -## Report rules - -- Name the exact `Plan:` path so every emitted command is runnable. -- Use **Status:** exactly `validated`, `failed`, or `blocked`. -- Never claim a check passed unless it ran successfully or the authorized - inspection confirmed it. -- Do not modify tests or product code to clear a failure; record it under - **What failed**. -- The failed variant must always end its **Recommended work** with - `/validate {plan path}` as the final step after repairs. -- The failed variant must be self-contained enough to hand to another session - without the original chat. -- Include **Context impact** only on `validated`. Omit it on `failed` and - `blocked`; plan context sync is not invoked for non-success states. -- Do not include context synchronization results in this report. The invoking - workflow runs `sce-plan-context-sync` only after `validated`. -- Do not select or describe an unrelated next implementation task when status is - `validated`. -- Omit empty optional sections rather than writing placeholders. +Validation report: {plan-path} +``` diff --git a/.pi/skills/sce-validate/references/validation-report.md b/.pi/skills/sce-validate/references/validation-report.md index c99ad85e..3121e1a4 100644 --- a/.pi/skills/sce-validate/references/validation-report.md +++ b/.pi/skills/sce-validate/references/validation-report.md @@ -5,7 +5,7 @@ The Markdown section `sce-validation` appends to the plan file when returning under exactly one `## Validation Report` heading. This is plan-file content. The result returned to the workflow is defined -separately in `references/validation-result.md`. +separately in `references/validation.md`. Do not author this section while planning. Only `/validate` through `sce-validation` writes it. @@ -23,11 +23,6 @@ writes it. - `{command}` -> exit {code} ({concise outcome summary}) - `{command}` -> exit {code} ({concise outcome summary}) -### Scaffolding removed - -- `{path}` — {why it was temporary} -- None. - ### Success-criteria verification - [x] AC1: {criterion statement} -> {evidence} @@ -66,19 +61,16 @@ After repairs, rerun: - Mark each acceptance criterion checkbox in the plan's `## Acceptance criteria` section to match the evidence. Do not mark a criterion met unless the check ran successfully or the inspection named by `Validate:` confirms it. -- Under **Scaffolding removed**, list only temporary debug code, intermediate - artifacts, or throwaway files introduced during the change. Write `None.` when - nothing temporary remained. -- Under **Failed checks and follow-ups**, record the failing check and its - evidence only. Do not describe code or test edits made during validation; +- Under **Failed checks and follow-ups**, record every failing check and its + evidence, including leftover debug-only flags, temporary artifacts, or local + scaffolding. Do not describe code or test edits made during validation; validation does not modify tests or product code to clear failures. Write `None.` when status is `validated`. - When status is `failed`, always include **Retry** with the exact `/validate {plan path}` command. Omit **Retry** when status is `validated`. - Keep evidence concise and factual. Do not narrate the whole implementation history. -- Do not claim context synchronization completed. Plan context sync is a later - workflow step and runs only after `validated`. +- Do not claim durable context synchronization as part of validation. - Do not rewrite task evidence or reopen completed tasks. - When a previous `## Validation Report` already exists, replace it with the new one rather than stacking duplicates. diff --git a/.pi/skills/sce-validate/references/validation.md b/.pi/skills/sce-validate/references/validation.md index 08d7f5a8..1816eaca 100644 --- a/.pi/skills/sce-validate/references/validation.md +++ b/.pi/skills/sce-validate/references/validation.md @@ -12,22 +12,18 @@ This phase owns: - Confirming every implementation task is complete. - Running the plan's full validation commands and each acceptance criterion check. -- Removing temporary scaffolding introduced by the change. - Writing the Validation Report into the plan. - Marking acceptance criteria against the evidence. - Returning one Markdown validation result. Return a result matching: -`references/validation-result.md` +the **Validation Result** section below in this file Write plan-file evidence matching: `references/validation-report.md` -Context synchronization is not this phase's job. The invoking `/validate` -workflow runs `sce-plan-context-sync` only after a `validated` result. - ## Input A plan name or path. @@ -60,7 +56,6 @@ From the plan, collect: - Every acceptance criterion and its `Validate:` check. - The `Full validation` command list. -- The `Context sync` requirements, for the context-impact handoff only. Return `blocked` when the plan has no usable acceptance criteria, or when no validation commands can be determined from the plan or repository conventions. @@ -69,20 +64,7 @@ Prefer the plan's authored checks. Fall back to repository-primary test, lint, and format commands only when `Full validation` is absent, and record that fallback under notes on a `validated` or `failed` result. -### 4. Remove temporary scaffolding - -Before or while running checks, remove temporary scaffolding introduced during -the change when it is clearly throwaway: - -- Debug-only patches or flags left enabled. -- Temporary files or intermediate artifacts not part of the delivered design. -- Local scaffolding the plan or task notes mark as temporary. - -Do not delete durable product code, tests, configuration, or context files. - -Record every removed path. When nothing temporary remains, report `None.` - -### 5. Run full validation and acceptance checks +### 4. Run full validation and acceptance checks Run the plan's `Full validation` commands. @@ -90,6 +72,11 @@ Then verify each acceptance criterion using its `Validate:` line. Prefer a runnable command. Use a named inspection only when the criterion authorizes it, and say exactly what was inspected. +Treat leftover debug-only flags, temporary files, intermediate artifacts, or +local scaffolding as a failed validation check. Record the path and evidence +under **Failed checks and follow-ups**; never delete or repair it during +validation. + When a check fails, record the failure and continue gathering evidence. Do not modify tests, application code, or configuration to make a check pass. Final validation measures the finished work; repair belongs to a later work session, @@ -101,7 +88,7 @@ inspection confirmed the criterion. Do not run task-by-task implementation work for incomplete tasks. That belongs to `/next-task`. -### 6. Update the plan +### 5. Update the plan For `validated` and `failed` outcomes: @@ -115,22 +102,7 @@ Do not reopen completed tasks, rewrite task evidence, or change the task stack. For `blocked`, leave the plan file unchanged. -### 7. Determine context impact for the handoff - -On `validated` only, classify the durable context impact of the finished plan -so `sce-plan-context-sync` can start from the plan's own requirements: - -- Start from the plan's `Context sync` section. -- Inspect what the completed implementation actually changed when needed. -- Report required context paths and affected areas. -- Use `none`, `local`, `domain`, or `root` with the same meanings as task-level - context sync. - -Do not edit context files here. - -On `failed` or `blocked`, omit context impact; context sync will not run. - -### 8. Return the Markdown result +### 6. Return the Markdown result Return exactly one Markdown result: @@ -138,7 +110,7 @@ Return exactly one Markdown result: passed, and the Validation Report was written. - `failed` when evidence was captured but required checks or criteria remain unsatisfied. Shape it as a session handoff per - `references/validation-result.md`, ending recommended work with + the **Validation Result** section below in this file, ending recommended work with `/validate {plan path}`. - `blocked` when validation cannot proceed safely. @@ -154,14 +126,14 @@ Do not: - Modify tests, application code, or configuration to make a failing check pass. - Apply lint or format auto-fixes that change product or test files as part of making validation green. -- Synchronize durable context under `context/` outside the plan file. +- Synchronize durable context under `context/`. - Create the context root. - Mark the plan archived or delete the plan. - Create a Git commit or push changes. - Invent acceptance criteria the plan does not state. - Claim verification that was not performed. - Return a YAML result. -- Invoke plan context sync. The workflow owns that step. + ## Completion The phase is complete after: @@ -169,7 +141,175 @@ The phase is complete after: - One plan was resolved, or resolution failed and was reported. - Implementation completeness was checked. - Validation ran to a terminal state, or a blocker prevented it. -- One valid Markdown result matching `references/validation-result.md` was +- One valid Markdown result matching the **Validation Result** section below in this file was returned. + +# Validation Result + +Return only one completed Markdown report using the applicable variant below. +Do not include unused sections, placeholders, YAML, or a fenced code block. + +The `Status` value must be exactly one of: + +- `validated` +- `failed` +- `blocked` + +The plan-file `## Validation Report` section is written separately using +`references/validation-report.md`. This file is the skill's return value to the +invoking workflow. + +## Validated variant + +# Validation Report + +**Status:** validated +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` + +## Commands run + +- `{command}` -> {passed} — {concise outcome summary} + +## Acceptance criteria + +- [x] AC1: {criterion statement} — {evidence} +- [x] AC2: {criterion statement} — {evidence} + +## Residual risks + +- {risk} +- None identified. + +## Notes + +{Include only non-blocking information worth retaining. +Omit this section when unnecessary.} + +--- + +## Failed variant + +This variant is a session handoff. Another agent or a later session must be +able to act from it alone. Write it as a prompt the user can paste forward, not +as a summary of the validation run. + +# Validation failed — handoff + +**Status:** failed +**Plan:** `{plan path}` +**Name:** `{plan name}` +**Tasks:** `{completed}/{total} complete` +**Date:** `{YYYY-MM-DD}` +**Validation report:** written to `{plan path}` + +## Goal for the next session + +Repair the unfinished validation so every acceptance criterion and full +validation command passes. Do not modify tests or product code inside a +`/validate` run to force green results; fix the implementation (or the plan) in +a normal work session, then rerun validation. + +## What failed + +- `{check or AC id}`: {problem} + - Evidence: {command output, exit summary, or inspection finding} + - Required action: {concrete repair or decision} + +## Acceptance criteria + +- [x] AC1: {criterion} — {evidence} +- [ ] AC2: {criterion} — {why unmet} + +## Commands run + +- `{command}` -> {passed | failed | not_run} — {concise outcome summary} + +## Constraints + +- All implementation tasks were already complete when validation ran. +- Validation did not modify tests, application code, or configuration to clear + failures. +- Validation does not synchronize durable context. +- Prefer the plan at `{plan path}` and its Validation Report as the source of + recorded evidence. + +## Residual risks + +- {risk} +- None identified. + +## Recommended work + +1. {First concrete fix, with files or areas when known} +2. {Second concrete fix, or decision the user must make} +3. Rerun final validation after the fixes land: + +`/validate {plan path}` + +Do not stop after the repair. The plan is not finished until `/validate` +returns `validated`. + +--- + +## Blocked variant + +# Validation blocked + +**Status:** blocked +**Plan:** `{plan path when resolved}` +**Name:** `{plan name when resolved}` + +## Issues + +- **{issue id}** ({category}): {problem} + - Impact: {impact} + - Required: {decision or action} + +## Incomplete tasks + +- `{task id}` — {title} +- Omit this section when no incomplete tasks apply. + +## Candidates + +- `{candidate plan path}` +- Omit this section when plan resolution was not ambiguous. + +## Next step + +{Exactly one continuation, matching the blocker:} + +- Incomplete tasks: + +`/next-task {plan path}` + +- Ambiguous plan: + +`/validate {candidate path}` + +- Missing plan content or other blocker: state the decision required. Do not + invent a command. + +--- + +## Report rules + +- Name the exact `Plan:` path so every emitted command is runnable. +- Use **Status:** exactly `validated`, `failed`, or `blocked`. +- Never claim a check passed unless it ran successfully or the authorized + inspection confirmed it. +- Do not modify tests or product code to clear a failure; record it under + **What failed**. +- The failed variant must always end its **Recommended work** with + `/validate {plan path}` as the final step after repairs. +- The failed variant must be self-contained enough to hand to another session + without the original chat. +- Do not include durable context synchronization results in this report. +- Do not select or describe an unrelated next implementation task when status is + `validated`. +- Omit empty optional sections rather than writing placeholders. diff --git a/config/pkl/base/decision-skill.pkl b/config/pkl/base/decision-skill.pkl index 79561861..a18192a8 100644 --- a/config/pkl/base/decision-skill.pkl +++ b/config/pkl/base/decision-skill.pkl @@ -19,18 +19,18 @@ description: > ## Purpose Write exactly one architecture decision record for one qualifying system-wide -important decision during successful task or plan context synchronization. Return +important decision during successful task context synchronization. Return a deterministic internal handoff to the invoking synchronization phase. Do not render an independent user-visible response. ## Input -Accept one structured decision request from `sce-next-task` or `sce-validate` -context synchronization. It must identify: +Accept one structured decision request from `sce-next-task` task context +synchronization. It must identify: - One decision stated as a single durable choice. - Why it qualifies under the decision gate. -- The implementation or validation evidence establishing the decision. +- The implementation / task-verification evidence establishing the decision. - The resolved plan path and relevant task IDs, when applicable. - Related current-state context and existing ADR paths. - An optional requested status. @@ -54,8 +54,12 @@ constraint involving at least one of: Routine implementation details, local refactors, naming and formatting choices, temporary experiments, and easily reversible choices do not qualify. When the -request does not demonstrate the threshold, return `blocked`; do not create an -ADR merely because context synchronization occurred. +request does not demonstrate the threshold, return `not_qualified` (or +`skipped` when the caller deliberately skips the gate); do not create an ADR +merely because context synchronization occurred. A nonqualifying or skipped +result is non-blocking, so the invoking synchronization phase continues +normally. Reserve `blocked` for missing, contradictory, or otherwise unsafe +decision input or history. ## Workflow @@ -66,20 +70,24 @@ path, references sufficient to make the record traceable, and no unresolved material contradiction. If it contains several decisions, require the caller to submit one request per decision. -Allowed statuses are exactly `Proposed`, `Accepted`, `Rejected`, `Deprecated`, -and `Superseded`. Use the explicitly requested allowed status; otherwise default -to `Accepted`. Reject any other status rather than guessing. +Allowed statuses for a newly written ADR are exactly `Proposed`, `Accepted`, +`Rejected`, `Deprecated`, and `Superseded`. Use the explicitly requested allowed +status; otherwise default to `Accepted`. `Deprecated` and `Superseded` remain +distinct creation-time-only statuses: use them to describe the record when it is +created, but never mutate an existing ADR into or out of either status. Reject any +other status rather than guessing. ### 2. Inspect existing decision history Read `context/decisions/` and the supplied related ADR paths before writing. -- Reuse an existing ADR when it already records the same decision; return its - path without creating a duplicate. -- Never edit an ADR whose status is `Accepted`. -- A correction, reversal, or changed decision creates a new dated ADR that - references and supersedes the accepted record. -- Do not overwrite any existing ADR or silently change its status. +- Reuse an existing ADR only when it records an equivalent decision and has an + active status: `Proposed` or `Accepted`. Return its path without creating a + duplicate. Never reuse a `Rejected`, `Deprecated`, or `Superseded` ADR. +- Existing ADRs are immutable regardless of status. Never edit an ADR whose status is `Accepted`; do not edit, overwrite, or silently change the status of any existing record. +- A correction, reversal, or any changed decision always creates a new dated ADR; + it references and supersedes the prior record when applicable, rather than + modifying that record. If `context/` or `context/decisions/` is absent, or history cannot be interpreted without inventing facts, return `blocked` without creating directories. @@ -127,8 +135,11 @@ Return exactly one internal handoff: - `written`: include `status`, `adr_path`, `decision`, `decision_status`, `created` (`true` for a new ADR and `false` for reuse), `supersedes`, and concise verification evidence. +- `not_qualified` or `skipped`: include `status`, the reason the decision gate + did not produce an ADR, and concise supporting evidence. These results are + non-blocking; the invoking synchronization phase continues normally. - `blocked`: include `status`, the specific `problem`, its `impact`, and the - `required_action`. + `required_action`. Use this only when decision writing cannot proceed safely. Use stable field names and repository-relative paths. Return no prose before or after the handoff. The invoking synchronization phase owns all user-visible @@ -139,7 +150,7 @@ reporting. Do not: - Write more than one ADR per request. -- Run outside successful task or plan context synchronization. +- Run outside successful task context synchronization. - Create a command, prompt, context root, or decisions directory. - Modify application code, tests, plans, current-state context, or existing accepted ADRs. diff --git a/config/pkl/base/workflow-catalog.pkl b/config/pkl/base/workflow-catalog.pkl index 51be6144..47852c87 100644 --- a/config/pkl/base/workflow-catalog.pkl +++ b/config/pkl/base/workflow-catalog.pkl @@ -39,7 +39,7 @@ workflows: Mapping = new Mapping { commandSlug = "validate" skillSlug = "sce-validate" title = "SCE Validate" - description = "Validate one completed SCE plan and synchronize its durable context" + description = "Validate one completed SCE plan and record final validation evidence" argumentHint = "" openCodeAgentSlug = "shared-context-code" claudeAllowedTools = "Task, Read, Glob, Grep, Edit, Write, Question, Skill, Bash" diff --git a/config/pkl/base/workflow-change-to-plan.pkl b/config/pkl/base/workflow-change-to-plan.pkl index 097d9fc2..8f576898 100644 --- a/config/pkl/base/workflow-change-to-plan.pkl +++ b/config/pkl/base/workflow-change-to-plan.pkl @@ -41,8 +41,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. \(model.helperSkillCompositionRule) @@ -112,7 +112,12 @@ Render the `plan_ready` result as the summary defined by the **Plan authoring ph Take the next task from `next_task`. A `plan_ready` result always names one. Do not evaluate its dependencies; the **Plan review phase** checks them when the emitted command runs and returns `blocked` if they are unmet. -The continuation invites revision. The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. +The workflow carries one of two explicit continuation shapes across a same-session wait: + +- **Initial-clarification continuation:** `original_request`, `clarification_answers`, and `loaded_context_brief`. `original_request` is the unchanged request from step 1; preserve it with the answers and never ask the user to provide it again. +- **Existing-plan revision continuation:** `plan_path`, `correction`, and `loaded_context_brief`. `plan_path` identifies the plan already written, and `correction` contains the user's requested revision. + +The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. Write `task` rather than `tasks` when `total_tasks` is 1. @@ -124,13 +129,13 @@ Then stop and wait. Do not implement, and do not run the handoff yourself. ### 4. Revise the plan on request -When the user answers clarification questions from step 2, answers open questions listed in the summary, or answers with changes to the plan, revise it in this session. Do not ask them to rerun `/change-to-plan`, and do not ask for the original change request again. +When the user answers clarification questions from step 2, resume the **Initial-clarification continuation** with `original_request`, `clarification_answers`, and the same `loaded_context_brief` from step 1. Preserve `original_request` unchanged and never ask the user for the original change request again. When the user answers open questions listed in the summary or requests changes to an already-written plan, resume the **Existing-plan revision continuation** with `plan_path`, `correction`, and the same `loaded_context_brief`. Do not ask them to rerun `/change-to-plan`. -Run the **Plan authoring phase** with their answer or correction and the same `loaded` brief from step 1. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. +Run the **Plan authoring phase** with the applicable continuation fields. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. An answer that resolves a doubt removes that open question. An answer that does not resolve it leaves the question standing; do not drop it because the user replied to it. If the reply raises a new doubt, the revised plan carries a new open question. -Pass the correction as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. +Pass `clarification_answers` or `correction` as written. Do not restate, soften, or pre-scope it. The **Plan authoring phase** owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. Branch on `status` exactly as in step 2. A revision may legitimately return `needs_clarification` or `blocked`. @@ -563,6 +568,17 @@ which criterion they map to. - {Durable context files that must describe the change once implemented.} +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + ## Constraints and non-goals - **In scope:** {files, modules, and surfaces this plan may touch} @@ -581,19 +597,19 @@ recorded. Remove the section otherwise.} - [ ] T01: `{single intent title}` (status:todo) - Task ID: T01 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: {task IDs, or none} - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending - [ ] T02: `{single intent title}` (status:todo) - Task ID: T02 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: T01 - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending ## Open questions @@ -614,11 +630,11 @@ invent one: `None.` is the expected answer for a well-specified change.} ```markdown - [ ] T02: `Add /auth/refresh endpoint` (status:todo) - Task ID: T02 - - Goal: Implement a POST `/auth/refresh` endpoint that exchanges a valid refresh token for a new access token. - - Boundaries (in/out of scope): In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. + - Scope: In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. - Dependencies: T01 - Done when: `POST /auth/refresh` returns a signed JWT on valid input and 401 on expired or invalid token; targeted tests pass; OpenAPI spec updated. - - Verification notes (commands or checks): `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Verify: `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Context synchronization: pending ``` ## Acceptance criteria rules @@ -631,7 +647,7 @@ invent one: `None.` is the expected answer for a well-specified change.} automated check exists, and say exactly what to look at. - List repository-wide checks once under `Full validation` instead of repeating them per criterion. -- Task-level `Verification notes` prove one task. Acceptance criteria prove the +- Task-level `Verify` proves one task. Acceptance criteria prove the plan. Keep them distinct: a task's checks are narrow and local, a criterion's check is end-to-end. - The union of the acceptance criteria must cover every success signal in the @@ -671,11 +687,14 @@ checkbox and status: ```markdown - [x] T01: `{title}` (status:done) - - {authored fields, unchanged} + - {authored fields, unchanged: Task ID, Scope, Dependencies, Done when} + - Verify: {each planned check, updated with its actual outcome} - Completed: {YYYY-MM-DD} - Files changed: {paths} - - Evidence: {commands run and their outcomes} - - Notes: {material deviations or approved assumptions} + - Result: {concise factual outcome, not a prose diff} + - Context impact: {durable context this change affects, or none} + - Context synchronization: pending | synced | blocked + - Context synchronization blocker: {present only when status is blocked} Blocker: {problem}; Required action: {action}; Retry condition: {condition} ``` `/validate` appends a `## Validation Report` section at the end of the plan. @@ -1099,7 +1118,12 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ Take the next task from `next_task`. A `plan_ready` result always names one. Do not evaluate its dependencies; \(planReview.render.apply(mode)) checks them when the emitted command runs and returns `blocked` if they are unmet. - The continuation invites revision. The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. + The workflow carries one of two explicit continuation shapes across a same-session wait: + + - **Initial-clarification continuation:** `original_request`, `clarification_answers`, and `loaded_context_brief`. `original_request` is the unchanged request from step 1; preserve it with the answers and never ask the user to provide it again. + - **Existing-plan revision continuation:** `plan_path`, `correction`, and `loaded_context_brief`. `plan_path` identifies the plan already written, and `correction` contains the user's requested revision. + + The plan was written from one prose request, so its assumptions are guesses about what the user meant, its scope is one reading of the request, and its task boundaries are the author's judgement. The user has seen none of it until now, and every one of those is cheaper to correct here than after a task has been built on it. A user who does not know revision is on the table will implement a plan they would have changed. Write `task` rather than `tasks` when `total_tasks` is 1. @@ -1111,13 +1135,13 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ ### 4. Revise the plan on request - When the user answers clarification questions from step 2, answers open questions listed in the summary, or answers with changes to the plan, revise it in this session. Do not ask them to rerun `/change-to-plan`, and do not ask for the original change request again. + When the user answers clarification questions from step 2, resume the **Initial-clarification continuation** with `original_request`, `clarification_answers`, and the same `loaded_context_brief` from step 1. Preserve `original_request` unchanged and never ask the user for the original change request again. When the user answers open questions listed in the summary or requests changes to an already-written plan, resume the **Existing-plan revision continuation** with `plan_path`, `correction`, and the same `loaded_context_brief`. Do not ask them to rerun `/change-to-plan`. - \(invoke.render.apply(mode)) \(planAuthoring.render.apply(mode)) with their answer or correction and the same `loaded` brief from step 1. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. + \(invoke.render.apply(mode)) \(planAuthoring.render.apply(mode)) with the applicable continuation fields. The brief still holds; durable context did not change because the user disagreed with a task boundary. Do not reload it. An answer that resolves a doubt removes that open question. An answer that does not resolve it leaves the question standing; do not drop it because the user replied to it. If the reply raises a new doubt, the revised plan carries a new open question. - Pass the correction as written. Do not restate, soften, or pre-scope it. \(planAuthoringUpper.render.apply(mode)) owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. + Pass `clarification_answers` or `correction` as written. Do not restate, soften, or pre-scope it. \(planAuthoringUpper.render.apply(mode)) owns resolving it against the existing plan, and owns preserving completed tasks and their evidence. Branch on `status` exactly as in step 2. A revision may legitimately return `needs_clarification` or `blocked`. @@ -2060,6 +2084,17 @@ local renderPlanTemplate = (mode: model.WorkflowRenderMode) -> """ - {Durable context files that must describe the change once implemented.} + ## Task context synchronization lifecycle + + Persist this field in every plan; this is durable plan state, not chat state: + + - **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. + - For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + ## Constraints and non-goals - **In scope:** {files, modules, and surfaces this plan may touch} @@ -2078,19 +2113,19 @@ local renderPlanTemplate = (mode: model.WorkflowRenderMode) -> """ - [ ] T01: `{single intent title}` (status:todo) - Task ID: T01 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: {task IDs, or none} - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending - [ ] T02: `{single intent title}` (status:todo) - Task ID: T02 - - Goal: {one outcome} - - Boundaries (in/out of scope): In — {tight scope}. Out — {excluded work}. + - Scope: In — {tight scope}. Out — {excluded work}. - Dependencies: T01 - Done when: {clear acceptance for one coherent change} - - Verification notes (commands or checks): {targeted checks for this change} + - Verify: {targeted checks for this change} + - Context synchronization: pending ## Open questions @@ -2111,11 +2146,11 @@ local renderPlanTemplate = (mode: model.WorkflowRenderMode) -> """ ```markdown - [ ] T02: `Add /auth/refresh endpoint` (status:todo) - Task ID: T02 - - Goal: Implement a POST `/auth/refresh` endpoint that exchanges a valid refresh token for a new access token. - - Boundaries (in/out of scope): In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. + - Scope: In — route handler, token validation logic, response schema. Out — refresh token rotation policy (covered in T03), client-side storage changes. - Dependencies: T01 - Done when: `POST /auth/refresh` returns a signed JWT on valid input and 401 on expired or invalid token; targeted tests pass; OpenAPI spec updated. - - Verification notes (commands or checks): `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Verify: `pnpm test src/auth/refresh.test.ts`; `curl -X POST localhost:3000/auth/refresh -d '{"token":"..."}' -w "%{http_code}"`. + - Context synchronization: pending ``` \(planTemplateHeadings.section.apply(mode, "Acceptance criteria rules")) @@ -2128,7 +2163,7 @@ local renderPlanTemplate = (mode: model.WorkflowRenderMode) -> """ automated check exists, and say exactly what to look at. - List repository-wide checks once under `Full validation` instead of repeating them per criterion. - - Task-level `Verification notes` prove one task. Acceptance criteria prove the + - Task-level `Verify` proves one task. Acceptance criteria prove the plan. Keep them distinct: a task's checks are narrow and local, a criterion's check is end-to-end. - The union of the acceptance criteria must cover every success signal in the @@ -2168,11 +2203,14 @@ local renderPlanTemplate = (mode: model.WorkflowRenderMode) -> """ ```markdown - [x] T01: `{title}` (status:done) - - {authored fields, unchanged} + - {authored fields, unchanged: Task ID, Scope, Dependencies, Done when} + - Verify: {each planned check, updated with its actual outcome} - Completed: {YYYY-MM-DD} - Files changed: {paths} - - Evidence: {commands run and their outcomes} - - Notes: {material deviations or approved assumptions} + - Result: {concise factual outcome, not a prose diff} + - Context impact: {durable context this change affects, or none} + - Context synchronization: pending | synced | blocked + - Context synchronization blocker: {present only when status is blocked} Blocker: {problem}; Required action: {action}; Retry condition: {condition} ``` `/validate` appends a `## Validation Report` section at the end of the plan. diff --git a/config/pkl/base/workflow-commit.pkl b/config/pkl/base/workflow-commit.pkl index d157895e..bfa6822e 100644 --- a/config/pkl/base/workflow-commit.pkl +++ b/config/pkl/base/workflow-commit.pkl @@ -12,47 +12,22 @@ local bypassPhaseReferenceQualifier = model.semanticReference.apply( "", " (described at the Regular path's step 2 above)" ) -local commitMessageStyle = model.semanticReference.apply("`references/commit-message-style.md`", "`references/output.md`") +local commitMessageStyle = model.semanticReference.apply( + "`references/commit-message-style.md`", + "`references/commit-message-style.md`" +) local invoke = model.semanticReference.apply("Invoke", "Run") local invokeLower = model.semanticReference.apply("invoke", "run") local yaml = model.semanticReference.apply("YAML", "internal state") local returnUpper = model.semanticReference.apply("Return", "Set internal status") local returnLower = model.semanticReference.apply("return", "set internal status") -local returningTerminalYamlResult = model.semanticReference.apply("Returning one terminal YAML result", "Recording one terminal internal state") -local returnExactlyOneYamlDocument = model.semanticReference.apply("Return exactly one YAML document", "Set exactly one internal state") -local returnOnlyYamlDocument = model.semanticReference.apply("Return only the YAML document", "Record only the internal state") -local returnYaml = model.semanticReference.apply("Return YAML", "Return internal state") - -/// Contract references. A package owns `references/commit-contract.yaml`, so -/// these sentences resolve there. A composite workflow has no such file and -/// describes its internal states inline, so the reference would resolve to -/// nothing and is dropped. The block-shaped reference owns its surrounding blank -/// lines. local commitContractHandoff = model.semanticReference.apply( """ - The skill must return a result matching its commit contract. Branch on + The skill must return one result. Branch on `status`: """, "Branch on `status`:" ) -local commitContractBlock = model.semanticReference.apply( - """ - - Return a result matching: - - `references/commit-contract.yaml` - - """, - "" -) -local commitContractClause = model.semanticReference.apply(" matching `references/commit-contract.yaml`", "") -local commitCompletionBullet = model.semanticReference.apply( - """ - - One valid terminal YAML result matching `references/commit-contract.yaml` was - returned. - """, - "- One valid terminal internal state was returned." -) /// User-visible layouts. Package mode states each layout at its branch, because a /// command file has no sibling `references/output.md`. Composite mode cites the @@ -126,8 +101,9 @@ local atomicCommitPackage = new model.SkillPackage { title = "SCE Atomic Commit" documents = model.packageDocuments.apply(new Listing { model.makeDocument.apply("SKILL.md", ATOMIC_COMMIT_SKILL) - model.makeDocument.apply("references/commit-contract.yaml", COMMIT_CONTRACT) - model.makeDocument.apply("references/commit-message-style.md", COMMIT_MESSAGE_STYLE) + model.makeDocument.apply("references/atomic-commit.md", renderAtomicCommitSkillBody.apply("package")) + model.makeDocument.apply("references/commit-message-style.md", renderCommitMessageStyle.apply("package")) + model.makeDocument.apply("references/output.md", renderCommitOutput.apply("package")) }) } @@ -226,12 +202,23 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ #### 3. Execute exactly one commit - Run `git commit` once with the returned message. + Follow the **Bypass execution handoff** in the Atomic commit reference: + + 1. Create the commit-message temp file outside the repository working tree, + and write the returned `message` verbatim to it using a file-writing + operation. Do not interpolate the multiline message into shell source or a + shell command. + 2. Run `git commit -F ` exactly once. + 3. Only after that command succeeds, retrieve the commit hash explicitly with + `git rev-parse --verify HEAD^{commit}`. Do not parse Git's human-readable + output. + 4. Delete the temp file after the commit attempt, including on failure, where + practical. \(bypassResultLayouts.render.apply(mode)) - Do not retry, do not amend, do not stage additional files, and do not invent a - fallback commit. + On failure, report Git's failure unchanged and do not retry, amend, stage more + files, or fabricate a commit hash. ## Rules @@ -240,7 +227,9 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ - Recognize `oneshot` and `skip` only as an exact case-insensitive first token. They are behaviorally identical. - Do not duplicate the internal instructions of \(atomicCommit.render.apply(mode)). - - Do not stage, unstage, restore, or otherwise modify files. + - Do not stage, unstage, restore, or otherwise modify repository or worktree + files. The bypass commit-message temp file is the sole exception: it must + live outside the working tree, so it is not a repository or worktree file. - Do not amend, reset, revert, rebase, or push. - Do not read unstaged or untracked changes as commit input. - Do not infer success when \(atomicCommit.render.apply(mode)) returns a non-success status. @@ -274,7 +263,7 @@ local renderAtomicCommitSkillBody = (mode: model.WorkflowRenderMode) -> """ Write messages matching: \(commitMessageStyle.render.apply(mode)) - \(commitContractBlock.render.apply(mode)) + Committing is not this skill's job. The invoking `/commit` workflow decides whether a returned message is committed, and it is the only thing that runs `git commit`. @@ -368,22 +357,35 @@ local renderAtomicCommitSkillBody = (mode: model.WorkflowRenderMode) -> """ - No plan slug or task ID appears that the staged diff does not support. - The mode's own constraints hold. - \(atomicCommitHeadings.stepHeading.apply(mode, 9, returnYaml.render.apply(mode))) + \(atomicCommitHeadings.section.apply(mode, "Bypass execution handoff")) - \(returnExactlyOneYamlDocument.render.apply(mode))\(commitContractClause.render.apply(mode)): + This phase returns the message; the invoking `/commit` workflow performs the + bypass commit. When the mode is `bypass`, the invoking workflow must: - - `proposal` in `regular` mode, with one or more messages. - - `bypass_message` in `bypass` mode, with exactly one message. - - `blocked` when messages cannot be written faithfully. + 1. Create the commit-message temp file outside the repository working tree, + and write the returned `message` verbatim to it using a file-writing + operation. Never interpolate a multiline message into shell source or a + shell command. + 2. Run `git commit -F ` exactly once. + 3. After and only after a successful commit, run + `git rev-parse --verify HEAD^{commit}` and use that explicit `HEAD` value as + the reported hash. Never parse Git's human-readable commit output. + 4. On any commit failure, report Git's failure and stop. Never retry, amend, + stage more files, or fabricate a hash. + 5. Delete the temp file after the commit attempt, including on failure, where + practical. - \(returnOnlyYamlDocument.render.apply(mode)). Do not add explanatory prose before or after it. + `oneshot` and `skip` select this same bypass behavior; they differ only in the + trigger token. \(atomicCommitHeadings.section.apply(mode, "Atomic commit boundaries")) Do not: - Run `git commit`, or any command that writes to the repository or its index. - - Stage, unstage, or modify files. + - Stage, unstage, restore, or otherwise modify repository or worktree files. + The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Ask the user to stage or confirm staging. - Analyze unstaged or untracked changes. - Return more than one message in `bypass` mode. @@ -392,7 +394,9 @@ local renderAtomicCommitSkillBody = (mode: model.WorkflowRenderMode) -> """ - Invent plan slugs, task IDs, or issue references. - Mention `context/` synchronization activity in a commit message. - Claim a message was committed. - - Run plan, task, or validation workflows.\(model.packageOnlyBlock.apply(""" + - Run plan, task, or validation workflows. + + \(model.packageOnlyBlock.apply(""" ## Completion @@ -400,7 +404,6 @@ local renderAtomicCommitSkillBody = (mode: model.WorkflowRenderMode) -> """ - The staged diff was read, or reading it failed and was reported. - Messages were written for every staged file, or a blocker prevented it. - \(commitCompletionBullet.render.apply(mode)) """).render.apply(mode)) """ @@ -426,281 +429,53 @@ local structuredAtomicCommitSkill = new model.StructuredWorkflowDocument { local ATOMIC_COMMIT_SKILL = structuredAtomicCommitSkill.render.apply("package", "").text -local COMMIT_CONTRACT = """ - version: 1 - name: sce-atomic-commit-result - - description: > - Output contract for sce-atomic-commit. The skill returns exactly one YAML - document representing proposal, bypass_message, or blocked. - - output_rules: - - Return exactly one result variant. - - The top-level status must be proposal, bypass_message, or blocked. - - Return YAML only, without a Markdown code fence or explanatory prose. - - Include only fields belonging to the selected variant. - - Omit optional fields that do not apply rather than sending them empty. - - Do not return empty strings or null placeholders. - - Return proposal only in regular mode, and bypass_message only in bypass - mode. blocked is valid in both. - - Every staged file must appear under exactly one commit's files. - - Report file paths exactly as `git diff --cached --name-only` reports them. - - Carry the message body inside message, separated from the subject by one - blank line. Do not split it into a separate field. - - Do not include a commit hash. This skill never commits. - - Do not report guidance the mode forbids. - - variants: - - proposal: - meaning: > - The staged changes were analyzed in regular mode and one or more atomic - commit messages were written. Proposal-only: nothing was committed. - - required_fields: - - status - - mode - - commits - - optional_fields: - - split_rationale - - scope_classification - - notes - - field_rules: - - Include split_rationale only when commits holds more than one entry. - - scope_classification reports the context-file guidance gate as - context_only or mixed. - - shape: - status: proposal - mode: regular - - commits: - - id: string - message: string - files: - - string - cites_plan: boolean - - split_rationale: string - scope_classification: context_only | mixed - - notes: - - string - - example: - status: proposal - mode: regular - - commits: - - id: C01 - - message: | - auth: Add token refresh endpoint - - Sessions expired without a way to renew them, forcing a full - re-login. Adds the refresh handler and reuses the existing token - issuer. - - files: - - src/auth/refresh.ts - - tests/auth/refresh.test.ts - - cites_plan: false - - - id: C02 - - message: | - build: Pin the formatter to the toolchain version - - The formatter floated across environments and produced diff noise - unrelated to any change. - - files: - - flake.nix - - cites_plan: false - - split_rationale: > - The refresh endpoint and the formatter pin pursue unrelated goals and - share no files. Committing them together would hide the build change - behind a feature subject. - - scope_classification: mixed - - bypass_message: - meaning: > - The staged changes were analyzed in bypass mode and exactly one commit - message covering all staged files was written. The invoking workflow - executes the commit. - - required_fields: - - status - - mode - - message - - files - - optional_fields: - - cites_plan - - notes - - field_rules: - - files lists every staged file, because one message covers all of them. - - Never include commits, split_rationale, or scope_classification. - - shape: - status: bypass_message - mode: bypass - - message: string - - files: - - string - - cites_plan: boolean - - notes: - - string - - example: - status: bypass_message - mode: bypass - - message: | - auth: Add token refresh endpoint - - Sessions expired without a way to renew them, forcing a full re-login. - Adds the refresh handler and reuses the existing token issuer. - - files: - - src/auth/refresh.ts - - tests/auth/refresh.test.ts - - cites_plan: false - - blocked: - meaning: > - Faithful commit messages cannot be written from the staged changes. - - required_fields: - - status - - issues - - optional_fields: - - mode - - files - - field_rules: - - Include mode whenever the workflow supplied one. - - Include files when staged files were read before blocking. - - Plan-citation ambiguity blocks in regular mode only. In bypass mode the - citation is omitted instead. - - shape: - status: blocked - mode: regular | bypass - - files: - - string - - issues: - - id: string - category: no_staged_changes | plan_citation_ambiguity | unreadable_diff | contradictory_context - problem: string - impact: string - decision_required: string - - example: - status: blocked - mode: regular - - files: - - context/plans/authentication.md - - issues: - - id: B01 - category: plan_citation_ambiguity - - problem: > - The staged plan diff changes two task checkboxes and does not - expose which task this commit completes. - - impact: > - The commit body would cite a task ID the staged diff does not - support. - - decision_required: > - State the completed task ID, or stage only that task's plan edit. - """ local renderCommitMessageStyle = (mode: model.WorkflowRenderMode) -> """ - # Commit message style - - The wording rules for every message \(atomicCommit.render.apply(mode)) returns, in either - mode. This file is the only authority for message content and shape. - - Messages are carried in the result's `message` field, subject first, then one - blank line, then the body. - - ## Subject - - Pattern: - - `: ` - - - Scope is the smallest stable subsystem or module name recognizable in the - repository. When no such name applies, use the primary directory or package - of the change. - - Start the summary with an imperative verb: Fix, Add, Remove, Implement, - Refactor, Simplify, Rename, Update, Ensure, Allow. - - Capitalize the verb. Do not end the subject with a period. - - Keep it concrete and technical. Name what changed, not how it felt. - - ## Body - - Include a body whenever the subject alone leaves the change unexplained. Omit - it for changes whose subject is self-evident. - - A body says what was wrong or missing, why it mattered, what changed - conceptually, and the impact. It does not restate the subject in longer words, - and it does not narrate the editing process. - - Wrap the body at a readable width and separate paragraphs with a blank line. - - ## Issue references - - Put each issue reference on its own line at the end of the body, for example - `Fixes #123`. - - Reference only issues the staged diff or the supplied commit context names. Do - not infer an issue number from a branch name. - - ## Plan citations - - When a commit's staged files include `context/plans/*.md`, the body must also - cite: - - - The affected plan slug. - - Every updated task ID (`T0X`). - - Cite only what the staged plan diff shows. When the diff does not expose the - slug or task IDs clearly enough to cite faithfully, the skill's mode decides - what happens — the ambiguity is not resolved here by guessing. - - ## Anti-patterns - - - Vague subjects: "cleanup", "updates", "various fixes". - - A body that repeats the subject without adding why or impact. - - Playful tone in a fix, security change, or architectural change. - - Mentioning `context/` synchronization activity. - - Inventing plan slugs, task IDs, or issue references. - - Splitting changes that already form one coherent unit. - - Forcing unrelated goals into a single commit. - - Describing intent the staged diff does not support. + # Commit Message Guide + + Use this repository style when writing new commits. + + ## Core rules + + - Start with `scope: Subject` for most code changes. + - Common scopes: `runtime`, `language`, `objects`, `tests`, `CI`, `README`. + - Combined scopes are fine when needed (for example `language+runtime`). + - Use an imperative verb in the subject: `Fix`, `Add`, `Refactor`, `Remove`, `Implement`, `Update`, `Rewrite`, `Use`, `Allow`. + - Keep the subject specific and technical (name the subsystem and actual change). + - Keep the subject to one line and do not end it with a period. + - Add a body when the change is non-trivial. + - Explain why the change is needed. + - Explain how it works at a high level. + - Include impact/tradeoffs/follow-ups when relevant. + - For performance-related commits, include concrete measurements and benchmark context. + - Include regressions as well as improvements. + - Add issue references when relevant on their own lines. + - Example: `Fixes #123` + - Example: `Ref: https://...` + + ## Practical template + + ```text + : + + + + + Fixes # (optional) + ``` + + ## Size-based defaults + + 1. Small fix: subject + 1 short reason line. + 2. Medium refactor: subject + short why + short what changed. + 3. Large architectural change: subject + context + bullets for major changes + impact/tradeoffs. + + ## Anti-patterns to avoid + + - Vague subjects like "misc updates" or "cleanup". + - Bodies that only repeat the subject without explaining why or impact. + - Overly playful tone in serious bug-fix or architectural change. """ -local COMMIT_MESSAGE_STYLE = renderCommitMessageStyle.apply("package") - local commitSkillBody = """ # SCE Commit @@ -734,8 +509,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. \(model.helperSkillCompositionRule) @@ -829,15 +604,25 @@ a contract violation: report it and stop without committing. #### 3. Execute exactly one commit -Run `git commit` once with the returned message. +Follow the **Bypass execution handoff** in `references/atomic-commit.md`: + +1. Create the commit-message temp file outside the repository working tree, and + write the returned `message` verbatim to it using a file-writing operation. Do + not interpolate the multiline message into shell source or a shell command. +2. Run `git commit -F ` exactly once. +3. Only after that command succeeds, retrieve the commit hash explicitly with + `git rev-parse --verify HEAD^{commit}`. Do not parse Git's human-readable + output. +4. Delete the temp file after the commit attempt, including on failure, where + practical. On success, render the **Bypass success** layout from `references/output.md` and stop. On failure, render the **Bypass Git failure** layout from the same file and stop. -Do not retry, do not amend, do not stage additional files, and do not invent a -fallback commit. +Do not retry, do not amend, do not stage additional files, and do not fabricate a +commit hash. ## Rules @@ -847,7 +632,9 @@ fallback commit. They are behaviorally identical. - Read `references/atomic-commit.md` before running the phase. - Do not duplicate the internal instructions of the **Atomic commit phase**. -- Do not stage, unstage, restore, or otherwise modify files. +- Do not stage, unstage, restore, or otherwise modify repository or worktree + files. The bypass commit-message temp file is the sole exception: it must live + outside the working tree, so it is not a repository or worktree file. - Do not amend, reset, revert, rebase, or push. - Do not read unstaged or untracked changes as commit input. - Do not infer success when the **Atomic commit phase** returns a non-success status. @@ -901,13 +688,12 @@ structuredComposite = new model.StructuredCompositeSource { compositeSkillBody = commitSkillBody referenceDocuments = new Listing { model.makeDocument.apply("references/atomic-commit.md", renderAtomicCommitSkillBody.apply("package")) + model.makeDocument.apply("references/commit-message-style.md", renderCommitMessageStyle.apply("package")) model.makeDocument.apply("references/output.md", renderCommitOutput.apply("package")) } phases = new Listing {} internalDocuments = new Listing {} - outputDocuments = new Listing { - model.makeDocument.apply("Commit message style", renderCommitMessageStyle.apply("composite")) - } + outputDocuments = new Listing {} } workflow = new model.WorkflowPackage { diff --git a/config/pkl/base/workflow-content.pkl b/config/pkl/base/workflow-content.pkl index 9c5bd268..c247f757 100644 --- a/config/pkl/base/workflow-content.pkl +++ b/config/pkl/base/workflow-content.pkl @@ -224,9 +224,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. \(helperSkillCompositionRule) @@ -261,12 +260,31 @@ Branch on `status`: `blocked` -> Do not run implementation. Render the **Review blocked** layout from `references/output.md`. When `candidates` is present the plan could not be resolved, and each entry is a candidate path for `/next-task {candidate-path}`. `executable_tasks_remaining` true means another task remains executable and `/next-task {plan-path} {task-id}` selects one; false means no task in the plan can proceed until the plan is updated. Do not print the raw result. Stop. +`sync_debt` -> Read `references/context-sync.md`, then run the **Task context synchronization phase** using the debt task's persisted `Context synchronization handoff` — and, when present, its persisted `Context synchronization blocker` — named by the **Plan review phase**. Do not reconstruct a missing handoff from conversation history. + +Write the debt task's lifecycle to the plan: `synced`, clearing its blocker, required action, and retry condition, for `synced` or `no_context_change`; a refreshed `blocked` state with the report's blocker, required action, and retry condition for `blocked`. If that lifecycle write fails, treat the outcome as `blocked`. + +Branch on the outcome: + +`blocked` -> Render the **Context synchronization blocked** layout from `references/output.md`, distinct from the **Review blocked** layout above. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select or start a new task. Stop. + +`synced` | `no_context_change` -> Re-invoke the **Plan review phase** with the same `plan-name-or-path` and, when present, `task-id` to resume normal task selection. + `plan_complete` -> Render the **Plan already complete** layout from `references/output.md`. Stop. `ready` -> Pass the complete readiness result to the **Task execution phase**. Do not reconstruct, summarize, or reinterpret the reviewed task before passing it. +The review inspects every completed task's `Context synchronization` field in +the plan, in plan order, regardless of its position relative to the task being +selected or resumed, before allowing a new implementation task to start. A +missing field, or any value other than `synced`, is unresolved synchronization +debt. Never infer `synced` from conversation history. When the debt-carrying +task has no durable `Context synchronization handoff` subsection, the **Plan +review phase** returns `blocked` directly with a legacy-migration required +action; otherwise it returns `sync_debt`, resolved by the branch above. + ### 2. Execute the task Read `references/task-execution.md`, then run the **Task execution phase** with @@ -308,9 +326,14 @@ Do not restate, summarize, or reconstruct any part of the execution result. This phase verifies the five root context files on every invocation, whatever the change's reported impact, so it is never correct to skip it as unnecessary. +Before branching on the synchronization result, write the completed task's +lifecycle to the plan file: `synced` for `synced` or `no_context_change`, and +`blocked` with the report's blocker, required action, and retry condition for +`blocked`. If that lifecycle write fails, treat synchronization as `blocked`. + Branch on the synchronization result. -`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. Nothing records the skipped synchronization, so it is lost once this session ends. +`blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. The plan's task lifecycle record contains the blocker, required action, and retry condition. Do not select another task. Stop. @@ -354,8 +377,7 @@ validateSkillBody = """ Own this workflow from input parsing through its terminal user-visible response. Execute the phases below directly and in order. Phase statuses are internal state, not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE -package, or SCE workflow command except `sce-decision`, and invoke `sce-decision` -only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates, +package, or SCE workflow command. Follow the canonical workflow's steps, gates, and stops exactly as written: never invent, skip, reorder, or merge a step. ## Phase references @@ -367,15 +389,12 @@ receives, and how its result branches — and each reference holds the phase its | Step | Read before running the phase | |---|---| | 1 | `references/validation.md` | -| 2 | `references/context-sync.md` | `references/validation-report.md` defines the `## Validation Report` section written into the plan file. Step 1 points to it at the moment it is needed, on a `validated` or `failed` outcome only. -Read a step's reference before taking any action for that step, not after. Read -only the reference for the step you have reached: a run that stops at a `blocked` -or `failed` validation never enters step 2, which is why they are separate files. +Read the reference before taking any action for step 1, not after. ## User-visible output @@ -388,9 +407,8 @@ wrap one in an added preamble, commentary, summary, or extra section. Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. -Approval, clarification, revision, failed-validation repair, and bootstrap waits -resume this same skill in the same session. Never expose an internal phase result -as the workflow's final response. +Any workflow-defined user wait resumes this same skill in the same session. +Never expose an internal phase result as the workflow's final response. \(helperSkillCompositionRule) @@ -429,66 +447,25 @@ Do not write the Validation Report yourself. Branch on the report's `Status:`. -`blocked` -> Do not run context synchronization. Print the blocked Markdown -report as returned. Do not rephrase it into a different layout. Stop. +`blocked` -> Print the blocked Markdown report as returned. Do not rephrase it +into a different layout. Stop. -`failed` -> Do not run context synchronization. Print the failed Markdown -report as returned. It is already a session handoff: self-contained, actionable, -and ending with `/validate {plan-path}` after repairs. +`failed` -> Print the failed Markdown report as returned. It is already a session +handoff: self-contained, actionable, and ending with `/validate {plan-path}` after +repairs. Do not rewrite it into a shorter summary. Do not drop the retry command. Do not -add an alternate continuation that replaces `/validate`. - -Stop. Do not mark the plan finished. Do not continue to context synchronization. -Do not start the repair work in this workflow unless the user explicitly asks -to continue here; the default is that the handoff can leave this session. - -`validated` -> Pass the complete validated Markdown result to the **Plan context synchronization phase**. - -Do not reconstruct, summarize, or reinterpret the validation result before -passing it. - -### 2. Synchronize plan context - -Read `references/context-sync.md`, then run the **Plan context synchronization -phase** with the `Status: validated` Markdown result from the **Validation -phase**. - -Do not run the **Plan context synchronization phase** for `failed` or `blocked`. Those are not -success states. - -Pass the validated result verbatim. It is the authoritative handoff, and the **Plan context synchronization phase** -owns reading the plan path, required context paths, validation evidence, and -reported context impact out of it. - -Do not restate, summarize, or reconstruct any part of the validation result. - -This phase verifies the five root context files on every invocation, whatever the -reported impact, and must account for every path in the plan's `Context sync` -section, so it is never correct to skip it as unnecessary. - -Branch on the synchronization result. - -`blocked` -> Validation itself succeeded and is already recorded in the plan. -Render the **Context synchronization blocked** layout from -`references/output.md`. Nothing records the skipped synchronization, so it is -lost once this session ends. - -Stop. +add an alternate continuation that replaces `/validate`. Stop. -`synced` | `no_context_change` -> Print out the report returned by the **Plan context synchronization phase**. +`validated` -> Print the complete validated Markdown result as returned. Continue to the next step. -### 3. Report completion +### 2. Report completion Return exactly one completion block. Do not start another workflow. Render the **Completion** layout from `references/output.md`. -When the synchronization status was `no_context_change`, keep the same -completion block. "Synchronized" here means the final context pass finished -successfully, including the case where no edit was warranted. - Stop. ## Rules @@ -496,14 +473,10 @@ Stop. - Validate at most one plan per invocation. - Read each phase's reference before running that phase. - Do not duplicate the internal instructions of embedded phases. -- The only permitted sibling-skill invocation is `sce-decision`, and only the - successful context-synchronization decision gate may invoke it. - Do not run final validation when implementation tasks remain; the **Validation phase** returns `blocked`, and this workflow stops. -- Run the **Plan context synchronization phase** only when the **Validation phase** returned - `Status: validated`. Do not run it for `failed` or `blocked`. - On `failed`, print the handoff Markdown as returned and stop. Preserve the - retry `/validate {plan-path}` instruction. Do not synchronize context. + retry `/validate {plan-path}` instruction. - Do not implement remaining plan tasks from this workflow unless the user explicitly continues in-session after a failed handoff. - Do not create a Git commit or push changes. @@ -511,6 +484,4 @@ Stop. - Do not execute a follow-up `/next-task`, `/change-to-plan`, or `/validate` yourself. - Do not infer success when an embedded phase returns a non-success status. -- Preserve validation evidence already written to the plan when context - synchronization fails. """ diff --git a/config/pkl/base/workflow-context-sync.pkl b/config/pkl/base/workflow-context-sync.pkl index 404c36a0..f75a5dfb 100644 --- a/config/pkl/base/workflow-context-sync.pkl +++ b/config/pkl/base/workflow-context-sync.pkl @@ -35,7 +35,8 @@ class SyncReportRole { noContextChangeSummary: (workflow.WorkflowRenderMode) -> String noContextChangeVerification: (workflow.WorkflowRenderMode) -> String blockedIdentity: (workflow.WorkflowRenderMode) -> String - retryCondition: (workflow.WorkflowRenderMode) -> String + blockedBlockerSection: (workflow.WorkflowRenderMode) -> String + blockedRetrySection: (workflow.WorkflowRenderMode) -> String rules: (workflow.WorkflowRenderMode) -> String } @@ -149,42 +150,73 @@ local taskRoleData = new SyncRole { \(packageOnlyBlock.apply(""" ## Input - The complete result returned by the task-execution phase. - """).render.apply(mode))The execution result must have: + Either the complete result returned by the task-execution phase + (same-session), or the plan path and task ID a plan-review recovery step + resolved for a `blocked` task, together with that task's own completed + record — read directly from the plan — and its persisted `Context + synchronization blocker` when present (cross-session retry). + """).render.apply(mode))A live execution result must have: ```\(yamlFence.render.apply(mode)) status: complete ``` - Treat the execution result as the authoritative handoff for: + A cross-session retry has no separate `status` field to check; the + completed task record's presence in the plan, identified by plan path and + task ID, is itself the authoritative signal. + + Treat whichever source was supplied — the live execution result, or the + completed task record read directly from the plan — as the authoritative + source for: - The resolved plan and completed task. - Files changed by implementation. - - Implementation summary. - - Verification evidence. - - Done-check evidence. + - The task's `Result`. + - `Verify` outcomes. + - `Done when` evidence. - Reported context impact. + Treat `changes.files_changed`, or the completed task record's own `Files + changed` field on retry, as the authoritative, pre-edit-baseline-relative + attribution. Use that list when reconciling the implementation; do not + replace it with a whole-working-tree scan or a fresh diff against `HEAD`. + If it is absent, not baseline-relative, or contradictory with the rest of + the record, return a `blocked` Markdown report without modifying context. + This phase must not be \(invokedFor.render.apply(mode)) `declined`, `blocked`, or `incomplete` execution results. - Do not reconstruct a missing execution result from conversation history. + Do not reconstruct a missing execution result or completed task record + from conversation history. """ workflow = (mode: workflow.WorkflowRenderMode) -> """ - \(packageOnlyBlock.apply("## Workflow").render.apply(mode))\(outer.headings.stepHeading.apply(mode, 1, "Validate the execution handoff")) + \(packageOnlyBlock.apply("## Workflow").render.apply(mode))\(outer.headings.stepHeading.apply(mode, 1, "Validate the handoff")) Confirm that: - - `status` is exactly `complete`. - - A `plan` object with a `path` is present. - - Exactly one completed task is identified. - - Changed files and an implementation summary are present. - - Verification evidence is present. + - A live execution result has `status` exactly `complete`; a cross-session + retry has no `status` field to check and is authoritative by the + completed task record's presence in the plan. + - A resolved plan path and task ID are present; a live execution result + carries them in its `plan` and `task` objects, and a cross-session + retry receives them directly from the caller that resolved the debt + task. + - Exactly one completed task is identified, and — on retry — its record is + read directly from the plan by that plan path and task ID rather than + reconstructed in-band. + - `changes.files_changed`, or the completed task record's own `Files + changed` field on retry, is present as the pre-edit-baseline-relative + changed-file list. + - Changed files and a `Result` (an implementation summary, for a live + result) are present. + - `Verify` outcomes (verification evidence, for a live result) are + present. - Done-check evidence is present. - A context-impact classification is present. - If the handoff is missing required information or is internally contradictory, - do not modify context. Return a `blocked` Markdown report. + If the required information is missing, the completed task record cannot be + read from the plan, or either is internally contradictory, do not modify + context. Return a `blocked` Markdown report. \(outer.headings.stepHeading.apply(mode, 2, "Confirm the context root")) @@ -393,6 +425,13 @@ local taskRoleData = new SyncRole { means existing context was checked and no edit was warranted. `blocked` means context could not be synchronized safely. + A `blocked` report always writes the plan path and task ID/title as + identity, plus a `Context synchronization blocker` section (blocker, + required action, retry condition), using the same field names the plan's + completion record uses, so the plan-review recovery step can persist the + blocker verbatim and a future retry can read the completed task record + directly from the plan by plan path and task ID. + \(returnOnlyMarkdownReport.render.apply(mode)). Do not add explanatory prose before or after it. @@ -502,7 +541,6 @@ local planRoleData = new SyncRole { - The resolved plan path. - Validation commands and outcomes. - Acceptance-criteria evidence. - - Scaffolding removals. - Reported context impact, required context paths, and affected areas. This phase must not be \(invokedFor.render.apply(mode)) `failed` or `blocked` validation results. @@ -848,11 +886,7 @@ local blockedReport = (role: SyncReportRole, mode: workflow.WorkflowRenderMode) **Status:** blocked\(" ") \(role.blockedIdentity.apply(mode)) - ## Blocker - - **Problem:** {specific synchronization blocker}\(" ") - **Impact:** {why context cannot be made authoritative safely}\(" ") - **Required action:** {decision or correction required} + \(role.blockedBlockerSection.apply(mode)) ## Context changes @@ -861,11 +895,7 @@ local blockedReport = (role: SyncReportRole, mode: workflow.WorkflowRenderMode) ## Architecture decisions - `{ADR path written or reused before the blocker}` — {decision and status} - - None written or reused before the blocker. - - ## Retry condition - - \(role.retryCondition.apply(mode)) + - None written or reused before the blocker.\(if (role.blockedRetrySection.apply(mode).isEmpty) "" else "\n\n" + role.blockedRetrySection.apply(mode)) """ local reportRules = (role: SyncReportRole, mode: workflow.WorkflowRenderMode) -> """ @@ -941,22 +971,27 @@ local taskReport = new SyncReportRole { blockedIdentity = (mode: workflow.WorkflowRenderMode) -> """ **Plan:** `{plan path}`\(" ") **Task:** `{task id} — {task title}` - - ## Updated files - - - {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} """ - retryCondition = (mode: workflow.WorkflowRenderMode) -> """ - {State the concrete condition under which context synchronization should run - again.} + blockedBlockerSection = (mode: workflow.WorkflowRenderMode) -> """ + ## Context synchronization blocker + + - Blocker: {specific synchronization blocker} + - Required action: {decision or correction required} + - Retry condition: {concrete condition under which context synchronization + should run again} """ + blockedRetrySection = (mode: workflow.WorkflowRenderMode) -> "" rules = (mode: workflow.WorkflowRenderMode) -> """ - - Under **Updated files**, list every changed file from the execution handoff - except paths under `context/`. + - Under **Updated files** (synced and no-context-change reports), list every + changed file from the execution handoff except paths under `context/`. A + blocked report does not repeat that list — it is already on the plan's + completed task record. - Report the missing context root as `blocked`, with `sce setup --bootstrap-context` as the required action and the existence of `context/` as the retry condition. + - In a blocked report, write the `Context synchronization blocker` + subsection using the same field names the plan's completion record + uses, so plan review can persist it verbatim. - Omit **Feature existence** only when the task implemented no feature. - Describe durable truth, not implementation-session chronology. - Keep evidence concise and factual. @@ -1042,7 +1077,16 @@ local planReport = new SyncReportRole { blockedIdentity = (mode: workflow.WorkflowRenderMode) -> """ **Plan:** `{plan path}` """ - retryCondition = (mode: workflow.WorkflowRenderMode) -> """ + blockedBlockerSection = (mode: workflow.WorkflowRenderMode) -> """ + ## Blocker + + **Problem:** {specific synchronization blocker}\(" ") + **Impact:** {why context cannot be made authoritative safely}\(" ") + **Required action:** {decision or correction required} + """ + blockedRetrySection = (mode: workflow.WorkflowRenderMode) -> """ + ## Retry condition + {State the concrete condition under which plan context synchronization should run again.} """ @@ -1087,46 +1131,70 @@ Run this phase for step 3 of the workflow, and only when task execution returned session inherits what this task established. It never touches code, tests, or plan state. -Input: the complete `complete` result from the task execution phase, passed -verbatim. It is the authoritative handoff, and this phase owns reading the plan, -task, changed files, verification evidence, and reported context impact out of -it. +Input: either the complete `complete` result from the task execution phase +(same-session), passed verbatim, or the plan path and task ID a plan-review +recovery step resolved for a `blocked` task, together with that task's own +completed record — read directly from the plan — and its persisted `Context +synchronization blocker` when present (cross-session retry). Whichever was +supplied is the authoritative source, and this phase owns reading the plan, +task, changed files, verification evidence, and reported context impact out +of it. -Do not restate, summarize, or reconstruct any part of the execution result. Do -not reconstruct a missing execution result from conversation history. +Do not restate, summarize, or reconstruct any part of it. Do not reconstruct a +missing execution result or completed task record from conversation history. -The execution result must have: +A live execution result must have: ```text status: complete ``` -Treat the execution result as the authoritative handoff for: +A cross-session retry has no separate `status` field to check; the completed +task record's presence in the plan, identified by plan path and task ID, is +itself the authoritative signal. + +Use the report format in: + +`references/sync-report.md` + +Treat whichever source was supplied — the live execution result, or the +completed task record read directly from the plan — as the authoritative +source for: - The resolved plan and completed task. +- `changes.files_changed`, or the completed task record's own `Files changed` + field on retry, already attributed relative to the pre-edit Git baseline. - Files changed by implementation. -- Implementation summary. -- Verification evidence. +- The task's `Result` (or implementation summary, for a live result). +- `Verify` outcomes (or verification evidence, for a live result). - Done-check evidence. - Reported context impact. This phase must not be run for `declined`, `blocked`, or `incomplete` execution results. -## 3.1 Validate the execution handoff +## 3.1 Validate the handoff Confirm that: -- `status` is exactly `complete`. -- A `plan` object with a `path` is present. -- Exactly one completed task is identified. -- Changed files and an implementation summary are present. -- Verification evidence is present. +- A live execution result has `status` exactly `complete`; a cross-session + retry has no `status` field to check and is authoritative by the completed + task record's presence in the plan. +- A resolved plan path and task ID are present; a live execution result + carries them in its `plan` and `task` objects, and a cross-session retry + receives them directly from the caller that resolved the debt task. +- Exactly one completed task is identified, and — on retry — its record is + read directly from the plan by that plan path and task ID rather than + reconstructed in-band. +- Changed files and a `Result` (an implementation summary, for a live result) + are present. +- `Verify` outcomes (verification evidence, for a live result) are present. - Done-check evidence is present. - A context-impact classification is present. -If the handoff is missing required information or is internally contradictory, do -not modify context. Return a `blocked` Markdown report. +If the required information is missing, the completed task record cannot be +read from the plan, or either is internally contradictory, do not modify +context. Return a `blocked` Markdown report. ## 3.2 Confirm the context root @@ -1371,6 +1439,13 @@ Set exactly one report status: existing context was checked and no edit was warranted. `blocked` means context could not be synchronized safely. +A `blocked` report always writes the plan path and task ID/title as identity, +plus a `Context synchronization blocker` section (blocker, required action, +retry condition), using the same field names the plan's completion record +uses, so the plan-review recovery step can persist the blocker verbatim and a +future retry can read the completed task record directly from the plan by +plan path and task ID. + Record only the Markdown report. Do not add explanatory prose before or after it. Do not determine whether the plan is complete. The `/next-task` workflow owns diff --git a/config/pkl/base/workflow-handover.pkl b/config/pkl/base/workflow-handover.pkl index 4232bb0b..132adf6a 100644 --- a/config/pkl/base/workflow-handover.pkl +++ b/config/pkl/base/workflow-handover.pkl @@ -92,11 +92,6 @@ local renderPersistedFormatBody = """ one. """ -local persistedFormatInline = model.semanticReference.apply( - "## Internal persisted-document format: Handover document\n\n" + renderPersistedFormatBody, - "" -) - local renderSkillBody = (mode: model.WorkflowRenderMode) -> """ \(titleAndPurpose.render.apply(mode))## Input @@ -125,7 +120,8 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """ Ground those facts against repository state: - - `git status` and `git diff` for uncommitted work. + - `git status`, `git diff`, and `git diff --cached` for uncommitted work, + including both unstaged and staged changes. - `context/plans/*.md` for the active plan and task, when one is being worked. - Recent commits, when they clarify what just landed. @@ -148,8 +144,9 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """ #### 3. Compose the handover document - Use the **Persisted-document format: Handover document** section embedded in - this file. Populate all four required sections: + Read `references/handover-template.md` before composing. It defines the + persisted-document format and is the only template authority. Populate all + four required sections: - `Current Task State` - `Decisions Made` @@ -199,10 +196,15 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """ Read the file and confirm it contains all four required sections: `Current Task State`, `Decisions Made`, `Open Questions / Blockers`, and - `Next Recommended Step`. + `Next Recommended Step`. For each section, inspect the content up to the + next required heading (or the end of the file): it must contain non-whitespace + content, and it must not consist only of an empty list marker, a template + placeholder such as `{What is being worked on...}`, or other unreplaced + `{...}` scaffolding. Explicit statements such as `None identified.` are real + content and are valid. - When any required section is missing, render the **Loader blocked** layout - (invalid handover) and stop. + When any required section is missing, empty, or placeholder-only, render the + **Loader blocked** layout (invalid handover) and stop. #### 3. Present for continuation @@ -226,7 +228,7 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """ - Never treat a file outside `context/handovers/`, or a non-Markdown file, as a loadable handover. - Never create the `context/` root; `sce setup --bootstrap-context` owns that. - - Do not begin, plan, or automate the loaded handover's recommended next step.\(persistedFormatInline.render.apply(mode)) + - Do not begin, plan, or automate the loaded handover's recommended next step. """ local structuredCommand = new model.StructuredWorkflowDocument { @@ -280,28 +282,6 @@ local OUTPUT_MD = """ **Path:** `{written path}` - ## Current Task State - - {same content written to the file} - - ## Decisions Made - - - {as written} - - ## Open Questions / Blockers - - - {as written} - - ## Next Recommended Step - - {as written} - - ## Assumptions - - - {as written, or `None.`} - - --- - To continue from this handover in another session: `/handover {written path}` @@ -371,16 +351,19 @@ local handoverPackage = new model.SkillPackage { title = "SCE Handover" documents = model.packageDocuments.apply(new Listing { model.makeDocument.apply("SKILL.md", SKILL) + model.makeDocument.apply("references/handover-template.md", renderPersistedFormatBody) model.makeDocument.apply("references/output.md", OUTPUT_MD) }) } structuredComposite = new model.StructuredCompositeSource { command = structuredCommand - phases = new Listing {} - internalDocuments = new Listing { - model.makeDocument.apply("Handover document", renderPersistedFormatBody) + referenceDocuments = new Listing { + model.makeDocument.apply("references/handover-template.md", renderPersistedFormatBody) + model.makeDocument.apply("references/output.md", OUTPUT_MD) } + phases = new Listing {} + internalDocuments = new Listing {} outputDocuments = new Listing { model.makeDocument.apply("Handover output layouts", OUTPUT_MD) } diff --git a/config/pkl/base/workflow-next-task.pkl b/config/pkl/base/workflow-next-task.pkl index 982dfd47..43153aca 100644 --- a/config/pkl/base/workflow-next-task.pkl +++ b/config/pkl/base/workflow-next-task.pkl @@ -172,10 +172,10 @@ local syncBlockedBranch = model.semanticReference.apply( - The action required to resolve the problem. - The retry condition stated by the report. - State that durable context is now out of date, and that synchronization must be resolved before continuing the plan. Nothing records the skipped synchronization, so it is lost once this session ends. + State that durable context is now out of date, and that synchronization must be resolved before continuing the plan. The task lifecycle record in the plan preserves the blocker and retry condition. """, """ - `blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. Nothing records the skipped synchronization, so it is lost once this session ends. + `blocked` -> The task itself succeeded and is already marked complete in the plan. Render the **Context synchronization blocked** layout from `references/output.md`. The task lifecycle record in the plan preserves the blocker, required action, and retry condition. """ ) local continuationLayouts = model.semanticReference.apply( @@ -379,6 +379,7 @@ local reviewSkillIntroduction = model.packageOnlyBlock.apply(""" This phase owns: - Resolving one plan. + - Recovering unresolved synchronization debt from earlier completed tasks. - Selecting at most one task. - Inspecting the context needed to judge readiness. - Determining readiness. @@ -402,8 +403,38 @@ local renderReviewSkillBody = (mode: model.WorkflowRenderMode) -> """ Read the selected plan before exploring the repository. \(planReviewHeadings.stepHeading.apply(mode, 2, "Resolve one task")) - - When a task ID is supplied, select that task. + + Before selecting or starting a task, inspect every earlier completed task's + `Context synchronization` field in the plan, in plan order. A missing field, or + any value other than `synced`, is unresolved synchronization debt. Never infer + `synced` from chat history. + + For the first task carrying debt: + + - When the task has no durable completed-task record (no `Files changed`, + `Result`, `Verify`, or `Context impact` recorded — a legacy plan + predating that structure, or an incomplete write), do not attempt a + reconstructed retry. \(returnUpper.render.apply(mode)) `blocked` with a required action to + migrate the plan (backfill the completion record, or resolve the debt + manually) and a retry condition of the plan carrying that structure. Stop. + - Otherwise, read that task's own completed record directly from the plan + — identified by plan path and task ID — and \(invoke.render.apply(mode)) \(taskContextSync.render.apply(mode)) + using that record as authoritative input — never reconstruct a missing + one from conversation history. + - When it returns `synced` or `no_context_change`, set that task's `Context + synchronization` field to `synced` in the plan and clear its blocker, + required action, and retry condition. Continue checking the next earlier + task for debt. + - When it returns `blocked`, persist the refreshed blocker, required + action, and retry condition into that task's `Context synchronization + blocker` subsection, \(returnLower.render.apply(mode)) `blocked`, and stop. + Do not select or start a new task. + + Only after every earlier completed task is `synced` does task selection + proceed. + + When a task ID is supplied, select that task only after the same synchronization- + debt check passes. Otherwise, select the first incomplete task in plan order whose declared dependencies are complete. @@ -480,14 +511,16 @@ local renderReviewSkillBody = (mode: model.WorkflowRenderMode) -> """ \(planReviewHeadings.section.apply(mode, "Plan review boundaries")) Do not: - + - Modify application code. - Modify tests. - - Update the plan. - - Mark the task complete. + - Update the plan, except to persist a synchronization-debt recovery + outcome for an earlier completed task per step 2. + - Mark a task complete. - Request implementation confirmation. - \(invokeTaskExecution.render.apply(mode)). - - Synchronize context. + - Synchronize context, except to retry an earlier completed task's + unresolved synchronization debt per step 2. - Run final validation. - Review more than one task.\(model.packageOnlyBlock.apply(""" @@ -815,10 +848,28 @@ local renderExecutionSkillBody = (mode: model.WorkflowRenderMode) -> """ - Relevant files and context. - Review assumptions. - If required handoff information is absent or stale, still show the gate using - what is known, clearly identify the handoff problem, and do not edit files. - After the user responds, \(returnLower.render.apply(mode)) `blocked`. - + If required handoff information is absent, stale, or contradictory, still show + the gate using what is known, clearly identify the handoff problem, and do not + edit files. With the `approve` flag supplied, do not treat pre-approval as + permission to repair or reinterpret the handoff: after showing the gate, + \(returnLower.render.apply(mode)) `blocked` deterministically. Without the flag, wait for the + user's response and then \(returnLower.render.apply(mode)) `blocked`; do not retry the handoff in + the same phase. + + A successful `complete` handoff must explicitly contain all of these fields: + + - The resolved `plan` object, including its path and completion counts. + - The selected `task` identity, including its ID and title. + - `changes.files_changed`, the implementation's baseline-relative changed-file list. + - `changes.summary`, a concise implementation summary. + - `verification`, with every reported outcome marked `passed` and its evidence. + - `done_checks`, pairing every done check with evidence. + - `plan_update`, proving the selected task was marked complete and evidence recorded. + - `context_impact`, including classification, affected areas, and reason. + + Do not omit, invent, or reconstruct any of these fields when handing off to + context synchronization. + \(model.packageOnlyBlock.apply("## Workflow").render.apply(mode))\(taskExecutionHeadings.stepHeading.apply(mode, 1, "Validate the handoff without editing")) Confirm that: @@ -873,14 +924,28 @@ local renderExecutionSkillBody = (mode: model.WorkflowRenderMode) -> """ \(taskExecutionHeadings.stepHeading.apply(mode, 4, "Prepare the implementation")) - Before editing: + Before editing, capture a Git baseline. Record the current `HEAD` commit, the + staged and unstaged patch/content state, and every untracked path/content state + using equivalent `git status`, `git diff`, and `git diff --cached` views. If the + baseline cannot be captured reliably, stop before editing and \(returnUpper.render.apply(mode)) + `blocked`. + + After implementation, capture the same views again. Compute + `changes.files_changed` by comparing the post-edit snapshot with the pre-edit + baseline, not by listing the whole working tree or by diffing only against + `HEAD`. Include each path whose state or content changed during this task once; + exclude paths unchanged from the baseline, including unrelated pre-existing + staged, unstaged, and untracked changes. A path already dirty at baseline is + included only when this task changed its state or content. + + Then: - Read the relevant files supplied by plan review. - Inspect nearby code and tests when needed. - Identify the smallest coherent change satisfying the task. - Follow surrounding naming, structure, error handling, and test style. - Preserve unrelated behavior. - + Do not create a second plan. Do not broaden the reviewed task. @@ -935,16 +1000,29 @@ local renderExecutionSkillBody = (mode: model.WorkflowRenderMode) -> """ \(taskExecutionHeadings.stepHeading.apply(mode, 7, "Update the plan")) Only after successful implementation and task-level verification: - + - Mark only the selected task complete. - - Record concise implementation evidence. - - Record verification commands and outcomes. + - Record directly on the completed task: `Completed` (the date), the + baseline-relative `Files changed` list, a concise factual `Result`, the + actual outcome of every planned `Verify` check, and `Context impact`. + - Set that task's `Context synchronization` field to `pending` in the plan file + before returning `complete`; this write must happen after the execution + facts above and before the synchronization phase is invoked. - Record material deviations or approved assumptions. - Preserve the plan's existing structure and terminology. Do not mark the task complete when returning `declined`, `blocked`, or `incomplete`. + Before determining terminal status for a `complete` result, verify that the + handoff contains the resolved plan, task identity, baseline-relative changed + files, implementation summary, verification evidence, done-check evidence, + plan update, and context-impact classification listed above. The mandatory + five-root-file context pass remains required for every completed task, + regardless of the reported context-impact classification, because it is + cheap, deterministic, and load-bearing for context accuracy; `context_impact` + must not be used to waive it. + \(taskExecutionHeadings.stepHeading.apply(mode, 8, "Determine the terminal status")) \(returnUpper.render.apply(mode)) `complete` when the task was implemented, verified, and marked complete @@ -1039,6 +1117,10 @@ local EXECUTION_CONTRACT = """ - Omit optional fields that do not apply rather than sending them empty. - Do not return empty strings or null placeholders. An empty list is a valid value for a required list field. + - For `complete`, explicitly include the resolved plan, task identity, + baseline-relative changed files, implementation summary, verification + evidence, done-check evidence, plan update, and context-impact fields; + do not reconstruct any of them in the next phase. - Report plan.completed_tasks and plan.total_tasks as they stand when the result is returned. - Do not serialize the implementation gate. The gate is user-facing prose @@ -1411,8 +1493,8 @@ nextTaskPlanReviewReference = """ # Plan review phase Run this phase for step 1 of the workflow. It resolves one plan, selects one -task, and decides whether that task can be implemented right now. It reads; it -never writes. +task, and decides whether that task can be implemented right now. It reads; +it never writes. Inputs: the parsed `plan-name-or-path`, and `task-id` when present. The `auto-approve` token is not passed here and has no meaning in this phase. @@ -1430,7 +1512,30 @@ Read the selected plan before exploring the repository. ## 1.2 Resolve one task -When a task ID is supplied, select that task. +Before selecting or starting a task, inspect every completed task's +`Context synchronization` field in the plan, in plan order, regardless of its +position relative to the task being selected or resumed. A missing field, or +any value other than `synced`, is unresolved synchronization debt. Never infer +`synced` from chat history. + +For the first task carrying debt: + +- When the task has no durable completed-task record (no `Files changed`, + `Result`, `Verify`, or `Context impact` recorded — a legacy plan predating + that structure, or an incomplete write), do not attempt a reconstructed retry. + Set internal status `blocked` with a required action to migrate the plan + (backfill the completion record, or resolve the debt manually) and a retry + condition of the plan carrying that structure. Stop. +- Otherwise, set internal status `sync_debt`, naming the debt task (its ID and + title) and its own completed record — read directly from the plan by plan + path and task ID — including, when its field is `blocked`, its persisted + `Context synchronization blocker`. Do not run or cite the Task context + synchronization phase. Stop. Do not select or start a new task. + +Only after every completed task is `synced` does task selection proceed. + +When a task ID is supplied, select that task only after the same synchronization- +debt check passes. Otherwise, select the first incomplete task in plan order whose declared dependencies are complete. @@ -1500,6 +1605,7 @@ Set exactly one internal state: - `ready` - `blocked` - `plan_complete` +- `sync_debt` Record only the internal state. Do not add explanatory prose before or after it. @@ -1513,8 +1619,14 @@ A `ready` result must identify: - Relevant files and context. - Review assumptions. -Step 2 consumes this result verbatim, so anything the execution phase needs has -to be present here. +A `sync_debt` result must identify: + +- The debt-carrying task's ID and title. +- Its own completed record, read directly from the plan by plan path and task ID. +- Its persisted `Context synchronization blocker`, when present. + +Step 2 consumes a `ready` result verbatim, so anything the execution phase +needs has to be present here. ## Plan review boundaries @@ -1523,7 +1635,7 @@ Do not: - Modify application code. - Modify tests. - Update the plan. -- Mark the task complete. +- Mark a task complete. - Request implementation confirmation. - Run task execution. - Synchronize context. @@ -1555,9 +1667,26 @@ workflow. It suppresses the approval question and the wait. It never suppresses the gate. Only the workflow entrypoint may set it, and only from an explicit user-supplied approval token. Never infer it. -If required handoff information is absent or stale, still show the gate using -what is known, clearly identify the handoff problem, and do not edit files. After -the user responds, set internal status `blocked`. +If required handoff information is absent, stale, or contradictory, still show the +gate using what is known, clearly identify the handoff problem, and do not edit +files. With the `approve` flag supplied, do not treat pre-approval as permission +to repair or reinterpret the handoff: after showing the gate, set internal status +`blocked` deterministically. Without the flag, wait for the user's response and +then set internal status `blocked`; do not retry the handoff in the same phase. + +A successful `complete` handoff must explicitly contain all of these fields: + +- The resolved `plan` object, including its path and completion counts. +- The selected `task` identity, including its ID and title. +- `changes.files_changed`, the implementation's baseline-relative changed-file list. +- `changes.summary`, a concise implementation summary. +- `verification`, with every reported outcome marked `passed` and its evidence. +- `done_checks`, pairing every done check with evidence. +- `plan_update`, proving the selected task was marked complete and evidence recorded. +- `context_impact`, including classification, affected areas, and reason. + +Do not omit, invent, or reconstruct any of these fields when handing off to context +synchronization. ## 2.1 Validate the handoff without editing @@ -1614,7 +1743,21 @@ If those constraints materially contradict the reviewed task, set internal statu ## 2.4 Prepare the implementation -Before editing: +Before editing, capture a Git baseline. Record the current `HEAD` commit, the +staged and unstaged patch/content state, and every untracked path/content state +using equivalent `git status`, `git diff`, and `git diff --cached` views. If the +baseline cannot be captured reliably, stop before editing and set internal status +`blocked`. + +After implementation, capture the same views again. Compute +`changes.files_changed` by comparing the post-edit snapshot with the pre-edit +baseline, not by listing the whole working tree or by diffing only against +`HEAD`. Include each path whose state or content changed during this task once; +exclude paths unchanged from the baseline, including unrelated pre-existing +staged, unstaged, and untracked changes. A path already dirty at baseline is +included only when this task changed its state or content. + +Then: - Read the relevant files supplied by plan review. - Inspect nearby code and tests when needed. @@ -1679,8 +1822,12 @@ Never report a check as passed unless it ran successfully. Only after successful implementation and task-level verification: - Mark only the selected task complete. -- Record concise implementation evidence. -- Record verification commands and outcomes. +- Record directly on the completed task: `Completed` (the date), the + baseline-relative `Files changed` list, a concise factual `Result`, the + actual outcome of every planned `Verify` check, and `Context impact`. +- Set that task's `Context synchronization` field to `pending` in the plan file + before returning `complete`; this write must happen after the execution + facts above and before the synchronization phase is invoked. - Record material deviations or approved assumptions. - Preserve the plan's existing structure and terminology. @@ -1707,6 +1854,14 @@ Set internal status `blocked` for every other non-successful outcome, including: Do not determine whether the plan is complete. The `/next-task` workflow owns that decision after context synchronization. +Before determining terminal status for a `complete` result, verify that the +handoff contains the resolved plan, task identity, baseline-relative changed +files, implementation summary, verification evidence, done-check evidence, plan +update, and context-impact classification listed above. The mandatory five-root- +file context pass remains required for every completed task, regardless of the +reported context-impact classification, because it is cheap, deterministic, and +load-bearing for context accuracy; `context_impact` must not be used to waive it. + ## 2.9 Return internal state After the phase reaches a terminal state, set exactly one internal state. @@ -1878,148 +2033,6 @@ When the `approve` flag is supplied, omit the question and end after wait. - If the handoff is stale or incomplete, show the known task information and identify the problem under **Risks or trade-offs**. - -# Context Sync Report - -Return only one completed Markdown report using the applicable variant below. -Do not include unused sections, placeholders, YAML, or a fenced code block. - -The `Status` value must be exactly one of: - -- `synced` -- `no_context_change` -- `blocked` - -The input execution status is always `complete` and does not need to be repeated -as a separate workflow state. - -## Synced variant - -# Context Sync Report - -**Status:** synced\(" ") -**Plan:** `{plan path}`\(" ") -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Updated context - -- `{context file}` — {concise description of the durable truth updated} - -## Architecture decisions - -- `{written or reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}` - -## Verification - -- {How the edited context was checked against implementation and execution evidence.} -- {File hygiene: line counts, relative links, diagrams where structure is complex.} -- {Documentation, link, or formatting checks that were run, when applicable.} - -## Notes - -{Include only non-blocking information worth retaining. -Omit this section when unnecessary.} - ---- - -## No-context-change variant - -# Context Sync Report - -**Status:** no_context_change\(" ") -**Plan:** `{plan path}`\(" ") -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Synchronization result - -{Explain why the completed implementation did not introduce durable, -non-obvious repository knowledge requiring an update.} - -## Context reviewed - -- `{context file or area}` — {what was checked and why it remains accurate} - -## Architecture decisions - -- `{reused ADR path}` — {decision and status} -- None qualified. - -## Feature existence - -- `{feature}` — `{context file that canonically describes it}`, already present. - -## Verification - -- {How existing context was compared with implementation and execution evidence.} - ---- - -## Blocked variant - -# Context Sync Report - -**Status:** blocked\(" ") -**Plan:** `{plan path}`\(" ") -**Task:** `{task id} — {task title}` - -## Updated files - -- {List each changed file from the execution handoff except paths under - `context/`; state `None.` when no files remain.} - -## Blocker - -**Problem:** {specific synchronization blocker}\(" ") -**Impact:** {why context cannot be made authoritative safely}\(" ") -**Required action:** {decision or correction required} - -## Context changes - -- {List safe context edits preserved, or state `No context files were changed.`} - -## Architecture decisions - -- `{ADR path written or reused before the blocker}` — {decision and status} -- None written or reused before the blocker. - -## Retry condition - -{State the concrete condition under which context synchronization should run -again.} - -## Report rules - -- Name exact context files when they were changed or reviewed. -- Under **Architecture decisions**, list every ADR path written or reused during - the decision gate. In a successful report, state `None qualified.` when the - gate skipped invocation. In a blocked report, state - `None written or reused before the blocker.` when applicable. -- Under **Updated files**, list every changed file from the execution handoff - except paths under `context/`. -- Report the missing context root as `blocked`, with `sce setup - --bootstrap-context` as the required action and the existence of `context/` as - the retry condition. -- Omit **Feature existence** only when the task implemented no feature. -- Describe durable truth, not implementation-session chronology. -- Keep evidence concise and factual. -- Do not claim final validation passed. -- Do not determine whether the plan is complete. -- Do not recommend a next implementation task. """ structuredComposite = new model.StructuredCompositeSource { @@ -2029,6 +2042,7 @@ structuredComposite = new model.StructuredCompositeSource { model.makeDocument.apply("references/plan-review.md", nextTaskPlanReviewReference) model.makeDocument.apply("references/task-execution.md", nextTaskTaskExecutionReference) model.makeDocument.apply("references/context-sync.md", contextSync.taskReference) + model.makeDocument.apply("references/sync-report.md", contextSync.taskOutputReport) model.makeDocument.apply("references/output.md", nextTaskOutputReference) } phases = new Listing {} diff --git a/config/pkl/base/workflow-validate.pkl b/config/pkl/base/workflow-validate.pkl index 98dde431..75d0ba3e 100644 --- a/config/pkl/base/workflow-validate.pkl +++ b/config/pkl/base/workflow-validate.pkl @@ -1,11 +1,17 @@ import "workflow-content.pkl" as model -import "workflow-context-sync.pkl" as contextSync local validation = model.semanticReference.apply("`sce-validation`", "the **Validation phase**") local validationUpper = model.semanticReference.apply("`sce-validation`", "The **Validation phase**") local validationName = model.semanticReference.apply("sce-validation", "Validation phase") -local planContextSync = model.semanticReference.apply("`sce-plan-context-sync`", "the **Plan context synchronization phase**") -local validationResult = model.semanticReference.apply("`references/validation-result.md`", "`references/output.md`") +/// Cited from inside `references/validation.md` itself, where the Validation +/// Result contract is now appended below the phase steps. +local validationResultSelfRef = model.semanticReference.apply( + "the **Validation Result** section below in this file", + "the **Validation Result** section below in this file" +) +/// Cited from `references/validation-report.md`, a sibling file, so it must +/// name `references/validation.md` explicitly rather than say "below". +local validationResultFileRef = model.semanticReference.apply("`references/validation.md`", "`references/validation.md`") local validationReport = model.semanticReference.apply("`references/validation-report.md`", "the **Plan-file validation report** section embedded in this file") /// The paragraph that separates the plan-file report from the returned result. /// Both spellings wrap at the same width as the surrounding body text. @@ -22,20 +28,18 @@ local reportVersusResult = model.semanticReference.apply( """ ) /// The validation handoff sentence. A package owns -/// `references/validation-result.md`, so naming that contract resolves there. A -/// composite workflow has no such file, so only the branch instruction survives. +/// `references/output.md`, so naming that contract resolves there. A composite +/// workflow uses the same emitted document, so the reference remains unchanged. local validationResultHandoff = model.semanticReference.apply( """ - The skill must return a Markdown result matching its validation-result contract. + The skill must return a Markdown result matching its validation result contract. Branch on the report's `Status:`. """, "Branch on the report's `Status:`." ) local invoke = model.semanticReference.apply("Invoke", "Run") -local invokeLower = model.semanticReference.apply("invoke", "run") local invokedSkills = model.semanticReference.apply("invoked skills", "embedded phases") local invokedSkill = model.semanticReference.apply("invoked skill", "embedded phase") -local invokedFor = model.semanticReference.apply("invoked for", "run for") local markdownResult = model.semanticReference.apply("Markdown result", "internal state") local returningValidationResult = model.semanticReference.apply("Returning one Markdown validation result", "Recording one Markdown validation result") local returnExactlyOneMarkdownResult = model.semanticReference.apply("Return exactly one Markdown result", "Set exactly one internal state") @@ -44,38 +48,12 @@ local returnLower = model.semanticReference.apply("return", "set internal status local returnUpper = model.semanticReference.apply("Return", "Set internal status") local returnYamlResult = model.semanticReference.apply("Return a YAML result", "Return a internal state") local yaml = model.semanticReference.apply("YAML", "internal state") -local invokePlanContextSync = model.semanticReference.apply("Invoke plan context sync", "Run plan context synchronization") local validationHeadings = new model.PhaseHeadings { step = 1 } local validationReportHeadings = new model.PhaseHeadings { step = 0 } /// User-visible layouts. Package mode states each layout at its branch, because a /// command file has no sibling `references/output.md`. Composite mode cites the -/// `output.md` section that owns the layout, and keeps only the field mapping, -/// branch conditions, and prohibitions that travelled with it. -local syncBlockedBranch = model.semanticReference.apply( - """ - `blocked` -> Validation itself succeeded and is already recorded in the plan. - Present: - - - That plan `{plan-path}` passed final validation and its Validation Report is - written. - - The context contradiction or synchronization failure. - - Any context edits the report says were preserved. - - The action required to resolve the problem. - - The retry condition stated by the report. - - State that durable context is now out of date relative to the validated - implementation, and that plan context synchronization must be resolved before - treating the plan as fully closed. Nothing records the skipped synchronization, - so it is lost once this session ends. - """, - """ - `blocked` -> Validation itself succeeded and is already recorded in the plan. - Render the **Context synchronization blocked** layout from - `references/output.md`. Nothing records the skipped synchronization, so it is - lost once this session ends. - """ -) +/// `output.md` section that owns the layout. local completionLayout = model.semanticReference.apply( """ ``` @@ -86,7 +64,6 @@ local completionLayout = model.semanticReference.apply( All implementation tasks were already complete. Final validation passed. - Durable context is synchronized. Validation report: {plan-path} ``` @@ -106,7 +83,7 @@ local validationPackage = new model.SkillPackage { documents = model.packageDocuments.apply(new Listing { model.makeDocument.apply("SKILL.md", VALIDATION_SKILL) model.makeDocument.apply("references/validation-report.md", VALIDATION_REPORT) - model.makeDocument.apply("references/validation-result.md", VALIDATION_RESULT) + model.makeDocument.apply("references/output.md", VALIDATION_RESULT) }) } @@ -143,7 +120,6 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ - Resolving one plan. - Confirming every implementation task is complete. - Running full validation and acceptance-criteria checks. - - Removing temporary scaffolding. - Writing the Validation Report into the plan. - Returning one Markdown validation result. @@ -151,57 +127,24 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ \(model.inlinePhaseBody.apply(validationSkillBody).render.apply(mode))\(validationResultHandoff.render.apply(mode)) - `blocked` -> Do not \(invokeLower.render.apply(mode)) context synchronization. Print the blocked Markdown - report as returned. Do not rephrase it into a different layout. Stop. + `blocked` -> Print the blocked Markdown report as returned. Do not rephrase it + into a different layout. Stop. - `failed` -> Do not \(invokeLower.render.apply(mode)) context synchronization. Print the failed Markdown - report as returned. It is already a session handoff: self-contained, actionable, - and ending with `/validate {plan-path}` after repairs. + `failed` -> Print the failed Markdown report as returned. It is already a + session handoff: self-contained, actionable, and ending with + `/validate {plan-path}` after repairs. Do not rewrite it into a shorter summary. Do not drop the retry command. Do not - add an alternate continuation that replaces `/validate`. - - Stop. Do not mark the plan finished. Do not continue to context synchronization. - Do not start the repair work in this workflow unless the user explicitly asks - to continue here; the default is that the handoff can leave this session. - - `validated` -> Pass the complete validated Markdown result to \(planContextSync.render.apply(mode)). - - Do not reconstruct, summarize, or reinterpret the validation result before - passing it. - - ### 2. Synchronize plan context - - \(invoke.render.apply(mode)) \(planContextSync.render.apply(mode)) only with a `Status: validated` Markdown result - from \(validation.render.apply(mode)). - - Do not \(invokeLower.render.apply(mode)) \(planContextSync.render.apply(mode)) for `failed` or `blocked`. Those are not - success states. - - Pass the validated result verbatim. It is the authoritative handoff, and \(planContextSync.render.apply(mode)) - owns reading the plan path, required context paths, validation evidence, and - reported context impact out of it. - - Do not restate, summarize, or reconstruct any part of the validation result. + add an alternate continuation that replaces `/validate`. Stop. - \(model.inlinePhaseBody.apply(contextSync.planSkillBody).render.apply(mode))Branch on the synchronization result. - - \(syncBlockedBranch.render.apply(mode)) - - Stop. - - `synced` | `no_context_change` -> Print out the report returned by \(planContextSync.render.apply(mode)). + `validated` -> Print the complete validated Markdown result as returned. Continue to the next step. - ### 3. Report completion + ### 2. Report completion Return exactly one completion block. Do not start another workflow. \(completionLayout.render.apply(mode)) - - When the synchronization status was `no_context_change`, keep the same - completion block. "Synchronized" here means the final context pass finished - successfully, including the case where no edit was warranted. Stop. @@ -209,14 +152,10 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ - Validate at most one plan per invocation. - Do not duplicate the internal instructions of \(invokedSkills.render.apply(mode)). - - The only permitted sibling-skill invocation is `sce-decision`, and only the - successful context-synchronization decision gate may invoke it. - Do not run final validation when implementation tasks remain; \(validation.render.apply(mode)) returns `blocked`, and this workflow stops. - - \(invoke.render.apply(mode)) \(planContextSync.render.apply(mode)) only when \(validation.render.apply(mode)) returned - `Status: validated`. Do not \(invokeLower.render.apply(mode)) it for `failed` or `blocked`. - On `failed`, print the handoff Markdown as returned and stop. Preserve the - retry `/validate {plan-path}` instruction. Do not synchronize context. + retry `/validate {plan-path}` instruction. - Do not implement remaining plan tasks from this workflow unless the user explicitly continues in-session after a failed handoff. - Do not create a Git commit or push changes. @@ -224,15 +163,13 @@ local renderCommandBody = (mode: model.WorkflowRenderMode) -> """ - Do not execute a follow-up `/next-task`, `/change-to-plan`, or `/validate` yourself. - Do not infer success when an \(invokedSkill.render.apply(mode)) returns a non-success status. - - Preserve validation evidence already written to the plan when context - synchronization fails. """ local structuredCommand = new model.StructuredWorkflowDocument { path = "validate.md" frontmatter = new model.WorkflowFrontmatter { lines = (mode: model.WorkflowRenderMode) -> new Listing { - "description: \"Run \(validation.render.apply(mode)) -> \(planContextSync.render.apply(mode)) to finish an SCE plan\"" + "description: \"Run \(validation.render.apply(mode)) to finish an SCE plan\"" "argument-hint: \"\"" } } @@ -259,22 +196,18 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ - Confirming every implementation task is complete. - Running the plan's full validation commands and each acceptance criterion check. - - Removing temporary scaffolding introduced by the change. - Writing the Validation Report into the plan. - Marking acceptance criteria against the evidence. - \(returningValidationResult.render.apply(mode)). Return a result matching: - - \(validationResult.render.apply(mode)) - + + \(validationResultSelfRef.render.apply(mode)) + Write plan-file evidence matching: \(validationReport.render.apply(mode)) - Context synchronization is not this phase's job. The invoking `/validate` - workflow runs \(planContextSync.render.apply(mode)) only after a `validated` result. - ## Input A plan name or path. @@ -304,7 +237,6 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ - Every acceptance criterion and its `Validate:` check. - The `Full validation` command list. - - The `Context sync` requirements, for the context-impact handoff only. \(returnUpper.render.apply(mode)) `blocked` when the plan has no usable acceptance criteria, or when no validation commands can be determined from the plan or repository conventions. @@ -313,20 +245,7 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ and format commands only when `Full validation` is absent, and record that fallback under notes on a `validated` or `failed` result. - \(validationHeadings.stepHeading.apply(mode, 4, "Remove temporary scaffolding")) - - Before or while running checks, remove temporary scaffolding introduced during - the change when it is clearly throwaway: - - - Debug-only patches or flags left enabled. - - Temporary files or intermediate artifacts not part of the delivered design. - - Local scaffolding the plan or task notes mark as temporary. - - Do not delete durable product code, tests, configuration, or context files. - - Record every removed path. When nothing temporary remains, report `None.` - - \(validationHeadings.stepHeading.apply(mode, 5, "Run full validation and acceptance checks")) + \(validationHeadings.stepHeading.apply(mode, 4, "Run full validation and acceptance checks")) Run the plan's `Full validation` commands. @@ -334,6 +253,11 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ runnable command. Use a named inspection only when the criterion authorizes it, and say exactly what was inspected. + Treat leftover debug-only flags, temporary files, intermediate artifacts, or + local scaffolding as a failed validation check. Record the path and evidence + under **Failed checks and follow-ups**; never delete or repair it during + validation. + When a check fails, record the failure and continue gathering evidence. Do not modify tests, application code, or configuration to make a check pass. Final validation measures the finished work; repair belongs to a later work session, @@ -345,7 +269,7 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ Do not run task-by-task implementation work for incomplete tasks. That belongs to `/next-task`. - \(validationHeadings.stepHeading.apply(mode, 6, "Update the plan")) + \(validationHeadings.stepHeading.apply(mode, 5, "Update the plan")) For `validated` and `failed` outcomes: @@ -359,22 +283,7 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ For `blocked`, leave the plan file unchanged. - \(validationHeadings.stepHeading.apply(mode, 7, "Determine context impact for the handoff")) - - On `validated` only, classify the durable context impact of the finished plan - so \(planContextSync.render.apply(mode)) can start from the plan's own requirements: - - - Start from the plan's `Context sync` section. - - Inspect what the completed implementation actually changed when needed. - - Report required context paths and affected areas. - - Use `none`, `local`, `domain`, or `root` with the same meanings as task-level - context sync. - - Do not edit context files here. - - On `failed` or `blocked`, omit context impact; context sync will not run. - - \(validationHeadings.stepHeading.apply(mode, 8, "Return the \(markdownResult.render.apply(mode))")) + \(validationHeadings.stepHeading.apply(mode, 6, "Return the \(markdownResult.render.apply(mode))")) \(returnExactlyOneMarkdownResult.render.apply(mode)): @@ -382,7 +291,7 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ passed, and the Validation Report was written. - `failed` when evidence was captured but required checks or criteria remain unsatisfied. Shape it as a session handoff per - \(validationResult.render.apply(mode)), ending recommended work with + \(validationResultSelfRef.render.apply(mode)), ending recommended work with `/validate {plan path}`. - `blocked` when validation cannot proceed safely. @@ -398,14 +307,14 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ - Modify tests, application code, or configuration to make a failing check pass. - Apply lint or format auto-fixes that change product or test files as part of making validation green. - - Synchronize durable context under `context/` outside the plan file. + - Synchronize durable context under `context/`. - Create the context root. - Mark the plan archived or delete the plan. - Create a Git commit or push changes. - Invent acceptance criteria the plan does not state. - Claim verification that was not performed. - \(returnYamlResult.render.apply(mode)). - - \(invokePlanContextSync.render.apply(mode)). The workflow owns that step.\(model.packageOnlyBlock.apply(""" + \(model.packageOnlyBlock.apply(""" ## Completion @@ -414,7 +323,7 @@ local renderValidationSkillBody = (mode: model.WorkflowRenderMode) -> """ - One plan was resolved, or resolution failed and was reported. - Implementation completeness was checked. - Validation ran to a terminal state, or a blocker prevented it. - - One valid \(markdownResult.render.apply(mode)) matching \(validationResult.render.apply(mode)) was + - One valid \(markdownResult.render.apply(mode)) matching \(validationResultSelfRef.render.apply(mode)) was returned. """).render.apply(mode)) """ @@ -431,8 +340,8 @@ local structuredValidationSkill = new model.StructuredWorkflowDocument { "description: >" " Internal SCE workflow skill that runs final plan validation after all" " implementation tasks are complete: full validation commands, acceptance" - " criteria checks, temporary scaffolding cleanup, a Validation Report written" - " into the plan, and one \(markdownResult.render.apply(mode)) (validated, failed, or blocked)." + " criteria checks, a Validation Report written into the plan, and one" + " \(markdownResult.render.apply(mode)) (validated, failed, or blocked)." " Failing checks are reported only; do not modify tests or product code to make" " validation pass. A failed result is a session handoff that ends by retrying" " /validate. Use from /validate. Do not synchronize durable context, implement" @@ -450,7 +359,7 @@ local renderValidationReport = (mode: model.WorkflowRenderMode) -> """ under exactly one `## Validation Report` heading. This is plan-file content. The result returned to the workflow is defined - separately in \(validationResult.render.apply(mode)). + separately in \(validationResultFileRef.render.apply(mode)). Do not author this section while planning. Only `/validate` through \(validation.render.apply(mode)) writes it. @@ -468,11 +377,6 @@ local renderValidationReport = (mode: model.WorkflowRenderMode) -> """ - `{command}` -> exit {code} ({concise outcome summary}) - `{command}` -> exit {code} ({concise outcome summary}) - ### Scaffolding removed - - - `{path}` — {why it was temporary} - - None. - ### Success-criteria verification - [x] AC1: {criterion statement} -> {evidence} @@ -511,19 +415,16 @@ local renderValidationReport = (mode: model.WorkflowRenderMode) -> """ - Mark each acceptance criterion checkbox in the plan's `## Acceptance criteria` section to match the evidence. Do not mark a criterion met unless the check ran successfully or the inspection named by `Validate:` confirms it. - - Under **Scaffolding removed**, list only temporary debug code, intermediate - artifacts, or throwaway files introduced during the change. Write `None.` when - nothing temporary remained. - - Under **Failed checks and follow-ups**, record the failing check and its - evidence only. Do not describe code or test edits made during validation; + - Under **Failed checks and follow-ups**, record every failing check and its + evidence, including leftover debug-only flags, temporary artifacts, or local + scaffolding. Do not describe code or test edits made during validation; validation does not modify tests or product code to clear failures. Write `None.` when status is `validated`. - When status is `failed`, always include **Retry** with the exact `/validate {plan path}` command. Omit **Retry** when status is `validated`. - Keep evidence concise and factual. Do not narrate the whole implementation history. - - Do not claim context synchronization completed. Plan context sync is a later - workflow step and runs only after `validated`. + - Do not claim durable context synchronization as part of validation. - Do not rewrite task evidence or reopen completed tasks. - When a previous `## Validation Report` already exists, replace it with the new one rather than stacking duplicates. @@ -564,26 +465,11 @@ local renderValidationResult = (mode: model.WorkflowRenderMode) -> """ - [x] AC1: {criterion statement} — {evidence} - [x] AC2: {criterion statement} — {evidence} - ## Scaffolding removed - - - `{path}` — {why it was temporary} - - None. - ## Residual risks - {risk} - None identified. - ## Context impact - - **Classification:** `{none | local | domain | root}` - **Affected areas:** `{comma-separated areas}` - **Required context:** - - - `{path or statement from the plan Context sync section}` - - {One or two sentences on why this classification fits the finished plan.} - ## Notes {Include only non-blocking information worth retaining. @@ -633,8 +519,7 @@ local renderValidationResult = (mode: model.WorkflowRenderMode) -> """ - All implementation tasks were already complete when validation ran. - Validation did not modify tests, application code, or configuration to clear failures. - - Durable context was not synchronized; plan context sync runs only after - validation succeeds. + - Validation does not synchronize durable context. - Prefer the plan at `{plan path}` and its Validation Report as the source of recorded evidence. @@ -652,7 +537,7 @@ local renderValidationResult = (mode: model.WorkflowRenderMode) -> """ `/validate {plan path}` Do not stop after the repair. The plan is not finished until `/validate` - returns `validated` and plan context sync completes. + returns `validated`. --- @@ -709,10 +594,7 @@ local renderValidationResult = (mode: model.WorkflowRenderMode) -> """ `/validate {plan path}` as the final step after repairs. - The failed variant must be self-contained enough to hand to another session without the original chat. - - Include **Context impact** only on `validated`. Omit it on `failed` and - `blocked`; plan context sync is not \(invokedFor.render.apply(mode)) non-success states. - - Do not include context synchronization results in this report. The invoking - workflow runs \(planContextSync.render.apply(mode)) only after `validated`. + - Do not include durable context synchronization results in this report. - Do not select or describe an unrelated next implementation task when status is `validated`. - Omit empty optional sections rather than writing placeholders. @@ -720,14 +602,40 @@ local renderValidationResult = (mode: model.WorkflowRenderMode) -> """ local VALIDATION_RESULT = renderValidationResult.apply("package") +/// `references/validation.md` carries both the phase steps and the result +/// contract, so the phase's own citation of the result contract must point +/// within this same file rather than at a separate `references/output.md`. +local VALIDATION_REFERENCE = renderValidationSkillBody.apply("package") + "\n\n" + VALIDATION_RESULT + +/// Composite-only user-visible layouts. The validation result contract lives +/// in the validation reference, while this file carries the completion layout +/// cited by the composite `SKILL.md`. +local VALIDATE_OUTPUT_LAYOUTS = """ + # Validate output layouts + + Use only the applicable layout. Values come from internal workflow state. + + ## Completion + + ```markdown + ------------------------------------- + + # Plan {plan-name} validated. + + All implementation tasks were already complete. + Final validation passed. + + Validation report: {plan-path} + ``` + """ + structuredComposite = new model.StructuredCompositeSource { command = structuredCommand compositeSkillBody = model.validateSkillBody referenceDocuments = new Listing { - model.makeDocument.apply("references/validation.md", renderValidationSkillBody.apply("package")) - model.makeDocument.apply("references/context-sync.md", contextSync.planSkillBody.render.apply("package")) + model.makeDocument.apply("references/validation.md", VALIDATION_REFERENCE) model.makeDocument.apply("references/validation-report.md", VALIDATION_REPORT) - model.makeDocument.apply("references/output.md", VALIDATION_RESULT) + model.makeDocument.apply("references/output.md", VALIDATE_OUTPUT_LAYOUTS) } phases = new Listing {} internalDocuments = new Listing { @@ -735,7 +643,6 @@ structuredComposite = new model.StructuredCompositeSource { } outputDocuments = new Listing { model.makeDocument.apply("Validation result", renderValidationResult.apply("composite")) - model.makeDocument.apply("Plan context sync report", contextSync.planOutputReport) } } @@ -748,6 +655,5 @@ workflow = new model.WorkflowPackage { } skills = new Mapping { [validationPackage.slug] = validationPackage - ["sce-plan-context-sync"] = contextSync.skills["sce-plan-context-sync"] } } diff --git a/config/pkl/check-generated.sh b/config/pkl/check-generated.sh index 3df9a904..2e198121 100755 --- a/config/pkl/check-generated.sh +++ b/config/pkl/check-generated.sh @@ -108,6 +108,67 @@ expect_pkl_fixture_failure \ expect_pkl_fixture_failure \ "config/pkl/renderers/fixtures/opencode-skill-permission-order-check.pkl" \ "OpenCode skill permissions must preserve the wildcard, deny, and explicit-allow order" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl" \ + "OpenCode skill permission names a missing generated workflow artifact" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/layout-reference-check.pkl" \ + "generated workflow layout citation does not match a heading in \`config/.opencode/skills/sce-next-task/references/output.md\`" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl" \ + "generated workflow layout citation does not match a heading in \`config/.opencode/skills/sce-next-task/references/wrong-file.md\`" +pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl >/dev/null +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/package-local-reference-check.pkl" \ + "generated package-local reference points to a missing document" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl" \ + "sce-validate must not generate context-sync.md, sync-report.md, or validation-result.md" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl" \ + "sce-commit must not generate a commit-contract reference file" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/atomic-commit-content-check.pkl" \ + "atomic-commit.md must delegate message rules to commit-message-style.md and omit the removed result contract" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl" \ + "sce-next-task output.md must not duplicate the context-sync report contract" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/target-neutral-reference-check.pkl" \ + "target-neutral package references differ between Pi, Claude, and OpenCode" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/stale-sync-debt-check.pkl" \ + "generated file contains the stale synchronization-loss wording" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl" \ + "plan-review reference must state sync-debt recovery and legacy-migration-failure behavior" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/compact-plan-template-schema-check.pkl" \ + "generated plan-template.md must use the compact task and completion schema and must not reference the removed Goal, Boundaries (in/out of scope), Verification notes, Implementation evidence, Verification evidence, or Context synchronization handoff fields" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/next-task-compact-completion-writing-check.pkl" \ + "sce-next-task task-execution.md must record execution facts directly on the completed task and must not construct a separate Implementation evidence, Verification evidence, or Context synchronization handoff record" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/plan-review-reads-completed-record-check.pkl" \ + "sce-next-task plan-review.md must recover synchronization debt by reading the completed task record directly from the plan by plan path and task ID, not a persisted Context synchronization handoff" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/context-sync-validates-task-record-check.pkl" \ + "sce-next-task context-sync.md must validate the completed task record directly from the plan and must not reference a persisted Context synchronization handoff" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl" \ + "sce-next-task SKILL.md sync-debt recovery branch must cite references/context-sync.md before invoking the Task context synchronization phase" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl" \ + "sce-next-task plan-review reference must state the synchronization-debt scan covers every completed task, with no earlier-completed-task position-relative wording remaining" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl" \ + "sce-next-task SKILL.md sync-debt recovery blocked outcome must route to the Context synchronization blocked layout, not Review blocked" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/validation-repair-check.pkl" \ + "final validation must not instruct the agent to repair implementation" +expect_pkl_fixture_failure \ + "config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl" \ + "generated sce-validate document must not contain a sce-decision reference or plan-context-sync wording" "$producer" "$repo_root" "$generated_input_root" generated_root="$generated_input_root/pkl-generated" diff --git a/config/pkl/renderers/fixtures/atomic-commit-content-check.pkl b/config/pkl/renderers/fixtures/atomic-commit-content-check.pkl new file mode 100644 index 00000000..fd0213fe --- /dev/null +++ b/config/pkl/renderers/fixtures/atomic-commit-content-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + for (path, text in contract.workflowDocuments) { + [path] = if (path.endsWith("/skills/sce-commit/references/atomic-commit.md")) + text.replaceFirst("Write messages matching:", "## Commit message rules\n\nWrite messages matching:") + else text + } +} + +result = contract.assertAtomicCommitContent.apply(documents) diff --git a/config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl b/config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl new file mode 100644 index 00000000..0bc8bdde --- /dev/null +++ b/config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl @@ -0,0 +1,10 @@ +import "../generation-contract-check.pkl" as contract + +local artifacts = new Mapping { + ["config/.opencode/skills/sce-commit/references/commit-contract.yaml"] = "forbidden" +} + +result = if ( + contract.assertCommitForbiddenPaths.apply(artifacts) +) "negative fixture unexpectedly accepted a forbidden commit reference" +else throw("sce-commit must not generate a commit-contract reference file") diff --git a/config/pkl/renderers/fixtures/compact-plan-template-schema-check.pkl b/config/pkl/renderers/fixtures/compact-plan-template-schema-check.pkl new file mode 100644 index 00000000..48cdcaf3 --- /dev/null +++ b/config/pkl/renderers/fixtures/compact-plan-template-schema-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-change-to-plan/references/plan-template.md"] = + "Goal: {task.goal}\nBoundaries (in/out of scope): {task.boundaries}" +} + +result = if ( + contract.assertCompactPlanTemplateSchema.apply(documents) +) "negative fixture unexpectedly accepted a plan template referencing the removed verbose fields" +else throw("generated plan-template.md must use the compact task and completion schema and must not reference the removed Goal, Boundaries (in/out of scope), Verification notes, Implementation evidence, Verification evidence, or Context synchronization handoff fields") diff --git a/config/pkl/renderers/fixtures/context-sync-validates-task-record-check.pkl b/config/pkl/renderers/fixtures/context-sync-validates-task-record-check.pkl new file mode 100644 index 00000000..db6bb7e9 --- /dev/null +++ b/config/pkl/renderers/fixtures/context-sync-validates-task-record-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/context-sync.md"] = + "Validate the persisted Context synchronization handoff." +} + +result = if ( + contract.assertContextSyncValidatesTaskRecord.apply(documents) +) "negative fixture unexpectedly accepted a context-sync reference validating a persisted handoff instead of the completed task record" +else throw("sce-next-task context-sync.md must validate the completed task record directly from the plan and must not reference a persisted Context synchronization handoff") diff --git a/config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl b/config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl new file mode 100644 index 00000000..21d81574 --- /dev/null +++ b/config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl @@ -0,0 +1,15 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" +local correctFilePath = "config/.opencode/skills/sce-next-task/references/correct-file.md" + +local documents = new Mapping { + for (path, text in contract.workflowDocuments) { + [path] = if (path == skillPath) + "\(text)\nRender the **Completion** layout from `references/correct-file.md`." + else text + } + [correctFilePath] = "# Correct file\n\n## Completion\n\nThis document has the matching heading." +} + +result = contract.assertLayoutReferences.apply(documents) diff --git a/config/pkl/renderers/fixtures/layout-reference-check.pkl b/config/pkl/renderers/fixtures/layout-reference-check.pkl new file mode 100644 index 00000000..c2c397c9 --- /dev/null +++ b/config/pkl/renderers/fixtures/layout-reference-check.pkl @@ -0,0 +1,13 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" +local documents = new Mapping { + for (path, text in contract.workflowDocuments) { + [path] = if (path == skillPath) "\(text)\nRender the **Missing layout** layout from `references/output.md`." else text + } +} + +result = if ( + contract.assertLayoutReferences.apply(documents) +) "negative fixture unexpectedly accepted a missing layout heading" +else throw("generated workflow layout citation does not match a heading in its cited references document") diff --git a/config/pkl/renderers/fixtures/next-task-compact-completion-writing-check.pkl b/config/pkl/renderers/fixtures/next-task-compact-completion-writing-check.pkl new file mode 100644 index 00000000..6f1af71c --- /dev/null +++ b/config/pkl/renderers/fixtures/next-task-compact-completion-writing-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/task-execution.md"] = + "Write the task's Context synchronization handoff subsection, plus Implementation evidence and Verification evidence." +} + +result = if ( + contract.assertNextTaskCompactCompletionWriting.apply(documents) +) "negative fixture unexpectedly accepted a task-execution reference building a separate handoff/evidence record" +else throw("sce-next-task task-execution.md must record execution facts directly on the completed task and must not construct a separate Implementation evidence, Verification evidence, or Context synchronization handoff record") diff --git a/config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl b/config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl new file mode 100644 index 00000000..8a6a9eaf --- /dev/null +++ b/config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/sync-report.md"] = "report" + ["config/.opencode/skills/sce-next-task/references/output.md"] = "# Context Sync Report\n## Synced" +} + +result = if ( + contract.assertNextTaskReportOwnership.apply(documents) +) "negative fixture unexpectedly accepted duplicated context-sync output" +else throw("sce-next-task output.md must not duplicate the context-sync report contract") diff --git a/config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl b/config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl new file mode 100644 index 00000000..d0cb9089 --- /dev/null +++ b/config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl @@ -0,0 +1,10 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/plan-review.md"] = "Resolve one task." +} + +result = if ( + contract.assertPlanReviewSyncDebtRecovery.apply(documents) +) "negative fixture unexpectedly accepted a plan-review reference missing sync-debt recovery wording" +else throw("plan-review reference must state sync-debt recovery and legacy-migration-failure behavior") diff --git a/config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl b/config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl new file mode 100644 index 00000000..ae6994f9 --- /dev/null +++ b/config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl @@ -0,0 +1,22 @@ +import "../generation-contract-check.pkl" as contract + +local renderedWithDanglingPermission = """ +skill: + "*": allow + "sce-*": deny + "sce-next-task": allow + "sce-misspelled": allow +--- +""" + +local emittedArtifacts = new Mapping { + ["config/.opencode/skills/sce-next-task/SKILL.md"] = true +} + +result = if ( + contract.assertOpenCodeSkillPermissionArtifacts.apply( + renderedWithDanglingPermission, + emittedArtifacts + ) +) "negative fixture unexpectedly accepted a missing OpenCode skill artifact" +else throw("OpenCode skill permission names a missing generated workflow artifact") diff --git a/config/pkl/renderers/fixtures/package-local-reference-check.pkl b/config/pkl/renderers/fixtures/package-local-reference-check.pkl new file mode 100644 index 00000000..fceae3ec --- /dev/null +++ b/config/pkl/renderers/fixtures/package-local-reference-check.pkl @@ -0,0 +1,13 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" +local documents = new Mapping { + for (path, text in contract.workflowDocuments) { + [path] = if (path == skillPath) "\(text)\nRead `references/missing.md`." else text + } +} + +result = if ( + contract.assertPackageLocalReferences.apply(documents) +) "negative fixture unexpectedly accepted a missing package-local reference" +else throw("generated package-local reference points to a missing document") diff --git a/config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl b/config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl new file mode 100644 index 00000000..a59ad0a5 --- /dev/null +++ b/config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/plan-review.md"] = + "Inspect every earlier completed task's Context synchronization field." +} + +result = if ( + contract.assertPlanReviewAllTasksScope.apply(documents) +) "negative fixture unexpectedly accepted position-scoped earlier-completed-task wording" +else throw("sce-next-task plan-review reference must state the synchronization-debt scan covers every completed task, with no earlier-completed-task position-relative wording remaining") diff --git a/config/pkl/renderers/fixtures/plan-review-reads-completed-record-check.pkl b/config/pkl/renderers/fixtures/plan-review-reads-completed-record-check.pkl new file mode 100644 index 00000000..f714cea6 --- /dev/null +++ b/config/pkl/renderers/fixtures/plan-review-reads-completed-record-check.pkl @@ -0,0 +1,11 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/plan-review.md"] = + "For sync debt, load the persisted Context synchronization handoff." +} + +result = if ( + contract.assertPlanReviewReadsCompletedRecord.apply(documents) +) "negative fixture unexpectedly accepted a plan-review reference recovering sync debt from a persisted handoff" +else throw("sce-next-task plan-review.md must recover synchronization debt by reading the completed task record directly from the plan by plan path and task ID, not a persisted Context synchronization handoff") diff --git a/config/pkl/renderers/fixtures/stale-sync-debt-check.pkl b/config/pkl/renderers/fixtures/stale-sync-debt-check.pkl new file mode 100644 index 00000000..662f8b0b --- /dev/null +++ b/config/pkl/renderers/fixtures/stale-sync-debt-check.pkl @@ -0,0 +1,14 @@ +import "../generation-contract-check.pkl" as contract + +local artifacts = new Mapping { + for (path, text in contract.generatedArtifacts) { + [path] = if (path == "config/.opencode/opencode.json") + "Nothing records the skipped synchronization, so it is lost once this session ends" + else text + } +} + +result = if ( + contract.assertNoStaleSyncDebtText.apply(artifacts) +) "negative fixture unexpectedly accepted stale synchronization-loss wording" +else throw("generated file contains the stale synchronization-loss wording") diff --git a/config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl b/config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl new file mode 100644 index 00000000..a1d381b9 --- /dev/null +++ b/config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl @@ -0,0 +1,17 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" + +local documents = new Mapping { + [skillPath] = + "`sync_debt` -> Read `references/context-sync.md`, then run the **Task " + + "context synchronization phase**.\n\n" + + "Branch on the outcome:\n\n" + + "`blocked` -> Render the **Review blocked** layout from `references/output.md`.\n\n" + + "`synced` -> Resume plan review." +} + +result = if ( + contract.assertSyncDebtBlockedRouting.apply(documents) +) "negative fixture unexpectedly accepted a sync-debt blocked outcome routed to the Review blocked layout" +else throw("sce-next-task SKILL.md sync-debt recovery blocked outcome must route to the Context synchronization blocked layout, not Review blocked") diff --git a/config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl b/config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl new file mode 100644 index 00000000..a2e14a7a --- /dev/null +++ b/config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl @@ -0,0 +1,15 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" + +local documents = new Mapping { + [skillPath] = + "`sync_debt` -> Run the **Task context synchronization phase** using the " + + "debt task's persisted handoff, then read `references/context-sync.md`.\n\n" + + "Write the debt task's lifecycle to the plan." +} + +result = if ( + contract.assertSyncDebtRecoveryBranch.apply(documents) +) "negative fixture unexpectedly accepted a sync-debt branch that invokes the phase before citing its reference" +else throw("sce-next-task SKILL.md sync-debt recovery branch must cite references/context-sync.md before invoking the Task context synchronization phase") diff --git a/config/pkl/renderers/fixtures/target-neutral-reference-check.pkl b/config/pkl/renderers/fixtures/target-neutral-reference-check.pkl new file mode 100644 index 00000000..ed00c62e --- /dev/null +++ b/config/pkl/renderers/fixtures/target-neutral-reference-check.pkl @@ -0,0 +1,12 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-next-task/references/task-execution.md"] = "opencode" + ["config/.claude/skills/sce-next-task/references/task-execution.md"] = "claude" + ["config/.pi/skills/sce-next-task/references/task-execution.md"] = "pi" +} + +result = if ( + contract.assertTargetNeutralReferences.apply(documents) +) "negative fixture unexpectedly accepted target-specific reference drift" +else throw("target-neutral package references differ between Pi, Claude, and OpenCode") diff --git a/config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl b/config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl new file mode 100644 index 00000000..2d2d1a80 --- /dev/null +++ b/config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl @@ -0,0 +1,10 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-validate/references/output.md"] = "Invoke `sce-decision` to record plan-context-sync." +} + +result = if ( + contract.assertValidateExcludesDecisionAndPlanSync.apply(documents) +) "negative fixture unexpectedly accepted a sce-decision reference or plan-context-sync wording in sce-validate" +else throw("generated sce-validate document must not contain a sce-decision reference or plan-context-sync wording") diff --git a/config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl b/config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl new file mode 100644 index 00000000..85f2c60d --- /dev/null +++ b/config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl @@ -0,0 +1,13 @@ +import "../generation-contract-check.pkl" as contract + +local artifacts = new Mapping { + for (path, text in contract.generatedArtifacts) { + [path] = text + } + ["config/.opencode/skills/sce-validate/references/context-sync.md"] = "forbidden" +} + +result = if ( + contract.assertValidateForbiddenPaths.apply(artifacts) +) "negative fixture unexpectedly accepted a forbidden validate reference" +else throw("sce-validate must not generate context-sync.md, sync-report.md, or validation-result.md") diff --git a/config/pkl/renderers/fixtures/validation-repair-check.pkl b/config/pkl/renderers/fixtures/validation-repair-check.pkl new file mode 100644 index 00000000..12f065dd --- /dev/null +++ b/config/pkl/renderers/fixtures/validation-repair-check.pkl @@ -0,0 +1,10 @@ +import "../generation-contract-check.pkl" as contract + +local documents = new Mapping { + ["config/.opencode/skills/sce-validate/SKILL.md"] = "Repair application code during final validation." +} + +result = if ( + contract.assertValidationIsObservational.apply(documents) +) "negative fixture unexpectedly accepted validation repair instructions" +else throw("final validation must not instruct the agent to repair implementation") diff --git a/config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl b/config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl new file mode 100644 index 00000000..884ba44c --- /dev/null +++ b/config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl @@ -0,0 +1,21 @@ +import "../generation-contract-check.pkl" as contract + +local skillPath = "config/.opencode/skills/sce-next-task/SKILL.md" +local outputPath = "config/.opencode/skills/sce-next-task/references/output.md" +local wrongFilePath = "config/.opencode/skills/sce-next-task/references/wrong-file.md" + +local documents = new Mapping { + for (path, text in contract.workflowDocuments) { + [path] = if (path == skillPath) + "\(text)\nRender the **Completion** layout from `references/wrong-file.md`." + else if (path == outputPath) + "\(text)\n## Completion\n" + else text + } + [wrongFilePath] = "# Wrong file\n\nThis document has no matching heading." +} + +result = if ( + contract.assertLayoutReferences.apply(documents) +) "negative fixture unexpectedly accepted a layout citation resolved against the wrong file" +else throw("generated workflow layout citation does not match a heading in `\(wrongFilePath)`") diff --git a/config/pkl/renderers/generation-contract-check.pkl b/config/pkl/renderers/generation-contract-check.pkl index 7e1726ce..2413bcd9 100644 --- a/config/pkl/renderers/generation-contract-check.pkl +++ b/config/pkl/renderers/generation-contract-check.pkl @@ -94,7 +94,7 @@ local brownfieldSkillDocuments = new Mapping { } local isDecisionInvokingWorkflowDocument = (path: String) -> - path.contains("/skills/sce-next-task/") || path.contains("/skills/sce-validate/") + path.contains("/skills/sce-next-task/") local decisionWorkflowText = (documents: Mapping, workflowSlug: String) -> new Listing { @@ -150,7 +150,6 @@ local requiredDecisionWorkflowTokens = new Listing { "Reuse a written ADR path" "returned `adr_path`" "On `blocked`" - "successful context synchronization" } local requiredHandoverSkillTokens = new Listing { @@ -221,7 +220,6 @@ local requiredPhaseReferencesBySkill = new Mapping { } ["sce-validate"] = new Listing { "references/validation.md" - "references/context-sync.md" "references/validation-report.md" "references/output.md" } @@ -256,19 +254,17 @@ local forbiddenWorkflowReferenceTokens = new Listing { "references/authoring-contract.yaml" "references/readiness-contract.yaml" "references/execution-contract.yaml" - "references/commit-contract.yaml" "references/implementation-gate.md" "references/plan-summary.md" "references/validation-result.md" - "references/sync-report.md" - "references/commit-message-style.md" } -/// Six cross-target workflow packages, with package-local phase references on -/// the four phase-based workflows, plus the decision package and retained -/// non-workflow assets. Stating the total as a literal makes an unintended -/// inventory change fail here instead of silently becoming the new expectation. -local expectedArtifactPathCount = 101 +/// Six cross-target workflow packages, with package-local phase references and +/// supporting documents on the four phase-based workflows, plus the decision +/// package and retained non-workflow assets. Stating the total as a literal makes +/// an unintended inventory change fail here instead of silently becoming the new +/// expectation. +local expectedArtifactPathCount = 107 local assertExactArtifactPaths = (actual: Mapping) -> if ( @@ -294,8 +290,8 @@ local assertDecisionWorkflowReferences = (documents: Mapping) -> ) && requiredDecisionWorkflowTokens.every((token) -> decisionWorkflowText.apply(documents, "sce-next-task").contains(token) - && decisionWorkflowText.apply(documents, "sce-validate").contains(token) ) + && decisionWorkflowText.apply(documents, "sce-next-task").contains("successful context synchronization") ) "generated decision invocation: synchronization-only" else throw("generated workflow document violates the synchronization-only sce-decision exception") @@ -324,6 +320,23 @@ local assertOpenCodeSkillPermissions = (agents) -> ) "OpenCode skill permissions: ordered and catalog-derived" else throw("OpenCode skill permissions must allow non-SCE skills, deny arbitrary SCE skills, and list only ordered catalog-owned workflows") +hidden openCodeExplicitSkillPermissions = (rendered: String) -> + new Listing { + for (line in rendered.split("\n")) { + when (line.contains("\"sce-") && line.endsWith("\": allow")) { + line.split("\"")[1] + } + } + } + +hidden assertOpenCodeSkillPermissionArtifacts = (rendered: String, artifacts: Mapping) -> + if ( + openCodeExplicitSkillPermissions.apply(rendered).every((permissionSlug) -> + artifacts.containsKey("config/.opencode/skills/\(permissionSlug)/SKILL.md") + ) + ) "OpenCode skill permissions: every explicit SCE allow names an emitted artifact" + else throw("OpenCode skill permission names a missing generated workflow artifact") + local assertOpenCodeDecisionPermission = (agents) -> let (codeAgent = agents["shared-context-code"].rendered) let (planAgent = agents["shared-context-plan"].rendered) @@ -457,6 +470,312 @@ local assertOptionalWorkflowManifest = (artifacts: Mapping) -> ) "generated optional-workflow manifest: matches the catalog's optional workflows" else throw("generated optional-workflow manifest does not match the catalog's optional workflows") +hidden layoutReferencesIn = (text: String) -> + Regex(#""" +Render the \*\*([^*]+)\*\* layout from `([^`]+)` +"""#).findMatchesIn(text) + +hidden packageReferencesIn = (text: String) -> + Regex(#""" +(references/[A-Za-z0-9._/-]+\.(?:md|yaml)) +"""#).findMatchesIn(text) + +hidden assertLayoutReferences = (documents: Mapping) -> + let (violations = documents.toMap().entries + .filter((entry) -> entry.key.endsWith("/SKILL.md") && !entry.key.contains("/skills/sce-decision/")) + .flatMap((entry) -> + let (path = entry.key) + layoutReferencesIn.apply(entry.value) + .flatMap((match) -> + let (referencePath = path.dropLast("SKILL.md".length) + match.groups[2].value) + if ( + !documents.containsKey(referencePath) + || !documents[referencePath].contains("## \(match.groups[1].value)") + ) + List(referencePath) + else List() + ) + )) + if (violations.isEmpty) + "generated output layouts: every citation resolves to a heading in its cited references document" + else throw("generated workflow layout citation does not match a heading in `\(violations[0])`") + +hidden assertPackageLocalReferences = (documents: Mapping) -> + if ( + documents.every((path, text) -> + let (packageRoot = if (path.contains("/references/")) + path.substring(0, path.lastIndexOf("/references/") + 1) + else + path.dropLast("SKILL.md".length)) + packageReferencesIn.apply(text).every((match) -> + documents.containsKey(packageRoot + match.groups[1].value) + ) + ) + ) "generated package-local references: every path exists" + else throw("generated package-local reference points to a missing document") + +hidden assertValidateForbiddenPaths = (artifacts: Mapping) -> + let (forbidden = new Listing { + "context-sync.md" + "sync-report.md" + "validation-result.md" + }) + if ( + artifacts.every((path, _) -> + !path.contains("/skills/sce-validate/references/") + || forbidden.every((filename) -> !path.endsWith("/" + filename)) + ) + ) "sce-validate package: forbidden reference files absent" + else throw("sce-validate must not generate context-sync.md, sync-report.md, or validation-result.md") + +hidden assertCommitForbiddenPaths = (artifacts: Mapping) -> + let (forbidden = new Listing { + "commit-contract.yaml" + "commit-contract.md" + }) + if ( + artifacts.every((path, _) -> + !path.contains("/skills/sce-commit/references/") + || forbidden.every((filename) -> !path.endsWith("/" + filename)) + ) + ) "sce-commit package: forbidden reference files absent" + else throw("sce-commit must not generate a commit-contract reference file") + +hidden assertAtomicCommitContent = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-commit/references/atomic-commit.md") + || ( + text.contains("`references/commit-message-style.md`") + && !text.contains("## Commit message rules") + && !text.contains("## Atomic commit result contract") + && !text.contains("commit-contract.yaml") + ) + ) + ) "sce-commit atomic reference: delegates message style and omits the result contract" + else throw("atomic-commit.md must delegate message rules to commit-message-style.md and omit the removed result contract") + +hidden assertNextTaskReportOwnership = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/sync-report.md") + || ( + let (outputPath = path.dropLast("sync-report.md".length) + "output.md") + documents.containsKey(outputPath) + && !documents[outputPath].contains("# Context Sync Report") + && !documents[outputPath].contains("## Synced") + && !documents[outputPath].contains("## No context change") + && !documents[outputPath].contains("## Blocked") + ) + ) + ) "sce-next-task report ownership: sync report is not duplicated in output.md" + else throw("sce-next-task output.md must not duplicate the context-sync report contract") + +hidden assertTargetNeutralReferences = (documents: Mapping) -> + let (opencodeReferences = new Mapping { + for (path, text in documents) { + when (path.startsWith("config/.opencode/") && path.contains("/skills/") && path.contains("/references/")) { + [path.substring(path.lastIndexOf("/skills/") + "/skills/".length, path.length)] = text + } + } + }) + if ( + opencodeReferences.every((relativePath, text) -> + documents["config/.claude/skills/" + relativePath] == text + && documents["config/.pi/skills/" + relativePath] == text + ) + ) "target-neutral references: Pi, Claude, and OpenCode bodies match" + else throw("target-neutral package references differ between Pi, Claude, and OpenCode") + +hidden assertNoStaleSyncDebtText = (artifacts: Mapping) -> + if ( + artifacts.every((_, text) -> + !text.contains("Nothing records the skipped synchronization, so it is lost once this session ends") + ) + ) "generated files: no stale synchronization-loss wording" + else throw("generated file contains the stale synchronization-loss wording") + +hidden assertPlanReviewSyncDebtRecovery = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/plan-review.md") + || ( + text.contains("do not attempt a reconstructed retry") + && text.contains("migrate the plan") + ) + ) + ) "sce-next-task plan-review reference: sync-debt recovery and legacy-migration wording present" + else throw("plan-review reference must state sync-debt recovery and legacy-migration-failure behavior") + +local forbiddenLegacyTaskRecordTokens = new Listing { + "Goal:" + "Boundaries (in/out of scope)" + "Verification notes" + "Implementation evidence" + "Verification evidence" + "Context synchronization handoff" +} + +local requiredCompactPlanTemplateTaskTokens = new Listing { + "Scope: In —" + "Done when:" + "Verify:" + "Context synchronization: pending" +} + +local requiredCompactPlanTemplateCompletionTokens = new Listing { + "Completed: {YYYY-MM-DD}" + "Files changed: {paths}" + "Result: {concise factual outcome, not a prose diff}" + "Context impact: {durable context this change affects, or none}" + "Context synchronization: pending | synced | blocked" +} + +hidden assertCompactPlanTemplateSchema = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-change-to-plan/references/plan-template.md") + || ( + requiredCompactPlanTemplateTaskTokens.every((token) -> text.contains(token)) + && requiredCompactPlanTemplateCompletionTokens.every((token) -> text.contains(token)) + && forbiddenLegacyTaskRecordTokens.every((token) -> !text.contains(token)) + ) + ) + ) "generated plan template: compact task/completion schema present, legacy fields absent" + else throw("generated plan-template.md must use the compact task and completion schema and must not reference the removed Goal, Boundaries (in/out of scope), Verification notes, Implementation evidence, Verification evidence, or Context synchronization handoff fields") + +local requiredNextTaskCompletionWritingTokens = new Listing { + "Record directly on the completed task:" + "`Files changed`" + "concise factual `Result`" + "`Context impact`" + "Set that task's `Context synchronization` field to `pending`" +} + +hidden assertNextTaskCompactCompletionWriting = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/task-execution.md") + || ( + requiredNextTaskCompletionWritingTokens.every((token) -> text.contains(token)) + && forbiddenLegacyTaskRecordTokens.every((token) -> !text.contains(token)) + ) + ) + ) "sce-next-task task-execution reference: execution facts recorded directly on the completed task" + else throw("sce-next-task task-execution.md must record execution facts directly on the completed task and must not construct a separate Implementation evidence, Verification evidence, or Context synchronization handoff record") + +local requiredPlanReviewCompletedRecordTokens = new Listing { + "no durable completed-task record (no `Files changed`," + "read directly from the plan by plan path and task ID" +} + +hidden assertPlanReviewReadsCompletedRecord = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/plan-review.md") + || ( + requiredPlanReviewCompletedRecordTokens.every((token) -> text.contains(token)) + && !text.contains("Context synchronization handoff") + ) + ) + ) "sce-next-task plan-review reference: sync-debt recovery reads the completed task record directly" + else throw("sce-next-task plan-review.md must recover synchronization debt by reading the completed task record directly from the plan by plan path and task ID, not a persisted Context synchronization handoff") + +local requiredContextSyncTaskRecordTokens = new Listing { + "completed task record" + "`Files changed`" + "`Result`" + "reported context impact" + "`Context synchronization blocker`" +} + +hidden assertContextSyncValidatesTaskRecord = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/context-sync.md") + || ( + requiredContextSyncTaskRecordTokens.every((token) -> text.contains(token)) + && !text.contains("Context synchronization handoff") + ) + ) + ) "sce-next-task context-sync reference: validates the completed task record, not a persisted handoff" + else throw("sce-next-task context-sync.md must validate the completed task record directly from the plan and must not reference a persisted Context synchronization handoff") + +hidden assertSyncDebtRecoveryBranch = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/SKILL.md") + || ( + text.contains("`sync_debt` ->") + && let (branch = text.drop(text.indexOf("`sync_debt` ->"))) + let (paragraph = if (branch.contains("\n\n")) branch.take(branch.indexOf("\n\n")) else branch) + paragraph.contains("references/context-sync.md") + && paragraph.contains("Task context synchronization phase") + && paragraph.indexOf("references/context-sync.md") < paragraph.indexOf("Task context synchronization phase") + ) + ) + ) "generated sce-next-task SKILL.md: sync-debt recovery cites context-sync.md before invoking the phase" + else throw("sce-next-task SKILL.md sync-debt recovery branch must cite references/context-sync.md before invoking the Task context synchronization phase") + +hidden assertPlanReviewAllTasksScope = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/references/plan-review.md") + || ( + text.contains("every completed task") + && !text.contains("earlier completed task") + ) + ) + ) "sce-next-task plan-review reference: synchronization-debt scan covers every completed task" + else throw("sce-next-task plan-review reference must state the synchronization-debt scan covers every completed task, with no earlier-completed-task position-relative wording remaining") + +hidden assertSyncDebtBlockedRouting = (documents: Mapping) -> + if ( + documents.every((path, text) -> + !path.endsWith("/skills/sce-next-task/SKILL.md") + || ( + text.contains("`sync_debt` ->") + && let (branch = text.drop(text.indexOf("`sync_debt` ->"))) + branch.contains("Branch on the outcome:") + && let (outcome = branch.drop(branch.indexOf("Branch on the outcome:"))) + let (matches = layoutReferencesIn.apply(outcome)) + !matches.isEmpty && matches[0].groups[1].value == "Context synchronization blocked" + ) + ) + ) "generated sce-next-task SKILL.md: sync-debt recovery routes its blocked outcome to the Context synchronization blocked layout" + else throw("sce-next-task SKILL.md sync-debt recovery blocked outcome must route to the Context synchronization blocked layout, not Review blocked") + +hidden assertValidationIsObservational = (documents: Mapping) -> + let (forbidden = new Listing { + "Repair application code during final validation" + "Modify application code during final validation" + "Modify tests during final validation" + "Delete scaffolding during final validation" + "Repair configuration during final validation" + }) + if ( + documents.every((path, text) -> + !path.contains("/skills/sce-validate/") + || forbidden.every((token) -> !text.contains(token)) + ) + ) "sce-validate validation: observational and non-repairing" + else throw("final validation must not instruct the agent to repair implementation") + +hidden assertValidateExcludesDecisionAndPlanSync = (documents: Mapping) -> + let (forbidden = new Listing { + "sce-decision" + "plan-context-sync" + "plan context sync" + "Plan context synchronization" + }) + if ( + documents.every((path, text) -> + !path.contains("/skills/sce-validate/") + || forbidden.every((token) -> !text.contains(token)) + ) + ) "sce-validate package: no sce-decision reference or plan-context-sync wording" + else throw("generated sce-validate document must not contain a sce-decision reference or plan-context-sync wording") + contractChecks { ["artifact-paths"] = assertExactArtifactPaths.apply(generatedArtifacts) ["optional-workflow-manifest"] = assertOptionalWorkflowManifest.apply(generatedArtifacts) @@ -466,6 +785,16 @@ contractChecks { ["phase-reference-contract"] = assertPhaseReferenceContract.apply(workflowDocuments) ["decision-invocation"] = assertDecisionWorkflowReferences.apply(compositeWorkflowDocuments) ["opencode-skill-permissions"] = assertOpenCodeSkillPermissions.apply(opencode.agents) + ["opencode-skill-permission-artifacts"] = + assertOpenCodeSkillPermissionArtifacts.apply( + opencode.agents["shared-context-plan"].rendered, + generatedArtifacts + ) + + "; " + + assertOpenCodeSkillPermissionArtifacts.apply( + opencode.agents["shared-context-code"].rendered, + generatedArtifacts + ) ["opencode-decision-permission"] = assertOpenCodeDecisionPermission.apply(opencode.agents) ["decision-package-paths"] = assertDecisionDocumentPaths.apply(decisionSkillDocuments) ["decision-package-content"] = assertDecisionContent.apply(decisionSkillDocuments) @@ -473,4 +802,22 @@ contractChecks { ["brownfield-package-content"] = assertBrownfieldContent.apply(brownfieldSkillDocuments) ["no-blank-line-runs"] = assertNoBlankLineRuns.apply(workflowDocuments) ["output-dedup"] = assertOutputNotDuplicated.apply(workflowDocuments) + ["layout-references"] = assertLayoutReferences.apply(workflowDocuments) + ["package-local-references"] = assertPackageLocalReferences.apply(workflowDocuments) + ["validate-forbidden-paths"] = assertValidateForbiddenPaths.apply(generatedArtifacts) + ["commit-forbidden-paths"] = assertCommitForbiddenPaths.apply(generatedArtifacts) + ["atomic-commit-content"] = assertAtomicCommitContent.apply(workflowDocuments) + ["next-task-report-ownership"] = assertNextTaskReportOwnership.apply(workflowDocuments) + ["target-neutral-references"] = assertTargetNeutralReferences.apply(workflowDocuments) + ["no-stale-sync-debt-text"] = assertNoStaleSyncDebtText.apply(generatedArtifacts) + ["plan-review-sync-debt-recovery"] = assertPlanReviewSyncDebtRecovery.apply(workflowDocuments) + ["compact-plan-template-schema"] = assertCompactPlanTemplateSchema.apply(workflowDocuments) + ["next-task-compact-completion-writing"] = assertNextTaskCompactCompletionWriting.apply(workflowDocuments) + ["plan-review-reads-completed-record"] = assertPlanReviewReadsCompletedRecord.apply(workflowDocuments) + ["context-sync-validates-task-record"] = assertContextSyncValidatesTaskRecord.apply(workflowDocuments) + ["sync-debt-recovery-branch"] = assertSyncDebtRecoveryBranch.apply(workflowDocuments) + ["plan-review-all-tasks-scope"] = assertPlanReviewAllTasksScope.apply(workflowDocuments) + ["sync-debt-blocked-routing"] = assertSyncDebtBlockedRouting.apply(workflowDocuments) + ["validation-observational"] = assertValidationIsObservational.apply(workflowDocuments) + ["validate-decision-sync-boundary"] = assertValidateExcludesDecisionAndPlanSync.apply(workflowDocuments) } diff --git a/config/pkl/renderers/metadata-coverage-check.pkl b/config/pkl/renderers/metadata-coverage-check.pkl index 660898d8..004948d6 100644 --- a/config/pkl/renderers/metadata-coverage-check.pkl +++ b/config/pkl/renderers/metadata-coverage-check.pkl @@ -20,9 +20,8 @@ local expectedWorkflowSkillByCommand = new Mapping { } } -/// Phase-based workflows render package-local phase references. Phase-free -/// workflows retain only output.md, and the standalone decision package retains -/// only its ADR template. Exact coverage rejects stale or missing references. +/// Workflow packages render the package-local references owned by each +/// canonical workflow. Exact coverage rejects stale or missing references. local phaseReferencePathsByWorkflow = new Mapping { ["change-to-plan"] = new Listing { "references/context-load.md" @@ -34,19 +33,23 @@ local phaseReferencePathsByWorkflow = new Mapping { "references/plan-review.md" "references/task-execution.md" "references/context-sync.md" + "references/sync-report.md" "references/output.md" } ["validate"] = new Listing { "references/validation.md" - "references/context-sync.md" "references/validation-report.md" "references/output.md" } ["commit"] = new Listing { "references/atomic-commit.md" + "references/commit-message-style.md" + "references/output.md" + } + ["handover"] = new Listing { + "references/handover-template.md" "references/output.md" } - ["handover"] = new Listing { "references/output.md" } ["brownfield"] = new Listing { "references/output.md" } } diff --git a/config/pkl/renderers/workflow-composite.pkl b/config/pkl/renderers/workflow-composite.pkl index 7a3e3758..02d1fbf8 100644 --- a/config/pkl/renderers/workflow-composite.pkl +++ b/config/pkl/renderers/workflow-composite.pkl @@ -119,8 +119,8 @@ local renderSkill = (workflow: CompositeWorkflow, extraFrontmatterLines: String) Keep phase results as internal state and continue immediately whenever the canonical workflow says to continue. Stop only at a user wait or terminal branch. - Approval, clarification, revision, failed-validation repair, and bootstrap waits - resume this same skill in the same session. Never expose an internal phase result + Any workflow-defined user wait resumes this same skill in the same session. + Never expose an internal phase result as the workflow's final response. \(model.helperSkillCompositionRule) diff --git a/context/architecture.md b/context/architecture.md index f6246885..900b6352 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -11,13 +11,13 @@ Current location for canonical workflow content primitives: - `config/pkl/base/workflow-content.pkl` (shared workflow command and self-contained skill-package document model, including structured composite sources with optional canonical `compositeSkillBody` plus deterministic `referenceDocuments`, alongside the typed package/composite rendering primitives; workflow-specific bodies and package-local documents remain in the canonical workflow modules rather than being catalogued here) - `config/pkl/base/workflow-catalog.pkl` (typed six-workflow catalog owning command and skill slugs, titles, descriptions, argument hints, OpenCode routing roles, Claude allowed-tool metadata, and the per-workflow `optional` flag that defaults to `false` and is `true` only for `brownfield`) - `config/pkl/base/optional-workflow-manifest.pkl` (install-time projection of the catalog's optional records into the generated `config/optional-workflows.json` manifest — `schemaVersion` plus one entry per optional workflow carrying `id`, `title`, `description`, `commandSlug`, and `skillSlug`. Optionality never affects generation: all six workflows are still generated for all three targets, so the manifest exists solely to carry optional-workflow identity out of Pkl for install-time and doctor-time consumers) -- `config/pkl/base/decision-skill.pkl` (canonical standalone `sce-decision` package outside the workflow catalog; renders its decision gate, one-record and immutable-accepted-ADR rules, deterministic written-or-blocked handoff, and `references/adr-template.md` for all three targets without creating a command or prompt) -- `config/pkl/base/workflow-change-to-plan.pkl` (canonical `/change-to-plan` package registering the target-neutral `SKILL.md` body plus `context-load.md`, `plan-authoring.md`, `plan-template.md`, and `output.md` package references) -- `config/pkl/base/workflow-next-task.pkl` (canonical `/next-task` package registering the target-neutral `SKILL.md` body plus `plan-review.md`, `task-execution.md`, `context-sync.md`, and `output.md` package references) -- `config/pkl/base/workflow-validate.pkl` (canonical `/validate` package registering the target-neutral `SKILL.md` body plus `validation.md`, `context-sync.md`, `validation-report.md`, and `output.md` package references) -- `config/pkl/base/workflow-commit.pkl` (canonical `/commit` package registering the target-neutral `SKILL.md` body plus `atomic-commit.md` and `output.md` package references; both regular and bypass paths read the same phase reference only after their pre-phase gate) -- `config/pkl/base/workflow-context-sync.pkl` (one role-parameterized source that renders exact, self-contained task and plan context-sync skills in named semantic section order, gives each lifecycle role its own composite step heading scale, renders their synced, no-context-change, and blocked report layouts through shared named section renderers from typed role data, and exposes both skills as mode-aware structured phases for next-task and validate composition) -- `config/pkl/base/workflow-handover.pkl` (canonical `/handover` package with the self-contained, phase-free `sce-handover` skill; its structured composite source has no phases and exposes one mode-invariant `references/output.md` as its sole output document, so composite rendering differs from its package-mode form only by the generic composite preamble the shared renderer supplies) +- `config/pkl/base/decision-skill.pkl` (canonical standalone `sce-decision` package outside the workflow catalog; renders its decision gate, one-record and immutable-ADR rules, active-only reuse and creation-time status semantics, deterministic written/not-qualified/skipped/blocked handoff, and `references/adr-template.md` for all three targets without creating a command or prompt) +- `config/pkl/base/workflow-change-to-plan.pkl` (canonical `/change-to-plan` package registering the target-neutral `SKILL.md` body plus `context-load.md`, `plan-authoring.md`, `plan-template.md`, and `output.md` package references; the plan template persists task synchronization lifecycle state and retains the plan format needed by existing plans) +- `config/pkl/base/workflow-next-task.pkl` (canonical `/next-task` package registering the target-neutral `SKILL.md` body plus `plan-review.md`, `task-execution.md`, `context-sync.md`, `sync-report.md`, and `output.md` package references; review gates new tasks on synced lifecycle state, execution records pending before task synchronization, and the execution reference defines an explicit Git-baseline-relative handoff consumed by task synchronization) +- `config/pkl/base/workflow-validate.pkl` (canonical `/validate` package registering the target-neutral `SKILL.md` body plus `validation.md`, `validation-report.md`, and `output.md` package references; `validation.md` carries the validation steps plus the validation result contract and keeps final validation observational by recording leftover debug/temp artifacts as failure evidence rather than deleting or repairing them; `/validate` does not invoke plan-level context synchronization, and `output.md` holds the `Completion` layout) +- `config/pkl/base/workflow-commit.pkl` (canonical `/commit` package registering the target-neutral `SKILL.md` body plus `atomic-commit.md`, `commit-message-style.md`, and `output.md` package references; `atomic-commit.md` owns staged-diff procedure, internal result branching, and commit boundaries, `commit-message-style.md` owns message wording, and both regular and bypass paths read the phase reference only after their pre-phase gate) +- `config/pkl/base/workflow-context-sync.pkl` (one role-parameterized source that renders exact, self-contained task and retained plan context-sync skills in named semantic section order, gives each lifecycle role its own composite step heading scale, renders their synced, no-context-change, and blocked report layouts through shared named section renderers from typed role data, and exposes task synchronization to `/next-task` while retaining the plan role without composing it into `/validate`) +- `config/pkl/base/workflow-handover.pkl` (canonical `/handover` package with the self-contained, phase-free `sce-handover` skill; its structured composite source has no phases and exposes package-local `references/handover-template.md` plus mode-invariant `references/output.md`, so composite rendering differs from its package-mode form only by the generic composite preamble the shared renderer supplies) - `config/pkl/base/workflow-brownfield.pkl` (canonical `/brownfield` package with the self-contained, phase-free `sce-brownfield` skill; like `workflow-handover.pkl` its structured composite source has no phases and exposes one `references/output.md` as its sole output document, and its preamble is a semantic reference so composite rendering keeps the workflow's cold-start and gap-fill scope statement the shared renderer has no generic equivalent for. It is the sixth catalog-registered workflow, composed by `workflow-composite.pkl` and generated for all three targets) - `config/pkl/base/opencode.pkl` - `config/pkl/base/sce-config-schema.pkl` @@ -26,7 +26,7 @@ Current target renderer helper modules: - `config/pkl/renderers/opencode-content.pkl` - `config/pkl/renderers/claude-content.pkl` -- `config/pkl/renderers/workflow-composite.pkl` (target-neutral composition of six workflow-level skills and deterministic package-local references; phase-based packages consume named phase, persisted-document, and output documents from their canonical workflow modules, phase-free packages retain only `references/output.md`, and target differences remain frontmatter-only) +- `config/pkl/renderers/workflow-composite.pkl` (target-neutral composition of six workflow-level skills and deterministic package-local references; phase-based packages consume named phase, persisted-document, and output documents from their canonical workflow modules, phase-free packages retain output layouts and may expose a persisted-format reference such as handover's template, and target differences remain frontmatter-only) - `config/pkl/renderers/pi-content.pkl` - `config/pkl/renderers/common.pkl` - `config/pkl/renderers/opencode-metadata.pkl` @@ -44,19 +44,19 @@ The scaffold provides stable canonical content-unit identifiers and reusable tar Renderer modules apply target-specific metadata/frontmatter rules while reusing canonical content bodies: -- All three renderers consume the six canonical workflow packages as behavior sources and emit the same six command-routed workflow packages: `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`. Each renderer also emits the standalone internal `sce-decision` package with `SKILL.md` plus `references/adr-template.md`; it stays outside workflow composition and has no command or prompt. For the four phase-based workflows, `workflow-composite.pkl` renders one `SKILL.md` that owns input parsing, phase order, branching, waits, and same-session resume, plus package-local references for each phase and persisted-document format. The applicable reference must be read before its phase runs. `references/output.md` remains the sole owner of human-visible gates and terminal layouts. Phase statuses remain internal, commands and prompts still invoke exactly one workflow skill, and SCE sibling handoffs remain limited to the successful synchronization gate's bounded `sce-decision` invocation; relevant non-SCE skills may help within the active step and must return control without weakening its invariants. `sce-handover` and `sce-brownfield` are phase-free and retain their two-file package shape. OpenCode, Claude, and Pi render identical package-relative inventories and document bodies for each workflow, apart from supported target frontmatter. +- All three renderers consume the six canonical workflow packages as behavior sources and emit the same six command-routed workflow packages: `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`. Each renderer also emits the standalone internal `sce-decision` package with `SKILL.md` plus `references/adr-template.md`; it stays outside workflow composition and has no command or prompt. For the phase-based workflows, `workflow-composite.pkl` renders one `SKILL.md` that owns input parsing, phase order, branching, waits, and same-session resume, plus package-local references for each applicable phase and persisted-document format. The applicable reference must be read before its phase runs. `references/output.md` remains the sole owner of human-visible gates and terminal layouts. Phase statuses remain internal, commands and prompts still invoke exactly one workflow skill, and SCE sibling handoffs remain limited to the successful task-synchronization gate's bounded `sce-decision` invocation; `/validate` reports validation directly without a plan-synchronization handoff. Relevant non-SCE skills may help within the active step and must return control without weakening its invariants. `sce-handover` and `sce-brownfield` are phase-free; handover has a package-local persisted-format template in addition to `SKILL.md` and `references/output.md`, while brownfield retains its two-file shape. OpenCode, Claude, and Pi render identical package-relative inventories and document bodies for each workflow, apart from supported target frontmatter. - Per-target differences are confined to frontmatter and the surrounding non-workflow outputs. The manual OpenCode renderer adds `agent`, `entry-skill`, and a one-entry `skills` list to command frontmatter, adds `compatibility: opencode` to package entrypoints, and emits two thin routing agents. Each OpenCode agent allows ordinary non-SCE skills by default, denies the `sce-*` wildcard, and then allows only its catalog-derived owned workflow skills; only the Code agent additionally allows `sce-decision` for the synchronization exception. The Claude renderer adds `compatibility: claude` plus command `allowed-tools:` and emits no agents; Claude settings and the hook helper remain separate retained outputs. The Pi renderer adds no frontmatter to either prompts or skills. -- Pi renderer consumes the same shared workflow composition as OpenCode and Claude. It emits exactly six thin prompts to `config/.pi/prompts/{slug}.md`, each routing to exactly one workflow skill, four phase-reference workflow packages plus two phase-free two-file packages under `config/.pi/skills/{slug}/`, and the standalone `sce-decision` package beside them. Pi prompts and skills carry no target-specific frontmatter beyond the shared description and argument hint, so Pi passes the empty extra-frontmatter string to both package render paths. It emits no Pi agent-role prompts. Pi has no settings/plugin manifest; runtime integration remains the project-local extension emitted verbatim from `config/lib/pi-plugin/sce-pi-extension.ts` to `config/.pi/extensions/sce/index.ts` (auto-discovered by Pi, no registration manifest; see `context/sce/pi-extension-runtime.md`). -- Workflow composition itself is shared rather than per target. `config/pkl/renderers/workflow-composite.pkl` owns the six composite workflow definitions and assembles their references, while each composite looks up its typed identity in `config/pkl/base/workflow-catalog.pkl` and migrated workflow modules supply canonical phase, persisted-document, and output documents. Every workflow supplies a required `StructuredCompositeSource`, so commands, phase documents, persisted-document formats, and output references render in package or composite mode before Markdown assembly. The renderer has no nullable legacy adapter, frontmatter stripping, or prose-wide replacement chain. Its `renderSkill`, `renderCommand`, and `skillDocuments` entrypoints take a newline-terminated `extraFrontmatterLines` string carrying only the frontmatter a target supports; a target that adds none passes the empty string. `renderSkill` assembles the document as an ordered section list — preamble (purpose, user-visible output, and the composite control-flow rules, all stated before the workflow's `## Input`), then the workflow body, then the phase appendix and any persisted-document formats, each emitted only when its listing is non-empty. Claude passes `compatibility: claude` for skills and a catalog-derived `allowed-tools` line for commands. The `renderSkill` preamble also carries the no-improvisation rule that every generated workflow `SKILL.md` states on every target: the executing agent follows the canonical workflow's steps, gates, and stops exactly as written and never invents, skips, reorders, or merges a step, and its user-visible output is limited to the `references/output.md` layouts with no invented layout and no added preamble, commentary, summary, or extra section. The rule is prose instruction only; the generation contract checks assert paths and metadata, not agent behavior. +- Pi renderer consumes the same shared workflow composition as OpenCode and Claude. It emits exactly six thin prompts to `config/.pi/prompts/{slug}.md`, each routing to exactly one workflow skill, four phase-based workflow packages with package-local phase and supporting references plus the phase-free handover and brownfield packages under `config/.pi/skills/{slug}/` (handover also has its persisted-format template), and the standalone `sce-decision` package beside them. Pi prompts and skills carry no target-specific frontmatter beyond the shared description and argument hint, so Pi passes the empty extra-frontmatter string to both package render paths. It emits no Pi agent-role prompts. Pi has no settings/plugin manifest; runtime integration remains the project-local extension emitted verbatim from `config/lib/pi-plugin/sce-pi-extension.ts` to `config/.pi/extensions/sce/index.ts` (auto-discovered by Pi, no registration manifest; see `context/sce/pi-extension-runtime.md`). +- Workflow composition itself is shared rather than per target. `config/pkl/renderers/workflow-composite.pkl` owns the six composite workflow definitions and assembles their references, while each composite looks up its typed identity in `config/pkl/base/workflow-catalog.pkl` and migrated workflow modules supply canonical phase, persisted-document, and output documents. Every workflow supplies a required `StructuredCompositeSource`, so commands, phase documents, persisted-document formats, and output references render in package or composite mode before Markdown assembly. The renderer has no nullable legacy adapter, frontmatter stripping, or prose-wide replacement chain. Its `renderSkill`, `renderCommand`, and `skillDocuments` entrypoints take a newline-terminated `extraFrontmatterLines` string carrying only the frontmatter a target supports; a target that adds none passes the empty string. `renderSkill` assembles the document as an ordered section list — preamble (purpose, user-visible output, and the composite control-flow rules, all stated before the workflow's `## Input`), then the workflow body, then the phase appendix and any persisted-document formats, each emitted only when its listing is non-empty. Claude passes `compatibility: claude` for skills and a catalog-derived `allowed-tools` line for commands. The `renderSkill` preamble also carries the no-improvisation rule that every generated workflow `SKILL.md` states on every target: the executing agent follows the canonical workflow's steps, gates, and stops exactly as written and never invents, skips, reorders, or merges a step, and its user-visible output is limited to the `references/output.md` layouts with no invented layout and no added preamble, commentary, summary, or extra section. Its generic control-flow wording says that any workflow-defined user wait resumes the same skill in the same session; workflow-specific wait semantics remain in the workflow that owns them. The rule is prose instruction only; the generation contract checks assert paths and metadata, not agent behavior. - Shared renderer document types and OpenCode plugin-registration helpers live in `config/pkl/renderers/common.pkl`. - The canonical OpenCode plugin-registration source for generated SCE plugins lives in `config/pkl/base/opencode.pkl`; `config/pkl/renderers/common.pkl` re-exports the shared plugin list and JSON-ready paths for OpenCode renderers, and the current generated registration scope is limited to SCE-managed plugins emitted by this repo (`sce-bash-policy` and `sce-agent-trace`). - Target renderers remain responsible for formatting target-supported metadata. OpenCode metadata owns thin-agent presentation and compatibility while deriving the ordered permission blocks — non-SCE wildcard allow, `sce-*` wildcard deny, then catalog-owned workflow allows — from catalog role assignments; OpenCode command routing derives the same role and skill identity from the catalog. Claude metadata derives command tools from catalog records. Pi has no metadata module because it adds no target-specific frontmatter. -- `config/pkl/renderers/metadata-coverage-check.pkl` derives commands and exact package-relative workflow-document expectations from the typed catalog and the four phase-reference inventories, adds the unchanged phase-free and decision-package expectations, verifies every command's one-to-one workflow-skill route for all three targets, and forces every rendered document and target metadata lookup to evaluate. -- `config/pkl/renderers/generation-contract-check.pkl` independently derives the complete expected artifact paths from those target document inventories plus explicitly retained non-workflow assets, compares them with `generate.pkl`'s `output.files`, and requires exactly 101 paths — stated as a literal `expectedArtifactPathCount` inside the same assertion so an unintended inventory change fails rather than redefining the expectation. It asserts the generated `config/optional-workflows.json` against the catalog (`optional-workflow-manifest`): every optional workflow appears with its catalog title and both slugs, no core workflow id appears, and `schemaVersion` is present. It also verifies that every required phase reference exists and is cited by its owning `SKILL.md`, scans generated workflow entrypoint `SKILL.md` documents for stale phase-skill slugs and unresolved package-local reference tokens while allowing package-local reference prose to mention its own persisted-format history, asserts the shared non-SCE helper-composition rule and SCE-scoped workflow prohibitions on every generated workflow skill, asserts the exact cross-target `sce-decision` paths plus its required gate, status, immutability, handoff, and ADR-template content, permits `sce-decision` references only in `sce-next-task` and `sce-validate`, verifies the exact catalog-derived OpenCode skill permission order and Code-only OpenCode decision permission, asserts the generated `sce-handover` `SKILL.md` covers both writer- and loader-mode content on all three targets, asserts the generated `sce-brownfield` `SKILL.md` still carries the bootstrap gate, documentation-discovery sweep, no-network rule, sub-`50` blocking threshold, always-disclosed contradiction contract, and additive-vs-`rebuild` write rule on all three targets, rejects two or more consecutive blank lines in generated workflow entrypoint `SKILL.md` documents (`no-blank-line-runs`), and rejects any generated `SKILL.md` that reproduces one of its sibling `references/output.md` fenced layouts verbatim (`output-dedup`, matched fence markers included). Checked-in negative fixtures prove that deterministic extra and missing artifacts, a forbidden workflow reference, missing helper composition, unscoped prohibitions, arbitrary or misordered OpenCode permissions, a blank-line run, and a duplicated output layout each fail the contract. +- `config/pkl/renderers/metadata-coverage-check.pkl` derives commands and exact package-relative workflow-document expectations from the typed catalog and the four workflow-document inventories, adds the unchanged phase-free and decision-package expectations, verifies every command's one-to-one workflow-skill route for all three targets, and forces every rendered document and target metadata lookup to evaluate. +- `config/pkl/renderers/generation-contract-check.pkl` independently derives the complete expected artifact paths from those target document inventories plus explicitly retained non-workflow assets, compares them with `generate.pkl`'s `output.files`, and requires the exact path count declared by the current generation contract — stated as a literal `expectedArtifactPathCount` inside the same assertion so an unintended inventory change fails rather than redefining the expectation. It asserts the generated `config/optional-workflows.json` against the catalog (`optional-workflow-manifest`): every optional workflow appears with its catalog title and both slugs, no core workflow id appears, and `schemaVersion` is present. It also verifies that every required phase reference exists and is cited by its owning `SKILL.md`, scans generated workflow entrypoint `SKILL.md` documents for stale phase-skill slugs and unresolved package-local reference tokens while allowing package-local reference prose to mention its own persisted-format history, asserts the shared non-SCE helper-composition rule and SCE-scoped workflow prohibitions on every generated workflow skill, asserts the exact cross-target `sce-decision` paths plus its required gate, status, immutability, handoff, and ADR-template content, permits `sce-decision` references only in `sce-next-task`, verifies the exact catalog-derived OpenCode skill permission order and Code-only OpenCode decision permission, asserts every explicit `sce-*` allow names an emitted OpenCode skill artifact, asserts the generated `sce-handover` `SKILL.md` covers both writer- and loader-mode content on all three targets, asserts the generated `sce-brownfield` `SKILL.md` still carries the bootstrap gate, documentation-discovery sweep, no-network rule, sub-`50` blocking threshold, always-disclosed contradiction contract, and additive-vs-`rebuild` write rule on all three targets, rejects two or more consecutive blank lines in generated workflow entrypoint `SKILL.md` documents (`no-blank-line-runs`), and rejects any generated `SKILL.md` that reproduces one of its sibling `references/output.md` fenced layouts verbatim (`output-dedup`, matched fence markers included), plus nineteen semantic checks for layout-heading resolution, package-local path existence, forbidden validate/commit files, consolidated atomic-commit content, next-task report ownership, cross-target reference parity, stale synchronization wording, observational validation, OpenCode permission-artifact integrity, plan-review sync-debt-recovery reference wording (`plan-review-sync-debt-recovery`, asserting the generated `sce-next-task/references/plan-review.md` states both the sync-debt recovery and legacy-migration-failure behavior), the compact completed-task record model (four checks replacing the removed `handoff-identity-fields` persisted-handoff check: `compact-plan-template-schema`, asserting the generated plan-template's new-task and completion examples use the compact `Scope`/`Done when`/`Verify`/`Result`/`Files changed`/`Context impact`/`Context synchronization` fields and name none of the removed `Goal`/`Boundaries (in/out of scope)`/`Verification notes`/`Implementation evidence`/`Verification evidence`/`Context synchronization handoff` fields; `next-task-compact-completion-writing`, asserting `task-execution.md` records execution facts directly on the completed task with no separate handoff/evidence construction; `plan-review-reads-completed-record`, asserting `plan-review.md`'s sync-debt recovery reads the completed task record directly by plan path and task ID rather than a persisted handoff; and `context-sync-validates-task-record`, asserting `context-sync.md` validates the completed task record rather than a persisted handoff), the `/next-task` sync-debt-recovery branch's reference-before-invocation ordering (`sync-debt-recovery-branch`, asserting its citation of `references/context-sync.md` precedes any instruction to run the Task context synchronization phase), the synchronization-debt scan's all-completed-task scope (`plan-review-all-tasks-scope`, asserting `plan-review.md` covers every completed task with no surviving position-relative wording), the sync-debt-recovery branch's blocked-outcome layout routing (`sync-debt-blocked-routing`, asserting its `blocked` branch cites the **Context synchronization blocked** layout rather than **Review blocked**), and the `sce-validate` decision/plan-sync exclusion (`validate-decision-sync-boundary`, asserting no generated `sce-validate` document contains a `sce-decision` reference or plan-context-sync wording). Checked-in negative fixtures prove the existing and nineteen semantic contract failures. - OpenCode, Claude, and Pi renderers expose command documents plus flattened `{skill slug}/{package-relative path}` skill documents consumed by `config/pkl/generate.pkl`; every target's flattened inventory contains `SKILL.md` and `references/output.md` for each workflow slug plus `sce-decision/SKILL.md` and `sce-decision/references/adr-template.md`. -- `config/pkl/generate.pkl` emits deterministic `output.files` mappings for all authored generated targets: OpenCode's six workflow commands, four phase-reference workflow packages, two phase-free workflow packages, standalone two-file decision package, and two thin routing agents; Claude's six thin commands, the same workflow-package inventories, and standalone decision package with no agents; Claude project settings and hook helper; shared bash-policy preset assets; OpenCode plugin entrypoints (`sce-bash-policy.ts` and `sce-agent-trace.ts`); generated OpenCode `opencode.json`; the Pi target tree (six thin workflow prompts, the same four phase-reference and two phase-free workflow packages, the standalone two-file decision package, and the extension emitted verbatim from `config/lib/pi-plugin/sce-pi-extension.ts`); the generated `sce/config.json` schema artifact; and the optional-workflow manifest at `config/optional-workflows.json`. The removed `config/automated/.opencode` profile has no generator ownership or output mappings. +- `config/pkl/generate.pkl` emits deterministic `output.files` mappings for all authored generated targets: OpenCode's six workflow commands, four phase-based workflow packages with package-local phase and supporting references, and two phase-free workflow packages (handover also has its persisted-format template), standalone two-file decision package, and two thin routing agents; Claude's six thin commands, the same workflow-package inventories, and standalone decision package with no agents; Claude project settings and hook helper; shared bash-policy preset assets; OpenCode plugin entrypoints (`sce-bash-policy.ts` and `sce-agent-trace.ts`); generated OpenCode `opencode.json`; the Pi target tree (six thin workflow prompts, the same four phase-based packages with package-local references and two phase-free workflow packages, with handover's persisted-format template, the standalone two-file decision package, and the extension emitted verbatim from `config/lib/pi-plugin/sce-pi-extension.ts`); the generated `sce/config.json` schema artifact; and the optional-workflow manifest at `config/optional-workflows.json`. The removed `config/automated/.opencode` profile has no generator ownership or output mappings. - Generated-file warning markers are not injected by the generator: Markdown outputs render deterministic frontmatter + body, and shared library outputs are emitted without a leading generated warning header. -- `config/pkl/check-generated.sh` is intentionally dev-shell scoped (`nix develop -c ...`): it requires `IN_NIX_SHELL`, rejects the repository-local `config/pkl/rendered` evaluation artifact before generation, rejects committed target trees, the generated SCE schema, and `cli/assets/generated`, evaluates exact metadata and generation contracts, confirms the extra/missing/reference/blank-line-run/output-dedup negative fixtures fail with their contract diagnostics, then delegates two-pass generation, input checks, and payload inventories to `scripts/produce-cli-generated-input.sh`. It projects the producer inventory only to preserve the established report digest path format; it does not rehash generated files. Required-path checks remain fast surface diagnostics, the Pkl contract owns exact complete-path coverage, and forbidden-output checks reject removed generator surfaces. +- `config/pkl/check-generated.sh` is intentionally dev-shell scoped (`nix develop -c ...`): it requires `IN_NIX_SHELL`, rejects the repository-local `config/pkl/rendered` evaluation artifact before generation, rejects committed target trees, the generated SCE schema, and `cli/assets/generated`, evaluates exact metadata and generation contracts, confirms the existing and nineteen semantic negative fixtures fail with their contract diagnostics, then delegates two-pass generation, input checks, and payload inventories to `scripts/produce-cli-generated-input.sh`. It projects the producer inventory only to preserve the established report digest path format; it does not rehash generated files. Required-path checks remain fast surface diagnostics, the Pkl contract owns exact complete-path coverage, and forbidden-output checks reject removed generator surfaces. Generated authored classes: @@ -194,7 +194,7 @@ Shared Context Plan and Shared Context Code remain separate architectural roles. - `/change-to-plan` and `/next-task` remain separate command entrypoints aligned to those roles. - Reuse is handled through shared canonical guidance blocks and skill-owned phase contracts, not by collapsing both roles into one agent. - OpenCode agents are thin routing surfaces rather than behavior owners: Plan routes to `/change-to-plan`; Code routes to `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield`. Claude and Pi have no generated agents. Workflow commands and self-contained skill packages are the sole behavior owners. -- The canonical `/change-to-plan` workflow sequences `sce-context-load` and `sce-plan-authoring`; `/next-task` sequences `sce-plan-review`, `sce-task-execution`, and `sce-task-context-sync`; `/validate` sequences `sce-validation` and validated-only `sce-plan-context-sync`; `/commit` sequences around `sce-atomic-commit`; `/handover` and `/brownfield` have no sibling phases at all — their single `sce-handover` and `sce-brownfield` skills own their whole routing directly. Those phase modules are canonical authoring source; no target generates them as packages. -- Every target embeds those same phase boundaries inside `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`, so no generated command or prompt invokes a phase or sibling SCE package. Workflow skills may use relevant non-SCE helpers inside the active step, but the helper returns control to that step; the only SCE sibling invocation remains the successful task/plan synchronization decision gate's bounded `sce-decision` call. +- The canonical `/change-to-plan` workflow sequences `sce-context-load` and `sce-plan-authoring`; `/next-task` sequences `sce-plan-review`, `sce-task-execution`, and `sce-task-context-sync`; `/validate` runs `sce-validation` only and reports its Validation Report; `/commit` sequences around `sce-atomic-commit`; `/handover` and `/brownfield` have no sibling phases at all — their single `sce-handover` and `sce-brownfield` skills own their whole routing directly. Those phase modules are canonical authoring source; no target generates them as packages. +- Every target embeds those same phase boundaries inside `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`, so no generated command or prompt invokes a phase or sibling SCE package. Workflow skills may use relevant non-SCE helpers inside the active step, but the helper returns control to that step; the only SCE sibling invocation remains the successful task-synchronization decision gate's bounded `sce-decision` call. - OpenCode, Claude, and Pi all generate `/handover` routed to exactly `sce-handover` (see [Handover workflow](sce/handover-workflow.md)) and `/brownfield` routed to exactly `sce-brownfield` (see [Brownfield workflow](sce/brownfield-workflow.md)); the automated OpenCode profile is removed. -- `/brownfield` is the only workflow outside the task and plan synchronization phases authorized to write durable `context/`, under its own additive-by-default boundary; see [Context workflow rules](sce/context-workflow-rules.md). +- `/brownfield` is the only workflow outside the task synchronization phase authorized to write durable `context/`, under its own additive-by-default boundary; see [Context workflow rules](sce/context-workflow-rules.md). diff --git a/context/context-map.md b/context/context-map.md index 29b10df1..cb90496c 100644 --- a/context/context-map.md +++ b/context/context-map.md @@ -30,14 +30,14 @@ Feature/domain context: - `context/sce/cli-shell-completion-contract.md` (implemented `sce completion` contract for deterministic Bash/Zsh/Fish completion script generation) - `context/sce/claude-raw-hook-capture.md` (removed feature: the former `sce hooks claude-capture` raw-capture route and its supporting types, replaced by the active `diff-trace` and `conversation-trace` intakes) - `context/sce/cli-observability-contract.md` (implemented config-backed runtime observability contract for the flat logging config-file shape with `log_dir` / `SCE_LOG_DIR` env-over-config-over-`/sce/logs` fallback, append-only local-date/session log file routing with a one-time complete-record `-v2.log` fallback on primary open/append/flush failure, creation-triggered retention of direct regular `*.log` files to 10 entries, reliable producer-native diff-trace/conversation-trace session routing and hook-specific non-duplicated Agent Trace DB-open error events, deterministic session filename sanitization, concrete logger/telemetry runtime behavior plus logger and object-safe telemetry trait boundaries, AppContext observability wiring, generic `RunOutcome` final rendering, runtime-classified repeated telemetry action protection, operator-facing `sce config show` observability reporting, and the trimmed `sce config validate` status-only validation surface) -- `context/sce/shared-context-code-workflow.md` (canonical `/next-task` and `/validate` lifecycles, package-local phase references with single-skill control flow, and the sole synchronization-scoped `sce-decision` sibling invocation with ADR reuse/blocker propagation) +- `context/sce/shared-context-code-workflow.md` (canonical `/next-task` task-synchronization lifecycle and validation-only `/validate` lifecycle, package-local phase references with single-skill control flow, and the task-synchronization-scoped `sce-decision` sibling invocation with ADR reuse/blocker propagation) - `context/sce/shared-context-plan-workflow.md` (canonical `/change-to-plan` workflow, package-local context-load/plan-authoring/template references, clarification/readiness gate contract, and one-task/one-atomic-commit task slicing) -- [Context workflow rules](sce/context-workflow-rules.md) (canonical bootstrap, ongoing context maintenance, task synchronization, plan synchronization, hygiene, discoverability, and feature-existence rules) -- `context/sce/plan-code-overlap-map.md` (overlap matrix for thin OpenCode Plan/Code routing agents and workflow phase ownership; its original three-workflow inventory predates the generated `/commit` package) -- `context/sce/dedup-ownership-table.md` (canonical owner-vs-consumer boundaries for workflow packages, canonical phase modules, the shared sync skeleton, and thin OpenCode agents; its original three-workflow/seven-skill inventory predates generated `/commit` and `sce-atomic-commit`) -- [Atomic commit workflow](sce/atomic-commit-workflow.md) (`/commit` regular proposal-only mode vs `oneshot`/`skip` bypass mode, staged-truth and plan-citation rules, and the cross-target `sce-commit` package with package-local atomic-commit instructions) +- [Context workflow rules](sce/context-workflow-rules.md) (canonical bootstrap, ongoing context maintenance, task synchronization, hygiene, discoverability, and feature-existence rules) +- `context/sce/plan-code-overlap-map.md` (overlap matrix for thin OpenCode Plan/Code routing agents and six workflow packages, including task-only context synchronization ownership) +- `context/sce/dedup-ownership-table.md` (canonical owner-vs-consumer boundaries for six workflow packages, canonical phase modules, the shared synchronization skeleton, and thin OpenCode agents) +- [Atomic commit workflow](sce/atomic-commit-workflow.md) (`/commit` regular proposal-only mode vs `oneshot`/`skip` bypass mode, staged-truth and plan-citation rules, and the cross-target `sce-commit` package with package-local atomic procedure, message-style, and output references) - [Brownfield workflow](sce/brownfield-workflow.md) (`/brownfield`, the sixth canonical SCE workflow, generated for OpenCode, Claude, and Pi from `config/pkl/base/workflow-brownfield.pkl`: the `[rebuild] [path ...]` argument contract, bootstrap gate, local-only evidence priority order with documentation sweep and three-month history floor, the `1`–`100` confidence model with its sub-`50` blocking clarification gate, always-disclosed contradiction handling, the additive-by-default writing contract whose sole rewrite path is `rebuild`, and its opt-in install status as the only optional workflow) -- [Handover workflow](sce/handover-workflow.md) (`/handover`, the fifth canonical SCE workflow, generated for OpenCode, Claude, and Pi from `config/pkl/base/workflow-handover.pkl`: dual writer/loader mode routing, the phase-free `sce-handover` two-file package, active-task-or-timestamped writer naming, the four-section persisted handover document, and the read-only loader contract) +- [Handover workflow](sce/handover-workflow.md) (`/handover`, the fifth canonical SCE workflow, generated for OpenCode, Claude, and Pi from `config/pkl/base/workflow-handover.pkl`: dual writer/loader mode routing, the phase-free `sce-handover` package with a package-local persisted-format template and output layouts, active-task-or-timestamped writer naming, staged-plus-unstaged Git fact gathering, substantive four-section validation, concise writer success, and the read-only loader contract) - `context/sce/automated-profile-contract.md` (deterministic gate policy for automated OpenCode profile, including 10 gate categories, permission mappings, automated `/commit` single-commit execution behavior, and automated profile constraints) @@ -96,6 +96,12 @@ Supporting repo docs: Recent decision records: +- `context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md` (the completed task record — `Completed`/`Files changed`/`Result`/`Verify`/`Context impact`/`Context synchronization`, identified only by plan path and task ID — is the sole durable input for immediate and cross-session task synchronization, with no separate persisted `Context synchronization handoff` structure; supersedes only the handoff-shape portion of `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md`, whose `pending`/`synced`/`blocked` lifecycle-state invariant remains in force) +- `context/decisions/2026-08-12-decision-gate-semantics.md` (nonqualifying/skipped decision gates are non-blocking; ADRs are immutable, active-only reuse is allowed, changed decisions create new dated records, and `Deprecated`/`Superseded` are creation-time-only statuses) +- `context/decisions/2026-08-12-observational-final-validation.md` +- `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` +- `context/decisions/2026-08-13-validate-validation-only.md` +- `context/decisions/2026-08-13-split-commit-reference-ownership.md` (separates generated commit-message guidance from atomic procedure and removes the obsolete YAML result-contract section while preserving internal commit statuses) - `context/decisions/2026-02-28-pkl-generation-architecture.md` - `context/decisions/2026-03-03-plan-code-agent-separation.md` - `context/decisions/2026-07-27-workflow-oriented-pkl-generation.md` (canonical workflow-oriented ownership, the `.pi/` behavioral baseline, the shared sync skeleton, and routing-only agents; its per-target phase-package inventory and phase-result transport claims are superseded, and its removal of the generated `/handover` surface is superseded by the 2026-07-30 restoration) @@ -105,7 +111,7 @@ Recent decision records: - `context/decisions/2026-07-29-claude-workflow-skill-packages.md` (the four command-to-workflow-skill routes, two-file package rule, and internal phase state, first established for Claude; its Claude-only scoping and Pi/OpenCode preservation clauses are superseded) - `context/decisions/2026-07-29-cross-target-workflow-skill-packages.md` (accepted history for the cross-target four-workflow single-skill model; its exact two-file phase-based package inventory is superseded, while single-skill control flow and internal phase state remain in force) - `context/decisions/2026-08-03-package-local-phase-reference-workflow-packages.md` (supersedes the two-file clause for phase-based workflow packages: `SKILL.md` retains control flow while package-local references own phase instructions and persisted-document formats on all three targets) -- `context/decisions/2026-07-30-synchronization-scoped-decision-writing.md` (accepted standalone `sce-decision` package, synchronization-only sibling invocation, decision threshold, default status, and immutable accepted-ADR policy) +- `context/decisions/2026-07-30-synchronization-scoped-decision-writing.md` (accepted standalone `sce-decision` package, synchronization-only sibling invocation, decision threshold, default status, and immutable accepted-ADR policy; superseded on ADR history semantics by `2026-08-12-decision-gate-semantics.md`) - `context/decisions/2026-08-07-sce-workflow-helper-composition.md` (accepted SCE workflow ownership boundary: non-SCE in-step helpers return control to the active step, while arbitrary SCE workflow chaining remains prohibited) - `context/decisions/2026-07-27-ephemeral-pkl-build-generation.md` (removes committed generated targets; establishes pre-Cargo generation, validated copying into Cargo `OUT_DIR`, and crates.io/Flatpak packaging fallbacks) - `context/decisions/2026-03-09-migrate-lexopt-to-clap.md` (CLI argument parsing migration from lexopt to clap derive macros) diff --git a/context/decisions/2026-08-12-decision-gate-semantics.md b/context/decisions/2026-08-12-decision-gate-semantics.md new file mode 100644 index 00000000..ec0a3bd2 --- /dev/null +++ b/context/decisions/2026-08-12-decision-gate-semantics.md @@ -0,0 +1,89 @@ +# Decision: Make synchronization decision-gate outcomes and ADR history explicit + +Date: 2026-08-12 +Status: Accepted +Plan: `context/plans/workflow-skill-boundary-cleanup.md` +Task: `T10` +Supersedes: `context/decisions/2026-07-30-synchronization-scoped-decision-writing.md` + +## Context + +Successful task and plan synchronization use the standalone `sce-decision` +skill to preserve qualifying system-wide decisions. The existing contract +classified a nonqualifying request as `blocked`, which incorrectly stopped +otherwise successful synchronization. Its ADR history rules also protected +accepted records without stating the equivalent active statuses that may be +reused or the immutable treatment of rejected, deprecated, and superseded +records. + +## Decision + +The synchronization decision gate will return `not_qualified` or `skipped` for +nonqualifying decisions and continue synchronization; `blocked` is reserved for +missing, contradictory, or unsafe decision-writing input or history. ADRs are +immutable regardless of status: only an equivalent `Proposed` or `Accepted` +(active) ADR may be reused, while rejected, deprecated, and superseded records +are never reused. Any changed, corrected, reversed, or replaced decision +creates a new dated ADR. `Deprecated` and `Superseded` remain distinct +creation-time-only statuses. + +## Rationale + +Separating nonqualification from failure keeps the decision gate from turning a +normal context synchronization into a blocker. Active-only reuse preserves one +canonical current record without reviving decisions that were rejected or +replaced. Immutable records and dated replacements keep the decision history +auditable, while retaining both terminal status names preserves their distinct +historical meaning. + +## Alternatives considered + +- **Keep nonqualifying decisions as `blocked`** — Rejected; ordinary changes + would incorrectly prevent durable context synchronization from completing. +- **Reuse any equivalent ADR regardless of status** — Rejected; rejected, + deprecated, and superseded records do not represent active guidance. +- **Edit an existing ADR when a decision changes** — Rejected; this would erase + the historical rationale and make reversals non-auditable. +- **Collapse `Deprecated` and `Superseded`** — Rejected; the statuses retain + useful distinct historical meaning, so both remain creation-time-only. + +## Compatibility and risks + +- Existing accepted ADRs remain valid and unchanged; existing rejected, + deprecated, and superseded ADRs become explicitly non-reusable. +- Generated synchronization consumers continue to stop on genuine `blocked` + decision handoffs while continuing after `not_qualified` or `skipped`. +- Callers must distinguish a nonqualifying result from a decision-writing + failure when interpreting the internal handoff. + +## Guardrails + +- Invoke `sce-decision` only from successful task or plan context synchronization. +- Write at most one ADR per structured decision request and never mutate an + existing ADR or its status. +- Use only `Proposed`, `Accepted`, `Rejected`, `Deprecated`, or `Superseded` + for a newly written ADR, defaulting to `Accepted`. +- Use a new dated ADR for every correction, reversal, or changed decision. + +## Consequences + +- Synchronization can complete normally when no architecture decision qualifies. +- Decision history records active reuse and all later changes without rewriting + prior records. +- The standalone decision package exposes a stable distinction between + nonqualification, deliberate skipping, and unsafe decision writing. + +## Follow-up + +None. + +## References + +- Plan: [`workflow-skill-boundary-cleanup`](../plans/workflow-skill-boundary-cleanup.md) +- Task: `T10` +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Current-state context: [`Architecture`](../architecture.md) +- Current-state context: [`Patterns`](../patterns.md) +- Evidence: [`decision-skill.pkl`](../../config/pkl/base/decision-skill.pkl) +- Evidence: [`workflow-context-sync.pkl`](../../config/pkl/base/workflow-context-sync.pkl) +- Related decision: [`Allow Decision Writing During Successful Context Synchronization`](2026-07-30-synchronization-scoped-decision-writing.md) diff --git a/context/decisions/2026-08-12-explicit-baseline-relative-task-handoffs.md b/context/decisions/2026-08-12-explicit-baseline-relative-task-handoffs.md new file mode 100644 index 00000000..9d225555 --- /dev/null +++ b/context/decisions/2026-08-12-explicit-baseline-relative-task-handoffs.md @@ -0,0 +1,86 @@ +# Decision: Use Explicit Baseline-Relative Task Handoffs + +Date: 2026-08-12 +Status: Accepted +Plan: `context/plans/workflow-skill-boundary-cleanup.md` +Task: `T06` + +## Context + +The task-execution and task-context-synchronization phases are separate internal +steps of one generated workflow, but the synchronization phase must receive an +authoritative account of what implementation changed. Whole-working-tree Git +status or a diff against `HEAD` can include unrelated pre-existing staged, +unstaged, or untracked work. Missing or contradictory handoff fields can also +cause synchronization to reconstruct facts from conversation history. + +## Decision + +Use an explicit, baseline-relative execution handoff as the sole interface from +task execution to task context synchronization. Before editing, execution +captures the Git `HEAD`, staged and unstaged patch/content state, and untracked +path/content state; after implementation it compares the same snapshot shape and +reports only task-caused paths in `changes.files_changed`, alongside the resolved +plan and task identity, implementation summary, verification and done-check +evidence, plan update, and context impact. The synchronization phase consumes +that list as authoritative. The mandatory five-root-file context pass remains +unconditional for every completed task. + +## Rationale + +An explicit contract makes the cross-phase handoff inspectable and prevents the +context phase from silently broadening its evidence to unrelated working-tree +changes. Baseline comparison preserves useful attribution when a repository was +already dirty before implementation, while required fields make successful +handoffs complete without conversational reconstruction. + +## Alternatives considered + +- **Diff the final working tree against `HEAD`** — This attributes unrelated + pre-existing changes to the task. +- **Let synchronization rediscover changed files** — This duplicates execution + responsibility and can produce different results from the implementation + evidence. +- **Make the root-file pass conditional on context impact** — This would permit + cross-cutting context drift to escape the deterministic accuracy check. + +## Compatibility and risks + +- The generated execution contract now requires richer complete handoffs and + blocks stale, invalid, or contradictory handoffs, including under auto-approval. +- A path modified both before and during implementation is reported once when + its state or content changes during the task; the handoff does not claim to + separate unrelated hunks within that path. + +## Guardrails + +- Capture the baseline before any file modification and compare the same state + shape after implementation. +- Do not use whole-working-tree status or a `HEAD`-only diff as `files_changed`. +- Do not omit, invent, or reconstruct required complete-handoff fields. +- Keep the five-root-file context pass mandatory regardless of reported impact. +- Keep the handoff contract in the execution phase reference and its existing + result contract; do not create a standalone execution reference. + +## Consequences + +- Task context synchronization receives deterministic, task-attributed file + evidence and can block safely when the handoff is incomplete or contradictory. +- Pre-existing unrelated work remains outside the changed-file list, reducing + accidental context edits and misleading synchronization reports. +- Every successful task still pays the small deterministic cost of verifying the + five root context files. + +## Follow-up + +None. + +## References + +- Plan: [`workflow-skill-boundary-cleanup`](../plans/workflow-skill-boundary-cleanup.md) +- Task: `T06` +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Current-state context: [`Architecture`](../architecture.md) +- Evidence: [`workflow-next-task.pkl`](../../config/pkl/base/workflow-next-task.pkl) +- Evidence: [`workflow-context-sync.pkl`](../../config/pkl/base/workflow-context-sync.pkl) +- Related decision: [`Persist Workflow Synchronization Lifecycle in Plans`](2026-08-12-persist-workflow-sync-lifecycle-in-plans.md) diff --git a/context/decisions/2026-08-12-observational-final-validation.md b/context/decisions/2026-08-12-observational-final-validation.md new file mode 100644 index 00000000..e7fa53f7 --- /dev/null +++ b/context/decisions/2026-08-12-observational-final-validation.md @@ -0,0 +1,74 @@ +# Decision: Make Final Validation Observational + +Date: 2026-08-12 +Status: Accepted +Plan: `context/plans/workflow-skill-boundary-cleanup.md` +Task: `T05` + +## Context + +Final validation is a repository-wide workflow boundary shared by OpenCode, +Claude, and Pi. A validation run must measure the finished implementation, but +its prior contract also allowed it to remove temporary scaffolding before +recording evidence. That makes validation destructive and can hide unfinished +or debug-only artifacts that should remain visible to the implementation +workflow. + +## Decision + +Final validation is observational: it never deletes or repairs application, +test, configuration, context, debug-only, temporary, or local-scaffolding +artifacts. Any leftover debug or temporary artifact is recorded as failed +validation evidence under the failure follow-ups, and repair occurs only in a +later implementation session. + +## Rationale + +Recording leftovers preserves complete evidence of the delivered state and +keeps validation's proof boundary separate from implementation repair. The +rule is target-neutral and fits the existing failed-validation handoff, which +already stops without modifying product or test code. + +## Alternatives considered + +- **Delete temporary artifacts during validation** — This can hide incomplete + work and makes the proof run mutate what it is meant to measure. +- **Repair artifacts automatically during validation** — This expands final + validation into an implementation phase and makes its evidence dependent on + unreviewed edits. + +## Compatibility and risks + +- Plans that expect a cleanup field in successful Validation Reports must use + the failure-evidence section for leftovers instead. +- Existing validation runs may expose artifacts that an older run would have + removed; this is intentional and gives the next implementation session an + actionable repair target. + +## Guardrails + +- Keep the no-repair rule for application, test, and configuration code. +- Record leftover debug or temporary artifacts as failed checks with their path + and evidence; do not delete them. +- Keep cleanup and repair outside `/validate` and within a normal implementation + workflow. + +## Consequences + +- `/validate` proves the existing finished state without changing it. +- A successful Validation Report has no scaffolding-removal field. +- Generated targets share one deterministic observational validation contract. + +## Follow-up + +None. + +## References + +- Plan: [`workflow-skill-boundary-cleanup`](../plans/workflow-skill-boundary-cleanup.md) +- Task: `T05` +- Current-state context: [`Architecture`](../architecture.md) +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Evidence: [`workflow-validate.pkl`](../../config/pkl/base/workflow-validate.pkl) +- Evidence: [`workflow-context-sync.pkl`](../../config/pkl/base/workflow-context-sync.pkl) +- Related decision: [`Persist Workflow Synchronization Lifecycle in Plans`](2026-08-12-persist-workflow-sync-lifecycle-in-plans.md) diff --git a/context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md b/context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md new file mode 100644 index 00000000..e7fca28e --- /dev/null +++ b/context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md @@ -0,0 +1,85 @@ +# Decision: Persist Workflow Synchronization Lifecycle in Plans + +Date: 2026-08-12 +Status: Accepted +Plan: `context/plans/workflow-skill-boundary-cleanup.md` +Task: `T03` + +## Context + +Task-level and plan-level context synchronization currently exists only as +in-session workflow state. A later session can therefore start another task or +finish validation without durable evidence that an earlier synchronization was +completed. The next-task and validate workflows share this lifecycle boundary +across all generated targets, so the state needs one durable, target-neutral +owner. + +## Decision + +Persist task-level and plan-level context synchronization lifecycle state in the +plan Markdown. Each lifecycle uses `pending`, `synced`, or `blocked`; a blocked +record carries its blocker, required action, and retry condition. A completed +task must have `synced` task-level state before another implementation task can +start or the plan can finish. The plan-level state becomes `pending` before +final plan synchronization and becomes `synced` or `blocked` afterward. + +## Rationale + +The plan already persists task status and completion evidence, is available in a +fresh session, and is the canonical input to both workflows. Extending that +format avoids a second persistence mechanism while making unresolved +synchronization debt explicit and actionable. Keeping the lifecycle contract in +the canonical Pkl plan template and shared workflow sources preserves parity for +Pi, Claude, and OpenCode. + +## Alternatives considered + +- **Keep lifecycle state in chat or transient phase results** — A fresh session + cannot reliably observe or gate on it. +- **Create a separate lifecycle database or sidecar file** — This introduces a + second persistence owner and complicates plan portability and cleanup. +- **Infer `synced` from historical evidence** — Existing reports may be absent or + ambiguous, so inference would allow unresolved debt to pass silently. + +## Compatibility and risks + +- Plans authored before this decision may lack lifecycle fields; the workflows + treat missing state on completed tasks as unresolved debt rather than silently + declaring it synchronized. +- Existing task and plan status fields remain unchanged; lifecycle state is an + additional persisted contract. +- A synchronization blocker now stops continuation while preserving the + completed implementation, with the plan recording how to retry. + +## Guardrails + +- Keep the state in the Markdown plan format; do not add a database or alternate + session store. +- Allow only `pending`, `synced`, and `blocked` lifecycle values. +- Require blocker, required-action, and retry-condition details for `blocked`. +- Write `pending` before invoking synchronization and write the terminal state + after the synchronization result is known. +- Do not infer `synced` from conversation history. + +## Consequences + +- `/next-task` can refuse new implementation work when an earlier completed task + has unresolved synchronization debt. +- `/validate` can refuse to treat a plan as finishable while task-level debt + remains. +- The plan file becomes the durable handoff for synchronization retry state across + sessions. + +## Follow-up + +None. + +## References + +- Plan: [`workflow-skill-boundary-cleanup`](../plans/workflow-skill-boundary-cleanup.md) +- Task: `T03` +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Evidence: [`workflow-change-to-plan.pkl`](../../config/pkl/base/workflow-change-to-plan.pkl) +- Evidence: [`workflow-next-task.pkl`](../../config/pkl/base/workflow-next-task.pkl) +- Evidence: [`workflow-validate.pkl`](../../config/pkl/base/workflow-validate.pkl) +- Evidence: [`workflow-content.pkl`](../../config/pkl/base/workflow-content.pkl) diff --git a/context/decisions/2026-08-13-split-commit-reference-ownership.md b/context/decisions/2026-08-13-split-commit-reference-ownership.md new file mode 100644 index 00000000..8fa34af4 --- /dev/null +++ b/context/decisions/2026-08-13-split-commit-reference-ownership.md @@ -0,0 +1,73 @@ +# Decision: Split Commit Message Guidance from Atomic Commit Procedure + +Date: 2026-08-13 +Status: Accepted +Plan: `context/plans/remove-validate-context-sync.md` +Task: `T02` + +## Context + +The generated `/commit` package is produced for OpenCode, Claude, and Pi from one +canonical Pkl module. Its staged baseline moves commit-message style guidance to a +package-local `references/commit-message-style.md` document and removes the +obsolete YAML result-contract section from `references/atomic-commit.md`. The +commit phase still uses internal statuses and preserves regular/bypass routing; +only reference ownership and the obsolete serialized contract documentation +change. + +## Decision + +The generated `sce-commit` package will emit a separate +`references/commit-message-style.md` for message wording, while +`references/atomic-commit.md` owns staged-diff procedure, result branching, and +commit boundaries without a generated YAML result-contract section or +`commit-contract.yaml` artifact. + +## Rationale + +A separate style reference gives message-writing rules one explicit owner and +keeps the operational phase document focused on staged-diff analysis. Removing +the obsolete YAML section matches the single-skill model, where result statuses +remain internal to `/commit` rather than being transported between generated +packages. + +## Alternatives considered + +- **Keep style guidance inline in `atomic-commit.md`** — This preserves the old + ownership split and makes the staged reference baseline non-canonical. +- **Retain the YAML result-contract section or generate `commit-contract.yaml`** — + This preserves a removed serialized contract that the current single-skill + workflow no longer needs. + +## Compatibility and risks + +- All three targets gain the same package-local style reference and retain the + same regular/bypass behavior and internal result statuses. +- The generated reference inventory changes, so exact path and target-parity + checks must reject the obsolete contract artifact and verify the new style file. + +## Guardrails + +- `config/pkl/base/workflow-commit.pkl` remains the sole authoring source. +- Commit routing, staged truth, message result fields, and human-visible output + layouts remain unchanged. +- Target differences remain limited to supported frontmatter. + +## Consequences + +- `references/commit-message-style.md` is a durable generated package reference + for all three targets. +- `references/atomic-commit.md` no longer carries the obsolete YAML result-contract + section, and no `commit-contract.yaml` replacement is generated. + +## Follow-up + +None. + +## References + +- Plan: [`remove-validate-context-sync`](../plans/remove-validate-context-sync.md) +- Task: `T02` +- Current-state context: [`Atomic commit workflow`](../sce/atomic-commit-workflow.md) +- Evidence: [`Canonical commit workflow`](../../config/pkl/base/workflow-commit.pkl) +- Related decision: [`Package-local phase references`](2026-08-03-package-local-phase-reference-workflow-packages.md) diff --git a/context/decisions/2026-08-13-validate-validation-only.md b/context/decisions/2026-08-13-validate-validation-only.md new file mode 100644 index 00000000..c83858ca --- /dev/null +++ b/context/decisions/2026-08-13-validate-validation-only.md @@ -0,0 +1,73 @@ +# Decision: Make `/validate` validation-only + +Date: 2026-08-13 +Status: Accepted +Plan: `context/plans/remove-validate-context-sync.md` +Task: `T01` +Supersedes: `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` + +## Context + +The canonical `/validate` workflow previously ran a plan-level context +synchronization phase after successful validation and persisted plan-level sync +lifecycle state around that handoff. The requested workflow boundary removes +that phase while retaining task-level synchronization in `/next-task`, final +validation, and the plan's Validation Report. The change is cross-target and +changes the ownership and completion contract of a core workflow. + +## Decision + +`/validate` performs final validation, writes the Validation Report, and returns +`validated`, `failed`, or `blocked`; it does not invoke plan-level context +synchronization or persist a plan-context-sync lifecycle handoff. Task-level +context synchronization remains owned by `/next-task`. + +## Rationale + +Validation and durable context reconciliation have distinct responsibilities. +Keeping `/validate` focused on observational validation removes the unwanted +plan-level synchronization lifecycle while preserving the task-level context +synchronization boundary and deterministic validation evidence. + +## Alternatives considered + +- **Retain automatic plan synchronization after validation** — This preserves + the removed lifecycle and contradicts the requested validation-only boundary. +- **Replace plan synchronization with another automatic mechanism** — This + expands scope and creates a new contract not established by the change. + +## Compatibility and risks + +- Plans and current-state documentation that describe `/validate` as invoking + plan synchronization must be migrated to the validation-only contract. +- Durable context is no longer reconciled automatically as part of `/validate`; + future replacement requires a separate decision and implementation. + +## Guardrails + +- Preserve all validation acceptance-criteria checks, full-validation commands, + Validation Report writing, and `validated`/`failed`/`blocked` statuses. +- Do not remove the shared task-context-sync implementation or `/next-task` + synchronization behavior. +- Do not generate a package-local plan context-sync reference for `/validate`. + +## Consequences + +- `/validate` has one validation phase and reports its result and Validation + Report path without claiming durable context synchronization. +- `/next-task` remains the owner of task-level context synchronization after + successful implementation. + +## Follow-up + +- Update current-state workflow and generation documentation to describe the + validation-only `/validate` boundary. + +## References + +- Plan: [`remove-validate-context-sync`](../plans/remove-validate-context-sync.md) +- Task: `T01` +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Evidence: [`workflow-validate.pkl`](../../config/pkl/base/workflow-validate.pkl) +- Evidence: [`workflow-content.pkl`](../../config/pkl/base/workflow-content.pkl) +- Related decision: [`Persist Workflow Synchronization Lifecycle in Plans`](2026-08-12-persist-workflow-sync-lifecycle-in-plans.md) diff --git a/context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md b/context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md new file mode 100644 index 00000000..c0c01724 --- /dev/null +++ b/context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md @@ -0,0 +1,103 @@ +# Decision: Compact Task Record Supersedes Persisted Synchronization Handoff + +Date: 2026-08-14 +Status: Accepted +Plan: `context/plans/simplify-task-record-format.md` +Task: `T01, T02, T03, T04, T05` +Supersedes: `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` + +## Context + +The prior decision established that task-level and plan-level context +synchronization lifecycle state (`pending`/`synced`/`blocked`) must persist in +the plan Markdown, durable across sessions. In practice, the completed-task +record also grew a second, duplicated representation of the same facts: a +serialized `Context synchronization handoff` completion-record field +(explicit `Plan path`, `Task ID`, `Task title`, changed files, implementation +summary, verification, done checks, and context impact) alongside separate +`Implementation evidence` and `Verification evidence` sections, all restating +information the completed task already carried through `Goal`, +`Boundaries (in/out of scope)`, `Verification notes`, `Files changed`, and +`Evidence`. Immediate synchronization and later sync-debt recovery both read +from this duplicated handoff rather than from the completed task record +itself. + +## Decision + +The completed task record — `Completed`, `Files changed`, `Result`, `Verify` +outcomes, `Context impact`, and `Context synchronization`, identified only by +plan path and task ID — is the sole durable input for both immediate task +context synchronization and later cross-session sync-debt recovery. No +separate `Context synchronization handoff` structure is constructed, +persisted, or read. A blocked synchronization adds only +synchronization-specific blocker metadata (`Blocker`, `Required action`, +`Retry condition`) beside that same task record; it does not duplicate the +task's execution facts. The `pending`/`synced`/`blocked` lifecycle-state +invariant decided in the superseded ADR is not re-decided and remains in +force unchanged. + +## Rationale + +The completed task record already contains everything a synchronization +retry needs once execution facts are written directly onto it in a compact +shape. A second serialized handoff duplicates that data, can drift from it, +and adds an evidence structure with no independent source of truth. Reading +the task record directly keeps the plan the single durable source for both +completion evidence and synchronization state, matching the same +plan-as-durable-store rationale the superseded decision already established. + +## Alternatives considered + +- **Keep the separate persisted handoff field** — Preserves duplication + between the completed task's own evidence and the handoff's restated copy, + with no benefit once the task record itself carries complete facts. +- **Drop the completed task record and rely solely on a handoff structure** — + Loses the task record's authoritative status field + (`(status:done)`/`Completed`) as a single place the plan-level view and the + synchronization view actually agree. + +## Compatibility and risks + +- Historical plans authored before this decision may still carry the + duplicated-field format; they are not migrated by this decision and are + read only by the surviving legacy-migration branch when their completed + tasks carry unresolved synchronization debt. +- Generation-contract checks and fixtures assert the compact fields are + present and the removed fields/sections are absent from newly generated + workflow instructions and plan-template examples. + +## Guardrails + +- Do not persist or read a `Context synchronization handoff` field, or any + differently named field that recreates the same duplication (for example, a + verbose "Implementation summary" alongside `Result`). +- Do not weaken the `pending`/`synced`/`blocked` lifecycle-state invariant or + its blocker/required-action/retry-condition requirement for `blocked`. +- Keep `Result` a short factual outcome, not a prose diff. + +## Consequences + +- A completed task is the single authoritative record of implementation + intent, completion conditions, verification, actual execution result, + changed files, context impact, and context synchronization state. +- Sync-debt recovery identifies a task only by plan path and task ID and + reads that same record, with no separate cross-session artifact to keep in + sync. +- Legacy plans predating this format are detected by the absence of the + compact execution fields and blocked with migration guidance rather than a + reconstructed retry. + +## Follow-up + +None. + +## References + +- Plan: [`simplify-task-record-format`](../plans/simplify-task-record-format.md) +- Task: `T01`, `T02`, `T03`, `T04`, `T05` +- Current-state context: [`Shared Context Code Workflows`](../sce/shared-context-code-workflow.md) +- Evidence: [`workflow-change-to-plan.pkl`](../../config/pkl/base/workflow-change-to-plan.pkl) +- Evidence: [`workflow-next-task.pkl`](../../config/pkl/base/workflow-next-task.pkl) +- Evidence: [`workflow-context-sync.pkl`](../../config/pkl/base/workflow-context-sync.pkl) +- Evidence: [`generation-contract-check.pkl`](../../config/pkl/renderers/generation-contract-check.pkl) +- Related decision: [`Persist Workflow Synchronization Lifecycle in Plans`](2026-08-12-persist-workflow-sync-lifecycle-in-plans.md) diff --git a/context/glossary.md b/context/glossary.md index 7844ab29..13146e0b 100644 --- a/context/glossary.md +++ b/context/glossary.md @@ -1,6 +1,6 @@ # Glossary -- `pkl-check-generated`: Flake app exposed as `nix run .#pkl-check-generated`; canonical ephemeral-generation check that rejects committed target/schema/mirror outputs, evaluates exact workflow metadata, the complete 101-path generated artifact contract, and the optional-workflow manifest's content against the catalog, requires the shared helper-composition rule and SCE-scoped workflow prohibitions, enforces ordered catalog-derived OpenCode skill permissions, rejects stale sibling-package references or unresolved internalization tokens in workflow entrypoint `SKILL.md` documents, proves extra/missing/reference/orchestration/permission failures through negative fixtures, and delegates deterministic generation plus payload/input inventories to the generated-input producer while preserving its established inventory report. +- `pkl-check-generated`: Flake app exposed as `nix run .#pkl-check-generated`; canonical ephemeral-generation check that rejects committed target/schema/mirror outputs, evaluates exact workflow metadata, the generated artifact contract, semantic layout/path/inventory/content/parity/observational checks, and the optional-workflow manifest's content against the catalog, requires the shared helper-composition rule and SCE-scoped workflow prohibitions, enforces ordered catalog-derived OpenCode skill permissions plus explicit-permission artifact integrity, rejects stale sibling-package references or unresolved internalization tokens in workflow entrypoint `SKILL.md` documents, proves contract failures through checked-in negative fixtures, and delegates deterministic generation plus payload/input inventories to the generated-input producer while preserving its established inventory report. - `repo-level verification preference`: Current repository guidance that contributor-facing validation/check flows should prefer `nix flake check`; direct Cargo verification commands are secondary and used only when explicitly requested or for narrow targeted debugging, while `cargo fmt` remains the explicit autofix path. - lightweight post-task verification baseline: Required quick checks after each completed task in this repo: `nix run .#pkl-check-generated` and `nix flake check`. - disposable plan lifecycle: Policy where `context/plans/` holds active execution artifacts only; completed plans are disposable and durable outcomes must be reflected in current-state context files and/or `context/decisions/`. @@ -9,9 +9,9 @@ - ephemeral generated payload: Files materialized by `config/pkl/generate.pkl` using payload-relative `config/.opencode/**`, `config/.claude/**`, `config/.pi/**`, and `config/schema/sce-config.schema.json` paths beneath Cargo `OUT_DIR`, temporary previews, or packaging fallbacks. These layouts are installed by `sce setup` but are never committed as repository target trees; `config/automated/.opencode/**` remains a forbidden generator surface. - `CLI generated-input handoff`: Repository-build contract rooted at the temporary directory named by `SCE_CLI_GENERATED_INPUT_DIR`. `config/pkl/generator-inputs.txt` declares the canonical `config/pkl` and referenced `config/lib` inputs; `scripts/produce-cli-generated-input.sh` discovers those files, generates Pkl twice, rejects nondeterminism and in-flight input mutation, and atomically places `pkl-generated/`, its exact `SHA256SUMS`, and `INPUTS.SHA256SUMS` there. `scripts/run-cli-cargo.sh` delegates production and removes its temporary handoff after Cargo exits. `cli/build.rs` verifies payload integrity and input freshness before copying `pkl-generated/` into Cargo `OUT_DIR`; missing, incomplete, modified, or stale handoffs fail rather than invoking Pkl or falling back to packaged assets. - `generated-input producer`: Repository-owned `scripts/produce-cli-generated-input.sh` contract driven by `config/pkl/generator-inputs.txt`. It is the canonical owner for expanding repository-relative generator inputs, snapshotting their inventory, two-pass Pkl evaluation, byte-tree determinism comparison, payload and canonical-input SHA-256 inventories, input-mutation rejection, atomic output publication, and private staging cleanup. The repository Cargo wrapper, generated-output check, package-fallback preparation, and Nix `cliGeneratedInput` derivation all consume it. -- `Pi workflow package`: Generated Pi workflow surface consisting of one thin prompt in `config/.pi/prompts/` plus the one workflow skill package under `config/.pi/skills/` that the prompt routes to. Phase-based workflows include `SKILL.md`, `references/output.md`, and named phase or persisted-document references; phase-free workflows contain the two core files only. Pi currently receives `/change-to-plan`, `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield` this way and has no generated agent-role prompts. -- `workflow skill package`: One of the six renderer-composed packages (`sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, `sce-brownfield`) emitted for every target. Its `SKILL.md` owns the canonical phase sequence, internal status branching, user waits and same-session resume behavior, and continuation; each phase-based workflow reads package-local phase references before acting, while `sce-handover` and `sce-brownfield` have no phases and retain two-file packages. Relevant non-SCE skills may assist inside an active step but return control to it without changing workflow invariants. The sole SCE sibling exception lets successful `sce-next-task` and `sce-validate` synchronization invoke `sce-decision` for one qualifying system-wide decision. `references/output.md` remains the sole owner of human-visible layouts. The canonical phase modules remain authoring inputs to composition and are not generated as packages for any target. -- `decision skill package`: Standalone internal `sce-decision` package emitted for OpenCode, Claude, and Pi from `config/pkl/base/decision-skill.pkl`, outside the command workflow catalog. Its `SKILL.md` accepts one qualifying system-wide decision from successful task or plan synchronization, enforces one dated ADR with allowed status vocabulary and `Accepted` default, never edits an accepted ADR, and returns a deterministic written-or-blocked handoff. Its only other file is `references/adr-template.md`; no user-facing command or prompt routes to it, and no workflow invokes it outside the synchronization decision gate. +- `Pi workflow package`: Generated Pi workflow surface consisting of one thin prompt in `config/.pi/prompts/` plus the one workflow skill package under `config/.pi/skills/` that the prompt routes to. Phase-based workflows include `SKILL.md`, `references/output.md`, and named phase, persisted-document, or supporting references; phase-free `/brownfield` has the two core files, while `/handover` also has `references/handover-template.md`. Pi currently receives `/change-to-plan`, `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield` this way and has no generated agent-role prompts. +- `workflow skill package`: One of the six renderer-composed packages (`sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, `sce-brownfield`) emitted for every target. Its `SKILL.md` owns the canonical phase sequence, internal status branching, user waits and same-session resume behavior, and continuation; each phase-based workflow reads package-local phase references before acting, while `sce-handover` and `sce-brownfield` have no phases; handover adds a package-local persisted-format template and brownfield retains the two-file package. Relevant non-SCE skills may assist inside an active step but return control to it without changing workflow invariants. The sole SCE sibling exception lets successful `/next-task` task synchronization invoke `sce-decision` for one qualifying system-wide decision; `/validate` is validation-only. `references/output.md` remains the sole owner of human-visible layouts. The canonical phase modules remain authoring inputs to composition and are not generated as packages for any target. +- `decision skill package`: Standalone internal `sce-decision` package emitted for OpenCode, Claude, and Pi from `config/pkl/base/decision-skill.pkl`, outside the command workflow catalog. Its `SKILL.md` accepts one qualifying system-wide decision from successful task synchronization, enforces one immutable dated ADR with active-only reuse, creation-time-only `Deprecated`/`Superseded` statuses, and `Accepted` default, and returns deterministic `written`, non-blocking `not_qualified`/`skipped`, or genuine `blocked` handoffs. Its only other file is `references/adr-template.md`; no user-facing command or prompt routes to it, and no workflow invokes it outside the synchronization decision gate. - `workflow catalog`: The typed mapping in `config/pkl/base/workflow-catalog.pkl` that declares each of the six workflows once and owns its command slug, skill slug, title, description, argument hint, OpenCode routing role, Claude allowed tools, and its `optional` flag. Composite identity, OpenCode routing/permissions, Claude tool frontmatter, and metadata coverage derive from these records; behavior remains in canonical phase modules and formatting remains renderer-owned. - `optional workflow`: A catalog workflow whose `WorkflowRecord.optional` flag is `true`. Optionality is an install-time concern only: the workflow is still authored, composed, and generated for OpenCode, Claude, and Pi exactly like a core workflow, and its generated files remain part of the ephemeral payload and the generation contract. `brownfield` is the only optional workflow; the other five leave the flag at its `false` default. - `optional-workflow manifest`: The generated `config/optional-workflows.json` artifact rendered by `config/pkl/base/optional-workflow-manifest.pkl`. It carries `schemaVersion` plus one `workflows` entry per optional workflow with its `id`, `title`, `description`, `commandSlug`, and `skillSlug`, and is the only carrier of optional-workflow identity outside Pkl. `generation-contract-check.pkl` asserts its content against the catalog rather than merely permitting the path. @@ -19,20 +19,22 @@ - `optional-workflow selection`: The set of optional workflow ids a repository has opted into. `iter_embedded_assets_for_setup_target_with_selection` in `cli/src/services/setup/mod.rs` applies it by excluding each unselected workflow's `{command_dir}/{command_slug}.md` file and `{skills_dir}/{skill_slug}/` subtree per target, leaving all other embedded assets untouched. `sce setup` resolves it per run — from the interactive multi-select, else `--workflow`, else the persisted value — installs by it, and persists it; its persisted form is the `integrations.optional_workflows` config key. `sce doctor` reads that same persisted key and applies the same filter, so it expects an optional workflow's files only where the repository opted in. See [setup local bootstrap](sce/setup-repo-local-config-bootstrap.md). - `sce setup --workflow`: Repeatable `sce setup` flag naming one optional workflow id to install for the run. Passing it at all makes the listed ids the exact selection for that run; omitting it reuses the persisted `integrations.optional_workflows`, so a repeat run never silently uninstalls a previously selected optional workflow. Unknown ids are rejected before any file is written, with the embedded catalog's available ids named in the error. It is rejected alongside `--bootstrap-context` and on a hooks-only run, neither of which installs target assets. - `integrations.optional_workflows`: Repo-local `sce/config.json` key recording a repository's optional-workflow selection as a unique array of optional workflow ids. Its accepted values are derived from the workflow catalog's `optional` records in `config/pkl/base/sce-config-schema.pkl`, and `cli/src/services/config/` parses it into `IntegrationsConfig.optional_workflows` alongside `integrations.target`, validating each id against the embedded optional-workflow catalog. See [CLI config precedence contract](cli/config-precedence-contract.md). -- `sce-handover`: Self-contained skill package (`SKILL.md` plus `references/output.md`) invoked by the `/handover` command, registered in `config/pkl/base/workflow-catalog.pkl` and generated for OpenCode, Claude, and Pi. Dual-mode: empty arguments select writer mode, which gathers session and repository facts and writes exactly one handover document; one path argument selects read-only loader mode, which validates and presents an existing handover for continuation. It has no phases or SCE workflow handoffs; relevant non-SCE helpers, if used, return control to the active step. See [Handover workflow](sce/handover-workflow.md). +- `sce-handover`: Self-contained skill package (`SKILL.md`, `references/handover-template.md`, and `references/output.md`) invoked by the `/handover` command, registered in `config/pkl/base/workflow-catalog.pkl` and generated for OpenCode, Claude, and Pi. Dual-mode: empty arguments select writer mode, which gathers session and repository facts and writes exactly one handover document; one path argument selects read-only loader mode, which rejects missing, empty, or unreplaced-placeholder-only required sections before presenting an existing handover for continuation. It has no phases or SCE workflow handoffs; relevant non-SCE helpers, if used, return control to the active step. See [Handover workflow](sce/handover-workflow.md). - `sce-brownfield`: Self-contained, phase-free skill package (`SKILL.md` plus `references/output.md`) invoked by `/brownfield` to reconstruct durable `context/` memory from an existing repository's own evidence. Its canonical source is `config/pkl/base/workflow-brownfield.pkl`; it is the sixth record in `config/pkl/base/workflow-catalog.pkl` and is generated for OpenCode, Claude, and Pi under the `shared-context-code` routing role. Local evidence only, in priority order (current code, then executable configuration, then discovered documentation plus argument-supplied paths, then at least three months of Git history), with no network access; it never creates the `context/` root and never writes outside it. See [Brownfield workflow](sce/brownfield-workflow.md). - brownfield confidence model: The internal `1`–`100` score `sce-brownfield` assigns to every fact it would write as durable truth, banded as `Verified` (`90`–`100`), `Strongly supported` (`70`–`89`), `Inferred` (`50`–`69`), and `Clarification required` (`1`–`49`), plus `Contradiction resolved` for a fact scored after conflicting evidence was resolved. Anything below `50` blocks with grouped clarification questions and is never written as truth. Scores are internal state and chat evidence only; no score is written under `context/`. - brownfield `rebuild` mode: The mode `sce-brownfield` enters when the literal token `rebuild` is the first argument, and the only thing that grants it rewrite authority over existing context files. Writes are otherwise additive — missing files and missing domains only. Even in `rebuild` mode no context file is deleted, `context/plans/`, `context/handovers/`, `context/decisions/`, and `context/tmp/` are untouched, and a file with uncommitted changes is not modified. The mode is never inferred from conversation content or repository state. - handover document: The four-required-section Markdown file (`Current Task State`, `Decisions Made`, `Open Questions / Blockers`, `Next Recommended Step`, plus a trailing `Assumptions` section) that `sce-handover` writer mode persists under `context/handovers/`, named by the active plan task or a collision-safe timestamp when no single task is unambiguous. - `non-SCE helper skill composition`: The workflow rule shared by every generated SCE workflow skill: a relevant non-SCE skill may assist during the active step, but it is not a workflow handoff; control returns to the active step and canonical phase order, gates, waits, writes, validation, stops, and terminal output remain unchanged. Arbitrary SCE workflow chaining remains prohibited, with only the synchronization-scoped `sce-decision` exception. -- `workflow composite renderer`: The shared, target-neutral Pkl module at `config/pkl/renderers/workflow-composite.pkl` that renders each canonical workflow as one workflow-level `SKILL.md` plus deterministic package-local documents. The four phase-based workflows emit named phase and persisted-document references; phase-free workflows emit only `references/output.md` beside the entrypoint. It requires structured composite sources for all six workflows and performs no frontmatter stripping or prose-wide internalization. All three targets render through it, parameterized only by the extra frontmatter each supports. +- `workflow composite renderer`: The shared, target-neutral Pkl module at `config/pkl/renderers/workflow-composite.pkl` that renders each canonical workflow as one workflow-level `SKILL.md` plus deterministic package-local documents. The four phase-based workflows emit named phase, persisted-document, and supporting references; phase-free workflows emit `references/output.md` beside the entrypoint, with handover also emitting its persisted-format template. It requires structured composite sources for all six workflows and performs no frontmatter stripping or prose-wide internalization. All three targets render through it, parameterized only by the extra frontmatter each supports. - `structured workflow rendering`: Canonical Pkl representation centered on the shared model in `workflow-content.pkl`, where package-vs-composite mode is selected through typed frontmatter, body, semantic-reference, structured-document, composite-source, heading-scale (`PhaseHeadings`), and single-mode block values before Markdown assembly. Canonical workflow modules supply workflow-specific behavior and migrated package-local phase, persisted-document, and output documents as named values; all six workflows render their commands and applicable internal documents without frontmatter stripping or prose-wide replacement. - `canonical phase module`: One of the eight phase definitions in `config/pkl/base/workflow-*.pkl` (`sce-context-load`, `sce-plan-authoring`, `sce-plan-review`, `sce-task-execution`, `sce-task-context-sync`, `sce-validation`, `sce-plan-context-sync`, `sce-atomic-commit`). Each is the single behavioral source for its phase and an authoring input to the composite renderer. Since 2026-07-29 no target generates them as installable skill packages; the names denote canonical source and the internal phases inside a composed `SKILL.md`. - `extra frontmatter lines`: The newline-terminated string a target passes to the workflow composite renderer carrying only the frontmatter its skills or commands support (for example `compatibility: claude`, or an `allowed-tools:` line). It is the sole per-target parameter of composition; a target that adds no frontmatter passes the empty string. - `workflow phase reference`: A package-local Markdown document emitted for one phase-based workflow step, such as `references/plan-review.md` or `references/context-sync.md`. It owns that phase's operational steps and boundaries and is read before the phase takes action; it does not create a sibling skill or transport phase state. +- `baseline-relative task handoff`: The explicit `/next-task` execution result passed to task context synchronization. It contains the resolved plan and task identity, implementation-changed paths computed by comparing post-edit state with a pre-edit Git baseline, implementation summary, verification and done-check evidence, plan update, and context impact; unchanged pre-existing working-tree paths are excluded. Its persisted form is the same completed task record `/next-task` writes onto the task itself — `Completed`, `Files changed`, `Result`, `Verify` outcomes, and `Context impact`, identified only by plan path and task ID — with no separate synchronization-handoff structure, so a cross-session retry reads that record directly from the plan rather than depending on an out-of-band plan/task value supplied by the caller. - `workflow output reference`: The required `references/output.md` in every workflow skill package. It defines all and only that workflow's human-visible Markdown gates, reports, and terminal layouts. Phase-based packages also carry operational phase and persisted-document references, but those never own user-visible layouts. It is also the single statement of each layout: a `SKILL.md` branch that produces output cites the section it renders by heading rather than reproducing it. - `no-improvisation rule`: The agent-behavior policy stated by the shared `renderSkill` preamble in every generated workflow `SKILL.md`, on every target. Its process half forbids inventing, skipping, reordering, or merging the canonical workflow's steps, gates, and stops; its output half limits user-visible output to the `references/output.md` layouts, forbidding invented layouts and any added preamble, commentary, summary, or extra section around them. It is prose instruction, not a machine-checked contract. See `context/architecture.md`. - `atomic commit bypass mode`: The `/commit` mode selected when the exact first argument token is `oneshot` or `skip` (case-insensitive; the two aliases are behaviorally identical). It skips the staging-confirmation prompt and the context-file guidance gate, requires exactly one commit message covering all staged files with no split proposals, makes plan citations best-effort instead of blocking, and lets the command run exactly one `git commit`. Any other first token selects proposal-only regular mode. See `context/sce/atomic-commit-workflow.md`. +- `split commit references`: The generated `sce-commit/references/atomic-commit.md`, `references/commit-message-style.md`, and `references/output.md` documents. The atomic reference owns staged-diff procedure, internal result branching, and commit boundaries; the style reference owns commit-message wording; and the output reference owns human-visible layouts. No `commit-contract.yaml` artifact or YAML result-contract section is generated. - `canonical OpenCode plugin registration source`: Shared Pkl-authored plugin-registration definition in `config/pkl/base/opencode.pkl`, re-exported from `config/pkl/renderers/common.pkl` as the canonical plugin list/path JSON consumed by OpenCode renderers before they emit generated `opencode.json` manifests; the current entries are `sce-bash-policy` and `sce-agent-trace`. - `checkout identity`: Stable UUIDv7 identifier assigned to a cloned repository or linked Git worktree, stored in `/sce/checkout-id` (never committed) and resolved via `git rev-parse --git-dir`. The identity is created or reused by `sce setup` through `AgentTraceDbLifecycle::setup()` and also auto-created by hook runtime when `sce setup` has not been run. Checkout identity is now diagnostic metadata for repository-scoped Agent Trace storage; it does not select the active DB and is not stored on Agent Trace rows. Any pre-migration per-checkout DB files at `/sce/agent-trace-{checkout_id}.db` are never touched by SCE and are no longer inspectable via the CLI (the checkout-scoped DB surface was removed by the `retire-legacy-agent-trace-db` plan). See `context/cli/checkout-identity.md`. - `repository identity`: Stable identity of a logical Git repository used to select the active repository-scoped Agent Trace DB path `/sce/repos//agent-trace.db` through the `agent_trace_storage` resolver. Resolved by `cli/src/services/repository_identity/` with precedence: explicit `agent_trace.repository_id` config value, then the URL of the configured Git remote (`agent_trace.repository_remote`, default `origin`), otherwise an actionable `.sce/config.json` error. Remote URLs canonicalize to a scheme-neutral, credential-free `host[:port]/path` form so equivalent SSH/SCP/HTTPS remotes converge, and the repository ID is `sha256("sce-repository-id-v1\0" + canonical_identity)` hex. Distinct from `checkout identity`, which stays per clone/worktree for diagnostics. See `context/cli/repository-identity.md`. @@ -174,7 +176,7 @@ - `SCE Plan/Code role separation`: Architecture decision recorded in `context/decisions/2026-03-03-plan-code-agent-separation.md` that keeps Shared Context Plan and Shared Context Code as separate OpenCode routing roles. The thin Plan agent routes to `/change-to-plan`; the thin Code agent routes to `/next-task` and `/validate`; canonical commands and skills own behavior. - `next-task thin orchestration contract`: Canonical `/next-task` sequencing across `sce-plan-review`, `sce-task-execution`, and `sce-task-context-sync`. Every target embeds that sequence in `sce-next-task` as internal phases and routes its command or prompt directly to that one skill. - `change-to-plan thin orchestration contract`: Canonical `/change-to-plan` sequencing from `sce-context-load` to `sce-plan-authoring`. Every target embeds that sequence in `sce-change-to-plan` as internal phases and routes its command or prompt directly to that one skill. -- `validate thin orchestration contract`: Canonical `/validate` sequencing from `sce-validation` to validated-only `sce-plan-context-sync`. Every target embeds that sequence in `sce-validate` as internal phases and routes its command or prompt directly to that one skill. +- `validate thin orchestration contract`: Canonical `/validate` validation-only sequencing. `sce-validation` verifies the finished plan, writes the Validation Report, and returns `validated`, `failed`, or `blocked`; every target embeds that behavior in `sce-validate` and routes its command or prompt directly to that one skill. - `OpenCode command skill metadata`: Machine-readable frontmatter on the four generated OpenCode workflow commands, defined by `config/pkl/renderers/opencode-content.pkl`. `entry-skill` and the one-entry `skills` list both name that command's single workflow skill, which owns every phase internally. - `one-task/one-atomic-commit planning contract`: `sce-plan-authoring` requirement that each executable plan task represents one coherent commit unit; broad multi-commit tasks must be split into sequential atomic tasks before execution handoff. - `agent trace historical reference docs`: Retained `context/sce/agent-trace-*.md` artifacts that describe the removed pre-v0.3 Agent Trace design and task slices; they are reference-only and do not describe the active local-hook runtime. @@ -242,4 +244,5 @@ - `musl static Linux release`: The Linux binary release targets (`x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`) compile against musl libc and link fully statically. The resulting binary has no runtime libc dependency and zero `/nix/store/` references in ELF metadata, strings, or dynamic-linker fields, satisfying the native portability audit. The musl targets replace the previous glibc-linked `*-unknown-linux-gnu` targets; macOS (`aarch64-apple-darwin`) is unchanged. Introduced in the `musl-static-linux-release` plan. - `parts table (Agent Trace DB)`: Agent Trace DB table created by migration `009_create_parts.sql`; stores append-only message parts with columns `type` (typed by Rust as `text`/`reasoning`/`patch`/`question` and stored as unconstrained `TEXT NOT NULL`), `text`, `message_id`, `session_id`, `generated_at_unix_ms`, `created_at`, `updated_at`. Uses only the internal `id` for row identity (no upsert/dedup). Multiple parts can exist for the same `(session_id, message_id)`. A compound index on `(session_id, message_id, generated_at_unix_ms, id)` enables ordered joins. No foreign keys to `messages` or any other table, so parts may be inserted before their parent message exists. -- `AgentTraceExportReader`: Read-only incremental export reader in `cli/src/services/agent_trace_export/mod.rs` over one `RepositoryAgentTraceDb`, exposing `read_messages_after`/`read_parts_after`/`read_diff_traces_after`/`read_agent_traces_after`, each `(cursor: i64, limit: usize) -> Result>` over `WHERE id > cursor ORDER BY id ASC LIMIT limit`. Holds no local cursor, performs no mutation, makes no network calls, and returns owned camelCase `serde::Serialize` export-row DTOs matching the shipped control-plane ingestion contract. See `context/sce/agent-trace-export-readers.md`. +- `AgentTraceExportReader`: Read-only incremental export reader in `cli/src/services/agent_trace_export/mod.rs` over one `RepositoryAgentTraceDb`, exposing `read_messages_after`/`read_parts_after`/`read_diff_traces_after`/`read_agent_traces_after`, each `(cursor: i64, limit: usize) -> Result>` over `WHERE id > cursor ORDER BY id ASC LIMIT {limit}`. Holds no local cursor, performs no mutation, makes no network calls, and returns owned camelCase `serde::Serialize` export-row DTOs matching the shipped control-plane ingestion contract. See `context/sce/agent-trace-export-readers.md`. +- `context synchronization lifecycle`: Durable task-level state for synchronization after successful `/next-task` execution. The task record is `pending`, `synced`, or `blocked`; blocked records carry a blocker, required action, and retry condition. Missing lifecycle state on a completed task is unresolved debt, not evidence of synchronization. `/validate` does not persist a plan-level synchronization lifecycle. See `context/sce/shared-context-code-workflow.md`. diff --git a/context/overview.md b/context/overview.md index 89832012..5cab2171 100644 --- a/context/overview.md +++ b/context/overview.md @@ -1,9 +1,11 @@ # Overview -This repository maintains shared assistant configuration for OpenCode, Claude, and Pi from a single canonical Pkl authoring source. One typed workflow catalog owns the six workflows' shared identity and target routing metadata, while canonical workflow/phase modules own behavior and migrated package-local documents, and target renderers own formatting. Generated target layouts are ephemeral: repository builds consume a pre-Cargo generated payload through `SCE_CLI_GENERATED_INPUT_DIR`, crates.io and Flatpak stage packaging-only fallbacks, and `config/.opencode`, `config/.claude`, `config/.pi`, and the generated SCE config schema are not committed. The catalog also marks a workflow `optional` — currently only `brownfield` — which changes nothing about generation and is projected into a generated `config/optional-workflows.json` manifest for install-time consumers. `nix run .#pkl-check-generated` preserves its exact 101-path artifact, metadata/package, phase-reference, internal-reference, optional-workflow-manifest, workflow-orchestration, OpenCode-permission, required-path, and forbidden-path checks while delegating deterministic payload production and inventories to the shared generated-input producer; `nix flake check` runs the same contract. The target matrix contains one manual OpenCode profile plus Claude and Pi; the former automated OpenCode profile has been removed. +This repository maintains shared assistant configuration for OpenCode, Claude, and Pi from a single canonical Pkl authoring source. One typed workflow catalog owns the six workflows' shared identity and target routing metadata, while canonical workflow/phase modules own behavior and migrated package-local documents, and target renderers own formatting. Generated target layouts are ephemeral: repository builds consume a pre-Cargo generated payload through `SCE_CLI_GENERATED_INPUT_DIR`, crates.io and Flatpak stage packaging-only fallbacks, and `config/.opencode`, `config/.claude`, `config/.pi`, and the generated SCE config schema are not committed. The catalog also marks a workflow `optional` — currently only `brownfield` — which changes nothing about generation and is projected into a generated `config/optional-workflows.json` manifest for install-time consumers. `nix run .#pkl-check-generated` preserves its exact 107-path artifact, metadata/package, phase-reference, internal-reference, optional-workflow-manifest, workflow-orchestration, OpenCode-permission, required-path, and forbidden-path checks while delegating deterministic payload production and inventories to the shared generated-input producer; `nix flake check` runs the same contract. The target matrix contains one manual OpenCode profile plus Claude and Pi; the former automated OpenCode profile has been removed. It also includes a Rust CLI (`sce`) for Shared Context Engineering workflows: auth, config inspection, setup, doctor, agent-trace hooks, bash-policy evaluation, and trace database inspection. See `context/architecture.md` for module-level boundaries and `context/context-map.md` for the full domain file index. +The generated `/next-task` workflow persists task-level context-synchronization lifecycle state in each plan (`pending`, `synced`, or `blocked`) so unresolved task synchronization debt survives a session boundary and gates new implementation. Successful `/next-task` execution hands task synchronization an explicit, pre-edit-Git-baseline-relative changed-file list plus implementation, verification, done-check, plan-update, and context-impact evidence, recorded directly on the completed task (`Completed`, `Files changed`, `Result`, `Verify`, `Context impact`, `Context synchronization`); the five-file root context pass remains mandatory. A later-session sync-debt retry reads that same completed task record directly from the plan by plan path and task ID, with no separate persisted synchronization handoff. `/validate` is validation-only: it runs final checks, writes the Validation Report, and reports `validated`, `failed`, or `blocked` without plan-level context synchronization. + ## Key cross-cutting contracts - **Exit codes:** `2` parse, `3` validation, `4` runtime, `5` dependency failure (see `context/sce/cli-exit-code-contract.md`). @@ -54,9 +56,9 @@ The current supported automated release target matrix is `x86_64-unknown-linux-m The downstream publish-stage implementation is now complete for both registries: `.github/workflows/publish-crates.yml` publishes the checked-in crate version after `.version`/tag/Cargo parity checks, and `.github/workflows/publish-npm.yml` publishes the checked-in npm package after `.version`/tag/npm parity checks plus verification of the canonical `sce-v-npm.tgz` GitHub release asset. The repository root now also owns the canonical Biome contract for the current JavaScript tooling slice: `biome.json` scopes formatting/linting to `npm/` and the shared `config/lib/` plugin package root while excluding package-local `node_modules/`, and the root Nix dev shell provides the `biome` binary so contributors do not need a host-installed formatter/linter for those areas. Flatpak validation/build orchestration is reduced to a minimal app surface: Linux flake apps expose the umbrella `sce-flatpak` (`nix run .#sce-flatpak -- ` for `validate`, `prepare-local-manifest`, etc.) plus `release-flatpak-package`, `release-flatpak-bundle`, and the `regenerate-flatpak-manifest` / `regenerate-cargo-sources` helpers; the previously separate `flatpak-validate`, `flatpak-local-manifest`, and `flatpak-build` wrapper apps are removed. Default `nix flake check` keeps the lightweight Nix-built static/AppStream validator plus the parity checks (`flatpak-manifest-parity`, `cargo-sources-parity`) and does not run a network-heavy Flatpak build. The former standalone install-channel integration runner and `install-channel-integration-tests` flake app are not active current-state surfaces. -Shared Context Plan and Shared Context Code remain separate OpenCode routing roles: the generated Plan agent routes only to `/change-to-plan`, while the generated Code agent routes to `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield`. Workflow behavior lives in the six workflow entrypoints and their six skill packages rather than in agent bodies. `config/pkl/base/workflow-catalog.pkl` assigns each workflow to its role, and OpenCode command routing plus each agent's ordered `skill:` permissions derive from those records: ordinary non-SCE skills are allowed by the wildcard, arbitrary `sce-*` skills are denied, and only the role's owned workflows are allowed after that deny — `sce-change-to-plan` for Plan; `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield` for Code. The Code agent additionally allows `sce-decision` for synchronization. -The canonical workflow definitions remain phase-decomposed as authoring source: `/change-to-plan` sequences `sce-context-load` then `sce-plan-authoring`; `/next-task` sequences `sce-plan-review`, `sce-task-execution`, and `sce-task-context-sync`; `/validate` sequences `sce-validation` then `sce-plan-context-sync`; `/commit` delegates staged-diff analysis and message generation to `sce-atomic-commit`; `/handover` has no phases, since writer and loader mode has no SCE sibling handoff or wait mid-run; `/brownfield` likewise has none, since its single skill owns investigation, the blocking clarification gate, writing, and reporting itself. Relevant non-SCE skills may help inside an active workflow step, but they return control to that step without changing its canonical invariants. No target generates those phase modules as packages. All three consume them as inputs to the shared `workflow-composite.pkl` renderer, which composes each workflow into one skill package. Every workflow supplies typed package/composite render values for frontmatter, bodies, semantic references, phases, persisted-document formats where applicable, and output references; the composite renderer performs no prose-wide internalization or frontmatter stripping. -Every target preserves the same gates and lifecycle semantics through six renderer-composed workflow packages: `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`. Each thin command or Pi prompt invokes exactly one corresponding skill, and OpenCode command frontmatter names that single skill as both `entry-skill` and the whole `skills` chain. Each phase-based package keeps control flow, internal status branching, waits, and same-session resume in `SKILL.md`, while package-local Markdown references own phase instructions and persisted-document formats; `references/output.md` remains the sole definition of human-visible gates and terminal Markdown. Phase-free workflows retain `SKILL.md` plus `references/output.md`. No target emits phase-skill packages or inter-skill machine contracts; phase statuses stay internal to one skill invocation. +Shared Context Plan and Shared Context Code remain separate OpenCode routing roles: the generated Plan agent routes only to `/change-to-plan`, while the generated Code agent routes to `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield`. Workflow behavior lives in the six workflow entrypoints and their six skill packages rather than in agent bodies. `config/pkl/base/workflow-catalog.pkl` assigns each workflow to its role, and OpenCode command routing plus each agent's ordered `skill:` permissions derive from those records: ordinary non-SCE skills are allowed by the wildcard, arbitrary `sce-*` skills are denied, and only the role's owned workflows are allowed after that deny — `sce-change-to-plan` for Plan; `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield` for Code. The Code agent additionally allows `sce-decision` for task synchronization. +The canonical workflow definitions remain phase-decomposed as authoring source: `/change-to-plan` sequences `sce-context-load` then `sce-plan-authoring`; `/next-task` sequences `sce-plan-review`, `sce-task-execution`, and `sce-task-context-sync`; `/validate` runs `sce-validation` only and reports its Validation Report; `/commit` delegates staged-diff analysis and message generation to `sce-atomic-commit`; `/handover` has no phases, since writer and loader mode has no SCE sibling handoff or wait mid-run; `/brownfield` likewise has none, since its single skill owns investigation, the blocking clarification gate, writing, and reporting itself. Relevant non-SCE skills may help inside an active workflow step, but they return control to that step without changing its canonical invariants. No target generates those phase modules as packages. All three consume them as inputs to the shared `workflow-composite.pkl` renderer, which composes each workflow into one skill package. Every workflow supplies typed package/composite render values for frontmatter, bodies, semantic references, phases, persisted-document formats where applicable, and output references; the composite renderer performs no prose-wide internalization or frontmatter stripping. +Every target preserves the same gates and lifecycle semantics through six renderer-composed workflow packages: `sce-change-to-plan`, `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`. Each thin command or Pi prompt invokes exactly one corresponding skill, and OpenCode command frontmatter names that single skill as both `entry-skill` and the whole `skills` chain. Each phase-based package keeps control flow, internal status branching, waits, and same-session resume in `SKILL.md`, while package-local Markdown references own phase instructions and persisted-document formats; `references/output.md` remains the sole definition of human-visible gates and terminal Markdown. Phase-free `/handover` retains `SKILL.md`, `references/handover-template.md`, and `references/output.md`, while `/brownfield` retains `SKILL.md` plus `references/output.md`. No target emits phase-skill packages or inter-skill machine contracts; phase statuses stay internal to one skill invocation. Context sync uses an important-change gate: cross-cutting/policy/architecture/terminology changes require root shared-file edits, while localized tasks run verify-only root checks without default churn. OpenCode and Claude no longer generate legacy bootstrap or context-sync skills; `/commit` and `/handover` are generated only as catalog-registered composite workflow packages. OpenCode retains only thin routing agents, while Claude emits no agents. The superseded grouped Markdown catalog and automated OpenCode profile have been removed from Pkl ownership and generated outputs. The prior no-git-wrapper Agent Trace design artifacts under `context/sce/agent-trace-*.md` are retained only as historical reference; the current CLI runtime no longer wires the removed Agent Trace schema adaptation, payload building, retry replay, or rewrite handling paths into local hook execution. @@ -103,7 +105,7 @@ Lightweight post-task verification baseline (required after each completed task) ## Cross-target parity -- OpenCode, Claude, and Pi are generated from canonical Pkl content with per-target capability mapping. All three receive the same six command-routed workflow packages plus a standalone internal `sce-decision` package. The decision package contains `SKILL.md` and `references/adr-template.md`, defines one qualifying system-wide decision per immutable dated ADR, defaults new records to `Accepted`, and returns a written-or-blocked internal handoff. It has no user-facing command or prompt and is not part of the workflow catalog. Successful task and plan synchronization apply the system-wide decision gate before current-state context edits, reuse existing ADRs, and invoke `sce-decision` only for qualifying SCE decisions; non-SCE helper skills remain usable inside an active step without becoming workflow handoffs. Pi consumes exactly six thin prompts with no agent-role prompts and no added frontmatter. Manual OpenCode consumes exactly six commands plus two thin routing agents, and its Code agent alone allows internal `sce-decision` invocation. Claude consumes exactly six thin commands with no generated agents; its generated settings and hook helper remain. The four phase-based command-routed packages add package-local phase and persisted-document references beside `SKILL.md` and `references/output.md`; the two phase-free workflows retain their two-file packages. +- OpenCode, Claude, and Pi are generated from canonical Pkl content with per-target capability mapping. All three receive the same six command-routed workflow packages plus a standalone internal `sce-decision` package. The decision package contains `SKILL.md` and `references/adr-template.md`, defines one qualifying system-wide decision per immutable dated ADR, defaults new records to `Accepted`, reuses only equivalent active ADRs, and returns a written/not-qualified/skipped/blocked internal handoff. It has no user-facing command or prompt and is not part of the workflow catalog. Successful task synchronization applies the system-wide decision gate before current-state context edits, continues normally for nonqualifying or skipped decisions, and invokes `sce-decision` only for qualifying SCE decisions; non-SCE helper skills remain usable inside an active step without becoming workflow handoffs. Pi consumes exactly six thin prompts with no agent-role prompts and no added frontmatter. Manual OpenCode consumes exactly six commands plus two thin routing agents, and its Code agent alone allows internal `sce-decision` invocation. Claude consumes exactly six thin commands with no generated agents; its generated settings and hook helper remain. The four phase-based command-routed packages add package-local phase, persisted-document, and supporting references beside `SKILL.md` and `references/output.md`; `/handover` additionally carries its package-local persisted-format template, while `/brownfield` retains its two-file package. - When capabilities differ, parity is implemented by supported target-specific behavior rather than forcing unsupported fields. ## Context navigation diff --git a/context/patterns.md b/context/patterns.md index da3dd62c..0eee1c55 100644 --- a/context/patterns.md +++ b/context/patterns.md @@ -61,33 +61,35 @@ ## Pkl renderer layering -- Keep the six target-agnostic workflow packages in `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit,handover,brownfield}.pkl`, with shared package/composite frontmatter, body, semantic-reference, document, and composite-source primitives in `workflow-content.pkl`, workflow-specific behavior and migrated package-local documents in their canonical workflow modules, and shared task/plan synchronization policy in `workflow-context-sync.pkl`. Render context-sync `SKILL.md` content from explicit role data in named frontmatter, purpose, input, workflow, boundaries, and completion order, and render its report introduction, synced, no-context-change, blocked, and rules sections from a typed report role. Do not use numbered fragments or positional role/common interleaving for either document. Canonical workflow modules must consume the shared constructors rather than defining local equivalents. Do not reintroduce the removed grouped shared-content catalog or automated-profile variants. +- Keep the six target-agnostic workflow packages in `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit,handover,brownfield}.pkl`, with shared package/composite frontmatter, body, semantic-reference, document, and composite-source primitives in `workflow-content.pkl`, workflow-specific behavior and migrated package-local documents in their canonical workflow modules, and shared task synchronization policy in `workflow-context-sync.pkl`. Persist task synchronization lifecycle state in the plan template and let `/next-task` own the pending/terminal lifecycle writes around task synchronization; `/validate` owns validation and Validation Report writing only. Render context-sync `SKILL.md` content from explicit role data in named frontmatter, purpose, input, workflow, boundaries, and completion order, and render its report introduction, synced, no-context-change, blocked, and rules sections from a typed report role. Do not use numbered fragments or positional role/common interleaving for either document. Canonical workflow modules must consume the shared constructors rather than defining local equivalents. Do not reintroduce the removed grouped shared-content catalog or automated-profile variants. +- Keep final validation observational: `/validate` measures the completed implementation without deleting or repairing application, test, configuration, context, debug, temporary, or local-scaffolding artifacts; leftover debug/temp artifacts are failure evidence for a later implementation session. - Keep standalone internal skills that have no user-facing entrypoint outside `workflow-catalog.pkl`. The current `sce-decision` package is authored in `config/pkl/base/decision-skill.pkl`, rendered directly by each target with only supported frontmatter, and covered by explicit exact-path and required-content assertions. Do not manufacture a command or prompt merely to make an internal package fit the command workflow matrix. -- Keep accepted ADRs immutable. Record a correction, reversal, or replacement in a new dated ADR that references and supersedes the accepted record; default a new decision to `Accepted` unless its request explicitly names another allowed status. +- Keep every ADR immutable regardless of status. Reuse only an equivalent active `Proposed` or `Accepted` record; never reuse `Rejected`, `Deprecated`, or `Superseded` records. Record a correction, reversal, or changed decision in a new dated ADR that references and supersedes the prior record; keep `Deprecated` and `Superseded` as creation-time-only statuses and default a new decision to `Accepted` unless its request explicitly names another allowed status. Nonqualifying or deliberately skipped synchronization decisions are non-blocking; reserve `blocked` for unsafe decision-writing input or history. - Declare the six workflows' command slug, skill slug, title, description, argument hint, OpenCode routing role, Claude allowed tools, and `optional` flag exactly once in the typed `config/pkl/base/workflow-catalog.pkl`. Marking a workflow `optional` is an install-time statement only; it never conditions generation, and the manifest that carries it (`config/pkl/base/optional-workflow-manifest.pkl` → `config/optional-workflows.json`) is derived from the catalog rather than hand-listed. Composite, target-routing, permission, and coverage consumers must iterate or look up those records rather than maintain parallel per-workflow matrices. Adding a workflow is one catalog record plus one base module plus one composite registration; the only hand-maintained number is the exact generated-artifact count in `generation-contract-check.pkl`, which is deliberately literal so an unintended inventory change fails. - Keep cross-target generated-config primitives in focused base modules under `config/pkl/base/` and re-export them through `config/pkl/renderers/common.pkl` when multiple renderers need the same contract. A migrated workflow's package-local phase, persisted-document, and output references are canonical named document values in that workflow module, not parallel snapshots in `workflow-content.pkl` or the composite renderer. -- Model workflow skills as self-contained packages with deterministic package-relative document paths, including nested `references/` files. Shared Pkl skeletons may instantiate multiple skills, but every rendered skill must receive complete local documents. SCE workflow skills own SCE control flow and may use relevant non-SCE skills as in-step helpers; helpers return control to the active step and preserve its order, gates, waits, writes, validation, stops, and output. The only allowed SCE sibling dependency is `sce-decision`, invoked once per qualifying decision from successful task or plan synchronization. +- Model workflow skills as self-contained packages with deterministic package-relative document paths, including nested `references/` files. Shared Pkl skeletons may instantiate multiple skills, but every rendered skill must receive complete local documents. SCE workflow skills own SCE control flow and may use relevant non-SCE skills as in-step helpers; helpers return control to the active step and preserve its order, gates, waits, writes, validation, stops, and output. The only allowed SCE sibling dependency is `sce-decision`, invoked once per qualifying decision from successful task synchronization. - Use the project-root `.pi/` workflows as the behavioral baseline for canonical workflow packages; do not use generated target Markdown as an authoring source. - Keep OpenCode agents as thin routing surfaces when canonical workflow commands and skills own the behavior; do not duplicate workflow doctrine in agent bodies. - Implement target-specific formatting in dedicated renderer modules under `config/pkl/renderers/`, but keep workflow composition itself in the shared `workflow-composite.pkl`. Every canonical workflow must choose package-vs-composite wording through typed semantic references before Markdown assembly, not by stripping frontmatter or post-processing prose. Require a structured composite source for each workflow and do not reintroduce a legacy rewrite path. A target parameterizes composition only through the frontmatter lines it supports, never by forking the composition logic. -- Compose each canonical workflow into one workflow-level skill package for every generated target; keep phase status handling internal to `SKILL.md`, emit no phase-skill package, and emit exactly one `references/output.md` containing that workflow's human-visible Markdown layouts. For the four phase-based workflows, emit named package-local phase and persisted-document references and require `SKILL.md` to read each reference before its phase runs. Phase-free workflows retain their two-file package shape. -- Keep cross-workflow agent-behavior policy such as the no-improvisation and non-SCE helper-composition rules in the shared `renderSkill` preamble of `workflow-composite.pkl`, which every workflow and target already composes through. Do not copy the same prose into the four canonical `workflow-*.pkl` modules or into per-workflow `references/output.md` bodies, and do not add a strictness knob or per-workflow override for it. +- Compose each canonical workflow into one workflow-level skill package for every generated target; keep phase status handling internal to `SKILL.md`, emit no phase-skill package, and emit exactly one `references/output.md` containing that workflow's human-visible Markdown layouts. For the four phase-based workflows, emit named package-local phase, persisted-document, and supporting references and require `SKILL.md` to read each reference before its phase runs. Phase-free `/handover` also emits its package-local persisted-format template; `/brownfield` retains the two-file package shape. `/commit` emits exactly `references/{atomic-commit.md,commit-message-style.md,output.md}`: the atomic reference owns staged-diff procedure, internal result branching, and commit boundaries, the style reference owns message wording, and the output reference owns human-visible layouts. +- Keep cross-workflow agent-behavior policy such as the no-improvisation and non-SCE helper-composition rules in the shared `renderSkill` preamble of `workflow-composite.pkl`, which every workflow and target already composes through. Its generic control-flow wording says that any workflow-defined user wait resumes the same skill in the same session; workflow-specific wait semantics stay in the owning workflow. Do not copy the same prose into the four canonical `workflow-*.pkl` modules or into per-workflow `references/output.md` bodies, and do not add a strictness knob or per-workflow override for it. - Keep shared renderer document and plugin-registration contracts in `config/pkl/renderers/common.pkl`; workflow identity metadata belongs in the typed workflow catalog rather than renderer-local description or routing maps. - Keep only actively consumed target metadata in dedicated modules (`opencode-metadata.pkl` and `claude-metadata.pkl`); Pi needs no metadata module because it adds no target-specific frontmatter. - Add OpenCode machine-readable orchestration metadata in `config/pkl/renderers/opencode-content.pkl`: catalog-derived `agent`, `entry-skill`, and a `skills` chain naming that command's single workflow skill. In `opencode-metadata.pkl`, derive ordered agent skill permissions from catalog role assignments: allow `*` for ordinary non-SCE skills, deny `sce-*`, then allow only the role's owned workflow skills; derive the additional `sce-decision` permission only for the Code agent. - Keep `config/pkl/renderers/metadata-coverage-check.pkl` as a fail-fast exact-inventory guard deriving command slugs, skill entrypoints, and package-local workflow paths from the typed catalog, while independently retaining the expected OpenCode agent inventory and per-target one-to-one command-to-workflow-skill route assertions; run it whenever workflow documents or target metadata change. -- Keep `config/pkl/renderers/generation-contract-check.pkl` independent of `generate.pkl` output assembly when deriving expected paths: build the exact target paths from renderer document inventories, name retained non-workflow assets explicitly, compare against all `output.files`, require every phase-based `SKILL.md` to cite each emitted phase reference, require the shared non-SCE helper-composition rule and SCE-scoped workflow prohibitions in every generated workflow skill, enforce the exact catalog-derived OpenCode skill permission order, and reject stale phase-skill slugs or unresolved package-local reference tokens in generated workflow entrypoint `SKILL.md` documents. Package-local reference prose is allowed to mention its own persisted-format history. It also rejects two or more consecutive blank lines in generated workflow entrypoint `SKILL.md` documents (`no-blank-line-runs`) and any `SKILL.md` that reproduces a sibling `references/output.md` fenced layout verbatim (`output-dedup`). Preserve controlled negative fixtures for extra paths, missing paths, forbidden references, missing helper composition, unscoped prohibitions, arbitrary or misordered OpenCode permissions, blank-line runs, and duplicated output layouts. +- Keep `config/pkl/renderers/generation-contract-check.pkl` independent of `generate.pkl` output assembly when deriving expected paths: build the exact target paths from renderer document inventories, name retained non-workflow assets explicitly, compare against all `output.files`, require every phase-based `SKILL.md` to cite each emitted phase reference, require the shared non-SCE helper-composition rule and SCE-scoped workflow prohibitions in every generated workflow skill, enforce the exact catalog-derived OpenCode skill permission order, and reject stale phase-skill slugs or unresolved package-local reference tokens in generated workflow entrypoint `SKILL.md` documents. Package-local reference prose is allowed to mention its own persisted-format history. It also rejects two or more consecutive blank lines in generated workflow entrypoint `SKILL.md` documents (`no-blank-line-runs`), any `SKILL.md` that reproduces a sibling `references/output.md` fenced layout verbatim (`output-dedup`), and nineteen semantic violations covering layout headings, package-local paths, forbidden files, consolidated commit content, report ownership, target parity, stale sync-debt wording, observational validation, OpenCode permission-artifact integrity, plan-review sync-debt-recovery reference wording, the compact completed-task record model (`compact-plan-template-schema`, `next-task-compact-completion-writing`, `plan-review-reads-completed-record`, and `context-sync-validates-task-record`, which together replaced the removed persisted-handoff `handoff-identity-fields` check), sync-debt-recovery branch reference-before-invocation ordering, the debt scan's all-completed-task scope, sync-debt blocked-outcome layout routing, and the `sce-validate` package excluding any `sce-decision` reference or plan-context-sync wording. Preserve controlled negative fixtures for each of these contracts. - Workflow renderers may extend canonical frontmatter only with target-supported metadata, must preserve behavior, and append only the required final newline at the output mapping. Structured composition renders semantic package/composite differences at their source while preserving one owner for every canonical gate, branch, write, and continuation. Composite mode emits a document's body only: frontmatter is a package-mode concern, so an embedded command or phase contributes no `name:`, `description:`, or `argument-hint:` line to the composed `SKILL.md`. Suppression happens in the typed model, never by parsing or stripping Markdown markers. Every reference a rendered document states must resolve in the mode that states it: composite text may name a section embedded in the same `SKILL.md` or the sibling `references/output.md`, but a sentence whose only target is a package-mode file — a `references/*-contract.yaml`, a removed `.md`, or the composed workflow itself — is package-only and its composite spelling drops the sentence rather than pointing at nothing. A phase's terminal internal states are named by its own steps, so dropping such a pointer removes no instruction. Migrate one workflow at a time and compare its OpenCode, Claude, and Pi paths against a retained pre-task root. Byte-identical generated payload is the regression guard for refactors that must preserve output; when a change intentionally alters generated text, the guard becomes the reviewed diff against that retained root, showing only the intended additions and removals. -- Every target's commands (Pi: prompts) must stay thin and invoke exactly one corresponding workflow skill (`sce-change-to-plan`, `sce-next-task`, `sce-validate`, or `sce-commit`). They must not sequence phase skills. The workflow skill executes package-local phases directly, after reading the applicable reference, and keeps phase statuses as internal state. Relevant non-SCE helper skills may run inside the active step only as helpers that return control to that step; only the successful task/plan synchronization decision gate may invoke sibling SCE `sce-decision`, preserving that exception as exact rather than general SCE orchestration. -- A phase-based workflow package contains `SKILL.md`, `references/output.md`, and named package-local references for its phase instructions and persisted-file templates. `SKILL.md` alone owns ordering, branching, waits, and same-session resume; it reads the applicable reference before phase side effects. Phase-free workflows contain exactly `SKILL.md` and `references/output.md`. Put every and only human-visible gate, report, and terminal response layout in `output.md`. State each layout exactly once inside `output.md`: when a phase's return-value layout and a workflow branch's layout describe the same output, keep the fuller statement (the one carrying every status variant, field, and report rule) and let the other place point at that section by heading instead of restating it. An `output.md` section must not reference a document outside its own package; a mode-aware semantic reference is how composite text names the embedded section that replaced a package-mode file. A composed `SKILL.md` states no layout of its own: a branch that produces user-visible output names the `output.md` section it renders (`Render the **{Section}** layout from `references/output.md`.`) and keeps only what `output.md` does not carry — the branch condition, the field mapping that fills the layout, the wait, and every prohibition. Model this per layout as a mode-aware semantic reference whose package spelling is the inline block a standalone command file still owns and whose composite spelling is the citation; do not achieve it by deleting text the sibling `output.md` never states. Wait points such as bootstrap, clarification, revision, implementation approval, and failed-validation repair remain real same-session turn boundaries owned by the composite skill. +- Every target's commands (Pi: prompts) must stay thin and invoke exactly one corresponding workflow skill (`sce-change-to-plan`, `sce-next-task`, `sce-validate`, or `sce-commit`). They must not sequence phase skills. The workflow skill executes package-local phases directly, after reading the applicable reference, and keeps phase statuses as internal state. Relevant non-SCE helper skills may run inside the active step only as helpers that return control to that step; only the successful task-synchronization decision gate may invoke sibling SCE `sce-decision`, preserving that exception as exact rather than general SCE orchestration. +- A phase-based workflow package contains `SKILL.md`, `references/output.md`, and named package-local references for its phase instructions and persisted-file templates. `SKILL.md` alone owns ordering, branching, waits, and same-session resume; it reads the applicable reference before phase side effects. Phase-free `/brownfield` contains exactly `SKILL.md` and `references/output.md`; `/handover` additionally emits `references/handover-template.md`. Put every and only human-visible gate, report, and terminal response layout in `output.md`. State each layout exactly once inside `output.md`: when a phase's return-value layout and a workflow branch's layout describe the same output, keep the fuller statement (the one carrying every status variant, field, and report rule) and let the other place point at that section by heading instead of restating it. An `output.md` section must not reference a document outside its own package; a mode-aware semantic reference is how composite text names the embedded section that replaced a package-mode file. A composed `SKILL.md` states no layout of its own: a branch that produces user-visible output names the `output.md` section it renders (`Render the **{Section}** layout from `references/output.md`.`) and keeps only what `output.md` does not carry — the branch condition, the field mapping that fills the layout, the wait, and every prohibition. Model this per layout as a mode-aware semantic reference whose package spelling is the inline block a standalone command file still owns and whose composite spelling is the citation; do not achieve it by deleting text the sibling `output.md` never states. Wait points such as bootstrap, clarification, revision, implementation approval, and failed-validation repair remain real same-session turn boundaries owned by the composite skill. - A phase-based workflow `SKILL.md` reads top to bottom as the control-flow document. Keep the phase-reference inventory ahead of `## Input`; state which reference each numbered workflow step reads before running; keep phase instructions and persisted-document formats in those named references rather than duplicating them in `SKILL.md`. A workflow that reaches the same phase from more than one step reuses one reference. Phase-free workflow rendering continues through the generic composite body path. +- Keep cross-phase handoffs explicit and authoritative: task execution captures a pre-edit Git baseline, reports changed files relative to that baseline with its implementation and verification evidence, and task context synchronization consumes that attribution rather than scanning the whole working tree. Keep the mandatory five-root-file context pass unconditional. - Keep the Markdown renderer contract in `config/pkl/renderers/common.pkl` limited to deterministic `frontmatter + body` assembly without injected generated-file marker text. - Validate each renderer module directly with `nix develop -c pkl eval ` before wiring output emission. ## Thin command orchestration - Keep every target's commands and Pi prompts equally thin: parse nothing and invoke exactly one workflow-level skill. The composite skill owns input parsing, every phase, user waits, writes, verification, synchronization, and continuation rendering. No command sequences phases. -- Keep the canonical phase sequence in the workflow modules, not in command bodies: `/change-to-plan` runs context load then plan authoring; `/next-task` runs review, execution, and task context sync; `/validate` runs validation then validated-only plan context sync; `/commit` runs staged-diff analysis and message authoring. Each sequence executes inside its one composed skill. +- Keep the canonical phase sequence in the workflow modules, not in command bodies: `/change-to-plan` runs context load then plan authoring; `/next-task` runs review, execution, and task context sync; `/validate` runs validation only and reports the Validation Report; `/commit` runs staged-diff analysis and message authoring. Each sequence executes inside its one composed skill. - Preserve mandatory gates and authoritative internal state while removing duplicated behavior from agents, commands, or sibling packages. ## Multi-file generation entrypoint @@ -95,7 +97,7 @@ - Use `config/pkl/generate.pkl` as the single generation module for authored config outputs. Flatten self-contained workflow skill documents as `{skill slug}/{package-relative path}` so nested references are emitted deterministically without sibling-package dependencies. - Use `config/pkl/README.md` as the contributor-facing runbook for prerequisites, ownership boundaries, regeneration steps, and troubleshooting. - Run multi-file generation only into an explicit temporary output root, for example `nix run .#pkl-generate -- "$(mktemp -d)"`; never evaluate with `-m .`. -- Run ephemeral generation validation through `nix run .#pkl-check-generated`; it wraps the dev-shell script, rejects committed target/schema/mirror outputs, evaluates exact metadata plus the complete 101-path artifact/reference contract, the optional-workflow manifest assertion, and its negative fixtures, requires all supported target roots, and delegates canonical input discovery, two-pass generation, and inventories to `scripts/produce-cli-generated-input.sh`. +- Run ephemeral generation validation through `nix run .#pkl-check-generated`; it wraps the dev-shell script, rejects committed target/schema/mirror outputs, evaluates exact metadata plus the complete 107-path artifact/reference contract, the optional-workflow manifest assertion, and its negative fixtures, requires all supported target roots, and delegates canonical input discovery, two-pass generation, and inventories to `scripts/produce-cli-generated-input.sh`. - Keep this contract anchored to the root `nix flake check` `pkl-generated` derivation. Removed target paths are forbidden repository artifacts even though the same path names remain valid inside temporary payload roots. - Treat `nix run .#pkl-check-generated` and `nix flake check` as the lightweight post-task verification baseline and run both after each completed task. - Keep `output.files` limited to payload-relative paths (`config/.opencode/{agent,command,skills,lib,plugins,opencode.json}`, `config/.claude/{commands,skills,hooks,settings.json}` with no Claude agents, `config/.pi/{prompts,skills,extensions}`, and the generated schema). Do not emit `config/automated/.opencode`. diff --git a/context/plans/emit-composite-workflow-references.md b/context/plans/emit-composite-workflow-references.md new file mode 100644 index 00000000..ddd4580a --- /dev/null +++ b/context/plans/emit-composite-workflow-references.md @@ -0,0 +1,186 @@ +# Plan: emit-composite-workflow-references + +## Change summary + +Complete the composite workflow package inventories emitted by the canonical Pkl +sources. The generated `/commit` packages currently contain +`references/atomic-commit.md` and `references/output.md`, but the phase document +still points at `references/commit-message-style.md` and +`references/commit-contract.yaml` without emitting either file. The generated +`/next-task` and `/validate` packages have the same missing-document problem for +`references/sync-report.md`. + +Add those package-local documents to the composite inventories and remove the +stale `references/validation-result.md` name from validation content. The +validation result is already emitted as `references/output.md`; retaining the +old name makes the generated validation references point at a file that does not +exist. Update the derived inventory and generation-contract assertions so these +missing-file regressions fail deterministically. + +## Acceptance criteria + +- [x] AC1: Every generated `sce-commit` package contains + `SKILL.md`, `references/atomic-commit.md`, `references/output.md`, + `references/commit-message-style.md`, and `references/commit-contract.yaml`, + and the commit phase reference resolves both newly emitted documents. + - Validate: Generate an ephemeral payload and inspect the OpenCode, Claude, and + Pi `sce-commit` directories with `find`; verify the two files exist and + `grep` confirms the references resolve within each package. +- [x] AC2: Every generated `sce-next-task` and `sce-validate` package contains a + package-local `references/sync-report.md`, and each context-sync reference + points to that emitted document. + - Validate: Generate an ephemeral payload and inspect all six target package + directories; verify `references/sync-report.md` exists and run a package-local + reference check for every `references/sync-report.md` citation. +- [x] AC3: No generated workflow document or active canonical composite reference + names `references/validation-result.md`; validation documents consistently + use the emitted `references/output.md` result document instead. + - Validate: Generate an ephemeral payload and `grep` all generated workflow + documents for `validation-result.md` (expect no matches), then inspect the + canonical validation module for the same stale path. +- [x] AC4: The generated artifact inventory and metadata coverage contract include + the new package-local documents and reject missing or extra paths + deterministically. + - Validate: `nix run .#pkl-check-generated` reports the updated exact inventory + successfully, including the increased artifact count and complete workflow + document coverage. + +### Full validation + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/overview.md` (current generated workflow-package inventory and the + absence of stale validation-result references) +- `context/architecture.md` (canonical Pkl package documents, composite + materialization, and generated artifact-count contract) +- `context/patterns.md` (package-local reference resolution and exact inventory + guard rules) +- `context/glossary.md` (workflow output/reference terminology and package + shape) + +## Constraints and non-goals + +- **In scope:** canonical workflow Pkl modules for commit, next-task, validate, + and context-sync; `config/pkl/renderers/metadata-coverage-check.pkl`; + `config/pkl/renderers/generation-contract-check.pkl`; and any narrowly + necessary durable context updates during `/validate` synchronization. +- **Out of scope:** Rust CLI behavior, target-specific runtime plugins, changes to + workflow gates or status semantics, new dependencies, and manual edits to the + root `.claude/`, `.pi/`, or `.opencode/` runtime-managed mirrors. +- **Constraints:** Use typed package/composite document inventories and + mode-aware semantic references; do not restore phase-skill packages or solve + missing references with prose-wide rewriting. Generated output remains + ephemeral and must be checked in a temporary root. +- **Non-goal:** Redesigning the validation report or synchronization report + content; this change only makes their emitted paths and citations resolve. + +## Assumptions + +- `references/output.md` is the composite validation result document, so replacing + `references/validation-result.md` with `references/output.md` preserves the + validation contract rather than changing its layout. +- The requested emitted files belong in each target's composite workflow package, + not in the non-generated root runtime mirrors; canonical Pkl changes and + ephemeral generation are the source of truth. +- The existing exact artifact count must increase by the number of newly emitted + target files: two commit documents plus one synchronization report for each of + the next-task and validate packages across three targets. + +## Task stack + +- [x] T01: `Emit missing commit and synchronization reference documents` (status:done) + - Task ID: T01 + - Goal: Add `commit-message-style.md`, `commit-contract.yaml`, and the + role-specific `sync-report.md` documents to the composite package inventories + and update derived document-path and exact-artifact-count contracts. + - Boundaries (in/out of scope): In — + `config/pkl/base/workflow-commit.pkl`, + `config/pkl/base/workflow-next-task.pkl`, + `config/pkl/base/workflow-validate.pkl`, + `config/pkl/renderers/metadata-coverage-check.pkl`, and + `config/pkl/renderers/generation-contract-check.pkl`. Out — validation-result + cleanup and all generated/root target trees. + - Dependencies: none + - Done when: Ephemeral OpenCode, Claude, and Pi payloads each contain the two + requested commit references plus the applicable synchronization report in + the correct package-relative paths, and the metadata/generation contracts + derive and accept the expanded inventory. + - Verification notes (commands or checks): Evaluate the affected Pkl modules + through `nix develop -c pkl eval`; generate a temporary payload and inspect + the six affected package inventories; run the targeted generated-output + contract check. + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-commit.pkl`, `config/pkl/base/workflow-next-task.pkl`, `config/pkl/base/workflow-validate.pkl`, `config/pkl/renderers/metadata-coverage-check.pkl`, `config/pkl/renderers/generation-contract-check.pkl` + - Evidence: Affected Pkl modules evaluated successfully; generated payload inspection confirmed all six affected package inventories and new citations; `nix run .#pkl-check-generated` passed with 113 files. + - Notes: Composite inventories now emit the two commit support documents and role-specific synchronization reports across OpenCode, Claude, and Pi; the exact artifact contract is updated from 101 to 113 paths. Stale validation-result cleanup remains assigned to T02. + +- [x] T02: `Remove stale validation-result references` (status:done) + - Task ID: T02 + - Goal: Make validation package references resolve to `references/output.md` + and remove the obsolete `references/validation-result.md` document/reference + from the canonical validation package representation. + - Boundaries (in/out of scope): In — + `config/pkl/base/workflow-validate.pkl` and narrowly related generated + reference assertions. Out — changing validation output fields, report + layouts, validation status behavior, or synchronization semantics. + - Dependencies: T01 + - Done when: No generated workflow document or active canonical composite text + names `references/validation-result.md`; the validation package still emits + exactly one `references/output.md` containing the existing result layouts, + and all validation references resolve locally. + - Verification notes (commands or checks): Generate an ephemeral payload; + `grep` generated workflow documents and the affected canonical Pkl source + for `validation-result.md` expecting no matches; inspect the validation + package inventory and run `nix run .#pkl-check-generated`. + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-validate.pkl` + - Evidence: Repointed the validation result reference and package document to + `references/output.md`, removed all stale `validation-result.md` text from + the canonical validation module, and preserved the existing result layouts. + Ephemeral generation produced `sce-validate/references/output.md` without a + `references/validation-result.md` file for OpenCode, Claude, and Pi. + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/workflow-validate.pkl` passed; generated workflow inspection + found no `validation-result.md` matches and confirmed the output document; + `nix run .#pkl-check-generated` passed with 113 files. + +## Open questions + +None. The requested document names and the existing Pkl package/composite +boundary determine the implementation surface; the only local choice is to +preserve `references/output.md` as the validation result owner, which matches +current generated inventories and repository patterns. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-12 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral generation and exact 113-file contract passed) +- `nix flake check` -> exit 0 (all flake checks passed) +- `nix run .#pkl-generate -- ` -> exit 0 (generated OpenCode, Claude, and Pi payloads) +- Ephemeral `find`/`grep` package acceptance inspection -> exit 0 (AC1–AC3 assertions passed across all target packages) + +### Scaffolding removed + +- None. + +### Success-criteria verification + +- [x] AC1: Every generated `sce-commit` package contains the required support documents and resolves both commit references -> all three target packages contained the five required files and both citations resolved. +- [x] AC2: Every generated `sce-next-task` and `sce-validate` package contains and cites `references/sync-report.md` -> the document existed in all six packages and each package-local context-sync reference cited it. +- [x] AC3: No generated workflow document or active canonical validation text names `references/validation-result.md` -> generated workflow inspection and canonical validation-module inspection found no matches. +- [x] AC4: The generated artifact inventory and metadata coverage contract include the expanded package documents -> `nix run .#pkl-check-generated` passed with the exact 113-file inventory. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/plans/next-task-sync-debt-recovery-refactor.md b/context/plans/next-task-sync-debt-recovery-refactor.md new file mode 100644 index 00000000..49b3deb9 --- /dev/null +++ b/context/plans/next-task-sync-debt-recovery-refactor.md @@ -0,0 +1,388 @@ +# Plan: next-task-sync-debt-recovery-refactor + +## Change summary + +Fixes five remaining review findings against PR #205 in the canonical Pkl +workflow sources under `config/pkl/base/**` and `config/pkl/renderers/**`, the +sole source of truth for the generated `.opencode`, `.claude`, and `.pi` +`sce-next-task` package. This extends the synchronization-lifecycle and +handoff-persistence behavior the already-validated +`sync-handoff-recovery-and-generation-fixes` plan established; it does not +replace the `pending`/`synced`/`blocked` model or introduce a second +persistence mechanism. + +1. The persisted "Context synchronization handoff" carries changed files, + implementation summary, verification, done checks, and context impact, but + task-context-sync's own validation still requires a `plan` object with a + `path` and exactly one identified task — fields the persisted handoff does + not itself carry. A cross-session retry currently depends on plan-review + supplying that identity out of band rather than the handoff being + self-contained. This plan adds the resolved plan path and task ID/title to + the persisted handoff and requires them at validation. +2. `sce-plan-review` currently detects earlier-completed-task synchronization + debt and then invokes the Task context synchronization phase itself, + without ever citing `references/context-sync.md` — the file that owns that + phase's steps and boundaries, per the "read a step's reference before + running it" rule stated at the top of every generated `SKILL.md`. This + plan turns plan review back into a read-only detect-and-report phase and + adds an explicit top-level recovery branch in `/next-task` that loads + `references/context-sync.md` before invoking the phase it owns. +3. The debt scan is currently scoped to tasks *earlier* than the one being + selected, which does not match the accepted lifecycle invariant recorded in + `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md`: no new + implementation may start while *any* completed task has unresolved + synchronization debt, regardless of its position relative to the task being + selected. This plan widens the scan to every completed task in plan order. +4. When recovery's own call to task-context-sync returns `blocked`, the + workflow currently reports it through the generic **Review blocked** + layout (plan review's own blocked branch) instead of the existing + **Context synchronization blocked** layout, discarding the sync report's + blocker/required-action/retry-condition shape. This plan routes a + recovery-time block through the sync-specific layout. +5. None of the above has a semantic generation-contract check or fixture, so a + future edit can silently regress any of them. This plan adds one check per + behavior, following the existing `assertX` + fixture + `check-generated.sh` + registration pattern already used for the prior plan's checks. + +## Acceptance criteria + +- [x] AC1: A persisted "Context synchronization handoff" carries `Plan path`, + `Task ID`, and `Task title` fields alongside the existing changed + files/summary/verification/done-checks/context-impact fields, and + task-context-sync's handoff validation requires those three fields to be + present in the handoff itself — not supplied out of band by the caller — + before treating a persisted retry handoff as valid. + - Validate: generated `sce-change-to-plan/references/plan-template.md` + completion-record example shows `Plan path`, `Task ID`, and `Task title` + inside the `Context synchronization handoff` line; generated + `sce-next-task/references/task-execution.md` instructs writing them; + generated `sce-next-task/references/context-sync.md` step "Validate the + handoff" lists them as required fields for a persisted handoff. +- [x] AC2: Sync-debt recovery is an explicit top-level `/next-task` workflow + branch, not behavior hidden inside plan review. The branch is reached + before normal task selection when debt is detected, explicitly states + reading `references/context-sync.md` before invoking the Task context + synchronization phase, writes `synced` or refreshed `blocked` lifecycle + state to the plan, and then either resumes plan review (to select a task + normally) or stops. `references/plan-review.md` no longer instructs + invoking the Task context synchronization phase itself; it only detects and + reports debt. + - Validate: generated `sce-next-task/SKILL.md` contains a recovery branch + that cites `references/context-sync.md` before any instruction to run the + Task context synchronization phase; generated + `sce-next-task/references/plan-review.md` no longer contains an + instruction to run that phase; new semantic check (name chosen by the + implementing task) plus its negative fixture pass. +- [x] AC3: The synchronization-debt scan inspects every completed task in + plan order before allowing a new implementation task to start, not only + tasks earlier than the one being selected or resumed. + - Validate: generated `sce-next-task/references/plan-review.md` states the + scan covers every completed task regardless of position, with no + "earlier completed task" position-relative scoping language remaining; + new semantic check plus negative fixture (using the old + position-scoped wording) pass. +- [x] AC4: When the recovery branch's own call to task-context-sync returns + `blocked`, `/next-task` renders the existing **Context synchronization + blocked** layout — preserving the sync report's blocker, required action, + retry condition, and preserved context edits — not the generic **Review + blocked** layout. + - Validate: generated `sce-next-task/SKILL.md` routes the recovery branch's + `blocked` outcome to the **Context synchronization blocked** layout + citation, distinct from plan review's own `blocked` branch; new semantic + check plus negative fixture pass. +- [x] AC5: One semantic generation-contract check and one negative fixture + exists for each of AC1's validation requirement, AC2's reference-loading + requirement, AC3's all-completed-tasks scope, and AC4's blocked-output + routing, registered in `config/pkl/check-generated.sh` alongside the + existing checks. + - Validate: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + passes with the new checks present; each new negative fixture throws when + evaluated directly; `nix run .#pkl-check-generated` passes. +- [x] AC6: Every claim in this plan's Context sync files still matches the + regenerated behavior across all three targets, and no target has fallen out + of parity. + - Validate: a temporary full regeneration (`nix run .#pkl-generate -- "$(mktemp -d)"`) + shows byte-identical `sce-next-task` package content under `.claude`, + `.opencode`, and `.pi` apart from supported per-target frontmatter. + +### Full validation + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/sce/shared-context-code-workflow.md` (`/next-task` phase-ownership + section: recovery is a top-level branch, not plan-review behavior; the scan + covers all completed tasks, not only earlier ones) +- `context/architecture.md` (generation-contract-check semantic-check count + and description) +- `context/glossary.md` (`baseline-relative task handoff` entry: add plan + path and task identity to what the handoff carries) +- `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` + (reference only; this plan implements that decision's already-recorded + "any completed task" invariant more precisely and does not change the + decision itself) + +## Context synchronization lifecycle + +- **Plan context synchronization:** synced + +## Constraints and non-goals + +- **In scope:** `config/pkl/base/workflow-next-task.pkl`, + `config/pkl/base/workflow-context-sync.pkl`, + `config/pkl/base/workflow-change-to-plan.pkl`, + `config/pkl/renderers/generation-contract-check.pkl`, + `config/pkl/renderers/fixtures/*.pkl`, and the fixture registration list in + `config/pkl/check-generated.sh`. Ephemeral regeneration of `.opencode`, + `.claude`, and `.pi` outputs through the normal generation pipeline. + Durable context updates limited to the files named under Context sync. +- **Out of scope:** `sce-validate`'s plan-role synchronization (`/validate` + never runs sync-debt recovery); `sce-handover`, `sce-brownfield`, and + `sce-decision` packages; any CLI Rust code under `cli/`; the bypass-commit + temp-file rule and the layout-reference checker fix (already resolved by + `sync-handoff-recovery-and-generation-fixes`); optional-workflow + install-time semantics. +- **Constraints:** `config/pkl/**` remains the sole source of truth; never + hand-edit generated `.pi/**`, `.claude/**`, or `.opencode/**` output; keep + the plan/completion-record Markdown format consistent with + `references/plan-template.md`; keep package-vs-composite rendering parity; + keep the `pending | synced | blocked` lifecycle values and the + Markdown-only persistence mechanism unchanged. +- **Non-goal:** Redesigning the synchronization lifecycle beyond making the + persisted handoff self-contained and making recovery an explicit, + reference-citing workflow branch; changing plan-role (`/validate`) + synchronization; adding new lifecycle values beyond + `pending`/`synced`/`blocked`. + +## Task stack + +- [x] T01: `Persist plan path and task identity in the synchronization handoff` (status:done) + - Task ID: T01 + - Goal: In `config/pkl/base/workflow-change-to-plan.pkl`'s plan template, + add `Plan path`, `Task ID`, and `Task title` fields to the + `Context synchronization handoff` completion-record line, and in + `config/pkl/base/workflow-next-task.pkl`'s task-execution phase (2.7 + "Update the plan"), instruct writing those three fields into the handoff + subsection alongside the existing changed-files/summary/verification/ + done-checks/context-impact fields. + - Boundaries (in/out of scope): In — the plan-template completion-record + example (package and composite) and the task-execution write instruction + (package and composite). Out — context-sync's read/validation side + (T02); plan-review's read side (T03). + - Dependencies: none + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` + and `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` + succeed; a temporary generation shows the completion-record example with + `Plan path`, `Task ID`, and `Task title` inside the handoff line, and + `sce-next-task/references/task-execution.md` instructs writing them. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl`; `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl`; targeted generation and grep for the three field names in both files. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T01; Task title: Persist plan path and task identity in the synchronization handoff; Changed files: config/pkl/base/workflow-change-to-plan.pkl, config/pkl/base/workflow-next-task.pkl; Implementation summary: Added `Plan path`, `Task ID`, and `Task title` fields to the `Context synchronization handoff` completion-record line in both the composite and package renderings of `workflow-change-to-plan.pkl`'s plan template, and updated the "Update the plan" (2.7) write instruction in both the composite and package renderings of `workflow-next-task.pkl` to state the handoff subsection includes the resolved plan path and task ID/title alongside the existing fields; Verification: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` passed, `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` passed, temporary `nix run .#pkl-generate` confirmed all three targets (`.claude`, `.opencode`, `.pi`) render the new fields in `sce-change-to-plan/references/plan-template.md` and `sce-next-task/references/task-execution.md`; Done checks: both pkl evals succeed (done), generated plan template and task-execution.md show the three fields (done); Context impact: none — this task only changes generation-source Pkl content whose downstream durable-context implications are covered by T06's closing regeneration/reconciliation pass + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-change-to-plan.pkl, config/pkl/base/workflow-next-task.pkl + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` (exit 0); `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` (exit 0); `nix run .#pkl-generate -- ` (exit 0) followed by grep confirming `Plan path: {path}; Task ID: {id}; Task title: {title}` in `sce-change-to-plan/references/plan-template.md` and "the resolved plan path, the task ID and title" in `sce-next-task/references/task-execution.md` across `.claude`, `.opencode`, and `.pi` + - Notes: none + +- [x] T02: `Require persisted plan/task identity at context-sync validation and in the blocked-report shape` (status:done) + - Task ID: T02 + - Goal: In the task role of `config/pkl/base/workflow-context-sync.pkl`, + require `Plan path`, `Task ID`, and `Task title` as present-in-the-handoff + fields at "Validate the handoff" (3.1) for a persisted retry handoff — so + validation succeeds using only the handoff text itself, without depending + on an out-of-band plan/task value supplied by the caller — and update the + blocked-report rendering to name those fields consistently with T01's + plan-template shape. + - Boundaries (in/out of scope): In — task-role "Validate the handoff" step + and blocked-report rendering in `workflow-context-sync.pkl` (package and + composite). Out — plan-role (`/validate`) synchronization, unaffected. + - Dependencies: T01 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` + succeeds; generated `sce-next-task/references/context-sync.md` lists + `Plan path`, `Task ID`, and `Task title` as required fields of a + persisted handoff at validation, and its blocked-report section names + them. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl`; targeted generation and grep of `sce-next-task/references/context-sync.md` for the new required-field wording. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T02; Task title: Require persisted plan/task identity at context-sync validation and in the blocked-report shape; Changed files: config/pkl/base/workflow-context-sync.pkl; Implementation summary: In `config/pkl/base/workflow-context-sync.pkl`'s task role, updated step 1 "Validate the handoff" (both the mode-based composite/package render function and the standalone `taskReference` string that generates `sce-next-task/references/context-sync.md`) to state that a persisted retry handoff satisfies the plan-identity and task-identity requirements via its own `Plan path`, `Task ID`, and `Task title` fields rather than an out-of-band value from the caller. Updated step 8's blocked-report description in both renderings to name `Plan path`, `Task ID`, and `Task title` alongside the existing changed-files/summary/verification/done-checks/context-impact fields. Added `Plan path`, `Task ID`, and `Task title` bullets to `taskReport.blockedHandoffSection`, which renders the actual blocked-report template in `references/sync-report.md`, matching T01's plan-template field order; Verification: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` passed; temporary `nix run .#pkl-generate` confirmed `sce-next-task/references/context-sync.md` and `references/sync-report.md` carry the new wording/fields identically across `.claude`, `.opencode`, and `.pi`; `nix run .#pkl-check-generated` passed; Done checks: pkl eval succeeds (done), generated context-sync.md lists the three required fields at validation and names them in the blocked-report section (done); Context impact: none — this task only changes generation-source Pkl content whose downstream durable-context implications are covered by T06's closing regeneration/reconciliation pass + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-context-sync.pkl + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` (exit 0); `nix run .#pkl-generate -- ` (exit 0) followed by grep confirming "carries its own `Plan path` field" and "carries its own `Task ID` and `Task title` fields" in `sce-next-task/references/context-sync.md`, and `Plan path: {plan path}` / `Task ID: {task id}` / `Task title: {task title}` bullets in `references/sync-report.md`, byte-identical across `.claude`, `.opencode`, and `.pi`; `nix run .#pkl-check-generated` (exit 0, "Ephemeral Pkl generation passed: 107 files") + - Notes: none + +- [x] T03: `Turn plan review back into a read-only debt detector covering every completed task` (status:done) + - Task ID: T03 + - Goal: In the plan-review phase of `config/pkl/base/workflow-next-task.pkl`, + change the synchronization-debt scan (1.2 "Resolve one task") to inspect + every completed task's `Context synchronization` field in plan order — + not only tasks earlier than the one being selected — and stop invoking + the Task context synchronization phase directly. When the first task + carrying debt has no durable `Context synchronization handoff` + subsection, still return `blocked` immediately with the legacy-migration + required action (this needs no recovery invocation). Otherwise, return a + new status (for example `sync_debt`) naming the debt task, its persisted + handoff, and its persisted blocker when present, without running or + citing the Task context synchronization phase. Restore plan review's + "reads; never writes" framing now that it no longer performs recovery + writes itself. + - Boundaries (in/out of scope): In — plan-review phase steps in + `workflow-next-task.pkl` (package and composite): the debt-scan scope and + the return-status change. Out — the new top-level recovery branch that + consumes the `sync_debt` status (T04); context-sync's own behavior (T02). + - Dependencies: T01, T02 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` + succeeds; generated `sce-next-task/references/plan-review.md` states the + scan covers every completed task regardless of position, no longer + instructs running the Task context synchronization phase, and still + states the legacy-no-handoff blocked case with its migration required + action. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl`; targeted generation and grep of `sce-next-task/references/plan-review.md` for "every completed task" and the absence of Task-context-synchronization-phase invocation wording. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T03; Task title: Turn plan review back into a read-only debt detector covering every completed task; Changed files: config/pkl/base/workflow-next-task.pkl; Implementation summary: In `config/pkl/base/workflow-next-task.pkl`'s `nextTaskPlanReviewReference` literal (the sole source of the generated `sce-next-task/references/plan-review.md`, confirmed byte-identical across `.claude`, `.opencode`, and `.pi`), widened the synchronization-debt scan in "1.2 Resolve one task" from "earlier completed task" to "every completed task ... regardless of its position relative to the task being selected or resumed"; replaced the direct "Run the **Task context synchronization phase**" invocation with a new `sync_debt` internal status that names the debt task's ID/title, its persisted `Context synchronization handoff`, and its persisted `Context synchronization blocker` when present, without running or citing the Task context synchronization phase; added `sync_debt` to "1.5 Return the result"'s internal-state list with its own required-fields bullet; and restored plan review's read-only "Plan review boundaries" framing by removing the "except to persist a synchronization-debt recovery outcome" and "except to retry ... unresolved synchronization debt" exceptions, and changed the phase's opening description from "It reads, and writes only to persist a synchronization-debt recovery outcome for an earlier completed task, per 1.2." to "It reads; it never writes." Left the parallel mode-based `renderReviewSkillBody`/`REVIEW_SKILL`/`planReviewPackage` scaffold in the same file untouched: confirmed dead code, since its `workflow.skills` mapping is never read by any of the three target renderers or by `generation-contract-check.pkl`, and its distinctive intro text does not appear anywhere in currently generated output; Verification: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` passed; `nix run .#pkl-generate -- ` confirmed the generated `sce-next-task/references/plan-review.md` is byte-identical across `.claude`, `.opencode`, and `.pi` (md5sum 7f331ce36149070e7afb1608c5b7ced7), states the scan covers "every completed task" with no "earlier completed task" wording remaining, and no longer contains "Run the **Task context synchronization phase**"; `nix run .#pkl-check-generated` passed ("Ephemeral Pkl generation passed: 107 files"), including the pre-existing `next-task-sync-debt-recovery-check.pkl` fixture; Done checks: pkl eval succeeds (done), generated plan-review.md states the scan covers every completed task regardless of position (done), no longer instructs running the Task context synchronization phase (done), still states the legacy-no-handoff blocked case with its migration required action (done, unchanged); Context impact: none — this task only changes generation-source Pkl content whose downstream durable-context implications (shared-context-code-workflow.md's phase-ownership description) are deferred to T06's closing regeneration/reconciliation pass, once T04's top-level recovery branch completes the described behavior + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-next-task.pkl + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` (exit 0); `nix run .#pkl-generate -- ` (exit 0) followed by grep confirming "every completed task" wording and the absence of "earlier completed task" / direct sync-phase-invocation wording in `sce-next-task/references/plan-review.md` across `.claude`, `.opencode`, and `.pi` (byte-identical, md5sum 7f331ce36149070e7afb1608c5b7ced7); `nix run .#pkl-check-generated` (exit 0, "Ephemeral Pkl generation passed: 107 files") + - Notes: none + +- [x] T04: `Add an explicit sync-debt-recovery branch to /next-task that loads context-sync.md and routes its own blocked result to the sync-specific layout` (status:done) + - Task ID: T04 + - Goal: In the composed `SKILL.md` workflow section of + `config/pkl/base/workflow-next-task.pkl`, add a top-level branch reached + when plan review returns the `sync_debt` status from T03, positioned + before normal task selection resumes. The branch must explicitly state + reading `references/context-sync.md` before invoking the Task context + synchronization phase with the persisted handoff, write `synced` + (clearing blocker fields) or a refreshed `blocked` lifecycle state to the + plan depending on the result, and then either re-invoke plan review to + resume normal task selection (on success) or render the existing + **Context synchronization blocked** layout and stop (on a renewed + block) — not the **Review blocked** layout plan review's own `blocked` + branch uses. + - Boundaries (in/out of scope): In — the top-level `Workflow` section of + the composed `SKILL.md` in `workflow-next-task.pkl` (package and + composite): the new branch, its reference-loading statement, its + lifecycle-state write, and its output routing. Out — plan review's own + detection logic (T03, already complete); context-sync's own steps (T02, + already complete). + - Dependencies: T03 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` + succeeds; generated `sce-next-task/SKILL.md` contains the recovery branch + citing `references/context-sync.md` before invoking the Task context + synchronization phase, and its `blocked` outcome renders the + **Context synchronization blocked** layout distinctly from plan review's + `blocked` branch. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl`; targeted generation and inspection of `sce-next-task/SKILL.md`'s Workflow section for the new branch, its reference citation, and its output routing. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T04; Task title: Add an explicit sync-debt-recovery branch to /next-task that loads context-sync.md and routes its own blocked result to the sync-specific layout; Changed files: config/pkl/base/workflow-content.pkl; Implementation summary: The composed composite `SKILL.md` body for `/next-task` is not stored in `workflow-next-task.pkl` itself — it is the `nextTaskSkillBody` literal in `config/pkl/base/workflow-content.pkl`, consumed directly as `compositeSkillBody` by `config/pkl/renderers/workflow-composite.pkl`'s `renderSkill`, bypassing the dead package-mode scaffold in `workflow-next-task.pkl` that T03 already confirmed is unread by every target renderer. Edited `nextTaskSkillBody`'s step-1 "Branch on `status`" list to add a new `sync_debt` branch, positioned between the existing `blocked` and `plan_complete` branches: it states reading `references/context-sync.md` before running the **Task context synchronization phase** with the debt task's persisted `Context synchronization handoff` (and persisted `Context synchronization blocker` when present) named by the **Plan review phase**; it writes the debt task's lifecycle to the plan (`synced` clearing blocker/required-action/retry-condition, or a refreshed `blocked` state) before branching on the outcome; its `blocked` outcome renders the existing **Context synchronization blocked** layout from `references/output.md`, explicitly distinguished from the **Review blocked** layout used by plan review's own `blocked` branch, and stops without selecting another task; its `synced`/`no_context_change` outcome re-invokes the **Plan review phase** to resume normal task selection. Also updated the immediately adjacent stale summary paragraph (previously "every earlier completed task ... blocks implementation until the plan records the blocker") to match T03's already-implemented behavior: the scan covers every completed task regardless of position, a legacy handoff-less debt task still returns `blocked` directly, and any other debt task returns `sync_debt` resolved by the new branch — this paragraph directly restated the same mechanism the new branch depends on, so leaving the pre-T03 wording in place would have made the section self-contradictory. Left the dead package-mode scaffold in `workflow-next-task.pkl` (confirmed unread by any renderer, per T03) untouched, consistent with T03's precedent; Verification: `nix develop -c pkl eval config/pkl/base/workflow-content.pkl` passed, `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` passed, temporary `nix run .#pkl-generate -- ` confirmed generated `sce-next-task/SKILL.md` across `.claude`, `.opencode`, and `.pi` carries byte-identical bodies (frontmatter differing only in the per-target `compatibility` line) containing the `sync_debt` branch that cites `references/context-sync.md` before invoking the Task context synchronization phase and routes its `blocked` outcome to the **Context synchronization blocked** layout distinctly from **Review blocked**, `nix run .#pkl-check-generated` passed ("Ephemeral Pkl generation passed: 107 files"), `nix flake check` passed ("all checks passed!"); Done checks: both pkl evals succeed (done), generated `sce-next-task/SKILL.md` contains the recovery branch citing `references/context-sync.md` before invoking the Task context synchronization phase (done), its `blocked` outcome renders the **Context synchronization blocked** layout distinctly from plan review's `blocked` branch (done); Context impact: none — this task only changes generation-source Pkl content whose downstream durable-context implications (shared-context-code-workflow.md's phase-ownership description) are deferred to T06's closing regeneration/reconciliation pass + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-content.pkl + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-content.pkl` (exit 0); `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` (exit 0); `nix run .#pkl-generate -- ` (exit 0) followed by diff confirming byte-identical `sce-next-task/SKILL.md` bodies across `.claude`, `.opencode`, and `.pi`, each containing the new `sync_debt` branch, its `references/context-sync.md` citation, and its distinct **Context synchronization blocked** routing; `nix run .#pkl-check-generated` (exit 0, "Ephemeral Pkl generation passed: 107 files"); `nix flake check` (exit 0, "all checks passed!") + - Notes: The plan's Goal/Boundaries text for this task named `config/pkl/base/workflow-next-task.pkl` as the file carrying "the composed `SKILL.md` workflow section"; the actual source is `nextTaskSkillBody` in `config/pkl/base/workflow-content.pkl` (confirmed via `config/pkl/renderers/workflow-composite.pkl`'s `renderSkill`, which uses `structuredSource.compositeSkillBody` directly whenever it is non-null, which it is for `next-task`). Treated as an ordinary local implementation-detail correction rather than a blocking scope question, since the required generated-output change was unambiguous from the acceptance criteria and `config/pkl/**` remains the sole source of truth either way. + +- [x] T05: `Add semantic generation-contract checks and fixtures for the handoff, reference-loading, all-tasks-scope, and blocked-routing behaviors` (status:done) + - Task ID: T05 + - Goal: In `config/pkl/renderers/generation-contract-check.pkl`, add one + semantic check per behavior added by T01-T04: (a) a persisted handoff + example and context-sync's validation wording both name `Plan path`, + `Task ID`, and `Task title`; (b) the generated `sce-next-task/SKILL.md` + recovery branch cites `references/context-sync.md` before any instruction + to run the Task context synchronization phase; (c) + `sce-next-task/references/plan-review.md` states the debt scan covers + every completed task, with no surviving "earlier completed task" + position-relative wording; (d) the recovery branch's `blocked` outcome + resolves to the **Context synchronization blocked** layout citation, not + **Review blocked**. Add one negative fixture per check (following the + existing `assertX` + fixture-stub + `check-generated.sh` pattern already + used for `plan-review-sync-debt-recovery` and + `wrong-file-layout-reference-check`), and register each in + `config/pkl/check-generated.sh`. + - Boundaries (in/out of scope): In — the four new `assertX` functions, + their fixtures under `config/pkl/renderers/fixtures/`, and their + `check-generated.sh` registration. Out — any existing check or fixture + not touched by this plan. + - Dependencies: T04 + - Done when: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + succeeds with all four new checks present; each new negative fixture + throws when evaluated directly with a diagnostic naming the missing + behavior; the real generated output passes all four; `nix run .#pkl-check-generated` + passes. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl`; `nix develop -c pkl eval config/pkl/renderers/fixtures/.pkl` (expect throw); `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T05; Task title: Add semantic generation-contract checks and fixtures for the handoff, reference-loading, all-tasks-scope, and blocked-routing behaviors; Changed files: config/pkl/renderers/generation-contract-check.pkl, config/pkl/check-generated.sh, config/pkl/renderers/fixtures/handoff-identity-fields-check.pkl, config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl, config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl, config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl; Implementation summary: In `config/pkl/renderers/generation-contract-check.pkl`, added four new `hidden assertX` functions immediately after the existing `assertPlanReviewSyncDebtRecovery`: `assertHandoffIdentityFields` checks that `sce-change-to-plan/references/plan-template.md` contains the literal `Plan path: {path}; Task ID: {id}; Task title: {title}` handoff-line fragment and that `sce-next-task/references/context-sync.md` contains both `carries its own \`Plan path\` field` and `carries its own \`Task ID\` and \`Task title\` fields` (AC1); `assertSyncDebtRecoveryBranch` isolates the \`sync_debt\` branch paragraph in `sce-next-task/SKILL.md` and asserts the index of `references/context-sync.md` precedes the index of `Task context synchronization phase` within it (AC2); `assertPlanReviewAllTasksScope` checks `plan-review.md` contains `every completed task` and does not contain `earlier completed task` (AC3); `assertSyncDebtBlockedRouting` isolates the `sync_debt` branch's `Branch on the outcome:` section and, reusing the existing `layoutReferencesIn` regex helper, asserts its first layout citation names `Context synchronization blocked` rather than `Review blocked` (AC4). Registered all four in the `contractChecks` mapping. Added one negative fixture per check under `config/pkl/renderers/fixtures/`, each importing `generation-contract-check.pkl` and calling the corresponding `assertX` directly on a minimal `Mapping` engineered to fail that specific behavior, following the existing `next-task-sync-debt-recovery-check.pkl`/`wrong-file-layout-reference-check.pkl` pattern; registered each in `config/pkl/check-generated.sh` via `expect_pkl_fixture_failure` with the exact thrown diagnostic (AC5). Staged the four new fixture files with `git add` (no commit) because `nix flake check`'s `pkl-generated-check` derivation builds from the git-tracked source tree and could not otherwise see the new untracked files; Verification: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` passed with all four new checks present and passing against the real generated content; each of the four new fixtures evaluated directly (`nix develop -c pkl eval config/pkl/renderers/fixtures/.pkl`) threw with its exact expected diagnostic; `nix run .#pkl-check-generated` passed ("Ephemeral Pkl generation passed: 107 files"); `nix flake check` passed ("all checks passed!"); Done checks: pkl eval succeeds with all four new checks present (done), each new negative fixture throws with a diagnostic naming the missing behavior (done), the real generated output passes all four (done), `nix run .#pkl-check-generated` passes (done); Context impact: none — this task only adds generation-source Pkl semantic checks and fixtures whose downstream durable-context implications (shared-context-code-workflow.md's phase-ownership description, architecture.md's generation-contract-check semantic-check count) are deferred to T06's closing regeneration/reconciliation pass, consistent with T01-T04's precedent + - Completed: 2026-08-13 + - Files changed: config/pkl/renderers/generation-contract-check.pkl, config/pkl/check-generated.sh, config/pkl/renderers/fixtures/handoff-identity-fields-check.pkl, config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl, config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl, config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl + - Evidence: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` (exit 0, all four new checks — `handoff-identity-fields`, `sync-debt-recovery-branch`, `plan-review-all-tasks-scope`, `sync-debt-blocked-routing` — present and passing); four fixture evaluations each exiting non-zero with their exact registered diagnostic; `nix run .#pkl-check-generated` (exit 0, "Ephemeral Pkl generation passed: 107 files"); `nix flake check` (exit 0, "all checks passed!") + - Notes: none + +- [x] T06: `Regenerate all targets, verify cross-target parity, and reconcile durable context` (status:done) + - Task ID: T06 + - Goal: Run a full temporary regeneration and confirm `sce-next-task` + package content is identical across `.claude`, `.opencode`, and `.pi` + apart from supported per-target frontmatter, with no target retaining + stale "earlier completed task" wording, a plan-review-invoked sync call, + or a recovery block routed to the generic Review blocked layout. Update + `context/sce/shared-context-code-workflow.md`, + `context/architecture.md`, and `context/glossary.md` to describe the + resulting `/next-task` phase-ownership and handoff-identity behavior. + - Boundaries (in/out of scope): In — the closing regeneration/verification + pass and the named durable-context files. Out — any other context file; + any ADR (this plan implements the existing lifecycle decision more + precisely and does not change it). + - Dependencies: T01, T02, T03, T04, T05 + - Done when: `nix run .#pkl-check-generated` and `nix flake check` both + pass; manual inspection of a temporary full generation confirms cross + target parity and the absence of stale wording named above; the three + Context sync files accurately describe the new behavior. + - Verification notes (commands or checks): `nix run .#pkl-generate -- "$(mktemp -d)"`; `nix run .#pkl-check-generated`; `nix flake check`; grep across `.claude`/`.opencode`/`.pi` `sce-next-task` output for stale wording. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/next-task-sync-debt-recovery-refactor.md; Task ID: T06; Task title: Regenerate all targets, verify cross-target parity, and reconcile durable context; Changed files: context/architecture.md, context/glossary.md, context/sce/shared-context-code-workflow.md; Implementation summary: Ran a full temporary regeneration (`nix run .#pkl-generate -- "$(mktemp -d)"`) and confirmed the generated `sce-next-task` package (`SKILL.md` plus `references/{plan-review,task-execution,context-sync,sync-report,output}.md`) is byte-identical across `.claude`, `.opencode`, and `.pi` apart from the supported per-target `compatibility` frontmatter line; grepped the generated output and confirmed no "earlier completed task" wording survives, `plan-review.md`'s "1.2 Resolve one task" states the scan covers "every completed task" and explicitly states it does not run or cite the Task context synchronization phase, and the generated `SKILL.md`'s `sync_debt` branch cites `references/context-sync.md` before running the Task context synchronization phase and routes its `blocked` outcome to the **Context synchronization blocked** layout, distinct from **Review blocked**. Updated `context/sce/shared-context-code-workflow.md`'s `/next-task` phase-ownership section (`sce-plan-review` entry) to describe plan review as a read-only debt detector covering every completed task regardless of position, returning `sync_debt` rather than retrying synchronization itself, and added a new bullet describing the top-level sync-debt-recovery branch (reads `references/context-sync.md`, runs task context synchronization, writes `synced`/`blocked` lifecycle state, and routes a renewed block to **Context synchronization blocked**). Updated `context/architecture.md`'s generation-contract-check description: raised the itemized semantic-check count from eleven to fifteen and named the four new checks (`handoff-identity-fields`, `sync-debt-recovery-branch`, `plan-review-all-tasks-scope`, `sync-debt-blocked-routing`) with what each asserts. Updated `context/glossary.md`'s `baseline-relative task handoff` entry to describe the persisted `Context synchronization handoff` completion-record field's `Plan path`/`Task ID`/`Task title` fields and their self-contained cross-session-retry purpose; Verification: `nix run .#pkl-generate -- "$(mktemp -d)"` (exit 0) followed by diff confirming byte-identical `sce-next-task` bodies across all three targets; `nix run .#pkl-check-generated` passed ("Ephemeral Pkl generation passed: 107 files"); `nix flake check` passed ("all checks passed!"); grep confirmed absence of "earlier completed task" and confirmed the `sync_debt` branch's reference-before-invocation ordering and blocked-layout routing; Done checks: `nix run .#pkl-check-generated` passes (done), `nix flake check` passes (done), manual inspection of the temporary full generation confirms cross-target parity and the absence of stale wording (done), the three Context sync files accurately describe the new behavior (done); Context impact: current-state — this task's own edits are the durable-context reconciliation the plan's Context sync section named (`shared-context-code-workflow.md`, `architecture.md`, `glossary.md`); no ADR applies, since this plan implements the already-recorded `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` "any completed task" invariant more precisely rather than changing the decision itself + - Completed: 2026-08-13 + - Files changed: context/architecture.md, context/glossary.md, context/sce/shared-context-code-workflow.md, context/patterns.md + - Evidence: `nix run .#pkl-generate -- "$(mktemp -d)"` (exit 0) plus diff confirming byte-identical `sce-next-task` package bodies across `.claude`/`.opencode`/`.pi` (frontmatter `compatibility` line excepted); `nix run .#pkl-check-generated` (exit 0, "Ephemeral Pkl generation passed: 107 files, inventory sha256 9b1906b124f0a3c3c380ccad557229b3b45c273f133b0af4c0f10bbbdfe0a10e"); `nix flake check` (exit 0, "all checks passed!"); grep of generated output confirming no "earlier completed task" wording, plan-review.md's "every completed task" scope wording and "Do not run or cite the Task context synchronization phase" statement, and SKILL.md's `sync_debt` branch citing `references/context-sync.md` before "Task context synchronization phase" and routing `blocked` to "Context synchronization blocked" distinct from "Review blocked" + - Notes: The context-synchronization phase's mandatory five-root-file pass (`overview.md`, `architecture.md`, `glossary.md`, `patterns.md`, `context-map.md`) found `patterns.md`'s `generation-contract-check.pkl` bullet still stated "eleven semantic violations" with a list omitting the four checks T05 added; corrected it to "fifteen" and named all four alongside the existing ones. `overview.md` and `context-map.md` were verified accurate at their existing generality and needed no edit. + +## Open questions + +None. The request names the exact persisted fields, the exact routing defect, +the exact scope defect, and the exact fixture-based testing approach this +repository already uses for its other semantic generation checks, so no +scope, acceptance-criteria, or ordering decision remains open. The precise +name chosen for plan review's new non-`blocked` status (`sync_debt` above is +illustrative) is an ordinary local implementation choice left to T03/T04, +consistent with existing status-naming conventions (`ready`, `blocked`, +`plan_complete`) in the same file. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-13 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (Ephemeral Pkl generation passed: 107 files, inventory sha256 9b1906b124f0a3c3c380ccad557229b3b45c273f133b0af4c0f10bbbdfe0a10e) +- `nix flake check` -> exit 0 (all checks passed!) +- `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` -> exit 0 (all 28 contract checks, including the four new ones, present and passing) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/handoff-identity-fields-check.pkl` -> exit 1 (throws expected diagnostic: "persisted handoff must carry Plan path, Task ID, and Task title, and context-sync validation must require them from the handoff itself") +- `nix develop -c pkl eval config/pkl/renderers/fixtures/sync-debt-recovery-branch-check.pkl` -> exit 1 (throws expected diagnostic: "sce-next-task SKILL.md sync-debt recovery branch must cite references/context-sync.md before invoking the Task context synchronization phase") +- `nix develop -c pkl eval config/pkl/renderers/fixtures/plan-review-all-tasks-scope-check.pkl` -> exit 1 (throws expected diagnostic: "sce-next-task plan-review reference must state the synchronization-debt scan covers every completed task, with no earlier-completed-task position-relative wording remaining") +- `nix develop -c pkl eval config/pkl/renderers/fixtures/sync-debt-blocked-routing-check.pkl` -> exit 1 (throws expected diagnostic: "sce-next-task SKILL.md sync-debt recovery blocked outcome must route to the Context synchronization blocked layout, not Review blocked") +- `nix run .#pkl-generate -- "$(mktemp -d)"` -> exit 0 (temporary full regeneration for cross-target inspection) + +### Success-criteria verification + +- [x] AC1: Persisted handoff carries `Plan path`/`Task ID`/`Task title` and context-sync validation requires them from the handoff itself -> `sce-change-to-plan/references/plan-template.md` line 171 shows `Context synchronization handoff: Plan path: {path}; Task ID: {id}; Task title: {title}; ...`; `sce-next-task/references/context-sync.md` "3.1 Validate the handoff" states a persisted retry handoff "carries its own `Plan path` field" and "carries its own `Task ID` and `Task title` fields", met by the handoff text itself +- [x] AC2: Sync-debt recovery is an explicit top-level `/next-task` branch citing `references/context-sync.md` before invoking the phase; plan review is read-only -> generated `SKILL.md` line 88 reads "Read `references/context-sync.md`, then run the **Task context synchronization phase**..."; generated `plan-review.md` states "Do not run or cite the Task context synchronization phase. Stop." +- [x] AC3: Debt scan covers every completed task regardless of position -> generated `plan-review.md` states "inspect every completed task's" and "Only after every completed task is `synced` does task selection proceed"; no "earlier completed task" wording remains +- [x] AC4: Recovery branch's own `blocked` outcome routes to **Context synchronization blocked**, not **Review blocked** -> generated `SKILL.md` line 92: "`blocked` -> Render the **Context synchronization blocked** layout ..., distinct from the **Review blocked** layout above." +- [x] AC5: One semantic check + negative fixture per behavior, registered in `check-generated.sh` -> `generation-contract-check.pkl` contains `handoff-identity-fields`, `sync-debt-recovery-branch`, `plan-review-all-tasks-scope`, `sync-debt-blocked-routing`, all passing against real generated output; each corresponding fixture throws its expected diagnostic when evaluated directly; `nix run .#pkl-check-generated` passes +- [x] AC6: `sce-next-task` package content is byte-identical across `.claude`, `.opencode`, and `.pi` apart from per-target frontmatter -> temporary regeneration diffed: `SKILL.md` differs only in the `compatibility:` frontmatter line; `references/{plan-review,task-execution,context-sync,sync-report,output}.md` are byte-identical across all three targets + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. + diff --git a/context/plans/remove-validate-context-sync.md b/context/plans/remove-validate-context-sync.md new file mode 100644 index 00000000..1eb17459 --- /dev/null +++ b/context/plans/remove-validate-context-sync.md @@ -0,0 +1,589 @@ +# Plan: remove-validate-context-sync + +## Change summary + +Canonicalize the currently staged `.pi/` workflow-reference changes in Pkl and +make the generator emit the same behavior for all three ephemeral target +payloads under `config/.pi/`, `config/.claude/`, and `config/.opencode/`. The +repository-root integration directories remain installation destinations owned +by `sce setup`; Pkl generation must not write them directly. + +The main workflow change is to remove plan-level context synchronization from +`/validate`: `sce-validate` will no longer generate or read a context-sync +reference, invoke `sce-plan-context-sync`, gate validation completion on sync +lifecycle fields, or claim that durable context was synchronized. It will stop +after recording and reporting validation. Task-level synchronization owned by +`/next-task` remains unchanged. + +The staged commit-reference changes are also canonicalized: `/commit` will use a +separate `references/commit-message-style.md`, while `references/atomic-commit.md` +will no longer carry the removed YAML result-contract section. Generation and +coverage checks will be updated so the resulting target payloads stay +consistent and deterministic. + +## Acceptance criteria + +- [x] AC1: A fresh Pkl generation writes only ephemeral payloads below the + `config/` generation root, including `.pi`, `.claude`, and `.opencode`, and + does not create or modify repository-root integration directories; `sce setup` + remains the documented installer of those root files. + - Validate: `nix run .#pkl-generate -- "$(mktemp -d)"`; inspect the generated + `config/.{pi,claude,opencode}` paths and confirm repository-root target + directories are not generation destinations. +- [x] AC2: Generated `sce-validate` contains no plan-context-sync invocation, + no `sce-plan-context-sync` handoff, and no package-local `context-sync.md`; + validation no longer requires completed-task sync fields or writes a + plan-level sync state before returning `validated`, and its completion output + does not claim durable context synchronization. + - Validate: inspect generated `sce-validate/SKILL.md` and its references for + the removed phase, call, lifecycle gate, and synchronization claim; assert + the generated `sce-validate/references/` inventory contains only the + validation and output/report documents defined by the canonical source. +- [x] AC3: Generated `/commit` packages contain + `references/commit-message-style.md`, and `references/atomic-commit.md` + delegates message-style rules to that file without the removed + `references/commit-contract.yaml` return section; all three target packages + preserve the staged reference content semantically. + - Validate: generate all targets and inspect each `sce-commit` reference + inventory and the atomic-commit/style documents; confirm the forbidden YAML + contract-return section is absent. +- [x] AC4: Generation metadata, package-relative reference checks, and exact + artifact/path assertions describe the new validate and commit inventories; + target-neutral workflow documents remain equivalent apart from supported + frontmatter, and setup can consume the generated payload without requiring + repository-root generated trees. + - Validate: `nix develop -c pkl eval + config/pkl/renderers/metadata-coverage-check.pkl`; `nix run + .#pkl-check-generated`; inspect the generated-input handoff paths. +- [x] AC5: Durable workflow documentation accurately states that `/next-task` + still owns task context synchronization while `/validate` performs validation + only and does not synchronize plan context; the commit reference ownership + description matches the new split. + - Validate: inspect the affected `context/sce/` and root context files and + confirm no current-state document says `/validate` invokes plan context + synchronization or that the removed commit contract remains generated. +- [x] AC6: The `/validate` catalog description, the plan template's + context-synchronization lifecycle section, `sce-decision`'s accepted + callers, and the generation contract's decision-invoking workflow list are + all validation-only-consistent with the accepted + `2026-08-13-validate-validation-only` ADR: the catalog description states + `/validate` records final validation evidence rather than synchronizing + durable context; the plan template documents only a task-level context + synchronization lifecycle, with no `Plan context synchronization` field or + `/validate`-sets-synced-or-blocked claim; `sce-decision` accepts requests + only from `sce-next-task` task context synchronization; and the generation + contract treats only `sce-next-task` as decision-invoking, asserting + generated `sce-validate` documents contain neither a `sce-decision` + reference nor plan-context-sync wording. + - Validate: inspect `workflow-catalog.pkl`, `workflow-change-to-plan.pkl`, + `decision-skill.pkl`, and `generation-contract-check.pkl`; generate all + three targets and inspect the emitted `/validate` description, plan + template, and `sce-decision` package; run `nix develop -c pkl eval + config/pkl/renderers/generation-contract-check.pkl`, `nix run + .#pkl-check-generated`, and `nix flake check`. + +### Full validation + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/sce/shared-context-code-workflow.md` +- `context/sce/context-workflow-rules.md` +- `context/sce/atomic-commit-workflow.md` +- `context/overview.md` +- `context/architecture.md` +- `context/patterns.md` +- `context/glossary.md` +- `context/context-map.md` when workflow ownership or a decision link changes +- A new or superseding ADR if synchronization determines that removing the + plan-context-sync lifecycle or changing the commit-reference contract is a + qualifying system-wide compatibility/ownership decision. + +## Constraints and non-goals + +- **In scope:** Canonical Pkl workflow sources for validate and commit; + workflow-composite integration where required; metadata and generation + contract checks; ephemeral generated payload verification; and durable + workflow documentation. +- **Out of scope:** Direct generation into `.pi/`, `.claude/`, or `.opencode/` + at repository root; changes to `sce setup` installation mechanics; Rust CLI + behavior; `/next-task` task-context synchronization behavior; and deletion of + the shared task-context-sync implementation. +- **Constraints:** `config/pkl/**` is the canonical source; root integration + files are installed from generated assets by `sce setup`; generated payloads + are inspected in temporary/config-derived locations; preserve validation's + full acceptance-criteria and repository-check behavior; preserve commit + mode routing and staged-diff semantics; use Nix-owned generation and checks. +- **Non-goal:** Replacing plan synchronization with another automatic context + synchronization mechanism. This change intentionally leaves `/validate` + without a plan-context-sync phase; any future replacement requires a separate + contract. + +## Assumptions + +- The user's clarification means `/validate` ends after the validation phase + writes its Validation Report and returns `validated`, `failed`, or `blocked`; + it no longer produces a downstream context-impact handoff or completion claim + that context is synchronized. +- The staged `.pi/` commit-reference edits are intentional desired behavior and + must be represented in canonical Pkl rather than copied into generated target + trees by hand. +- Task-level context synchronization remains required for successful + `/next-task` execution and is not removed by this plan. +- The existing `sce setup` asset pipeline consumes the ephemeral generated + payload and installs the corresponding root target files, so no new installer + behavior is needed. + +## Task stack + +- [x] T01: `Remove plan context synchronization from the validate workflow` (status:complete) + - Task ID: T01 + - Goal: Remove the plan-context-sync phase, invocation, package-local reference, + lifecycle gate, context-impact handoff, and synchronization completion claim + from the canonical `/validate` workflow while retaining validation and + Validation Report behavior. + - Boundaries (in/out of scope): In — `workflow-validate.pkl`, its composed + package/reference inventory, validation result and completion layouts, and + any directly required shared-model wiring. Out — task context synchronization + in `workflow-context-sync.pkl` and `/next-task` behavior. + - Dependencies: none + - Done when: generated validate skills contain no context-sync call or + `sce-plan-context-sync` reference; validation no longer blocks on task/plan + sync lifecycle state; validated completion reports final validation and the + report path without claiming durable context synchronization; all validation + statuses remain defined. + - Verification notes (commands or checks): direct Pkl evaluation of + `workflow-validate.pkl`; temporary generation; focused searches over all + three generated `sce-validate` packages; package-relative inventory review. + - Implementation evidence: removed plan-level context synchronization from + `workflow-validate.pkl` and the composed validate workflow in + `workflow-content.pkl`; retained validation, report writing, and all three + terminal statuses. + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/workflow-validate.pkl` passed; temporary generation emitted + only `validation.md`, `validation-report.md`, and `output.md` under each + target's `sce-validate/references/`; focused forbidden-content searches + passed across `.pi`, `.claude`, and `.opencode`. + - Context synchronization: synced (manually confirmed; durable context + realigned by T04) + +- [x] T02: `Canonicalize staged commit reference ownership in Pkl` (status:complete) + - Task ID: T02 + - Goal: Make the canonical commit package emit the staged + `commit-message-style.md` reference and remove the obsolete YAML result + contract section from `atomic-commit.md`, while preserving commit mode + routing, staged truth, result branching, and human-visible output layouts. + - Boundaries (in/out of scope): In — `workflow-commit.pkl`, its package and + composite reference documents, and narrowly required workflow-model changes. + Out — changing commit execution semantics, message wording beyond the staged + style guide, Rust commit behavior, or generated root target trees. + - Dependencies: none + - Done when: all target packages contain the split style reference; the + atomic-commit reference points to it and no longer instructs a YAML result + section; no generated document names or requires the removed + `commit-contract.yaml` file; regular and bypass paths remain unchanged. + - Verification notes (commands or checks): direct Pkl evaluation of + `workflow-commit.pkl`; generate temporary target payloads; compare affected + Pi package content with the staged baseline and normalize supported target + frontmatter for Claude/OpenCode. + - Implementation evidence: updated `workflow-commit.pkl` to emit the staged + commit-message guide as a package-local reference for all targets, point the + atomic-commit phase at it, and remove the obsolete YAML result-contract + section while preserving commit routing and result branching. + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/workflow-commit.pkl` passed; temporary generation emitted + `atomic-commit.md`, `commit-message-style.md`, and `output.md` under each + target's `sce-commit/references/`; generated references were target-neutral, + included the split guide, and contained no result-contract section or + `commit-contract.yaml` reference. + - Context synchronization: synced (manually confirmed; durable context + realigned by T04) + +- [x] T03: `Align generation contracts with the new workflow inventories` (status:complete) + - Task ID: T03 + - Goal: Update renderer metadata coverage, generation-contract assertions, and + any producer/check expectations so removal of validate context-sync output and + addition of commit-message-style output are exact, deterministic, and + target-parity checked. + - Boundaries (in/out of scope): In — Pkl coverage/contract modules, focused + negative fixtures or check registration required by the changed inventories, + and generated-input inspection. Out — implementing workflow behavior already + covered by T01/T02 or changing setup installation code. + - Dependencies: T01, T02 + - Done when: exact generated paths and package-local references match the + canonical renderers; forbidden validate context-sync and commit-contract + artifacts are rejected; target-neutral references agree; ephemeral producer + output passes its inventories and remains consumable by setup. + - Verification notes (commands or checks): `nix develop -c pkl eval + config/pkl/renderers/metadata-coverage-check.pkl`; `nix run + .#pkl-check-generated`; targeted negative-fixture evaluations; inspect a + producer handoff under a temporary directory. + - Implementation evidence: updated metadata coverage and generation contracts + for the validate and commit inventories; added explicit forbidden-path and + atomic-reference ownership assertions; updated negative fixtures and check + diagnostics; retained target-neutral and producer inventory validation. + - Verification evidence: `nix develop -c pkl eval + config/pkl/renderers/metadata-coverage-check.pkl` passed; `nix run + .#pkl-check-generated` passed with 107 generated files and all registered + negative fixtures failing as expected; temporary producer output contained + all three `config/.{pi,claude,opencode}` roots, validate references only + `output.md`, `validation.md`, and `validation-report.md`, and commit references + included `commit-message-style.md`; `git diff --check` passed. + - Context synchronization: synced (manually confirmed; durable context + realigned by T04) + +- [x] T04: `Realign durable workflow ownership documentation` (status:complete) + - Task ID: T04 + - Goal: Update current-state context to describe validation-only `/validate`, + task-only context synchronization ownership, ephemeral Pkl target payloads, + setup-installed root targets, and split commit-reference ownership. + - Boundaries (in/out of scope): In — the files listed under Context sync and + any required decision-record cross-reference. Out — historical plan files, + generated target trees, implementation code, and unrelated context cleanup. + - Dependencies: T03 + - Done when: current context contains no contradictory claim that `/validate` + invokes plan context synchronization or that the removed commit contract is + generated; the ephemeral `config/` generation boundary and setup ownership + remain accurately documented; changed context files stay within repository + hygiene limits. + - Verification notes (commands or checks): focused context searches; read + affected context files against generated output and canonical Pkl; verify + links and line counts; run `git diff --check` for authored files. + - Implementation evidence: realigned root context and workflow ownership + documents with validation-only `/validate`, task-only synchronization, + ephemeral target payloads, setup-owned installation, and split commit + references; updated the overlap/dedup ownership indexes and context map. + - Verification evidence: temporary `nix run .#pkl-generate -- ` emitted + only the ephemeral `config/.{opencode,claude,pi}` payload roots; generated + validate references were `validation.md`, `validation-report.md`, and + `output.md`, while commit references included `commit-message-style.md`; + metadata and generation contract evaluations passed; focused stale-claim + searches, relative-link checks, line-count checks, `git diff --check`, and + `nix run .#pkl-check-generated` passed (107 files). + - Context synchronization: synced (manually confirmed; this task performed + the durable-context realignment directly) + +- [x] T05: `Fix the /validate catalog description to state validation-only evidence recording` (status:complete) + - Task ID: T05 + - Goal: Update the `/validate` workflow record's `description` in + `config/pkl/base/workflow-catalog.pkl` from claiming it "synchronize[s] + its durable context" to stating it records final validation evidence, + then regenerate and verify the Pi/Claude/OpenCode `/validate` skill + frontmatter. + - Boundaries (in/out of scope): In — `workflow-catalog.pkl`'s `/validate` + record `description` field and the generated frontmatter it drives. Out — + any other catalog field, other workflow descriptions, or `/validate` + behavior itself (already validation-only per T01). + - Dependencies: none + - Done when: the catalog description no longer says "synchronize its + durable context"; generated `/validate` command/skill frontmatter across + all three targets reflects the corrected description; lightweight + post-task verification passes. + - Verification notes (commands or checks): direct Pkl evaluation of + `workflow-catalog.pkl`; temporary generation; grep generated `/validate` + frontmatter for the old and new wording across `.pi`/`.claude`/ + `.opencode`; `nix run .#pkl-check-generated`; `nix flake check`. + - Implementation evidence: changed the `["validate"]` record's `description` + field in `config/pkl/base/workflow-catalog.pkl` from "Validate one + completed SCE plan and synchronize its durable context" to "Validate one + completed SCE plan and record final validation evidence". + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/workflow-catalog.pkl` passed; temporary generation + confirmed the old wording is absent and the new wording appears in all + six affected generated files (`config/.pi/prompts/validate.md`, + `config/.pi/skills/sce-validate/SKILL.md`, + `config/.claude/commands/validate.md`, + `config/.claude/skills/sce-validate/SKILL.md`, + `config/.opencode/command/validate.md`, + `config/.opencode/skills/sce-validate/SKILL.md`); `nix run + .#pkl-check-generated` passed (107 files); `nix flake check` passed (all + checks green). + - Context synchronization: synced + - Context synchronization handoff: Plan path: + context/plans/remove-validate-context-sync.md; Task ID: T05; Task title: + Fix the /validate catalog description to state validation-only evidence + recording; Changed files: config/pkl/base/workflow-catalog.pkl; + Implementation summary: changed the `["validate"]` record's `description` + field from "Validate one completed SCE plan and synchronize its durable + context" to "Validate one completed SCE plan and record final validation + evidence"; Verification: `nix develop -c pkl eval + config/pkl/base/workflow-catalog.pkl` passed; temporary generation + confirmed old wording absent and new wording present in all six affected + generated files across `.pi`/`.claude`/`.opencode`; `nix run + .#pkl-check-generated` passed (107 files); `nix flake check` passed; Done + checks: all three done-when criteria met (old wording removed, new + wording present across all three targets, verification passed); Context + impact: local — this only corrects a workflow-catalog description string + that root context files already describe accurately in validation-only + terms (per T04's realignment); no root or domain context edit expected. + +- [x] T06: `Remove the plan-level context-sync lifecycle from the plan template` (status:complete) + - Task ID: T06 + - Goal: In `config/pkl/base/workflow-change-to-plan.pkl`, replace the + `## Context synchronization lifecycle` section's `Plan context + synchronization` field and its `/validate`-sets-synced-or-blocked claim + with a `## Task context synchronization lifecycle` section stating only + the per-task `pending | synced | blocked` lifecycle `/next-task` owns, + including the sync-before-next-task-or-handoff rule and the `blocked` + retained fields; apply the fix everywhere the template text is rendered + so package and composite output agree. + - Boundaries (in/out of scope): In — the plan-template text/rendering + functions in `workflow-change-to-plan.pkl` (`changeToPlanPlanTemplate` + and `renderPlanTemplate`), and the generated `references/plan-template.md` + / inlined template they produce. Out — the completion-record block's + per-task `Context synchronization: pending | synced | blocked` field, + which already matches the task-only lifecycle and stays unchanged; + `/next-task`'s own canonical source. + - Dependencies: none + - Done when: no generated plan-template document or inlined template + mentions `Plan context synchronization` or claims `/validate` sets a + synced/blocked plan-level state; a `## Task context synchronization + lifecycle` section states the task-only lifecycle in all three targets; + lightweight post-task verification passes. + - Verification notes (commands or checks): direct Pkl evaluation of + `workflow-change-to-plan.pkl`; temporary generation; grep generated + `/change-to-plan` package/template output for `Plan context + synchronization` (expect none) across all targets; `nix run + .#pkl-check-generated`; `nix flake check`. + - Implementation evidence: in both `changeToPlanPlanTemplate` and + `renderPlanTemplate` in `config/pkl/base/workflow-change-to-plan.pkl`, + renamed `## Context synchronization lifecycle` to `## Task context + synchronization lifecycle`, removed the `Plan context synchronization` + field and its `/validate`-sets-synced-or-blocked claim, and kept the + `Task context synchronization` sync-before-next-task-or-finish rule and + the `blocked` retained fields (Blocker, Required action, Retry + condition) unchanged. + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/workflow-change-to-plan.pkl` passed; temporary generation + via `nix run .#pkl-generate -- ` produced `references/plan-template.md` + under `.pi`, `.claude`, and `.opencode`, each containing `## Task context + synchronization lifecycle` and none containing `Plan context + synchronization`; `nix run .#pkl-check-generated` passed (107 files); `nix + flake check` passed (all checks green). + - Context synchronization: synced + - Context synchronization handoff: Plan path: + context/plans/remove-validate-context-sync.md; Task ID: T06; Task title: + Remove the plan-level context-sync lifecycle from the plan template; + Changed files: config/pkl/base/workflow-change-to-plan.pkl; Implementation + summary: replaced the `## Context synchronization lifecycle` section's + `Plan context synchronization` field and `/validate`-sets-state claim + with a `## Task context synchronization lifecycle` section stating only + the task-only lifecycle, in both `changeToPlanPlanTemplate` and + `renderPlanTemplate`; Verification: `nix develop -c pkl eval + config/pkl/base/workflow-change-to-plan.pkl` passed; temporary generation + confirmed the new heading present and `Plan context synchronization` + absent across all three targets' `plan-template.md`; `nix run + .#pkl-check-generated` passed (107 files); `nix flake check` passed; Done + checks: all three done-when criteria met (no `Plan context + synchronization` mention or plan-level-state claim in any generated + plan-template document, `## Task context synchronization lifecycle` + present in all three targets, verification passed); Context impact: + possibly local — this only removes stale plan-level context-sync wording + from the plan-template document that plans copy when authored; root + context files already describe validation-only `/validate` and + task-only synchronization per T04's realignment, but context-sync should + confirm no root/domain document still describes a plan-level + `Plan context synchronization` field. + +- [x] T07: `Narrow sce-decision's accepted callers to task context synchronization` (status:complete) + - Task ID: T07 + - Goal: In `config/pkl/base/decision-skill.pkl`, restrict the accepted + decision-request source to `sce-next-task` task context synchronization + only, removing `sce-validate`/plan-context-synchronization wording and + the "implementation or validation evidence" phrasing in favor of + "implementation / task-verification evidence", then regenerate and verify + all three `sce-decision` target packages. + - Boundaries (in/out of scope): In — `decision-skill.pkl`'s Purpose/Input/ + Boundaries prose. Out — the decision gate criteria, ADR template, and + `sce-next-task`'s own invocation of `sce-decision` (unchanged). + - Dependencies: none + - Done when: generated `sce-decision/SKILL.md` on all three targets accepts + requests only from `sce-next-task`/task context synchronization, contains + no `sce-validate` or plan-context-synchronization caller wording, and + uses "implementation / task-verification evidence"; lightweight + post-task verification passes. + - Verification notes (commands or checks): direct Pkl evaluation of + `decision-skill.pkl`; temporary generation; grep generated + `sce-decision/SKILL.md` across targets for `sce-validate` and `plan + context` (expect none); `nix run .#pkl-check-generated`; `nix flake + check`. + - Implementation evidence: in `config/pkl/base/decision-skill.pkl`, changed + the Purpose paragraph from "successful task or plan context + synchronization" to "successful task context synchronization"; changed + the Input section's accepted-caller sentence from "`sce-next-task` or + `sce-validate` context synchronization" to "`sce-next-task` task context + synchronization"; changed the evidence bullet from "The implementation or + validation evidence" to "The implementation / task-verification + evidence"; changed the Boundaries bullet from "Run outside successful + task or plan context synchronization" to "Run outside successful task + context synchronization". The decision gate criteria, ADR template, and + `sce-next-task`'s own invocation were left unchanged. + - Verification evidence: `nix develop -c pkl eval + config/pkl/base/decision-skill.pkl` passed (exit 0); temporary generation + via `nix run .#pkl-generate -- ` emitted `sce-decision/SKILL.md` + under `.pi`, `.claude`, and `.opencode`; a grep for `sce-validate` and + `plan context` across all three generated files returned no matches; a + grep confirmed "implementation / task-verification evidence" and + "`sce-next-task` task context" present in all three; `nix run + .#pkl-check-generated` passed (107 files, inventory sha256 + afed23f3c581a761518b0a83b46c96ec185e898e84f5b773e897df0e071b5d33); `nix + flake check` passed (all checks green). + - Context synchronization: synced + - Context synchronization handoff: Plan path: + context/plans/remove-validate-context-sync.md; Task ID: T07; Task title: + Narrow sce-decision's accepted callers to task context synchronization; + Changed files: config/pkl/base/decision-skill.pkl; Implementation + summary: restricted the accepted decision-request source to + `sce-next-task` task context synchronization only in + `decision-skill.pkl`'s Purpose, Input, and Boundaries prose, removing + `sce-validate`/plan-context-synchronization wording and replacing + "implementation or validation evidence" with "implementation / + task-verification evidence"; Verification: `nix develop -c pkl eval + config/pkl/base/decision-skill.pkl` passed; temporary generation + confirmed `sce-validate`/plan-context wording absent and the new + evidence/caller wording present across all three targets' generated + `sce-decision/SKILL.md`; `nix run .#pkl-check-generated` passed (107 + files); `nix flake check` passed; Done checks: the single done-when + criterion met (generated `sce-decision/SKILL.md` on all three targets + accepts requests only from `sce-next-task`/task context synchronization, + contains no `sce-validate` or plan-context-synchronization caller + wording, and uses "implementation / task-verification evidence"; + verification passed); Context impact: possibly local — this only narrows + prose in the standalone `sce-decision` package describing its accepted + callers; root context files already describe task-only synchronization + per T04's realignment, but context-sync should confirm no root/domain + document still describes `sce-validate` as an accepted `sce-decision` + caller. + +- [x] T08: `Tighten the generation contract's decision-invoking workflow boundary` (status:complete) + - Task ID: T08 + - Goal: In `config/pkl/renderers/generation-contract-check.pkl`, narrow + `isDecisionInvokingWorkflowDocument` to match only `sce-next-task`, and + add a negative assertion that every generated `sce-validate` document + contains neither a `sce-decision` reference nor plan-context-sync + wording, so the new task/validate decision boundary is enforced as a + generation invariant rather than resting on prose alone. + - Boundaries (in/out of scope): In — `generation-contract-check.pkl`'s + decision-invoking predicate and its assertions, plus any required + negative fixture. Out — other generation-contract assertions, + `metadata-coverage-check.pkl`, and workflow behavior itself (fixed in + T05-T07). + - Dependencies: T05, T06, T07 + - Done when: `isDecisionInvokingWorkflowDocument` matches only + `/skills/sce-next-task/`; a new assertion fails generation when a + generated `sce-validate` document contains `sce-decision` or + plan-context-sync wording and passes on current output; `nix develop -c + pkl eval config/pkl/renderers/generation-contract-check.pkl` passes; + `nix run .#pkl-check-generated` and `nix flake check` are green. + - Verification notes (commands or checks): direct Pkl evaluation of the + contract module; temporary generation; a controlled negative fixture + proving the new assertion fails as expected; `nix run + .#pkl-check-generated`; `nix flake check`. + - Implementation evidence: in `config/pkl/renderers/generation-contract-check.pkl`, + narrowed `isDecisionInvokingWorkflowDocument` to + `path.contains("/skills/sce-next-task/")` only (removed the + `sce-validate` disjunct); added `hidden assertValidateExcludesDecisionAndPlanSync`, + which fails when any generated `sce-validate` document contains + `sce-decision`, `plan-context-sync`, `plan context sync`, or `Plan + context synchronization`, and registered it in `contractChecks` under + `validate-decision-sync-boundary`; added the negative fixture + `config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl`, + which injects a forbidden `sce-decision`/`plan-context-sync` string into + a fake `sce-validate` document and asserts the new check throws; wired + the fixture into `config/pkl/check-generated.sh` via + `expect_pkl_fixture_failure` with the exact expected diagnostic. + - Verification evidence: `pkl eval + config/pkl/renderers/generation-contract-check.pkl` passed, printing + `["validate-decision-sync-boundary"] = "sce-validate package: no + sce-decision reference or plan-context-sync wording"` alongside all + other checks including `["decision-invocation"] = "generated decision + invocation: synchronization-only"`; `pkl eval + config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl` + failed with exactly the expected diagnostic; the full + `config/pkl/check-generated.sh` (equivalent to `nix run + .#pkl-check-generated`) passed, reporting "Ephemeral Pkl generation + passed: 107 files"; `pkl eval + config/pkl/renderers/metadata-coverage-check.pkl` passed; `nix flake + check` passed after staging the new fixture file (Nix only sees + git-tracked sources) — `checks.x86_64-linux.pkl-generated` passed + directly (`nix build .#checks.x86_64-linux.pkl-generated -L`), a + transient unrelated flaky failure in + `services::agent_trace_export::tests::read_parts_after_limit_truncates_and_follow_up_continues` + (a SQLite `UNIQUE constraint failed: parts.id` under concurrent + agent-trace DB writes, unrelated to this task's scope) was confirmed + flaky by an isolated rebuild of `checks.x86_64-linux.cli-tests` passing + 334/334, and a full rerun of `nix flake check` then printed "all checks + passed!" with exit 0. + - Context synchronization handoff: Plan path: + context/plans/remove-validate-context-sync.md; Task ID: T08; Task title: + Tighten the generation contract's decision-invoking workflow boundary; + Changed files: config/pkl/renderers/generation-contract-check.pkl, + config/pkl/check-generated.sh, + config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl; + Implementation summary: narrowed `isDecisionInvokingWorkflowDocument` to + match only `/skills/sce-next-task/`; added a `sce-validate`-scoped + negative assertion (`assertValidateExcludesDecisionAndPlanSync`) + rejecting `sce-decision` references and plan-context-sync wording, + registered it in `contractChecks`, added a negative fixture proving it + fails on injected forbidden content, and wired the fixture into + `check-generated.sh`; Verification: `pkl eval + config/pkl/renderers/generation-contract-check.pkl` passed with the new + check present; the new negative fixture failed with the exact expected + diagnostic; `config/pkl/check-generated.sh` (`nix run + .#pkl-check-generated` equivalent) passed (107 files); `nix flake check` + passed ("all checks passed!") after confirming an unrelated transient + test flake was not caused by this change; Done checks: all four + done-when criteria met (predicate narrowed, new assertion fails on + injected content and passes on current output, contract module + evaluation passes, `pkl-check-generated`/`nix flake check` green); + Context impact: possibly local — this only strengthens a generation-time + invariant (`sce-validate` excludes `sce-decision`/plan-context-sync + content) that root context files already describe behaviorally via + T04-T07's realignment; context-sync should confirm no root/domain + document still needs updating to describe this contract-level + enforcement, and that none contradicts the narrowed decision-invoking + predicate. + - Context synchronization: synced + +## Open questions + +None. The clarification explicitly removes plan-level context synchronization +and its invocation from `/validate`, while retaining task-level synchronization +and the existing ephemeral-generation/setup boundary. The staged commit +reference edits are treated as the requested canonicalization target. This PR +review's follow-up fixes (T05-T08) close the remaining gap between the +accepted `2026-08-13-validate-validation-only` ADR and the canonical Pkl +sources, without reopening any design question the ADR already settled. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-13 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (Ephemeral Pkl generation passed: 107 files, inventory sha256 afed23f3c581a761518b0a83b46c96ec185e898e84f5b773e897df0e071b5d33) +- `nix flake check` -> exit 0 (all checks passed!) +- `nix run .#pkl-generate -- "$(mktemp -d)"` -> exit 0 (wrote only `config/.{pi,claude,opencode}` and `config/{optional-workflows.json,schema/}` under the temp dir; no repository-root `.pi`/`.claude`/`.opencode` paths were created or modified) +- `nix develop -c pkl eval config/pkl/renderers/metadata-coverage-check.pkl` -> exit 0 +- `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` -> exit 0 (includes `["validate-decision-sync-boundary"] = "sce-validate package: no sce-decision reference or plan-context-sync wording"`) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/validate-decision-sync-boundary-check.pkl` -> exit 1, as required (fixture asserts the new check throws on injected forbidden content; failed with the expected `assertValidateExcludesDecisionAndPlanSync` diagnostic) + +### Success-criteria verification + +- [x] AC1: A fresh Pkl generation writes only ephemeral payloads below `config/`, not repository-root integration directories -> temp-dir generation via `nix run .#pkl-generate -- "$(mktemp -d)"` produced only `/config/.{pi,claude,opencode}` and no root-level `.pi`/`.claude`/`.opencode` paths; the existing repository-root `.pi`/`.claude`/`.opencode` directories (installed previously by `sce setup`) were untouched by the run. +- [x] AC2: Generated `sce-validate` has no plan-context-sync invocation, handoff, or package-local `context-sync.md`, and its completion output does not claim synchronization -> `sce-validate/references/` inventory across `.pi`/`.claude`/`.opencode` contains exactly `validation.md`, `validation-report.md`, `output.md`, `SKILL.md`; a grep for `sce-plan-context-sync|context-sync.md|plan-context-sync|sce-decision|synchronize durable context` across all three generated `sce-validate` packages and `/validate` command/prompt files returned only the expected negation statements ("Do not: Synchronize durable context under `context/`." and "Validation does not synchronize durable context."). +- [x] AC3: Generated `/commit` packages contain `references/commit-message-style.md`; `atomic-commit.md` delegates to it without a YAML result-contract section -> `commit-message-style.md` present in all three targets' `sce-commit/references/`; `atomic-commit.md` references `references/commit-message-style.md` for subject/body wording; a grep for `commit-contract.yaml|## Result contract|result contract` across all three `sce-commit` packages returned no matches. +- [x] AC4: Generation metadata and generation-contract checks describe the new validate/commit inventories -> `metadata-coverage-check.pkl` and `pkl-check-generated` both passed (107 files, matching inventory hash). +- [x] AC5: Durable workflow documentation states `/next-task` owns task context synchronization while `/validate` is validation-only -> `context/sce/shared-context-code-workflow.md`, `context/sce/context-workflow-rules.md`, `context/architecture.md`, and `context/glossary.md` each state `/validate` does not invoke or persist plan-level context synchronization; `context/sce/atomic-commit-workflow.md` and `context/glossary.md` state no `commit-contract.yaml` artifact is generated; no matches found for stale claims that `/validate` invokes plan-context synchronization. +- [x] AC6: Catalog description, plan template, `sce-decision` callers, and generation-contract decision-invoking list are validation-only-consistent with `2026-08-13-validate-validation-only` -> `workflow-catalog.pkl`'s `["validate"]` record `description` reads "Validate one completed SCE plan and record final validation evidence"; `workflow-change-to-plan.pkl` emits `## Task context synchronization lifecycle` (no `## Context synchronization lifecycle` or `Plan context synchronization` field) in both `changeToPlanPlanTemplate` and `renderPlanTemplate`; `decision-skill.pkl` accepts requests only from "`sce-next-task` task context synchronization"; `generation-contract-check.pkl`'s `isDecisionInvokingWorkflowDocument` matches only `/skills/sce-next-task/`, and its `assertValidateExcludesDecisionAndPlanSync` check (registered under `validate-decision-sync-boundary`) passed on current generated output and the paired negative fixture failed as required. + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/plans/simplify-task-record-format.md b/context/plans/simplify-task-record-format.md new file mode 100644 index 00000000..5a49927e --- /dev/null +++ b/context/plans/simplify-task-record-format.md @@ -0,0 +1,528 @@ +# Plan: simplify-task-record-format + +## Change summary + +The SCE plan task record currently represents the same implementation and +verification facts through several overlapping fields: `Goal`, `Boundaries +(in/out of scope)`, `Done when`, `Verification notes`, `Implementation +evidence`, `Verification evidence`, `Files changed`, `Evidence`, and a +separately serialized `Context synchronization handoff` (itself repeating +plan path, task ID, task title, changed files, implementation summary, +verification, done checks, and context impact). This plan compacts the +task-record format so a completed task is the single authoritative record of +implementation intent, completion conditions, verification, actual execution +result, changed files, context impact, and context synchronization state, +with no second "handoff" representation. `/change-to-plan` continues to +author task intent (title, `Scope`, `Dependencies`, `Done when`, planned +`Verify` checks, `Context synchronization: pending`); `/next-task` writes +execution facts (`Completed`, `Files changed`, `Result`, `Verify` outcomes, +`Context impact`, `Context synchronization: pending`) directly onto that same +task instead of building a second record; task context synchronization reads +that completed task record directly — identified only by plan path and task +ID — for both immediate synchronization and later sync-debt recovery, and +writes back only `Context synchronization: synced` or `blocked` plus +blocker-only-when-blocked metadata. This extends the existing +`config/pkl/base/workflow-{change-to-plan,next-task,context-sync}.pkl` +sources and the `generation-contract-check.pkl` contract; it does not migrate +historical plans or add backward compatibility for the old format. + +## Acceptance criteria + +How this plan is proven complete. Each criterion is observable and names the +check that proves it. `/validate` runs these checks; no task in the stack +performs final validation. + +- [x] AC1: A newly generated plan task uses the compact schema — title, + `Scope`, `Dependencies`, `Done when`, `Verify`, + `Context synchronization` — and never `Goal`, + `Boundaries (in/out of scope)`, or `Verification notes`. + - Validate: generate an ephemeral payload and inspect + `sce-change-to-plan/references/plan-template.md`'s new-task example for + the compact fields and the absence of the removed fields. +- [x] AC2: After `/next-task` completes implementation, execution facts are + recorded once on the task itself (`Completed`, `Files changed`, + `Result`, `Verify` outcomes, `Context impact`, + `Context synchronization`), with no `Implementation evidence` or + `Verification evidence` section. + - Validate: inspect generated `sce-next-task/references/task-execution.md` + and the plan-template completed-task example for the compact completion + shape and the absence of `Implementation evidence` / + `Verification evidence`. +- [x] AC3: No `Context synchronization handoff` appears in newly generated + plans or workflow instructions. + - Validate: `rg -n "Context synchronization handoff" ` returns no matches. +- [x] AC4: Task context synchronization performs both immediate + synchronization and later sync-debt recovery by reading the completed + task record identified by plan path and task ID, without a duplicated + handoff structure. + - Validate: inspect generated + `sce-next-task/references/{context-sync,plan-review}.md` for direct + completed-task-record reading on both the immediate-sync and + sync-debt-recovery paths. +- [x] AC5: A blocked context sync adds only synchronization-specific blocker + metadata (`Blocker`, `Required action`, `Retry condition`) and leaves + the task's existing execution facts authoritative. + - Validate: inspect the generated `context-sync.md` blocked write-back + instructions and the plan-template completion-record blocked example. +- [x] AC6: Generated Pi, Claude, and OpenCode workflow packages all express + the same compact model. + - Validate: generate the ephemeral payload and compare the three targets' + `sce-change-to-plan` / `sce-next-task` reference documents for parity + apart from supported target-specific frontmatter. +- [x] AC7: Generation-contract checks and fixtures prevent the old + duplicated-field model from being reintroduced. + - Validate: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl`; + `nix run .#pkl-check-generated`. +- [x] AC8: Durable `context/` documentation describes the completed task + record as the durable synchronization/recovery source, with no + current-state claim that a separate synchronization handoff must be + persisted. + - Validate: `rg -n "Context synchronization handoff" context/overview.md + context/sce/*.md context/glossary.md` returns no current-state matches. + +### Full validation + +Repository-wide checks `/validate` runs after the last task, regardless of +which criterion they map to. + +- `nix flake check` +- `nix run .#pkl-check-generated` +- `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` +- `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` +- `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` +- `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + +### Context sync + +- `context/overview.md` +- `context/sce/shared-context-plan-workflow.md` +- `context/sce/shared-context-code-workflow.md` +- `context/glossary.md` (the `baseline-relative task handoff` entry) +- A new ADR under `context/decisions/` superseding the handoff-shape portion + of `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` + +## Task context synchronization lifecycle + +Persist this field in every plan; this is durable plan state, not chat state: + +- **Task context synchronization:** every task carries `pending | synced | blocked`. + A completed task must be `synced` before another task can start or the plan can + finish. +- For `blocked`, record **Blocker**, **Required action**, and **Retry condition** + beside the status. Never infer `synced` from conversation history; write every + lifecycle transition to the plan file. + +## Constraints and non-goals + +- **In scope:** `config/pkl/base/workflow-change-to-plan.pkl`, + `config/pkl/base/workflow-next-task.pkl`, + `config/pkl/base/workflow-context-sync.pkl`, + `config/pkl/renderers/generation-contract-check.pkl` and its fixtures, the + ephemeral generated Pi/Claude/OpenCode payloads (verification only, never + hand-edited), and current-state `context/` documentation describing this + lifecycle (including one new/superseding ADR). +- **Out of scope:** `/validate`'s validation-only boundary, plan-level + context synchronization, `sce-decision` ownership, task approval + semantics, baseline-relative changed-file computation, and migration of + historical `context/plans/` files. +- **Constraints:** No backward compatibility for the old verbose task-record + format. `config/pkl/**` remains the canonical source; generated targets are + produced and inspected only through ephemeral generation + (`nix run .#pkl-generate` / `nix run .#pkl-check-generated`), never + hand-edited. +- **Non-goal:** Do not introduce a differently named field that recreates the + removed duplication (for example, keeping both a verbose "Implementation + summary" and `Result`). `Result` stays a short factual outcome, not a prose + diff. + +## Assumptions + +- Both the composite reference document (`changeToPlanPlanTemplate`, which + feeds the real generated `sce-change-to-plan/references/plan-template.md`) + and the legacy package-mode renderer (`renderPlanTemplate` / + `PLAN_TEMPLATE`, which feeds the currently-unrendered canonical phase + module `planAuthoringPackage`) are updated together for consistency, even + though only the composite path is emitted to real targets today. This + follows the existing "canonical phase module" convention recorded in + `context/glossary.md`. +- The new ADR supersedes only the handoff-shape aspect of + `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md`; that decision's + `pending`/`synced`/`blocked` lifecycle-state invariant is not re-decided + and remains in force. + +## Task stack + +- [x] T01: `Compact the change-to-plan task schema and plan template` (status:done) + - Task ID: T01 + - Goal: Replace the new-task authoring shape and completed-task shape in + `config/pkl/base/workflow-change-to-plan.pkl` with the compact schema — + title/`Scope`/`Dependencies`/`Done when`/`Verify`/`Context + synchronization` for new tasks; `Completed`/`Files changed`/`Result`/ + `Context impact`/`Verify` outcomes/`Context synchronization`, plus a + blocker subsection only when blocked, for completed tasks — removing + `Goal`, `Boundaries (in/out of scope)`, `Verification notes`, + `Implementation evidence`, `Verification evidence`, and + `Context synchronization handoff` from every rendering of the plan + template. + - Boundaries (in/out of scope): In — `renderPlanTemplate` / `PLAN_TEMPLATE` + (package-mode template render), `changeToPlanPlanTemplate` (composite + reference document), the filled-in task example, the completion-record + example, and any authoring-skill prose (`changeToPlanPlanAuthoring`, + `AUTHORING_SKILL` / `renderAuthoringSkillBody`, `AUTHORING_CONTRACT`) + that names the removed fields or describes task authoring. Out — + `workflow-next-task.pkl`, `workflow-context-sync.pkl`, + `generation-contract-check.pkl`, and durable `context/` docs (covered by + later tasks); no change to the clarification gate, acceptance-criteria + section, or task-slicing rules unrelated to the per-task field schema. + - Dependencies: none + - Done when: Both the composite (`changeToPlanPlanTemplate`) and legacy + package-mode (`renderPlanTemplate`) renderings of the plan template + define the compact new-task shape and the compact completed-task shape + exactly as specified, and reference neither `Goal:`, + `Boundaries (in/out of scope):`, `Verification notes`, + `Implementation evidence`, `Verification evidence`, nor + `Context synchronization handoff`. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl`; + generate an ephemeral payload (`nix run .#pkl-generate -- "$(mktemp -d)"`) + and inspect the generated `sce-change-to-plan/references/plan-template.md` + for the new task/completed-task examples. + - Context synchronization: synced + - Context synchronization handoff: Plan path: context/plans/simplify-task-record-format.md; Task ID: T01; Task title: Compact the change-to-plan task schema and plan template; Changed files: config/pkl/base/workflow-change-to-plan.pkl; Implementation summary: Replaced the new-task and completed-task field shapes in both the composite (`changeToPlanPlanTemplate`) and legacy package-mode (`renderPlanTemplate`/`PLAN_TEMPLATE`) plan-template renders: dropped `Goal`, renamed `Boundaries (in/out of scope)` to `Scope`, and renamed `Verification notes (commands or checks)` to `Verify` for new tasks (template block and filled-in example, both renders); replaced the completed-task template's `Context synchronization handoff`/`Evidence`/`Notes` fields with `Result` and `Context impact`, keeping `Completed`, `Files changed`, `Context synchronization`, and the blocker-only-when-blocked subsection; fixed two stray prose references to the old `Verification notes` field name in "Acceptance criteria rules" (both renders) to say `Verify`. Authoring-skill prose (`changeToPlanPlanAuthoring`, `AUTHORING_SKILL`/`renderAuthoringSkillBody`, `AUTHORING_CONTRACT`) named no removed field directly (it references the template by pointer), so no change was needed there.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` succeeded; generated an ephemeral payload via `nix run .#pkl-generate -- ` and inspected `sce-change-to-plan/references/plan-template.md` for Claude, Pi, and OpenCode targets — all three render the compact schema and are byte-identical (`diff` empty); `rg` confirmed no removed field names (`Goal:`, `Boundaries (in/out of scope)`, `Verification notes`, `Implementation evidence`, `Verification evidence`, `Context synchronization handoff`) remain anywhere in the source or generated plan-template. `nix run .#pkl-check-generated` still fails on the pre-existing `handoff-identity-fields` check in `generation-contract-check.pkl`, which is out of scope for T01 and owned by T04.; Done checks: Both renderings define the compact new-task shape and compact completed-task shape exactly as specified, and reference none of the removed field names — satisfied, confirmed by grep and generated-output inspection.; Context impact: none — this task changes only ephemeral-generation source (`config/pkl/**`); durable `context/` doc updates are T05's responsibility. + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-change-to-plan.pkl + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` (passed); ephemeral generation via `nix run .#pkl-generate -- ` followed by `rg`/`diff` inspection of the generated `plan-template.md` across all three targets (compact schema present, targets identical, no removed field names); `nix run .#pkl-check-generated` (fails only on the out-of-scope `handoff-identity-fields` check, expected until T04). + - Notes: None. + +- [x] T02: `Persist next-task execution facts directly on the completed task` (status:done) + - Task ID: T02 + - Goal: Change `/next-task` in `config/pkl/base/workflow-next-task.pkl` so + successful task execution writes `Completed`, baseline-relative + `Files changed`, a concise `Result`, actual `Verify` outcomes, and + `Context impact` directly onto the completed task, sets + `Context synchronization: pending`, and invokes task context + synchronization using that same task record — with no separate + `Implementation evidence`, `Verification evidence`, or + `Context synchronization handoff` construction — and change sync-debt + recovery so it identifies the debt task from the plan and carries that + task's own record forward instead of loading a persisted handoff. + - Boundaries (in/out of scope): In — the task-execution completion-writing + steps, the sync-debt recovery branch's task-record loading, and any + prose in `workflow-next-task.pkl` describing the handoff/evidence + fields. Out — the implementation gate, the `approved` flag, the + verification-running steps themselves (only what gets recorded changes), + plan-review's synced/blocked debt-scan trigger logic, and + `workflow-context-sync.pkl` (covered by T03). + - Dependencies: T01 + - Done when: `/next-task`'s completion-writing instructions in + `workflow-next-task.pkl` name only `Completed`, `Files changed`, + `Result`, updated `Verify` entries, `Context impact`, and + `Context synchronization: pending`; no instruction constructs or + persists `Implementation evidence`, `Verification evidence`, or a + `Context synchronization handoff` block; sync-debt recovery locates the + completed task by plan path and task ID and uses that record directly. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl`; + generate an ephemeral payload and inspect the generated `sce-next-task` + references (`task-execution.md`, `plan-review.md`) for the new + completion-writing and sync-debt-recovery wording. + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-next-task.pkl + - Result: Replaced the "Update the plan" completion-writing steps (both the + composite `nextTaskTaskExecutionReference` and the legacy package-mode + `renderExecutionSkillBody`) so a completed task now records `Completed`, + baseline-relative `Files changed`, a concise `Result`, actual `Verify` + outcomes, and `Context impact` directly, dropping the `Context + synchronization handoff` subsection and the separate implementation- + evidence/verification-evidence bullets. Replaced the sync-debt recovery + branch's task-record loading (both the composite + `nextTaskPlanReviewReference` and the legacy package-mode + `renderReviewSkillBody`, plus the composite `sync_debt` result-contract + prose) so it detects unrecoverable debt from a missing completed-task + record (no `Files changed`/`Result`/`Verify`/`Context impact`) instead of + a missing handoff subsection, and otherwise reads that task's own + completed record directly from the plan by plan path and task ID rather + than loading a persisted handoff. Left plan-review's debt-scan trigger + loop and `workflow-content.pkl`'s SKILL.md-level `sync_debt` branch prose + unchanged, per the task's declared boundaries. + - Context impact: none — this task changes only ephemeral-generation source + (`config/pkl/**`); durable `context/` doc updates are T05's responsibility. + - Verify: + - `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` — passed. + - Generated an ephemeral payload via `nix run .#pkl-generate -- ` + and inspected `sce-next-task/references/task-execution.md` and + `plan-review.md` for Claude, Pi, and OpenCode targets — all three show + the new completion-writing and sync-debt-recovery wording and are + byte-identical (`diff` empty). `rg` confirmed none of the removed field + names (`Context synchronization handoff`, `Implementation evidence`, + `Verification evidence`) remain in either file, on any target. One + unrelated, pre-existing `Verification evidence` string remains at + `workflow-next-task.pkl:153`, inside user-facing presentation prose for + the `incomplete` execution-result branch (not a plan-record field); + out of scope for this task. + - `nix run .#pkl-check-generated` now clears the `plan-review-sync-debt- + recovery` check (which required literal "do not attempt a + reconstructed retry" / "migrate the plan" wording — reflowed to keep + both phrases on one line after the rewrite) and progresses to the next, + still-failing `handoff-identity-fields` check — the same pre-existing + failure T01 already recorded as out of scope, owned by T04. + - Context synchronization: synced + +- [x] T03: `Read the completed task record directly in task context synchronization` (status:done) + - Task ID: T03 + - Goal: Change task context synchronization in + `config/pkl/base/workflow-context-sync.pkl` so it resolves plan path and + task ID, reads the matching completed task from the plan as its + authoritative input, and on completion writes only + `Context synchronization: synced` or `Context synchronization: blocked` + plus a `Context synchronization blocker` (`Blocker` / `Required action` / + `Retry condition`) — removing every instruction, validation rule, and + prose reference describing a separate persisted + `Context synchronization handoff`, including the rule that a valid retry + depends on a persisted handoff and the duplicated Plan + path/Task ID/Task title/Changed files/Implementation summary/ + Verification/Done checks/Context impact validation inside it. + - Boundaries (in/out of scope): In — the task-context-sync phase's input + resolution, handoff-shaped validation rules, and successful/blocked + write-back instructions in `workflow-context-sync.pkl`. Out — the + plan-level context-sync phase (already out of `/validate`'s boundary), + the `sce-decision` gate invocation contract, and the root-context + five-file pass. + - Dependencies: T02 + - Done when: `workflow-context-sync.pkl`'s task-context-sync phase names + the completed task record (`Files changed`, `Result`, `Verify`, + `Done when`, `Context impact`, `Context synchronization`) as its input + and validation source, contains no `Context synchronization handoff` + validation rule or reference, and its successful/blocked write-back + instructions match the target completion-record shape + (blocker-only-when-blocked). + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl`; + generate an ephemeral payload and inspect the generated task + context-sync references in `sce-next-task` (`context-sync.md`, + `sync-report.md`) for direct completed-task-record reading. + - Completed: 2026-08-13 + - Files changed: config/pkl/base/workflow-context-sync.pkl + - Result: Rewrote the task-context-sync phase's input, §3.1 handoff + validation, and §3.8 blocked write-back — in both the composite + (`taskRoleData`) and legacy package-mode (`taskReference`) renders — to + resolve plan path and task ID and read the matching completed task + record directly from the plan (`Files changed`, `Result`, `Verify`, + `Done when`, `Context impact`, `Context synchronization`) for + cross-session retry, alongside the unchanged live same-session + `status: complete` execution-result path; dropped the "persisted vs. + live handoff" dual-path framing and the requirement that a retry carry + its own `Plan path`/`Task ID`/`Task title` fields, since plan review now + supplies those identifiers directly (matching T02's updated contract). + Removed the `blockedHandoffSection` field from the shared `SyncReportRole` + class and its two implementations (`taskReport`'s eight-field + `Context synchronization handoff` block, and `planReport`'s already-empty + stub), simplifying `blockedReport`'s template to always render plan/task + identity plus the `Context synchronization blocker` section, with no + conditional handoff block. Updated `taskReport.rules` so blocked reports + no longer restate the changed-files list (already on the plan's completed + task record) and blocked write-back cites only the blocker subsection. + Left `planRoleData`/`planReport` and the `sce-decision` gate untouched, per + the task's declared boundaries. + - Context impact: none — this task changes only ephemeral-generation source + (`config/pkl/**`); durable `context/` doc updates are T05's responsibility. + - Verify: + - `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` — passed. + - Generated an ephemeral payload via `nix run .#pkl-generate -- ` + and inspected `sce-next-task/references/context-sync.md` and + `sync-report.md` for Claude, Pi, and OpenCode targets — all three show + the completed-task-record input/validation/write-back wording and are + byte-identical (`diff` empty). `rg -n "Context synchronization + handoff|persisted handoff|live or persisted" ` returned no + matches anywhere in the generated tree. + - `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + and `nix run .#pkl-check-generated` both still fail on the same + pre-existing `handoff-identity-fields` throw T01 and T02 already + recorded as out of scope, owned by T04; confirmed unchanged by stashing + this task's edit and reproducing the identical failure beforehand, so + this task neither introduced nor worsened it. + - Context synchronization: synced + +- [x] T04: `Update generation-contract checks and fixtures for the compact task record` (status:done) + - Task ID: T04 + - Goal: Update `config/pkl/renderers/generation-contract-check.pkl` — + removing or rewriting the `handoff-identity-fields` check and any + assertion expecting `Goal`, `Boundaries (in/out of scope)`, + `Verification notes`, `Implementation evidence`, `Verification + evidence`, or `Context synchronization handoff` — and add semantic + checks plus focused negative fixtures asserting: the compact fields + (`Scope`, `Done when`, `Verify`, `Result`, `Files changed`, + `Context impact`, `Context synchronization`) are present where expected; + the removed fields/sections are absent from generated task-schema and + workflow instructions; `/next-task` persists execution facts directly; + sync-debt recovery reads the completed task record; and task context + synchronization validates the completed task record rather than a + handoff. + - Boundaries (in/out of scope): In — `generation-contract-check.pkl` + checks/assertions, their registration, and any new negative fixture + files the existing `assertX + fixture + check-generated.sh` pattern + requires. Out — non-generation-contract renderer files, and any + target-specific frontmatter/permission checks unrelated to the + task-record schema. + - Dependencies: T03 + - Done when: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + succeeds; `nix run .#pkl-check-generated` passes with the new/updated + checks active; the old duplicated-field checks (including + `handoff-identity-fields`) are removed or rewritten to match the compact + model, with no check asserting the old field names as required content. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl`; + `nix run .#pkl-check-generated`; confirm each new negative fixture fails + the check it targets before the corresponding contract fix and passes + after. + - Completed: 2026-08-14 + - Files changed: config/pkl/renderers/generation-contract-check.pkl; + config/pkl/check-generated.sh; + config/pkl/renderers/fixtures/handoff-identity-fields-check.pkl (deleted); + config/pkl/renderers/fixtures/compact-plan-template-schema-check.pkl (new); + config/pkl/renderers/fixtures/next-task-compact-completion-writing-check.pkl (new); + config/pkl/renderers/fixtures/plan-review-reads-completed-record-check.pkl (new); + config/pkl/renderers/fixtures/context-sync-validates-task-record-check.pkl (new) + - Result: Replaced the single `handoff-identity-fields` check with four + focused checks matching the compact model: `compact-plan-template-schema` + (asserts the plan template's new-task and completion examples contain the + compact-schema tokens and none of the six removed legacy field names), + `next-task-compact-completion-writing` (asserts `task-execution.md` + records execution facts directly on the task with no separate handoff/ + evidence construction), `plan-review-reads-completed-record` (asserts + `plan-review.md`'s sync-debt recovery reads the completed task record by + plan path and task ID rather than a persisted handoff), and + `context-sync-validates-task-record` (asserts `context-sync.md` validates + the completed task record, not a persisted handoff). Added one negative + fixture per check, each importing the contract and asserting the check + throws its exact diagnostic against a document containing the disallowed + old-format content, and registered all four via `expect_pkl_fixture_failure` + in `check-generated.sh` in place of the removed + `handoff-identity-fields-check.pkl` registration. Deleted the obsolete + `handoff-identity-fields-check.pkl` fixture. + - Context impact: none — this task changes only ephemeral-generation source + (`config/pkl/**`); durable `context/` doc updates are T05's responsibility. + - Verify: + - `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` — + passed; all 32 registered checks, including the four new ones, evaluate + successfully. + - `nix run .#pkl-check-generated` — passed (exit 0); ephemeral generation + produced 107 files and every `expect_pkl_fixture_failure` assertion, + including the four new fixtures, matched its expected diagnostic. + - `rg` (via `nix run nixpkgs#ripgrep`) confirmed no remaining reference to + `handoff-identity-fields`/`assertHandoffIdentityFields` anywhere in + `config/pkl/`, and confirmed the six legacy field-name tokens + (`Goal:`, `Boundaries (in/out of scope)`, `Verification notes`, + `Implementation evidence`, `Verification evidence`, + `Context synchronization handoff`) appear in + `generation-contract-check.pkl` only inside the forbidden-token listing + and `!text.contains(...)` absence assertions, never as required content. + - Context synchronization: synced + +- [x] T05: `Describe the compact task-record model in durable context` (status:done) + - Task ID: T05 + - Goal: Update current-state `context/` documentation (at minimum + `context/overview.md`, `context/sce/shared-context-plan-workflow.md`, + `context/sce/shared-context-code-workflow.md`, and the + `context/glossary.md` `baseline-relative task handoff` entry) to + describe the completed task record as the sole durable + synchronization/recovery input, with no remaining current-state claim + that a separate `Context synchronization handoff` must be persisted, and + write a new ADR under `context/decisions/` superseding the + handoff-shape portion of + `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` while + preserving its `pending`/`synced`/`blocked` lifecycle-state decision. + - Boundaries (in/out of scope): In — the current-state prose named above + plus one new dated ADR. Out — historical `context/plans/` files, + `context/handovers/`, other ADRs not describing the handoff shape, and + any code change (this task is documentation-only). + - Dependencies: T04 + - Done when: The listed current-state docs describe completed-task-record + recovery with no remaining reference to a persisted + `Context synchronization handoff` as the recovery mechanism; the new + ADR exists, states what it supersedes, and preserves the + lifecycle-state invariant; `nix flake check` and + `nix run .#pkl-check-generated` both pass. + - Verification notes (commands or checks): `rg -n "Context synchronization handoff" context/` + (expect no current-state hits outside historical plan/decision files); + `nix run .#pkl-check-generated`; `nix flake check`. + - Completed: 2026-08-14 + - Files changed: context/overview.md; context/sce/shared-context-code-workflow.md; + context/glossary.md; context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md + - Result: Updated `context/overview.md`'s `/next-task` synchronization paragraph + to state that execution facts are recorded directly on the completed task and + that sync-debt retry reads that same record by plan path and task ID, with no + separate persisted handoff. Rewrote the `baseline-relative task handoff` entry + in `context/glossary.md` so its persisted form is described as the completed + task record itself (`Completed`/`Files changed`/`Result`/`Verify`/`Context + impact`), dropping the old `Context synchronization handoff` field reference. + Updated `context/sce/shared-context-code-workflow.md`'s purpose paragraph and + the `sce-plan-review` phase bullets (legacy-detection condition, `sync_debt` + naming, and sync-debt-recovery wording) to match the exact generated wording + in `sce-next-task/references/{plan-review,context-sync}.md` — reading the + completed task record directly rather than a persisted handoff. + `context/sce/shared-context-plan-workflow.md` was inspected and needed no + change: it never named the handoff field. Added + `context/decisions/2026-08-14-compact-task-record-supersedes-handoff.md`, + which supersedes only the handoff-shape portion of + `2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` (status left + `Accepted`, matching the same partial-supersession precedent set by + `2026-08-13-validate-validation-only.md`) while explicitly leaving that + decision's `pending`/`synced`/`blocked` lifecycle-state invariant in force. + Also staged (via `git add`, no content change) T04's four new fixture files + under `config/pkl/renderers/fixtures/`, which were left untracked and caused + `nix flake check`'s git-sourced `pkl-generated` derivation to fail on a + missing-module error; this was required for this task's own mandated + `nix flake check` verification to pass and made no code or doc change. + - Context impact: none — this task itself performs the current-state + documentation update; no further context work follows from it. + - Verify: + - `rg -n "Context synchronization handoff" context/` (via `nix run + nixpkgs#ripgrep`) — the only current-state hit outside historical + plan/decision files is `context/architecture.md`'s description of the + generation-contract check that asserts the field's absence from generated + output (a forbidden-token listing, not a current-state persistence claim), + which is expected and unchanged. + - `nix run .#pkl-check-generated` — passed (exit 0), 107 files, matching the + inventory hash already established by T04. + - `nix flake check` — passed (`all checks passed!`) after staging T04's + untracked fixture files described above; before staging, the + `pkl-generated` check failed on a missing-module error unrelated to this + task's own edits. + - Context synchronization: synced + +## Open questions + +None. The change request fully specifies the target schema, the file-by-file +scope, the explicit non-goals, and the acceptance criteria. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-14 + +### Commands run + +- `nix flake check` -> exit 0 (all checks passed) +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral generation passed: 107 files, inventory sha256 a1adb1667e2675dcdfba5353518f802d791859ea237be6135e05c66ef3157f42) +- `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` -> exit 0 +- `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` -> exit 0 +- `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` -> exit 0 +- `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` -> exit 0 +- `nix run .#pkl-generate -- ` -> exit 0 (ephemeral payload generated for acceptance-criteria inspection) + +### Success-criteria verification + +- [x] AC1: New-task shape uses the compact schema -> generated `sce-change-to-plan/references/plan-template.md` (Claude target) shows the template and filled-in example using `Task ID`/`Scope`/`Dependencies`/`Done when`/`Verify`/`Context synchronization`; `grep` for `Goal:`, `Boundaries (in/out of scope)`, and `Verification notes` returned no matches in the file. +- [x] AC2: Completed-task execution facts recorded once, no separate evidence sections -> generated `plan-template.md`'s "Completion records" section and `sce-next-task/references/task-execution.md` §2.7 record `Completed`, `Files changed`, `Result`, `Verify` outcomes, and `Context impact` directly on the task; `grep` for `Implementation evidence` and `Context synchronization handoff` returned no matches in either file (one unrelated lowercase "verification evidence" phrase remains in `task-execution.md` §2.8 describing the internal handoff-state contract, not a persisted plan-file field). +- [x] AC3: No `Context synchronization handoff` in newly generated plans/instructions -> `rg -n "Context synchronization handoff" ` returned no matches (exit 1). +- [x] AC4: Task context synchronization reads the completed task record directly for both immediate sync and sync-debt recovery -> generated `sce-next-task/references/context-sync.md` (§3.1, §3.8) and `plan-review.md` (§1.2, §1.5) both read/write the completed task record by plan path and task ID, with no separate handoff structure. +- [x] AC5: Blocked context sync adds only synchronization-specific blocker metadata -> generated `context-sync.md` §3.8 writes plan/task identity plus a `Context synchronization blocker` section (`Blocker`/`Required action`/`Retry condition`) using the same field names as the plan's completion record; `plan-template.md`'s completion-record example shows the identical blocked shape. +- [x] AC6: Pi, Claude, and OpenCode packages express the same compact model -> `diff -q` across all three targets for `plan-template.md`, `task-execution.md`, `context-sync.md`, `plan-review.md`, and `sync-report.md` reported no differences. +- [x] AC7: Generation-contract checks and fixtures prevent regressions -> `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` passed; `nix run .#pkl-check-generated` passed with all fixture-based checks (including the four new ones from T04) active. +- [x] AC8: Durable `context/` documentation has no current-state handoff claim -> `rg -n "Context synchronization handoff" context/overview.md context/sce/*.md context/glossary.md` returned no matches (exit 1). + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/plans/sync-handoff-recovery-and-generation-fixes.md b/context/plans/sync-handoff-recovery-and-generation-fixes.md new file mode 100644 index 00000000..16578153 --- /dev/null +++ b/context/plans/sync-handoff-recovery-and-generation-fixes.md @@ -0,0 +1,362 @@ +# Plan: sync-handoff-recovery-and-generation-fixes + +## Change summary + +Fixes four review findings against PR #205 in the canonical Pkl workflow +sources under `config/pkl/base/**` and `config/pkl/renderers/**`, which are the +sole source of truth for the generated `.opencode`, `.claude`, and `.pi` +workflow packages. This extends existing behavior; it does not replace the +synchronization lifecycle or the optional-workflow install-time architecture. + +1. Task-level context-sync `blocked` state currently has no durable retry + record, so a session boundary after a block deadlocks: `/next-task` refuses + a new task while sync debt exists, but context-sync forbids reconstructing + the original execution handoff from chat history, and nothing durable + preserves it. This plan defines one authoritative "Context synchronization + handoff" record owned by the task completion entry, has `/next-task` write + it at completion and read it back for retry, and has context-sync retry + from either the live or the persisted handoff. +2. The generated `/commit` bypass path tells the agent to write a commit + message to a temporary file for `git commit -F` while a separate rule bans + modifying files at all — a literal self-contradiction. This plan scopes the + mutation rule to repository/worktree files and states the out-of-worktree, + no-interpolation, single-invocation, and cleanup requirements for the one + permitted temp file. +3. The layout-reference semantic checker in `generation-contract-check.pkl` + parses `Render the **X** layout from \`references/foo.md\`` instructions but + always validates the `## X` heading against a hardcoded `references/output.md` + instead of the captured `foo.md`, so a broken instruction pointing at the + wrong file can pass. This plan makes the assertion resolve the captured path. +4. The plan template renders `Context synchronization: pending | synced | blocked` + as if it were a literal field value instead of the documented value domain, + and the completion-record shape only allows `synced | blocked` even though + `pending` is a valid state for a completed task awaiting sync. This plan + makes new tasks start at the single concrete value `pending` and makes the + completion schema accept `pending | synced | blocked`. + +## Acceptance criteria + +- [x] AC1: A completed task whose context sync is `blocked` carries a durable + "Context synchronization handoff" (changed files, implementation summary, + verification, done checks, context impact) and a "Context synchronization + blocker" (blocker, required action, retry condition) in the plan file, + sufficient for a later session to retry using only the plan. + - Validate: generated `sce-change-to-plan/references/plan-template.md` + completion-record example shows both subsections with those fields; + `sce-next-task/references/task-execution.md` writes them at completion. +- [x] AC2: `/next-task` never starts a new implementation task while an + earlier completed task's `Context synchronization` field is `pending` or + `blocked`. It first loads the persisted handoff from the plan and + retries/repairs synchronization for that task; on success it persists + `synced` and clears blocker fields before continuing normal selection; on a + renewed block it persists the updated blocker/required-action/retry + condition and stops. A legacy plan with sync debt but no durable handoff + structure fails explicitly with migration guidance instead of attempting a + reconstructed retry. + - Validate: `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` + (new negative fixture) throws; `nix run .#pkl-check-generated` passes. +- [x] AC3: Task-context-sync accepts either the live execution handoff + (same-session) or the persisted plan-recorded handoff (cross-session retry) + as authoritative input, never reconstructs a missing one from conversation + history, and its blocked report always writes the handoff and blocker + subsections from AC1. + - Validate: generated `sce-next-task/references/context-sync.md` documents + both input sources and the blocked-report shape; `nix develop -c pkl eval + config/pkl/base/workflow-context-sync.pkl`. +- [x] AC4: Bypass-mode `/commit` instructions permit exactly the + out-of-worktree commit-message temp file (no shell interpolation, exactly + one `git commit -F `, cleanup including failure paths, explicit + post-success hash retrieval) and no longer state an unscoped "do not modify + files" rule beside that instruction. + - Validate: generated `sce-commit/SKILL.md` and + `sce-commit/references/atomic-commit.md` contain the reconciled wording in + every rendered occurrence (package and composite). +- [x] AC5: The layout-reference checker validates the `## X` heading against + the exact file an instruction cites, not a hardcoded `references/output.md`, + while still keeping the general package-local-reference-existence + assertion. + - Validate: `nix develop -c pkl eval + config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` + (new negative fixture) throws; `nix develop -c pkl eval + config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` + (new positive fixture) passes; `nix run .#pkl-check-generated` passes. +- [x] AC6: A freshly authored task in a newly written plan starts with + `Context synchronization: pending` as a concrete value; the + `pending | synced | blocked` domain is documented separately from that + value. Completed tasks may legitimately be `pending`, `synced`, or + `blocked`, and blocker metadata is present only for `blocked`. + - Validate: generated `sce-change-to-plan/references/plan-template.md` + (package and composite) task-authoring example and completion-record + example. + +### Full validation + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/sce/shared-context-code-workflow.md` +- `context/sce/atomic-commit-workflow.md` +- `context/decisions/2026-08-12-persist-workflow-sync-lifecycle-in-plans.md` +- `context/architecture.md` (generation-contract-check semantic-check count and + the plan-template lifecycle description) + +## Constraints and non-goals + +- **In scope:** `config/pkl/base/workflow-change-to-plan.pkl`, + `config/pkl/base/workflow-next-task.pkl`, + `config/pkl/base/workflow-context-sync.pkl`, + `config/pkl/base/workflow-commit.pkl`, + `config/pkl/renderers/generation-contract-check.pkl`, + `config/pkl/renderers/fixtures/*.pkl`, and the fixture registration list in + `config/pkl/check-generated.sh`. Ephemeral regeneration of `.opencode`, + `.claude`, and `.pi` outputs through the normal generation pipeline. +- **Out of scope:** `sce-handover`, `sce-brownfield`, and `sce-decision` + packages; any CLI Rust code under `cli/`; optional-workflow install-time + semantics and the 2026-07-31 install-time-optional-workflows ADR; any + workflow behavior not named in the four findings. +- **Constraints:** `config/pkl/**` remains the sole source of truth; never + hand-edit generated `.pi/**`, `.claude/**`, or `.opencode/**` output; keep + the plan/completion-record Markdown format consistent with the existing + template conventions in `references/plan-template.md`; keep the + package-vs-composite rendering parity the existing renderer contract + requires. +- **Non-goal:** Redesigning the synchronization lifecycle beyond persisting + and retrying the existing `pending`/`synced`/`blocked` model; introducing a + second persistence mechanism (database, sidecar file) for lifecycle state, + which the accepted `persist-workflow-sync-lifecycle-in-plans` decision + already rules out. + +## Task stack + +- [x] T01: `Redefine the plan-template lifecycle values and durable handoff shape` (status:done) + - Task ID: T01 + - Goal: In `config/pkl/base/workflow-change-to-plan.pkl`, make a freshly + authored task's `Context synchronization` field a concrete `pending` + value (never the `pending | synced | blocked` union used as a value), keep + that union only as separate value-domain documentation, widen the + completion-record schema from `synced | blocked` to + `pending | synced | blocked`, and add the "Context synchronization + handoff" (changed files, implementation summary, verification, done + checks, context impact) and "Context synchronization blocker" (blocker, + required action, retry condition) subsections to the completion record, + matching the PR's conceptual example. Apply the fix to every rendered + occurrence of the plan template (package mode and the composite-skill + duplicate). + - Boundaries (in/out of scope): In — `references/plan-template.md` + rendering (task-authoring example, completion-record example, lifecycle + value documentation) in `workflow-change-to-plan.pkl` only. Out — the + phases that read/write these fields at runtime (T02-T04); any other + workflow package. + - Dependencies: none + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` + succeeds; a temporary generation shows `references/plan-template.md` + (package) and the composite plan-template section both render + `Context synchronization: pending` for a new task, + `pending | synced | blocked` for the completion-record field, and the two + new subsections with the named fields; no template literal still uses the + union as a concrete field value. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl`; `nix run .#pkl-generate -- "$(mktemp -d)"` then grep the generated plan-template content for `Context synchronization: pending` and the new subsection headings. + - Context synchronization: pending + - Context synchronization handoff: Changed files: `config/pkl/base/workflow-change-to-plan.pkl`; Implementation summary: In both the composite reference-document template (`changeToPlanPlanTemplate`, ~L601-719) and the package-mode `renderPlanTemplate` function (~L2124-2242), changed the task-authoring and filled-in-task examples' `Context synchronization` line from the `pending | synced | blocked` union to the concrete value `pending` (and removed the now-inapplicable `When blocked:` line from those authoring examples, since a freshly authored task starts `pending`); widened the completion-record example's `Context synchronization` line from `synced | blocked` to `pending | synced | blocked`; added `Context synchronization handoff` (changed files, implementation summary, verification, done checks, context impact) and `Context synchronization blocker` (blocker, required action, retry condition; present only when blocked) lines to the completion-record example, in both occurrences.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` (pass); `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` (pass, includes package-vs-composite target-neutral-references parity check); `nix run .#pkl-check-generated` (pass, 107 files); targeted generation + grep of `references/plan-template.md` under `.claude/.opencode/.pi` skills/sce-change-to-plan confirmed `Context synchronization: pending` for authoring examples, `pending | synced | blocked` plus both new handoff/blocker lines for the completion-record example.; Done checks: All satisfied — pkl eval succeeds, generated plan-template renders the concrete `pending` value for new tasks, the widened union for completion records, and both new subsection lines; no template literal retains the union as a task-authoring value.; Context impact: None — this is workflow-generation source only; no `context/` file describes plan-template field shape independently of the generated reference itself. + - Completed: 2026-08-13 + - Files changed: `config/pkl/base/workflow-change-to-plan.pkl` + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` — pass; `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` — pass (24 semantic checks, including target-neutral-references parity); `nix run .#pkl-check-generated` — pass (107 files, ephemeral generation). + - Notes: Removed the `When blocked:` line from the task-authoring/filled-in-task examples (not just widened the status value) since a freshly authored task is always `pending` and can never legitimately show blocker fields at authoring time; this is a reversible, in-scope local formatting choice consistent with the task's intent. + +- [x] T02: `Persist the synchronization handoff at task completion` (status:done) + - Task ID: T02 + - Goal: In `config/pkl/base/workflow-next-task.pkl`'s task-execution phase, + write the T01 "Context synchronization handoff" subsection (changed + files, implementation summary, verification, done checks, context impact) + into the plan's completion record for the just-completed task before + invoking task-context-sync, using the same field set context-sync + consumes rather than duplicating the entire execution result. + - Boundaries (in/out of scope): In — task-execution phase steps that mark a + task complete and record its `Context synchronization: pending` state, in + both package and composite renderings. Out — sync-debt recovery in plan + review (T03); context-sync's own read/write behavior (T04). + - Dependencies: T01 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` + succeeds; the generated task-execution reference instructs writing the + handoff subsection with the field names T01 defined, immediately before + the `Context synchronization: pending` write. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl`; targeted generation and grep for the new instruction text in `sce-next-task/references/task-execution.md`. + - Context synchronization: pending + - Context synchronization handoff: Changed files: `config/pkl/base/workflow-next-task.pkl`; Implementation summary: In step 7 "Update the plan" of the task-execution phase (both the package-mode `renderTaskExecution` rendering and the composite `nextTaskTaskExecution` template), added one instruction bullet — "Write the task's `Context synchronization handoff` subsection into the completion record: changed files, implementation summary, verification, done checks, and context impact, using the same field set task-context-sync consumes rather than duplicating the entire execution result." — placed immediately after "Mark only the selected task complete." and before the existing "Set that task's `Context synchronization` field to `pending`" bullet, which was reworded to state it happens after the new handoff-subsection write.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` (pass); targeted generation via `nix run .#pkl-generate` into a temp dir, grep of `sce-next-task/references/task-execution.md` under `.claude` and `.pi` confirmed the new bullet renders identically in both, immediately before the `pending` write line (`.opencode` renders this reference under a different path not covered by this task's done-when).; Done checks: All satisfied — pkl eval succeeds; generated task-execution reference instructs writing the handoff subsection with T01's field names immediately before the `Context synchronization: pending` write, in both package and composite renderings.; Context impact: None — this is workflow-generation source only; no `context/` file describes task-execution phase step ordering independently of the generated reference itself. + +- [x] T03: `Add /next-task sync-debt recovery before new-task selection` (status:done) + - Task ID: T03 + - Goal: In the plan-review phase of `config/pkl/base/workflow-next-task.pkl`, + before selecting or starting a task, inspect completed tasks' `Context + synchronization` field; when debt (`pending` or `blocked`) exists, load + the persisted handoff (and blocker/required-action/retry-condition, if + blocked) from the plan and retry/repair synchronization for that task + instead of refusing outright. On success, persist `synced`, clear + obsolete blocker/retry metadata, and continue normal task selection. On a + renewed block, persist the updated blocker/required-action/retry + condition and stop. Never start a later implementation task while earlier + debt remains, and never reconstruct a missing handoff from chat history: + a legacy plan with debt but no durable handoff structure fails explicitly + with a migration/recovery message. Add one semantic generation-contract + check (alongside the existing ten in `generation-contract-check.pkl`) + asserting the generated plan-review reference states the recovery and + legacy-migration-failure behavior, with a negative fixture proving its + absence fails, registered in `config/pkl/check-generated.sh`. + - Boundaries (in/out of scope): In — plan-review phase recovery steps in + `workflow-next-task.pkl` (package and composite); the new semantic + assertion and its fixture. Out — task-context-sync's own retry mechanics + (T04, though T03 depends on T04's contract existing conceptually — order + is fine since this task only asserts plan-review's own instructions, not + context-sync's). + - Dependencies: T01, T02 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` + and `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` + succeed; the new negative fixture throws when the recovery/migration + wording is missing and the real generated output passes the new + assertion. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` (expect throw); `nix run .#pkl-check-generated`. + - Context synchronization: pending + - Context synchronization handoff: Changed files: `config/pkl/base/workflow-next-task.pkl`, `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/check-generated.sh`, `config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl`; Implementation summary: In both renderings of the plan-review phase (the static composite `nextTaskPlanReviewReference` string that produces `references/plan-review.md`, and the package-mode `renderReviewSkillBody` function that produces the standalone `sce-plan-review` skill and the composite command's inlined step 1), rewrote step "1.2 Resolve one task" so that, on finding unresolved synchronization debt in an earlier completed task, the phase no longer refuses outright: it first checks for a durable `Context synchronization handoff` subsection (absent means a legacy plan, which fails explicitly with a migration required action instead of a reconstructed retry), otherwise loads the persisted handoff/blocker and runs the Task context synchronization phase with it as authoritative input, then on `synced`/`no_context_change` persists `synced` and clears blocker fields before continuing to check earlier debt or proceed to normal selection, or on a renewed `blocked` persists the refreshed blocker/required-action/retry-condition and stops. Updated the phase's opening framing ("It reads; it never writes" → reads, and writes only to persist a recovery outcome) and the "Plan review boundaries" list (scoped the "Update the plan" and "Synchronize context" prohibitions to exclude the new recovery path) in both renderings, plus added a "Recovering unresolved synchronization debt from earlier completed tasks" bullet to the package-mode skill's purpose list. Added `assertPlanReviewSyncDebtRecovery` to `generation-contract-check.pkl` (registered as `plan-review-sync-debt-recovery`), asserting every generated `sce-next-task/references/plan-review.md` contains both the recovery wording ("do not attempt a reconstructed retry") and the legacy-migration wording ("migrate the plan"). Added the negative fixture `fixtures/next-task-sync-debt-recovery-check.pkl` (a plan-review.md stub missing both phrases, asserting the check throws) and registered its expected diagnostic in `check-generated.sh`.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` (pass); `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` (pass, 25 semantic checks including the new one); `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` (throws as expected, diagnostic matches); `nix develop -c ./config/pkl/check-generated.sh` (pass, 107 files, all fixtures including the new one); `nix flake check` (all checks passed, after staging the new fixture file with `git add` so the flake's git-tracked source includes it); manual ephemeral regeneration confirmed `references/plan-review.md` renders byte-identical recovery/migration wording under `.claude`, `.opencode`, and `.pi`.; Done checks: All satisfied — both pkl eval commands succeed, the new negative fixture throws with the expected diagnostic when recovery/migration wording is missing, and the real generated `plan-review.md` passes the new assertion under all three targets.; Context impact: None — this is workflow-generation source only; the referenced decision (`2026-08-12-persist-workflow-sync-lifecycle-in-plans`) already describes the intended lifecycle at the level `context/` documents; no `context/` file describes plan-review phase step-level behavior independently of the generated reference itself. + - Completed: 2026-08-13 + - Files changed: `config/pkl/base/workflow-next-task.pkl`, `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/check-generated.sh`, `config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` — pass; `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` — pass (25 semantic checks); `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` — throws with expected diagnostic; `nix develop -c ./config/pkl/check-generated.sh` — pass (107 files, ephemeral generation); `nix flake check` — all checks passed. + - Notes: Kept the recovery loop scoped to "the first task carrying debt" per invocation rather than resolving all debt in one pass, since the phase's existing "review at most one task per invocation" discipline and the retry/stop branching in the goal statement both describe single-step progress; a plan with multiple debts is recovered incrementally across successive `/next-task` invocations. The new semantic check and fixture follow the existing `assertAtomicCommitContent`/`atomic-commit-content-check.pkl` pattern (per-path document substring assertion with a minimal negative-fixture stub) rather than the `assertNoStaleSyncDebtText` pattern, since the check is scoped to one specific reference document rather than all generated artifacts. + +- [x] T04: `Retry task-context-sync from the persisted handoff` (status:done) + - Task ID: T04 + - Goal: In the task-role synchronization defined in + `config/pkl/base/workflow-context-sync.pkl`, accept either the live + execution handoff (same-session, from T02) or the persisted plan-recorded + handoff (cross-session retry invoked by T03) as authoritative input, + while still refusing to reconstruct a missing handoff from conversation + history. Update the blocked-report writing to populate the handoff and + blocker subsections from T01 rather than only the current flat fields. + - Boundaries (in/out of scope): In — task-role sections of + `workflow-context-sync.pkl` (handoff validation step, blocked-report + rendering), package and composite. Out — plan-role (`/validate`) + synchronization, which this finding does not touch. + - Dependencies: T01, T02 + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` + succeeds; generated `sce-next-task/references/context-sync.md` documents + accepting the persisted handoff on retry and writes the new subsections + when blocked. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl`; targeted generation and grep for the retry-input and blocked-report wording. + - Context synchronization: pending + - Context synchronization handoff: Changed files: `config/pkl/base/workflow-context-sync.pkl`; Implementation summary: In the task role's `input` function (package) and the composite `taskReference` string's introduction/step-3.1, widened the accepted handoff to either the live `status: complete` execution result (same-session) or the persisted `Context synchronization handoff` (and, on retry of a `blocked` task, `Context synchronization blocker`) subsection loaded from the plan by the plan-review recovery step (cross-session), and generalized the "do not reconstruct" rule to cover both sources; renamed step 1/3.1 from "Validate the execution handoff" to "Validate the handoff" in both renderings and adjusted its bullet list accordingly. Added a note to step 8/3.8 "Return the Markdown report" (both renderings) stating a `blocked` report always writes `Context synchronization handoff` and `Context synchronization blocker` subsections using the plan's completion-record field names. Extended `SyncReportRole` with `blockedHandoffSection`, `blockedBlockerSection`, and `blockedRetrySection` function fields (replacing the previously hardcoded `## Blocker`/`## Retry condition` blocks in the shared `blockedReport` template with role-supplied content, joined via blank-line-safe conditional interpolation); gave `taskReport` new `## Context synchronization handoff` (Changed files, Implementation summary, Verification, Done checks, Context impact) and `## Context synchronization blocker` (Blocker, Required action, Retry condition) subsections matching T01's completion-record field names, removing the redundant standalone "Updated files" list from the blocked identity; gave `planReport` the same `blockedBlockerSection`/`blockedRetrySection` fields reproducing its prior `## Blocker`/`## Retry condition` content unchanged, and `blockedHandoffSection` as empty, so plan-role rendering is unaffected.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` (pass); `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` (pass, 25 semantic checks, none newly failing); `nix develop -c ./config/pkl/check-generated.sh` (pass, 107 files, ephemeral generation); `nix flake check` (all checks passed); targeted generation + grep of `sce-next-task/references/context-sync.md` confirmed the persisted-handoff and blocked-subsection wording renders; direct inspection of `taskOutputReport`/`planOutputReport` via `pkl eval -x` confirmed the task blocked variant renders both new subsections cleanly (no blank-line runs) and the plan blocked variant is byte-identical to its prior `## Blocker`/`## Retry condition` rendering.; Done checks: All satisfied — pkl eval succeeds; generated `context-sync.md` documents accepting the persisted handoff on retry (`persisted`, `Context synchronization handoff`, `Context synchronization blocker` wording present) and states the blocked report writes the new subsections.; Context impact: None — this is workflow-generation source only; the referenced decision (`2026-08-12-persist-workflow-sync-lifecycle-in-plans`) already describes the intended lifecycle at the level `context/` documents; no `context/` file describes context-sync phase step-level behavior independently of the generated reference itself. + - Completed: 2026-08-13 + - Files changed: `config/pkl/base/workflow-context-sync.pkl` + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` — pass; `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` — pass (25 semantic checks); `nix develop -c ./config/pkl/check-generated.sh` — pass (107 files, ephemeral generation); `nix flake check` — all checks passed. + - Notes: Kept plan-role (`/validate`) blocked-report rendering byte-identical to its pre-task form by giving it the same new role fields with content reproducing the old hardcoded text, rather than special-casing the shared `blockedReport` template per role; this keeps the shared template single-sourced while satisfying the task's "out of scope" boundary for plan-role synchronization. Removed the `retryCondition` field from `SyncReportRole` entirely (folded into `blockedRetrySection`) since nothing referenced it once the blocked-report skeleton switched to role-supplied section functions — a reversible, in-scope local cleanup rather than a second field carrying duplicate content. + +- [x] T05: `Reconcile the bypass-commit temp-file rule` (status:done) + - Task ID: T05 + - Goal: In `config/pkl/base/workflow-commit.pkl`, scope the "do not stage, + unstage, restore, or otherwise modify files" rule to repository/worktree + files, and explicitly permit exactly the commit-message temp file the + bypass path already instructs writing. State that the file must be + created outside the repository working tree, written verbatim without + shell interpolation, used for exactly one `git commit -F `, + and cleaned up after the commit attempt including failure paths where + practical, with the resulting hash retrieved explicitly only after + success via `git rev-parse --verify HEAD^{commit}`. Apply consistently to + every rendered occurrence (package command, package skill, composite + skill). + - Boundaries (in/out of scope): In — the bypass execution handoff and + "Atomic commit boundaries"/rule blocks in `workflow-commit.pkl`. Out — + regular-mode proposal behavior; anything under next-task/context-sync. + - Dependencies: none + - Done when: `nix develop -c pkl eval config/pkl/base/workflow-commit.pkl` + succeeds; generated `sce-commit/SKILL.md` and + `sce-commit/references/atomic-commit.md` no longer contain an unscoped + "do not modify files" statement beside the temp-file instruction, and + every occurrence states the out-of-worktree, no-interpolation, exactly-once, + cleanup, and explicit-hash requirements. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/base/workflow-commit.pkl`; targeted generation and grep across `sce-commit/SKILL.md` plus `references/atomic-commit.md` for the reconciled wording. + - Context synchronization: synced + - Context synchronization handoff: Changed files: `config/pkl/base/workflow-commit.pkl`; Implementation summary: In `renderCommandBody` (package command / composite command shared body), the "Execute exactly one commit" bypass step now instructs creating the temp file outside the repository working tree and adds an explicit "delete the temp file after the commit attempt, including on failure, where practical" step, and the command's "Rules" list scopes the mutation prohibition to "repository or worktree files" with the temp file named as the sole exception because it lives outside the working tree. Made the matching pair of edits in `renderAtomicCommitSkillBody`'s "Bypass execution handoff" section (canonical package-skill/`.opencode`/`.pi` source) and its "Atomic commit boundaries" `Do not:` list. Made the same pair of edits in the static composite `commitSkillBody` string's step 3 "Execute exactly one commit" and its "Rules" list (renders into `.claude`'s `sce-commit/SKILL.md`). All three rendered occurrences now state: out-of-worktree temp-file creation, no shell interpolation (pre-existing wording, unchanged), exactly one `git commit -F ` (pre-existing, unchanged), post-success-only explicit hash retrieval via `git rev-parse --verify HEAD^{commit}` (pre-existing, unchanged), and post-attempt cleanup including failure paths where practical (new); the "do not modify files" rule now reads "repository or worktree files" with the out-of-worktree temp file carved out explicitly, so the bypass instruction to write it no longer contradicts the rule beside it.; Verification: `nix develop -c pkl eval config/pkl/base/workflow-commit.pkl` (pass); targeted ephemeral generation via `nix run .#pkl-generate` into a temp dir, grep of `sce-commit/SKILL.md` under `.claude`/`.opencode`/`.pi` and `sce-commit/references/atomic-commit.md` under `.opencode`/`.pi` confirmed the out-of-worktree/cleanup wording and the scoped mutation rule render identically in all three targets; `nix develop -c ./config/pkl/check-generated.sh` (pass, 107 files, ephemeral generation).; Done checks: All satisfied — pkl eval succeeds; every generated occurrence states the out-of-worktree, no-interpolation, exactly-once, cleanup, and explicit-hash requirements, and no occurrence retains an unscoped "do not modify files" statement beside the temp-file instruction.; Context impact: None — this is workflow-generation source only; no `context/` file describes the bypass-commit temp-file rule independently of the generated reference itself. + - Completed: 2026-08-13 + - Files changed: `config/pkl/base/workflow-commit.pkl` + - Evidence: `nix develop -c pkl eval config/pkl/base/workflow-commit.pkl` — pass; `nix develop -c ./config/pkl/check-generated.sh` — pass (107 files, ephemeral generation); targeted generation + grep of `sce-commit/SKILL.md` (`.claude`, `.opencode`, `.pi`) and `sce-commit/references/atomic-commit.md` (`.opencode`, `.pi`) confirmed the reconciled wording renders in every occurrence. + - Notes: The package-mode `commit.md` command document (`structuredCommand.render.apply("package")`) is not among the three currently-generated targets — all three (`.claude`, `.opencode`, `.pi`) render the composite command stub plus the full skill body — but the shared `renderCommandBody` function was still updated since it is the single source for that content wherever it is later used, satisfying "every rendered occurrence" for what the generation pipeline currently produces. + +- [x] T06: `Resolve the layout-reference checker against the captured file and verify all targets` (status:done) + - Task ID: T06 + - Goal: In `config/pkl/renderers/generation-contract-check.pkl`, change + `assertLayoutReferences` so each `Render the **X** layout from + \`references/foo.md\`` instruction resolves the exact captured + `references/foo.md` (package-root-relative) instead of a hardcoded + `references/output.md`, asserting both that the document exists and that + it contains a matching `## X` heading, while keeping the existing general + package-local-reference-existence assertion + (`assertPackageLocalReferences`) unchanged. Add a negative fixture (an + instruction citing `references/wrong-file.md`, which lacks `## Completion`, + while `references/output.md` has it) proving the filename matters, and a + positive fixture (heading present in the actually-referenced document), + registered in `config/pkl/check-generated.sh`. Finish by regenerating all + three targets and running the full verification suite, confirming no + generated file retains contradictory lifecycle or temporary-file wording + from T01-T05 across `.opencode`, `.claude`, and `.pi`. + - Boundaries (in/out of scope): In — `assertLayoutReferences`, its two new + fixtures, `check-generated.sh` registration, and the closing + regeneration/verification pass. Out — any other semantic check or + fixture. + - Dependencies: T01, T02, T03, T04, T05 + - Done when: `nix develop -c pkl eval + config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` + throws with a diagnostic naming the mismatched file; `nix develop -c pkl + eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` + passes; `nix run .#pkl-check-generated` and `nix flake check` both pass; + manual inspection of a temporary full generation confirms items 1-9 in + the originating request's Verification section. + - Verification notes (commands or checks): `nix develop -c pkl eval config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` (expect throw); `nix develop -c pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl`; `nix run .#pkl-check-generated`; `nix flake check`. + - Context synchronization: pending + - Context synchronization handoff: Changed files: `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/renderers/fixtures/layout-reference-check.pkl`, `config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` (new), `config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` (new), `config/pkl/check-generated.sh`; Implementation summary: Rewrote `assertLayoutReferences` in `generation-contract-check.pkl` to stop hardcoding `references/output.md`: it now iterates every `Render the **X** layout from \`references/foo.md\`` citation in each SKILL.md (excluding `sce-decision`), resolves the exact captured `references/foo.md` path relative to that skill's package root, and collects a violation (the resolved path) whenever that document is missing or lacks a matching `## X` heading, using `documents.toMap().entries.filter/flatMap` instead of the prior nested `every`/hardcoded-path form; the throw now names the first violating file (`` `\(violations[0])` ``) instead of a static "its references/output.md" message, and the success message reads "every citation resolves to a heading in its cited references document". `assertPackageLocalReferences` was left unchanged. Updated the pre-existing `layout-reference-check.pkl` fixture's unreachable local fallback message to match (cosmetic only — the fixture's real diagnostic comes from the production throw). Added `wrong-file-layout-reference-check.pkl`: an sce-next-task SKILL.md instruction citing `references/wrong-file.md` (injected, heading-less) while `references/output.md` in the same package is also given a `## Completion` heading, proving the checker resolves the cited file rather than falling back to `output.md`; asserts the throw names the `wrong-file.md` path. Added `correct-file-layout-reference-check.pkl`: an instruction citing `references/correct-file.md` (injected, containing `## Completion`), asserting `assertLayoutReferences.apply(documents)` returns its success string without throwing. Registered both fixtures in `check-generated.sh`: the wrong-file fixture via `expect_pkl_fixture_failure` with the file-naming diagnostic, the correct-file fixture via a plain `pkl eval ... >/dev/null` (following the existing metadata-coverage/generation-contract eval-only pattern, since it is the suite's first positive fixture); also updated the pre-existing `layout-reference-check.pkl` registration's expected diagnostic to the new file-naming message. Staged both new fixture files with `git add` so `nix flake check`'s git-tracked source filter includes them (same requirement T03 encountered).; Verification: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` (pass, 25 semantic checks, `layout-references` message updated); `nix develop -c pkl eval config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` (throws, diagnostic names `config/.opencode/skills/sce-next-task/references/wrong-file.md`); `nix develop -c pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` (passes, no throw); `nix run .#pkl-check-generated` (pass, 107 files, ephemeral generation); `nix flake check` (all 5 checks passed, after staging the new fixtures); manual ephemeral regeneration into a temp dir + grep across `.claude`/`.opencode`/`.pi` confirmed: no unscoped "do not modify files" wording beside the commit temp-file instruction (the only remaining "do not modify files" hits are the unrelated task-execution approval-decline rule), the commit rule's "repository or worktree files" scoping renders in all three targets, the plan-template's concrete `Context synchronization: pending` and widened `pending | synced | blocked` completion-record union render in all three targets, the plan-review sync-debt-recovery wording ("do not attempt a reconstructed retry") renders in all three targets, and no target contains the stale "Nothing records the skipped synchronization" text.; Done checks: All satisfied — both new fixtures behave as specified (negative throws naming the mismatched file, positive passes), `nix run .#pkl-check-generated` and `nix flake check` both pass, and manual inspection of a temporary full generation found no contradictory lifecycle or temporary-file wording surviving from T01-T05 across any target.; Context impact: None — this is workflow-generation source only; no `context/` file describes the layout-reference checker's resolution behavior independently of the generated reference/check itself. + - Completed: 2026-08-13 + - Files changed: `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/renderers/fixtures/layout-reference-check.pkl`, `config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl`, `config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl`, `config/pkl/check-generated.sh` + - Evidence: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` — pass (25 semantic checks); `nix develop -c pkl eval config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` — throws, names `config/.opencode/skills/sce-next-task/references/wrong-file.md`; `nix develop -c pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` — passes; `nix run .#pkl-check-generated` — pass (107 files, ephemeral generation); `nix flake check` — all checks passed. + +## Open questions + +None. The request specifies exact field names, per-state semantics, the +checker's exact defect and fix, and the fixture-based testing approach this +repository already uses for semantic generation checks, so no scope, +acceptance-criteria, or ordering decision remains open. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-13 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (107 files, ephemeral generation, pass) +- `nix flake check` -> exit 0 (all checks passed) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` -> exit 1 (throws as required: "plan-review reference must state sync-debt recovery and legacy-migration-failure behavior") +- `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` -> exit 0 (pass) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` -> exit 1 (throws as required, names `config/.opencode/skills/sce-next-task/references/wrong-file.md`) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` -> exit 0 (passes, returns success string) +- `nix develop -c pkl eval config/pkl/base/workflow-change-to-plan.pkl` -> exit 0 (pass) +- `nix run .#pkl-generate -- ` -> exit 0 (targeted regeneration for AC1/AC3/AC4/AC6 inspection) + +### Scaffolding removed + +- None. + +### Success-criteria verification + +- [x] AC1: durable "Context synchronization handoff" and "Context synchronization blocker" subsections in the completion-record example -> generated `sce-change-to-plan/references/plan-template.md` completion-record example (line ~170-172) shows both subsections with the named fields; `task-execution.md` (T02) writes them at completion, confirmed via `nix develop -c pkl eval config/pkl/base/workflow-next-task.pkl` pass and prior task evidence. +- [x] AC2: `/next-task` sync-debt recovery before new-task selection -> `nix develop -c pkl eval config/pkl/renderers/fixtures/next-task-sync-debt-recovery-check.pkl` throws as required; `nix run .#pkl-check-generated` passes. +- [x] AC3: task-context-sync accepts live or persisted handoff, never reconstructs -> generated `sce-next-task/references/context-sync.md` documents both input sources ("live" / "persisted") and the blocked-report shape (lines 9, 17, 25, 33, 37, 52, 308, 310); `nix develop -c pkl eval config/pkl/base/workflow-context-sync.pkl` passes. +- [x] AC4: bypass-mode `/commit` reconciled wording -> generated `sce-commit/SKILL.md` (`.claude`, `.opencode`, `.pi`) and `sce-commit/references/atomic-commit.md` (`.opencode`, `.pi`) all state "Create the commit-message temp file outside the repository working tree" and the scoped "do not... modify repository or worktree files" rule, in every rendered occurrence. +- [x] AC5: layout-reference checker resolves the cited file, not a hardcoded `references/output.md` -> `nix develop -c pkl eval config/pkl/renderers/fixtures/wrong-file-layout-reference-check.pkl` throws naming the mismatched file; `nix develop -c pkl eval config/pkl/renderers/fixtures/correct-file-layout-reference-check.pkl` passes; `nix run .#pkl-check-generated` passes. +- [x] AC6: freshly authored task starts `Context synchronization: pending` as a concrete value, domain documented separately -> generated `sce-change-to-plan/references/plan-template.md` (package and composite) task-authoring examples (lines 81, 90, 116) render the concrete `pending` value; the `pending | synced | blocked` domain is documented separately (lines 48, 51); completion-record example accepts the full union (line 170). + +### Failed checks and follow-ups + +- None. + +### Residual risks + +- None identified. diff --git a/context/plans/workflow-skill-boundary-cleanup.md b/context/plans/workflow-skill-boundary-cleanup.md new file mode 100644 index 00000000..2b75f8d0 --- /dev/null +++ b/context/plans/workflow-skill-boundary-cleanup.md @@ -0,0 +1,486 @@ +# Plan: workflow-skill-boundary-cleanup + +## Change summary + +Cleans up redundant generated workflow references, makes context-synchronization +lifecycle state durable across sessions, tightens several workflow boundaries +(validation must not repair what it validates, next-task's execution handoff +must be explicit and Git-baseline-aware, handover must not trust +placeholder-only sections), hardens bypass-commit execution, cleans up +decision semantics, removes misleading generic composite boilerplate, and +strengthens `generation-contract-check.pkl` with semantic (not just +inventory/path) checks. `config/pkl/**` remains the sole canonical source; +`.pi/**`, `.claude/**`, and `.opencode/**` are never hand-edited — every +change lands in Pkl and is proven through regeneration. + +This extends existing behavior; it does not replace the six-workflow, +single-skill-control-flow, package-local-reference architecture. Direct +inspection before authoring confirmed several premises as real, current gaps +rather than already-fixed ones: + +- `config/pkl/base/workflow-validate.pkl:731` currently renders + `references/output.md` from the validation phase's `validated`/`failed`/ + `blocked` result variants, while the composite command body (`:73-77`) + already cites a `**Context synchronization blocked**` layout and a + `**Completion**` layout from `references/output.md` that do not currently + exist as headings anywhere in that file — a live reference-to-missing- + heading bug T01 fixes and T12 guards against recurring. +- `config/pkl/base/workflow-next-task.pkl:1882-2023` embeds a full, literal + `# Context Sync Report` block (all three variants) inside + `nextTaskOutputReference` (next-task's own `references/output.md`), which + is a byte-for-byte duplicate of `contextSync.taskOutputReport`, separately + emitted as `references/sync-report.md` (`:2032`) — the exact duplication + T02 removes. +- `config/pkl/base/workflow-validate.pkl:316-327` deletes scaffolding + ("temporary files or intermediate artifacts... local scaffolding... + marked as temporary") as a real step inside validation itself, and the + report format (`:471-474`, `:567-570`) carries a **Scaffolding removed** + field — the repair-during-validation behavior T05 removes. +- `config/pkl/base/workflow-handover.pkl:128` grounds writer mode only in + `git status` and `git diff` (no `--cached`), and its loader-mode + validation (`:198-205`) checks only that the four required headings are + present, with no check for real section content — the gaps T07 closes. +- `config/pkl/base/workflow-change-to-plan.pkl:125-133` models both "answer + earlier clarification questions" and "answer with changes to an + already-written plan" through the same undifferentiated prose, with no + named `original_request`/`clarification_answers` vs. `plan_path`/ + `correction` structure — the gap T04 closes. +- `config/pkl/renderers/opencode-metadata.pkl` derives the Code agent's + `skill:` permission block from the full workflow catalog unconditionally, + including the optional `sce-brownfield` workflow, with no per-repository + installed-selection concept available at generation time. This is + intentional, existing, ADR-backed behavior: `context/decisions/ + 2026-07-31-install-time-optional-workflows.md` states the `optional` flag + "must never condition generation, composition, routing, permissions, or + the artifact-path contract" and accepts the dangling `sce-brownfield` + permission as inert-by-design. T13 is scoped to add a generation-time + integrity assertion only; it does not change this behavior. (Resolved via + clarification; see Open questions.) + +## Acceptance criteria + +- [x] AC1: The 9 named generated files no longer exist under any of + `.pi/skills/**`, `.claude/skills/**`, `.opencode/skills/**` in a fresh + generation, and none of the forbidden replacement files exist either. + - Validate: `nix run .#pkl-generate -- "$(mktemp -d)"`, then inspect the + output tree for the absence of `sce-commit/references/commit-contract.yaml`, + `sce-commit/references/commit-message-style.md`, + `sce-validate/references/sync-report.md` (three targets each), and the + absence of `sce-commit/references/commit-contract.md` and + `sce-validate/references/validation-result.md`. +- [x] AC2: `sce-next-task` is still the only package generating + `references/sync-report.md`, and its own `references/output.md` no longer + contains an embedded Context Sync Report block or its variants. + - Validate: `grep -c "# Context Sync Report" /sce-next-task/references/output.md` is `0`; `references/sync-report.md` still exists per target. +- [x] AC3: Context-synchronization lifecycle (task-level and plan-level: + `pending → synced | blocked`) is persisted in the plan file, survives a + fresh session, blocks `/next-task` from starting a new implementation task + while an earlier task's sync debt is unresolved, and blocks `/validate` + from treating the plan as finishable while task-level sync debt remains. + - Validate: read the generated `sce-next-task`/`sce-validate` `SKILL.md` + and reference content for the new lifecycle fields and the gating rule + text; confirm `references/plan-template.md` documents the new fields. +- [x] AC4: The generated `sce-change-to-plan` `SKILL.md` models + initial-clarification continuation (`original_request`, + `clarification_answers`, `loaded_context_brief`) and existing-plan + revision (`plan_path`, `correction`, `loaded_context_brief`) as distinct, + explicitly named continuations, and the original change request is never + re-requested from the user across a clarification wait. + - Validate: read generated `sce-change-to-plan/SKILL.md` steps 1/3/4 for + the named fields. +- [x] AC5: A failed final validation records the result, emits the failure + handoff, and stops — it performs no scaffolding deletion and no repair of + application/test/config code; `Scaffolding removed` no longer appears as a + successful-validation field. + - Validate: `grep -i scaffold /sce-validate/references/*.md` + returns only failure-evidence language (leftover artifacts reported, + never deleted); no step deletes files. +- [x] AC6: The generated `sce-next-task` execution phase reference states + explicit required handoff fields, captures a pre-edit Git baseline, + computes `files_changed` from that baseline rather than the whole working + tree, and states deterministic stale-handoff behavior under + auto-approval. + - Validate: read the generated execution reference for the baseline- + capture step and the `files_changed` attribution rule. +- [x] AC7: The generated `sce-handover` writer mode reads both `git diff` + and `git diff --cached`; loader mode rejects a handover whose required + sections are present only as empty/placeholder headings. + - Validate: read generated `sce-handover/SKILL.md` (and + `references/handover-template.md` if extracted) for both changes. +- [x] AC8: The generated `sce-commit` package contains exactly `SKILL.md` + and `references/{atomic-commit.md,output.md}`; every rule and + result-contract field previously carried by `commit-contract.yaml` and + `commit-message-style.md` that the composite workflow actually needs now + lives in `atomic-commit.md`. + - Validate: directory listing of generated `sce-commit/references/` per + target; grep `atomic-commit.md` for commit-message rules and the result + contract. +- [x] AC9: Bypass commit (`oneshot`/`skip`) writes the commit message to a + file (or pipes it via stdin) rather than interpolating it into a shell + command, runs `git commit` exactly once, and retrieves the resulting + commit hash explicitly from `HEAD` rather than parsing Git's + human-readable output; on failure it does not retry, amend, stage more + files, or fabricate a hash. + - Validate: read the generated bypass-mode instructions in + `sce-commit/references/atomic-commit.md` for the message-file/stdin + mechanism and the explicit `HEAD` hash-retrieval step. +- [x] AC10: A nonqualifying decision-gate invocation reports + `not_qualified` or `skipped`, never `blocked`, and synchronization + continues normally after it; existing ADRs are immutable regardless of + status; a changed decision always creates a new dated ADR; only an + equivalent *active* ADR is reused. + - Validate: read generated `sce-decision/SKILL.md` for the nonqualifying + result vocabulary and the reuse-only-active-ADR rule. +- [x] AC11: The shared composite preamble (and generated command + boilerplate) no longer claims every workflow supports clarification, + validation repair, or bootstrap waits; it instead states that any + workflow-defined user wait resumes the same skill in the same session, + and workflow-specific wait semantics stay in the workflows that own them. + - Validate: grep every generated workflow `SKILL.md` for the old + overclaiming boilerplate (absent) and the new generic wording (present). +- [x] AC12: `generation-contract-check.pkl` enforces the 9 semantic checks + from the change request (heading-vs-reference match, package-local path + existence, validate/commit forbidden-file checks, atomic-commit.md dual + content, next-task non-duplication, Pi/Claude/OpenCode reference parity, + absence of the stale "Nothing records the skipped synchronization" line, + no-repair-during-validation), each proven by a negative fixture. + - Validate: `nix run .#pkl-check-generated` passes; each new negative + fixture demonstrably fails with its intended diagnostic when evaluated. +- [x] AC13: The generated OpenCode Code-agent `skill:` permission block + stays catalog-derived and unconditional (including the inert + `sce-brownfield` permission), and a new generation-time assertion + confirms every explicitly allowed `sce-*` permission names a workflow + artifact the generator is capable of emitting for that target. + Installation status is not consulted or required. + - Validate: read `config/pkl/renderers/opencode-metadata.pkl` (unchanged + permission derivation) and the new assertion in + `generation-contract-check.pkl`; confirm a deliberately-misspelled + permission fixture fails the assertion. + +### Full validation + +- `nix run .#pkl-check-generated` +- `nix flake check` + +### Context sync + +- `context/architecture.md` — package-relative reference inventories for + `sce-validate`, `sce-next-task`, `sce-commit` (file counts and names + change); the `renderSkill` preamble description (T11); the + generation-contract-check description (T12, T13); the plan template + description (T03). +- `context/patterns.md` — the Pkl renderer layering / phase-reference + bullets describing `sce-validate`'s and `sce-commit`'s package-local + reference inventories, and the generic composite preamble bullet (T11). +- `context/sce/shared-context-code-workflow.md` — the `sce-validate` and + `sce-next-task` package file listings (T01, T02), the durable + sync-lifecycle description (T03), and the validation/decision semantics + (T05, T10). +- `context/sce/shared-context-plan-workflow.md` — the + clarification/revision continuation contract (T04). +- `context/sce/atomic-commit-workflow.md` — the `sce-commit` package file + listing and bypass-mode determinism (T08, T09). +- `context/sce/handover-workflow.md` — the writer/loader hardening (T07). +- `context/glossary.md` — glossary entries naming the removed files + (`commit-contract.yaml`, `commit-message-style.md`, + `sce-validate/references/sync-report.md`) or describing the old + plan-task field set. +- A new dated ADR under `context/decisions/` if the task/plan + synchronization gate judges this cross-cutting change qualifying (it + changes validation, commit execution, and decision semantics repository- + wide). This plan does **not** author or supersede + `context/decisions/2026-07-31-install-time-optional-workflows.md`; T13 + is fully compatible with it. + +## Constraints and non-goals + +- **In scope:** `config/pkl/base/workflow-{validate,next-task,context-sync, + change-to-plan,handover,commit}.pkl`, `config/pkl/base/decision-skill.pkl`, + `config/pkl/base/workflow-catalog.pkl` (read-only reference), + `config/pkl/renderers/{workflow-composite,generation-contract-check, + opencode-metadata}.pkl`, the plan template these workflows share, and + ephemeral regeneration verification. +- **Out of scope:** the Rust `sce` CLI (`cli/**`) — no task in this plan + changes CLI code; non-workflow Pkl (`sce-config-schema.pkl`, + `opencode.pkl` plugin registration); which workflows are core vs. + optional; the install-time optional-workflow selection architecture; any + change to the six-workflow / single-skill-control-flow / + package-local-reference model beyond what each task explicitly + authorizes. +- **Constraints:** `config/pkl/**` is the sole source of truth; never + hand-edit `.pi/**`, `.claude/**`, or `.opencode/**`; preserve the + existing package/composite structured-rendering model + (`workflow-content.pkl`); preserve the sole allowed SCE sibling + invocation (`sce-decision` from the synchronization gate); keep + Pi/Claude/OpenCode target-neutral except where a target's runtime + genuinely requires a difference; do not manually patch generated output + to make checks pass — fix canonical sources; do not touch or supersede + `context/decisions/2026-07-31-install-time-optional-workflows.md`. +- **Non-goal:** this plan does not redesign workflow architecture beyond + the 13 described boundary/redundancy fixes; it does not add or remove + any catalog workflow; it does not make any optional workflow's + generation, permissions, or artifact paths conditional on install-time + selection; it does not change the CLI's actual Git/database behavior — + only the generated agent instructions describing how an executing agent + should use Git/state. + +## Assumptions + +- "Persist" in T03 means: write the lifecycle state into the plan file's + Markdown (new task-level and plan-level fields), the same durable medium + the plan already uses for `(status:todo|done)` and completion evidence — + not a new database or file format, consistent with the `disposable plan + lifecycle` policy (`context/glossary.md`). +- T04's "explicit typed structures" are named prose fields inside the + generated agent instructions (state an executing LLM skill is told to + track across a same-session wait), not a Pkl `class`/`typealias` runtime + type — these files author Markdown instructions for an executing agent, + not compiled state, so there is no runtime consumer for a literal type. +- T06's "review of the mandatory five-root-file pass" is resolved as: keep + it mandatory for every task (current behavior), and state that choice + plainly in the execution/context-sync reference text, since the request + explicitly forbids silently removing it and the pass is cheap, + deterministic, and already load-bearing for context accuracy. +- T10's "review whether Deprecated and Superseded should be creation-time + statuses" is resolved during T10's implementation by inspecting + `decision-skill.pkl`'s current status vocabulary; if collapsing them + would lose information a downstream reader needs, both statuses are kept + and made creation-time-only (never mutated after creation) rather than + merged. +- T13 is scoped exactly per clarification: catalog-derived, unconditional + OpenCode permissions are preserved as-is; only a generation-time + artifact-integrity assertion is added. No CLI code changes, no ADR + supersession, no installed-selection concept at generation time. +- Each of T01–T13 lands as one atomic commit, per the change request's + stated commit split; the task stack mirrors that split one-to-one. + +## Task stack + +- [x] T01: `Consolidate sce-validate references (validation.md, context-sync.md, no sync-report.md/validation-result.md)` (status:done) + - Task ID: T01 + - Goal: Move the validation phase result contract into `references/validation.md` and the plan context-sync result/report contract into `references/context-sync.md`, stop generating `references/sync-report.md` for `sce-validate`, and reduce `references/output.md` to only composite user-visible layouts — including the currently-missing `Context synchronization blocked` and `Completion` headings the command body already cites. + - Boundaries (in/out of scope): In — `config/pkl/base/workflow-validate.pkl` (`references/output.md` restructuring, the validation result contract, the `referenceDocuments`/`outputDocuments` listings), reading from `workflow-context-sync.pkl`'s existing plan-role report content rather than duplicating it. Out — behavior changes to what validation actually checks (T05), next-task's package (T02). + - Dependencies: none + - Done when: generated `sce-validate/references/` contains exactly `validation.md`, `context-sync.md`, `validation-report.md`, `output.md` (no `sync-report.md`, no `validation-result.md`) for Pi, Claude, and OpenCode; `output.md` contains `## Context synchronization blocked` and `## Completion` headings and no validation-phase `validated`/`failed`/`blocked` result variants; every `Render the **X** layout from references/output.md` instruction in generated `sce-validate/SKILL.md` has a matching heading in `output.md`. + - Verification notes (commands or checks): `nix run .#pkl-generate -- "$(mktemp -d)"`; inspect `sce-validate/references/`; `nix run .#pkl-check-generated`. + - Implementation evidence: In `config/pkl/base/workflow-validate.pkl` — `references/validation.md` is now `renderValidationSkillBody` concatenated with the `VALIDATION_RESULT` contract (self-cited via new `validationResultSelfRef`; `validation-report.md`'s cross-file citation uses new `validationResultFileRef` = `` `references/validation.md` ``); `references/context-sync.md` is `contextSync.planSkillBody` (package mode) with its `` `references/sync-report.md` `` marker redirected in place to a self-reference, concatenated with `contextSync.planOutputReport`; `references/sync-report.md` is no longer in `referenceDocuments`; `references/output.md` is now the new `VALIDATE_OUTPUT_LAYOUTS` constant carrying only `## Context synchronization blocked` and `## Completion`, matching the two `Render the **X** layout from references/output.md` citations already in the composite `SKILL.md` body (`workflow-content.pkl`'s `validateSkillBody`, unchanged). Also fixed two stale canonical-inventory literals this change made incorrect: `config/pkl/renderers/metadata-coverage-check.pkl`'s `validate` entry in `phaseReferencePathsByWorkflow` (dropped `references/sync-report.md`), and `config/pkl/renderers/generation-contract-check.pkl`'s `expectedArtifactPathCount` (113 -> 110, for the 3 removed per-target files; matches the plan's own T13 note of a net -9 from T01/T08). + - Verification performed: `nix run .#pkl-generate -- "$(mktemp -d)"` then inspected `config/.claude/skills/sce-validate/references/` for all three targets — contains exactly `validation.md`, `context-sync.md`, `validation-report.md`, `output.md`; `output.md` has exactly `## Context synchronization blocked` and `## Completion` headings and no validation result variants; `grep -rn sync-report` under generated `sce-validate/**` returns nothing; both `Render the **X** layout from references/output.md` citations in generated `SKILL.md` match `output.md` headings. `nix run .#pkl-check-generated` passes (110 files). `nix flake check` passes. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `context/architecture.md`, `context/patterns.md`, `context/sce/shared-context-code-workflow.md` + - Evidence: `context/sce/shared-context-code-workflow.md`'s `sce-validate` package listing (`references/{validation,context-sync,validation-report,output}.md`, no `sync-report.md`) and `output.md`'s two-layout description now reflect T01's restructuring; `context/architecture.md` and `context/patterns.md` carry the same corrected inventory. + - Notes: This record was backfilled after the fact; the underlying context files were already updated to T01's final state but the plan's task-level lifecycle field was never written at the time, which the plan's own T03 note flagged as unresolved sync debt. Backfilled per validation gate's sync-debt finding rather than re-running context sync, since inspection of the current `context/` files confirms no drift from T01's actual output. + +- [x] T02: `Deduplicate next-task sync output` (status:done) + - Task ID: T02 + - Goal: Remove the literal duplicated `# Context Sync Report` block (all three variants) from `nextTaskOutputReference` in `config/pkl/base/workflow-next-task.pkl`, keeping only workflow gates and terminal layouts in `references/output.md`; keep `references/sync-report.md` as the sole owner of the task context-sync report contract. + - Boundaries (in/out of scope): In — `workflow-next-task.pkl`'s `nextTaskOutputReference` string and its reference listings. Out — the context-sync phase's actual behavior (`workflow-context-sync.pkl`, T03 territory), validation's package (T01). + - Dependencies: none + - Done when: generated `sce-next-task/references/output.md` contains no `# Context Sync Report` heading or report-variant content; generated `sce-next-task/references/sync-report.md` still exists and is unchanged in meaning; the `Context synchronization blocked` workflow gate in `output.md` still exists, distinct from the report itself. + - Verification notes (commands or checks): `nix run .#pkl-generate -- "$(mktemp -d)"`; `grep -c "# Context Sync Report" /sce-next-task/references/output.md` is `0`; `nix run .#pkl-check-generated`. + - Implementation evidence: In `config/pkl/base/workflow-next-task.pkl` — removed the entire embedded `# Context Sync Report` section (all three `synced`/`no_context_change`/`blocked` report variants plus the shared **Report rules**) from the `nextTaskOutputReference` string, which previously duplicated `contextSync.taskOutputReport` byte-for-byte. `nextTaskOutputReference` now ends after the **Implementation gate**'s `## Rules` block; the workflow gate layout `## Context synchronization blocked` (distinct, decision-relevant workflow prose) is untouched. The `references/sync-report.md` document binding (`model.makeDocument.apply("references/sync-report.md", contextSync.taskOutputReport)`) was not touched and remains the sole owner of the report contract. + - Verification performed: `nix run .#pkl-generate -- "$(mktemp -d)"` then, for all three targets (`.pi`, `.claude`, `.opencode`) under `sce-next-task/references/`: `grep -c "# Context Sync Report" output.md` is `0`; `sync-report.md` still exists; `grep -c "^## Context synchronization blocked" output.md` is `1`. `nix run .#pkl-check-generated` passes (110 files, unchanged count from T01's post-fix baseline). + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `context/sce/shared-context-code-workflow.md` + - Evidence: `context/sce/shared-context-code-workflow.md`'s `sce-next-task` package listing (`references/{plan-review,task-execution,context-sync,sync-report,output}.md`) already reflects `sync-report.md` as the sole owner of the task context-sync report with no duplication in `output.md`, matching T02's actual generated output. + - Notes: This record was backfilled after the fact for the same reason as T01: the context files were already correct but the plan's task-level lifecycle field was never written at the time. Backfilled per validation gate's sync-debt finding after confirming no drift between current `context/` files and T02's actual output. + +- [x] T03: `Persist context synchronization lifecycle` (status:done) + - Task ID: T03 + - Goal: Add durable `pending → synced | blocked` lifecycle tracking for both task-level and plan-level context synchronization to the plan template/model, and update `sce-next-task` and `sce-validate` to write `pending` before invoking sync, write `synced`/`blocked` (with blocker/required-action/retry-condition) after, and refuse to proceed (new task, or plan-finish) while unresolved sync debt exists. + - Boundaries (in/out of scope): In — `references/plan-template.md` (canonical source in `workflow-change-to-plan.pkl`), `workflow-next-task.pkl` (plan-review and task-context-sync steps), `workflow-validate.pkl` (finishability gating), `workflow-context-sync.pkl` only insofar as it reports the same status values it already returns — its own scope stays synchronizing `context/`. Out — inventing a non-plan-file persistence mechanism. + - Dependencies: T01, T02 + - Done when: the plan template documents new lifecycle field(s) with `pending`/`synced`/`blocked` states plus blocker/required-action/retry-condition for the blocked case; generated `sce-next-task/SKILL.md` states it will not start a new implementation task while an earlier completed task's sync lifecycle is not `synced`; generated `sce-validate/SKILL.md` states it will not treat the plan as finishable while any task's lifecycle is not `synced`; the state is written to the plan file (not only asserted in chat) at each transition; generated text no longer says anything equivalent to "nothing records the skipped synchronization, so it is lost once this session ends." + - Verification notes (commands or checks): read generated `sce-next-task/SKILL.md`, `sce-validate/SKILL.md`, and the generated `plan-template.md` reference for the new field/gating language; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-change-to-plan.pkl`, `config/pkl/base/workflow-content.pkl`, `config/pkl/base/workflow-context-sync.pkl`, `config/pkl/base/workflow-next-task.pkl`, `config/pkl/base/workflow-validate.pkl` + - Evidence: `nix run .#pkl-check-generated` passed; ephemeral generation produced 110 files; generated next-task, validate, context-sync, and plan-template references contain the lifecycle fields, transition rules, and unresolved-debt gates. + - Notes: Existing T01/T02 records predate the lifecycle fields; missing lifecycle records are intentionally treated as unresolved synchronization debt rather than inferred as synced. + +- [x] T04: `Fix change-to-plan clarification continuation` (status:done) + - Task ID: T04 + - Goal: In `config/pkl/base/workflow-change-to-plan.pkl`, name the initial-clarification continuation explicitly (`original_request`, `clarification_answers`, `loaded_context_brief`) distinct from existing-plan revision (`plan_path`, `correction`, `loaded_context_brief`), so the generated skill never needs to re-request the original change request from the user. + - Boundaries (in/out of scope): In — step 3/4 prose and any shared continuation description in `workflow-change-to-plan.pkl`. Out — the plan-authoring phase's actual authoring logic, the plan template (T03 territory), the clarification-gate question format. + - Dependencies: T03 + - Done when: generated `sce-change-to-plan/SKILL.md` names both continuation shapes explicitly and distinctly; it states plainly that the original request is preserved and never re-asked for across a clarification wait. + - Verification notes (commands or checks): read generated `sce-change-to-plan/SKILL.md` steps 3 and 4; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-change-to-plan.pkl` + - Evidence: Ephemeral generation produced the updated `sce-change-to-plan/SKILL.md` for Pi, Claude, and OpenCode; the generated steps 3 and 4 contain both named continuation shapes and the no-reask rule. `nix run .#pkl-check-generated` passed (110 files). + - Notes: The continuation shapes are explicit Markdown fields in the generated instructions, not runtime Pkl types, as assumed by the plan. + +- [x] T05: `Make validation observational` (status:done) + - Task ID: T05 + - Goal: Remove the scaffolding-deletion step and the `Scaffolding removed` report field from `workflow-validate.pkl`'s successful-path content; require leftover debug flags/temp artifacts to be recorded as failure evidence instead; confirm the existing "validation never repairs application/test/config code" language stays intact and unambiguous. + - Boundaries (in/out of scope): In — the validation phase reference content placed in `references/validation.md` by T01, the plan-file Validation Report layout, the validation result contract. Out — restructuring the reference tree itself (already done by T01); this task edits content, not file layout. + - Dependencies: T01 + - Done when: no generated `sce-validate` document instructs deleting/removing scaffolding, debug flags, or temporary artifacts; no successful-validation report field is titled `Scaffolding removed`; a failing check for leftover debug/temp artifacts is recorded as validation failure evidence, not repaired. + - Verification notes (commands or checks): `grep -i scaffold /sce-validate/references/*.md` for the new wording; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-validate.pkl`, `config/pkl/base/workflow-context-sync.pkl` + - Evidence: Removed validation's temporary-scaffolding deletion step and successful-path `Scaffolding removed` fields; leftover debug flags, temporary artifacts, and local scaffolding are recorded under failed checks without deletion or repair; removed the stale scaffolding field from the validation handoff consumed by plan context synchronization. Existing validation boundaries continue to prohibit modifying tests, application code, or configuration to make checks pass. + - Verification performed: `nix run .#pkl-check-generated` passed; ephemeral generation produced 110 files for all targets, with four `sce-validate/references/` files per target and no scaffolding-removal instructions or successful-validation cleanup field. Generated validation references explicitly classify leftover debug/temp artifacts as failure evidence. + +- [x] T06: `Harden next-task execution handoff` (status:done) + - Task ID: T06 + - Goal: In `workflow-next-task.pkl`'s execution phase, make the required handoff fields explicit (resolved plan, task identity, changed files, implementation summary, verification evidence, done-check evidence, plan update, context impact), add a pre-edit Git baseline capture step, compute `files_changed` relative to that baseline (excluding unrelated pre-existing staged/unstaged changes), state deterministic behavior for a stale/invalid/contradictory handoff (including under auto-approval), and explicitly state whether the mandatory five-root-file context pass stays mandatory for every task. + - Boundaries (in/out of scope): In — the execution phase reference and its handoff contract, the task-context-sync phase's consumption of `files_changed`. Out — creating a new standalone reference file for the contract (keep it with the execution phase, per the change request); the plan template (already extended by T03). + - Dependencies: T02, T03 + - Done when: the generated execution reference states the pre-edit baseline step, computes `files_changed` from that baseline, states the auto-approval stale-handoff rule, and explicitly states the five-root-file pass's mandatory status with a one-sentence rationale; no new standalone `references/execution-*.md` file is introduced. + - Verification notes (commands or checks): read the generated execution reference for the new steps; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-next-task.pkl`, `config/pkl/base/workflow-context-sync.pkl` + - Evidence: Made the complete execution handoff fields explicit; added pre-edit Git baseline capture and post-edit baseline-relative `files_changed` attribution that excludes unchanged unrelated work; defined deterministic stale, invalid, and contradictory handoff blocking under auto-approval; and stated that the mandatory five-root-file context pass remains required for every task. Task context sync now consumes the authoritative baseline-relative file list. + - Verification performed: `nix run .#pkl-check-generated` passed; ephemeral generation produced 110 files and the generated `sce-next-task/references/task-execution.md` contains the required handoff fields, baseline capture, attribution rule, auto-approval stale-handoff behavior, and mandatory root-pass rationale. + +- [x] T07: `Harden handover` (status:done) + - Task ID: T07 + - Goal: In `workflow-handover.pkl`, read both `git diff` and `git diff --cached` in writer mode; validate loaded handover sections for real content (not just heading presence) in loader mode and reject placeholder-only handovers; if it reduces control-plane size, extract the persisted handover template into `references/handover-template.md`, keeping `SKILL.md` focused on workflow/control flow, with writer success staying concise (path + continuation instruction) and loader success still exposing full loaded content. + - Boundaries (in/out of scope): In — `workflow-handover.pkl`'s writer/loader steps and persisted-format body. Out — any other workflow package. + - Dependencies: T06 + - Done when: generated `sce-handover/SKILL.md` writer-mode step reads both `git diff` and `git diff --cached`; loader-mode step rejects a section present only as an empty/placeholder heading; if extracted, `references/handover-template.md` exists per target and `SKILL.md` reads it before composing; writer success output stays concise; loader success output still surfaces full loaded content. + - Verification notes (commands or checks): read generated `sce-handover/SKILL.md` (and `references/handover-template.md` if extracted); `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-handover.pkl`, `config/pkl/renderers/metadata-coverage-check.pkl`, `config/pkl/renderers/generation-contract-check.pkl` + - Evidence: Writer mode now inspects both unstaged and staged diffs; loader mode validates each required section's substantive content and rejects empty or unreplaced placeholder sections; the persisted template is a package-local reference, and writer success reports only the path and continuation command while loader success still exposes the full loaded content. + - Verification performed: `nix run .#pkl-generate -- "$(mktemp -d)"` passed for Pi, Claude, and OpenCode with `references/handover-template.md` and `references/output.md`; generated references were target-identical and the generated skill contained the staged-diff, template-read, and placeholder-validation rules. `nix run .#pkl-check-generated` passed (113 files). + +- [x] T08: `Consolidate commit references` (status:done) + - Task ID: T08 + - Goal: In `workflow-commit.pkl`, stop generating `references/commit-contract.yaml` and `references/commit-message-style.md` (and do not introduce `references/commit-contract.md`); merge their procedure, subject/body/issue-reference/plan-citation rules, anti-patterns, result variants, and required/optional result fields into `references/atomic-commit.md`; simplify the internal result contract, dropping fields such as `scope_classification`, `notes`, and `cites_plan` unless the composite commit workflow actually consumes them. + - Boundaries (in/out of scope): In — `workflow-commit.pkl`'s reference listing and `atomic-commit.md` content. Out — bypass-mode execution mechanics (T09). + - Dependencies: T07 + - Done when: generated `sce-commit/references/` contains exactly `atomic-commit.md` and `output.md` per target; `atomic-commit.md` contains both commit-message rules and the atomic-commit result contract; no generated file is named `commit-contract.yaml`, `commit-message-style.md`, or `commit-contract.md`. + - Verification notes (commands or checks): directory listing of generated `sce-commit/references/`; grep `atomic-commit.md` for message-style and result-contract content; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-commit.pkl`, `config/pkl/renderers/metadata-coverage-check.pkl`, `config/pkl/renderers/generation-contract-check.pkl` + - Evidence: Consolidated the commit-message rules and atomic-commit result contract into `references/atomic-commit.md`, removed the two obsolete reference documents from package and composite inventories, and removed result fields not consumed by the composite workflow. The generated package now retains only `atomic-commit.md` and `output.md`. + - Verification performed: `nix run .#pkl-generate -- "$(mktemp -d)"` passed; generated Pi, Claude, and OpenCode `sce-commit/references/` directories each contain exactly `atomic-commit.md` and `output.md`; `atomic-commit.md` contains the message rules and result-contract sections; forbidden filenames are absent. `nix run .#pkl-check-generated` passed (107 files). + +- [x] T09: `Make bypass commit execution deterministic` (status:done) + - Task ID: T09 + - Goal: In the bypass-mode instructions now living in `atomic-commit.md`, replace multiline-message shell interpolation with a message-file/stdin mechanism (`git commit -F ` or equivalent), run `git commit` exactly once, retrieve the resulting hash explicitly from `HEAD` after success (never parsed from human-readable Git output), and state that failure never retries, amends, stages more files, or fabricates a hash; confirm `oneshot` and `skip` stay behaviorally identical. + - Boundaries (in/out of scope): In — `atomic-commit.md`'s bypass-mode section. Out — regular (proposal-only) commit mode. + - Dependencies: T08 + - Done when: generated `atomic-commit.md` bypass instructions use a message-file/stdin mechanism, state exactly one `git commit` invocation, state explicit post-success `HEAD` hash retrieval, and state the no-retry/no-amend/no-stage-more/no-fabricated-hash failure rule; `oneshot` and `skip` remain described identically apart from the trigger token. + - Verification notes (commands or checks): read generated `atomic-commit.md` bypass section; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-commit.pkl` + - Evidence: Added a package-local bypass execution handoff requiring verbatim message-file transport, exactly one `git commit -F `, explicit post-success `git rev-parse --verify HEAD^{commit}` retrieval, and no retry/amend/additional staging/fabricated-hash behavior; `oneshot` and `skip` remain identical apart from the trigger token. + - Verification performed: `nix run .#pkl-generate -- "$(mktemp -d)"` passed; generated Pi, Claude, and OpenCode atomic-commit references contain the deterministic bypass handoff and generated workflow commands follow it. `nix run .#pkl-check-generated` passed (107 files). + +- [x] T10: `Clean up decision semantics` (status:done) + - Task ID: T10 + - Goal: In `decision-skill.pkl`, make a nonqualifying invocation return `not_qualified`/`skipped` (never `blocked`) with synchronization continuing normally afterward; state existing ADRs stay immutable regardless of status; state a changed decision always creates a new dated ADR; state only an equivalent *active* ADR may be reused (never a rejected/deprecated one); review whether `Deprecated`/`Superseded` should be creation-time-only statuses and simplify without losing needed information. + - Boundaries (in/out of scope): In — `decision-skill.pkl` and any status-vocabulary reference it owns. Out — the synchronization phases that invoke it (`workflow-context-sync.pkl`) beyond confirming they already treat a nonqualifying/skip result as non-blocking. + - Dependencies: T09 + - Done when: generated `sce-decision/SKILL.md` states the nonqualifying result is `not_qualified`/`skipped` and non-blocking; states ADR immutability and active-only reuse explicitly; the status vocabulary is coherent (either both `Deprecated`/`Superseded` kept as creation-time-only, or intentionally simplified, stated plainly either way). + - Verification notes (commands or checks): read generated `sce-decision/SKILL.md`; confirm `sce-next-task`/`sce-validate` context-sync steps that consume the decision-gate result do not describe a nonqualifying result as blocking; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/decision-skill.pkl` + - Evidence: Nonqualifying and deliberately skipped decision gates now return non-blocking `not_qualified`/`skipped` states; genuine missing, contradictory, or unsafe decision input remains `blocked`. Existing ADRs are immutable regardless of status, only equivalent `Proposed` or `Accepted` ADRs are reusable, changed decisions always create new dated ADRs, and `Deprecated`/`Superseded` remain distinct creation-time-only statuses. + - Verification performed: Generated `sce-decision/SKILL.md` for Pi, Claude, and OpenCode was inspected; generated next-task and validate context-sync references continue to block only on a blocked decision handoff; `nix run .#pkl-check-generated` passed (107 files). + +- [x] T11: `Remove misleading generic composite boilerplate` (status:done) + - Task ID: T11 + - Goal: In `config/pkl/renderers/workflow-composite.pkl` (the shared `renderSkill` preamble) and any generated command boilerplate that copies its overclaiming, replace wording implying every workflow supports clarification, validation repair, and bootstrap waits with wording equivalent to "Any workflow-defined user wait resumes this same skill in the same session," while workflow-specific wait/resume semantics stay stated in the workflow that actually owns them. + - Boundaries (in/out of scope): In — the shared preamble text in `workflow-composite.pkl` and matching command-boilerplate text. Out — rewriting each workflow's own wait semantics; this task only removes the generic overclaim. + - Dependencies: T10 + - Done when: no generated workflow `SKILL.md` preamble claims universal support for clarification, validation repair, or bootstrap waits; every generated `SKILL.md` preamble instead carries the generic "any workflow-defined user wait resumes this same skill in the same session" wording (or an equivalent); each workflow's own wait semantics remain stated where they already were. + - Verification notes (commands or checks): grep every generated workflow `SKILL.md` for the old and new wording; `nix run .#pkl-check-generated`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/base/workflow-change-to-plan.pkl`, `config/pkl/base/workflow-commit.pkl`, `config/pkl/base/workflow-content.pkl`, `config/pkl/renderers/workflow-composite.pkl` + - Evidence: Replaced the misleading universal clarification, validation-repair, and bootstrap wait list with generic workflow-defined same-session wait wording in the shared composite preamble and matching canonical composite workflow bodies. Workflow-specific wait semantics remain unchanged. + - Verification performed: Ephemeral generation for Pi, Claude, and OpenCode confirmed every generated workflow `SKILL.md` has zero instances of the stale universal wording and at least one instance of the new generic wording. `nix run .#pkl-check-generated` passed (107 files). + +- [x] T12: `Add semantic generation checks` (status:done) + - Task ID: T12 + - Goal: Extend `config/pkl/renderers/generation-contract-check.pkl` with the 9 semantic checks from the change request: (1) every `Render the **X** layout from references/foo.md` instruction has a matching `## X` heading in that file; (2) every package-local referenced path exists; (3) validate never generates `references/sync-report.md` or `references/validation-result.md`; (4) commit never generates `references/commit-contract.yaml`, `references/commit-contract.md`, or `references/commit-message-style.md`; (5) `atomic-commit.md` contains both commit-message rules and the atomic-commit result contract; (6) if next-task emits `sync-report.md`, its `output.md` does not also contain the context-sync report contract; (7) Pi/Claude/OpenCode target-neutral references are identical unless explicitly target-specific; (8) no generated file contains "Nothing records the skipped synchronization, so it is lost once this session ends" (stale after T03); (9) validation contains no instruction to repair implementation during final validation. Add a checked-in negative fixture proving each check fails when violated. + - Boundaries (in/out of scope): In — `generation-contract-check.pkl` and its negative-fixture set. Out — re-verifying inventory/path-presence checks that already exist; this task adds semantic checks alongside them. + - Dependencies: T11 + - Done when: `generation-contract-check.pkl` asserts all 9 checks; a corresponding negative fixture exists per check and demonstrably fails evaluation with an actionable diagnostic; `nix run .#pkl-check-generated` passes against the now-compliant generated output. + - Verification notes (commands or checks): `nix run .#pkl-check-generated`; evaluate each negative fixture directly (`nix develop -c pkl eval `) and confirm it fails. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/check-generated.sh`, `scripts/test-check-generated.sh`, `config/pkl/renderers/fixtures/` + - Evidence: Added nine semantic generation-contract assertions with actionable diagnostics for layout-heading resolution, package-local path existence, forbidden validate/commit artifacts, atomic-commit dual content, next-task report ownership, cross-target reference parity, stale synchronization wording, and observational validation. Added one checked-in negative fixture per assertion and registered all fixtures in the generated-output check harness. + - Verification performed: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` passed with all contract checks; `nix run .#pkl-check-generated` passed with ephemeral generation producing 107 files. Each new fixture was evaluated and failed with its intended diagnostic. + - Task ID: T12 + - Goal: Extend `config/pkl/renderers/generation-contract-check.pkl` with the 9 semantic checks from the change request: (1) every `Render the **X** layout from references/foo.md` instruction has a matching `## X` heading in that file; (2) every package-local referenced path exists; (3) validate never generates `references/sync-report.md` or `references/validation-result.md`; (4) commit never generates `references/commit-contract.yaml`, `references/commit-contract.md`, or `references/commit-message-style.md`; (5) `atomic-commit.md` contains both commit-message rules and the atomic-commit result contract; (6) if next-task emits `sync-report.md`, its `output.md` does not also contain the context-sync report contract; (7) Pi/Claude/OpenCode target-neutral references are identical unless explicitly target-specific; (8) no generated file contains "Nothing records the skipped synchronization, so it is lost once this session ends" (stale after T03); (9) validation contains no instruction to repair implementation during final validation. Add a checked-in negative fixture proving each check fails when violated. + - Boundaries (in/out of scope): In — `generation-contract-check.pkl` and its negative-fixture set. Out — re-verifying inventory/path-presence checks that already exist; this task adds semantic checks alongside them. + - Dependencies: T11 + - Done when: `generation-contract-check.pkl` asserts all 9 checks; a corresponding negative fixture exists per check and demonstrably fails evaluation with an actionable diagnostic; `nix run .#pkl-check-generated` passes against the now-compliant generated output. + - Verification notes (commands or checks): `nix run .#pkl-check-generated`; evaluate each negative fixture directly (`nix develop -c pkl eval `) and confirm it fails. + +- [x] T13: `Assert generated OpenCode permission integrity` (status:done) + - Task ID: T13 + - Goal: Preserve the current catalog-derived, unconditional OpenCode Code-agent `skill:` permission block exactly as-is, including the inert `sce-brownfield` permission for the optional workflow. Add a generation-time assertion in `generation-contract-check.pkl` that every explicitly allowed `sce-*` permission names a workflow artifact the generator is capable of emitting for that target. Do not consult or require installation status, and do not condition generation, composition, routing, permissions, or artifact paths on `optional`. + - Boundaries (in/out of scope): In — a new integrity assertion in `generation-contract-check.pkl` covering `opencode-metadata.pkl`'s permission output. Out — any change to `opencode-metadata.pkl`'s permission-derivation logic itself, `workflow-catalog.pkl`'s `optional` flag, install-time selection logic, or `cli/**`. Also updates the literal `expectedArtifactPathCount` in `generation-contract-check.pkl` if this repository's earlier tasks changed the total generated artifact count (net -9 from T01/T08). + - Dependencies: T12 + - Done when: `opencode-metadata.pkl`'s permission derivation is unchanged (still catalog-derived, unconditional, `sce-brownfield` still present); `generation-contract-check.pkl` asserts every explicitly allowed `sce-*` permission corresponds to an artifact the generator emits for that target; a deliberately-misspelled or dangling-to-a-nonexistent-workflow permission fixture fails that assertion with an actionable diagnostic; no assertion consults an installed/selected-workflow set. + - Verification notes (commands or checks): read `opencode-metadata.pkl` (confirm unchanged) and the new assertion in `generation-contract-check.pkl`; `nix run .#pkl-check-generated`; `nix flake check`. + - Context synchronization: synced + - Completed: 2026-08-12 + - Files changed: `config/pkl/renderers/generation-contract-check.pkl`, `config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl`, `config/pkl/check-generated.sh` + - Evidence: Added a generation-time assertion that parses explicit `sce-*` allow permissions for both OpenCode agents and verifies each maps to an emitted `config/.opencode/skills/{slug}/SKILL.md` artifact. Preserved the catalog-derived unconditional permission renderer, including `sce-brownfield`, and added a dangling-permission negative fixture with the expected diagnostic. + - Verification performed: `nix develop -c pkl eval config/pkl/renderers/generation-contract-check.pkl` passed; direct evaluation of the negative fixture failed with `OpenCode skill permission names a missing generated workflow artifact`; `nix run .#pkl-check-generated` passed with 107 generated files; `nix flake check` passed. + +## Validation Report + +**Status:** validated +**Date:** 2026-08-12 + +### Commands run + +- `nix run .#pkl-check-generated` -> exit 0 (ephemeral generation and registered negative-fixture checks passed; 107 files) +- `nix flake check` -> exit 0 (T13 negative fixture now tracked in the repository source; `pkl-generated` derivation and all flake checks pass) +- `nix develop -c pkl eval config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl` -> exit 1 as intended, diagnostic: `OpenCode skill permission names a missing generated workflow artifact` + +### Scaffolding removed + +- None. + +### Success-criteria verification + +- [x] AC1: Generated forbidden files are absent from all three targets — inspected ephemeral output. +- [x] AC2: `sce-next-task` alone owns `references/sync-report.md` and its output has no embedded report — inspected ephemeral output. +- [x] AC3: Durable pending/synced/blocked lifecycle and synchronization-debt gates are present — inspected generated skills and plan template. +- [x] AC4: Clarification and existing-plan revision continuations use distinct named fields — inspected generated `sce-change-to-plan` skill. +- [x] AC5: Validation records leftover scaffolding as failure evidence and performs no repair — inspected generated validation references. +- [x] AC6: Execution handoff baseline and relative `files_changed` rules are present — inspected generated task-execution reference. +- [x] AC7: Handover reads staged and unstaged diffs and rejects placeholders — inspected generated skill. +- [x] AC8: Commit reference inventory and merged message/result contract are correct — inspected generated package. +- [x] AC9: Bypass commit uses one message-file commit and explicit `HEAD` hash retrieval — inspected generated atomic-commit reference. +- [x] AC10: Decision nonqualification is non-blocking and ADR reuse is active-only — inspected generated decision skill. +- [x] AC11: Composite workflow skills use the workflow-defined same-session wait wording — inspected all six generated workflow skills. +- [x] AC12: Semantic generation checks and negative fixtures pass — direct checker and all registered fixtures passed. +- [x] AC13: OpenCode permission derivation remains catalog-based and the artifact-integrity fixture fails as intended — `nix flake check` now includes the tracked T13 negative fixture and passes; direct fixture evaluation fails with the expected diagnostic. + +### Notes + +- Repair carried over from the prior failed run: the T13 negative fixture + (`config/pkl/renderers/fixtures/opencode-missing-skill-artifact-check.pkl`) + was untracked and thus absent from the Nix flake's source snapshot, + causing `nix flake check` to fail. It has been `git add`-ed (staged, not + committed) so the flake's source snapshot includes it; no fixture content, + test, or configuration was modified to force success. + +### Residual risks + +- None outstanding. The T13 fixture is staged but not yet committed — a + future commit for this plan's work must include it alongside the other + T13 files. + +## Open questions + +None. T13's only real ambiguity — whether "workflows actually installed" could be checked at generation time without reversing the accepted install-time-optionality architecture — was resolved via clarification: permissions stay catalog-derived and unconditional, and T13 narrows to a generation-time integrity assertion. The remaining tasks match the change request's own precise specification (exact target file trees, exact forbidden/required generated paths, exact wording equivalents, an exact 13-task commit split); direct inspection of `workflow-validate.pkl`, `workflow-next-task.pkl`, `workflow-handover.pkl`, `workflow-change-to-plan.pkl`, and `opencode-metadata.pkl` confirmed the premises behind T01, T02, T04, T05, T07, and T13 are real, current conditions rather than already-fixed or misdiagnosed ones. Each remaining task's own `Done when` names the concrete generated artifact it must produce, so a wrong premise in T03, T06, T08–T12 fails visibly at verification time rather than silently. diff --git a/context/sce/atomic-commit-workflow.md b/context/sce/atomic-commit-workflow.md index ae220ed6..93822c6b 100644 --- a/context/sce/atomic-commit-workflow.md +++ b/context/sce/atomic-commit-workflow.md @@ -10,9 +10,10 @@ project-root `.pi/` baseline and generated for OpenCode, Claude, and Pi. Every target emits one thin command (Pi: prompt) invoking `sce-commit`. The package contains `SKILL.md`, which owns mode routing, proposal/commit control flow, and internal statuses; `references/atomic-commit.md`, which owns staged-diff -analysis and message construction; and `references/output.md`, which owns all -human-visible prompts, result layouts, and commit-message style rules. The phase -reference is read only after the selected path clears its pre-phase gate. +analysis, result branching, and commit boundaries; `references/commit-message-style.md`, +which owns message wording; and `references/output.md`, which owns all human-visible +prompts and result layouts. The phase reference is read only after the selected +path clears its pre-phase gate. No target emits an `sce-atomic-commit` package or invokes it as a sibling skill; each `sce-commit` package embeds the canonical phase behavior directly. @@ -44,8 +45,9 @@ flowchart TD D --> D1{git diff --cached
non-empty?} D1 -- no --> D2([Stop: No staged changes.
Stage changes before commit.]) D1 -- yes --> D3[Phase: atomic commit, mode: bypass] - D3 --> D4[Exactly one git commit] - D4 --> D5([Report hash, or report failure
with no retry or amend]) + D3 --> D4[Write message file
then exactly one git commit -F] + D4 --> D5[Read hash from HEAD after success] + D5 --> D6([Report hash, or report failure
with no retry or amend]) ``` ## Regular mode @@ -67,10 +69,16 @@ user runs the commits they accept. Single-message, command-committed. The command first checks that staged content exists and stops with `No staged changes. Stage changes before commit.` when nothing is staged. It then requests exactly one message covering all staged -files and runs `git commit` once. +files, creates a temporary message file outside the repository working tree, +writes that message verbatim to it, and runs `git commit -F ` +exactly once. The multiline message is never interpolated into shell source or a +shell command. -On success it reports the commit hash. On failure it reports the failure and -stops — no retry, no amend, no fallback commit. +Only after the commit succeeds does the command retrieve the hash explicitly +from `git rev-parse --verify HEAD^{commit}`; it never parses Git's human-readable +output. On failure it reports the failure and stops — no retry, amend, additional +staging, fallback commit, or fabricated hash. Either way, the command deletes the +temp file after the commit attempt, including on failure, where practical. Bypass mode relaxes three regular-mode rules: no split proposals, no context-file guidance gating, and plan citations are best-effort rather than @@ -95,8 +103,11 @@ about staging. Staged changes are the only input describing what is being committed. Neither document reads unstaged or untracked changes, and neither stages, unstages, or -otherwise modifies files. Supplied commit context refines wording but never -overrides the diff and never adds a claim the diff does not support. +otherwise modifies repository or worktree files. The bypass commit-message temp +file is the sole exception to that mutation rule: it lives outside the working +tree, so writing and later deleting it is not a repository or worktree +modification. Supplied commit context refines wording but never overrides the +diff and never adds a claim the diff does not support. ## Plan citations @@ -110,20 +121,22 @@ staged explicitly; bypass mode omits the citation instead of stopping. ## Result contract -The canonical analysis phase reaches exactly one of three results: +The canonical analysis phase reaches exactly one of three internal results. The +message wording rules live in `references/commit-message-style.md`; the phase +reference describes the procedure and result branches, while the workflow keeps +the statuses internal rather than serializing them between packages. No +`commit-contract.yaml` artifact or YAML result-contract section is generated. -- `proposal` — regular mode, one or more messages, optional split rationale and - staged-scope classification. +- `proposal` — regular mode, one or more messages and an optional split rationale. - `bypass_message` — bypass mode, exactly one message plus the full staged file list. - `blocked` — messages cannot be written faithfully. Categories are `no_staged_changes`, `plan_citation_ambiguity`, `unreadable_diff`, and `contradictory_context`. -Every target keeps that status as internal `sce-commit` state and renders only -the applicable layout from `references/output.md`; no result is serialized -between packages. Every staged file still belongs to exactly one commit message. The analysis phase never reports a hash; -only successful bypass-mode `git commit` produces one. +Every target renders only the applicable layout from `references/output.md`. +Every staged file still belongs to exactly one commit message. The analysis phase +never reports a hash; only successful bypass-mode `git commit` produces one. ## Related context diff --git a/context/sce/context-workflow-rules.md b/context/sce/context-workflow-rules.md index 3430b168..f335db53 100644 --- a/context/sce/context-workflow-rules.md +++ b/context/sce/context-workflow-rules.md @@ -97,14 +97,14 @@ continues with the original request. ## Synchronization lifecycle -Ongoing context maintenance is owned by the two synchronization phases below. -`/brownfield` writes durable context outside this lifecycle, but only as a -cold-start and gap-fill reconstruction; it is not a drift-repair or maintenance -path and never substitutes for synchronization. +Ongoing context maintenance is owned by task context synchronization after +successful `/next-task` execution. `/brownfield` writes durable context outside +this lifecycle, but only as a cold-start and gap-fill reconstruction; it is not a +drift-repair or maintenance path and never substitutes for synchronization. -Synchronization is split by lifecycle boundary. Both phases share the canonical -rules in `config/pkl/base/workflow-context-sync.pkl`, but receive different -authoritative handoffs. +Task synchronization shares the canonical rules in +`config/pkl/base/workflow-context-sync.pkl` with the retained plan-sync source, +but `/validate` no longer invokes the plan-level synchronization role. ### Task synchronization @@ -113,22 +113,14 @@ authoritative handoffs. verified task with durable context. Declined, blocked, and incomplete executions do not enter synchronization. -### Plan synchronization - -`sce-plan-context-sync` runs from `/validate` only after `sce-validation` -returns `Status: validated`. It performs the final plan-level pass using the -plan's Context sync requirements and validation evidence. Failed or blocked -validation does not enter synchronization. - -A synchronization blocker does not undo the successful prior phase. The task or -validation evidence remains recorded, but the workflow stops because durable -context is out of date and must be reconciled before continuing or closing the -plan. +`/validate` performs final validation only. It writes the Validation Report and +returns `validated`, `failed`, or `blocked`; it does not invoke plan-level context +synchronization or persist a plan-sync lifecycle handoff. ### Architecture-decision gate -After impact discovery and before current-state context edits, both successful -synchronization roles test whether the change establishes or changes a system-wide +After impact discovery and before current-state context edits, the successful task +synchronization role tests whether the change establishes or changes a system-wide constraint involving boundaries or ownership, public or cross-domain interfaces, data or persistence, compatibility, security, deployment/distribution, a major dependency, or another durable and costly-to-reverse constraint. Routine local @@ -151,7 +143,7 @@ it. ### Mandatory synchronization pass -Every task and plan synchronization verifies these files against code truth, +Every task synchronization verifies these files against code truth, whether or not an edit is warranted: - `context/overview.md` @@ -177,10 +169,8 @@ preserve paths produced before the blocker. Task reports list the execution handoff's changed files outside `context/` under `Updated files`; they do not render impact classification or the root-pass checklist, although task synchronization still uses the classification and performs the mandatory -pass. Plan reports continue to render impact classification, plan context -requirements, and each root file as verified, edited, or absent. Task -synchronization does not run full-plan validation, and plan synchronization does -not rerun final validation. +pass. Task synchronization does not run full-plan validation or alter the +plan's validation evidence. ## Canonical sources diff --git a/context/sce/dedup-ownership-table.md b/context/sce/dedup-ownership-table.md index a9a25528..32a940d9 100644 --- a/context/sce/dedup-ownership-table.md +++ b/context/sce/dedup-ownership-table.md @@ -2,11 +2,11 @@ ## Scope and method -- Canonical workflow sources: `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit}.pkl`. +- Canonical workflow sources: `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit,handover,brownfield}.pkl`. - Canonical standalone decision-skill source: `config/pkl/base/decision-skill.pkl`. - Shared package model: `config/pkl/base/workflow-content.pkl`. - Shared synchronization source: `config/pkl/base/workflow-context-sync.pkl`. -- Generated consumers: `config/.opencode/**`, `config/.claude/**`, and `config/.pi/**`. +- Generated consumers: ephemeral `config/.opencode/**`, `config/.claude/**`, and `config/.pi/**` payloads beneath generation roots. - Assignment rule: each workflow phase has one canonical module owner; commands and agents only orchestrate or route. - Canonical phase modules are authoring inputs to `config/pkl/renderers/workflow-composite.pkl`. No target generates them as packages; each is composed into the workflow skill named in the consumer column. @@ -20,16 +20,16 @@ | Approval-gated one-task implementation | `sce-task-execution` in `workflow-next-task.pkl` | `/next-task`; composed into `sce-next-task`; thin OpenCode Code agent | intentional/keep | | Post-task durable context synchronization | Task instance from `workflow-context-sync.pkl` | `/next-task`; composed into `sce-next-task` | dedup/shared skeleton | | Final validation and validation report | `sce-validation` in `workflow-validate.pkl` | `/validate`; composed into `sce-validate`; thin OpenCode Code agent | intentional/keep | -| Validated-plan durable context synchronization | Plan instance from `workflow-context-sync.pkl` | `/validate`; composed into `sce-validate` | dedup/shared skeleton | +| Validated-plan durable context synchronization | Retained plan instance from `workflow-context-sync.pkl` | No current workflow consumer; `/validate` is validation-only | retained source, not generated | | Staged-diff analysis and commit-message authoring | `sce-atomic-commit` in `workflow-commit.pkl` | `/commit`; composed into `sce-commit`; thin OpenCode Code agent | intentional/keep | -| Workflow routing | Four command documents in the workflow modules | Thin OpenCode Plan/Code agents | intentional/keep | -| Standalone ADR writing contract | `decision-skill.pkl` | Cross-target `sce-decision` packages; successful task/plan synchronization invokes it through the shared decision gate | intentional/keep | +| Workflow routing | Six command documents in the workflow modules | Thin OpenCode Plan/Code agents | intentional/keep | +| Standalone ADR writing contract | `decision-skill.pkl` | Cross-target `sce-decision` package; successful task synchronization invokes it through the shared decision gate | intentional/keep | ## Guardrails - Keep Plan and Code routing roles separate without placing workflow doctrine in agent bodies. - Keep commands thin: each routes to exactly one workflow skill and owns no phase behavior. -- Keep task and plan synchronization policy in the one shared Pkl skeleton even though each workflow skill composes its own instance. -- Keep SCE workflow control flow inside the owning workflow skill. Relevant non-SCE skills may assist as in-step helpers that return control to the active step; `sce-decision` remains the sole SCE sibling-skill exception, usable only from successful task or plan synchronization's decision gate, once per qualifying decision. -- Do not reintroduce removed `/handover`, legacy context-sync, or automated-profile Markdown ownership. -- Do not reintroduce phase skills as a generated surface. Workflow behavior belongs in the canonical modules and installation belongs to the four command-routed workflow packages (see [Atomic commit workflow](atomic-commit-workflow.md) for `/commit`). The standalone `sce-decision` package is a separate internal surface, not a generated phase package or user-facing workflow. +- Keep task and retained plan synchronization policy in the one shared Pkl skeleton; only task synchronization is composed into a current workflow. +- Keep SCE workflow control flow inside the owning workflow skill. Relevant non-SCE skills may assist as in-step helpers that return control to the active step; `sce-decision` remains the sole SCE sibling-skill exception, usable only from successful task synchronization's decision gate, once per qualifying decision. +- Do not reintroduce the removed `/validate` plan-context-sync handoff, legacy context-sync, or automated-profile Markdown ownership. +- Do not reintroduce phase skills as a generated surface. Workflow behavior belongs in the canonical modules and installation belongs to the six command-routed workflow packages (see [Atomic commit workflow](atomic-commit-workflow.md) for `/commit`). The standalone `sce-decision` package is a separate internal surface, not a generated phase package or user-facing workflow. diff --git a/context/sce/handover-workflow.md b/context/sce/handover-workflow.md index 4065bb63..dfda906c 100644 --- a/context/sce/handover-workflow.md +++ b/context/sce/handover-workflow.md @@ -24,11 +24,12 @@ Purpose / User-visible-output / Composite-control-flow preamble from `workflow-composite.pkl`, distinct from the skill's own package-mode-only Purpose text. -The package contains only `SKILL.md`, which owns mode routing, writer and -loader behavior, and internal statuses, plus `references/output.md`, which -owns every human-visible layout. No SCE sibling skill, package, or workflow -command is invoked as a workflow handoff; relevant non-SCE helpers may assist -inside the active step and return control to it. +The package contains `SKILL.md`, which owns mode routing, writer and loader +behavior, `references/handover-template.md`, which owns the persisted document +format, and `references/output.md`, which owns every human-visible layout. No +SCE sibling skill, package, or workflow command is invoked as a workflow +handoff; relevant non-SCE helpers may assist inside the active step and return +control to it. ## Modes @@ -54,15 +55,15 @@ flowchart TD D1 -- no --> D2([Loader blocked — stop]) D1 -- yes --> D3{All four sections present?} D3 -- no --> D2 - D3 -- yes --> D4([Present read-only for continuation]) + D3 -- yes, substantive --> D4([Present read-only for continuation]) ``` ## Writer mode Gathers task-relevant facts from the current conversation and grounds them -against repository state (`git status`/`git diff`, the active -`context/plans/*.md` task, recent commits). Any detail not directly evidenced -is labeled as an assumption rather than presented as fact. +against repository state (`git status`, `git diff`, and `git diff --cached`, the +active `context/plans/*.md` task, and recent commits). Any detail not directly +evidenced is labeled as an assumption rather than presented as fact. The written file name is `context/handovers/{plan_name}-{task_id}.md` when exactly one plan task is unambiguously active, otherwise the collision-safe @@ -71,8 +72,12 @@ Writer mode never overwrites an existing handover file. The persisted document always has four required sections, in order: `Current Task State`, `Decisions Made`, `Open Questions / Blockers`, and -`Next Recommended Step`, plus a trailing `Assumptions` section. A section with -nothing to report still appears, stating `None identified.` or an equivalent. +`Next Recommended Step`, plus a trailing `Assumptions` section. Writer mode +uses the package-local template and keeps every required section substantive, +while allowing explicit `None identified.` statements. Loader mode rejects +missing, empty, or unreplaced-placeholder-only required sections. Writer +success reports only the path and continuation command; loader success still +surfaces the full loaded content. ## Loader mode diff --git a/context/sce/plan-code-overlap-map.md b/context/sce/plan-code-overlap-map.md index 6512fb47..46f56494 100644 --- a/context/sce/plan-code-overlap-map.md +++ b/context/sce/plan-code-overlap-map.md @@ -2,22 +2,24 @@ ## Scope reviewed -- Canonical packages: `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit}.pkl` +- Canonical packages: `config/pkl/base/workflow-{change-to-plan,next-task,validate,commit,handover,brownfield}.pkl` - Shared synchronization skeleton: `config/pkl/base/workflow-context-sync.pkl` -- Generated OpenCode routing agents and workflow commands under `config/.opencode/` -- Eight canonical phase modules, composed into four workflow skills for every target and rendered as packages for none +- Generated OpenCode routing agents and workflow commands in ephemeral target payloads +- Eight canonical phase modules, composed into six workflow skills for every target; phase modules are not rendered as packages ## Overlap matrix | Surface | Shared reusable content | Surface-specific content | Ownership rule | | --- | --- | --- | --- | | Shared Context Plan agent | References the planning workflow | Routes Plan work to `/change-to-plan` | Keep routing-only; command and skills own behavior | -| Shared Context Code agent | References implementation lifecycle | Routes Code work to `/next-task`, `/validate`, and `/commit` | Keep routing-only; command and skills own behavior | +| Shared Context Code agent | References implementation lifecycle | Routes Code work to `/next-task`, `/validate`, `/commit`, `/handover`, and `/brownfield` | Keep routing-only; command and skills own behavior | | `/change-to-plan` | Thin routing to one workflow skill | Routes to `sce-change-to-plan` | The skill runs the context-load and plan-authoring phases internally | | `/next-task` | Thin routing to one workflow skill | Routes to `sce-next-task` | The skill runs review, one-task execution, task sync, and continuation internally | -| `/validate` | Thin routing to one workflow skill | Routes to `sce-validate` | The skill runs final validation then validated-only plan sync internally | +| `/validate` | Thin routing to one workflow skill | Routes to `sce-validate` | The skill runs final validation, writes the Validation Report, and reports its status | | `/commit` | Thin routing to one workflow skill | Routes to `sce-commit` | The skill runs mode routing, staged-diff analysis, and message authoring internally | -| Task/plan context sync | Root-pass, context hygiene, and synchronization policy | Distinct entry gates and report lifecycle | One parameterized Pkl skeleton composes into `sce-next-task` and `sce-validate` | +| `/handover` | Thin routing to one workflow skill | Routes to `sce-handover` | The phase-free skill owns writer/loader modes and handover reporting internally | +| `/brownfield` | Thin routing to one workflow skill | Routes to `sce-brownfield` | The phase-free skill owns local investigation, clarification, context writes, and reporting internally | +| Task context sync | Root-pass, context hygiene, and synchronization policy | Task execution handoff, lifecycle, and report ownership | One parameterized Pkl skeleton composes into `sce-next-task`; the retained plan-sync source is not invoked by `/validate` | ## Current dedup boundary @@ -25,5 +27,6 @@ - Workflow skills own phase ordering, status branching, gates, edits, verification, and result/report formats. - Thin OpenCode agents own only role-to-command routing and target permissions; their ordered skill permissions allow ordinary non-SCE helpers, deny arbitrary `sce-*` skills, and allow only catalog-derived owned workflow slugs, with the Code-only synchronization exception for `sce-decision`. - Claude and Pi do not receive generated agents. -- Each workflow package carries exactly one reference file, `references/output.md`, so it remains self-contained without cross-package dependencies. +- Each workflow package owns its package-local phase or persisted-document references and exactly one `references/output.md`, so it remains self-contained without cross-package dependencies. +- `/validate` does not compose a plan-context-sync phase; the retained plan-sync source has no generated package owner or user-facing route. - The removed grouped shared-content catalog and automated OpenCode profile have no current owner or consumer. diff --git a/context/sce/shared-context-code-workflow.md b/context/sce/shared-context-code-workflow.md index 1e27ff71..cbf24338 100644 --- a/context/sce/shared-context-code-workflow.md +++ b/context/sce/shared-context-code-workflow.md @@ -2,7 +2,7 @@ ## Purpose -The implementation lifecycle executes at most one reviewed task per `/next-task` invocation, synchronizes durable context only after successful task execution, and runs final plan validation separately through `/validate`. The generated OpenCode Code agent only routes to these commands. Every target keeps each complete lifecycle in `sce-next-task` or `sce-validate`; each `SKILL.md` owns control flow and reads a package-local reference before running the applicable phase. The phases below are internal to those skills, not separate generated packages. +The implementation lifecycle executes at most one reviewed task per `/next-task` invocation, synchronizes durable context only after successful task execution, and runs final plan validation separately through `/validate`. Task-level context synchronization lifecycle state is persisted in each plan as `pending`, `synced`, or `blocked` with blocker, required-action, and retry-condition details for blocked transitions. A completed task records its own execution facts directly — `Completed`, `Files changed`, `Result`, `Verify` outcomes, `Context impact`, and `Context synchronization` — so that same completed task record, identified only by plan path and task ID, is the durable input a later session reads to retry or repair synchronization for that task, with no separate persisted handoff and without reconstructing it from conversation history. `/validate` is validation-only: it writes the Validation Report and returns its validation status without invoking plan-level context synchronization. The generated OpenCode Code agent only routes to these commands. Every target keeps the complete task lifecycle in `sce-next-task` and the validation lifecycle in `sce-validate`; each `SKILL.md` owns control flow and reads a package-local reference before running the applicable phase. The phases below are internal to those skills, not separate generated packages. ## `/next-task` entrypoint @@ -19,28 +19,44 @@ Phase names below identify canonical modules in `config/pkl/base/workflow-next-t 1. `sce-plan-review` - Resolves exactly one plan and at most one task. - - Selects the requested task or the first incomplete task whose declared dependencies are complete. - - Returns `ready`, `blocked`, or `plan_complete`. + - Read-only debt detector: inspects every completed task's context lifecycle in plan order, regardless of its position relative to the task being selected or resumed, before allowing any new task to start. A missing field, or any value other than `synced`, is unresolved debt. It never retries or invokes task context synchronization itself. + - When the first debt-carrying task has no durable completed-task record (no `Files changed`, `Result`, `Verify`, or `Context impact` recorded — a legacy plan predating that structure, or an incomplete write), it returns `blocked` directly with migration guidance rather than a reconstructed retry. + - Otherwise it returns `sync_debt`, naming the debt task's ID and title and its own completed record — read directly from the plan by plan path and task ID — and, when its lifecycle is `blocked`, its persisted "Context synchronization blocker". + - Once every completed task is `synced`, selects the requested task or the first incomplete task whose declared dependencies are complete. + - Returns `ready`, `sync_debt`, `blocked`, or `plan_complete`. + - Sync-debt recovery: an explicit top-level `/next-task` branch — not plan-review behavior — reached on `sync_debt` before normal task selection resumes. It reads `references/context-sync.md`, then runs task context synchronization using the debt task's completed record read directly from the plan by plan path and task ID (and persisted blocker, when present). On `synced`/`no_context_change` it writes `synced` to the plan, clearing blocker fields, and re-invokes plan review to resume normal task selection. On a renewed `blocked` it writes the refreshed blocker, required action, and retry condition to the plan and stops, rendering the **Context synchronization blocked** layout — distinct from plan review's own **Review blocked** layout. 2. `sce-task-execution` - Receives the complete `ready` result. - Always presents the implementation gate before editing. - Waits for confirmation unless the user supplied `approved` to the command. + - Captures a pre-edit Git baseline, then reports `changes.files_changed` by comparing + post-edit state with that baseline, excluding unchanged unrelated staged, unstaged, + and untracked work. + - Returns an explicit complete handoff containing the resolved plan, task identity, + changed files, implementation summary, verification evidence, done-check evidence, + plan update, and context impact; stale, invalid, or contradictory handoffs block + deterministically even under auto-approval. - Implements and verifies exactly one task, then records status and evidence in the plan. - Returns `declined`, `blocked`, `incomplete`, or `complete`. 3. `sce-task-context-sync` - Runs only from the complete successful execution handoff. + - Consumes the handoff's baseline-relative `changes.files_changed` list as authoritative; + it does not replace it with a whole-working-tree scan or a fresh diff against `HEAD`. - Reconciles one task with durable context and performs the mandatory root-file pass. - Applies the system-wide decision gate before current-state context edits. Routine, local, temporary, and easily reversible choices skip decision writing; each qualifying decision reuses an existing ADR or invokes `sce-decision` once. - - A blocked decision handoff blocks synchronization; written or reused ADR paths - become synchronization evidence and are available for current-state links. + - A `not_qualified` or `skipped` decision handoff is non-blocking and synchronization + continues normally; a `blocked` decision handoff blocks synchronization. Written + or reused ADR paths become synchronization evidence and are available for + current-state links. - Returns a Markdown report with `synced`, `no_context_change`, or `blocked`. - Every report variant lists changed files outside `context/` under `Updated files`; task reports omit the impact classification and rendered root-pass checklist without changing synchronization behavior. 4. Command continuation - Emits exactly one next-task command for the first unchecked task in plan order, or a `/validate` command when all implementation tasks are complete. + - A completed task's lifecycle is persisted as `pending` before task synchronization and as `synced` or `blocked` afterward; `/next-task` refuses new implementation while completed-task lifecycle debt remains. - Never executes the continuation in the same invocation. A context-sync blocker does not undo successful implementation: the task remains complete in the plan, but the workflow stops because durable context is stale. On every target, review, approval, execution, evidence recording, synchronization, and continuation are internal phases of one `sce-next-task` invocation. Relevant non-SCE skills may assist inside an active step only as helpers that return control to that step; the sole SCE sibling-skill exception is the synchronization decision gate's bounded invocation of `sce-decision`. @@ -49,11 +65,11 @@ A context-sync blocker does not undo successful implementation: the task remains `/validate {plan-name-or-path}` -1. `sce-validation` verifies that implementation tasks are complete, runs the plan's full validation commands and acceptance checks, cleans temporary scaffolding, and writes the Validation Report. -2. Failed or blocked validation ends the session without repair edits; retry uses `/validate {plan-path}`. -3. `sce-plan-context-sync` runs only from a successful `Status: validated` handoff, applies the same decision gate before current-state edits, and reconciles the completed plan with durable repository context. ADR paths already written during task synchronization are reused for the same decision. +1. `sce-validation` verifies that implementation tasks are complete, runs the plan's full validation commands and acceptance checks, records leftover debug/temp artifacts as failure evidence without deleting or repairing them, and writes the Validation Report. +2. Failed or blocked validation ends the session without repair edits; repair occurs in a later implementation session and retry uses `/validate {plan-path}`. +3. A validated result is reported directly with the Validation Report path; `/validate` does not invoke plan-level context synchronization or persist a plan-sync lifecycle handoff. -On every target, `sce-validate/SKILL.md` dispatches workflow steps 1 and 2 through `references/validation.md` and `references/context-sync.md`, while `references/validation-report.md` owns the plan-file Validation Report format. Failed and blocked statuses stop before synchronization exactly as in the canonical flow. Final validation never runs from an individual implementation task. Non-SCE helper skills, when relevant, return control to the active validation or synchronization step without changing its workflow invariants. +On every target, `sce-validate/SKILL.md` dispatches its validation phase through `references/validation.md` and keeps `references/validation-report.md` as the plan-file Validation Report format. Failed, blocked, and validated statuses remain validation-owned terminal outcomes. Final validation never runs from an individual implementation task. Non-SCE helper skills, when relevant, return control to the active validation step without changing its workflow invariants. ## Flow @@ -74,17 +90,21 @@ flowchart TD I -- "No" --> K["Emit /validate command"] K --> L["Phase: validation"] L --> M{"validated?"} - M -- "Yes" --> N["Phase: plan context sync"] + M -- "Yes" --> N["Report validation and Validation Report path"] M -- "No" --> O["Stop and retry /validate later"] ``` ## Target ownership - OpenCode, Claude, and Pi: thin commands (Pi: prompts) invoking `sce-next-task` or `sce-validate`. -- `sce-next-task` packages contain `SKILL.md`, `references/{plan-review,task-execution,context-sync,output}.md`. -- `sce-validate` packages contain `SKILL.md`, `references/{validation,context-sync,validation-report,output}.md`. +- `sce-next-task` packages contain `SKILL.md`, `references/{plan-review,task-execution,context-sync,sync-report,output}.md`. +- `sce-validate` packages contain `SKILL.md`, `references/{validation,validation-report,output}.md`. `validation.md` carries the validation steps plus the validation result contract; `output.md` holds the `Completion` layout. - OpenCode adds `entry-skill` and a one-entry `skills` list naming that skill. Its Plan and Code routing agents allow ordinary non-SCE skills by default, deny arbitrary `sce-*` skills, and then allow only catalog-owned workflows: Plan allows `sce-change-to-plan`; Code allows `sce-next-task`, `sce-validate`, `sce-commit`, `sce-handover`, and `sce-brownfield`, plus the synchronization-only `sce-decision` exception. +## Generated contract checks + +The generated-output contract independently verifies semantic workflow integrity in addition to inventory checks: every cited output layout has a matching heading, every package-local reference exists, removed validate/commit reference files stay absent, the commit package emits its split style reference and rejects the obsolete atomic-commit contract section, next-task owns the sync report without duplicating it in `output.md`, target-neutral reference bodies remain identical, stale synchronization-loss wording stays absent, final validation remains observational, and every explicit OpenCode `sce-*` permission names an emitted skill artifact. One checked-in negative fixture covers each semantic assertion. + ## Canonical sources - `config/pkl/base/workflow-next-task.pkl` diff --git a/context/sce/shared-context-plan-workflow.md b/context/sce/shared-context-plan-workflow.md index 2e24dc73..8e7285fd 100644 --- a/context/sce/shared-context-plan-workflow.md +++ b/context/sce/shared-context-plan-workflow.md @@ -44,6 +44,20 @@ context-load phase and continues with the original request in the same session. - Durable repository context is read, not synchronized, during planning. - A ready plan ends with an exact `/next-task {plan-path} {task-id}` handoff; the workflow does not request implementation approval itself. +## Continuation contract + +Same-session waits preserve one of two explicit continuation shapes inside the +single `sce-change-to-plan` skill: + +- Initial clarification keeps `original_request`, `clarification_answers`, and + `loaded_context_brief`. The original request is unchanged and is never + re-requested after the clarification wait. +- Existing-plan revision keeps `plan_path`, `correction`, and + `loaded_context_brief`. + +The plan-authoring phase receives the applicable continuation fields as written; +it does not reload durable context or require a sibling skill handoff. + ## Flow ```mermaid diff --git a/flake.nix b/flake.nix index f7d7cc31..ef47c62c 100644 --- a/flake.nix +++ b/flake.nix @@ -211,6 +211,15 @@ root = workspaceRoot; fileset = pkgs.lib.fileset.unions [ ./config/pkl + ./config/pkl/renderers/fixtures/atomic-commit-content-check.pkl + ./config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl + ./config/pkl/renderers/fixtures/layout-reference-check.pkl + ./config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl + ./config/pkl/renderers/fixtures/package-local-reference-check.pkl + ./config/pkl/renderers/fixtures/stale-sync-debt-check.pkl + ./config/pkl/renderers/fixtures/target-neutral-reference-check.pkl + ./config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl + ./config/pkl/renderers/fixtures/validation-repair-check.pkl ./config/lib/pi-plugin/sce-pi-extension.ts ./config/lib/bash-policy-plugin/opencode-bash-policy-plugin.ts ./config/lib/agent-trace-plugin/opencode-sce-agent-trace-plugin.ts diff --git a/scripts/test-check-generated.sh b/scripts/test-check-generated.sh index cdee9b77..6e293509 100755 --- a/scripts/test-check-generated.sh +++ b/scripts/test-check-generated.sh @@ -27,7 +27,16 @@ touch \ "${test_repo}/config/pkl/renderers/generation-contract-check.pkl" \ "${test_repo}/config/pkl/renderers/fixtures/extra-artifact-check.pkl" \ "${test_repo}/config/pkl/renderers/fixtures/missing-artifact-check.pkl" \ - "${test_repo}/config/pkl/renderers/fixtures/forbidden-workflow-reference-check.pkl" + "${test_repo}/config/pkl/renderers/fixtures/forbidden-workflow-reference-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/layout-reference-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/package-local-reference-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/validate-forbidden-path-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/commit-forbidden-path-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/atomic-commit-content-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/next-task-report-ownership-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/target-neutral-reference-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/stale-sync-debt-check.pkl" \ + "${test_repo}/config/pkl/renderers/fixtures/validation-repair-check.pkl" cat > "${fake_bin}/pkl" <<'EOF' #!/usr/bin/env bash @@ -60,6 +69,42 @@ case "${module}" in 'generated workflow document contains a forbidden sibling-package reference or unresolved internalization token' >&2 exit 1 ;; + *layout-reference-check.pkl) + printf '%s\n' 'generated workflow layout citation does not match a heading in its references/output.md' >&2 + exit 1 + ;; + *package-local-reference-check.pkl) + printf '%s\n' 'generated package-local reference points to a missing document' >&2 + exit 1 + ;; + *validate-forbidden-path-check.pkl) + printf '%s\n' 'sce-validate must not generate sync-report.md or validation-result.md' >&2 + exit 1 + ;; + *commit-forbidden-path-check.pkl) + printf '%s\n' 'sce-commit must not generate a legacy commit reference file' >&2 + exit 1 + ;; + *atomic-commit-content-check.pkl) + printf '%s\n' 'atomic-commit.md must contain both commit-message rules and the atomic-commit result contract' >&2 + exit 1 + ;; + *next-task-report-ownership-check.pkl) + printf '%s\n' 'sce-next-task output.md must not duplicate the context-sync report contract' >&2 + exit 1 + ;; + *target-neutral-reference-check.pkl) + printf '%s\n' 'target-neutral package references differ between Pi, Claude, and OpenCode' >&2 + exit 1 + ;; + *stale-sync-debt-check.pkl) + printf '%s\n' 'generated file contains the stale synchronization-loss wording' >&2 + exit 1 + ;; + *validation-repair-check.pkl) + printf '%s\n' 'final validation must not instruct the agent to repair implementation' >&2 + exit 1 + ;; esac if [ -z "${destination}" ]; then