Skip to content

fix(spec): os migrate meta --stored lists a stored connector_action whose connectorConfig.input carries a {…} token - #28249

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-vgdwpp
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-vgdwpp

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #28163

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:os migrate meta --stored 的“已存储但新版本会拒收”清单,现在会列出 connector_action 节点 connectorConfig.input 里带 {…} 旧模板记号的值,位置写成 connectorConfig.input.KEY,归在 connector-action-input-value-slot-judged 这条升级条目下。
  • 为什么改:上一版(fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150)让启动时的 registerFlow 拒收这种流程,但迁移预览只看节点的 config,看不到 connectorConfig,运维升级前用这个工具查不到它,升级后流程直接不注册、不运行。
  • 风险与回滚:只改“列出”这一步,不改任何存储行,也不改任何门的判定;config 里的值照旧列在原条目下。回滚即 revert 本 PR。
  • 先看:packages/spec/src/conversions/refused-shapes.ts(新的共用遍历 forEachFlowValueSlotRefusal 和新检测器 connectorActionInputTemplateToken),packages/spec/src/conversions/refused-shapes.test.ts(migrate meta --stored does not list a stored connector_action whose connectorConfig.input carries a template token, though boot refuses it #28163 一节)。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run src/conversions/refused-shapes.test.ts,预期 80 个测试全过,其中 “one TODO under the connector input entry, at connectorConfig.input.KEY” 通过。
  • 需要你做的:无,席位落地(等契约复审记录)。

What changes

  • One walk, the door's call. forEachFlowValueSlotRefusal walks every node of every stored flow (region bodies included) and calls flowNodeValueTemplateRefusals(node.type, node.config, node). That is the call registerFlow (flow-registration-checks.ts) and objectstack validate (validate-expressions.ts) make, so the listing reads the same ledger, including the connectorConfig · input.* entry fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 added. No second list of node types or paths exists.
  • Two detectors, split by the block each refusal names.
    • flow-value-slot-template-dialect-refused keeps the config refusals. Its path, from and reason are unchanged.
    • New connector-action-input-value-slot-judged takes the connectorConfig refusals. It lists them at NODE_PATH.connectorConfig.input.KEY, with the judge's own message (the value's CEL spelling).
  • Why a second detector, not the suggested single one. A detector's id is the D3 entry of the retirement that refuses the shape (header of refused-shapes.ts, pinned by names each refused shape by a D3 entry of its step). fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 registered the connector-input refusal as its own D3 entry, connector-action-input-value-slot-judged. The flow-value-slot-template-dialect-refused entry's surface names config only, and its reason says "the template resolved a token CEL reads differently". For this slot that is false: the action was handed the token's text as written, as the fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 entry says. So the row is listed under the entry that refuses it, with a reason that holds for it. Both detectors share the one walk and the one judge. The route is otherwise the order's.
  • The header invariant ("a site is listed here exactly when that door refuses it") now names that the judge is handed the whole node, as the doors hand it.
  • Changeset 28163-stored-connector-input-listed.md: @objectstack/spec patch, both declaration lines.

Tests

packages/spec/src/conversions/refused-shapes.test.ts, new #28163 block:

  • A stored row with input: { text: '{record.name}', queue: 'support' } (through applyConversionsToStoredItem) gives one TODO: conversionId connector-action-input-value-slot-judged, path flows[0].nodes[1].connectorConfig.input.text, from the token string, and a reason naming the flow, the node, the slot and the CEL envelope. The row is returned byte-identical.
  • A nested value (tags[1]) in a connector node inside a loop body is listed at ...config.body.nodes[0].connectorConfig.input.tags[1].
  • A loose config.input that the D2 lift moves onto connectorConfig is listed at the lifted path. The listing judges the converted row, as boot does.
  • Controls: a CEL envelope, a literal and an absent input are not listed.
  • Agreement with the door: on a mixed flow, the listed paths equal the judge's refusals when handed the node, as a set (one config filter operand, one connectorConfig input value), each under its own id.
  • Block pin: config plus FLOW_NODE_SIBLING_EXPRESSION_BLOCKS equals ['config', 'connectorConfig'], so a new sibling block reds here until it has a detector.
  • The registered-id pin gains the new id in report order.
Ablation: removing the node argument turns the new cases red

node scripts/ablation-replace.mjs --file packages/spec/src/conversions/refused-shapes.ts --anchor 'flowNodeValueTemplateRefusals(typed.type, node.config, node)' --replacement 'flowNodeValueTemplateRefusals(typed.type, node.config)'. The anchor hit once (1 → 0, blob 88279418 → 46343966). The suite went to Tests 4 failed | 76 passed (80): the stored-row case, the nested/region case, the lifted-loose case and the door-agreement case. The controls stayed green, as expected, since nothing is listed either way. The file was restored to its HEAD blob 88279418 with git diff HEAD empty. The test imports the module from source, so no dist rebuild is involved.

Acceptance notes

  • Not listed, by design of this card: the same D3 entry also covers a malformed CEL envelope in connectorConfig.input, which registerFlow refuses through the value-envelope judge. No refused-shape detector lists a malformed envelope in any value slot, config included. That is the existing scope of REFUSED_STORED_SHAPES (template tokens), not something this PR widens. Noted for the seat, not filed.
  • The connector-action-input-value-slot-judged entry's acceptance text names the boot warn as the locator for a row that exists only in sys_metadata. That stays true. The entry does not yet say that os migrate meta --stored lists the row too. Editing it is outside this card's file surface, so it is left for the seat.

Verification

All readings are on head dbc77fa9: the fix commit 2c3f9f50 merged with origin/main at 3739d8d6, after pnpm install --frozen-lockfile and a full pnpm exec turbo run build (Tasks 116 successful, 116 total).

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 77 commands (the same 77 before and after the merge). All 77 were run with stdin from /dev/null and the exit code captured before any pipe: 77 exited 0. On the pre-merge head 2c3f9f50, 73 exited 0 and 4 refused with exit 3 (PREREQUISITE NOT MET: no workspace build for check:dual-build-cjs-loads, check:lean-entry-closure and check:doc-formula-expressions, and a pinned fixture commit missing from the shallow clone for check-plugin-teardown-shape --self-test). After the build and a fetch of that commit, all four exit 0 on dbc77fa9.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE: "77 derived famil(ies) accounted for — 77 run, 0 NOT-MEASURED".
  • pnpm --filter @objectstack/spec test (on 2c3f9f50, under the verify lock): VERDICT command-exit 0, Tests 24007 passed, 1 todo (24008). pnpm --filter @objectstack/spec typecheck: exit 0.
  • On dbc77fa9: refused-shapes.test.ts + flow-value-slot-template.test.ts: Test Files 2 passed (2), Tests 253 passed (253). pnpm --filter @objectstack/spec check:generated: "All 14 generated artifacts are up to date". node scripts/check-dev-prereqs.mjs: exit 0. node scripts/check-nul-bytes.mjs: exit 0.
  • Dogfood: not owed. The title carries no ! and Clause-② carries no (narrowing) arm.
  • CI on this PR: not yet read; the seat reads the check-runs.

Generated by Claude Code

…de, so a connector_action's connectorConfig.input token is listed

The flow value-slot detector judged a node's `config` alone, so the slot the
ledger roots in a connector_action node's `connectorConfig.input` was never
read: `os migrate meta --stored` named nothing for a stored row that
`registerFlow` refuses at boot. Both flow detectors now share one walk that
hands the judge the whole node, as registerFlow and objectstack validate do,
and split its refusals by block: `config` under
flow-value-slot-template-dialect-refused, `connectorConfig` under
connector-action-input-value-slot-judged, the D3 entry that refuses it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz8wBXN5R4YRtm5e5r
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/automation/connectors.mdx (via connectorConfig (literal, a string literal in detect))
  • content/docs/automation/flows.mdx (via connectorConfig (literal, a string literal in detect))

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

  • content/docs/releases/v17/17-5.mdx (via connectorConfig (literal, a string literal in detect))
  • content/docs/releases/v18/18-0.mdx (via connectorConfig (literal, a string literal in detect))

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
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 168 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 32387500a601958826fe0b5ba72f53948c9dc5d2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 3112504ef9193b2a877c7a38f6b03c9df9bc7a5b — the merge of head dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34 into base 32387500a601958826fe0b5ba72f53948c9dc5d2, 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 3112504ef9193b2a877c7a38f6b03c9df9bc7a5b && git checkout 3112504ef9193b2a877c7a38f6b03c9df9bc7a5b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 32387500a601958826fe0b5ba72f53948c9dc5d2 dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34 && git checkout -B drift-repro 32387500a601958826fe0b5ba72f53948c9dc5d2 && git merge --no-ff dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34

node scripts/docs-audit/affected-docs.mjs --json 32387500a601958826fe0b5ba72f53948c9dc5d2

⚠️ 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 32387500a601958826fe0b5ba72f53948c9dc5d2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34 · 2026-10-30T19:15Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34
Local-runs: none
Scope: packages/spec/src/conversions/refused-shapes.ts + packages/spec/src/conversions/refused-shapes.test.ts, .changeset/28163-stored-connector-input-listed.md; read for the claims on main: packages/spec/src/automation/flow-value-slot-template.ts (FlowNodeValueTemplateRefusal.block, the node argument of flowNodeValueTemplateRefusals), packages/spec/src/automation/flow-node-expression-paths.ts (FLOW_NODE_SIBLING_EXPRESSION_BLOCKS = connectorConfig), packages/spec/src/migrations/entries/semantic/18.connector-action-input-value-slot-judged.ts (the D3 entry the new detector id names)

Inputs: card #28163 (body, triage 6525913359 and 6527344285, claim 6528709142, os-dev-report 6531351334), PR body, the 3-file diff against merge base 3739d8d, the head's check-runs (read 2026-10-30T19:41Z: check-runs on dbc77fa: 54 total, 40 success (Auto Label, Build & E2E UI, Build Core, Build UI Deps, Check Changeset, Check PR Size, Dogfood Regression Gate, Flag docs affected by code changes, Governed Surface Queue Guard, Lint & Repo Gates, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Live E2E UI, PR-body guards (claim · duplicate · Part-of · single-writer path), Plan Test Core shards, Spec property liveness, Test Core, Test Core (all 8), Test UI, Type Check UI, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, TypeScript Type Check, filter); 14 skipped path-filtered (18.x upgrade harness (opt-in), Auto Label, Build Docs, Check PR Size, Dogfood Regression Gate (matrix shard/3), Dogfood Verify CLI, Lint UI, Packed-tarball smoke (opt-in), Schedule gate · skip when a green push run covers the commit, Temporal Conformance (live PG + MySQL), Test UI (matrix shard/16), UI Published Artifacts); 0 failure, 0 in progress). Author user.login is objectstack-fleet[bot], so no login note is owed. The reviewer is not the drafter (session_01L9qibz8wBXN5R4YRtm5e5r).

