Skip to content

feat(spec,client): declare the search and data.clone route response contracts (#11924) - #12203

Merged
os-litant merged 8 commits into
mainfrom
claude/issue-11924-declare-search-clone-contracts
Aug 25, 2026
Merged

feat(spec,client): declare the search and data.clone route response contracts (#11924)#12203
os-litant merged 8 commits into
mainfrom
claude/issue-11924-declare-search-clone-contracts

Conversation

@os-litant

@os-litantos-litant commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Part of #11924 — the maintainer-ruled half (2026-08-25, decision-inbox batch 8: option A). Declares the search + data.clone pair only; the automation.create / automation.update pair was explicitly NOT ruled there — it returns to the decision inbox as its own card carrying a consumer-survey reading first, so #11924 remains open for the PM to disposition at acceptance. No automation route, file or docblock is touched here.

(Angle-bracket generic spellings are written out in words below — the platform body sanitizer eats literal less-than fragments even inside code spans; the first stored revision of this body lost them, and this edit repairs it.)

What this does

Two of the four SDK routes #8140 had to leave as deliberate Promise-of-any holes get their missing contracts, declared as produced (both routes answer BARE — no { success, data } envelope — relaying the @objectstack/metadata-protocol producer verbatim):

  • GET /api/v1/searchSearchAllResponseSchema / SearchAllHitSchema (+ SearchAllResponse / SearchAllHit types) in @objectstack/spec/api (packages/spec/src/api/protocol.zod.ts): { query, hits, totalObjects, totalHits, truncated }, hits of { object, id, title, snippet?, record }.
  • POST /data/:object/:id/cloneCloneDataResponseSchema (+ CloneDataResponse) beside its structural sibling CreateDataResponseSchema: { object, id, sourceId, record }, answered with 201. No droppedFields member — measured: the clone producer emits none.

Conformance coverage (#3877: no ledger row filled without it — filled and covered in the same change):

  • Producer half — packages/metadata-protocol/src/search-clone-schema-conformance.test.ts drives the REAL searchAll / cloneData over fixture engines and safeParses what they emit (plus key-set subset checks in the direction a plain z.object parse is blind to, on the JSON round-trip a caller actually holds; both searchAll return statements covered; snippet's two branches measured on one body).
  • Relay half — packages/rest/src/search-clone-route-schema-conformance.test.ts drives both real mounts, proves the bodies parse, are relayed bare and unre-shaped (201 for clone), and pins the ledger rows to the very schema objects the suite parses with (identity, not just resolvability — the discovery-gate pattern).
  • packages/rest/src/rest-route-ledger.ts: both rows' responseSchema filled, with notes naming the coverage.

Client bindings (type-only; no request, response, unwrapping or error path changes): search now returns a Promise of SearchAllResponse; data.clone becomes generic over T (default any, matching its data.* siblings) returning a Promise of CloneDataResult of T — a new exported interface mirroring the spec schema beside CreateDataResult. SearchAllHit / SearchAllResponse re-exported from the client for consumers.

The SearchResult near-miss trap (ruled: must stay pinned)

SearchResult (@objectstack/spec/contracts) types the per-object ISearchService.search — hits of score / document — and is the WRONG type for this route. The #8140 compile-time guard in packages/client/src/return-type-precision.test.ts is untouched in its assertions and stays green; its prose note is updated (it described the search binding as deliberately absent — Promise-of-any — which this change makes false). The new declaration was written from the producer, not from that neighbour, and two new direction-2 pins make the trap expressible at the binding: a search result assigned to SearchResult is a compile error, as is a create result read through CloneDataResult (no sourceId).

Verification

All heavy runs went through scripts/pm/os-verify-lock.sh; exit codes captured directly (no pipes), verdict lines quoted from the gates' own output. Final union re-run at commit 0968af1:

  • @objectstack/spec: full test at the merge commit 3c1cda7: 425/426 files, 11347/11348 tests green — the sole red was the pin file's own count self-check ("expected [ Array(838) ] to have a length of 835"), corrected per that file's receipt convention in 0968af1; typecheck and the corrected pin file re-run green at 0968af1 (3/3). The ratchet families (check:spec-parsed-alias, check:strictness-ledger, check:merge-driver, changeset family) all re-ran green at 0968af1 in the gate farm below
  • @objectstack/client: typecheck (compiles the type pins via tsconfig.test.json / check:test-typecheck) + full test — 25 files / 332 tests passed at 0968af1
  • @objectstack/metadata-protocol: new conformance suite — 6/6 passed at 0968af1 (no typecheck script exists in that package — type-check-debt ledger; the suite runs under vitest)
  • @objectstack/rest: new conformance suite + rest-route-ledger.conformance.test.ts — 13/13 passed at 0968af1
  • pnpm --filter @objectstack/spec check:generated — "All 14 generated artifacts are up to date" on the merged tree (batch A, merge commit) after regenerating the four proved stale pre-merge (api-surface — 6 added / 0 removed, export-origins, docs, strictness-ledger); authorable-surface/api.json + json-schema.manifest/api.json rewritten by gen:schema as expected products
  • Derived local gate farm (node scripts/pm/dispatch-gates.mjs, no args — change set taken from merge-base by the script): 36 of 37 gates pass at 0968af1 (including check:nul-bytes, check:spec-parsed-alias, check:cross-package-test-inputs, check:strictness-ledger, check:adr-0087-registration — the additive changeset carries no breaking declaration so no marker is owed). Deviation:check-dev-prereqs reds locally on a worktree precondition — "36 of 67 workspace packages declare an entry point under dist/ that is not on disk" (apps/account, setup, studio, …): only the targeted dependency closures were built in this worktree; the gate measures workspace built-ness, which this diff does not influence, and CI's lint job builds the workspace before running it
  • Declared narrowing: package test runs are scoped to the four touched packages (--filter), and the two docs-drift-check.yml scripts (check-affected-docs / check-drift-comment) are CI-owned PR-context runs not reproduced locally. Downstream-consumer sweep for the client narrowing measured by call-site grep: zero client.search( / .data.clone( call sites exist outside packages/client itself (downstream direction, ...@objectstack/client). CI runs the full farm regardless

Generated by Claude Code

…ontracts (#11924)
The maintainer-ruled half of the four undeclared SDK route returns (2026-08-25,
decision-inbox batch 8, option A): search + data.clone are stable,
server-produced shapes — declared in packages/spec exactly as produced, with
conformance coverage on both the producer and the mounted route, honouring
#3877's rule that no route-ledger responseSchema row is filled without
conformance coverage (both rows filled in this same change). The
automation.create / automation.update pair is NOT touched — it returns to the
decision inbox as its own card with a consumer-survey reading first.
- spec: SearchAllResponseSchema / SearchAllHitSchema + CloneDataResponseSchema
in api/protocol.zod.ts (+ z.input type aliases and isomorphism pin lines);
regenerated artifacts: api-surface (6 added / 0 removed), export-origins,
reference docs, strictness-ledger counts; authorable-surface and
json-schema.manifest shards rewritten by gen:schema as expected products.
- rest: both ledger rows filled (responseSchema + coverage-naming notes);
search-clone-route-schema-conformance.test.ts pins the relay half — bodies
parse, answered bare (201 for clone), rows resolve to the very schema
objects the suite parses with.
- metadata-protocol: search-clone-schema-conformance.test.ts pins the producer
half — the real searchAll / cloneData over fixture engines, safeParse plus
key-set subset checks at both levels, both searchAll return statements, both
snippet branches.
- client: search → Promise<SearchAllResponse>; data.clone →
clone<T = any>(...): Promise<CloneDataResult<T>> with the new exported
CloneDataResult mirror interface; SearchAllHit / SearchAllResponse
re-exported; return-type-precision pins extended in both directions. The
#8140 SearchResult near-miss guard's assertions are untouched and green —
only its prose note is updated (the binding it described as deliberately
absent now exists).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
…pins (835 -> 838)
The pin file asserts its own pin count and recomputes the two prose
statements from the file; the three new Iso859/860/861 pins moved the
count. Receipt paragraph added per the file's own convention.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
@github-actionsgithub-actionsBot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 25, 2026
@github-actions

github-actionsBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/client, @objectstack/rest, @objectstack/spec, touching 19 documentable anchor(s). ⚠️5 changed file(s) yielded no anchor (packages/client/exported-any-returns.json, packages/spec/api-surface/api.json, 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.

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

  • content/docs/ai/knowledge-rag.mdx(via sourceId (symbol))
  • content/docs/api/client-sdk.mdx(via ObjectStackClient (symbol))
  • content/docs/api/data-api.mdx(via sourceId (symbol), /data/:object/:id/clone (route))
  • content/docs/api/environment-routing.mdx(via ObjectStackClient (symbol))
  • content/docs/api/wire-format.mdx(via ObjectStackClient (symbol))
  • content/docs/kernel/runtime-services/data-service.mdx(via ObjectStackClient (symbol))
  • content/docs/kernel/runtime-services/storage-service.mdx(via ObjectStackClient (symbol))
  • content/docs/permissions/authentication.mdx(via ObjectStackClient (symbol))
  • content/docs/permissions/sharing-rules.mdx(via /:object/:id/shares (route))
  • content/docs/plugins/packages.mdx(via ObjectStackClient (symbol))
  • content/docs/protocol/kernel/realtime-protocol.mdx(via ObjectStackClient (symbol))
  • content/docs/protocol/knowledge.mdx(via sourceId (symbol))

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

  • content/docs/releases/implementation-status.mdx(via /api/v1/data/:object/:id/clone (route), /data/:object/:id/clone (route))
  • content/docs/releases/v16.mdx(via /api/v1/search (route))
  • content/docs/releases/v17.mdx(via ObjectStackClient (symbol), sourceId (symbol), /:object/:id/shares (route))

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
  • 5 changed file(s) yielded no anchor (packages/client/exported-any-returns.json, packages/spec/api-surface/api.json, 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 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 177: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 107 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.

Coarse fallback — 130 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 f86839d4cd5543a0f8c7a3f4806b0d0b35a33524packageMentionDocs.

Which tree this was computed on

This run read content/docs from dc549cb6f18b330dfc06adc1d10b4e795d71ed5c — the merge of head 94e84ec6bce650ed465bfbac1704ca971a621aca into base f86839d4cd5543a0f8c7a3f4806b0d0b35a33524, 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 dc549cb6f18b330dfc06adc1d10b4e795d71ed5c && git checkout dc549cb6f18b330dfc06adc1d10b4e795d71ed5c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f86839d4cd5543a0f8c7a3f4806b0d0b35a33524 94e84ec6bce650ed465bfbac1704ca971a621aca && git checkout -B drift-repro f86839d4cd5543a0f8c7a3f4806b0d0b35a33524 && git merge --no-ff 94e84ec6bce650ed465bfbac1704ca971a621aca
node scripts/docs-audit/affected-docs.mjs --json f86839d4cd5543a0f8c7a3f4806b0d0b35a33524

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

os-litantand others added 2 commits August 25, 2026 21:26
…clare-search-clone-contracts
# Conflicts:
#	packages/spec/src/type-alias-convention.pin.test.ts
…, export-origins, docs, strictness-ledger via check:generated --fix)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
…explicit .js extension; index instead of Array.prototype.at)
check:type-check-debt re-measure went 155 -> 157 on @objectstack/rest from
this PR's new test file (TS2835 missing extension, TS2550 .at under the
package lib). Fixed the two new errors; ledger stays at its recorded 155.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
…jectStackClient.search, ObjectStackClient.data.clone)
This PR binds real return types to both sites, voiding their DELIBERATE
Promise<any> ledger reasons; the shrink-only ledger is judged exactly, so
the closed-gap entries must go in the same change (check:exported-any-returns
went red on them at CI).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
@os-litant
os-litant marked this pull request as ready for review August 25, 2026 16:13
@os-litant
os-litant added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit daae7aaAug 25, 2026
35 checks passed
@os-litant
os-litant deleted the claude/issue-11924-declare-search-clone-contracts branch August 25, 2026 16:41
os-litant pushed a commit that referenced this pull request Aug 25, 2026
Discharges the os-regen deferral recorded by the merge commit. `main` gained
`SearchAllResponseSchema` / `SearchAllHitSchema` / `CloneDataResponseSchema`
(#12203) while this branch was open, so the pages this branch regenerated at
its fork point described a source the merged tree no longer has — which is what
`check:docs` reported on the merge ref.
Regenerated with `gen:schema && gen:docs` from the merged tree, never by hand.
`api/protocol.mdx` gains the three new schema sections plus one nested-shape
table this branch's renderer produces for the new array-of-object property,
`SearchAllResponse.hits[number]`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-litant@claude