Skip to content

hono: a 409 CONCURRENT_UPDATE rendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries no currentVersion or currentRecord, which the wire contract and objectui's conflict bar read #23531

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): a door's answer omits fields its declared wire contract names. It was measured at a public door.

reach: measured by #23365's dev (os-dev-report 6176061093, out-of-scope finding 1). The setup was a real @objectstack/hono createHonoApp over the real dispatcher and protocol, in PR #23530's conformance rig, at 167cebe824 and later. Before PR #23530 the same request answered 500.

  • PATCH and DELETE /api/v1/data/task/ID with a stale If-Match answer 409 {"success":false,"error":{"code":"CONCURRENT_UPDATE","message":"Record task/ID was modified by another user (current version T1, expected T0)"}}.
  • The body has no currentVersion and no currentRecord.
  • The standalone REST server's 409 carries both, from packages/types/src/data-error-classification.ts's CONCURRENT_UPDATE arm.

Filed by domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, at PR #23530's ACCEPT. ⛔ Not a claim. Lane and grade are triage's.

Reader who acts: triage grades it. The landing is declaredEnvelopeForThrow in packages/plugins/plugin-hono-server/src/adapter.ts, exported by PR #23530. It forwards only the details that resolveThrownHttpError derives, while currentVersion and currentRecord are own properties of ConcurrentUpdateError. It is reached by createHonoApp's catch arms (after PR #23530) and by the direct-mount adapter's wrap/onError. That package is domain:cli by the lane table.

Governing text

  • content/docs/api/wire-format.mdx, section "Concurrent Update — 409 Conflict", carries currentVersion.
  • ConcurrentUpdateError's docblock (packages/metadata-protocol/src/protocol.ts) says the HTTP layer "includes both the current server-side version and the current record payload".
  • Renderer that reads them: objectui packages/plugin-detail/src/InlineEditSaveBar.tsx:292-:301 reads err.currentRecord and err.currentVersion.

Seam: contract: wire-format.mdx 409 CONCURRENT_UPDATE + ConcurrentUpdateError.currentVersion/currentRecord → runtime: plugin-hono-server declaredEnvelopeForThrow (createHonoApp thrownJson, HonoHttpServer wrap/onError) | renderer: objectui InlineEditSaveBar.

