Skip to content

fix(loader): reject bare own-segment var refs at load with namespaced suggestion (zk5)#95

Merged
brandon-fryslie merged 1 commit into
mainfrom
brandon-config-validation-zk5_namespaced-only
Jun 10, 2026
Merged

fix(loader): reject bare own-segment var refs at load with namespaced suggestion (zk5)#95
brandon-fryslie merged 1 commit into
mainfrom
brandon-config-validation-zk5_namespaced-only

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Contributor

Ticket

brandon-config-validation-zk5 — Sheriff finding #1 (law audit 2026-06-09), locked decision NAMESPACED-ONLY. [LAW:one-source-of-truth]

Problem

The cross-ref validator admitted a segment's own locals in bare form (segScope.add(vName)), with a comment claiming this 'matches runtime scope-proxy rules'. It doesn't: the runtime scope proxy resolves only keys literally present in the store, and segment locals are stored namespaced as segName.varName. A bare own-segment ref therefore passed validation and threw MissingFieldError at render — the validator certified configs that fail, and two comments asserted opposite facts about the same contract.

Fix

  • cross-ref.ts — one templateScope set, a faithful mirror of the runtime store's key set (globals bare, segment locals namespaced only), checks every template ref: segment template/bg/fg/when, segment-var templates and cache keys, global-var templates, and layout-node when. A ref's meaning is now a pure function of the ref string, never of which segment is rendering. The segment name survives purely as a diagnostic hint: a bare ref to an own local fails with Template references unknown variable ".local" (segment-local vars are namespaced — write ".s.local").
  • cycles.ts — deleted the segCtx bare→namespaced aliasing (a second copy of the rejected resolution rule) and its false matches-runtime comment.
  • render.ts / dsl-types.ts — corrected the comments that claimed bare-name access works or was a 'planned follow-up'; the locked decision forecloses that path.
  • allVarNames (bare + namespaced) survives for depends_on only — a literal name list, not a template ref; its own bare-name admission is filed as brandon-config-validation-095.

No working config breaks

A bare own-segment ref already failed at render unconditionally; this moves a guaranteed render-time failure to a load-time diagnostic that names the correct form. Neither the bundled default config nor any known user config declares segment-local vars.

Verification

  • pnpm typecheck, pnpm lint: clean
  • pnpm test: 66 suites, 1117 tests pass
  • Flipped the test that encoded the old behavior; added coverage for the suggestion message (own-segment field templates, segment-var templates) and an end-anchored assertion that no hint appears outside a segment context

… suggestion (zk5)

Sheriff finding #1, locked decision NAMESPACED-ONLY. [LAW:one-source-of-truth]

The cross-ref validator admitted a segment's own locals in bare form while
the runtime scope proxy resolves only literal store keys (segment locals are
stored namespaced as segName.varName) — so a bare own-segment ref validated
at load, then threw MissingFieldError at render.

- cross-ref: ONE templateScope mirroring the runtime store's key set
  (globals bare, segment locals namespaced) checks every template ref —
  segment fields, segment vars, global vars, layout-node when. The segment
  name survives only as a diagnostic hint: a bare ref to an own local is
  rejected with a message naming the namespaced form to write.
- cycles: drop the segCtx bare→namespaced aliasing (a second copy of the
  rejected resolution rule) and its false matches-runtime comment.
- render.ts / dsl-types.ts: correct the comments that claimed bare-name
  access works or was a planned follow-up.
- allVarNames (bare + namespaced) survives for depends_on only — a literal
  name list, not a template ref; its bare-name admission is a separate
  pre-existing contract (follow-up ticket).

No working config breaks: a bare own-segment ref already failed at render;
this moves the guaranteed failure to a load-time diagnostic.

@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

This PR tightens segment-local variable scoping so that bare-name refs (.local) are rejected at load time and only the namespaced form (.segName.local) is valid — making a variable reference's meaning a pure function of the reference string, independent of which segment is rendering. This is a genuine [LAW:one-source-of-truth] improvement.

No must-change issues found. Specifics:

  • templateScope is built once and used uniformly for all template ref validation — globals bare, segment locals namespaced — faithfully mirroring the runtime store's key set. Correct.
  • segCtx is purely a diagnostic hint that only affects error messages (suggesting the namespaced form for own-segment bare refs). It never gates resolution logic. Correct.
  • allVarNames is preserved for depends_on validation (bare names admitted there), clearly documented as a separate pre-existing contract. Correct.
  • Cycle detection (cycles.ts) is simplified correctly — removing segCtx since refs are now always literal store keys. Bare refs that slip through would simply be unresolved edges, but cross-rejection blocks them before cycle detection runs.
  • Tests cover the key cases: bare own-segment ref → hint, sibling var template → hint, global context → no hint, namespaced form → passes, cross-segment bare ref → rejected without hint, cross-segment namespaced → passes.

One pre-existing observation (not introduced by this diff): allVarNames in cross-ref.ts includes bare segment-local names (line 37: allVarNames.add(v)) for depends_on scope. This means depends_on: ["local"] inside segment s would match segment s's own var local — this is the "deliberately untouched" contract noted in the comment. Worth being aware of if a future PR further unifies the two scopes.

✅ Approved

@brandon-fryslie brandon-fryslie merged commit 5e654b5 into main Jun 10, 2026
7 of 8 checks passed
@brandon-fryslie brandon-fryslie deleted the brandon-config-validation-zk5_namespaced-only branch June 10, 2026 07:06
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