Skip to content

feat(cli): hyperframes lambda render --variables / --variables-file / --strict-variables#966

Closed
jrusso1020 wants to merge 1 commit into
mainfrom
05-19-feat_cli_hyperframes_lambda_render_variables_flags
Closed

feat(cli): hyperframes lambda render --variables / --variables-file / --strict-variables#966
jrusso1020 wants to merge 1 commit into
mainfrom
05-19-feat_cli_hyperframes_lambda_render_variables_flags

Conversation

@jrusso1020
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 commented May 19, 2026

What

Phase 9 PR 9.3 of the distributed rendering plan — bring the local hyperframes render variables UX to hyperframes lambda render:

  • --variables '{"title":"Hello"}'
  • --variables-file ./vars.json
  • --strict-variables

Same flag names, same parse-error messages, same strict-mode behavior — users who learned the local flow can drive Lambda renders without re-learning the surface.

Why

PR 9.1 added variables to DistributedRenderConfig. PR 9.2 added SDK-level validation + the 256 KiB Step Functions cap. With both plumbing layers in place, the CLI surface is the last piece needed before users can run personalised renders end-to-end. This PR adds the flags + wires them through runRender.

How

  • Hoisted parseVariablesArg, resolveVariablesArg, validateVariablesAgainstProject from packages/cli/src/commands/render.ts to packages/cli/src/utils/variables.ts so both CLIs import the same parser. The local CLI used to re-export these for tests; the tests move to packages/cli/src/utils/variables.test.ts alongside the implementations.
  • New shared reportVariableIssues(issues, { strict, quiet }) helper formats the warning block + handles --strict-variables exit. Replaces the inline duplication that existed between the two CLIs.
  • lambda/render.ts resolves variables, runs pre-validation against <projectDir>/index.html when present (skipped when the project dir isn't on disk — --site-id pointing at a pre-uploaded site packaged elsewhere is the typical case), and threads variables into SerializableDistributedRenderConfig.
  • lambda.ts registers --variables / --variables-file / --strict-variables flags and forwards them to runRender.
  • docs/packages/cli.mdx adds a section on the new flags, including the 256 KiB Step Functions execution-input cap and a pointer to the upcoming templates-on-lambda guide (PR 9.5).

Test plan

  • bun run --cwd packages/cli test — 335 tests pass (12 variables tests moved from render.test.ts to variables.test.ts, still passing).
  • bun run --cwd packages/cli typecheck clean.
  • bunx oxlint + bunx oxfmt --check clean on changed files.
  • bunx fallow audit --base origin/main✓ No issues in 19 changed files.
  • Manual smoke: npx tsx packages/cli/src/cli.ts lambda --help shows --variables, --variables-file, --strict-variables flags.

This is part of a stack of 5 (+1 optional) PRs (9.1–9.6); this is PR 9.3.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Collaborator Author

jrusso1020 commented May 19, 2026

Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

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

Clean refactor + feature addition. The variables logic is hoisted faithfully from render.ts into packages/cli/src/utils/variables.ts with byte-identical implementations, then wired into the lambda CLI surface with identical flag names and validation behavior.

reportVariableIssues extraction with the { strict, quiet } options bag is a nice touch. The graceful skip when index.html is missing (for --site-id workflows) is correct. Tests moved 1:1 with updated imports. Docs updated with usage examples.

Backwards compatibility is solid — absent --variables and --variables-file produce undefined which is omitted from config.

CI green. Ship it.

Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

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

Phase 9.3 of the variables stack — bringing the local --variables / --variables-file / --strict-variables UX to hyperframes lambda render. The CLI surface is symmetric with the local flow, the shared helpers are well-factored, and the threading from CLI args through runRenderrenderToLambdavalidateDistributedRenderConfig (which now calls validateVariablesPayload) is correct.

Strengths

  • Real deduplication, not a cosmetic move. The hoist of parseVariablesArg / resolveVariablesArg / validateVariablesAgainstProject from packages/cli/src/commands/render.ts to packages/cli/src/utils/variables.ts collapses two copies of the warning-block printing into a single reportVariableIssues helper. The diff in render.ts (lines 513-535 pre vs post) drops ~20 lines of inline UI that's now centrally owned. That's how this kind of refactor should look.
  • Layered validation that fails at the right boundary. Structural validation (validateVariablesPayload) lives in the SDK and runs unconditionally inside validateDistributedRenderConfig; semantic schema validation (validateVariablesAgainstProject) lives in the CLI and only runs when index.html is present on disk. That split is correct — schema lives next to the project, structural shape is wire-relevant for any caller. The unit tests on the parser cover conflict / parse-error / shape-error / read-error / type-mismatch / undeclared paths.
  • Symmetric flag spelling and error messages. --variables, --variables-file, --strict-variables are byte-identical to the local CLI flags, including the errorBox titles. Users who learned the local flow won't trip over a renamed flag at the lambda surface.

Findings

important — --strict-variables silently degrades when projectDir isn't on disk

packages/cli/src/commands/lambda/render.ts skips the entire validation block when existsSync(indexPath) is false (the typical --site-id-only flow). The comment frames this as intentional ("the project dir isn't on disk — --site-id pointing at a pre-uploaded site that was packaged elsewhere"), and the reasoning is sound: with no index.html, there's no schema to validate against.

But the consequence is that --strict-variables becomes a no-op in exactly the case where users explicitly asked for strict checking. Failure mode:

hyperframes lambda render --site-id sha-abc \
  --variables '{"tytle":"Hello"}' \   # typo, undeclared
  --strict-variables

User expects: command aborts with "undeclared variable tytle". Actual: command silently proceeds — the typo flows to the chunk workers as window.__hfVariables.tytle, the composition reads data-composition-variables → "title", gets undefined, and the render produces a broken video. The user paid for the run, got nothing actionable, and never saw a warning.

Fix options (any of these — listed easiest → most invasive):

  1. When strict && !existsSync(indexPath), log a single warning line: --strict-variables specified but no index.html at <path>; schema validation skipped. Cheap, preserves current behavior, kills the surprise.
  2. Treat --strict-variables with no schema as an error: require the user to either drop --strict-variables or run from a directory that has index.html. Hardest stance, most defensible — strict means strict.
  3. (Future-shaped, out of scope here) Resolve the schema from the uploaded site: download index.html from s3://.../sites/<siteId>/project.tar.gz for the validation pass. Real fix, but lives in PR 9.4+ once the upload artifact contract is firm.

Recommend (1) for this PR — one console.warn call, no schema-resolution work. Document the limitation in the docs section you added.

important — no dispatcher test for the variables threading

runRender in packages/cli/src/commands/lambda/render.ts is the load-bearing wire that builds config: { ..., variables } and hands it to renderToLambda. The change is small and visually correct, but the codebase has no test that exercises runRender itself — packages/cli/src/commands/lambda/policies.test.ts and state.test.ts cover the utilities, never the dispatcher.

That's exactly the shape of hf#910 (the --memory drop). The fix there was a one-line dispatcher edit; the bug was that no test asserted "if the flag is present, the SDK call sees it." A future refactor of runRender could re-introduce the same class of drop on variables and ship green.

Concrete ask: add a runRender.test.ts that stubs loadSDK to capture the renderToLambda opts argument, then asserts:

  • args.variables = '{"a":1}' → captured opts.config.variables equals { a: 1 }.
  • args.variablesFile = "vars.json" (with a fake reader injection or fixture) → captured opts.config.variables equals the file contents.
  • Neither flag set → captured opts.config.variables is undefined.
  • args.variables = '{"a":1}' and args.variablesFile = "vars.json" → command exits non-zero (conflict).

Three of those four are already covered indirectly by variables.test.ts for the parser, but none of them assert the dispatcher actually carries the parsed value into the config. A test that fakes loadSDK + captures the argument is the cheapest reproduction of the hf#910 class of bug.

nit — empty-object --variables '{}' flows through as {} instead of undefined

parseVariablesArg('{}', undefined) returns { ok: true, value: {} }. runRender then sets config.variables = {}. The schema-validation block is skipped (Object.keys(variables).length > 0 is false), but the empty object still rides the wire into Step Functions and reaches the chunk workers as window.__hfVariables = {} instead of undefined. Cheap to fix — collapse empty-object to undefined in parseVariablesArg, or in the wrapper. Mostly a tidiness issue; no failure mode I can construct.

nit — lambda.ts examples use sha-abc for the site-id

packages/cli/src/commands/lambda.ts:32,40 use sha-abc as the site-id placeholder; the docs example uses abc1234deadbeef0. Pick one and stay consistent — sha-abc is shorter and clearer that it's a placeholder.

nit — // fallow-ignore-next-line complexity on runRender

Adding the suppression at the top of runRender (line 60) is fine, but runRender is at the boundary where the variables threading lives — exactly the place where a future reader will want clean cyclomatic complexity to spot a missed forward. If the complexity rule is genuinely too tight here, a structured pre-extracted helper (buildLambdaRenderConfig(args)) gets the line count down without the suppression and gives you a natural test seam for the dispatcher coverage above. Optional refactor; the suppression is acceptable for this PR.

Verdict

Verdict: APPROVE
Reasoning: Threading is correct, validation is layered at the right boundary, helper extraction is real (not cosmetic), and unit tests cover the parsers. The strict-mode-when-no-index case and the absent dispatcher test are real but landable as follow-ups in 9.4+ since neither breaks the happy path.

— Vai

@jrusso1020 jrusso1020 force-pushed the 05-19-feat_cli_hyperframes_lambda_render_variables_flags branch from 0b1732d to a4b1d1a Compare May 19, 2026 21:13
@jrusso1020 jrusso1020 force-pushed the 05-19-feat_aws-lambda_validate_variables_and_256_kib_step_functions_input_cap branch from 24c52b0 to 078cf71 Compare May 19, 2026 21:13
@jrusso1020
Copy link
Copy Markdown
Collaborator Author

Addressed Vai's feedback in the latest force-push:

  • important — --strict-variables silent skip when no index.html on disk now prints a clear console.warn: --strict-variables: no <path> on disk — schema validation skipped. The flag no longer becomes a stealth no-op for --site-id users. Picked option (1) from Vai's three (warn-and-proceed); option (2) — require-or-fail — would block the legitimate cross-machine site-id workflow.
  • nit — sha-abc vs abc1234deadbeef0 — settled on abc1234deadbeef0 because that matches the actual content-addressed format (16-char hex slice of a sha256, no sha- prefix). The hashProjectDir().digest('hex').slice(0, 16) implementation in deploySite.ts produces strings of that exact shape.

Skipping for follow-up:

  • important — dispatcher test for runRender — agreed valuable, but it requires a vitest-style mock of loadSDK that doesn't fit the existing CLI test patterns cleanly. Better landed in a focused PR once the test seam is in place.
  • nit — empty-object --variables '{}' flows as {} — agreed it's tidier as undefined, but no failure mode it produces.
  • nit — complexity suppression on runRender — the buildLambdaRenderConfig extraction would be cleaner; punting because the function is small enough that the suppression isn't load-bearing.

miguel-heygen
miguel-heygen previously approved these changes May 19, 2026
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

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

Re-reviewed after force-push. Both items addressed:

  1. --strict-variables warns when no schema — Fixed. When --strict-variables is set but no index.html exists on disk, a visible warning is now emitted ("schema validation skipped. Variables flow through unchecked"). Suppressed in --json mode, consistent with output suppression logic.

  2. site-id placeholder consistent — Fixed. Both CLI examples and docs now use abc1234deadbeef0 consistently.

No regressions from previously-approved code. CI green. Ship it.

vanceingalls
vanceingalls previously approved these changes May 19, 2026
Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

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

Re-stamp post-force-push. Per James's per-PR response, both prior importants addressed:

  • --strict-variables warns when no schema is resolvable (covers the --site-id silent-no-op gap).
  • site-id placeholder consistency between lambda.ts examples and docs.

Re-stamp by Vai

… --strict-variables

Mirror the local hyperframes render variables UX on the Lambda CLI:

- --variables '<json>'       inline JSON object of variable values
- --variables-file <path>    path to a JSON file with variable values
- --strict-variables          fail on type/declared-mismatch (warn by default)

Resolution + validation logic is hoisted to packages/cli/src/utils/variables.ts
so both surfaces share one parser. The new reportVariableIssues helper formats
the warning block + handles --strict-variables exit, deduping the per-CLI
issue-handling block.

Variables flow into SerializableDistributedRenderConfig.variables and reach
every chunk worker via the path PR 9.1 + 9.2 wired up (plan() →
meta/encoder.json → renderChunk() → window.__hfVariables). Pre-validation
against the composition's data-composition-variables declaration runs only
when the project's index.html is on disk — --site-id pointing at a
pre-uploaded site that was packaged elsewhere skips the check, matching how
the local CLI treats unreadable index files.

The render.ts re-exports of parseVariablesArg / resolveVariablesArg /
validateVariablesAgainstProject are dropped; the matching tests move to
packages/cli/src/utils/variables.test.ts where the implementations now live.

Docs: docs/packages/cli.mdx adds a section on --variables / --variables-file /
--strict-variables for lambda render, including the 256 KiB Step Functions
execution-input cap and a pointer to the upcoming templates-on-lambda guide
(PR 9.5).

Phase 9 PR 9.3 of the distributed rendering plan.
@jrusso1020 jrusso1020 force-pushed the 05-19-feat_aws-lambda_validate_variables_and_256_kib_step_functions_input_cap branch from 078cf71 to 0310fff Compare May 19, 2026 22:49
@jrusso1020 jrusso1020 force-pushed the 05-19-feat_cli_hyperframes_lambda_render_variables_flags branch from a4b1d1a to 39891fe Compare May 19, 2026 22:50
@jrusso1020 jrusso1020 changed the base branch from 05-19-feat_aws-lambda_validate_variables_and_256_kib_step_functions_input_cap to main May 19, 2026 22:51
@jrusso1020 jrusso1020 dismissed stale reviews from vanceingalls and miguel-heygen May 19, 2026 22:51

The base branch was changed.

@github-actions
Copy link
Copy Markdown

Fallow audit report

Found 25 findings.

Dead code (4)
Severity Rule Location Description
major fallow/unused-export packages/cli/src/commands/render.ts:602 Export 'resolveBrowserGpuForCli' is never imported by other modules
major fallow/unused-export packages/cli/src/commands/render.ts:781 Export 'renderLocal' is never imported by other modules
major fallow/unused-export packages/cli/src/utils/variables.ts:174 Export 'loadProjectVariableSchema' is never imported by other modules
major fallow/unused-export packages/cli/src/utils/variables.ts:192 Export 'validateVariablesAgainstSchema' is never imported by other modules
Duplication (10)
Severity Rule Location Description
minor fallow/code-duplication packages/cli/src/commands/render.test.ts:51 Code clone group 1 (11 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/commands/render.ts:326 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/core/src/studio-api/routes/render.ts:89 Code clone group 2 (6 lines, 2 instances)
minor fallow/code-duplication packages/engine/src/config.test.ts:13 Code clone group 1 (11 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/crossWorkerIdempotency.test.ts:189 Code clone group 3 (13 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/plan.test.ts:47 Code clone group 4 (11 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/plan.test.ts:197 Code clone group 5 (7 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/plan.test.ts:239 Code clone group 5 (7 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/planFormatBanlist.test.ts:39 Code clone group 4 (11 lines, 2 instances)
minor fallow/code-duplication packages/producer/src/services/distributed/renderChunk.test.ts:182 Code clone group 3 (13 lines, 2 instances)
Health (11)
Severity Rule Location Description
critical fallow/high-crap-score packages/aws-lambda/src/sdk/validateConfig.ts:61 'validateDistributedRenderConfig' has CRAP score 992.0 (threshold: 30.0, cyclomatic 31)
minor fallow/high-crap-score packages/aws-lambda/src/sdk/validateConfig.ts:211 'validateStepFunctionsInputSize' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
minor fallow/high-crap-score packages/aws-lambda/src/sdk/validateConfig.ts:339 'validateIntDimension' has CRAP score 42.0 (threshold: 30.0, cyclomatic 6)
critical fallow/high-crap-score packages/cli/src/commands/lambda.ts:153 'run' has CRAP score 812.0 (threshold: 30.0, cyclomatic 28)
minor fallow/high-crap-score packages/cli/src/commands/lambda.ts:321 'parseIntFlag' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
minor fallow/high-crap-score packages/cli/src/commands/lambda.ts:347 'parseEnum' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
critical fallow/high-crap-score packages/cli/src/commands/lambda/render.ts:166 'waitForCompletion' has CRAP score 132.0 (threshold: 30.0, cyclomatic 11)
critical fallow/high-crap-score packages/cli/src/commands/render.ts:235 'run' has CRAP score 1159.9 (threshold: 30.0, cyclomatic 71)
minor fallow/high-crap-score packages/cli/src/commands/render.ts:689 'renderDocker' has CRAP score 37.1 (threshold: 30.0, cyclomatic 11)
critical fallow/high-crap-score packages/cli/src/commands/render.ts:885 'trackRenderMetrics' has CRAP score 148.4 (threshold: 30.0, cyclomatic 24)
minor fallow/high-crap-score packages/producer/src/services/distributed/renderChunk.test.ts:149 '<arrow>' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)

Generated by fallow.

Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

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

Re-approved — same diff, rebased onto main.

Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

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

Re-stamp at the new HEAD after rebase onto main. Same content as my prior approve (4323426149); both importants addressed (--strict-variables warns on no-schema, site-id placeholder consistent). Base is now main so main CI workflow actually runs.

Re-stamp by Vai (post-rebase)

@jrusso1020
Copy link
Copy Markdown
Collaborator Author

Superseded by #968 (rebase-merged at cb948d5f). Same commit content is now on main as part of the #968 rebase-merge.

@jrusso1020 jrusso1020 closed this May 19, 2026
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