Skip to content

fix(rest)!: answer 404 RESOURCE_NOT_FOUND for an unknown resource at the package draft, flow runs and datasource by-name doors - #25628

Draft
objectstack-fleet[bot] wants to merge 7 commits into
mainfrom
claude/issue-25486-unknown-resource-404
Draft

objectstack-fleet[bot] wants to merge 7 commits into
mainfrom
claude/issue-25486-unknown-resource-404

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25486

Clause-②: no (narrowing)
Metadata-protocol: none

BREAKING (a served-set narrowing of eight REST doors): a request that names a package id, flow name or datasource name that does not exist is now refused 404 RESOURCE_NOT_FOUND. Five of these doors used to answer it 200 with an empty result, and three answered 400 DATASOURCE_ADMIN_ERROR. The changeset ships patch with the ADR-0087 disposition not-required (no-migration-prescription): no metadata key changes, only the HTTP answer for a resource that is not there.

What changes

Eight doors answered a request about a resource that does not exist as if it were empty or malformed, while their sibling doors answered 404 for the same name. Each now answers 404 RESOURCE_NOT_FOUND with its sibling's status, code and message. RESOURCE_NOT_FOUND is an existing standard code; no new code is registered.

door before after sibling it now matches
POST /api/v1/packages/:id/publish-drafts (unknown id) 200 { success: false, outcome: 'nothing_to_publish' } 404 "Package 'ID' not found" GET /api/v1/packages/:id
POST /api/v1/packages/:id/discard-drafts (unknown id) 200 { success: true, discardedCount: 0 } same 404 GET /api/v1/packages/:id
GET /api/v1/packages/:id/commits (unknown id) 200 { commits: [] } same 404 GET /api/v1/packages/:id
GET /api/v1/automation/:name/runs (unknown flow) 200 { runs: [], hasMore: false } 404 "Flow 'NAME' not found" POST /api/v1/automation/:name/trigger
DELETE /api/v1/automation/:name (unknown flow) 200 { name, deleted: true } same 404 POST /api/v1/automation/:name/trigger
PATCH /api/v1/datasources/:name (unknown name) 400 DATASOURCE_ADMIN_ERROR "Datasource 'NAME' not found." 404 'Datasource "NAME" does not exist.' GET /api/v1/datasources/:name, word for word
DELETE /api/v1/datasources/:name (unknown name) same 400 same 404 GET /api/v1/datasources/:name
POST /api/v1/datasources/:name/migrate-credential (unknown name) same 400 same 404 GET /api/v1/datasources/:name

A resource that exists keeps its answer: an empty runs page, nothing_to_publish, an empty commit trail, and 400 DATASOURCE_ADMIN_ERROR for a code-defined datasource are all unchanged.

Where absence is decided, per door

  • Datasource (the card's A1). The producer is DatasourceAdminService: updateDatasource, removeDatasource and migrateCredential threw a plain Error. They now throw datasourceNotFoundError(name), which carries code: 'RESOURCE_NOT_FOUND' and status: 404 (the same shape as the service's existing metadataIncompleteError 503). Its message is GET's own words, Datasource "NAME" does not exist.. The route's error renderer (badRequest in admin-routes.ts) relays that envelope, exactly as it already relayed the 503. All four by-name doors of datasource-admin are covered; GET was already right.
  • Runs (A2). GET /:name/runs now asks flowIsUnknown (packages/runtime/src/flow-dispatch-status.ts) before listing. That is the trigger door's own registry probe and message, reused rather than written a second time. It is asked before the query is judged, as the trigger door asks it. A service without getFlow cannot be asked, so it lists as before.
  • Flow delete. Measured before the fix: 200 { name, deleted: true }. unregisterFlow is a no-op for a name the engine does not hold, and deleteMetaItem answers a name with no row { success: true, reset: false } ("nothing to delete"). unregisterAndDeleteFlow now asks flowIsUnknown. It answers 404 with flowNotFoundMessage only when the engine did not hold the flow AND the store deleted no row (reset !== true).
  • Publish-drafts (A3). Measured. The registry (qlService.registry.getPackage) is the existence authority every package read sibling answers its 404 from. Authored packages are installed into it too (POST /packages, service-package at boot). The runtime packages domain decides absence, so packages/metadata-protocol/src/protocol.ts is untouched, and no [maintainer] refactor(metadata-protocol): split src/protocol.ts (33,065 lines, +10,169 in 30 days) along its ObjectStackProtocolImplementation seams, one move-only slice per PR #25467 region is near this diff. The 404 is answered only when the protocol's answer was empty (nothing_to_publish), nothing was un-hidden, no visibility-flip failure was recorded (unhideError), and the registry lacks the id. Drafts the protocol holds for such an id are still published.
  • Discard-drafts and commits. These use the same packageIsUnknown rule, after the protocol answers and only when that answer is empty. Drafts or commits held for an id the registry lacks are still discarded or listed. discard-drafts is the twin of publish-drafts, fixed in place under os-dev rule 3's bounded exemption. commits and flow delete were added in the seat's patch round, as members of this closing card's family in the files this PR already edits.

Consumers (A4)

ObjectStackClient.fetch throws on any non-2xx answer, using the envelope's message. So packages.publishDrafts, packages.discardDrafts, packages.listCommits, automation.listRuns and automation.delete now reject loudly for an unknown id; there is no crash path. No in-repo non-test consumer calls these doors for an id it did not first install or register: the dogfood callers all POST /packages first. objectui consumers: see trade-off (b) below.

Accepted trade-offs (recorded; no code change)

  • (a) An orphan commit trail, whose package the registry no longer lists but which still has commits, is still served 200. GET /api/v1/automation/:name/runs/:runId still answers by run id alone, as before.
  • (b) A draft-only flow, stored but not registered in the engine, now gets 404 at GET /:name/runs. Before, it got an empty page. objectui's FlowRunsPanel.tsx (around lines 240–244) renders that rejection as "unavailable".
  • (c) DELETE /:name counts existence in the store or the engine, while runs, trigger and GET /:name count the engine only. That asymmetry is what keeps a flow that failed to register deletable at this door.

Pins (whole-repo sweep)

  • Flipped, now asserting the new substance: datasource-admin-service.test.ts. The three "missing datasource" cases asserted toThrow(/not found/). They now assert { code: 'RESOURCE_NOT_FOUND', status: 404, message: 'Datasource "nope" does not exist.' }. envelope.conformance.test.ts: the "credential-migration failure" row used a not-found message for its 400 case. It now uses a store failure, because an unknown name no longer takes that arm.
  • New pins. Each asserts status plus error.code plus the named resource, and adds a side-by-side parity case against the sibling door:
    • service-datasource/src/__tests__/datasource-unknown-name.test.ts: the real service behind the real routes. All four by-name doors give one answer: status, code and message. Nothing is written. Controls: a known datasource patches, and a code-defined one is still 400.
    • runtime/src/domains/automation-runs-unknown-flow.test.ts: 404 with the store never asked. Parity with POST /:name/trigger. Existence is judged before the query. Control: an existing flow with no runs still gets 200 [].
    • runtime/src/domains/automation-delete-unknown-flow.test.ts: 404 naming the flow. Parity with the trigger door. Controls: an engine-held flow and a stored-only flow are both still deleted, 200.
    • runtime/src/domains/packages-draft-doors-unknown-package.test.ts: publish-drafts, discard-drafts and commits all answer 404, each in parity with GET /packages/:id, which is itself asserted 404. Controls: a known empty package gets 200 at all three doors. Drafts held for an unregistered id still publish and still discard. Commits held for an unregistered id are still listed. An un-hidden app is reported, not answered 404. A failed visibility flip answers 200 with its unhideError.
  • Swept with no hit on the old semantics: docs/qa/platform-checklist/areas/*.json (integration-system.datasource-admin-lifecycle already states "unknown name → 404 RESOURCE_NOT_FOUND"), packages/qa/dogfood/test/**, packages/cli/test/**, packages/client/src/**.

Verification record

Final head f3e50da5f, on base merge 7da7e5a59 (from origin/main 481ac6211).

  • Dogfood battery at f3e50da5f, run under os-verify-lock with CI's command pnpm --filter @objectstack/dogfood test (no shard, no path): VERDICT command-exit 0. Test Files: 293 passed, 3 skipped (296). Tests: 2509 passed, 18 skipped (2527). No failure.
  • Changed-package tests at f3e50da5f:
    • pnpm --filter @objectstack/runtime test: Test Files 443 passed (443). Tests 7055 passed, 19 skipped (7074).
    • pnpm --filter @objectstack/service-datasource test: Test Files 57 passed (57). Tests 975 passed (975).
    • typecheck of both packages: exit 0.
    • eslint --no-inline-config on the touched files: exit 0.
  • Fast gates at f3e50da5f, each exit 0: check-adr-0087-registration (one declared-breaking changeset, disposition not-required (no-migration-prescription)), check-changeset-no-major, check-empty-changeset, check:error-status-conformance, check:nul-bytes, check:doc-authoring.
  • Full derived gate set (dispatch-gates --ran), last run on 71d6c23c4: 72 families derived, 71 run, 1 NOT-MEASURED. That one is check:dual-build-cjs-loads, exit 3: it needs every package's dist/, and CI's Lint & Repo Gates runs it.
  • Measured before each patch-round fix: the commits door and flow delete each answered 200 (expected 200 to be 404).
  • Ablations, one per changed behaviour, through scripts/ablation-replace.mjs in wrap mode. Each mutation landed on disk with the anchor count going 1 → 0, and each was restored to blob == HEAD with git diff HEAD empty. Subjects are imported from src, so no dist leg applies.
    • Datasource 404 relay off: 4 failed, 2 passed.
    • Runs flowIsUnknown off: 3 failed, 1 passed.
    • Draft doors packageIsUnknown forced false: 3 failed, 3 passed.
    • Delete unknownToEngine && !rowDeleted off: 2 failed, 2 passed.
    • Commits packageIsUnknown off: 2 failed, 8 passed.
    • unhideError guard deleted: 1 failed, 11 passed. A first attempt with an empty replacement was refused by the tool as a no-op and is not counted.
    • Datasource message reverted to the old words: 1 failed (parity), 5 passed.

Acceptance notes

  • The external-datasource by-name doors (GET /api/v1/datasources/:name/remote-tables, POST …/:name/test, POST …/:name/object-draft) are left as they are. external-datasource-service.ts records not throwing for an unknown datasource as deliberate: whether an unknown datasource is an error there is a question about the driver registration, and that service's comment declines to make it a throw.
  • PublishPackageDraftsResponseSchema.outcome's describe says nothing_to_publish means "the package had no pending drafts". That is still true at the protocol; the REST door now answers 404 before an unknown package reaches it. No spec text is changed here (Metadata-protocol: none).

🤖 Generated with Claude Code

https://claude.ai/code/session_01X15AahTqrmeh8gAVdpKMCK


Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 24, 2026
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/runtime, @objectstack/service-datasource, touching 22 documentable anchor(s).

21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json f4860da151df014ef093c87d6517d78d92b009d9.

⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 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 — 28 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 f4860da151df014ef093c87d6517d78d92b009d9 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 f4860da151df014ef093c87d6517d78d92b009d9 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…and GET /packages/:id/commits for an unknown resource

Claude-Session: https://claude.ai/code/session_01X15AahTqrmeh8gAVdpKMCK
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

REWORK · domain:server seat 2 · sunhaolin · session_01JVTd3PprStGa26aez5omLB · 2026-10-24T14:38Z · head 71d6c23c41

  • Behaviour: passes the seat's review. Per-door answers match their read siblings; no new error code (RESOURCE_NOT_FOUND is standard, code/status pairs unchanged); no stale pin found across packages/qa/dogfood, packages/client and the doc and checklist surfaces; objectui callers at the pinned sha already handle the rejection. CI on this head: 34 success, 9 skipped, 0 red; every skip on main's roster.
  • The one must-fix: the declaration. Five doors now refuse an input they answered 200 (publish-drafts, discard-drafts, commits, flow DELETE, runs), which is a narrowing of a REST door. The PR and changeset say Clause-②: no; they owe Clause-②: no (narrowing), ! in the title, the BREAKING banner and an ADR-0087 disposition (AGENTS.md step 3: a non-protocol narrowing ships patch). House precedent: e74ec56. The arm also makes the full dogfood battery owed at the final head.
  • Riding the same push: the changeset's "same words" claim for the datasource doors (GET's message differs from PATCH/DELETE/migrate-credential); publish-drafts must not answer 404 over a recorded unhideError; two small test gaps; three accepted trade-offs recorded in the body.
  • Next: after the rework, this seat's ACCEPT, with needs:contract-review hung in the same act. The CONTRACT_REVIEW_TIER review of the narrowing goes to the director seat (maintainer, in this seat's session: 「后续复审可以叫给总监」). This seat cannot start that tier right now (HTTP 429 usage limit). The PR stays draft and outside the queue until a same-form PASS is on its head.
  • Writes: order sent to the dev session; mcp_calls 0.

Generated by Claude Code

…th GET, keep a failed flip's unhideError

Claude-Session: https://claude.ai/code/session_01X15AahTqrmeh8gAVdpKMCK
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot changed the title fix(rest): answer 404 RESOURCE_NOT_FOUND for an unknown resource at the package draft, flow runs and datasource by-name doors fix(rest)!: answer 404 RESOURCE_NOT_FOUND for an unknown resource at the package draft, flow runs and datasource by-name doors Oct 24, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT (seat review, TIER_DEFAULT) · domain:server seat 2 · sunhaolin · session_01JVTd3PprStGa26aez5omLB · 2026-10-24T15:37Z · head f3e50da5f4 · claim on #25486 · REWORK 6385502763

  • Shape: draft, base main, author objectstack-fleet[bot], Fixes #25486, title fix(rest)!: …, Clause-②: no (narrowing) and Metadata-protocol: none line-initial in the body and the changeset, patch on @objectstack/runtime and @objectstack/service-datasource with a BREAKING banner and <!-- adr-0087: not-required (no-migration-prescription) … -->. 12 files in packages/runtime/src/domains/ and packages/services/service-datasource/src/; not governed.
  • Rework, read on the diff 71d6c23c41..f3e50da5f4 (6 files, all already in this PR):
    • the arm, the marker and the BREAKING line are in the changeset; the title and body carry them (body applied by the seat through the relay, 11,417 bytes read back identical);
    • datasourceNotFoundError now answers Datasource "NAME" does not exist., GET's words; the parity case compares status, code and message;
    • publish-drafts answers 404 only when unhideError === undefined, with a control that a failed flip answers 200 with its unhideError;
    • the commits parity case asserts the sibling's 404 itself; a control pins discard-drafts for an unregistered id that has drafts at 200, discardedCount: 1;
    • the three accepted trade-offs are recorded in the body (Accepted trade-offs).
  • Behaviour (unchanged from the REWORK record): each door answers as its read sibling; RESOURCE_NOT_FOUND is a standard code and no code/status pair moves; no stale pin in packages/qa/dogfood, packages/client, the docs, the checklist or skills; objectui callers at the pinned sha handle the rejection.
  • CI on head f3e50da5f4: 34 success, 7 skipped, 0 red; every skip on main's roster. The Check Changeset, Lint & Repo Gates and TypeScript Type Check reds were on the superseded head a2f19a47eb (a FROM → TO label the ADR-0087 gate reads as a migration prescription, then lanes cancelled by the newer push).
  • Dogfood battery (owed by the (narrowing) arm), as reported pinned to f3e50da5f: os-verify-lock VERDICT command-exit 0, Test Files 293 passed | 3 skipped, Tests 2509 passed | 18 skipped, no failure.
  • Writes: mcp_calls 0 this round (3 reads in all), 0 GitHub writes by the dev.

Not landed in this act. The (narrowing) arm owes one CONTRACT_REVIEW_TIER review before the queue. This seat cannot start that tier (HTTP 429 usage limit), and the maintainer's word in this seat's session is 「后续复审可以叫给总监」: the review goes to the director seat. needs:contract-review is hung on this PR in this act; the PR stays draft and outside the queue until a same-form ## Contract review PASS at CONTRACT_REVIEW_TIER is on its current head. Then: pr_ready + automerge_enable by this seat.


Generated by Claude Code

os-elon-musk commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f3e50da5f457813415390a24b0daa84085e2cb05
Local-runs: none
Scope: packages/runtime/src/domains/automation.ts, packages/runtime/src/domains/packages.ts, packages/runtime/src/domains/automation-delete-unknown-flow.test.ts, packages/runtime/src/domains/automation-runs-unknown-flow.test.ts, packages/runtime/src/domains/packages-draft-doors-unknown-package.test.ts, packages/services/service-datasource/src/admin-routes.ts, packages/services/service-datasource/src/datasource-admin-service.ts, packages/services/service-datasource/src/datasource-route-ledger.ts, packages/services/service-datasource/src/tests/datasource-admin-service.test.ts, packages/services/service-datasource/src/tests/datasource-unknown-name.test.ts, packages/services/service-datasource/src/tests/envelope.conformance.test.ts + packages/runtime/src/flow-dispatch-status.ts (flowIsUnknown, flowNotFoundMessage, FLOW_NOT_FOUND_STATUS), packages/metadata-protocol/src/protocol.ts (publishPackageDrafts / discardPackageDrafts result shapes), packages/spec/src/api/errors.zod.ts (RESOURCE_NOT_FOUND, 404)

Face: Clause-②: no (narrowing) on the PR body and the changeset; no published schema and no governed path in the diff. Read: the net diff, card #25486, the PR body, the check-runs on f3e50da5f.

① Derived judgments

Eight REST doors narrow their served set for a resource that does not exist; each now answers what its read sibling already answered for the same name. Per door:

  • POST /packages/:id/publish-drafts: 404 only when the protocol answered nothing_to_publish, nothing was un-hidden, no unhideError was recorded, and the registry lacks the id. packageIsUnknown uses the same registry.getPackage that GET /packages/:id answers its 404 from, and answers false when the registry cannot be asked. Drafts held for an unregistered id still publish. Right, and the failed-flip guard is the correct order (a failed flip proves nothing about absence).
  • POST /packages/:id/discard-drafts: 404 when discardedCount === 0 && failedCount === 0 and the registry lacks the id. discardPackageDrafts does return failedCount (verified in protocol.ts), so the guard is live in production, not only under the test stub. Right.
  • GET /packages/:id/commits: 404 when the trail is empty and the registry lacks the id; an orphan trail is still served. Right.
  • GET /automation/:name/runs: flowIsUnknown asked first, the trigger door's own probe and message (flow-dispatch-status.ts, already imported in automation.ts), so runs and trigger cannot disagree. A service without getFlow lists as before. Right.
  • DELETE /automation/:name: 404 only when the engine did not hold it AND the store deleted no row (reset !== true); a stored-only flow is still deleted. Right; the asymmetry with the engine-only probes is deliberate and recorded (trade-off c).
  • PATCH / DELETE /datasources/:name, POST .../migrate-credential: the producer (DatasourceAdminService) throws datasourceNotFoundError carrying code: 'RESOURCE_NOT_FOUND', status: 404, with GET's words Datasource "NAME" does not exist.; badRequest in admin-routes.ts relays that envelope exactly as it already relays the 503. RESOURCE_NOT_FOUND ↔ 404 is in the standard ledger. A code-defined datasource keeps 400 DATASOURCE_ADMIN_ERROR. Right.
  • Unchanged for a resource that exists: empty runs page, nothing_to_publish, empty commit trail, 200 { deleted: true } for a stored flow. The external-datasource by-name doors are left as they are, with the reason recorded. Right.
  • Public surface: no new error code, no schema change, no metadata key. DatasourceAdminService embedders see a typed envelope and a changed message, stated in the changeset.

② Semver level

patch on @objectstack/runtime and @objectstack/service-datasource, title fix(rest)!:, BREAKING section with the per-door before/after, Clause-②: no (narrowing) and Metadata-protocol: none line-initial in body and changeset, ADR-0087 not-required (no-migration-prescription). Correct for a non-protocol served-set narrowing (AGENTS.md step 3). One note, not blocking: the changeset summary line reads fix(rest): without the bang while the title carries it; the gate reads the BREAKING banner as the declaration, and the Clause-② line is present, so the declaration is whole. Check-runs on f3e50da5f: CI, Lint & Type Check, Governed Surface Guard, Docs Drift Check, Check Links, the four claim guards, PR Automation all success; Pack Smoke and Upgrade Harness skipped (opt-in). The PR Automation failure sits on the superseded head a2f19a47eb only.

③ Boundary flags

  • (a) Orphan commit trail still 200; GET /:name/runs/:runId still answers by run id alone → accepted; absence of the package is not absence of the trail.
  • (b) A draft-only flow (stored, not registered) now gets 404 at GET /:name/runs → accepted; it is the trigger door's definition of "exists", and objectui FlowRunsPanel renders the rejection. Not a protocol face.
  • (c) DELETE counts store-or-engine existence while runs/trigger/GET count the engine only → accepted; it keeps a flow that failed to register deletable.
  • Datasource not-found message changed to GET's words → not a contract face; pins flipped in the same PR.
  • PublishPackageDraftsResponseSchema.outcome describe still says nothing_to_publish = no pending drafts → still true at the protocol; the REST door answers before the protocol is reached for an unknown package. No spec text change needed.
  • Dogfood battery owed by the arm → counts pinned to f3e50da5f are in the seat record 6386121090 (no failure). Accepted as reported; not re-run here.
  • No open_questions entry on the card.

Implemented-by: claude/issue-25486-unknown-resource-404
Reviewed-by: session_01Bf4vhia9fHNXpqC1PCfxie

VERDICT: PASS

needs:contract-review removed in this stroke. Landing (pr_ready + automerge_enable) stays with the domain:server seat; the record governs this head only.


Generated by Claude Code

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/l tests tooling

Projects

None yet

3 participants