Skip to content

feat(core,cli): variable schema validation + lint rules (PR 3/4)#602

Merged
jrusso1020 merged 2 commits intomainfrom
feat/get-variables-validation
May 4, 2026
Merged

feat(core,cli): variable schema validation + lint rules (PR 3/4)#602
jrusso1020 merged 2 commits intomainfrom
feat/get-variables-validation

Conversation

@jrusso1020
Copy link
Copy Markdown
Collaborator

What

Adds two lint rules + render-time validation for the variable system shipped in PR #600 + PR #601. Authors get fast feedback when JSON is malformed, declarations are missing required fields, or --variables values don't match the declared schema.

This is PR 3 of a 4-PR stack — based on feat/get-variables-subcomp (PR #601). Will retarget to main after PRs 1 + 2 merge.

Why

The runtime today silently masks several classes of mistake:

  • A typo in data-variable-values JSON makes the parser return {} and the script reads stale declared defaults, with no visible signal.
  • A typo in a data-composition-variables declaration (missing id, wrong type) gets silently filtered out.
  • --variables '{"titel":"x"}' instead of "title" renders fine and produces the wrong output.

These are exactly the cases lint + schema validation are good at catching.

How

Lint rules (packages/core/src/lint/rules/composition.ts):

  • invalid_variable_values_json — host's data-variable-values must parse as a JSON object.
  • invalid_composition_variables_declaration — root <html>'s data-composition-variables must parse as an array of objects with id, type (one of string/number/color/boolean/enum), label, and default. Per-entry findings report which fields are missing or invalid.

Both rules read via a new readJsonAttr helper in lint/utils.ts. The existing readAttr's regex ["']([^"']+)["'] truncates JSON-in-attribute values at the first internal quote — data-variable-values='{"x":"y"}' would capture only {. The new helper alternates double-vs-single-quoted branches with quote-specific char classes. A second helper findHtmlTag returns the <html> open tag (where data-composition-variables lives), distinct from findRootTag which returns the first in-body composition element.

Render-time validation (packages/core/src/runtime/validateVariables.ts):

  • validateVariables(values, declarations) returns a structured array of VariableValidationIssues — undeclared, type-mismatch, or enum-out-of-range. Pure / sync; works in any environment.
  • formatVariableValidationIssue renders one-line user-facing strings for CLI output.
  • Both exported from @hyperframes/core.

CLI integration (packages/cli/src/commands/render.ts):

  • New --strict-variables flag. Default: print warnings, continue. Strict: print warnings, exit 1.
  • New validateVariablesAgainstProject(indexPath, values) helper reads the project's index.html, pulls the declared schema via extractCompositionMetadata, validates the CLI payload. Uses the existing ensureDOMParser polyfill (same pattern as compositions.ts).

Test plan

  • Unit tests added/updated
    • 11 validateVariables unit tests — happy path, undeclared keys, type mismatches (every type), enum range, multi-issue aggregation, formatter output.
    • 11 composition.test.ts cases — both lint rules: parse errors, shape errors, per-entry validation, unknown types, positive cases for valid declarations.
    • 5 render.test.ts casesvalidateVariablesAgainstProject: no-declarations, happy path, undeclared, type-mismatch, missing-file.
    • All existing tests green: core 646, cli 213.
  • Manual flow walkthrough
    • --variables '{"title":"x"}' against an index that declares title as string → no warnings.
    • --variables '{"count":"three"}' against count: number → warning printed, render continues.
    • Same with --strict-variables → exit 1 before render starts.
  • Documentation updated
    • docs/packages/cli.mdx — added --strict-variables flag row.

Backwards compatibility

Fully additive. Existing compositions emit zero new lint findings (the rules only fire on malformed JSON or invalid declarations, which the runtime would have silently dropped anyway). Existing hyperframes render invocations behave identically without the new flag.

🤖 Generated with Claude Code

@jrusso1020 jrusso1020 force-pushed the feat/get-variables-subcomp branch 2 times, most recently from 75f892b to 1da6f45 Compare May 4, 2026 19:42
@jrusso1020 jrusso1020 force-pushed the feat/get-variables-validation branch from a10b3f6 to 239c3c8 Compare May 4, 2026 19:42
@jrusso1020 jrusso1020 changed the base branch from feat/get-variables-subcomp to graphite-base/602 May 4, 2026 20:05
Two lint rules + render-time validation built on top of the existing
data-composition-variables schema.

Lint rules (packages/core/src/lint/rules/composition.ts):
- invalid_variable_values_json — host's data-variable-values must parse as
  a JSON object. Today the runtime swallows parse failures silently and
  falls back to declared defaults, masking typos.
- invalid_composition_variables_declaration — root <html>'s
  data-composition-variables must parse as an array of objects with
  `id` (string), `type` (one of string/number/color/boolean/enum), `label`
  (string), and `default`. Per-entry findings report which fields are
  missing or invalid.

Both rules read attributes via a new `readJsonAttr` helper in lint/utils.ts.
The existing `readAttr` regex `["']([^"']+)["']` truncates JSON-in-attribute
values at the first internal quote (e.g. `data-variable-values='{"x":"y"}'`
captures only `{`); `readJsonAttr` alternates double-vs-single-quoted
branches with quote-specific char classes so JSON values round-trip cleanly.
A second helper `findHtmlTag` returns the actual <html> open tag (where
data-composition-variables lives) — distinct from `findRootTag` which
returns the first in-body composition element.

Render-time validation (packages/core/src/runtime/validateVariables.ts):
- validateVariables(values, declarations) returns a structured array of
  issues: undeclared keys, type mismatches, enum-out-of-range values.
  Pure / sync; works in any environment.
- formatVariableValidationIssue(issue) renders a one-line user-facing
  string for CLI output.
- Both exported from @hyperframes/core for studio/tooling reuse.

CLI integration (packages/cli/src/commands/render.ts):
- New --strict-variables flag. Default behavior: print warnings and
  continue. With --strict-variables: print warnings then exit 1.
- New `validateVariablesAgainstProject(indexPath, values)` helper:
  reads the project's index.html, runs extractCompositionMetadata to
  pull the declared schema, validates the CLI's --variables payload
  against it. ensureDOMParser polyfill for Node-side parsing (same
  pattern as compositions.ts).

Tests:
- 11 new validateVariables unit tests covering happy path, undeclared
  keys, type mismatches (string/number/boolean/color/enum), enum range,
  multiple-issue aggregation, and formatter output.
- 11 new composition.test.ts cases for both lint rules: parse errors,
  shape errors, per-entry validation, unknown types, missing fields,
  positive cases.
- 5 new render.test.ts cases for validateVariablesAgainstProject:
  no-declarations, happy path, undeclared, type-mismatch, missing-file.
- All 646 core tests + 213 cli tests still green.

Docs:
- docs/packages/cli.mdx — added --strict-variables flag row.

This is PR 3 of a 4-PR stack. PR 4 ships skill/scaffold distribution.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@jrusso1020 jrusso1020 force-pushed the feat/get-variables-validation branch from 239c3c8 to 366a64c Compare May 4, 2026 20:05
@graphite-app graphite-app Bot changed the base branch from graphite-base/602 to main May 4, 2026 20:06
- core.types.ts: export COMPOSITION_VARIABLE_TYPES, a runtime tuple of
  every CompositionVariableType variant guarded by `as const satisfies
  readonly CompositionVariableType[]`. Adding a new variant to the union
  without also adding it to the tuple becomes a compile error rather
  than silent drift in callers that maintain their own list.
- composition.ts (lint rule): the local
  `new Set(["string","number","color","boolean","enum"])` now derives
  from COMPOSITION_VARIABLE_TYPES instead of duplicating the list.
- index.ts: export COMPOSITION_VARIABLE_TYPES alongside the rest of the
  variable type guards.

Reuse + efficiency reviews otherwise clean. The other reuse finding
(loadProjectHtml helper to dedupe readFileSync + ensureDOMParser across
3 callers) is real but reaches files outside this PR's scope; it's a
better fit as a follow-up cleanup once the variable-feature stack lands.

All 48 composition lint tests + 49 core suite tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@jrusso1020 jrusso1020 force-pushed the feat/get-variables-validation branch from 366a64c to 09da5db Compare May 4, 2026 20:06
@jrusso1020 jrusso1020 merged commit 1c9d726 into main May 4, 2026
43 checks passed
Copy link
Copy Markdown
Collaborator Author

Merge activity

@jrusso1020 jrusso1020 deleted the feat/get-variables-validation branch May 4, 2026 20:25
jrusso1020 added a commit that referenced this pull request May 4, 2026
…603)

## What

Distribution PR for the variables feature stack. Tells agents how to declare, read, and override variables across the four authoring surfaces — the two skill files agents load (`hyperframes`, `hyperframes-cli`), the public docs (`docs/packages/core.mdx`), and the in-CLI docs (`npx hyperframes docs compositions`).

This is **PR 4 of 4**, the final PR in the stack. Stacked on `feat/get-variables-validation` (PR #602).

## Why

PRs 1–3 added the runtime helper, sub-comp scoping, and schema validation, but the only places that mention them are the docs in those PRs. Agents loading `/hyperframes` or `/hyperframes-cli` skills won't know the new attributes/flags exist. This PR closes the loop.

## How

- **`skills/hyperframes/SKILL.md`** — new "Variables (Parametrized Compositions)" section right after "Composition Structure" with: declare/read/override pattern, full worked example (with enum), sub-comp per-instance pattern (two hosts sharing a source), rules of thumb (defaults always, read-once, `--strict-variables` in CI, type validation behavior). Also added `data-variable-values` + `data-composition-variables` rows to the existing data-attributes tables.
- **`skills/hyperframes-cli/SKILL.md`** — added `--variables`, `--variables-file`, `--strict-variables` to the render flag table; short paragraph forwarding to the hyperframes skill for the full pattern.
- **`docs/packages/core.mdx`** — added a code snippet showing `getVariables<T>()` and `validateVariables` / `formatVariableValidationIssue` for tooling that validates CLI / host overrides.
- **`packages/cli/src/docs/compositions.md`** — replaced the obsolete `JSON.parse(host.dataset.variableValues)` example with the modern `getVariables()` pattern.

The `openai/plugins` mirror is intentionally out of scope. Skills in this repo are the source of truth; the downstream mirror is updated after each release as a separate workflow.

## Test plan

- [x] Doc-only PR — no source code, no tests.
- [x] Format check passes via `bunx oxfmt --check` on the touched markdown files.
- [x] Manual review confirms each example compiles in head against the runtime/CLI surface that PRs 1–3 ship.

## Backwards compatibility

Doc-only — no behavior change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

3 participants