Skip to content

fix(loader): reject non-ttl cache forms on time vars at load, ttl-only by type (cje)#100

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

fix(loader): reject non-ttl cache forms on time vars at load, ttl-only by type (cje)#100
brandon-fryslie merged 1 commit into
mainfrom
brandon-config-validation-cje

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Contributor

Summary

Sheriff finding #5 (law audit 2026-06-09), ticket brandon-config-validation-cje[LAW:no-silent-failure].

A time variable declaring cache: watch_file / depends_on / key / never passed the loader, then src/dsl/render.ts bypassed toCachePolicy and silently treated the declaration as the default 1s TTL — the config's stated meaning replaced with a different one, no diagnostic, plus an orphaned 'follow-up ticket' comment promising remediation.

Remediation (locked decision: reject at load, the zk5 parallel)

  • TimeVarDecl.cache narrows to TtlCacheDecl (src/config/dsl-types.ts) — the ttl arm named once, composed into CacheDecl; past the load boundary a non-ttl cache on a time var is unrepresentable [LAW:types-are-the-program].
  • ttlOnlyCacheSpec() (src/config/loader/cache.ts) — a one-arm OneOfPresentSchema<TtlCacheDecl> sharing CACHE_SCHEMA's own ttl arm (subset of the vocabulary, not a parallel grammar; PresentArmMap forces exactly the ttl arm at compile time). A non-ttl form is a load-time diagnostic naming ttl: Unknown time-variable cache key "watch_file". Expected exactly one of: ttl.
  • Both n8p facets update from the one declaration: the parse facet is the validator; the json facet narrows the published schema — schema/cc-candybar.schema.json regenerated, the time arm's cache anyOf collapses to ttl, so editors flag the form at authoring time.
  • Residue deleted: the render-time "ttl" in guard and the apologetic follow-up-ticket comment at src/dsl/render.ts — the TTL mapping is now total.

Verification

  • New loader tests: all four rejected forms produce the diagnostic; the shared ttl leaf still enforces the duration grammar; valid ttl parses.
  • pnpm typecheck, pnpm lint, pnpm check:schema, pnpm build all clean.
  • Targeted suites green: dsl-loader (100), config-schema, schema-validator, dsl-spine, default-dsl-config, var-sources (186).
  • Residue greps: 'follow-up ticket' gone from src/; the only remaining "ttl" in is toCachePolicy's full-vocabulary handling for file/shell.

…y by type (cje)

Sheriff finding #5 [LAW:no-silent-failure]: a time var declaring
cache: watch_file / depends_on / key / never passed the loader, then
renderDsl silently coerced it to the default 1s TTL — the config's
stated meaning replaced with no diagnostic.

TimeVarDecl.cache narrows to TtlCacheDecl, so the illegal form is
unrepresentable past the load boundary [LAW:types-are-the-program].
The loader's TIME_FIELDS uses a ttl-only OneOfPresentSchema sharing
CACHE_SCHEMA's own ttl arm — one declaration source feeding both the
validator and the emitted JSON schema (regenerated). The render-time
'ttl' in guard and the orphaned follow-up-ticket comment delete; the
TTL mapping is total.

Closes brandon-config-validation-cje.

@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 restricts time-variable cache declarations to the ttl arm only, rejecting all other CacheDecl variants (watch_file, depends_on, key, never) at load time with a clear diagnostic instead of silently coercing them to the default TTL at render time.

The change is well-structured and law-compliant:

  • [LAW:types-are-the-program]: TimeVarDecl.cache is narrowed from CacheDecl | undefined to TtlCacheDecl | undefined, making non-ttl cache forms unrepresentable past the loader boundary. The runtime declareOne case no longer needs a "ttl" in decl.cache guard — the type guarantees the shape.
  • [LAW:one-source-of-truth]: TtlCacheDecl is a single named interface that CacheDecl composes from and TimeVarDecl narrows to — not a parallel shape. TTL_ONLY_CACHE_SCHEMA reuses CACHE_SCHEMA.arms.ttl directly.
  • [LAW:no-silent-failure]: The old code silently treated non-ttl cache forms as "use default 1s TTL." Now the loader rejects them with a diagnostic naming ttl as the only supported key.
  • [LAW:single-enforcer]: The enforcement lives in the loader's ttlOnlyCacheSpec, not duplicated in the runtime.

Tests cover all four rejected arms, an invalid ttl value, and a valid ttl pass-through. The JSON schema is kept in sync. No must-change items.

✅ Approved

@brandon-fryslie brandon-fryslie merged commit abfcdd5 into main Jun 10, 2026
7 checks passed
@brandon-fryslie brandon-fryslie deleted the brandon-config-validation-cje branch June 10, 2026 08:55
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