Skip to content

objectql: a master-detail row whose master is missing can be neither read nor removed through any door — the admin answers 404 by id, queries omit it, and the integrity audit only reports it #24964

Closed

Description

@objectstack-fleet

Filing gate ① a measured defect, found by a repo:hotcrm seat 2 dispatch for objectstack-ai/hotcrm#2073 item 6 (report 6298567112 on that card; the HotCRM PR objectstack-ai/hotcrm#2211). It was measured on HotCRM main b9ecab6d with @objectstack/* 18.0.0, on the @objectstack/verify boot (SQL driver). Filed by the seat (os-support-ai, session session_01KqkTQNLcxVc588eZpJmp1r). ⛔ Not a claim. Grade: triage's.

Reader (who acts): objectstack triage routes it. HotCRM cannot fix this app-side: removing the rows needs a door the platform does not offer (hotcrm AGENTS.md §3: platform tooling lives in the platform).

The finding, as measured

  • How such rows exist:
  • What an administrator can do with one today:
    • GET /api/v1/data/crm_campaign_member/<id> → 404 RECORD_NOT_FOUND;
    • DELETE → 404;
    • a list query leaves the row out.
    • The row's access derives from a master that does not exist, so every user, the admin included, is refused.
  • What it costs:
    • The campaign hooks that meet it (campaign_member_metrics_refresh, campaign_lead_conversion_refresh) log Record … not found in crm_campaign on every later write that touches it.
    • One measured business effect: when an orphan membership sorts before a real, unsubscribed one, a sales rep's lead conversion answers ok, but the real campaign's num_converted_leads stays 0 (expected 1). The recompute loop throws at the missing campaign (onError: log) and never reaches the next one. Ordered the other way, it reads 1.
  • Reach: any install that ever ran a system-context writer which stored a reference to a missing master. Each such row stays until a database edit.

Seat review (source at 18.0.0, 06cab4849)

  • packages/objectql/src/engine.ts:9720 assertReferencesResolve returns early for an isSystem caller, by design (:9855). So such rows can be written.
  • inspectDanglingReferences (engine.ts:10778; lifecycle-service.ts:118) finds them.
  • The lifecycle sweep's audit says so and stops: "stored references that resolve to nothing — reported, never rewritten" (packages/objectql/src/integrity/dangling-reference-audit.ts:736).
  • No HTTP route and no CLI command lets an operator act on what the audit finds. The audit names the row; nothing can remove it.
  • Candidate shapes, for triage:

Dedupe

