fix(presets): seed constitution from preset constitution-template (#3272)#3276
fix(presets): seed constitution from preset constitution-template (#3272)#3276BenBtg wants to merge 6 commits into
Conversation
|
+1 — we hit this independently. We maintain a spec-kit harness for a team with a fixed, ratified constitution authored upstream in a canonical repo and installed verbatim downstream. We read #3276's diff against our use case, and it handles our edge cases correctly:
One small maintainability suggestion: Our current stopgap is (Disclosure, per CONTRIBUTING: this comment was drafted with the assistance of Claude Code (an AI coding agent) and reviewed by a human before posting.) |
|
Addressed review feedback in f954e30.\n\n- Fixed constitution seeding to handle composing strategies (//) in both paths ( and ): we now materialize composed content via whenever the winning layer is non-.\n- Added regression tests that verify wrap composition is materialized (no unresolved ) in both seeding paths.\n- Added a drift-guard test that pins to placeholders present in the core , per gglachant’s maintainability suggestion.\n\nPosted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.3-codex). |
|
Correction to my previous comment (shell quoting ate formatting). Addressed review feedback in f954e30.
Posted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.3-codex). |
|
Addressed the latest Copilot feedback in aa09272.
Posted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.3-codex). |
) The constitution is the only template materialized to a live file (.specify/memory/constitution.md) rather than resolved on demand, yet ensure_constitution_from_template hardcoded a copy from the core template and ignored PresetResolver. Combined with init seeding the constitution before preset installation, a preset's constitution-template (e.g. strategy: replace with a ratified constitution) could never go live. Changes: - ensure_constitution_from_template now resolves constitution-template through PresetResolver, so a preset/override/extension wins and core is the fallback. - init seeds the constitution after preset installation so init --preset uses the resolved stack. - install_from_directory re-seeds memory/constitution.md from the resolved preset template, guarded to only act when the memory file is missing or still contains generic placeholder tokens — authored constitutions are never overwritten. Covers preset add and install_from_zip. - Tests for preset seeding, placeholder re-seed, authored-constitution preservation, override resolution, and resolver-aware init seeding. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Take on review feedback from Copilot and gglachant:
- constitution seeding previously copied the top layer file path verbatim
even when the winning layer used a composing strategy
(prepend/append/wrap), which could leave {CORE_TEMPLATE} unresolved.
- both seeding paths now inspect resolver layers and only copy verbatim for
replace; non-replace strategies materialize composed content via
PresetResolver.resolve_content().
- add regression tests for wrap strategy composition in both
PresetManager seeding and ensure_constitution_from_template.
- add a drift-guard test pinning _CONSTITUTION_PLACEHOLDER_TOKENS to the
placeholders in templates/constitution-template.md.
Assisted-by: GitHub Copilot (model: GPT-5.3-Codex, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Address latest Copilot feedback on the constitution seeding path: - moved resolver/layer I/O behind the existing-memory fast path in init - corrected tracker output for composed materialization - deduplicated materialization logic shared by init and preset install seeding into presets._materialize_constitution_template() Behavior is unchanged for replace strategies (copy verbatim) and remains composed for prepend/append/wrap via resolve_content(). Assisted-by: GitHub Copilot (model: GPT-5.3-Codex, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The constitution materialization refactor removed the module import, but init still uses shutil.rmtree when cleaning up a failed new-project initialization. Restore the import so the required ruff check passes. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
aa09272 to
e03fb69
Compare
|
Resynced this PR with current
Posted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.6-sol). |
Address the outstanding review batch for preset constitution seeding: - use checked atomic writes and reject symlinked memory paths - replace placeholder heuristics with hash/source provenance - rematerialize unchanged generated constitutions by resolver priority - preserve authored or edited constitutions, including placeholder mentions - warn non-fatally when post-install materialization cannot complete - retain exact core-template comparison for legacy projects without provenance Add focused provenance, priority, symlink, and failure-path coverage, and update integration inventories for the generated provenance sidecar. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b2c095d-b45c-4d52-8d56-bd6121d96ab6
|
Addressed the outstanding review batch in commit 116ab2c.
Validation: ruff passed; 343 focused preset tests passed; full suite passed with 4,045 tests and 4 skips. Posted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.6-sol). |
When the removed preset supplied constitution-template, rematerialize the winning remaining resolver layer only if provenance proves the live file is still generated and unchanged. Preserve edited constitutions and report post-removal reconciliation failures as non-fatal warnings. Add coverage for restoring the core layer, falling back from a removed higher-priority preset, and preserving edited generated content. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b2c095d-b45c-4d52-8d56-bd6121d96ab6
|
Addressed the latest review round in commit 8685bcc.
Validation: ruff passed; 344 focused preset tests passed; full suite passed with 4,046 tests and 4 skips. Posted on behalf of @BenBtg by GitHub Copilot (model: gpt-5.6-sol). |
| # Older projects have no provenance sidecar. Only the exact known core | ||
| # template is safe to treat as generated; placeholder substrings are not. | ||
| for layer in layers: | ||
| if layer["source"].startswith("core") and layer["path"].read_bytes() == content: |
| # Older projects have no provenance sidecar. Only the exact known core | ||
| # template is safe to treat as generated; placeholder substrings are not. | ||
| for layer in layers: | ||
| if layer["source"].startswith("core") and layer["path"].read_bytes() == content: | ||
| return True |
| if ( | ||
| tmpl.get("type") == "template" | ||
| and tmpl.get("name") == "constitution-template" | ||
| ): | ||
| removed_constitution = True |
Summary
Fixes #3272. Preset-provided
constitution-templateentries are now materialized to.specify/memory/constitution.mdthrough the same resolver priority stack used by other templates: project overrides -> installed presets -> extensions -> core.Initialization seeds the constitution after preset installation, allowing
specify init --presetto select the preset-provided template while retaining the core fallback.Changes
ensure_constitution_from_templatematerializes the resolvedconstitution-templateafter preset installation and preserves an existing constitution.constitution-template. Installing a higher-priority preset or removing the winning preset rematerializes an unchanged generated constitution from the new winning layer.replacecopies the winning template bytes verbatim;prepend,append, andwrapusePresetResolver.resolve_content()to materialize the composed result.Key guard
Each generated constitution receives
.specify/memory/.constitution-template.json, recording its SHA-256 and winning source. A later preset installation or removal may overwrite the live constitution only when its current bytes still match the recorded SHA-256.For legacy projects without a provenance sidecar, overwrite is permitted only when the live file exactly matches a known core
constitution-template. Authored or edited constitutions are preserved, including files that merely contain[PROJECT_NAME]or other placeholder text.Validation
ruff check src/ tests/test_presets.pypytest tests/test_presets.py -q- 344 passedpytest tests/ -q -x- 4046 passed, 4 skippedThis PR was authored autonomously by GitHub Copilot. Each commit carries an
Assisted-by:trailer.