Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions docs/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,66 @@ Restart your IDE to refresh the command list.

---

## Behavior change: `/constitution` no longer propagates into templates (0.14.4)

Starting in **0.14.4** ([#3790](https://github.com/github/spec-kit/pull/3790)), the
`/constitution` command is scoped to its own artifact. It updates
`.specify/memory/constitution.md` and writes a Sync Impact Report, and **no longer edits**
`plan-template.md`, `spec-template.md`, `tasks-template.md`, installed command files, or
guidance docs.

### Why

Spec Kit uses **runtime resolution**: `plan`, `tasks`, and `analyze` read
`.specify/memory/constitution.md` live on every run, and `analyze` is the dedicated drift
checker. The governed templates carry a pointer, not a copy — `plan-template.md` ships
`[Gates determined based on constitution file]`, and `/plan` fills that section from the live
constitution each run. Propagation duplicated the single source of truth and fought the
preset/override composition system (a `replace` preset shadows an edited core template).

### Is this a breaking change for existing projects?

**No — your workflow keeps working.** The templates are scaffolds, not authorities. When you
run `/plan`, it copies the template into a per-feature `plan.md` and re-derives the Constitution
Check from the live constitution; `/analyze` validates against it. Even if a previous
`/constitution` run materialized concrete gate text into `.specify/templates/plan-template.md`,
the live constitution remains the source of truth at runtime.

On a **non-forced upgrade**, a materialized template is *preserved* (its hash diverges from the
recorded managed copy, so the refresh treats it as a customization and does not overwrite it).
Nothing regresses.

### Optional cleanup — return to the runtime pointer

A frozen, pre-filled Constitution Check is a slightly misleading scaffold and can bias the first
`/plan` pass. To move fully back to runtime resolution, reset the section body in
`.specify/templates/plan-template.md` to the pointer:

```text
## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

[Gates determined based on constitution file]
```

Leave the rest of the file untouched. This is cleanup, not a required migration.

### Keeping the old behavior (opt-in)

If your team treats the materialized templates as **reviewed, committed artifacts** and wants
`/constitution` to keep propagating, install the bundled **`constitution-sync`** preset:

```bash
specify preset add constitution-sync
```

It wraps the core `/constitution` command and re-adds the propagation pass. It does **not** edit
versioned preset- or extension-provided templates (those are owned by their packages). See
`presets/constitution-sync/README.md` for the tradeoffs.

---

## Common Scenarios

### Scenario 1: "I just want new slash commands"
Expand Down
23 changes: 23 additions & 0 deletions presets/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@
"workflow",
"core"
]
},
"constitution-sync": {
"name": "Constitution Template Sync",
"id": "constitution-sync",
"version": "1.0.0",
"description": "Opt-in: restores /constitution propagation of amended guidance into plan/spec/tasks templates and installed command files, for teams that treat materialized templates as reviewed artifacts.",
"author": "github",
"repository": "https://github.com/github/spec-kit",
"license": "MIT",
"bundled": true,
"requires": {
"speckit_version": ">=0.14.4"
},
"provides": {
"commands": 1,
"templates": 0
},
"tags": [
"constitution",
"governance",
"templates",
"compatibility"
]
}
}
}
113 changes: 113 additions & 0 deletions presets/constitution-sync/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Constitution Template Sync

An **opt-in** preset that restores the pre-0.14.4 `/constitution` behavior: after the
constitution is updated, it propagates the amended guidance into the project's dependent
templates and installed command files.

## Background

Through 0.14.3, `/constitution` performed a "consistency propagation checklist" — it read
`.specify/templates/plan-template.md`, `spec-template.md`, `tasks-template.md`, the installed
Spec Kit command files, and guidance docs, and updated them to match the amended principles.

