Skip to content

fix(lint): connector, permission-set and form-view advisories carry a user sentence in every platform locale - #33145

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

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ci8fau

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #32842

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:保存连接器、权限集和表单视图时返回的 3 类提示,现在都带一句面向用户的话,覆盖平台全部 11 种语言;规则 id、英文 message 和 hint 一字未改。卡片列出的另外 7 类提示保存时根本不会返回,本 PR 不为它们写文案,只在下方写明原因。
  • 为什么改:此前这些提示只有给开发者看的英文,非英文界面的管理员保存连接器或权限集时看不懂;这是 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 键。
  • 先看:lint-connector-credential-literals.user-text.ts(4 种情形,最贴近用户),再看 validate-predicate-path-refs.user-text.ts(两种理解都给出写法),最后看 authoring-rules.ts 里两行转发。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run src/finding-user-text.connector-permission-form.test.ts,21 个用例全绿,每条都从真实元数据经运行时保存门取出并逐语言渲染。
  • 需要你做的:无,席位落地。

What changes

Three advisory rule ids that the save door returns 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 three catalogs merge into FINDING_USER_TEXT_CATALOG through mergeUserTextCatalogs, the way #32655 landed validate-security-posture.user-text.ts.

Rule file Catalog Id Write
lint-connector-credential-literals.ts CONNECTOR_CREDENTIAL_LITERALS_USER_TEXT connector-credential-literal connector
validate-retired-permission-residue.ts RETIRED_PERMISSION_RESIDUE_USER_TEXT permission-retired-lifecycle-residue permission
validate-predicate-path-refs.ts PREDICATE_PATH_REFS_USER_TEXT predicate-rhs-path-shaped (bare-word warning arm only) view
  • connector-credential-literal has three push sites and four arms, chosen by position: header and env (a literal in one of the four maps; params connector, name), credential-env (a credential typed into transport.credentialEnv) and stdio-auth (a stdio MCP server whose auth reaches nothing); both carry only connector. ⛔ No param and no sentence carries the credential value, following the lint-flow-credential-literals precedent. The sentences name the console's own page, Connector credentials (connectorCredentials.title).
  • permission-retired-lifecycle-residue: params permissionSet, object, key. The sentence says the line grants nothing, to delete it, and that restore and purge are granted through the recycle bin (recycleBin.title).
  • predicate-rhs-path-shaped: params view, word, op, set on the bare-word arm only; the dotted arm refuses the write and carries none. The view name is threaded through checkPredicate / walkFields (same file). Per the rule's own note (validate-predicate-path-refs.ts, §One position, one prescription: which reading the author meant is not decided), the sentence names both readings and the supported spelling of each (== 'active', or the field on the left: data.active == 'yes') and never prescribes the root in place (== data.active is the refused arm).
  • authoring-rules.ts: the lintConnectorCredentialLiterals and validateRetiredPermissionResidue adapters rebuilt findings field by field and dropped userTextParams; each gets the one-line conditional spread (fix(lint): the approval and flow advisories a flow save returns carry a user sentence in every platform locale #33120, feat(lint): data-model advisories carry a localized user sentence #33140 precedent). validatePredicatePathRefs passes findings through unchanged.
  • Changeset 32842-advisory-user-text.md, @objectstack/lint patch.

Tests

New finding-user-text.connector-permission-form.test.ts, 21 tests, in the shape of finding-user-text.data-model.test.ts:

  • exact coverage of each catalog (ids × 11 locales) and its identity in FINDING_USER_TEXT_CATALOG; the seven never-at-door ids have no entry;
  • every push site rendered from its real finding in every locale (connector header, env, credentialEnv and stdio-auth; permission allowRestore and allowPurge; the bare-word predicate), with no undefined, no unfilled ${, every shown name present, and no locale falling back to the English sentence; the four connector arms render apart in every locale;
  • no credential sentinel in any param or any locale's sentence;
  • both predicate readings in every locale, for == and !=, and never the root in place; the dotted arm carries no params;
  • one runtime-gate case per write type (connector, permission, view) through runRuntimeAuthoringRules, with the advisory's params checked and userMessage parsed under RuntimeAuthoringIssueSchema (de-DE, ja-JP, fr-FR).

Acceptance notes

Re-measure. The card's table lists 10 ids in 8 files; the static scan matches (each id produces exactly one warning whole-stack). Through the save door (runRuntimeAuthoringRules on each id's own write type with an objects context, main a83c3667), 3 reach a user. This PR writes catalogs and pins for those 3 only. Under #32835's Done-when alternative, the 7 that never reach the door:

  • (a) reference-integrity suite members on its frozen flow default. An agent, app, skill or mapping write dispatches none of them, and a flow write's snapshot carries no agents, apps, skills or mappings. Measured: 0 at the door for each.
    • agent-authoring-withdrawn, default-agent-outside-roster, default-agent-legacy-alias (validate-ai-agent-authoring.ts)
    • mapping-connector-source-unknown (validate-mapping-connector-source.ts)
    • ai-skill-tool-unresolved (validate-ai-tool-references.ts): kept off the door on purpose by ruling 5791822697 (ADR-0109 Decision §3), since a per-write snapshot cannot resolve a skill's tools truthfully.
  • (b) CLI-only registry entries.
    • security-external-owner-unmapped (validateExternalOwnerSharing, RUNTIME_OBJECT_ADVISORY_VOLUME)
    • sharing-rule-object-not-shareable (validateSharingRuleEnforceability, pending a sharing_rule door that carries sharingRules)

Agent-written sentences for the native-review round. Every non-English sentence in this PR is agent-written: 3 ids (6 sentences counting the connector's four arms) × 10 locales (zh-Hans, zh-Hant, es, ar, fr, pt-BR, ru, de, ja, ko), in the three *.user-text.ts files above. Terms follow ui/packages/i18n: Connector credentials 连接器凭据 / 聯結器憑據 / Credenciales de conectores / بيانات اعتماد الموصلات / Identifiants des connecteurs / Credenciais de conectores / Учётные данные коннекторов / Connector-Zugangsdaten / コネクタ認証情報 / 커넥터 자격 증명; recycle bin 回收站 / 回收站 / papelera / سلة المحذوفات / corbeille / lixeira / корзина / Papierkorb / ごみ箱 / 휴지통; permission set as in validate-security-posture.user-text.ts. zh-Hant uses the console's 聯結器 and 回收站, which differ from the flow catalog's 連接器; the native round may want to settle one spelling. The connector stdio-auth and the predicate sentences carry the most technical wording and are the best first read.

Metadata-protocol. none. The #32655 precedent (#32790) declared added / Clause-②: yes because it added a spec field (userMessage, the api authorable shard); this PR adds no spec field and moves no shard, so it follows #32835's reading for its lint-only sub-cards (#32836, #32837, #32838 all none).

Ruling grep (ruling|Ruling-ref|maintainer|⛔ across the surface, read before editing): connector credential literals 2 (never the value; stdio needs credentialEnv), retired permission residue 3 (director ruling D, not re-adjudicable; table not derived live), predicate path refs 2 (the consumer's ruling on the right side; which reading the author meant is undecided), finding-user-text.ts 2, the four never-at-door files 0–2 each (untouched). The sentences keep every one of them; none is reversed.

Serial. #32838 (PR #33140) landed on finding-user-text.ts and authoring-rules.ts while this was in flight; main was merged once and every catalog line and adapter spread from both is kept. #33072's validate-expressions.ts and expression entry are untouched.

Ablations (source, committed first, scripts/ablation-replace.mjs, anchor 1 → 0 on disk, each restored to the HEAD blob 9968f378 and verified by hash):

  • deleting the lintConnectorCredentialLiterals adapter spread: 1 failed / 20 passed, exactly the connector-write gate case;
  • deleting the validateRetiredPermissionResidue adapter spread: 1 failed / 20 passed, exactly the permission-write gate case.

Verification

Head ad51300d, c371f044 merged with origin/main at 6482c083 (once, after #33140 landed on finding-user-text.ts; conflict on the catalog list resolved by keeping both sides; authoring-rules.ts auto-merged with all four adapter spreads present). After the merge: pnpm install --frozen-lockfile, full pnpm turbo run build --concurrency=2 (lock VERDICT exit 0), packages/runtime/.objectstack cleared.

  • pnpm --filter @objectstack/lint test: 206 files, 8762 tests passed (VERDICT exit 0). Before the merge, at c371f044: 204 files, 8732 passed, 5 skipped.
  • pnpm --filter @objectstack/lint typecheck: exit 0 (includes check:test-typecheck, debt held). node scripts/check-cli-command-ids.mjs: exit 0. check:nul-bytes: exit 0.
  • Consumers of the widened finding shape, each its real reading at the merged head:
    • @objectstack/metadata-protocol (the save door; protocol.runtime-authoring-gate.test.ts names these ids): 337 files passed, 7 skipped; 22799 tests passed, 36 skipped.
    • @objectstack/cli test/connector-credential-literal-doors.e2e.test.ts (nightly tier, integration project, spawns the CLI): 2 tests passed.
  • Derived gates: dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at ad51300d derives 62 families (identical list to c371f044); every line run with stdin closed, exit captured before any pipe, all 62 exit 0. --ran reconciliation: 62 derived, 62 run, 0 NOT-MEASURED (a derived zero). Record: spec-seat-2/r2026-11-13/out/32842-gates-ran-ad51300d.list.
Gate readings that were not exit 0 on the first sweep (c371f04)
  • check-plugin-teardown-shape --self-test exit 3: the shallow clone could not reach the pinned fixture commit 621a4876; after fetching it, exit 0 (and exit 0 in the post-merge sweep).
  • No dogfood battery: the title has no ! and Clause-②: carries no (narrowing) arm.

Generated by Claude Code

… user sentence in every platform locale

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibwqZc5F1URP3eghRix
…-ci8fau

# Conflicts:
#	packages/lint/src/finding-user-text.ts
@github-actions github-actions Bot added size/l 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 17 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 credentialEnv (literal, a string literal in CONNECTOR_CREDENTIAL_LITERALS_USER_TEXT; a string literal in env))
  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see
  • 4 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 6e4a2031f5b16d1849f8af51301b3d6458eb83e2 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 6e4a2031f5b16d1849f8af51301b3d6458eb83e2

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

…-ci8fau

# Conflicts:
#	packages/lint/src/finding-user-text.ts
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

维护者速读

  • 改了什么:连接器凭据明文、权限集里已退役的生命周期键、表单显示条件里未加引号的单词这 3 条 lint 提示,在运行时保存连接器、权限集、视图时会带上用户可读的一句话(11 种平台语言);规则 id、message、hint 和 CLI 输出都不变。authoring-rules.ts 加了两行,把参数透传给运行时门禁。
  • 为什么改:非英语 Studio 里这些提示原来只显示英文;这是 lint: every save-door advisory rule carries a localized user sentence (67 rule ids in 36 rule files remain after the security posture), per ruling C on #32440 #32835 系列的最后一张卡。
  • 风险与回滚:只加文案和参数,@objectstack/lint patch,Clause-②: no;回滚即 revert。凭据提示的参数只带连接器名、键名和位置,从不带值。显示条件那句同时给出“文本”和“字段”两种读法,不替作者选。
  • 先看:packages/lint/src/lint-connector-credential-literals.user-text.ts 和 validate-predicate-path-refs.user-text.ts。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/finding-user-text.connector-permission-form.test.ts,预期全绿。
  • 需要你做的:无,席位落地。非英语句子由 agent 写,已列出等母语复核。

席位意见:可以落地。

席位核对

ACCEPT · seat domain:spec#2 · head b5684363 · 2026-11-13T20:09Z

  1. Shape: Fixes #32842, draft, base main, 10 files, +842/−4; changeset .changeset/32842-advisory-user-text.md (@objectstack/lint patch). Clause-②: no, Metadata-protocol: none, matching claim 6859691635 and dev report 6861998143.

  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, in the catalog list 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–lint: the pages, forms, navigation and translations advisories carry a localized user sentence (9 rule ids in 8 rule files), per ruling C on #32440 #32840 already use. The dev re-measured through runRuntimeAuthoringRules before writing catalogs: 3 of the card's 10 ids reach the publish door (connector, permission and view writes). 7 never do and are named with reasons in the changeset and Acceptance notes: 5 are reference-integrity members on the flow default, so agent, app, skill and mapping writes never run them (ruling 5791822697 for the skill one); 2 are CLI_ONLY entries.

  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:

    • the three catalog imports;
    • the two adapter spreads (lintConnectorCredentialLiterals, validateRetiredPermissionResidue), which rebuilt findings field by field and dropped the params;
    • connector-credential-literal's params at its sites: connector name, entry key and position (header · env · credential-env · stdio-auth). ⛔ Never the value;
    • permission-retired-lifecycle-residue's set, object and key;
    • the view name threaded through checkPredicate / walkFields, with params set on the bare-word arm only (the dotted arm refuses the write).

    Against the ⛔ note at validate-predicate-path-refs.ts:212 (the author's reading is undecided), the bare-word sentence names both readings and gives each one's spelling: quote the text, or put the field data.<word> on the LEFT. It never suggests adding the root in place. Pins hold both readings in every locale for == and !=.

  4. Gates / check-runs: dev at ad51300d: lint 206 files / 8762, metadata-protocol 337 files / 22799, cli connector e2e 2, 62/62 derived gates exit 0; each adapter spread ablated alone (1 failed / 20 passed each), restored by hash. After merging main 6e4a2031 (feat(lint): form layout, visibility and page-sort advisories carry a localized user sentence #33143; conflict only in finding-user-text.ts's two lists, both kept): lint 207 files / 8788, typecheck exit 0, check-cli-command-ids exit 0. CI on b5684363: 66 runs, 55 success, 11 skipped, 0 failed. K = 2; git merge-tree against main 40c73a8f is clean. The docs drift check names automation/connectors.mdx only through a credentialEnv string inside the new catalog; no documented behaviour changes.

  5. Accepted deviations and out-of-scope findings: the 7 unreachable ids stay on the English message. Agent-written non-English sentences are listed for the native-review round. The adapter lines in authoring-rules.ts fall inside the claim's surface.

  6. Landing: not governed, 846 changed lines. The seat readies the PR and arms auto-merge under the maintainer's standing words 「车道经理应该使用 pm-dispatch 技能审核和落地pr」. On MERGED: lint: the AI agents and tools, connectors, mappings and sharing advisories carry a localized user sentence (10 rule ids in 8 rule files), per ruling C on #32440 #32842 closes via Fixes; pm:dispatched comes off; lint: every save-door advisory rule carries a localized user sentence (67 rule ids in 36 rule files remain after the security posture), per ruling C on #32440 #32835's last listed sub-card is done.

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/l 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