## [2026-05-28T07:42:15.441Z]
Initial split from cli/io/parsers. This sub-node owns parsers for project configuration — yg-config (reviewer tiers, providers), yg-secrets (api keys), and schema files. The split bounds the reviewer's working set: 3 files here, 4 in cli/io/parsers/graph, instead of all 7 in one node. Reviewer hallucination on silent-missing-files at the larger context size was the trigger.
## [2026-05-28T07:55:21.872Z]
Added parsing of the optional 'references' sub-mapping on tier entries in yg-config.yaml. This field carries per-tier source-file size limits (max_bytes_per_file and max_total_bytes_per_aspect). The parser validates that references is a YAML mapping, that all keys are from the allowed set, and that each numeric value is a positive integer. Unknown keys and non-positive values produce specific error codes (tier-references-unknown-key, tier-references-max-bytes-per-file-invalid, tier-references-max-total-bytes-invalid). The references key was added to the tier allowed-keys set to prevent false config-tier-unknown-key errors. Defaults (64 KiB per file / 256 KiB total) are applied at consumer sites, not in the parser — absent field or absent sub-key means no override.
## [2026-05-29T15:56:59.142Z]
The per-call CLI-provider timeout in yg-config.yaml was documented in seconds but the parser passed the raw number through as milliseconds, causing a 1000x discrepancy for any user who set the field. The parser now multiplies the configured value by 1000 before storing it, so the internal representation is always milliseconds while the user-facing unit matches the documented seconds. The default (no timeout configured) continues to yield undefined, leaving cli-base to apply its 120-second default unchanged. This is a behavior change: any existing config that set timeout expecting to pass raw milliseconds will now get a 1000x longer timeout.
## [2026-05-29T16:00:24.311Z]
Fixed inspectSecretsForValidation to throw on malformed YAML structure instead of silently returning an empty result. When the top-level YAML document is not a mapping (non-null, non-array object), the function now throws with the message 'yg-secrets.yaml: top level must be a YAML mapping'. When the reviewer field is present but not a mapping, it throws 'yg-secrets.yaml: "reviewer" must be a YAML mapping'. When the file is absent or the top-level is null/undefined, it continues to return an empty array (the file may legitimately not exist). This aligns the function with the parser-contract requirement that structural validation failures produce a thrown error with file path and field name rather than a silent empty result.
## [2026-05-30T02:29:44.534Z]
The per-node size guard changed from limiting the raw count of mapped source files to limiting the total number of characters in a node's reviewer context — its mapped source files plus the reference files of its effective aspects — and from a non-blocking warning to a blocking error. The motivation is that the reviewer concatenates all of a node's files into each aspect prompt, so an oversized node risks exceeding the model's context window, which deterministically rejects unchanged code near the truncation point; a file-count limit missed this because one very large file could stay under the count while still blowing the prompt. Binary files do not count toward the budget, and a node mapping a single unsplittable generated or binary artifact (a lockfile, an append-only changelog, an image) may carry a documented per-node exemption with a required justification.
## [2026-05-30T04:04:46.282Z]
Added validation that the per-node character budget is a positive integer. A zero, negative, or fractional value is now a clear configuration error rather than silently producing a size gate that would flag every node or no node.
## [2026-05-31T21:53:57.148Z]
Removed legacy-shape detection branches (isLegacyConfigFormat, isMixedConfigFormat) from config-parser.ts. The runtime parser now assumes the current 5.0 format — a reviewer mapping that lacks a tiers: key falls through to parseReviewer which emits config-tiers-missing, giving a clear actionable error without duplicating migration logic in the parser. The fail-closed version gate (Task 1) ensures the graph is already at 5.0 before any parser runs, making this safe.
## [2026-06-03T07:05:22.228Z]
Removed the dead context-length configuration field and the max-tokens reviewer setting from config parsing. These tuned a prompt-chunking and truncation path that no longer exists now that each node is reviewed whole, so keeping the knobs would imply a capability the reviewer no longer has.
## [2026-06-08T07:21:51.399Z]
Added optional coverage config block to the config parser. CoverageConfig type (required/excluded string arrays) was added to the graph type model. The parser now reads coverage: from yg-config.yaml and returns a CoverageConfig — defaulting to { required: ['/'], excluded: [] } (whole-repo required) when the key is absent, preserving existing behavior. A parseStringArray helper validates that list entries are strings and throws ConfigParseError with what/why/next on invalid input. This is a foundation change only: no runtime behavior changes in check or scanning — later tasks will consume the parsed coverage value.
## [2026-06-08T08:36:17.281Z]
Exported the DEFAULT_COVERAGE constant so it can be reused by check.ts instead of duplicating the literal. Previously the constant was module-private; exporting it eliminates the duplicate inline object '{ required: ["/"], excluded: [] }' that existed in check.ts. No behavior change.
## [2026-06-08T09:59:49.732Z]
Added two new validation rules to parseCoverage (Fix 2 and Fix 4): (1) an explicit empty coverage.required list now throws ConfigParseError with code 'config-invalid' — an empty required list silently turns every unmapped file into a non-blocking warning, effectively disabling coverage enforcement without any visible indication; (2) any root in required or excluded that contains a '..' path segment now throws ConfigParseError — '..' segments are never valid in repo-relative prefixes and silently mis-scope enforcement.
## [2026-06-08T14:21:59.565Z]
An explicit empty coverage.required list is now accepted and means require nothing: every uncovered file outside excluded or nested subtrees becomes a non-blocking advisory warning and nothing blocks. It was previously rejected as a config error. The change is intentional pure-advisory adoption and stays visible (uncovered files still surface as warnings), not a silent disabling of enforcement. The absent-block default remains whole-repo-required; only an explicitly written empty list opts into require nothing.
## [2026-06-08T19:22:44.093Z]
Two configuration fields needed stricter validation at parse time so misconfiguration fails loudly rather than degrading silently. The maximum-out-edges fan-out threshold must be a positive whole number; a zero, negative, or fractional value is meaningless as a count and is now rejected with a clear message, matching how the per-node character budget is already guarded.

The requirement that a reviewer tier name an explicit endpoint applies only to the generic OpenAI-compatible provider, which has no built-in host and would otherwise fall back to the public hosted endpoint — a silent and potentially surprising destination. Providers that ship a sensible default host, such as the local model runner that defaults to a loopback address, must not be forced to restate an endpoint; demanding one of them wrongly rejected valid single-tier setups. The endpoint requirement is therefore scoped to the one provider that genuinely lacks a safe default.
## [2026-06-12T14:17:31.287Z]
Added max_prompt_chars as a per-tier config field. It is a quality gate: a positive-integer character cap on the assembled reviewer prompt checked deterministically before the LLM call. It is excluded from tier identity hashing so tuning the cap never invalidates recorded baselines. Absent means unlimited.
