Repository navigation
fix(lint): one-line fix lines for 15 author-time rule ids; os explain RULE_ID carries what they enumerated - #22878
Conversation
…ations move to os explain WIP: hint text and RULE_EXPLANATIONS data only; tests updated text-only. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…verted ids Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-11-hints
…-lint-slice-11-hints
…rrier example verbatim Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-11-hints # Conflicts: # packages/lint/src/rule-explanations.ts # packages/lint/src/validate-approval-approvers.ts
…s and explanation to #22824's administrators' slate Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…approvers-may-resolve-empty Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 139 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 54bc8794e32097fe1b6d2656cb3d3d4df294aed2 && git checkout 54bc8794e32097fe1b6d2656cb3d3d4df294aed2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 72b26ed4f5dcdf32b4b167764fa1ecc71121f181 6e2c2f068ce1cbd146cecabcfee138e04b11a4ec && git checkout -B drift-repro 72b26ed4f5dcdf32b4b167764fa1ecc71121f181 && git merge --no-ff 6e2c2f068ce1cbd146cecabcfee138e04b11a4ec
node scripts/docs-audit/affected-docs.mjs --json 72b26ed4f5dcdf32b4b167764fa1ecc71121f181
|
… re-pointed description Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…-lint-slice-11-hints
… main after the administrators' slate landed The slice changeset's FROM figures for approval-approvers-may-resolve-empty were measured before the merge that brought in the administrators' slate: main's hints are now 2,243 (manager arm) and 440 (group arm) characters, and the group arm's one-line fix is 200 characters after it was restated in that verdict's words. The spec changeset now says plainly that the describe's "the request waits" clause names the onEmptyApprovers policy, and that the default policy opens the request on the organization's administrators. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
Landing pre-checks at
|
Part of #22161
Clause-②: no
Stage 2 of the card, slice 11: the over-long
fix:lines, item 3 of slice 10's landing record as triage routed it. Each finding'shint(the CLI'sfix:line, the runtime issue'shint) of 15packages/lintrule ids is now one remedy sentence of at most 200 characters, and what the lines used to enumerate is each id'sRULE_EXPLANATIONSentry behindos explain RULE_ID. The card stays open: 107 author-time ids still carry a hint over 200 characters (below).What changes
sharing-rule-runtime-variable-conditionvalidate-sharing-rule-enforceability.tssharing-rule-unlowerable-conditionapproval-approvers-may-resolve-emptyvalidate-approval-approvers.tsflowwrite, 2xx advisoryrls-predicate-unenforceablevalidate-rls-predicate-enforceability.tspermissionwrite, 422rls-predicate-over-budgetpermissionwrite, 422rls-predicate-unknown-fieldpermissionwrite, 422filter-empty-combinatorvalidate-empty-combinators.tsflow/reportwrite, 422permission-retired-lifecycle-residuevalidate-retired-permission-residue.tspermissionwrite, 2xx advisoryvisibility-predicate-over-budgetvalidate-visibility-predicates.tsviewwrite, 422hook-api-update-readonly-fieldvalidate-readonly-hook-writes.tsreact-chart-aggregate-invalidvalidate-react-page-props.tsreact-chart-drilldown-invalidflow-time-relative-descriptor-invalidvalidate-flow-trigger-readiness.tsflowwrite, 422component-type-unknownvalidate-component-types.tsfield-no-consumersvalidate-field-consumers.tsos validate: 24 · 68–624 → 24 · 68–199)A count that falls is several old hints that differed only in what the new line no longer lists (the over-budget bound's name, the object's field list, the retired key's name). The drill-down hint grows: it carried a pointer, and now carries the remedy.
sharing-rule-runtime-variable-condition(2,280): the hint was the whole mechanism essay. It is now "keep only the record's own properties inconditionand name the audience insharedWith; what replacescurrent_userdepends on the object's sharingModel". ⛔ The one-line hint recommends no RLS at all, because an RLS policy narrows onlypublic_read/public_read_writeand grants nothing onprivate. The model-qualified split, the AND-composition, the ADR-0057 D1 depth scopes, thepositionover-broad-grant trap and the record-relative gap are the explanation's, with the Thesharing-rule-runtime-variable-conditionfix-hint sends authors to RLS to widen a private object, but the layers are AND-composed — the advice cannot work on the case that most needs it #14234 wording legs moved there (an RLS sentence must name the models it holds for) and a new leg that holds the hint to naming no RLS.react-chart-drilldown-invalidandreact-chart-aggregate-invalid("the rejection above carries the fix") now say "use the rename it suggests, or delete / move a key real on another surface / one layer out";flow-time-relative-descriptor-invalid("satisfies each message above") now says to start with the key the verdict names and re-run until the schema accepts it.component-type-unknown's "Apply the prescription above": the retired arm now carries the prescription's own "Delete the …" sentence, cut (never rewritten) byretiredTypeRemedy()fromRETIRED_PAGE_COMPONENT_TYPES, at its first dash only when the sentence would pass 200 (forelement:form: "Delete theelement:formcomponent and use the object-boundobject-formblock instead").approval-approvers-may-resolve-empty'smanagerarm (2,243 onmainafter feat(plugin-approvals): an empty admin_rescue slate opens on the organization's administrators; retire the unstaffed arm #22850): "Set every submitter's sys_user.manager_id (Setup → Users → Set Manager, or the admin user import), or add an approver that cannot resolve empty".MANAGER_ONLY_REMEDY/MANAGER_ONLY_ROUTESmove, verbatim and under the same names, into the id's explanation; the rule file keeps the measurement docblock behind each graded route. The group arm (463) is one line too.field-no-consumers' carrier list (629 printed, 624 as the hint): the first carrier site and(and N more), the shape its verdict's same-name clause already uses.rls-predicate-unenforceablerewrites the first faulty site and counts the rest; for a barecurrent_userit names the key slot instead of listing every kernel key.rls-predicate-unknown-fielddrops the object's field list (the verdict's "Did you mean" names the nearest column).permission-retired-lifecycle-residuecarries the tombstone prescription's "Delete the key" sentence, cut byretiredKeyRemedy()(the clause after its dash, on what stays denied, goes to the explanation), never retyped.CROSS_CLASS_LISTINGparagraph, held equal toCROSS_FIELD_CLASS_LISTINGby both rules' cross-class tests), the rule eachcurrent_userholding breaks, the null-comparand rewrites, the four ways to shrink an RLS predicate (with the top-level&&warning kept on the one-line hint as well), the declared match-nothing filter spelling, the drop-the-field alternatives for a readonly hook write, and what deleting a retired permission key leaves.CROSS_CLASS_REMEDY(module export, not on the barrel) is gone; each rule's line names its own alternatives.message, rule id, severity andpath, and what each rule accepts or refuses, is unchanged: every hunk in the 11 rule files is ahintexpression, a comment, a hint constant, or a string-in / string-out cut helper (retiredTypeRemedy,retiredKeyRemedy,firstPathAndCount,firstAndCount);typeFaultRewritekeeps one rewrite per site andkernelKeysHolding(which fed only the removed alternatives) is deleted. No condition, branch, skip or dedupe moved..changeset/22161-lint-slice-11-one-line-fix.md:@objectstack/lintpatch,Clause-②: no, naming every door that printsfix:and the runtimehintFROM → TO.The runtime wire (Zone 2 item 4)
Measured at the door with
runRuntimeAuthoringRuleson the base dist and on the rebuilt one (scratch probe):flowwrite:flow-time-relative-descriptor-invalid(422issues[].hint, 342 → 176),filter-empty-combinator(422, 647 / 764 → 134 / 154),approval-approvers-may-resolve-empty(2xxadvisories[].hintand the[Protocol] authoring advisorylog line, 2,243 / 440 → 198 / 200);permissionwrite:rls-predicate-unenforceable(422, 912 / 312 → 186 / 121),rls-predicate-unknown-field(422, 708 → 154),permission-retired-lifecycle-residue(2xx advisory, 573 / 681 → 14);rls-predicate-over-budgetruns on that write but fires only once the platform CEL bounds refuse (the suite holds it withatGa), so the probe drew no finding either side;viewwrite:visibility-predicate-over-budget(422, 651 → 179);hookwrite runs none of the 15; the sharing-rule, component-type, react-chart,field-no-consumersandhook-api-update-readonly-fieldids are CLI only.Readers of
hint, measured read-only against objectuimainatdca25af(git grep, no edit): Studio'ssaveAdvisoryToast.tsformatFindingrenders[rule] where — message hintfor every 2xx advisory, andDraftChangesPanel.tsxrendersp.hint || p.messagefor the in-browser security-posture lint (none of the 15).metadata-client.tsreadSaveAdvisorieschecks only thathintis a string. The 422issuesrenderers (ResourceEditPage.tsx) readpathandmessage. REST (PUT /api/v1/meta,SaveMetaItemResponseSchema.advisories, the error envelope'sissues) forwards the array as JSON;packages/mcphas no metadata write tool, so an MCP or AI author reaches the gate through REST. No reader in this repository or in objectui matches onhinttext; the one text match in this repository's sources (check-yaml-examples.ts) reads an unrelated object.check:watch-hint-literalguards something else of the same name: that every*_WATCH_HINTSpopulation declaration in a gate script is a literal array (a computed one drops out ofdispatch-gates). It ran green before the gate set (72 declarations across 4 rostered names) and inside it.Census (taken first, before any edit, at the base
31b5a5f7f5)Method: slices 4 to 10's scratch preload (
NODE_OPTIONS=--import, never committed), patchingArray.prototype.pushto record every finding-shaped object, here deduped by (rule, hint) per process, with its push site. Lengths are thehintalone. Runs, each before and after:packages/lintsuite (137 files, 6,455 tests at the base): 240 ids fire; 144 carry a hint over 200: 21 of the 26 fenced ids, 3 repo-gate ids (lint-startup-registry-verdict.ts), and 120 author-time ids. The named five's 7 ids and the 8 longest others (excluding the twolint-flow-patterns.tsids, below) are this slice's 15.@objectstack/metadata-protocolsuite reading@objectstack/lintfrom its built dist (base dist before, rebuilt after): firesrls-predicate-unenforceable(300 → 139) andpermission-retired-lifecycle-residue(573–681 → 14) of the 15.packages/cliunit tier (281 files): fireshook-api-update-readonly-field(622 → 175) andfield-no-consumers' unchanged inert line (68).os validate(the built CLI) onapp-crm,app-todo,app-showcase,app-multi-package, exit 0 each, before and after:field-no-consumerscrm 68–336 → 68–179, todo 263–339 → 168–196, showcase 68–624 → 68–199, multi-package 68 → 68;approval-approvers-may-resolve-emptyfires on the showcase, 440–2,243 → 198–200 (FROM measured onmainafter feat(plugin-approvals): an empty admin_rescue slate opens on the organization's administrators; retire the unstaffed arm #22850). Each app prints the same number ofrule:lines before and after (9 / 7 / 70 / 3).Tests
Every heavy run went through
scripts/pm/os-verify-lock.sh; its VERDICT lines are in the report.Each rule's own suite pins the new shape. Every touched suite records what its cases fire; a new last block per id holds every recorded
fix:line of the converted ids to one line of at most 200 characters behind a coverage control (each arm fired: the three unlowerable arms across the main file and its two siblings; both empty-slate arms; the shape, type-fault and null arms; all three combinator shapes; both retired keys; both readonly-hook verbs; every retired component type and the own-namespace arm; the counted carrier arm), plus exact pins per arm and a pin per id thatexplainRule(id)holds what the line stopped saying. Pins that read the old hint text moved to the explanation, text-only; the four sibling files' bounds are scoped to the converted ids (the anchor andunknown-user-variable/unparseableids keep longer hints).Lint suite at the final head: Test Files 137 passed (137), Tests 6,496 passed (6,496); VERDICT command-exit 0.
Lint build + typecheck:
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. The rebuilt dist carries the new text (Staff at least one target1 hit in each ofindex.js,index.cjs,runtime.js,runtime.cjs;Apply the prescription above0).CLI unit tier on the rebuilt dist: Test Files 281 passed (281), Tests 4,196 passed (4,196); VERDICT command-exit 0. No
packages/cliorpackages/metadata-protocoltest asserts these ids' hint text, so the declared rider went unused; the runtime-gate test that reads the residue hint asserts its length over 10 (Delete the keyis 14).Runtime gate: the whole
@objectstack/metadata-protocolsuite, after: Test Files 225 passed, 3 skipped (228), Tests 28,131 passed, 19 skipped; VERDICT command-exit 0.Corpus pins over
packages/lint/src:pnpm --filter @objectstack/spec test:repo: Test Files 55 passed (55), Tests 971 passed (971); VERDICT command-exit 0. No new text carries anos migrate meta --fromsentence (the residue explanation namesos migrate metawithout one), and no tracker id is in a printed hint or explanation (citations stay in comments and test names).Ablation (one-shot, from the committed head
b4cf329ada, throughscripts/ablation-replace.mjsunder the lock, with a shelltraprestoringHEADby absolute path). The rule suites import the rule source, so there is no dist leg.permission-retired-lifecycle-residue's pre-slice line was restored:hint: retiredKeyRemedy(prescription),→hint: prescription,(anchor x1 → x0, blob52140529de29→c568d4a53e68). Predicted before the run: exactly the exact-remedy case and the new bound pin go red. Observed:src/validate-retired-permission-residue.test.tsTests 2 failed, 21 passed (23), exactly those two; the bound pin read 573. Restored: blob52140529de29== HEAD,git diff HEADempty,git status --porcelainempty.ESLint, narrowed:
npx eslint --no-inline-config --format jsonover the 28 changed.tsfiles: 28 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.Gates: two merges of
origin/mainthroughscripts/pm/os-regen-merge.sh(to12b9daf749, then55382dc02a; neither touched a line this slice edits, and the branch edits no generated artifact).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackwith no paths at the final headb4cf329adaderived 62 commands; 60 ran, each exit code captured before any pipe, all 0. NOT MEASURED, as dispatched: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 atb4cf329ada,check:published-readme-exportsafter building its four missing dists (client-react,embedder-openai,knowledge-ragflow,organizations) under the lock; the three PR-context guards run against this PR and report on the card.Round 2 (seat order
6111995055):bf78c173d3with feat(plugin-approvals): an empty admin_rescue slate opens on the organization's administrators; retire the unstaffed arm #22850 (69d4218058, the organization administrators' slate). feat(plugin-approvals): an empty admin_rescue slate opens on the organization's administrators; retire the unstaffed arm #22850's messages and its first explanation paragraph win. The two one-line fixes and the moved paragraphs are restated against it, and a test block holds that no fix line or explanation describes a retired rescue path;4ca9154ae6;6e2c2f068c.test:repo55 / 971;check:generated14 up to date. The no-path union is 109 derived, 107 run, 2 NOT MEASURED (dual-build-cjs-loads,type-check-debt), 0 UNRUN, and the 3 PR-context guards are green.Remaining for this card
packages/lintids still carry a hint over 200 characters in the final lint census (atb4cf329ada: 131 over 200 = 107 + 21 fenced + 3 repo-gate). The longest, by file:lint-flow-patterns.ts'sflow-double-brace-interpolation1,339 andflow-bare-dollar-reference1,231 (not taken here: PR feat(spec,service-automation)!: a screen's defaults, a field's defaultValue and recordId are value slots — a CEL envelope evaluated before the screen is served, the {…} token refused (#19939 pass 4, S2) #22832, then open, editedlint-flow-patterns.test.ts, and the slice stayed off that file; feat(spec,service-automation)!: a screen's defaults, a field's defaultValue and recordId are value slots — a CEL envelope evaluated before the screen is served, the {…} token refused (#19939 pass 4, S2) #22832 has since landed);validation-rule-json-schema-unknown-format646;flow-credential-literal646;measure-aggregate-field-type-refused645;predicate-rhs-path-shaped643;rls-predicate-unknown-user-variable618;hook-api-update-readonly-when-field607;unique/legacy-organization-composite578;dashboard-filter-field-unprovisioned561;action-name-undefined560;flow-update-readonly-when-field549;ai-skill-tool-unresolved548;org-axis-permission-inheritance542;rls-predicate-unparseable523;object-reference-unknown522;filter-empty-node515; then 90 more down to 201. The full list, by file, is in the report on the card.os explain RULE_IDcarries their reasoning #22448 lists them (21 fire a hint over 200), and the 3 repo-gate ids.packages/cliids live on cli: the 9packages/cli-owned rule ids and the action-governance boot-log lines print one verdict sentence each (≤ 200 chars), with the reasoning behindos explain rule <id>(#22161, thedomain:clihalf) #22841.Acceptance notes
field-no-consumersonshowcase_project_membership.allocation_percentat 199, whose first carrier path is 80 characters.field-no-consumers' carrier list beyond the first now prints nowhere on the CLI. The finding object still holds every site incarriersfor a programmatic caller, butos validate --jsoncarries an advisory as onewhere: messagestring andos lint --jsonmaps the finding tofixwithoutcarriers. The card's "Asked for" names no carrier list at all; the first site plus the count is the dispatch's route.6111995055).ApproverType's.describe()(packages/spec/src/automation/approval.zod.ts) now points atos explain approval-approvers-may-resolve-empty, and its comment namesrule-explanations.tsas the carrier. The reference pagecontent/docs/references/automation/approval.mdxis regenerated, and.changeset/22161-spec-approver-type-describe.md(@objectstack/specpatch,Clause-②: no) carries it. This PR now publishes@objectstack/lintpatchand@objectstack/specpatch.rls-predicate-unknown-field's old hint said a missing column is "an outage in one position and an open door in the other", which the explanation's own history note records as no longer true (the compiled-predicate guard fails closed everywhere). The sentence is not carried into the explanation.Generated by Claude Code