Repository navigation
Commit 10ba46b
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X7JKgiM29GUcitRvoGGj5BU)_
Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Cut 14 of the `protocol.ts` split (#24586 ruling A, recorded in
`6270097840`). Cuts 1–13 landed as #25494, #25518, #25538, #25589,
#25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939 and
#25972. The claim is `6410996628`; the scope is the cut 14 proposal of
the cut 13 report, as the server seat approved it in its dispatch order,
unnarrowed.
Two `ObjectStackProtocolImplementation` members move unedited into a new
module, `packages/metadata-protocol/src/protocol-publish-batch.ts`
(#23050 shape B): the publish batch's seed apply `applySeedBodies` and
the collection of the batch's own pending declarations
`collectBatchPendingDeclarations`. `protocol.ts` loses 132 net lines
(29,620 to 29,488). The cut is move-only, and the card stays open for
later cuts.
Branch `claude/project-thread-ybjm3h` is the harness-pinned branch for
this thread, restarted from `main` `0479cd6a0a` after cut 13 merged. It
names no issue, so the claim comment is the identity record. It holds
the move commit `c2c08b1ae` and two merges of `main` (`4003f5015` with
`05f7758f0`, `721669cb1` with `a4eae0649`). Neither merge touches
`packages/metadata-protocol/`: the package's `src/` tree is `941b828d`
at `0479cd6a0a`, at `a4eae0649` and at the base of this diff.
**Serial with the file's in-flight PRs.** Measured just before this PR
was opened: of the 20 open PRs, two touch
`packages/metadata-protocol/src/`, and neither touches `protocol.ts` or
the new module. #25989 edits `protocol.runtime-authoring-gate.test.ts`
(behaviour, no by-path read) and #25986 edits
`protocol-package-install.ts` and its test.
## What moves
| Member | Base line | Visibility | Body lines | AST tokens (params +
return + body) |
|---|---|---|---|---|
| `applySeedBodies` | `:23330` | private | 79 | 400 |
| `collectBatchPendingDeclarations` | `:23526` | private | 71 | 258 |
150 body lines (the order's estimate was about 146).
## The shape (#23050 shape B, as cuts 1–13 apply it)
- **The class keeps every member** with its name, docblock, signature
and position. Each body is now one host literal plus a `.call`, marked
`[#25467 cut 14]`.
- **Typing.** `PublishBatchHost` types every member as the class member
itself (an indexed access on `ObjectStackProtocolImplementation`). Each
moved function types its `this` as the `Pick` of the host over exactly
the members its body reads. Each stub annotates its literal as
`ThisParameterType` of `typeof fn`. No member changes visibility.
- **Members read, measured by AST walk** (property and element accesses
on `this`, with `as` casts and parentheses stripped):
- `applySeedBodies` reads `engine` (a live getter) and `getMetaItem` (a
closure). **The `getMetaItem` read was not in the proposal.** The body
spells it `(this as any).getMetaItem(...)` inside the loader's metadata
adapter, so a `this.NAME` screen does not see it. Without it in the
host, the adapter would call `undefined` at runtime while `tsc` stayed
green (the `any` cast hides it). The closure calls `getMetaItem` on the
protocol at call time, so a spy set on the instance is still the one the
adapter reaches.
- `collectBatchPendingDeclarations` reads `getOverlayRepo` and
`warnClosureReadUnavailable`, both as closures.
- **What stays.** `warnClosureReadUnavailable` stays in the class, as
the order said: it writes the instance field `closureReadWarned`.
Neither moved body reads a private-static member: the AST walk found no
access on the `ObjectStackProtocolImplementation` identifier and none on
`this.constructor`, in either body.
- **Every other caller still calls the class method.**
`runPublishSideEffects` and `publishPackageDrafts` call
`applySeedBodies`; `publishPackageDrafts` calls
`collectBatchPendingDeclarations`.
## Module-level declarations, by reader
- **Two gain `export` in place, none moves.** `declaresClientRefusal`
(read by `applySeedBodies`' catch) and `memberOption` (read by
`collectBatchPendingDeclarations`). `canonicalMetaType`,
`clientFacingFailureText` and `seedRequestValidationError` were already
exported. None is re-exported from `index.ts`, so the package's export
table does not change (below).
- **Imports.** `CLOSURE_CONTEXT_KEY_BY_TYPE` loses its last reader in
`protocol.ts` and leaves the `./runtime-authoring-gate.js` import; the
new module imports it from there, beside the type
`RuntimePendingDeclarations`. The body's two dynamic imports
(`./seed-loader.js`, `@objectstack/spec/data`) move unchanged and
resolve from the same directory. The one new statement in `protocol.ts`
is the import of the new module. The new module's runtime import from
`./protocol.js` is the cycle earlier modules already use
(`protocol-batch-data.ts`, `protocol-drafts.ts`,
`protocol-publish-backfill.ts` among them): it reads only function
declarations, and only at call time.
## The published `.d.ts`
The series bar is ruling `6348891160`, sorted-union identity, measured
first. Base and head were built with `pnpm --filter
@objectstack/metadata-protocol build` against one dependency tree: the
closure built at head by a full `pnpm build` (119/119), then the
package's `src/` swapped tree-only to `0479cd6a0a` for the base build
and restored from `HEAD` under a trap (`git diff HEAD` empty, `git
status` clean and both touched blobs equal to `HEAD`'s after). Measured
twice: on the pre-merge tree (base `0479cd6a0a`, head `c2c08b1ae`) and
after merging `main` (head `4003f5015`, closure rebuilt by a full `pnpm
build`, 119/119). The readings below hold for both.
- **Raw sha256**:
`08914e65f936a2b031c3f3f215481a97f53d132eefd869e4f42d928947ebe173` at
base and at head, on both `dist/index.d.ts` and `dist/index.d.cts`,
775,874 bytes each. **Raw identity holds**, so sorted-union identity
holds too. Both members stay `private NAME;` lines.
- **Normalised sha256**:
`46ccd7e5c4db673652513d1801ec9c9f06bf34a0a7fb6ba97fb2531ef0cc544b` at
both ends (80 string-literal union runs sorted; the cut 12 normaliser).
- **The two builds differ where they should**: `index.js` hashes
`46f350a33fcbf439…` at base and `a63f0a4b6b52b222…` at head; each
function's `.call(host` site occurs 0 times in the base bundles and once
in the head bundles (ESM and CJS alike).
- **Positive control** on the head tree (on disk, restored from `HEAD`
under a trap, hash equal to `HEAD`'s after): `private` dropped from
`applySeedBodies` and the package rebuilt. Both `.d.ts` files move to
`a0203ce8529250b7…` and print the member's full signature
(`applySeedBodies(bodies: unknown[]): Promise<{ success: boolean;
inserted: number; … }>`), so the bar can see a change on these members.
- **Export tables.** The ESM and CJS bundles, loaded at runtime from the
package, each export the same 78 names at base and head; the sorted list
hashes to `9f18a0177ac736ed…` on all four. Neither moved function, the
host type, `declaresClientRefusal`, `memberOption` nor
`CLOSURE_CONTEXT_KEY_BY_TYPE` appears.
No changeset: `skip-changeset`, per ruling `6348891160`.
## Proof of move-only
**Bodies, by AST token** (the TypeScript compiler's AST leaf tokens,
JSDoc skipped, over parameters less the added `this`, return type and
body), base `protocol.ts` at `0479cd6a0a` against the head module:
```
applySeedBodies: base tokens 400, head tokens 400, differ 0, text-equal(ws-collapsed, comments incl.) true
collectBatchPendingDeclarations: base tokens 258, head tokens 258, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=658 differ=0 members=2
CLASS members base=300 head=300 order-same=true byte-identical=298 stubs-with-identical-docblock+signature=2 other=[]
TOP base=242 head=243 byte-identical=239
```
**Negative control**, one token changed in an in-memory copy of the head
module (`seeds.length === 0` to `seeds.length !== 0`, one occurrence):
```
MUTATED packages/metadata-protocol/src/protocol-publish-batch.ts "seeds.length === 0" -> "seeds.length !== 0"
applySeedBodies: base tokens 400, head tokens 400, differ 1, text-equal(ws-collapsed, comments incl.) false
collectBatchPendingDeclarations: base tokens 258, head tokens 258, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=658 differ=1 members=2
```
- **Class side.** 300 members at base and at head, in the same order.
The 298 that did not move are byte-identical to base, leading comments
included. Each of the 2 stubs sits at its base position and keeps its
docblock and signature byte-identical (everything before the body's
opening brace).
- **Top level of `protocol.ts`.** 239 of the 242 base statements are
byte-identical, leading comments included. The other three: the
`./runtime-authoring-gate.js` import losing
`CLOSURE_CONTEXT_KEY_BY_TYPE`, and `memberOption` and
`declaresClientRefusal` gaining `export`. Head adds one statement, the
new module's import.
- **Size.** +240 / −148 over 2 files, 388 changed lines (the order's
estimate was 350–420). `protocol-publish-batch.ts` has 224 lines.
## Readers of the moved lines
The seven test files the cut 13 report named were re-grepped by path for
both member names. None reads `protocol.ts` by path (no `readFileSync`,
no `protocol.ts` path literal); none names
`collectBatchPendingDeclarations`.
| File | Hits | Verdict |
|---|---|---|
| metadata-protocol `protocol.batch-verb-driver-text.test.ts` | 49
(comment); 315, 631, 661, 704 call `protocol.applySeedBodies(...)` |
Behaviour through the class method, which the stub keeps. Passes at both
ends. |
| objectql `build-probes.test.ts` | 200, `vi.spyOn(protocol,
'applySeedBodies')` | The spy replaces the class method, which is what
callers reach. Passes at head. |
| objectql `protocol-publish-package-drafts.test.ts` | 418–512 spies;
518–571 direct calls with the real `SeedLoaderService` | Spies as above.
The real-loader smoke test runs the moved body end to end, including the
`getMetaItem` adapter read. Passes at head. |
| objectql `publish-package-drafts-response-conformance.test.ts` | 283
(comment) | No edit owed. Passes at head. |
| runtime `packages-publish-drafts-response-conformance.test.ts` | 215
(comment) | No edit owed. Passes at head. |
| runtime `packages-seed-apply-disclosure.test.ts` | 8 (comment) | No
edit owed. Passes at head. |
| spec `api/protocol.test.ts` | 1291 (comment) | No edit owed (the
comment names the producer, not its file). |
- `docs/qa/platform-checklist/areas/platform-core.json:2880` anchors
`packages/metadata-protocol/src/protocol.ts#applySeedBodies+`. The
method keeps its name in `protocol.ts`; `check-platform-checklist
--changed-from origin/main` judges it (see Verification).
- Every other test or script that reads `protocol.ts` by path and names
a symbol the bodies use (`protocol.hydrate-overlay-canonical-type`,
`protocol.read-verb-canonical-fold`,
`protocol.lookup-artifact-item-call-sites`,
`protocol.register-item-call-sites`,
`protocol.object-registry-write-through-spelling`,
`protocol.view-expansion-reconcile-call-sites`) ran in the package suite
and passes at both ends; `check-durability-degradation-log-level` ran as
a hand gate (below).
- **Bracketed-refusal pin** (`PRODUCERS`): the moved bodies construct no
error (0 `new Error(`, 0 `new TypeError(`; `applySeedBodies` throws the
`seedRequestValidationError(...)` it calls, which is built in
`protocol.ts`). No row is owed, as the order said.
- **ADR anchors and decision letters.** `check-adr-anchors` exits 0 at
both ends: 86 anchored files, 58,982 citations, files 8,267 → 8,268, and
**1,844 → 1,843** decision-letter citations. The one that leaves the
anchored file is the `[ADR-0131 D6, §12]` comment in `applySeedBodies`'
body (`protocol.ts` `ADR-0131 D6` 114 → 113). The `protocol.ts` anchor
does not cover ADR-0131, and no entry paragraph describes the seed
apply, so the new module gets no entry (disclosure only, as cuts 8, 12
and 13).
- **Durability checker**: `check-durability-degradation-log-level` exits
0 at both ends with identical output, and its `--list` is identical once
`[@:][0-9]+` is stripped (159 lines).
- **Engine-double contract** (`check-engine-double-contract`): exits 0
at both ends. The `updateData`/`deleteData` seams keep their entries at
new line numbers (14085 → 14086, 14110 → 14111). `productionFiles` is
3,710 at base and 3,712 at head: +1 is the new file, +1 is the
gitignored `packages/spec/src/migrations/registry.ts`, which the spec
build writes in the head tree and the clean base tree had not built.
- **Identical output at both ends:** `check-adr-symbol-anchors`,
`check-system-context-census`, `check-tenant-audit-census`,
`check-query-options-erasure-ratchet`, `check-filter-alias-parity`.
`check-nul-bytes` differs only in the scanned file count, +1. The
base-end readings were taken in a clean detached worktree at
`0479cd6a0a`.
- **Governed surface.** `check-governed-merges --branch
claude/project-thread-ybjm3h`: 0 of 2 paths hit the register, NOT
governed; 388 changed lines, under the 3,000-line human-merge bar.
## Verification
- `tsc --noEmit` in `packages/metadata-protocol` exits 0 before and
after the merges. `pnpm exec eslint --no-inline-config
packages/metadata-protocol` (the whole touched package; it has no `lint`
script, these are the root `lint` script's flags) exits 0, no output.
- **`packages/metadata-protocol` suite**, base and head, compared per
test name and status (vitest `--maxWorkers=2`, JSON reporter, each run
under the shared verify lock, `VERDICT command-exit 0` both legs), on
the pre-merge tree (the merges changed no file of the package):
- Base `0479cd6a0a`: 21,553 tests (21,534 passed, 0 failed, 19 skipped),
268 files.
- Head `c2c08b1ae`: 21,553 tests (21,534 passed, 0 failed, 19 skipped),
268 files.
- None lost, none added, none changed status. The base leg is the
package `src/` swapped tree-only from `0479cd6a0a` (the new module
removed from the tree) and restored from `HEAD` under a trap.
- **Cross-package readers**, at head under the verify lock: objectql
`build-probes`, `protocol-publish-package-drafts`,
`publish-package-drafts-response-conformance`: 3 files, 43/43 passed,
exit 0. runtime `packages-publish-drafts-response-conformance`,
`packages-seed-apply-disclosure`: 2 files, 16/16 passed, exit 0.
- `check-platform-checklist --changed-from origin/main` (merged tree):
OK, 1 anchored file in the change set, 29 anchors judged, 0 lost. That
includes `platform-core.json`'s `protocol.ts#applySeedBodies+`.
- **Derived gates.** `dispatch-gates --commands --repo
objectstack-ai/objectstack` derives 53 gates on 2 changed paths from the
merged tree `721669cb1`, with no stale-tree warning; all 53 exit 0 after
the full `pnpm build` (119/119). `dispatch-gates --ran`: 53 derived, 53
run, 0 NOT-MEASURED, 0 UNRUN, exit 0. (Cut 13 derived 59:
`.github/workflows/ci.yml` changed on `main` between the two cuts.)
## Acceptance notes
- **A docblock that now reads narrower than the code.**
`clientFacingFailureText`'s docblock (which ships in the `.d.ts`) says
"`declaresClientRefusal` stays private on purpose: nothing outside this
file needs the raw predicate". This cut exports it from `protocol.ts`
because the moved catch reads it. Its purpose still holds at the package
boundary: `index.ts` does not re-export it and the export table is
unchanged. The sentence was left as it is, because editing it would
change the `.d.ts` this series holds byte-identical. Rewording it to
"outside this package" can ride a later cut that already moves `.d.ts`
text, or a docs-only PR.
- The proposal said `applySeedBodies` reads `engine` only. The AST walk
with casts stripped found the `(this as any).getMetaItem` read too; the
cut 15 screen below uses the same walk.
## Carry-forward
Unchanged from cut 13: public members whose return type is inferred, the
bodies kept by a reader (`probeRecord`, `getMetaItemCached`,
`findReferencesToMeta`, `packagelessAddress`,
`refuseUnmintableMetaType`, `metaTypeNamespaceExists`,
`viewContainerNameCollisionRefusal` with
`overlaidShippedContainerViewNames`, `reportPageRequiresAbsentAtLoad`,
the 7 `applyRegistryWriteThrough` callers,
`readCodeLayerForCarryForward`), and the private-static readers
(`refusePackagedBaseRemoval`, `packagedBaseRefusal`,
`runPublishSideEffects`, `promoteDraftForPublish`,
`publishPackageDrafts`, `discardPackageDrafts`) stay in `protocol.ts`.
Added this cut: `warnClosureReadUnavailable`, which writes an instance
field.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7bbc0b9 commit 10ba46b
2 files changed
Lines changed: 240 additions & 148 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
0 commit comments