mirror of
https://github.com/github/spec-kit
synced 2026-06-21 13:51:39 +00:00
Implement /speckit.converge command
Add the built-in converge command that assesses the codebase against a feature's spec.md, plan.md, and tasks.md and appends remaining unbuilt work as new traceable tasks to tasks.md (append-only; no git, no change tracking). - templates/commands/converge.md: full command body (load artifacts, assess code, classify findings missing/partial/contradicts/unrequested, append '## Phase N — Convergence' tasks with source-ref + gap-type, read-only guardrails, converged branch, handoff, before/after_converge hooks) - Register converge as a core command across all enumeration sites (SKILL_DESCRIPTIONS, _FALLBACK_CORE_COMMAND_NAMES, ARGUMENT_HINTS, and the integration test command lists incl. copilot/generic file inventories) - init.py Next Steps panel + README Core Commands table - tasks.md: T001-T024 complete (T025 manual quickstart pending) Full suite green: 2343 passed.
This commit is contained in:
@@ -163,6 +163,7 @@ Essential commands for the Spec-Driven Development workflow:
|
||||
| `/speckit.tasks` | `speckit-tasks` | Generate actionable task lists for implementation |
|
||||
| `/speckit.taskstoissues` | `speckit-taskstoissues`| Convert generated task lists into GitHub issues for tracking and execution |
|
||||
| `/speckit.implement` | `speckit-implement` | Execute all tasks to build the feature according to the plan |
|
||||
| `/speckit.converge` | `speckit-converge` | Assess the codebase against spec/plan/tasks and append remaining work as new tasks |
|
||||
|
||||
### Optional Commands
|
||||
|
||||
|
||||
@@ -97,19 +97,22 @@ templates/
|
||||
└── converge.md # NEW — the command template (primary deliverable)
|
||||
|
||||
src/specify_cli/
|
||||
├── __init__.py # EDIT — add "converge" to SKILL_DESCRIPTIONS + post-init "Next Steps" panel (after implement)
|
||||
├── __init__.py # EDIT — add "converge" to SKILL_DESCRIPTIONS
|
||||
├── extensions.py # EDIT — add "converge" to _FALLBACK_CORE_COMMAND_NAMES
|
||||
├── commands/
|
||||
│ └── init.py # EDIT — add "converge" to the post-init "Next Steps" panel (after implement)
|
||||
└── integrations/
|
||||
└── claude/__init__.py # EDIT — add "converge" argument hint
|
||||
|
||||
tests/
|
||||
├── test_agent_config_consistency.py # VERIFY — converge token resolves correctly
|
||||
└── integrations/
|
||||
├── test_integration_base_markdown.py # EDIT — add "converge" to expected command stems
|
||||
├── test_integration_base_markdown.py # EDIT — add "converge" to COMMAND_STEMS
|
||||
├── test_integration_base_toml.py # EDIT — add "converge" to COMMAND_STEMS
|
||||
├── test_integration_base_yaml.py # EDIT — add "converge" to COMMAND_STEMS
|
||||
├── test_integration_base_skills.py # EDIT — add "converge" to expected_commands + _SKILL_COMMANDS
|
||||
└── test_integration_copilot.py # EDIT — add "converge" to expected_commands + _SKILL_COMMANDS
|
||||
├── test_integration_copilot.py # EDIT — add "converge" to expected_commands + _SKILL_COMMANDS
|
||||
└── test_integration_generic.py # EDIT — add "converge" to the constitution-context parametrize list
|
||||
|
||||
README.md # EDIT — add "/speckit.converge" to the Core Commands table (the canonical slash-command enumeration; Constitution III & V)
|
||||
```
|
||||
|
||||
@@ -26,8 +26,8 @@ in `docs/`.
|
||||
|
||||
**Purpose**: Establish the command template file and its invocation plumbing.
|
||||
|
||||
- [ ] T001 Create the command template skeleton at `templates/commands/converge.md` modeled on `templates/commands/analyze.md`: YAML frontmatter with a `description` and a `scripts:` block (`sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks`, `ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks`), a `## User Input` section echoing `$ARGUMENTS`, and empty section placeholders for the workflow.
|
||||
- [ ] T002 In `templates/commands/converge.md`, add the `## Execution Steps` initialization step that runs `{SCRIPT}` once, parses JSON for `FEATURE_DIR`/`AVAILABLE_DOCS`, derives `SPEC`/`PLAN`/`TASKS`/constitution paths, and aborts with a prerequisite message if `plan.md` or `tasks.md` is missing (per [contracts/command-interface.md](contracts/command-interface.md)).
|
||||
- [X] T001 Create the command template skeleton at `templates/commands/converge.md` modeled on `templates/commands/analyze.md`: YAML frontmatter with a `description` and a `scripts:` block (`sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks`, `ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks`), a `## User Input` section echoing `$ARGUMENTS`, and empty section placeholders for the workflow.
|
||||
- [X] T002 In `templates/commands/converge.md`, add the `## Execution Steps` initialization step that runs `{SCRIPT}` once, parses JSON for `FEATURE_DIR`/`AVAILABLE_DOCS`, derives `SPEC`/`PLAN`/`TASKS`/constitution paths, and aborts with a prerequisite message if `plan.md` or `tasks.md` is missing (per [contracts/command-interface.md](contracts/command-interface.md)).
|
||||
|
||||
---
|
||||
|
||||
@@ -37,15 +37,16 @@ in `docs/`.
|
||||
|
||||
**⚠️ CRITICAL**: Until registration is complete, the command cannot be installed or tested under any agent, and the integration test suite will fail.
|
||||
|
||||
- [ ] T003 [P] Add `"converge"` with a one-line description to `SKILL_DESCRIPTIONS` in `src/specify_cli/__init__.py`.
|
||||
- [ ] T004 [P] Add `"converge"` to the `_FALLBACK_CORE_COMMAND_NAMES` frozenset in `src/specify_cli/extensions.py` (keep alphabetical order).
|
||||
- [ ] T005 [P] Add a `"converge"` argument-hint entry to `ARGUMENT_HINTS` in `src/specify_cli/integrations/claude/__init__.py`.
|
||||
- [ ] T006 [P] Add `"converge"` to the `COMMAND_STEMS` list in `tests/integrations/test_integration_base_yaml.py`.
|
||||
- [ ] T007 [P] Add `"converge"` to the `COMMAND_STEMS` list in `tests/integrations/test_integration_base_toml.py`.
|
||||
- [ ] T008 [P] Add `"converge"` to the expected command-stems list in `tests/integrations/test_integration_base_markdown.py`.
|
||||
- [ ] T009 [P] Add `"converge"` to both command enumerations in `tests/integrations/test_integration_base_skills.py`: the `expected_commands` set (~L102) and the `_SKILL_COMMANDS` list (~L361).
|
||||
- [ ] T010 [P] Add `"converge"` to both command enumerations in `tests/integrations/test_integration_copilot.py`: the hardcoded `expected_commands` set (~L129) and the `_SKILL_COMMANDS` list (~L286).
|
||||
- [ ] T011 Run `uv run python -m pytest tests/test_agent_config_consistency.py tests/integrations -q` and confirm the new `converge` entries resolve and all command-set assertions pass (fix any enumeration missed in T003–T010).
|
||||
- [X] T003 [P] Add `"converge"` with a one-line description to `SKILL_DESCRIPTIONS` in `src/specify_cli/__init__.py`.
|
||||
- [X] T004 [P] Add `"converge"` to the `_FALLBACK_CORE_COMMAND_NAMES` frozenset in `src/specify_cli/extensions.py` (~L31; it is a frozenset, so simply append the entry — ordering is not significant).
|
||||
- [X] T005 [P] Add a `"converge"` argument-hint entry to `ARGUMENT_HINTS` in `src/specify_cli/integrations/claude/__init__.py`.
|
||||
- [X] T006 [P] Add `"converge"` to the `COMMAND_STEMS` list in `tests/integrations/test_integration_base_yaml.py`.
|
||||
- [X] T007 [P] Add `"converge"` to the `COMMAND_STEMS` list in `tests/integrations/test_integration_base_toml.py`.
|
||||
- [X] T008 [P] Add `"converge"` to the expected command-stems list in `tests/integrations/test_integration_base_markdown.py`.
|
||||
- [X] T009 [P] Add `"converge"` to both command enumerations in `tests/integrations/test_integration_base_skills.py`: the `expected_commands` set (~L102) and the `_SKILL_COMMANDS` list (~L395).
|
||||
- [X] T010 [P] Add `"converge"` to both command enumerations in `tests/integrations/test_integration_copilot.py`: the hardcoded `expected_commands` set (~L129) and the `_SKILL_COMMANDS` list (~L323).
|
||||
- [X] T010a [P] Add `"converge"` to the `command_stem` parametrize list of `test_command_loads_constitution_context` in `tests/integrations/test_integration_generic.py` (~L211–L223) — converge reads the constitution, so it must reference `constitution.md` like its peer commands (Constitution III; the generic test enforces this).
|
||||
- [X] T011 Run `uv run python -m pytest tests/test_agent_config_consistency.py tests/integrations -q` and confirm the new `converge` entries resolve and all command-set assertions pass (fix any enumeration missed in T003–T010a).
|
||||
|
||||
**Checkpoint**: `converge` is a recognized core command; the template installs across integrations and the suite is green. User-story behavior can now be added to the template.
|
||||
|
||||
@@ -57,11 +58,11 @@ in `docs/`.
|
||||
|
||||
**Independent Test**: In a feature whose code omits one specified requirement, run the command and confirm a new task describing exactly that requirement is appended to `tasks.md`, with no other file changed (quickstart Scenario 1).
|
||||
|
||||
- [ ] T012 [US1] In `templates/commands/converge.md`, add the artifact-loading step that reads `spec.md` (requirements FR-/SC-, acceptance scenarios), `plan.md` (decisions/constraints), `tasks.md` (existing tasks + referenced files), and `.specify/memory/constitution.md` — skipping constitution checks gracefully if it is an unfilled template (per [data-model.md](data-model.md) and FR-001, FR-002).
|
||||
- [ ] T013 [US1] Add the assessment step that scans the code paths named by the artifacts and produces `Finding` records classified as `missing`/`partial`/`contradicts`/`unrequested` with a severity, bounding scope to the feature artifacts only (FR-003, FR-004; [data-model.md](data-model.md) Finding entity).
|
||||
- [ ] T014 [US1] Add the task-append step implementing the [contracts/tasks-output.md](contracts/tasks-output.md) format: compute the next phase number and next task IDs from the current max, write a `## Phase N — Convergence` section at the end of `tasks.md`, one checklist item per actionable finding (FR-005, FR-006).
|
||||
- [ ] T015 [US1] Add explicit read-only guardrail instructions to the template: MUST NOT modify `spec.md` or `plan.md`, MUST NOT rewrite/delete existing tasks, MUST NOT modify application code — the only write is the appended Convergence phase (FR-008, FR-009, FR-010).
|
||||
- [ ] T016 [US1] Add the in-session findings summary output (severity-graded list/table of what was checked and what remains) ahead of the append (FR-012).
|
||||
- [X] T012 [US1] In `templates/commands/converge.md`, add the artifact-loading step that reads `spec.md` (requirements FR-/SC-, acceptance scenarios), `plan.md` (decisions/constraints), `tasks.md` (existing tasks + referenced files), and `.specify/memory/constitution.md` — skipping constitution checks gracefully if it is an unfilled template (per [data-model.md](data-model.md) and FR-001, FR-002).
|
||||
- [X] T013 [US1] Add the assessment step that scans the code paths named by the artifacts and produces `Finding` records classified as `missing`/`partial`/`contradicts`/`unrequested` with a severity, bounding scope to the feature artifacts only (FR-003, FR-004; [data-model.md](data-model.md) Finding entity).
|
||||
- [X] T014 [US1] Add the task-append step implementing the [contracts/tasks-output.md](contracts/tasks-output.md) format: compute the next phase number and next task IDs from the current max, write a `## Phase N — Convergence` section at the end of `tasks.md`, one checklist item per actionable finding (FR-005, FR-006).
|
||||
- [X] T015 [US1] Add explicit read-only guardrail instructions to the template: MUST NOT modify `spec.md` or `plan.md`, MUST NOT rewrite/delete existing tasks, MUST NOT modify application code — the only write is the appended Convergence phase (FR-008, FR-009, FR-010).
|
||||
- [X] T016 [US1] Add the in-session findings summary output (severity-graded list/table of what was checked and what remains) ahead of the append (FR-012).
|
||||
|
||||
**Checkpoint**: Running the command on a feature with a gap appends a correct, traceable Convergence task and touches nothing else — MVP is functional.
|
||||
|
||||
@@ -73,8 +74,8 @@ in `docs/`.
|
||||
|
||||
**Independent Test**: Run against a feature whose code fully satisfies its artifacts and confirm a clean result is reported and `tasks.md` is unchanged (quickstart Scenario 3).
|
||||
|
||||
- [ ] T017 [US2] In `templates/commands/converge.md`, add the converged-state branch: when there are no actionable findings, emit a clean "converged" summary with counts of requirements, acceptance criteria, and plan decisions checked, and explicitly do NOT modify `tasks.md` (no empty phase header) (FR-011; [contracts/tasks-output.md](contracts/tasks-output.md) clean case).
|
||||
- [ ] T018 [US2] Add the next-step handoff guidance to the template: on `converged` suggest proceeding to review/PR; on `tasks_appended` suggest running `/speckit.implement` to continue (FR-014).
|
||||
- [X] T017 [US2] In `templates/commands/converge.md`, add the converged-state branch: when there are no actionable findings, emit a clean "converged" summary with counts of requirements, acceptance criteria, and plan decisions checked, and explicitly do NOT modify `tasks.md` (no empty phase header) (FR-011; [contracts/tasks-output.md](contracts/tasks-output.md) clean case).
|
||||
- [X] T018 [US2] Add the next-step handoff guidance to the template: on `converged` suggest proceeding to review/PR; on `tasks_appended` suggest running `/speckit.implement` to continue (FR-014).
|
||||
|
||||
**Checkpoint**: Both the gap path and the clean path produce correct, distinct outcomes.
|
||||
|
||||
@@ -86,7 +87,7 @@ in `docs/`.
|
||||
|
||||
**Independent Test**: Run against a feature with several distinct gaps and confirm each appended task names its originating reference and gap-type label (quickstart Scenario 1 trace assertions).
|
||||
|
||||
- [ ] T019 [US3] In `templates/commands/converge.md`, refine the append step so each task line carries its `source-ref` (e.g. `FR-003`, `SC-002`, `US1/AC2`, `plan: …`, `Constitution II`) and a `(gap-type)` label, and so constitution violations are emitted as `CRITICAL` (FR-007; [data-model.md](data-model.md) Convergence Task entity).
|
||||
- [X] T019 [US3] In `templates/commands/converge.md`, refine the append step so each task line carries its `source-ref` (e.g. `FR-003`, `SC-002`, `US1/AC2`, `plan: …`, `Constitution II`) and a `(gap-type)` label, and so constitution violations are emitted as `CRITICAL` (FR-007; [data-model.md](data-model.md) Convergence Task entity).
|
||||
|
||||
**Checkpoint**: Appended tasks are fully traceable and prioritized.
|
||||
|
||||
@@ -96,11 +97,11 @@ in `docs/`.
|
||||
|
||||
**Purpose**: Hooks, cross-integration discoverability, docs, and full validation.
|
||||
|
||||
- [ ] T020 [P] In `templates/commands/converge.md`, add the `before_converge` pre-execution hook block and the `after_converge` post-execution hook block (copied from the `analyze.md` pattern, keys renamed), ensuring `after_converge` receives the `converged` vs `tasks_appended` outcome (FR-015; [contracts/hooks.md](contracts/hooks.md)).
|
||||
- [ ] T021 [P] Add `converge` to the post-init guidance in `src/specify_cli/__init__.py` (the "Next Steps" panel, ~L1567–L1575) as a step shown after `implement` — using the existing `_display_cmd('converge')` helper so it renders correctly across skill/slash modes (FR-017).
|
||||
- [ ] T022 [P] Add a `/speckit.converge` row to the **README Core Commands table** in `README.md` (~L300–L307) — the only canonical slash-command enumeration in the repo — describing purpose, scope (code → spec), and append-only behavior (Constitution Principles III & V). Note: `docs/reference/core.md` is the `specify` CLI reference and `docs/reference/workflows.md` only defines the Full SDD Cycle workflow (which converge is not part of), so neither requires edits.
|
||||
- [ ] T023 Mentally apply `process_template` to `templates/commands/converge.md` and confirm no leftover `{SCRIPT}`, `$ARGUMENTS`, or `__SPECKIT_COMMAND_*__` tokens remain unresolvable and that the frontmatter is valid (FR-016).
|
||||
- [ ] T024 Run `uv run python -m pytest tests/test_agent_config_consistency.py tests/integrations -q` again to confirm the full suite passes after all edits.
|
||||
- [X] T020 [P] In `templates/commands/converge.md`, add the `before_converge` pre-execution hook block and the `after_converge` post-execution hook block (copied from the `analyze.md` pattern, keys renamed), ensuring `after_converge` receives the `converged` vs `tasks_appended` outcome (FR-015; [contracts/hooks.md](contracts/hooks.md)).
|
||||
- [X] T021 [P] Add `converge` to the post-init guidance in `src/specify_cli/commands/init.py` (the "Next Steps" panel, ~L576–L582) as a step shown after `implement` (~L580) — using the existing `_display_cmd('converge')` helper so it renders correctly across skill/slash modes (FR-017).
|
||||
- [X] T022 [P] Add a `/speckit.converge` row to the **README Core Commands table** in `README.md` (~L158–L165) — the only canonical slash-command enumeration in the repo — describing purpose, scope (code → spec), and append-only behavior (Constitution Principles III & V). Note: `docs/reference/core.md` is the `specify` CLI reference and `docs/reference/workflows.md` only defines the Full SDD Cycle workflow (which converge is not part of), so neither requires edits.
|
||||
- [X] T023 Mentally apply `process_template` to `templates/commands/converge.md` and confirm no leftover `{SCRIPT}`, `$ARGUMENTS`, or `__SPECKIT_COMMAND_*__` tokens remain unresolvable and that the frontmatter is valid (FR-016).
|
||||
- [X] T024 Run `uv run python -m pytest tests/test_agent_config_consistency.py tests/integrations -q` again to confirm the full suite passes after all edits.
|
||||
- [ ] T025 Execute the [quickstart.md](quickstart.md) scenarios 1–6 manually through a coding agent and capture agent/OS/shell + pass/fail per scenario for the PR (Constitution Principle IV — manual slash-command validation).
|
||||
|
||||
---
|
||||
@@ -110,7 +111,7 @@ in `docs/`.
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies — start immediately. T002 depends on T001 (same file).
|
||||
- **Foundational (Phase 2)**: Depends on Setup. T003–T010 are parallelizable; T011 depends on T003–T010. BLOCKS all user stories (command must be registered to be testable).
|
||||
- **Foundational (Phase 2)**: Depends on Setup. T003–T010a are parallelizable; T011 depends on T003–T010a. BLOCKS all user stories (command must be registered to be testable).
|
||||
- **User Stories (Phase 3–5)**: All depend on Foundational completion. They edit the same template file, so within the template they are sequential, but each story is independently testable once present.
|
||||
- **Polish (Phase 6)**: Depends on the desired user stories being complete.
|
||||
|
||||
@@ -127,7 +128,7 @@ in `docs/`.
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- **Phase 2**: T003, T004, T005, T006, T007, T008, T009, T010 all touch different files → run in parallel; then T011.
|
||||
- **Phase 2**: T003, T004, T005, T006, T007, T008, T009, T010, T010a all touch different files → run in parallel; then T011.
|
||||
- **Phase 6**: T020 (template), T021 (init.py), T022 (docs) touch different files → run in parallel; then T023/T024/T025.
|
||||
- The template-body tasks (T001–T002, T012–T019) all edit `templates/commands/converge.md` and must be sequential.
|
||||
|
||||
@@ -145,6 +146,7 @@ Task: T007 Add "converge" to COMMAND_STEMS in tests/integrations/test_integratio
|
||||
Task: T008 Add "converge" to tests/integrations/test_integration_base_markdown.py
|
||||
Task: T009 Add "converge" to tests/integrations/test_integration_base_skills.py
|
||||
Task: T010 Add "converge" to tests/integrations/test_integration_copilot.py
|
||||
Task: T010a Add "converge" to tests/integrations/test_integration_generic.py
|
||||
# Then:
|
||||
Task: T011 Run the integration + agent-config test suite
|
||||
```
|
||||
@@ -156,7 +158,7 @@ Task: T011 Run the integration + agent-config test suite
|
||||
### MVP First (User Story 1 only)
|
||||
|
||||
1. Complete Phase 1: Setup (T001–T002).
|
||||
2. Complete Phase 2: Foundational registration (T003–T011) — CRITICAL, blocks everything.
|
||||
2. Complete Phase 2: Foundational registration (T003–T011) — CRITICAL, blocks everything. (Includes T010a.)
|
||||
3. Complete Phase 3: User Story 1 (T012–T016).
|
||||
4. **STOP and VALIDATE**: Run quickstart Scenario 1 — confirm a gap becomes a traceable appended task and nothing else changes. This is a usable, shippable increment.
|
||||
|
||||
|
||||
@@ -429,6 +429,7 @@ SKILL_DESCRIPTIONS = {
|
||||
"plan": "Generate technical implementation plans from feature specifications.",
|
||||
"tasks": "Break down implementation plans into actionable task lists.",
|
||||
"implement": "Execute all tasks from the task breakdown to build the feature.",
|
||||
"converge": "Assess the codebase against spec.md, plan.md, and tasks.md and append remaining work as new tasks.",
|
||||
"analyze": "Perform cross-artifact consistency analysis across spec.md, plan.md, and tasks.md.",
|
||||
"clarify": "Structured clarification workflow for underspecified requirements.",
|
||||
"constitution": "Create or update project governing principles and development guidelines.",
|
||||
|
||||
@@ -578,6 +578,7 @@ def register(app: typer.Typer) -> None:
|
||||
steps_lines.append(f" {step_num}.3 [cyan]{_display_cmd('plan')}[/] - Create implementation plan")
|
||||
steps_lines.append(f" {step_num}.4 [cyan]{_display_cmd('tasks')}[/] - Generate actionable tasks")
|
||||
steps_lines.append(f" {step_num}.5 [cyan]{_display_cmd('implement')}[/] - Execute implementation")
|
||||
steps_lines.append(f" {step_num}.6 [cyan]{_display_cmd('converge')}[/] - Assess the codebase and append remaining work as tasks")
|
||||
|
||||
steps_panel = Panel("\n".join(steps_lines), title="Next Steps", border_style="cyan", padding=(1, 2))
|
||||
console.print()
|
||||
|
||||
@@ -32,6 +32,7 @@ _FALLBACK_CORE_COMMAND_NAMES = frozenset({
|
||||
"analyze",
|
||||
"clarify",
|
||||
"constitution",
|
||||
"converge",
|
||||
"implement",
|
||||
"plan",
|
||||
"checklist",
|
||||
|
||||
@@ -17,6 +17,7 @@ ARGUMENT_HINTS: dict[str, str] = {
|
||||
"plan": "Optional guidance for the planning phase",
|
||||
"tasks": "Optional task generation constraints",
|
||||
"implement": "Optional implementation guidance or task filter",
|
||||
"converge": "Optional feature name to converge",
|
||||
"analyze": "Optional focus areas for analysis",
|
||||
"clarify": "Optional areas to clarify in the spec",
|
||||
"constitution": "Principles or values for the project constitution",
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
---
|
||||
description: Assess the current codebase against the feature's spec, plan, and tasks, then append any remaining unbuilt work as new tasks to tasks.md so implement can complete it.
|
||||
scripts:
|
||||
sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks
|
||||
ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks
|
||||
---
|
||||
|
||||
## User Input
|
||||
|
||||
```text
|
||||
$ARGUMENTS
|
||||
```
|
||||
|
||||
You **MUST** consider the user input before proceeding (if not empty).
|
||||
|
||||
## Pre-Execution Checks
|
||||
|
||||
**Check for extension hooks (before convergence)**:
|
||||
- Check if `.specify/extensions.yml` exists in the project root.
|
||||
- If it exists, read it and look for entries under the `hooks.before_converge` key
|
||||
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
|
||||
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||
- For each executable hook, output the following based on its `optional` flag:
|
||||
- **Optional hook** (`optional: true`):
|
||||
```
|
||||
## Extension Hooks
|
||||
|
||||
**Optional Pre-Hook**: {extension}
|
||||
Command: `/{command}`
|
||||
Description: {description}
|
||||
|
||||
Prompt: {prompt}
|
||||
To execute: `/{command}`
|
||||
```
|
||||
- **Mandatory hook** (`optional: false`):
|
||||
```
|
||||
## Extension Hooks
|
||||
|
||||
**Automatic Pre-Hook**: {extension}
|
||||
Executing: `/{command}`
|
||||
EXECUTE_COMMAND: {command}
|
||||
|
||||
Wait for the result of the hook command before proceeding to the Goal.
|
||||
```
|
||||
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||
|
||||
## Goal
|
||||
|
||||
Close the gap between what a feature's specification, plan, and tasks call for and what the
|
||||
codebase currently implements. Read `spec.md`, `plan.md`, and `tasks.md` as the **sole
|
||||
source of intent** (with the constitution as governing constraints), assess the current
|
||||
state of the code, determine which requirements, acceptance criteria, plan decisions, and
|
||||
existing tasks are unmet, incomplete, or only partially satisfied, and **append each piece
|
||||
of remaining work as a new, traceable task** at the bottom of `tasks.md` so that
|
||||
`__SPECKIT_COMMAND_IMPLEMENT__` can complete it. This command MUST run only after
|
||||
`__SPECKIT_COMMAND_TASKS__` has produced a complete `tasks.md`.
|
||||
|
||||
This is **not** a diff tool and does **not** track changes. It assesses the present state
|
||||
of the code relative to the feature's artifacts — no git, no branch comparison, no history.
|
||||
|
||||
## Operating Constraints
|
||||
|
||||
**APPEND-ONLY, NEVER REWRITE**: The command's **only** write is appending a new
|
||||
`## Phase N — Convergence` section to `tasks.md`. It MUST NOT:
|
||||
|
||||
- modify `spec.md` or `plan.md` in any way;
|
||||
- rewrite, renumber, reorder, or delete any existing task (including tasks from a prior
|
||||
Convergence phase);
|
||||
- modify, create, or delete any application code — completing the appended tasks is the
|
||||
job of `__SPECKIT_COMMAND_IMPLEMENT__`.
|
||||
|
||||
When the codebase already satisfies everything, the command MUST leave `tasks.md`
|
||||
**byte-for-byte unchanged** (no empty Convergence header) and report a clean result.
|
||||
|
||||
**Constitution Authority**: The project constitution (`/memory/constitution.md`) is
|
||||
**non-negotiable**. Code that violates a MUST principle is the highest-severity finding and
|
||||
produces a corresponding remediation task. If the constitution is an unfilled template,
|
||||
skip constitution checks gracefully rather than failing.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### 1. Initialize Convergence Context
|
||||
|
||||
Run `{SCRIPT}` once from repo root and parse JSON for FEATURE_DIR and AVAILABLE_DOCS. Derive absolute paths:
|
||||
|
||||
- SPEC = FEATURE_DIR/spec.md
|
||||
- PLAN = FEATURE_DIR/plan.md
|
||||
- TASKS = FEATURE_DIR/tasks.md
|
||||
- CONSTITUTION = `.specify/memory/constitution.md` (if present)
|
||||
|
||||
If `plan.md` or `tasks.md` is missing, STOP with a clear, actionable message naming the
|
||||
prerequisite command to run (`__SPECKIT_COMMAND_PLAN__` for a missing plan,
|
||||
`__SPECKIT_COMMAND_TASKS__` for missing tasks). Do not produce partial output.
|
||||
For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot").
|
||||
|
||||
### 2. Load Artifacts (Progressive Disclosure)
|
||||
|
||||
Load only the minimal necessary context from each artifact:
|
||||
|
||||
**From spec.md:**
|
||||
|
||||
- Functional Requirements (FR-###)
|
||||
- Success Criteria (SC-###) — include only items requiring buildable work; exclude
|
||||
post-launch outcome metrics and business KPIs
|
||||
- User Stories and their Acceptance Scenarios
|
||||
- Edge Cases (if present)
|
||||
|
||||
**From plan.md:**
|
||||
|
||||
- Architecture/stack choices and technical decisions
|
||||
- Data Model references
|
||||
- Phases and named touch-points (files/components the plan says will be created or edited)
|
||||
- Technical constraints
|
||||
|
||||
**From tasks.md:**
|
||||
|
||||
- Task IDs (to compute the next ID and next phase number)
|
||||
- Descriptions, phase grouping, and referenced file paths
|
||||
|
||||
**From constitution (if not an unfilled template):**
|
||||
|
||||
- Principle names and MUST/SHOULD normative statements
|
||||
|
||||
### 3. Build the Intent Inventory
|
||||
|
||||
Create an internal model (do not echo raw artifacts):
|
||||
|
||||
- **Requirements inventory**: one stable key per FR-### / SC-### / user-story acceptance
|
||||
scenario (e.g. `US1/AC2`), plus the plan decisions and constitution principles that
|
||||
impose buildable obligations.
|
||||
- **Code-scope map**: from the file paths named in `plan.md` and `tasks.md`, plus a keyword
|
||||
search for the concepts each requirement describes, derive the set of source files and
|
||||
components in scope for assessment. Bound the assessment to these — do **not** infer
|
||||
scope beyond what the artifacts define (FR-001).
|
||||
|
||||
### 4. Assess the Codebase and Classify Findings
|
||||
|
||||
For each item in the intent inventory, inspect the current code in scope and produce a
|
||||
`Finding` only where there is a gap. Classify every finding by **gap type**:
|
||||
|
||||
- **`missing`**: the required work is absent from the code entirely.
|
||||
- **`partial`**: the work exists but does not yet fully satisfy the requirement /
|
||||
acceptance criterion / plan decision.
|
||||
- **`contradicts`**: the code does something that conflicts with stated intent or a
|
||||
constitution MUST principle.
|
||||
- **`unrequested`**: the code contains work not called for by the spec, plan, or tasks
|
||||
(surfaced for awareness — converge does **not** delete code, it only appends a task to
|
||||
review/justify or remove it).
|
||||
|
||||
Each `Finding` records: a stable id, the `source-ref` it traces to, the `gap-type`, a
|
||||
severity, and a short human-readable description with the evidence (the file/area observed).
|
||||
|
||||
**Edge cases:**
|
||||
|
||||
- **Little or no code yet**: treat the entire specified scope as `missing` remaining work
|
||||
rather than failing.
|
||||
- **Nothing remains**: produce zero findings and follow the converged branch in Step 7.
|
||||
|
||||
### 5. Assign Severity
|
||||
|
||||
- **CRITICAL**: violates a constitution MUST principle, or a `missing`/`contradicts` gap
|
||||
that blocks baseline functionality of a P1 user story.
|
||||
- **HIGH**: a `missing` or `partial` gap on a core functional requirement or acceptance
|
||||
criterion.
|
||||
- **MEDIUM**: a `partial` gap on a secondary requirement, or an `unrequested` addition with
|
||||
unclear justification.
|
||||
- **LOW**: minor partial gaps, polish, or low-risk `unrequested` additions.
|
||||
|
||||
### 6. Present the In-Session Findings Summary
|
||||
|
||||
Before appending anything, output a compact, severity-graded summary (no file writes yet):
|
||||
|
||||
## Convergence Findings
|
||||
|
||||
| ID | Gap Type | Severity | Source | Evidence | Remaining Work |
|
||||
|----|----------|----------|--------|----------|----------------|
|
||||
| F1 | missing | HIGH | FR-008 | converge template has no guardrail text | Add append-only enforcement |
|
||||
|
||||
**Summary metrics:**
|
||||
|
||||
- Requirements / acceptance criteria checked
|
||||
- Plan decisions checked
|
||||
- Constitution principles checked (or "skipped — template")
|
||||
- Findings by gap type (missing / partial / contradicts / unrequested)
|
||||
- Findings by severity
|
||||
|
||||
### 7. Append Convergence Tasks (or report converged)
|
||||
|
||||
**If there are one or more actionable findings** (`tasks_appended` outcome):
|
||||
|
||||
Append to the **end** of `tasks.md`, per the append contract:
|
||||
|
||||
1. Scan all existing task IDs; let `M` be the maximum. Determine the next phase number `N`
|
||||
(highest existing phase + 1).
|
||||
2. Write a single new section header `## Phase N — Convergence`.
|
||||
3. Emit one checklist item per actionable finding, ordered CRITICAL/HIGH first, assigning
|
||||
IDs `M+1, M+2, …`:
|
||||
|
||||
```markdown
|
||||
- [ ] T<NNN>: <imperative description> per <source-ref> (<gap-type>)
|
||||
```
|
||||
|
||||
- `<source-ref>` traces the task to its origin: e.g. `FR-003`, `SC-002`, `US1/AC2`,
|
||||
`plan: storage decision`, `Constitution II`.
|
||||
- `<gap-type>` is one of `missing`, `partial`, `contradicts`, `unrequested`.
|
||||
- Constitution-violation tasks MUST be emitted first and described as `CRITICAL`.
|
||||
4. Never reuse or renumber existing IDs. If a prior Convergence phase exists, add a new,
|
||||
separately-numbered one below it — do not touch the old one.
|
||||
|
||||
**If there are no actionable findings** (`converged` outcome):
|
||||
|
||||
- Do **not** modify `tasks.md` at all — no empty phase header.
|
||||
- Report: **"✅ Converged — the implementation satisfies the spec, plan, and tasks."**
|
||||
- Include the summary counts of what was checked.
|
||||
|
||||
### 8. Provide Next Actions (Handoff)
|
||||
|
||||
- On `tasks_appended`: state how many tasks were appended under which phase, and recommend
|
||||
running `__SPECKIT_COMMAND_IMPLEMENT__` to complete them; note that a follow-up converge
|
||||
run will find fewer or no remaining items.
|
||||
- On `converged`: recommend proceeding to review / opening a PR. No further implement pass
|
||||
is needed for this feature's specified scope.
|
||||
|
||||
### 9. Check for extension hooks
|
||||
|
||||
After producing the result, check if `.specify/extensions.yml` exists in the project root.
|
||||
- If it exists, read it and look for entries under the `hooks.after_converge` key
|
||||
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
|
||||
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||
- Pass the convergence outcome (`converged` or `tasks_appended`) to the hook context so an
|
||||
extension can branch on it.
|
||||
- For each executable hook, output the following based on its `optional` flag:
|
||||
- **Optional hook** (`optional: true`):
|
||||
```
|
||||
## Extension Hooks
|
||||
|
||||
**Optional Hook**: {extension}
|
||||
Command: `/{command}`
|
||||
Description: {description}
|
||||
|
||||
Prompt: {prompt}
|
||||
To execute: `/{command}`
|
||||
```
|
||||
- **Mandatory hook** (`optional: false`):
|
||||
```
|
||||
## Extension Hooks
|
||||
|
||||
**Automatic Hook**: {extension}
|
||||
Executing: `/{command}`
|
||||
EXECUTE_COMMAND: {command}
|
||||
```
|
||||
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||
@@ -254,7 +254,7 @@ class MarkdownIntegrationTests:
|
||||
|
||||
COMMAND_STEMS = [
|
||||
"agent-context.update",
|
||||
"analyze", "clarify", "constitution", "implement",
|
||||
"analyze", "clarify", "constitution", "converge", "implement",
|
||||
"plan", "checklist", "specify", "tasks", "taskstoissues",
|
||||
]
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class SkillsIntegrationTests:
|
||||
skill_files = [f for f in created if "scripts" not in f.parts]
|
||||
|
||||
expected_commands = {
|
||||
"analyze", "clarify", "constitution", "implement",
|
||||
"analyze", "clarify", "constitution", "converge", "implement",
|
||||
"plan", "checklist", "specify", "tasks", "taskstoissues",
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ class SkillsIntegrationTests:
|
||||
# -- Complete file inventory ------------------------------------------
|
||||
|
||||
_SKILL_COMMANDS = [
|
||||
"analyze", "clarify", "constitution", "implement",
|
||||
"analyze", "clarify", "constitution", "converge", "implement",
|
||||
"plan", "checklist", "specify", "tasks", "taskstoissues",
|
||||
]
|
||||
|
||||
|
||||
@@ -486,6 +486,7 @@ class TomlIntegrationTests:
|
||||
"analyze",
|
||||
"clarify",
|
||||
"constitution",
|
||||
"converge",
|
||||
"implement",
|
||||
"plan",
|
||||
"checklist",
|
||||
|
||||
@@ -365,6 +365,7 @@ class YamlIntegrationTests:
|
||||
"analyze",
|
||||
"clarify",
|
||||
"constitution",
|
||||
"converge",
|
||||
"implement",
|
||||
"plan",
|
||||
"checklist",
|
||||
|
||||
@@ -125,9 +125,9 @@ class TestCopilotIntegration:
|
||||
agents_dir = tmp_path / ".github" / "agents"
|
||||
assert agents_dir.is_dir()
|
||||
agent_files = sorted(agents_dir.glob("speckit.*.agent.md"))
|
||||
assert len(agent_files) == 9
|
||||
assert len(agent_files) == 10
|
||||
expected_commands = {
|
||||
"analyze", "clarify", "constitution", "implement",
|
||||
"analyze", "clarify", "constitution", "converge", "implement",
|
||||
"plan", "checklist", "specify", "tasks", "taskstoissues",
|
||||
}
|
||||
actual_commands = {f.name.removeprefix("speckit.").removesuffix(".agent.md") for f in agent_files}
|
||||
@@ -198,6 +198,7 @@ class TestCopilotIntegration:
|
||||
".github/agents/speckit.checklist.agent.md",
|
||||
".github/agents/speckit.clarify.agent.md",
|
||||
".github/agents/speckit.constitution.agent.md",
|
||||
".github/agents/speckit.converge.agent.md",
|
||||
".github/agents/speckit.implement.agent.md",
|
||||
".github/agents/speckit.plan.agent.md",
|
||||
".github/agents/speckit.specify.agent.md",
|
||||
@@ -208,6 +209,7 @@ class TestCopilotIntegration:
|
||||
".github/prompts/speckit.checklist.prompt.md",
|
||||
".github/prompts/speckit.clarify.prompt.md",
|
||||
".github/prompts/speckit.constitution.prompt.md",
|
||||
".github/prompts/speckit.converge.prompt.md",
|
||||
".github/prompts/speckit.implement.prompt.md",
|
||||
".github/prompts/speckit.plan.prompt.md",
|
||||
".github/prompts/speckit.specify.prompt.md",
|
||||
@@ -268,6 +270,7 @@ class TestCopilotIntegration:
|
||||
".github/agents/speckit.checklist.agent.md",
|
||||
".github/agents/speckit.clarify.agent.md",
|
||||
".github/agents/speckit.constitution.agent.md",
|
||||
".github/agents/speckit.converge.agent.md",
|
||||
".github/agents/speckit.implement.agent.md",
|
||||
".github/agents/speckit.plan.agent.md",
|
||||
".github/agents/speckit.specify.agent.md",
|
||||
@@ -278,6 +281,7 @@ class TestCopilotIntegration:
|
||||
".github/prompts/speckit.checklist.prompt.md",
|
||||
".github/prompts/speckit.clarify.prompt.md",
|
||||
".github/prompts/speckit.constitution.prompt.md",
|
||||
".github/prompts/speckit.converge.prompt.md",
|
||||
".github/prompts/speckit.implement.prompt.md",
|
||||
".github/prompts/speckit.plan.prompt.md",
|
||||
".github/prompts/speckit.specify.prompt.md",
|
||||
@@ -321,7 +325,7 @@ class TestCopilotSkillsMode:
|
||||
"""Tests for Copilot integration in --skills mode."""
|
||||
|
||||
_SKILL_COMMANDS = [
|
||||
"analyze", "clarify", "constitution", "implement",
|
||||
"analyze", "clarify", "constitution", "converge", "implement",
|
||||
"plan", "checklist", "specify", "tasks", "taskstoissues",
|
||||
]
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ class TestGenericIntegration:
|
||||
[
|
||||
"analyze",
|
||||
"clarify",
|
||||
"converge",
|
||||
"implement",
|
||||
"plan",
|
||||
"checklist",
|
||||
@@ -306,6 +307,7 @@ class TestGenericIntegration:
|
||||
".myagent/commands/speckit.checklist.md",
|
||||
".myagent/commands/speckit.clarify.md",
|
||||
".myagent/commands/speckit.constitution.md",
|
||||
".myagent/commands/speckit.converge.md",
|
||||
".myagent/commands/speckit.implement.md",
|
||||
".myagent/commands/speckit.plan.md",
|
||||
".myagent/commands/speckit.specify.md",
|
||||
@@ -370,6 +372,7 @@ class TestGenericIntegration:
|
||||
".myagent/commands/speckit.checklist.md",
|
||||
".myagent/commands/speckit.clarify.md",
|
||||
".myagent/commands/speckit.constitution.md",
|
||||
".myagent/commands/speckit.converge.md",
|
||||
".myagent/commands/speckit.implement.md",
|
||||
".myagent/commands/speckit.plan.md",
|
||||
".myagent/commands/speckit.specify.md",
|
||||
|
||||
Reference in New Issue
Block a user