Skip to content

Fix/afterheader tail truncation#144

Merged
2 commits merged into
mainfrom
fix/afterheader-tail-truncation
Jul 18, 2026
Merged

Fix/afterheader tail truncation#144
2 commits merged into
mainfrom
fix/afterheader-tail-truncation

Conversation

@josealekhine

Copy link
Copy Markdown
Member

No description provided.

insert.AfterHeader returned content[:insertPoint] + entry, truncating
the file at the insertion point and silently discarding the tail. Its
sole caller is beforeFirstEntry's fallback, taken when a knowledge file
has no `## [` entry — so `ctx learning add` / `ctx decision add` against
an entry-less LEARNINGS.md/DECISIONS.md destroyed any content below the
H1 header and its comment block. Same family as the clobber bug
index.Validate guards: silent loss of persisted memory, git-only
recovery.

Masked today because an entry-less file from `ctx init` has nothing
after the comment block (empty tail), so nothing was lost yet — but any
hand-written section below the preamble of an as-yet-entry-less file was
one add away from deletion.

Fix: preserve the tail (the pattern sibling Task already uses). Empty
tail returns the original bytes unchanged, so every file shape that
works today is byte-identical; only the losing shape changes. When a
tail follows, the entry is separated from it by a `---` rule.

The package had no tests. Added insert_test.go (tail-preserved,
empty-tail-unchanged, primary-path-unchanged) and a TestMain calling
lookup.Init() — without it desc.Text returns "" and strings.Index(s,"")
== 0 silently rewrites every insert anchor to offset 0, so tests would
pass exercising a path production never takes. Verified end-to-end
against the built binary: a Notes section below the preamble survives
`ctx learning add` and the entry lands above it.

Found by the layout proof for specs/progressive-disclosure.md; the fix
stands alone and ships alone.

Spec: specs/fix-afterheader-tail-truncation.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Two gotchas from fixing the AfterHeader truncation:
- desc.Text() returns "" in a test binary without lookup.Init(), and
  strings.Index(s, "") == 0 turns anchor-based inserts into offset-0
  matches — tests pass exercising a path production never takes.
- An insert helper returning content[:i]+x without content[i:] is
  append-with-truncation, not insert; a lone content[:i] is the smell.

Spec: specs/fix-afterheader-tail-truncation.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine self-assigned this Jul 18, 2026
@josealekhine josealekhine closed this pull request by merging all changes into main in 249935b Jul 18, 2026
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.

1 participant