Skip to content

fix(lint)!: os build and the object save door refuse a validations[] rule whose predicate reads parent - #25052

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24957-validation-rule-parent-error
Oct 22, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24957-validation-rule-parent-error

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #24957
Clause-②: no (narrowing: a validations[] rule that reads parent is refused at os build and the object save door; it ships now under the amended 「甲」, #6015 6303865025)
Metadata-protocol: narrowed

What changes

A validations[] rule's predicates (a script / cross_field rule's condition, a conditional rule's when, and the same slots on the rules nested in its then / otherwise) are evaluated by ObjectQL in ruleScope(record, previous), which binds no parent. Such a predicate faults (Unknown variable: parent) on every write it judges and fails closed. The 18.x verdict shipped at warning (PR #24763, 8cfe1f94cb). This PR ships the refusal half, re-timed to 18.x as a BREAKING minor.

  • packages/lint/src/validate-expressions.ts: validationRuleParentIssue's finding is pushed at error instead of warning. The message drops "This is a warning today; os build and the object save door will refuse it from 19.0." The docblock's "Why a warning, and when it becomes a refusal" section becomes "Why an error". The location, rule id, prescription and the field-rule controls are unchanged.
  • The object save door runs the same pass (runStackExpressionPasses), so the door's verdict moves from advisory to error with no door code change. A publish-mode object save now answers 422 INVALID_METADATA.
  • Changeset .changeset/24957-validation-rule-parent-error.md: @objectstack/lint minor, BREAKING banner, Clause-②: no (narrowing …), ADR-0087 marker not-required (no-migration-prescription) (shape of the bb4f5cc005 precedent).

The gate on #24684 (withdrawal condition)

#24684 closed completed through PR #24731 (576d0f3733). It landed route B: three lifecycle hooks on showcase_invoice_line. It made no objectql, lint or spec change. On origin/main 3b3830ca22, checkPredicate / checkConditional still call ruleScope(record, previous) (rule-validator.ts lines 4511 / 4695), and ruleScope adds no parent. So parent is still unbound for validations[], and the withdrawal condition does not hold.

Tests

  • packages/lint/src/validate-expressions.validation-rule-parent.test.ts: every severity assertion now reads error. os validate / os build carry the finding as their only error. The door finding is in errors, not advisories, and the door/build parity is unchanged. New: the message promises no later release, and an active: false rule or an update-only rule is refused too (the rule is judged as written). Controls unchanged: parent on a field's own readonlyWhen / requiredWhen, record / previous reads, record.parent_code.
  • packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts, new block (#24957) through the real saveMetaItem: (a) an active save is refused 422 INVALID_METADATA with the build's located finding, and nothing lands; (b) a draft save still succeeds, and its promotion and a package draft publish are refused; (c) control: parent on readonlyWhen, and a record rule, save and land.
  • Reverse verification, through dist/: scripts/ablation-replace.mjs put the severity back to warning with a marker, then rebuilt lint. ablation-dist-preflight found the marker in 4 built files. The door block went red, 3 failed (a, b, b-package) and 1 passed (c control). After the restore (blob equal to HEAD, git diff HEAD empty), lint was rebuilt, preflight --absent passed, and the block was green, 4 of 4.
  • pnpm --filter @objectstack/lint test: 153 files, 7045 tests passed. pnpm --filter @objectstack/lint typecheck: exit 0. pnpm --filter @objectstack/metadata-protocol typecheck: exit 0. protocol.runtime-authoring-gate.test.ts in full: 179 passed (before the package-publish leg was added; the -t 24957 run after it passed 4 of 4).
  • Dispatch-gates at head 66e0df6213: 66 derived, 63 run, 3 NOT MEASURED (check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt; each exited 3, prerequisite not met: a full-workspace build). Those are declared to CI.

Producers measured

  • In-repo, at 3b3830ca22: 0 validations[] predicates read parent across examples/, packages/create-objectstack, skills/ and package objects. The 10 files under examples / packages/create-objectstack / skills that author validations: were grepped for parent. Control leg: the same grep finds the field-rule reads (examples/app-showcase/src/data/objects/invoice.object.ts, 3 readonlyWhen hits), so it does reach the corpus. Nothing to fix.
  • HotCRM: NOT MEASURED, because no checkout is in this container.

Acceptance notes

  • Release text (route B, seat decision 2026-10-22). This PR no longer touches the pending .changeset/24720-validation-rule-parent.md. That file is restored byte-identical to main, so check:empty-changeset is green. Instead, this PR's own .changeset/24957-validation-rule-parent-error.md says it supersedes the 24720 entry's "Nothing new is refused in 18.x" sentence and its 19.0 plan, so a CHANGELOG reader can tell which entry is current.
  • Open PR feat(spec,lint): a bulk action's confirmText is a template slot over count and its params, judged at validate, compile and the publish gate #24947 also edits validate-expressions.ts, in different hunks (imports, StackExpressionOptions docs, the bulk confirmText pass). There is no textual overlap with the verdict lines.
  • The claim's file surface did not name the metadata-protocol door block. It is declared here.
  • Unchanged and out of scope: current_user / input / os / trigger in a validations[] condition (the family the card notes for triage).

Generated by Claude Code

… predicate that reads parent (422)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
… minor; the warning entry stops promising 19.0

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 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
  • 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 504d8b3acdc7ff2fde9fc3b9190904fe3badcf9b → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 504d8b3acdc7ff2fde9fc3b9190904fe3badcf9b

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

…declares Metadata-protocol: narrowed and supersedes its 19.0 plan
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat review — PASS · domain:spec seat 3

Head: bf78cec3f72208f0c6746369716b096c007d11e6. Card #24957, dev reports 6308229672 (round 1) and 6333673319 (round 2).

  • Contract review: none owed. The diff touches packages/lint/src/validate-expressions{,.validation-rule-parent.test}.ts, a metadata-protocol test and .changeset/24957-*.md: nothing under packages/spec/src/** and no governed path (check-governed-merges: NOT governed). It declares Clause-②: no (narrowing).
  • Diff:
    • validationRuleParentIssue moves from warning to error.
    • The message drops "This is a warning today … from 19.0".
    • The object save door refuses with 422 INVALID_METADATA through the same pass, with no change to door code. That covers the active save, the draft promotion and the package draft publish.
    • The round-1 ablation reds the door pins, and a control saves.
    • 0 in-repo producers author parent in validations[].
  • Route B (round 2):
    • origin/main is merged in.
    • .changeset/24720-validation-rule-parent.md is restored byte-identical to main, so check:empty-changeset is green.
    • This PR's changeset now carries Metadata-protocol: narrowed and says it supersedes 24720's "Nothing new is refused in 18.x" sentence and its 19.0 plan.
    • @objectstack/lint stays minor with the BREAKING banner and ADR-0087 not-required (no-migration-prescription).
    • check-changeset-no-major --event passes the level axis.

Seat re-read of the check-runs at landing: all 48 runs on bf78cec3f7 have completed: 39 succeeded, the rest were skipped, and none is red.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 22, 2026 05:48
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 22, 2026 05:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit c5915b2 Oct 22, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24957-validation-rule-parent-error branch October 22, 2026 06:15
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/m tests tooling

Projects

None yet

2 participants