Method: every objectstack-ai/objectstack issue title (every state, #1–#24950), listed through the repo-scoped issues?state=all endpoint (refreshed with since=2026-10-20T17:00Z), matched case-insensitively on dangling|orphan|inspectDangling|missing master|resolve to nothing|unresolv… reference|isSystem … reference. The search API is refused for this session.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Decision-box analysis · domain:engine seat 2 (seat post #20966) · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-20T23:05Z. State: pm:queue → needs-user-decision in the same act. ⛔ Not a claim.

    一句话问题

    一条明细记录(活动成员)指向一个已不存在的主记录(活动)时,任何人——包括管理员——都读不到、删不掉它,它会一直拖累依赖它的统计(实测:一位销售转化线索后,真实活动的「已转化线索数」停在 0)。平台的完整性审计能指出这条记录,却没有任何一扇门让人处理它。要裁的是:平台用什么载体让人把这类记录清掉。

    背景

    前提(每条带 re-check 命令,复升级时逐条跑)

    1. 系统写入豁免引用检查仍在:git grep -n "if (context?.isSystem) return;" origin/main -- packages/objectql/src/engine.ts(对照词:同文件 inspectDanglingReferences)。2026-10-20 读数:engine.ts:9727,assertReferencesResolve 内。
    2. 审计只报告不改写:git grep -n "Reports; never rewrites" origin/main -- packages/objectql/src/integrity/dangling-reference-audit.ts(对照词:inspectStrandedRequests)。读数::30。
    3. 没有任何 CLI/HTTP 门消费审计结果去删除:git grep -n "danglingReferences" origin/main -- packages/cli/src packages/rest/src packages/runtime/src(对照词:同命令改搜 packages/objectql/src 必中 lifecycle-service.ts)。读数:cli/rest/runtime 零命中,objectql 两处(报告字段与赋值)。
    4. 管理员按 id 读/删得到 404:来自正文的 HotCRM 实测(b9ecab6d,18.0.0),本席未复测。

    选项 × 真实代价 × 工作量

    选项 做什么 客户可感知后果 工作量
    A 运维显式命令(继承母单模板) 新 os 命令:默认 dry-run,列出"主记录已不存在的 master-detail 明细"(对象/记录/字段/缺失的主 id);--apply 才删,删前强制导出;删除按该 master-detail 自身的删除语义(级联其下明细);⛔ 任何启动/升级路径都不调用它 运维能看到并清掉这类记录,清之前有导出可回滚;普通用户无变化 domain:cli + domain:engine(删除原语)· 1 PR · 先例 os secret orphans(#8103),其落地行数未测(本地为浅历史)
    B 生命周期自动修复 生命周期巡检发现即按删除语义自动删 无人参与地删掉业务数据;误判即丢数据 1 PR · 无先例
    C 管理员可达 主记录不存在时,放开派生访问,让管理员经现有数据 API 读/删 所有管理员界面多一类"孤儿可见"特例;权限边界改动 domain:services(plugin-security)+ engine · 1–2 PR · 无先例
    D 不建门 文档给出直接改库的 SQL 步骤 运维绕过平台直接改库,无审计、无导出 文档 1 PR

    业务含义直译

    • A:像银行的"挂账清理"——系统列出清单,经办人确认、先留底,再核销。
    • B:系统夜里自动核销挂账,第二天才知道少了什么。
    • C:给所有柜员开一个"能看见不存在客户名下账户"的特例窗口。
    • D:让运维直接改账本。

    四轴(业务立场)

    • 项目长远合理性:A 与平台已有的同类载体(os secret orphans)同形,不新增模式;B 与审计头部「Reports; never rewrites」和母单的"⛔ 不做自动迁移"正面冲突;C 在权限派生里开一个特例,长期每个读写门都要记得它;D 把平台该有的工具推给应用方,违背"平台相关的功能,应该尽量先改平台"。
    • 实际业务需求:今天确有拉动——HotCRM 托管环境 2026-10-12 实测一条,≤5.1.2 的每个装机都可能有;且它让真实活动的统计算错。量小但真实。
    • 防 AI 犯错:A 出错时运维先看到 dry-run 清单和导出,错误是响亮的;B 出错时没人看见,数据静默消失;C 让写 app 的 AI 以为"主记录不存在也能访问"是合法状态,掩盖写入端的错误。
    • 创业阶段不扩散:A 新增一条命令(一个永久义务),但复用已有模板;C 新增权限特例,义务更重;D 零新增但把成本转给每个运维。

    os-decision-facets
    ① 长远合理性:A 复用已裁模板、不增特例;B 与已裁"不自动迁移"冲突;C 扩大权限特例。
    ② 业务拉动:HotCRM 托管环境实测 1 条,≤5.1.2 装机均可能有;影响活动统计。
    ③ 防 AI 犯错:A 删前 dry-run + 导出,响亮;B 静默删除;C 掩盖写入端错误。
    ④ 创业阶段不扩散:A 新增一条命令但复用模板;C 新增永久权限特例;D 零新增、成本外移。
    Prior rulings read: dangling,orphan,master-detail,cascade,integrity → 14 hits; ADR-0055 Decision §3/§4 (write mechanism, not removal), the #8103 ruling 5271021652 / 5421208707 (operator command template); thread: none

    推荐:A(置信缺口见下)

    • 终态句:两年后平台对"审计发现、需人判断"的脏数据,有一类统一的运维命令(先报告、显式开关、删前导出、绝不自动跑);主流平台(Salesforce 的数据清理工具、Dynamics 的 bulk delete job)也都是运维显式发起、可预览、可留底。
    • 只看①选 A;②③④ 是否翻转:否。
    • 不取 B:与已裁"⛔ 不自动迁移"冲突。不取 C:动权限边界换一个罕见场景,不成比例。不取 D:平台工具外推给应用。
    • 置信缺口:删除按 master-detail 自身的删除语义级联时,若明细下还有更深一层的明细,要不要一并列入 dry-run 清单——本席倾向"一并列出、一并删",由 dev 实测后在 PR 里申报。

    裁后执行

    • 裁 A:本卡回 pm:queue(带母单五条约束:显式命令、默认 dry-run、显式删除开关、删前强制导出、任何启动/升级路径不调用),engine 席认领;CLI 命令面按跨车道声明给 domain:cli。
    • 裁其它:按所裁选项改写本卡范围后回队;裁 D 则转 domain:devx 文档卡。

    相关

    #8103 / #8082(母单与同模板),#19837(审计覆盖面,已关),objectstack-ai/hotcrm#2073 / objectstack-ai/hotcrm#2211(发现来源;HotCRM 侧 hook 保持响亮失败)。


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruled: A · domain:engine seat 2 (seat post #20966) · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-20T23:07Z. State: needs-user-decision → pm:queue in the same act.

    Provenance: the maintainer, in this seat's session (session_01QK6pdbxKZiHdFApL8RYVh9), answering the in-session question on this card's analysis 6303771280, verbatim: 「A 运维显式命令 (Recommended)」.

    Freshness: the thread was re-read before recording; 6303771280 is its only comment, so nothing newer than the analysis was outstanding. No Blocked-by: line stands on this card.

    Ruling, as it binds the dispatch (the #8103 template, 5271021652 / 5421208707, inherited with its reasons):

    1. An explicit operator command (os CLI). ⛔ Nothing on any boot, upgrade, migrate or lifecycle-sweep path invokes it, and it must not grow such a caller.
    2. The default run is a dry-run report: each detail row whose required master-detail master does not exist — object, record id, field, and the missing master id.
    3. Deletion only behind an explicit flag, and only of the rows the same run's report lists.
    4. A mandatory pre-delete export of every row the run deletes, written before the first delete.
    5. Deletion applies the master-detail's own delete semantics (its cascade to deeper details). Deeper details the cascade would reach are listed in the dry-run report too; the dev measures and declares this in the PR.

    Out of this ruling: a lifecycle auto-remedy (B), any change to the permission derivation (C), and the system-write exemption in assertReferencesResolve (the audit header keeps it).

    Execution: back to pm:queue, domain:engine (the removal primitive in packages/objectql), with the CLI command face declared cross-lane to domain:cli. The claiming seat cites Ruling-ref: this comment in its claim and dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 · 2026-10-21T01:45Z · p2, domain:engine (triage's grade), ruled A by the maintainer (6303808312). A master-detail row whose required master is missing can be neither read nor removed through any door; the ruling builds an explicit operator command for it on the #8103 template.
    Session: session_01QK6pdbxKZiHdFApL8RYVh9 (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
    Account: os-support-ai (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24964-orphan-detail-operator-command
    Worktree: objectstack-issue-24964
    Domain: domain:engine
    Seat: domain:engine#2 (seat post #20966)
    Ruling-ref: 6303808312 (read at this claim; its five binding constraints are the dispatch's ruling section)
    File surface:

    • packages/objectql: the removal of a listed orphan detail row, applying the master-detail's own delete semantics, through the engine's existing system-context doors over inspectDanglingReferences' findings (engine.ts near :10820; integrity/dangling-reference-audit.ts). The audit itself stays report-only.
    • packages/cli: one new os command beside os db clean / os secret orphans (commands/db/ or commands/data/, the dev's measured choice). It is dry-run by default, deletes only behind an explicit flag, writes a mandatory pre-delete export first, and no boot, upgrade, migrate or lifecycle path calls it. This is cross-lane, declared to domain:cli's seat.
    • Pins for both halves, and a changeset for each package moved.

    ⛔ Not in scope: a lifecycle auto-remedy (B); any change to the permission derivation (C); assertReferencesResolve's system-write exemption; dangling plain lookups that are not a required master-detail master (report-only stays, unless the dev measures that the same command reads them, and then it names them in the dry run but never deletes them); HotCRM.
    Container & model: M, mode:subagent, model: TIER_DEFAULT (this run's dispatch-gates.mjs --tier: "no path-derived mandate"; judgement under a five-constraint ruling)
    Clause-②: no (a new operator command under the ruled template; no published accept set widens). Whether an engine method the CLI needs becomes a package export is NOT read by the seat: the dev measures it on the built declaration closure and reports, and the claim is re-declared yes if one is added.
    Responsibility: HotCRM's pre-5.1.2 elevated scripts wrote the rows; the platform's system-write exemption let them in by design | the dangling-reference audit, which only reports | any operator whose install ran such a writer; the 2026-10-12 hosted HotCRM rig holds one row
    Thread-read: 6303808312
    Serial constraints cleared: the open PRs' file lists were read at this claim. None touches dangling-reference-audit.ts, lifecycle-service.ts, packages/cli/src/commands/data/ or commands/db/. Engine regions held elsewhere: #25009 (this seat, the ReadableIds leaf near :6135), PR #24893 and PR #25016 (other regions); this card's region is inspectDanglingReferences near :10820.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24964,
    "status": "done",
    "branch": "claude/issue-24964-orphan-detail-operator-command",
    "pr": "#25039",
    "session": "session_01QK6pdbxKZiHdFApL8RYVh9 — mode:subagent run of the dispatching seat's session (the parent's id; told apart by its branch)",
    "premise_still_valid": true,
    "summary": "New explicit operator command os db orphans (packages/cli/src/commands/db/orphans.ts, plan in utils/orphan-detail-sweep.ts). Its default run is a read-only report: each detail row whose REQUIRED master_detail master does not exist, with object, record id, field and missing master id, plus the deeper details its delete's cascade removes. Undetermined references, other dangling fields and withheld orphans are reported and never deleted. --delete requires --export: the export holds every record to be deleted as stored, is written 0600 with wx and read back before the first delete. The run re-runs the audit and re-reads each cascade just before deleting, deletes through the engine's own system-context delete(), and judges each delete by reading its records back. ERR_SUMMARY_RECOMPUTE on the missing master's roll-up is noted rather than failed. objectql gains one read-only public method, ObjectQL.inspectCascadeDelete(object, id): the cascade set delete() collects, read through the same walk, filing no elevation record. MEASURED: it reaches the published d.ts closure (dist/util-.d.ts 2 hits; control inspectDanglingReferences 3 hits), so @objectstack/objectql's surface widens by one method, and the claim's Clause-② no is for the seat to re-declare (open_questions). H1 holds (undetermined is a count only). H2 holds, measured on a real kernel and SQL driver: the system delete removes the orphan and its cascade, and a roll-up on the missing master throws SummaryRecomputeError(written) after removal. H3 holds (secret orphans shape and helpers reused). Ruling constraint 5, declared in the PR: the dry run lists every record any cascade relation reaches. restrict and set_null are not listed, because they remove nothing. A restrict dependent refuses the delete atomically (pinned). set_null clearing is the engine's existing behaviour, NOT MEASURED here.",
    "tests": "HEAD 0770e4f. objectql targeted: vitest run --project local --maxWorkers=2 engine-cascade engine-inspect-cascade-delete engine-dangling-reference integrity/dangling-reference lifecycle-service: Test Files 18 passed (18), Tests 328 passed (328), exit 0. cli unit tier, whole: Test Files 318 passed (318), Tests 4699 passed (4699), exit 0. cli integration targeted: orphans.integration.test.ts Tests 9 passed (9), exit 0. One-shot family pin at 26706a6 with -t 'db orphans|the bootSchemaStack family is the table above|arms no lifecycle sweep': 10 passed, 98 skipped, exit 0. Unit: orphan-detail-sweep.test.ts 6 passed and orphans.no-auto-run.test.ts 7 passed. typecheck: pnpm --filter @objectstack/cli typecheck and @objectstack/objectql typecheck both exit 0, check:test-typecheck OK with no new debt. Ablation through scripts/ablation-replace.mjs, CLI src imported directly (no dist in the path), each leg's marker grep -c 1 during the run, each restore proven blob==HEAD with git diff HEAD empty. (a) master-detail filter -> return true: integration 5 failed / 4 passed, unit 1 failed / 5 passed. (b) export not written before deletes (verified from memory): integration 3 failed / 6 passed; the order pin read 'no file' at the first delete. H2 measurement (scratch test, deleted before commit): inspectDanglingReferences returned the orphan in dangling; system delete removed it and its note; SummaryRecomputeError ERR_SUMMARY_RECOMPUTE written=true, 'Record Spring Promo not found in oc_campaign'. JSON purity, single-member spawn (tsx bin/run-dev.js db orphans --json, no database): exit 0, one JSON document on stdout, boot diagnostics on stderr, no database file created.",
    "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 0770e4f derived 105 commands, each run with its exit code captured to a file before any pipe: 105/105 exit 0. --ran reconciliation: 'Run reconciliation — 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. The first run at 43f2159 had one real red: check:slot-lookup, NEW service-lookup erasure in orphans.integration.test.ts, fixed in 0770e4f. It also had four prerequisite refusals: engine-split-ratio (shallow, so history was deepened with --shallow-since=2026-07-16), check:skill-examples, check:dual-build-cjs-loads and check:i18n-coverage (missing builds of out-of-diff packages). All four are green at 0770e4f. CI check-runs at head 0770e4f, read once: No other open PR may claim the same issue = success. Packed-tarball smoke and 18.x upgrade harness = skipped. Type Check (workspace, consumer gates, debt ledger, source gates), Lint (PM dispatch-gates, ESLint, tooling self-tests, CI/release/engine, slot lookup & corpus, docs & code guards, repo & tooling, query-options erasure), Check Changeset, Governed Surface Queue Guard, The card this PR closes must claim this branch, Part-of PR must not also close its card, Check PR Size, Spec property liveness, Check Documentation Links and Flag docs affected = in_progress. Test Core and Build Core were not yet listed.",
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "2 relay strokes (scripts/pm/with-fleet.sh --via dispatch, executed as objectstack-fleet[bot]). Each is sent as POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/25039/assignees. Stroke 2, comment: POST /repos//issues/24964/comments (this report). Plus git push (not REST). Reads: single-card REST GETs of issue 24964, its comments, pulls/25039 and its check-runs.",
    "files_changed": [
    ".changeset/24964-orphan-detail-operator-command.md",
    "content/docs/deployment/cli.mdx",
    "packages/cli/src/commands/db/orphans.ts",
    "packages/cli/src/commands/db/orphans.integration.test.ts",
    "packages/cli/src/commands/db/orphans.no-auto-run.test.ts",
    "packages/cli/src/utils/orphan-detail-sweep.ts",
    "packages/cli/src/utils/orphan-detail-sweep.test.ts",
    "packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts",
    "packages/cli/test/json-stdout-purity.e2e.test.ts",
    "packages/objectql/src/engine.ts",
    "packages/objectql/src/engine-inspect-cascade-delete.test.ts"
    ],
    "line_budget": "n/a — no skills/** file and no line-ratcheted ledger touched. Diff +1788/-1 over 11 files, under the 3000-line human-merge threshold.",
    "deviations": [
    "Declared narrowings: objectql tests ran targeted by file name (cascade, dangling-reference and lifecycle families: 18 files), not the whole 453-file suite. The cli integration tier ran only the files this diff touches. The one-shot family pin ran -t-selected rows at 26706a6. test/json-stdout-purity.e2e.test.ts is declared to CI, with a single-member spawn measured locally instead. CI Test Core runs all of it.",
    "origin/main gained 5 commits after the merge at 43f2159, up to 3b3830c. Their only overlap is packages/cli/test/hook-timeout-override-refusal.test.ts, which this diff does not touch, so they were not merged again before the PR. The CI merge ref covers them.",
    "The changeset carries no Clause-② line. The PR body carries the claim's line verbatim, as the role file requires, but the measured new public method makes 'no' understate. One home is left for the seat's re-declaration, rather than publishing 'no' in CHANGELOG text. Both packages are graded minor: the WHICH LEVEL rule for an additive published-surface widening, and the repo's convention for a new command.",
    "Attribution followed AGENTS.md over the harness reminder. Commits carry the model-free trailer pair (Claude-Session plus Co-authored-by: Claude), and the PR body uses the session-URL footer form.",
    "History was deepened (git fetch --shallow-since=2026-07-16 origin main) to measure check-engine-split-ratio. The fetch was additive and touched the shared object store only."
    ],
    "open_questions": [
    {
    "question": "Clause-② on PR 25039: the claim declared 'no', and this PR adds one public method, ObjectQL.inspectCascadeDelete, measured in @objectstack/objectql's published dist/util-
    .d.ts. Repo precedent for a new CLI command is also 'Clause-②: yes (widening)' (cc305a3 os migrate organization-ownership; os migrate unmapped-columns). Should the seat re-declare?",
    "options": [
    "A: re-declare 'Clause-②: yes (widening)' in the PR body, and add the same line to .changeset/24964-orphan-detail-operator-command.md. Both packages are already minor, so the level axis passes.",
    "B: keep 'no'. The axis stands down either way, but the declaration understates a measured surface widening."
    ],
    "recommendation": "A. The widening is measured on the built declaration closure, and the dispatch named this exact re-declaration as the seat's act."
    }
    ],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — DanglingReferenceReport.undetermined is a count, not itemised, so os db orphans can say how many references it could not check but not which rows. Itemising it would change the audit's published report shape (PR Acceptance notes).",
    "carrier: none · noted, not filed — 'required master-detail' is read through isControlledByParentMasterField (@objectstack/spec/data), whose body is exactly type master_detail plus required true. Its name speaks of controlled_by_parent; the predicate does not depend on the sharing model (PR Acceptance notes).",
    "carrier: none · noted, not filed — the HotCRM premise (an administrator's GET/DELETE by id answer 404) was not re-measured. This boot composes no security plugin, and a user-context read there returned the row. The removal door does not depend on it."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24964,
    "status": "done",
    "branch": "claude/issue-24964-orphan-detail-operator-command",
    "pr": "#25039",
    "session": "session_01QK6pdbxKZiHdFApL8RYVh9 — mode:subagent patch round of the dispatching seat's session (the parent's id; told apart by its branch)",
    "premise_still_valid": true,
    "summary": "Patch round on PR 25039, head 0770e4f verified before the first edit and the newest Claim: (comment 6305964995) names this branch. Item 1: .changeset/24964-orphan-detail-operator-command.md gains the line Clause-②: yes (widening) on its own line after the summary line, where other changesets on main place it (checked against 24750-invite-role-not-found-code and 24808-user-field-foreign-reference-warning). Levels stay minor for @objectstack/cli and @objectstack/objectql. One commit, 5231f8f, pushed normally (0770e4f..5231f8f, no force, no amend). Item 2: the PR body's Clause-② line (line 3) now reads Clause-②: yes (widening) (the new public method ObjectQL.inspectCascadeDelete on @objectstack/objectql, and the new command os db orphans), written by one issue_patch stroke through the relay, which accepted it. Read back: the stored body is byte-identical to the bytes sent, only line 3 differs from the previous body (60 lines before and after), one session-URL footer, and the PR is still draft with the same assignee, labels and head. No code, test or doc changed.",
    "tests": "None run: the diff is two lines of changeset text. The gates below are the evidence.",
    "gates": "HEAD 5231f8f, --base origin/main (d68aab6), merge base 4d99158; every exit code was captured to a file before any pipe. (1) node scripts/check-changeset-no-major.mjs --base origin/main --event FILE, the payload built from GET /pulls/25039 AFTER the body edit (stored body, labels, draft): exit 0. Its lines: "This diff introduces no major bump", "LEVEL AXIS: this PR declares clause-② yes (widening), and no package whose published source it moves is graded patch", "direction arm: widening". The same gate with the proposed body before the write: exit 0, same lines. (2) node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0, "adds no declared-breaking changeset (1 non-breaking changeset(s) seen)". (3) node scripts/check-empty-changeset.mjs --base origin/main: exit 0, "1 declaring changeset(s) added". (4) pnpm check:changeset-gate-self-tests: exit 0 (170 + 548 + 401 assertions). Extra, because they read changeset text, all exit 0: check-issue-citations, check-keyed-text-bounds, check:closed-ceremony-text, check:driver-memory-census, check:nul-bytes (no raw control bytes), check:objectui-changeset, check:pm-changeset-deadline-census, check:published-files. Declared narrowing: the other commands of the 105-command derived list (node scripts/pm/dispatch-gates.mjs --commands, exit 0, still 105 at 5231f8f) were not re-run, because this round's delta is two changeset lines and the previous round ran 105/105 at 0770e4f; a grep of the 76 scripts that list references for the .changeset directory found 14, and every one of them that reads changeset text, bar two release-lane self-tests, was run above. CI check-runs at head 5231f8f, read once and not waited on. success: Check Documentation Links, Spec property liveness, Auto Label, Check PR Size, 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, The card this PR closes must claim this branch (one of its two runs). skipped: 18.x upgrade harness, Packed-tarball smoke, one Auto Label run and one Check PR Size run. in_progress: Check Changeset (two runs), Governed Surface Queue Guard, Flag docs affected by code changes, Lint (CI, release & engine gates; ESLint; PM dispatch-gates; docs & code guards; repo & tooling gates; query-options erasure; slot lookup & corpus; tooling self-tests), Type Check (consumer gates, debt ledger, source gates, workspace), and the second run of The card this PR closes must claim this branch. Test Core and Build Core were not yet listed.",
    "dogfood_battery": "n/a — no ! in the PR title and the declared arm is (widening), not (narrowing)",
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "2 relay strokes (scripts/pm/with-fleet.sh --via dispatch, executed as objectstack-fleet[bot]), each sent as POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1, issue_patch: PATCH /repos//issues/25039, body only (run 39538875952, conclusion success; the relay's read-back: 11103 bytes sent, 11103 stored, identical). Stroke 2, comment: POST /repos//issues/24964/comments (this report). Plus one git push (not REST). Reads: single-object REST GETs of pulls/25039 (four), the check-runs of commit 5231f8f, issues/24964/comments and two comments by id.",
    "files_changed": [
    ".changeset/24964-orphan-detail-operator-command.md"
    ],
    "line_budget": "n/a — no skills/** file and no line-ratcheted ledger touched. This round adds 2 lines to one changeset.",
    "deviations": [
    "The brief limited the PR-body edit to the one Clause-② line, so line 5 of the body was left as written. It now contradicts the line above it (see open_questions).",
    "The PR-body line carries its reason as a second parenthetical after the arm, as briefed. The gate reads the arm from the first parenthetical (echoed above: widening). The changeset line carries no reason, as briefed.",
    "The branch was not merged with main in this round (changeset text only). The gates diff against the merge base 4d99158, with --base origin/main as briefed.",
    "Attribution followed AGENTS.md over the harness reminder: the commit carries the model-free trailer pair (Claude-Session plus Co-authored-by: Claude).",
    "A worktree was built at /home/user/objectstack-issue-24964 from the existing local branch (equal to the remote head 0770e4f); pnpm install --frozen-lockfile exit 0. It is removed after this report, without --force."
    ],
    "open_questions": [
    {
    "question": "PR 25039 body, line 5, now contradicts the line it follows. It reads "The line above is the claim's (comment 6305964995), copied as the dispatch requires. Measured for the seat to re-declare: ... The changeset carries no Clause-② line, so the re-declaration lands in one place." After this round the line above is the seat's re-declaration and the changeset does carry the line. Should that paragraph be replaced or removed?",
    "options": [
    "A: the seat rewrites it to say the declaration is the seat's re-declaration, in the body line and in the changeset, or deletes it, through one more issue_patch stroke.",
    "B: leave it; it is explanatory prose and no gate reads it."
    ],
    "recommendation": "A, because a body that says the changeset carries no Clause-② line is false about the diff the reviewer reads. It was not mine to change: the brief limited this round to the one line."
    }
    ],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25039 comment 6307800302 · contract review (Clause-②: yes (widening), declaration face) PASS 6307770129 on 5231f8fd7 · ready + auto-merge armed 2026-10-21T04:09Z. The merge closes this card via Fixes.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:engine seat 2 · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-21T04:40Z

    Probe Reading
    PR #25039 merged: true, merge commit 157ebd008, through the merge queue on its first entry
    origin/main 157ebd008 feat(cli): os db orphans — report detail rows whose required master-detail master is missing, and delete them only behind --delete after a pre-delete export (#25039)
    queue branch gh-readonly-queue/main/pr-25039-* gone
    card closed completed by Fixes #24964

    State: pm:dispatched removed in this act. Ruling A (6303808312) is delivered as os db orphans. The acceptance notes (the undetermined count, the predicate's name, HotCRM's 404 premise) have no carrier, as the seat's ACCEPT recorded.


    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

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions