Repository navigation
fix(lint): one-line fix lines for 17 more author-time rule ids (slice 15); os explain RULE_ID carries what they explained - #23125
Conversation
The hint of 17 more author-time rule ids in eight rule files is one remedy line; what the lines explained moves into their RULE_EXPLANATIONS entries. Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
… lines (WIP) Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-15-hints
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
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 fbf520d2d1c07d27506eeeb1cbf7438464aa49de && git checkout fbf520d2d1c07d27506eeeb1cbf7438464aa49de
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bcebd8409d4fa6f962f83fe2d1d32d944c7818bf c6f65c38a267171b94357b7e38d7c68cc74d35ba && git checkout -B drift-repro bcebd8409d4fa6f962f83fe2d1d32d944c7818bf && git merge --no-ff c6f65c38a267171b94357b7e38d7c68cc74d35ba
node scripts/docs-audit/affected-docs.mjs --json bcebd8409d4fa6f962f83fe2d1d32d944c7818bf
|
Part of #22161
Clause-②: no
Stage 2 of the card, slice 15: the over-long
fix:lines, continued in slices 11 to 14's shape. Each finding'shint(the CLI'sfix:line, the runtime issue'shint) of 17 morepackages/lintrule ids, in eight 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: 41 author-time ids still carry a hint over 200 characters (the last section).What changes
react-chart-field-unprovisionedvalidate-react-page-props.tsreact-prop-missing-requiredreact-page-source-unparseablereact-block-needs-record-contextaction-body-write-unprovisioned-anchorvalidate-action-body-writes.tsaction-body-write-unknown-fieldaction-record-write-discardedaction-body-source-unparseableagent-authoring-withdrawnvalidate-ai-agent-authoring.tsdefault-agent-outside-rosterflow-node-write-unprovisioned-anchorvalidate-flow-node-writes.tsflowwrite, 2xx advisoryflow-node-write-unknown-fieldflowwrite, 422validation-rule-json-schema-uncompilablevalidate-rule-compilability.tsobjectwrite, 422validation-rule-regex-uncompilableobjectwrite, 422translation-section-name-missingvalidate-translatable-sections.tsobject/managed-api-method-unaffordablevalidate-managed-api-methods.tsobjectwrite, 422nav-object-unservablevalidate-nav-object-servability.tsA count that rises is a case this slice added (a far-miss unknown field on the action and flow rules, every write verb on the managed-object rule, a third required binding fired from the contract).
react-chart-field-unprovisioned,action-body-write-unprovisioned-anchor,flow-node-write-unprovisioned-anchor) printunprovisionedAnchorFix(), slice 14's one-line form: the three ways out in one line. How (columnMap,ownership: 'none',systemFields: { audit: false }) is the sharedUNPROVISIONED_ANCHOR_REMEDYparagraph, now in their three entries too.react-page-source-unparseable,action-body-source-unparseable) printPARSE_FAILURE_FIX. What a partially recovered tree leaves unchecked was already their entries'.action-body-write-unknown-fieldprints the hook sibling's line. Which shapesACTION_BODY_WRITE_PATTERNSchecks, whyctx.inputis not one, and that the warning never blocks a build are a new paragraph of its entry.action-record-write-discardedkeeps theupdateByIdremedy; why only a provably dead write is reported was already the entry's, and "never blocks a build" joins it.flow-node-write-unknown-field: why it gates where the body rules advise was already the entry's.ask/buildbysurfaceaffinity is one shared paragraph,SKILLS_ATTACH_BY_SURFACE, in both entries. The withdrawn-agent entry also says why dropping the persona loses nothing. The line keeps the count of referenced skills, now in the singular for one.formats, and the shapestype/required/$reftake, are new paragraphs. The regex line still quotes the source.translation-section-name-missing: that the suggested name is never a key derived from the label is the explanation's.object/managed-api-method-unaffordable: removal first, then the affordance "once a guard enforces the write". The ADR-0092 D4 condition was already the entry's.nav-object-unservable: the line keeps "unless it is API-disabled on purpose". The credential-material reason was already the entry's.react-block-needs-record-contextprints the spec's replacement for the block (REACT_RECORD_BLOCK_ALTERNATIVES) behind the lead-inInstead:, where it used to sayOn a react page bind the record yourself:. That the replacement binds the record itself is a new paragraph of the entry. The longest,record:related_list's, is 195.react-prop-missing-requiredquotes the contract's description of the missing binding, now its first sentence. That sentence names the binding and its spelling (Pass data={…}: The object this block binds to, written as the metadata-tier data source: data={{ provider: 'object', object: '…' }}). The cut is the oneschemaRefusalHeadmakes for a forwarded spec refusal. The rest ofListView.data's description (which providers are accepted, why the prop is required) is the contract's, and the published react-blocks reference carries it whole.REACT_RECORD_BLOCK_ALTERNATIVESentry and every required binding inREACT_BLOCKSis fired and held to 200. A spec edit that grows one past it turns the rule's test red.message, rule id, severity andpathis unchanged, and so is what each rule accepts or refuses.hintexpression, a comment, an import swapped to a shared short form, ordescriptionHead()..changeset/22161-lint-slice-15-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 (
6133662163) names the eight rule files,rule-explanations.ts, the shared one-line producers where an id converges on them, a text-only test rider and the changeset.system-fields.tsandchecked-parse.tsneeded no edit: the ids converge on slice 14'sunprovisionedAnchorFix()andPARSE_FAILURE_FIXas they are.packages/cliandpackages/metadata-protocolneeded no rider: no entry was added, and no test there reads these hints' text.unprovisionedAnchorHint()keeps three callers (validate-widget-bindings.ts,validate-flow-template-paths.ts,validate-page-field-bindings.ts).PARSE_FAILURE_HINTkeeps one (lint-startup-registry-verdict.ts).The runtime wire
Measured at the door with
runRuntimeAuthoringRules, on the base dist and on the rebuilt one: a scratch probe of 19 writes acrossobject,flow,action,page,app,agentandview.objectwrite:object/managed-api-method-unaffordable(422issues[].hint): two refused verbs / every write verb, 335 / 372 → 132 / 169;validation-rule-regex-uncompilable(422): the pattern([/ a 28-character pattern, 288 / 314 → 123 / 149;validation-rule-json-schema-uncompilable(422): 341 → 124.flowwrite:flow-node-write-unknown-field(422): with / without a near miss, 271 / 250 → 92 / 71;flow-node-write-unprovisioned-anchor(2xxadvisories[].hint): 361 → 174.translation-section-name-missingandnav-object-unservableare reference-suite members on the frozenflowdefault. A flow snapshot carries none of the collections they read.Readers of
hint, read-only against objectuimainat1fb1ced3(the localorigin/main, equal to the remote's;git grepfor.hintreads, nothing fetched):saveAdvisoryToast.tsformatFindingrenders[rule] where — message hintfor every 2xx advisory, so the flow-node anchor toast shortens.DraftChangesPanel.tsxreadshintonly for its in-browser security-posture pass (none of these ids).hint.PUT /api/v1/meta) forwardsissues/advisoriesas JSON (toIssuecopieshintverbatim).packages/mcpregisters no metadata write tool, so an MCP or AI author reaches the gate through REST.hinttext.check:watch-hint-literalguards something else of the same name and ran in the gate set.Census (taken first, before any edit, at the base
abf86554c)Method: the earlier slices' scratch preload (
NODE_OPTIONS=--import, never committed). It patchesArray.prototype.pushto record 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 (362, 277, 254, 228, 358, 323, 309, 254, 363, 237, 358, 289, 341, 300, 359, 357, 348).c6f65c38a: 240 ids fire, 65 over 200 = 21 fenced + 3 repo-gate + 41 author-time, and every hint of the 17 is at most 200 (the table). The 41 are the 58 less these 17.@objectstack/metadata-protocolsuite (which reads@objectstack/lintfrom source), in a base worktree and at the branch:validation-rule-json-schema-uncompilable341 → 124 at the door, and none of the other 16 fires there. Test Files 228 passed, 3 skipped (231), Tests 28,224 passed, 19 skipped, in both.packages/cliunit tier, base worktree and branch:agent-authoring-withdrawn(its zero-skill arm) 196 → 112, and none of the other 16 fires there. Test Files 285 passed (285), Tests 4,280 passed (4,280), in both.os validate(the built CLI) onapp-crm,app-todo,app-showcaseandapp-multi-package, base worktree and branch:rule:lines;Load time,Validation passed (…ms)) are normalised. Those are the only differing lines.Tests
Every heavy run went through
scripts/pm/os-verify-lock.sh(slotsissue-22161-s15,issue-22161-s15-base). Its VERDICT lines are in the report.Each rule's own suite pins the new shape. Each of the eight test files gains a last block. It holds every recorded
fix:line of its converted ids to one line of at most 200 characters, behind a coverage control naming each arm:A check per id then asserts that
explainRule(id)holds what the line stopped saying. Seven recorders now keephintbesidemessage.Moved pins. Pins that read the old hint text moved to the explanation, text-only: the section rule's "never a key derived from the label", and the managed-object rule's
ADR-0092 D4. The agent rule's zero-skills negative now names the new wording.New pins hold an explanation's spelled-out list to its source: the named regex formats to
FormatValidationSchema'sformatenum, and the JSON Schema primitive types.Lint suite at
c6f65c38a: Test Files 137 passed (137), Tests 6,646 passed (6,646); VERDICT command-exit 0. At the base: Test Files 137 passed (137), Tests 6,614 passed (6,614).Lint build and typecheck at
c6f65c38a:pnpm install --frozen-lockfile && pnpm turbo run build --filter=@objectstack/lint...(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. The rebuilt dist carries the new text:Instead:,once a guard enforces the writeandauthor skills insteadhave 1 hit each inindex.jsandruntime.js, and the new agent paragraph's text is inindex.jsandrule-explanations.js. The old lines are gone:On a react page bind the record yourself,Do NOT open the API on an objectandThis gates the build rather than warning: the key is literalhave 0 hits in all three.The built entries' export closure is unchanged (the
Clause-②check): the runtime export keys and the declaration exports ofindex,runtimeandrule-explanationsare identical, read from the base dist (abf86554c) and from the branch's rebuilt dist (5aaa606c9): 371 / 553, 6 / 11 and 2 / 3 names, 0 added and 0 removed. Read again at the final head against amaindist (21c86444e): the same counts, 0 added and 0 removed.CLI unit tier and metadata-protocol suite: measured before the merge, at
5aaa606c9, against a base worktree atabf86554c, VERDICT command-exit 0 for all four runs. CLI unit tier: Test Files 285 passed (285), Tests 4,280 passed (4,280), in both. metadata-protocol: Test Files 228 passed, 3 skipped (231), Tests 28,224 passed, 19 skipped, in both. Not rerun after the merge:mainbrought nopackages/lintfile, and the final lint census equals the pre-merge one id for id.Ablation: one-shot, from the committed head
5aaa606c9, throughscripts/ablation-replace.mjsunder the lock, with a shelltraprestoringHEADby absolute path. The rule suite imports the rule source, so there is no dist leg.flow-node-write-unknown-field's pre-slice caveat ("This gates the build rather than warning: …") restored infixHint().validate-flow-node-writes.test.tsgo red, and every other case stays green.084b6a3acbae→e2b288d993f2) and was restored (blob084b6a3acbae== HEAD,git diff HEADempty,git status --porcelainempty). The rule file and its test have the same blobs at the final head.ESLint, narrowed:
npx eslint --no-inline-config --format jsonover the 17 changed.tsfiles atc6f65c38a: 17 files, 0 errors, 0 warnings.eslint.config.mjs's**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}block plus itspackages/**blocks.parserOptions.project), so no untouched file's verdict can move.Gates: one merge of
origin/mainthroughscripts/pm/os-regen-merge.sh(5aaa606c9→c6f65c38a, onto21c86444e). No conflict, and the branch edits no generated artifact.mainbroughtpackages/spec,packages/cliandpackages/metadata-protocolchanges and nopackages/lintfile.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, atc6f65c38a(18 paths vs the merge base21c86444e) derived 62 commands.check:query-options-erasure290s,check:slot-lookup151s,check-comment-mask-corpus112s.check:dual-build-cjs-loadsfirst refused (exit 3, PREREQUISITE NOT MET: six packages had no dist). After building those six with their closure under the lock (VERDICT command-exit 0) it ran, exit 0 (107 entry points across 66 packages).check:type-check-debt.--ran:62 derived famil(ies) accounted for — 61 run, 1 NOT-MEASURED.origin/mainmoved five commits after the merge (240890cfb…e9e10fdab), touching nopackages/lintfile; not merged again.Acceptance notes
react-block-needs-record-contextat 195 (a fixed spec string,record:related_list's replacement). After it comestranslation-section-name-missingat 172 (showcase_contact/who_is_this).react-prop-missing-requiredstill has noos explainentry. An entry is keyed to a rule id constant the root barrel exports, and this id has none: it is a literal invalidate-react-page-props.ts. Exporting one would add a name to@objectstack/lint's published entry, which isClause-②: yes, against this claim'sno. So the route's "an id without an entry gains one" is not applied here. What its line stopped quoting is the contract's own text, which the published react-blocks reference keeps whole.record:related_list's replacement is 186 characters on its own, so the lead-in is a word. If the spec's table grows past the bound, the new pin turns red rather than printing a second line.unprovisionedAnchorHint()keeps three ids outside this slice andPARSE_FAILURE_HINTone (a repo-gate id). Each converges on the short form when its slice comes.content/docs/ui/react-pages.mdxquotes anos validatetranscript ofreact-block-needs-record-contextwhose verdict andfix:line are both pre-[maintainer] validate: thefield-no-consumerswarning is one 856-character line, printed by validate, build and dev alike — one-line verdict +rule:id + a pointer to the full reasoning (os explain, which today takes only schema names) #22161 wording. The verdict changed in an earlier slice. It is a hand-written example, andcheck:docs-transcript-driftcompares only the registry-derived numbers in a transcript, never finding text. No code path consumes it. Carrier: none named; noted, not filed.Remaining for this card
41 author-time
packages/lintids still carry a hint over 200 characters in the final lint census (atc6f65c38a: 65 over 200 = 41 + 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-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-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 41: 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