Skip to content

docs(skills): a flow door's innerRefusal is the refusal any node met, not only a write's - #33390

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-co1yph
Nov 14, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-co1yph

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #32379

domain:skills#1 · os-project-manager · session_01FEkQ5nPZFfbs98ogV2mAFM

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:objectstack-api 技能里流程入口 400 FLOW_FAILED 一段,原文把 details.innerRefusal 说成「节点的写入被拒」并只列三个错误码;现改为「节点遇到的拒绝」(数据节点的读或写、script 节点的函数名),错误码改为举例并补上 RESOURCE_CONFLICT。另在「apis: 还是代码处理器」表格的 script 行加一个链接,指向 automation 技能里讲「到底运行哪个函数」的条目,不复述。
  • 为什么改:自 PR fix(service-automation)!: a flow script node runs its function by package (ADR-0150 D3) #32257 起,script 节点的函数名在本包范围内有歧义或只在依赖闭包外,流程入口答 400 FLOW_FAILED,innerRefusal.code 为 RESOURCE_CONFLICT;按旧文按错误码分支的客户端会漏掉它。
  • 风险与回滚:纯技能文字,skills/objectstack-api/SKILL.md 改 5 行、净增 1 行,外加一个 changeset;不动 schema、不动运行时;回滚即还原本 PR。
  • 先看:skills/objectstack-api/SKILL.md 第 360–363 行(innerRefusal 句),再看第 133 行(表格链接)。
  • 两分钟验证:git diff origin/main -- skills/,预期只见上述两处:innerRefusal 句改为「a node met」并举 RESOURCE_CONFLICT,表格 script 行多一个指向 objectstack-automation「Valid-but-silently-wrong」第 3 条的链接。
  • 需要你做的:受管面 Tier H(skills/**):请批准或合并。

What changes

  • skills/objectstack-api/SKILL.md lines 360–363, the flow-door paragraph. innerRefusal? was "the { code, message } a node's write was refused with, code in its wire spelling (UNIQUE_VIOLATION, VALIDATION_FAILED, PERMISSION_DENIED)". It now reads "the { code, message } refusal a node met (a data node's read or write, a script node's function name), code an ErrorCode member in wire spelling (e.g. UNIQUE_VIOLATION, PERMISSION_DENIED, RESOURCE_CONFLICT)". The codes are examples now, not a set.
  • skills/objectstack-api/SKILL.md line 133, the "Choosing between apis: and a code handler" table. The script node row gains one link, "which one runs: objectstack-automation item 3", and restates nothing.
  • .changeset/32379-api-flow-inner-refusal-any-node.md: '@objectstack/skills': patch, carrying both declaration lines.

This resumes the released commit ab838308 (branch claude/project-thread-xb2225, Release comment 6807417751) and merges origin/main 6538ba48. Every claim the commit makes was re-read on that main (below); none moved, so the text is unchanged.

Why

Each claim, read on origin/main 6538ba48:

  • A script node's function-name refusal fills innerRefusal with RESOURCE_CONFLICT. packages/services/service-automation/src/builtin/screen-nodes.ts:651-660: when engine.resolveFunction throws a package-scoped refusal, the node returns code: 'RESOURCE_CONFLICT' plus refusalCarriedBy(err). Pinned at the flow door by packages/qa/dogfood/test/bare-name-one-holder-request-family.dogfood.test.ts:280-298 ([400, 'FLOW_FAILED'], details.innerRefusal.code === 'RESOURCE_CONFLICT') and in-engine by packages/services/service-automation/src/flow-function-package-scope.test.ts:134 (both from PR fix(service-automation)!: a flow script node runs its function by package (ADR-0150 D3) #32257).
  • A data node's read fills it too, not only a write. refusalCarriedBy is called by all four data-node catch arms in packages/services/service-automation/src/builtin/crud-nodes.ts (get_record :669, and :831, :947, :1026 for the three writes). It carries any declared 4xx throw (inner-refusal.ts:86-102).
  • code is an ErrorCode member. FlowInnerRefusalSchema.code is ErrorCode (packages/spec/src/api/automation-api.zod.ts:361-366).
  • The link target exists. skills/objectstack-automation/SKILL.md:230 is the heading ## Valid-but-silently-wrong (passes build, fails at runtime), and item 3 (:262-275) states the package rule for which function a script node runs (landed in PR docs(skills): teach the script node's package rule in the automation skill #32377). The anchor follows the form skills/objectstack-data/SKILL.md:221 already uses for another heading in that skill.

Acceptance notes

  • skills/ sweep (git grep for innerRefusal and FLOW_FAILED): this paragraph is the only skill text that teaches innerRefusal. No evals/*.json mentions it, so no eval was made false.
  • Not in scope, noted only: the spec's own .describe on FlowFailureDetailsSchema.innerRefusal (packages/spec/src/api/automation-api.zod.ts:640-642, mirrored in content/docs/references/api/automation-api.mdx:389 and :1140) still says "the refusal a node's write met". It is spec prose, outside this card's surface (carrier: none).

Verification

Head 9adabd41, base origin/main 6538ba48.

Line and token budget (skills/**)
  • skills/objectstack-api/SKILL.md: 431 to 432 lines, 20628 to 20841 bytes; token ratchet 5152 to 5206 (ceiling 6314, headroom 1108). No ceiling moved.
  • Whole package, all skills/*/SKILL.md (11 files): 4602 to 4603 lines. Bundle total (whole shipped tree): 244310 to 244364 tokens.
  • node scripts/pm/check-skill-line-ratchet.mjs: exit 0. node scripts/check-skills-token-ratchet.mjs: exit 0.
Derived gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 46 families for the two paths. Each was run on head 9adabd41, exit code captured before any pipe (record: project file pm-orders/skills/32379-gates-ran.txt):

  • 44 exit 0, among them check-adr-0087-registration (scan and --self-test), check-changeset-no-major, check-empty-changeset (scan and --self-test), check-skills-token-ratchet (scan and --self-test), @objectstack/spec check:skill-docs, check:changeset-gate-self-tests, check:doc-authoring, check:nul-bytes, check:pm-governed-merges, check:published-files, check:skill-compatibility, check:skill-frame-sync, check:skill-identifier-liveness, check:skill-links.
  • @objectstack/lint check:doc-formula-expressions exit 0, after building the @objectstack/lint... closure under the verify lock (VERDICT command-exit 0, 67s). check:dts-closure and check:sourcemap-no-sources-content exit 0 over the 5 packages that build left in dist/ only; CI builds every package and runs them in full.
  • NOT MEASURED, exit 3 PREREQUISITE NOT MET (each reads every package's dist/): check:dual-build-cjs-loads, check:lean-entry-closure. No package source is touched, so no full build was paid locally.
  • --ran reconciliation: "46 derived famil(ies) accounted for — 44 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)".
  • CI families are read from this PR's check-runs, not re-run here.
Tier and governed readings
  • dispatch-gates --tier: MANDATORY contract-review tier (skills/**, clause ①, the published catalog); no one-line exit for this surface.
  • node scripts/pm/check-governed-merges.mjs --branch claude/project-thread-co1yph: exit 3, GOVERNED, landing tier H (人合). 1 of 2 paths on the register (skills/**); the changeset is not on it. Size +15 / -4, under the 3000 threshold.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5nPZFfbs98ogV2mAFM

… not only a write's

The api skill's flow-door paragraph described `details.innerRefusal` as a
write refusal with a three-code list. A data node's refused read fills it
too, and so does a `script` node's ambiguous or out-of-reach function name
(`RESOURCE_CONFLICT`). The codes are now examples of any `ErrorCode` member.
The code-handler table links the `script` node row to the automation skill's
package rule instead of restating it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAkdBkcn8QBYvVe7z6y
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 14, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 9adabd41acd1db496b30e22f4c0c572b70d1488f
Local-runs: none
Scope: skills/objectstack-api/SKILL.md + none

Inputs: card #32379 (body and all five comments: triage 6784704236, claim 6787593131, Release 6807417751, claim 6881923473, os-dev-report 6882093316), PR #33390 (body, two-file list, net diff against merge base 6538ba48), and the check-runs on this head. Code outside the diff was read on origin/main df675575 to judge the skill text, not reviewed. Between the merge base and that main, no cited file moved except connector-nodes.ts (#33281), which is checked under ④.

① Derived judgments

  • Accept-set: no change. The diff touches no schema, no runtime and no validator: FlowFailureDetailsSchema and FlowInnerRefusalSchema (packages/spec/src/api/automation-api.zod.ts) are unchanged, and so is every producer. Named right in the PR (Metadata-protocol: none).
  • Public surface: the published skills catalog (@objectstack/skills, built from skills/**). One teaching sentence gets wider so it matches what main ships: details.innerRefusal is now taught as the refusal any node met, not only a write's, and its codes are examples rather than a closed set. One table row gains a link and restates nothing. Named right.
  • Client-facing effect: a reader who branched on the three listed codes as if they were the set is now told that the set is the ErrorCode enum. Nothing a client could do before is taken away. Right.

② Semver level

  • .changeset/32379-api-flow-inner-refusal-any-node.md: '@objectstack/skills': patch. The package is real (packages/skills/package.json, which ships the skills/** tree as dist). A text correction with Metadata-protocol: none takes patch under the changeset rule in AGENTS.md. Right.
  • Clause-②: no and Metadata-protocol: none each start a line in both the PR body and the changeset body. There is no widening or narrowing arm, which is correct because no accept-set moves.
  • Clause-②: no. Agreed.

③ Boundary flags

  • open_questions: empty. Nothing to answer.
  • Deviation 1 (no HTML-comment signed trailer from the dev): the dev's account holds. Note that the body on this head does end with a signed: HTML comment naming the seat session, so a later seat edit added it. That is body hygiene, not part of the contract. Accepted.
  • Deviation 2 (the card thread ran on its configured model, not the mandatory tier): answered by this record. It is the at-tier contract review for the skills/** face. Accepted.
  • Deviation 3 (the merge commit of origin/main uses git's default message with no trailer): it carries no content of its own, because the net diff is the released commit ab838308. Accepted.
  • Deviation 4 (the sentence's line numbers moved): informational only.
  • out_of_scope_findings (spec .describe drift, carrier: none): escalated. See ④ N1. It needs a follow-up card so it is not left without a carrier.
  • CI: in_progress, not read in the dev report: superseded by the head's check-runs. All 48 have completed: 30 success, 18 skipped, none failed, cancelled or timed out. Those conclusions are the gate verdicts. Green on this head: Check Changeset, Lint · docs & code guards, Lint · repo & tooling gates, Lint · PM dispatch-gates, Governed Surface Queue Guard, both PR-body guards runs, Build Core, and every Test Core and Type Check run. The Queue verdict and Borrowed queue verdict runs were skipped.

④ Design judgment

skills/objectstack-api/SKILL.md, the flow-door paragraph (lines 360 to 363), checked sentence by sentence against main:

  • "the { code, message } refusal a node met (a data node's read or write, ...)": holds. On main, refusalCarriedBy (packages/services/service-automation/src/inner-refusal.ts:86) runs in all four data-node catch arms of builtin/crud-nodes.ts: get_record at :669, and create_record, update_record and delete_record at :831, :947 and :1026. There are no other data nodes among the builtin executors. A read refusal therefore fills innerRefusal, exactly as a write refusal does.
  • "... a script node's function name": holds, and the wording is no wider than the code. builtin/screen-nodes.ts:651-660 carries the refusal only when engine.resolveFunction throws a package-scoped refusal (isPackageScopedRefusal). When the function itself throws, the node returns a bare error (:707-713) with no innerRefusal, so the narrow parenthetical is right. I tried to break the enumeration in three ways, and it held each time:
  • "code an ErrorCode member in wire spelling (e.g. UNIQUE_VIOLATION, PERMISSION_DENIED, RESOURCE_CONFLICT)": holds. FlowInnerRefusalSchema.code is ErrorCode (automation-api.zod.ts:361-366). RESOURCE_CONFLICT and PERMISSION_DENIED are standard members (errors.zod.ts:79 and :95), and UNIQUE_VIOLATION is a registered ledger member (error-code-ledger.zod.ts:1280). Making the codes examples matches the schema's closed set.
  • The scenario on the card (a client branching on the listed codes misses a script node's RESOURCE_CONFLICT) is closed. The flow door's answer is pinned by packages/qa/dogfood/test/bare-name-one-holder-request-family.dogfood.test.ts:280-298: [400, 'FLOW_FAILED'] with details.innerRefusal.code equal to RESOURCE_CONFLICT.
  • The render ladder and the "absent keys" sentence are unchanged and still agree with the FlowFailureDetailsSchema jsdoc.

skills/objectstack-api/SKILL.md, the table row (line 133):

  • The link target exists. skills/objectstack-automation/SKILL.md:230 is ## Valid-but-silently-wrong (passes build, fails at runtime), whose slug is the anchor the row uses. Item 3 (:262-275) is the package rule for which function a script node runs, and it names the same 400 FLOW_FAILED / innerRefusal answer. The row restates nothing, as the card required (⛔ no restatement).
  • The api skill's only eval (evals/endpoints-auth-routes.json, id 3) still asks for a type: 'flow' endpoint with a script node naming a stack function. Its assertions (x-objectstack-signature, functions) are not made false by this change.

Findings:

  • must-fix: none.
  • should-fix: none.
  • N1, nit (outside this surface): the spec's own .describe on FlowFailureDetailsSchema.innerRefusal (automation-api.zod.ts:640-642, mirrored in content/docs/references/api/automation-api.mdx) still says "the refusal a node's write met". The skill is now right and the spec prose under-describes. This needs a follow-up card on the spec lane. It is not fixed here.
  • N2, nit: "item 3" is a positional reference, so it would go stale if the automation skill's silently-wrong list were renumbered. check:skill-links checks only the anchor. Accepted deviation, because the card asked for exactly this link.

Implemented-by: claude/project-thread-co1yph
Reviewed-by: session_01FEkQ5pjSPT7ui2EUcunaVb

VERDICT: PASS

Adopted by the seat 2026-11-14T18:36Z · isolated sub-agent at CONTRACT_REVIEW_TIER · Ruling-ref: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb, option A, tapped 2026-11-14T15:37Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

维护者速读

  • 改了什么:skills/objectstack-api/SKILL.md 一段加一行表格链接,外加一个 patch changeset。流程门 400 FLOW_FAILED 的 details.innerRefusal 改为"任一节点遇到的拒绝":数据节点的读或写,或 script 节点的函数名;code 为任一 ErrorCode,UNIQUE_VIOLATION、PERMISSION_DENIED、RESOURCE_CONFLICT 只作举例。"apis: 还是代码处理器"表的 script 行链到自动化技能决定哪个函数运行的规则。
  • 为什么改:PR fix(service-automation)!: a flow script node runs its function by package (ADR-0150 D3) #32257 之后,script 节点函数名有歧义或不在依赖闭包内时,流程以 innerRefusal.code: RESOURCE_CONFLICT 失败;按旧的三码列表分支的客户端会漏掉它。
  • 风险与回滚:只改技能文字,不动 schema、运行时与 eval;回滚即还原本 PR。遗留一处:spec 自己的 .describe 仍写"节点的写",另立卡跟进。
  • 先看:skills/objectstack-api/SKILL.md 第 360–363 行,再看第 133 行的表格链接。
  • 两分钟验证:git diff origin/main -- skills/objectstack-api/SKILL.md,只见这两处。
  • 需要你做的:受管面 Tier H(skills/**),契约复审 PASS 评论 6882343575(Head-sha 9adabd41acd1db496b30e22f4c0c572b70d1488f)已在案,请批准或直接合并。
  • 席位意见:可以合。复审逐一核对了四个数据节点和 script 节点在 main 上填 innerRefusal 的位置,措辞与代码一致。

席位核对

Verdict: ACCEPT, held for the maintainer · head 9adabd41acd1db496b30e22f4c0c572b70d1488f · card #32379 · report 6882093316 · contract review 6882343575 (PASS on this head, CONTRACT_REVIEW_TIER; Ruling-ref for the in-seat review: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb option A).

  • Shape: draft → main, Fixes #32379. skills/objectstack-api/SKILL.md (+5 / -4) and one '@objectstack/skills': patch changeset. Resumed from the released claude/project-thread-xb2225@ab838308 plus a merge of main; every claim held, text unchanged. GOVERNED, Tier H (skills/**). Clause-②: no; Metadata-protocol: none.
  • Changeset (TIER_DEFAULT): well-formed, accurate, correct level for a text correction to the published catalog.
  • Gates: 46 derived, 44 exit 0, 2 NOT MEASURED (dual-build-cjs-loads, lean-entry-closure; no package touched). Token ratchet 5206 of 6314, no raise. Check-runs on the head: 48, 30 success, 18 skipped, none failing. Mergeable clean.
  • Accepted deviations: N2, the link names "item 3", a list position that goes stale on renumbering (the card asked for exactly this link); the thread ran on the session model and the tier was met by this in-seat review; no HTML signed trailer from the dev (the seat added it).
  • Follow-up owed (N1): FlowFailureDetailsSchema.innerRefusal's .describe in packages/spec/src/api/automation-api.zod.ts still says "the refusal a node's write met", mirrored in content/docs/references/api/automation-api.mdx. Spec lane; the seat files it.
  • Landing: Tier H. The seat does not flip this PR ready or enqueue it. It needs the maintainer's merge, or an authorised APPROVED review after which the seat lands it.

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 14, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 14, 2026 21:28
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 14, 2026
Merged via the queue into main with commit d683e6f Nov 14, 2026
54 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-co1yph branch November 14, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

3 participants