Skip to content

feat(verify): hooks.run takes the system principal on an insert and a delete - #22596

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-22301-system-insert-delete-door
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-22301-system-insert-delete-door

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #22301
Clause-②: yes (widening)

Item 5 of the card: a user-less record trigger on an insert or a delete, and a flow over a record the engine no longer holds. Items 6 and 7 and item 1's remaining composition gap are not in this PR, and the card stays open for them.

What changes

  • hooks.run(object, 'insert' | 'delete', input, { system: true }) now runs. It is the same engine call a person's write makes (ql.insert(object, input, { context }), ql.delete(object, { where: { id }, context })), with context set to { isSystem: true } (the handle's existing SYSTEM_CONTEXT). This is the write an integration or a system job makes. Until now hooks.run refused { system: true } on anything but update, with INVALID_REQUEST / 400, and that refusal is removed.
  • The AsSystem overload of hooks.run widens from 'update' to 'insert' | 'update' | 'delete'. Its JSDoc says what runs: the hooks (they see session.isSystem and no userId), the declared validations, and the record-change trigger with no trigger user. It also says what does not run: the permission gate. It says what a delete-triggered flow is handed, and how this door differs from seed, which sets skipTriggers.
  • A call naming two callers ({ as, system: true }) is still refused with INVALID_REQUEST / 400 before the engine is touched, on every operation.
  • packages/verify/README.md: the handle example and the caller and refusal bullets.
  • .changeset/22301-verify-system-insert-delete-door.md: @objectstack/verify minor, Clause-②: yes (widening).
  • Declared deviation: two pending release notes from PR feat(verify): the handle gains a system-context update door and a predicate update door #22517 (items 2 and 3, not yet released) said this PR's door was refused, and both are corrected in this change. .changeset/22301-verify-update-doors.md said "Only update takes it. { system: true } on insert or delete … is refused"; it now keeps only the two-caller refusal and points at this entry. .changeset/22301-spec-ledger-verify-provenance.md gave "{ system: true } on an insert or delete" as an INVALID_REQUEST example; it now names a call naming two callers ({ as: token, system: true }) and calls hooks.run and hooks.updateWhere the two write doors (patch round, after the contract review FAIL 6093381495). check-empty-changeset and Check Changeset are RED on these two M rows by design (the "DELIBERATE CORRECTION" class). The same-head contract review PASS 6093488852 names both notes and judges each rewritten sentence, which is the confirmation the landing rule takes.

What was measured before the door was opened

These were measured on a booted stack (requires: ['automation', 'triggers']) at d6c37919c7. A scratch probe (since deleted) called the booted objectql with { isSystem: true }, which is exactly the call the door would make.

  1. On origin/main, the refusal sat at handle.ts:486–:491, and the JSDoc said so at :201–:203. The insert and delete arms already made the engine calls above, so lifting the refusal hands those calls SYSTEM_CONTEXT. Confirmed.
  2. A system insert and a system delete each fire the record-change trigger. No dispatch was invented: the engine's own hook chain fired it.
    • On a system insert, beforeInsert and afterInsert see isSystem: true and no userId. A record-after-create flow runs (completed), with trigger { type: 'record-after-create', object, recordId } and no userId.
    • On a system delete, beforeDelete and afterDelete see isSystem: true, no userId, and the pre-image as previous. A record-after-delete flow runs (completed) with no trigger userId.
    • Only skipTriggers suppresses the trigger (trigger-record-change/src/record-change-trigger.ts:291); isSystem does not.
  3. A record the engine no longer holds. A record-after-delete flow is handed the pre-image as record and as previous (both read status: 'closing'), and its get_record of {record.id} answers no row. On the same delete, a record-before-delete flow's identical get_record finds the row. The system delete door reaches this with no further door.
  4. seed is unchanged and still skips record-change flows (pinned below).

Other readings from the same probe:

  • ql.delete answers true for a system delete and for a person's delete alike. See the Acceptance notes.
  • A readonly: true field is KEPT by a system insert and stripped from an admin's insert.

Pins: packages/verify/src/handle.system-insert-delete.test.ts (7 tests)

The fixture is neutral. sid_case can be read, but not created or deleted, by a fresh member. It has one capture hook and three record-change flows: on create, before a delete, and after a delete. The two delete flows read the case back with get_record and write held or gone to a ledger.

  • System insert:
    • The member's insert is refused PERMISSION_DENIED / 403 and leaves no row; the system insert writes the row.
    • The hooks see the system and no user. The record-after-create flow runs with no trigger user and writes its ledger row.
    • Controls: an admin's insert carries adminId at the hook and on the run, and seed of the same shape fires no run.
  • Validation: a declared validation rule still refuses a system insert with VALIDATION_FAILED and leaves no row. Control: an in-range value is written.
  • System delete:
    • The member's delete is refused PERMISSION_DENIED / 403 and leaves the row; the system delete removes it.
    • The hooks see the system, no user and the pre-image. The record-after-delete flow runs with no trigger user.
    • Control: an admin's delete carries adminId on the run.
  • Deleted-record read: the after-delete flow's ledger reads record_status: 'closing', previous_status: 'closing', held: 'gone'. The before-delete flow on the same delete reads held: 'held', the control showing gone is the engine's answer about a deleted row.
  • Two callers ({ as, system: true }) on an insert and on a delete: refused INVALID_REQUEST / 400, with no row written or deleted, no hook dispatched and no flow run.

handle.update-doors.test.ts: the old pin "the system is accepted on update only" asserted the refusal this PR removes. Its insert and delete legs are dropped, since they are now doors pinned above. Its two-caller leg on update stays.

Ablations

Both ran on the committed head 915f88e526, through node scripts/ablation-replace.mjs (anchor hit 1 → 0, blob changed). Each was restored to HEAD blob a6e175d1c0d3 with git diff HEAD empty. The direction of each was predicted before the run.

  • A1, the update-only refusal put back (throw callShapeRefusal(…) when system && operation !== 'update'). Predicted: the 6 system insert and delete pins red, the two-caller pin and all 9 update-door pins green. Observed: 6 failed, 10 passed, and each failure is the ablation's own refusal (6 hits of its message in the log).
    • The first attempt was refused by the tool before anything ran: its replacement contained the anchor. It was re-anchored and re-run, and that first attempt is not counted.
    • A second attempt never acquired the verify lock (queue-timeout) and is NOT MEASURED. It was restored by the tool.
  • A2, the seed context handed instead of the system context in hooks.run. Predicted: the 3 flow-run pins red here, plus the item 2 flow pin in handle.update-doors.test.ts; the row-level pins green. Observed: 4 failed, 12 passed, exactly those four.

Verification

All at 84a4a1e07f, the final head (a merge of origin/main 25be87612d, which touches none of this PR's paths), unless named otherwise.

  • Dependency closure: pnpm exec turbo run build --filter='@objectstack/verify^...' --concurrency=2 rebuilt after the merge, 46/46 tasks.
  • @objectstack/verify full suite: vitest run --maxWorkers=2 passed, 27 files / 216 tests.
  • pnpm --filter @objectstack/verify typecheck (tsc --noEmit and check:test-typecheck) exited 0, at 915f88e526 and again at 84a4a1e07f. tsc -p tsconfig.test.json --listFiles lists 27/27 of the package's test files, the new one included.
  • Consumer typecheck (packages/qa/dogfood): the @objectstack/dogfood^... closure was built (63/63; @objectstack/verify rebuilt as a cache miss, and its dist/index.d.ts carries the widened overload 1 time and the old one 0 times), then pnpm --filter @objectstack/dogfood typecheck exited 0 at 915f88e526. The dogfood fake stack types hooks as never, so an overload change cannot break it.
  • Reverse check against the rebuilt .d.ts, from dogfood: a scratch file calling hooks.run('o', 'insert' | 'delete', …, { system: true }) compiled (exit 0). Adding 'upsert' turned tsc red with TS2769 "No overload matches this call". The file was removed and git status is clean.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with no paths gives 63 commands; the 4 extra families from the dispatch-time list (spec check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs) also ran.
    • 61 exited 0. One is red: check-empty-changeset, the deliberate correction above.
    • check:dual-build-cjs-loads is NOT MEASURED: it needs a whole-workspace build, which this dispatch excludes, so it is left to CI.
    • --ran reconciliation over the exit-coded record: 63 derived, 62 run, 1 NOT-MEASURED, 0 UNRUN.
  • The artifact-roster block: see the report on the card.

Acceptance notes

  • The delete arm's return type. hooks.run(…, 'delete', …) is typed as a promise of EngineRow, and the engine answers true, measured for a person's delete and a system delete. The type is older than this PR on the person overload, and the system overload repeats it. Nothing reads a field of it: in hotcrm f0afcbd, no hooks.run(…, 'delete', …) call site reads the answer beyond asserting that it is defined. Noted, not filed.
  • Carried from items 2 and 3, unchanged here:
    • the engine's ValidationError has no status at the engine door;
    • requireId's and the bad-operation call-shape errors carry no code.
  • Not served by this door: an UPDATE-triggered flow over a row that vanished between the trigger and the flow's get_record. The engine runs a record-change flow inside the write, while it still holds the row (the synchronous run log), so no write produces that state. The card's report carries this as an open question with options.

For hotcrm (runRecordFlow, test/helpers/verify-stack.ts:255; read at hotcrm f0afcbd)

The helper has 8 call sites in 2 files:

  • Served by the system insert door: test/flow-record-change.test.ts:633, :647, :684 and :689. Each is an integration-written lead or contact whose record-after-create flow must run with no trigger user.
  • Likely served, not measured on hotcrm:
    • :249, an ownerless contact. The owner stamp is the person's; a system insert leaves an unset owner null in the fixture.
    • :500 and :501, a deal born pending. If approval_status is held by readonly: true, the fixture reading above says a system insert keeps it.
  • Not served: test/flow-escalation-ownerless-case.test.ts:233, the ghost case: an update-triggered flow over a row the engine does not hold. See the note above.

Generated by Claude Code

A system insert or delete is the engine's own insert / delete under
{ isSystem: true }, the write an integration or a system job makes: no
permission gate, the hooks and declared validations run, and the
record-change trigger fires the object's flows with no trigger user. A
delete-triggered flow is handed the pre-image and its get_record finds a
row the engine no longer holds. A call naming two callers stays refused.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <[email protected]>
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/verify/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/verify/README.md) — 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 — 3 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 76bc1e03a3237150934a35370406a5ddd7cde4fb → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 76bc1e03a3237150934a35370406a5ddd7cde4fb

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 84a4a1e07f5210a30b45ede73413151066a4e6f5
Local-runs: none

Head resolved from origin/claude/issue-22301-system-insert-delete-door and it is the head the brief named. Net diff read against the merge base with origin/main, 25be87612d (the branch merged main once): 6 files, +455 / −45, all under packages/verify/** and .changeset/. Inputs: card #22301 (body and 41 comments), PR #22596 (body, file list, diff), the check-runs on this head. Nothing built, run or re-run.

① Derived judgments

Accept-set and public-surface changes the diff implies, each judged:

  1. hooks.run(object, 'insert', input, { system: true }) — widened from refused to accepted. RIGHT. On the merge base namesSystem ran and then system && operation !== 'update' threw callShapeRefusal (INVALID_REQUEST / 400). The diff removes that branch and nothing else in the switch: the insert arm is the same ql.insert(object, input, { context }) the person's write makes, with context = { ...SYSTEM_CONTEXT } ({ isSystem: true }). Pinned in handle.system-insert-delete.test.ts: the member's insert is PERMISSION_DENIED / 403 and leaves no row while the system insert writes it; beforeInsert / afterInsert see isSystem: true and no userId; a record-after-create run exists with completed and no trigger.userId; the admin control carries adminId at the hook and on the run; a declared validation refuses the system insert with VALIDATION_FAILED and leaves no row.
  2. hooks.run(object, 'delete', { id }, { system: true }) — widened from refused to accepted. RIGHT. Same removal; the delete arm is the unchanged ql.delete(object, { where: { id }, context }). Pinned: the member's delete is 403 and leaves the row, the system delete removes it; beforeDelete / afterDelete see the system, no user and the pre-image status as previous; a record-after-delete run with no trigger user; the before-delete flow's get_record reads held, the after-delete flow's reads gone on the same delete, with record_status and previous_status both the pre-image. That is the door the card's item 5 second half asked for (a record the engine no longer holds), served for a delete-triggered flow.
  3. { as: token, system: true } on every operation — still refused INVALID_REQUEST / 400 before the engine is touched. RIGHT, unchanged. namesSystem is reached before requireId, contextFor and engine(). Pinned on insert and delete (no row written or deleted, no hook dispatched, no flow run) in the new file, and on update by the kept leg of handle.update-doors.test.ts.
  4. requireId on a system delete with no id, and the bad-operation guard — unchanged. Both still throw the handle's own plain Error with no code (carried from items 2–3, acceptance-noted there and here). Right to leave; nothing in this diff moves them.
  5. The exported VerifyHandle type: the AsSystem overload of hooks.run widens 'update' to 'insert' | 'update' | 'delete'. RIGHT, additive. Every call that compiled before still compiles with the same resolution; the person overload and updateWhere are untouched; the implementation signature (opts: AsUser | AsSystem) is unchanged. Hand-built implementers: repo-wide, VerifyHandle is referenced only inside packages/verify (index.ts, harness.ts, handle.ts). The one fake, packages/qa/dogfood/test/rls-runner.test.ts:132, types hooks: undefined as never, so no overload change can break it; no packages/qa/dogfood/src call site names { system: true }. The five type-check contexts on this head are green (TypeScript Type Check, Type Check · workspace / consumer gates / source gates / debt ledger). Pre-existing observation, not this PR's: a caller holding AsUser | AsSystem as one union cannot call run (an overload set, not a union parameter), while updateWhere takes the union. Unchanged since items 2–3.
  6. seed is unchanged (SEED_CONTEXT: isSystem, seedReplay, skipTriggers). The claim that it fires no record-change flow is pinned by the control in the system-insert describe. RIGHT.
  7. The AsSystem docblock, the handle.ts door-roster header, both run JSDocs and the README now say insert, update and delete take the system principal, what runs (hooks with session.isSystem and no userId, validations, the record-change trigger with no trigger user), what does not (the permission gate), the before/after-delete get_record contrast, and seed versus a system write. Each sentence matches a pin above. One claim is stated wider than its pin: "a system (or a person's) delete hands a record-after-delete flow the pre-image … get_record finds nothing" — the gone reading is pinned for the system delete; for the admin's delete only the run's userId is read. The mechanism is the engine's (the same trigger after the same ql.delete), accepted as stated.
  8. harness.ts untouched. Checked: its text names no system door and no update-only rule (its two isSystem: true hits are the harness's own boot contexts). The dev's deviation holds.
  9. The dropped pin. handle.update-doors.test.ts loses exactly the insert and delete legs of "the system is accepted on update only", which asserted the refusal this PR removes; its two-caller update leg stays. No other test in packages/verify pins the old refusal (handle.test.ts has no system call). RIGHT.
  10. ERROR_CODE_LEDGER['@objectstack/verify'] (INVALID_REQUEST) stays true: the two-caller shape and the hooks.updateWhere by-id shape still answer it. The row's comment at packages/spec/src/api/error-code-ledger.zod.ts:1483 ("the handle's update doors") is now narrower than the surface but not false. No packages/spec source change is owed by this PR.
  11. The delete arm's return type. Typed as a promise of EngineRow while the engine answers true. Pre-existing on the person overload; this diff writes a new overload line that repeats it. See ③.

② Semver level

Packages this diff publishes in: @objectstack/verify only (the diff touches packages/verify/** and two .changeset files; no other package's source, export or ledger moves). Its entry, .changeset/22301-verify-system-insert-delete-door.md: '@objectstack/verify': minor, with Clause-②: yes (widening) on its own line. RIGHT. A call the published handle refused is now accepted and the index-exported interface's literal union grows: a purely additive widening of a published surface, which takes at least minor and is not breaking (no call that compiled or succeeded before changes outcome). The PR body's second line carries the same Clause-②: yes (widening); the level axis in the Check Changeset job read it and passed ("no package whose packages/**/src/** it moves is graded patch"), and the ADR-0087 step saw no declared-breaking changeset. The arm is right: widening, not narrowing.

The new note's own text, read as the CHANGELOG it becomes: every behavioural sentence matches a pin in ①. Two phrasings are carried verbatim from the items 2–3 note and pre-date this PR: "Every other refusal is the engine's own error" (the handle's own requireId and bad-operation Errors carry no code) and "assert on its code and status" (the engine's ValidationError has no status at the engine door). Both are acceptance-noted on this card already. Noted, not failing.

DELIBERATE CORRECTION — .changeset/22301-verify-update-doors.md

The pending @objectstack/verify minor note of items 2–3 (added by PR #22517 at 5910b5e3ed; unreleased: packages/verify is at 17.7.0 and its CHANGELOG has no updateWhere). It is present on the merge base, was not added by this PR, and is the one M row the #17712 foreign-changeset rule names in the Check Changeset log. Nothing else in the job is red: "No empty-frontmatter changeset introduced", no major, level axis green. The diff is +1 / −1 on this file. Sentence by sentence:

  • OLD "Only update takes it." — false once this PR lands (insert and delete take it; ① 1–2). Removed. Right.
  • OLD "{ system: true } on insert or delete, or beside an as token, is refused with code: 'INVALID_REQUEST', status: 400 before anything is written." — the insert / delete half is false once this lands (the first pin of each new describe writes, or deletes, the row); the beside-as half stays true (① 3). NEW "{ system: true } beside an as token is refused with code: 'INVALID_REQUEST', status: 400 before anything is written." — keeps exactly the true half. True. Right.
  • OLD = NEW "Fixture rows are still seed(object, rows), which also skips record triggers." — unchanged and still true (SEED_CONTEXT, pinned by the seed control). Right.
  • NEW "(insert and delete take { system: true } too: see the system insert and delete entry.)" — true once this lands; the pointer names the new note's subject, which compiles into the same release section. Right.

The rewrite of this note is right. This record is the confirmation of that rewrite, and of nothing wider.

A second pending note this PR falsifies and does not correct — the FAIL

.changeset/22301-spec-ledger-verify-provenance.md — the pending @objectstack/spec minor note added by the same PR #22517, on the merge base, unreleased, untouched by this diff — reads: "The verify handle's two update doors answer it for a malformed call, such as { system: true } on an insert or delete, or a hooks.updateWhere the engine would write by id."

  • Once this PR lands, { system: true } on an insert or delete is not a malformed call and answers no INVALID_REQUEST: it is the door this PR opens (① 1–2, pinned). The sentence is false in exactly the way the corrected sentence above was false.
  • It ships as @objectstack/spec's CHANGELOG in the same release as the corrected verify note, and the two then contradict each other on the same call shape. Once released, the repo's rule makes the fix a dedicated docs-only PR; now it is one line.
  • Same class (DELIBERATE CORRECTION, the gate's own words: "your change may have made this PENDING release note false"), same remedy: rewrite the example to what stays true, for instance "such as a caller named twice ({ as, system: true }), or a hooks.updateWhere the engine would write by id". That adds a second M row to the same designed red, which the fresh review on the new head judges. ⛔ Not a packages/spec/src change: the ledger row itself stays true (① 10).
  • Precedent on this card: 6069311153 failed PR fix(metadata-protocol): a cel-dated seed row reaches every hook in its temporal field's stored form #22353 on one changeset sentence because it would ship as CHANGELOG text.

③ Boundary flags

Dev deviations (report 6093281118), each answered:

  1. The items 2–3 note edited — judged in ②: right.
  2. harness.ts untouched — checked in ① 8: holds.
  3. origin/main 25be87612d merged before the PR opened, none of its 3 commits on this PR's paths — checked: that commit is the merge base, and the net diff is the 6 files. Holds.
  4. Ablation A1 took three attempts (one refused before running, one queue-timeout NOT MEASURED, the third reported) — declared, not silently retried; read-only review does not re-run it. Accepted as reported; the reported direction matches the prediction.
  5. Two ablations where one was asked — fine; A2 is what separates this door from a seed-like door in the pins.
  6. Commit-trailer form — not a contract matter.
  7. 63 gate commands derived versus 58 at dispatch — the check-runs on this head are the gate verdicts (below).

open_questions (1) — the UPDATE-triggered flow over a row deleted between its trigger and its get_record (hotcrm's ghost case), A out of scope versus B fronting automation.execute. The seat's ACCEPT 6093298129 decided A, within the maintainer's window. Reviewer's reading: A is right on the card's own terms. The card's acceptance allows "a stated decision that the door is out of scope"; the engine runs a record-change flow synchronously inside the write while it still holds the row, so no write produces that state; B would hand a flow a record no trigger built (it skips RecordChangeTrigger.buildContext), the hand-built stand-in the B′ ruling this card rests on forbids. Answered: A. Not blocking.

out_of_scope_findings (1) — hooks.run(…, 'delete', …) is typed as a promise of EngineRow and the engine answers true. Dev: noted, not filed. Seat: acceptance notes, no carrier. Reviewer: ESCALATED — file it. The person overload is older than this PR, but this diff writes a new overload line repeating a return type the runtime does not deliver, on a published interface; a declared-but-not-delivered type is the contract-violation class Prime Directive #10 files rather than notes. The fix (a return-type change on a published interface) is its own semver act, so it is not for this PR; a card against @objectstack/verify is owed, and it does not move this verdict.

Carried acceptance notes (the engine's ValidationError has no status at the engine door; requireId and the bad-operation errors carry no code): pre-existing, consistent with the items 2–3 record, carried.

The hotcrm runRecordFlow mapping in the PR body (4 served, 3 likely, 1 not) is not an input to this review and is not judged.

Check-runs on this head at the last read: every completed context green (Build Core, all six Test Core shards, the four Type Check contexts and TypeScript Type Check, the three Dogfood Regression Gate shards, Dogfood Verify CLI, Temporal Conformance, Governed Surface Queue Guard, the claim and single-writer guards) except Check Changeset, which is red by design on the one path named in ②, and that is the #17712 foreign-changeset refusal and nothing else. Still in progress at that read: Lint & Repo Gates — its verdict was not available to this record, so the fresh review on the patched head reads it.

Patch round owed (one file, one line): correct .changeset/22301-spec-ledger-verify-provenance.md as ② states. Every code judgment in ① and the level in ② carry over unchanged to a head that differs only by that file.

Implemented-by: claude/issue-22301-system-insert-delete-door
Reviewed-by: session_01KNKBCRDJCu5tGy3TEbvtrF

VERDICT: FAIL


Generated by Claude Code

…le still makes

The pending note's example, a system principal on an insert or delete,
is a door after this branch; the refusal it still makes is a call naming
two callers, on either write door.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <[email protected]>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 98846f98c710ba2d706e2903690a71c1e4d89370
Local-runs: none

Round 2, on the patch round the seat ordered (6093400773) after the round-1 FAIL 6093381495 was adopted; the dev's report on it is 6093415172. Head resolved from origin/claude/issue-22301-system-insert-delete-door; its parent is the round-1 head 84a4a1e07f, one commit, .changeset/22301-spec-ledger-verify-provenance.md +1 / −1 and nothing else (git diff between the two heads is that one file; packages/** and the two verify notes are byte-equal). The branch did not merge main again: the merge base with origin/main is still 25be87612d, and the net diff is 7 files, +456 / −46. Inputs: the card (body and comments through the dev's round-2 report), the PR (body, file list, diff), the check-runs on this head. Nothing built, run or re-run.

① Derived judgments

Carry-over. The code diff is unchanged from the round-1 head — git diff 84a4a1e07f 98846f98c7 -- packages/ .changeset/22301-verify-system-insert-delete-door.md .changeset/22301-verify-update-doors.md is empty — so every judgment in round 1 (6093381495, ①) carries over as it stands, each still RIGHT: (1) hooks.run(…, 'insert', …, { system: true }) accepted, the same engine call with only the context changed, pinned; (2) the same for 'delete', with the after-delete flow's get_record reading gone while the before-delete flow's reads held on the same delete; (3) { as, system: true } refused INVALID_REQUEST / 400 on all three operations before the engine is touched; (4) requireId and the bad-operation guard unchanged; (5) the AsSystem overload widened additively, the implementation signature unchanged, and no hand-built implementer broken (the one fake, packages/qa/dogfood/test/rls-runner.test.ts:132, types hooks as never); (6) seed unchanged and pinned as firing no record-change flow; (7) the JSDoc and README claims each matched to a pin; (8) harness.ts untouched and nothing in it false; (9) the dropped pin exactly the legs this PR falsifies; (10) the ERROR_CODE_LEDGER['@objectstack/verify'] row still true; (11) the delete arm's return type — now filed, see ③.

New on this head, judged: the rewritten spec note's claim that both write doors answer INVALID_REQUEST for a call naming two callers. TRUE by source. One helper, namesSystem(opts, door) (handle.ts about :393), throws callShapeRefusal (about :388: an Error with code: 'INVALID_REQUEST', status: 400, statusCode: 400) when system === true and as is set. hooks.run calls it before requireId, contextFor and engine() (about :502); hooks.updateWhere calls it after its two call-shape checks and before engine() (about :536). Pinned for hooks.run on update (handle.update-doors.test.ts, the kept two-caller leg), and on insert and delete (handle.system-insert-delete.test.ts: no row written or deleted, no hook dispatched, no flow run). For hooks.updateWhere the two-caller shape is not pinned — its pins cover the by-id refusals and a non-object where — a pin gap carried from items 2–3, not this PR's; noted, not blocking: the sentence is true by the one shared helper.

② Semver level

Carry-over. Packages this diff publishes in: @objectstack/verify only, unchanged. Its entry, .changeset/22301-verify-system-insert-delete-door.md: '@objectstack/verify': minor, Clause-②: yes (widening) on its own line — RIGHT, as in round 1: a call the published handle refused is accepted and the index-exported interface's literal union grows, a purely additive widening that takes at least minor and is not breaking. The PR body's second line carries the same declaration; on this head the level axis read it and passed ("no package whose packages/**/src/** it moves is graded patch"), the ADR-0087 step saw no declared-breaking changeset (3 non-breaking seen), and no major is introduced. The spec note's own '@objectstack/spec': minor and Clause-② line are #22517's declaration for #22517's ledger row, untouched here: this PR adds nothing to @objectstack/spec and owes it no entry of its own.

DELIBERATE CORRECTION — two M rows

The Check Changeset job on this head reads, in order: "This PR adds 1 changeset(s)" (the verify note, the A row); "No empty-frontmatter changeset introduced by this diff (3 declaring changeset(s) added)"; the #17712 foreign-changeset refusal naming exactly .changeset/22301-spec-ledger-verify-provenance.md and .changeset/22301-verify-update-doors.md, each "present on the merge base and CHANGED by this PR"; then the ADR-0087 step and the major / level steps green. That refusal is the job's only red, and it is on those two paths and nothing else. Both notes are pending and unreleased (@objectstack/verify and @objectstack/spec are at 17.7.0 and neither CHANGELOG carries the #22517 text), both were added by PR #22517 at 5910b5e3ed, and both are present on the merge base and not added by this PR.

1. .changeset/22301-verify-update-doors.md (pending @objectstack/verify minor; unchanged since round 1, +1 / −1 against the base). Sentence by sentence, the round-1 judgment re-read on this head:

  • OLD "Only update takes it." — false once this PR lands (① 1–2). Removed. Right.
  • OLD "{ system: true } on insert or delete, or beside an as token, is refused with code: 'INVALID_REQUEST', status: 400 before anything is written." — the insert / delete half is false once this lands (the first pin of each new describe writes, or deletes, the row); the beside-as half stays true (① 3). NEW "{ system: true } beside an as token is refused with code: 'INVALID_REQUEST', status: 400 before anything is written." — exactly the true half. True. Right.
  • OLD = NEW "Fixture rows are still seed(object, rows), which also skips record triggers." — unchanged and still true (SEED_CONTEXT, pinned by the seed control). Right.
  • NEW "(insert and delete take { system: true } too: see the system insert and delete entry.)" — true once this lands; the pointer names the new note's subject, which compiles into the same release section. Right.

2. .changeset/22301-spec-ledger-verify-provenance.md (pending @objectstack/spec minor; the round-2 commit, +1 / −1 on its one body sentence; frontmatter, title, the Clause-② line and the rest of the paragraph byte-for-byte unchanged). The rewritten sentence, clause by clause:

  • OLD "The verify handle's two update doors answer it for a malformed call," — narrower than the surface once this lands: hooks.run answers it on insert and delete as well. NEW "The verify handle's two write doors, hooks.run and hooks.updateWhere, answer it for a malformed call," — hooks has exactly those two members (the run overload set and updateWhere), and both answer it (① new). True. Right.
  • OLD "such as { system: true } on an insert or delete," — false once this PR lands: that call is the door this PR opens (① 1–2; it writes or deletes the row and answers no refusal). NEW "such as a call naming two callers ({ as: token, system: true })," — true (① new: one helper, both doors, before the engine). Right.
  • OLD = NEW "or a hooks.updateWhere the engine would write by id." — unchanged and still true (resolveEngineUpdateDispatch answering by-id goes to callShapeRefusal; pinned for a where naming only an id and for an id in data beside an empty where). Right.
  • The unchanged remainder, re-read: "They answer in-process, with a thrown Error carrying code, status and statusCode; it is a test door with no HTTP path." — true (callShapeRefusal). "No code is added: ErrorCode, RegisteredErrorCode and REGISTERED_ERROR_CODES are unchanged …" — feat(verify): the handle gains a system-context update door and a predicate update door #22517's claim about feat(verify): the handle gains a system-context update door and a predicate update door #22517's change, untouched and unaffected here. Right.

Both rewrites are right: each old sentence is false once this PR lands, and each new one is true. No third pending note names the retired refusal: a search of .changeset/, packages/verify, packages/spec/src, packages/qa, content/docs and docs at this head for the insert-or-delete refusal and the update-only rule finds nothing beyond the two corrected notes (the comment at packages/spec/src/api/error-code-ledger.zod.ts:1483, "the handle's update doors", is narrower than the surface but not false, and is not a release note). This record is the confirmation of both corrections, and of nothing wider.

③ Boundary flags

  • The round-1 escalation, closed: the delete arm's return type (typed as a promise of EngineRow while the engine answers true) is filed as verify: hooks.run(object, 'delete', …) is typed to resolve with a row (Promise<EngineRow>), but the engine answers true #22599 (open, finding, filed by the seat in the patch-order act). Its fix is its own semver act on a published interface and is not for this PR. Nothing further is owed here.
  • Dev deviations (6093415172): one, answered. The branch was not re-merged with main after the merge base 25be87612d, per the order's "nothing else". Right: the diff is judged at its merge base, and CI on this head already tests the branch against main's tip through the PR merge ref, as the queue's merge group will.
  • open_questions: none. out_of_scope_findings: none. The round-1 ghost-case decision (A, out of scope, by the seat's ACCEPT 6093298129, within the maintainer's window) stands; the carried acceptance notes (the engine's ValidationError has no status at the engine door; requireId and the bad-operation errors carry no code) stand.
  • For the seat, not blocking: the PR body's "Declared deviation" paragraph names one corrected note; the diff now corrects two. The body is the seat's to align after the push (the precedent on this card, 6069311153), and the landing rule reads this record, not the body.
  • Check-runs on this head at the last read: the designed Check Changeset red is the only red, on the two paths named in ②. Green, 21: Auto Label, Build Core, Check Documentation Links, Check PR Size, Dogfood Regression Gate (1/3), Dogfood Regression Gate (2/3), Dogfood Regression Gate (3/3), 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, Temporal Conformance (live PG + MySQL), Test Core (4/6), Test Core (5/6), Test Core (6/6), The card this PR closes must claim this branch, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, filter. Skipped by their own filters: Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in). Still in progress after a four-minute wait, 6: Dogfood Verify CLI, Lint & Repo Gates, Test Core (1/6), Test Core (2/6), Test Core (3/6), Type Check · workspace — their verdicts were not available to this record. The code they test is byte-equal to the round-1 head, where every one of them completed green; the seat reads them on this head before enqueue, and a red among them is a finding this record does not cover.

Implemented-by: claude/issue-22301-system-insert-delete-door
Reviewed-by: session_01KNKBCRDJCu5tGy3TEbvtrF

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6093488852 · 98846f98c7 → 798e68ffb8 · unexplainedPathsBetween(from, to, base: origin/main) → (empty)

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T04:23Z.

The head moved after the contract review PASS 6093488852 (round 2) by one merge of origin/main 76bc1e03a3, and nothing else:

  • the merge commit is 798e68ffb8, with parents 98846f98c7 and 76bc1e03a3, merged with no conflict;
  • git diff --name-only 98846f98c7 798e68ffb8 lists 113 paths main moved, none of them one of this PR's seven paths;
  • scripts/pm/record-recognisers.mjs's unexplainedPathsBetween, run on the committed trees with base origin/main, returns no path;
  • git diff --name-only origin/main 798e68ffb8 is still exactly the PR's seven paths.

The record carries to 798e68ffb8. That includes the round-2 confirmation of both corrected pending notes: their bytes are unchanged. The merge exists only for a fresh CI run. Dogfood Verify CLI was cancelled at 98846f98c7 by its job timeout after about 18 minutes of runner starvation (order 6093644867). This seat does not re-run jobs, and a re-run would have reused the old merge ref.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Landing pre-checks at 798e68ffb8, by the owning seat: queued with one red by design (Check Changeset, the DELIBERATE CORRECTION class)

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T04:33Z · holder of claim 6092413378.

  • The review: contract review PASS 6093488852 (round 2) at 98846f98c7, carried to 798e68ffb8 by Regen-provenance 6093728160: one merge of main, no PR path moved, and unexplainedPathsBetween names no path. Round 1 (6093381495) failed on one pending note; the patch round fixed it.
  • CI: 30 success, 3 skipped, 1 failure. check-expected-skips --pr 22596 reads "OK — 3 skipped check-run(s), every one in the roster". The one other conclusion is Check Changeset = failure.
  • Check Changeset goes to the queue red, under the three conditions together:
    1. The source says it is red by design. scripts/check-empty-changeset.mjs's DELIBERATE CORRECTION text reads "this gate stays red either way, and staying red is what puts the decision in front of a person instead of routing around it". The job's red names exactly the two pending notes this PR corrects, .changeset/22301-verify-update-doors.md and .changeset/22301-spec-ledger-verify-provenance.md.
    2. It does not run on merge_group. Check Changeset lives in .github/workflows/pr-automation.yml, whose only trigger is pull_request.
    3. It is recorded on the PR: this comment. The confirmation the landing rule takes for this class is the same-head PASS record that names both notes and judges each rewritten sentence: 6093488852, carried here.
  • Governed: check-governed-merges --pr objectstack-ai/objectstack#22596 reads NOT governed; 502 changed lines.
  • Closing keywords: the body's first line is Part of #22301. No closing keyword appears in the body or in any commit, so the card stays open for items 6, 7 and item 1's remaining gap.
  • Mergeability: GitHub reports mergeable: true (unstable, from the red above).

needs:contract-review comes off; pr_ready and automerge_enable follow.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 10, 2026 04:34
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 10, 2026 04:34
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 38024748561 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (2/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/dev-no-watch.pin.test.ts > `--no-watch` where os dev boots no environment is refused, not ignored > the fixture starts its own dev script when asked — 
      ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 1 to be +0 // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • test/dev-no-watch.pin.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 13 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue ejection: Test Core (2/6) red in merge group run 38024748561. A new signature in @objectstack/cli, initial diagnosis "not this PR's"; ⛔ not re-queued

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T04:56Z · holder of claim 6092413378. Triage record: the merge-queue triage comment above (6093954331).

The signature, complete:

  • Run 38024748561 (merge_group, ref gh-readonly-queue/main/pr-22596-d748ae80af…, head ebdcacfe51); the queue base is d748ae80af, with this PR alone in the group. Job Test Core (2/6), step "Run this shard's tests".
  • @objectstack/cli integration, test/dev-no-watch.pin.test.ts › "--no-watch where os dev boots no environment is refused, not ignored" › "the fixture starts its own dev script when asked — the control".
  • AssertionError: expected 1 to be +0 at dev-no-watch.pin.test.ts:167:34 (expect(bareAtWorkspace.code).toBe(0)). 1 failed of 8 tests in the file, 1 of 1806 in the package. Package wall time 605.72s (import 382.16s, tests 1406.98s).
  • In the merge-queue triage's 24h ledger, only this PR has hit this file (a lower bound, since the ledger read was cut short).

Initial diagnosis: not this PR's.

  • The diff is disjoint: packages/verify/src/handle.ts, its two test files, packages/verify/README.md and three .changeset/22301-*.md files. The test imports src/commands/dev.js, @oclif/core and test/helpers/serve-process.js. Its spawned child runs the fixture workspace's own dev script, node -e "console.log('DEV-SCRIPT-RAN')".
  • The same child CLI ran correctly in the same beforeAll: the two sibling spawns in this file (--watch=false at the project, and --no-watch at the workspace) passed both their exit-code and their failure-line assertions. So the CLI loaded every command module, @objectstack/verify's included.
  • runDev reports a signalled child as code 1 (dev-no-watch.pin.test.ts, the code: arm, "a signalled child has none and is reported as a failure"), and it prints nothing of out on failure. A killed third cold tsx start under full-suite load reads exactly like this. This is a reading, not a measurement.
  • The queue base's same shard is green: Test Core (2/6) on d748ae80af's push run.

Why it is not re-queued. By the landing rule, a new signature is re-queued once only when all three facts hold:

  1. the failing file's import closure is disjoint from the diff — it holds;
  2. the queue base's same shard is green — it holds;
  3. the first error is a timeout, not an assertion — it does not hold. The first error is an AssertionError on an exit code, and it names no duration, so the triage's own exception for time measurements does not apply.

Next: this seat measures the signature locally at the merged tree (this PR's head plus d748ae80af) and on main, and captures the control child's own output. The result goes on this PR and on #22301. If it is a flake in the test's control, it gets its own card rather than a bypass.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

The ejection, measured: not this PR's. The re-queue is left to a person, because the three-fact rule does not admit it

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T05:55Z · holder of claim 6092413378. Measurement report: os-dev-report 6094394009 on #22301. It follows this seat's record 6093977110 above.

Measured, at the signature's own file:

  • The merged tree (local only, never pushed) is this PR's head 798e68ffb8 merged with the queue base d748ae80af; its diff against the base is exactly this PR's 7 paths. test/dev-no-watch.pin.test.ts ran 3 times, 3/3 PASS, 8/8 each.
  • main at d748ae80af: 3/3 PASS, 8/8 each.
  • The control's own child, run by hand at the merged tree in a byte-equal fixture: exit 0, no signal, DEV-SCRIPT-RAN printed. That holds under a plain environment and under a pnpm exec parent's.

Corrected reading of "the siblings passed":

Why the seat does not re-queue: the landing rule re-queues a new signature once only when three facts hold.

  1. The failing file's import closure is disjoint from the diff: it holds.
  2. The queue base's same shard is green: it holds.
  3. The first error is a timeout, not an assertion: it does not hold.

The evidence above says this PR does not cause the red, but this seat does not route around the rule. The PR stays open, with auto-merge off, until a person re-queues it or the rule's conditions change. The maintainer is told in this seat's round report.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Re-queue at 798e68ffb8, on the maintainer's word: the landing pre-checks re-read, queued with the same one red by design

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T14:16Z · holder of claim 6098385162 on #22301.

  • Why now: the seat left the re-queue to a person (6094411822), because the three-fact rule did not admit it: the first error was not a timeout. The maintainer answered in this session's chat, verbatim: 「22596 什么情况,你应该跟进到合并」. This act is that re-queue. The ejection measurement stands: the merged tree and the queue base each passed dev-no-watch.pin.test.ts 3 / 3. cli test: dev-no-watch.pin.test.ts's control folds every failure (a signal, a pnpm failure, a spawn error) into exit code 1 and prints nothing, so a red in the merge queue cannot be diagnosed (ejected PR #22596) #22618, the gate that folds a failure into exit 1, is still open.
  • The review: contract review PASS 6093488852, carried to this head by 6093728160. The head has not moved since the first queueing 6093807448.
  • CI re-read on this head: 38 check-runs: 31 success, 5 skipped, 2 failures. check-expected-skips --pr 22596 reads every skip in the roster. Both failures are Check Changeset, the DELIBERATE CORRECTION class, under the same three conditions as 6093807448:
    1. the source says it stays red by design;
    2. Check Changeset lives in .github/workflows/pr-automation.yml, whose only trigger is pull_request, so it does not run on merge_group;
    3. it is recorded on this PR, with the same-head PASS record 6093488852 naming both corrected notes.
  • Governed: check-governed-merges --pr objectstack-ai/objectstack#22596 reads NOT governed; +456 / −46.
  • Closing keywords: the body carries Part of #22301 alone, and no commit message carries one.
  • main drift since the merge base 76bc1e03a3: main (now d973c34cdd) moved none of the PR's 7 paths. .changeset/22301-verify-update-doors.md, the pending note this PR corrects, is still on main. GitHub reports mergeable: true (unstable, from the red above).
  • If it is ejected again: on the same dev-no-watch signature, the seat reports the run and the first error to the maintainer and does not loop. On a new signature, the ejection rule applies as written.

automerge_enable follows; the PR is already out of draft.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit b4ce5e2 Oct 10, 2026
37 of 39 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22301-system-insert-delete-door branch October 10, 2026 14:51
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

Development

Successfully merging this pull request may close these issues.

2 participants