Skip to content

fix(objectql): refusals, log lines and metadata text state each decision in words instead of a tracker number (stage 3) - #20848

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20513-stage3-objectql
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20513-stage3-objectql

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20513
Clause-②: no

Stage 3 of 5 of this lane (objectql), under the maintainer's A / A ruling on the card. The card stays open for stages 4-5, so this PR carries no closing keyword. Text only: no error code, field name, HTTP status, export or control flow moves. Every changed source line is a string-literal line, except two single-line log calls (port.warn?.(...) in the dangling-reference audit and ctx.logger.info(...) in plugin.ts) whose only changed token is the literal; stripping every string literal from the removed and the added lines leaves the same code skeleton apart from + joins for re-wrapped literals.

What this does

The query engine's refusals, metadata text and log lines sent the reader to a tracker number for the reason behind them. Each rewritten string now says that reason in words (form D, as the migration-entry rewrite and stages 1 and 2 applied it). Where the sentence already stated what was decided, only the citation goes. Where it did not, the decision is added in words:

Where Cited The sentence now says
engine.ts bulk update and bulk delete row-scoping refusals (thrown, [Security]) 2982 The missing seed is the AST seeded before the middleware chain, the one RLS and sharing compose their row-scoping onto, so a bulk write reaches only the rows this caller may edit.
hook-target-rebind-errors.ts by-id REBOUND branch (thrown) 6752 delete() used to honour a rebind by re-resolving the new target; that is retired too, because a handler that silently redirects which row gets deleted is a trap.
hook-target-rebind-errors.ts unscoped-multi branch (thrown) 9719, 9974 The whole-operation dispatch goes to a shape guard registered with dispatchUnscopedMultiWrite, on update and delete alike.
engine.ts non-atomic cascade warning (warn) 7413 The cascade runs unwrapped, as every cascade did before a single-datasource cascade was made one transaction.
engine.ts system-ledger write inside a transaction (debug) 5351 The ADR-0057 section 3.6 system ledger is the one class carved out of the cross-datasource write refusal.
integrity/dangling-reference-audit.ts summary (warn) 4551 Findings are reported, never rewritten: a system-context write is exempt from the write-time reference check, so this audit is where such a reference surfaces.
registry.ts legacy apiMethods warning (warn) 3543 The authorable values are now the six primitives only, because every other operation is derived from them or retired.
validation/rule-validator.ts predicate and when-predicate evaluation failures (warn) 4649 Write rejected: a rule that cannot be evaluated fails closed, it is never skipped.
everything else: the unknown-option, filter-array (two), credential-aggregation, HAVING-operator, empty-hook-target, hook-target CLEARED, strict read-only (two clauses) and system-write organization refusals; the lifecycle retention_overrides setting description and the search companion field description (metadata text); the eight ADR-0104 value-shape gate lines; the delegated protocol-assembly line; the read-only and runtime-owned strip warnings (eight clauses) 4371, 5158, 3171, 7922, 4286, 4001, 5574, 5846, 3407, 3493, 8844, 5195, 2486, 3617, 3438, 4797, 4769, 2462, 5126, 5503, 2948 The sentence already said what was decided; only the citation goes. Where an ADR stood beside the number (ADR-0100, ADR-0078, ADR-0104, ADR-0076 Step 2), the ADR stays.

Each claim was checked against today's code, not only against the cited card: the bulk-write AST is seeded before executeWithMiddleware on both verbs; planCascadeAtomicity returns 'atomic' for a single-datasource cascade and delete() then runs it inside transaction(); a cross-datasource business write throws CrossDatasourceTransactionWriteError while a system-ledger object runs outside the transaction; the write-path reference check is non-system writes only; LEGACY_API_METHODS holds the eight derived or retired values; dispatchUnscopedMultiWrite is a registration option valid on beforeUpdate / beforeDelete; an unevaluable rule returns unevaluableRuleError. All 31 cited cards read closed as completed.

One string held for a later stage

engine.ts's findOne no-predicate refusal keeps its citation (4419, 1 occurrence). @objectstack/metadata-core's engineFindOnePredicateRefusalMessage is documented as byte-identical to it, and this package's engine-findone-predicate.test.ts compares the two (strict equality and toThrow). Moving the metadata-core copy is another package's ledger row, which this stage may not touch. A one-off ablation proves the hold: dropping the citation in engine.ts alone turns 9 of 24 tests in that file red. Stage 5 (the other seven packages, metadata-core among them) rewrites the pair together, and its ledger diff will move this objectql row too.

Order inside the stage

All 39 rewritten literals (42 occurrences) fit one PR, under the stop line, so the stage lands whole in three ordered commits, each recomputing the ledger so every commit is green on check:doc-authoring:

  1. 3432973ff author-visible text: 16 literals (19 occurrences), the thrown refusals and the two metadata texts, plus the one byte-exact re-pin;
  2. bccd37a25 log lines: 23 literals (23 occurrences), plus the two re-pinned tests;
  3. 26848968c the changeset.

objectql has no ledgered src/-shipped test string: the census's 3 test-facing strings sit in engine-data-events.bench.ts, which the ledger excludes.

Pins re-pinned: 6 assertion lines in 3 test files

  • integrity/dangling-reference-audit.test.ts 377, 498, 715, 959 found the summary warning by the number. They now find it by "reported, never rewritten".
  • registry.test.ts 1201 asserted the number in the legacy apiMethods warning. It now asserts "derived from them or retired".
  • engine-dropped-fields-primary-key.test.ts 318 is the byte-exact pin on the strict read-only refusal; it moves with the text and stays byte-exact.

A one-off mutation proves each moved pin can fail, run on the committed head 26848968cc with scripts/ablation-replace.mjs in wrap mode (anchor hit once, disk-verified) under a shell trap that restores each file from HEAD. The tests import src directly, so no build leg applies. "reported, never rewritten" to "reported, never repaired": 4 failed / 35 passed. "derived from them or retired" to "derived from them or dropped": 1 failed / 100 passed. "onFieldsDropped instead." to "onFieldsDropped instead!": 1 failed / 15 passed. After each leg the blob equals HEAD and git diff HEAD is empty; after the run git status --porcelain has 0 lines.

No consumer outside the package pins a changed string. Every fragment of every removed source line was searched in all test files of the repository: the hits are comments, fragments the new text keeps, the package's own tests above, and plugin-security's auto-org-admin-grant.test.ts, whose test double keeps its own copy of the unknown-option sentence and is never compared with the engine's (see Acceptance notes).

Ledger burn-down

scripts/doc-authoring-prose-id.baseline.json was regenerated with --census-ledger. Only objectql rows move; no other package's row changes.

File Before After
src/engine.ts 18 1 (4419, held)
src/validation/rule-validator.ts 10 0
src/hook-target-rebind-errors.ts 5 0
src/readonly-strict-errors.ts 2 0
src/tenancy/system-write-organization.ts 1 0
src/search-companion.ts 1 0
src/registry.ts 1 0
src/plugin.ts 1 0
src/lifecycle/lifecycle-settings.ts 1 0
src/integrity/dangling-reference-audit.ts 1 0
src/hook-binder.ts 1 0
src/having-filter.ts 1 0
objectql 43 in 12 files 1 in 1 file
whole ledger 811 occurrences, 546 pairs, 215 files 769 occurrences, 514 pairs, 204 files

The census's 38 messages for objectql are the 40 ledgered literals minus the two metadata texts it bucketed separately; the 40 literals carry 43 occurrences because three of them cite two numbers each.

