Skip to content

refactor(metadata-protocol): move the automation service's flow reads out of protocol.ts into protocol-automation-flow-reads.ts (#25467 cut 27) - #26933

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840

Cut 27 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–26 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972, #26014, #26104, #26168, #26264, #26317, #26350, #26366, #26404, #26449, #26654, #26704, #26807 and #26873. The claim is 6452474807. It takes the cut 27 proposal of the cut 26 report (6451437092), a themed group. All three members passed the AST screen and the read-seam census, and all three moved unedited.

Three ObjectStackProtocolImplementation members move unedited into one new module, packages/metadata-protocol/src/protocol-automation-flow-reads.ts (#23050 shape B). They are the protocol's flow reads from the automation service, the engine of this process:

protocol.ts loses 9 net lines (29,187 to 29,178). 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. It was restarted from main 161e00e4ea (after cut 26's merge). It names no issue, so the claim comment is the identity record. It holds the move commit 13cd879d32 and two merges of main:

protocol.ts auto-merged both times with no conflict. So the merged tree was measured against main itself as well (see "The merged tree against main").

Serial with the file's in-flight PRs

The claim named #26856, #26861 and #26880 as also editing protocol.ts and asked for a git merge-tree against each before this PR was opened. All three had merged by then:

The open PRs were checked against the head e78313a068 too. Of the 38 open, four touch this package:

main moved seven commits after the second merge: #26894, #26901, #26905, #26908, #26861, #26913 and #26915. Only #26861 touches this package (above). The derivation on e78313a068 found nothing stale, so main was not merged a third time. The PR's merge ref runs them against this cut.

The order's screen (private statics, new Error(, the read-seam census)

All three members passed:

  • No private-static read. The AST walk found no access on the ObjectStackProtocolImplementation identifier and none on this.constructor.
  • No error constructed. No body has new Error(, so no PRODUCERS row is owed.
  • Read-seam census. check-durability-degradation-log-level --list was taken on the base and again on the cut: 88 read seam(s) and an identical list once [@:][0-9]+ is stripped. The full readings are under "The durability --list".

What moves

Member Base line Visibility Body lines AST tokens (params + return + body) Reads
resolveFlowCanonicalizer :6585 private 12 134 getServicesRegistry (field)
resolveFlowRegistrationReader :6693 private 7 95 getServicesRegistry (field); module-level canonicalMetaType
flowRegistrationsOf :6711 private 9 142 resolveFlowRegistrationReader (method)

protocol-automation-flow-reads.ts has 104 lines.

The shape (#23050 shape B, as cuts 1–26 apply it)

  • The class keeps all three members. Each keeps its name, docblock, signature and position. Each body now delegates, marked [#25467 cut 27].

  • AutomationFlowReadsHost types the two members the bodies read as the class members themselves (ObjectStackProtocolImplementation['getServicesRegistry'] and ['resolveFlowRegistrationReader']). Each function's this is a Pick of the members it reads.

  • getServicesRegistry, the constructor-supplied field, is handed over as a getter (the hand-over of cuts 20, 24 and 26). So each read finds the registry at call time, as before.

  • flowRegistrationsOf's call to resolveFlowRegistrationReader is handed over as a closure, (...a) => this.resolveFlowRegistrationReader(...a), as the order asked. So that call still reaches the class method (now its stub), not the moved function directly.

  • No member changes visibility.

  • Every caller still calls the class method:

    • resolveFlowCanonicalizer: 4 callers (:20784, :21680, :25284, :25575, base);
    • resolveFlowRegistrationReader: flowRegistrationsOf and :11244;
    • flowRegistrationsOf: :10100.

    None changes.

Module-level declarations, by reader: no import leaves protocol.ts

Measured by an AST walk over each body's identifiers, against protocol.ts's top-level declarations and import bindings (property names excluded):

Name Where protocol.ts has it Other readers in protocol.ts What this cut does
canonicalMetaType its own export yes (22 references at head) stays; the new module imports it from ./protocol.js, read at call time only
FlowRegistration (type) @objectstack/spec/api yes (the two stubs' declared signatures, and others) stays; imported as a type too
StoredFlowCanonicalization (type) ./stored-migration.js yes (the stub's declared return type, and others) stays; imported as a type too

So the top level of protocol.ts changes by the new module's import alone. No import comment moves.

The published .d.ts

The series bar is ruling 6348891160: sorted-union identity, measured first, with raw identity reported when it holds. Both hold for this cut, including on the merged tree against main. The cut adds one import statement and its comment ahead of every class docblock, so the cut 25 comment-offset trap was watched. It did not fire: the raw hash is identical at both ends.

Base 161e00e4ea and head 13cd879d32 were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree. The closure was built at head by a full pnpm build (116/116). For the base build, the package's src/ was swapped tree-only to base (the new module removed) and restored from HEAD under a trap. Afterwards git diff HEAD was empty, git status was clean, and both touched blobs equalled HEAD's.

  • Normalised sha256 (the ruling's bar): c5edca148ff159d4ee49cb2a2b739a2db8695df0267142816c084fb94ca80dce at both ends, on dist/index.d.ts and dist/index.d.cts. The normaliser sorted 80 string-literal union runs. Sorted-union identity holds.
  • Raw sha256: 455d53280fb9c9226324aeb678e5da84e5a0efae88a4bf210d35c4405942d1b5 at both ends, 777,661 bytes. Raw identity holds.
  • The two builds differ where they should.
    • index.js hashes c9423dc904760728… at base and 2dcd91000df2937a… at head (index.cjs 599562da548797ec… → 362063271dc2cdba…).
    • ….call(host goes 0 → 1 for each of the three members in both bundles.
    • Each function …( declaration goes 0 → 1.
  • Positive control on the head tree. private was dropped from resolveFlowRegistrationReader on disk (restored from HEAD under a trap) and the package rebuilt. Both .d.ts files move to 409763bd3a5619d6… (normalised 2856b5a8fc02c6ba…, which differs from the bar's c5edca14…) and print resolveFlowRegistrationReader(type: string): ((name: string) => FlowRegistration) | undefined;. 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 of them. None of the three functions, and not AutomationFlowReadsHost, appears.

The merged tree against main

The merges brought in #26880's and #26856's protocol.ts changes, so the merged tree e78313a068 was measured against main 57820085fa, its second parent, in the same way. The closure was rebuilt by a full pnpm build (116/116), and main's package src/ was swapped in tree-only and restored under a trap. The merged tree differs from main only by this cut: 2 files, +123 / −28.

  • .d.ts: raw 4f48a58b638106f9… (779,140 bytes) and normalised 6b3acbaf32ddd812…, the same at main and merged. Both identities hold.
  • index.js: 9c5d5525453d5395… at main, 3f469d2dc9064d24… merged. Export tables: 78 names, 9f18a0177ac736ed… on both.
  • AST compare, main's protocol.ts against the merged one: 371 tokens, 0 differ.
  • Suite: main and merged both give 21,719 tests (21,693 passed, 0 failed, 26 skipped, 277 files), per test identical.

No changeset: skip-changeset, per ruling 6348891160.

The durability --list

check-durability-degradation-log-level exits 0 at both ends with identical output: 88 read seam(s) at both ends. Its --list is identical once [@:][0-9]+ is stripped (164 lines, and no entry changes file).

  • None of the three bodies has a try, and none reads the gate's read vocabulary (find/findOne/count).
  • No seam enters or leaves the census.
  • On the merged tree the list is identical too, and the verdict still reads 88 read seam(s).

Proof of move-only

Bodies, by AST token. The compare reads the TypeScript compiler's AST leaf tokens, with JSDoc skipped, over the parameters (less the added this), the return type and the body. It compares base protocol.ts at 161e00e4ea against the head module:

resolveFlowCanonicalizer: base tokens 134, head tokens 134, differ 0, text-equal(ws-collapsed, comments incl.) true
resolveFlowRegistrationReader: base tokens 95, head tokens 95, differ 0, text-equal(ws-collapsed, comments incl.) true
flowRegistrationsOf: base tokens 142, head tokens 142, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=371 differ=0 members=3
CLASS members base=301 head=301 order-same=true byte-identical=298 stubs-with-identical-docblock+signature=3 other=[]
TOP base=259 head=260 byte-identical=259

Negative controls, one per member. Each changed one token in an in-memory copy of the head module, and only the mutated member moved (differ 1; the other two stayed 0):

"typeof canonicalize !== 'function'"      -> "… !== 'object'"   resolveFlowCanonicalizer differ 1
"canonicalMetaType(type) !== 'flow'"      -> "… !== 'view'"     resolveFlowRegistrationReader differ 1
"typeof name !== 'string'"                -> "… !== 'number'"   flowRegistrationsOf differ 1
  • Class side. There are 301 members at base and at head, in the same order. The 298 that did not move are byte-identical to base, leading comments included. All three stubs sit at their base positions and keep their docblocks and signatures byte-identical.
  • Top level of protocol.ts. All 259 base statements are byte-identical, leading comments included. Head adds one statement, the new module's import.
  • Size. +123 / −28 over 2 files, 151 changed lines (the proposal's estimate was 140–190).

Readers of the moved lines

The tests and scripts that read protocol.ts by path, the test files in packages/, docs/qa/, scripts/ and the @objectstack/spec liveness ledgers were grepped for the three member names.

  • By path: none reads a moved line. The only hits outside the two source files are comments: the header of protocol.flow-canonicalizer.test.ts and a docblock in runtime-authoring-gate.ts. The two CHANGELOGs also mention them.
  • Behaviour, no edit owed. protocol.flow-canonicalizer.test.ts drives resolveFlowCanonicalizer through the protocol's doors. It passes at both ends, in the package suite.
  • The liveness ledgers. No row points at any of the three. Carried forward: manifest.json's dependencies row → protocol.ts#resolveWritePackageScope (cut 19), and seed.json → protocol.ts#applySeedBodies (cut 14). These are packages/spec/**, outside this card.
  • ADR anchors. check-adr-anchors exits 0 with identical output at both ends (1,897 decision-letter citations, 60,008 citations). No moved line cites an ADR.
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends.
    • The updateData/deleteData seams keep their entries at new line numbers (13898 → 13889, 13923 → 13914).
    • productionFiles is 3,810 at base and 3,812 at head. The +2 are the new file and the gitignored packages/spec/src/migrations/registry.ts that the spec build writes in the head tree, as in cuts 20–26.
  • Identical output at both ends: check-adr-anchors, check-adr-symbol-anchors, check-system-context-census, check-tenant-audit-census, check-query-options-erasure-ratchet, check-filter-alias-parity and check-durability-degradation-log-level. check-nul-bytes differs only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at 161e00e4ea.

Verification

  • tsc --noEmit in packages/metadata-protocol exits 0 at head, on the merged tree, and on the merge-tree with main 8074feb642.
  • pnpm exec eslint --no-inline-config packages/metadata-protocol exits 0 with no output (the whole touched package, with the root lint script's flags).
  • packages/metadata-protocol suite, compared per test name and status. Each leg ran vitest --maxWorkers=2 with the JSON reporter under the shared verify lock.
    • Base 161e00e4ea: 21,692 tests (21,666 passed, 0 failed, 26 skipped), 274 files.
    • Head 13cd879d32: 21,692 tests (21,666 passed, 0 failed, 26 skipped), 274 files.
    • None lost, none added, none changed status. The base leg is the package src/ swapped tree-only from 161e00e4ea (the new module removed) and restored from HEAD under a trap.
    • The merged head e78313a068 and main 57820085fa both give 21,719 tests, per test identical (0 lost, 0 added, 0 changed).
  • check-platform-checklist --changed-from origin/main (merged tree): OK, 31 anchors judged, 0 lost.
  • Governed surface. check-governed-merges --branch claude/project-thread-ybjm3h finds 0 of 2 paths in the register (13 surfaces), so the PR is NOT governed. It has 151 changed lines, under the 3,000-line human-merge bar.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 46 gates on 2 changed paths from e78313a068, with no stale-tree finding. All 46 exit 0 after the full pnpm build (116/116). dispatch-gates --ran reports 46 derived, 46 run, 0 NOT-MEASURED and 0 UNRUN, and exits 0.

Carry-forward

These stay in protocol.ts:

  • The read-seam members (cut 21): findEnvironmentRows, packagelessAddress, findServedOverlayRow, foldStoredCollection, applyObjectRegistryMutation, unmanagedImportCollisions, rollBackUnmanagedImport, readRevertableCommit and storedFlowBindingAgrees. rethrowUnlessMetadataStoreUnprovisioned cannot move at all.
  • Public members whose return type is inferred.
  • The bodies kept by a by-path reader:
    • probeRecord, getMetaItemCached, findReferencesToMeta, refuseUnmintableMetaType and metaTypeNamespaceExists.
    • viewContainerNameCollisionRefusal with overlaidShippedContainerViewNames, and reportPageRequiresAbsentAtLoad.
    • The 7 applyRegistryWriteThrough callers, readCodeLayerForCarryForward and ensureObjectStorage.
    • emitMetadataMutation, isArtifactBacked and isNestedArtifactField (the lookupArtifactItem site pins), and getOverlayRepo.
  • The private-static readers: refusePackagedBaseRemoval, packagedBaseRefusal, runPublishSideEffects, promoteDraftForPublish, publishPackageDrafts and discardPackageDrafts.
  • The field writers: warnClosureReadUnavailable and resolveSduiManifest.

🤖 Generated with Claude Code

https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6

… out of protocol.ts into protocol-automation-flow-reads.ts (#25467 cut 27)

Move-only, shape B. resolveFlowCanonicalizer, resolveFlowRegistrationReader
and flowRegistrationsOf move, unedited, into one module. The
getServicesRegistry field is handed over as a getter; flowRegistrationsOf's
call to resolveFlowRegistrationReader is handed over as a closure, so it still
reaches the class method.

Claude-Session: https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

7 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 55 of 207 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 — 11 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 e1ab32ed0b68a58096dd9d1c5e2e510e6bbb3a79 → packageMentionDocs.

Which tree this was computed on

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

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-27T22:22Z

Head e78313a068 (move 13cd879d32 plus two main merges), report 6454673268 (on #25467), claim 6452474807. No contract review is owed: there is no packages/spec/** diff, no governed path, no narrowing and no Clause-②: yes.

item reading
Shape Draft, base main, Part of #25467 (cut 27). 2 files: protocol.ts and one new module for the claimed theme, protocol-automation-flow-reads.ts (resolveFlowCanonicalizer, resolveFlowRegistrationReader, flowRegistrationsOf). skip-changeset (move-only). Clause-②: no, Metadata-protocol: none.
Move check (seat) All 28 non-blank lines the PR removes from protocol.ts are found verbatim (indent-stripped) in the new module. The added lines in protocol.ts are the import with its header comment and three stubs, each through AutomationFlowReadsHost: the two resolvers read getServicesRegistry through a getter, and flowRegistrationsOf reaches resolveFlowRegistrationReader through the class member, so callers and overrides still go through the class.
Evidence CI green on head e78313a068 (34 success, 16 skipped, 0 failed). Governed check: NOT governed, 151 changed lines. The report gives an AST compare of 371 tokens with 0 differ (each negative control differs by 1), raw and sorted-union .d.ts identity (ruling 6348891160) at both ends and on merged vs main 57820085fa, the suite at 21,692 tests per-test identical at base and head (21,719 merged and main), the read-seam census at 88 with an identical --list, and 46 of 46 derived gates.
Overlap The claim named #26856, #26861 and #26880. All three have merged and are inside this head or main; against main 8074feb642 the head merge-tree is clean, AST 0 differ, tsc 0. Open #26912 and #26928 are clean against it. #26920 and #26831 conflict with main itself on their own paths, not with this cut.
Landing 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants