Skip to content

fix: make each workflow meta a literal first statement - #426

Merged
sv-tmueller merged 2 commits into
mainfrom
fix/424-literal-workflow-meta
Oct 1, 2026
Merged

sv-tmueller merged 2 commits into
mainfrom
fix/424-literal-workflow-meta

Conversation

@sv-tmueller

@sv-tmueller sv-tmueller commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Closes #424

Part of batch #423

What

The workflow runtime reads each script's meta export before it runs the script, so meta has to be a plain literal and the first statement of the file. Today it is computed from SPEC and sits below the SPEC and TIER_MODELS constants.

  • test: adds .claude/workflows/__tests__/workflow-meta.test.mjs. For every .js file in .claude/workflows/ it checks that the file starts at byte 0 with export const meta = {, that the literal holds only quoted strings, keys, braces, brackets and commas, and that it equals SPEC with each tier resolved through TIER_MODELS. It fails on today's files.
  • fix: moves meta to a literal at byte 0 in tm-review-changes.js, tm-review-codebase.js and tm-map-codebase.js, copying the values the old meta evaluated to. SPEC is unchanged and keeps its header comment. plugin.json goes 2.9.0 -> 2.9.1 (patch).

meta is a copy of SPEC's name, description and phases rather than a read from SPEC, because three tests evaluate const SPEC alone in an empty vm context. The new sync check fails if the copy drifts.

Live run

Lead step, after tester PASS at 1afc284.

  • Loaded: yes. The Workflow tool accepted the branch's .claude/workflows/tm-review-changes.js by path (scriptPath), unmodified, on the first try. No refusal, no hoisted copy. On main the same call is refused ("export const meta = { name, description, phases } must be the FIRST statement in the script", A/B arm: live ultracode on Opus 5.5 on a replayed merged issue #405 and feat: verify stage in tm-review-changes with spec-format item transforms #419).
  • Run: id wf_170be40a-a33, 2026-10-01, args: { base: "origin/main" }. The lead checkout was detached at 1afc284 for the run, and HEAD was checked unchanged afterward.
  • Result: verdict approve, 0 must-fix, 0 should-fix, 0 nits, 3 dismissed. 8 agents: 7 Sonnet 5.5 reviewers and 1 Opus 5.5 critic. Six dimensions returned {"findings": []} (the runtime counts these as "empty result"); the seventh raised the three nits the critic dismissed. Confirmed 0, refuted 0, unverified 0, so the verify stage did not fire.
  • Cost: $1.23 at list price (Opus critic $0.40, 7 Sonnet reviewers $0.83). Source: token-report.mjs and token-prices.json from PR fix: token report prices Sonnet 5.5 and uses the lead's real output tokens #427's branch (which prices claude-sonnet-5-5), run read-only against a scratch config dir holding only this run's 8 transcripts. Subagent output is that script's characters-divided-by-4 estimate and leaves out thinking, so the figure undercounts.
  • Wall-clock: 136,708 ms (2 min 17 s), measured: the workflow's own task-notification duration_ms.

Checks

  • npm test: 477 pass, 0 fail (tester, at 1afc284).
  • node scripts/check-version-bump.mjs origin/main HEAD: exit 0, 2.9.0 -> 2.9.1 (tester).

🤖 Generated with Claude Code

sv-tmueller and others added 2 commits October 1, 2026 13:00
The workflow runtime reads meta before running the script, so it has to be a plain literal at byte 0. The test also checks the literal against SPEC and TIER_MODELS so the copy cannot drift.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The workflow runtime reads meta before it runs the script, so a meta computed from SPEC below it is not read reliably. Move meta to the top as a plain literal with the values the old expression produced. SPEC is unchanged. Bump plugin 2.9.0 -> 2.9.1.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sv-tmueller

Copy link
Copy Markdown
Owner Author

Tester report (round 1)

VERDICT: PASS
COMMIT: 1afc284
FINDINGS: none
(Evidence summary: npm test 477 pass, 0 fail. node scripts/check-version-bump.mjs origin/main HEAD exit 0 ("2.9.0 -> 2.9.1"). Commit 1's test run against origin/main's three workflow files in a scratch dir: 9 of 10 fail, 1 pass (the file-discovery test). meta in all three files deep-equals main's computed meta, and SPEC bytes are identical to origin/main. Mutations on a scratch copy, all caught and named by file: identifier value, call, [].concat(...) call, spread, template literal, line comment inside, block comment inside, arrow IIFE, header comment above meta, blank line above meta, statement above meta, SPEC tier drift, meta model drift, meta detail drift, description drift, TIER_MODELS drift. A new tm-new.js with a computed meta fails; one with a literal meta but no SPEC also fails, via the sync test. The diff adds no em dashes, banned words or "fable".)
UNTESTED CLAIMS: Live run (acceptance criterion 4): the runtime actually loads the unmodified branch tm-review-changes.js, with PR body recording that it loaded, cost and wall-clock. This is the lead's step and I did not run it. Also unproven: that the runtime rejects a header comment above meta (the test is stricter than needed, by design).
LESSONS: A literal-shape guard needs mutation tests per forbidden construct (calls, spreads, comments), because evaluating in an empty vm context alone passes .map() and Object.freeze() calls.

@sv-tmueller

Copy link
Copy Markdown
Owner Author

Reviewer report (round 1)

VERDICT: APPROVE
STAGE: both clean
FINDINGS:

  1. .claude/workflows/tm-review-changes.js:15, tm-review-codebase.js:15, tm-map-codebase.js:15, nit. The sub-plan moved one comment, the "display purposes" comment, and it applied only to tm-review-changes.js. The PR adds that line to all three files and appends "only", which claims something about runtime behavior that nobody checked. It also drops the original's first sentence ("The adapter table resolves the tier to a concrete model for the Claude Code host."). Fix: go back to the original wording, without "only", and keep it to tm-review-changes.js, or keep all three lines but drop "only".
    Evidence: sub-plan says "The one in tm-review-changes.js ("meta.phases carries the model for display purposes") moves into a comment after the literal."
  2. Commit 1afc284 message body, nit. It says a computed meta "is not read reliably", but the runtime actually refuses to load the file. Fix: if this text survives the squash, say the runtime refuses to load a script whose meta is not a literal first statement.
    Evidence: issue Make each tm- workflow's meta a literal first statement #424: "The Workflow runtime refuses all three tm- workflows ... by name and by path."
    CHECKS: n/a (full track). Reviewed at 1afc284. Pass 1 found no gaps.
  • AC1-3, AC5: met. The test has a separate commit, 76d34cd. It lists files with readdirSync and checks byte-0 position, punctuation left after stripping, the empty-vm eval, and the JSON-round-trip sync against SPEC/TIER_MODELS. It fails on main. The plugin version goes 2.9.0 -> 2.9.1.
  • SPEC, stages and prompts: untouched. No docs/architecture text describes a computed meta.
  • AC4 (PR body "Live run" section): checked against the criterion, all met.
    • Loaded on the first try, by scriptPath, unmodified, with the checkout detached at 1afc284.
    • args base origin/main, so the workflow's git diff origin/main...HEAD covered this PR's own diff.
    • Cost $1.23 at list price, flagged as an undercount. Wall-clock 136,708 ms, measured.
    • The 8 agents match the 7 DIMENSIONS plus the critic. The section matches batch Batch: follow-ons-from-417 #423 Decision 3.
  • The header-comment question stays open, as the sub-plan intends.
  • As a spot check I ran node --test .claude/workflows/__tests__/workflow-meta.test.mjs at the head: exit 0, 10 of 10 pass.

@sv-tmueller
sv-tmueller marked this pull request as ready for review October 1, 2026 11:11
@sv-tmueller
sv-tmueller merged commit 9c6669d into main Oct 1, 2026
2 checks passed
@sv-tmueller
sv-tmueller deleted the fix/424-literal-workflow-meta branch October 1, 2026 11:22
sv-tmueller added a commit that referenced this pull request Oct 1, 2026
Resolve the plugin.json version to 2.10.0, above main's 2.9.1 from #426.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Make each tm- workflow's meta a literal first statement

1 participant