Verification (head 26848968cc)

  • Build: turbo run build over @objectstack/objectql and its closure, 14/14; then the whole workspace (./packages/*, ./packages/*/*), 71/71; then @objectstack/objectql directly after the ablation run touched its sources. The new sentences are in dist/index.js, and none of the removed citations remains in a string there (the matches left are comments).
  • @objectstack/objectql tests (vitest run --project local --maxWorkers=2, three shards): 115 + 115 + 114 files passed, 2253 + 2001 + 2515 tests passed, 0 failed.
  • @objectstack/objectql typecheck: exit 0 (tsc --noEmit, the scripts config, and check:test-typecheck holding its ledger).
  • node scripts/pm/dispatch-gates.mjs --commands: 76 derived commands, all run, all exit 0. check:dual-build-cjs-loads and check:type-check-debt first answered exit 3 (prerequisite not met) and were re-run after the full build and the direct objectql rebuild; check:dts-closure and check:lean-entry-closure were re-run there too. --ran: 76 derived, 76 run, 0 NOT-MEASURED, 0 UNRUN.
  • check:doc-authoring: sibling-package prose ids hold the baseline, no growth, no burn-down unrecorded (647 pinned sites across 204 files).
  • Narrowed lint: eslint --no-inline-config --format json over the 15 touched .ts files: 15 files, 0 errors, 0 warnings. The resolved parserOptions for engine.ts are ecmaVersion: latest and sourceType: module only, with no project and no projectService, so no type-aware rule can move an untouched file. Repo-wide pnpm lint is CI's.
  • Not measured locally (CI's): the repo vitest project's one file (action-owner-key-single-source.test.ts, untouched by this diff), the Test Core shards, Dogfood, and the workspace type-check lanes.

Acceptance notes

  • Held for stage 5: the findOne refusal pair (objectql engine.ts and metadata-core engine-findone-predicate.ts), as above. Stage 5's file surface needs packages/objectql/src/engine.ts as well.
  • Noted, not filed: plugin-security's auto-org-admin-grant.test.ts carries a test double of the engine's unknown-option refusal with its own copy of the sentence, citation included. It is a test file, outside the ledger and outside the ruling's shipped-src/ scope, and no test compares it with the engine's text, so the two now differ by the citation only. Carrier: none.

Generated by Claude Code

…ecision in words instead of a tracker number (stage 3, part 1)

The engine's unknown-option and filter-array refusals, the two bulk-write
row-scoping refusals, the credential-aggregation refusal, the HAVING
operator refusal, the empty hook target, the hook-target rebind refusal,
the strict read-only refusal, the system-write organization refusal, the
lifecycle retention-override setting description and the search companion
field description no longer cite tracker numbers. Where the sentence did
not already say what was decided, it now does. The findOne refusal keeps
its citation: metadata-core's byte-identical twin is compared in this
package's tests, and that twin is a later stage's row.

Text only. The prose-id ledger is recomputed with --census-ledger.

Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY
Co-authored-by: Claude <noreply@anthropic.com>
…acker number (stage 3, part 2)

The ADR-0104 value-shape gate lines, the non-atomic cascade warning, the
system-ledger transaction carve-out, the dangling-reference audit summary,
the delegated protocol assembly line, the legacy apiMethods warning, the
read-only and runtime-owned strip warnings and the two unevaluable-rule
warnings no longer cite tracker numbers. Where the sentence did not already
say what was decided, it now does. Two tests that pinned a number now pin
the sentence.

Text only. The prose-id ledger is recomputed with --census-ledger.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/data-modeling/objects.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/kernel/services.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/permissions/authentication.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/index.mdx (via ObjectQLPlugin (symbol, a top-level class))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via ObjectQLPlugin (symbol, a top-level class))
  • content/docs/releases/v17/17-1.mdx (via ObjectQLPlugin (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 70 pages)
  • 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 — 17 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 30839063b56181e396ddd3065cab47e6d884dc66 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 30839063b56181e396ddd3065cab47e6d884dc66

⚠️ 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 30839063b56181e396ddd3065cab47e6d884dc66 → 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 26848968cc5bd863ddcc6d9acf106afefee523d3
Local-runs: none

Read-only, at tier, adversarial to the dispatch. Inputs: card #20513 (body and all 21 comments — the census 5900801368, the decision request 5902063603, the ruling 5902360492 「20513 A」 A / A, the lane checklist 5902678544, the stage-3 claim 5909669496, the stage-3 os-dev-report 5910562542), PR #20848 (body, 17-file list, the net diff origin/main...26848968cc at merge base b28054654), PR #20795 at df67985b0 with its record 5906164285 and PR #20830 at fe463b45a with its record 5908808617 as the prior applications of the same ruling, the head's check-runs, the 31 cited cards (all readable; #6752's ruling read from its comment 5229992683), the six ADRs the rewritten strings keep, and the head's own source through git show / git grep. Nothing built, run or re-run; no worktree, no checkout. The PR head was confirmed as 26848968cc5bd863ddcc6d9acf106afefee523d3 before the read and had not moved.

Check-runs on 26848968cc, read once for this record at 2026-09-30T12:00Z and not polled: 31 check-runs — 16 completed / success (Build Core, Check Changeset, Check PR Size, Check Documentation Links, Flag docs affected by code changes, Governed Surface Queue Guard, Part-of PR must not also close its card, The card this PR closes must claim this branch, the two claim / single-writer guards, Dogfood Regression Gate (3/3), Dogfood Verify CLI, Type Check · source gates, Type Check · debt ledger, Auto Label, filter), 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke — path-filtered), 12 in_progress (Test Core 1-6, Dogfood Regression Gate 1/3 and 2/3, Lint & Repo Gates, Temporal Conformance (live PG + MySQL), Type Check · workspace, Type Check · consumer gates), 0 failure. Their conclusions are the gate verdicts: the runs still going carry check:doc-authoring (Lint & Repo Gates), the six re-pinned assertions and the findOne parity test (Test Core), and the lanes the dev did not measure. This record judges the contract on the diff, the cards and the head's source; a failure among those runs, if one lands, is a new fact for the adopting session, not one this record has read.

