Repository navigation
Description
观察到的缺口
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) bootsnew ObjectQLPlugin()with NO environmentId, so the flagship showcase — a self-hosted app server whosePUT /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 条共享规则的影响面,是独立的契约决定,不该搭在一个护栏单里顺手做。
可能的方向(不预判,留给分诊/维护者)
- 照 metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086 的思路,把门的激活从
environmentId(一个行作用域键)改为按拓扑/来源判定,让「包作者自有通道」这个真实意图有一个自己的表达,而不是借用 environmentId; - 维持现状,但把「host-config 拓扑无 environmentId」本身当缺陷修掉(让该装配也绑一个 environmentId),这样短路的前提重新成立;
- 若复现不出该拓扑,关单。
Refs:#4463(门的立单与四项裁决)、#5086(同一前提的上一次修补,只移了 code-only 一处)、#6285 / PR #6708(本次核验的来处)、#6602(同族:另一处只按 environmentId 而不按 org 把关的 seam)。
Generated by Claude Code
Activity
os-zhuang commentedon Aug 8, 2026
Triage:
needs-user-decision+domain:metadata+target:v17.origin/main@cfeb9a0:packages/metadata-protocol/src/protocol.ts:2426—assertRuntimeAuthoringRulesstill opens with theenvironmentId === undefinedearly return;packages/cli/src/commands/serve.tsauto-register branch still bootsnew ObjectQLPlugin()with no environmentId (the suspect topology's in-repo producer). Premise live.AUTHORING_RULESon an end-user surface — and the [转移] 发布期护栏:多组织下「平台级 + schedule + create_record + 未显式 organization_id」拒绝——#6155 Q3=A 裁决的 lint 半边 #6285 dispatch already reserved exactly this call ("STOP and escalate, do not widen the gate's activation surface unilaterally"). The fork the maintainer owns: (1) re-key activation fromenvironmentIdto topology/source per the metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086 pattern, (2) make host-config assemblies bind an environmentId so the carve-out's premise holds again, or (3) close if the topology no longer reproduces. The factual repro (does a host config still assemble without environmentId?) is the first step of whichever execution card follows the ruling — the in-repo producer above suggests it still does, but that is one hop short of a boot-level repro.target:v17on ground ② — on the affected topology the [runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463 gate, described at its own filing as the ONLY authoring gate for Studio/MCP authors, runs zero of its rules on the end-userPUT /api/v1/meta/*surface. If the repro shows the topology is gone, pull the tag together with option 3.assertRuntimeAuthoringRulessearch — only [转移] 发布期护栏:多组织下「平台级 + schedule + create_record + 未显式 organization_id」拒绝——#6155 Q3=A 裁决的 lint 半边 #6285 (the verification source, correctly landed without touching activation) and this card. metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086 (closed) fixed the code-only slice only; An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only onenvironmentId, never on org #6602 is the org-axis sibling, different key. Clean.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 8, 2026
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):
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
os-zhuang commentedon Aug 8, 2026
Claim: PM loop round 4 (
domain:metadataseat, sticker #6367)Session:
session_01W6bLax4KMrSfnE1ydFU8DwBranch:
claude/issue-6710-gate-activation-rekeyWorktree:
objectstack-issue-6710Domain:
domain:metadataFile surface:
packages/metadata-protocol/src/protocol.ts(assertRuntimeAuthoringRulesactivation:2426) +runtime-authoring-gatetests; boot-level reproduction readspackages/cli/src/commands/serve.tsandpackages/objectql/src/plugin.ts(read/measure); if Option A's topology signal requires a source change outsidemetadata-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
os-zhuang commentedon Aug 8, 2026
Dev report (session
session_01W6bLax4KMrSfnE1ydFU8Dw, branchclaude/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 thatPUT /api/v1/meta/flow/leave_approvalfunnels into (runtime/src/domains/meta.ts:271,rest/src/rest-server.ts:5126), using #4463's own measured body (the broken-CEL approval flowrecord.owner ==):The throw is from the engine's persistence layer, not the gate — i.e. execution ran straight past
assertRuntimeAuthoringRules(which throws 422INVALID_METADATAbefore 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)(anyplugins:entry with aninit) makesshouldBootWithLibrary()returnfalse(cli/src/utils/plugin-detection.ts), soserve.ts:800skips the standalone stack (the one that bindsproj_local) and falls to the lightweight assembler atserve.ts:996-1003.examples/app-showcase/objectstack.config.ts:130is 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.
environmentIdassembleMetadataProtocolassembleMetadataProtocol/api/v1/meta/*control-plane-preset.ts:277note)Every derivable signal I evaluated is a proxy of the same class the ruling condemns: delegated-vs-built-in mount (
createMetadataProtocolPlugin()vsObjectQLPlugin's built-in — cloud'sobjectos-stack.ts:223host 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'scontrol-plane-preset.ts, outside this PR.Two supporting measurements the decision should have:
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 predatesallowRuntimeCreateand speaks only of the overlay list"). The "package-author own-channel" reading exists only in code comments.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) andobjectos-stack.ts:175(host router, in-memory driver).3. The fork (three-axis analysis in the dev report; summary here)
authoringChannel: 'package-author'oncreateMetadataProtocolPlugin()/ObjectQLPlugin). Writes the semantic down once; an assembly that forgets it gets MORE enforcement, never less. Costs a new plugin option (dispatch STOP) and a cross-repo window like ADR-0076 Step 2 (ADR-0076: remaining objectql/protocol layering work (cross-repo window) #2462) — until cloud lands it the control plane runs gated, softened by the existingOS_ALLOW_UNLINTED_METADATA_WRITEShatch ([runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463 D4). My recommendation.packages/metadata-protocol; only the server-statedsourcecarve-outs and the draft rule survive. Requires re-opening the ruling's "the carve-out stays" clause, which I am not doing unilaterally.No PR opened, per the dispatch's STOP rule. Worktree torn down;
target:v17untouched (the premise stands, so the tag is earned).Generated by Claude Code
os-zhuang commentedon Aug 8, 2026
PM: moved to
needs-user-decision(metadata seat, sessionsession_01W6bLax4KMrSfnE1ydFU8Dw)The dev's measurement (comment above) confirmed the premise at boot level: a host-config kernel assembled exactly as
serve.ts:1000builds it yieldsenvironmentId === undefined, and #4463's broken-CEL approval flow runs straight pastassertRuntimeAuthoringRulesinto 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 haveenvironmentId === undefinedand share the same assembly seam — so the signal must be minted, which requires a new plugin option. No PR was opened;target:v17stays.The decision — which expression should the carve-out get:
authoringChannel: 'package-author') plumbed throughassembleMetadataProtocol. Costs a cross-repo window withcloud(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 existingOS_ALLOW_UNLINTED_METADATA_WRITEShatch. An assembly that forgets to declare gets MORE enforcement, never less.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 inmetadata-protocol's existing gate test, plus one assembly-wiring pin inpackages/objectql(the dependency cycle keeps the assembly pin out ofmetadata-protocol).Generated by Claude Code
os-project-manager commentedon Aug 9, 2026
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 throughassembleMetadataProtocol, which only the genuine control-plane assembly sets. The 26 rules' test posture migrates with it, split as the dev recommended: posture matrix inmetadata-protocol's existing gate test plus one assembly-wiring pin inpackages/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'scontrol-plane-preset.tsdeclares the channel, the control plane runs gated in the safe direction, softened by the existingOS_ALLOW_UNLINTED_METADATA_WRITEShatch — 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
os-zhuang commentedon Aug 9, 2026
Re-dispatching under the A1 ruling — PM loop round 6 (
domain:metadataseat, 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-declarationWorktree:
objectstack-issue-6710Base:
origin/main@68feaadd6(satisfies the ≥47a4e676floor this lane requires forprotocol.tswork)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 inmetadata-protocol's existing gate test, plus one assembly-wiring pin inpackages/objectql.File surface declared:
packages/metadata-protocol/src/protocol.ts(assertRuntimeAuthoringRulesactivation region ~:2426+ the assembly entry point that threads the option) andpackages/objectql(option declaration + assembly-wiring pin), plus their tests and a changeset.cloud'scontrol-plane-preset.tsis out of this repo's scope — the PR body records the one-line follow-up. Any need to touchpackages/specgets 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
saveMetaItemwrite-gate region (~:7xxx) — region-disjoint from this card's activation region; merge-main discipline applies on both.Generated by Claude Code
os-zhuang commentedon Aug 9, 2026
os-dev report
Session
session_01W6bLax4KMrSfnE1ydFU8Dw, branchclaude/issue-6710-authoring-channel-declaration, baseorigin/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
ObjectKernelbooted withnew ObjectQLPlugin()(byte-for-byte whatserve.ts'sconfig.objects && !hasObjectQLbranch constructs), then #4463's own measured body (the broken-CEL approval flowrecord.owner ==) pushed throughsaveMetaItem:The throw is the engine's persistence layer, not the gate — the gate throws 422
INVALID_METADATAbefore any engine call, so execution had already run past it. Identical to round 4's measurement; nothing intervening closed the topology. Producer still present atpackages/cli/src/commands/serve.ts:996-1001.PM mechanism assumptions, checked:
68feaadd6authoringChannelis an e.g., semantics must be "I am the package author's channel"assembleMetadataProtocol→ activationenvironmentIdkeeps its other jobs; only this activation re-keys2. What I changed, file by file
packages/metadata-protocol/src/protocol.tsMetadataAuthoringChannel = 'environment' | 'package-author';authoringChannelfield; 4th constructor param defaulting to'environment'; the activation line becomesif (this.authoringChannel === 'package-author') return;packages/metadata-protocol/src/plugin.tsMetadataProtocolPluginOptions.authoringChannel; newAssembleMetadataProtocolOptions;assembleMetadataProtocolgains a 4th options-bag param and resolves?? 'environment'at the seampackages/metadata-protocol/src/index.tspackages/objectql/src/plugin.tsObjectQLPluginOptions.authoringChannel; private field; forwarded to the assemblypackages/metadata-protocol/src/protocol.runtime-authoring-gate.test.tspackages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.tspackages/objectql/src/plugin.authoring-channel.test.ts.changeset/authoring-channel-declaration.mdTwo design choices worth the maintainer's eye:
authoringChannel?: 'package-author'alone and not a boolean.skipAuthoringRules: truewould 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 (unlikeskipSchemaSync), so a deployment cannot switch an end-user guardrail off from outside the code; the per-writeOS_ALLOW_UNLINTED_METADATA_WRITEShatch remains the only softening, and it degrades rather than silences.environmentIdis 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
saveMetaItemtwo-tier write-gate region (#6190 in flight),packages/spec,content/docs/releases/, and thecloudrepo.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):environment⇒ GATEDpackage-author⇒ bypassedenv_test, channel omitted ⇒ GATEDenv_test, channelenvironment⇒ GATEDenv_test, channelpackage-author⇒ bypassedThree notes the pass count would hide:
envId undefined + channel package-authoris green before and after — the old code bypasses there too, for the wrong reason. Honest coverage of the surviving carve-out, but not evidence for this change; the matrix says so in its own comment.envId env_test + channel package-authorgoes red on the revert because the OLD code gates where the new code bypasses. That row is what proves the declaration is genuinely read.does not disturb the OTHER gateswent red, not green. Cause on inspection: its tenant leg saves a broken flow, and under the old keying the [runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463 gate refuses it with a 422 before the OWD posture is lint-only at runtime: env can widen a packaged object's sharingModel via OS_METADATA_WRITABLE, and external≤internal (ADR-0090 D11) is never enforced on the write path #3050 gate is reached, so the observed gate list stayed empty. The red is real and informative, but I predicted green having reasoned only about the OWD posture is lint-only at runtime: env can widen a packaged object's sharingModel via OS_METADATA_WRITABLE, and external≤internal (ADR-0090 D11) is never enforced on the write path #3050 gate's own condition, not about what precedes it.plugin.authoring-channel.test.ts: predicted 3 red, measured 3 (exactly cases 1, 3, 5). The load-bearing detail — the boot-level case failed onexpected undefined to be 'INVALID_METADATA', not on "did it throw". The unfixed build throws too (the engine's bareNo driver available, anErrorwithcodeandstatusboth undefined), so arejects.toThrow()assertion would have stayed green on the exact topology this issue is about. Every rejection-class case asserts the ADR-0112code+statuspair for that reason.Side observation: under the reverted line
pnpm --filter @objectstack/metadata-protocol buildfails 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.lockwithNODE_OPTIONS=--max-old-space-size=4096:rest/runtime/cliare 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):completed: successcompleted: successcompleted: successcompleted: successcompleted: successcompleted: successFull 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
cloudfollow-up is unexecuted by design.control-plane-preset.ts:99needsauthoringChannel: '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.sys_metadata. The gate's non-firing is the measured fact; "the row would have persisted" is inference from where the throw originates.Generated by Claude Code
Generated by Claude Code
Merge origin/main into claude/issue-6190-reject-org-scoped-write
fix(metadata-protocol): key the objectstack-ai#3050 authoring gate on…
fix(metadata-protocol,metadata-core,rest): saving or publishing a pub…
fix(metadata-protocol)!: the ADR-0010 _lock gate refuses on a host-co…