Skip to content

[bug-fix] Fix preset-constitution-not-installed: use PresetResolver in constitution setup#3297

Draft
github-actions[bot] wants to merge 1 commit into
mainfrom
fix/3272-preset-constitution-not-installed-e6ce3ab03838f7ba
Draft

[bug-fix] Fix preset-constitution-not-installed: use PresetResolver in constitution setup#3297
github-actions[bot] wants to merge 1 commit into
mainfrom
fix/3272-preset-constitution-not-installed-e6ce3ab03838f7ba

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Bug fix — preset-constitution-not-installed

Proposed fix for issue #3272, applying the remediation from the bug assessment.

Verdict: Valid · Severity: High

Summary

ensure_constitution_from_template hardcoded a read from the generic core template path, bypassing the preset resolution stack entirely, so a preset's constitution-template (with strategy: replace) was never used when seeding .specify/memory/constitution.md. Additionally, for specify init --preset, the function ran before the preset was installed, so even a resolver-aware call would have missed the preset. This PR fixes both issues and adds a post-install re-seed hook for specify preset add on existing projects.

Changes

File Change Notes
src/specify_cli/commands/init.py modified ensure_constitution_from_template now resolves template via PresetResolver; call moved to after preset installation block
src/specify_cli/presets/__init__.py modified Added _maybe_reseed_constitution helper; called from install_from_directory post-install
tests/test_presets.py added tests TestConstitutionReseedOnPresetInstall and TestEnsureConstitutionFromTemplate classes

Tests Added or Updated

  • TestConstitutionReseedOnPresetInstall::test_install_reseeds_generic_constitution_memory — pins that install_from_directory re-seeds memory from preset when [PROJECT_NAME] is present
  • TestConstitutionReseedOnPresetInstall::test_install_reseeds_on_principle_placeholder — pins re-seed when [PRINCIPLE_1_NAME] is present even without [PROJECT_NAME]
  • TestConstitutionReseedOnPresetInstall::test_install_does_not_overwrite_authored_constitution — pins the safety guard against overwriting authored constitutions
  • TestConstitutionReseedOnPresetInstall::test_install_skips_reseed_when_no_constitution_template — pins no-op when preset has no constitution-template entry
  • TestConstitutionReseedOnPresetInstall::test_install_skips_reseed_when_memory_absent — pins that install_from_directory does not create the memory file
  • TestEnsureConstitutionFromTemplate::test_uses_preset_constitution_when_available — pins that the function picks the preset's template over the generic core
  • TestEnsureConstitutionFromTemplate::test_falls_back_to_core_template_without_preset — pins fallback to core template when no preset is installed
  • TestEnsureConstitutionFromTemplate::test_skips_when_memory_already_exists — pins the existing-file guard

Local Verification

  • Commands run: none — the test toolchain (pytest, uv) was unavailable in this CI environment (network blocked, no pre-installed venv). Correctness was verified by careful code inspection against the PresetResolver contract and the existing TestSelfTestPreset fixture patterns.
  • The logic mirrors the existing _register_commands / PresetResolver.resolve call patterns already exercised by the test suite.

Deviations from Assessment

None. The implementation follows the preferred three-part fix exactly:

  1. ensure_constitution_from_template uses PresetResolver.
  2. Init flow reordered so constitution seeding runs after preset installation.
  3. Guarded re-seed in install_from_directory (skips when memory is absent or has been authored).

The two NEEDS CLARIFICATION open questions were non-blocking:

  • Q1 (warn vs. silently skip for authored constitutions): implemented as silent skip, consistent with the assessment's own wording ("if and only if the existing memory file is still the unmodified generic placeholder").
  • Q2 (speckit.constitution preset-awareness): not in scope; the second assessment comment confirmed this is a separate concern.

Risks & Review Notes

  • The re-seed guard ([PROJECT_NAME] / [PRINCIPLE_1_NAME] detection) must cover both tokens since a partially-filled constitution might have the project name set but still carry [PRINCIPLE_1_NAME]. Both checks are independent (either triggers a re-seed).
  • Moving ensure_constitution_from_template after preset installation is a no-op for projects without --preset (the common case).
  • _maybe_reseed_constitution is best-effort: an OSError on the final shutil.copy2 is silently swallowed so it cannot fail the overall preset installation.

Refs #3272 · cc @PechiSW

Generated by 🛠️ Fix Bug from Labeled Issue for issue #3272 · 2K AIC · ⌖ 53.4 AIC · ⊞ 34K ·

…tion setup

Apply the remediation from the bug assessment on issue #3272.

Changes:
1. Modify ensure_constitution_from_template (init.py) to resolve the
   constitution-template through the preset priority stack via
   PresetResolver, instead of hardcoding the core template path. This
   ensures a preset's replacement constitution-template is used when
   seeding .specify/memory/constitution.md.

2. Reorder init flow: move ensure_constitution_from_template to after
   the preset installation block so that 'specify init --preset' seeds
   the memory file from the already-resolved template stack, not from
   the generic template that existed before the preset arrived.

3. Add _maybe_reseed_constitution to PresetManager (presets/__init__.py):
   a post-install hook that re-seeds .specify/memory/constitution.md
   from the preset's constitution-template during 'specify preset add'
   on an existing project, but only when the memory file still contains
   generic placeholder tokens ([PROJECT_NAME] or [PRINCIPLE_1_NAME]).
   Legitimately authored constitutions (no placeholder tokens) are never
   overwritten.

4. Add regression tests covering both code paths (TestConstitutionReseedOnPresetInstall
   and TestEnsureConstitutionFromTemplate in tests/test_presets.py).

Refs #3272

Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added automated bug-fix Trigger the bug-fix agentic workflow labels Jul 1, 2026
@BenBtg BenBtg self-assigned this Jul 6, 2026
@BenBtg BenBtg requested a review from Copilot July 13, 2026 15:07
@BenBtg BenBtg marked this pull request as ready for review July 13, 2026 15:10
@BenBtg BenBtg requested a review from mnriem as a code owner July 13, 2026 15:10
@BenBtg BenBtg marked this pull request as draft July 13, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes preset-aware constitution seeding during initialization and preset installation.

Changes:

  • Resolves constitution templates through PresetResolver.
  • Reorders initialization and adds guarded post-install reseeding.
  • Adds constitution seeding and preservation tests.
Show a summary per file
File Description
src/specify_cli/commands/init.py Adds preset-aware constitution resolution and reorders seeding.
src/specify_cli/presets/__init__.py Adds post-install constitution reseeding.
tests/test_presets.py Tests constitution resolution and overwrite guards.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 5
  • Review effort level: Medium

Comment on lines +53 to +55
template_constitution = _PresetResolver(project_path).resolve(
"constitution-template", "template"
)
Comment on lines +1658 to +1659
resolver = PresetResolver(self.project_root)
resolved = resolver.resolve("constitution-template", "template")
Comment on lines +1649 to +1652
try:
content = memory_constitution.read_text(encoding="utf-8")
except OSError:
return
return

try:
shutil.copy2(resolved, memory_constitution)
continuing="Continuing without the optional preset.",
)

ensure_constitution_from_template(project_path, tracker=tracker)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated bug-fix Trigger the bug-fix agentic workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preset constitution-template (strategy: replace) is not installed, and speckit.constitution is not preset-aware

3 participants