① Derived judgments

  • The defect and the fix match the card's measurement: the flow-value-slot-template-dialect-refused detector called flowNodeValueTemplateRefusals(node.type, node.config) with no node, so the connectorConfig · input.* ledger entry fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 added was never read by os migrate meta --stored. The new shared walk forEachFlowValueSlotRefusal hands the judge the whole node, which is the call registerFlow and objectstack validate make (read on main: the function takes the node and each refusal carries block), so the listing reads the same ledger as the doors with no second list of types or paths.
  • Two detectors split by refusal.block: config refusals keep their id, path, from and reason byte-for-byte; connectorConfig refusals are listed under connector-action-input-value-slot-judged, at NODE_PATH.connectorConfig.input.KEY, with a reason true for that slot (the action was handed the token's text as written) and the judge's CEL spelling. The id is the D3 entry fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 registered (present on main under migrations/entries/semantic/18.*), which is what the registered-id pin requires: a detector names the entry of the retirement that refuses the shape. Listing the row under the config entry would have carried a reason that is false for this slot, so the second detector is the smaller and the truer shape.
  • Tests pin both directions: the stored row listed once with id, path, from and reason, returned byte-identical; a nested value inside a loop body; a loose config.input the D2 lift moves onto connectorConfig listed at its lifted path (the listing judges the converted row, as boot does); controls for a CEL envelope, a literal and an absent input; door agreement on a mixed flow (listed paths equal the judge's refusals as a set, each under its own id); a block pin ['config', ...FLOW_NODE_SIBLING_EXPRESSION_BLOCKS] = ['config', 'connectorConfig'] so a new sibling block reds until it has a detector. The one-shot ablation (node argument removed) turns four cases red and nothing else.
  • Changeset @objectstack/spec patch, Clause-②: no, Metadata-protocol: none: the listing is a report over stored rows, no accepted shape, door verdict or stored row changes.

② Semver level

patch. The published TODO notice shape is unchanged; one more detector reports under an id the migration chain already registers; no gate verdict moves (the doors refused this slot since #28150, the preview now says so).

③ Boundary flags

  • Stays inside packages/spec/src/conversions (the claimed surface) plus its test and changeset; no generated artefact moves (check:generated 14/14 per the PR body), no ui/**, no governed path, no migration entry edited.
  • Not listed, by the existing scope of REFUSED_STORED_SHAPES: a malformed CEL envelope in connectorConfig.input (refused by the value-envelope judge at boot) is not a template token and no detector lists malformed envelopes in any slot. Correctly noted, not widened here.
  • Should-fix (not blocking, for the seat): the D3 entry connector-action-input-value-slot-judged's acceptance text still names only the boot warn as the locator for a stored row; now that os migrate meta --stored lists the row too, one sentence there (a migrations/entries edit, outside this card's surface) would make the entry's own prescription complete.

④ Design judgment

The change holds against the code it cites and the invariant it restores. The header's rule, "a site is listed here exactly when that door refuses it", is met by construction rather than by a parallel list: one walk, the door's own call with the node, and the detector split keyed on the block the judge already reports. The block pin and the door-agreement test make the invariant executable, so the next sibling block (or the next ledger entry rooted outside config) cannot go unlisted silently. Choosing the entry's own id over the order's single-detector suggestion is right for the reason the PR gives: the TODO's conversionId is the operator's pointer to the prescription, and the config entry's reason ("the template resolved a token CEL reads differently") is false for a connector input, which was passed the text verbatim. Nothing is rewritten and no row moves, which is the card's scope.

Implemented-by: claude/project-thread-vgdwpp
Reviewed-by: session_01L9qibw58ehQ2gRHP2h2Puz

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

维护者速读

  • 改了什么: os migrate meta --stored 现在也会列出“调用连接器”节点的 connectorConfig.input 里带 {…} 模板写法的已存流程。新检测项为 connector-action-input-value-slot-judged,定位到 NODE.connectorConfig.input.KEY。原有的 config 值槽检测不变。
  • 为什么改: fix(spec,service-automation,lint)!: registration and validate judge a connector_action's connectorConfig.input value slot #28150 之后启动时会拒绝注册这类流程,但升级前的清单工具原来只看 config,列不出它们,运维要等启动日志才发现。
  • 风险与回滚: 只多列出启动本来就会拒绝的行,不改写任何数据。回滚即 revert。
  • 先看: packages/spec/src/conversions/refused-shapes.ts 新的共享遍历 forEachFlowValueSlotRefusal 和新检测项。
  • 两分钟验证: pnpm --filter @objectstack/spec exec vitest run src/conversions/refused-shapes.test.ts。
  • 需要你做的: 无。不属于受管面,契约复审 PASS(评论 6531985735),由席位落地。
  • 席位意见: ACCEPT,可落地。

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · 2026-10-30T19:43Z

Head dbc77fa981a585aad9a4d0ff1ccbc78fd259fb34. Card #28163, claim 6528709142, os-dev-report 6531351334. The maintainer's ruling, verbatim: 「按 pm-dispatch 审过的pr你可以自主落地」.

  • Shape: draft to main, made by the relay, with Fixes #28163. Clause-②: no and Metadata-protocol: none sit line-initial. The changeset is @objectstack/spec patch.
  • Contract review: record 6531985735 from the contract-review lane at CONTRACT_REVIEW_TIER, full ①–④. It is a PASS with no must-fix. Should-fix: the D3 entry's acceptance text could mention the listing. It was dropped, because the listing's own row reason already names the boot refusal, so there is no reader-facing gap.
  • Accepted deviation: the order suggested one detector, and the PR adds a second one with id connector-action-input-value-slot-judged.
  • Diff read by the seat: the config detector's output is byte-identical to main; it now filters refusal.block === 'config'. The new detector reports at ${path}.connectorConfig.${refusal.path}. An ablation that drops the node argument reds 4 tests.
  • Gates: the newest run of every check on this head is success or skipped (37 success, 12 skipped). mergeable_state is clean. check-governed-merges --pr 28249: NOT governed, +174/−21.
  • Out of scope: the dev noted that no detector lists a malformed CEL envelope in any value slot. That is a separate class: a malformed envelope was never accepted at publish, so no stored row is known to carry one. No card.
  • Landing: pr_ready + automerge_enable in this act.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 19:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 30, 2026 19:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 04c8930 Oct 30, 2026
58 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-vgdwpp branch October 30, 2026 20:25
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 size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

migrate meta --stored does not list a stored connector_action whose connectorConfig.input carries a template token, though boot refuses it

1 participant