Skip to content

fix(lint): one-line fix lines for 18 more author-time rule ids; os explain RULE_ID carries what they explained - #22936

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22161-s2-lint-slice-13-hints
Oct 12, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22161-s2-lint-slice-13-hints

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Part of #22161
Clause-②: no

Held out of the merge queue by #22949. Released 2026-10-12: #22949 closed, and the seat re-queued this PR.

Stage 2 of the card, slice 13: the over-long fix: lines, continued in slices 11 and 12's shape. Each finding's hint (the CLI's fix: line, the runtime issue's hint) of 18 more packages/lint rule ids, in eight whole rule files, is now one remedy line of at most 200 characters, and what the lines used to explain is each id's RULE_EXPLANATIONS entry behind os explain RULE_ID. The card stays open: 73 author-time ids still carry a hint over 200 characters (the last section).

What changes

  • One remedy line per finding. The 18 ids, every hint the lint suite fires, before → after (count of distinct hints · length range):
rule id file lint suite, before → after runtime door
flow-credential-literal lint-flow-credential-literals.ts 3 · 488–646 → 3 · 141–177 flow write, 2xx advisory
flow-update-readonly-when-field validate-readonly-flow-writes.ts 2 · 531–549 → 2 · 165–174 flow write, 2xx advisory
flow-update-readonly-field same 10 · 230–400 → 10 · 107–143 flow write, 422
action-dispatch-contract-mismatch validate-action-dispatch-contract.ts 3 · 491–514 → 3 · 124–157 CLI only
security-controlled-by-parent-ambiguous-relation validate-security-posture.ts 1 · 496 → 3 · 167–174 object write, 422
security-master-detail-ungranted same 8 · 271–314 → 8 · 124–153 object / permission write, 2xx advisory
security-controlled-by-parent-no-relation same 1 · 294 → 1 · 194 object write, 422
security-fls-unknown-field same 3 · 276–293 → 4 · 89–132 permission write, 422
security-wildcard-vama same 1 · 218 → 1 · 103 permission write, 422
sharing-rule-object-not-shareable validate-sharing-rule-enforceability.ts 3 · 471–482 → 3 · 133–144 CLI only
sharing-rule-object-controlled-by-parent same 2 · 313–395 → 2 · 155–169 CLI only
page/visualization-without-binding validate-page-visualization-bindings.ts 14 · 395–480 → 14 · 148–183 CLI only
action-api-update-readonly-when-field validate-readonly-action-writes.ts 4 · 458–470 → 4 · 182–188 CLI only
flow-api-trigger-secret-missing validate-flow-trigger-readiness.ts 1 · 461 → 1 · 171 flow write, 422
flow-time-relative-descriptor-unroutable same 1 · 441 → 1 · 166 flow write, 422
flow-trigger-unroutable same 1 · 295 → 1 · 163 flow write, 422
flow-trigger-unknown-event same 2 · 191–268 → 2 · 96–101 flow write, 422
flow-trigger-unknown-object same 2 · 179–210 → 2 · 125–156 flow write, 2xx advisory

A count that rises is a line that now names something per finding: the ambiguous-master line names the tier that tied (three tiers fire), and the FLS line names the nearest column (with and without one).

  • The reasoning moves, the remedy stays.
    • flow-credential-literal: each position names the auth variant that holds the credential and the connector_action node; the connector's provider ('rest'), the Basic variant, where credentialRef resolves, that a {variable} template draws nothing, that nothing is refused and the connector guide are the explanation's. The variants are held to ConnectorInstanceAuthSchema by the rule's test.
    • flow-update-readonly-field: one line per verb (runAs:'system', the create-time beforeInsert stamp, removing the field); why the system channel is intended and why a hook-assigned key survives the strip are the explanation's.
    • flow-update-readonly-when-field and action-api-update-readonly-when-field: the two routes and the refusal of elevation in one line, the same shape hook-api-update-readonly-when-field took in slice 12; why a hook-derived value lands on a locked record, the drop-the-field alternative and "never blocks a build" are the explanations'.
    • action-dispatch-contract-mismatch: both ends in one line (declare the wiring's contract, or rewire to the declaration's); how to pick, and why two wanted wirings are two actions, are the explanation's.
    • The five security ids: the tier precedence, what each sharing baseline means, the sys_* / isSystem alternative, what a field rename did to an FLS mask and where an app-level admin set belongs (ADR-0090 D9) are the explanations'.
    • The two sharing anchor ids: both honest fixes in one line; how to tell which half is wrong (ADR-0049 enforce-or-remove), why re-homing onto another public object fixes nothing, and that the detail rows follow the master are the explanations'.
    • page/visualization-without-binding: the three routes in one line, naming the field TYPE the derivation looks for; why sourceView is the one channel on this door and the name fallback are the explanation's (which already writes out the whole derivation, held to OBJECTUI_DERIVATION_PREDICATES).
    • The flow trigger ids: the insert synonym and the missing "any change" token, why multi-event arrays are deferred, the descriptor's optional keys, the schedule's default and its docs page, the signature format and its 300-second window, and the autolaunched alternative are the explanations'. The descriptor keys the line names are held to TimeRelativeTriggerSchema by the rule's test.
  • A list becomes a nearest name. security-fls-unknown-field listed up to twelve of the object's fields; it now names the nearest declared or registry-injected column when one is close (nearestName, the package's one "Did you mean" helper, over the two sets the rule's own resolution accepts) and none otherwise. At the runtime door the old list made the line longer than anywhere in the lint suite (324).
  • Two ids gain an explanation entry, because their reasoning had no entry to move into: security-wildcard-vama ("why the '*' entry cannot carry View All / Modify All") and flow-trigger-unknown-object ("why an unknown trigger object only warns"). Their rule: line now ends with the os explain pointer, which explainPointer prints for every id the table holds. Both constants were already exported, so the barrel is unchanged. The other 16 entries gain paragraphs.
  • Nothing else moves. Every message, rule id, severity and path, and what each rule accepts or refuses, is unchanged: every hunk in the eight rule files is a hint expression, a comment, a deleted hint-only constant (RESOLVES, NOT_A_GATE, and the FLS roster), the nearest-name computation that feeds the FLS hint, or describeSearch dropping its name-pattern clause (its only caller is the hint). No condition, branch, skip or dedupe moved.
  • .changeset/22161-lint-slice-13-one-line-fix.md: @objectstack/lint patch, Clause-②: no, with the runtime hint FROM → TO for every id that fires at the runtime gate.

The runtime wire (Zone 2 item 3)

Measured at the door with runRuntimeAuthoringRules on the base dist and on the rebuilt one (a scratch probe, 22 writes):

  • a flow write: flow-credential-literal (2xx advisories[].hint, header / query / connector input 646 / 528 / 488 → 173 / 177 / 141), flow-update-readonly-field (422 issues[].hint, update / create 239 / 396 → 116 / 139), flow-update-readonly-when-field (2xx advisory, 527 → 161, under runAs: 'user' and 'system'), flow-trigger-unknown-object (2xx advisory, record / time-relative 210 / 179 → 156 / 125), flow-trigger-unknown-event (422, token / array 191 / 268 → 101 / 96), flow-time-relative-descriptor-unroutable (422, 441 → 166, alone and beside a schedule), flow-trigger-unroutable (422, 295 → 163, an off-chain token and none) and flow-api-trigger-secret-missing (422, 461 → 171);
  • an object write: security-controlled-by-parent-no-relation (422, 294 → 194), security-controlled-by-parent-ambiguous-relation (422, 496 → 167) and security-master-detail-ungranted (2xx advisory, 304 → 147);
  • a permission write: security-fls-unknown-field (422, 324 → 132), security-wildcard-vama (422, 218 → 103) and security-master-detail-ungranted (2xx advisory, 304 → 147);
  • no write reaches action-dispatch-contract-mismatch, page/visualization-without-binding, action-api-update-readonly-when-field or the two sharing-rule-object-* ids (a view bulk wiring, a list page, a sharing_rule and an action body write were probed): validateSharingRuleEnforceability is CLI-only, and the other three are reference-suite members on the frozen flow default whose collections a flow snapshot does not carry.

Readers of hint, read-only against objectui main at 2498a075 (fetched into a ref of its own, git grep, no edit; the ref deleted after): Studio's saveAdvisoryToast.ts formatFinding renders [rule] where — message hint for every 2xx advisory, so the credential, readonlyWhen, unknown-object and ungranted-detail toasts shorten. DraftChangesPanel.tsx renders p.hint || p.message for the in-browser security-posture pass (securityPostureLint.ts, which runs validateSecurityPosture from the installed @objectstack/lint over object drafts and keeps error findings), so for security-controlled-by-parent-no-relation and -ambiguous-relation the panel shows the new line in place of the message once objectui takes this release. metadata-client.ts checks only that hint is a string, and the 422 issues renderers read path and message. REST (PUT /api/v1/meta) forwards issues / advisories as JSON; packages/mcp has no metadata write tool, so an MCP or AI author reaches the gate through REST. In this repository toIssue forwards hint verbatim and the advisory log line prints it; the one test outside packages/lint that reads one of these hints (runtime-authoring-gate.flow-credential-literal.test.ts, toContain('credentialRef')) holds. No reader in either repository matches on hint text.

