Skip to content

refactor(loader): schema-engine kernel + globals migration (n8p/3tm.1)#88

Merged
brandon-fryslie merged 1 commit into
mainfrom
brandon-loader-schema-kernel-n8p
Jun 9, 2026
Merged

refactor(loader): schema-engine kernel + globals migration (n8p/3tm.1)#88
brandon-fryslie merged 1 commit into
mainfrom
brandon-loader-schema-kernel-n8p

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Contributor

First child of epic n8pDeclarative loader schema + one combinator engine (the "ceiling" follow-up to 3tm's file-split floor).

What

Replace the hand-rolled validation style with a declarative schema interpreted by one combinator engine. This PR lands the kernel and proves it on the lowest-risk module.

  • validate-core.ts grows from a helper-bag into an interpreter:
    • FieldSpec<T> — the parser for one record field (reports issues, yields value-or-undefined, carries required).
    • FieldSpecMap<T> / RecordSchema<T> — the field map is checked against the target type (-? forces a spec per field; forgetting one is a compile error) [LAW:types-are-the-program].
    • record() — the interpreter: guard object → reject unknown keys → run each spec → collect present values, as one unconditional sequence [LAW:dataflow-not-control-flow]. Absorbs the per-type isPlainObject guard, the reject-unknown-key loop, the result-threading, and the optional-omission spreads.
  • globals.ts: 70-line loop-and-guard function → a 16-line GLOBALS_SCHEMA declaration + a 4-line wrapper.

Parity (the hard bar)

Every error message/path/line is the contract. The unknown-key noun ("globals key") is the one per-record message variation, carried as schema data; all other messages are reused from the existing field combinators.

  • dsl-loader + dsl-merge + config-resolution119 tests byte-green
  • config-schema + default-dsl-config + dsl-spine — 33 green
  • pnpm typecheck, pnpm lint

Scope discipline

The tag combinators (taggedUnion for variables, oneOfPresent for cache/actions), refine, and lazy land with their first consumers in later epic children, so nothing ships untested. The 8 non-offender modules (cross-ref, cycles, refs, …) stay as-is — their branches are load-bearing.

…ion (n8p/3tm.1)

Introduce the declarative-schema kernel in validate-core.ts: a FieldSpec
vocabulary and a record() interpreter that owns the isPlainObject guard,
the reject-unknown-key loop, result-threading, and optional-omission as ONE
unconditional sequence [LAW:dataflow-not-control-flow]. FieldSpecMap<T> checks
each schema against its target type — a missing field is a compile error
[LAW:types-are-the-program].

Prove it on the lowest-risk module: globals.ts drops from a 70-line loop-and-
guard function to a 16-line GLOBALS_SCHEMA declaration. The unknown-key "noun"
is the one per-record message variation, carried as schema data; every other
message is reused from the existing field combinators, so the 119-test parity
surface (dsl-loader/dsl-merge/config-resolution) stays byte-green.

First child of epic n8p (declarative loader schema + one combinator engine).
The tag combinators (taggedUnion / oneOfPresent), refine, and lazy land with
their first consumers (cache, variables, actions) so nothing ships untested.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Z.ai Coding Agent Review

Clean refactoring that replaces per-type validation loops with a data-driven schema engine. No must-change violations.

Notable pattern (pre-existing, documented): paletteSpec() (line 263) discards the field parameter and delegates to validatePaletteName, which hardcodes "palette" as the key to read from raw. The old code had the same coupling; the diff wraps it in the general FieldSpec interface and documents the convention in a comment. This is a latent [LAW:composability] concern — the type says "usable for any field" but it silently reads the wrong key if placed under a different name — but it's not introduced by this diff and is only used once, under the key "palette". If FieldSpec is later adopted more broadly, consider making validatePaletteName accept the field name as a parameter.

✅ Approved

@brandon-fryslie brandon-fryslie merged commit e7cf16a into main Jun 9, 2026
7 checks passed
@brandon-fryslie brandon-fryslie deleted the brandon-loader-schema-kernel-n8p branch June 9, 2026 09:45
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.

2 participants