Skip to content

fix(core,drivers,formula,metadata,objectql,platform-objects): refusals, field help and log lines state each decision in words instead of a tracker number (stage 5) - #20924

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20513-stage5-seven-packages
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20513-stage5-seven-packages

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20513
Clause-②: no

Stage 5 of this lane (the other seven packages, plus the objectql findOne refusal stage 3 held), under the maintainer's A / A ruling on the card: the author-visible text and the log lines. The stage is over the ~80-string stop line (94 ledgered literals plus 2 URL-form messages), so it lands in two parts: this PR carries every refusal, prescription, metadata text and log line, and the 28 src/-shipped test strings are reported as stage 5b below. #20513 remains open for stage 5b, so this PR carries no closing keyword. Text only: no error code, field name, HTTP status, export or control flow moves.

What this does

Refusals, prescriptions, field help and log lines in core, driver-memory, driver-mongodb, formula, metadata, metadata-core and platform-objects (and the one held objectql string) sent the reader to a tracker number for the reason behind them. Each rewritten string now says the reason in words (form D, as the migration-entry rewrite and stages 1 to 4 applied it). Where the sentence already stated the decision, only the citation goes; where an ADR stood beside the number, the ADR stays. Where the sentence did not carry the decision, it now does:

Where Cited The sentence now says
core IMetadataService.register refusals (thrown, 400) 7378 rows 3 and 1 "... so the contract refuses loudly and never coerces a value into storability" / "... so the contract refuses loudly and names the mismatch"
core init-order fault suffix on a getService miss during init() (thrown) 4131 "a composition/ordering fault; registration order is not a contract."
core admin standing surface ledger reasons 8613, 11663 "active, which this resolver honours"; "RECLASSIFIED from reads-only when the config anchor landed"
driver-mongodb retired aggregate function (thrown, 400) 6188, 5907 "was REMOVED from @objectstack/spec AggregationFunction (ADR-0049 enforce-or-remove: no SQL backend compiled it)"
driver-mongodb undeclared aggregate function (thrown, 400) 5907, 12818 "before this refusal, this builder answered any unrecognised name with a $sum of that column ..."
metadata route ledger note (HMR POST route) 12140 "CHANGED when the development-only mount gate landed"
metadata history-seq failure (error log) 4825 "numbering it from 1 (what this code once did) would collide ..."
driver-memory / driver-mongodb multi-tenancy refusals (MEMORY_MULTI_TENANT_UNSUPPORTED, MONGODB_MULTI_TENANT_UNSUPPORTED) 6915, 16589, 3724 as tracker URLs The trailing Tracking: line and its URL constants go; the consequence sentence above it already says the driver refuses rather than run (or answer) unisolated.

Every other rewritten literal loses only its citation.

The findOne pair. objectql engine.ts requireFindOnePredicate and metadata-core engineFindOnePredicateRefusalMessage are byte twins compared by objectql's engine-findone-predicate.test.ts, so both drop the citation in one commit (0a25891e6d) and the parity pin stays byte-exact.

Metadata text. The sys_account token help, the sys_email headers help and the SCIM credential store's protection reason drop their numbers in the source. The en bundle is regenerated with node scripts/check-i18n-bundles.mjs --write --filter=platform-objects: the generator rewrote the four en leaves from the source and kept every translated-locale value verbatim (merge mode). So the number inside the three translated sys_account token helps (es-ES, ja-JP, zh-CN) leaves only by editing those values, which AGENTS.md permits for translated-locale values (keys and structure untouched). None of those leaves has a source-hash companion entry (they are translations, not source copies), so nothing falls back; check:i18n and check:i18n-stale-fill are green.

