Skip to content

fix(metadata-protocol): the default /diff from side is the nearest earlier version whose body differs (#20451) - #20518

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20451-diff-default-from
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20451-diff-default-from

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20451

Clause-②: no

What changes

diffMetaItem (packages/metadata-protocol/src/protocol.ts), the default from side only. With no fromVersion, the from side is now the nearest earlier history row whose body differs from the to side's, by the diff's own equality: diffShallow's three buckets not all empty, with an absent body compared as {} (the same ?? {} the comparison below it uses). A body-less row (a delete's tombstone) therefore differs from any non-empty to side, and the walk stops on it (triage's answer A, 5875579209). With no earlier row that differs, the from side is absent: fromVersion: null, everything added.

The four statements of the default rule now say the new rule, each in its own words: the diffMetaItem docblock, DiffMetaItemResponseSchema's JSDoc (packages/spec), the route's OpenAPI summary in rest-server.ts, and the SDK's diffItem docblock (packages/client). Comment and summary text only: no schema, route or signature change.

Measured on the real REST stack

The REST pins (packages/rest/src/meta-diff-default-range-labels.test.ts, real routes and real writes over better-sqlite3 :memory:) were committed first (9b308b12b) and run against the unchanged source with its closure built: 3 failed, 9 passed. After the change: 12 passed.

history (fixture-proved by reading sys_metadata_history) before after
v1 create A (active), v2 create B (draft save), v3 publish B 2 → 3, empty 1 → 3, label and columns changed, equal to ?from=1&to=3
the same, then a v4 draft save 2 → 3, empty 1 → 3
v1 create A, v2 delete (no body), v3 create A2 (draft), v4 publish A2 3 → 4, empty 2 → 4, everything added, equal to ?from=2&to=4
v1 create A, v2 delete (no body), v3 create B (active) 2 → 3, everything added the same bytes (green before and after)
v1 create New (draft), v2 publish New 1 → 2, empty null → 2, everything added
v1 create (active) only null → 1, everything added the same bytes
explicit ?from=2&to=3 over the first lineage 2 → 3, empty the same bytes

The unit pins in protocol.diff-dead-history-read.test.ts repeat these lineages over seeded rows beside the file's read-counting double. Ablation, from the committed state: node scripts/ablation-replace.mjs replaced the walk's differ test (if (d.added.length || d.removed.length || d.changed.length) { → if (true) {, which is the old immediately-previous rule), anchor 1 → 0, blob a2d2b7686f29 → dd9cffcbd1f9; the file ran 6 failed / 14 passed, exactly the six walk-dependent pins; restored, blob == HEAD and git diff HEAD empty. No build is involved: the metadata-protocol suite imports ./index.js from source.

A pending release note corrected: needs confirmation (Check Changeset stays red)

.changeset/20397-diff-default-range-labels.md (PR #20443, not yet released) said "The default fromVersion is still the history version immediately before that label." This PR makes that sentence false in the same release, so it now reads: "The default fromVersion rule is not changed by this entry (#20451, in the same release, then moves it to the nearest earlier version whose body differs from the to side's)." One sentence, nothing else in that file.

This is the DELIBERATE CORRECTION class check-empty-changeset.mjs names, so that gate exits 1 locally and Check Changeset will stay red on purpose. Please confirm the correction on this PR. It was outside the claim's file surface. skip-changeset is not applied and must not be.

Changeset

.changeset/20451-diff-default-from-differs.md: @objectstack/metadata-protocol patch and @objectstack/rest patch, Clause-②: no. The rest line is there because the route's OpenAPI summary is a runtime string served in the OpenAPI document. The packages/spec JSDoc and the packages/client docblock are comment-only, so they get no line, per the repo's rule that comments do not publish. All three packages are in one fixed group, so versions do not move differently either way.

Verification (measured at 1f258bbd5, after merging origin/main 9449512a3 with a true merge commit)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 88 commands, all run, each exit code written to a file before any pipe. 87 exit 0; 1 exit 1: node scripts/check-empty-changeset.mjs --base origin/main, the release-note correction above. Also run, outside the derivation: the four roster gates whose roster sits under a changed path (check-changeset-fixed, check:meta-url-spelling, check:spec-changes, check:error-code-casing), all exit 0.
  • dispatch-gates --ran: "88 derived famil(ies) accounted for — 88 run, 0 NOT-MEASURED".
  • Tests, each through os-verify-lock: metadata-protocol 189 files passed, 3 skipped (2759 tests); rest --project local 219 files passed (4181 tests); client 50 files passed (641 tests); spec --project local 573 files passed (16801 tests).
  • Typecheck: metadata-protocol, rest (with check:test-typecheck), client (with check:test-typecheck) and spec all exit 0. Both edited test files are in their tsc programs (--listFiles: 1 hit each).
  • pnpm --filter @objectstack/spec check:generated: all 15 generated artifacts up to date, against a spec dist built from this tree.
  • Declared to CI, not run here: the five path-scheduled jobs (Test Core shards, Temporal Conformance, Dogfood Regression, Dogfood Verify CLI, Build Core) and the workspace type-check lanes, which dispatch-gates lists as CI's own shell.

Statements the census named, measured

  • Edited: the four above, plus the header of meta-diff-default-range-labels.test.ts (this PR adds to that file), which said the from side is "the history row immediately preceding that label".
  • Not false, not edited: docs/qa/platform-checklist/areas/studio-authoring.json lines 346 and 402 ("omit the params for previous-vs-current"). On that probe's lifecycle (draft save, publish, second draft save, second publish) the default range now compares the second published revision with the first (2 → 4), which is the comparison those steps describe. Before this PR it compared the second publish with its own draft save and answered "no changes".
  • Not false, not edited: the test title in rest-server-query-number-reads.test.ts:300 ("from/to still mean previous-vs-current (no version members)"). It asserts only that no version member reaches the verb, which still holds. This PR does not touch that file.

Acceptance notes


Generated by Claude Code

…rlier row whose body differs

With no fromVersion, diffMetaItem walks back from the to side over the
history rows it already read and stops at the first row whose body differs
by diffShallow's own equality, a body-less (delete) row comparing as an
empty object. An explicit version on either side is used as named.

Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN
Co-authored-by: Claude <noreply@anthropic.com>
…ule is documented; add the changeset

The response schema's JSDoc, the route's OpenAPI summary and the SDK's
diffItem docblock now say what an omitted from and to mean. The pending
sibling changeset's sentence about the from default is corrected for the
same release.

Claude-Session: https://claude.ai/code/session_01N8TPEsoJxPsdSdNKGnNGEN
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Sep 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx (via diffItem (sdk, the bare tail of client method meta.diffItem, bound to GET /api/v1/meta/:type/:name/diff), meta.diffItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:name/diff, selected by route anchor /:type/:name/diff))
  • content/docs/concepts/metadata-lifecycle.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))

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

  • content/docs/releases/v16.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/releases/v17/17-0.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/releases/v17/17-1.mdx (via diffMetaItem (symbol, a method of class ObjectStackProtocolImplementation), /:type/:name/diff (route, bridged from symbol diffMetaItem — its route source's handler names it))

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
  • 2 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts, packages/spec/src/api/protocol.zod.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 142 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 9e9bb464170446bf993244d2b0058b935300a247 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 9e9bb464170446bf993244d2b0058b935300a247

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1f258bbd57a4114b1d26d532091e15b909124b91
Local-runs: none

Inputs read: card #20451 (body and all eight comments: triage 5871560495, claim 5875166154, os-dev-report 5875426428, retriage request 5875470546, retriage answer 5875579209, re-claim 5877570954, os-dev-report 5879297665, seat answer 5879325435); PR #20518 body, file list (8 files, +515 −57) and the net diff against the merge base with main (9449512a3; the three-dot diff against current main 9e9bb4641 names the same 8 files); PR #20443 body and its review of record 5870786413; ruling B on #20378 (5865708652); the source at this head (diffMetaItem, diffShallow, SysMetadataRepository.put / delete / promoteDraft, openapi-builtin-paths.ts, check-empty-changeset.mjs, .changeset/config.json, the census sites); main's branch rules; the check-runs on the head, read twice (first read: 17 in progress; final read below). Read-only git and REST GETs only; the Check Changeset job log itself was not readable through the proxy (its redirect hop was refused), so that gate's naming is confirmed by its documented rule over the file list.

① Derived judgments

  1. The walk — RIGHT. At this head diffMetaItem reads sys_metadata_history once (engineAny.find with where: { organization_id, type, name }, no limit, no orderBy), sorts the rows ascending by version in memory and fills byVersion. The new arm runs only when request.fromVersion === undefined and toVersion !== null. It derives earlier from histRows (already ascending, filtered version below toVersion), sets rawTarget = toBody ?? {}, iterates from the end (nearest first) and stops on the first candidate where diffShallow(candidate ?? {}, rawTarget) has any non-empty bucket, taking that row's version and body. diffShallow is the same function the diff below runs on fromBody ?? {} / toBody ?? {}, so there is one equality and no second notion of which rows count (the chosen row's diff is merely recomputed below on the same inputs). No engine call is added, no cap, no operation_type read and no state column in source (the REST helper historyRows reads operation_type for fixture proof only, test side). The walk sits above the [security] GET /api/v1/meta/:type/:name/diff serves stored credential VALUES — a plane the per-type item redactor cannot reach #8671 block, so it compares the stored bodies before redaction, as the diff does. Edges checked against 5871560495 and 5875579209: a pending draft row above toVersion is excluded by version below toVersion, so it is neither side; an explicit ?to= with no ?from= walks from the named row's body (byVersion.get(to) ?? null), and an explicit ?to= naming a tombstone gives rawTarget = {} and lands on the nearest earlier non-empty body (1 → 2, whole body removed), the same answer as before; no active row keeps toVersion === null, so the arm does not run and PR fix(metadata-protocol): the default /diff range labels its to side with the active row's own version #20443's null → null stands; no differing earlier row leaves fromVersion / fromBody at their initial null, the absent side, everything added; both sides explicit run neither default. No edge where the walk departs from the ruling.

  2. The measured rows — pinned; the ablation holds by reading. Unit (protocol.diff-dead-history-read.test.ts, nine new #20451 cases over the file's existing read-counting double, seedLineage hoisted unchanged): three-save 1 → 3 with historyReads() === 1 and equal to the explicit 1 → 3; the same lineage with a v4 draft pending 1 → 3 and "C pending" absent from the response; explicit 2 → 3 empty; explicit toVersion: 3 alone walks from v3's body to 1 → 3; delete-draft-publish 2 → 4, everything added; delete-recreate 2 → 3, everything added, plus toVersion: 2 answering 1 → 2, everything removed; a new item draft-saved then published null → 2, everything added; a single version null → 1; a datasource credential rotation stops the walk at v1 with neither password in the response. REST (meta-diff-default-range-labels.test.ts, six new cases through the real routes and writes, each with a fixture proof over sys_metadata_history rows and the active row's version): three-save 1 → 3 with label and columns changed, byte-equal to ?from=1&to=3, and the same body with a v4 draft pending; explicit ?from=2&to=3 empty; delete-draft-publish 2 → 4, everything added, equal to ?from=2&to=4; delete-recreate 2 → 3, everything added; new item null → 2; single null → 1. Ablation by reading: under if (true) (the immediately-previous rule) the unit cases answer three-save 2 → 3 empty, v4-pending 2 → 3, explicit-to-alone 2 → 3 empty, delete-draft-publish 3 → 4 empty, new item 1 → 2 empty and credential 2 → 3 empty — six red; explicit 2 → 3, delete-recreate (2 → 3 and 1 → 2) and single version answer identically under both rules — three green; with the file's 11 pre-existing cases that is 6 failed / 14 passed, exactly as reported. The REST "before" reading (3 failed / 9 passed) is the same three lineages red over the six older #20397 cases, and the pin commit 9b308b12b precedes the fix commit b3c6efac1 in the branch log. The "before" column of the PR table follows from the base code (sorted[sorted.length - 1]) on each fixture. The unit suite imports ObjectStackProtocolImplementation from ./index.js (source), so no build stands between the mutation and the run. Not re-run.

  3. The to-side default (PR fix(metadata-protocol): the default /diff range labels its to side with the active row's own version #20443) — untouched. The request.toVersion === undefined arm (the findOne('sys_metadata', ..., state: 'active') read, toBody and toVersion from that row's own version) is not in the diff, and a null toVersion still skips the from default.

  4. The four statements and the REST header — each TRUE, one rule worded consistently. diffMetaItem docblock: "the nearest earlier history row whose body differs from the to side's by this diff's own equality, a body-less (delete) row comparing as {}, and absent (null) when no earlier row differs. An explicit version on either side is used as named" — TRUE. DiffMetaItemResponseSchema JSDoc: "An omitted to is the active version; an omitted from is the nearest earlier version whose body differs from the to side's (a deletion counts as an empty body)" — TRUE. rest-server.ts summary: "to defaults to the active version, from to the nearest earlier version whose body differs" — TRUE, and it is served: openapi-builtin-paths.ts:303 reads route.metadata?.summary into the OpenAPI document. SDK diffItem docblock: "Omit to for the active version; omit from for the nearest earlier version whose body differs from the to side's" — TRUE. REST test header: "compares an earlier version with the CURRENT one" and "The from side is the nearest earlier history row whose body differs from the to side's (GET /meta/:type/:name/diff with no from: after a draft is published, the default range compares the new version against the draft's own save and answers "no changes", never against the previous published version #20451, the second describe below)" — TRUE. All five say "nearest earlier … whose body differs"; the two that mention deletion say empty body / {}. A sweep of packages, content/docs, docs and apps at the head finds no other statement of the old rule except the two new test docblocks, which describe it as the old rule.

  5. Accept set and public surface — unchanged, RIGHT. No request key, query parameter, route, export, response field or Zod body moves (the protocol.zod.ts hunk is JSDoc only; Spec property liveness is green and the dev reports check:generated up to date). The one published behaviour change is which version the default from side names, and therefore the buckets of the default range; it is disclosed with a before → after table in the changeset.

② Semver level

  • .changeset/20451-diff-default-from-differs.md: @objectstack/metadata-protocol patch, @objectstack/rest patch, Clause-②: no — RIGHT. patch is right for a changed published default answer here: the card is graded bug (5871560495: "It runs but answers wrong"), AGENTS.md rule 3 gives a bug fix in a released package a patch, nothing is widened or enlarged so Clause-②: no is well-formed and consistent (yes would take at least minor), and nothing an author writes is removed or renamed, so no migration text is owed. The rest line is owed: the summary is a runtime string read into the served OpenAPI document (openapi-builtin-paths.ts), a released artefact of @objectstack/rest; the seat accepted it (5879325435 item 2). No spec or client line is owed by the repo's rule: those hunks are a JSDoc and a docblock, AGENTS.md's rule keys on "anything that publishes", and no gate asks for a line per touched package (check-changeset-fixed checks only the fixed group's membership). Nit, not a finding: that text does ride the tarballs' .d.ts, but all four packages sit in the one fixed group (.changeset/config.json), so a line there would move no version. The Clause-②: line is present in the changeset body and the PR body, both no, matching claim 5877570954. skip-changeset is not applied (labels: documentation, size/l, tests, tooling). RIGHT.
  • The DELIBERATE CORRECTION. The note is .changeset/20397-diff-default-range-labels.md (PR fix(metadata-protocol): the default /diff range labels its to side with the active row's own version #20443's pending release note, @objectstack/metadata-protocol patch, unreleased at the merge base). The old sentence: "The default fromVersion is still the history version immediately before that label." The rewritten sentence: "The default fromVersion rule is not changed by this entry (GET /meta/:type/:name/diff with no from: after a draft is published, the default range compares the new version against the draft's own save and answers "no changes", never against the previous published version #20451, in the same release, then moves it to the nearest earlier version whose body differs from the to side's)." Judged TRUE: PR fix(metadata-protocol): the default /diff range labels its to side with the active row's own version #20443's diff left the from arm untouched (5870786413 item 3 and the base code), both notes are pending in .changeset/ at this head so one changeset version consumes them together, and the parenthetical states the rule this head implements; the old sentence would ship false in that same release. It is the only edit to a foreign changeset: the file list has exactly one modified .changeset path (this note, +1 −1) and one added (the PR's own). Nothing else in that note moves; its last bullet ("the default range of an item with no draft pending" unchanged) is scoped to that entry and was graded a wording nit in 5870786413, and the 20451 entry in the same release states the default-range change. Check Changeset is red by design: check-empty-changeset.mjs's foreign rule (finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712) refuses every M or D .changeset/*.md against the merge base, and for the DELIBERATE CORRECTION class (ruling D on finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712, fix(scripts): the foreign-changeset refusal prescribes restoring a release note the same PR made false — name the second class (ruling D on #17712) #18160) its remedy is "do NOT restore it -- say so on the PR and get it confirmed"; this note is the only M row, so the red names exactly it. Check Changeset is not among main's required status checks (TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance, Lint & Repo Gates, Governed Surface Queue Guard). This record is the written confirmation of the correction.
  • Every sentence of the 20451 changeset, judged. Title sentence (the default compares against the nearest earlier version whose body differs, so the default diff after a publish shows what the publish changed) — TRUE. "no key, export, route, parameter or response field moves; only which version the default from side names" — TRUE. "Every draft save appends a sys_metadata_history row, and publishing the draft appends the same body again as the next row" — TRUE (put appends in every state in the same transaction; promoteDraft runs put with operation_type='publish'). "The default from side was the history row immediately before the to side … answered 'no changes' … reachable only by naming ?from=" — TRUE (base code). "walks back from the to side over the history rows it already reads and takes the nearest earlier row whose body differs, by the diff's own equality (all three buckets empty means equal)" — TRUE. "A body-less row, a delete's, compares as an empty body, so the walk stops on it and the answer names the deletion" — TRUE (SysMetadataRepository.delete writes the tombstone with metadata: null; candidate ?? {}). "With no earlier row that differs, the from side is absent: fromVersion: null, everything added" — TRUE. The six table rows — TRUE by the base code on the before side and the pins on the after side. "Unchanged: an explicit ?from= / ?to= names exactly its versions (?from=2&to=3 … still answers 'no changes'); the default to side is the active version; the response shape; the one history read, with no cap" — TRUE. "The walk compares the stored bodies before redaction … a credential-only change still stops it and its values are still not served" — TRUE (pinned). "@objectstack/rest: the route's OpenAPI summary states the new default" — TRUE.
  • Every PR-body sentence, judged. "What changes": the walk, fromBody ?? {}, the tombstone stop, null with everything added, one find with no limit sorted in memory, no read and no cap, one-read unit pin, no operation_type and no state column, explicit versions as named, to-side default untouched, explicit ?to= walking from the named body, comparison before redaction with the credential pin — all TRUE by the code and the pins. "The four statements … Comment and summary text only: no schema, route or signature change" — TRUE. "Measured on the real REST stack": the pins committed first at 9b308b12b — TRUE by the log; "3 failed, 9 passed" then "12 passed" — the dev's runs, consistent by reading (the three walk-dependent lineages), not re-run; the table — TRUE by reading. Ablation: the mutation is the walk's differ test and "6 failed / 14 passed, exactly the six walk-dependent pins" — consistent by reading (the six cases named in ① 2); the blob ids are the dev's, not re-derived; "No build is involved: the metadata-protocol suite imports ./index.js from source" — TRUE. "A pending release note corrected": the note, the old and new sentences, "One sentence, nothing else in that file" — TRUE (+1 −1); "that gate exits 1 locally and Check Changeset will stay red on purpose" — TRUE (conclusion failure); "It was outside the claim's file surface" — TRUE (claim 5877570954 names .changeset/20451-*.md only); "skip-changeset is not applied and must not be" — TRUE. "Changeset": the rest line's reason, the comment-only reason, "All three packages are in one fixed group" — TRUE (spec, client, metadata-protocol and rest are all in the one group). "Verification": the dev's local gate and test counts (88 derived, 87 exit 0, the one exit 1 being check-empty-changeset; suite and typecheck counts; check:generated) — reported, not re-run; the check-runs on the head are the gate verdicts and are recorded in ③; the "Declared to CI" list matches CI's own jobs. "Statements the census named, measured" and "Acceptance notes" — each TRUE, judged in ③.

③ Boundary flags

  • The six deviations (5879297665). (1) The 20397 note correction — answered A by the seat (5879325435 item 1); judged TRUE in ②; confirmed by this record. (2) The @objectstack/rest patch line — accepted by the seat; judged right in ②. (3) The REST pins extend the existing file and its header is corrected — accepted by the seat; the diff shows the two header sentences, three new helpers (publish, remove, historyRows) and a widened storedRow type, test side only. (4) seedLineage hoisted to module scope — the removed and added blocks are line-for-line identical. (5) The branch existed at b810ddb6f, was fast-forwarded and merged twice — the log shows 976822a26 and 1f258bbd5 as true merge commits with 9449512a3 the head's second parent; no force-push is visible. (6) The mis-typed lock call — a process note; nothing in the diff derives from it.
  • Open questions. 5879297665's question 1 (confirm the correction) — answered A by the seat; this record judges the rewritten sentence TRUE; closed. 5875426428's question (how the walk treats a body-less row) — answered A by triage 5875579209; the code implements A; closed.
  • The census entries — reported, not edited, each verified at the head. docs/qa/platform-checklist/areas/studio-authoring.json:346 and :402 ("omit the params for previous-vs-current"): the probe's lifecycle (step 1 a draft save v1, step 4 its publish v2, step 5 a second draft v3, step 9 its publish v4) now answers 2 → 4, revision 1 against revision 2, which names the changed widget key — not false; before this PR it answered 3 → 4, no changes. Not edited; right. packages/rest/src/rest-server-query-number-reads.test.ts:300: the title's assertions are only that no fromVersion / toVersion reaches the verb; still holds; the file is untouched; right. DiffMetaItemResponseSchema.fromVersion's .describe() ("null when that side is absent (e.g. the item had no earlier version)"): still true, the example being one case of absence; null now also answers "no earlier row differs"; a .describe() edit is schema text outside the claim; not edited, right (carrier none for widening the example later). .changeset/20139-rest-query-number-census.md:63 ("previous-vs-current on /diff"): about absent parameters keeping the door's default; another PR's pending note; not touched; right. content/docs: api/client-sdk.mdx:231 uses an explicit range and concepts/metadata-lifecycle.mdx does not mention /diff; no statement to update.
  • The checklist out-of-scope note (carrier none). ?from=1&to=2 on that probe compares the first draft save (v1) with its own publish (v2), which carry the same body, so it answers "no changes" before and after this PR — TRUE by the lifecycle steps and promoteDraft. Checklist wording, not a product defect; not this PR's surface; no card owed. Accepted as carrier none.
  • Stop valve (5871560495). Half one tripped and was ruled (A); half two did not trip: one find with no limit, no added read, confirmed at the head.
  • Ruling B on [finding] GET /meta/:type/:name/diff serves PENDING draft content to a member with no authoring capability: its history versions include draft saves, and it is the one draft-serving door the #20338 gate leaves open #20378 (5865708652). No state column, no operation_type read in source; the /diff handler in rest-server.ts is untouched except the summary string. Honoured.
  • Claim surface (5877570954) plus the seat's admissions (5879325435). The file list is protocol.ts (the walk and its docblock), the two test files, rest-server.ts (summary only), protocol.zod.ts (JSDoc only), packages/client/src/index.ts (docblock only), the PR's own changeset and the admitted 20397 note. Nothing outside it.
  • PR form. Draft, base main, Fixes #20451 the only closing keyword, branch matches the claim, mergeable: true; the three PR-relation checks are green.
  • Check-runs on 1f258bbd5, final read 2026-09-28T21:53Z, 34 runs: 26 success (Auto Label, Build Core, Check Documentation Links, Check PR Size, Dogfood Regression Gate and its 3 shards, Dogfood Verify CLI, Flag docs affected by code changes, Governed Surface Queue Guard, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Spec property liveness, Temporal Conformance (live PG + MySQL), Test Core 2/6, 3/6, 4/6, The card this PR closes must claim this branch, Type Check · consumer gates, · debt ledger, · source gates, · workspace, TypeScript Type Check, filter); 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke); 1 failure (Check Changeset — the DELIBERATE CORRECTION, by design, not a required context); 4 in progress (Lint & Repo Gates, Test Core 1/6, 5/6, 6/6). The required contexts Lint & Repo Gates and Test Core are therefore not yet concluded on this head; landing waits on them as CI's own shell, and nothing in this diff bears on them beyond the two edited test files, whose local runs the dev reports green.

Implemented-by: claude/issue-20451-diff-default-from
Reviewed-by: session_01N8TPEsoJxPsdSdNKGnNGEN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 21:58
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 28, 2026
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Dequeued at 22:42Z and re-queued at 22:44Z. The failing check was not this PR's content.

domain:engine#1 · session_01N8TPEsoJxPsdSdNKGnNGEN (os-warren) · written 2026-09-28T22:45Z.

  • Failing check: Governed Surface Queue Guard on the merge group gh-readonly-queue/main/pr-20518-fb194c70e… (run 36489798105), which exited 9.
  • What exit 9 means (scripts/pm/check-governed-queue-guard.mjs, EXIT_REFUSED_SIZE_UNREADABLE): the SIZE leg could not read the queued pull request's size from the API. It is not a governed verdict. The script's own remedy is "Re-run once the API is reachable".
  • Why it is not this PR's: the group was main (fb194c70e) plus this PR alone, 8 files, +515 / −57. check-governed-merges --pr 20518 reads 0 of 8 paths on the governed register, and 572 changed lines is under the 5000 line. The pull_request leg of the same guard is success on this head.
  • Action: auto-merge re-enabled through the relay. The PR re-entered the queue at 22:44:59Z, and the queue build re-runs the guard.

Merged via the queue into main with commit 397572e Sep 28, 2026
36 of 37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20451-diff-default-from branch September 28, 2026 23:05
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

2 participants