Skip to content

#4463 运行时发布门在 host-config 拓扑(environmentId 未绑定)上整体不跑——#5086 只把 code-only 拒绝移出了同一条短路 #6710

Description

@os-zhuang

观察到的缺口

ObjectStackProtocolImplementation.assertRuntimeAuthoringRules(packages/metadata-protocol/src/protocol.ts,#4463 的运行时发布门)第一行是:

if (this.environmentId === undefined) return;

这条短路本身是既有设计且正确——它是 ADR-0005「控制面 / 包作者自有通道」的 carve-out,与其下方 #3050 authoring gate 的 if (this.environmentId !== undefined) 同一口径,protocol.runtime-authoring-gate.test.ts 也把它作为刻意行为钉住。本单不是要求撤掉它。

问题在于 environmentId === undefined 并不只意味着控制面内核。#5086 在同一文件里(saveMetaItem 的 code-only 拒绝块注释)留下的原话即是证据:

「…the premise the carve-out rests on ("this kernel is the package author's own bootstrap channel") is simply not true for the CLI's lightweight assembler: a host config with instantiated plugins (isHostConfig → shouldBootWithLibrary === false) boots new ObjectQLPlugin() with NO environmentId, so the flagship showcase — a self-hosted app server whose PUT /api/v1/meta/* is an END-USER surface — ran with this entire gate disengaged.」

#5086 据此把 code-only 类型的拒绝移出了 environmentId 门(「Keying authorization off a row-scoping key is what made a type-level declaration depend on deployment topology; the declaration decides it here instead」),但只移了那一处。#4463 的运行时发布门(共享 AUTHORING_RULES 的 26 条规则)仍留在短路后面。

于是在 host-config 拓扑上,PUT /api/v1/meta/* 这个终端用户面上一条作者时规则都不跑——而 #4463 立单的理由恰恰是「对 Studio 租户 / MCP-AI 作者来说这不是四道门里较弱的一道,而是唯一的一道」。

现状核验(本单只测了这些,未测的如实标出)

已实测:每条常规服务路径都绑定 environmentId,所以它们照常进门——

路径 environmentId 位置
os dev env_local(默认) packages/cli/src/commands/dev.ts:225
os start env_local(默认) packages/cli/src/commands/start.ts:197
standalone artifact stack proj_local(默认) packages/runtime/src/standalone-stack.ts:378
cloud 单项目内核 各自项目 id packages/objectql/src/plugin.ts:295

未实测:host-config 拓扑今天是否仍会走到无 environmentId 的装配。仓内有两处形状可疑——packages/cli/src/commands/serve.ts:1000 的 config.objects && !hasObjectQL 自动注册分支(new ObjectQLPlugin(),无 environmentId),以及任何 objectstack.config.ts 里手写 plugins: [new ObjectQLPlugin()] 的 host。上面引的注释是 #5086 当时的测量,不是我这轮的复测。定级前应先复现这一条:若该拓扑已随其它改动消失,本单可直接关掉;若仍在,则是一个终端用户面上的门整体失效。

为什么单独立单

发现于 #6285(PR #6708)。该单的 dispatch 明确要求「若短路使护栏在关键部署形态不可达,STOP 上报,⛔ 不得擅自拓宽 gate 激活面」。核验结论是本护栏可达(多组织部署都在上表的路径上),所以 #6285 按裁决正常落地、未动激活面。但拓宽与否会同时改变全部 26 条共享规则的影响面,是独立的契约决定,不该搭在一个护栏单里顺手做。

可能的方向(不预判,留给分诊/维护者)

  1. 照 metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086 的思路,把门的激活从 environmentId(一个行作用域键)改为按拓扑/来源判定,让「包作者自有通道」这个真实意图有一个自己的表达,而不是借用 environmentId;
  2. 维持现状,但把「host-config 拓扑无 environmentId」本身当缺陷修掉(让该装配也绑一个 environmentId),这样短路的前提重新成立;
  3. 若复现不出该拓扑,关单。

Refs:#4463(门的立单与四项裁决)、#5086(同一前提的上一次修补,只移了 code-only 一处)、#6285 / PR #6708(本次核验的来处)、#6602(同族:另一处只按 environmentId 而不按 org 把关的 seam)。


Generated by Claude Code

Activity

  1. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Triage: needs-user-decision + domain:metadata + target:v17.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Maintainer ruling (2026-08-08): conditional ruling — premise first, then direction 1 (Option A).

    Premise (falsifiable — verify before implementing): the host-config topology still assembles without environmentId. A boot-level reproduction on current origin/main is the dispatch's first deliverable (the serve.ts auto-registration branch is the suspected in-repo producer). If the premise is falsified — the topology no longer exists — report the fork and close per direction 3; do not force the implementation, and do not silently fall back to another option.

    If the premise holds: Option A — re-key the gate's activation from the environmentId proxy to an explicit topology/origin judgment, extending the #5086 pattern, so the package-author own-channel carve-out gets its own expression. The 26 rules' test posture migrates with it.

    Rationale (three-axis review):

    • Business: if reproduced, an end-user-facing PUT /api/v1/meta/* surface runs zero of the 26 authoring rules on that topology — the v17 tag is earned.
    • Long-term: environmentId-undefined is a proxy key for "control plane", and this card is itself the proof that proxy keys lie. Direction 2 would re-prop the proxy, and every future assembly variant reopens the hole; A writes the semantic down once.
    • AI-error containment: the 26 authoring rules are the primary anti-AI-error surface; a topology that silently skips them all is the largest single hole.

    target:v17 stays while the premise stands; if the reproduction falsifies it, the closure takes the tag with it.

    State: needs-user-decision removed; pm:queue added — metadata lane.

    Maintainer directive (verbatim, covering this batch of 6 inbox cards): 「接受你的建议」. Recorded by PM session session_01JaVVMrSxt7Tgi1uwEuDtH7.


    Generated by Claude Code

  3. self-assigned this
    on Aug 8, 2026
  4. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 4 (domain:metadata seat, sticker #6367)
    Session: session_01W6bLax4KMrSfnE1ydFU8Dw
    Branch: claude/issue-6710-gate-activation-rekey
    Worktree: objectstack-issue-6710
    Domain: domain:metadata
    File surface: packages/metadata-protocol/src/protocol.ts (assertRuntimeAuthoringRules activation :2426) + runtime-authoring-gate tests; boot-level reproduction reads packages/cli/src/commands/serve.ts and packages/objectql/src/plugin.ts (read/measure); if Option A's topology signal requires a source change outside metadata-protocol, declare file-by-file in the PR body and keep it minimal — a new public contract shape is a STOP.
    Serial constraints cleared: #6285/PR #6708 (the gate's newest rule) and #6602/#6621 all MERGED. Maintainer conditional ruling of 2026-08-08 governs: premise first (boot-level repro of the no-environmentId host-config topology; falsified ⇒ close per direction 3 with the measurement, take the v17 tag off with it), then Option A (re-key activation from the environmentId proxy to an explicit topology/origin judgment extending #5086's pattern; the 26 rules' test posture migrates). Round-4 siblings: #6780 (
    :7829), #6640 (~:1045) — disjoint regions.


    Generated by Claude Code

  5. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Dev report (session session_01W6bLax4KMrSfnE1ydFU8Dw, branch claude/issue-6710-gate-activation-rekey): premise HOLDS — boot-level reproduction below. But Option A cannot be implemented within the dispatch's declared scope: every candidate topology signal is either a NEW plugin option (the dispatch's explicit STOP), a verdict move on the control-plane topology (forbidden by the same dispatch), or another proxy key (the exact mistake the ruling forbids). Escalating with the measurement + the fork.

    1. Premise: CONFIRMED at boot level (origin/main @ 6de592c43)

    Not a source read — an actual kernel boot of the serve.ts host-config branch's construction (ObjectKernel + new ObjectQLPlugin(), no options, serve.ts:1000), then the end-user publish that PUT /api/v1/meta/flow/leave_approval funnels into (runtime/src/domains/meta.ts:271, rest/src/rest-server.ts:5126), using #4463's own measured body (the broken-CEL approval flow record.owner ==):

    === A. host-config topology — serve.ts:1000 `new ObjectQLPlugin()` ===
      protocol.environmentId : undefined
      PUT /meta verdict      : {"outcome":"THROWN","message":"[ObjectQL] No driver available for object 'sys_metadata'"}
      #4463 GATE FIRED?      : false
    

    The throw is from the engine's persistence layer, not the gate — i.e. execution ran straight past assertRuntimeAuthoringRules (which throws 422 INVALID_METADATA before any engine call) and into the write. The bare repro attaches no driver, which is why persistence is where it stops; the gate's verdict is the measured fact.

    Route to the branch re-verified: isHostConfig(config) (any plugins: entry with an init) makes shouldBootWithLibrary() return false (cli/src/utils/plugin-detection.ts), so serve.ts:800 skips the standalone stack (the one that binds proj_local) and falls to the lightweight assembler at serve.ts:996-1003. examples/app-showcase/objectstack.config.ts:130 is exactly that shape (plugins: [new ConnectorOpenApiPlugin(), ...]) — the flagship showcase is on the affected topology.

    2. Why Option A stops here

    The ruling requires the carve-out to stay AND the host-config topology to become gated. Those two topologies must therefore be told apart. Measured: nothing in either repo tells them apart today.

    genuine control plane host-config topology
    environmentId undefined undefined
    assembly assembleMetadataProtocol assembleMetadataProtocol
    serves /api/v1/meta/* yes (cloud control-plane-preset.ts:277 note) yes

    Every derivable signal I evaluated is a proxy of the same class the ruling condemns: delegated-vs-built-in mount (createMetadataProtocolPlugin() vs ObjectQLPlugin's built-in — cloud's objectos-stack.ts:223 host router uses the delegated one too), OS_MODE=cloud (an operator env var that would let a deployment switch off an end-user guardrail), or presence of control-plane service plugins. So the signal has to be minted and stated by an assembly — and the only assembly that can truthfully state "package author's own channel" is cloud's control-plane-preset.ts, outside this PR.

    Two supporting measurements the decision should have:

    • The carve-out has no ADR text of its own. ADR-0005 §"Whitelist enforcement" says: "Single-kernel deployments (no projectId) keep their existing behaviour (any type writable)" — a back-compat clause about the overlay whitelist, not a statement that unscoped kernels are the package author's channel. metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086's comment already noticed this ("That sentence predates allowRuntimeCreate and speaks only of the overlay list"). The "package-author own-channel" reading exists only in code comments.
    • The carve-out's only genuine consumer is the cloud control plane. Every other unscoped new ObjectQLPlugin() in either repo is an end-user-serving kernel or a test harness: cli/commands/serve.ts:1000 (the hole), objectql/kernel-factory.ts:38, verify/harness.ts:271, plugin-dev/dev-plugin.ts:302, scripts/analytics-reconcile/boot.ts, examples/app-todo/test/*; cloud: control-plane-preset.ts:99 (genuine) and objectos-stack.ts:175 (host router, in-memory driver).

    3. The fork (three-axis analysis in the dev report; summary here)

    No PR opened, per the dispatch's STOP rule. Worktree torn down; target:v17 untouched (the premise stands, so the tag is earned).


    Generated by Claude Code

  6. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    PM: moved to needs-user-decision (metadata seat, session session_01W6bLax4KMrSfnE1ydFU8Dw)

    The dev's measurement (comment above) confirmed the premise at boot level: a host-config kernel assembled exactly as serve.ts:1000 builds it yields environmentId === undefined, and #4463's broken-CEL approval flow runs straight past assertRuntimeAuthoringRules into persistence — the flagship showcase's own boot shape is on the affected topology. The Option-A implementation then hit the dispatch's own STOP rule: no signal exists today that separates the control-plane topology (whose carve-out your ruling keeps) from the host-config topology (which your ruling gates) — both have environmentId === undefined and share the same assembly seam — so the signal must be minted, which requires a new plugin option. No PR was opened; target:v17 stays.

    The decision — which expression should the carve-out get:

    • A1 — opt-in carve-out, fail-safe default (dev's recommendation; I concur). Gate active by default on every kernel; the control-plane assembly declares itself via a new option (e.g. authoringChannel: 'package-author') plumbed through assembleMetadataProtocol. Costs a cross-repo window with cloud (control-plane-preset.ts must set it — same coordination ADR-0076 Step 2/ADR-0076: remaining objectql/protocol layering work (cross-repo window) #2462 already ran); until cloud lands it, the control plane runs gated in the safe direction, softened by the existing OS_ALLOW_UNLINTED_METADATA_WRITES hatch. An assembly that forgets to declare gets MORE enforcement, never less.
    • A2 — opt-in enforcement, fail-open default. Only end-user assemblies stamp an "environment channel" origin; no cloud change. But every future assembly that forgets to stamp silently reopens this exact hole — the fail-open shape that produced this card.
    • A3 — retire the topology key entirely (most literal metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086 extension). No new option, no cross-repo window — but it permanently gates the control-plane topology, contradicting your "the carve-out itself is legitimate and stays" clause, so it needs that clause re-opened first. Honest second choice if you want to avoid the cross-repo window.

    Two supporting measurements: the carve-out has no ADR text of its own (ADR-0005's clause covers only the overlay whitelist), and its only genuine consumer in either repo is cloud's control-plane-preset.ts:99. Secondary (only if A1/A2): where the boot-level pin lives — dev recommends the split: posture matrix stays in metadata-protocol's existing gate test, plus one assembly-wiring pin in packages/objectql (the dependency cycle keeps the assembly pin out of metadata-protocol).


    Generated by Claude Code

  7. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Maintainer ruling (2026-08-09): A1 — opt-in carve-out, fail-safe default. pm:queue.

    The gate is active by default on every kernel; the package-author own-channel carve-out becomes an explicit declaration (e.g. authoringChannel: 'package-author') plumbed through assembleMetadataProtocol, which only the genuine control-plane assembly sets. The 26 rules' test posture migrates with it, split as the dev recommended: posture matrix in metadata-protocol's existing gate test plus one assembly-wiring pin in packages/objectql.

    Rationale (three-axis): the boot-level repro stands — the flagship showcase's own topology runs zero of the 26 authoring rules on an end-user PUT /api/v1/meta/* surface, so the business half is not hypothetical. Long-term, this card is itself the proof that proxy keys lie; A1 writes the semantic down once, where A2's forget-to-stamp failure mode silently reopens exactly this hole on every future assembly. For AI-error containment the asymmetry is decisive: an assembly that forgets the declaration gets MORE enforcement, never less. A3 is acknowledged as the honest no-new-option alternative but is rejected because it would overturn the standing "the carve-out stays" clause.

    Cross-repo window, accepted: until cloud's control-plane-preset.ts declares the channel, the control plane runs gated in the safe direction, softened by the existing OS_ALLOW_UNLINTED_METADATA_WRITES hatch — same coordination shape as ADR-0076 Step 2 (#2462). The cloud repo is outside this session's scope; the implementing PR records the one-line cloud follow-up in its body. New public plugin-option shape: the PR body must declare it explicitly (the prior dispatch's STOP is hereby answered — the option is authorized).

    Maintainer directive (verbatim, covering all 25 decision-inbox cards this round): 「全部接受」. Recorded by PM session session_01LGRN2cSRfggfX9B2L83bQc. Veto window open — comment to overturn.


    Generated by Claude Code

  8. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    Re-dispatching under the A1 ruling — PM loop round 6 (domain:metadata seat, sticker #6367).

    Session: session_01W6bLax4KMrSfnE1ydFU8Dw (same seat/session that claimed this card at 2026-08-08 21:41Z — that claim stands, this is its continuation, not a re-claim)
    Branch: claude/issue-6710-authoring-channel-declaration
    Worktree: objectstack-issue-6710
    Base: origin/main @ 68feaadd6 (satisfies the ≥ 47a4e676 floor this lane requires for protocol.ts work)

    The 2026-08-09 05:41Z ruling answers the STOP that ended round 4: A1 — the gate is active by default on every kernel; the package-author own-channel carve-out becomes an explicit declaration plumbed through assembleMetadataProtocol, which only the genuine control-plane assembly sets. The new public plugin-option shape is authorized. Test posture splits as recommended: posture matrix stays in metadata-protocol's existing gate test, plus one assembly-wiring pin in packages/objectql.

    File surface declared: packages/metadata-protocol/src/protocol.ts (assertRuntimeAuthoringRules activation region ~:2426 + the assembly entry point that threads the option) and packages/objectql (option declaration + assembly-wiring pin), plus their tests and a changeset. cloud's control-plane-preset.ts is out of this repo's scope — the PR body records the one-line follow-up. Any need to touch packages/spec gets a cross-seat declaration on #6298 first (precedent #6532/#5586); a second new public contract shape beyond the authorized option is a STOP.

    Round-6 sibling in flight: #6190 on the saveMetaItem write-gate region (~:7xxx) — region-disjoint from this card's activation region; merge-main discipline applies on both.


    Generated by Claude Code

  9. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    os-dev report

    Session session_01W6bLax4KMrSfnE1ydFU8Dw, branch claude/issue-6710-authoring-channel-declaration, base origin/main @ 68feaadd6. Draft PR: #6971. Implements the A1 ruling; no re-litigation, no substitution.

    1. What I measured

    Premise re-verified at boot level on this base — it HOLDS. Not a source read: a real ObjectKernel booted with new ObjectQLPlugin() (byte-for-byte what serve.ts's config.objects && !hasObjectQL branch constructs), then #4463's own measured body (the broken-CEL approval flow record.owner ==) pushed through saveMetaItem:

    PROBE environmentId     : undefined
    PROBE PUT /meta verdict : {"outcome":"THROWN","message":"[ObjectQL] No driver available for object 'sys_metadata'"}
    PROBE #4463 GATE FIRED? : false
    

    The throw is the engine's persistence layer, not the gate — the gate throws 422 INVALID_METADATA before any engine call, so execution had already run past it. Identical to round 4's measurement; nothing intervening closed the topology. Producer still present at packages/cli/src/commands/serve.ts:996-1001.

    PM mechanism assumptions, checked:

    # assumption verdict
    1 premise proven at boot level, re-verify confirmed, unchanged on 68feaadd6
    2 authoringChannel is an e.g., semantics must be "I am the package author's channel" followed — kept the name, made it a channel-name union rather than a boolean (see §2)
    3 option threads plugin → assembleMetadataProtocol → activation correct as described; wiring found exactly that shape, no deviation
    4 environmentId keeps its other jobs; only this activation re-keys correct — the two are cleanly separable; no STOP needed. Now pinned by a dedicated test

    2. What I changed, file by file

    file change
    packages/metadata-protocol/src/protocol.ts new exported type MetadataAuthoringChannel = 'environment' | 'package-author'; authoringChannel field; 4th constructor param defaulting to 'environment'; the activation line becomes if (this.authoringChannel === 'package-author') return;
    packages/metadata-protocol/src/plugin.ts MetadataProtocolPluginOptions.authoringChannel; new AssembleMetadataProtocolOptions; assembleMetadataProtocol gains a 4th options-bag param and resolves ?? 'environment' at the seam
    packages/metadata-protocol/src/index.ts exports the new type + options interface
    packages/objectql/src/plugin.ts ObjectQLPluginOptions.authoringChannel; private field; forwarded to the assembly
    packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts the 26 rules' posture matrix (6 rows) + 3 supporting cases; the old carve-out case re-spelled to declare the channel
    packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts one fixture re-spelled + one companion added — see below
    packages/objectql/src/plugin.authoring-channel.test.ts new: the assembly-wiring pin (6 cases)
    .changeset/authoring-channel-declaration.md minor for both packages

    Two design choices worth the maintainer's eye:

    • A channel-name union, not authoringChannel?: 'package-author' alone and not a boolean. skipAuthoringRules: true would be the same bytes with the opposite meaning — a kill switch any assembly could reach for to make a red publish go away. A caller must claim to be the package author. There is deliberately no env-var fallback (unlike skipSchemaSync), so a deployment cannot switch an end-user guardrail off from outside the code; the per-write OS_ALLOW_UNLINTED_METADATA_WRITES hatch remains the only softening, and it degrades rather than silences.
    • The declaration alone decides; environmentId is not AND-ed back in. A hybrid (channel === 'package-author' && environmentId === undefined) would be marginally stricter but would re-admit the retired proxy into the activation judgment, and a control plane that later gained a row scope would silently flip posture. That posture is pinned explicitly in the matrix with its reasoning, so the rule reads one way only.

    Fixture sweep beyond the edited package (the consumption-radius rule): one hit, protocol.platform-schedule-org-gate.test.ts (#6285), which carried its own copy of the carve-out pin spelled with the retired key. Disposition re-spell, not delete — the case's subject is intact, only its expression changed. It also gained a companion case, because #6285's guardrail is one of the 26 and its reach widened too; without it the file would assert only the side that stayed the same.

    Not touched: the saveMetaItem two-tier write-gate region (#6190 in flight), packages/spec, content/docs/releases/, and the cloud repo.

    3. Reverse verification — predictions vs results

    Predictions written down before running, then the activation line alone reverted to if (this.environmentId === undefined) return; with plumbing and tests left in place. Predicted 7 red, measured 8 — one miss, recorded rather than tidied away.

    protocol.runtime-authoring-gate.test.ts (predicted 4 red, measured 5; the 11 pre-existing #4463 cases stayed green):

    case predicted measured
    envId undefined, channel omitted ⇒ GATED RED RED
    envId undefined, channel environment ⇒ GATED RED RED
    envId undefined, channel package-author ⇒ bypassed green (cannot go red) green
    envId env_test, channel omitted ⇒ GATED green green
    envId env_test, channel environment ⇒ GATED green green
    envId env_test, channel package-author ⇒ bypassed RED (inverted) RED
    gated by the rules, not blanket green green
    D4 hatch covers the newly-gated topology RED RED
    does not disturb the #3050 gate green RED — MISSED

    Three notes the pass count would hide:

    plugin.authoring-channel.test.ts: predicted 3 red, measured 3 (exactly cases 1, 3, 5). The load-bearing detail — the boot-level case failed on expected undefined to be 'INVALID_METADATA', not on "did it throw". The unfixed build throws too (the engine's bare No driver available, an Error with code and status both undefined), so a rejects.toThrow() assertion would have stayed green on the exact topology this issue is about. Every rejection-class case asserts the ADR-0112 code + status pair for that reason.

    Side observation: under the reverted line pnpm --filter @objectstack/metadata-protocol build fails its DTS step (the field becomes unread). ESM/CJS bundles emit before DTS fails, so the behavioural measurement above is still valid.

    4. Tests (local) and CI (per-job conclusions)

    All local runs through flock /tmp/os-heavy-verify.lock with NODE_OPTIONS=--max-old-space-size=4096:

    @objectstack/metadata-protocol  65 files / 813 tests  passed
    @objectstack/objectql          160 files / 2758 tests passed
    @objectstack/rest               72 files / 1131 tests passed
    @objectstack/runtime           115 files / 1743 tests passed
    @objectstack/cli               100 files / 1044 tests passed
    typecheck (metadata-protocol, objectql)  Done, 0 errors
    pnpm lint (whole repo)                   exit 0
    

    rest / runtime / cli are in the sweep because this change widens enforcement — they boot kernels previously on the bypassed side. All green with no fixture edits needed.

    CI on 5901d60, per job (not the aggregate):

    job conclusion
    ESLint (carries the family gates) completed: success
    TypeScript Type Check completed: success
    Build Core / Test Core (1-3 of 3) completed: success
    Dogfood Regression Gate (1-3 of 3) + Dogfood Verify CLI completed: success
    Temporal Conformance (live PG + MySQL) completed: success
    Check Changeset / Check PR Size / ADR maintainer approval completed: success

    Full run converged: 0 pending, 0 failures, 23 check runs. Family gates also confirmed locally before push (check:engine-double-contract, check:driver-memory-census, check:error-code-casing, check:route-envelope, check:meta-type-normalized, check:init-service-contract, check:published-files, check:doc-authoring, check:docs-audit-scope, check:nul-bytes).

    5. What I could NOT verify — stated rather than omitted

    • The cloud follow-up is unexecuted by design. control-plane-preset.ts:99 needs authoringChannel: 'package-author'; the cloud repo is out of this card's scope and I did not open it, so I am relying on round 4's inventory for the claim that it is the only genuine consumer — I did not re-enumerate it this round. Recorded in the PR body as required.
    • Real control-plane behaviour during the cross-repo window is untested end-to-end. I pinned that the D4 hatch works on precisely the posture the window creates (envId undefined + channel undeclared), which is the mechanism the ruling relies on — but no live control plane was booted.
    • The probe used the bare kernel with no driver attached, so the pre-fix verdict stops at persistence rather than showing the broken row landing in sys_metadata. The gate's non-firing is the measured fact; "the row would have persisted" is inference from where the throw originates.
    • Blast radius beyond the packages I swept. I ran metadata-protocol, objectql, rest, runtime, cli plus repo-wide lint; I did not run every service/plugin package's suite. CI's Test Core shards did, and went green.

    Generated by Claude Code


    Generated by Claude Code

  10. added a commit that references this issue on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions