Recover global rules clobbered by CLAUDE.md regeneration (#240)#241
Merged
Conversation
The bug. lib/engines.js#_generateClaudeMd regenerates CLAUDE.md from the DB-stored global rules on every session launch / engine PATCH / startup sync, overwriting the on-disk file in place. PR-driven raw- file edits to CLAUDE.md are silently discarded the next time TC restarts — the content lives on in git log but is functionally absent from the live ruleset. Affected this session. Two PRs: - #234: versioning bump-level decision table (Releases & Versioning section). - #236: stale-plan archive rule + gh issue view <N> --json state issue-state check (Build Plans & Chunks section). Both were clobbered after the first TC restart following each merge. Recovery already performed via store.globalRules.save() in the restart-bug investigation session. The regenerated CLAUDE.md is now byte-for-byte identical to HEAD (diff is empty), so the next TC restart produces no surprise drift. This commit adds: - A defensive comment block to lib/engines.js#_generateClaudeMd documenting the regeneration contract and pointing future readers (human or automated agent) at the three durable edit paths: landing-page editor, PUT /api/rules/global (10 KB body cap), or store.globalRules.save() from a node script. The same regeneration pattern applies to Codex / Aider / Gemini config files (cross- referenced in the comment). - A CHANGELOG ### Fixed entry summarizing the recovery + linking to #240 for the public-facing diagnostic and recovery procedure for other cloners. The structural fix (preventing the trap from ever firing again) is tracked separately in #240 — likely either a tracked-file canonical source with startup sync, or an append-only marker pattern that regeneration preserves. Design call belongs to whoever picks up the structural work. Refs #240
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Refs #240. Recovers two PR-driven
CLAUDE.mdrule additions that were silently clobbered by TC'sCLAUDE.mdregeneration, then adds a defensive comment tolib/engines.js#_generateClaudeMdso future contributors (human or AI) don't fall into the same trap.The actual recovery happened in-session via a direct
store.globalRules.save()call (the merged content exceeded the 10 KBPUT /api/rules/globalbody limit). This PR ships the artifacts: a CHANGELOG### Fixedentry, an in-code warning, and the link back to #240 for the public diagnostic.Why
lib/engines.js#_generateClaudeMdis the sole authority forCLAUDE.mdcontent. It runs on every session launch (launchSession), engine/methodology PATCH (updateProject), and startup sync (syncAllProjects). The file is overwritten in place — there is no merge with on-disk edits. So:CLAUDE.mdvia the landing-page editor orPUT /api/rules/global→ content lands instore.globalRules(the DB-stored ruleset) → next regeneration includes it → survives.CLAUDE.mddirectly viagit(PR-driven raw-file edit) → content sits in the file → next TC restart regenerates fromstore.globalRules(which does NOT include the PR addition) → file is overwritten → silently lost.This session shipped two PRs whose rule additions were lost this way:
Rule: TC's version-bump wrap step picks the bump level from [Unreleased] content(+ the bump-level decision table + the### Internalsubsection guidance). Functional impact: code shipped fine (the new### Internalhandling inversion-bump.jsworks), only the documentation of the rule was lost from CLAUDE.md.Rule: Archive plans whose chunk has shipped.(+ thegh issue view <N> --json state -q .stateissue-state check sub-rule). Functional impact: this was pure documentation — a process rule for future sessions. The rule is meaningless if it's not in CLAUDE.md, so losing it means sessions starting up would not see the plan-archive rule and could walk back into the exact#137-trap that prompted the rule.The session that surfaced the bug was 2026-05-23 (the same one that shipped #234 and #236 + #207 Chunk 4 + #230 + #235). Recovery was done by:
store.globalRules.load().Build Plans & Chunksfor Stale-plan archive convention + issue-state check (Build Plans rule) #236,Releases & Versioningfor Add ### Internal subsection + tighten minor-bump trigger (#231) #234) preserving sibling content.store.globalRules.save(merged).engines.generateConfig('claude', projConfig, methTemplate)and confirming the output now includes both restored rules.diff CLAUDE.md /tmp/tc-regenerated-claude.mdreturned empty — regeneration now produces byte-for-byte HEAD content, no surprise drift on next restart.What's in this PR (diff)
lib/engines.js#_generateClaudeMd— new 23-line JSDoc preamble warning about the regeneration contract. Three durable edit paths listed (landing-page editor / API /store.globalRules.save()script). Cross-references to the sibling generators (_generateCodexYaml,_generateAiderConf,_generateGeminiMd) which have the same regeneration trap.CHANGELOG.md[Unreleased]— new### Fixedentry under the existing### Changedblock, summarizing the diagnosis + recovery + link to [bug] CLAUDE.md regeneration silently clobbers PR-driven rule edits #240.The recovery itself (the
store.globalRules.save(merged)call) is not in this PR diff — it modified~/.tangleclaw/global-rules.mddirectly (TC's per-user state directory, outside this repo). Other cloners affected by the same bug will need to run their own recovery per the #240 procedure.Test plan
store.globalRules.save(merged),store.globalRules.load()returns the merged content (14012 bytes; 1-char trim from_normalizeRulesContent).engines.generateConfig('claude', projConfig, methTemplate)includes both restored rules verbatim (verified each substring: bump-level table, BREAKING marker row, Internal subsection explanation, "would an operator notice" heuristic, archive-plans rule, issue-state check sub-rule).diff CLAUDE.md /tmp/tc-regenerated-claude.mdis empty — regeneration now produces HEAD content exactly.grep -E "^## \[" CHANGELOG.md | head -5confirms[Unreleased]and[3.18.0]headings intact post-edit.No unit tests added — the recovery is a one-shot data fix, and the in-code warning is documentation. Tests for the structural fix belong with the structural fix in a future PR per #240.
Out of scope (tracked in #240)
The structural fix that would prevent this trap from ever firing again. Four design options surfaced in #240:
data/default-global-rules.mdor similar, TC syncs file → DB on startup).<!-- @user-additions -->marker on regeneration).CLAUDE.mdand points the contributor at the API.Recommendation pending. Belongs to whoever picks up the structural work.
Refs #240