Skip to content

feat(service-automation): definition-level input-schema refusal is non-retryable (FLOW_INPUT_SCHEMA_INVALID) - #13371

Merged
os-elon merged 3 commits into
mainfrom
claude/issue-10025-input-schema-refusal-nonretryable
Aug 30, 2026
Merged

feat(service-automation): definition-level input-schema refusal is non-retryable (FLOW_INPUT_SCHEMA_INVALID)#13371
os-elon merged 3 commits into
mainfrom
claude/issue-10025-input-schema-refusal-nonretryable

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10025

Implements the maintainer ruling (2026-08-20, Option B taken whole, recorded on the card): a definition-level input-schema refusal is non-retryable and classifies as a never-dispatched exit. The contract half landed first (#11504, PR #12611); this PR is the producer half plus the two docs rows the spec seat deliberately withheld until a producer existed.

What changed

Engine (packages/services/service-automation/src/engine.ts)

Transcription row (packages/runtime/src/flow-dispatch-status.ts)

Tests

  • input-schema-retry-parity.test.ts: the two expect(result.status).toBe('failed') assertions are inverted to toBeUndefined() plus expect(result.code).toBe('FLOW_INPUT_SCHEMA_INVALID'), the run-log row count drops from 3 to 1 (the retry-budget evidence), and the header paragraph that explained "why there is no code" — now stating the opposite of what the file asserts — is retired and replaced with the ruled classification. The third test (a schema-valid flow with a transient downstream failure still retries normally, runs.count === 2) is deliberately untouched.
  • endpoint-flow-dispatch-status.test.ts: the new row is asserted per-row (422 + code + producer message at the endpoint door), in the distinguishable-set pin (statuses [404, 409, 422, 422, 400], five distinct codes — two rows now share 422 by design), and in the three-door parity loop.

Docs (landing WITH the behaviour, per the recorded split)

  • content/docs/automation/flows.mdx — the trigger-refusal table gains the 422 FLOW_INPUT_SCHEMA_INVALID row (now at the five-outcome table around line 1442) with the non-retryable remedy text.
  • content/docs/api/client-sdk.mdx — the never-dispatched enumeration and the err.code union comment gain the member (around lines 720-741).

Changeset (patch, @objectstack/service-automation + @objectstack/runtime) states the deliberate behaviour change plainly: one refusal row instead of 1 + maxRetries, no retry budget or backoff consumed, 422 FLOW_INPUT_SCHEMA_INVALID where this exit previously answered 400 FLOW_FAILED.

Evidence

  • Reverse verification, direction declared before running: with engine.ts reverted to the branch base (3ec8646f1, parity floor only) and the new assertions in place, tests 1-2 went red exactly as predicted (result.code undefined vs 'FLOW_INPUT_SCHEMA_INVALID'), test 3 stayed green. Restore proven by blob hash (git hash-object == HEAD blob eba2d9a12c, git diff HEAD empty). The parity test resolves ./engine.js as an in-package relative import (source, not dist/), so no rebuild was needed for either leg; the mutation leg was confirmed on disk (InputSchemaViolationError grep count 0 under the reverted tree).
  • Full suites at final commit 208562689: @objectstack/service-automation 91 files / 1091 tests passed; @objectstack/runtime 201 files / 2970 tests passed; runtime typecheck clean (note: its tsconfig excludes **/*.test.ts, so the edited test file is outside that program — it is exercised by vitest and the DTS build instead). Both packages rebuilt with DTS success.
  • Gate families derived from the actual changed set (scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, answer derived at commit 208562689): all ~50 matched families run locally and green, including check:dispatcher-error-vocabulary, check:error-code-casing, the docs-audit family, check:engine-double-contract, check:where-matcher, check:type-check-debt (--re-measure: 30 entries, none above recorded) and check:dual-build-cjs-loads (102 entry points load). Repo-wide pnpm lint (eslint, full population — no narrowing) exit 0. Two gates exited 3 = PREREQUISITE NOT MET, i.e. NOT MEASURED locally, left to CI: check-test-completeness.mjs (needs CI shard attestations) and pm/check-half-states.mjs (needs board-sweep context).

Scope notes

Generated by Claude Code


Generated by Claude Code

…n-retryable (FLOW_INPUT_SCHEMA_INVALID)
Fixes#10025
Implements the maintainer ruling (2026-08-20, Option B taken whole): the
definition-level input-schema guard's verdict is a pure function of the flow
definition, so execute() now classifies its throw as a never-dispatched exit
- typed InputSchemaViolationError recognised in the catch, stamped with the
spec-registered FLOW_INPUT_SCHEMA_INVALID code (contract half: #11504), no
status, and no handoff to retryExecution. One refusal row in the run log
instead of 1 + maxRetries identical ones. The #9889 parity floor underneath
is untouched: both attempt paths still call the guard identically.
The FlowRefusalCode transcription row maps the code to 422 in the shared
classifyFlowRefusal table, so all three doors answer it identically. The
trigger-refusal docs table and the SDK never-dispatched enumeration gain the
member in the same change, ending the docs-side declared-not-enforced gap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
@github-actions

github-actionsBot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/runtime, @objectstack/service-automation, touching 5 documentable anchor(s). ⚠️1 changed file(s) yielded no anchor (packages/runtime/src/domains/automation.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx(via AutomationEngine (symbol))
  • content/docs/releases/v17.mdx(via AutomationEngine (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/runtime/src/domains/automation.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 26 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c38b7eff7816751f5b7c45cd5de5cb094a5262cbpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 0d4b57142da72a98be960e8c89cc7f3d723aa0d6 — the merge of head 207fd0dbcf166b942f6f644e49a496ea2ce6b2eb into base c38b7eff7816751f5b7c45cd5de5cb094a5262cb, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0d4b57142da72a98be960e8c89cc7f3d723aa0d6 && git checkout 0d4b57142da72a98be960e8c89cc7f3d723aa0d6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c38b7eff7816751f5b7c45cd5de5cb094a5262cb 207fd0dbcf166b942f6f644e49a496ea2ce6b2eb && git checkout -B drift-repro c38b7eff7816751f5b7c45cd5de5cb094a5262cb && git merge --no-ff 207fd0dbcf166b942f6f644e49a496ea2ce6b2eb
node scripts/docs-audit/affected-docs.mjs --json c38b7eff7816751f5b7c45cd5de5cb094a5262cb

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs c38b7eff7816751f5b7c45cd5de5cb094a5262cb → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 30, 2026
@os-elon
os-elon marked this pull request as ready for review August 30, 2026 10:22
@os-elon
os-elon enabled auto-merge August 30, 2026 10:22
@os-elon
os-elon added this pull request to the merge queueAug 30, 2026
@os-elon
os-elon requested a review from os-zhuangAugust 30, 2026 10:37
@os-elon
os-elon marked this pull request as draft August 30, 2026 10:37
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 30, 2026
@zhuangjianguo
zhuangjianguo marked this pull request as ready for review August 30, 2026 10:53
@zhuangjianguo
zhuangjianguo added this pull request to the merge queueAug 30, 2026
@zhuangjianguoClaude

Copy link
Copy Markdown
Collaborator

Director seat (session session_01DxbNgzPMo4YuRBmGmCQp9m), re-executing the recorded contract-review PASS disposition: verdict on card #10025 (2026-08-30T10:22:09Z) is pinned to this exact head 2085626896 and ends "清 needs:contract-review(卡 + PR),ready + auto-merge". The 10:22Z un-park questioned in #13412 was that review chain acting legitimately; the 10:36Z re-park was a good-faith revert of a completed review. Label cleared, ready, auto-merge re-armed — no new delta to review since the verdict head. Full reasoning on #13412.


Generated by Claude Code

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 30, 2026
@os-elon
os-elon added this pull request to the merge queueAug 30, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 30, 2026
@os-elon
os-elon enabled auto-merge August 30, 2026 12:12
The comment-only hunk this branch adds to
packages/runtime/src/domains/automation.ts inserted 7 net lines of doc
comment above the anonymous-deny seam, shifting the `ec.isSystem` read from
line 982 to line 989. Row 52 of content/docs/permissions/system-context.mdx
still anchored 982, so check-system-context-census reported the pair it
reports for pure line rot:
[site-without-a-row] automation.ts:989 reads `ec.isSystem`, no row
anchors it
[anchor-is-not-a-read-site] the page anchors automation.ts:982, which is
not a read site
Repaired with the gate's own sanctioned path,
`node scripts/check-system-context-census.mjs --fix`, which rewrote exactly
one anchor and nothing else.
The population did not move: the census still finds 109 elevation read sites
in 20 packages across 45 files, the page already declares 109, and no
[declared-count] problem was reported before or after. No count was
hand-edited and no census row was hand-written or deleted. No changeset --
a docs-anchor repair inside this branch adds no user-visible change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
@os-elon
os-elon added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit b6d3d76Aug 30, 2026
34 checks passed
@os-elon
os-elon deleted the claude/issue-10025-input-schema-refusal-nonretryable branch August 30, 2026 14:09
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

4 participants

@os-elon@zhuangjianguo@os-zhuang@claude