Skip to content

fix(spec): os migrate meta guidance for the datasource-*, filter-*, action-*, data-* and element-* migration entries states each lesson in words, not tracker numbers (stage 4) - #20509

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20233-migrate-meta-tracker-free-stage-4
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20233-migrate-meta-tracker-free-stage-4

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20233
Stage 4: the datasource-, filter-, action-, data- and element- families.

Clause-②: no

Stage 4 of a staged card. The card stays open for later stages; this PR carries no closing keyword. Text only: no entry id, surface, from / to, conversion or matching logic moves, and the chain rewrites exactly what it rewrote before.

What this does

os migrate meta prints every ADR-0087 semantic entry it crosses as one block: ⚠ [protocol N] SURFACE → REPLACEMENT, then why: (the entry's reason) and verify: (its acceptanceCriteria). AGENTS.md's runtime-string rule applies to all of it: 「Runtime strings — refusal prose, prescriptions, anything an author is shown — carry no tracker number (pnpm check:doc-authoring): the lesson goes into the text.」 Form D of ruling C+D on #19123 (5749154545) sets the shape: the lesson in words, and no number, dead or alive; a cross-repo number (cloud#N, objectui#N) is still a tracker number.

This stage covers the next five families, datasource-, filter-, action-, data- and element-: 150 sites → 0 in the three prose fields, across 33 entry files. None of the 37 entries in these families carries a tracker id in surface (ruling A of the stage-1 ACCEPT, 5858839916, is checked and has nothing to do here). Each site now says what the cited ruling, measurement or fix decided. ADR ids stay. registry.ts, spec-changes.json and docs/protocol-upgrade-guide.md are regenerated from the entries (gen:migration-registry, gen:spec-changes, gen:upgrade-guide), never hand-edited. The pin now holds eleven families.

Census — tracker ids in the author-shown fields

Instrument. The stage-2 AST instrument, re-written to its published description (the stage-2 and stage-3 copies lived in removed scratchpads): a TypeScript-AST walk over every packages/spec/src/migrations/entries/**/*.ts. For each entry object literal it evaluates the string value of replacement, reason, acceptanceCriteria and (counted separately) surface, joining string literals with +, then counts # followed by 4 or 5 digits at a word boundary. Validated first by reproducing earlier readings on their own trees (extracted with git archive): on 443b2f4fdc (stage 1) engine- 67, datasource- 43, filter- 30, action- 26, element- 25, data- 24, whole tree 266 entries / 1,016 sites / 9 surface; on 0d7ed5a378 (stage 3 landed) whole tree 711 sites / 7 surface — every figure equal to the stage-1 census and to stage 3's after-count. Unevaluable fields: 0.

Controls, same run.

Base fc0db22b: datasource- 9 entries, 43 sites (3 / 38 / 2); filter- 11, 32 (1 / 30 / 1); action- 6, 26 (0 / 23 / 3); element- 5, 25 (2 / 19 / 4); data- 6, 24 (0 / 24 / 0). 150 sites (6 / 134 / 10) in 33 of the 37 entries; 88 distinct ids (80 bare, 8 objectui#, 2 cloud#). Whole tree: 310 entries, 721 sites (711 after stage 3, plus entries main added since in other families), 7 surface.

After this PR: all five families 0; the six earlier families still 0; whole tree 721 → 571; surface 7 (other families). The PM's rough line count (156 sites, 33 files) is a wider instrument; the AST reading is 150 in the same 33 files.

entry sites (replacement / reason / acceptanceCriteria) short #NN
17.action-descriptor-is-async-retired 2 (0 / 2 / 0)
17.action-descriptor-resume-authority-default-flip 10 (0 / 7 / 3)
17.action-session-roles-to-positions 12 (0 / 12 / 0)
17.data-driver-find-stream-retired 1 (0 / 1 / 0)
17.data-driver-query-omit-object 9 (0 / 9 / 0)
17.data-engine-batch-retired 1 (0 / 1 / 0)
17.data-field-changed-event-retired 4 (0 / 4 / 0)
17.datasource-config-inline-credential-refused 1 (0 / 1 / 0)
17.datasource-config-placeholder-refused 5 (0 / 5 / 0)
17.datasource-config-url-userinfo-refused 4 (0 / 4 / 0)
17.filter-regex-options-retired 8 (0 / 8 / 0)
18.action-bulk-dispatch-contract-undeclared 1 (0 / 1 / 0)
18.action-engine-facade-find-query-envelope 1 (0 / 1 / 0) 1
18.data-file-value-duration-unit-in-key 6 (0 / 6 / 0) 1
18.data-nosql-query-options-timeout-unit-in-key 3 (0 / 3 / 0) 1
18.datasource-config-mongo-options-credential-refused 5 (0 / 5 / 0)
18.datasource-config-postgres-url-unparseable-refused 4 (0 / 4 / 0)
18.datasource-config-url-query-credential-refused 4 (0 / 4 / 0)
18.datasource-credentialsref-mongo-composed-no-username-refused 10 (2 / 7 / 1)
18.datasource-credentialsref-mongo-url-no-user-refused 10 (1 / 8 / 1)
18.element-data-source-and-object-block-filter-rule-array 11 (1 / 9 / 1) 1
18.element-number-filter-rule-array 7 (0 / 6 / 1)
18.element-record-picker-filter-rule-array 7 (1 / 4 / 2)
18.filter-between-blank-endpoint-refused 3 (0 / 3 / 0) 1
18.filter-between-field-reference-endpoint-refused 5 (1 / 4 / 0)
18.filter-comparand-types-and-widget-nested-slots-refused-at-save 2 (0 / 2 / 0)
18.filter-equality-array-comparand-refused 1 (0 / 1 / 0)
18.filter-equality-array-comparand-refused-at-save 1 (0 / 1 / 0)
18.filter-icontains-comparand-refused-at-parse 3 (0 / 3 / 0)
18.filter-ne-array-comparand-refused 1 (0 / 1 / 0)
18.filter-preset-ordering-comparand-refused 4 (0 / 4 / 0)
18.filter-query-face-comparands-refused-at-save 1 (0 / 1 / 0)
18.filter-text-operator-declared-type-refused 3 (0 / 2 / 1) 1
total, 33 entries 150 (6 / 134 / 10) 6

Text only — proved by a base-vs-head AST comparison

For every entry file this PR changes, both versions (fc0db22b and the head) are parsed and three things are compared token for token: every import declaration, every property other than the three prose fields (so id, surface, from / to and any matcher, by evaluated value), and every comment token in the file. 33 files compared, 0 with a non-prose change. The instrument is shown able to fail first: on an in-memory copy it reports DETECTED for a mutated id, a mutated comment and a mutated surface. So none of #20234's comment lines moved, and no entry's identity or matching moved.

Every citation read, and what the text now says

Each cited id was read with a single-card REST read (body plus the ruling, measurement or landing comments), resolved against the repository its sentence names. Ids are in code spans so this body posts no cross-references. 10 bare ids answer 404 on both the issues and the pulls endpoint (re-probed with a 200 control, 14478), and 2 cloud# ids are unreadable from this session (403); those sentences are rewritten from what main records, listed in Acceptance notes.

datasource- (14 ids)

cited what it decided (read) how the text now carries it
7990 Maintainer, 2026-08-12, Option A: close inline credentials per artefact (each schema refuses at publish and diverts to its existing mechanism); the heuristic sys_metadata write guard parked. "The maintainer ruled on 2026-08-12 to close that per artefact: each schema that admitted an inline credential refuses it at publish …"; "the inline-credential closure"
8078 (PR) The spec half of that ruling; measured that ${…} placeholders reach the client verbatim and that config.url still took the secret. "measured by the credential census while the inline-credential refusal was being built"; "measured when the inline-credential refusal was built"
8082 Maintainer, 2026-08-12, option A: refuse URL userinfo at publish through one shared value-level parse; runtime DSNs unaffected. "The maintainer ruled on 2026-08-12 (Option A) to refuse the URL userinfo password at publish, through one value-level parse the driver schemas share"
8336 Maintainer, 2026-08-13, direction 2 of two: refuse ${…} at publish; implementing resolution rejected. "The maintainer ruled on 2026-08-13 for the second of two directions: refuse the syntax loudly at publish"
8337 The credential-bearing URL query parameter refusal, one syntax over from userinfo. "the credential-bearing URL query parameters"; "the query string was the third spelling of the identical secret, one syntax over"
8876 404 — see Acceptance notes. "the asymmetry the URL grammar keeps between its two userinfo halves — a username is not credential material"
8696 404 — see Acceptance notes. "measured when the bound secret was made to reach the mongo client on its URL branch"; "new URL() rejects the multi-host form outright, and the mongo arm hands the authored URL to its client untouched"; "the defect class closed when each DSN branch was made to inject the bound secret its composed branch already used"
9041 404 — see Acceptance notes. "the sibling URL-branch entry, datasource-credentialsref-mongo-url-no-user-refused"; "this URL-branch refusal"
9147 The composed-branch twin: bound secret + no url + no username refused, inheriting the URL-branch ruling. "this composed-branch refusal"
7314, 7385, 8152, 8875 The driver-factory arms that dropped something declared: the turso loader's install remedy and half its config, the other optional arms' missing-package remedy, turso's never-read bound secret, and (8875, 404) the mysql DSN branch. "the family of driver-factory arms, closed one driver at a time, that each dropped something declared without a word: the optional-driver arms that answered a missing package with no remedy, the turso arm that never read its bound secret, and the mysql and mongo DSN branches that discarded one"
8873 404 — see Acceptance notes. "the postgres equivalent is judged on its own client's measurement, never inherited"

filter- (24 ids)

cited what it decided (read) how the text now carries it
4706 Maintainer, 2026-08-06, option B: retire $regex loudly and add $icontains; five-backend true regex (A) excluded (turso remote cannot, no business pull). "the maintainer's 2026-08-06 ruling (option B: retire $regex loudly and add $icontains, rather than make five backends agree on one regex dialect)"
5701, 5702 The contract half and the driver half of that ruling. "the retirement's own contract-half change"; "the contract half (the $icontains declaration, …)"; "the driver half"
6148 404 — see Acceptance notes. "the gate that makes a breaking changeset state its ADR-0087 disposition"
18012 404 — see Acceptance notes. "Maintainer ruling A of 2026-09-17: a blank $between endpoint is refused at the authoring door, and the refusal names the blank side."
13495 driver-memory's reference matcher answered a null-bounded range with every valued row. "The reference matcher had already been taught to survive the null-bound form of exactly this"
objectui#9695 The console filter builder stops padding a half-typed pair with an empty string. "is a change to the console's own filter builder and lands on its own schedule"
7596 Maintainer, 2026-08-11, ADR-0049 REMOVE: no { $field } endpoint in either $between union. "Maintainer ruling of 2026-08-11 on column-reference range endpoints, ADR-0049 enforce-or-remove: REMOVE"; "reviewed and accepted with that ruling"
7713 (PR) Shipped that removal with a not-required disposition. "the 2026-08-11 changeset carried the disposition not-required"
5222 $field compiled to a column-to-column comparison on the SQL faces. "the position the column-to-column comparison compiles on every face"
19377 404 — see Acceptance notes. "filed as an entry of its own rather than as an already-registered rider"
20116 The collector for the family "the save door accepts comparand shapes the query faces refuse", closed in stages. "the second stage of closing the family of comparand shapes the save door accepted and the query faces refused"; "the family of comparand shapes …"
7872 Maintainer, 2026-08-12: the shared face accepts string / number / bigint / boolean / null / Date and refuses everything else. "the accepted set the maintainer ruled on 2026-08-12: string, number, bigint, boolean, null and Date"
19889 Director seat, 2026-09-24, option A: the schema door refuses what the compile face refuses (the standing array-equality refusal applied to it). "Ruled on 2026-09-24 (option A), applying the standing refusal of an array in the equality slot to the schema door"
19757 Maintainer, 2026-09-23, option 乙: refuse an equality-slot array at the shared face; 甲 (declare array equality) and 丙 (document the divergence) rejected. "Maintainer ruling of 2026-09-23 (option 乙 — rather than declaring an array equality the SQL-family backends would have to invent, or documenting a divergence that stays silent on one backend)"
19514, objectui#9050 The protocol half of the maintainer's 2026-09-20 ruling C′ on the console's filter converter, rule 1 「the differences are the protocol's to close」. "The protocol half of the maintainer's 2026-09-20 ruling (option C-prime) on the console's filter converter, whose first rule reads, verbatim and untranslated: 「the differences are the protocol's to close」"
18113 Published the text-comparand refusal predicate and reason beside FILTER_TEXT_CASES. "the pair published in this package beside FILTER_TEXT_CASES for exactly this reason"
19886 Director seat, 2026-09-24, option A on standing text: $ne with an array refused at the shared face and the schema door. "Ruled on 2026-09-24 by the director seat, on the standing contract text (option A)"
8690 Ruled 2026-08-15: option B (engine door refuses an uninterpretable temporal comparand) with option C (the preset refusal at authoring) alongside. "The authoring half (option C) of the maintainer's 2026-08-15 ruling on uninterpretable temporal comparands, ruled alongside the engine door (option B)"; "measured on the defect report"
8808 (PR) The engine door. "before the engine door and a 400 after"
15661, 15773 Maintainer, 2026-09-05, C-deny: refuse a text operator over a never-string declared type, over the existing type sets; landed at the engine seam. "Maintainer ruling of 2026-09-05 (option C-deny: refuse now, over the type sets the contract already declares, minting no new vocabulary), landed at the engine seam"
14079 Ruling A: a stored non-string value never satisfies a positive text operator and satisfies $notContains. "(a stored value that is not a string never satisfies a positive text operator and satisfies $notContains)"

action- (19 ids)

cited what it decided (read) how the text now carries it
6748 Retire isAsync: zero readers on a fresh three-repo measurement. "a fresh three-repo measurement (taken when the key was filed for retirement, and re-run at pickup)"
6667 Enforce supportsPause at runtime. "The sibling took the ENFORCE leg of the same ruling"
3801 The generic resume route needs an authorization gate keyed on the suspended node. "The generic resume route's authorization gate keys on the SUSPENDED NODE"
3823 The revise-window wait pause is service-owned but inherited 'any'. "The revise-window incident decided the direction"
4484, 5540, 6011 The three retirements whose disposition this one shares. named by their entry ids, which the sentence already carried
5561 resumeAuthority defaulted to 'any' — fail-open by omission. the sentence already states it; ADR-0019's resume-seam addendum named by date and title
5703 supportsPause / isAsync were zero-reader declarations. "no longer the declaration nothing enforced"
5613 Maintainer, 2026-08-06, contract-first ("C skeleton + A semantics"): declare the shape as it stands, then rename on the typed face. "The maintainer ruled contract-first on 2026-08-06 (…: declare the shape as it stands first, then rename on the typed face)"; "the runtime half of the same ruling"
5697, 5779 Phase 1 (the schema) and phase 2's spec half (the canonical key and the alias). "phase 1 declared …"; "positions is now the canonical key …"
5050 The hook-side roles removed outright. "(removed outright)"
3280, 3290 The hook ctx.session.tenantId alias: deprecated, then removed in the next major. "the hook ctx.session.tenantId alias: deprecated first, removed in the next major"
4579, 4657 The openApi31 and activationEvents retirements. named by their surfaces, which the sentence already carried
17319 Ruled 2026-09-12, A: an action declares its dispatch contract; B (unify the two wirings) refused. "the 2026-09-12 ruling that made an action declare its dispatch contract refused exactly that option"
14175 The earlier typing fix that gave find the filter alone. "the parameter shape an earlier typing fix chose (the filter alone), ruled by the director seat on 2026-09-12, with the maintainer's agreement"

data- (15 ids)

cited what it decided (read) how the text now carries it
4484, 4618, 4673 The three retirements, each already stated by its own entry. trailing ids dropped (ADR ids kept)
4639 Multi-record predicate writes got data.records.* events. "since multi-record predicate writes were given events of their own"; "the way bulk writes were given theirs"
3196 Webhook triggers trimmed to producers that exist. the sentence already states it
cloud#1053, cloud#1030 403 here — see Acceptance notes. "20 such sites were measured in the downstream cloud codebase, and a $like the type layer would have caught reached runtime there through exactly that hole"
6350 The stock reconciliation of the v17 train's breaking changesets against the ledger; this change was its control sample and was itself an omission. "Registered by the stock reconciliation that compared the breaking changesets already on the v17 release train against this ledger. This change was that audit's CONTROL sample …"; "(backfilled by that reconciliation)"
5181 The DriverQuery narrowing itself. "this change"; "this narrowing"
6321, 6083 Two later call-parameter changes, both registered (6083 404; main records it as ADR-0122 phase 2). "Two later, smaller driver call-parameter changes both registered"
18669 Maintainer, 2026-09-17, ruling A: rename the last two duration keys no closed duration type could express, no new closed type, no narrowing. "Maintainer ruling A of 2026-09-17 on the last two duration keys no closed duration type could express"
18122 Published DurationMs / DurationSeconds beside EpochMs, the unit set derived from six genuine duration rows. "published beside EpochMs as a closed duration type"; "one of the six genuine durations the closed types' unit set was derived from"
14478, 15680 Ruling B of 2026-09-02 on duration units; the data-directory rename round. "Maintainer ruling B on duration units (2026-09-02)"; trailing ids dropped

element- (17 ids)

cited what it decided (read) how the text now carries it
objectui#6206 Maintainer, 2026-08-25, option B: one filter orthography platform-wide — the ViewFilterRule array, not a record-shaped exception; measurement-first binding. "the maintainer's 2026-08-25 ruling, option B: …" (three entries); "the console-side half of this convergence"
15442, 15449 Ruled 2026-09-06, option A: converge the binding and the four object-* doors family-wide, one entry; exception and mixed rejected. "ruled 2026-09-06, option A: converge the binding and the four block doors family-wide, under one entry, rather than record an exception"; "a gap measured on its own"
7751 Maintainer, 2026-08-12: the object-* block props enter ComponentPropsMap. "a read-point record derived from the renderers on 2026-08-13, when the object-* blocks first got props schemas in the map"
objectui#6948 The console's filter converter had no branch for $and / $or / $not. "(the console's filter converter had no branch for them)"
15828 POST /analytics/query refused the array where the adapter sent. the sentence already states it; "the runtime route's refusal of the array corrects it here"
16626 404 — see Acceptance notes. "parked behind a bump of that pin"
objectui#7754, objectui#7752, objectui#6828 The console adapter lowers an array analytics filter before the wire; aggregate() runs translateFilterArray. "the console adapter was changed so ObjectStackAdapter.aggregate() runs the same translateFilterArray"; "The adapter-side lowering lands in the console's own repository."
5158 Maintainer, 2026-08-04, ruling C: FilterArray is input-only sugar with one lowering seam (parseFilterAST). "since the maintainer's 2026-08-04 ruling C declared the array input-only sugar with one lowering seam"
5334 The in-process analytics door, added when an array where was silently dropped. "(added when an array where was found silently dropped on the analytics path)"
17321 Ruled 2026-09-12, B: a partial D2 conversion for the losslessly mappable record forms; combinators pass through, named. "(ruled 2026-09-12, option B: convert what maps losslessly and name what does not, rather than leave every stored row to its next save or flatten combinators)"
14406 Converged element:record_picker; its census pin asks whether any filter door refuses the array. "the twin of the census pin that asks whether any filter door still refuses the array"; the 2026-08-25 Option-A ordering ruling stated as "measure the consumer's read path before the contract moves"
12039 element:number converged. "after element:number converged"
objectui#7663 The console registry's inputs.filter flip for the picker. "a console-side change filed in the objectui repository, blocked on that release"
15829 The dashboard widget filter location, a finding of its own. "a different family, judged on its own"

Pin — packages/cli/test/migrate-meta-engine-guidance.test.ts, widened

COVERED_PREFIXES is now engine-, ui-, plugin-, driver-, kernel-, system-, datasource-, filter-, action-, data-, element- (data- does not select datasource- or dataset-: the match is startsWith('data-')). The REWRITTEN floor rises from 55 to 88 ids: the 33 entries of this stage that carried a tracker id. The three it blocks are textually unchanged: the detector is exercised on both sides, every covered prefix must select an entry, and each covered block is found verbatim in the real CLI's stdout before it is asserted clean. The file keeps its stage-1 name; the header lists the eleven covered families.

Ablation — the widened pin can fail on a new-family block

From committed state, HEAD d8bcc46a, in one lock turn, with scripts/ablation-replace.mjs in wrap mode (it owns the mutation's restore trap; the leg script adds its own trap … EXIT INT TERM that restores registry.ts from HEAD by absolute path and checks the blob) and scripts/ablation-dist-preflight.mjs gating each leg. The bundle is built from the generated registry.ts, so that is the file mutated.

  • Mutation. In registry.ts, the acceptanceCriteria of datasource-credentialsref-mongo-url-no-user-refused: anchor parse reports this URL-branch refusal. → parse reports the #9041 refusal. The tool read anchor 1 → 0 and replacement 0 → 1, blob 7001c102 → 6f3f81ff.
  • Mutate leg. Spec build exit 0. Preflight: marker present in 4 built files. Pin: red, 1 failed | 2 passed — datasource-credentialsref-mongo-url-no-user-refused: the printed guidance cites a tracker id: expected '#9041' to be undefined.
  • Restore. Tool-proven: blob 7001c102 == HEAD, git diff HEAD empty.
  • Restore leg. Spec build exit 0. The --absent preflight found the marker in none of 224 built files, with the working tree clean against HEAD. Pin: green, 3 passed. Whole tree afterwards: 0 dirty paths.

Verification

Final head d8bcc46a. Every heavy run went through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 on each turn; per-step exit codes recorded separately). Where a line was taken at 95fb97ea it says so: the one commit after it (d8bcc46a) changes one entry sentence and the three generated projections, and every reader of that text was re-run at d8bcc46a.

  • Build: pnpm exec turbo run build --concurrency=2 --filter='@objectstack/cli^...' gives Tasks: 58 successful, 58 total (at 95fb97ea); the spec package was rebuilt at d8bcc46a in both ablation legs (exit 0).
  • Pin: at d8bcc46a, the ablation's restore leg: test/migrate-meta-engine-guidance.test.ts 3 passed. With its neighbour at 95fb97ea: pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/migrate-meta-engine-guidance.test.ts test/migrate-meta-default-range.test.ts gives Test Files 2 passed, Tests 10 passed | 1 skipped (the skip is the default-range file's own skipIf). Re-run with its neighbour at d8bcc46a: Test Files 2 passed, Tests 10 passed | 1 skipped. One earlier run at this head is void and is not counted: it spawned the CLI while a concurrent lock-free gate was rebuilding the package dist/ trees (MODULE_NOT_FOUND, both files); the re-run is the reading.
  • Spec, the whole local project (at 95fb97ea): pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 gives Test Files 572 passed (572), Tests 16789 passed | 1 todo. The repo project (at d8bcc46a): Test Files 38 passed, Tests 690 passed. The readers of the changed sentence (at d8bcc46a): src/migrations, src/ui/action-params.test.ts, src/ui/filter-rule-array-guidance.test.ts give Test Files 5 passed, Tests 229 passed.
  • CLI unit (at 95fb97ea, no CLI file changed after): test/vitest-tiers-partition.test.ts and src/utils/spec-release-changes.test.ts give Test Files 2 passed, Tests 28 passed.
  • The call-spelling census that reads registry.ts: pnpm --filter @objectstack/driver-sql exec vitest run --maxWorkers=2 src/sql-driver-query-signature.test.ts gives 15 passed.
  • Typecheck (at 95fb97ea; the last commit edits string literals only): pnpm --filter @objectstack/spec typecheck exits 0 (test layer: 53 files / 251 errors held in its ledger); pnpm --filter @objectstack/cli typecheck exits 0 (3 files / 28 errors held).
  • Gate families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 90 families at d8bcc46a (the same set it derived at 95fb97ea). --ran over the recorded exit codes reads 90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0. They include check:doc-authoring ("16735 customer-facing string(s) across 1167 spec sources clean"), check:issue-citations, check:migration-registry ("registry.ts is current (310 semantic, 230 retired-key, 206 retired-def)"), check:spec-changes, check:upgrade-guide, check:generated ("All 15 generated artifacts are up to date"), check:org-identifier, check:nul-bytes, check:dual-build-cjs-loads (104 require entry points across 66 packages load), check:type-check-debt, check:adr-0087-registration and check:changeset-no-major.
    • How the reading was assembled: a container restart cut the pass at d8bcc46a after 30 families; the other 60 were run afterwards on the same head, and check:type-check-debt, whose first resumed run lost a package build to an OOM kill (exit 3, PREREQUISITE NOT MET), was re-run alone and exited 0 ("4 ledger entr(ies) re-measured … none above its recorded number").
    • The earlier full pass at 95fb97ea read 88 exit 0, check:dual-build-cjs-loads exit 3 (dists not yet built) and check:org-identifier exit 1: my rewrite of the action-session entry had spelled the removed hook read literally. d8bcc46a names that alias in words instead, and the gate reads OK there.
  • Lint (a proven narrowing, not the repo-wide run, which is CI's): eslint --no-inline-config --format json over the 36 changed .ts files at d8bcc46a reports 36 files, 0 errors, 0 warnings.
    • The population is read from eslint.config.mjs: **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED, and all 36 are in it (no file-ignored warning).
    • Invariance: the config enables no type-aware linting (no parserOptions.project, no typed rules), so a text edit cannot move the verdict on a file it does not touch.
  • Mergeability: main moved 10 commits past the base, to 4a1df196; the only ones under packages/spec/src/migrations/ are the landed 20488 (two entries in the field- and ui- families, plus registry.ts). A driver-free bare-clone merge-tree --write-tree of d8bcc46a against 4a1df196 exits 0 with no conflicted path, and the census over that merged tree reads 0 sites in all eleven covered families, so main was not merged in; CI's merge ref runs the registry gates on the merged tree.

Acceptance notes

  • Ten dead ids, rewritten from the code on main. Each answers 404 on both the issues and the pulls endpoint, with a 200 control (14478).
    • 8696 / 8875 / 8873 (the bound secret reaching the mongo, mysql and postgres clients): service-datasource's default-datasource-driver-factory.ts, its CHANGELOG (the mongo URL-branch entry says it "closes the last arm of the family" the other cards closed one driver at a time; the mysql DSN fix is { uri, password }) and bound-secret-dsn-branches.test.ts, which pins the bound secret outranking options.auth. common.zod.ts and pg-url-grammar.server.ts record why the shared helpers must not parse (mongo's multi-host form, which new URL() rejects).
    • 8876: common.zod.ts (urlUserinfoUsername, the username half of the same grammar; a username is not credential material).
    • 9041: datasource.zod.ts (CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED and its docblock); the text now names the sibling entry by id.
    • 6148: scripts/check-adr-0087-registration.mjs's header (a declared-breaking changeset must state its ADR-0087 disposition in writing).
    • 18012: .changeset/18012-between-blank-endpoint-refused.md and filter.zod.ts (ruling A of 2026-09-17: a blank bound refused at the authoring door, the blank side named).
    • 19377: .changeset/19377-between-field-endpoint-runtime-door.md and filter-comparand-shape.ts (the runtime door brought under the 2026-08-11 removal).
    • 16626: .changeset/filter-orthography-binding-and-object-blocks.md on main records it as the objectui pin bump the element convergence was parked behind; the text now says "a bump of that pin", and the pin sha it names is unchanged.
    • 6083: main records it as ADR-0122 phase 2 (the spec CHANGELOG); the sentence keeps its claim ("two later, smaller driver call-parameter changes both registered") without the numbers.
  • Two cross-repo ids this session cannot read. cloud#1053 and cloud#1030 answer 403 (objectstack-ai/cloud is not reachable here, and attaching it was refused). The sentence was rewritten from what this repository records: packages/spec/CHANGELOG.md (the published DriverQuery entry: 20 measured as any sites downstream, and a $like that reached runtime through that hole) and the 5181 card body ("cloud 实测 20 处 cast … cloud#1030 的 $like 本可在类型层拦住"). Nothing the cloud cards add beyond that is claimed.
  • Short numbers and ruling-record ids went too (invisible to the regex). Six decision-batch numbers (#43 ×2, #55, #123, #146, #151), three ruling-record comment ids and one batch 217 item 3 spelling are numbers an author is shown and cannot follow, so each is dropped. So are the maintainer's batch acknowledgements 「146 同意」 and 「217 同意」 and the bare 「同意」 beside objectui#6206 (×3), 15442 and 14175: they record only that a batch was approved, and each sentence now states the ruling's date and content instead. The one verbatim quote that carries a lesson, 「the differences are the protocol's to close」, is kept untranslated.
  • "issue NNNN" / "PR NNNN" spellings, checked by hand. A scan of the five families' evaluated prose for any run of three or more digits and for issue / card / PR / batch / record / summon plus a number now finds only HTTP statuses, ports and example values. The two PR #… citations were #-spelled, so the instrument saw them.
  • One test pinned a removed tracker number. packages/spec/src/ui/action-params.test.ts asserted reason matches the 5613 number; it is re-pinned on the sentence that carries the ruling (/ruled contract-first/). No other test reads these entries' prose for a tracker id (a grep of test files for the 88 cited numbers finds only comment lines and assertions on other runtime strings).
  • No open PR touches these five families. Read twice. At 2026-09-28T19:04Z: 11 open PRs' file lists (227 rows). Again at 20:28Z, just before opening this one: 8 open PRs (195 rows; the Version Packages PR 17076 skipped both times), none carrying a migrations/entries/semantic/NN.(datasource|filter|action|data|element)-* file. PRs 20504, 20503, 20460 and 20458 add or edit entries in other families (turso-, view-, stack-, cube-) and regenerate registry.ts: ordinary concurrency, regenerate on merge. Widening the pin reds no sibling.
  • Generated projections (spec-changes.json, docs/protocol-upgrade-guide.md) are regenerated, as in stages 1–3; only the protocol-17 entries appear in them.
  • What later stages pick up (whole tree at this head, same instrument): 571 prose-field sites in the other families, 61 short #NN sites, 7 surface sites, and three cloud# sites (storage-service-list-retired, cloud-subpath-retired, cluster-driver-dangling-values-removed; the fourth, data-driver-query-omit-object, is done here).
  • Noted, not filed — the same rule outside this card's fields. Runtime strings outside the migration entries still carry tracker numbers. One read in passing: AutomationEngine's boot warning for a pausing node type that never declares resumeAuthority (packages/services/service-automation/src/engine.ts, the line that reads so the #3801 resume gate REFUSES every pause it creates) is printed to a plugin author, and resume-authority-declaration.test.ts asserts the 3801 and 3823 numbers are in it. That is the runtime-string rule this card applies, on a producer outside its surface; it is reported to the seat, not fixed here.

Line budget

Entry files: 367 changed lines (+214 / −153) across 33 files, against the stage-1 ≈400 budget. The whole diff is 871 lines (+527 / −344) in 39 files. Of the rest, registry.ts is 367, the two projections are 68 (spec-changes.json 44, the upgrade guide 24), the widened pin is 42, the re-pinned test 2 and the changeset 25.


Generated by Claude Code

…s, not tracker numbers (#20233 stage 4, wip)

Rewrite the reason / replacement / acceptanceCriteria text of the datasource-*
ADR-0087 semantic entries in form D: each cited ruling, measurement or fix is
stated in words; no tracker number remains. Text only: no id, surface,
from/to or matcher moves. registry.ts is regenerated in a later commit.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
…ot tracker numbers (wip)

Rewrite the reason / replacement / acceptanceCriteria text of the filter-*
ADR-0087 semantic entries in form D. Text only: no id, surface, from/to or
matcher moves.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
…ot tracker numbers (wip)

Rewrite the reason / replacement / acceptanceCriteria text of the action-*
ADR-0087 semantic entries in form D. Text only: no id, surface, from/to or
matcher moves.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
… tracker numbers (wip)

Rewrite the reason / replacement / acceptanceCriteria text of the data-*
ADR-0087 semantic entries in form D, the downstream-repo citation included.
Text only: no id, surface, from/to or matcher moves.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
…nges and upgrade guide; widen the pin

Rewrite the element-* ADR-0087 semantic entries in form D, regenerate
registry.ts, spec-changes.json and docs/protocol-upgrade-guide.md with their
generators, widen the os migrate meta guidance pin to the datasource-,
filter-, action-, data- and element- families (REWRITTEN floor 55 -> 88),
re-pin the one test that matched a removed tracker number on the sentence,
and add the patch changeset.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
…moved read

check:org-identifier refuses the literal removed read in author-facing
source; the action-session entry now names the alias in words. Registry,
spec-changes and upgrade guide regenerated.

Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json) — pages documenting those are invisible to this run
  • 3 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 — 137 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 4a1df19656bf5b12258ef20e5aa9e8d99c3461bd → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 4a1df19656bf5b12258ef20e5aa9e8d99c3461bd

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

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d8bcc46ad4eb6c25fc83c7487e4e06730441ec57
Local-runs: none

Head unmoved across the review (re-read at the end: same sha, open, draft). Base sha fc0db22bcf (merge base with origin/main 4a1df19656, 10 commits past base). Inputs: card #20233 (body + all 15 comments), PR #20509 (body, 39-file list, net diff against main), the head's check-runs. Read-only throughout: git archive extracts of base and head parsed by my own tokenising instruments; nothing built, run or re-run.

① Derived judgments

  1. Text only — holds, mechanically. For each of the 33 entry files, base vs head tokenised: every // and /* */ comment token byte-identical, every string literal outside reason / replacement / acceptanceCriteria identical in sequence and value (so id, surface, from / to, every matcher string), regex literals identical, the code skeleton with string runs collapsed identical, the property-key sequence identical. Only the three prose fields' evaluated values differ (reason in 33, acceptanceCriteria in 7, replacement in 5). The 33 files are exactly the PR's entry files; the four fenced files (18.field-scale-precision-integer-refused, 18.ui-form-field-precision-scale-integer-refused, 18.stack-config-default-export-unbuilt-refused, 18.cube-member-inner-name-retired) are absent from the file list; no entry file outside the 33 differs. A head-prose scan of the five families for issue / card / PR / batch / record / summon + number, objectui#, cloud# and any 3+ digit run leaves only HTTP statuses, ports, dates, ADR ids and example values — the "issue NNNN" spellings and the batch 217 item 3 spelling are gone. A raw-diff pass finds no + / - line that is not a string-literal fragment or a reason: / replacement: / acceptanceCriteria: label line.
  2. Truth of the rewrites — holds; no lesson lost, softened or overstated. Read in full against single REST reads (68 in-repo ids + comments, 8 objectui# ids; the 10 bare ids answer 404 on issues and pulls with #14478 as the 200 control; cloud#1053 / cloud#1030 answer 403):
  3. Census — reproduced exactly with my own instrument (string runs under the three keys, #\d{4,5}\b; surface separate; comment lines counted apart; 746 files, 310 semantic entries, 0 unevaluable). Base fc0db22b: whole tree 721, surface 7; datasource- 9 entries / 43 (3/38/2, 14 distinct), filter- 11 / 32 (1/30/1, 24), action- 6 / 26 (0/23/3, 19), element- 5 / 25 (2/19/4, 17), data- 6 / 24 (0/24/0, 15) = 150 (6/134/10) in 33 of 37 entries, 88 distinct ids (78 bare incl. the objectstack# spellings, 8 objectui#, 2 cloud#; no number in two spellings). Head: the five families 0/0/0/0/0, the six earlier families still 0, whole tree 571, surface 7. Lit control 17.aggregation-node-distinct-retired 7 (1/6/0) both sides. Dark control: 823 comment lines with a tracker id, both sides. Short #N{1,3}: the five families 6 → 0 (filter 2, action 1, data 2, element 1); whole tree 68 → 62 on my reading against the dev's 67 → 61 — the same delta of 6, the off-by-one an instrument-boundary difference outside these families. The 88 distinct ids equal the dev's set; nothing the three fields carry was missed, cross-repo spellings included.
  4. Pin — widened, not weakened. COVERED_PREFIXES 6 → 11; data- selects by startsWith('data-'), which matches neither datasource- nor dataset-. REWRITTEN 55 → 88, and the 33 added ids are exactly the 33 changed entries, so the floor follows from the measured count. The three it blocks and every expect are textually identical base → head (shifted 36 lines): the detector self-test, the every-prefix-non-empty check, the verbatim-block-in-stdout check and the TRACKER_ID assertion are unchanged; only the header comment, COVERED_PREFIXES and REWRITTEN moved. packages/spec/src/ui/action-params.test.ts: one regex /#5613/ → /ruled contract-first/; the head reason carries "The maintainer ruled contract-first on 2026-08-06", and the /deprecation window/ anti-removal guard and the ctx\.session\.positions acceptance guard are untouched, so the test still guards that the reason names the contract-first ruling and stays a deprecation notice, not a removal notice.
  5. Generated artifacts — exact. registry.ts parsed at base and head: 310 entries both sides, skeleton and comment tokens identical, exactly the 33 ids differ, 0 non-prose differences; all 310 head registry entries equal the head entry files byte-for-byte on surface / reason / replacement / acceptanceCriteria; 0 tracker ids remain in the eleven covered families' four fields. spec-changes.json (22 lines) and docs/protocol-upgrade-guide.md (12 lines): every one of the 34 + lines equals a head entry's prose field and every one of the 34 - lines a base entry's; only the eleven protocol-17 entries of this stage appear, as expected.
  6. Check-runs on d8bcc46a (last read after the review): 34 runs, 29 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke — roster skips), 0 failed, 2 still running: Test Core (1/6) and Test Core (5/6). Green include Lint & Repo Gates (the registry / spec-changes / upgrade-guide / generated / doc-authoring / org-identifier / changeset gates), Check Changeset, Build Core, all four Type Check jobs, TypeScript Type Check, Spec property liveness, Temporal Conformance, Dogfood Verify CLI and the three Dogfood shards, the four remaining Test Core shards, and the single-writer / single-issue / Part-of guards. The dev's local runs split across 95fb97ea and d8bcc46a are superseded by these verdicts.
  7. Changeset — every sentence true. '@objectstack/spec': patch; Clause-②: no stands alone on its own line; "some of which no longer resolve" (ten 404s), "some in other repositories" (objectui#, cloud#), "ADR ids are kept", "Text only … no entry id, surface, from / to, conversion or matching logic changes", "the generated … carry the same text" all verified above.

② Semver level

patch is correct: no accept set moves, no export, key or matcher changes, only the author-shown guidance text of 33 entries, their three generated projections, a widened pin and one re-pinned regex. Clause-②: no with no arm is the well-formed declaration; no new authorable key, no accept-set narrowing or widening, so no Clause-② review is owed beyond this at-tier record. Check Changeset and Lint & Repo Gates (check:changeset-no-major, check:adr-0087-registration) are green on the head.

③ Boundary flags

Implemented-by: claude/issue-20233-migrate-meta-tracker-free-stage-4
Reviewed-by: session_01ARcDurZ5j34RdqsGgc4jgH

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 20:58
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 9e9bb46 Sep 28, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20233-migrate-meta-tracker-free-stage-4 branch September 28, 2026 21:29
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 protocol:ui size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants