Skip to content

feat(spec): register FLOW_INPUT_SCHEMA_INVALID — the never-dispatched ADR-0112 code for the definition-level input-schema refusal - #12611

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-11504-flow-input-schema-code
Aug 26, 2026
Merged

feat(spec): register FLOW_INPUT_SCHEMA_INVALID — the never-dispatched ADR-0112 code for the definition-level input-schema refusal#12611
os-zhuang merged 1 commit into
mainfrom
claude/issue-11504-flow-input-schema-code

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#11504

The ruled contract half of #10025's Option B (maintainer ruling 2026-08-20, recorded in comment 5353928368 on #10025, provenance verbatim 「其他接受你的建议。」): the definition-level input-schema refusal becomes non-retryable and receives its own ADR-0112 code as a never-dispatched exit, mirroring FLOW_DISABLED / FLOW_NO_START_NODE (the #9378 classification). #10025 remains open — its services half (the execute() catch short-circuit, the test inversions) is not addressed here and stays blocked on this PR landing.

What this PR does

  1. packages/spec/src/contracts/automation-service.tsAutomationResult.code (a closed union, the [Decision] AutomationResult.code is a CLOSED union that cannot carry FLOW_FAILED — #8684's ruled producer-first shape landed without the spec edit #9384 ruling) gains one member, FLOW_INPUT_SCHEMA_INVALID, documented in the trigger-time-refusals block: classified BEFORE dispatch, carries no status, transport maps it to 422 exactly as FLOW_NO_START_NODE (unexecutable stored definition), and distinct from it on purpose — that one says the definition has nothing to dispatch, this one says a node's static config contradicts the inputSchema the definition itself declares.
  2. packages/spec/src/api/error-code-ledger.zod.ts — one ERROR_CODE_LEDGER row under @objectstack/runtime (alphabetical slot between FLOW_FAILED and FLOW_NO_START_NODE) with the required TSDoc justification: never-dispatched semantics, non-retryable because the guard's verdict is a pure function of the flow definition, ONE refusal row instead of 1 + maxRetries identical failed rows, registered ahead of its producer by design (the /api/v1/analytics/query still drops per-measure and dataset-level filter on the ObjectQL path — engine.aggregate receives no filter at all #10413engine.aggregate: add per-aggregation filter to the contract — ruled half of #10413 (measure-level filters on the ObjectQL analytics path) #10576 split shape), and registered under the trigger door's package for the same reason as its three FLOW_* siblings.
  3. Testserror-code-ledger.test.ts pins ErrorCode.parse, ledger containment under @objectstack/runtime, and standardSynonymOf answering undefined (no waiver needed); automation-service.test.ts pins the union member at compile level plus the never-dispatched shape (status absent).
  4. Generated artifactscontent/docs/references/api/contract.mdx and error-code-ledger.mdx regenerated via gen:docs; check:generated reports all 14 artifacts current.
  5. Changeset (minor, @objectstack/spec) — states plainly, per the ruling: retry accounting and run-log volume change for affected flows once the services half lands (1 + maxRetries identical failed rows become one row carrying the new code).

Decisions recorded (this seat owns them per the dispatch)

Verification (all at head 16a65fd84, the final commit; origin/main re-fetched immediately before the PR — unchanged since branch point 7c0d0c395)

  • pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 — vitest verdict line: Test Files 432 passed (432) / Tests 11503 passed (11503).
  • pnpm --filter @objectstack/spec typecheck — all three layers green; final gate's own verdict line: check:test-typecheck: OK — @objectstack/spec's test layer compiles. Both edited test files confirmed INSIDE the tsconfig.test.json program via tsc --listFiles (not merely executed).
  • pnpm --filter @objectstack/spec check:generated — its verdict line: All 14 generated artifacts are up to date.
  • Gate batch (single set -e run, lock-serialized, exit 0): check:nul-bytes, check:error-code-casing (no unlisted lowercase error codes in 4783 scanned file(s)), check:dispatcher-error-vocabulary (OK — 21 unregistered code-stamping site(s), all classified), check:engine-double-contract (OK — 689 pinned, 134 in the DEBT ledger, 3 exempt), check:where-matcher, check-adr-0087-registration (changeset is additive/minor — no disposition marker required), check-changeset-no-major, check-empty-changeset.
  • Reverse verification against the rebuilt dist, from a consumer package (packages/runtime), both directions: a probe assigning the new member to AutomationResult compiles (exit 0 — impossible against the pre-change dist), and a probe assigning a bogus member fails with TS2322 whose message prints the widened nine-member union — proof the reading is the rebuilt .d.ts, not cache. Probes deleted after measurement.
  • ESLint, declared narrowing (repo sweep is CI-owned): targeted run over all 7 changed paths with --no-inline-config --format json — 4 TS files linted, 0 errors 0 warnings; the 3 non-TS paths answered ESLint's own "File ignored because no matching configuration was supplied" (population statement from the config, not a guess); invariance: eslint.config.mjs states (line ~328) no parserOptions.project / no typed rules, so this diff cannot move any untouched file's lint verdict.

Out of scope, untouched

packages/services/service-automation/**, the input-schema-retry-parity test, and packages/runtime/src/flow-dispatch-status.ts — all #10025's half. No labels or assignees changed by this PR. Clause-② applies (public envelope addition, contract-review tier): this PR stays draft; landing is the review flow's call.


Generated by Claude Code

… input-schema refusal's never-dispatched ADR-0112 code
The contract half of the #10025 ruling (maintainer 2026-08-20, Option B taken
whole): a definition-level input-schema refusal is non-retryable and receives
its own ADR-0112 code as a never-dispatched exit, mirroring FLOW_DISABLED /
FLOW_NO_START_NODE (#9378 classification).
- spec: AutomationResult.code gains 'FLOW_INPUT_SCHEMA_INVALID', documented as
a trigger-time refusal classified BEFORE dispatch (no status; transport maps
it to 422 like FLOW_NO_START_NODE — unexecutable stored definition).
- ledger: one ERROR_CODE_LEDGER row under @objectstack/runtime with the
required TSDoc justification: never-dispatched semantics, non-retryable per
the #10025 ruling, one refusal row instead of 1 + maxRetries identical
failed rows; registered ahead of its producer by design (the #10413 to
#10576 split shape) — the emitting half is #10025's and asserts this exact
string by value.
- tests: ErrorCode.parse / ledger-containment / no-synonym pins in
error-code-ledger.test.ts; a compile-level union pin plus the
status-absent never-dispatched shape in automation-service.test.ts.
- generated: contract.mdx / error-code-ledger.mdx regenerated via gen:docs.
- changeset (minor): states plainly that retry accounting and run-log volume
change for affected flows once the services half lands (N identical failed
rows become one).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012xGvxcwPRTJfA7RfjXEYA4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 2 documentable anchor(s).

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx(via AutomationResult (symbol), ERROR_CODE_LEDGER (symbol))
  • content/docs/api/error-catalog.mdx(via ERROR_CODE_LEDGER (symbol))
  • content/docs/api/error-handling-server.mdx(via ERROR_CODE_LEDGER (symbol))
  • content/docs/automation/flows.mdx(via AutomationResult (symbol))
  • content/docs/kernel/contracts/data-engine.mdx(via ERROR_CODE_LEDGER (symbol))

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

  • content/docs/releases/v17.mdx(via AutomationResult (symbol), ERROR_CODE_LEDGER (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
  • 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 — 126 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 146f448a502893df21e85e1038005cbccdf7e7ffpackageMentionDocs.

Which tree this was computed on

This run read content/docs from f717f43878fa3eab6361d2cd6ddaf28b9d74c15e — the merge of head 16a65fd843d4979504612917ad57a8bd3ee84935 into base 146f448a502893df21e85e1038005cbccdf7e7ff, 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 f717f43878fa3eab6361d2cd6ddaf28b9d74c15e && git checkout f717f43878fa3eab6361d2cd6ddaf28b9d74c15e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 146f448a502893df21e85e1038005cbccdf7e7ff 16a65fd843d4979504612917ad57a8bd3ee84935 && git checkout -B drift-repro 146f448a502893df21e85e1038005cbccdf7e7ff && git merge --no-ff 16a65fd843d4979504612917ad57a8bd3ee84935
node scripts/docs-audit/affected-docs.mjs --json 146f448a502893df21e85e1038005cbccdf7e7ff

⚠️ 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 146f448a502893df21e85e1038005cbccdf7e7ff → 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 26, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 26, 2026 20:13
@os-zhuang
os-zhuang added this pull request to the merge queueAug 26, 2026
Merged via the queue into main with commit f90e820Aug 26, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11504-flow-input-schema-code branch August 26, 2026 20:35
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

2 participants

@os-zhuang@claude