The URL-form ids (bounded in-place fix). The claim names driver-memory's URL-form id (the per-call seam's dead card link). The same Tracking: line carries a second URL on both seams, and driver-mongodb's twin guard carries the same line. All three go together: same defect class as this card, a mechanical fix, files inside this stage's surface, and no new verification surface (each package's own tests, re-pinned). These are the only changes that move code shape (two module constants and one ternary that only composed the removed line).

Ledger (scripts/doc-authoring-prose-id.baseline.json), per file, occurrences before → after

Recomputed with node scripts/check-doc-authoring.mjs --census-ledger in each text commit. Only these packages' rows move; every other file's row is identical (0 moved). Measured at merge base 4d0b9cd542 (the ledger is byte-identical at cb4c31dd52, where the work started, and at 599cc8c5f).

File Before After
core/src/health-monitor.ts 3 0
core/src/metadata-service-contract.ts 2 0
core/src/plugin-order.ts 3 0
core/src/plugin-registration.ts 1 0
core/src/security/admin-standing-surface.ts 3 0
core/src/security/authz-cache-posture.ts 1 0
drivers/driver-memory/src/filter-refusal.ts 12 0
drivers/driver-mongodb/src/mongodb-aggregation.ts 7 0
drivers/driver-mongodb/src/mongodb-filter.ts 5 0
drivers/driver-mongodb/src/test-mongod.ts 3 3 (stage 5b)
formula/src/matches-filter.ts 1 0
metadata-core/src/contract-suite.ts 1 1 (stage 5b)
metadata-core/src/engine-delete-dispatch.ts 4 4 (stage 5b)
metadata-core/src/engine-dispatch-unhonoured-predicate.ts 1 0
metadata-core/src/engine-findone-predicate.ts 4 3 (stage 5b)
metadata-core/src/engine-update-dispatch.ts 20 18 (stage 5b)
metadata-core/src/object-schema-fls-contract.ts 3 3 (stage 5b)
metadata/src/endpoint-matcher.ts 1 0
metadata/src/loaders/database-loader.ts 1 0
metadata/src/metadata-manager.ts 1 0
metadata/src/metadata-route-ledger.ts 2 0
metadata/src/plugin.ts 1 0
objectql/src/engine.ts 1 0
platform-objects/src/apps/translations/en.objects.generated.ts 4 0
platform-objects/src/apps/translations/es-ES.objects.generated.ts 3 0
platform-objects/src/apps/translations/ja-JP.objects.generated.ts 3 0
platform-objects/src/apps/translations/zh-CN.objects.generated.ts 3 0
platform-objects/src/audit/sys-email.object.ts 1 0
platform-objects/src/identity/sys-account.object.ts 3 0
platform-objects/src/identity/sys-scim-connection-credential.object.ts 1 0
platform-objects/src/plugin.ts 2 0
This stage 101 in 31 files 32 in 6 files

Whole ledger: files 200 → 175, pairs 485 → 441, occurrences 722 → 653. Gate census sites 615 → 549; every other package's (file, ids) sequence is identical before and after.

Stage 5b (the rest of this stage, not in this PR)

The src/-shipped test strings, 28 literals carrying 32 occurrences in 6 files, all case labels or suite titles: metadata-core contract-suite.ts (1 describe title), engine-delete-dispatch.ts (4 case labels), engine-findone-predicate.ts (3 case labels), engine-update-dispatch.ts (15 case labels, 18 occurrences), object-schema-fls-contract.ts (2 case why texts, 3 occurrences), and driver-mongodb test-mongod.ts (3: a skip reason, a warning and a thrown message), whose number two assertions in mongodb-memory-server-gate.test.ts pin. After 5b these packages leave the ledger entirely and the card can close.

Text-only proof

A TypeScript-AST skeleton of each changed source file (every string literal and template text one placeholder, pure-literal + operands collapsed, comments never read) at merge base 4d0b9cd542 and head 1432546ac8: 27 of 29 files SAME; 2 DIFF, the two tenancy guards, whose diff is exactly the removed URL constants, the removed tracking ternary and the removed Tracking: line. Control: the same tool reports DIFF on rest import-template.ts across 6f1f1c1035 (a real code change).

Tests (head 1432546ac8 unless noted)

  • Build: turbo run build over ./packages/* and ./packages/*/*, 71/71, after the merge of origin/main.
  • @objectstack/objectql vitest run --project local, three shards after the merge: 116 + 116 + 115 files, 2260 + 2011 + 2528 tests passed. typecheck exit 0 (test layer: 40 files / 234 errors / 65 signatures held in the debt ledger, unchanged).
  • At 214951a19c (the incoming main commits touch none of these packages): core 61 files / 1795 tests; driver-memory 65 / 1413; driver-mongodb 29 passed, 5 skipped / 661 passed, 172 skipped (the skips need a MongoDB server); formula 42 / 1240; metadata 55 / 828; metadata-core 16 / 298; platform-objects 59 / 948. Each package's typecheck exit 0.
  • Consumer check: plugin-dev dev-plugin-optional-load-failure.test.ts (reads the driver-memory refusal) 10 / 10.
  • Consumer pins: every string fragment of every removed source line was searched across all 4298 test files in the repo; no test pins a fragment that no longer exists, other than the number pins re-pinned here.

Re-pins (8 assertions in 8 test files), each proven able to fail. The number pins in health-monitor.test.ts, authz-cache-posture.test.ts, mongodb-aggregation.test.ts, mongodb-unrecognised-aggregate-function.test.ts, mongodb-tenancy-guard.test.ts, memory-tenancy-guard.test.ts, memory-tenant-scope-refusal.test.ts and objectql engine-update-dispatch.test.ts now assert the sentence that states the decision.

Ablation and mutations, one-off, on the committed head (214951a19c), under a restore trap, through scripts/ablation-replace.mjs:

  • metadata-core face alone (objectql's tests resolve it through dist/): the findOne twin and the id-predicate message each marked, pnpm --filter @objectstack/metadata-core build, ablation-dist-preflight marker present in 2 built files. engine-findone-predicate.test.ts + engine-update-dispatch.test.ts: 10 failed / 83 passed (9 parity cases, 1 re-pin). Restore: blob == HEAD and git diff HEAD empty for both, rebuild, preflight --absent (absent from all 12 built files), rerun 93 / 93.
  • Source-imported re-pins (no build leg): health-monitor 1 failed / 25; authz posture 1 failed / 21; the two MongoDB retired-aggregate files 4 failed / 31; MongoDB tenancy 1 failed / 16; driver-memory boot 1 failed / 19; driver-memory per-call 1 failed / 10. Every restore: blob == HEAD, git diff HEAD empty. After the run: git diff HEAD 0 bytes, git status --porcelain 0 lines.

Gates

node scripts/pm/dispatch-gates.mjs --commands (no paths) at 1432546ac8: 78 commands, 39 paths vs merge base 4d0b9cd542, 309 changed lines. All 78 run one at a time from the worktree, each exit 0; --ran: "78 derived famil(ies) accounted for — 78 run, 0 NOT-MEASURED (a DERIVED zero)". Among them: check:doc-authoring (549 pinned sites across 175 files, no growth, no burn-down unrecorded), check:i18n (9 packages in sync), check:i18n-stale-fill, check:nul-bytes, check:driver-conformance, check:driver-memory-census, check:engine-double-contract, check:type-check-debt. Also run after the merge: pnpm --filter @objectstack/spec check:generated exit 0.

Narrowed lint: eslint --no-inline-config --format json over the 37 changed .ts files: 37 files, 0 errors, 0 warnings (count read from eslint's JSON; none ignored). The resolved parserOptions are ecmaVersion: latest, sourceType: module with no project or projectService, so no type-aware rule runs and this diff cannot move an untouched file's verdict. Repo-wide pnpm lint is CI's.

NOT MEASURED locally (CI's): the live MongoDB cells of driver-mongodb, the Test Core shards, Dogfood, the workspace type-check lanes.

Acceptance notes

  • origin/main was merged once before opening (1432546ac8); its only overlap is objectql engine.ts, in disjoint hunks, and objectql was re-tested after the merge.
  • #20822 group 1b (branch claude/issue-20822-g1b-f3-route-then-delete) edits metadata database-loader.ts in queryHistory; this PR touches that file only at the history-seq error line, in a different hunk. Whichever lands later merges main. It does not touch filter-refusal.ts.
  • The es-ES, ja-JP and zh-CN sys_email.headers_json help values were translated from an older source revision and lack its last sentence (never returned on the generic data path). They carry no source-hash entry, so they are legacy-trusted and never reported. Noted, not filed; no carrier.
  • The $null diagnostics in driver-memory and driver-mongodb still describe the backends' old disagreement in the past tense, while the Turso diagnostic (stage 4) says every driver now refuses it. Both are true; only the wording differs. Noted, not filed.
  • Code comments and docblocks in these files still cite ids; they are outside this ruling.

Generated by Claude Code

…ts tracker number on both faces at once

The refusal already says why it refuses (limit: 1 would hand back an
arbitrary, plausible-looking row no null-check can catch), so the
citation goes and the sentence stays. The engine's
requireFindOnePredicate and metadata-core's
engineFindOnePredicateRefusalMessage are byte twins compared by
objectql's engine-findone-predicate.test.ts, so both move in this one
commit. Ledger recomputed with --census-ledger: the objectql row and
metadata-core's pair for this id leave.

Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY
Co-authored-by: Claude <noreply@anthropic.com>
…thor-visible refusals and metadata text state each decision in words instead of a tracker number

Stage 5 of the per-package burn-down, author-visible part. Refusals,
prescriptions and thrown messages in core, driver-memory,
driver-mongodb, formula, metadata and metadata-core drop their
tracker citations; where a sentence did not already carry the
decision it now says it (the register contract refuses loudly and
names the mismatch; registration order is not a contract; the
retired aggregate left AggregationFunction because no SQL backend
compiled it; the undeclared aggregate used to be summed before this
refusal existed). Where an ADR stood beside the number, the ADR stays.

The two in-process drivers' tenancy refusals end with a "Tracking:"
line that linked a tracker card by URL; the consequence sentence above
it already states the decision, so the line and its URL constants go.

Metadata text: the sys_account token help, the sys_email headers help
and the SCIM credential store's protection reason drop their numbers
in the source; the three hand-written translated values of the token
help drop theirs too (keys untouched). The en bundle is regenerated in
the next commit.

Pins that asserted a number are re-pinned onto the sentence. Ledger
recomputed with --census-ledger.

Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY
Co-authored-by: Claude <noreply@anthropic.com>
…ited field help

`node scripts/check-i18n-bundles.mjs --write --filter=platform-objects`
after the source edits in the previous commit: the generator rewrote
the four en leaves from the source and kept the translated locales'
values verbatim (merge mode), so the only moves are the three
sys_account token helps and the sys_email headers help. Ledger
recomputed with --census-ledger: the en bundle's row leaves.

Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY
Co-authored-by: Claude <noreply@anthropic.com>
…bers, last per the ruling

The superseded-registration warning, the authz cache posture line, the
endpoint matcher's excluded-item error, the history-seq and
loader-read failure errors, and the two fresh-datastore attestation
info lines each already say the decision; the citation goes and any
ADR beside it stays. The authz posture pin that asserted the number
is re-pinned onto the sentence. Ledger 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 8 package(s): @objectstack/core, @objectstack/driver-memory, @objectstack/driver-mongodb, @objectstack/formula, @objectstack/metadata-core, @objectstack/metadata, @objectstack/objectql, @objectstack/platform-objects, touching 36 documentable anchor(s).

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

  • content/docs/api/error-catalog.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/data-modeling/validation-rules.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/deployment/environment-variables.mdx (via sys_email (symbol, a field of const object enObjects), sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/deployment/self-hosting.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/kernel/runtime-services/sms-service.mdx (via sys_email (symbol, a field of const object enObjects))
  • content/docs/permissions/authentication.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/permissions/authorization.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/permissions/delegated-administration.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/permissions/permission-sets.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/plugins/packages.mdx (via sys_email (symbol, a field of const object enObjects))
  • content/docs/protocol/objectui/actions.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))

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

  • content/docs/releases/v12.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/releases/v13.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/releases/v15.mdx (via sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/releases/v17/17-0.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects), sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/releases/v17/17-1.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects), sys_email (symbol, a field of const object enObjects), sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))
  • content/docs/releases/v17/17-5.mdx (via sys_account (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects), sys_permission_set (symbol, a field of const object ADMIN_STANDING_SURFACE))

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: sys_user (symbol, 38 pages)
  • 4 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 — 48 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 d78a0bda07c4bed2e3b71e051ac5db2632b82741 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 d78a0bda07c4bed2e3b71e051ac5db2632b82741 → 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: 1432546ac85aa5a7b150f27d0c7c8a5bb22d3dcb
Local-runs: none

Read-only, at tier, adversarial to the dispatch. Inputs: card #20513 (body and all 29 comments — the census 5900801368, the ruling 5902360492 「20513 A」 A / A, the services pointer 5903999785, the stage-5 claim 5916824275, the stage-5 os-dev-report 5918105255), PR #20924 (body, 39-file list, the net diff origin/main...1432546ac8 at merge base 4d0b9cd542), PR #20848 at a94f3bad3 with its record 5910833957 (which held the findOne pair) and PR #20877 at 42d78b97f with its record 5913631724 as the prior applications of the same ruling, the head's check-runs, the 38 cited cards, the five ADRs the rewritten strings keep, and the head's own source through git show / git grep / git diff (the ledger parsed at the merge base, at origin/main and at the head). Nothing built, run or re-run; no worktree, no checkout. The PR head was confirmed as 1432546ac85aa5a7b150f27d0c7c8a5bb22d3dcb (branch claude/issue-20513-stage5-seven-packages, draft, Part of #20513, no closing keyword, mergeable: true) before the read and had not moved.

Check-runs on 1432546ac8, read once for this record (2026-09-30T19:36Z is the write; the read preceded it) and not polled: 31 check-runs — 12 completed / success (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, Type Check · source gates, Auto Label, filter), 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke — path-filtered), 16 in_progress (Test Core 1/6 to 6/6, Dogfood Regression Gate 1/3 to 3/3, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Build Core, Lint & Repo Gates, Type Check · consumer gates, · debt ledger and · workspace), 0 failure; one commit status, Vercel, success. Their conclusions are the gate verdicts: the runs still going carry check:doc-authoring, check:i18n and check:i18n-stale-fill (Lint & Repo Gates), the eight 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 the sixteen open 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 until the ledger is empty, all three categories, form D, --census-ledger in the same PR, author-visible text first and log lines last; the claim scopes stage 5 to the seven packages' src/**, the held objectql findOne string with its metadata-core twin, driver-memory's URL-form id, the ledger and a patch changeset per package, and forbids wire code / field / status changes, comment or docblock edits (#20595) and other lanes' ledger rows. The 39-file list is exactly that surface: 29 source files (28 in the seven packages, plus objectql engine.ts at the held string only), 8 test files, the ledger, the changeset. No governed path. The order inside the stage is honoured as commit order: 0a25891e6d the findOne pair on both faces, 69aace736f the author-visible refusals and metadata text, 6e2d61d158 the en bundle regenerated, 4e33fedc49 the log lines last, 214951a19c the changeset, then one merge of origin/main (1432546ac8, whose second parent is the merge base 4d0b9cd542, on origin/main).

Text only — right, checked line by line, with the two exceptions the dev names. The net diff changes 151 lines in the 29 source files; a filter for changed lines without a string delimiter returns 4, all removals in the two tenancy guards: a blank line and const tracking = / ? ISSUE_URL in memory-tenancy-guard.ts, a blank line in mongodb-tenancy-guard.ts. Every other changed source line sits inside a string literal or is the literal token of a one-line entry. No code, status, export, field name, branch or call moves: MULTI_TENANT_UNSUPPORTED_CODE (both spellings) is a context line, every throw, logger.* / ctx.logger.* call, err.code = StandardErrorCode.enum.* and err.status = ... is a context line, and internal: true on the three sys_account token fields is untouched. Comments and docblocks in src/ are untouched; the only comment edit is two lines in memory-tenant-scope-refusal.test.ts beside the re-pinned assertion (③ item 8).

The tenancy-guard removal — text only in effect, and the widening is right. What leaves is two module-private const URLs (never exported; nothing on the head references ISSUE_URL or CALL_SEAM_ISSUE_URL), a local ternary that only composed the removed line, and the trailing Tracking: ... line of each message. The refusal itself is unchanged: the same Error subclass, the same code, the seam parameter still varies only the wording, the headline and the consequence sentence ("Rather than run unisolated, the driver fails at startup." / "Rather than answer it unisolated, the driver refuses it.") are context lines, and no test anywhere on the head compares the full message or a Tracking: fragment (git grep over every .ts for Tracking: and the three issues/NNNN URLs: 0 hits; the plugin-dev consumer test asserts code: MEMORY_MULTI_TENANT_UNSUPPORTED only). The claim names driver-memory's URL-form id; the same line carried a second URL on both seams and driver-mongodb's twin guard carried the same line with its own URL. Taking all three is the bounded-fix reading: same defect class as the card (a tracker number in author-visible refusal prose, spelled as a URL), mechanical, inside this stage's file surface (driver-mongodb/src/** is stage 5), and no new verification surface. Leaving the mongodb line would have left an author-visible tracker number in a stage-5 package after the stage meant to empty it. A git grep of the seven packages' non-test src/ on the head for github.com/objectstack-ai/objectstack/(issues|pull)/ returns nothing.

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 (INVALID_QUERY / 400, NOT_IMPLEMENTED / 501, INVALID_FILTER / 400, the boot and per-call tenancy refusals, the thrown [Kernel] and [MetadataPlugin] errors). The regenerated en bundle and the three translated bundles change four help values each way and nothing else — no key added or dropped, no structure moved.

Ledger diff — right, exact, recounted independently. Parsing scripts/doc-authoring-prose-id.baseline.json at the merge base (byte-identical to origin/main today) and at the head: files 200 to 175, (file, id) pairs 485 to 441, occurrences 722 to 653. Exactly 27 rows move and no row is added or grows: 25 rows leave (the six core files, filter-refusal.ts, mongodb-aggregation.ts, mongodb-filter.ts, matches-filter.ts, engine-dispatch-unhonoured-predicate.ts, the five metadata files, objectql engine.ts, the four objects.generated.ts bundles, sys-email.object.ts, sys-account.object.ts, sys-scim-connection-credential.object.ts, platform-objects plugin.ts) and 2 shrink (engine-findone-predicate.ts 4 to 3 with #4419 gone; engine-update-dispatch.ts 20 to 18 with #11142 and #11230 each 5 to 4). The seven packages plus objectql go from 101 occurrences in 31 files to 32 in 6, matching the PR body's per-file table row for row. Every one of the 69 removed occurrences reconciles against a citation deleted in the source diff. No row outside these packages moves (0). check:doc-authoring inside Lint & Repo Gates is the mechanical confirmation, pending at read time; its printed site count (615 to 549) is the gate's own and is not re-derived here.

The findOne pair — right, both faces in one commit, byte-identical on the head. engine.ts requireFindOnePredicate (about 11562-11567) and metadata-core's engineFindOnePredicateRefusalMessage (about 128-133) carry the same six template pieces, character for character, citation gone from both. objectql's engine-findone-predicate.test.ts compares them by strict equality (about 95, message === engineFindOnePredicateRefusalMessage(OBJECT)) and by toThrow(engineFindOnePredicateRefusalMessage(OBJECT)) (about 123). The dev's ablation (9 parity cases red with the metadata-core face alone re-marked, through the dist/ leg) is its own measurement; the byte identity on the head is what this record verified. Stage 3's hold is discharged and objectql's row leaves the ledger.

Form D, string by string — right. Of the 38 cited cards, 36 read closed / completed; #12140 and #16589 answer 404, and their sentences were judged against the head's code; #11663 is open (the platform-admin re-anchor design card), but the sentence that cited it describes the L2 landing (#11970, closed), which is on the head. No cited decision has been reversed. The nine literals that gained words, each judged against the card and the head's code:

  • IMetadataService.register non-document refusal (7378 row 3): "so the contract refuses loudly and never coerces a value into storability". The card's row 3 records accept-then-drop as the indefensible answer and refusal as the consistent one; assertMetadataRegisterContract throws registerRefusal on a non-object data. True.
  • IMetadataService.register name-disagreement refusal (7378 row 1): "so the contract refuses loudly and names the mismatch". The card's candidate (c), refuse loudly rather than silently resolve either way, is what the code does, and the message names both spellings. True.
  • Init-order fault suffix (4131): "a composition/ordering fault; registration order is not a contract." The card's finding is that ordering was an unenforced convention; plugin-order.ts enforces requiresServices at init and its own violation text (about 144) already reads "Registration order is not a contract — declare ...". True.
  • admin_full_access row reason (8613): "(ADR-0049 active, which this resolver honours)". resolve-authz-context.ts filters permission-set rows through isRowActive (about 1031) and positions likewise (about 1006-1011). True.
  • sys_user row reason (11663 L2): "RECLASSIFIED from reads-only when the config anchor landed". The §6b-config anchor (OS_PLATFORM_OWNER_EMAIL matched against the caller's own sys_user row) is on the head at about 1073 of the same file. True.
  • MongoDB retired-aggregate refusal (6188): "(ADR-0049 enforce-or-remove: no SQL backend compiled it)". #6188 asked exactly that question of count_distinct / array_agg / string_agg; the spec's AggregationFunction today keeps count_distinct and retires the other two with AGG_RETIRED_MIDDLE reading "was removed from AggregationFunction in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no SQL backend ever compiled it"; refuseRetiredAggregateFunction is reached for those two names only. True, and the same reason the spec records.
  • MongoDB undeclared-aggregate refusal (5907, 12818): "before this refusal, this builder answered any unrecognised name with a $sum of that column". #12818's title is that silent SUM; the head's undeclaredAggregateFunctionError refuses and the file's own header says the switch's default answered a $sum. True.
  • HMR route ledger note (12140): "CHANGED when the development-only mount gate landed". hmr-routes.ts (about 114-118) opens the door only under a literal NODE_ENV=development through isDevMetadataEndpointEnabled, and its docblock records the gate as the change. True.
  • History-seq error (4825): "numbering it from 1 (what this code once did)". #4825's title is exactly that (静默从 1 重新发号). True.

Where only the citation goes (the other literals): each sentence already carried the decision and is otherwise unchanged — the three retired health-check keys (12032), the plugin-ordering violation and the requiresServices prescription (4131), the superseded-registration warning (9864), the §6b-config clause (11970), the authz cache posture line (4785), the nine driver-memory filter refusals (10413, 5158, 5240, 3948, 4286, 5345, 5324, 5328, 5347), the MongoDB date-bucket, per-aggregation-filter and declared-but-uncompiled refusals (6212, 10413, 5907) and its four filter refusals (5158, 5347, 5240, 5346), formula's empty-constraint refusal (5240), the by-id unhonoured-predicate and the two update id-conflict refusals (11009, 11142, 11230), the endpoint matcher's excluded-item error (5040 E7), the degraded-list warning (5184), the retired artifact-api source (4246), the sys_account token helps (7987), the sys_email headers help (8149), the SCIM protection reason (3653) and the two attestation info lines (4795). Where an ADR stood beside the number, the ADR stays and resolves on the head: ADR-0049 (health monitor, admin standing, MongoDB retired aggregate), ADR-0078 (the uncompilable-operator refusal), ADR-0104 (the attestation lines), ADR-0121 (the endpoint matcher), ADR-0134 (the SCIM store).

Generated translations — right, and it follows the repo's i18n rule. AGENTS.md's guardrail row for **/translations/*.generated.ts says the file structure is never hand-edited and is regenerated with node scripts/check-i18n-bundles.mjs --write (merge mode keeps every translated-locale value; en is rewritten from the source), and that translated-locale values are hand-written: editing one is fine, adding or dropping keys is drift. The generator itself prints the same rule ("Merge mode is on for translated locales, so no existing translation is overwritten"). So the number inside the es-ES / ja-JP / zh-CN token helps can leave only by hand, and the dev's reading of the order's "never by hand" against AGENTS.md is the right one (AGENTS.md wins over any other instruction). Verified on the diff: en.objects.generated.ts changes the four leaves whose source changed and nothing else; each translated bundle changes exactly the three sys_account token helps, and every edited value differs from the base by the parenthesised number alone ((#7987) / (#7987)), so each still says what the new English says. Keys and structure untouched (3 changed lines per bundle, all help: values). None of the twelve edited leaves has a *.source-hashes.generated.ts companion entry (0 hits in all three), so check:i18n-stale-fill, which reads those companions, has nothing to trip on; check:i18n is the bundle-drift gate and the en bundle is regenerated. Both run inside Lint & Repo Gates, pending at read time.

Pins — right, 8 assertions in 8 files, each phrase on the head. health-monitor.test.ts asserts in @objectstack/spec 17.3.0 (ADR-0049 enforce-or-remove), contiguous at runtime in all three retired-key messages; authz-cache-posture.test.ts asserts the full "a silently-absent invalidation bridge ... without anyone noticing." sentence, which spans a + boundary in the source and is one sentence at runtime; the two MongoDB retired-aggregate tests assert (ADR-0049 enforce-or-remove: no SQL backend compiled it); the three tenancy tests assert the consequence sentences ("Rather than run unisolated," / "Rather than answer it unisolated, the driver refuses it."); objectql engine-update-dispatch.test.ts asserts "and any declared multi:true — silently ignored.", present across the + boundary. None of the eight files still asserts its old number. The dev's mutation legs are its own measurement. A sweep of every test file on the head for a matcher on any of the 38 removed ids finds three unrelated hits: rest's truncation fixture's negative pin on its own copy of the $null sentence, runtime's endpoint-executor.test.ts pinning a runtime string (#5040 §7-3, another lane's row), and check-prerelease-pin-watch.mjs's self-test reading its own follow-up-card constant. No consumer pins a changed string.

Review faces — right, with one wording note. The changeset (below) reads as a CHANGELOG entry: every family it names is in the diff and each sentence it summarises matches the string, including the MongoDB retirement reason and the Tracking: line removal. The PR body's table, per-file ledger rows, commit order, 5b inventory and whole-ledger numbers match the diff and the recount above; it carries Clause-②: no at line start and no closing keyword. The metadata texts an administrator reads: the three sys_account token helps say "never returned on the data API; better-auth reads it back through the engine's privileged internal-field accessor" — the fields are internal: true and plugin-auth's internal-field-readback.ts is the accessor named; the sys_email headers help and the SCIM protection reason lose the number and nothing else. The note: the changeset does not name the two ledger-prose families (the admin-standing-surface reasons and the metadata route-ledger note); they fall under its opening sentence and are not author-facing refusals, so nothing is misstated. Carrier: none.

② Semver level

.changeset/20513-seven-packages-runtime-strings-state-the-decision.md: patch for @objectstack/core, driver-memory, driver-mongodb, formula, metadata, metadata-core, objectql and platform-objects — right. All eight are released (17.5.0, none private); the diff publishes changed prose in each of them and nothing else — no key, export, envelope code, status or default moves — so this is a fix-class patch in each, 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 findOne pair and the Tracking: line, and closes "Text only: no error code, field name, status or behaviour changes". No model identifier in the changeset, the five commit trailers (Claude-Session plus a model-free Co-authored-by; the merge commit carries none) or the PR body (session-URL footer) — the shape stages 1 to 4 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 5918105255), each answered:

  1. The split at the stop line (H2) — accepted, and verified. 94 ledgered literals plus 2 URL-form lines is over the ~80 line, so this PR carries every author-visible string and every log line (66 ledgered literals plus the two Tracking: lines) and stage 5b carries the 28 src/-shipped test strings. On the head the six remaining rows are exactly 5b's inventory (32 occurrences: test-mongod.ts 3, contract-suite.ts 1, engine-delete-dispatch.ts 4, engine-findone-predicate.ts 3, engine-update-dispatch.ts 18, object-schema-fls-contract.ts 3), and every one of those lines was read: contract-suite what: case labels and a describe title, two contract-case why texts, and test-mongod.ts's skip reason, download warning and thrown message — a test helper's strings, read by whoever runs the driver's suite, none of them shown to an author. Nothing author-visible is left for 5b, and 5b's inventory in the PR body is complete. The PR is Part of #20513 and the Part-of check is success.
  2. The generated-translation conflict (H4) — accepted; AGENTS.md's rule was followed (①). Named rather than silently resolved, which is the right shape.
  3. The URL-form scope reading — accepted (①); the bounded-fix clause applies and nothing beyond text moves in effect.
  4. One merge of origin/main before opening — accepted. The merge commit's second parent is the merge base 4d0b9cd542, on origin/main; the PR reads mergeable: true; the only overlapping file is objectql engine.ts, which this PR touches at the held findOne string alone. CI tests the merge ref. The neighbour #20822 group 1b edits database-loader.ts in queryHistory, a different hunk from the history-seq line here; whichever lands later merges main, and the ledger's growth refusal makes a stale ledger red rather than silent.
  5. The $null diagnostics' wording differing across drivers — accepted, both true. driver-memory's and driver-mongodb's sentences describe the backends' past disagreement in the past tense and are refusals on the head; the Turso sentence (stage 4) says every driver now refuses it, which stage 4's record verified across all five. Noted, not filed, is right.
  6. The es-ES / ja-JP / zh-CN sys_email.headers_json helps translated from an older source revision — accepted as noted. They carry no tracker number, no source-hash entry (legacy-trusted), and this PR does not touch them. The missing sentence is the one about the generic data path, so a carrier would be the next translation refresh of platform-objects; not filed here, and not this PR's.
  7. check:doc-authoring blind to URL-form ids (carrier check-issue-citations closeout (extractor spellings): CITATION_RE refuses a hyphen after the digits, so a dead #N-word citation (#13398-class) is invisible to the diff gate and to the census #20636, open) — accepted. The services pointer folded the grammar blind spot there; after this PR no URL-form id remains in the seven packages' non-test src/ (verified by git grep), and other packages were not measured, as the dev says.
  8. A test-file comment reworded beside the re-pin — accepted. The two lines in memory-tenant-scope-refusal.test.ts described asserting the tracking card; left alone they would describe an assertion that no longer exists. It is a test file, outside dead tracker citations in the domain:engine packages (645 sites, 160 numbers, 104 files): the ruling C+D stage for this lane (from #20556) #20595's src/ comment surface, and no src/ comment or docblock moved.
  9. Commit trailers (the model-free pair, not the harness reminder's line) — accepted, as stages 1 to 4 were under AGENTS.md's rule.
  10. Gate-runner foreground cap and worktree removal — process notes, no bearing on the contract.
  11. NOT MEASURED locally (live MongoDB cells, Test Core shards, Dogfood, workspace type-check lanes) — CI's; at read time Type Check · source gates is success, the rest in_progress, none failure.

open_questions: none in the stage-5 report; none raised here. The carriers stages 3 and 4 handed to this stage are all discharged: the findOne pair, driver-memory's nonBooleanNullComparandError citation (filter-refusal.ts about 616, now gone) and the URL-form id. The card stays open for stage 5b; the lane children (#20749, #20751, #20753, Blocked-by: #20513) are unaffected; the ledger's other rows are untouched.

Implemented-by: claude/issue-20513-stage5-seven-packages
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 20:59
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 3fbf3ca Sep 30, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20513-stage5-seven-packages branch September 30, 2026 21:26
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…rness text state each case in words instead of a tracker number (stage 5b) (objectstack-ai#21139)

Fixes objectstack-ai#20513
Clause-②: no

**Stage 5b, the last stage of this lane under the maintainer's A / A
ruling on the card (5902360492): the `src/`-shipped test strings.**
These are the 28 literals (32 occurrences in 6 files) that PR objectstack-ai#20924's
body inventoried as stage 5b and release 5922539397 restates: shared
case labels, a suite title, two contract `why` texts and the mongod
test-harness text. After this PR the `domain:engine` packages hold zero
rows in `scripts/doc-authoring-prose-id.baseline.json`. Text only: no
error `code`, export, HTTP status, case value or control flow moves.

## What this does

Each label or harness line sent the reader to a tracker number for the
reason behind it. In form D, as stages 1 to 5a applied it, the number
goes. Where the sentence already stated the case, only the citation goes
(17 literals). Where the sentence leaned on the number, it now says the
case in words (11 literals):

| Where | Cited | The text now says |
|---|---|---|
| `driver-mongodb` `test-mongod.ts` skip reason | 5517 | "retired from
default test runs because concurrent downloads made green runs exit 1" |
| `driver-mongodb` `test-mongod.ts` abandoned-download warning | 5517 |
"Ignoring the MongoDB binary download abandoned by a concurrent-download
race" |
| `ENGINE_DELETE_DISPATCH_CASES`: where.id plus a CAS operator, `multi:
false` | 11009 | "refused, the by-id path would drop the CAS guard" |
| `ENGINE_FINDONE_PREDICATE_CASES`: an empty `where` object | 3896 |
"read as match-every-row, so NOT a predicate" |
| `ENGINE_UPDATE_DISPATCH_CASES`: payload id beside a DIFFERENT scalar
where.id | 11142, 5748 | "refused; the payload id no longer silently
wins over a where.id naming another row" |
| same table: payload id beside an `$in` where.id, no multi | 11230 |
"refused; the by-id write would silently drop the declared row SET" |
| same table: payload id beside a NULL where.id | 11230 | "refused; a
declared where.id that is not one primary key is never silently dropped"
|
| same table: payload id beside a FALSY scalar where.id | 11142, 11230 |
"(it is a scalar, so neither the different-row nor the non-scalar
refusal applies)" |
| same table: where.id plus a CAS operator, `multi: false` | 11009 |
"the redeliver shape, refused rather than dropping its CAS guard" |
| same table: payload id plus an extra where predicate, no multi | 11009
| "refused; the by-id write would silently drop the predicate, through
the payload door" |
| `OBJECT_SCHEMA_MASK_CASES` `write-capable-caller/exempt` `why` | 7020,
6603 | "D4 is DERIVED from the schema write gate (`manage_metadata`)" |

Citation only: the contract suite's `serialized-form identity` group
title; 3 delete, 2 findOne and 9 update labels; the empty-readable-set
`why`; the mongod acquisition-timeout message. Each new clause was read
against today's code: `resolveEngineUpdateDispatch` (the different-row
refusal fires only on a truthy scalar where.id, the non-scalar refusal
only on a declared non-scalar), `ObjectQL.requireFindOnePredicate`
(absent, null and an empty object are read as match-every-row), and
`OBJECT_SCHEMA_WRITE_CAPABILITIES` (`manage_metadata`, the key the D4
exemption is derived from).

The `test-mongod.ts` docblock "Names the issue and the switch" now says
"Names the reason and the switch", so it stays true. No other comment or
docblock moves; code comments still cite ids, as in every earlier stage.

**Re-pins (H4).** The two `objectstack-ai#5517` number pins in
`mongodb-memory-server-gate.test.ts` now assert the text that replaced
the number: the skip line contains "retired from default test runs
because concurrent downloads made green runs exit 1", and the guard's
warning contains "download abandoned by a concurrent-download race".
Neither pin was deleted. Both were proven able to fail (below).

## Ledger (`scripts/doc-authoring-prose-id.baseline.json`)

Recomputed with `node scripts/check-doc-authoring.mjs --census-ledger`
(exit 0: no growth refusal) and copied into place. The diff is 25
deleted lines, nothing added.

| File | Before | After |
|---|---|---|
| `drivers/driver-mongodb/src/test-mongod.ts` | 3 | 0 |
| `metadata-core/src/contract-suite.ts` | 1 | 0 |
| `metadata-core/src/engine-delete-dispatch.ts` | 4 | 0 |
| `metadata-core/src/engine-findone-predicate.ts` | 3 | 0 |
| `metadata-core/src/engine-update-dispatch.ts` | 18 | 0 |
| `metadata-core/src/object-schema-fls-contract.ts` | 3 | 0 |
| **This stage** | **32 in 6 files** | **0** |

Whole ledger: files 175 → 169, pairs 441 → 428, occurrences 653 → 621.
Gate pinned sites 549 → 521. No other file's row moves. After each of
the four merges of `origin/main` (below) the recomputed ledger was
byte-identical to the committed one.

## Hypotheses, measured

- **H1, held.** At `origin/main` `9c8b65aa2` the six files held exactly
the inventory: `objectstack-ai#5517` ×3; `objectstack-ai#7856` ×1; `objectstack-ai#11009` ×4 (delete); `objectstack-ai#11767`,
`objectstack-ai#3896`, `objectstack-ai#4346` (findOne); `objectstack-ai#11009` ×6, `objectstack-ai#11142` ×4, `objectstack-ai#11230` ×4,
`objectstack-ai#5748` ×4 (update); `objectstack-ai#3545`, `objectstack-ai#6603`, `objectstack-ai#7020` (FLS). The gate's census
listed 28 literals carrying 32 occurrences.
- **H2, held.** The recomputed ledger has 0 rows under
`packages/objectql`, `core`, `formula`, `metadata*`, `platform-objects`,
`drivers/driver-*` and `plugins/plugin-pinyin-search` (the
`domain:engine` path set). The lane's ledger is empty. The whole ledger
is not, so the gate's stale-baseline arm keeps its blindness floor.
- **H3, nothing keys on the old labels.** Each of the 28 old literals
was searched byte for byte across the repo (`dist` excluded). Each was
found only in this stage's own source files (two delete labels have
update twins), with two exceptions. The tail `download — objectstack-ai#5517)` also
sits in a comment in `mongodb-pipeline-evaluator.testkit.ts`.
`serialized-form identity (objectstack-ai#7856)` also titles an independent `describe`
in `metadata-core/test/canonicalize.test.ts`, a test file outside the
ledger. Both are left alone. No CI workflow, `package.json` script,
vitest config, snapshot or skip list carries the labels or a `-t` filter
on them. The cited numbers appear elsewhere only in workflow comments,
an ADR anchor's invariant prose and a merge-parent sha. The labels reach
other code only as test names: objectql's `engine-*-dispatch` /
`engine-findone-predicate` parity loops, `service-queue`'s `it.each`,
and `assertObjectSchemaMaskCase`'s failure text. Each label stays unique
within its table (35 / 19 / 18).
- **H4, held.** See Re-pins.

## Changeset

`.changeset/20513-metadata-core-case-labels-state-the-case.md`: `patch`
for `@objectstack/metadata-core`. Its case tables ship in
`dist/index.*`, and the contract suite and FLS table ship in
`dist/testing.js` (measured after build: the new labels found in those
files).

There is **no `@objectstack/driver-mongodb` entry**, because
`test-mongod.ts` is not published. The package builds `src/index.ts`
only, and only test files import `test-mongod.ts`. Measured after build:
`mongodSkipReason`, "retired from default test runs",
"concurrent-download race" and "waiting for the MongoDB binary" are each
in 0 files under `packages/drivers/driver-mongodb/dist`. The positive
control `MongoDBDriver` is in 4.

## Text-only proof

A TypeScript-AST skeleton of each changed `.ts` file compares merge base
`670680e93` with head `24b72b809`. In the skeleton, every string literal
and template text is one placeholder, pure-literal `+` operands
collapse, and comments are never read. Result: 7 of 7 SAME, with token
and literal-slot counts identical per file. Control: the same tool
reports DIFF on `rest` `import-template.ts` across `6f1f1c103`, a real
code change.

## Tests (head `24b72b809` unless noted)

- Build: `turbo run build` over `./packages/*` and `./packages/*/*`
under the verify lock, 71/71, after the last merge.
- `@objectstack/metadata-core`: 16 files / 298 tests passed; `typecheck`
exit 0.
- `@objectstack/driver-mongodb`: 30 files passed and 5 skipped, 675
tests passed and 172 skipped (the skips need a MongoDB server);
`typecheck` exit 0, including `check:test-typecheck`.
- Consumers of the case tables, against the rebuilt `metadata-core`
`dist`:
- `objectql`, 7 files / 181 tests: `engine-delete-dispatch`,
`engine-update-dispatch`, `engine-findone-predicate`,
`engine-update-by-id-payload-id`, `engine-update-multi-payload-id`,
`engine-write-not-found-gate` and
`validation/operator-object-write-value`;
  - `service-queue` `db-queue-adapter`, 38;
  - `rest` `meta-object-fls`, 95;
  - `runtime` `domains/meta-object-fls`, 85;
  - `metadata-fs` `contract`, 31;
- `metadata-protocol` `sys-metadata-repository.contract` and
`protocol.update-path-id-wins`, 64.
- A verbose run before the merges showed the parity loops naming the new
labels. objectql's tests resolve `metadata-core` through `dist`, so this
shows they read the rebuilt build.
- Re-pin mutations, one-off, on the committed head (`2729901db`),
through `scripts/ablation-replace.mjs` in WRAP mode (restore armed on
EXIT/INT/TERM):
- Skip-reason anchor ×1 → ×0: `mongodb-memory-server-gate.test.ts` 1
failed / 7 passed.
  - Warning anchor ×1 → ×0: 1 failed / 7 passed.
- Each restore: blob equals HEAD's (`22a95643c8`), and `git diff HEAD`
is empty. After both, `git diff HEAD` was 0 bytes and `git status
--porcelain` 0 lines.

## Gates

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) at `24b72b809` derived 69
commands. All 69 ran one at a time from the worktree, each exit 0.
`--ran` reports "69 derived, 69 run, 0 NOT-MEASURED, 0 UNRUN". Among
them:

- `check:doc-authoring`: 521 pinned sites across 169 files, no growth,
no burn-down unrecorded;
- `check:driver-conformance`: 50 covered cells, 0 DEBT, 0 exempt, the
same reading as before the change;
- `check:engine-double-contract`, `check:dual-build-cjs-loads` (105
require entry points across 66 packages load, read from the post-merge
build), `check:nul-bytes`, `check:type-check-coverage` and
`check:type-check-debt`.

After the first merge (`packages/spec` moved on main): `pnpm --filter
@objectstack/spec check:generated` reported all 15 artifacts up to date.

Narrowed lint: `eslint --no-inline-config --format json` over the 7
changed `.ts` files reported 7 files, 0 errors and 0 warnings, none
ignored (counts from eslint's JSON). The resolved `parserOptions` are
`ecmaVersion: latest, sourceType: module`, with no `project` or
`projectService`. So no type-aware rule runs, and this diff cannot move
an untouched file's verdict. Repo-wide `pnpm lint` is CI's.

NOT MEASURED locally (CI's): the live MongoDB cells of `driver-mongodb`,
the Test Core shards, Dogfood, and the workspace type-check lanes.

## Acceptance notes

- `origin/main` was merged four times while this was open (`1d55ee66e`,
`4f1c99822`, `4516a0643`, `24b72b809`; the third brings in objectstack-ai#21097). None
of the incoming commits touches a file this PR changes. They touch
`objectql`, `rest`, `runtime` and `metadata-protocol`, so those consumer
suites were re-run at the final head. `main` moved again after the gate
union; the queue rebuilds on it.
- PR objectstack-ai#21105, in `driver-mongodb`'s filter and temporal suites, landed
before the first merge and shares no file with this PR.
- `metadata-core/test/canonicalize.test.ts` titles its own `describe`
`serialized-form identity (objectstack-ai#7856)`, and
`mongodb-memory-server-gate.test.ts` titles two `describe` blocks
`[objectstack-ai#5517] …`. Both are `*.test.ts` files, which the ruling's third
category (`src/`-shipped test strings) and the ledger do not cover. They
are left as they are: noted, not filed.
- The lane children objectstack-ai#20749, objectstack-ai#20751 and objectstack-ai#20753 carry `Blocked-by:` lines
naming this card. They unlock when it leaves the open state on merge.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Ujdtvqs7ree7WyQmEDwEnG)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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