Skip to content

install --project / claude install deletes CLAUDE.md content between a stray ## graphify match and the next heading (data loss) #1688

Closed

Description

@bdfinst

Summary

graphify install --project (and graphify claude install) can silently delete existing CLAUDE.md content. Its idempotent "update the graphify section" logic locates the literal header text ## graphify anywhere in the file and replaces everything from that match down to the next ## heading with the canonical section. When the string ## graphify appears anywhere other than the real trailing section — e.g. a bullet that references the section, or a prior heading with different casing/context — every line between that match and the next heading is destroyed.

This is data loss on a file users hand-curate, with no prompt and no backup.

Environment

  • graphify 0.9.6 (uv tool install graphifyy)
  • macOS (darwin), zsh; git repo

Minimal reproduction

CLAUDE.md before:

# My Project

## Setup
- See the `## graphify` section for graph usage.

## Release Process
Critical steps that must not be lost.

Run:

graphify install --project

CLAUDE.md after — the entire ## Setup body (the bullet) is gone, replaced by the graphify section spliced in at that location:

# My Project

## Setup

## graphify

This project has a knowledge graph at graphify-out/ ...
(rules...)

## Release Process
Critical steps that must not be lost.

The bullet under ## Setup was deleted. Anything between the stray ## graphify match and the next heading is lost. (Here ## Release Process survived only because it sits after the match.)

Real-world impact

In our repo this wiped ~46 lines of a curated CLAUDE.md — an entire "scripts must be Python" policy section, a "Releasing" section, testing instructions, etc. — because a Prerequisites bullet mentioned the `## graphify` section by name. The loss is easy to miss in a large diff and ships if not caught in review.

A corollary: a second install --project run is itself unsafe if any non-canonical ## graphify occurrence exists, so the operation isn't reliably idempotent.

Suggested fixes (any one)

  1. Delimit the managed region with explicit markers and only ever replace between them, e.g.:
    <!-- graphify:start -->
    ## graphify
    ...
    <!-- graphify:end -->
    
    Never match on the human-readable header text.
  2. If markers aren't desired, anchor strictly: only treat a match as the section start when the line is exactly ## graphify at column 0 (not a substring, not inside code spans/bullets), and prefer the last such occurrence.
  3. Append-only + de-dupe: if a canonical block is absent, append at EOF; if present, replace only the exact previously-written block (match on the full block, not the header).
  4. At minimum, write a .bak and warn when the edit removes any pre-existing line.

Happy to test a fix. Thanks for graphify!

Activity

  1. safishamsi commented on Jul 6, 2026

    @safishamsi
    Member

    Fixed on v8 (21b851b), shipping in the next release.

    Root cause: _replace_or_append_section located its managed block by substring (marker in content and next(... if marker in line)), so a heading that merely appeared as a substring of another line, or a duplicate heading, matched the wrong offset and the rewrite could truncate or drop unrelated content.

    It now matches the section heading exactly (line.strip() == marker), appends when the section is absent, and prefers the last exact match when several exist. Added regression tests covering the substring-collision, duplicate-heading, and append cases. Thanks for the clear repro.

  2. added a commit that references this issue on Jul 6, 2026
  3. added a commit that references this issue on Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions