Skip to content

Commit 9d9a06e

Browse files
refactor(metadata-protocol): move the save door's refusals out of protocol.ts into protocol-save-door-refusals.ts (#25467 cut 6) (#25623)
<!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4Uu2dTVig4NQQadyC11Fs5BFZ)_ Part of #25467 Clause-②: no Metadata-protocol: none Ruling-ref: 6270097840 Cut 6 of the `protocol.ts` split (#24586 ruling A, recorded in `6270097840`). Cuts 1–5 landed as #25494, #25518, #25538, #25589 and #25601. The claim is `6383176261`; the seat agreed the scope before it, including the two narrowings below, and asked for the anchor-entry check and the re-measure on merged main, both answered in the claim and below. Nine PRIVATE `ObjectStackProtocolImplementation` methods, the save door's refusals, move unedited into a new module, `packages/metadata-protocol/src/protocol-save-door-refusals.ts`, with the five lock-family declarations only they read. They are one contiguous run of the class, less three members that stay. It is move-only, and the card stays open for the later cuts. Branch: `claude/project-thread-uasfag`, the harness-pinned branch for this session, restarted from `origin/main` at `60d8fd2ea6` after cut 5 merged. The branch names no issue, so the claim comment is the identity record. **Serial with the file's in-flight PRs.** Measured just before this PR was opened, on main `7265b33e77`. None of the 11 open PRs touches `protocol.ts`, `protocol-save-door-refusals.ts` or the bracketed-refusal pin (each PR's diff read against its merge base), and `git merge-tree` of this head with each of them, and with main, is clean. Since `60d8fd2ea6`, main has touched only `build-probes.ts` and its test in this package. ## What moves | Method | Base line | Tokens | |---|---|---| | `refuseUngrammaticalMetaItemName` | `:19858` | 83 | | `defaultRecordPageSaveRefusal` | `:20040` | 626 | | `externalBindingSaveRefusal` | `:20155` | 347 | | `externalBindingDatasourceDefinition` | `:20197` | 61 | | `objectsBoundExternallyTo` | `:20213` | 296 | | `anonymousElevatedFlowRefusal` | `:20286` | 333 | | `authoredLockFamily` | `:20603` | 439 | | `saveWriteBinding` | `:20666` | 204 | | `objectNamespacePrefixViolation` | `:20710` | 54 | All nine are `private`, so none is on the series' skip list of public members with inferred return types. **Stay in the class**, inside the run, narrowed out during the trial before the claim: - **`refuseUnmintableMetaType` and `metaTypeNamespaceExists`.** `scripts/check-durability-degradation-log-level.mjs` follows the chain `saveMetaItem -> refuseUnmintableMetaType -> metaTypeNamespaceExists -> engine.findOne` (its own table, rows 3 and 5), and its `--list` carries a `metaTypeNamespaceExists` read seam. This is the rule that kept `packagelessAddress` in cut 5. - **`viewContainerNameCollisionRefusal`**, with its one helper `overlaidShippedContainerViewNames`. `protocol.lookup-artifact-item-call-sites.test.ts` reads `protocol.ts` and records this member's `lookupArtifactItem(` site. ## The shape (#23050 shape B, as cuts 1–5 apply it) - **The class keeps every method** with its name, docblock, signature and position, less `async`. Each body is now one host literal plus `fn.call(host, …)`, marked `[#25467 cut 6]`. - **Typing.** `SaveDoorRefusalHost` types every member as the class member itself (`ObjectStackProtocolImplementation['m']`). Each moved function types its `this` as the `Pick` of the host over exactly the members its body reads, measured by AST. Each stub annotates its literal as `ThisParameterType<typeof fn>`. There is no cast, and no member becomes public or protected. - **Members read**, measured by AST: - The fields `engine` and `getServicesRegistry` (the constructor-supplied function field, passed as cut 4 passed it), as getters, so each read happens when the body makes it. - These methods, as late-bound closures (`(...a) => this.m(...a)`): `artifactLockLayerAt`, `externalBindingDatasourceDefinition`, `getOverlayRepo`, `objectsBoundExternallyTo`, `overlayLockLayerAt`, `readActiveOverlayRows`, `readMetaItem`, `rethrowUnlessMetadataStoreUnprovisioned` and `storedOverlayEntries`. A spy or double set on the instance is still the one the body reaches. - No field is assigned, no moved body names `isSystem`, and the host carries no elevation member. ## Module-level declarations, by reader Each declaration the moved bodies read was checked by an AST walk over the whole of base `protocol.ts`. The walk finds every identifier reference outside the declaration itself and takes the transitive closure over the moved set. **Move with the bodies, byte-identical and unexported.** Each has 0 readers outside the moved code and carries exactly its own docblock. None is a class or a type: `LOCK_FAMILY_KEYS`, `lockFamilyOf`, `sameLockFamily`, `withLockFamily`, `authoredLockFamilyRefusal`. `item-lock.ts` declares its own, separate `LOCK_FAMILY_KEYS`, untouched. **Stay in `protocol.ts` and gain `export` in place**, the only edit to each: | Declaration | Read by code that stays | |---|---| | `MetadataIssueEntry` | `permissionSetOwnerBindingRefusal`, `protectionBlockRefusal`, `publishBackfillRefusal`, `readPublishBackfillRequest` | | `MetadataWriteFace` | `specValidationFindings`, `destructiveChangeRemedy`, `flowSaveReplaysRetired`, `saveMetaItem` | | `specValidationFindings` | `saveMetaItem` | `overlaySchemaDeclaredKey`, also read by the moved code, is already exported. `src/index.ts` exports from `./protocol.js` by name, so none is published (measured below). **Imports narrowed in `protocol.ts`**, each having lost its last reader: `METADATA_ITEM_NAME_PATTERN`, `defaultRecordPageObject`, `MetadataProtectionFields`, `validateObjectNamespacePrefix`, and the whole `@objectstack/spec/api` value import. That import's 3-line `[ADR-0138 D2b]` comment moves with it onto the new module's import of the same names. `protocol.ts` gains one statement, the import of the new module. ## The published `.d.ts` Under the series bar (ruling `6348891160`), measured first on merged main as the seat asked, on the final layout, at base `60d8fd2ea6` and at head. Both ends were built with `pnpm --filter @objectstack/metadata-protocol build` against the same dependency tree (`turbo run build --filter='@objectstack/metadata-protocol^...'` at base first). - **Raw sha256** of `dist/index.d.ts` and `dist/index.d.cts`: `25da084d43455d125bfd41b85b1e70477ef7caf77f2cb7da23143b325e51a14e` at base and at head, both files. That is **raw identity**: no moved declaration prints anywhere new, and no docblock splices onto a neighbour where a declaration gains `export`. - **Export tables.** The ESM and CJS bundles each export the same 78 names at base and head, loaded at runtime (`import()` and `require` of the built bundles). The sorted list hashes `e47ac7c753bcb8bee1e79c1a5f79f7394cf030bd526de81ea8bcbfcb71fa024a` on all four, as at cuts 1–5. None of `MetadataIssueEntry`, `MetadataWriteFace`, `specValidationFindings` or the moved declarations appears. No changeset: `skip-changeset`, per ruling `6348891160`. ## Proof of move-only - **Bodies and declarations, by AST token** (recursive `getChildren` leaves). For each moved function, the parameters (less the added `this`), return type and body have the same token sequence as the base method. Each moved declaration has the same tokens, and its own docblock is byte-identical. 2,966 tokens compared, 0 differ. Negative control: one changed token makes the check report a difference. - **Class side.** The class has 299 members at base and at head. The 290 that did not move are byte-identical to base, and each of the 9 stubs keeps its docblock and signature byte-identical, less `async`. - **Top level of `protocol.ts`.** 230 of the 243 base statements other than the class are byte-identical with their leading comments. The other 13 are the 5 moved declarations, the 3 that gain `export`, and the 5 import statements narrowed or removed. The one new statement is the import of the new module. - **Size.** +551 / −321 over 3 files, 872 changed lines. `protocol.ts` goes from 30,548 to 30,312 lines, and `protocol-save-door-refusals.ts` has 464. ## Readers of the moved lines - **Bracketed-refusal pin** (`protocol.bracketed-refusal-opener-absence.test.ts`, `PRODUCERS`). 5 `new Error(` sites move. The new module is listed with its measured count as its floor, `'protocol-save-door-refusals.ts': 5`, as cuts 3 and 5 listed theirs. That is the only edit outside the move. No moved site opens with a bracketed tag. - **Durability checker** (`check-durability-degradation-log-level --list`): identical at both ends apart from line numbers (159 lines), with the two members above kept. - **ADR anchors and decision letters.** `check:adr-anchors`: 85 → 85 anchored files. Decision-letter citations on the anchored surface go **1,871 → 1,868**, measured exactly at both ends. The three that leave: `ADR-0138 D2b` in the moved import comment, `ADR-0138 D2b` in `anonymousElevatedFlowRefusal`'s body, and `ADR-0131 D6` in a moved body. - **Anchor entry.** `scripts/adr-anchors/packages__metadata-protocol__src__protocol.ts.json` governs ADR-0005/0029/0087/0119/0126 and names `applyObjectRegistryMutation`, `loadMetaFromDb`, `removeObjectOverlay`, the `OBJECT_OVERLAY_PACKAGE_MISMATCH` producer, `batchData`/`transaction(`, `saveMetaItem`/`projectStorableViewBody`/`VIEW_CONSOLE_ROUND_TRIP_KEYS`, `hydrateOverlayIntoRegistry`, `readFlattenedMetaItems` and `isShippedFlowName`/`isStoredFlowEntryOfShippedName`. None appears in the new module (0 hits), and both `OBJECT_OVERLAY_PACKAGE_MISMATCH` producers stay in `protocol.ts`. The new module gets no entry. - **Identical output at both ends:** `check-adr-symbol-anchors`, `check-platform-checklist` (`protocol.ts#objectNamespacePrefixViolation` resolves on the shell, which stays; not edited), `check-system-context-census`, `check-tenant-audit-census`, `check-query-options-erasure-ratchet` (no counted site moves; no file key added), and spec `check-liveness`. - **Engine-double contract.** `check-engine-double-contract` differs only in `productionFiles` +1 (the new file). - **Governed surface.** `check-governed-merges --branch` on the final 3-path list: NOT governed. 872 changed lines, under the 3,000-line human-merge bar. - **Path readers.** Files that name a moved method (`protocol.restore-doors-save-refusal-family.test.ts`, `protocol.item-name-every-door.test.ts`, `protocol.item-name-grammar.test.ts`, objectql `default-record-page-save-door.test.ts`, service-datasource's `datasource-admin-service.ts` docblock, the platform checklist) name it in prose or exercise it through the instance. None reads the moved lines by path, and all pass. ## Verification - `tsc --noEmit` (package) exit 0. `eslint` on `protocol.ts` and `protocol-save-door-refusals.ts` exit 0. - **`packages/metadata-protocol` suite**, base and head, compared per test name and status. Base `60d8fd2ea6`: 21,490 tests (21,471 passed, 0 failed, 19 skipped), 266 files. Head: 21,491 (21,472 passed, 0 failed, 19 skipped), 266 files. None lost, none changed status, one added: the new `PRODUCERS` row, `protocol-save-door-refusals.ts raises no message opening with a bracketed lowercase tag` (passed). - **Tests outside the package that read `protocol.ts` by path or name these methods**, run at head, all pass: - `core` `row-serving-door-exposure.pin` and `second-object-read-exposure.pin` (7, at both ends); - `objectql` `core-boundary.ratchet`, `protocol-publish-canonical-fold` and `default-record-page-save-door` (46); - `spec` `metadata-type-registry-allow-org-override-absence` and `scripts/liveness/evidence` (44); - `client` and `rest` `discovery-double-retired-key` (3 + 2). - **Hand gates at base and head**, all exit 0, identical output except the letter count above: `check-adr-anchors`, `check-adr-symbol-anchors`, `check-platform-checklist`, `check-system-context-census`, `check-tenant-audit-census`, `check-query-options-erasure-ratchet`, `check-durability-degradation-log-level --list`, and spec `check-liveness`. - **Derived gates.** `dispatch-gates --commands --changed` derives 59 gates on the final tree (3 changed paths). All 59 exit 0 at head, and `--ran` reconciles 59 of 59 with 0 NOT-MEASURED. The full list and the `--ran` reconcile are in the report on this PR. ## Carry-forward These stay in `protocol.ts` in this series: - **Public members whose return type is inferred.** Annotating the type would be a signature edit and needs a ruling. Later cuts skip `getDiscovery`, `getMetaTypes`, `getMetaItems`, `getMetaItemsForExecution`, `getMetaItem`, `getMetaItemForExecution`, `getUiView`, `findData`, `getData`, `validateData`, `createData`, `cloneData`, `updateData`, `deleteData` and `saveMetaItem`. - **Bodies kept by a reader:** - `probeRecord` (per-file erasure-baseline site); - `getMetaItemCached` (the #9157 pin reads its body by path); - `packagelessAddress`, `refuseUnmintableMetaType`, `metaTypeNamespaceExists` (the durability read-seam census); - `viewContainerNameCollisionRefusal` and `overlaidShippedContainerViewNames` (the `lookupArtifactItem(` call-site pin). - **Private-static readers.** The inferred-return `.d.ts` still depends on `protocol.ts` byte offsets (cut 3's series note), so each cut measures it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01J8pUyVs5eas1AjMdReJM7C Co-authored-by: Claude <noreply@anthropic.com>
1 parent e8de8a7 commit 9d9a06e

3 files changed

Lines changed: 551 additions & 321 deletions

File tree

0 commit comments

Comments
 (0)