Documentation / VFX-D-1203

VFX-D-1203

Title

PlaceholderUsedBeforeDefinition

Explanation

A {placeholder} token is interpolated by a step that runs before anything provides its value. A name resolves if a root variables entry declares it (those are loaded into the shared variable context before the first step runs) or if an earlier step captures it. A step's own capture does not count for that same step: a capture is produced by running the step, so it is available from the next step onwards.

This rule is order-aware, which is what distinguishes it from VFX-D-1204. The same token in the same suite is a defect in step 0 and correct in step 2 — the finding says "not yet", not "never".

Engine reserved forms — {svc::<name>.<field>}, {conn::<name>}, {__outcome::...} — are never reported: they resolve from the environment rather than from the variable context.

Host-owned listener and receiver names count as defined everywhere in the suite, not just after the step that names them. A webhook-listen.* step's listener and a trace-expect.* step's receiver name infrastructure the engine stands up, and the engine stages each one at its plain Vars key precisely so an earlier step can interpolate it — which is how the canonical webhook suite is written: step 0 registers a callback URL as {callbacks}, and step 1 is the webhook-listen.http with listener: callbacks. That suite is correct and is not reported.

A script.* step's code and file are not scanned for placeholders. C# spells its own string interpolation with the same braces, so $"order {id} created" inside a script body is not a suite placeholder: the engine never interpolates a script body against the variable context — the script reads Vars["id"] itself. The same exclusion applies to summary.placeholders, so the digest and this rule always agree about what a placeholder is.

Reported as a warning in the semanticDiagnostics channel, located at the step that used the token rather than at the individual field, because the fix is a decision about step ordering.

Common causes

  • Two steps written in the wrong order — the step that consumes an id placed before the step that creates it.
  • A step interpolating a value it captures itself, in the same step.
  • A typo in either the placeholder or the capture key, so the two never match ({orderID} vs a capture named orderId — matching is case-sensitive).
  • A placeholder left over from a step that was deleted or moved.

Fixes

  • Move the step that captures the value above the step that uses it.
  • Or declare the name under the root variables block if it is a constant rather than a captured value.
  • Check the capture key's exact spelling against the placeholder — validate_suite's summary.captures and summary.placeholders list both sides.