[#3790](https://github.com/github/spec-kit/pull/3790) (shipped in 0.14.4) **removed** that
propagation from the core command. The default model is now **runtime resolution**: `plan`,
`tasks`, and `analyze` read `.specify/memory/constitution.md` live on every run, so the
templates only need to carry a pointer (`plan-template.md` ships
`[Gates determined based on constitution file]`) rather than a materialized copy. This avoids
duplicating the source of truth and avoids fighting the preset/override composition system.

## When to use this preset

Install it **only** if your team treats the materialized templates as **reviewed, committed
artifacts** — for example, if `plan-template.md`'s Constitution Check is read in PRs as "here are
our current gates" and is expected to stay in sync with the constitution.

If you rely on the default runtime-resolution model, you do **not** need this preset. Your
workflow is already correct without it: the live constitution is the single source of truth.

## What it does

This preset ships a single `wrap`-strategy override of `speckit.constitution`. It composes on top
of the current core command (via `{CORE_TEMPLATE}`), so it stays forward-compatible with core
changes, and appends a propagation pass that:

- Aligns `plan/spec/tasks-template.md` with the updated principles.
- Scans installed command files for stale agent-specific references.
- Updates guidance-doc references to changed principles.
- Extends the Sync Impact Report with the templates it touched.

It deliberately **does not** edit versioned preset- or extension-provided template files — those
are owned by their packages and recomposed on update.

## Interaction with the resolution stack (important limitation)

This preset and the preset resolution stack are built on **opposing philosophies**, and they can
bite each other:

- The resolution stack (the default model since #3790) treats templates and commands as
**layered, package-owned artifacts that are recomposed on demand** — nothing is meant to be a
frozen copy.
- Auto-propagation does the **opposite**: it **materializes** constitutional guidance *into*
template files and freezes it there.

So if a template you propagate into is actually **provided by another preset or extension in your
stack**, the two mechanisms fight: your propagated gate text is clobbered the next time that
package is reconciled/updated, and your hand edits are lost. For the same reason the wrapper only
ever writes into the project's **own** `.specify/templates/` scaffolds and installed command files
— never into stack-owned template layers.

Practical guidance:

- This preset is safe and useful when your governed templates are **project-local scaffolds** you
own and review.
- If your `plan/spec/tasks` templates come from **other presets or extensions**, propagation will
not stick — keep the default runtime-resolution model instead, where the live constitution is
read on every run and there is nothing to sync.

## Tradeoffs

- Re-introduces a materialized copy of constitutional guidance in the templates, which can drift
if `/constitution` is not re-run. The default runtime model does not have this problem.
- Materializing concrete gates into `plan-template.md` replaces the runtime pointer; a pre-filled
Constitution Check can bias the first `/plan` pass. Keep the pointer unless you specifically
want committed gates.

## Installation

```bash
# constitution-sync is a bundled preset — no download needed
specify preset add constitution-sync
```

## Development

```bash
# Test from local directory
specify preset add --dev ./presets/constitution-sync

# Verify the wrapped command resolves
specify preset resolve speckit.constitution

# Remove when done
specify preset remove constitution-sync
```

## Migrating back to the default

If you decide to move to runtime resolution, reset each materialized
`## Constitution Check` section in `.specify/templates/plan-template.md` back to the pointer:

```text
## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

[Gates determined based on constitution file]
```

Then remove this preset. See `docs/upgrade.md` for details.

## License

MIT
42 changes: 42 additions & 0 deletions presets/constitution-sync/commands/speckit.constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
description: Create or update the project constitution, then propagate the amended guidance into dependent templates and installed command files (opt-in template sync).
strategy: wrap
handoffs:
- label: Build Specification
agent: speckit.specify
prompt: Implement the feature specification based on the updated constitution. I want to build...
---

{CORE_TEMPLATE}

## Constitution Template Sync

After you have written the updated constitution above, perform a consistency propagation pass
so the dependent artifacts reflect the amended principles:

1. Read `.specify/templates/plan-template.md` and ensure any "Constitution Check" or rules align
with the updated principles. Only materialize concrete gate text here if your team intends to
review it as committed content; otherwise leave the runtime pointer
`[Gates determined based on constitution file]` in place so `/plan` fills it from the live
constitution.
2. Read `.specify/templates/spec-template.md` for scope/requirements alignment — update if the
constitution adds/removes mandatory sections or constraints.
3. Read `.specify/templates/tasks-template.md` and ensure task categorization reflects new or
removed principle-driven task types (e.g., observability, versioning, testing discipline).
4. Read each installed Spec Kit command file for your agent (including this one) — named
`speckit.*` or `speckit-*` (dot or hyphen depending on the agent), or laid out as
`speckit-<name>/SKILL.md` for skills-based integrations, e.g. in `.github/agents/`,
`.github/skills/`, `.claude/skills/`, or your agent's equivalent commands directory — to verify
no outdated references (CLAUDE-only or other agent-specific names) remain when generic guidance
is required.
5. Read any runtime guidance docs (e.g., `README.md`, `docs/quickstart.md`, or agent-specific
guidance files if present) and update references to principles that changed.

Then extend the Sync Impact Report at the top of `.specify/memory/constitution.md` with:

- Templates requiring updates (✅ updated / ⚠ pending) with file paths.

**Do not edit versioned preset- or extension-provided template files directly.** Those artifacts
are owned by their packages and are recomposed on the package's next update — hand edits are
clobbered. Limit propagation to the project's own `.specify/templates/` scaffolds and installed
command files.
30 changes: 30 additions & 0 deletions presets/constitution-sync/preset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
schema_version: "1.0"

preset:
id: "constitution-sync"
name: "Constitution Template Sync"
version: "1.0.0"
description: "Opt-in: restores /constitution propagation of amended guidance into plan/spec/tasks templates and installed command files, for teams that treat materialized templates as reviewed artifacts."
author: "github"
repository: "https://github.com/github/spec-kit"
license: "MIT"

requires:
# Requires the runtime-resolution baseline (#3790, shipped in 0.14.4) where the
# core /constitution command no longer propagates. Installing this preset on an
# older core would double-apply propagation.
speckit_version: ">=0.14.4"

provides:
templates:
- type: "command"
name: "speckit.constitution"
file: "commands/speckit.constitution.md"
description: "Wrap /constitution to also propagate guidance into dependent templates and command files"
strategy: "wrap"

tags:
- "constitution"
- "governance"
- "templates"
- "compatibility"
56 changes: 56 additions & 0 deletions tests/test_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12493,3 +12493,59 @@ def test_resolve_renders_composition_strategy_labels(self, temp_dir, project_dir
assert "Composition chain" in output, output
assert "[base]" in output, output
assert "[append]" in output, output


class TestConstitutionSyncPreset:
"""The bundled opt-in ``constitution-sync`` preset re-adds propagation.

Follow-up to #3790: core ``/constitution`` no longer propagates guidance
into templates. This preset restores that behavior for teams that treat
materialized templates as reviewed artifacts, delivered as a ``wrap`` of
the core command so it stays forward-compatible with core changes.
"""

PRESET_DIR = Path(__file__).parent.parent / "presets" / "constitution-sync"

def test_manifest_provides_wrap_of_constitution(self):
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
assert manifest["preset"]["id"] == "constitution-sync"
entries = manifest["provides"]["templates"]
assert len(entries) == 1
entry = entries[0]
assert entry["type"] == "command"
assert entry["name"] == "speckit.constitution"
assert entry["strategy"] == "wrap"
# Must target the post-#3790 baseline so propagation is not double-applied.
assert manifest["requires"]["speckit_version"] == ">=0.14.4"

def test_wrapper_uses_core_template_and_propagates(self):
text = (self.PRESET_DIR / "commands" / "speckit.constitution.md").read_text()
assert "strategy: wrap" in text
assert "{CORE_TEMPLATE}" in text
# The three governed scaffolds the old checklist propagated into.
assert "plan-template.md" in text
assert "spec-template.md" in text
assert "tasks-template.md" in text
# Must not mutate versioned preset/extension artifacts.
assert "Do not edit versioned preset- or extension-provided template files" in text

def test_catalog_lists_bundled_preset(self):
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
catalog = json.loads((self.PRESET_DIR.parent / "catalog.json").read_text())
entry = catalog["presets"]["constitution-sync"]
assert entry["bundled"] is True
assert entry["version"] == manifest["preset"]["version"]
assert entry["provides"]["commands"] == 1
assert entry["provides"]["templates"] == 0

def test_wrap_composes_over_core_constitution(self, project_dir):
"""Installing the preset yields a wrap layer atop the bundled core."""
manager = PresetManager(project_dir)
manager.install_from_directory(self.PRESET_DIR, "0.15.0")

resolver = PresetResolver(project_dir)
layers = resolver.collect_all_layers("speckit.constitution", "command")
assert len(layers) >= 2, "expected preset wrap layer plus a core base"
assert layers[0]["strategy"] == "wrap"
assert any("constitution-sync" in str(layer["path"]) for layer in layers)
assert layers[-1]["source"] == "core (bundled)"