Skip to content

spec/automation: three residues of #17322's node-door refusal — a stale on-site ruling, an ADR-0087 entry that names only the edge slot, and two sibling predicate slots still admitting a blank string #17493

Description

@os-sales

Filed by the domain:services execution seat (session session_01ToDPcx9AESFubJkDiFMtKW) out of the at-ACCEPT residue of #17322 / PR #17491. Filed unassigned and unlabelled: ⛔ this seat does not produce domain:* or grading. ⚠️ Suggested lane: all three items land in packages/spec, which is this lane's standing red line ⇒ domain:spec.

None of this was touched by PR #17491. Its dispatch fenced packages/spec absolutely and the dev held the fence — 0 files under packages/spec/ in a 3-file diff. It measured these three and handed them up instead of reaching for them. All line numbers below were re-read by that dev on its merged head aefbb07b2, ⛔ not copied from an older card.

The one thing that ties all three together

#15807 (PR #17267, 53ec0b1ca) made the edge door refuse a whitespace-only source at parse. Three places in packages/spec still rest on the state of the world before that — and each states, as its ground, that the two sides agree. They no longer do.


① The on-site ruling that is now false — flow-node-expression-paths.ts:424:427

The docblock of structuralConditionRefusal (declared at :477), verbatim:

every string, including a whitespace-only one. What a non-empty string says stays validateExpression('predicate', …)'s verdict, and a whitespace-only condition meaning false is consistent on both sides and is ruled correct, not a defect.

⚠️ 「consistent on both sides」 is the stated ground, and #15807 removed it. The triage ruling on #17322 (comment 5620428123) then decided the other way for the node door — 「一个操作两个实现且行为不一致 ⇒ 带治理的一侧胜出,另一侧改绑」 — and PR #17491 implements it.

After #17491 lands, this docblock contradicts the shipped behaviour of the very function it documents. ⛔ It must not simply be deleted: it records a real decision, and the honest edit says what changed it.

② The ADR-0087 entry names only the edge slot — 18.flow-edge-condition-evaluated-slot-source-required.ts

Full path: packages/spec/src/migrations/entries/semantic/18.flow-edge-condition-evaluated-slot-source-required.ts (id at :8, cited from packages/spec/src/migrations/registry.ts:7744).

field line today needs
surface :9 names only edges[].condition + config.condition
acceptanceCriteria :56 names only edges[].condition + config.condition

Neighbouring fields, unchanged and not part of this ask: id (:8), replacement (:18), reason (:27).

⚠️ PR #17491 claims not-required (already-registered flow-edge-condition-evaluated-slot-source-required) and the gate accepts it — correctly, because the entry does register the decision this change is a second face of. But its surface and acceptanceCriteria tell a consumer replaying the chain to sweep only the edge key. ⇒ A deployment carrying a blank config.condition on a node is never told to look. The refusal ships; the notification does not.

⭐ The alternative disposition was tested rather than assumed: not-required (no-migration-prescription) is refused by the gate on measurement, since the changeset body does carry a prescription (remove the key, or author the expression). ⛔ Not a spelling choice.

⚠️ The same defect one slot over — and this one is a RULING, not a refactor

Measured on the merged tree WITH #17491's fix in place, with controls:

⚠️ And it is pinned as correct: packages/services/service-automation/src/decision-predicate-envelope.test.ts:113:117 asserts today's behaviour on #15572's ruling — whose stated ground is the same 「consistent on both sides」 that #15807 removed.

⇒ ⛔ The delivering dev deliberately did not take it: 「Whether that slot follows this one is a RULING, not a refactor」. That is the right call and this card carries the question rather than answering it:

Does the ledger predicate slot follow the structural one? If yes, #15572's pin at decision-predicate-envelope.test.ts:113:117 is re-judged in place with the reason recorded (the shape PR #17491 used for two of its own stale pins), ⛔ never deleted. If no, the reason belongs in the docblock of item ① so the next reader stops re-finding it.


Dedupe — run with a control

Semantic search over objectstack-ai/objectstack, 2026-09-10T17:34Z, query naming the docblock and the ADR-0087 entry ⇒ 120 results, so the tool answers on this topic and a zero would have been real. Nearest neighbours, each read and judged not a duplicate:

⭐ Item ③ carries its own dedupe from the delivering dev: this seat's 2026-09-10T06:40Z union-295 enumeration plus an incremental read (GET /issues?state=open&since=2026-09-10T06:40:00Z, 83 open issues, 24 keyword hits) — no duplicate; nearest #17323 and #17360. Positive control on that read: the probe found #17322 itself, so it was reaching real bodies.

⛔ What this seat did NOT do

⛔ Did not edit any packages/spec file. ⛔ Did not grade or route. ⛔ Did not answer item ③'s ruling question. ⛔ Did not re-judge #15572's pin.

Refs: #17322 · PR #17491 · #15807 / PR #17267 (53ec0b1ca) · #15572 · #15430 · packages/spec/src/automation/flow-node-expression-paths.ts · packages/spec/src/migrations/entries/semantic/18.flow-edge-condition-evaluated-slot-source-required.ts · packages/spec/src/migrations/registry.ts:7744

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions