Skip to content

feat(config): strict, self-explanatory config.json parsing - #141

Open
Emasoft wants to merge 3 commits into
mlhher:mainfrom
Emasoft:feat/config-strict-parsing
Open

Emasoft wants to merge 3 commits into
mlhher:mainfrom
Emasoft:feat/config-strict-parsing

Conversation

@Emasoft

@Emasoft Emasoft commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

A typo in config.json used to buy you either silence or a pile of silently-applied defaults. Now every parse problem is named, positioned, and fatal — and booleans speak human.

FlexBool — the full on/off synonym table

Booleans accept true/on/enabled/enable/active/activated/yes/y/1 vs false/off/disabled/disable/no/not/n/0 — case-insensitive, whitespace-tolerant, with bare 1/0 numbers allowed. Anything else is an error that lists what's accepted.

Every config error is self-explanatory

Each error renders as error in <full path> at line L, column C: <detail> — for syntax errors, unknown keys (with a Levenshtein did-you-mean suggestion), wrong-typed values, invalid enum values, and invalid boolean synonyms alike.

Config errors are fatal

A broken config exits 1 before the TUI starts. No silent degradation to defaults while you believe everything's fine. A missing file still boots a fresh install, unchanged.

Parser hardening

  • trailing data (concatenated top-level JSON values) is rejected instead of silently dropped — a partially-applied config is worse than an error
  • UTF-8 BOM is tolerated: stripped, with offsets kept editor-visible
  • EOF errors anchor to the end of input
  • duplicate keys, CRLF column math, nested keys not flagged as unknown, and tri-state null handling are all pinned by tests

docs/config-reference.md

A full reference for this branch's config keys, enforced by a reflection guard test: every documented row must be a real Config json tag and every tag must be documented, so doc and struct can't drift apart.

Scope note: this PR covers the fields that exist on today's main. Sibling PRs extend both the schema and the doc as they add fields.

Testing

$ go test -count=1 -race ./...
ok  	late/cmd/late	3.780s
?  	late/cmd/mcp-run	[no test files]
ok  	late/internal/agent	2.194s
?  	late/internal/assets	[no test files]
ok  	late/internal/client	2.622s
ok  	late/internal/common	4.699s
ok  	late/internal/config	2.716s
ok  	late/internal/executor	9.329s
ok  	late/internal/git	2.348s
ok  	late/internal/mcp	2.945s
ok  	late/internal/orchestrator	2.669s
?  	late/internal/pathutil	[no test files]
ok  	late/internal/plugin	12.131s
ok  	late/internal/session	3.807s
ok  	late/internal/skill	3.926s
ok  	late/internal/tool	2.990s
ok  	late/internal/tool/ast	3.303s
ok  	late/internal/tui	8.322s

exit 0 — all 18 packages green (15 ok, 3 without test files).

FlexBool accepts the full on/off synonym table (true/on/enabled/enable/
active/activated/yes/y/1 vs false/off/disabled/disable/no/not/n/0,
case-insensitive, whitespace-tolerant, 1/0 numbers). Every config error
now renders 'error in <path> at line L, column C: <detail>' — syntax
errors, unknown keys (with Levenshtein 'did you mean' suggestions),
wrong-typed values, enum values, and boolean synonyms. Config errors
are fatal: late exits 1 before the TUI instead of silently degrading.
Missing file still boots a fresh install.
Adversarial review of the strict parser: concatenated top-level JSON
values were silently dropped (partially-applied config), a UTF-8 BOM
bricked startup with a confusing offset, and truncated input rendered
as bare EOF. All now positioned errors; BOM stripped with offsets kept
editor-visible. Pins: duplicate keys, CRLF columns, nested keys not
flagged, tri-state null handling, SaveConfig omission, upstream-era
config compat, flag-explicit-at-default precedence, -replay-shadow/
-check-compaction behavior with broken configs.
Adds docs/config-reference.md and the internal/config/docs_test.go guard,
scoped to this branch's Config schema (16 top-level keys): every documented
table row must be a real Config json tag, every tag must be documented, and
the precedence / boolean-synonyms / strict-parsing sections must stay
present. The guard reflects over the same struct the strict parser's
unknown-key walk uses, so the doc cannot drift from what the parser accepts.
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