check:watch-hint-literal guards something else of the same name (every *_WATCH_HINTS population declaration in a gate script is a literal array): exit 0 standalone (72 declarations across 4 rostered names) and inside the gate set.

Census (taken first, before any edit, at the base 7dbb89dc5d)

Method: the earlier slices' scratch preload (NODE_OPTIONS=--import, never committed) patching Array.prototype.push to record every finding-shaped object, deduped by (rule, hint) per process, with its push site, appended per new key so a worker killed at teardown loses nothing. Lengths are the hint alone. Runs, each before and after:

  • packages/lint suite (137 files; 6,529 tests at the base): the 18 ids re-measured at the base equal the claim's lengths exactly (646, 549, 400, 514, 496, 314, 294, 293, 218, 482, 395, 480, 470, 461, 441, 295, 268, 210). After, at ec4f1802f: 97 ids over 200 = 21 of the 26 fenced ids, the 3 repo-gate ids (lint-startup-registry-verdict.ts) and 73 author-time ids (slice 12's 91 less these 18; every one at slice 12's length). The first "before" run recorded only hint-shaped findings; the after run also records the fix-shaped findings data-model-rules.ts builds (object/missing-name-field 327, rollup/non-numeric-aggregand 282, field/select-missing-options 207), files this slice does not touch, so the comparable before is 115.
  • Runtime publish gate, the whole @objectstack/metadata-protocol suite reading @objectstack/lint from its built dist (base dist before, rebuilt after): flow-credential-literal (3 · 488–646 → 3 · 141–177), security-master-detail-ungranted (299 → 146), flow-api-trigger-secret-missing (461 → 171) and flow-trigger-unknown-object (210 → 156) of the 18.
  • packages/cli unit tier (282 files): none of the 18 fires, before or after.
  • os validate (the built CLI) on app-crm, app-todo, app-showcase, app-multi-package, exit 0 each, before and after: none of the 18 fires; each prints the same 9 / 7 / 71 / 3 rule: lines, and the output is byte-identical once the two millisecond timings (Load time, Validation passed (…ms)) are normalised — those four lines per app are the only difference.

Tests

Every heavy run went through scripts/pm/os-verify-lock.sh (slot issue-22161-s13); its VERDICT lines are in the report.

  • Each rule's own suite pins the new shape. Every touched suite already records what its cases fire; a new last block per file holds every recorded fix: line of its converted ids to one line of at most 200 characters behind a coverage control (each arm fired: the three credential positions; both readonly verbs; both dispatch directions; all five security ids, the FLS id with and without a nearest column; both sharing anchor ids, the detail id with and without a resolved master; every visualization's type half; every flow trigger arm), plus a check per id that explainRule(id) holds what the line stopped saying. Pins that read the old hint text moved to the explanation, text-only (the readonlyWhen flow pins' "Elevation is not a workaround here" / "does land, even on a locked record", the array-trigger pin's "multi-event arrays are deferred"). Three new pins hold an explanation's spelled-out list to its source of truth: the auth variants to ConnectorInstanceAuthSchema, the descriptor keys to TimeRelativeTriggerSchema, and the FLS nearest name across declared and injected columns.
  • Lint suite at ec4f1802f: Test Files 137 passed (137), Tests 6,561 passed (6,561); VERDICT command-exit 0 (6,529 at the base).
  • Lint build + typecheck: pnpm --filter @objectstack/lint build (check-dts-emitted 6/6) and run typecheck (check:test-typecheck OK, 2 files / 6 errors / 2 pinned signatures held), VERDICT command-exit 0 each. The rebuilt dist carries the new text (rewire it as and is the nearest) 1 hit each in index.js, index.cjs, runtime.js, runtime.cjs; the old Pick the contract the body and Object names match exactly. Check 0).
  • The built entries' export closure is unchanged (Clause-② check): the runtime export keys and the declaration exports of index, runtime and rule-explanations, read from the base dist and from the rebuilt one, are identical — 370 / 552, 6 / 11 and 2 / 3 names, 0 added and 0 removed.
  • CLI unit tier on the rebuilt dist: Test Files 282 passed (282), Tests 4,262 passed (4,262), VERDICT command-exit 0 (the same before). It includes explain-rule-id.test.ts, which runs every RULE_EXPLANATIONS entry through os explain, the two new ones included. No packages/cli or packages/metadata-protocol test asserts these ids' hint text beyond the credentialRef pin above, so the declared rider went unused.
  • Runtime gate: the whole @objectstack/metadata-protocol suite, after: Test Files 226 passed, 3 skipped (229), Tests 28,171 passed, 19 skipped; VERDICT command-exit 0 (the same before).
  • Ablation (one-shot, from the committed head ec4f1802f, through scripts/ablation-replace.mjs under the lock, with a shell trap restoring HEAD by absolute path). The rule suite imports the rule source, so there is no dist leg. security-wildcard-vama's pre-slice 218-character hint was restored as one literal (anchor x1 → x0, blob ee3feebdbbe9 → 0db2ab189761). Predicted before the run: exactly the new bound pin goes red, every other case stays green. Observed: src/validate-security-posture.test.ts Tests 1 failed, 154 passed (155), exactly that pin, reading 218. Restored: blob ee3feebdbbe9 == HEAD, git diff HEAD empty, git status --porcelain empty.
  • ESLint, narrowed: npx eslint --no-inline-config --format json over the 17 changed .ts files: 17 files in the report, 0 errors, 0 warnings. The population is eslint.config.mjs's **/*.{ts,…} block plus its packages/** blocks; the config never enables type-aware linting (no parserOptions.project), so no untouched file's verdict can move. A control-character scan of every changed file found no match.
  • Gates: one merge of origin/main through scripts/pm/os-regen-merge.sh (407cee32b → ec4f1802f, onto 018b077fb; no conflict, and the branch edits no generated artifact). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with no paths at the final head ec4f1802f (18 paths vs the merge base) derived 62 commands; 60 ran, each exit code captured before any pipe, all 0 (longest: check:query-options-erasure 336s, check:slot-lookup 182s, check-comment-mask-corpus 156s). check-plugin-teardown-shape --self-test first refused (exit 3: its positive-control fixture is pinned to commit 621a48760788, unreachable in this shallow clone); after fetching that one commit it ran, exit 0. NOT MEASURED, as dispatched for slice 12: check:dual-build-cjs-loads (needs every package's dist) and check:type-check-debt (its script is --re-measure; check:type-check-coverage ran). --ran: Run reconciliation — 62 derived, 60 run, 2 NOT-MEASURED, 0 UNRUN. The artifact-roster block (51 families): the 48 that need no pull request exited 0 at ec4f1802f, the 4 rostered under a touched directory among them (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity); check:published-readme-exports after building its four missing dists (client-react, embedder-openai, knowledge-ragflow, organizations, 54 tasks) under the lock (its first answer was the exit-3 prerequisite refusal, not a verdict); the three PR-context guards run against this PR and report on the card. origin/main moved three commits after the merge (e4ad6776d, 06cc45dc3, 85b4ac634); none touches a file of this slice, and the branch was not merged again.

Acceptance notes

  • A long name can still lengthen a line. The bound holds on every variant the suites, the runtime probe and the four example apps fire; the names in a hint (the field and object of the readonly ids, the action name, the master and detail objects, the FLS object and nearest column, the page's object) are the author's. The closest measured cases are security-controlled-by-parent-no-relation at 194 (no author text: a constant line) and action-api-update-readonly-when-field at 188.
  • The FLS field list now prints nowhere. A near miss gets the nearest column; a far miss gets none, where it used to get up to twelve of the object's fields. The card's "Asked for" names no list, and slices 11 and 12 made the same call for their lists.
  • Two rule: lines gain the os explain pointer (security-wildcard-vama, flow-trigger-unknown-object), through the new entries. That is the printer's own rule for an id the table holds, not a printer change.
  • Studio's draft panel is a second reader of two security hints. It shows hint in place of message for the two controlled-by-parent errors; the new lines read as remedies there, as the old ones did, only shorter.

Remaining for this card

73 author-time packages/lint ids still carry a hint over 200 characters in the final lint census (at ec4f1802f: 97 over 200 = 73 + 21 fenced + 3 repo-gate), by file, longest first:

  • lint-flow-patterns.ts (2): flow-double-brace-interpolation 1339, flow-bare-dollar-reference 1231
  • data-model-rules.ts (6): unique/legacy-organization-composite 578, unique/unscoped-declared-index 380, object/missing-name-field 327, rollup/non-numeric-aggregand 282, unique/double-declaration 279, field/select-missing-options 207
  • validate-widget-bindings.ts (11): dashboard-filter-field-unprovisioned 561, chart-field-unknown 415, dashboard-filter-field-unknown 403, chart-measures-missing 369, widget-legacy-analytics-shape 338, widget-measures-missing 330, widget-filter-field-unknown 322, widget-legacy-analytics-unrenderable 320, table-count-only 311, chart-dimensions-missing 291, dashboard-filter-field-not-included 218
  • validate-searchable-fields.ts (3): searchable-field-unknown 448, searchable-field-unprovisioned 362, searchable-field-unsearchable 328
  • validate-flow-filter-tokens.ts (1): flow-filter-token-unknown 427
  • validate-action-locations.ts (1): action-no-placement 424
  • validate-mapping-target-fields.ts (1): mapping-target-field-unknown 405
  • validate-dashboard-widget-options.ts (1): unconsumed-widget-option 396
  • validate-sortable-fields.ts (3): sort-field-unprovisioned 378, sort-field-unsortable 332, sort-field-unknown 301
  • validate-visibility-predicates.ts (3): visibility-predicate-unknown-function 375, visibility-bare-identifier 221, visibility-predicate-syntax 214
  • validate-nav-access.ts (1): nav-object-ungranted 370
  • validate-hook-body-writes.ts (3): hook-body-write-unprovisioned-anchor 365, hook-body-write-unknown-field 275, hook-body-source-unparseable 254
  • validate-ai-agent-authoring.ts (2): agent-authoring-withdrawn 363, default-agent-outside-roster 237
  • validate-react-page-props.ts (4): react-chart-field-unprovisioned 362, react-prop-missing-required 277, react-page-source-unparseable 254, react-block-needs-record-context 228
  • validate-translatable-sections.ts (1): translation-section-name-missing 359
  • validate-action-body-writes.ts (4): action-body-write-unprovisioned-anchor 358, action-body-write-unknown-field 323, action-record-write-discarded 309, action-body-source-unparseable 254
  • validate-flow-node-writes.ts (2): flow-node-write-unprovisioned-anchor 358, flow-node-write-unknown-field 289
  • validate-managed-api-methods.ts (1): object/managed-api-method-unaffordable 357
  • validate-nav-object-servability.ts (1): nav-object-unservable 348
  • validate-rule-compilability.ts (2): validation-rule-json-schema-uncompilable 341, validation-rule-regex-uncompilable 300
  • validate-dataset-references.ts (2): dataset-include-unknown 336, dataset-field-unknown 206
  • validate-translation-references.ts (1): translation-target-unknown 317
  • validate-approval-approvers.ts (3): approval-approver-not-membership-tier 313, approval-expression-no-empty-policy 294, approval-expression-invalid 292
  • validate-seed-replay-safety.ts (1): seed-insert-mode-duplicates-on-replay 309
  • validate-chart-bindings.ts (1): chart-measure-unknown 285
  • validate-object-field-refs.ts (1): object-field-ref-unknown 284
  • validate-capability-references.ts (1): capability-reference-unknown 273
  • validate-record-title.ts (2): title-format-retired 267, title-unresolvable 237
  • validate-list-view-field-refs.ts (2): list-view-field-unknown 258, list-view-field-dotted 233
  • validate-view-containers.ts (1): view-container-shape 256
  • validate-seed-state-machine.ts (1): seed-value-outside-state-machine 251
  • validate-ai-surface-affinity.ts (1): ai-skill-surface-mismatch 235
  • validate-page-source-styling.ts (1): page-source-className-tailwind 231
  • lint-liveness-properties.ts (1): liveness-planned-property 230
  • validate-list-view-mode.ts (1): list-view-filters-in-views-mode 219

Not in the 73: the 26 fenced ids as PR #22448 lists them (21 fire a hint over 200), the 3 repo-gate ids (startup-open-vocabulary-verdict, startup-source-unparseable, startup-verdict-assertive-wording), and the packages/cli ids on #22841.


Generated by Claude Code

Slice 13 work in progress: hints of the eight claimed rule files, their
RULE_EXPLANATIONS entries and the rules' tests.

Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json e4ad6776d138132d170c1e7d51ad7239167b9821.

⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: master_detail (literal, 32 pages)
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 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 e4ad6776d138132d170c1e7d51ad7239167b9821 → packageMentionDocs.

Which tree this was computed on

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

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 38203387941 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core — 失败步骤: Check the per-package suite-duration ceilings(日志不可读,点进 job 看)

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️ 本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 5 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue triage for run 38203387941 (6117914840) · domain:spec seat 1 · session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T04:48Z. Card #22161.

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 38204753915 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core — 失败步骤: Check the per-package suite-duration ceilings(日志不可读,点进 job 看)

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️ 本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 6 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 12, 2026
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit 6a7eca6 Oct 12, 2026
50 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22161-s2-lint-slice-13-hints branch October 12, 2026 09:13
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants