Repository navigation
feat(lint,cli): one-line author-time rule verdicts, and os explain RULE_ID for the reasoning - #22339
Conversation
… pointer field-no-consumers and security-owd-unset print one verdict sentence and one fix; their long reasoning moves into rule explanations keyed by rule id (@objectstack/lint, also published as the import-free `@objectstack/lint/rule-explanations` entry). `os explain` resolves a rule id after the schema names, and the CLI's `rule:` line names the command for the rules that have an explanation, from one spelling in utils/format.ts. `os validate` now prints the fix and rule lines under each registry warning, as `os build` does. Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
…in <rule-id>` Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
…-time-rules test reads the field from where Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 15 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 8 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 30 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 a93abdc9daee6b74fa6de19c5609a5e71f061da5 && git checkout a93abdc9daee6b74fa6de19c5609a5e71f061da5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 54c3ce10ce8cf89290b67813c34d1f10dbab6938 1e016895c333ac726d83467331777fa84da91849 && git checkout -B drift-repro 54c3ce10ce8cf89290b67813c34d1f10dbab6938 && git merge --no-ff 1e016895c333ac726d83467331777fa84da91849
node scripts/docs-audit/affected-docs.mjs --json 54c3ce10ce8cf89290b67813c34d1f10dbab6938
|
…` / `__proto__` are refused, not crashed The schema lookup this change restructured read `SCHEMAS[name]` bare, so `os explain constructor` printed "Schema: Object … undefined" and threw `schema.required is not iterable` (exit 1, stack trace). Both lookups are own-key reads now; a prototype key is an unknown id like any other. Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
…shed on Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
…le-message-one-line
…s message; four context-only hints state their fix `os validate` / `os build` label every finding's hint `fix:`. Five producers put something else there: expression-invalid quoted the authored source (printed as `fix: source: …`), and component-props-invalid, flow-time-relative-descriptor-invalid, react-prop-missing-required (the contract description branch) and liveness-experimental-property carried context only. The source now ends the expression-invalid message (` — source: …`, the flow engine's spelling) with an empty hint; the other four lead with the instruction. Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
…l, source in the verdict); changeset line Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
… reworded hints that run at the publish gate Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Read: card #22161 (body and all six comments), PR #22339 (body, the 28-file list, the net diff against its base e9a1f5c — 28 files, +919 / -81), the check-runs on the head, and the files the diff touches as they stand at the head (git show, no checkout, no build, no test). ① Derived judgments
② Semver level
The changeset's text does not match what the diff publishes on the runtime wire:
Both are text fixes to the changeset, one commit; name the ③ Boundary flags
Implemented-by: VERDICT: FAIL FAIL reasons:
Next: one changeset commit correcting both (naming the Generated by Claude Code |
…ed hint reaches a 422, security-owd-unset at the object door, the os explain unknown-id string Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Written 2026-10-08T21:18Z. Read: card #22161 (body and all seven comments, the prior record ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #22161
Clause-②: yes (widening:
os explainaccepts a rule id, andpackages/lintexports the rule explanations)What changes
field-no-consumersandsecurity-owd-unsetprint one verdict sentence and one fix. Their long reasoning moves into one static explanation per rule id,RULE_EXPLANATIONS/explainRule()in@objectstack/lint(new modulepackages/lint/src/rule-explanations.ts, exported from the root barrel and from a new import-free entry,@objectstack/lint/rule-explanations). Nothing else carries a copy.os explain RULE_ID(the maintainer's spelling, one positional, norulesub-word): a schema name resolves exactly as before; otherwise an exact rule id resolves to its explanation (--jsonprints{ rule, covers, paragraphs }). The no-argument listing also names the rule explanations (--jsonaddsrules: [{ id, covers }]). An unknown id exits 1 and names both lists.rule:line carries the pointer, spelled once —explainPointer()/authoringFindingDetailLines()inpackages/cli/src/utils/format.ts, used by the build advisory printer, the gating-error printer (validate, build, verify, init), the validate advisory list, andos lint's rule line. It appears only for a rule id the table holds, so it never names a command that would answer "unknown". The hint line is labelledfix:.os explain's schema lookup reads own keys only.os explain constructor/__proto__printedSchema: Object … undefinedand threwschema.required is not iterableon main. They are now refused as unknown ids (6d2eb857c; pinned intest/explain-rule-id.test.ts; with the own-key check reverted → 1 failed | 10 passed).fix:line is always a fix (patch round 2).expression-invalid's authored source is a quote, not a fix, so it now ends the finding'smessageas— source: `…`and itshintis empty: the CLI prints nofix:line for it, and the source still reaches the text face and the runtime 422 issue. Four other hints that carried no instruction now open with one:component-props-invalid(its consequence moved into the message),flow-time-relative-descriptor-invalid,react-prop-missing-required(the contract-description branch),liveness-experimental-property.The maintainer's shape, as
os validateandos buildnow print it on a tutorial-shaped project:Measured (local CLI built from this branch; tutorial-shaped project:
my_app_note+my_app_ticket, a grid view ontitle/status)59d993c97)os validate,field-no-consumersos build,field-no-consumersos validate,security-owd-unsetos dev --compilerunhint) are built inpackages/lint/src/validate-field-consumers.ts.os validateprinted the registry advisory as its⚠line only (commands/validate.ts), with no fix and no rule line;os buildprinted message, hint and rule line throughprintAuthoringAdvisories.rule:line is the CLI printer's, not the rules' (utils/format.ts, two printers). The pointer is spelled there once.os validate's advisory list had no rule line at all, so it now renders the same two lines through the same helper (below).os explainschema names, 214 rule id constants exported frompackages/lint— intersection empty (no rule id is a single word). Pinned inpackages/cli/test/explain-rule-id.test.ts(lowercased, against every exported rule id constant).os dev --compile -p PORT --freshrun printed the warning once (grep -c field-no-consumers= 1, before and after). No printer change was made for it.field-no-consumersandsecurity-owd-unsetonly. The dead-buttonaction-governanceline is not an author-time rule: it is the boot-timelogger.warninpackages/objectql/src/action-governance.ts([action-governance] declared script actions with NO handler …— 163 chars as the source writes it, plus a{count, actions}payload; its sibling "registered handlers with NO declaration" line is 628). It lives outsidepackages/lintand outside the CLI printer, so it is named here and not edited.Landing outside the claim's file surface, and why
packages/cli/src/utils/format.ts— the H2 printer:explainPointer()andauthoringFindingDetailLines(); both printers render through them.packages/cli/src/commands/validate.ts— measured:os validateprinted a registry warning with no fix and no rule line, so a shortened message would have reached the maintainer's first-named command with no pointer. The text face now prints the two lines under each registry advisory via the same helper. Thewarningslist--strictand--jsonread is unchanged.packages/cli/src/commands/lint.ts—os lintprints the same shortened message; its rule line gains the same pointer (one call toexplainPointer).packages/lint/package.json,packages/lint/tsup.config.ts,packages/lint/src/rule-id-barrel-exports.test.ts— the new./rule-explanationsentry.format.tsis documented as "a pure formatter with no rule-engine import", and every command imports it; loading the@objectstack/lintroot barrel after@objectstack/specmeasured 456–547 ms (three runs), which every command (os explain objectincluded) would otherwise pay. The entry's module imports nothing (pinned by a source scan); its keys and thefield-no-consumersroots list are literals held to the rule's constants byrule-explanations.test.ts.packages/cli/README.md— theos explainrow.packages/cli/src/utils/author-time-rules.test.ts(read the field fromwhere, notmessage),packages/cli/test/truncation-remainder-notices.test.ts(fix:label),packages/cli/test/validate-build-gate-parity.test.ts(classifiesauthoringFindingDetailLinesas presentation). No test outsidepackages/lint/packages/clipins either old message.Tests, round 1 (all local, this branch; head
315a26618unless a run names another; round 2's readings are under## Patch round 2)New pins:
packages/lint/src/rule-explanations.test.ts(every key is an exported rule id under its own key;coversfits the pointer; no tracker number in the text; the roots paragraph equalsCONSUMER_ROOTS/CARRIER_ROOTS; exact-id lookup; the module imports nothing), the shape pins invalidate-field-consumers.test.tsandvalidate-security-posture.test.ts(verdict line and fix line, exact),packages/cli/test/explain-rule-id.test.ts(H3 disjointness; every pointer target resolves throughExplain.run; the printed verdict /fix:/rule:lines of each rule's REAL finding; schema lookup unchanged; unknown id exits 1), andpackages/cli/test/rule-line-explain-pointer.e2e.test.ts(spawnsos validateandos build; a*.e2efile, so the nightly tier).pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2→Test Files 128 passed (128),Tests 5853 passed (5853)(ated786eb3e; no lint file changed after it).pnpm --filter @objectstack/lint run typecheck→ exit 0,check:test-typecheck: OK — … 2 file(s) / 6 error(s) / 2 pinned signature(s) held.pnpm --filter @objectstack/cli run typecheck→ exit 0,check:test-typecheck: OK — … 3 file(s) / 28 error(s) / 6 pinned signature(s) held(at315a26618).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 --shard=N/3(the full unit tier, in three foreground shards because one run exceeds the container's foreground cap under load): shard 189 passed/1523 passedand shard 288 passed, 1 failed(ated786eb3e); shard 389 passed/1264 passed(at315a26618). The shard-2 failure wassrc/utils/author-time-rules.test.tsreading the field name frommessage; fixed in315a26618and re-run withtest/lint-per-package-authoring-seam.test.ts→2 passed/10 passed.--project integration(declared to CI as a whole), the ten files that spawn validate / build / verify / lint and read their text:test/build-text-face-advisory-count,verify-author-time-stage,validate-per-package-authoring-parity,union-fold-command-parity,authoring-rule-command-parity,validate-view-container-name,build-view-container-name,picklist-reference-doors,lint-per-package-authoring-parity,validate-lint-mapping-connector-source→Test Files 10 passed (10),Tests 62 passed (62).OS_TEST_TIERS=nightly … vitest run test/rule-line-explain-pointer.e2e.test.ts→2 passed;validate-json-warning-parity.e2e.test.ts(the⚠line still pairs with--json) →3 passed(both on theed786eb3etree).scripts/ablation-replace.mjsreplacedexplainPointer's return with''inpackages/cli/src/utils/format.ts(anchor 1 → 0, blob9d90c98c409d→4427f41a866d),test/explain-rule-id.test.ts→3 failed | 7 passed; restored, blob9d90c98c409d== HEAD,git diff HEADempty.packages/clibuilds against@objectstack/lint/rule-explanations, an entry that exists only in the rebuiltdist/(dist/rule-explanations.{js,cjs,d.ts,d.cts}), so the CLI build read the rebuilt declarations. CJSrequireand ESMimportof the entry both load (['field-no-consumers', 'security-owd-unset']).npx eslint --no-inline-config --format jsonover the 18 changed.tsfiles → 18 files in the JSON report, 0 errors, 0 warnings;eslint.config.mjsnever enables type-aware linting (noparserOptions.project, its own comment at:327), so this diff cannot move a verdict on an untouched file. Repo-widepnpm lintis CI's.Gates
node scripts/pm/dispatch-gates.mjs --commands(no paths) at315a26618derived 78 commands, a superset of the 51 at dispatch. Ran all 78: 76 exit 0;pnpm check:dual-build-cjs-loadsandpnpm check:i18n-coverageexit 3, PREREQUISITE NOT MET (packages outside the CLI's build closure have nodist/in this worktree) — NOT MEASURED, CI's. Reconciliation:✓ dispatch-gates --ran: 78 derived famil(ies) accounted for — 76 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3).Also run, exit 0:check:authz-resolver,check:error-code-casing,check:filter-alias-parity(the three artifact-roster gates whose roster sits underpackages/),check:published-readme-exports,check:published-readme-links,check:cli-examples-parity. Control-character scan over every changed file: no match.Second stage — the over-long rules this PR does not shorten
Measured by running the whole
packages/lintsuite at59d993c97(127 files, 5843 tests) with a scratch hook that recorded, per rule id, the longestmessageof any finding pushed: 240 rule ids fired, 157 with a message over 200 characters. Lengths are themessagealone (the printed line addswhereand:). A rule id that fired in no test is not in this count.Second stage, in
packages/lint(not touched here) — 124 rule id(s):validate-rls-predicate-enforceability.ts:rls-predicate-unparseable2056,rls-predicate-unenforceable1679,rls-predicate-unknown-user-variable1544,rls-predicate-unknown-field1399,rls-predicate-over-budget1259validate-sharing-rule-enforceability.ts:sharing-rule-unlowerable-condition1093,sharing-rule-object-not-shareable774,sharing-rule-object-controlled-by-parent660,sharing-rule-runtime-variable-condition492validate-rule-schema-formats.ts:validation-rule-json-schema-unknown-format1049validate-action-dispatch-contract.ts:action-dispatch-contract-mismatch927validate-component-props.ts:component-props-invalid920,component-props-unknown-key844validate-sortable-fields.ts:sort-field-unprovisioned844,sort-field-unsortable369,sort-field-unknown287validate-dataset-measure-aggregates.ts:measure-aggregate-field-type-refused809,dimension-json-stored-field-refused531validate-component-types.ts:component-type-unknown807validate-flow-trigger-readiness.ts:flow-time-relative-descriptor-invalid796,flow-time-relative-descriptor-unroutable532,flow-trigger-unroutable518,flow-api-trigger-secret-missing336,flow-trigger-unknown-event222validate-hook-body-writes.ts:hook-body-write-unprovisioned-anchor792,hook-body-write-unknown-field465,hook-body-source-unparseable212validate-react-page-props.ts:react-chart-drilldown-invalid784,react-chart-aggregate-invalid507,react-chart-field-unprovisioned429,react-block-needs-record-context267,react-page-source-unparseable211validate-action-body-writes.ts:action-body-write-unprovisioned-anchor778,action-body-write-unknown-field433,action-record-write-discarded293,action-body-source-unparseable212validate-flow-node-writes.ts:flow-node-write-unprovisioned-anchor739,flow-node-write-unknown-field421validate-security-posture.ts:security-controlled-by-parent-ambiguous-relation697,security-fls-unknown-field593,security-controlled-by-parent-no-relation526,security-master-detail-ungranted449,security-owd-alias354,security-delegation-missing-reason210validate-preset-comparands.ts:filter-preset-comparand669validate-visibility-predicates.ts:visibility-predicate-unknown-function655,visibility-predicate-over-budget507,visibility-bare-identifier411,visibility-predicate-syntax341,visibility-root-mislayered304validate-predicate-path-refs.ts:predicate-rhs-path-shaped648,predicate-path-unrooted434,predicate-path-unresolved371validate-page-visualization-bindings.ts:page/visualization-without-binding629validate-translatable-sections.ts:translation-section-name-missing553validate-nav-object-servability.ts:nav-object-unservable528validate-searchable-fields.ts:searchable-field-unprovisioned512,searchable-field-unsearchable449,searchable-field-unknown295validate-widget-bindings.ts:dashboard-filter-field-unprovisioned509,chart-field-unknown407,dashboard-filter-field-not-included370,widget-filter-field-unknown364,dashboard-filter-field-unknown333,chart-dimensions-missing306,widget-filter-field-not-included282,widget-measures-missing255,widget-sortby-unselected242,chart-measures-missing224,widget-legacy-analytics-unrenderable205validate-rule-compilability.ts:validation-rule-json-schema-uncompilable489,validation-rule-regex-uncompilable482validate-page-field-bindings.ts:page-field-unprovisioned484,page-section-group-unknown214data-model-rules.ts:unique/legacy-organization-composite478,unique/unscoped-declared-index427,unique/double-declaration381validate-mapping-target-fields.ts:mapping-target-field-unknown466validate-ai-agent-authoring.ts:default-agent-legacy-alias466,default-agent-outside-roster388,agent-authoring-withdrawn352validate-readonly-hook-writes.ts:hook-api-update-readonly-field464,hook-api-update-readonly-when-field267validate-approval-approvers.ts:approval-approvers-may-resolve-empty451,approval-approver-not-membership-tier272validate-dataset-references.ts:dataset-field-not-included446,dataset-field-unknown296,dataset-filter-field-unknown294,dataset-include-unknown260validate-list-view-field-refs.ts:list-view-field-dotted445,list-view-field-unknown355validate-chart-bindings.ts:chart-measure-unknown442,chart-axis-not-selected327validate-ai-tool-references.ts:ai-skill-tool-unresolved441lint-view-refs.ts:view-ref-nav-view-missing437,view-key-collision257validate-nav-target-refs.ts:nav-target-unresolved426validate-managed-api-methods.ts:object/managed-api-method-unaffordable412validate-readonly-flow-writes.ts:flow-update-readonly-field410,flow-update-readonly-when-field317validate-action-name-refs.ts:action-name-undefined409validate-readonly-action-writes.ts:action-api-update-readonly-when-field396lint-flow-credential-literals.ts:flow-credential-literal390validate-filter-tokens.ts:filter-token-unknown386validate-print-page-blocks.ts:print-page-block-unprintable368validate-org-axis-red-lines.ts:org-axis-cross-org-bu-grant365validate-nav-access.ts:nav-object-ungranted353validate-empty-combinators.ts:filter-empty-combinator352,filter-empty-node226validate-translation-references.ts:translation-target-unknown345,translation-option-key-unknown230validate-object-references.ts:object-reference-unregistered-platform324validate-object-field-refs.ts:object-field-ref-unknown320validate-seed-state-machine.ts:seed-value-outside-state-machine320validate-semantic-roles.ts:semantic-role-field-unprovisioned291validate-view-containers.ts:view-container-shape290validate-ai-surface-affinity.ts:ai-skill-surface-mismatch281validate-flow-filter-tokens.ts:flow-filter-token-unknown278validate-dashboard-action-refs.ts:dashboard-action-route-unresolved242,dashboard-action-target-undefined239validate-retired-permission-residue.ts:permission-retired-lifecycle-residue235validate-seed-replay-safety.ts:seed-insert-mode-duplicates-on-replay222validate-capability-references.ts:capability-reference-unknown219validate-form-layout.ts:form-section-group-unknown214Excluded this round — files open PRs #22268, #22315, #22319 edit — 19 rule id(s):
validate-expressions.ts:expression-invalid2027lint-flow-patterns.ts:flow-multi-write-unfiltered656,flow-decision-mode-invalid528,flow-loop-body-uncontained522,flow-try-catch-without-catch520,flow-approval-revise-target-not-service-owned366,flow-decision-unconditional-branch342,flow-error-label-not-fault315,flow-inert-node-condition286,flow-runas-unscoped284,flow-branch-label-unmatched272,flow-decision-inclusive-overlap250,flow-default-edge-with-condition239,flow-multiple-default-edges211,flow-time-relative-antipattern208,flow-date-equality-filter208validate-flow-template-paths.ts:flow-template-field-unprovisioned440,flow-template-lookup-traversal349,flow-template-unknown-field258Message lives outside
packages/lint—packages/spec/src/kernel/functional-completeness.ts(named, not edited) — 8 rule id(s):functional-completeness.ts:view/row-color-without-colors793,view/layout-without-binding673,webhook/without-triggers594,view/tree-without-parent-field592,field/summary-without-operations319,field/formula-without-expression259,field/choice-without-options250,field/relationship-without-reference239Not an author-time registry rule — 4 rule id(s):
lint-startup-registry-verdict.ts(the repo gatecheck:startup-registry-verdict):startup-open-vocabulary-verdict779,startup-verdict-assertive-wording731data-model-rules.tslintDataModel(os lint's own data-model rubric):relationship/master-detail-required462,rollup/non-numeric-aggregand364Each second-stage rule takes the same shape: move the long text into
RULE_EXPLANATIONS(the pointer then appears on itsrule:line by itself), leave one verdict sentence and one fix, and pin the new shape in the rule's own test. The excluded three files can follow once #22268, #22315 and #22319 land.Acceptance notes
fix:label now prefixes the hint under every author-time finding the CLI prints (build, validate, verify, init), not only the two shortened rules. Round 2 measured every hint producer for text that is not a fix and changed five (listed under## Patch round 2); every other rule's hint text is unchanged. Borderline rows were counted as fixes, because each carries an instruction or a spelling to write:validate-component-types.ts:150,validate-flow-trigger-readiness.ts:632,runtime-gate.ts:1020,lint-liveness-properties.ts:254/:273, and thefixsnippets infunctional-completeness.ts.expression-invalid's runtime 422 issue now carrieshint: ''; its message carries the source. objectui's save-advisory toast already skips an empty hint (saveAdvisoryToast.ts:97). The one place that prints the bare value is the deduped operator log line inmetadata-protocolruntime-authoring-gate.ts:1130(… (${advisory.hint})), which now ends in()for anexpression-invalidwarning. It is cosmetic, server-log only, and not changed here.os validate's text face now showsfix:andrule:lines under every registry warning (it showed neither before); thewarningslist--strictand--jsonread is unchanged, sovalidate-json-warning-parity.e2e.test.tsstill pairs the faces.security-owd-unsetalso runs at the metadata write door, so a Studio / REST / MCP refusal carries the shorter message and hint too; the explanation is reachable from the CLI only.origin/maine9a1f5c40is merged (d33862bde, a merge commit).Patch round 2 (seat order
6067462250→74bed8f56)Written into this body by the
domain:specseat 2 at 2026-10-08T20:46Z from the dev's report6068666691; the role file reserves a later body edit to the seat.hint:values inpackages/lint/src, plus thefix:values infunctional-completeness.tsand the shared hint helpers). Five, at the stop condition's limit, none in the three excluded files:authoring-rules.ts:687,expression-invalid: quoted the source. The source now ends the message, and the hint is empty.validate-component-props.ts:336,component-props-invalid: context only. The hint is the fix, and the consequence moved into the message (a CLI-only rule).validate-flow-trigger-readiness.ts:497,flow-time-relative-descriptor-invalid: context only. The hint opens with the instruction.validate-react-page-props.ts:1166,react-prop-missing-required: the hint was the binding's description alone. It is nowPass REQ={…}: DESCRIPTION.lint-liveness-properties.ts:247,liveness-experimental-property: the hint was a statement. It now opens with an instruction.packages/cli/test/explain-rule-id.test.tsruns the real registry adapter on the tutorial's action and prints throughprintAuthoringRuleErrors. It asserts exactly two lines, the source inside the verdict line and nofix:line. Ablated with the dist leg (adapter reverted, lint rebuilt): 1 failed | 11 passed. Restored to the HEAD blob, and the rebuilt dist carries no marker.content/docs/getting-started/build-with-claude-code.mdx:309–:313andcontent/docs/ui/react-pages.mdx:361–:363,:403–:405. The:403block was already stale before this PR (the fallback hint where the contract has a description). Cross-lane on [PM seat] domain:devx @ objectstack — ⏳ vacant #6023.messagechange forexpression-invalid. Its count of the reworded rules that reach a runtime response is corrected in patch round 3, below.e84732425.expression-invalid), and typecheck exit 0, all at819444f50..tsfiles: 0 errors / 0 warnings.dispatch-gates --commands(no paths) at819444f50: 106 derived (round 1's 78 plus 28 docs/spec families), all 106 exit 0.--ran: 106 run, 0 NOT-MEASURED. The 20 changeset/text families re-ran on74bed8f56: exit 0.check:dual-build-cjs-loads,check:i18n-coverage) also exit 0 at6d2eb857c.e9a1f5c40is 28 files, +919 / -81. Governed paths touched: 0.Patch round 3 (contract review FAIL
6068965879→ seat order6068983639→1e016895c)Written into this body by the
domain:specseat 2 at 2026-10-08T21:10Z from the dev's direct report; the role file reserves a later body edit to the seat. One commit,.changeset/22161-rule-message-one-line.mdonly (+4 / -2). Each sentence was checked againstsurfaces,runtimeTypesand severity in the code before it was written.flow-time-relative-descriptor-invalidreaches a 422hint: it is anerroronflowwrites.liveness-experimental-propertyis always awarningonemail_template,mappinganddatasourcewrites, so it would ride the 2xxadvisories. No ledger row on those types isexperimentaltoday, so it reaches no runtime response yet. This corrects the seat's own order, which put it on the 422.component-props-invalidis CLI-only.react-prop-missing-requiredjudges nopagewrite at the gate.security-owd-unsetat theobjectwrite door. A custom object (neitherisSystemnorsys_-named) with nosharingModelis refused with a 422, and its issue now carries the newmessageandhint, both quoted verbatim.whereandpathstill carry the object;os explain security-owd-unsetprints the incident.expression-invalid: the source rides the issuemessageat the gate forflow,action,hookandobjectwrites. Anerrorlands in the 422, awarningin the 2xxadvisories. The runtimehintis''.os explainunknown id: it still exits 1. The text changes fromUnknown schema: "X"toUnknown schema or rule id: "X", followed by aRules with an explanation: …line. The--jsonerrorchanges the same way.1e016895c: the 20 familiesdispatch-gates --commandsderives for the changeset, pluscheck-changeset-fixed.mjs, all exit 0. NoPREREQUISITE NOT MET.mainwas not merged (the push was accepted).Generated by Claude Code