Skip to content

[#966] Reseed worktree CLAUDE.md alongside AGENTS.md#993

Merged
realproject7 merged 1 commit into
mainfrom
task/966-reseed-claude-md
Jul 7, 2026
Merged

[#966] Reseed worktree CLAUDE.md alongside AGENTS.md#993
realproject7 merged 1 commit into
mainfrom
task/966-reseed-claude-md

Conversation

@realproject7

Copy link
Copy Markdown
Owner

Closes #966

Summary

Auto-reseed (server/routes.js _performReseedWrites) refreshed a project's worktree AGENTS.md on every template upgrade but never CLAUDE.md, so after a template update the two instruction files drifted (AGENTS.md = new, CLAUDE.md = old). This adds a CLAUDE.md refresh to the same per-target loop, merged through the existing reseedAgentsMd helper (fresh template wins per-H2, operator-added H2 sections preserved).

Core design constraint — never clobber a project's OWN hand-authored CLAUDE.md. Setup only wrote the template CLAUDE.md when a worktree had none, so a worktree's CLAUDE.md may be entirely project-authored. A new pure predicate _isSeededClaudeMd() refreshes a worktree CLAUDE.md only when it carries the QuadWork template's signature H2 set (## Multi-Agent System + ## GitHub Workflow + ## Communication Rules) — three distinctive headings stable across template versions that a project's own dev rules won't have. Anything else (foreign, empty, or missing) is left byte-identical and recorded in skipped.

Return shape unchanged: CLAUDE.md outcomes join the existing {reseeded, skipped, preserved} arrays/map (<agent>/CLAUDE.md keys). Both the manual reseed endpoint and the version-upgrade auto-reseed run through _performReseedWrites, so both now keep CLAUDE.md in sync.

EPIC Alignment

EPIC #967 — folded / isolated (per the batch brief). No sibling contracts to reconcile; this ticket stands alone. The change is confined to the reseed write path and does not alter EPIC-tracked surfaces.

Self-Verification

Adversarial diff re-read — the only production change is the CLAUDE.md block inside _performReseedWrites plus a pure predicate + its export. AGENTS.md handling above it is untouched.

Kill-list / safety invariants:

  • Do NOT change AGENTS.md reseed behavior — AGENTS.md write path is byte-for-byte unchanged; existing reseedAgentsMd / token-preservation / auto-reseed tests pass.
  • Never overwrite a non-QuadWork CLAUDE.md_isSeededClaudeMd fail-closed: empty/null/foreign → false → skipped, verified byte-identical in test.
  • No CLAUDE.md handled without error — missing file → skipped (<agent>/CLAUDE.md (none)), no file created.
  • Consumers unaffectedpreserved is echoed as-is in the JSON response and the auto-reseed log only reads reseeded.length / skipped.length; the frontend (ControlBar.tsx) reads only reseeded.length. Mixed preserved key format is display-only.

Build + test evidence:

  • npm test62 passed, 0 failed, 2 skipped (pre-existing Jest-style skips).
  • New server/routes.reseedClaudeMd.test.js: predicate unit cases + endpoint E2E (seeded refreshed & operator H2 preserved; foreign left byte-identical & skipped; missing handled without error; AGENTS.md still refreshed).
  • node server/pack-smoke.test.js → PASS.
  • node -e "require('./server/routes.js')" → loads OK.

Acceptance criteria (1:1):

  • Reads templates/CLAUDE.md, substitutes {{project_name}}, merges via reseedAgentsMd
  • Seeded CLAUDE.md refreshed + operator H2 preserved ✅ (test)
  • Foreign CLAUDE.md left byte-identical / skipped ✅ (test)
  • No CLAUDE.md handled without error ✅ (test)
  • Reports refreshed/skipped in {reseeded, skipped, preserved}

Deviations

  • Signature-detection over provenance-marker (both offered in the ticket). The signature approach recognizes CLAUDE.md files seeded by older templates — which is exactly the existing-upgraded-user population auto-reseed serves. A provenance marker would only match files seeded after it shipped, stranding every already-installed worktree; it would also require editing the setup seed paths (expanding scope and AGENTS-adjacent surface). No setup-path changes were needed.
  • The preserved-notes trailer emitted by the reused reseedAgentsMd reads <!-- Operator notes preserved from prior AGENTS.md (#845) --> even inside a merged CLAUDE.md. Cosmetic; left as-is because the ticket directs reuse of the same helper unchanged.

Auto-reseed refreshed worktree AGENTS.md on template upgrade but never
CLAUDE.md, so the two instruction files drifted. _performReseedWrites now
also refreshes CLAUDE.md via the same reseedAgentsMd merge (fresh template
wins per-H2, operator-added H2 sections preserved).

Guard: only a provably QuadWork-seeded CLAUDE.md is touched — one carrying
the signature H2 set (_isSeededClaudeMd). A project's own hand-authored
CLAUDE.md, or a missing one, is left byte-identical and recorded in skipped.
AGENTS.md reseed behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict: APPROVE

Epic Alignment: PASS

#966 requires CLAUDE.md reseed through existing per-H2 merge semantics while preserving AGENTS.md behavior and never overwriting foreign CLAUDE.md; PR #993 implements that in server/routes.js:4390 with coverage in server/routes.reseedClaudeMd.test.js:137.

Checked (evidence)

  • Structural gate: gh pr view 993 --json body → filled ## EPIC Alignment and ## Self-Verification; non-UI PR.
  • Issue/epic context: gh issue view 966 + gh api repos/realproject7/quadwork/issues/967 → standalone folded ticket under #967; reseed contract is reuse reseedAgentsMd, preserve operator H2s, do not change AGENTS.md behavior.
  • Diff scope: gh api repos/realproject7/quadwork/pulls/993/files → exactly server/routes.js (+58) and server/routes.reseedClaudeMd.test.js (+181).
  • Acceptance: seeded CLAUDE.md gate at server/routes.js:4160; reseed/skip/report path at server/routes.js:4395; endpoint tests cover seeded refresh, foreign byte-identical skip, missing skip, and AGENTS.md still refreshed at server/routes.reseedClaudeMd.test.js:157.
  • Riskiest part of this diff: the provenance heuristic could clobber foreign files if too broad; it requires all three stable template H2s and fails closed for empty/missing/foreign cases, with endpoint regression coverage.
  • Kill-list: scanned all items — clean.
  • CI: gh pr checks 993test pass 48s.

Findings

  • None.

Decision

Approving at 24160df828049189221b95bce8537434fc84227d; the implementation matches #966's safety constraints and keeps the change local to the reseed path plus focused tests.

@realproject7

Copy link
Copy Markdown
Owner Author

@re2 APPROVE at 24160df828049189221b95bce8537434fc84227d

Scope is exactly the two files claimed: server/routes.js (new predicate + CLAUDE.md block in the reseed loop + test export) and the new server/routes.reseedClaudeMd.test.js. AGENTS.md reseed behavior is unchanged — the CLAUDE.md handling is appended after the existing AGENTS.md write and touches none of it.

Checked (evidence)

  • Core no-clobber constraint holds. _isSeededClaudeMd (server/routes.js:4165) requires ALL of SEEDED_CLAUDE_SIGNATURE = Multi-Agent System + GitHub Workflow + Communication Rules. templates/CLAUDE.md carries all three (## Multi-Agent System, ## GitHub Workflow, ## Communication Rules), so a freshly-rendered template is recognized; this repo's own hand-authored CLAUDE.md (H2s: What is QuadWork, Tech Stack, Design Tokens, … — none match) and the test's FOREIGN_CLAUDE are not. Empty/null/read-fail → false (fail-closed).
  • Security invariant: the only write to CLAUDE.md (server/routes.js, fs.writeFileSync(claudeMd, mergedClaude.content)) sits inside the _isSeededClaudeMd(claudeExisting) truthy branch. Missing → skipped "(none)", foreign → skipped "(not QuadWork-seeded)", no template → skipped "(no template)". No unguarded path can overwrite a non-QuadWork CLAUDE.md.
  • Merge semantics correct. Reuses reseedAgentsMd (fresh template wins per-H2, non-fresh existing H2s preserved under trailer). {{project_name}} substituted with dirName before merge; refreshed CLAUDE.md reported in reseeded, preserved H2s under preserved["<agent>/CLAUDE.md"].
  • Parser only matches H2. _splitAgentsMdSections regex ^##\s+(.+?)\s*$ won't capture ###; heading comparison is case-insensitive + whitespace-normalized in both predicate and merger — consistent.
  • Tests, independently run at 24160df (fresh worktree, node server/routes.reseedClaudeMd.test.js): all assertions passed. Full reseed suite green: autoReseed, reseedAgentsMd, reseedTokenPreservation, resolveReseedTargets, reseedClaudeMd all PASS. CI gh pr checks 993test pass 48s. state OPEN, mergeable MERGEABLE, head unmoved.

Non-blocking nit (no change required): the preserved-notes trailer text is shared with AGENTS.md, so an operator H2 preserved inside a refreshed CLAUDE.md is labeled <!-- Operator notes preserved from prior AGENTS.md (#845) -->. Cosmetic only; behavior is correct.

Standing no-merge rule — operator merges.

@realproject7 realproject7 merged commit cb9963e into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reseed worktree CLAUDE.md alongside AGENTS.md so the two instruction files never drift

2 participants