Skip to content

finding: a stacked PR series has no model in this repo's tooling — three costs measured end-to-end on the #14478 six-card stack #16149

Closed

Description

@os-sales

Filed bare for triage by the domain:spec execution seat (session_01G4138K1EG7kQ81FNba5Kp4) after the #14478 stack landed on main at e9fcd6bbd (2026-09-06T03:17Z). Observation, not a graded defect — no fix proposed, no domain:* set, no pm:queue.

The stack was six cards deep (#15676 → #15682, PRs #15814 · #15837 · #15906 · #15938 · #15988 · #16022), each branched off the one below it, landing on a shared trunk branch and then to main as PR #15626. It worked, but three separate pieces of tooling turned out to have no representation of "this PR is stacked on another PR", and each cost real work. All three are measurements from that campaign, not predictions.


1. check:adr-0087-registration — already-registered is base-relative, and a stacked card has two bases

The gate's own header states the rule:

already-registered — every named id must resolve at HEAD and already exist at the merge base. If the id is new in this diff the honest disposition is registered, and the two cannot be confused because base decides.

Card 6/6 renamed a key already covered by a conversion card 5/6 registers. So:

base is turso-config-timeout-to-timeout-ms there? honest disposition
card 6/6's own base (card 5/6's head) yes already-registered — exit 0
origin/main no registered — anything else fires R5, severity 3

Neither card is wrong. The two base levels demand contradictory dispositions, and R5 consults only the ids this diff adds, with no cross-reference to a sibling changeset carrying registered in the same eventual merge. Writing registered on card 6/6 would have fired R4 against its own base instead.

The workaround that shipped: land all six cards on the trunk, then edit the disposition to registered on the trunk before it merged to main. Verified there — exit 0, .changeset/driver-turso-config-timeout-ms.md [BREAKING+bang] registered turso-config-timeout-to-timeout-ms (new here: …).

Two traps found while pre-verifying that edit, worth recording whether or not this is ever fixed:

  • The gate reads the changeset from the committed diff, not the working tree. An uncommitted edit returns the identical finding, so it reads as "the fix does not work".
  • Trailing prose on the marker line is parsed as further ids. Keeping the explanatory sentence inline produced "29 of those id(s) do not exist in the ADR-0087 registries at HEAD: —, that, protocol-18, conversion, added, by, …". The not-required (…) form tolerates prose only because the parentheses delimit the ids.

2. Squash-only × stacked ⇒ one repair lap per descendant, cumulative

Merge commits are refused at the API level:

PUT /repos/objectstack-ai/objectstack/pulls/15906/merge  (merge_method: merge)
  → 405 Merge commits are not allowed on this repository

and main carries 0 merge commits in its last 154 (control: the same --merges query does find one on a feature branch, so the zero is a reading).

A squash keeps the content and destroys the ancestry link, so every descendant's merge base rewinds past everything already landed. Measured after card 2/6 landed:

git merge-base --is-ancestor dcd1be0db 43cc4659a   → exit 1   (2/6's head is NOT in the trunk's history)
git merge-base 43cc4659a adad660a1                 → 140e0b266  (before cards 1/6 AND 2/6)

The descendant then re-applies content the trunk already holds and collides. Three repair laps were paid — cards 3/6, 4/6 and 6/6 — each a merge, a hand resolution and a regeneration pass. The conflicts were benign in shape but not free: card 4/6's four conflicts were all one prose correction made after it forked, and its side was a purely inherited stale snapshot (blobs byte-identical to its fork point).

⭐ The generalisation, which is what makes this worth a card: the cost is not per-stack, it is per-descendant, and it recurs on every landing. A six-deep stack pays it five times if nothing changes.

3. Heavy CI never runs on a stacked PR at all

lint.yml and ci.yml both declare:

on:
  pull_request:
    branches:
      - main

A PR based on a feature branch triggers neither, so none of the six required contexts reports on any stacked head. Measured across this whole stack: every card PR showed 8–11 light checks (Auto Label, Check Changeset, PR Size, docs drift, …) and zero of Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Dogfood Regression Gate, Temporal Conformance.

Consequence: a stack's first real signal arrives only at the trunk PR, after every card is written, reviewed and merged. Six cards' worth of work was verified by local runs quoted into PR bodies — which is a discipline that worked here, but it is a discipline, not a gate. On the trunk PR the real run came back 35/35 green on the first try; that is a fact about this stack's care, not about the arrangement's safety.


Prior art in this repo

#12933 is the same family through a different mechanism: GitHub's speculative queue stacking made merge-queue-triage count one deterministic failure as N distinct victim PRs, because the tooling had no notion that the victims were one stack. That card's phrasing applies here too — the count (or the disposition, or the trigger) is systematically wrong in a knowable, mechanically detectable way, and every reader re-derives it by hand.

For triage

Deliberately not split into three cards: the three costs were measured on one campaign and a reader deciding whether this repo should support stacked series needs them together. Splitting is triage's call. Each has a different owner and a different kind of fix — a gate rule, a repository setting or policy, and a workflow trigger — and it is entirely reasonable to grade them differently, including grading some or all of them "won't fix, stacks are rare enough".

⚠️ What this card does not claim: that stacking was the wrong choice for #14478. Six cards over one contract surface, each independently reviewable, is what the maintainer ruled and it produced a clean landing. The finding is only that the tooling around it is silent about the arrangement, and the silence is paid for by hand each time.

Activity

  1. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊:domain:devx / enhancement + tooling + finding / needs-user-decision / priority:p3

    ⛔ needs-user-decision 不与 pm:* 并存(先例 #15854 / #15617 / #15542),故不挂 pm:queue。

    落点核实(origin/main,本轮实读)

    第 3 项——本席可以在源码上直接复核,且成立:

    # .github/workflows/lint.yml          # .github/workflows/ci.yml
    on:                                    on:
      push:                                  push:
        branches: [main]                       branches: [main]
      pull_request:                          pull_request:
        branches: [main]     ← 就是这一行      branches: [main]     ← 同
      merge_group:                           merge_group:

    两个工作流都把 pull_request 限定到 branches: [main]。⇒ 一个基于 feature 分支的 PR 两者都不触发,卡片对第 3 项的机制描述逐字成立。

    ⚠️ 第 1、2 项本席未复核:check:adr-0087-registration 的双基线矛盾、405 Merge commits are not allowed、git merge-base --is-ancestor 的 exit 1、以及三次修复回合的成本,都是该 campaign 的现场实测,需要那次 stack 的分支才能复现,本容器的克隆是浅克隆(本 lane 纪律⑫:任何 git log 历史查询在此不构成历史读数)。⛔ 不要把本席对第 3 项的核对当成对第 1、2 项的复现继承下去。

    ✅ 但卡片第 2 项自己带了活控制,符合本 lane 纪律②:"main carries 0 merge commits in its last 154 (control: the same --merges query does find one on a feature branch, so the zero is a reading)" ——零命中配同一次读取里的正控制,⇒ 该零是读数。这一条本席背书。


    为什么挂决定箱,而不是拆成三张卡直接排期

    因为卡片自己把待答问题写成了一个策略问题,而不是三个技术问题:

    a reader deciding whether this repo should support stacked series needs them together … it is entirely reasonable to grade them differently, including grading some or all of them "won't fix, stacks are rare enough".

    ⇒ 「本仓是否要支持 stacked PR series」这个前置问题不答,三项的排期都无从谈起:答"不支持",三项全部降为"记录在案,遇到时按本卡的 workaround 手动处理";答"支持",三项各自有明确且互不相同的 owner。⛔ 本席不裁决(本会话 claude-opus-5,CONTRACT_REVIEW_TIER 硬门要求 fable)。

    四facet

    ① 事实(第 3 项已复核,第 1/2 项为现场实测)
    #14478 的六卡 stack(#15676 → #15682,PR #15814 · #15837 · #15906 · #15938 · #15988 · #16022,经 trunk 分支以 PR #15626 落 main,e9fcd6bbd @ 2026-09-06T03:17Z)成功落地,但三处工具链对"这个 PR 叠在另一个 PR 上"没有任何表示,各自产生了实际工作量:

    1. check:adr-0087-registration 的 already-registered 是相对基线的,而 stacked 卡有两条基线,同一处置在两条基线下互相矛盾(对自身 base 应写 already-registered,对 origin/main 应写 registered;写错任一条分别触发 R5 严重度 3 或 R4)。
    2. squash-only × stacked ⇒ 每个后代一次修复回合,且累计:squash 保留内容、销毁祖先链接,后代的 merge base 回退到已落地内容之前 ⇒ 重放已有内容并冲突。实测付了三次(卡 3/6、4/6、6/6)。⭐ 成本是按后代计、每次落地重复,六层 stack 若不改会付五次。
    3. 重型 CI 在 stacked PR 上从不运行:六张卡 PR 各显示 8–11 个轻量检查,六个必需上下文(Lint & Repo Gates / TypeScript Type Check / Test Core / Build Core / Dogfood Regression Gate / Temporal Conformance)零个报告。第一次真实信号只在 trunk PR 到达——在六张卡全部写完、评审完、合并完之后。

    ② 分叉

    • A. 不支持:明确记录"本仓不支持 stacked series",把本卡的三条 workaround 写进 AGENTS.md,遇到时手动付。
    • B. 支持,三项分别修:① 让 R5/R4 能交叉引用同一次最终合并里的兄弟 changeset;② 改仓库合并策略或引入 stack 感知的重建;③ 给 lint.yml / ci.yml 的 pull_request 触发器加上 feature 分支。
    • C. 只修第 3 项:承认 stack 罕见,但不接受"六个 PR 零必需检查"——把 CI 触发面补上,其余两项按 A 处理。

    ③ 各支的代价

    • A:零工程成本;代价是每次 stack 都重付,且卡片指出成本按后代计,深 stack 的边际代价是超线性的。⚠️ 且第 3 项在 A 下仍然存在:即使明确不鼓励 stack,一旦有人用了,六个 PR 仍是零门通过。
    • B:三个不同 owner(门规则 / 仓库设置或策略 / workflow 触发器),其中②可能触及 squash-only 这条既定策略——⛔ 那是维护者的决定,不是工程取舍。
    • C:成本最低且独立于 stack 是否受支持——见下。

    ④ 需要裁决者提供的东西
    一句话:本仓是否把 stacked PR series 当作受支持的工作形态? 答"是"⇒ 走 B 并按下面的拆分排期;答"否"⇒ 走 A,但请单独回答第 3 项(见下)。


    ⭐ 本席的拆分建议(卡片明说拆分是 triage 的活)

    建议拆成 1 + 2,而不是 3:

    • 第 3 项应当单独拆出,并可以独立于本决定推进,建议 p2。 理由:它的成立不依赖 stack 是否受支持。pull_request: branches: [main] 意味着任何基于 feature 分支的 PR 都拿不到六个必需上下文——这不是 stack 特有的,stack 只是把它暴露出来的场景。卡片自己写得很克制:"Six cards' worth of work was verified by local runs quoted into PR bodies — which is a discipline that worked here, but it is a discipline, not a gate." ⇒ "一条纪律,不是一道门",正是本 lane 一整天在追的那个失败类的另一种说法。而 trunk PR 首轮 35/35 全绿,卡片诚实地指出那 "is a fact about this stack's care, not about the arrangement's safety" ——本席原样背书这句自我限定。
    • 第 1、2 项留在本卡,作为「是否支持 stacked series」这个决定的两份代价证据。它们只有在答"支持"时才需要修。

    ⛔ 本席不代为拆卡:拆卡会新建卡片,而本轮的纪律是先清完既有裸卡;且第 3 项的粒度(只加触发器 vs 同时处理 merge_group 与 concurrency 的相互作用)需要 CI owner 判断。建议由承接席或 CI lane 在取卡时执行拆分,并在本卡上回链新卡号。

    ⚠️ 若拆出第 3 项,取卡人必须先读 lint.yml:12-20 与 ci.yml:9-15 的那两段注释——它们记录了 merge_group 触发器与队列必需检查之间的耦合(ci.yml:12-15:"Every workflow that produces a branch-protection-required check MUST carry this trigger, or queue builds wait forever on a check that never reports"),以及 lint.yml:16 起那段 MEASURED 2026-08-25 (#12211) 的否定结果。放宽 pull_request 的分支面会显著增加 runner 用量,且与 ci.yml:17 起的 concurrency 取消策略相互作用。⛔ 不是一行改动。


    定级理由

    • domain:devx:三项落点分别是 scripts/ 的门、仓库合并策略、.github/workflows/ ——全部在 domain:devx 的 lane 内。
    • enhancement 而非 bug:main 上没有东西是假的——三处工具各自在其被设计的场景下都正确工作,只是对 stacked 这个形态没有表示。卡片自己的定性 "Observation, not a graded defect" 本席采纳。⚠️ 例外:若第 3 项拆出后被判为"必需检查在一整类 PR 上不报告"是缺陷而非缺失,那张子卡应挂 bug。
    • p3:无运行期后果、无用户可见影响、无数据风险;且触发需要有人主动采用 stacked 形态。⛔ 不降更低:三项成本已被实测(不是预测),且卡片给出了"按后代计、每次落地重复"这个会随 stack 深度放大的机制。
    • finding:整卡是一次 campaign 的端到端实测,带控制(第 2 项的 --merges 正控制)、带日期、带具体 sha 与 PR 号。

    与 #12933 的关系

    卡片说它是"同一族、不同机制"(GitHub 的推测式队列堆叠让 merge-queue-triage 把一次确定性失败记成 N 个不同的受害 PR,因为工具没有"这些受害者是同一个 stack"的概念)。本席认同并背书卡片引用的那句概括——the count (or the disposition, or the trigger) is systematically wrong in a knowable, mechanically detectable way, and every reader re-derives it by hand——它同时描述了本卡的三项。⛔ 不合并:#12933 的机制在队列里,本卡的在 PR 与门上。⚠️ 但裁决者应当把两张一起看:若答案是"本仓不支持 stacked series",那么 #12933 的那个错误计数就是一个不该存在的形态的产物,处置会随之改变。

    ✅ 最后记一笔:卡片明确写下它不主张的东西——"that stacking was the wrong choice for #14478. Six cards over one contract surface, each independently reviewable, is what the maintainer ruled and it produced a clean landing." ⭐ 把"我测到了成本"与"我认为当初的决定错了"分开,是本席希望被复制的填卡纪律。


    ⛔ 本席为 triage 席位:不认领、不派单、不写码、不合并、不裁决 decision-box。


    Generated by Claude Code

  2. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Ruling recorded — option C: fix item 3 on its own; items 1–2 recorded as "stacked series are not a supported form" (director seat, decision batch #66, 2026-09-07)

    Maintainer reply, verbatim: 「同意」 (all five batch #66 recommendations adopted).

    Ruling.

    Execution (this card, skills seat, governed PR): one AGENTS.md paragraph stating the above, pointing at this card for the measured costs and workarounds. #12933's mis-count of speculative-queue stacking is a separate mechanism and is unaffected.

    Labels: needs-user-decision → pm:queue; lane domain:devx → domain:skills (the remaining work is the AGENTS.md paragraph). Ledger on #12708 (batch #66).


    Generated by Claude Code

  3. self-assigned this
    on Sep 8, 2026
  4. hotlong commented on Sep 8, 2026

    @hotlong
    Contributor

    Claim: PM loop round 1 — flight B: ruling C's AGENTS.md paragraph — stacked PR series are not a supported working form; item 3 lives in #16482
    Session: session_01P58euzUXCVJNwmhuPC9DXY
    Branch: claude/issue-16149-stacked-series-not-supported
    Worktree: objectstack-issue-16149
    Domain: domain:skills
    File surface: AGENTS.md (one paragraph added, fold-paid inside the same file at 1068/1068; ⛔ no gate, workflow or merge-policy change) (stop on breach; explain in the report)
    Container & model: S (one governed paragraph plus its ratchet payment), mode:subagent, model: opus — --tier output at 02:40Z: "no path-derived mandate … floor sonnet · default opus · ceiling fable"; default judgement tier: a ruling implementation on the instruction architecture file, with a fold-payment judgement
    Clause-②: no
    Thread-read: 5570948653
    Serial constraints cleared: AGENTS.md was held by devx PR #16573 (#16479), MERGED 2026-09-08T02:13Z as 5022924 — the serial queue released this fire; #16633 (the footer sentence, same file) is queued BEHIND this card, fold-or-serial answered SERIAL on the seat post (different defect shape and fix); open objectstack PRs at 02:39Z (15) — none names AGENTS.md by title; no lane in-flight claim; no claude/issue-16149-* ref at 02:32Z; no H17 hold trigger file hit; verify lock free at 02:39Z. Governed ⇒ draft only at the governed terminal.


    Generated by Claude Code

  5. hotlong commented on Sep 8, 2026

    @hotlong
    Contributor

    os-dev-report

    {
      "issue": 16149,
      "status": "done",
      "branch": "claude/issue-16149-stacked-series-not-supported",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16743",
      "premise_still_valid": true,
      "summary": "Ruling C's execution item landed as one AGENTS.md paragraph in Multi-agent working discipline, as an indented continuation of numbered rule 2 (the rule that already reads 'Branch off main') — that is the sentence the ruling qualifies. The paragraph declares stacked PR series unsupported, states each of the three tooling blind spots in one mechanism clause, gives the trunk-branch route with both recorded workarounds (correct the trunk's disposition to registered before it merges; pay the rebase laps), and closes with the boundary that no gate or merge-policy change is made. No gate, workflow or merge policy was touched; item 3 (card #16482, domain:devx) untouched. PR is draft and stays draft: governed surface, exit 3 GOVERNED. Claim verified before any edit — newest Claim: comment 5578356540 names this branch and this session; assignee never written.",
      "tests": "No build/test applies — the diff is one Markdown file in a private root package, so no package closure, no typecheck target, no vitest target; the verify lock was never needed and never taken. Gate union re-run at final commit 7a233b9b on a clean tree, all exit 0: node scripts/check-closing-keyword-parity.mjs=0; --self-test=0; node scripts/check-comment-mask-corpus.mjs=0; check:agent-test-spelling=0; check:docs-audit-scope=0; check:driver-memory-census=0; check:nul-bytes=0; check:pm-governed-merges=0; check:pm-governed-prose=0; check:pm-skill-id-lint=0; check:pm-skill-ratchet=0; check:refd-timer-probe=0; check:required-contexts=0; check:watch-hint-literal=0. Verdict lines: 'check-nul-bytes: OK (scanned 8270 text file(s) -- 8270 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).'; 'check-skill-id-lint: 26 file(s) clean (pattern /#[0-9]{3,}/g).'; 'check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** . .claude/** . skills/** . AGENTS.md . CLAUDE.md) and claim no others.' Reconciliation: 'dispatch-gates --ran: 14 derived famil(ies) accounted for — 14 run, 0 NOT-MEASURED.' Governance predicate: node scripts/pm/check-governed-merges.mjs --test AGENTS.md EXIT=3, 'GOVERNED — a human merge is the review record for this PR.' Lint narrowing PROVEN not skipped, three readings: (1) population from eslint.config.mjs — every files: block globs {ts,tsx,mts,cts,js,jsx,mjs,cjs}, no markdown block; (2) count from --format json — one entry, errorCount 0, single warning 'File ignored because no matching configuration was supplied.'; (3) invariance — the one touched path is outside eslint's population and type-aware linting is scoped to the TS globs, so no untouched file's verdict can move. Merge-tree vs origin/main 1ea349f0: exit 0, no conflicted paths; git check-attr merge -- AGENTS.md = 'merge: unspecified', so no os-regen driver ran and that exit 0 is a real textual reading, not a deferral. No ablation applies (documentation change, no gate or code behaviour added). Control-byte self-scan of AGENTS.md beyond the gate: grep -naP over the control ranges, zero hits.",
      "line_budget": "AGENTS.md before 1068, after 1068, net 0. Ratchet verdict at 7a233b9b: 'check-skill-line-ratchet: AGENTS.md is 1068 lines (ceiling 1068; headroom 0).' and 'check-skill-line-ratchet: AGENTS.md: widest table row is 768 bytes (pin 768; headroom 0).' Ceiling NOT raised. Paid by deleting content, not by re-wrapping: the fenced ADR-0087 marker block in Post-Task Checklist item 3 plus its surrounding prose, 10 lines rewritten to 3, funding the 7-line paragraph (6 content lines + 1 blank). Survivor check for every cut line — the four adr-0087 marker spellings still live in FIXIT in scripts/check-adr-0087-registration.mjs, which prints them on every failure and prints SEVEN categories where AGENTS.md listed four (runtime-interface-only and type-surface-only were missing); the category semantics live in ADR-0087's addendum of 2026-08-13, named by that same FIXIT output; and the three load-bearing facts (add exactly one marker, the gate name, the CI step name) were KEPT in place. The cut copy was drifted and under-reporting inside a paragraph whose own closing sentence already said the gate's output is the authority, so the deletion removes a misleading duplicate rather than a rule.",
      "gates": "14/14 derived families exit 0 at 7a233b9b; list re-derived in-worktree with 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' (no path args, merge-base derived) and it matched the dispatch's 14 exactly — no family added, none dropped. --ran reconciliation: 14 derived, 14 run, 0 NOT-MEASURED, 0 UNRUN. Plus governed predicate EXIT=3. Full command/exit/verdict detail is in the tests field.",
      "files_changed": "AGENTS.md (only). +10 / -10, 1 file, 1 commit 7a233b9b. No changeset: skip-changeset label applied, justified by measurement — no non-private package lists AGENTS.md in its files[], and the root package is private:true, so nothing publishes.",
      "deviations": [
        "RULING-VS-GATE, declared: the ruling says the paragraph should point 'at this card for the measured costs and workarounds'. That form is mechanically unavailable in this file — AGENTS.md is in check-skill-id-lint's scan set (EXTRA_FILES) with pattern /#[0-9]{3,}/g, so a five-digit card number there is a hard red. Ran the gate and read its source rather than guessing, per the dispatch. Resolved the way that gate's own header prescribes: 'a rule's provenance lives in the PR that landed it' — the card pointer lives in the PR body (Fixes #16149) and the paragraph carries the lesson self-contained (failure mode + discipline + boundary), one mechanism clause per cost, no numbers restated. PM assumption 2 therefore resolved to the RESTRICTED branch.",
        "DISPATCH ERRATUM: the order asked for 'node scripts/pm/check-governed-queue-guard.mjs --test AGENTS.md' expecting exit 3 GOVERNED. That script has no --test PATHS mode; it reads GITHUB_EVENT_PATH only and correctly refuses with exit 1 when absent ('could not look must never exit 0'). The --test predicate belongs to the sibling scripts/pm/check-governed-merges.mjs, whose header states exit 3 is deliberately the same code. Ran the correct script: EXIT=3 GOVERNED. Not a defect in either script.",
        "BASE MOVED: dispatch quoted origin/main 5e53d73; on my own fetch it had advanced to 1ea349f0. Worktree branched from 1ea349f0, all readings taken there.",
        "CHANNEL: repo-scoped REST is 403 for this seat ('GitHub access is not enabled for this session'); /rate_limit reads 15000/hr, so the credential is live and repo-scoped writes are gated, not exhausted. Card body read via the zero-quota public payload channel; comments, PR create, labels and this report via MCP. Label write used the documented fallback (read current -> union -> whole-group write -> comparative read-back) because the add-only REST endpoint is unreachable; read-back returned exactly union(size/s, skip-changeset) with nothing stripped and draft still true."
      ],
      "mcp_calls": "8 — issue_read(get_comments), create_pull_request, pull_request_read(get), issue_read(get_labels, errored: does not resolve PR numbers), list_pull_requests(labels read), issue_write(labels), list_pull_requests(label read-back), add_issue_comment(this report)",
      "open_questions": [],
      "out_of_scope_findings": [
        "repaired here as the fold payment, no card needed: the AGENTS.md ADR-0087 marker block listed 4 of the gate's 7 disposition categories, omitting runtime-interface-only and type-surface-only — an author trusting it would have picked from an under-reported set. Same file, same gate family, mechanical fix, no new verification surface, so it met the bounded-in-place criteria; declared in the PR body with the survivor check.",
        "noted, not filed: scripts/pm/check-governed-queue-guard.mjs has no --test PATHS mode (see deviations). Successor: the PM, who owns the dispatch template that named it. Not a defect — the script's refusal-without-event-payload is its documented correct behaviour, so it is not (a), (b) or (c).",
        "noted, not filed: no other observation. Deliberately did not touch card #16633 (same file, footer sentence), which the claim comment records as queued serially behind this one."
      ]
    }

    Generated by Claude Code

  6. hotlong commented on Sep 8, 2026

    @hotlong
    Contributor

    os-dev-report

    {
      "issue": 16149,
      "status": "done",
      "round": "patch round 2 (REWORK-lite) — incremental over comment 5578512590",
      "branch": "claude/issue-16149-stacked-series-not-supported",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16743",
      "premise_still_valid": true,
      "head": "1c56734885aa4613ae19ad4c147030822d76f609 (short 1c5673488), was 7a233b9be; merge base now 7c12e475e",
      "summary": "Seat review accepted and applied: the heavy-CI clause (card item 3) is removed from the AGENTS.md paragraph. The ruling grades item 3 as 'a defect independent of stacking' and splits it to its own devx card, so stating it as an inherent property of stacking both mis-grades a defect as an accepted cost and goes false the moment that card lands. The paragraph now carries only the two structural costs the ruling assigned here: squash landing destroys the ancestry link so every descendant pays a rebuild lap per landing; and a breaking changeset's ADR-0087 disposition is base-relative so a stacked card's two bases demand contradictory markers. No number pointing at the devx card was added (id-lint), and no hedged substitute such as 'CI does not run on these yet' was used — that wording has the same expiry date. Three commits appended, no amend, no force-push. PR still draft, never flipped ready, never enqueued, never armed, no review requested.",
      "commits_appended": [
        "6d5ba7fce merge origin/main (clean; only scripts/check-regen-pending.mjs arrived; AGENTS.md untouched by others between 1ea349f0e and 7c12e475e)",
        "82f067290 drop the heavy-CI clause from the stacked-series paragraph",
        "1c5673488 keep the stacked-series paragraph inside the 120-byte budget"
      ],
      "line_budget": "AGENTS.md 1068 -> 1067, net -1. Verdict at 1c5673488: 'check-skill-line-ratchet: AGENTS.md is 1067 lines (ceiling 1068; headroom 1).' and 'check-skill-line-ratchet: AGENTS.md: widest table row is 768 bytes (pin 768; headroom 0).' Nothing was restored to pad the count back to 1068 — the ADR-0087 fold payment from round 1 stands untouched, and the ratchet is a cap so a net decrease is legal. NEW FINDING, reported because it is a distinct check the round-1 report did not name: check:pm-skill-ratchet ALSO enforces a per-LINE 120-BYTE budget beside the per-file line ceiling. The first reflow of the shortened paragraph tripped it by one byte — '✗ check-skill-line-ratchet: AGENTS.md has 1 line(s) over the 120-byte budget: L469 (121B).' Fixed in 1c5673488 by rewrapping to five lines, each under budget (118/119/120/119/112 B), shortening the closing boundary to 'No gate or merge-policy change is made for it' to avoid a 6-byte orphan line. Whole-file rescan: 14 lines remain over 120B, all of them tables or a blockquote, i.e. structurally exempt by shape, and none of them mine.",
      "gates": "14/14 exit 0 at 1c5673488 on a clean tree. List re-derived TWICE. First derivation warned 'STALE TREE — 1 file(s) it derives from CHANGED': scripts/check-regen-pending.mjs had moved on origin/main, and since that file's new copy literally contains the string AGENTS.md the derivation could have shifted. Rather than reason about it I merged origin/main and re-derived from a current tree: no stale warning, merge base 7c12e475e, and the derived list came back byte-identical to the dispatch's 14. Reconciliation: 'dispatch-gates --ran: 14 derived famil(ies) accounted for — 14 run, 0 NOT-MEASURED.' (14 derived, 14 run, 0 UNRUN). Commands and exits: node scripts/check-closing-keyword-parity.mjs=0; --self-test=0; node scripts/check-comment-mask-corpus.mjs=0; check:agent-test-spelling=0; check:docs-audit-scope=0; check:driver-memory-census=0; check:nul-bytes=0; check:pm-governed-merges=0; check:pm-governed-prose=0; check:pm-skill-id-lint=0; check:pm-skill-ratchet=0; check:refd-timer-probe=0; check:required-contexts=0; check:watch-hint-literal=0. Verdicts: 'check-skill-id-lint: 26 file(s) clean (pattern /#[0-9]{3,}/g).'; 'check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces ... and claim no others.' Governance predicate: node scripts/pm/check-governed-merges.mjs --test AGENTS.md EXIT=3, 'GOVERNED — a human merge is the review record for this PR.' No os-regen debt outstanding after the merge (git-dir os-regen-pending absent). Control-byte self-scan of AGENTS.md: zero hits.",
      "files_changed": "AGENTS.md only (plus scripts/check-regen-pending.mjs arriving via the origin/main merge, not authored here). Branch delta vs merge base 7c12e475e is AGENTS.md alone.",
      "pr_body": "Rewritten and read back in full to the tail. 'three tooling blind spots' -> 'two structural costs'; new section 'Why item 3 is not in the paragraph' stating the ruling's grading and the rot argument; 维护者速读(草稿) gains a matching 为什么第三条没写进来 paragraph; measurements updated to 1c5673488 / 1067 lines / merge base 7c12e475e, plus the byte-budget finding.",
      "deviations": [
        "FOOTER, measured: the documented edit behaviour is that the platform appends a bare footer block under an edited body. It did NOT on this edit (issue_write body update): the body now ends at the last prose line with no footer block at all. Not re-sending a footer — re-sending is the documented double-footer failure mode. Durable attribution is carried by the commit trailers (Claude-Session on each commit).",
        "LABELS: auto-labeler added 'documentation' between rounds (another actor). Read-back after the body edit: ['documentation','size/s','skip-changeset'] — my skip-changeset survived, nothing of mine stripped, draft still true. Not corrected, per the do-not-touch-other-actors' state rule.",
        "BODY EDIT CHANNEL: used issue_write (issues API) rather than update_pull_request, because update_pull_request sends the draft bit on every call whether or not you pass it — the documented way governed drafts have been published into the merge queue by accident. issue_write carries no draft field. Read-back confirms draft:true.",
        "Round-1 deviations still stand: the id-lint constraint forbidding a card number in AGENTS.md; the dispatch's erratum naming check-governed-queue-guard.mjs instead of check-governed-merges.mjs for --test; REST 403 for this seat with MCP as the write channel."
      ],
      "mcp_calls": "12 cumulative (round 1: 8; round 2: issue_write(body), list_pull_requests(labels/draft), list_pull_requests(body read-back), add_issue_comment(this report))",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: check:pm-skill-ratchet's per-line 120-byte budget is enforced but is not mentioned in AGENTS.md's own text, so an author editing a ratcheted file meets it only as a gate failure. Successor: whoever next edits a ratcheted instruction file — which is the same small set of seats. Not filed as a defect: the gate's failure message is complete and self-explanatory, so it is not (a), (b) or (c).",
        "repaired in round 1 as the fold payment, no card needed: the AGENTS.md ADR-0087 marker block listed 4 of the gate's 7 disposition categories.",
        "noted, not filed: scripts/pm/check-governed-queue-guard.mjs has no --test PATHS mode (round-1 finding, unchanged)."
      ]
    }

    Generated by Claude Code

  7. hotlong commented on Sep 8, 2026

    @hotlong
    Contributor

    ACCEPT — flight B, PR #16743 at head 1c5673488 after one patch round (skills seat, hotlong, session session_01P58euzUXCVJNwmhuPC9DXY, contract-tier review in seat, 2026-09-08T03:52Z; reports 5578512590 and 5578745311 read; the diff, the ruling 5564758585 and the check runs read on GitHub rather than the reports' narrative).

    Checklist: draft ✓, base main ✓, first line Fixes #16149 ✓; changed files 1 = AGENTS.md, the claim's declared surface (the merge commit brings only origin/main's own change) ✓; skip-changeset ✓ (nothing publishes AGENTS.md); ratchet 1068 → 1067 — a cap, so the net decrease is legal — and the per-line 120-byte budget met after the dev's reflow ✓; fold payment verified by the seat on origin/main 7c12e475: all seven ADR-0087 disposition categories live in scripts/check-adr-0087-registration.mjs's FIXIT output, and nothing under scripts/ asserts AGENTS.md carries the marker block (control: 5 adr-0087 mentions remain in AGENTS.md) — the cut copy listed 4 of 7 and was a misleading duplicate, not a rule ✓; the paragraph carries the ruling's two structural costs, the trunk-branch route with both recorded workarounds, and the no-gate/no-policy boundary ✓; item 3 removed in the patch round — the ruling grades it "a defect independent of stacking", split out to #16482, so writing it as an inherent cost would go false when that card lands ✓; 14/14 derived gates exit 0 at 1c5673488, --ran reconciled, governed predicate exit 3 ✓.

    Declared deviation from the ruling's letter, accepted: "pointing at this card" is mechanically unavailable in AGENTS.md (check-skill-id-lint's EXTRA_FILES, pattern /#[0-9]{3,}/), so the pointer lives in the PR body (Fixes #16149) and the paragraph is self-contained — that gate's own prescription. Seat erratum on record: the dispatch named check-governed-queue-guard.mjs --test where the predicate is check-governed-merges.mjs --test; the dev ran the right one. The dev's body-edit reading (an issue_write body update on a PR appended no footer; attribution rides the commit trailers) is carried to the readings flight as a candidate, not written here.

    CI on 1c5673488 at 03:44Z: Lint & Repo Gates and Type Check · workspace in progress, the rest green or skipped by filter. Terminal: governed (AGENTS.md) ⇒ the PR stays draft; needs-user-decision + the seat's 速读终稿 go on the PR; review requested from os-zhuang, PR assigned to hotlong (author-identity fallback). #16633 (same file) is dispatched once this is on main. This card stays pm:dispatched with its assignee until the merge closes it.


    Generated by Claude Code

  8. removed their assignment
    on Sep 8, 2026
  9. hotlong commented on Sep 8, 2026

    @hotlong
    Contributor

    Landing record — skills seat (hotlong, session session_01P58euzUXCVJNwmhuPC9DXY), 2026-09-08T04:28Z: PR #16743 MERGED through the merge queue at 2026-09-08T04:26Z under os-zhuang's APPROVED review (5137116866 on head 1c5673488; the seat armed auto-merge 03:58Z after its own revert error, recorded on the PR). ACCEPT 5578838078. pm:dispatched and the assignee are cleared here in one write; labels left: enhancement · tooling · domain:skills · priority:p3. Read back after the write. The same-file follow-up #16633 (the PR-body footer sentence) is released by this landing and is claimed next.


    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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions