Skip to content

feat(core): WorkflowYAMLParser (1.L) + post-review hardening#14

Merged
cemililik merged 7 commits into
mainfrom
development
Jun 12, 2026
Merged

feat(core): WorkflowYAMLParser (1.L) + post-review hardening#14
cemililik merged 7 commits into
mainfrom
development

Conversation

@cemililik

@cemililik cemililik commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • 1.L WorkflowYAMLParser (packages/core): the engine's first real source — parses a .relavium.yaml string against WorkflowSchema, producing a typed WorkflowDefinition or a field-named, secret-free error. Pure by contract: no filesystem, no env, no state. Hardened YAML decode profile (ADR-0035: schema:'core', maxAliasCount:0, uniqueKeys:true, 2 MiB cap, no Date/Buffer). Structured interpolation reference collector (collectReferences) for the DAG builder (1.M).
  • ADR-0035 (yaml parser dependency): records the yaml package adoption and the hardened decode options.
  • Docs refresh: current.md, phase-1-engine-and-llm.md, tech-stack.md, roadmap status paragraphs updated for 1.K (done) and 1.AD (done); CLAUDE.md/README.md/AGENTS.md aligned with the current milestone.
  • Post-review hardening (second commit): six targeted fixes from the code-review pass:
    • syntaxErrorFrom: guard err.pos[0] === -1 sentinel — skips line/column when the yaml library has no position, preventing {line:0,col:-1} in diagnostics
    • messageFor(): add explicit case 'custom': (invariant-documented) + safe default: 'invalid value' fallback so no unknown Zod code can accidentally echo an authored payload
    • JSDoc ordering: MAX_SOURCE_CHARS moved before the parseWorkflow docblock (was orphaned, IDE hover was broken)
    • engine-deps/check.mjs: remove @relavium/llm from the packages/core allowlist — it isn't in package.json yet; pre-adding it defeated the guard whose purpose is to force a co-located ADR + allowlist edit at adoption time
    • collect.ts: document why the SAFE_LABEL guard from parser.ts is not needed (input is fully schema-validated); pin a TODO(1.M) for the context/run.outputs semantic constraint
    • references.ts: fix escape-sequence handling in findClose + splitTopLevel\" / \' inside a quoted filter argument incorrectly closed the quote state

Test plan

  • pnpm turbo run typecheck test --filter=@relavium/core — 66 tests, all green
  • node tools/engine-deps/check.mjs — all three engine packages on allowlist
  • Secret-leak invariant: never echoes an authored value + does not echo an invalid id into the field locator tests pass
  • Hardened decode: anchor/alias rejection, size cap, date-as-string tests pass
  • Escape-sequence tests: handles an escaped quote inside a filter string argument (double + single quote variants) pass
  • context/run.outputs known-gap test pins current permitting behavior, includes TODO(1.M) for DAG builder follow-up

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New engine package: YAML→workflow parsing, typed parse errors, and interpolation reference discovery for workflow templates.
  • Documentation

    • Status/roadmap updated to mark the FallbackChain and policy work as landed and shift focus to the engine/YAML parser; added ADR for hardened YAML parsing and updated ADR index and tech-stack.
  • Tests

    • Broad unit/integration tests for parsing, interpolation, and error diagnostics.
  • Chores

    • Build/config updates, package metadata and workspace catalog changes, and stricter coverage thresholds.

cemililik and others added 4 commits June 11, 2026 19:50
… merged)

1.K landed in PR #13 (2026-06-11). Reflect it everywhere status lives:
- phase-1: §1.K header ✅ Done; 1.m2 milestone complete (1.B PR #7, 1.K PR #13);
  the dependency-matrix 1.K row gets its Done note; the top status blockquote now
  points past 1.K (next is 1.L, which scaffolds packages/core).
- current.md: the seam policy lane is complete (1.K merged); the engine lane (1.L)
  is the active next step; the multimodal + PR #12 notes no longer call 1.K "next".
- llm-provider-seam.md: the ADR-0030 strip-on-failover is now enforced by 1.K, not
  "not yet exercised — no consumer exists".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The root README / CLAUDE.md / AGENTS.md / roadmap-README status lines still stopped
at M1 (PR #9) and framed the FallbackChain (1.K) as upcoming/active. A doc audit
(verified against the repo) found these were the only stale status surfaces left
after the 1.K done-marking:
- README.md: 1.K dropped from "next on the critical path" (it landed, PR #13); the
  engine (@relavium/core, not yet scaffolded) is now the sole next item.
- CLAUDE.md: the Status headline notes 1.K landed completing 1.m2; the "active work
  continues on" clause points at the engine (1.L next), not 1.K.
- AGENTS.md: the mirror gains the 1.AD (PR #11) + 1.K (PR #13) landings; active work
  redirected to the engine lane.
- roadmap/README.md: the M1 row's "complete just after, at 1.m2" → past tense.

The deferred-tasks audit found nothing closeable by completed work (the pricing
item was already checked off; everything else is correctly future work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scaffold the engine package (platform-free — tsconfig types:[], zero platform
imports) and land its first workstream: parseWorkflow(yamlText, opts?), which decodes
a .relavium.yaml STRING and validates it against the strict @relavium/shared
WorkflowSchema, returning a typed WorkflowDefinition or a typed, field-named,
secret-free error. Pure: takes text, never reads the filesystem or the environment.

- YAML loader: the `yaml` package (eemeli/yaml) under an ADR-0035 hardened,
  deterministic profile — version 1.2 / schema 'core' (no Date/Buffer), merge off,
  uniqueKeys, stringKeys, maxAliasCount 0 (no anchor/alias expansion), prettyErrors off
  + logLevel error (pure, no console), a LineCounter for line/col, and a 2 MiB pre-parse
  size cap. Every parse-stage throw (a YAML fault or the alias ReferenceError) is
  normalized to a typed WorkflowSyntaxError; the source text never enters a message.
- Errors: WorkflowParseError (abstract base + code) -> WorkflowSyntaxError /
  WorkflowValidationError, mirroring @relavium/llm's LlmConfigError. The Zod issue ->
  field locator resolves a node/agent/input/edge index to its authored id
  ("node `summarize`.agent_ref"); messages are code-derived and never echo an authored
  value. The raw ZodError is NOT attached as cause, and only a well-formed identifier is
  echoed into a locator — so an invalid/sensitive id never leaks.
- Interpolation: parseTemplate(text) — a pure, total lexer turning `{{ … }}` templates
  into structured, UN-EVALUATED segments (kind inputs|ctx|node|secrets|unknown, identifier,
  path, ordered pipe filters, verbatim raw); collectReferences(workflow) walks the template
  fields for the DAG builder (1.M). Evaluation + secret-taint stay in 1.L2.
- Wiring (same change as the ADR, per the engine-deps guard): `yaml` pinned in the pnpm
  catalog, added to ENGINE_ALLOWLISTS['packages/core'], a tech-stack row, and the core
  coverage floor (>=90% line+branch) enabled.

63 core tests; @relavium/core at 95.5% branch / 98.9% line. A multi-agent adversarial
review (6 dimensions) folded in: dropped the ZodError cause, guarded the id-echo, made
the `{{ }}` close-scan quote-aware, and tightened numeric filter-arg parsing.

Refs: ADR-0035, ADR-0023, ADR-0009

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d hygiene)

Six targeted fixes from the code-review pass on the 1.L WorkflowYAMLParser:

* parser: guard err.pos[0]===-1 sentinel — lineCounter.linePos(-1) returned a
  nonsensical {line:0,col:-1}; now the field is omitted when the yaml library
  has no position for the fault.
* parser: fix orphaned JSDoc — MAX_SOURCE_CHARS sat between the parseWorkflow
  docblock and its function; swap order so IDE hover attaches correctly.
* parser: harden messageFor() — add explicit `case 'custom':` (with an invariant
  comment explaining all @relavium/shared superRefines emit structural-only
  messages) and change the `default` branch to return 'invalid value' so an
  unknown Zod code can never accidentally echo an authored payload.
* tools: remove @relavium/llm from the packages/core engine-deps allowlist — it
  is not declared in packages/core/package.json yet; pre-adding it defeats the
  guard whose purpose is to force a co-located ADR + allowlist edit when the dep
  is actually introduced.
* interpolation/collect: document why the SAFE_LABEL guard from parser.ts is not
  needed here (collectReferences accepts a fully schema-validated Workflow); pin a
  TODO(1.M) comment for the context/run.outputs semantic constraint.
* interpolation/references: fix escape-sequence handling in findClose and
  splitTopLevel — a `\"` or `\'` inside a quoted filter argument incorrectly
  closed the quote state, breaking templates like `default("say \"hi\"")`.

Tests: 66 pass (3 new for escape sequences, 1 custom-code-path invariant pin,
1 context/run.outputs 1.M known-gap pin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@sourcery-ai sourcery-ai 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.

Sorry @cemililik, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 089a7dfd-eee2-48c8-a801-31c9b363a8ec

📥 Commits

Reviewing files that changed from the base of the PR and between 89e1b96 and c644fc0.

📒 Files selected for processing (5)
  • docs/roadmap/current.md
  • docs/roadmap/phases/phase-1-engine-and-llm.md
  • packages/core/src/interpolation/collect.ts
  • packages/core/src/interpolation/references.test.ts
  • packages/core/src/parser.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/roadmap/phases/phase-1-engine-and-llm.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/core/src/interpolation/references.test.ts
  • docs/roadmap/current.md
  • packages/core/src/parser.test.ts
  • packages/core/src/interpolation/collect.ts

📝 Walkthrough

Walkthrough

This PR introduces the @relavium/core package: a platform-neutral YAML→Workflow parser with hardened YAML decoding, typed parse/validation errors, interpolation reference extraction, package scaffolding, tests, and documentation updates marking FallbackChain as landed and engine work next.

Changes

Core Engine Package Launch

Layer / File(s) Summary
Roadmap Status: FallbackChain Completion and Engine Start
AGENTS.md, CLAUDE.md, README.md, docs/decisions/0035-yaml-parser-dependency.md, docs/decisions/README.md, docs/reference/shared-core/llm-provider-seam.md, docs/roadmap/*, docs/tech-stack.md
Roadmap/docs updated to mark Phase 1.K (FallbackChain runner, PR #13) complete and shift active work to Phase 1.L (Workflow YAML parser). ADR-0035 documents using yaml (eemeli/yaml) with a hardened YAML 1.2 core profile confined to @relavium/core.
Workflow Parse Error Taxonomy
packages/core/src/errors.ts, packages/core/src/errors.test.ts
Adds discriminated parse errors: WorkflowSyntaxError (invalid_yaml, optional line/column/source) and WorkflowValidationError (schema_validation with WorkflowIssue[]), plus a safe base class and tests validating codes, messages, positions, and source.
Interpolation Reference Parsing and Collection
packages/core/src/interpolation/references.ts, packages/core/src/interpolation/references.test.ts, packages/core/src/interpolation/collect.ts
Implements parseTemplate/templateReferences (lexer for {{ … }}), parseReference and pipe/filter arg parsing, and collectReferences(workflow) to extract interpolation sites from validated workflows. Tests cover quoting, escaping, balanced brackets, filter args, unterminated delimiters, and raw fidelity.
Workflow YAML Parser and Validation
packages/core/src/parser.ts, packages/core/src/parser.test.ts
Adds parseWorkflow(yamlText, opts?): size cap, hardened YAML parsing (YAML 1.2 core, no tag resolution/merges, unique/string keys, aliases/anchors disabled), Zod validation, and normalization into typed, secret-free diagnostics. Includes locator/mapping helpers for field-named errors and comprehensive test coverage for parsing, round-tripping, hardened rules, oversize handling, and diagnostic mapping.
Package Configuration and Dependency Allowlist
packages/core/package.json, packages/core/README.md, packages/core/tsconfig.json, packages/core/tsconfig.build.json, pnpm-workspace.yaml, tools/engine-deps/check.mjs, vitest.config.ts
Creates @relavium/core package config (ESM, export map, scripts), documents engine goals, adds yaml to workspace catalog, sets typecheck-only tsconfig with types: [], adds build tsconfig, updates engine allowlist to permit yaml for @relavium/core, and enforces 90% lines/branches coverage for packages/core.
Package Public API Surface
packages/core/src/index.ts
Exports parseWorkflow, parse types/options, parse error classes/types, and interpolation utilities and types for DAG construction and downstream use.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant parseWorkflow
  participant YAML
  participant Schema
  Caller->>parseWorkflow: submit YAML text (+ source?)
  parseWorkflow->>YAML: hardened parse (yaml core, no aliases)
  YAML-->>parseWorkflow: raw JS object or parse error
  parseWorkflow->>Schema: validate with WorkflowSchema
  Schema-->>parseWorkflow: success or Zod issues
  parseWorkflow-->>Caller: WorkflowDefinition or WorkflowSyntaxError/WorkflowValidationError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • HodeTech/Relavium#2: Shares the WorkflowSchema validation surface used by parseWorkflow.
  • HodeTech/Relavium#12: Earlier changes to tools/engine-deps/check.mjs that this PR extends by permitting yaml for @relavium/core.
  • HodeTech/Relavium#1: Prior workspace / Vitest configuration work that this PR builds on.

"With yaml in paw and schemas so tight,
the engine parses workflows into light.
References found, diagnostics neat,
platform-free parsing — a rabbit's feat. 🐇"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding WorkflowYAMLParser (1.L) with post-review hardening to the core package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request scaffolds the @relavium/core engine package and implements the WorkflowYAMLParser (1.L) to parse and validate .relavium.yaml files. It introduces the pure-JS yaml package as a runtime dependency under a hardened, deterministic profile (ADR-0035) and implements structured interpolation-reference extraction and secret-free validation error mapping. Feedback on the parser suggests improving developer experience by explicitly handling common Zod validation codes (like too_small, too_big, and invalid_string) in the error message formatter instead of falling back to a generic message.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +207 to +211
default:
// A Zod code not explicitly handled — return a generic message rather than risking an echo of
// an authored value via issue.message (which is code-derived for the built-in codes, but could
// change in future Zod versions).
return 'invalid value';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To improve the developer experience (DX) when authoring workflows, consider explicitly handling common Zod validation codes such as too_small, too_big, and invalid_string in messageFor. Currently, these fall back to a generic 'invalid value' message, making it difficult for users to diagnose issues like string length violations or regex mismatches (e.g., invalid kebab-case IDs). Since Zod's default messages for these codes are structural and do not echo the authored input values, returning issue.message for them is completely safe and preserves the secret-leak invariant.

    case 'too_small':
    case 'too_big':
    case 'invalid_string':
      return issue.message;
    default:
      // A Zod code not explicitly handled — return a generic message rather than risking an echo of
      // an authored value via issue.message (which is code-derived for the built-in codes, but could
      // change in future Zod versions).
      return 'invalid value';

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/README.md`:
- Line 10: The README incorrectly lists `@relavium/llm` as a runtime dependency
in the phrase "confined to `@relavium/shared`, `@relavium/llm`, `zod`, and the
pure-JS `yaml` loader`; remove `@relavium/llm` from that runtime dependency list
so it matches package.json and the allowlist behavior described in
tools/engine-deps/check.mjs (the engine dependency guard comments). Update the
sentence to only include the actual runtime packages (e.g., `@relavium/shared`,
`zod`, and the pure-JS `yaml` loader) and ensure any nearby documentation
referencing `@relavium/llm` clarifies it is intentionally excluded until the
runner imports it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8c1804f-7b14-461e-b61c-ef7021984b9e

📥 Commits

Reviewing files that changed from the base of the PR and between 36b0d14 and ffc5978.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • docs/decisions/0035-yaml-parser-dependency.md
  • docs/decisions/README.md
  • docs/reference/shared-core/llm-provider-seam.md
  • docs/roadmap/README.md
  • docs/roadmap/current.md
  • docs/roadmap/phases/phase-1-engine-and-llm.md
  • docs/tech-stack.md
  • packages/core/README.md
  • packages/core/package.json
  • packages/core/src/errors.test.ts
  • packages/core/src/errors.ts
  • packages/core/src/index.ts
  • packages/core/src/interpolation/collect.ts
  • packages/core/src/interpolation/references.test.ts
  • packages/core/src/interpolation/references.ts
  • packages/core/src/parser.test.ts
  • packages/core/src/parser.ts
  • packages/core/tsconfig.build.json
  • packages/core/tsconfig.json
  • pnpm-workspace.yaml
  • tools/engine-deps/check.mjs
  • vitest.config.ts

Comment thread packages/core/README.md Outdated
Addresses all actionable findings from the PR #14 review pass. Each item
verified against the current code before acting; two items skipped (noted below).

parser.ts
- Flip all `!== undefined ? X : Y` ternaries to the positive form so Sonar's
  "Unexpected negated condition" rule is satisfied (5 call sites).
- Replace `(o) => String(o)` with the point-free `String` (2 sites, "arrow
  function is equivalent to String").
- Add explicit `case 'invalid_string': case 'too_small': case 'too_big':` branches
  that return `issue.message` directly (Zod's structural messages are safe here,
  unlike the `default` fallback which intentionally returns 'invalid value').

errors.ts
- Extract the inner `rest === 1 ? '' : 's'` ternary into a `suffix` variable so
  Sonar's "nested ternary" smell is resolved.

collect.ts
- Refactor to reduce cognitive complexity from 23 → well below 15: extract
  `buildSite` (pure template→site builder) and `collectNodeSites` (per-node
  dispatch) as module-level helpers; `collectReferences` is now a flat loop.

references.ts
- Replace `[\s\S]` with `.` + `s` (dotAll) flag on NODE_OUTPUT, NAMESPACED, and
  FILTER. Resolves the Sonar security hotspot (super-linear backtracking risk on
  character-class alternation). NODE_OUTPUT's node-ID capture also tightened to
  `[^'"]*?` (IDs can't contain quotes).
- Replace `[A-Za-z0-9_]` with `\w` in FILTER (concise character-class smell).
- Extract `isQuoteChar(ch: string | undefined): boolean` helper; use it in both
  `findClose` and `splitTopLevel` to remove the `||` operator that pushed
  `splitTopLevel`'s cognitive complexity to 16.

references.test.ts
- Simplify `refOf` guard to `segment?.kind !== 'reference'` (optional chain).

parser.test.ts
- Rename all `caught` catch parameters to `error_` (6 sites, Sonar convention).

README.md
- Remove `@relavium/llm` from the runtime dependency list (not in package.json
  yet; the sentence now matches the engine-deps allowlist).

Skipped intentionally:
- `String.raw` on references.test.ts L147/149/154/156: double-escaping is
  semantically required to pass `\"` / `\'` into the tested template strings;
  using `String.raw` would change the input and break the tests.
- "Complete the TODO" on collect.ts L51 and parser.test.ts L388: these are
  intentional TODO(1.M) deferred tasks, not actionable smells.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
packages/core/src/interpolation/collect.ts (1)

49-72: ⚡ Quick win

Reduce cognitive complexity by extracting field-collection helper.

SonarCloud correctly flags this function's cognitive complexity (22 vs. allowed 15). The repetitive nested conditionals can be simplified by extracting a helper:

♻️ Proposed refactor to reduce complexity

Add a local helper before collectNodeSites:

+function collectFieldSite(
+  sites: ReferenceSite[],
+  nodeId: string,
+  fieldName: string,
+  value: string | undefined
+): void {
+  if (value !== undefined) {
+    const site = buildSite(`node \`${nodeId}\`.${fieldName}`, value);
+    if (site) sites.push(site);
+  }
+}
+
 function collectNodeSites(node: WorkflowNode): ReferenceSite[] {
   const sites: ReferenceSite[] = [];
   if (node.type === 'agent') {
-    if (node.prompt_template !== undefined) {
-      const site = buildSite(`node \`${node.id}\`.prompt_template`, node.prompt_template);
-      if (site !== undefined) sites.push(site);
-    }
-    if (node.system_prompt_append !== undefined) {
-      const site = buildSite(`node \`${node.id}\`.system_prompt_append`, node.system_prompt_append);
-      if (site !== undefined) sites.push(site);
-    }
+    collectFieldSite(sites, node.id, 'prompt_template', node.prompt_template);
+    collectFieldSite(sites, node.id, 'system_prompt_append', node.system_prompt_append);
   } else if (node.type === 'human_gate') {
-    if (node.assignee !== undefined) {
-      const site = buildSite(`node \`${node.id}\`.assignee`, node.assignee);
-      if (site !== undefined) sites.push(site);
-    }
-    if (node.message_template !== undefined) {
-      const site = buildSite(`node \`${node.id}\`.message_template`, node.message_template);
-      if (site !== undefined) sites.push(site);
-    }
+    collectFieldSite(sites, node.id, 'assignee', node.assignee);
+    collectFieldSite(sites, node.id, 'message_template', node.message_template);
   }
   return sites;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/interpolation/collect.ts` around lines 49 - 72, The
function collectNodeSites has repetitive nested checks causing high cognitive
complexity; add a small local helper (e.g., addFieldSite or collectField)
declared above or inside collectNodeSites that accepts the human-readable field
label (like `node \`${node.id}\`.prompt_template`) and the field value, checks
for undefined, calls buildSite(label, value), and if non-undefined pushes the
result into the sites array; then replace each repeated if block for
prompt_template, system_prompt_append, assignee, and message_template with calls
to this helper to simplify control flow and reduce complexity while keeping
existing symbols (collectNodeSites, buildSite, WorkflowNode, and the field
names) unchanged.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/interpolation/collect.ts`:
- Around line 49-72: The function collectNodeSites has repetitive nested checks
causing high cognitive complexity; add a small local helper (e.g., addFieldSite
or collectField) declared above or inside collectNodeSites that accepts the
human-readable field label (like `node \`${node.id}\`.prompt_template`) and the
field value, checks for undefined, calls buildSite(label, value), and if
non-undefined pushes the result into the sites array; then replace each repeated
if block for prompt_template, system_prompt_append, assignee, and
message_template with calls to this helper to simplify control flow and reduce
complexity while keeping existing symbols (collectNodeSites, buildSite,
WorkflowNode, and the field names) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c32dc729-dd64-4534-bcde-eb9b4f2a852e

📥 Commits

Reviewing files that changed from the base of the PR and between ffc5978 and 89e1b96.

📒 Files selected for processing (7)
  • packages/core/README.md
  • packages/core/src/errors.ts
  • packages/core/src/interpolation/collect.ts
  • packages/core/src/interpolation/references.test.ts
  • packages/core/src/interpolation/references.ts
  • packages/core/src/parser.test.ts
  • packages/core/src/parser.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/core/README.md
  • packages/core/src/interpolation/references.test.ts
  • packages/core/src/errors.ts
  • packages/core/src/parser.ts
  • packages/core/src/interpolation/references.ts
  • packages/core/src/parser.test.ts

cemililik and others added 2 commits June 12, 2026 08:38
The four repeated if-field-undefined / buildSite / push-if-defined blocks in
collectNodeSites are collapsed into a single addFieldSite closure, removing the
repetition while keeping all existing symbols (collectNodeSites, buildSite,
WorkflowNode, field names) unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dge cases; refresh 1.L roadmap

- parser.test.ts: add regression tests for the invalid_string, too_small, and too_big
  messageFor branches (each returns issue.message directly; pins the secret-free invariant
  for those code paths)
- references.test.ts: add {{}} and {{ }} edge-case tests (both produce kind:unknown with
  empty identifier — lexer defers validity judgment to the resolver)
- docs/roadmap/current.md, phase-1-engine-and-llm.md: remove stale "not started / still
  only a README" language for 1.L; mark 1.L ✅ Done (PR #14, 2026-06-12) and point forward
  to 1.L2 → 1.M

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

@cemililik
cemililik merged commit 290cf62 into main Jun 12, 2026
6 of 7 checks passed
cemililik added a commit that referenced this pull request Jun 12, 2026
… current.md pointers

- phase-1-engine-and-llm.md: mark 1.L workstream heading ✅ Done (PR #14, 2026-06-12)
- current.md: update "Last updated" date to 2026-06-12; fix active-phase doc link
  (phase-0-foundations → phase-1-engine-and-llm); mark "1.L has since landed" in the
  PR-12 callout and point forward to 1.L2 as the next workstream

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Jun 12, 2026
…bligations

PR #15 (the {{ … }} interpolation engine + parse-time secret-taint gate) merged
2026-06-12. Mark 1.L2 Done everywhere and point the engine lane at 1.M:

- phase-1-engine-and-llm.md: top status, the 1.L/1.L2 workstream-table rows, and
  the §1.L2 section header all show ✅ Done (PR #14 / #15).
- current.md: the engine-lane paragraph and the status callout mark 1.L2 Done;
  1.M (DAG builder + RunPlan) is the next workstream.
- CLAUDE.md / README.md: the active-work lines note 1.L + 1.L2 landed, next 1.M.
- deferred-tasks.md: a new "Interpolation engine (1.L2) follow-ups" section
  records the two cross-layer forward-obligations the pre-merge review deferred —
  structured-default reference flow (→ 1.M) and frozen-ctx structuredClone
  transport (→ 1.R). The two security-critical deferrals are already 1.O
  acceptance criteria, so they are not duplicated.

Co-Authored-By: Claude Opus 4.8 <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.

1 participant