Skip to content

Showcase flow comments overstate behaviour: TaskCompletedRestPingFlow / ShowcaseDeclarativeConnectorPingFlow claim the connector response is captured on the run, but neither declares an isOutput variable #7542

Closed

Description

@huangyiirene

Symptom

The source comments on TaskCompletedRestPingFlow and ShowcaseDeclarativeConnectorPingFlow claim that "the call and its {status:'ok'} response are captured on the flow run".

They are not. Both flows dispatch correctly, but run.output is empty — neither flow declares an isOutput variable, so nothing from the connector call is ever surfaced on the run.

The comment is the damaging part. A reader — human or agent — takes it as a statement of platform behaviour and concludes that connector responses are captured automatically. When run.output then comes back empty, the natural reading is "the engine dropped the response", and the next step is to go hunting in the connector dispatch path for a bug that does not exist. The engine is fine; the fixture simply never asked for the value. A comment that describes behaviour the code does not implement is worse than no comment, because it redirects debugging away from the real cause.

This is an authoring gap, not an engine one, and the sibling fixture proves it: the MCP connector flow does declare an isOutput variable and its run output is captured correctly (upper=OBJECTSTACK). Same engine, same dispatch path, different fixture — one asks for the output, two do not.

Root cause

TaskCompletedRestPingFlow and ShowcaseDeclarativeConnectorPingFlow declare no isOutput variable. run.output is therefore legitimately empty, and the source comments describe an outcome the flows do not arrange for.

Fix — either direction, but do both halves of whichever you pick:

  1. Add the isOutput variable to both flows, matching what the MCP connector flow already does. This makes the comments true and makes the response genuinely inspectable on the run. Preferred: it is what the fixtures were evidently meant to demonstrate.
  2. Or correct the comments to say the response is not captured — and in that case also fix the checklist clause that asserts capture, since it is written against the comment's claim rather than the code's behaviour. Correcting the comment while leaving the clause asserting capture just relocates the same contradiction.

Reproduction

  1. Boot the showcase on an isolated file DB and port. (Note the connectors' hard-wired baseUrl will need working around on a non-3000 port — see the companion finding on StatusApi/StatusOpenApi.)
  2. Trigger TaskCompletedRestPingFlow; repeat for ShowcaseDeclarativeConnectorPingFlow.
  3. Read the resulting run → dispatch succeeded, but run.output is empty.
  4. Trigger the MCP connector flow for contrast → run.output carries upper=OBJECTSTACK.
  5. Compare the three flow definitions: only the MCP one declares an isOutput variable.

Re-check the fixtures:

rg -rn "TaskCompletedRestPingFlow|ShowcaseDeclarativeConnectorPingFlow" examples
rg -rn "isOutput" examples

Source

Extracted from the QA run #7516 (framework a86db17). Listed there as item 3 under "Fixture / authoring issues to fix"; it is what held connector-dispatch-matrix to PARTIAL.

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 11, 2026
  2. removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 11, 2026
  3. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings triage — resolved the filing-time pm:queue+finding dual state to pm:queue (finding removed; the pair corrupts both the queue view and the finding stock — same resolution class as the 7 QA-wave duals handled last round).

    • Grade rationale: concrete authoring defect with named fixtures, a scripted repro, a proving sibling fixture, and a stated preferred fix (add the isOutput variable both flows were evidently meant to carry). Nothing observation-class about it — dispatchable as filed.
    • Premise verified on origin/main @ 744b8f5: in examples/app-showcase/src/automation/flows/index.ts, neither TaskCompletedRestPingFlow nor ShowcaseDeclarativeConnectorPingFlow declares isOutput (the nearest isOutput hit belongs to the next flow in the file, ShowcaseMcpConnectorEchoFlow — exactly the card's contrast fixture, variables: [{ name: 'echo.structuredContent', type: 'json', isOutput: true }]). Premise holds.
    • Routing check: showcase automation fixtures exercise the flow/connector subsystem ⇒ filer's domain:services matches the domain table (examples/** belongs to the subsystem it exercises). Kept.
    • Dedup: companion card Showcase REST connectors hard-wire baseUrl to http://127.0.0.1:3000, so self-ping flows fail fetch failed on any isolated instance — a port mismatch, not an egress block #7538 (hard-wired baseUrl) is the separate port-mismatch defect the repro notes; no overlap in fix site.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 11, 2026
  5. self-assigned this
    on Aug 11, 2026
  6. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    Claim: PM loop, services lane, wave 2 round 3
    Session: session_015fkdTyGmMD5s8ZtEifvuGy
    Branch: claude/issue-7542-showcase-flow-isoutput
    Worktree: objectstack-issue-7542
    Domain: domain:services
    File surface: examples/app-showcase/src/automation/flows/index.ts (the TaskCompletedRestPingFlow / ShowcaseDeclarativeConnectorPingFlow declarations) + any flow-fixture test in the same app. Stop on breach.
    Container & model: S-grade fixture repair with triage-verified premise, the contrast fixture named (ShowcaseMcpConnectorEchoFlow's isOutput: true variable), mode:subagent, model: opus.
    Serial constraints cleared: ⚠️ batch-mate #7538 is in the SAME example app — fix sites are disjoint per triage's dedup (src/automation/flows/index.ts here vs src/system/connectors/* there). Region-level declaration is this line; both devs merge origin/main before opening their PR, and any conflict goes to the merge queue, not manual ordering. No open PR or other in-flight claim touches examples/app-showcase.


    Generated by Claude Code

  7. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    ACCEPT — PR #7618, reviewed against GitHub rather than the report's own claims.

    Verified in the diff (2 files, both inside the declared surface — src/system/connectors/ untouched, batch-mate #7538's face respected):

    • Route 1 executed with the name derived, not copied. The dispatch warned against blindly copying the contrast fixture's echo.structuredContent; the dev traced the real production chain — connector_action writes handler output under ${nodeId}.${key}, the rest connector's request returns { status, ok, body }, both flows' node id is ping — so the declared variable is ping.body. Both flows get variables: [{ name: 'ping.body', type: 'json', isOutput: true }], and the comments now name the variable so claim and declaration cannot drift apart again.
    • Runtime-capture verification, the stronger of the two levels offered. The new test drives the real flow definitions through a real AutomationEngine with the real connector bundles (plugin rest via createRestConnector, the ADR-0097 declarative instance via createRestProviderFactory fed the real StatusApiConnector metadata) — only fetch stubbed. Content pin: expect(result.output).toEqual({ 'ping.body': { status: 'ok' } }).
    • Reverse verification reproduces the symptom verbatim: fix removed → expected {} to deeply equal { 'ping.body': …} with the recorded outbound request still present — proving dispatch was never the defect, exactly the card's diagnosis. 3/3 red → restored 3/3 green.
    • Route 2 rejected on measurement, not preference: the capture works once asked for, so weakening the comments would have documented a defect instead of fixing an omission — and would have forced an edit to the connector-dispatch-matrix checklist clause, which under route 1 becomes true as written.
    • Suites: example-showcase 18 files / 171 tests green, typecheck clean, validate exit 0.
    • Changeset route judged correctly: examples/app-showcase is private: true, nothing releases ⇒ skip-changeset label. Applied at this review (label read-back to follow with the flip).

    Cross-batch note: the test asserts only the flow-owned path, deliberately staying green across #7538's env-overridable baseUrl — the two same-app cards remain non-interfering by construction.

    CI converging. Driving to landing: gate conclusions → ready → arm → queue verify.


    Generated by Claude Code

  8. os-help commented on Aug 11, 2026

    @os-help
    Collaborator
    {
      "issue": 7542,
      "status": "done",
      "branch": "claude/issue-7542-showcase-flow-isoutput",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/7618",
      "premise_still_valid": true,
      "summary": "Premise re-verified on origin/main (59768f72b, later than triage's 744b8f5): neither TaskCompletedRestPingFlow nor ShowcaseDeclarativeConnectorPingFlow declared a `variables` block at all, while their comments claimed the `{status:'ok'}` response was captured on the run — and `AutomationEngine.execute` collects `run.output` from declared `isOutput` variables only. Took the card's preferred route (add the variable, not weaken the comments), since the capture works fine once asked for. The variable name was derived from the step rather than copied from the contrast fixture: `connector_action` writes handler output back under `${nodeId}.${key}`, the rest connector's `request` action returns `{status, ok, body}` (and `showcase_status_api` is materialized by the same `createRestConnector` bundle via `createRestProviderFactory`), and both flows' connector node id is `ping` — so both now declare `{ name: 'ping.body', type: 'json', isOutput: true }`, with the comments tightened to name the variable. `src/system/connectors/` untouched (#7538's surface); origin/main merged before the PR.",
      "tests": "New examples/app-showcase/test/connector-ping-run-output.test.ts — RUNTIME capture level, not schema level: real flow definitions imported from src/automation/flows/index.ts, driven through a real AutomationEngine with the real createRestConnector / createRestProviderFactory bundles (only fetch stubbed), content-pinning `expect(result.output).toEqual({'ping.body': {status:'ok'}})` for both flows. A booted app was not needed. || `pnpm --filter @objectstack/example-showcase test` -> 'Test Files 18 passed (18) / Tests 171 passed (171)'. `typecheck` -> clean (tsc --noEmit, no output). `validate` -> exit 0, only pre-existing unrelated warnings. `node scripts/check-nul-bytes.mjs` -> 'OK (scanned 7056 text file(s) ... no raw ASCII control bytes)'; self-scan grep of both touched files -> no hits. || REVERSE VERIFICATION, predicted direction red-with-EMPTY-output, fix removed via `git checkout origin/main -- <path>` (never git stash): 3/3 red, 'AssertionError: expected {} to deeply equal { ping.body: { status: ok } }' on both dispatch cases — the issue's symptom reproduced verbatim — while the recorded outbound request was still present, confirming dispatch was never the defect. Fix restored -> 3/3 green. || CI at report time (card directs report-at-draft-PR, no idle-poll): Check Changeset SUCCESS (skip-changeset landed ahead of its re-read), Check PR Size / Auto Label / ADR approval / Console Pin Freshness / Doc Links / same-issue-claim SUCCESS, Test Core (3/3) SUCCESS; ESLint and TypeScript Type Check still in_progress, as are Build Core / Test Core 1-2 / Dogfood gates — no red at snapshot time, but neither ESLint nor Type Check has a conclusion yet.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Notes for the PM, outside the JSON:


    Generated by Claude Code


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions