Skip to content

fix(lint): one-line fix lines for the last eight fenced author-time rule ids (slice 21); os explain carries what they explained - #24292

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-22161-s21-validate-hints
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-22161-s21-validate-hints

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #22161
Clause-②: no

Slice 21 of the card. It covers the other 8 author-time packages/lint ids that slice 2 fenced. Those ids are inside the card's close condition (triage answer A, comment 6219576699), and this slice uses the shape of slices 11–20. Each finding's hint for these 8 ids (the CLI's fix: line, and the runtime finding's hint) is now one remedy line of at most 200 characters. What the old lines explained is now in each id's new RULE_EXPLANATIONS entry, which os explain RULE_ID prints. The card stays open. The last section lists what is left.

Claim: 6247309763. It is the newest Claim: on the card and names this branch; I checked that before any edit. This run resumes from claude/issue-22161-s21-validate-hints@c4eca88abc, which a dead earlier run pushed with no PR and no report. It never started over from zero. I reviewed that head's three commits (fe9bb145c fix lines and entries, 8160ab7da a main merge, c4eca88ab changeset) hunk by hunk and kept them, with the corrections listed under "On top of the inherited head". Every reading below was taken again in this run.

What changes

One remedy line per finding. The table lists every hint the lint suite fires for the eight ids, before and after, as distinct hints · length range. The instrument is a census preload over the whole lint suite: Array.prototype push / unshift / map / filter / forEach / find / some / every / flatMap / concat / slice hooks that record every finding-shaped object, with its hint, fix, message, where, path and severity. "Before" is this branch's tests run against main's rule sources, which are 93b4acb57 blobs written into the tree and restored under a trap (method below). "After" is this branch at its final head. The runtime column comes from a scratch probe of the runtime gate (below), FROM → TO.

rule id lint suite, before → after runtime door (runRuntimeAuthoringRules)
flow-template-field-unprovisioned 1 · 362 → 1 · 175 flow write, 2xx advisory, 362 → 175
flow-template-lookup-traversal 5 · 251–316 → 5 · 125–139 flow write, 2xx advisory, 249 → 131 (trigger root), 314 → 126 (flow variable)
page-field-unprovisioned 1 · 362 → 1 · 175 does not fire on a page write (a reference-integrity member on the frozen flow default)
page-section-group-unknown 4 · 188–279 → 4 · 118–151 does not fire on a page write (same)
print-page-block-unprintable 1 · 362 → 1 · 170 page write, 422 issue, 362 → 170
form-section-group-unknown 5 · 178–275 → 5 · 104–147 CLI only (surfaces: CLI_ONLY)
filter-token-unknown 5 · 132–259 → 5 · 132–170 CLI only (surfaces: CLI_ONLY)
component-props-invalid 33 · 164–258 → 33 · 157–188 page write, 422 issue, value line 248 → 157 (page:tabs), 250 → 159 (object-grid), 253 → 162 (record:details), 258 → 167 (record:related_list)

Measured on main first, as the order asked. I ran main itself at 93b4acb57, with its own rule sources and its own tests. All eight ids fire over 200 there, at the lengths the seat's record 6217435246 lists: 362, 316, 362, 279, 362, 275, 259 and 258. On main the census finds 11 ids over 200: these eight, plus the 3 repo-gate ids outside the condition. Some lines did not change. filter-token-unknown's four "Did you mean" lines (132–161) were one remedy line already. So were component-props-invalid's retired-key line (164–181) and its node-binding line (188). Only the value-rejection line (248–258 → 157–167) moves.

What each line says now.

  • The two anchor ids print the shared one-line anchor fix (unprovisionedAnchorFix()) that the other unprovisioned-anchor ids already print.
  • The lookup-traversal line names the start node's config.expand opt-in on the trigger root, and never on a variable root.
  • The two section-group ids name the nearest declared group ("Did you mean") instead of listing every group the object declares. For an object with no groups, the line is: declare a group, or enumerate the section's fields.
  • The print line points at os explain print-page-block-unprintable for the printable list. This finding is also refused at the save door, where no rule: line carries the pointer.
  • The filter line names the two vocabularies briefly.
  • The props line asks for a value the props schema accepts, or for a schema fix.

os explain carries the rest. Each of the eight ids gains a RULE_EXPLANATIONS entry, so its rule: line now carries the os explain pointer. What the lines stopped saying is there:

  • how to vouch for an unprovisioned anchor through columnMap, or opt the object out (ownership: 'none', systemFields: { audit: false }), shared with the earlier anchor entries;
  • what config.expand does: a one-hop re-read of the triggering record as the run's identity. It covers the trigger record alone;
  • how a section inherits a field group's members, and that membership is declared on the field;
  • the printable block list, held equal to the spec's PRINTABLE_PAGE_COMPONENT_TYPES by the rule's test, and the three reasons a block is refused;
  • the context tokens (held equal to CONTEXT_TOKENS by the rule's test) and the date macros. It also says where {record_id} resolves, and that the runtime refuses an unresolvable placeholder;
  • where a page component's props schema lives, why the page, not the schema, is almost always what to fix, and the retired-key and node-binding cases.

I checked every fact in the eight entries against the rule source and its docblock, and against the spec where they cite it: date-macros.zod.ts, PRINTABLE_PAGE_COMPONENT_TYPES and ComponentPropsMap. I also checked the runtime they describe: the automation engine's expandDeclaredLookups for config.expand, and @objectstack/core's UnknownFilterTokenError for the 400. Why Clause-②: no holds:

  • the eight rule-id constants were already root-barrel exports;
  • the new entry constants are module-local;
  • RULE_EXPLANATIONS (already published) gains eight keys.

So no published name is added. The one removed function, unprovisionedAnchorHint, was module-exported from system-fields.ts but is reached by no package entry. node scripts/check-platform-checklist.mjs --changed-from origin/main reads OK (1 anchor judged, 0 lost).

Nothing else moves. Every message, rule id, severity and path is unchanged, and so is what each rule accepts or refuses. The census proves this mechanically: it records the (message, where, path, severity) tuple of every finding. For each of the eight ids, the set of those tuples is identical with main's sources and with this branch's: 1, 8, 8, 4, 55, 6, 27 and 123 tuples, 0 differing. Every other id's rows are identical too: 4067 rows, run under the same instruments, with one extra row explained in the Verification section.

The two files the claim named conditionally

  • packages/lint/src/object-field-groups.ts: kept. checkSectionGroupRefs() builds the hint of both section-group ids. validate-form-layout.ts and validate-page-field-bindings.ts call it with their rule id, so the one-line form lives there. That is why validate-form-layout.ts itself has no diff.
  • packages/lint/src/system-fields.ts: kept. unprovisionedAnchorFix(), which builds both anchor ids' hint, lives there. The edit deletes unprovisionedAnchorHint(), the long form those two ids called. They were its last two callers. The edit also rewrites the shared fix's docblock.

On top of the inherited head

  • main merged first (1e1554e15, clean). Slice 20's rule-explanations.ts entries (b6805ac0f) merged without a conflict, and all 20 entries are present: the twelve lint-flow-patterns.ts ids plus these eight.
  • Two entry sentences made exact.
    • The form entry named only data.object as a form's own binding. The rule reads objectName, object or data.object (viewObjectName).
    • The page entry skipped the binding a nested component inherits from its container (walkPageComponents).
  • A docblock that overclaimed. unprovisionedAnchorFix()'s docblock said every unprovisioned-anchor rule in the package prints that wording. Two (validate-expressions.ts, validate-semantic-roles.ts) still word their own fix, as the cause docblock beside it already notes. It now says so.
  • The changeset carries this run's numbers. The retired-key range is 164–181 on the merged tree; it was 164–180. The flow lookup-traversal door readings are this run's probe: 249 → 131 and 314 → 126.

File surface: the claim's

The diff touches six rule files, but only five have a diff. validate-form-layout.ts has none, because its hint is built in object-field-groups.ts. The rest of the diff:

  • each rule file's test;
  • rule-explanations.ts, entries for these eight ids only;
  • object-field-groups.ts and system-fields.ts, as explained above;
  • .changeset/22161-s21-validate-fix-lines.md.

It does not touch lint-flow-patterns.ts, lint-startup-registry-verdict.ts or content/docs/**. 15 files, +674 / −76.

Verification

Final head 49e48cd21. Heavy runs went through scripts/pm/os-verify-lock.sh (slot issue-22161-s21), and each reading below is its VERDICT line. Every exit code was captured before any pipe.

  • Lint suite at 49e48cd21: 143 files / 6847 passed, VERDICT 0. The census run is the same run.
  • The main leg.
    • Method: main's 93b4acb57 blobs of the 14 changed lint files were written into the tree only. Their landing was proved by blob hash. They were restored from HEAD by absolute path under an EXIT/INT/TERM trap, and the restore was proved by blob hash, an empty git diff HEAD and a clean status.
    • Result: 143 files / 6820 passed | 5 skipped, VERDICT 0. The 5 skips are the dist-dependent lazy-deps / runtime-lazy-deps cases. The lint dist did not exist yet, and it does now.
  • Reverse verification. Only main's 8 rule sources were swapped in (same method), with this branch's tests: 6 files / 70 tests red, exactly this slice's six test files. The pins fail on the old text, so they can fail. Restore proved the same way. This one leg is also the census "before" column above.
  • Lint build and typecheck at 49e48cd21, VERDICT 0 each.
    • Build: tsup plus check-dts-emitted, 6/6 declaration files.
    • Typecheck: tsc plus check:test-typecheck OK (2 files / 6 errors / 2 pinned, unchanged).
    • tsc -p tsconfig.test.json --listFiles lists all six changed test files.
  • Spec repo project (--project repo, whose cross-package pins read packages/lint/src):
    • The 11 files that read packages/lint: 11 / 201 passed, VERDICT 0.
    • The whole project: 68 files / 1125 passed, VERDICT 0 (the lock held 15m30s on a shared box).
  • Runtime probe. 9 writes through runRuntimeAuthoringRules with tsx: 6 page and 3 flow. It ran with main's 8 rule sources swapped in (same method), then on this branch. Results are in the table above. No write changes channel. Each page case ran 4 rules and each flow case 9 rules, on both sides.
  • The one extra census row. At the final head, one validation-rule-json-schema-uncompilable row appears that the earlier legs did not record. It comes from the five dist-dependent cases, which ran once the lint dist existed. It is not one of the eight ids.
  • Derived gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, at 49e48cd21: 64 families over 15 paths since the merge base 93b4acb57. Each family was run one by one and its exit code recorded. 62 exit 0. 2 exit 3 (PREREQUISITE NOT MET) and are NOT MEASURED: check:dual-build-cjs-loads needs every package built, and check:lean-entry-closure needs packages/objectql/dist. --ran reports 64 derived, 62 run, 2 NOT-MEASURED, 0 UNRUN. This diff adds no import edge, so CI's full build gives those two their reading. For every family CI runs, this PR's own check-runs are the reading. check:dev-prereqs and check:skill-frame-freshness were not run: the diff touches neither a packages/spec build input nor the four-axis frame files.
  • Control characters: grep -naP over every changed file and this body finds nothing (exit 1). A planted control byte is found (exit 0).

Acceptance notes

  • filter-token-unknown's verdict sentence contradicts the runtime it describes. It says an unresolvable placeholder "is sent to the data engine as a literal string, matches no record, and the surface renders empty". So does the rule file's module header. The runtime refuses such a filter instead: UnknownFilterTokenError (FILTER_TOKEN_UNKNOWN, 400, packages/core/src/utils/filter-tokens.ts). That is the spec's own declared behaviour (date-macros.zod.ts: "the runtime resolver throws"). packages/objectql/src/engine-filter-token-today-spelling.test.ts pins the refusal. The new entry states the refusal. This slice keeps messages unchanged by its shape, so the sentence is not edited here. It was not measured through os validate in this run (the CLI was not built), so it is noted, not filed.

  • The 200-character bound is measured over the suite's fixtures, as in slices 11–20. Some lines interpolate names:

    • the group line: about 88 characters plus two copies of the key and the object name, and a nearest-group suggestion when one is close;
    • the shared anchor line: 155 characters plus the field and object names.

    Long enough names can push either past 200. The anchor line's wording predates this slice. component-props-invalid's test re-measures its two type-naming lines at the longest type ComponentPropsMap declares.

  • The census sees what the lint suite fires. It measures 213 of the 215 id-shaped rule constants the root barrel exports. The other two never fire in the suite. chart-config-missing has no emit site in packages/lint/src. react-prop-deprecated's hint is the react-blocks contract's own note for a deprecated prop.

  • main moved after the one merge. The branch carries main at 93b4acb57, and origin/main is now at e15897c0f. None of the 5 newer commits touches packages/lint. Per the order, I merge again only for a conflict, a main-caused red or a queue removal.

  • validate-expressions.test.ts still names the deleted unprovisionedAnchorHint in a historical comment about an earlier card. It is outside this slice's surface and left as written.

  • Not run locally, declared to CI.

    • The packages/cli tiers. test/explain-rule-id.test.ts holds every RULE_EXPLANATIONS key disjoint from the os explain schema names and resolves each through the command.
    • os validate / os explain through a built CLI.
    • Both need the CLI build closure: 62 packages, most of them downstream of @objectstack/lint.

Remaining for this card

The census at 49e48cd21 covers the whole lint suite: 239 ids fire, and 3 are over 200. All 3 are outside the close condition (repo gate, not author-time): lint-startup-registry-verdict.ts's startup-open-vocabulary-verdict 972, startup-verdict-assertive-wording 972 and startup-source-unparseable 254. No author-time packages/lint id fires a hint or fix: over 200 characters in the suite. The close-out slice still owes two things: the census re-measured on main after this lands, and the content/docs/ui/react-pages.mdx transcript carry from slice 15's landing record 6136999237.


Generated by Claude Code

…ule ids; os explain carries what they explained

The `fix:` line (the finding's `hint`) of flow-template-field-unprovisioned,
flow-template-lookup-traversal, page-field-unprovisioned,
page-section-group-unknown, print-page-block-unprintable,
form-section-group-unknown, filter-token-unknown and component-props-invalid
is one remedy line of at most 200 characters on every variant that ran past
200. What the lines used to explain moves into one RULE_EXPLANATIONS entry per
id, behind `os explain RULE_ID`. Messages, ids, severities, paths and
accept/refuse results are unchanged.

- The two anchor ids print the shared one-line anchor fix
  (`unprovisionedAnchorFix`); the long form, `unprovisionedAnchorHint`, lost
  its last two callers and is deleted.
- The two section-group ids share `checkSectionGroupRefs`: the declared group
  list is no longer echoed, the nearest group is named ("Did you mean").
- The print id no longer lists the printable set; its explanation does, held
  equal to PRINTABLE_PAGE_COMPONENT_TYPES by the rule's test.

Claude-Session: https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk
Co-authored-by: Claude <noreply@anthropic.com>
…ng key a form resolves its object from

Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK
Co-authored-by: Claude <noreply@anthropic.com>
…er binding; changeset carries the re-measured lengths

Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK
Co-authored-by: Claude <noreply@anthropic.com>
… still word their own

Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK
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 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

18 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • 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 d537a318b3116a2bcb4f70cf85ffd2a05c13d002 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 (seat check) · PR #24292 · #22161 slice 21 · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-18T09:22Z

ACCEPT at head 49e48cd217971ca1cc040c2d779f3a4a5c9682ba (claim 6247309763, report 6248147190). It lands once every check on this head is green. No contract review is owed:

  • the diff touches neither packages/spec/src/** nor a governed path (check-governed-merges --pr 24292: 0 of 15 paths, 750 changed lines);
  • it declares Clause-②: no with no narrowing arm, and what each rule accepts or refuses is unchanged.
item reading
Shape Draft, base main. The body's line 1 is Part of #22161, line 2 is Clause-②: no, and the body has no closing keyword, so the card stays open for its close-out slice. Assignee zhuangjianguo = the card's. 15 files, all inside the claim's surface.
The slice's work, read in the diff The 8 ids' hints become one remedy line each. unprovisionedAnchorHint is deleted, and its two callers move to the shared unprovisionedAnchorFix. checkSectionGroupRefs (object-field-groups.ts) builds both section-group ids' line, which is why validate-form-layout.ts has no diff, so the claim's conditional keep holds. printableList() is gone from the print rule. No message, rule id, severity or path line changes.
RULE_EXPLANATIONS rule: 'ID' entries on main → this head: 0 → 1 for each of the 8 ids. Slice 20's entries survive the merge (flow-loop-body-uncontained 1).
Tracker ids The added #22161 mentions are in comments and describe titles only, none in a runtime string, which is the earlier slices' convention.
Changeset, sentence by sentence .changeset/22161-s21-validate-fix-lines.md: "@objectstack/lint": patch, Clause-②: no. Checked against the diff: the 8 before → after lengths (the dev's census, method in the report); the two examples (print-page-block-unprintable, flow-template-lookup-traversal on a variable); "Lists are no longer echoed" (the declared-group list and printableList() are both removed); "The anchor ids converge" (unprovisionedAnchorFix); "Unchanged: every message …". The section-group line names the nearest group only when suggestName finds one; "Did you mean" reads that way. The publish-gate FROM → TO figures are the dev's runtime probe and were not re-measured here.
Gates (check-runs on this head, read now) 37 runs: 20 success, 6 skipped, 11 in progress (Test Core 1–6, Build Core, the type-check lanes, Lint · repo & tooling gates), 0 failure. Not converged, so it does not land yet.
Accepted deviations (1) The whole spec repo project was run locally (11 of its files read packages/lint/src). (2) The packages/cli tiers and a built os validate / os explain are declared to CI. (3) Reverse verification swapped in main's whole-file blobs, proven by blob hash. (4) A commit landed before a queued tree-swap leg, so the restore kept it (shown in the head). (5) main moved after the one merge; none of the new commits touches packages/lint. (6) The trailers are AGENTS.md's model-free pair. (7) No label writes.
Out of scope filed #24293: filter-token-unknown's message says the value "matches no record, and the surface renders empty", while the runtime refuses it (UnknownFilterTokenError, 400 FILTER_TOKEN_UNKNOWN, core/src/utils/filter-tokens.ts:547), re-read by the seat. #22161's close-out slice (census re-measure on main, the react-pages.mdx carry) is this card's own remainder. The two long-name 200-character edges and the historical unprovisionedAnchorHint comment in validate-expressions.test.ts: Acceptance notes.
Landing After every check on this head is green: pr_ready then automerge_enable, through the relay. On merge, the card goes back to pm:queue for its close-out slice, with a Release: line.

Generated by Claude Code

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