Skip to content

spec+executor: a wait node with no waitEventConfig parses clean and suspends forever with success: true — require the block (and timerDuration for timers) at the contract, retire the executor's ?? 'timer'; declare http_request.method / screen.mode defaults per the executor (objectui#9354's objectstack half) #17928

Description

@os-tesla

Filed by the director seat (decision batch #127 item 5, ruled by the maintainer on 2026-09-13; ruling recorded on objectui#9354). ⛔ Not claimed, ⛔ not dispatched. Graded here because the ruling names the priority and the landing point.

The ruling this card executes

Maintainer, verbatim (live PM chat, 2026-09-13T05:5xZ), to decision batch #127 presented as 1B·2A·3B·4B·5C′: 「16678 具体解释,计划用哪个字段判断经理。其他同意」 — item 5 (C′) is covered by 「其他同意」. C′, as presented: the protocol is the source of truth; a designer never invents a default the protocol does not apply; a default the protocol should have is declared by the protocol; a required key has no "unset behaves as". The objectui half (the designer's two required-enum declarations are deleted now) is objectui#9354; this card is the objectstack half.

What is measured, and by whom

  1. A wait node with no waitEventConfig parses clean and suspends forever, silently, answering success: true. Triage's source reading on origin/main (packages/services/service-automation/src/builtin/wait-node.ts, execute; measurement request(flow executor): what does a wait / boundary_event node with NO config block do at run time? — the parse contract accepts it, and objectui's inspector already advertises it as a timer #17843 comment 5651444086): the block is optional at the contract, the executor applies eventType ?? 'timer' with a comment declaring that intended, timerDuration is absent so no waitUntil is written and no job is scheduled, the only warn fires only when no job service exists, and the node returns { success: true, suspend: true }. ⚠️ Source reading, ⛔ not an observed run — measurement request(flow executor): what does a wait / boundary_event node with NO config block do at run time? — the parse contract accepts it, and objectui's inspector already advertises it as a timer #17843 stays open for the real run and its result is this card's first acceptance line.
  2. Inside a present block, eventType is REQUIRED for both wait.waitEventConfig and boundary_event.boundaryConfig (objectui#9109's table, rows 9–10, installed spec 17.4.0). So the contract refuses an omitted key but accepts an omitted block — two documents, two verdicts, and the accepted one is the silent one.
  3. HttpConfigSchema.method is z.string().optional() with no default; ScreenConfigSchema.mode is an optional enum with no default (same table, rows 7–8). What the executors apply for an absent value is NOT MEASURED (PR objectui#9279's own caveat: "the installed spec's Zod applies no default is NOT the runtime applies no default").
  4. boundary_event's executor on an absent boundaryConfig is NOT MEASURED (measurement request(flow executor): what does a wait / boundary_event node with NO config block do at run time? — the parse contract accepts it, and objectui's inspector already advertises it as a timer #17843 read the wait branch only).

What lands

A — the contract stops accepting the silent shape (packages/spec)

  • FlowNodeSchema: waitEventConfig is required when type: 'wait'; boundaryConfig is required when type: 'boundary_event'. With eventType already required inside each block, a wait / boundary node then always says what it waits for.
  • WaitEventConfigSchema: timerDuration is required when eventType: 'timer' (a timer with no duration is the forever-hang; refuse it at authoring with a remedy naming the key).
  • Accept-set narrowing ⇒ Clause-②: no; changeset under the launch-window convention (minor, BREAKING banner); ADR-0087 semantic migration entry: a stored wait node with no block has no lossless conversion (the author's intent is unknown) ⇒ structured TODO naming the node id.

B — the executor follows the contract (packages/services/service-automation)

  • wait-node.ts: the ?? 'timer' fallback and the ?? {} block fallback are retired; a node that reaches execute without the block (a stored pre-migration document) fails the run loudly with a named error — ⛔ never suspends with success: true. The comment "a wait node without one is a VALID TIMER WAIT" is deleted with the reversal named.
  • Read boundary_event's executor on an absent block; apply the same treatment if it defaults silently.
  • Pin both directions: absent block ⇒ named failure; present timer block with duration ⇒ job scheduled, waitUntil written.

C — the two optional keys get the default the executor actually applies, or none

  • Read what the http_request executor sends when method is absent. If it sends GET (the universal HTTP default), HttpConfigSchema.method declares .default('GET') and the reference page renders it; if it refuses or does something else, that is recorded and the key stays default-free (objectui then deletes its declaration).
  • Same for screen.mode and 'create'.
  • ⛔ The two required enums (wait.eventType, boundary_event.eventType) get NO default here: the maintainer ruled that a required key has no "unset behaves as", and the designer's declarations for them are deleted on the objectui side (objectui#9354).

Cross-domain declaration

Landing points: packages/spec/src/automation/** (A, C) and packages/services/service-automation/src/builtin/wait-node.ts (+ the boundary executor) (B). Routed domain:spec — the protocol decides and the executor follows it — with the service-automation file face declared in the claim comment per the cross-domain exception path; ⛔ not two PRs that could land the executor change without the contract change or vice versa.

Acceptance

  1. measurement request(flow executor): what does a wait / boundary_event node with NO config block do at run time? — the parse contract accepts it, and objectui's inspector already advertises it as a timer #17843's real run confirms (or refutes) the silent hang; if refuted, section B narrows to whatever the run shows and objectui#9354's p1 is re-graded by triage.
  2. After A+B: a wait node with no block is refused at parse with a message naming waitEventConfig; a stored one fails the run with a named error; a timer wait with no timerDuration is refused at parse.
  3. After C: objectui's declared-default ledger (UNBACKED_REGISTER, PR objectui#9279) re-measures the two optional rows against the adopted spec and the entries are removed in the version-adoption PR; the register must read 0 for http_request.method / screen.mode once the spec carries whatever this card declares.
  4. objectui#9354 is notified on landing (its two remaining declarations follow this card's answer for C).

Relations

priority:p1 per the triage escalation on objectui#9109 / objectui#9354 (silent forever-hang reachable from the designer's default state); type Bug; pm:queue.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:specpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions