Skip to content

fix(lint): the approval and flow advisories a flow save returns carry a user sentence in every platform locale - #33120

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-ltr3oq
Nov 13, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-ltr3oq

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #32836

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:保存流程(含审批节点)时返回的 19 类提示,现在都带一句面向用户的话,覆盖平台全部 11 种语言;规则 id、英文 message 和 hint 一字未改。
  • 为什么改:此前这些提示只有给开发者看的英文,中文等界面的用户在保存流程时看到的是看不懂的技术句;这是 spec: user-facing refusal and advisory text is in the request locale (userMessage locale rule, advisory user text field), per ruling C on #32440 #32655 已落地做法在自动化领域的延续。
  • 风险与回滚:只新增文案和 userTextParams 字段,不改变哪些写入被拒、哪些通过;回滚即撤销本 PR。os validate --json / os build --json 的 warnings 里会多出 userTextParams 键。
  • 先看:validate-approval-approvers.user-text.ts(8 条审批提示,最贴近用户),再看 finding-user-text.test.ts 里按真实流程经保存门渲染的用例。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run src/finding-user-text.test.ts,102 个用例全绿,每条都从真实流程经运行时保存门取出并逐语言渲染。
  • 需要你做的:无,席位落地。

What changes

Nineteen advisory rule ids that the save door returns on a flow write now carry userTextParams and a user sentence in all eleven PLATFORM_LOCALE_TAGS. Each rule file gets a *.user-text.ts catalog beside it, and the six catalogs merge into FINDING_USER_TEXT_CATALOG through mergeUserTextCatalogs, the way #32655 landed validate-security-posture.user-text.ts.

Rule file Catalog Ids
validate-approval-approvers.ts APPROVAL_APPROVERS_USER_TEXT approval-approver-type-unknown, approval-expression-invalid (its info arm only), approval-approver-not-membership-tier, approval-approver-type-deprecated, approval-approver-type-unsupported, approval-approvers-may-resolve-empty, approval-expression-no-empty-policy, approval-escalation-reassign-no-target
validate-flow-trigger-readiness.ts FLOW_TRIGGER_READINESS_USER_TEXT flow-trigger-unknown-object, flow-input-unknown-object (advisory arm, no package closure), flow-input-object-less, flow-input-record-read-as-list, flow-draft-status-ambiguous
validate-flow-template-paths.ts FLOW_TEMPLATE_PATHS_USER_TEXT flow-template-unknown-field, flow-template-lookup-traversal, flow-template-field-unprovisioned
validate-flow-node-writes.ts FLOW_NODE_WRITES_USER_TEXT flow-node-write-unprovisioned-anchor
validate-readonly-flow-writes.ts READONLY_FLOW_WRITES_USER_TEXT flow-update-readonly-when-field
lint-flow-credential-literals.ts FLOW_CREDENTIAL_LITERALS_USER_TEXT flow-credential-literal
  • Push sites set userTextParams (flow, node, object, field, input names, plus small arm keys such as slate, locked, trigger, root, position). Rule ids, message and hint are unchanged.
  • Refusals that share an id with one of these advisories (approval-expression-invalid's error arm, flow-trigger-unknown-object / flow-input-unknown-object under a package closure) carry no params, so they render no user sentence.
  • flow-credential-literal's sentence names where the credential sits (header, query parameter or input name) and never its value.
  • authoring-rules.ts: the lintFlowCredentialLiterals adapter keeps userTextParams when it re-maps its findings (one spread, approved by the seat's surface amendment 6855561105), so the publish gate's advisory carries them.
  • Changeset 32836-advisory-user-text.md, @objectstack/lint patch.

Tests

  • finding-user-text.test.ts gains a shared harness and 25 cases from real flows:
    • exact coverage of each catalog (ids × 11 locales) and its identity in FINDING_USER_TEXT_CATALOG;
    • every push site rendered from its real finding in every locale, with no undefined, no unfilled ${, every shown name present, and no locale falling back to the English sentence; arms that differ (group vs manager slate, locked vs not, record vs schedule trigger, trigger vs variable root, header vs query vs input) render apart;
    • every case also runs its flow through runRuntimeAuthoringRules({ type: 'flow' }), checks the advisory's params, parses it under RuntimeAuthoringIssueSchema and compares userMessage to the zh-Hans sentence;
    • the refusal arms carry no params; credential sentences never contain the value; the adapter keeps params (ja renders ヘッダー「Authorization」).
  • validate-approval-approvers.unsupported-type.test.ts adds the same pins for approval-approver-type-unsupported under its mocked binding table, including a gate advisory parsed for a ko-KR request.

Acceptance notes

Re-measure. The card's table lists 27 ids; a static scan of the listed files finds 29 (action-body-writes 4, not 3; flow-trigger-readiness 5, not 4). Through the save door, 19 reach a user, all on flow writes. This PR writes catalogs and pins for those 19 only, as the seat ruled (it supersedes the amendment's "cover all 29" clause), and writes no dead catalog. Under #32835's Done-when alternative, the 10 ids that never reach the door:

  • (a) the write's snapshot never carries what the rule reads. These are reference-integrity suite members with no runtimeTypes, so they default to flow, but a flow write's snapshot carries no hooks or actions. Measured through the gate: rulesRun 1 for a hook write, 1 for an action write, 14 for an object-with-action write, and none of these advisories.
    • hook-body-source-unparseable, hook-body-write-unprovisioned-anchor, hook-body-write-unknown-field
    • action-body-source-unparseable, action-record-write-discarded, action-body-write-unprovisioned-anchor, action-body-write-unknown-field
    • hook-api-update-readonly-when-field, action-api-update-readonly-when-field
  • (b) the advisory arm is off at the door. email-template-hole-grammar's warning arm (rung ④, a platform-prefixed option object) runs only when liveUniverse is false; the registry sets liveUniverse: ctx.runtimeWriteType !== undefined, so at the door the same hole is an error.

approval-approver-type-unsupported has no live trigger today (no binding is unsupported since queue landed), so its sentence is pinned only under the mocked binding table, as its existing message pin is.

Agent-written sentences for the native-review round. Every non-English sentence in this PR is agent-written: 19 ids × 10 locales (zh-Hans, zh-Hant, es, ar, fr, pt-BR, ru, de, ja, ko), in the six *.user-text.ts files above. Terms follow ui/packages/i18n (flow 流程 / 流程 / flujo / التدفق / flux / fluxo / поток / Flow / フロー / 플로우; field 字段 / 欄位 / … / 項目 / 필드; object 对象 / 物件 / … / オブジェクト / 개체). The approval-approvers-may-resolve-empty and flow-draft-status-ambiguous sentences carry the most domain wording and are the best first read.

Metadata-protocol. none, following #32835's stated reading for its sub-cards. #32790 declared added/yes because it added a spec field (userMessage); this PR adds no spec field.

Ruling grep (ruling|Ruling-ref|maintainer|⛔ across the surface, read before editing): approval approvers 14 (manager kept out of the expandable routes; never name an empty-slate outcome or default; tracker ids stay out of strings), flow trigger readiness 9 (#9612 ruling, #29425 ruling A, #7215 maintainer-ruled, ruling G), readonly flow writes 6 (the 2026-09-03 ruling: never say "INSERT is exempt"), flow template paths 1, credential literals 2 (never the value; no url-path promise), finding-user-text.ts 2, flow node writes 0. The sentences keep every one of them; none is reversed.

Serial. #32837 also adds one line to finding-user-text.ts; whichever lands second merges main once and keeps both catalogs.

Ablations (both red, both restored to HEAD and verified clean):

  • deleting the authoring-rules.ts spread: 4 credential gate tests red;
  • deleting userTextParams: { flow: flowName }, at the draft-status push site: 2 tests red.

Verification

Head 8b347e57, merged with origin/main at 7c36f524.

  • pnpm --filter @objectstack/lint exec vitest run: 202 files, 8692 tests passed (after the merge with main).
  • pnpm --filter @objectstack/lint typecheck and tsc --noEmit: exit 0. node scripts/check-cli-command-ids.mjs: exit 0.
  • Consumers of the widened finding shape, each its real reading:
    • @objectstack/metadata-protocol: 337 files passed, 7 skipped; 22799 tests passed, 36 skipped.
    • @objectstack/objectql: 595 files, 12789 tests passed.
    • @objectstack/cli (the 4 test files naming these rule ids, incl. flow-credential-literal-doors.e2e.test.ts and authoring-rule-command-parity.test.ts): 63 tests passed.
    • examples/app-showcase test/author-time-advisories.test.ts: 2 tests passed.
  • Derived gates: dispatch-gates.mjs --ran reconciles 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN; the derivation at the merged head is identical.
Gate readings that were not exit 0 on the first sweep
  • check:test-source-alias exit 1: validate-approval-approvers.unsupported-type.test.ts loaded @objectstack/spec/system and @objectstack/spec/api inside a clocked body. Fixed in af4e1c7 (module-top imports); rerun exit 0.
  • check-plugin-teardown-shape --self-test exit 3: shallow clone could not reach the pinned fixture commit; after fetching it, exit 0.
  • check:docs-transcript-drift exit 1 and check:dts-closure exit 1: ran while a closure build was rewriting spec/dist; rerun exit 0.
  • check:dual-build-cjs-loads exit 3 and check:lean-entry-closure exit 3: prerequisite (no dist); after pnpm build, exit 0.
  • check:type-check-debt exit 124: my 600s per-gate timeout; rerun without it, exit 0.

Generated by Claude Code

@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 13, 2026
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 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 5c005e0e42e207b073843b255248877b84442342 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 5c005e0e42e207b073843b255248877b84442342

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

Keeps both catalog lines in finding-user-text.ts: the automation
catalogs from this branch and the dashboard binding catalogs from main.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibymXZPShrjayTXHyCg
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

维护者速读

  • 改了什么:自动化类(审批、流程触发、流程模板路径、流程节点写入、只读写入、流程凭据明文)的 19 条 lint 提示,在运行时保存流程时会带上用户可读的一句话(11 种平台语言);规则 id、message、hint 和 CLI 输出都不变。authoring-rules.ts 只加了一行,把参数透传给运行时门禁。
  • 为什么改:非英语 Studio 里这些提示原来只显示英文;与 lint: the dashboards, charts and widgets advisories carry a localized user sentence (11 rule ids in 3 rule files), per ruling C on #32440 #32837 和安全类提示同一做法。
  • 风险与回滚:只加文案和参数,@objectstack/lint patch,Clause-②: no;回滚即 revert。凭据提示的参数只带名字,从不带值。
  • 先看:packages/lint/src/validate-approval-approvers.user-text.ts 和 finding-user-text.ts 的合并列表。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/finding-user-text.test.ts,预期全绿。
  • 需要你做的:无,席位落地。非英语句子由 agent 写,已列出等母语复核。

席位意见:可以落地。

席位核对

ACCEPT · seat domain:spec#2 · head 08d77c00 · 2026-11-13T16:38Z

  1. Shape: Fixes #32836, draft, base main, 17 files, +1945/−5; changeset .changeset/32836-advisory-user-text.md (@objectstack/lint patch). Clause-②: no, Metadata-protocol: none, matching claim 6855513639, amendment 6855561105 (the lintFlowCredentialLiterals adapter line) and dev report 6857324509.
  2. Premise: spec: user-facing refusal and advisory text is in the request locale (userMessage locale rule, advisory user text field), per ruling C on #32440 #32655's pattern; lint: the dashboards, charts and widgets advisories carry a localized user sentence (11 rule ids in 3 rule files), per ruling C on #32440 #32837 (PR feat(lint): dashboard and report advisories carry a localized user sentence #33118) merged into the same catalog list. The dev re-measured through runRuntimeAuthoringRules: 19 of 29 ids reach the publish door, all from flow writes. The seat agreed in-thread to catalog only those 19 (superseding the amendment's "all 29"). The 10 unreachable ids are named with reasons in Acceptance notes: (a) reference-integrity members on the flow runtime type, whose snapshot carries no hooks or actions; (b) the email-template hole grammar's warning arm, which is off at the door.
  3. Review: dispatch-gates --tier: no path-derived mandate (packages/lint only, no narrowing), so this is the seat's own review of record. It read the code diff: catalog imports and the merge list; the adapter's conditional spread; params at the push sites. lint-flow-credential-literals carries the entry's name and position, ⛔ never its value. validate-flow-trigger-readiness threads params through its shared advisory helper. The merge of main at 08d77c00 keeps both the automation and dashboard catalog sets (seat read the conflict resolution).
  4. Gates / check-runs: dev after the merge: lint 203 files / 8716 tests, check-cli-command-ids exit 0; before it 63/63 derived gates, metadata-protocol 22799, objectql 12789, cli 63 and showcase 2 passed; 2 ablations red as expected. CI on 08d77c00: 66 runs, 55 success, 11 skipped, 0 failed. K = 0; clean with main. The docs drift check names deployment/validating-metadata.mdx via AUTHORING_RULES; the adapter line changes no documented behaviour.
  5. Accepted deviations and out-of-scope findings: the 10 unreachable ids stay on the English message. Agent-written non-English sentences are listed for the native-review round. validate-approval-approvers.unsupported-type.test.ts (a test under packages/lint, inside the surface) moved spec imports to module top for check:test-source-alias.
  6. Landing: not governed (check-governed-merges --pr 33120), 1950 changed lines. The seat readies the PR and arms auto-merge under the maintainer's standing words 「车道经理应该使用 pm-dispatch 技能审核和落地pr」. On MERGED: lint: the automation writes: approvals, flows, hooks, actions and email templates advisories carry a localized user sentence (27 rule ids in 11 rule files), per ruling C on #32440 #32836 closes via Fixes; pm:dispatched comes off.

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/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants