feat(automation): write doors answer the canonicalized parsed flow (#12206, Option A) - #13525
feat(automation): write doors answer the canonicalized parsed flow (#12206, Option A)#13525os-project-manager wants to merge 3 commits into
Conversation
…12206, Option A) POST /automation and PUT /automation/:name relay the FlowParsed that registerFlow now returns — the same shape GET serves. SDK binds Promise<FlowParsed> on create/update; response schemas conformant; UpdateFlowRequestSchema requires the complete definition; SDK unit tests get registrable bodies; changeset carries the migration note. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
…aithful to registerFlow's return (#12206) Predicted red observed red: the old echo pin failed against the new answer (data undefined from a void double); the double now returns the parsed flow and the pin asserts the materialized version default. Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
📓 Docs Drift CheckThis PR changes 4 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 133 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b60626fb2f14b21ff692fa5820ac6131e3ac418c && git checkout b60626fb2f14b21ff692fa5820ac6131e3ac418c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 430a2783714f74b1c13add9a6347bedd32ea070a 8d2d0c43bb55f8a675b2a45e118966bbd903bab9 && git checkout -B drift-repro 430a2783714f74b1c13add9a6347bedd32ea070a && git merge --no-ff 8d2d0c43bb55f8a675b2a45e118966bbd903bab9
node scripts/docs-audit/affected-docs.mjs --json 430a2783714f74b1c13add9a6347bedd32ea070a
|
|
Docs Drift Check follow-up — hand-read verdict on the two flagged pages (symbol anchor
Zero doc edits were required: callers that ignore Generated by Claude Code Generated by Claude Code |
f734a5f to
8d2d0c4
Compare
Fixes #12206
Implements the maintainer's Option A ruling (2026-08-26, card comment 5421206583, verbatim 「12452 不处理,其他接受」) on the automation write-door response contracts, exactly as ruled — evidence base: survey 5420919914 (accepted 5420937223), re-verified unchanged at
240aad5f(5469341400). The#12452-related surface is untouched per the ruling; #12452 remains open and is not addressed here.Ruled items
POST /automationandPUT /automation/:namenow enddeps.success(registered)whereregisteredis the parsed flowregisterFlowstored — the same shapeGET /automation/:nameserves — instead of echoing the caller's pre-parse bytes (packages/runtime/src/domains/automation.ts). The plumbing is the contract itself:IAutomationService.registerFlowreturnsFlowParsed(previouslyvoid) — the value was already computed and discarded on every successful write (AutomationEngine.canonicalizeStoredFlow→registerFlow), so this is zero marginal compute and no new refusal surface. The PUT answer now always carriesname, which the old echo could omit.client.automation.create/.updatereturnPromise<FlowParsed>, replacing the two deliberatePromise<any>declarations; the justifying comments ("no published type describes what comes back") were falsified by the survey (CreateFlowResponseSchema/UpdateFlowResponseSchemaexist and promisedata: Flow) and fall out with theany. The twoexported-any-returns.jsonledger entries are deleted (shrink-only graduation; gate line: "no NEW exported callable of @objectstack/client resolves toany: 317 callables reached … 40 ledgered site(s) still open"), andreturn-type-precision.test.tscompile-pins both.CreateFlowResponseSchema/UpdateFlowResponseSchemaare now conformant instead of aspirational: the new end-to-end test (packages/client/src/automation-write-door-parsed-answer.test.ts, realAutomationEngine+ realHttpDispatcher+ realObjectStackClient, in-process socket) parses both schemas against the REAL wire bodies — the first route responses these schemas have ever seen.UpdateFlowRequestSchema.definitiondrops.partial(): the engine'sregisterFlowrunsFlowSchema.parse, so a partial body was always a 400 against a real server; the declaration now matches the complete definition the engine requires. A real partial-update capability remains a future feature card — none was built.packages/client/src/client.test.ts) now use registrable bodies, each pinned registrable by a realFlowSchema.parseof the exact wire body the SDK sends.edge.conditionstring →{dialect, source}type change, zero measured consumers (cloud repo unmeasured, declared as the gap).Ablation / reverse verification (direction predicted before each run; mutation and restore both proved on disk)
automation-api.zod.test.ts"should accept a partial update" failed against the tightenedUpdateFlowRequestSchema(ZodError:type/nodes/edgesrequired) — 1 failed / 42 passed — then replaced by a reject-partial + accept-complete pair, green.automation-register-error-class.test.ts"a well-formed body still registers…" failed withdata: undefined(its void double + the door now relayingregisterFlow's return) — the double now returnsparsed(faithful to the engine) and the pin asserts the materializedversion: 1the caller never wrote.return parsed→return undefined, marker grep 1→0/0→1 on disk): predicted red observed red on the new engine pin ("returns the canonicalized parsed flow it stored", exit 1) and both client e2e cases (exit 1); restore viagit checkout HEAD --, proven bygit hash-object= HEAD blob (1a3bf67f…); rerun green (exit 0 / 0).deps.success(registered)×2 reverted to the echo, marker grep 2→0/0→2 on disk): predicted red observed red on the updated runtime pin (echo carries noversion, exit 1); restore hash-verified (0ff5f6bd…= HEAD); rerun green (exit 0).@objectstack/service-automationthrough the vitest src alias; runtime domain tests are in-package src). The runtime→dist seam (client e2e over@objectstack/runtime) was deliberately not used as a leg for that reason.Gates (all at f734a5f unless noted; judgment lines quoted from the gates' own output)
dispatch-gates.mjs --repo objectstack-ai/objectstack, no hand-fed paths; provenance line: "gate list derived from the tree of 'objectstack-ai/objectstack'"): 28 families at the pre-regen head, +17 delta families after the docs regen commit — all green, includingcheck:docs("230 generated files in sync with packages/spec"),check:authorable-surface,check:generated,check:cross-package-test-inputs,check:test-source-alias,check:engine-double-contract,check:dual-build-cjs-loads("102 published require entry point(s) across 66 package(s) load"),check:skill-examples("260 prose examples type-check"),check:docs-single-h1,check:type-check-coverage,check:type-check-debt("29 ledger entr(ies) re-measured … none above its recorded number").check:exported-any-returnsgreen on the rebuilt client dist (line quoted in item 2).@objectstack/spec,@objectstack/client,@objectstack/runtimegreen (single&&chain, wrapper VERDICT command-exit 0).@objectstack/service-automationhas notypecheckscript (pre-existing ratchet-ledger posture) — not measured, not claimed. Runtime's tsc program excludes**/*.test.ts(pre-existing), so the edited runtime test files are vitest-executed but not tsc-measured.f734a5fa: spec 443 files / 11812 tests, client 31 / 407 (incl. the new e2e), runtime 201 / 2973, service-automation 91 / 1092 — PKG-EXITS all 0, each suite's exit captured before any pipe.Notes for review (clause-②: parked draft,
needs:contract-review)IAutomationService.registerFlowreturn-type change is implementer-facing: any third-partyregisterFlowmust now return the stored parsed flow. In-repo, the only typed implementer isAutomationEngine; route/test doubles were re-measured (spy doubles untyped; the two faithful doubles now returnparsed).body.definition ?? body) is pre-existing and untouched.create(name, definition: any)/update(name, definition: any)parameter types deliberately untouched: the ruling names the RETURN type; narrowing the request accept-set is a separate decision.Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
Generated by Claude Code
Generated by Claude Code