Skip to content

Recover global rules clobbered by CLAUDE.md regeneration (#240)#241

Merged
Jason-Vaughan merged 1 commit into
mainfrom
chore/recover-lost-global-rules-240
May 24, 2026
Merged

Recover global rules clobbered by CLAUDE.md regeneration (#240)#241
Jason-Vaughan merged 1 commit into
mainfrom
chore/recover-lost-global-rules-240

Conversation

@Jason-Vaughan
Copy link
Copy Markdown
Owner

What

Refs #240. Recovers two PR-driven CLAUDE.md rule additions that were silently clobbered by TC's CLAUDE.md regeneration, then adds a defensive comment to lib/engines.js#_generateClaudeMd so 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 KB PUT /api/rules/global body limit). This PR ships the artifacts: a CHANGELOG ### Fixed entry, an in-code warning, and the link back to #240 for the public diagnostic.

Why

lib/engines.js#_generateClaudeMd is the sole authority for CLAUDE.md content. 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:

  • Edit CLAUDE.md via the landing-page editor or PUT /api/rules/global → content lands in store.globalRules (the DB-stored ruleset) → next regeneration includes it → survives.
  • Edit CLAUDE.md directly via git (PR-driven raw-file edit) → content sits in the file → next TC restart regenerates from store.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:

  • Add ### Internal subsection + tighten minor-bump trigger (#231) #234Rule: TC's version-bump wrap step picks the bump level from [Unreleased] content (+ the bump-level decision table + the ### Internal subsection guidance). Functional impact: code shipped fine (the new ### Internal handling in version-bump.js works), only the documentation of the rule was lost from CLAUDE.md.
  • Stale-plan archive convention + issue-state check (Build Plans rule) #236Rule: Archive plans whose chunk has shipped. (+ the gh issue view <N> --json state -q .state issue-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:

  1. Read live DB content via store.globalRules.load().
  2. Splice the two missing rule blocks back into their respective sections (Build Plans & Chunks for Stale-plan archive convention + issue-state check (Build Plans rule) #236, Releases & Versioning for Add ### Internal subsection + tighten minor-bump trigger (#231) #234) preserving sibling content.
  3. Write back via store.globalRules.save(merged).
  4. Verify by calling engines.generateConfig('claude', projConfig, methTemplate) and confirming the output now includes both restored rules.
  5. diff CLAUDE.md /tmp/tc-regenerated-claude.md returned 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 ### Fixed entry under the existing ### Changed block, 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.md directly (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

  • After 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.md is empty — regeneration now produces HEAD content exactly.
  • grep -E "^## \[" CHANGELOG.md | head -5 confirms [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:

  • (A) Tracked-file canonical source with startup sync (PR edits land in data/default-global-rules.md or similar, TC syncs file → DB on startup).
  • (B) Append-only marker pattern (TC preserves content below a <!-- @user-additions --> marker on regeneration).
  • (C) Pre-commit / CI guard that blocks raw-file edits to CLAUDE.md and points the contributor at the API.
  • (D) Detect-and-warn on regeneration when on-disk content has non-trivial differences from the regenerated version.

Recommendation pending. Belongs to whoever picks up the structural work.

Refs #240

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
@Jason-Vaughan Jason-Vaughan added the bug Something isn't working label May 24, 2026
@Jason-Vaughan Jason-Vaughan merged commit e48e3cf into main May 24, 2026
@Jason-Vaughan Jason-Vaughan deleted the chore/recover-lost-global-rules-240 branch May 24, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant