You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PUT /api/v1/automation/:name answers 500 for the same four malformed flow bodies #8055 just reclassified on POST #8123
Found while implementing #8055. Filed rather than fixed there: #8055's scope is the POST / registration route, and this is a second route.
Symptom
PUT /api/v1/automation/:name (updateFlow) makes the identicalautomationService.registerFlow(name, definition) call the POST / branch makes, in the same file, with no classification around it — packages/runtime/src/domains/automation.ts, the parts.length === 1 && m === 'PUT' branch (locate by symbol; the POST branch is the one carrying the #8055 comment).
So every body #8055 measured as a wrong-class 500 on POST is still a wrong-class 500 on PUT:
a node missing label — 500 carrying a raw Zod issue array
Same two costs #7535 and #8055 spell out: a retry-on-5xx client re-sends a request that can never succeed, and an authoring agent reads "the server broke" instead of "your metadata is wrong".
Why it matters beyond the repeat
After #8055 lands, POST and PUT disagree about the class of an identical refusal on the same domain — the exact drift #7535's fix was shaped to avoid ("the two routes cannot disagree"). An author who publishes a flow through the update route gets a different answer from one who publishes through create, for the same broken definition.
Fix
One line, once #8055 is merged: wrap the PUT branch's registerFlow call the same way, reusing the flowDefinitionRefusal helper #8055 adds to that file. The helper was written module-local and route-agnostic for this. Note the PUT branch has its own body.definition ?? body unwrap, so the definition it forwards is not always the request body — worth a test case of its own.
Not licensed by this card
Widening the refusal, softening any refusal into a 200, or changing which bodies are rejected. Class and envelope only, as in #8055.
Blocked-by
Blocked-by: #8055 (the helper this reuses lands there).
PR #8125 (#8055) has merged, so the Blocked-by on this card is cleared and the module-local, route-agnostic flowDefinitionRefusal helper is on main.
This card goes first in the packages/runtime/src/domains/automation.ts serial chain — ahead of #8054 and #7968 — by PM ruling. The reason is stated on #8125: that merge left POST and PUT disagreeing about the class of an identical refusal on the same call in the same file, and "two doors answering differently" is the defect class this lane has now filed six times this shift. It was accepted as a strictly-better interim (before: both wrong; after: one right), on the explicit condition that the window closes next rather than eventually.
The dispatch's binding pin: both doors must be asserted to agree — same code, same status, same details.fields[] shape for the same malformed body — not PUT pinned in isolation. A per-route pin would let them drift apart again without reddening anything, which is how this gap opened in the first place.
⛔ Not licensed: changing which bodies are refused, altering the helper's behaviour for POST, or touching #8054's and #7968's concerns in the same file.
{
"issue": 8123,
"status": "done",
"branch": "claude/issue-8123-put-automation-error-class",
"pr": "https://github.com/objectstack-ai/objectstack/pull/8177",
"premise_still_valid": true,
"summary": "PUT /api/v1/automation/:name made the same automationService.registerFlow call POST / makes, in the same file, with no classification around it, so it still 500'd for all four malformed flow bodies #8055 fixed on POST. Wrapped the PUT branch's registerFlow call in the same try/catch POST uses, routed through the same module-local, route-agnostic flowDefinitionRefusal helper (unchanged) via deps.errorFromThrown(..., VALIDATION_FAILED_STATUS). Added a table-driven cross-door parity suite that drives both POST and PUT with the same four bodies and asserts the two responses agree on status, code, and details.fields[] shape, plus PUT-specific coverage for the #4277 message surviving verbatim and the PUT-only { definition } unwrap.",
"tests": "pnpm --filter '@objectstack/runtime^...' build (closure) -- clean. pnpm --filter @objectstack/runtime test -- --maxWorkers=2 -- 146 test files, 2205 tests, all pass. pnpm --filter @objectstack/runtime typecheck -- clean (no output, exit 0). pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' (full closure, required by the debt gate) -- 70/70 tasks successful. pnpm check:type-check-debt -- 'check-type-check-coverage --re-measure: OK -- 33 ledger entr(ies) re-measured in 324.8s, 1966 raw tsc error(s) total, none above its recorded number'; no drift note printed for @objectstack/runtime (stays at its recorded 227 ceiling). node scripts/check-nul-bytes.mjs -- OK (7413 files scanned, no raw control bytes). Reverse verification: reverted only the source fix (git checkout origin/main -- packages/runtime/src/domains/automation.ts), re-ran the parity file -- 9 of 9 PUT-touching tests went red for the expected reason (the uncaught throw escapes handleAutomationRequest at automation.ts:880, so the test's await rejects instead of observing a classified response), e.g. 'FAIL ... case missingNodeLabel: POST and PUT answer the SAME envelope ... ZodError: [{expected:string, code:invalid_type, path:[nodes,0,label], message:Invalid input: expected string, received undefined}] at handleAutomationRequest src/domains/automation.ts:880:35' and 'FAIL ... case undeclaredConfigKey ... Error: Flow welcome_flow rejected: 1 undeclared config key(s) (#4277). - node n (notify): unknown config key totallyBogusKey at config.totallyBogusKey ... at handleAutomationRequest src/domains/automation.ts:880:35'. The other 145 test files (2196 tests) stayed green throughout. Restored via git apply of the saved patch; git diff --stat HEAD on automation.ts was identical before and after the round trip (13 insertions, 1 deletion both times) and a byte diff against the saved patch was empty.",
"open_questions": [],
"out_of_scope_findings": []
}
(Correction: the previous comment's <!-- os-dev-report --> marker was HTML-entity-escaped by my own tool call and never rendered as a real comment marker — this comment supersedes it with the plain-text fallback per the reporting contract. Full report JSON below is identical to the previous comment's.)
{
"issue": 8123,
"status": "done",
"branch": "claude/issue-8123-put-automation-error-class",
"pr": "https://github.com/objectstack-ai/objectstack/pull/8177",
"premise_still_valid": true,
"summary": "PUT /api/v1/automation/:name made the same automationService.registerFlow call POST / makes, in the same file, with no classification around it, so it still 500'd for all four malformed flow bodies #8055 fixed on POST. Wrapped the PUT branch's registerFlow call in the same try/catch POST uses, routed through the same module-local, route-agnostic flowDefinitionRefusal helper (unchanged) via deps.errorFromThrown(..., VALIDATION_FAILED_STATUS). Added a table-driven cross-door parity suite that drives both POST and PUT with the same four bodies and asserts the two responses agree on status, code, and details.fields[] shape, plus PUT-specific coverage for the #4277 message surviving verbatim and the PUT-only { definition } unwrap.",
"tests": "pnpm --filter '@objectstack/runtime^...' build (closure) -- clean. pnpm --filter @objectstack/runtime test -- --maxWorkers=2 -- 146 test files, 2205 tests, all pass. pnpm --filter @objectstack/runtime typecheck -- clean (no output, exit 0). pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' (full closure, required by the debt gate) -- 70/70 tasks successful. pnpm check:type-check-debt -- 'check-type-check-coverage --re-measure: OK -- 33 ledger entr(ies) re-measured in 324.8s, 1966 raw tsc error(s) total, none above its recorded number'; no drift note printed for @objectstack/runtime (stays at its recorded 227 ceiling). node scripts/check-nul-bytes.mjs -- OK (7413 files scanned, no raw control bytes). Reverse verification: reverted only the source fix (git checkout origin/main -- packages/runtime/src/domains/automation.ts), re-ran the parity file -- 9 of 9 PUT-touching tests went red for the expected reason (the uncaught throw escapes handleAutomationRequest at automation.ts:880, so the test's await rejects instead of observing a classified response), e.g. 'FAIL ... case missingNodeLabel: POST and PUT answer the SAME envelope ... ZodError: [{expected:string, code:invalid_type, path:[nodes,0,label], message:Invalid input: expected string, received undefined}] at handleAutomationRequest src/domains/automation.ts:880:35' and 'FAIL ... case undeclaredConfigKey ... Error: Flow welcome_flow rejected: 1 undeclared config key(s) (#4277). - node n (notify): unknown config key totallyBogusKey at config.totallyBogusKey ... at handleAutomationRequest src/domains/automation.ts:880:35'. The other 145 test files (2196 tests) stayed green throughout. Restored via git apply of the saved patch; git diff --stat HEAD on automation.ts was identical before and after the round trip (13 insertions, 1 deletion both times) and a byte diff against the saved patch was empty.",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
hotlong commentedon Aug 12, 2026
Unblocked and dispatched —
domain:cliPM seat (#6024), sessionsession_01B3Kurx8qufrDzNjk4rag7V, branchclaude/issue-8123-put-automation-error-class.PR #8125 (#8055) has merged, so the
Blocked-byon this card is cleared and the module-local, route-agnosticflowDefinitionRefusalhelper is onmain.This card goes first in the
packages/runtime/src/domains/automation.tsserial chain — ahead of #8054 and #7968 — by PM ruling. The reason is stated on #8125: that merge leftPOSTandPUTdisagreeing about the class of an identical refusal on the same call in the same file, and "two doors answering differently" is the defect class this lane has now filed six times this shift. It was accepted as a strictly-better interim (before: both wrong; after: one right), on the explicit condition that the window closes next rather than eventually.The dispatch's binding pin: both doors must be asserted to agree — same
code, samestatus, samedetails.fields[]shape for the same malformed body — notPUTpinned in isolation. A per-route pin would let them drift apart again without reddening anything, which is how this gap opened in the first place.⛔ Not licensed: changing which bodies are refused, altering the helper's behaviour for
POST, or touching #8054's and #7968's concerns in the same file.Generated by Claude Code
hotlong commentedon Aug 12, 2026
<!-- os-dev-report -->
{ "issue": 8123, "status": "done", "branch": "claude/issue-8123-put-automation-error-class", "pr": "https://github.com/objectstack-ai/objectstack/pull/8177", "premise_still_valid": true, "summary": "PUT /api/v1/automation/:name made the same automationService.registerFlow call POST / makes, in the same file, with no classification around it, so it still 500'd for all four malformed flow bodies #8055 fixed on POST. Wrapped the PUT branch's registerFlow call in the same try/catch POST uses, routed through the same module-local, route-agnostic flowDefinitionRefusal helper (unchanged) via deps.errorFromThrown(..., VALIDATION_FAILED_STATUS). Added a table-driven cross-door parity suite that drives both POST and PUT with the same four bodies and asserts the two responses agree on status, code, and details.fields[] shape, plus PUT-specific coverage for the #4277 message surviving verbatim and the PUT-only { definition } unwrap.", "tests": "pnpm --filter '@objectstack/runtime^...' build (closure) -- clean. pnpm --filter @objectstack/runtime test -- --maxWorkers=2 -- 146 test files, 2205 tests, all pass. pnpm --filter @objectstack/runtime typecheck -- clean (no output, exit 0). pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' (full closure, required by the debt gate) -- 70/70 tasks successful. pnpm check:type-check-debt -- 'check-type-check-coverage --re-measure: OK -- 33 ledger entr(ies) re-measured in 324.8s, 1966 raw tsc error(s) total, none above its recorded number'; no drift note printed for @objectstack/runtime (stays at its recorded 227 ceiling). node scripts/check-nul-bytes.mjs -- OK (7413 files scanned, no raw control bytes). Reverse verification: reverted only the source fix (git checkout origin/main -- packages/runtime/src/domains/automation.ts), re-ran the parity file -- 9 of 9 PUT-touching tests went red for the expected reason (the uncaught throw escapes handleAutomationRequest at automation.ts:880, so the test's await rejects instead of observing a classified response), e.g. 'FAIL ... case missingNodeLabel: POST and PUT answer the SAME envelope ... ZodError: [{expected:string, code:invalid_type, path:[nodes,0,label], message:Invalid input: expected string, received undefined}] at handleAutomationRequest src/domains/automation.ts:880:35' and 'FAIL ... case undeclaredConfigKey ... Error: Flow welcome_flow rejected: 1 undeclared config key(s) (#4277). - node n (notify): unknown config key totallyBogusKey at config.totallyBogusKey ... at handleAutomationRequest src/domains/automation.ts:880:35'. The other 145 test files (2196 tests) stayed green throughout. Restored via git apply of the saved patch; git diff --stat HEAD on automation.ts was identical before and after the round trip (13 insertions, 1 deletion both times) and a byte diff against the saved patch was empty.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
hotlong commentedon Aug 12, 2026
os-dev-report
(Correction: the previous comment's
<!-- os-dev-report -->marker was HTML-entity-escaped by my own tool call and never rendered as a real comment marker — this comment supersedes it with the plain-text fallback per the reporting contract. Full report JSON below is identical to the previous comment's.){ "issue": 8123, "status": "done", "branch": "claude/issue-8123-put-automation-error-class", "pr": "https://github.com/objectstack-ai/objectstack/pull/8177", "premise_still_valid": true, "summary": "PUT /api/v1/automation/:name made the same automationService.registerFlow call POST / makes, in the same file, with no classification around it, so it still 500'd for all four malformed flow bodies #8055 fixed on POST. Wrapped the PUT branch's registerFlow call in the same try/catch POST uses, routed through the same module-local, route-agnostic flowDefinitionRefusal helper (unchanged) via deps.errorFromThrown(..., VALIDATION_FAILED_STATUS). Added a table-driven cross-door parity suite that drives both POST and PUT with the same four bodies and asserts the two responses agree on status, code, and details.fields[] shape, plus PUT-specific coverage for the #4277 message surviving verbatim and the PUT-only { definition } unwrap.", "tests": "pnpm --filter '@objectstack/runtime^...' build (closure) -- clean. pnpm --filter @objectstack/runtime test -- --maxWorkers=2 -- 146 test files, 2205 tests, all pass. pnpm --filter @objectstack/runtime typecheck -- clean (no output, exit 0). pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' (full closure, required by the debt gate) -- 70/70 tasks successful. pnpm check:type-check-debt -- 'check-type-check-coverage --re-measure: OK -- 33 ledger entr(ies) re-measured in 324.8s, 1966 raw tsc error(s) total, none above its recorded number'; no drift note printed for @objectstack/runtime (stays at its recorded 227 ceiling). node scripts/check-nul-bytes.mjs -- OK (7413 files scanned, no raw control bytes). Reverse verification: reverted only the source fix (git checkout origin/main -- packages/runtime/src/domains/automation.ts), re-ran the parity file -- 9 of 9 PUT-touching tests went red for the expected reason (the uncaught throw escapes handleAutomationRequest at automation.ts:880, so the test's await rejects instead of observing a classified response), e.g. 'FAIL ... case missingNodeLabel: POST and PUT answer the SAME envelope ... ZodError: [{expected:string, code:invalid_type, path:[nodes,0,label], message:Invalid input: expected string, received undefined}] at handleAutomationRequest src/domains/automation.ts:880:35' and 'FAIL ... case undeclaredConfigKey ... Error: Flow welcome_flow rejected: 1 undeclared config key(s) (#4277). - node n (notify): unknown config key totallyBogusKey at config.totallyBogusKey ... at handleAutomationRequest src/domains/automation.ts:880:35'. The other 145 test files (2196 tests) stayed green throughout. Restored via git apply of the saved patch; git diff --stat HEAD on automation.ts was identical before and after the round trip (13 insertions, 1 deletion both times) and a byte diff against the saved patch was empty.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
fix(runtime): PUT /automation/:name answers 400 VALIDATION_FAILED, no…