Repository navigation
fix(lint): one-line fix lines for 17 more author-time rule ids (slice 16); os explain RULE_ID carries what they explained - #23194
Conversation
… 16) (WIP) 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>
…ix lines (WIP) Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-16-hints
…-lint-slice-16-hints
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 5 release-owned page(s) also name something this change touched. These are read-only:
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 4a29b554f3527aa77bbcae183ddfaf12f2687377 && git checkout 4a29b554f3527aa77bbcae183ddfaf12f2687377
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4 abe4ce231a02ef0248491c676b4137a940e44afe && git checkout -B drift-repro d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4 && git merge --no-ff abe4ce231a02ef0248491c676b4137a940e44afe
node scripts/docs-audit/affected-docs.mjs --json d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4
|
Part of #22161
Clause-②: no
Stage 2 of the card, slice 16: the over-long
fix:lines, continued in slices 11 to 15's shape. Each finding'shint(the CLI'sfix:line, the runtime issue'shint) of 17 morepackages/lintrule ids, in twelve 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: 24 author-time ids still carry a hint over 200 characters (the last section).What changes
approval-approver-not-membership-tiervalidate-approval-approvers.tsflowwrite, 2xx advisoryapproval-expression-no-empty-policyflowwrite, 2xx advisoryapproval-expression-invalidflowwrite, 422 (theresolveAsarm a 2xx advisory)dataset-include-unknownvalidate-dataset-references.tsdatasetwrite, 422dataset-field-unknowndatasetwrite, 422title-format-retiredvalidate-record-title.tstitle-unresolvablelist-view-field-unknownvalidate-list-view-field-refs.tsview/objectwrite, 422list-view-field-dottedview/objectwrite, 422translation-target-unknownvalidate-translation-references.tsseed-insert-mode-duplicates-on-replayvalidate-seed-replay-safety.tschart-measure-unknownvalidate-chart-bindings.tsreportwrite, 422 (presentation positions a 2xx advisory)object-field-ref-unknownvalidate-object-field-refs.tsobjectwrite, 422view-container-shapevalidate-view-containers.tsseed-value-outside-state-machinevalidate-seed-state-machine.tsai-skill-surface-mismatchvalidate-ai-surface-affinity.tspage-source-className-tailwindvalidate-page-source-styling.tsA count that falls is a list that no longer makes each hint distinct; a count that rises is a case this slice added (a long position, object, skill or page name fired to read the bound on it).
dataset-include-unknown,dataset-field-unknown,list-view-field-unknownandobject-field-ref-unknownno longer append the object's whole field list (account.detail);chart-measure-unknownno longer lists the dataset's measures, and keeps its own "Did you mean" after the remedy.translation-target-unknownno longer lists the declared names on any leg. Two facts a list used to carry stay as one bounded clause: a stale_viewskey is told that a nameless defaultlistis keyeddefault(the packages/lint:collectViewRecord收窄_views键到运行时裸键单拼写 —— #5164 裁 A 的 lint 段 #6038 steer the declared-view list gave a_views.listkey), and a_tabskey over an object with no preset bar is told to declare the tab in a page'sinterfaceConfig.userFilters.tabs, because a list view's owntabshas no renderer (the mis-fix the old tail guarded).recordandpreviousare unbound at an approval node, and whatresolveAsdoes are the newapproval-expression-invalidentry; what eachonEmptyApproverspolicy does is the newapproval-expression-no-empty-policyentry (the nudge still names all four policies); the membership-tier line names the value once and points at the tiers the verdict lists, and why a business role is always a position was already the entry's.a.bincludesa, and that a join is derived from the object graph (no ON clause) are the entries'.titleFormatis retired (render-only, neither returned nor queryable,nameFieldwins) and how a record title resolves, with itsRecord #IDfallback.list-view-field-dottedkeeps both doors' remedies in their own words ("denormalise the value onto … and name that"); what a denormalised field is moves to the entry, in a paragraph naming each door's ways out.chart-measure-unknown: whatseries[].nameandyAxis[]carry at a presentation position, and that none of these surfaces has asuppressWarningskey, are the entry's.externalIdexamples and that the seed exemption never licenses an undeclared state are the entries'.view-container-shape: that machine-assembled manifests carry a ViewItem underviewItems:was already the entry's.ai-skill-surface-mismatch: whensurface: 'both'is right is the entry's.page-source-className-tailwindgains an entry: why a Tailwind class in page source produces no CSS, the theme-token examples and the drawer/modal overlay advice.os explainentry, so theirrule:line now carries the pointer:approval-expression-invalid,approval-expression-no-empty-policy,title-format-retired,title-unresolvableandpage-source-className-tailwind. Each already had a rule id constant on the root barrel, so no published name is added (the closure reading below). Ten existing entries gain text. Read from the built tables, none removed: 146 → 151 against the based206d08ca, 145 → 150 againstmainate540e7ffb(which feat(spec,service-automation,lint)!: a CRUD node's filter operands are value slots, the query placeholders stay — the {…} token refused (#19939 pass 4, S6) #23154 left one entry shorter).ApprovalNodeConfigSchema'sonEmptyApproversenum, the expression roots toAPPROVER_EXPRESSION_ROOTS, and the title-eligible types toTITLE_ELIGIBLE_TYPES.message, rule id, severity andpathis unchanged, and so is what each rule accepts or refuses.hintexpression, a comment, or the removal of the now-unusedSHAPE_HINTtable invalidate-chart-bindings.ts. Two hint expressions choose between two remedy texts on a fact the rule already read: the_tabsleg on whether the object has a preset bar (it chose between two list tails before), andcheckFieldPathkeeps the field list fordataset-filter-field-unknown, outside this slice..changeset/22161-lint-slice-16-one-line-fix.md:@objectstack/lintpatch,Clause-②: no, with the runtimehintFROM → TO for every id that fires at the runtime gate.File surface: the claim's
The claim (
6137662991) names the twelve rule files,rule-explanations.ts, the shared one-line producers where an id converges on them, a text-only test rider inpackages/cli/packages/metadata-protocol, and the changeset.rule-explanations.tsand the changeset.system-fields.tsandchecked-parse.tsneeded no edit: none of the 17 is an anchor or a parse id.packages/cliorpackages/metadata-protocolrider was needed: no test there reads these hints' text, and both suites pass unchanged on the branch (below).The runtime wire
Measured at the door with
runRuntimeAuthoringRules, on the base dist and on the rebuilt one: a scratch probe of 20 writes acrossflow,dataset,object,view,report,seed,pageandagent.flowwrite with an approval node:approval-approver-not-membership-tier(2xxadvisories[].hint): 313 → 138;approval-expression-invalid(422issues[].hint): an empty expression 216 → 90, arecordroot 292 → 144; a parse failure (129), a bare root (110) and theresolveAsadvisory (102) unchanged;approval-expression-no-empty-policy(2xx advisory): 294 → 121.datasetwrite:dataset-include-unknown(422), an entry naming nothing / an ordinary field, 239 / 187 → 93 / 93;dataset-field-unknown(422), a dimension / a measure, 184 / 182 → 132 / 130.objectwrite:object-field-ref-unknown(422) 180 → 128 — the same text is thefixof the publish receipt'sobject_field_ref_unknownruntime probe issue (build-probes.ts); on itslistViews,list-view-field-unknown(422) 110 → 57 andlist-view-field-dotted(422) 231 → 157.viewwrite:list-view-field-unknown(422) 110 → 57;list-view-field-dotted(422), a projection / a filter, 231 / 143 → 157 / 95.reportwrite:chart-measure-unknown, 422 at the query position 117 → 106, 2xx advisory at a presentation position 301 → 113.view-container-shapeandpage-source-className-tailwindareCLI_ONLYin the registry, andtranslation-target-unknownandai-skill-surface-mismatchare reference-suite members on the frozenflowdefault (a flow snapshot carries neither translations nor skills).Readers of
hint, read-only against objectuimainatfd4872f9(fetched into a ref of this session's own,git grepfor.hintreads):saveAdvisoryToast.tsformatFindingrenders the rule, where, message and hint for every 2xx advisory, so the three approval advisories and the chart presentation advisory shorten there.DraftChangesPanel.tsxreadshintonly for its in-browservalidateSecurityPosturepass (none of these ids).hint.PUT /api/v1/meta) forwardsissues/advisoriesas JSON (toIssuecopieshintverbatim).packages/mcpregisters no metadata write tool (its tools are the record, action, run and expression tools), so an MCP or AI author reaches the gate through REST.hinttext.Census (taken first, before any edit, at the base
d206d08ca)Method: the earlier slices' scratch preload (
NODE_OPTIONS=--import, never committed), which records every finding-shaped object, deduped by (rule, hint) per process, with its push site. Lengths are thehintalone.packages/lintsuite: the 17 ids re-measured at the base equal the claim's lengths exactly (313, 294, 292, 336, 206, 267, 237, 258, 233, 317, 309, 285, 284, 256, 251, 235, 231).0a66efe55(before the merges): 240 ids fire, 48 over 200 = 21 fenced + 3 repo-gate + 24 author-time, and every hint of the 17 is at most 200 (the table). The 24 are the 41 less these 17.abe4ce231(after both merges, rebuilt): 239 ids fire (feat(spec,service-automation,lint)!: a CRUD node's filter operands are value slots, the query placeholders stay — the {…} token refused (#19939 pass 4, S6) #23154 retired one), 48 over 200, the same 21 + 3 + 24, and the 17 ids' lengths equal the pre-merge census id for id.mainate540e7ffb, built in the base worktree, fires the 17 at exactly the base lengths.@objectstack/metadata-protocolsuite (which reads@objectstack/lintfrom source), in a base worktree and at the branch:approval-expression-no-empty-policy294 → 121,dataset-field-unknown188 → 136,list-view-field-unknown102 → 58,object-field-ref-unknown171–202 → 128–161;approval-expression-invalidfires there only on its parse arm (129, unchanged); none of the other 12 fires there.packages/cliunit tier, base worktree and branch:title-unresolvable237 → 122;view-container-shapefires on its flat arm only (187, unchanged); none of the other 15 fires there.os validate(the built CLI) onapp-crm,app-todo,app-showcaseandapp-multi-package, base worktreed206d08caagainst the branch at0a66efe55, and againmaine540e7ffbagainst the final headabe4ce231, with the same result both times:rule:lines;app-crm,app-todoandapp-multi-packageare byte-identical once the worktree path and the millisecond timings are normalised;app-showcasediffers in exactly two lines, both oftitle-unresolvableonshowcase_project_membership: itsfix:line (237 → 122 characters of hint) and itsrule:line, which gains the pointer toos explain title-unresolvable. Only this id of the 17 fires on the four apps.Tests
Every heavy run went through
scripts/pm/os-verify-lock.sh(slotsissue-22161-s16,issue-22161-s16-base). 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 naming each arm (every message arm of the three approval ids; each include, dimension, measure and joined-path arm of the dataset ids; both title ids on a long object name; the unknown and dotted list-view ids at both doors; all 27 hint arms oftranslation-target-unknown; every chart position, with and without a near miss; every object-field position; all four view-container arms; each page kind on a long page name). Exact pins read each new line, and a check per id asserts thatexplainRule(id)holds what the line stopped saying.enumeratedUniverseread-back (now the verdict's suggestion, which only a collector that reached the name can offer) and the per-app contributed-id pin (now a silent control: the contributed id resolves under its own app);d206d08caTest Files 138 passed (138), Tests 6,654 passed (6,654); at0a66efe55138 / 6,688; after the first merge (04b1eb95b) 138 / 6,688; at the final headabe4ce231Test Files 137 passed (137), Tests 6,654 passed (6,654), againstmaine540e7ffb's 137 / 6,620 (the 34 added cases, on amainfeat(spec,service-automation,lint)!: a CRUD node's filter operands are value slots, the query placeholders stay — the {…} token refused (#19939 pass 4, S6) #23154 trimmed by one file).pnpm turbo run buildover the CLI, metadata-protocol and example closures at0a66efe55(lint's build script runscheck-dts-emitted) andpnpm --filter @objectstack/lint run typecheck(tsc --noEmit+check:test-typecheckOK: 2 files / 6 errors / 2 pinned signatures held), VERDICT command-exit 0 each; again at the final headabe4ce231, afterpnpm install --frozen-lockfileand a rebuild of the CLI, metadata-protocol and example closures, VERDICT command-exit 0 each.Clause-②check): the runtime export keys and the declaration exports ofindex,runtimeandrule-explanations, read from the base dist (d206d08ca) and the branch's rebuilt dist (0a66efe55): 371 / 553, 6 / 11 and 2 / 3 names, 0 added and 0 removed. Read again at the final headabe4ce231against amaindist built ate540e7ffb: 369 / 550, 6 / 11 and 2 / 3 names (feat(spec,service-automation,lint)!: a CRUD node's filter operands are value slots, the query placeholders stay — the {…} token refused (#19939 pass 4, S6) #23154 retired two), 0 added and 0 removed.d206d08caagainst the branch at0a66efe55, VERDICT command-exit 0 for all four runs. CLI unit tier: Test Files 285 passed (285), Tests 4,283 passed (4,283), in both. metadata-protocol: Test Files 230 passed, 3 skipped (233), Tests 28,244 passed, 19 skipped, in both. Rerun at the final headabe4ce231, because the second merge broughtpackages/lintchanges: CLI unit tier 285 / 4,283, metadata-protocol Test Files 231 passed, 3 skipped (234), Tests 28,253 passed, 19 skipped, VERDICT command-exit 0 both, and the same per-id census as at0a66efe55.515b093fd, throughscripts/ablation-replace.mjsunder the lock, with a shelltraprestoringHEADby absolute path. The rule suite imports the rule source, so there is no dist leg.object-field-ref-unknown's pre-slice hint restored injudge()(the prescription followed byaccount.detail).validate-object-field-refs.test.tsgo red (thehighlightFieldsexact-line pin, therelatedListColumnsno-field-list pin and the new bound case), and every other case stays green.ed7515826362→f3add49e4bd7) and was restored (blobed7515826362== HEAD,git diff HEADempty,git status --porcelainempty).npx eslint --no-inline-config --format jsonover the 25 changed.tsfiles: 25 files, 0 errors, 0 warnings. The population iseslint.config.mjs's**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}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/main, each throughscripts/pm/os-regen-merge.sh, both without conflict; the branch edits no generated artifact.515b093fd→04b1eb95b, ontodc93781c1:mainbroughtpackages/spec,plugin-auditandservice-messagingchanges and nopackages/lint,packages/cliorpackages/metadata-protocolfile.04b1eb95b→abe4ce231, ontoe540e7ffb: feat(spec,service-automation,lint)!: a CRUD node's filter operands are value slots, the query placeholders stay — the {…} token refused (#19939 pass 4, S6) #23154 ([v18] retire the{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 S6) had landed, editingrule-explanations.ts(one flow-filter entry and its map row removed) and otherpackages/lintfiles, so every measurement above was retaken atabe4ce231.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, atabe4ce231(26 paths vs the merge basee540e7ffb) derived 62 commands, the same 62 at every head.abe4ce231but the one skipped by order, each exit code captured before any pipe, all 0. The longest:check:query-options-erasure332s,check:slot-lookup182s,check-comment-mask-corpus135s. At04b1eb95bthe same set ran all-0 as well,check:dual-build-cjs-loadsthere after a first refusal (exit 3, PREREQUISITE NOT MET: six packages had no dist) and a locked build of those six (VERDICT command-exit 0).check:type-check-debt.--ran:62 derived famil(ies) accounted for — 61 run, 1 NOT-MEASURED.origin/mainmoved 11 commits after the second merge (e540e7ffb…d4aaeaf0e), touchingpackages/lintfiles outside this slice (validate-component-props.ts,validate-page-field-bindings.ts,authoring-rules.ts) and none of its files orrule-explanations.ts; not merged again, and a localgit merge-treeontod4aaeaf0eis clean.Acceptance notes
view-container-shape's flat arm at 187 (fixed text, unchanged), thenobject-field-ref-unknownat 186 (therelatedListColumnsprescription, fixed text).field_10inhighlightFields,estimate_hoursagainst measures namedest_hours), the old line listed what exists and the new one names the remedy only. That is slices 11, 12 and 14's rule for a list the verdict's "Did you mean" covers; the two cases where the list carried the one fact an author needed (_views.default, the dead list-viewtabscarrier) keep that fact as a bounded clause.dataset-filter-field-unknownkeeps its field list. It sharescheckFieldPathwithdataset-field-unknown, and it is not among the claimed ids (its hint stays at most 200 on every variant the suite fires), socheckFieldPathstill appends the list for it. A later slice can drop it with one condition.docs/adr/0079-record-display-name.mdquotes the oldtitle-format-retiredhint ("titleFormat is a render-only template the server cannot return or query") as whatvalidate-record-title.tsstates. The file's module note still states that consequence, and the explanation now carries it; the ADR is a governed surface outside the claim, and the quotation records the implementation at decision time. Noted, not edited.{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 S6) landed during this slice and is merged in (above).apidata source — a view binds its object or static rows; an API call is the page element's binding (#23082) #23160 ([Decision] Does a view'sapidata source adopt the page-element rule — a same-originurland noheaderswritten in metadata? #23082, draft, opened after the claim's census) edits one fixture ofvalidate-list-view-field-refs.test.ts(a skip case's provider), away from this slice's hunks.Remaining for this card
24 author-time
packages/lintids still carry a hint over 200 characters in the final lint census (atabe4ce231: 48 over 200 = 24 + 21 fenced + 3 repo-gate), by file, longest first:lint-flow-patterns.ts(2):flow-double-brace-interpolation1413,flow-bare-dollar-reference1234data-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-capability-references.ts(1):capability-reference-unknown273lint-liveness-properties.ts(1):liveness-planned-property230validate-page-field-bindings.ts(2):page-section-count-not-multi-value228,page-field-unknown206validate-list-view-mode.ts(1):list-view-filters-in-views-mode219Not in the 24: 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