## [2026-05-28T07:42:10.369Z]
Initial split from cli/io/parsers. This sub-node owns parsers for graph-shape YAML — architecture, aspects, nodes, flows. The parent node was a single parser-adapter mapping all 7 file-reading parsers, which exceeded the reviewer's reliable context limit at ~54 KB; the LLM hallucinated absent files when verifying silent-missing-files. Splitting the parsers into graph-shape and config groups keeps each child at 3-4 files, restoring reviewer accuracy. Architecture rule: parser-adapter has parents [module], so the converted parent (now type module) remains a valid parent.
## [2026-05-28T11:10:58.412Z]
Parse new 'status' field on yg-aspect.yaml. Accepts values 'draft', 'advisory', or 'enforced' — three-level enforcement model that lets the reviewer skip aspects still in draft and surface advisory ones as warnings rather than blocking errors. Field is optional; absent means the resolver applies the 'enforced' default. Invalid string values return a dedicated 'aspect-status-invalid' parser error pointing the agent at the three valid values and the knowledge topic for the feature. Early-return on validation failure follows the existing parser convention.
## [2026-05-28T11:23:25.029Z]
aspect-parser now passes 'implies-edge' mode to parseAspectAttachment and harvests parsed.statusInherit into a new impliesStatusInherit side-table on AspectDef. When the underlying parser throws specifically for invalid status_inherit value (message contains 'status_inherit must be one of'), aspect-parser translates the bare throw into a structured implies-status-inherit-invalid error tuple — keeping the caller-friendly {code, messageData} shape used elsewhere in this module. The string-match is precise (full phrase) to avoid misclassifying unrelated parse errors whose messages happen to mention status_inherit in an 'allowed:' list.
## [2026-05-28T11:30:31.043Z]
Node parser harvests per-attachment status overrides from object-form aspect entries. The aspect parsing loop already extracts id and when from the parsed attachment record; it now also reads the optional status field and accumulates a side table keyed by aspect id. The same harvesting runs inside the ports parser so per-port aspect attachments may also carry a status override. The side table is returned only when at least one attachment supplied a status, keeping the common bare-string form free of empty maps. This wires the channel-1 and channel-6 status overrides through to downstream effective-aspect resolution, which already consumes aspectStatus from NodeMeta and PortDef.
## [2026-05-28T11:37:00.570Z]
Architecture parser harvests per-attachment status overrides from object-form aspect entries on node_type definitions. The aspect attachment loop already extracts id and when from the parsed record; it now also reads the optional status field and accumulates a side table keyed by aspect id. The side table is attached to the constructed ArchitectureNodeType only when at least one attachment supplied a status, keeping the common bare-string form free of empty maps. This wires the channel-3 (architecture-default) status overrides through to downstream effective-aspect resolution, which already consumes aspectStatus from ArchitectureNodeType.
## [2026-05-28T11:42:34.594Z]
flow-parser now parses the per-attachment 'status:' field declared on flow aspect attachments and surfaces it on FlowDef.aspectStatus. This is part of the v5 aspect-status work: aspects can attach with one of three enforcement statuses (draft, advisory, enforced), and that override must travel from the YAML attachment site through every parser into the in-memory graph so the propagation engine and reviewer see it. Mirrors the same parsing pattern already applied to node-parser (channel 1) and architecture-parser (channel 3); this addition covers channel 5 (flow). The status field is optional — bare aspect strings continue to mean 'no override' and aspectStatus stays undefined.
## [2026-05-29T05:14:30.108Z]
Extend AspectReviewerSpec.type to accept 'structure' for structure-based aspects. Updated parser whitelist and type cast to recognize this new reviewer type.
## [2026-05-29T05:22:19.319Z]
Tighten parser error messaging to enumerate all three reviewer.type values consistently — the why-line already mentioned the full set, the next-line guidance only mentioned two. Avoids agent confusion when receiving the diagnostic.
## [2026-05-29T05:30:01.307Z]
Add cross-field guard aspect-references-on-structure: rejects references: on structure-type aspects. Structure aspects call check.mjs locally; references are only usable by LLM reviewers. Mirrors existing aspect-references-on-ast check at the same insertion site.
## [2026-05-29T05:35:19.984Z]
Add cross-field guard aspect-structure-tier-not-allowed: rejects tier: on structure-type aspects. Structure aspects run locally without an LLM and cannot use reviewer tiers, which configure LLM provider selection. Mirrors existing aspect-ast-tier-not-allowed check at the same insertion site in parseReviewer().
## [2026-05-29T05:38:45.936Z]
Add cross-field guard aspect-language-on-structure: rejects language: on structure-type aspects. Structure aspects are language-agnostic and call ctx.parseAst(file, lang) on demand; they do not declare a fixed language list. Placed before the references block to ensure capture of language-only configs.
## [2026-05-30T02:29:44.701Z]
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-30T14:09:01.731Z]
The per-aspect language declaration was removed from the system. An aspect that ships a parsed-tree check used to carry a `language:` list naming the languages it targeted, and the system validated that list with four dedicated checks. Nothing in the runtime ever read it: the engine already determines each source file's language from its file extension through one shared registry, then loads the matching grammar. The declaration was therefore inert metadata that could silently disagree with what the engine actually parsed — an authoritative-looking field that governed nothing.

