Skip to content

The publish door discards the runtime authoring gate's advisory findings — and Studio's designer only ever uses that door #9176

Description

@os-zhuang

Measured on origin/main @ 66beee0f3 while taking #4716's cost measurement. Filed unassigned, not a claim.

What was measured

The runtime authoring gate runs on both metadata write doors (#4463 D1, deliberately, so ?mode=draft then /publish is not a bypass). Only the save door puts its advisory findings on the response.

packages/metadata-protocol/src/protocol.ts — the gate hook's own docblock states it plainly:

[#4717] Throws on the gating half, RETURNS the advisory half. Advisories do not block anything, so the only honest place for them is the 2xx the write earns — saveMetaItem attaches them to its response, and the only other caller (the draft-to-active promotion in publishMetaItem) simply ignores the value

Confirmed against the tree: runtimeAdvisories is referenced at exactly two lines (12621 gather, 12943 attach), both inside saveMetaItem. advisories does not appear in PublishMetaItemResponseSchema (packages/spec/src/api/protocol.zod.ts:588), which declares success / version / seq / seedApplied / materializeApplied / projectionApplied and no finding channel.

Why this is worth filing now rather than when it was deferred

It was a knowing deferral, and the reason it was deferred is discharged.

The same card also recorded why it matters: "Studio's designer uses draft-then-publish on every edit." So the rendering that objectui shipped is wired to the door Studio's designer does not take. A Studio tenant or an MCP/AI author has no os lint and no CLI config for sys_metadata overlay rows — for them this gate is not the weakest of four doors, it is the only one, and on their actual edit path its advisory half is still produced and thrown away server-side.

Shape of the fix (not a proposal, just the surface)

publishMetaItem's promotion call site ignores the hook's return value; PublishMetaItemResponseSchema would need the same optional, omitted-when-empty advisories key SaveMetaItemResponseSchema already carries (RuntimeAuthoringIssueSchema elements, declared once in packages/spec). The save door's conformance suite pins both directions of the optional key and is the precedent to copy.

Related

#4463 (D1/D3), #4717 (the save-door half), #7294 / PR #7356 (the publish door's response contract), objectui#4133 / objectui PR #4236 (the Studio rendering), #4716 (the card this was measured under).

Activity

  1. os-project-manager commented on Aug 16, 2026

    @os-project-manager
    Collaborator

    Triage: lands in packages/metadata-protocol (the publishMetaItem promotion call site) plus packages/spec (PublishMetaItemResponseSchema gains the same optional, omitted-when-empty advisories key the save door carries) → domain:spec (spec-touching card, spec seat is sole owner) + pm:queue, type Feature (extends a public response face).

    Rationale: no live design question — the mother ruling #4463 D1 already put the gate on both doors, the save-door response shape is ruled and shipped (protocol.zod.ts:529, re-verified on origin/main this round, and confirmed absent from the publish response schema), and the recorded deferral reason (#7294) closed completed on 2026-08-10. Same-family reuse of the existing ruling; the save-door conformance suite is the stated precedent to copy.

    Size/model suggestion: M; claude-fable-5 floor — this widens a public contract face (standing tiering rule), even though the shape is a mirror of an already-ruled key.

    Serial note for the spec seat: touches protocol.ts, which currently has #9111 in flight (PR #9173) and #9157 newly queued (metadata lane, same file) — clear the hot-file serial queue before dispatch.


    Generated by Claude Code

  2. self-assigned this
    on Aug 17, 2026
  3. os-steve commented on Aug 17, 2026

    @os-steve
    Collaborator

    Unlock note + Claim: PM loop round 5
    Session: session_01Fs18A2DdXLVN2h8PaaFBcP
    Branch: claude/issue-9176-publish-door-advisories
    Worktree: objectstack-issue-9176
    Domain: domain:spec
    File surface: packages/metadata-protocol/src/protocol.ts (region: publishMetaItem's promotion call site consuming the gate hook's return), packages/spec/src/api/protocol.zod.ts (region: PublishMetaItemResponseSchema gains the optional omitted-when-empty advisories key), the save-door conformance suite's publish twin, .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: fable — clause ② per triage's note (widens a public contract face); dispatch-gates --tier flags protocol.zod.ts as clause-② suspect surface
    Serial constraints cleared: the protocol.ts hot-file queue this card waited on is now empty — PR #9191 (#9157) merged 08:00Z and PR #9173 (#9111) merged 11:16Z (1d5ba9e9a); re-verified this round that no third open PR touches protocol.ts. Premises re-verified on post-merge origin/main: runtimeAdvisories gathered at protocol.ts:12766 and attached only in saveMetaItem (:13088); advisories appears in protocol.zod.ts only on the save response (:529) and a comment (:1815) — absent from PublishMetaItemResponseSchema. Same-day churn: both hot-queue PRs merged TODAY on this exact file — line numbers in the card body have drifted, work from current main.


    Generated by Claude Code

  4. os-steve commented on Aug 17, 2026

    @os-steve
    Collaborator
    {
      "issue": 9176,
      "status": "done",
      "branch": "claude/issue-9176-publish-door-advisories",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9344",
      "premise_still_valid": true,
      "summary": "Mirrored the ruled save-door key onto the publish door: promoteDraftForPublish now returns the #4463 gate's advisory half (previously discarded at the promotion call site) and publishMetaItem attaches it omitted-when-empty; PublishMetaItemResponseSchema gains the same optional advisories key (RuntimeAuthoringIssueSchema elements). PM assumption 1 refined, not forked: the discard sits one frame below the response builder (in promoteDraftForPublish, whose result publishMetaItem consumes), so the wiring is a local return-type widening; the batch caller publishPackageDrafts destructures around the new key, no behaviour change (its own discard filed as follow-up). Directional conformance cases (present-with-substance / absent-not-empty / 422 envelope unchanged / labelled guard) copied from the save-door precedent, plus spec-side declaration pins. Changeset: minor for spec + metadata-protocol (precedent level not discoverable from the shallow clone).",
      "tests": "All at 844a8b2d9 unless noted: spec suite 408 files/10877 green (pin file re-run 56/56); metadata-protocol 117/1617 green; objectql full suite 213/3769 green (both conformance files re-run 23/23; package-suite runs took the tree at 7138c1e2c, differing from final head only by the regenerated reference mdx, no test input); check:generated all 13 artifacts current; check:type-check-debt --re-measure 33 entries none above recorded; ~19 derived gates green (nul-bytes, merge-driver, cross-package-test-inputs, engine-double-contract, where-matcher, changeset family, docs family, etc.). Reverse verification from committed state, direction predicted first, two layers observed: (1) removing the declaration alone is REFUSED by gen:schema's authorable-surface guard (dist stays stale, suite stays green on old dist); (2) removing declaration + shard line rebuilds (marker proven gone from dist/api/index.js) and the directional case goes red exactly as predicted: strippedKeys ['advisories'], 1 failed/12 passed; restored and re-run green 13/13. CI on the PR not awaited per standing clause; gate status in_progress at report time.",
      "open_questions": [],
      "out_of_scope_findings": ["filed as #9343: publishPackageDrafts (Studio's publish-whole-app) still discards the gate's per-draft advisories — batch response face needs its own per-item mapping decision, unassigned, no label"]
    }

    Generated by Claude Code

  5. os-steve commented on Aug 17, 2026

    @os-steve
    Collaborator

    ACCEPT — PR #9344 (reviewer of record: spec seat, session session_01Fs18A2DdXLVN2h8PaaFBcP, round 5).

    Verified on GitHub (file list cross-checked via the branch diff; the PR-files API answered 404 transiently):

    • The ruled mirror is exact: PublishMetaItemResponseSchema gains the save door's optional, omitted-when-empty advisories key (declared once, RuntimeAuthoringIssueSchema); promoteDraftForPublish returns the previously-discarded advisory half and publishMetaItem attaches it. The gating half (422 INVALID_METADATA) is pinned unchanged.
    • Directional conformance per the save-door precedent: substance-asserting present case, ABSENT-not-empty-array case, gating-half case, labelled guard — plus spec-side declaration pins including refusal cases.
    • Mechanism assumption 1 refined, not forked, and documented: the discard sits in promoteDraftForPublish, one frame below the response builder; wiring is a local return-type widening; both callers verified, the batch caller unchanged.
    • Reverse verification is two-layer and honest: the authorable-surface ratchet catches bare declaration removal before any test can (itself a useful reading), and the dist-ablated leg goes red exactly as predicted (strippedKeys ['advisories']), restored byte-clean.
    • Clause-② enqueue gate: diff touches protocol.zod.ts (contract surface); dispatch tier claude-fable-5 = CONTRACT_REVIEW_TIER — at the floor, may enqueue.
    • Changeset: minor for both published packages; the follow-up for the batch door's own discard is filed as publishPackageDrafts still discards the runtime authoring gate's per-draft advisories — Studio's "publish whole app" reports none of them #9343 (unassigned, awaiting triage) rather than scope-crept here.

    Landing: flips ready after ESLint + TypeScript Type Check conclude success at head 844a8b2d9. Generated-artifact note: sibling #9227 (in flight, this seat) will also carry generated artifacts — if both are pending enqueue simultaneously, they land one at a time per the serial-relay rule.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions