fix(metadata-protocol): the layered read of a shipped flow name reports the loader's body as the effective layer, as the by-name read and the list do (#21002) - #21043
Conversation
…ts the loader's body as the effective layer For a flow name the loader ships, getMetaItemLayered now decides its effective layer with the stored-row predicate the list and the by-name read already call (isShippedFlowName): the code layer is effective, and a stored row of that name stays in the overlay layer as a shadowed layer of its own scope. Every other type, and a flow name no package ships, keeps overlay-wins. Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…name agree across a cold boot Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…e engine-double ledger row its unit pin needs Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0637601f03daa6b01ba012f9cdd69dec42a3ec0d && git checkout 0637601f03daa6b01ba012f9cdd69dec42a3ec0d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b253fadfb7d1cb59448b1d7af8172e82aa245591 39ed9ac48a71c828ed30ef77711c1707dc6dd668 && git checkout -B drift-repro b253fadfb7d1cb59448b1d7af8172e82aa245591 && git merge --no-ff 39ed9ac48a71c828ed30ef77711c1707dc6dd668
node scripts/docs-audit/affected-docs.mjs --json b253fadfb7d1cb59448b1d7af8172e82aa245591
|
Contract reviewServed-tier: This is the record of record for PR #21043 at Inputs:
Check-runs on Mergeability: Disclosure is kept at the card's level: doors, roles, codes and statuses. The body's package-provenance stamps, the row's package binding, the tenant marker and the artifact's protection envelope are named abstractly here; the three layers are named by the method's own layer names, as the card and the precedent record name them; no request-body, header or field spelling appears, and no seeding step is written. ① Derived judgments(a)
(b) The registry-half predicate is not called — RIGHT; the branch would be unreachable, verified from source.
(c) The pins — RIGHT; they hold the direction's three pins and the controls, and they red without the fix as reported.
(d)
(e) The changeset
Surface inventory: no route, schema, query set, status code or exported signature changes; one method's effective layer and the flags' referent move for exactly the shipped-flow-name-with-stored-row case, through the three doors that read it; the published-snapshot door and its dispatcher twin are untouched; one ② Semver levelThe PR body's line 2 reads
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #21002
Clause-②: no
What
For a flow name the loader ships from a managed package, the layered read (
GET /api/v1/meta/flow/NAME/layers, and the deprecated layers flag on the by-name door, which uses the same helper) now reports the loader's body as the effective layer. That is the body the by-name read (GET /api/v1/meta/flow/NAME) and the flow list (GET /api/v1/meta/flow) have answered for that name since #20946 and #20913. A stored row of that name is still reported, as a separate shadowed layer of its own scope, and is never the effective layer under the package's lock and provenance flags.getMetaItemLayeredinpackages/metadata-protocol/src/protocol.tsnow decides its effective layer with the stored-row predicate PR #20942 introduced and PR #20994 reuses,isShippedFlowName, judged by name. It adds no precedence rule of its own.getMetaItemLayeredmoves inprotocol.ts: the effective-layer binding and its docblock (+29 / -2 there).lookupArtifactItem, blind to tenant-authored rows) before the registry's bare slot, and a shipped name is one that set holds by definition. SoisStoredFlowEntryOfShippedNamewould add an unreachable branch.Why
5923270373: "IngetMetaItemLayered, the effective layer for a shipped flow name is the loader's body, decided by the predicate PR fix(service-automation, metadata-protocol): a shipped flow name arms the loader's body at both boot steps, and a stored row of that name is reported as shadowed (#20913) #20942 / fix(metadata-protocol): the by-name read of a shipped flow name serves the loader's body, as the list does (#20946) #20994 put on the list and the by-name read. ⛔ No fourth precedence path." And: "The stored row appears as a shadowed layer, with its own provenance, never as the effective layer under the package's lock flags."flowis Regime C): "⛔ Never silent override, never an overlay read path". ADR-0131 D6: managed definitions are sealed.5904938166, rule 1: a body's package-provenance stamps are display only.What becomes of the stored rows themselves (keep, refuse, migrate) belongs to #15206. This PR does not decide it.
Why this PR is
Part of: the published-snapshot door does not followThe triage expected the published-snapshot read to follow the effective layer automatically. Measured, it does not.
GET /api/v1/meta/flow/NAME/published(packages/rest/src/rest-server.ts, about:8413–:8425) reads the layered answer, but it picks a layer itself: when a stored layer is present it serves that layer, and it never reads the effective one.packages/runtime/src/domains/meta.ts(about:1121–:1137) has the same shape, by source reading. It was not measured: the dogfood stack routes through the REST transport.200with the stored body, both before and after this change.The dispatch said to stop there and report, and not to edit that door in this card. The measurement and the options are in the report on #21002. #21002 remains open for that half.
Repro, before and after
Showcase composition on a database file, cold boot. A stored row is at rest under a shipped flow name, with a body that can be told apart from the loader's. There is also an organization-scoped row under a second shipped name, and an environment-wide row under a name no package ships.
origin/main2f2fa11d75/meta/flow/NAME/layers, shipped name with a stored row: the effective layerGET /meta/flow/NAMEGET /meta/flow, the entry for NAMEGET /meta/flow/NAME/published501 NOT_IMPLEMENTED(this kernel has no code/package store)Pins
packages/metadata-protocol/src/protocol.flow-layered-shipped-name.test.ts, 9 cases. It reuses the registry double of PR fix(metadata-protocol): the by-name read of a shipped flow name serves the loader's body, as the list does (#20946) #20994's unit pin: the realSchemaRegistrykey shapes, itsgetItemprecedence (the bare slot first) and its artifact lookup.packages/qa/dogfood/test/flow-shipped-name-layered-read.dogfood.test.ts, 8 cases, a new file.flow-shipped-name-by-name-read.dogfood.test.tsandflow-provenance-server-held.dogfood.test.tsare not touched.Verification, at head
39ed9ac48aprotocol.tsand both pins are byte-identical between5cdb27e44dand39ed9ac48a. The last commit adds only the changeset and the ledger row.pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2(the whole package), at39ed9ac48a: 196 files passed, 3 skipped; 2905 tests passed, 19 skipped.pnpm --filter @objectstack/metadata-protocol run typecheck: exit 0.tsc --listFilesincludes the new unit pin.vitest runover four files: the new pin, PR fix(metadata-protocol): the by-name read of a shipped flow name serves the loader's body, as the list does (#20946) #20994'sflow-shipped-name-by-name-read, PR fix(service-automation, metadata-protocol): a shipped flow name arms the loader's body at both boot steps, and a stored row of that name is reported as shadowed (#20913) #20942'sflow-shipped-name-stored-row-bootandflow-provenance-server-held. 4 files, 36 tests passed. The metadata-protocoldistcarries the fix: the guard's text is in 2 built files.pnpm --filter @objectstack/dogfood run typecheck: exit 0.--listFilesincludes the new pin.origin/mainbuild of metadata-protocol, the layered door's effective layer for the subject was the stored body. The table above shows this.Ablation. The fix was committed first (
d690943261). Each leg ran throughscripts/ablation-replace.mjsand deleted the predicate clause from the effective-layer binding. The anchor hit once, and the blob changed from6056394ec7a6toed01c7ddc863../protocol.jsfrom source, no rebuilddist, rebuilt after the mutationscripts/ablation-dist-preflight.mjsfound the guard absent from all 24 built files after the mutated build.git diff HEADis empty. After the restored build, the guard is present in 2 built files and the working tree is clean against HEAD.Derived gates.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsprinted 74 commands for this tree at39ed9ac48a. All 74 were run, each exit code captured before any pipe.--ranreconciliation: 74 derived, 74 run, 0 NOT-MEASURED, 0 unrun.check:dual-build-cjs-loadsexited 3,PREREQUISITE NOT MET. Eight packages outside this diff had nodist/in this fresh worktree. After building those eight (all turbo cache hits), it exited 0.check-changeset-fixed,check-published-list-mirrors(plain and--self-test),check:authz-resolver,check:console-injection,check:engine-double-contract,check:error-code-casing,check:i18n-stale-fill,check:published-readme-exportsandcheck-dts-references --self-test.origin/mainhas not moved since the branch point2f2fa11d75.Lint, a proven narrowing of
pnpm lint(the repo-wide run is CI's), at39ed9ac48a:.tsfiles. The.mdand.jsonfiles answer "File ignored because no matching configuration was supplied."--format json: 5 results. The 3 linted files have 0 errors and 0 warnings.eslint.config.mjsnever enables type-aware linting. All sevenparserOptionsblocks areecmaVersionandsourceTypeonly, with noproject. The only other files the config reads arescripts/slot-lookup-baseline.jsonandscripts/query-options-erasure-baseline.json, and this diff touches neither. So the diff cannot move the verdict on any untouched file.NOT MEASURED locally, declared to CI:
Deviations
Part of #21002, not a closing line. The dispatch named a closing line. The published-snapshot door half of the card is measured unresolved and is now a decision for the seat, so this PR does not close the card. The seat can rewrite the first line if it rules that half out of the card.scripts/engine-double-contract.pinned.json, one generated row. The new unit pin's engine double has afindOne, socheck:engine-double-contractrequires the coverage ledger to learn the file, through--write. The diff is exactly that one row. PR fix(metadata-protocol): the by-name read of a shipped flow name serves the loader's body, as the list does (#20946) #20994 has the same precedent.Acceptance notes
flowfirst);flowdeclares no org override;501 NOT_IMPLEMENTEDfor a shipped flow with no stored row. That kernel has no code/package store for it to fall back to. This bears on what that door could answer for a shipped name, so it is part of the report.Generated by Claude Code