Skip to content

fix(lint): a flow node config-value finding carries the slot's key path, so Studio's Problems row selects the node - #28192

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #27828

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:流程节点配置值上的 lint 发现(例如「更新记录」里留空的表达式)现在除了原来的文字位置说明,还带上指向该节点配置值的键路径(如 flows[0].nodes[1].config.fields.status,草稿诊断里读作 nodes.1.config.fields.status)。
  • 为什么改:之前这类发现只有一句文字说明,Studio 的「问题」面板能列出它,却点不到、也标不到对应节点;流程一大,作者很难找到挡住发布的那个节点。连线的同类问题 Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858 已经修过,这次补上节点这一半。
  • 风险与回滚:只是给发现多加一个 path 字段,拒收与否、提示文字都不变;CLI 的 rule: … at … 一行会改印键路径(连线自 Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858 起已是如此)。回滚即 revert 本 PR。
  • 先看:packages/lint/src/validate-expressions.ts 节点遍历末尾的路径标记段,packages/lint/src/validate-expressions.node-config-path.test.ts,packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts 新增的 [#27828] 用例。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run src/validate-expressions.node-config-path.test.ts,预期 7 条全过,其中 “every node-config arm locates its finding at the slot it judged” 覆盖六类节点发现。
  • 需要你做的:无,席位落地。

What changes

  • Producer (packages/lint/src/validate-expressions.ts, the flow node walk in runStackExpressionPasses). Each node site that pushes a finding now also records, in a per-node where → key map, the key it judged: config.condition, config.KEY (the spec's node-config judge), BLOCK.SLOT_PATH for a ledger slot and a value-template refusal (so a connector_action input from automation: registration does not judge a connector_action node's connectorConfig.input value slot, because the spec expression ledger cannot address a sibling block of config #27968 reads connectorConfig.input.KEY), config.TEXT_SLOT, and for a script node's callable check config.function (or the first retired dispatch key the node still carries). At the end of the node's walk, every finding that node produced gets path: flows[F].nodes[N].KEY, the Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858 edge spelling.
  • Same limits as the edge path. N indexes the authored flow.nodes list (a null member before the node does not renumber it). A node inside a nested region (loop body) and a map-form flows collection have no index to spell, so their findings keep where alone. where and message are unchanged everywhere.
  • No consumer change. authoring-rules.ts already sets path: i.path ?? i.where (only its comment gains the node case), and metadata-protocol itemRelativeIssuePath already re-spells flows[0].nodes[1].config.fields.status as nodes.1.config.fields.status, which ui flow-problems.ts serverTarget maps onto the node. ui/ is untouched.
  • ExprIssue.path docblock names the node case.
  • Changeset 27828-flow-node-config-finding-path.md: @objectstack/lint patch, both declaration lines.

Tests

  • New packages/lint/src/validate-expressions.node-config-path.test.ts (7 tests): the Update Records blank-expression finding carries flows[0].nodes[1].config.fields.status with where unchanged; a null member before the node and a second flow before this one index correctly; one fixture exercises six arms (node condition, value-template token, text-slot token, undeclared config key, script with no function, connector_action input) and asserts each node's path set; map-form flows and a node inside a loop body keep path undefined; the runtime authoring gate's refusal is located at the node config value.
  • packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts gains [#27828] a node config value refused by expression-invalid is located at the node, not in where prose: a draft with a blank Update Records value is stored, and its draft read's _diagnostics reports valid: false with every expression-invalid entry at nodes.1.config.fields.name. Before the fix it read flow 'blank_value' · node 'set' (update_record) update_record field value at config.fields.name.
  • validate-expressions.edge-condition-path.test.ts: the case "a finding off the edge condition gains no path" asserted that a node condition finding has no path. That is the behaviour this card changes, so it now asserts the node's own path (flows[0].nodes[0].config.condition), still no edge path.
  • validate-expressions.test.ts receiver meta-test (validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017): the new local nodeSlotKeys is excused with its reason (its keys are Map methods, never metadata keys).
Red before the fix, and the ablation
  • Before the producer edit (tests committed first on origin/main 9f13ee81): the new lint file ran 5 failed | 2 passed (the two undefined-path cases passed), and the metadata-protocol case failed with the prose path quoted above.
  • Ablation on committed head e8b5683d with scripts/ablation-replace.mjs: the stamp line (if (key !== undefined) issues[k] = { ...issues[k]!, path: ... }) replaced by void key; (anchor 0 left, replacement 1, counted on disk), lint rebuilt (OS_SKIP_DTS=1), ablation-dist-preflight.mjs @objectstack/lint 'nodes[${nodeIndex}]' --absent exit 0. Lint: 6 failed | 7 passed across the node and edge path files. metadata-protocol (reads lint's dist/): the [#27828] case failed, 17 passed. Restored by the EXIT trap; git diff HEAD empty and the blob hash matched HEAD; lint rebuilt, preflight 'void key;' --absent exit 0, metadata-protocol 18/18.
Local runs

Acceptance notes

Verification

Head 6360f96e (merged with origin/main 3cc45cc4, which carries #28150). The gate sweep, the suites and the ablation ran on this tree.

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 59 commands. All 59 ran with stdin from /dev/null, exit codes captured before any pipe: 56 exited 0 and 3 first exited 3 (PREREQUISITE NOT MET): check-plugin-teardown-shape --self-test (shallow clone; pinned commit 621a4876 fetched), check:dual-build-cjs-loads and check:lean-entry-closure (unbuilt packages). After the prerequisite, all 3 exited 0.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE: "59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED". Ran list: spec-seat-1/r2026-10-30/out/27828-gates-ran-6360f96e.list in the seat's folder.
  • Full dogfood: not owed (no ! in the title, no (narrowing) arm).
  • test:repo was not run locally (CI's Test Core). CI on this PR: not yet read; the seat reads the check-runs.

Generated by Claude Code

…value finding (WIP)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibxhpYpVUxgK5jbQMXT
validateStackExpressions located a finding on a top-level flow node's
config value by its where prose alone, so a draft's _diagnostics path was
prose and Studio's Problems row could not select the node. Each node
arm now records the key it judged, and the node's findings carry
flows[F].nodes[N].<block>.<path>, the edge shape. Nested-region nodes
and map-form flows keep where alone, as edges do.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibxhpYpVUxgK5jbQMXT
@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/lint, touching 4 documentable anchor(s).

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

  • content/docs/automation/flows.mdx (via config.condition (literal, a string literal in runStackExpressionPasses))
  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))

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

  • content/docs/releases/v17/17-5.mdx (via config.condition (literal, a string literal in runStackExpressionPasses))

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
  • 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 — 4 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 c48cd2c103a9dfa99313a67a3ec622291d793900 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json c48cd2c103a9dfa99313a67a3ec622291d793900

⚠️ 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 c48cd2c103a9dfa99313a67a3ec622291d793900 → 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_01L9qibwb6hBqszPTYJobitm

维护者速读

  • 改了什么: 流程节点配置值上的 lint 发现(例如「更新记录」里留空的表达式)现在除了文字位置说明,还带上指向该配置值的键路径,例如 flows[0].nodes[1].config.fields.status,草稿诊断里读作 nodes.1.config.fields.status。连接器输入值落在自己的区块,例如 connectorConfig.input.customerId。
  • 为什么改: 之前这类发现只有一句文字,Studio 的「问题」面板能列出它,却选不中、也标不到对应节点。流程一大,作者很难找到挡住发布的那个节点。连线的同类问题 Studio: an expression field that says "Valid CEL" should read the draft's server verdict at its own path (RLS predicate, flow edge condition) #26858 已修过,这次补上节点这一半。
  • 风险与回滚: 只是给发现多加一个 path 字段,拒收与否和提示文字都不变;CLI 的 rule: … at … 一行改印键路径,和连线一致。回滚即 revert。
  • 先看: packages/lint/src/validate-expressions.ts 节点遍历末尾的路径标记段。
  • 两分钟验证: pnpm --filter @objectstack/lint exec vitest run src/validate-expressions.node-config-path.test.ts,预期 7 条全过。
  • 需要你做的: 无。不属于受管面,不碰 spec 契约面,由席位落地。
  • 席位意见: ACCEPT,可落地。

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

Head 6360f96ec8ba5d6348886a9eb609b71403e17fad. Card #27828, claim 6526142484, os-dev-report 6527354051. The maintainer's ruling, verbatim: 「按 pm-dispatch 审过的pr你可以自主落地」.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 16:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 3efccfd Oct 30, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-2rqpic branch October 30, 2026 16:56
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

1 participant