This change deletes the field, the validators that policed its shape, and the documentation and tests that described it, and promotes the extension-to-grammar registry to the single authority for matching a file to its parser. The motivation is to remove a confusing mismatch between what an aspect claimed about languages and what the engine did, and to collapse the duplicated extension-knowledge that had drifted into more than one place.

The language a parsed-tree check sees for a given file is now derived solely from that file's extension; an unrecognized extension yields no parsed tree rather than a per-aspect error. The drift identity of a graph-aware structural check is deliberately held stable across this change so existing approvals are not invalidated by metadata that never affected behavior.
## [2026-05-30T18:08:10.104Z]
The vocabulary for how a rule is verified was reduced from three kinds to two. Previously a rule was checked by one of: a human-language reviewer, a single-file programmatic check, or a graph-aware programmatic check. The two programmatic kinds are now a single "deterministic" kind, leaving just deterministic-or-reviewer.

The motivation: the three-way split was drawn on the wrong axis. It described HOW a programmatic check reached its context (one file at a time, versus the whole graph), but the distinction that actually matters to a rule author and to cost is whether verification is local-and-free or requires the paid, non-deterministic reviewer. The single-file kind was already a strict subset of the graph-aware kind — every input the former could see, the latter also provides — so maintaining two of them forced rule authors to make a false choice up front and forced the engine to carry two parallel handling paths for one concept. Collapsing them removes that false choice and the duplicated handling, and routes every programmatic check through the one graph-aware path.

The language a programmatic check infers for a source file is determined solely from that file's extension, so a check no longer declares which languages it targets. A rule's verification kind being deterministic is also no longer carried as a separate synthetic identity signal — a deterministic rule's identity is fully covered by the files it already tracks — which keeps re-verification of such rules free.
## [2026-05-31T06:24:49.653Z]
The parsers for array fields that name nodes, types, or targets (flow participants, node-type parents, relation targets, consumed port names) previously dropped any non-string entry silently, so a malformed entry in a mixed array vanished without a signal — and for a flow that silently removed a participant from aspect enforcement. They now fail loud: any non-string entry is a blocking validation error naming the field, the offending value, and its position. The motivation is that a silently-dropped reference can disable enforcement or hide a typo; a malformed list must be reported, never quietly trimmed.
## [2026-05-31T16:03:34.795Z]
Replaced the hand-inlined path-separator normalization with calls to a single shared helper. The same small idiom — convert backslash separators to forward slashes, and in most places also strip a trailing slash — had been copied across many modules, so the normalization rule lived in dozens of places at once and any change to it risked drifting them out of step. Consolidating it behind one well-named helper means the rule lives in exactly one spot and each call site reads by intent instead of by a repeated regex. Behavior is unchanged: the helper bodies are byte-for-byte equivalent to the expressions they replace, and the full test suite passes identically.
## [2026-05-31T16:51:20.677Z]
Moved the pure when-predicate parsers (file-level and aspect-level, plus the shared boolean-clause helper they both use) out of the core layer and into the shared utilities layer. The file-reading parsers in the io layer need these helpers, but the layering rules forbid the io layer from depending on core/engine code; placing the helpers among the leaf utilities — which any layer may import — makes that dependency legal without weakening the rule. The helpers are pure: they transform already-parsed objects and do no file I/O, so they belong with the other shared utilities rather than with the engine. Importers and the relocated files internal paths were updated accordingly.
## [2026-05-31T21:54:02.356Z]
Removed the aspect-reviewer-legacy-string branch from aspect-parser.ts. A string-valued reviewer field now falls through to the existing aspect-reviewer-not-mapping branch. The 5.0 version gate (Task 1) guarantees all graphs are migrated before parsing, so the migration hint in the old branch was unreachable in normal operation. The migration in to-5.0.0.ts retains the string-form transformation for yg init --upgrade.
## [2026-06-03T07:05:22.402Z]
Aspect parsing now infers the reviewer kind from which rule source file is present rather than requiring an explicit declaration: content.md yields an LLM aspect, check.mjs a deterministic one, and neither-file-with-implies an aggregating aspect. An explicit reviewer.type, when supplied, must agree with the inferred kind.
## [2026-06-08T19:22:52.082Z]
Two parsing rules were tightened so the graph accepts exactly the shapes the model intends. A relation's consumed-ports declaration is conceptually a list of port names; a scalar or mapping in that position is malformed and previously slipped through to confuse downstream consumption logic. A non-list value there is now rejected at parse time with a clear message.

An aspect that only bundles other aspects, owning no reviewer rule of its own, has an inferred kind of aggregate. When an author additionally spells that kind out explicitly, the explicit declaration agrees with what was inferred and must be accepted; rejecting it forced authors to leave the kind implicit even when stating it plainly was clearer. The reviewer-kind parser now treats an explicit aggregate declaration as valid alongside the language-reviewer and deterministic kinds, provided it agrees with the inference.
## [2026-06-12T13:14:07.110Z]
Updated parseFileWhen call in architecture-parser to pass site: 'node-type-when', satisfying the new required parameter. This ensures that when a node-family atom is misused inside node_types.*.when, the error message identifies node_types.*.when rather than scope.files.
