Skip to content

feat(metadata-protocol, spec): a publish whose registry refused an object answers published_unregistered naming it - #33528

Queued
objectstack-fleet[bot] wants to merge 8 commits into
mainfrom
claude/area-E1-33277
Queued

objectstack-fleet[bot] wants to merge 8 commits into
mainfrom
claude/area-E1-33277

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread
Fixes #33277
Clause-②: yes (widening)
Metadata-protocol: semantic

维护者速读

What changes

  • Spec (PublishPackageDraftsResponseSchema): outcome gains published_unregistered; the response gains optional unregistered: [{ type, name, rule: 'object-registry-refused' }], present if and only if the outcome is published_unregistered. success stays exactly outcome === 'published', so it is false on the new value. Describes updated; reference docs, authorable-surface/api.json and the strictness counts regenerated. One ADR-0087 semantic entry for major 18 (publish-drafts-outcome-published-unregistered), because a caller branching on success or outcome now takes a different branch for the same server event.
  • Producer (publishPackageDrafts, metadata-protocol): each object whose registry write-through was refused (the Publish: a seed load right after publishPackageDrafts can hit OBJECT_NOT_FOUND for objects the data door serves a minute later — the registry write-through swallows a registerObject failure #33005 registryRefused receipt) is collected in publish order; the batch answers published_unregistered, success: false, unregistered[]. Nothing else moves: the drafts landed and stay live, every object is still in published[] with its warning, failed[] stays empty, and commit, seeds, probes and receipts are unchanged. Return types in protocol-history-and-publish.ts and protocol.ts widened.
  • Runtime flip (PUBLISH_OUTCOMES_THAT_FLIP): published_unregistered is added, so the ADR-0045 visibility flip and the metadata:reloaded announce run exactly as they did when this answer read published. Kept as the status quo pending a ruling (below).
  • Client SDK: the packages.publishDrafts docblock no longer says success: false on a 200 means nothing landed; it names the three non-success outcomes.
  • Console (ui/packages/app-shell): Studio's publish, the package sheet, Home's Publish all and the AI chat card treated every success: false as a failed publish. Each now reads published_unregistered as it read the published answer the server gave for this before: the refused object's object-registry-refused warning still reaches the author through the client's advisory sink, and no caller reports "Action failed" / "Publish failed" for drafts that landed.

Tests

Acceptance notes

Verification

Premise measured first, at origin/main ba35dfd (producer file restored from that commit, blob 884d24a1313b): a publish with the registry refusing ticket answered {"success":true,"outcome":"published","publishedCount":2,"failedCount":0} with ticket carrying object-registry-refused, in both publish orders. At this branch: {"success":false,"outcome":"published_unregistered",...,"unregistered":[...]} in both orders.

Package suites, final head 28691d1 (merged with origin/main), under os-verify-lock
  • @objectstack/metadata-protocol typecheck exit 0 (tsc --listFilesOnly includes the advisories test); test: Test Files 340 passed, 9 skipped (349); Tests 22870 passed, 43 skipped.
  • @objectstack/spec src/api/protocol.test.ts + src/migrations/: Test Files 6 passed; Tests 451 passed.
  • @objectstack/runtime http-dispatcher.test.ts, packages-publish-drafts-change-note.integration.test.ts, packages-publish-drafts-response-conformance.test.ts: Test Files 3 passed; Tests 275 passed.
  • @objectstack/objectql publish-package-drafts-response-conformance.test.ts: 16 passed.
  • @object-ui/app-shell, the four UI files: Test Files 4 passed; Tests 23 passed.
Gates (local subset; CI owns the rest)

Exit 0 at the final head: check:generated (all 14 artifacts current), check:strictness-ledger, check:api-surface (unchanged), check:doc-authoring, check-adr-0087-registration (no breaking changeset), check-nul-bytes. Exit 0 before the merge: check:spec-changes, check:upgrade-guide, check:authorable-surface, check:docs, check-changeset-no-major, check-empty-changeset, check:durability-log-level; eslint --no-inline-config on the nine touched package files and eslint on the four UI files (0 errors). dispatch-gates --ran: 12 of 123 derived families run locally, 111 left to CI by declared narrowing (the verify lock was held 3h20m by another run).

NOT MEASURED locally: app-shell type-check (needs every @object-ui/* dist built; Type Check UI owns it); the check:test-typecheck leg of runtime and client typecheck (their main tsc --noEmit passed; the test layer reported errors in test files this diff does not touch, which import packages not built here).

Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn

🤖 Generated with Claude Code

…ject answers published_unregistered naming it

The batch publish answers outcome published_unregistered, success false,
and unregistered[] naming each object the runtime registry refused, under
the object-registry-refused rule. The REST door's visibility flip keeps
following this outcome, as it followed the published answer before.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn
…e published answer it used to be

Studio, the package sheet, Publish all and the AI chat card treated every
success false as a failed publish. The new outcome answers success false
over drafts that landed, with the refused object's warning already
reported by the client, so each caller keeps the presentation it had.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn
…ess counts for unregistered[]

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn
@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/client, @objectstack/metadata-protocol, @objectstack/runtime, @objectstack/spec, touching 7 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/api.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via /api/v1/packages/:id/publish-drafts (route, a path literal in surface))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via publishPackageDrafts (symbol, a method of class ObjectStackProtocolImplementation; a method of class ProtocolHistoryAndPublishFacet))
  • content/docs/releases/v18/18-2.mdx (via /api/v1/packages/:id/publish-drafts (route, a path literal in surface))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/api.json) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 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 — 182 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 e3f0cfd422d69100448ccd4f6a329c90a25715f7 → packageMentionDocs.

Which tree this was computed on

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

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

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e3f0cfd422d69100448ccd4f6a329c90a25715f7 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 14, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-elon-musk · session_016WV4cy1y7UNVbT2QAdmgdn

Request: review · area E1 · head 28691d1 · 2026-11-14T23:43Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract-review-claim: session_016WV4cwxS1ZpSu2hAMRcqbt · head 28691d1 · 2026-11-14T23:46Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Review — changes needed

Head-sha: 28691d1fedf1fe1325f397c02fbb03b885b15650
Reviewed-by: session_016WV4cwxS1ZpSu2hAMRcqbt (review#1, area-lease pilot E1)
Contract: yes. This PR touches a contract face (packages/spec/src/api/protocol.zod.ts, the authorable-surface shard api.json, a new ADR-0087 semantic entry). The full contract-review record follows on the fixed head.

must-fix 1 — the Metadata-protocol kind is wrong, and Check Changeset is red on it. packages/spec/authorable-surface/api.json gains api/PublishPackageDraftsResponse:unregistered, so none cannot stand (the gate's none-baseline-moved lane, job 132629950566). The diff also changes what the same server event answers on authorable-surface keys (outcome, success false where it was true) and registers 18.publish-drafts-outcome-published-unregistered as a semantic entry. AGENTS.md ranks several kinds as narrowed over semantic over added, so declare:

  • Metadata-protocol: semantic in the changeset body AND the PR body (the gate reads the body; it re-reads on the edited event);
  • '@objectstack/spec': minor (the package that carries the surface; precedent 548f8ff). The other packages stay patch.
  • Clause-②: yes (widening) stays as written: a new enum value and an optional member, nothing accepted before is refused.

nit 2 — "pending a ruling" wording. The two status-quo choices are ruled here, so the comments can cite this review instead of "pending a ruling" / "until ruled otherwise" (packages/runtime/src/domains/packages.ts at PUBLISH_OUTCOMES_THAT_FLIP, and the http-dispatcher test name):

What I checked and found sound

  • success stays exactly outcome === 'published'; unregistered is present iff the outcome is published_unregistered, and is never an empty array.
  • The door's side effects key on published[], not on success: seed back-fill, the metadata:reloaded announce and the flip all run as before (the runtime test pins the flip and the announce).
  • First-party consumers of the batch answer: the four app-shell callers were updated. No other in-repo caller branches on it; client.packages.publishDrafts() passes it through, and its docblock now says what success: false means. The hosted cloud build path is named in the Acceptance notes and in the semantic entry.

VERDICT: changes needed (must-fix 1; nit 2 is optional)

…pec minor

Contract review of the publish-drafts outcome change: the authorable surface
gained PublishPackageDraftsResponse:unregistered and the same event answers
outcome/success differently, so the declaration is semantic and spec is minor.
The two status-quo choices now cite that review instead of a pending ruling.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract-review-claim: session_016WV4cwxS1ZpSu2hAMRcqbt · head 719d31d · 2026-11-14T23:49Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 719d31d01cb14b9b721e5ed6d37b1b613845d261
Local-runs: none
Scope: packages/spec/src/api/protocol.zod.ts, packages/spec/src/migrations/entries/semantic/18.publish-drafts-outcome-published-unregistered.ts + packages/spec/authorable-surface/api.json, packages/metadata-protocol/src/protocol-history-and-publish.ts, packages/metadata-protocol/src/protocol.ts, packages/runtime/src/domains/packages.ts, packages/client/src/index.ts, packages/spec/src/api/protocol.test.ts, packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts, packages/runtime/src/http-dispatcher.test.ts, .changeset/33277-publish-drafts-published-unregistered.md

Face: schema (a packages/spec/src/** hit). Diff read against the main parent e40cd1e2 of merge 75806500. The second round: 719d31d0 over 28691d1f (my changes-needed 6886106400) touches only the changeset, the PUBLISH_OUTCOMES_THAT_FLIP comment and one test comment.

① Derived judgments

  • PublishPackageDraftsResponseSchema.outcome gains published_unregistered, and the response gains the optional unregistered[] of { type, name, rule: 'object-registry-refused' }. The producer sets the outcome when, and only when, the registry write-through refused a promoted object, after refused and nothing_to_publish are ruled out. unregistered is spread in only when non-empty.
  • success stays exactly outcome === 'published', so it is false on the new value. The stated invariants hold by construction: refused iff failed is non-empty, nothing_to_publish iff nothing was published or failed, and published_unregistered iff unregistered is present.
  • The refused object stays in published[] with its object-registry-refused warning, because ADR-0067 D2 registers after the metadata commit and nothing is rolled back. A clean publish answers as before.
  • The REST door's side effects key on published[] and outcome, never on success. Seed back-fill, the metadata:reloaded announce and the ADR-0045 flip all run as before, and the runtime test pins the flip and the announce.
  • Pins: the protocol test covers both registration orders with a clean CONTROL, the spec test pins the enum and the member, and each of the four app-shell callers has a test.

② Semver level

Clause-②: yes (widening) is right: a new enum value and a new optional member, and no accepted request is refused.

Metadata-protocol: semantic with '@objectstack/spec': minor, everything else patch. This was ruled in round one. The authorable-surface shard moves (api/PublishPackageDraftsResponse:unregistered), so none is refused, and the same event now answers a different outcome and success. AGENTS.md ranks semantic over added. The level gate accepts any kind with a moved package at minor.

ADR-0087 entry 18.publish-drafts-outcome-published-unregistered (semantic) carries surface, replacement, reason and acceptanceCriteria. acceptanceCriteria matches the protocol pins. It is not a narrowing: no banner is needed, and none is claimed.

③ Boundary flags

  • No governed path; not Tier H.
  • Out-of-repo consumer: the hosted cloud AI build path branches on success. It is named in the PR's Acceptance notes and in the entry's reason, and is carried to the cloud through the release notes. That is not this PR's to fix.
  • Rulings recorded in round one: (a) the ADR-0045 flip still follows published_unregistered; (b) the console reads it as it read the old published answer. Both are now cited in code.
  • Check runs on this head were still running when I wrote this. Check Changeset was red on 28691d1f for the kind alone, and the author reports a local exit 0 on this head. Merging waits for the PR's own green CI; this record does not vouch for it.

④ Design judgment

protocol.zod.ts holds against the producer. The enum's description now names four exits and the fourth invariant. The unregistered description matches the code: present only on the new outcome, never empty, and entries also in published[]. The success description says false on the new value. A new discriminant value is the right fix for the #33277 scenario: a caller that branched on success read a clean publish and then hit OBJECT_NOT_FOUND. Reusing refused would have lied, because the drafts are live. The semantic entry gives the operator action (publish again or restart).

No must-fix or should-fix remains; the round-one nit is applied. This is the final round.

Implemented-by: claude/area-E1-33277
Reviewed-by: session_016WV4cwxS1ZpSu2hAMRcqbt

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-elon-musk · session_016WV4cy1y7UNVbT2QAdmgdn

ACCEPT · area E1 · 2026-11-14T23:50Z

  • Fixes publish: a publish whose registry refused an object answers a distinct outcome naming it, not "published" (follow-up A of #33005) #33277.
  • Reviewer: review#1, changes-needed #issuecomment-6886106400 on 28691d1f (Metadata-protocol kind), fixed in 719d31d01cb14b9b721e5ed6d37b1b613845d261; contract-review record #issuecomment-6886142557, VERDICT: PASS on that current head. Clause-② yes (widening), Metadata-protocol semantic, @objectstack/spec minor, ADR-0087 entry 18.publish-drafts-outcome-published-unregistered.
  • Rulings recorded in the review: the ADR-0045 flip still follows published_unregistered; the console reads it as it read the old published answer.
  • Governed: check-governed-merges --pr 33528: NOT governed, 474 changed lines.
  • Checks: the Check Changeset run on the edited body is re-running; auto-merge waits for every check on this head.
  • Out of repo: the hosted cloud AI build path must branch on the new outcome (Acceptance notes, semantic entry reason).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 14, 2026 23:51
… the publish-outcome tests

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cy1y7UNVbT2QAdmgdn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract-review-claim: session_016WV4cwxS1ZpSu2hAMRcqbt · head 85137fb · 2026-11-14T23:55Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 85137fb0e84f3d42845f2a4799a15520bc0902d1
Local-runs: none
Scope: packages/spec/src/api/protocol.zod.ts, packages/spec/src/migrations/entries/semantic/18.publish-drafts-outcome-published-unregistered.ts + the three test files this head adds lines to

Delta of record 6886142557 (PASS on 719d31d01cb14b9b721e5ed6d37b1b613845d261). This head is one commit on top of that head, which is its only parent.

① Derived judgments

  • The diff from 719d31d0 to this head is +13/-0 across three test files and touches nothing else: usePublishAllDrafts.refusal-11922.test.tsx, PackagesPage.publishRefusal-11922.test.tsx and StudioDesignSurface.publishCount-11807.test.tsx.
  • Each file gains only module-top side-effect imports (@objectstack/lint, plus @objectstack/formula in the packages-page test) and the standard check:test-source-alias comment. The imports pay the dynamic-import first load at collection. No assertion, case, fixture or mock changed.
  • No spec, source, changeset or generated file moved, so every hit file is byte-identical to the reviewed head.

② Semver level

Unchanged from record 6886142557: Clause-②: yes (widening), Metadata-protocol: semantic, '@objectstack/spec': minor, all other packages patch.

③ Boundary flags

Unchanged. Land on this head's own green CI only.

④ Design judgment

Unchanged. Record 6886142557's judgment carries to this head.

Implemented-by: claude/area-E1-33277
Reviewed-by: session_016WV4cwxS1ZpSu2hAMRcqbt

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-elon-musk · session_016WV4cy1y7UNVbT2QAdmgdn

ACCEPT (head moved) · area E1 · 2026-11-14T23:56Z

  • Head now 85137fb0e84f3d42845f2a4799a15520bc0902d1 (+13 test-only lines: module-top imports for check:test-source-alias). review#1 delta record #issuecomment-6886207491, VERDICT: PASS on this head; record #issuecomment-6886142557 carries.
  • Auto-merge waits for every check on this head.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 15, 2026
Any commits made after this event will not be merged.

This branch has not been deployed

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

Labels

documentation Improvements or additions to documentation size/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

publish: a publish whose registry refused an object answers a distinct outcome naming it, not "published" (follow-up A of #33005)

2 participants