Repository navigation
test(http-conformance): a real createHonoApp host holds PUT /meta If-Match, If-None-Match and ?mode=draft - #22341
Merged
objectstack-fleet[bot] merged 1 commit intoOct 8, 2026
Conversation
…f-Match, If-None-Match and ?mode=draft Four rows through app.request over a real ObjectStackProtocolImplementation (ObjectQLPlugin's built-in assembly on in-memory SQLite WASM): a stale If-Match, If-None-Match: * over an existing row, ?mode=draft, and the unguarded control. Each reads the store after the request. Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU Co-Authored-By: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Oct 8, 2026
objectstack-fleet
Bot
deleted the
claude/issue-22221-hono-meta-write-conformance
branch
October 8, 2026 23:22
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #22221
Clause-②: no
What this adds
One test file, no production code:
packages/qa/http-conformance/src/hono-meta-save-preconditions.conformance.test.ts. It composes the realcreateHonoAppover aLiteKerneland sends every request throughapp.request(...). So the@objectstack/honocatch-all's hand-off todispatch()(packages/adapters/hono/src/index.ts:739) is in the path, which no test held before. Four rows. Each asserts the status, the envelope, and the stored rows, read back fromsys_metadataafter the request:PUT /api/v1/meta/view/case_gridtwice, no header200,success: true, receiptsstate: 'active'; active rowv2, no draft rowIf-MatchPUTwithIf-Matchset to the first receipt'sversion409,success: false,error.codeMETADATA_CONFLICT; active row stillv2; the current token then savesv3If-None-Match: *200), then the same over the existing row409,success: false,METADATA_CONFLICT; active row stillfirst?mode=draftPUT ?mode=draftover an active row200, receiptstate: 'draft'; active row unchanged, draft rowstaged;GET ...?state=draftanswersstaged, and the plainGETstill answers the active rowFile choice (the seat's cut): a new file beside the read-side twin, not rows added to it.
hono-meta-item-read-gate.conformance.test.tsis about the read door, and it answers from a stub protocol (avi.fnover a plain object) that keeps no version and no draft. The write rows need a real store. So they live in their own file, and each file stays about one door.There is no
package.json,turbo.jsonorscripts/cross-package-test-inputs.mjschange: every import is a dependency this package already declares (H5 below). There is no changeset:@objectstack/http-conformanceisprivate: true, and the diff is one test file.Readings (the PM's H1 to H5, measured on
origin/main28bff18d)H1, the line under test: holds. The read path:
dispatcher.dispatch(method, subPath, body, queryParams, { request: c.req.raw }, prefix)(packages/adapters/hono/src/index.ts:739);handleMetadataRequest'sPUTbranch, which callsmetaSaveRequestOptions({ headers: _context.request?.headers, query })(packages/runtime/src/domains/meta.ts:1366; the function is inpackages/rest/src/meta-save-request.ts);protocol.saveMetaItem.The function that reads each one:
If-MatchandIf-None-Match:metaSaveRequestOptions, through itsrequestHeader()helper. That helper callsHeaders.geton the rawRequestthe catch-all hands on ascontext.request.?mode=draft: alsometaSaveRequestOptions, but fromhttp.query. That is the catch-all'squeryParamsargument (flattened from the URL), not theRequest. See H4.H2, the harness: the twin's shape is reused, its store is not. Same shape:
LiteKernel, thencreateHonoApp({ kernel, prefix: '/api/v1', cors: false }), thenapp.request. Same single stubbed seam:HttpDispatcher.prototype.timedResolveExecutionContext. Two differences, both deliberate:ObjectQLPlugin's built-in assembly (registerProtocolat its default). It registers a realObjectStackProtocolImplementationand thesys_metadata*objects. It runs overSqliteWasmDriver({ filename: ':memory:' }), handed in as adriver.memoryservice. The boot logsSchema sync complete {"synced":5}. Both packages were already in this package'sdevDependencies, because the integration suite boots them.manage_metadata) for every request, so no row depends on the request to know who the caller is. Otherwise, ablating the{ request }hand-off would also strip the caller. Every row, the control included, would then go red for the wrong reason.H3, the four rows through the real app: hold. See the table above. 4 passed.
H4, the ablation: partly falsified. Passing
{}turns only the two header rows red. The?mode=draftrow stays green under it, becausemoderides thequeryParamsargument of the same call, notcontext.request. So the draft row gets its own ablation of the hand-off: the query argument. The control stayed green in every leg. Table below.H5, cross-package test inputs: nothing to add.
pnpm check:cross-package-test-inputsexits 0:OK: 30 package(s) read outside themselves, all declared ... 2854 test file(s) import a workspace sibling by bare specifier over 378 package pair(s), every one declared.The new file imports only by bare specifier, over pairs that are already declared.Ablations (one per row, never committed)
The subject is
packages/adapters/hono/src/index.ts, HEAD blob8c32e89e8fc2.vitest.config.ts's anchored alias points@objectstack/honoat that SOURCE, so nodist/leg applies. Each leg went red whilepackages/adapters/hono/distwas untouched, which proves the source is what runs.How each leg ran:
node scripts/ablation-replace.mjsin WRAP mode, underos-verify-lock.sh;8c32e89e8fc2, equal to HEAD) and an emptygit diff HEAD.I wrote down the expected direction of each leg before running it. Each leg landed as predicted.
:739If-MatchIf-None-Match: *?mode=draft{ request: c.req.raw }→{}(blob3900c478d42e)200, success: truewhere409was expected200where409was expected{ request: new Request(c.req.raw.url, { method: c.req.method }) }, aRequestwith no headers (blobeffd7d0f0b20)queryParams→{}in the same call (blobb64245cd76d5)state: 'active'where'draft'was expectedEach red reproduces the defect #22141 described, on the wire: a guarded save was answered
200and written, or a draft went live.Tier
Test Core, on every PR.ci.yml'sTest Core (N/6)shards runpnpm turbo run testover the affected packages. This package'stestscript is a plainvitest run(one config, no project split), so the new file runs whenever@objectstack/http-conformanceis affected.Test Coreis one of the seven required contexts.Verification (head
e66efff7, base28bff18d)pnpm --workspace-concurrency=2 --filter '@objectstack/http-conformance^...' build(the dependency closure): exit 0. A grep confirms that@objectstack/runtime'sdist/carriesmetaSaveRequestOptions({ headers: _context.request?.headers, query }).pnpm --filter @objectstack/http-conformance test:Test Files 9 passed (9),Tests 106 passed (106).pnpm --filter @objectstack/http-conformance typecheck: exit 0,check:test-typecheck: OK ... 3 file(s) / 27 error(s) / 10 pinned signature(s) held, ledger unchanged. The new file is in that program:tsc --listFiles -p tsconfig.test.jsonlists 9 of the package's 9 test files, the new one included. A file the ledger does not list may carry no error.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived at head, gives 53 commands (1 path, 206 changed lines), a subset of the 71.--ranreconciliation:✓ dispatch-gates --ran: 53 derived famil(ies) accounted for — 52 run, 1 NOT-MEASURED, 0 unrun.pnpm check:dual-build-cjs-loads, reason: exit 3,PREREQUISITE NOT MET. 34 packages are unbuilt in this worktree. This diff adds one test file to a private package that has nobuildscript, so it emits nothing that gate reads. CI runs the gate in full.pnpm lint(eslint . --no-inline-config, the whole repo) ate66efff7: exit 0, no findings.origin/mainis 1 commit past the base (e36ee535). It touches 10 files underservice-storage,plugins/organizationsandqa/dogfood, none of them in this suite's dependency closure, so I did not mergemain.Acceptance notes
{ request: c.req.raw }argument carries the two precondition headers. ThequeryParamsargument carries the lifecycle. A row now goes red if either one is lost.[sql-driver] DATABASE_ERRORlines (no such table: sys_setting,no such table: _objectstack_sequences) atkernel:ready. They come from platform-migration probes against tables this minimal composition does not provision. They print before the first request, and no row depends on them.409still carries nocurrentVersionas data (PR fix(runtime): the @objectstack/hono catch-all's PUT /meta honours If-Match, If-None-Match and ?mode=draft #22206's open question H6). These rows assert onlystatus,successanderror.code, so they neither pin nor block any answer to that question.Written by session
session_01RWZbGvPFcRKvUqASZtunCU.Generated by Claude Code