Skip to content

docs(qa): record the #7545 ruling — an unknown flow node type is late-bound, not refused - #7664

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7545-node-type-clause-ruling
Aug 11, 2026
Merged

docs(qa): record the #7545 ruling — an unknown flow node type is late-bound, not refused#7664
os-help merged 1 commit into
mainfrom
claude/issue-7545-node-type-clause-ruling

Conversation

@os-help

Copy link
Copy Markdown
Collaborator

Fixes#7545

Records the maintainer's ruling in the flow-node-type-matrix checklist item. Zero engine code — one checklist item edited, per the ruling's stated remaining scope.

The premise, verified before editing

The item's negative clause demanded a registration-time refusal:

"registering a flow with an unregistered node type (e.g. type 'bogus_node') must be REFUSED at registerFlow / POST /api/v1/automation — the type is validated against the live action registry (ADR-0018), and silent acceptance of an inert node is the #1887 failure shape"

The platform never promised that. registerFlow() in packages/services/service-automation/src/engine.ts says so in its own comment — the ADR-0018 §M1 check "stays soft-fail — a flow authored against a currently-absent plugin must still register, and executeNode() throws NO_EXECUTOR at run time" — and ADR-0018 §M1 states it as shipped scope: "registerFlow() soft-validates node types against the live registry (warn, don't hard-fail)". So QA run #7516 recorded a FAIL against a promise that does not exist.

The ruling (issue #7545, maintainer, 2026-08-11 — quoted verbatim)

Ruling: status quo upheld. Registration-time soft-fail on an unknown node type is the intended posture. The rationale is real and documented (ADR-0018 §M1: a flow authored against a currently-absent plugin must still register; #4771 fixed the ordering bug this protects against), and the failure is already loud at three points (registration WARN with the full vocabulary, trigger answers success: false, run recorded failed). This is deliberate late binding with a declaration — not tolerance masking an error, so the hard-refuse instinct does not apply here.

Remaining scope of this card: record this ruling in the flow-node-type-matrix checklist clause so future runs read the 200-on-unknown-type as pass-by-design, then close.

After — the new acceptance clause

The refusal expectation is replaced by a positive four-part clause asserting what the platform actually does. Full text as landed:

"an UNKNOWN node type is late-bound, not refused — and a run reads that as PASS BY DESIGN (maintainer ruling on #7545, 2026-08-11: registration-time soft-fail on an unknown node type is the intended posture, 'deliberate late binding with a declaration — not tolerance masking an error'). The contract has four parts and ALL FOUR must hold: (1) POST /api/v1/automation with type 'bogus_node' answers 200 and really registers the flow; (2) the registration emits a located WARN whose message carries the load-bearing phrase 'no registered executor or descriptor' and names the offending flow, with unknownTypes + the FULL knownTypes vocabulary in the record's structured meta slot (#6654 — both lists ride meta, never the message text); (3) triggering that flow answers success:false carrying the NO_EXECUTOR-class message "No executor registered for node type 'bogus_node'"; (4) the run is recorded status 'failed', with the offending step status 'failure' and error.code 'NO_EXECUTOR'. Rationale, do not re-litigate: ADR-0018 §M1 has registerFlow() soft-validate node types against the live registry ('warn, don't hard-fail') so a flow authored against a currently-absent plugin still registers, and #4771 is the ordering incident that seam protects — the check fires only once sealNodeTypeVocabulary() has closed the vocabulary, and only for an ENABLED flow"

oracle: api. Its verify states the disclaimer in the operative place a runner reads: "The 200 at registration is the EXPECTED answer and is never on its own a finding — the finding is any of the other three parts missing".

After — the rewritten negative

The negative no longer demands refusal; it names silence as the failure shape, so the #1887 anti-goal the old clause was reaching for is still guarded — just at the seam where it is real:

"the unknown-type probe going SILENT is the FAIL this clause guards — registration answering 200 with no WARN at all, or a WARN that drops the phrase 'no registered executor or descriptor' / omits meta.unknownTypes / omits the full meta.knownTypes vocabulary, is the #1887 silent-inert-node failure shape. So is the run side going quiet: a trigger answering success:true, a run not recorded 'failed', or a failed run whose step carries no NO_EXECUTOR error code. ⛔ The 200 itself is NOT a finding — #7545 ruled the registration-time soft-fail intended (ADR-0018 §M1, #4771); a run reporting FAIL on the 200 alone is misreading this item"

Also in this item

Accuracy notes against the source, beyond the ruling text

Two details were tightened against engine.ts rather than copied from the issue body, both stated in the clause:

Verification

docs/qa is not a published package and this PR releases nothing ⇒ no changeset; skip-changeset label applied.

$ node scripts/checklist-select.mjs --self-test
✓ checklist-select self-test: 17 cases pass.
$ node scripts/checklist-select.mjs automation.flow-node-type-matrix
selector: automation.flow-node-type-matrix → 1 runnable item(s)
P1 mixed automation.flow-node-type-matrix
$ node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 7089 text file(s) -- 7089 tracked, 0 untracked-not-ignored;
skipped 5 binary; no raw ASCII control bytes).

node scripts/check-platform-checklist.mjs (the pnpm check:platform-checklist gate) reports exactly one problem, and it is pre-existing on main, unrelated to this item — already filed as #7347:

check-platform-checklist: 1 problem(s)
✗ coverage.json · qa: UNCLASSIFIED — the platform has this capability (liveness ledger
exists) but the checklist neither tests nor waives it.

Confirmed pre-existing by running the gate on a pristine origin/main worktree with this PR's changes absent — identical single problem, byte for byte. This PR neither adds nor clears it: the item's own structural checks (revision/history agreement, acceptance clauses each naming an oracle, the enumSource variants ratchet) all pass. coverage.json indexes this item by id only (automation.flow-node-type-matrix, no expectation text), so nothing there needed a matching edit — grepped before editing.

No reverse-verification theater: this is a prose clause, and accuracy against the ruling text plus the engine source is the review bar.


Generated by Claude Code

…node type is late-bound, not refused
The item's negative clause demanded that POST /api/v1/automation REFUSE a flow
carrying an unregistered node type. The platform never promised that: ADR-0018
§M1 has registerFlow() soft-validate node types against the live action registry
("warn, don't hard-fail") so a flow authored against a currently-absent plugin
still registers, and #4771 is the ordering incident that seam protects. QA run
#7516 therefore recorded a FAIL against a promise that does not exist.
The maintainer ruling on #7545 (2026-08-11) upheld the status quo: this is
deliberate late binding with a declaration, not tolerance masking an error.
Recorded, checklist-side only — zero engine code:
- new acceptance clause asserting the real four-part contract (200 + located
WARN carrying 'no registered executor or descriptor' with unknownTypes and the
full knownTypes vocabulary in the record's meta slot + trigger success:false
with the NO_EXECUTOR message + run recorded 'failed'), stating pass-by-design
explicitly and citing #7545 / ADR-0018 §M1 / #4771
- a driving step for the unknown-type probe
- the negative rewritten to name SILENCE as the failure shape, with the 200
explicitly disclaimed as a finding
- source anchors for the ADR section, the engine seam and the ruling
- revision 2 -> 3 with the matching history entry
Fixes#7545
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fkdTyGmMD5s8ZtEifvuGy
@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 11, 2026 10:09am

Request Review

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] One seam, two strictness levels: POST /api/v1/automation hard-refuses an undeclared node config key but accepts an unknown node type

2 participants

@os-help@claude