Repository navigation
fix(lint): one-line fix lines for 18 more author-time rule ids; os explain RULE_ID carries what they explained - #22936
Conversation
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>
Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-13-hints
📓 Docs Drift CheckThis PR changes 1 package(s): 25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # 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
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38203387941 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
Queue triage for run
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38204753915 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Part of #22161
Clause-②: no
Stage 2 of the card, slice 13: the over-long
fix:lines, continued in slices 11 and 12's shape. Each finding'shint(the CLI'sfix:line, the runtime issue'shint) of 18 morepackages/lintrule 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'sRULE_EXPLANATIONSentry behindos explain RULE_ID. The card stays open: 73 author-time ids still carry a hint over 200 characters (the last section).What changes
flow-credential-literallint-flow-credential-literals.tsflowwrite, 2xx advisoryflow-update-readonly-when-fieldvalidate-readonly-flow-writes.tsflowwrite, 2xx advisoryflow-update-readonly-fieldflowwrite, 422action-dispatch-contract-mismatchvalidate-action-dispatch-contract.tssecurity-controlled-by-parent-ambiguous-relationvalidate-security-posture.tsobjectwrite, 422security-master-detail-ungrantedobject/permissionwrite, 2xx advisorysecurity-controlled-by-parent-no-relationobjectwrite, 422security-fls-unknown-fieldpermissionwrite, 422security-wildcard-vamapermissionwrite, 422sharing-rule-object-not-shareablevalidate-sharing-rule-enforceability.tssharing-rule-object-controlled-by-parentpage/visualization-without-bindingvalidate-page-visualization-bindings.tsaction-api-update-readonly-when-fieldvalidate-readonly-action-writes.tsflow-api-trigger-secret-missingvalidate-flow-trigger-readiness.tsflowwrite, 422flow-time-relative-descriptor-unroutableflowwrite, 422flow-trigger-unroutableflowwrite, 422flow-trigger-unknown-eventflowwrite, 422flow-trigger-unknown-objectflowwrite, 2xx advisoryA 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).
flow-credential-literal: each position names the auth variant that holds the credential and theconnector_actionnode; the connector'sprovider('rest'), the Basic variant, wherecredentialRefresolves, that a{variable}template draws nothing, that nothing is refused and the connector guide are the explanation's. The variants are held toConnectorInstanceAuthSchemaby the rule's test.flow-update-readonly-field: one line per verb (runAs:'system', the create-timebeforeInsertstamp, 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-fieldandaction-api-update-readonly-when-field: the two routes and the refusal of elevation in one line, the same shapehook-api-update-readonly-when-fieldtook 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.sys_*/isSystemalternative, what a field rename did to an FLS mask and where an app-level admin set belongs (ADR-0090 D9) are the explanations'.page/visualization-without-binding: the three routes in one line, naming the field TYPE the derivation looks for; whysourceViewis the one channel on this door and the name fallback are the explanation's (which already writes out the whole derivation, held toOBJECTUI_DERIVATION_PREDICATES).TimeRelativeTriggerSchemaby the rule's test.security-fls-unknown-fieldlisted 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).security-wildcard-vama("why the '*' entry cannot carry View All / Modify All") andflow-trigger-unknown-object("why an unknown trigger object only warns"). Theirrule:line now ends with theos explainpointer, whichexplainPointerprints for every id the table holds. Both constants were already exported, so the barrel is unchanged. The other 16 entries gain paragraphs.message, rule id, severity andpath, and what each rule accepts or refuses, is unchanged: every hunk in the eight rule files is ahintexpression, a comment, a deleted hint-only constant (RESOLVES,NOT_A_GATE, and the FLSroster), the nearest-name computation that feeds the FLS hint, ordescribeSearchdropping 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/lintpatch,Clause-②: no, with the runtimehintFROM → TO for every id that fires at the runtime gate.The runtime wire (Zone 2 item 3)
Measured at the door with
runRuntimeAuthoringRuleson the base dist and on the rebuilt one (a scratch probe, 22 writes):flowwrite:flow-credential-literal(2xxadvisories[].hint, header / query / connector input 646 / 528 / 488 → 173 / 177 / 141),flow-update-readonly-field(422issues[].hint, update / create 239 / 396 → 116 / 139),flow-update-readonly-when-field(2xx advisory, 527 → 161, underrunAs: '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) andflow-api-trigger-secret-missing(422, 461 → 171);objectwrite:security-controlled-by-parent-no-relation(422, 294 → 194),security-controlled-by-parent-ambiguous-relation(422, 496 → 167) andsecurity-master-detail-ungranted(2xx advisory, 304 → 147);permissionwrite:security-fls-unknown-field(422, 324 → 132),security-wildcard-vama(422, 218 → 103) andsecurity-master-detail-ungranted(2xx advisory, 304 → 147);action-dispatch-contract-mismatch,page/visualization-without-binding,action-api-update-readonly-when-fieldor the twosharing-rule-object-*ids (aviewbulk wiring, a listpage, asharing_ruleand anactionbody write were probed):validateSharingRuleEnforceabilityis CLI-only, and the other three are reference-suite members on the frozenflowdefault whose collections a flow snapshot does not carry.Readers of
hint, read-only against objectuimainat2498a075(fetched into a ref of its own,git grep, no edit; the ref deleted after): Studio'ssaveAdvisoryToast.tsformatFindingrenders[rule] where — message hintfor every 2xx advisory, so the credential, readonlyWhen, unknown-object and ungranted-detail toasts shorten.DraftChangesPanel.tsxrendersp.hint || p.messagefor the in-browser security-posture pass (securityPostureLint.ts, which runsvalidateSecurityPosturefrom the installed@objectstack/lintover object drafts and keepserrorfindings), so forsecurity-controlled-by-parent-no-relationand-ambiguous-relationthe panel shows the new line in place of the message once objectui takes this release.metadata-client.tschecks only thathintis a string, and the 422issuesrenderers readpathandmessage. REST (PUT /api/v1/meta) forwardsissues/advisoriesas JSON;packages/mcphas no metadata write tool, so an MCP or AI author reaches the gate through REST. In this repositorytoIssueforwardshintverbatim and the advisory log line prints it; the one test outsidepackages/lintthat reads one of these hints (runtime-authoring-gate.flow-credential-literal.test.ts,toContain('credentialRef')) holds. No reader in either repository matches onhinttext.check:watch-hint-literalguards something else of the same name (every*_WATCH_HINTSpopulation 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) patchingArray.prototype.pushto 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 thehintalone. Runs, each before and after:packages/lintsuite (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, atec4f1802f: 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 onlyhint-shaped findings; the after run also records thefix-shaped findingsdata-model-rules.tsbuilds (object/missing-name-field327,rollup/non-numeric-aggregand282,field/select-missing-options207), files this slice does not touch, so the comparable before is 115.@objectstack/metadata-protocolsuite reading@objectstack/lintfrom 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) andflow-trigger-unknown-object(210 → 156) of the 18.packages/cliunit tier (282 files): none of the 18 fires, before or after.os validate(the built CLI) onapp-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 / 3rule: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(slotissue-22161-s13); its VERDICT lines are in the report.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 thatexplainRule(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 toConnectorInstanceAuthSchema, the descriptor keys toTimeRelativeTriggerSchema, and the FLS nearest name across declared and injected columns.ec4f1802f: Test Files 137 passed (137), Tests 6,561 passed (6,561); VERDICT command-exit 0 (6,529 at the base).pnpm --filter @objectstack/lint build(check-dts-emitted6/6) andrun typecheck(check:test-typecheckOK, 2 files / 6 errors / 2 pinned signatures held), VERDICT command-exit 0 each. The rebuilt dist carries the new text (rewire it asandis the nearest)1 hit each inindex.js,index.cjs,runtime.js,runtime.cjs; the oldPick the contract the bodyandObject names match exactly. Check0).index,runtimeandrule-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.explain-rule-id.test.ts, which runs everyRULE_EXPLANATIONSentry throughos explain, the two new ones included. Nopackages/cliorpackages/metadata-protocoltest asserts these ids' hint text beyond thecredentialRefpin above, so the declared rider went unused.@objectstack/metadata-protocolsuite, after: Test Files 226 passed, 3 skipped (229), Tests 28,171 passed, 19 skipped; VERDICT command-exit 0 (the same before).ec4f1802f, throughscripts/ablation-replace.mjsunder the lock, with a shelltraprestoringHEADby 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, blobee3feebdbbe9→0db2ab189761). Predicted before the run: exactly the new bound pin goes red, every other case stays green. Observed:src/validate-security-posture.test.tsTests 1 failed, 154 passed (155), exactly that pin, reading 218. Restored: blobee3feebdbbe9== HEAD,git diff HEADempty,git status --porcelainempty.npx eslint --no-inline-config --format jsonover the 17 changed.tsfiles: 17 files in the report, 0 errors, 0 warnings. The population iseslint.config.mjs's**/*.{ts,…}block plus itspackages/**blocks; the config never enables type-aware linting (noparserOptions.project), so no untouched file's verdict can move. A control-character scan of every changed file found no match.origin/mainthroughscripts/pm/os-regen-merge.sh(407cee32b→ec4f1802f, onto018b077fb; no conflict, and the branch edits no generated artifact).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackwith no paths at the final headec4f1802f(18 paths vs the merge base) derived 62 commands; 60 ran, each exit code captured before any pipe, all 0 (longest:check:query-options-erasure336s,check:slot-lookup182s,check-comment-mask-corpus156s).check-plugin-teardown-shape --self-testfirst refused (exit 3: its positive-control fixture is pinned to commit621a48760788, 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) andcheck:type-check-debt(its script is--re-measure;check:type-check-coverageran).--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 atec4f1802f, 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-exportsafter 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/mainmoved three commits after the merge (e4ad6776d,06cc45dc3,85b4ac634); none touches a file of this slice, and the branch was not merged again.Acceptance notes
security-controlled-by-parent-no-relationat 194 (no author text: a constant line) andaction-api-update-readonly-when-fieldat 188.rule:lines gain theos explainpointer (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.hintin place ofmessagefor 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/lintids still carry a hint over 200 characters in the final lint census (atec4f1802f: 97 over 200 = 73 + 21 fenced + 3 repo-gate), by file, longest first:lint-flow-patterns.ts(2):flow-double-brace-interpolation1339,flow-bare-dollar-reference1231data-model-rules.ts(6):unique/legacy-organization-composite578,unique/unscoped-declared-index380,object/missing-name-field327,rollup/non-numeric-aggregand282,unique/double-declaration279,field/select-missing-options207validate-widget-bindings.ts(11):dashboard-filter-field-unprovisioned561,chart-field-unknown415,dashboard-filter-field-unknown403,chart-measures-missing369,widget-legacy-analytics-shape338,widget-measures-missing330,widget-filter-field-unknown322,widget-legacy-analytics-unrenderable320,table-count-only311,chart-dimensions-missing291,dashboard-filter-field-not-included218validate-searchable-fields.ts(3):searchable-field-unknown448,searchable-field-unprovisioned362,searchable-field-unsearchable328validate-flow-filter-tokens.ts(1):flow-filter-token-unknown427validate-action-locations.ts(1):action-no-placement424validate-mapping-target-fields.ts(1):mapping-target-field-unknown405validate-dashboard-widget-options.ts(1):unconsumed-widget-option396validate-sortable-fields.ts(3):sort-field-unprovisioned378,sort-field-unsortable332,sort-field-unknown301validate-visibility-predicates.ts(3):visibility-predicate-unknown-function375,visibility-bare-identifier221,visibility-predicate-syntax214validate-nav-access.ts(1):nav-object-ungranted370validate-hook-body-writes.ts(3):hook-body-write-unprovisioned-anchor365,hook-body-write-unknown-field275,hook-body-source-unparseable254validate-ai-agent-authoring.ts(2):agent-authoring-withdrawn363,default-agent-outside-roster237validate-react-page-props.ts(4):react-chart-field-unprovisioned362,react-prop-missing-required277,react-page-source-unparseable254,react-block-needs-record-context228validate-translatable-sections.ts(1):translation-section-name-missing359validate-action-body-writes.ts(4):action-body-write-unprovisioned-anchor358,action-body-write-unknown-field323,action-record-write-discarded309,action-body-source-unparseable254validate-flow-node-writes.ts(2):flow-node-write-unprovisioned-anchor358,flow-node-write-unknown-field289validate-managed-api-methods.ts(1):object/managed-api-method-unaffordable357validate-nav-object-servability.ts(1):nav-object-unservable348validate-rule-compilability.ts(2):validation-rule-json-schema-uncompilable341,validation-rule-regex-uncompilable300validate-dataset-references.ts(2):dataset-include-unknown336,dataset-field-unknown206validate-translation-references.ts(1):translation-target-unknown317validate-approval-approvers.ts(3):approval-approver-not-membership-tier313,approval-expression-no-empty-policy294,approval-expression-invalid292validate-seed-replay-safety.ts(1):seed-insert-mode-duplicates-on-replay309validate-chart-bindings.ts(1):chart-measure-unknown285validate-object-field-refs.ts(1):object-field-ref-unknown284validate-capability-references.ts(1):capability-reference-unknown273validate-record-title.ts(2):title-format-retired267,title-unresolvable237validate-list-view-field-refs.ts(2):list-view-field-unknown258,list-view-field-dotted233validate-view-containers.ts(1):view-container-shape256validate-seed-state-machine.ts(1):seed-value-outside-state-machine251validate-ai-surface-affinity.ts(1):ai-skill-surface-mismatch235validate-page-source-styling.ts(1):page-source-className-tailwind231lint-liveness-properties.ts(1):liveness-planned-property230validate-list-view-mode.ts(1):list-view-filters-in-views-mode219Not 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 thepackages/cliids on #22841.Generated by Claude Code