Problem Statement
constitution-template is the only template that isn't resolved on demand. It's materialized into .specify/memory/constitution.md at init and re-composed on every preset install that ships a constitution-template (_seed_constitution_from_preset → reconcile_constitution, src/specify_cli/presets/__init__.py:3470-3535). The code comment says so outright: "the constitution is the only template that is materialized to a live file rather than resolved on demand."
This asymmetry causes real pain when composing category presets. A team building an org "common constitution" as a bundle (common/security, dotnet, frontend, …), each contributing constitution content via append/wrap, finds that installing the bundle immediately regenerates constitution.md — composing every layer into a single 1200+ line file, including rules that don't apply. The workaround is a before_constitution hook that re-resolves and prunes on every /constitution run — high rework, and exactly the "materialized copies drift / composed content gets clobbered on recompose" anti-pattern that presets/constitution-sync/README.md warns about.
Structurally there's no reason for the split: spec-template → spec.md and constitution-template → constitution.md are the same shape (placeholder scaffold → filled, stateful artifact). /specify resolves spec-template through the priority stack at command time; /constitution uniquely does not.
Proposed Solution
Make runtime resolution the default and treat materialization as the opt-in, reusing the existing constitution-sync dimension rather than adding a new field:
- Core default — drop install-time re-seeding. Installing/removing a preset no longer regenerates the live
constitution.md.
- Core default — resolve at command time.
/constitution resolves the composed constitution-template through the priority stack (overrides → presets → extensions → core), fills placeholders, and writes constitution.md — the same model /specify uses for spec-template.
- Core, always — one-time init seed. Init still creates
constitution.md once so plan/tasks/analyze have something to read from project start. (The command already half-does this: "if missing, copy the template first.")
- Gated behind
constitution-sync — install-time seeding. The re-seed-on-install code stays in core but only fires when constitution-sync is installed. That preset already represents the "materialize into reviewed artifacts" camp, so one opt-in = materialization mode (command-time propagation it already ships + install-time seeding), default = runtime resolution.
This removes the surprise regeneration, makes the before_constitution pruning hook unnecessary, and avoids a new orthogonal knob for a philosophy the repo already models.
Alternatives Considered
- Standalone
apply_on_install: false preset field (default true). Works, but adds a second knob for the same materialize-vs-resolve choice constitution-sync already encodes; preferred to fold it into that preset instead.
before_constitution pruning hook (current workaround). Re-resolves and deletes ~1200 lines every run — high rework, fights the composition stack.
- Provisioning-side selection (install only common + one stack preset per repo). Correct and orthogonal — it fixes which categories compose; this issue fixes when/how the template is resolved. Best done together.
- A preset owning install-time seeding directly. Not mechanically possible — presets have no hook that fires when a different preset is installed. Hence the "core code gated on
constitution-sync presence" design.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
All agents
Use Cases
- Org-wide constitution bundle. A frontend repo installs common + frontend, a dotnet repo installs common + dotnet;
/constitution composes exactly the applicable rules at command time — no giant file, no pruning hook.
- Adding a preset mid-project. Installing a new category preset no longer silently rewrites a generated
constitution.md; the change lands, reviewably, on the next /constitution run.
- Materialization camp unaffected. Teams that want frozen, reviewed artifacts install
constitution-sync and keep today's install-time seeding behavior.
Acceptance Criteria
Additional Context
- Precedent:
/constitution propagation was removed in the 0.14.4 baseline and preserved as the opt-in constitution-sync preset (requires: speckit_version >=0.14.4). This extends that same "runtime-resolution default + constitution-sync as the materialization escape hatch" model to install-time seeding.
- Backwards compatibility: no data-safety impact (author edits already skipped via provenance/SHA guard). The one observable change is timing — preset-provided constitution content applies on the next
/constitution run rather than at install. That timing change is the fix. Teams wanting the old timing install constitution-sync.
- Relevant code:
src/specify_cli/presets/__init__.py (_materialize_constitution_template, _seed_constitution_from_preset, reconcile_constitution, _constitution_is_generated); src/specify_cli/commands/init.py (ensure_constitution_from_template); templates/commands/constitution.md; presets/constitution-sync/.
Problem Statement
constitution-templateis the only template that isn't resolved on demand. It's materialized into.specify/memory/constitution.mdat init and re-composed on every preset install that ships aconstitution-template(_seed_constitution_from_preset→reconcile_constitution,src/specify_cli/presets/__init__.py:3470-3535). The code comment says so outright: "the constitution is the only template that is materialized to a live file rather than resolved on demand."This asymmetry causes real pain when composing category presets. A team building an org "common constitution" as a bundle (common/security, dotnet, frontend, …), each contributing constitution content via
append/wrap, finds that installing the bundle immediately regeneratesconstitution.md— composing every layer into a single 1200+ line file, including rules that don't apply. The workaround is abefore_constitutionhook that re-resolves and prunes on every/constitutionrun — high rework, and exactly the "materialized copies drift / composed content gets clobbered on recompose" anti-pattern thatpresets/constitution-sync/README.mdwarns about.Structurally there's no reason for the split:
spec-template → spec.mdandconstitution-template → constitution.mdare the same shape (placeholder scaffold → filled, stateful artifact)./specifyresolvesspec-templatethrough the priority stack at command time;/constitutionuniquely does not.Proposed Solution
Make runtime resolution the default and treat materialization as the opt-in, reusing the existing
constitution-syncdimension rather than adding a new field:constitution.md./constitutionresolves the composedconstitution-templatethrough the priority stack (overrides → presets → extensions → core), fills placeholders, and writesconstitution.md— the same model/specifyuses forspec-template.constitution.mdonce soplan/tasks/analyzehave something to read from project start. (The command already half-does this: "if missing, copy the template first.")constitution-sync— install-time seeding. The re-seed-on-install code stays in core but only fires whenconstitution-syncis installed. That preset already represents the "materialize into reviewed artifacts" camp, so one opt-in = materialization mode (command-time propagation it already ships + install-time seeding), default = runtime resolution.This removes the surprise regeneration, makes the
before_constitutionpruning hook unnecessary, and avoids a new orthogonal knob for a philosophy the repo already models.Alternatives Considered
apply_on_install: falsepreset field (default true). Works, but adds a second knob for the same materialize-vs-resolve choiceconstitution-syncalready encodes; preferred to fold it into that preset instead.before_constitutionpruning hook (current workaround). Re-resolves and deletes ~1200 lines every run — high rework, fights the composition stack.constitution-syncpresence" design.Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
All agents
Use Cases
/constitutioncomposes exactly the applicable rules at command time — no giant file, no pruning hook.constitution.md; the change lands, reviewably, on the next/constitutionrun.constitution-syncand keep today's install-time seeding behavior.Acceptance Criteria
constitution-templatedoes not rewrite.specify/memory/constitution.md./constitutionresolves the composedconstitution-templatethrough the priority stack at command time.plan/tasks/analyzehave something to read from project start.constitution-syncinstalled: install-time re-seeding behaves as it does today._constitution_is_generatedguarantee preserved).constitution.mdbyte-for-byte after upgrade.presets/README.md,presets/ARCHITECTURE.md,presets/constitution-sync/README.md,templates/commands/constitution.md).Additional Context
/constitutionpropagation was removed in the0.14.4baseline and preserved as the opt-inconstitution-syncpreset (requires: speckit_version >=0.14.4). This extends that same "runtime-resolution default +constitution-syncas the materialization escape hatch" model to install-time seeding./constitutionrun rather than at install. That timing change is the fix. Teams wanting the old timing installconstitution-sync.src/specify_cli/presets/__init__.py(_materialize_constitution_template,_seed_constitution_from_preset,reconcile_constitution,_constitution_is_generated);src/specify_cli/commands/init.py(ensure_constitution_from_template);templates/commands/constitution.md;presets/constitution-sync/.