Skip to content

fix(lint)!: os build and the page save door refuse a page component props CEL slot over the platform parse bounds (#25002) - #25088

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-25002-page-predicate-budget-error
Oct 21, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-25002-page-predicate-budget-error

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #25002
Clause-②: no (narrowing: a page component props CEL slot over the platform parse bounds is refused at os build and the page door; it ships now under the amended 「甲」, #6015 6303865025)

What changes

page-predicate-over-budget (validatePagePredicateBounds, packages/lint/src/validate-page-predicate-bounds.ts) reports at error instead of warning. In the same edit, its entry in packages/lint/src/authoring-rules.ts moves from the advisory tier to the gating tier. A page carrying a CEL slot inside a component's properties bag whose source overruns DEFAULT_LIMITS (maxAstNodes 256, maxDepth, maxListElements) now fails os validate, os build and os lint. An active write of that page, or the promotion of its draft, is refused at the page save door. This is the refusal half of #24953, re-timed to 18.x by triage (6305866852) under the amended 「甲」 (#6015 6303865025).

  • The message drops "A future major release refuses it." The docblock's "Why a warning" section becomes "Why an error" and cites the amended ruling. The registry entry's comment says why the tier is gating.
  • PagePredicateBoundsFinding.severity is typed 'error'.
  • No runtime or door code moves: the door already splits findings by severity, error to the refusal and the rest to advisories.

The door's code, measured

The card says 422 VALIDATION_ERROR. The page save door actually answers 422 INVALID_METADATA, the author-time refusal envelope every runtime-gate refusal uses. The new pin asserts { code: 'INVALID_METADATA', status: 422 }, the same as the #23388 and #24491 page blocks next to it. The changeset says INVALID_METADATA.

Tests

Evidence (run on head 53ae2b07d2, after pnpm --filter '@objectstack/lint...' build and pnpm --filter '@objectstack/metadata-protocol^...' build):

  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-page-predicate-bounds.test.ts src/runtime-gate.page-visibility-predicate-writes.test.ts src/authoring-rule-wiring.test.ts → Test Files 3 passed (3), Tests 59 passed (59).
  • pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 src/protocol.runtime-authoring-gate.test.ts → Test Files 1 passed (1), Tests 180 passed (180).
  • pnpm --filter @objectstack/lint typecheck (tsc plus check:test-typecheck) → exit 0.
  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 (the whole package) → Test Files 153 passed (153), Tests 7056 passed (7056). pnpm --filter @objectstack/metadata-protocol typecheck → exit 0 (in the same && chain, lock VERDICT command-exit 0).

Ablation (one-shot, nothing left in the tree)

Committed first, then mutated through scripts/ablation-replace.mjs (anchor severity: 'error', above rule: PAGE_PREDICATE_OVER_BUDGET,, hit x1 -> x0, blob e4a15923c57d -> 51d908070c3a) to the pre-change severity, spelled ['warning', 'ablation25002'][0] as 'error' so the marker survives the bundle. pnpm --filter @objectstack/lint build, then scripts/ablation-dist-preflight.mjs @objectstack/lint ablation25002 → marker present in 4 built files.

  • Mutant, lint: validate-page-predicate-bounds.test.ts → Tests 5 failed | 13 passed (18) (the error finding, os validate / os build / os lint, the door's errors channel).
  • Mutant, door: protocol.runtime-authoring-gate.test.ts -t '#25002' → Tests 3 failed | 1 passed | 176 skipped ((a) refusal, (a) draft promotion, (b) parity fail; (c) the within-bounds control stays green, as it must).
  • Restore: git checkout HEAD -- PATH, git diff HEAD empty, hash e4a15923c57d equals the HEAD blob; rebuilt; ablation-dist-preflight --absent → marker absent from all 20 built files, tree clean. Restored run: lint file 18 passed (18), door file 180 passed (180).

Producers, measured before crossing

0 in-repo producers carry such a slot, so this PR fixes none:

  • Page modules: every *.page.ts and pages/*.ts module under examples/ and packages/ (30 files, 65 page values), loaded with tsx and judged by the built validatePagePredicateBounds, gives 0 findings.
  • Composed stacks: the app-crm, app-multi-package and app-todo stacks also give 0. The app-showcase config did not load here, because an unbuilt @objectstack/connector-mcp was missing; its pages are covered by the page-module pass.
  • Literal scan: every tracked .ts/.tsx/.mts/.js/.mjs/.json/.yaml file under examples/, packages/ (dogfood and the package pages included), skills/, apps/ and content/docs/ was scanned (9,919 files). Each string literal or +-chain of 120 or more characters (13,840) was judged by validateExpression's bounds verdict, and 0 are cel-too-large.
  • HotCRM: the same literal scan over objectstack-ai/hotcrm at c8a7a70 (611 files) finds 0.
  • Positive controls: a planted 78-term predicate is found by both instruments.
  • Residual: a predicate assembled at run time (template substitution, .join) is outside the literal scan. The page-module pass covers it for the in-repo pages.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 66 commands at 53ae2b07d2, and all 66 were run. 63 exit 0, including check-adr-0087-registration --base origin/main (it reads the not-required (no-migration-prescription) marker), check-changeset-no-major, check-empty-changeset, check:changeset-gate-self-tests, check:nul-bytes, check:cross-package-test-inputs and check:test-source-alias. Three are NOT MEASURED, each exiting 3 on its own prerequisite and not on a finding:

  • check:dual-build-cjs-loads: needs a whole-workspace dist/.
  • check:lean-entry-closure: needs a built @objectstack/objectql.
  • check:type-check-debt: its closure build hit the shared verify lock's queue budget.

CI runs all three. dispatch-gates --ran with every exit code recorded: 66 derived, 63 run, 3 NOT-MEASURED, 0 UNRUN.

Acceptance notes

  • The pending changeset .changeset/24953-page-predicate-bounds-warning.md (from feat(lint): a page component props CEL slot over the platform parse bounds warns at validate, build and the page door #24999, not yet released) still says "A future major release refuses it." If both ship in one release, the two CHANGELOG entries for @objectstack/lint disagree. This PR's changeset says it supersedes the warning-only behaviour. The other file is outside this claim's file surface, so it is not edited here. The seat can amend that one sentence in a follow-up if wanted.
  • The card's VALIDATION_ERROR is not what the door answers. See "The door's code, measured" above.
  • No generated docs reference this rule, and the CLI's rule count (Running author-time rules (50)) does not move. Nothing was regenerated.

Release

@objectstack/lint minor, BREAKING (FROM a warning with the page saved, TO an error with the page refused). The one-line fix: shrink the predicate, or precompute it into a formula or roll-up field. ADR-0087: not-required (no-migration-prescription), following the shape of bb4f5cc. content/docs/releases/ is untouched.


Generated by Claude Code

…lot over the parse bounds; changeset

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 2 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see
  • 2 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 a39389eb51738cc91fa450a1f3035d221ec65642 → packageMentionDocs.

Which tree this was computed on

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

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

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs a39389eb51738cc91fa450a1f3035d221ec65642 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat review — PASS · domain:spec seat 3

Head: 53ae2b07d2f2be712aff6188db39f23a1e46081e. Card #25002, claim 6309820350, dev report 6311971017.

  • Contract review: none owed. The diff touches only packages/lint/src/**, a metadata-protocol test and .changeset/25002-*.md: nothing under packages/spec/src/** and no governed path (check-governed-merges: NOT governed). It declares Clause-②: no.
  • Diff read:
    • validatePagePredicateBounds now reports severity: 'error', and its exported PagePredicateBoundsFinding.severity literal moves with it. That is part of the declared narrowing.
    • The authoring-rules.ts entry moves from advisory to gating, so the finding fails os validate / os build / os lint and the page door refuses the next save.
    • The message drops "A future major release refuses it.", and the hint is unchanged.
    • The door answers 422 INVALID_METADATA (measured; the card's VALIDATION_ERROR was wrong).
    • The ablation turns the new pins red, and a within-bounds control saves.
    • 0 in-repo producers carry such a slot.
  • Changeset: @objectstack/lint minor with the BREAKING FROM/TO section, the claim's Clause-② line, and ADR-0087 not-required (no-migration-prescription). That disposition holds, because a predicate cannot be shrunk mechanically.
  • Boundary: the pending .changeset/24953-page-predicate-bounds-warning.md still says "A future major release refuses it." The correction is held with the seat's other release-note corrections for the maintainer.

Seat re-read of the check-runs at landing: all 41 runs on 53ae2b07d2 have completed: 34 succeeded, the rest were skipped, and none is red.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 21, 2026 09:24
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 21, 2026 09:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 31f0f27 Oct 21, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25002-page-predicate-budget-error branch October 21, 2026 09:59
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