Acceptance (a proposal; triage's call)

Dedupe

MCP search_issues on objectstack-ai/objectstack, closed included: 409 CONCURRENT_UPDATE body missing currentVersion currentRecord createHonoApp declaredEnvelopeForThrow conflict body. It returned 1 hit: #22222 (closed), the /meta door's 409 METADATA_CONFLICT, which named the version only in prose. That is a different code and a different producer.

Dedupe words: CONCURRENT_UPDATE currentVersion currentRecord 409 createHonoApp declaredEnvelopeForThrow conflict body.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · bug · priority:p2 · domain:cli · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T19:54Z

    Read fresh. A 409 CONCURRENT_UPDATE from the Hono doors (createHonoApp, and the direct-mount adapter's wrap/onError) carries no currentVersion and no currentRecord. This was measured on createHonoApp.

    • The wire contract (wire-format.mdx, "Concurrent Update — 409 Conflict") and ConcurrentUpdateError's docblock name both fields.

    • RestServer's 409 carries them.

    • objectui's InlineEditSaveBar reads them.

    • priority:p2: a declared wire contract is broken on a measured public door, and a shipped renderer reads the missing fields. The conflict bar loses the server's version and record on these doors.

    • Not in the protocol-first class: the fix adds declared fields to an existing answer and refuses no request.

    • Lane domain:cli (plugin-hono-server, by the lane table). The order is met: PR fix(hono): createHonoApp's two catch arms answer a declared ADR-0112 envelope as declared, not as 500 (#23365) #23530 landed at 19:36Z.

    • Scope: the card's acceptance. Reuse RestServer's one classification (⛔ no third spelling of the conflict body), with pins on both Hono doors for PATCH and DELETE.

    • finding leaves on grading.

  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 11 (#23531: a 409 CONCURRENT_UPDATE that #16545's recogniser renders, on createHonoApp and on the direct-mount adapter, carries currentVersion and currentRecord as RestServer's does, through RestServer's one classification) · 2026-10-15T00:30Z
    Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23531-hono-conflict-body
    Worktree: objectstack-issue-23531
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Scope: batch 3, under the maintainer's 「并发加到3」. Released #23552 freed this slot.
    Order, read at this act:

    Container & model: S, judged (one recogniser, one existing classification to reuse), mode:subagent, model: default tier. Ceiling: Clause-②: no.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23531,
    "status": "done",
    "branch": "claude/issue-23531-hono-conflict-body",
    "pr": "#23582",
    "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (this run's harness-stamped id; subagent, so it is the parent's)",
    "premise_still_valid": true,
    "summary": "Premise confirmed on origin/main 7ffc578. Hypothesis (a) held: declaredEnvelopeForThrow (packages/plugins/plugin-hono-server/src/adapter.ts) forwarded only the details resolveThrownHttpError derives (a non-string code, issues, validation fields), and currentVersion/currentRecord are own properties of ConcurrentUpdateError that the resolver never reads. Hypothesis (b) held: structuredCodeAnswer is already exported from @objectstack/types (an export-block member of data-error-classification.ts, re-exported by index.ts export *), so no new export was needed and the grade stays Clause-②: no. Fix: a helper beside the recogniser, declaredConflictContext, asks structuredCodeAnswer for a 409 CONCURRENT_UPDATE only. It forwards everything the arm's body carries except its own sentence and code, and only when the arm agrees with the envelope already decided (same status, same code). The result rides error.details: that is ApiErrorSchema's declared context slot, and it is where @objectstack/client reads a wrapped body's context (errorBody.error.details), which objectui's normaliseClientError reads currentVersion/currentRecord from. Hypothesis (d) held: createHonoApp's thrownJson, wrap() through runHandler, the onError seam and the metrics observer all call declaredEnvelopeForThrow, so adapter.ts alone moves both doors; packages/adapters/hono/src/index.ts is not edited. Hypothesis (c), measured by reading: RestServer forwards currentRecord as the protocol threw it, which is the system-context probeRecord row, unfiltered. Matched exactly per the ruling, and filed as a disclosure finding against the producer and both doors (finding 1). Branch merged with origin/main (9d467b9) before the PR opened, because seat 1's #23532 landed a change to thrownJson's fallback arm; no conflict and no interaction, and the suites were re-run on the merged head. Worktree cleanup (rm node_modules, git worktree remove) runs right after this report posts.",
    "tests": "RED before the fix (built tree, pins only, 13b5267): plugin-hono-server src/handler-throw-declared-envelope.test.ts [#23531] block: x PATCH, x DELETE, 'expected undefined to deeply equal { ...(2) }', Tests 2 failed | 21 passed (23). http-conformance src/hono-declared-envelope.conformance.test.ts [#23531] block (real createHonoApp, real dispatcher and protocol over SQLite WASM): the body read was {success:false,error:{code:CONCURRENT_UPDATE,message:'Record task/ID was modified by another user (current version 2026-10-15T00:47:11.377Z, expected 2001-01-01T00:00:00.000Z)'}}, then 'expected undefined to match object', Tests 2 failed | 13 passed (15). GREEN after (7d88c43, plugin-hono-server rebuilt): 23/23 and 15/15. currentVersion is pinned by use: echoed as the next If-Match it is accepted (PATCH writes, DELETE deletes). Controls pass on both trees: the stored version is accepted, and a conflict stating no current state keeps the two-key envelope. ABLATION (node scripts/ablation-replace.mjs, wrap mode, plus an outer trap restore EXIT INT TERM on the absolute path): anchor if (resolved.code !== 'CONCURRENT_UPDATE') return undefined; was replaced by the same line comparing to 'ABLATION_23531_NO_SUCH_CODE' (anchor x1 to x0, replacement x0 to x1, blob 8e0d9721b86b to c4ebfc79a791). plugin-hono-server rebuilt; ablation-dist-preflight showed the marker present in dist/index.js and dist/index.mjs. Direction as predicted: the four field pins went RED (unit 2 failed | 21 passed, conformance 2 failed | 13 passed). Restore: blob equal to HEAD, git diff HEAD empty; rebuilt; preflight --absent showed the marker gone from all 6 built files with a clean tree; both suites green. PACKAGES at d7484b8: pnpm --filter @objectstack/plugin-hono-server typecheck (tsc, tsconfig.typecheck.json, check:test-typecheck OK with 0 debt) and test: Test Files 29 passed, Tests 340 passed. pnpm --filter @objectstack/http-conformance typecheck (check:test-typecheck OK, ledger unchanged) and vitest run: Test Files 13 passed, Tests 139 passed. MERGED HEAD 9d467b9 (the merge touched neither plugin-hono-server nor types): metadata-protocol rebuilt, then http-conformance vitest run gave Test Files 13 passed, Tests 139 passed, and the direct-mount file gave 23/23. @objectstack/hono's own suite was not run locally: it stubs @objectstack/runtime, its source is untouched by this diff, and the composed-host pin covers its behaviour. It is declared to CI.",
    "gates": "LOCAL at PR head 9d467b9: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 4 changed paths vs merge base fa6a608) derived 66 commands. All 66 ran, each exit captured before any pipe: 66 x exit 0. --ran reconciliation: '66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero, every line recorded as 'COMMAND :: exit N'). Pre-merge at d7484b8 the same 66 ran: 65 x exit 0, plus pnpm check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET (no dist/), which was NOT MEASURED there; at 9d467b9 it measured '110 published require entry point(s) across 68 package(s) load'. The four artifact-roster gates the derivation marks as keeping their roster under a touched directory also ran, all exit 0: node scripts/check-changeset-fixed.mjs, pnpm check:authz-resolver, pnpm check:error-code-casing, pnpm check:filter-alias-parity. Lint narrowed (see deviations): eslint --no-inline-config on the 3 changed sources gave exit 0, 0 errors and 0 warnings each; each file was linted, not ignored (6/5/5 active rules by --print-config); eslint.config.mjs is byte-identical between base and origin/main. CI on PR #23582 head 9d467b9, read once when this report was written: completed success for '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' and 'The card this PR closes must claim this branch'; 'Packed-tarball smoke (opt-in)' was skipped; in_progress for Check Changeset, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Governed Surface Queue Guard, Lint (CI release and engine gates, ESLint, PM dispatch-gates, docs and code guards, repo and tooling gates, the three scoped lanes), Type Check (consumer gates, debt ledger, source gates, workspace) and filter. Test Core and Build Core were not yet listed. CI convergence is the PM's.",
    "mcp_calls": "0",
    "api_writes": "2 relay strokes (scripts/pm/fleet-write/dispatch.mjs, executed as objectstack-fleet[bot]), each sent as one POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, read back 10418/10418 chars identical) and POST /repos//issues/23582/assignees (sunhaolin; read back). Stroke 2: POST /repos//issues/23531/comments (this report). label-write: 0, because the dispatch named no label and skip-changeset does not apply (the diff publishes). git push x5 (not REST).",
    "deviations": [
    "pnpm lint: the dispatch says the lane always adds pnpm lint or a proven narrowing; the role file says a repo-level scan led by pnpm lint is CI's run and never owed. Conflict stated, not resolved silently: I ran the narrowing, with three pieces of evidence: (1) the derived change set is 4 paths, 3 of them lintable; (2) eslint --no-inline-config on those 3 gave exit 0 with 0 messages; (3) each file was really linted (active rules by --print-config), and the ESLint config is unchanged between base and origin/main. The full pnpm lint is declared to CI (the 'Lint · ESLint' check-run).",
    "File surface: besides declaredEnvelopeForThrow and the helper beside it, adapter.ts gained one name, structuredCodeAnswer, on its existing @objectstack/types import line. That is necessary for the call; there is no new export anywhere.",
    "Changeset names @objectstack/hono (patch) as well as @objectstack/plugin-hono-server. createHonoApp's answer is the measured door, and the still-unreleased #23365 changeset states this gap in hono's CHANGELOG. Both packages are in the one fixed version group, so no version moves because of it. Nothing in hono's own files changed.",
    "A scratch measurement test (packages/qa/http-conformance/src/zz-scratch-23531-family.conformance.test.ts, for finding 2) existed in the worktree for about 30 s while the pre-merge gate battery ran. It was never committed and was deleted; git status was clean after. The three gates whose run overlapped that window were re-run clean, and the authoritative battery was re-run in full on the merged head with a clean tree.",
    "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Per the role file and AGENTS.md (the pre-push hook refuses a model id in the pair), commits carry the model-free pair Co-authored-by: Claude / Claude-Session, and the PR body ends with the session-URL footer.",
    "Merged origin/main into the branch before opening the PR (AGENTS.md §10), because #23532's thrownJson change landed meanwhile. A plain merge commit, no force-push."
    ],
    "files_changed": [
    "packages/plugins/plugin-hono-server/src/adapter.ts",
    "packages/plugins/plugin-hono-server/src/handler-throw-declared-envelope.test.ts",
    "packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts",
    ".changeset/23531-hono-conflict-body.md"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: (b) data disclosure · reach: exception: security (possible data leak; not measured at a door with a security plugin booted) · evidence (read on 7ffc578): the 409 CONCURRENT_UPDATE's currentRecord is the row metadata-protocol's probeRecord reads with engine.findOne(object, { where: { id }, context: { isSystem: true } }) (packages/metadata-protocol/src/protocol.ts, probeRecord). updateData runs assertVersionOf on that row BEFORE engine.update applies the caller's write policy, and assertVersionMatch does the same for delete. structuredCodeAnswer's CONCURRENT_UPDATE arm (packages/types/src/data-error-classification.ts) forwards error.currentRecord unfiltered, so a caller who sends a stale If-Match for a row, or fields of it, that RLS/FLS hides would receive the system-context row. True at RestServer since before this card; PR #23582 extends the same answer to both Hono doors, by the dispatch ruling ('Match RestServer exactly ... a finding against both doors, not a reason to differ'). Landing is the producer (metadata-protocol: read currentRecord under the caller's context, or narrow it before throwing), which fixes every door at once. Seam: spec: ConcurrentUpdateError.currentRecord (wire-format.mdx 409 Conflict) → runtime: metadata-protocol probeRecord/assertVersionOf → rest mapDataError + plugin-hono-server declaredEnvelopeForThrow | renderer: objectui InlineEditSaveBar / ConcurrentUpdateDialog · dedupe words: currentRecord isSystem probeRecord 409 CONCURRENT_UPDATE disclosure field-level security row-level security conflict body",
    "class: (b) · reach: measured at a public door. A real createHonoApp (real dispatcher, protocol and SQLite WASM; scratch test, not committed) at d7484b8: DELETE /api/v1/data/parent/ID, where a child lookup with deleteBehavior 'restrict' references the parent, answered 409 {success:false,error:{code:DELETE_RESTRICTED,message:'This Parent is still referenced by 1 Child record(s) through “Parent”. Delete or reassign them first.'}}. The body has no developerMessage, dependentObject, dependentCount or object, which RestServer's structuredCodeAnswer DELETE_RESTRICTED arm carries. Same family as this card (the Hono doors drop structured-arm fields), so name it into the family's closing card rather than a single-point card. Not measured: METADATA_CONFLICT's currentVersion, INVALID_FIELD's field. Mechanical shape: widen declaredConflictContext from one code to 'the arm agrees on status and code'. Seam: contract: structuredCodeAnswer arms (DELETE_RESTRICTED developerMessage/dependentObject/dependentCount) → runtime: plugin-hono-server declaredEnvelopeForThrow (createHonoApp thrownJson, HonoHttpServer wrap/onError) | renderer: consumer: objectui delete-restricted toast/dialog (not measured) · dedupe words: DELETE_RESTRICTED developerMessage dependentObject dependentCount createHonoApp declaredEnvelopeForThrow structured arm Hono door"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23531,
    "round": "patch round 1 (REWORK per the seat's review 6182753773, same claim 6181557788)",
    "status": "done",
    "branch": "claude/issue-23531-hono-conflict-body",
    "pr": "#23582",
    "head": "69215f8575af35727d4ef50e98e879c46cd4af0b",
    "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (this run's harness-stamped id; subagent, so it is the parent's)",
    "premise_still_valid": true,
    "summary": "The one item is done: currentRecord does not ride this PR. (1) declaredConflictContext (packages/plugins/plugin-hono-server/src/adapter.ts) now forwards everything structuredCodeAnswer's CONCURRENT_UPDATE arm carries except error, code and currentRecord, with a one-line pointer to #23586 at the exclusion. The ⚠️ paragraph is rewritten: the field is HELD because probeRecord reads the row with a system context and assertVersionOf throws it before the caller's write policy runs, so it may carry rows or fields RLS/FLS hide; a version token carries no row data, so currentVersion is forwarded. The function-level doc says currentRecord is the ONE field named, and only to hold it; declaredEnvelopeForThrow's own docblock says the same in one line. (2) Pins: the direct-mount file asserts error.details toEqual { currentVersion } exactly, not.toHaveProperty('currentRecord') citing #23586, and that the row's value appears nowhere in the body. The conformance file keeps the currentVersion assertion and the If-Match echo (PATCH writes, DELETE deletes), and asserts currentRecord absent although the real protocol states it, citing #23586. Controls kept. (3) Changeset: the TO body is details.currentVersion only, with one sentence that currentRecord is held until #23586 and the REST server's answer is unchanged; the summary line and the #23365 gap sentence are adjusted to match. (4) PR body: line 1 'Part of #23531', line 2 'Clause-②: no'; no closing verb stands next to #23531 anywhere; #23586 and #23587 are named in Acceptance notes. Updated through the relay (issue_patch), read back byte-identical. The PR title was amended in the same PATCH (see deviations). git merge-tree --write-tree HEAD origin/main was clean (exit 0, origin/main one docs commit ahead), so there was no merge. New head 69215f8. Worktree removed after this report.",
    "tests": "At 69215f8, after rebuilding http-conformance's dependency closure (plugin-hono-server included) under the lock: pnpm --filter @objectstack/plugin-hono-server typecheck (tsc, tsconfig.typecheck.json, check:test-typecheck OK 0 debt) and test gave Test Files 29 passed, Tests 340 passed. pnpm --filter @objectstack/http-conformance typecheck (check:test-typecheck OK, ledger unchanged) and vitest run gave Test Files 13 passed, Tests 139 passed. ABLATION (pin structure changed, so both legs ran; node scripts/ablation-replace.mjs wrap mode plus an outer trap restore EXIT INT TERM on the absolute path; each leg rebuilt plugin-hono-server, and ablation-dist-preflight showed the marker in dist/index.js and dist/index.mjs): Leg A dropped the new exclusion (anchor && key !== 'currentRecord'), replaced by marker ABLATION_23531_HELD_FIELD, blob 880175e8d1ec to ed02c80843d0). The absence pins went RED: unit 2 failed | 21 passed ('expected { ...(2) } to deeply equal { Object (currentVersion) }'), conformance 2 failed | 13 passed ('to not have property "currentRecord"'). Leg B disabled the guard (anchor if (resolved.code !== 'CONCURRENT_UPDATE') return undefined; replaced by marker ABLATION_23531_NO_SUCH_CODE, blob to 49308e91060e). The currentVersion pins went RED: unit 2 failed | 21 passed ('expected undefined to deeply equal ...'), conformance 2 failed | 13 passed ("expected 'undefined' to be 'string'"). The first run at bb6d0bf showed leg B's conformance red arriving as a TypeError: the absence assertion ran on an undefined details before the currentVersion check. The assertions were reordered (69215f8) and BOTH legs re-run, giving the result above. Restore: blob equal to HEAD, git diff HEAD empty, porcelain 0; rebuilt; preflight --absent showed both markers gone from all 6 built files with a clean tree; both pin files green (23/23, 15/15). Round-0 red-before evidence stands (13b5267: unit 2 failed | 21 passed, conformance 2 failed | 13 passed).",
    "gates": "LOCAL at 69215f8: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 4 changed paths vs merge base fa6a608) derived the same 66 commands as round 0. All 66 ran, exit captured before any pipe. 65 exited 0 on the first pass. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: the fresh worktree had no workspace dist/). After a cache-backed pnpm turbo run build under the lock (75 successful, 74 cached), it gave exit 0, '110 published require entry point(s) across 68 package(s) load'. --ran reconciliation: '66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Roster gates under touched directories, all exit 0: node scripts/check-changeset-fixed.mjs, pnpm check:authz-resolver, pnpm check:error-code-casing, pnpm check:filter-alias-parity. Narrowed eslint --no-inline-config on the 3 changed sources: exit 0, 0 errors and 0 warnings each; eslint.config.mjs unchanged merge-base to origin/main. CI on 69215f8, read once after the body edit: completed success for Build Core, TypeScript Type Check (and its four lanes), Lint & Repo Gates (and its lanes, including 'Lint · ESLint'), Governed Surface Queue Guard, Test Core 1/6, 2/6, 4/6, 5/6 and 6/6, Check Changeset, Check Documentation Links, Check PR Size, Dogfood Regression Gate, Flag docs affected by code changes, filter, and the four card/claim checks. Test Core (3/6) was in_progress. The body edit re-triggered Check Changeset and three claim checks, which were in_progress. CI convergence is the PM's.",
    "mcp_calls": "0",
    "api_writes": "This round, 3 relay strokes (scripts/pm/fleet-write/dispatch.mjs, executed as objectstack-fleet[bot]), each sent as one POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1, issue_patch: PATCH /repos//issues/23582 (title and body; read back 10571 bytes identical). Stroke 2: POST /repos//issues/23531/comments (this patch-round report). Stroke 3, comment_edit: PATCH /repos/objectstack-ai/objectstack/issues/comments/6183237753, to correct this report's head field, which was first posted with a wrong full SHA (its 10-character prefix 69215f8 was right). label-write: 0. git push x2 (bb6d0bf, 69215f8; not REST).",
    "deviations": [
    "PR title amended in the same issue_patch as the body, from '... carries currentVersion and currentRecord (#23531)' to '... carries currentVersion (#23531)'. The order said 'Nothing else changes', but the old title stated a behaviour this PR no longer has, and it becomes the squash subject on main. The seat can revert it in one PATCH if it wants the old title.",
    "Pins: the conformance block's two assertions were reordered (currentVersion before the currentRecord absence) after the first ablation run showed leg B's red arriving as a TypeError, not as the currentVersion assertion. This is a separate commit, 69215f8, and both legs were re-run on it.",
    "Ablation leg A (the new currentRecord exclusion) was run beside the requested guard leg. The round's diff changes the behaviour the new absence pins hold, and leg A is the proof that those pins can fail.",
    "Fresh worktree: check:dual-build-cjs-loads needed workspace dist/, so a cache-backed turbo build (74/75 cached, 5 s under the lock) ran before it measured."
    ],
    "files_changed": [
    "packages/plugins/plugin-hono-server/src/adapter.ts",
    "packages/plugins/plugin-hono-server/src/handler-throw-declared-envelope.test.ts",
    "packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts",
    ".changeset/23531-hono-conflict-body.md"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: #23586 (currentRecord disclosure at the producer; filed by the seat from round 0's finding 1) · noted, not refiled",
    "carrier: #23587 (DELETE_RESTRICTED structured fields at createHonoApp; filed by the seat from round 0's finding 2) · noted, not refiled"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23582 comment 6183269783 · 2026-10-15T02:49Z


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23582 → 19c554ddc1 (Part of; the card stays open) · 2026-10-15T03:19Z

    domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU · holder of claim 6181557788, released below. Review: REWORK 6182753773, then the seat record 6183269783 on the PR (pointer 6183277013 on this card). No contract review was owed.

    Reading Value
    PR merged: true, merged_at 2026-10-15T03:18Z, through the merge queue (added_to_merge_queue 2026-10-15T02:52Z)
    Queue branch gh-readonly-queue/main/pr-23582-* absent (git ls-remote, 0 refs)
    origin/main 19c554ddc1 is an ancestor; one parent (1a9d488136); 4 files, +259 / −1, as reviewed at 69215f8575
    Closing keywords in the landed commit none (Part of #23531), so this card stays open, as intended
    Lane reconciliation 15 open domain:cli cards (read at this stamp), #23531 among them, released below

    What landed: a 409 CONCURRENT_UPDATE answered by either Hono door (createHonoApp, and a route on HonoHttpServer) now carries currentVersion in error.details, read from RestServer's one classification.

    What remains on this card: currentRecord at the Hono doors. It is held at declaredConflictContext's one exclusion (packages/plugins/plugin-hono-server/src/adapter.ts), pointing at #23586. The protocol reads that row with a system context before the caller's write policy runs. The remainder is to lift the exclusion once #23586 has been answered at the producer, and to flip the two absence pins.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: domain:cli seat 2 · sunhaolin · session_01Q9PZ5Pzqcus4woVvTZwWNU · 2026-10-15T03:19Z · cause: PR #23582 landed the currentVersion half (Part of), and the currentRecord half waits on #23586 · destination: pm:queue, unassigned


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 17 (#23531's remainder: the Hono doors' 409 CONCURRENT_UPDATE carries currentRecord again, now that #23586's producer fix makes it the caller's own masked read; the hold in declaredArmContext is lifted and the absence pins flip) · 2026-10-15T10:09Z
    Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23531-lift-current-record-hold
    Worktree: objectstack-issue-23531b
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Scope: batch 3, by 「并发恢复3」. #23558 is in the merge queue, #23546 waits on the maintainer (needs-user-decision), and this card takes a free slot.
    Order, read at this act:


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23531,
    "round": "remainder (claim 6189624811): lift the currentRecord hold",
    "status": "done",
    "branch": "claude/issue-23531-lift-current-record-hold",
    "pr": "#23667",
    "head": "4bc9b0c08be1b3398bd5b3d73136b17f5e8b5a49",
    "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (this run's harness-stamped id; subagent, so it is the parent's)",
    "premise_still_valid": true,
    "summary": "Claim verified: the newest Claim: (6189624811) names this branch. Premise confirmed on origin/main 7cb33cf: PR #23628 (7dc7d22) is an ancestor (merge-base --is-ancestor exit 0), and metadata-protocol's assertVersionOf now re-reads the row with the caller's context (readAsCaller): no row gives 404 RECORD_NOT_FOUND, a readable row gives ConcurrentUpdateError with the caller's masked read as currentRecord. Hypothesis (a) held: the only site that strips currentRecord on the Hono path is declaredArmContext's filter. The sole producer is metadata-protocol's assertVersionOf; createHonoApp's thrownJson, wrap()/onError and the metrics observer hand declaredEnvelopeForThrow's body to c.json unchanged; the ablation that restores the filter turns every presence pin red. Fix: the filter drops only the arm's error and code (adapter.ts), and the held-docblock lines (:303-:304, :381-:383, :415-:426) are rewritten. The field is forwarded because the producer reads it as the caller, and an arm that grows a field read with a system context must still be held by name. Pins flipped to presence: the unit rows assert error.details toEqual { currentVersion, currentRecord } exactly. The conformance rows (real createHonoApp, real protocol) assert details equal to what the protocol threw (spied), keep the If-Match echo pins, and add PATCH/DELETE rows where a field the caller's read masks is absent from currentRecord, which equals the caller's own read and the thrown record while the store still holds the field. Hypothesis (b) held: with the real SecurityPlugin (scratch test on #23628's runtime rig, not committed), the Hono exit's details equal RestServer's { currentVersion, currentRecord } for the same request, with no masked value on either wire (4/4). Three docs pages that said a Hono door never sends currentRecord were corrected (see deviations: outside the listed file surface). Changeset @objectstack/plugin-hono-server + @objectstack/hono patch with FROM to TO. origin/main (286eb9e) was merged before the PR opened. Worktree cleanup (rm node_modules, git worktree remove) runs right after this report posts.",
    "tests": "FINAL HEAD 4bc9b0c, after a full turbo run build (75/75): pnpm --filter @objectstack/plugin-hono-server typecheck (tsc + tsconfig.typecheck.json + check:test-typecheck OK, 0 debt) and test: Test Files 29 passed, Tests 354 passed. pnpm --filter @objectstack/http-conformance typecheck (check:test-typecheck OK, ledger unchanged 3 files/27 errors/10 signatures) and vitest run: Test Files 13 passed, Tests 144 passed. Consumer pins (runtime resolves plugin-hono-server through dist/): data-conflict-caller-visibility, data-update-expected-version, data-delete-expected-version: 3 files, 115 passed. ABLATION at 7a8e9bf (node scripts/ablation-replace.mjs wrap mode plus an outer trap restore on EXIT INT TERM over the absolute path; HEAD blob checked non-empty first). The filter clause key !== 'error' && key !== 'code' (the anchor) was replaced by the same clause plus && key !== 'currentRecord' && key !== 'ABLATION_23531_RESTORE_HOLD': anchor x1 to x0, replacement x0 to x1, blob 4b7ca0f62478 to 3e3313e4060c, marker in src x1. plugin-hono-server rebuilt; ablation-dist-preflight: marker in dist/index.js and dist/index.mjs. Direction as predicted (turned red): unit 2 failed | 35 passed (PATCH, DELETE: 'expected { Object (currentVersion) } to deeply equal { ...(2) }'); conformance 4 failed | 16 passed (both presence rows; both masked-field rows 'expected undefined to match object'). RESTORE: blob after restore == HEAD 4b7ca0f62478, git diff HEAD empty (tool and outer trap both); rebuild 6/6 turbo cache hits; preflight --absent: marker absent from all 6 built files, tree clean; porcelain 0; both files green 37/37 and 20/20. HYPOTHESIS (b) at 7a8e9bf: scratch copy of data-conflict-caller-visibility.test.ts plus 4 rows comparing, for the same stale PATCH/DELETE on the FLS-masked row and the plain row, the Hono exit's error.details with RestServer's { currentVersion, currentRecord }: 4/4 equal, no masked value on either wire; the committed file 16/16 beside it; scratch deleted, porcelain 0. Not run: a producer-side ablation (reverting #23628's read), because this diff does not change the producer. @objectstack/hono's own suite was not run locally (it stubs @objectstack/runtime, and its source is untouched); declared to CI.",
    "gates": "LOCAL at 4bc9b0c: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 7 paths vs merge base 286eb9e; first stderr line names objectstack-ai/objectstack at 4bc9b0c) derived 95 commands, the same list as at 7a8e9bf. All 95 ran, each exit captured before any pipe: 95 x exit 0. --ran: '95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Pass 1 at 7a8e9bf: 92 x exit 0, check:skill-examples and check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, no dist), and check:query-options-erasure exit 1. That red was real and mine: two as any on findOne options took the test surface from 236 to 238. Fixed in bc83077 (back to 236); pass 2 measured all three after the full build. The --ran run printed a STALE TREE warning: origin/main moved to 1ced0f8 after the merge, touching only scripts/pm/record-recognisers.mjs and a pm-dispatch reference. dispatch-gates.mjs and dispatch-gates.data.mjs are unchanged across that range, and no derived family runs that file, so the list stands; not merged. LINT narrowed (see deviations): eslint --no-inline-config on the 3 changed TS sources gave exit 0, 0 errors and 0 warnings each; each file was really linted (6/5/5 active rules by --print-config); eslint.config.mjs is unchanged base to origin/main; the 3 .mdx pages are ignored by the ESLint config, as under pnpm lint. CI on PR #23667 head 4bc9b0c, read once at report time: completed success for 'No other open PR may claim the same issue'; 'Packed-tarball smoke (opt-in)' skipped. in_progress for Check Changeset, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Governed Surface Queue Guard, Lint (CI release and engine gates, ESLint, PM dispatch-gates, docs and code guards, repo and tooling gates, the three scoped lanes), 'No other open PR may claim the same single-writer path', 'Part-of PR must not also close its card', Spec property liveness, 'The card this PR closes must claim this branch', Type Check (consumer gates, debt ledger, source gates, workspace) and filter. Test Core and Build Core were not yet listed. CI convergence is the PM's.",
    "mcp_calls": "0",
    "api_writes": "2 relay strokes (scripts/pm/fleet-write/dispatch.mjs, executed as objectstack-fleet[bot]), each sent as one POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1, pr_create (run 38727737401, success): POST /repos/objectstack-ai/objectstack/pulls (draft forced; read back 8799/8799 bytes identical) and POST /repos//issues/23667/assignees (sunhaolin; read back). Stroke 2: POST /repos//issues/23531/comments (this report). label-write: 0 (the dispatch named none; skip-changeset does not apply because the diff publishes). git push x4 (empty branch probe, 7a8e9bf, bc83077, 4bc9b0c; not REST).",
    "deviations": [
    "File surface BREACH, stated rather than resolved silently. Three docs pages are edited outside the claim's listed surface: content/docs/api/index.mdx, content/docs/api/wire-format.mdx (the 'Hono hosts nest the extras' paragraph and its example) and content/docs/protocol/kernel/error-handling.mdx (the Hono-door table row and the 'never sends' line). PR #23616 wrote 'a Hono door never sends currentRecord' into them, and this change makes that false. The claim says 'stop on a breach'; the role file says a shipped surface this round's change makes false is fixed in the round, and that the role file wins a conflict with the dispatch. I took the role file's side and say so here. Each edit only removes the hold claim and adds one sentence that the record is the row as the caller reads it. If the seat rules otherwise, those three hunks revert alone.",
    "pnpm lint: the dispatch says this lane always adds pnpm lint or a proven narrowing, and the role file says a repo-level scan led by pnpm lint is CI's run. I ran the narrowing with its three pieces of evidence: (1) the derived change set is 7 paths, 3 of them TS sources and 3 .mdx that ESLint's config ignores; (2) eslint --no-inline-config on the 3 sources: exit 0, 0 messages; (3) each was really linted (active rules by --print-config), and eslint.config.mjs is unchanged base to origin/main. The full pnpm lint is declared to CI (the 'Lint · ESLint' check-run).",
    "Hypothesis (b) is measured on #23628's runtime rig (real SecurityPlugin, real RestServer, the dispatcher rendered through declaredEnvelopeForThrow), not on the conformance rig. The conformance rig has no RestServer or plugin-security dependency; there, the door's details are asserted equal to the currentVersion/currentRecord the real protocol threw, which is exactly what RestServer's CONCURRENT_UPDATE arm forwards (plus the route-only object). The scratch file packages/runtime/src/domains/zz-scratch-23531-door-parity.test.ts existed for one locked run (about 20 s), outside any gate battery's window; it was never committed and was deleted; porcelain 0 after.",
    "FLS on the conformance rig is an engine-middleware stand-in (a caller-context read rewritten after next(), the seam plugin-security's field-level rules use), not SecurityPlugin itself. http-conformance does not depend on plugin-security, and adding it means package.json and pnpm-lock.yaml, outside this claim. The real FLS/RLS evidence is the runtime pin (16/16 at the final head) and the parity measurement above.",
    "Merged origin/main (286eb9e, 4 commits, none touching these files; one touches driver-sqlite-wasm, which the conformance rig boots) before opening the PR, per AGENTS.md section 10. Merge commit 4bc9b0c, no force-push; the full build and every suite and gate were re-run on it.",
    "The two unreleased changesets that say currentRecord stays held (.changeset/23531-hono-conflict-body.md, .changeset/23587-hono-structured-arm-fields.md) are not edited (outside this claim); the new changeset says it supersedes that note. This is named in the PR's Acceptance notes, carrier: the seat.",
    "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Per the role file and AGENTS.md (the pre-push hook refuses a model id in the pair), commits carry the model-free pair Co-authored-by: Claude / Claude-Session, and the PR body ends with the session-URL footer."
    ],
    "files_changed": [
    "packages/plugins/plugin-hono-server/src/adapter.ts",
    "packages/plugins/plugin-hono-server/src/handler-throw-declared-envelope.test.ts",
    "packages/qa/http-conformance/src/hono-declared-envelope.conformance.test.ts",
    ".changeset/23531-hono-conflict-current-record.md",
    "content/docs/api/index.mdx",
    "content/docs/api/wire-format.mdx",
    "content/docs/protocol/kernel/error-handling.mdx"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed (named in the PR's Acceptance notes): packages/runtime/src/domains/data-conflict-caller-visibility.test.ts's dispatcher-door rows read currentRecord off the thrown error, not the wire, because the wire did not carry it before this change. They could now assert the wire; the parity measurement above shows it equals RestServer's.",
    "carrier: none · noted, not filed, and not in the PR's Acceptance notes (the body is written once): content/docs/api/wire-format.mdx's flat 'Concurrent Update — 409 Conflict' example shows currentVersion and object but not currentRecord, which the REST server sends and whose Fields list names it. This is example drift on the flat door, untouched by this diff."
    ]
    }

  10. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23667 comment 6191403700 · domain:cli seat 2 · 2026-10-15T11:53Z


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23667 → 5bb9a83bd0 · 2026-10-15T12:22Z

    domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU · holder of claim 6189624811, released by this landing. Review: ACCEPT 6191403700 on the PR (no contract face).

    Reading Value
    PR merged: true, merged_at 2026-10-15T12:21Z, through the merge queue (added_to_merge_queue 2026-10-15T11:55Z)
    Queue branch gh-readonly-queue/main/pr-23667-* absent (git ls-remote, 0 refs)
    origin/main 5bb9a83bd0 is an ancestor; one parent (cad343e201); 7 files, +178 / −67, as reviewed at 4bc9b0c08b
    Closing keywords in the landed commit Fixes #23531 only
    Lane reconciliation 16 open domain:cli cards (read at this stamp), #23531 no longer among them

    State change: #23531 closed completed by the merge; pm:dispatched removed in this act. Both halves of the card are now on main: currentVersion (PR #23582, 19c554ddc1) and currentRecord (this PR).

    Carried, not filed: the two earlier unreleased changesets (.changeset/23531-hono-conflict-body.md, .changeset/23587-hono-structured-arm-fields.md) still say currentRecord stays held; .changeset/23531-hono-conflict-current-record.md supersedes them. The one-story question rides release-text review, not a code PR.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions