Conversation
On a CRLF knowledge file (core.autocrlf=true on Windows), ParseEntryBlocks splits on LF, so every line keeps its trailing carriage return and the EntryHeader title group (.+) captures it. ParseHeaders has the same bug over the whole file: "." stops at LF, not at CR. `ctx disclosure inspect --json` printed titles such as "Foo (consolidated)\r", and a digest plan that names the clean title fails apply with ErrEntryNotInStaging. Trim the captured title at both read points, as conventionBlocks already does for convention sections. Block lines stay verbatim, so the disclosure mover's byte-exact cuts are unaffected. No writer or regex change. Also add the spec covering this and the two follow-up fixes (drift header check on CRLF files; malformed entry headers absorbed by the previous entry). Spec: specs/fix-knowledge-crlf-and-malformed-headers.md Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
checkTemplateHeaders compares extractFirstComment(template) with extractFirstComment(live) byte-for-byte; TrimSpace only trims the ends, not interior newlines. A CRLF context file (core.autocrlf=true on Windows) against the LF embedded template, or a Windows-built binary with CRLF-embedded templates against an LF file, mismatches on line endings alone. Every context file then warns "comment header ... does not match template", and `ctx init --reset` cannot clear it. Normalize CRLF to LF inside extractFirstComment, so both sides of the comparison are LF. The check stays content-sensitive: an edited header still warns. Spec: specs/fix-knowledge-crlf-and-malformed-headers.md Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
ParseEntryBlocks starts a block only at a line matching the strict
EntryHeader regex, so an entry whose header lacks the time part
("## [2026-09-26] Title", legacy or hand-written) is silently absorbed
into the previous entry's block. `ctx disclosure inspect` never lists
it, and `ctx disclosure apply` moves it, inside the previous entry's
span, into that entry's theme file. Validate only caught the case
where no staged entry parsed at all.
For the timestamped kinds, Validate now refuses any "## [" line
outside an HTML comment that is not a full EntryHeader, with a typed
MalformedEntryHeaderError naming the 1-based line and the heading and
telling the user to give it a full YYYY-MM-DD-HHMMSS timestamp. Its Is
matches ErrStagingUnparsable, of which it is the located form, so
existing callers and the "unparsable staging" case keep working. The
comment skip keeps DECISIONS.md's shipped "## [YYYY-MM-DD] Decision
Title" format example from tripping the guard. Conventions are
exempt: their sections carry no timestamp. The accepted header format
is unchanged.
Spec: specs/fix-knowledge-crlf-and-malformed-headers.md
Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
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.
fix: CRLF-robust knowledge parsing + refuse malformed entry headers
Three read-side bugs, all observed live on Windows, where
core.autocrlf=truechecks
.context/files out with CRLF line endings. The accepted entry-headerformat is unchanged; no writer changes.
Spec:
specs/fix-knowledge-crlf-and-malformed-headers.md(committed on-branch).What ships
A.
\rleaks into entry titles (internal/heading/entry.go,internal/heading/index.go)Content is split on
\n, so on CRLF filesregex.EntryHeader's(.+)titlecapture keeps the trailing
\r.ctx disclosure inspect --jsonprinted titleslike
"Foo (consolidated)\r", and anapplyplan written with clean titlesfailed with "entry that is not in the root's staging zone". Fix:
strings.TrimSpaceon the captured title at both read points; block lines are untouched.
B. False
stale_headerdrift warnings (internal/drift/check.go)checkTemplateHeaderscompared the first comment of the live file and theembedded template byte-for-byte, so a line-ending-only difference produced one
warning per knowledge file that
ctx init --resetcannot fix. Fix: normalizeCRLF to LF inside
extractFirstComment, which both sides go through.C. Date-only entry header silently absorbed — data integrity (
internal/disclosure)ParseEntryBlocksonly starts a block at a strict## [YYYY-MM-DD-HHMMSS] Titleheader, so a legacy/hand-written
## [2026-09-26] Titleentry becomes part ofthe previous entry's block:
disclosure applymoved it into the previousentry's theme file, and
inspectnever listed it. Fix: root validation (whichalready refuses structurally malformed roots so the pass never mutates) now
refuses a root containing a
## [heading that is not a full entry header, witha typed
MalformedEntryHeaderError{Line, Heading}(it also satisfieserrors.Is(err, ErrStagingUnparsable), so existing callers are unaffected).HTML comments are skipped, so the
## [YYYY-MM-DD] Decision Titleexample insideDECISIONS.md's
<!-- DECISION FORMATS -->template does not trigger;CONVENTIONS roots are exempt. Message:
On the same file upstream reports "Moved 1 entries" and writes the Legacy entry
into the neighbour's theme file; this branch exits 1 with the root byte-identical.
Verification
heading(CRLF titles for blocks and index),drift(the realLEARNINGS template in LF and CRLF, plus an edited header that must still warn),
disclosurevalidate (date-only header in LF and CRLF with exact line/heading,the real DECISIONS template comment, a convention title containing
## [).golangci-lintv2.13.2 (go1.27.1): 0 issues.go test ./...on Windows: identical failure-name roster before and afterevery commit (pre-existing Windows-only failures: CRLF checkout, file
permissions, raft cluster) — 0 new. The changed files add no LineLength /
NoMagicValues findings.
Non-goal
ctx disclosure inspectis documented as never failing, so on a malformed root itstill lists the well-formed staging entries and leaves the malformed one out; only
applyrefuses. Happy to follow up if you'd rather haveinspectsurface it too.🤖 Generated with Claude Code