① Derived judgments

Scope against the ruling and the claim — right. The ruling orders stages per package, all three categories, form D, --census-ledger in the same PR, author-visible text first and log lines last; the claim scopes stage 3 to packages/objectql/src/**, the pins, the ledger and one patch changeset, and forbids wire code / field / status changes, comment or docblock edits (#20595) and other packages' ledger rows. The 17-file list is exactly that surface: 12 source files, 3 test files, the ledger, the changeset. No governed path. "Author-visible first, log lines last" is honoured as commit order inside the stage (3432973ff the refusals and the two metadata texts, bccd37a25 the log lines, 26848968c the changeset); 39 literals sit under the stop line, so nothing was left to split.

Text only — right, checked hunk by hunk. Every removed and added line in the 12 source files is a string-literal line, with two exceptions the dev names and I confirm: dangling-reference-audit.ts 736 (port.warn?.(…)) and plugin.ts 498 (ctx.logger.info(…)) are single-line calls whose only changed token is the literal — the call, its second argument and its punctuation are unchanged. The two rejoined boundaries in rule-validator.ts (2568-2569 and 2631-2632, the preserveAudit clauses) move a comma from one template piece to the previous one and nothing else; the + skeleton is the same. No code, status, export, field name, branch or call moves: HookTargetRebindError's code, unevaluableRuleError's code: 'rule_violation', invalidFilterError, ReadonlyFieldRejectedError, every logger.* call shape and every throw new Error( are context lines. The three test-file changes are the 6 pin lines only. Comments and docblocks are untouched; on the head the only tracker id left inside a string in the 12 files is #4419 at engine.ts 11390, the held one.

Accept-set and public surface — nothing moves — right. No schema, filter grammar, route, wire code, HTTP status, field name, default or export changes; a caller sees different prose in the same envelopes. I searched the whole head for every removed fragment: outside packages/objectql the hits are comments (packages/cli/src/utils/schema-migrate.teardown.integration.test.ts 12 quotes the old dangling line in a docblock, with no assertion on it; service-storage's attachment-lifecycle.ts 49 and the engine's own docblocks carry the bracketed [#9719, both write verbs since #9974] tag), two drivers' own filter-array refusals that keep their own (#5158) (other packages' ledger rows), and the plugin-security test double (③ item 4). No test outside the package asserts any of the 30 removed numbers as a string. No page under content/docs or docs/ quotes any rewritten sentence verbatim (the stage-2 class of finding does not recur here).

Ledger diff — right, exact. One hunk, the packages/objectql/src/** block only: 11 file rows leave and engine.ts shrinks from 12 pairs / 18 occurrences to #4419: 1. I reconciled every removed occurrence against a citation deleted in the source diff: engine.ts 17 (#2982 ×2 the two bulk-write refusals, #5158 ×2, #3617 ×2, #3438 ×2, #4797 ×2, #4769 ×2, #4371, #3171 and #7922 the one credential refusal, #7413, #5351); rule-validator.ts 10 (#3407 ×2, #3493 ×2, #4649 ×2, #5126 ×2, #2948, #5503); hook-target-rebind-errors.ts 5 (#5574, #5846, #6752, #9719, #9974); readonly-strict-errors.ts 2; and one each in having-filter.ts, hook-binder.ts, dangling-reference-audit.ts, lifecycle-settings.ts, plugin.ts, registry.ts, search-companion.ts, system-write-organization.ts. 42 occurrences, 32 (file, id) pairs, 11 files — 43 to 1, and the whole-ledger deltas 811 to 769, 546 to 514, 215 to 204 follow. check:doc-authoring inside Lint & Repo Gates is the mechanical confirmation, pending at read time.

Form D, string by string — right. All 31 cited cards read closed / completed; no cited decision has been reversed. The 10 literals that gained words, each judged against the card and the head's code:

Where only the citation goes (29 literals): each sentence already carried the decision and is otherwise unchanged, and each card's title matches its sentence — the unknown-option refusal (4371, "silently drops sort/select/skip/populate"); the two filter-array refusals (5158, the array dialect settled as input-only sugar lowered before any driver); the credential-aggregation refusal (3171 / 7922); the HAVING unknown-operator refusal (4286); the empty hook target (4001); the hook-target CLEARED branch (5574 / 5846); the strict read-only refusal tail (3407) and the preserveAudit clause (3493) in both readonly-strict-errors.ts and rule-validator.ts; the system-write organization refusal (8844, the NULL-organization autonumber fork); the strip warnings (2948, 5126, 5503); the eight ADR-0104 gate lines (3617, 3438, 4797, 4769); the delegated protocol-assembly line (2462); the two metadata texts (5195, 2486).

Every kept ADR says what its sentence claims — right. ADR-0100's non-goals paragraph names the aggregate masking gap and says "the correct fix is to reject aggregations that reference a credential field" (the credential refusal). ADR-0078 is the no-silently-inert-declaration decision the HAVING refusal applies ("refused rather than ignored"). ADR-0104 is the value-shape contract with the per-deployment verification flags and os migrate remedies the eight gate lines name. ADR-0058 carries Addendum II (2026-08, #5574 ruling B) with "the dispatch ladder must be resolved BEFORE the before phase" and Amendment II.1 settling it. ADR-0057 has §3.2 (declare, incl. the #5195 retention-floor amendment) and §3.6 (physically separate telemetry). ADR-0119 D1 (one driver's connection, no two-phase commit) is unchanged context in the cascade warning. ADR-0076 names "Step 2" only as the cross-repo window (lines 111 and 130); the step itself is defined on #2462 ("make the @objectstack/objectql package itself protocol-free … MetadataProtocolPlugin"), and plugin.ts's own comment reads "ADR-0076 Step 2 PR-C" — the pointer resolves, through the card.

Pins — right, 6 lines in 3 files. dangling-reference-audit.test.ts 377, 498, 715, 959 now find the summary by "reported, never rewritten", which is contiguous in the head's new literal; registry.test.ts 1201 asserts "derived from them or retired", present in the head's chain; engine-dropped-fields-primary-key.test.ts 318 stays the byte-exact pin on the update-branch strict refusal — its tail "strictReadonlyWrites and pass options.onFieldsDropped instead." equals readonly-strict-errors.ts's tail on the head. The dev's mutation legs (4 / 35, 1 / 100, 1 / 15) are its own measurement; what this record verified is that each pinned phrase is on the head and that no other test in the repository pins a changed string.

Review faces — right. The changeset (below) reads as a CHANGELOG entry; every family it names is in the diff and every sentence it summarises matches the string. The PR body's table, counts and commit order match the diff (16 + 23 = 39 literals, 19 + 23 = 42 occurrences; eight ADR-0104 lines; eight strip clauses), it carries Clause-②: no at line start and no closing keyword ("Part of #20513"). The two metadata texts an administrator reads: the retention_overrides description ("An override BELOW a retention floor a consumer registered … is rejected at sweep time and logged at error — the declared window keeps running") is live — registerRetentionFloor documents "REJECTED … rather than clamped" and the violation is logged through logger.error (about 1221); the search companion field description ("Maintained by plugin-pinyin-search; never hand-edited") names a real maintainer (packages/plugins/plugin-pinyin-search/src/companion-projection.ts).

One wording note, not a FAIL item. packages/cli/src/utils/schema-migrate.teardown.integration.test.ts 12 reproduces the old summary line, citation included, inside a docblock that narrates the #4747 repro; it is a comment in a test file, outside the ruling's scope and outside what #20595 lets this lane touch. Carrier: whoever next edits that file.

② Semver level

.changeset/20513-objectql-runtime-strings-state-the-decision.md: '@objectstack/objectql': patch — right. The package is released (17.5.0, not private); the diff publishes changed prose in that one package and nothing else — no key, export, envelope code, status or default moves — so this is a fix-class patch, never skip-changeset. The body is CHANGELOG-fit: it names each message family, says which sentences gained words and what they now say, explains the held findOne refusal and its metadata-core twin, and closes "Text only: no error code, field name, status or behaviour changes". No model identifier in the changeset, the three commit trailers (Claude-Session plus a model-free Co-authored-by) or the PR body (session-URL footer) — the same shape stages 1 and 2 landed with. Check Changeset is success.

Clause-②: no — right. Nothing an author can write is widened or narrowed; the PR body carries Clause-②: no at line start and the changeset repeats it.

③ Boundary flags

Dev flags (report 5910562542), each answered:

  1. Census reconciliation (40 literals / 43 occurrences / 38 messages) — holds, recomputed independently. The census (taken at 36d043be17) lists objectql at 40 messages / 44 id occurrences plus 2 metadata texts and 3 test-facing strings. PR fix(objectql,service-automation,runtime): the card's named warnings and endpoint hints state each decision in words instead of a tracker number #20738 then landed the card's two named objectql messages (engine.ts 7079 with #4639, #4626 and 7173 with #4639): 40 − 2 = 38 messages, 44 − 3 = 41 occurrences — the ruling's "objectql 38". Adding the two metadata texts gives 40 literals / 43 occurrences, which is the ledger at the merge base; three literals carry two ids (#3171, #7922; #5574, #5846; #9719, #9974), so 40 literals are 43 occurrences. The 3 test-facing strings sit in engine-data-events.bench.ts, which check-doc-authoring.mjs excludes by its .(test|spec|bench) pattern, so no ledgered src/-shipped test string exists here. Exact.
  2. The held findOne refusal (#4419) — accepted, and verified. engine.ts 11387-11393 and metadata-core's engineFindOnePredicateRefusalMessage (126-134) are byte-identical, citation included, and engine-findone-predicate.test.ts compares them by strict equality (about 95) and toThrow(engineFindOnePredicateRefusalMessage(OBJECT)) (about 123). The metadata-core row is another package's ledger row, which the claim forbids; the dev's ablation (9 of 24 red on dropping the citation alone) is its own measurement, and the parity pin is reason enough. The row stays in the ledger as #4419: 1 and the PR body names stage 5 as carrier, with packages/objectql/src/engine.ts added to that stage's surface. Held, not lost.
  3. 6 re-pinned assertion lines and their mutation legs — verified in ①.
  4. plugin-security's test double — accepted as stated. auto-org-admin-grant.test.ts 70-75 keeps its own copy of the unknown-option sentence with (#4371); it is a .test.ts file (outside the ledger and the ruling's shipped-src/ scope), no test compares it with the engine's text, and no test in the repository asserts #4371 as a string. The two now differ by the citation only. Carrier: none, as the dev says.
  5. NOT MEASURED locally (Test Core shards, Dogfood, the workspace type-check lanes) — CI's; at read time Dogfood (3/3), Dogfood Verify CLI, Build Core and the source / debt-ledger type-check lanes are success, the rest in_progress, none failure.
  6. Hygiene deviation (a fleet credential variable echoed into the dev's own tool output) — escalated to the adopting seat, and bounded here. The dev report, the PR body, the three commit messages and the net diff carry zero credential-shaped tokens (checked for the GitHub token and PAT prefixes, bearer strings and cloud key shapes). Nothing reached a GitHub surface. Whether the echoed value is rotated is the seat's call, not this record's; carrier: session_01DEvba2nBuD4tWzfq8r8NFY.
  7. Attribution deviation — accepted. The commits carry the model-free trailer pair and the PR body the session-URL footer, as stages 1 and 2 did under AGENTS.md's rule.

open_questions: none in the stage-3 report; none raised here. The card's lane children (#20749, #20751, #20753, Blocked-by: #20513) are unaffected by this stage; the ledger's other rows are untouched.

Implemented-by: claude/issue-20513-stage3-objectql
Reviewed-by: session_01DEvba2nBuD4tWzfq8r8NFY

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 12:16
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit a94f3ba Sep 30, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20513-stage3-objectql branch September 30, 2026 12:51
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

Development

Successfully merging this pull request may close these issues.

2 participants