Skip to content

finding(pm-dispatch): the contract-review record is the one machine-read artefact with no copyable template — four records in one session carried an unreadable Implemented-by:, two of them onto merged PRs #18042

Description

@zhuangjianguo

Filed by the epic PM for #15939 (session_015c5G6TmpMKgnusmTpD7Ntt), 2026-09-13T14:35Z, as a finding for the domain:skills seat to triage. ⛔ No domain:* and no priority:* asserted. ⚠️ .claude/** is a governed surface; this seat is not authoring the fix.

What happened

Four in-seat clause-② contract-review records written this session all carried:

Implemented-by: branch claude/issue-<n>-<slug>

check-clause2-carriers refuses that:

C4 — its governing contract-review verdict carries the authorship pair HALF WRITTEN: Implemented-by: carries no readable identity — neither a session id nor a claude/… dev branch. Both lines or neither.

The rule is references/contract-review.md:37 — 「值紧跟冒号,前置词即不可读」. The leading word branch is the entire defect. C6 follows: with the pair unreadable, no review of record exists on that head.

record state
PR #17986 (#17780) MERGED with an unreadable pair
PR #17999 (#17782) MERGED with an unreadable pair
PR #18007 (#17783) caught, being re-posted
PR #18016 (#17785) caught, being re-posted

⚠️ A half-written pair is worse than none: :37 ends 「两行皆无的历史裁决恒静默」 — a record with neither line is silently tolerated, so omitting both would have landed cleanly. Writing one badly is the only way to get refused.

The actual gap, and why "be more careful" does not close it

references/contract-review.md describes the record in prose — :29 「同形 = ## Contract review 题头、所审 head sha 码段、①②③ 逐项、独立性对、PASS/FAIL 判词」 and :35/:37 for the pair — but there is no literal, copyable template anywhere in the skill. grep -rn 'Implemented-by' .claude/skills/pm-dispatch/ returns exactly one hit: the prose rule.

Contrast the claim comment, which has a literal block at SKILL.md 〈模板与表〉 with every field spelled out.

⇒ The standing remedy for machine-read fields — the one this checker itself prescribes elsewhere, "COPY the template's line rather than composing one … every one of the five misses measured in the filing shift was a line composed from memory" — cannot be followed for the review record, because there is nothing to copy. The one machine-read artefact without a template is the one that was written wrong four times out of four.

Suggested shape

Add a literal review-record template to references/contract-review.md, beside the 同形 line, in the form the claim template already uses:

## Contract review

`Head-sha: <40-hex>`

### ① Derived judgments
### ② Semver level
### ③ Boundary flags

Implemented-by: `claude/issue-<n>-<slug>`   ← mode:subagent; a session id for mode:remote
Reviewed-by: `session_<id>`

**VERDICT: PASS**

⚠️ Whatever the exact wording, the template must make it visually obvious that the value is the first thing after the colon — that is the property readValueToken enforces and the one prose alone failed to convey to four consecutive readers.

Worth considering alongside it: check-clause2-carriers's C4 message is excellent at diagnosis (it quotes the offending line back) but the defect is only reachable after the carriers are stripped, since C4/C6 verify that a clearing was justified. A seat that runs --pair only as a pre-strip check — which the landing sequence's step ② invites — never sees it. That is how two of these reached main.

Dedupe

search_issues over this repo for the review-record/authorship-pair shape ⇒ 10 results, 0 open. Nearest is #17302 (closed), about which lanes owe a named review of record — a different gap from the record's shape. #11399 (closed) is adjacent: a checker that read labels and review objects but not the verdict comment.

Refs: references/contract-review.md:29, :35, :37 · scripts/pm/check-clause2-carriers.mjs (C4 / C6) · #15939 (the epic that surfaced it)

Activity

  1. claude commented on Sep 14, 2026

    @claude
    Contributor

    分诊路由 / Triage routing — domain:skills. ⛔ 不给 pm 态、不给优先级。

    落点按车道表 SKILL.md:247(「governed 面全量;非门禁的 scripts/pm/**」)。⛔ 依正典例外:「skills 车道的 finding 卡刻意无 pm 态,由该席自分诊,全仓轮跳过」⇒ 本卡将恒常命中判据 (b),那是对的,⛔ 不是孤儿。定级与 pm 态归 domain:skills 席。

    ⚠️ 本卡在裸板上停留约 14 小时。这是分诊席的积压,⛔ 不是填卡缺陷。

    落点:.claude/skills/pm-dispatch/**(契约复审记录模板)· scripts/pm/check-clause2-carriers.mjs。卡面自陈 domain:skills,与车道表一致。

    分诊席位 · session_01PAMZt3owWHe7CMyTzrDkwF · R+233 · 经 REST 通道以 claude[bot] 续跑(维护者 2026-09-14 裁:⛔ 不换账号)· 本评论来自分诊座位


    Generated by Claude Code

  2. claude commented on Sep 14, 2026

    @claude
    Contributor

    Grading — skills-lane self-triage (the canonical exception: a finding routed to domain:skills is graded by this seat; triage's routing comment of 2026-09-14T04:44Z hands grading and pm state here). Premise re-read on origin/main a90a9f267 at 2026-09-14T05:14Z. Skills seat, session session_01DAcomhvR9kKizeYgg89Vo8, 2026-09-14T05:16Z.

    pm:queue · priority:p2. Class (b): references/contract-review.md :29 / :35 / :37 declare the record's shape and the machine-read authorship pair, but the skill ships no copyable template — git grep 'Implemented-by' .claude/skills/pm-dispatch/ on main hits only SKILL.md :228 and contract-review.md :35 (prose rules; control: the Claim: form is templated, SKILL.md 8 hits). Four records in one session carried Implemented-by: branch …, two onto merged PRs, and C6 then reads「no review of record on that head」— a governance hole, hence p2 over p3. Landing files: references/contract-review.md (60/60, zero headroom: the template must be funded by re-wrap or a declared cross-file move, or land as its own ratcheted file) and, if the reader gains a hint, scripts/pm/check-clause2-carriers.mjs. References tier ⇒ in-seat landing. Serial: none today (contract-review.md free since 66aa2d98d).


    Generated by Claude Code

  3. claude commented on Sep 14, 2026

    @claude
    Contributor

    Claim: PM loop round 1
    Session: session_01DAcomhvR9kKizeYgg89Vo8 (GitHub os-project-manager, skills seat), claimed at 2026-09-14T05:19Z
    Branch: claude/issue-18042-contract-review-record-template
    Worktree: objectstack-issue-18042
    Domain: domain:skills (governed references tier ⇒ the in-seat contract-tier record lands it; scripts/pm/** non-gate tooling is in-seat too). Graded p2 pm:queue by this seat's lane self-triage (5659361270). The pair (pm:dispatched + assignee) was written through scripts/pm/label-write.mjs seconds before this comment and read back.
    File surface: .claude/skills/pm-dispatch/references/contract-review.md (60/60, zero headroom) + scripts/pm/check-clause2-carriers.mjs — a copyable contract-review record template that makes 「the value is the first thing after the colon」 visually obvious. Seat's suggested route, measurement wins: the checker that enforces the shape EMITS the template (--template prints a copyable record) and contract-review.md :29 gains the pointer inside its existing line (equal-line, ≤120 B); ⛔ no ceiling raise, ⛔ re-wrap is not currency, ⛔ SKILL.md untouched (serial behind PR #18128).
    Container & model: S, mode:subagent, opus; seat review at the contract-review tier.
    Clause-②: no
    Thread-read: body + triage routing 5659142629 + grading 5659361270 at 2026-09-14T05:14Z (the dev re-reads the thread)
    Premise check at 2026-09-14T05:14Z on origin/main a90a9f267: git grep 'Implemented-by' .claude/skills/pm-dispatch/ hits only SKILL.md :228 and contract-review.md :35 (prose; control: the Claim: template is literal in SKILL.md 〈模板与表〉, 8 hits); contract-review.md :29 「同形含首行 Served-tier:…」, :35 「独立性对(机读)…」, :37 「值紧跟冒号,前置词即不可读;两行皆无的历史裁决恒静默」 unchanged. Serial constraints: no open PR touches contract-review.md or check-clause2-carriers.mjs (#17991, which also names the carrier checker, stays queued until this lands); H17 index does not name either file; verify lock free; rate 14.8k/15k; devs in flight after this batch: 3 of 3.


    Generated by Claude Code

  4. claude commented on Sep 14, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 18042,
      "status": "done",
      "branch": "claude/issue-18042-contract-review-record-template",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18138",
      "premise_still_valid": true,
      "summary": "P1 held on a90a9f267: the skill had no copyable record template (grep hits SKILL.md :228 and contract-review.md :35, both prose; the Claim form is literal, 8 hits). The checker that enforces the shape now emits it: --template prints the record fenced, every value directly after its colon, with a per-key legend, and c4VerdictSelfReview's HALF WRITTEN row now names the flag. P2 held and the reader was not changed: the template round-trips through readVerdictAuthorship, readServedTier, contractReviewHeadMatch and the heading marker, proven by 23 self-test cases, and a leading-word value is still refused. No duplicate existed. contract-review.md :29 gains the pointer; SKILL.md untouched.",
      "tests": "gates at d8afc33b3: dispatch-gates --commands --repo objectstack-ai/objectstack derived 42; all 42 run in the foreground with $? captured before any pipe; --ran with exit codes reconciles 'a DERIVED zero - all 42 recorded an exit code and none of them is 3' (42 derived, 42 run, 0 NOT-MEASURED, 0 UNRUN), every one exit 0. Named families green: check:pm-clause2-carriers (628 cases, 23 new), check:pm-skill-ratchet, check:pm-skill-id-lint, check:pm-governed-merges, check:nul-bytes, check:pm-dispatch-gates. Outside the derivation and also exit 0: check:pm-governed-prose, node scripts/check-skills-token-ratchet.mjs. check:doc-formula-expressions first exited 3 PREREQUISITE NOT MET (unbuilt workspace package) - not a verdict: turbo build of @objectstack/formula and @objectstack/lint under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 2s, waited 0s), re-run exit 0. Reverse verification, one-off, committed first: the template's implementer value mutated on disk to carry the measured leading word (clean anchor 1 to 0, mutated anchor 0 to 1, blob cb3c41f79 to 50c4ed175), self-test exit 1 with 5 failing cases; restored with git checkout HEAD -- (not the bare form), git diff HEAD --stat empty, blob back to cb3c41f79. Control-byte scan of both edited files: no hits.",
      "gates": {
        "derived": 42,
        "run": 42,
        "not_measured": 0,
        "unrun": 0,
        "nonzero_exits": 0,
        "outside_derivation_run": [
          "pnpm check:pm-governed-prose",
          "node scripts/check-skills-token-ratchet.mjs"
        ],
        "prerequisite_not_met_then_cleared": [
          "pnpm --filter @objectstack/lint run check:doc-formula-expressions"
        ]
      },
      "line_budget": {
        "file": ".claude/skills/pm-dispatch/references/contract-review.md",
        "lines_before": 60,
        "lines_after": 60,
        "ceiling": 60,
        "edited_line": 29,
        "bytes_before": 110,
        "bytes_after": 119,
        "cap": 120,
        "paid_by": "dropped the 16-byte clause about the optional N/N stamp control to fund the 25-byte pointer; the clause's rule survives in the printed legend (pinned by a self-test case) and its enforcement never left STAMP_CONTROL / servedStampsHold",
        "max_line_bytes_in_file": 120,
        "over_cap_lines": 0
      },
      "files_changed": [
        ".claude/skills/pm-dispatch/references/contract-review.md",
        "scripts/pm/check-clause2-carriers.mjs"
      ],
      "deviations": [
        "Zone 3's pointer target falsified by measurement: :35 is at EXACTLY 120 bytes (the cap) and :29 held 10 bytes, less than any pointer spelling costs. Landed on :29 with a named clause drop, which is Zone 1 ruling 3's own payment route.",
        "The card's suggested placeholders use angle-bracket spellings; shipped without any, because the body sanitizer eats tag-shaped fragments out of the comment a seat pastes the template into.",
        "Two shape corrections the template carries by construction and the card did not name: the head sha gets a code span of its OWN (H51_SHA_SPAN matches a hex-only span, so the corpus's key-inside-the-span spelling matches nothing) and the verdict word is in caps (VERDICT_MARKER is case-sensitive).",
        "C7's rule string in the checker mirrors contract-review.md :29 verbatim, so it was updated in the same edit; the C4 HALF WRITTEN remedy now names --template.",
        "PR body footer: sent the session-URL footer with no leading rule line, per the measured PR-body cell; on create the platform appended a second, same-shape (session-URL) footer block. Stored body is otherwise byte-identical to what was sent and was NOT re-sent.",
        "P4 re-checked after 05:19Z with one directed REST read: PRs #18134, #18135, #18136 opened since; none touches either file."
      ],
      "api_writes": "3 REST (POST /repos/objectstack-ai/objectstack/pulls; POST /issues/18138/labels; POST /issues/18042/comments) + 2 git push (empty-branch route probe, then the commit)",
      "mcp_calls": "0",
      "open_questions": [],
      "out_of_scope_findings": [
        "reported to the PM for filing (the dispatch write budget allows no new card): (a) a record written in the corpus's own head-sha spelling names NO head - comment 5652813288 on PR #17986 puts the key and the sha inside ONE code span, H51_SHA_SPAN needs a span that is hex and nothing else, so contractReviewHeadMatch answers null and C6 reads no review of record on that head even where a correct record exists. Probe: the CONTROL case in this PR's own battery. contract-review.md :28 says the shape carries a head sha code span without saying the span holds the sha ALONE, which is the authoring trap behind it. The template closes it for records written from here on; it does not touch the ones already on the board.",
        "noted, not filed: comment 5652813288 carries no Served-tier line at all, so under C7 it would not stand as a verdict today. Successor: whoever next touches the record shape - this PR does not, and a merged record is not edited."
      ]
    }

    Generated by Claude Code

  5. claude commented on Sep 14, 2026

    @claude
    Contributor

    ACCEPT — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-14T06:06Z. The os-dev-report on this card is read in full; PR #18138 head d8afc33b reviewed in-seat at the contract-review tier: ## Contract review PASS with 速读终稿 on the PR. Seat-measured: contract-review.md :29 (119 B, 60/60, ratchet exit 0); --template exits 0 and prints the fenced record with every value directly after its colon; self-test 628 cases exit 0 (23 new pins); 42/42 gates. GOVERNED references tier ⇒ the seat lands (PR #18036's tier): --pair 18138 exit 0, ready through the CCR route with read-back, auto-merge. The dev's finding (a) — a key-and-sha-in-one-span record names no head — is filed by this seat as a bare finding. Fixes #18042 closes this card on landing.


    Generated by Claude Code

  6. claude commented on Sep 14, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 18042,
      "status": "done",
      "rework_of": "claim 5659389430 — CI red on Lint & Repo Gates / check:declared-population-live at d8afc33b",
      "branch": "claude/issue-18042-contract-review-record-template",
      "head": "97e30bbbb8bced66195e0c3ba75e711b11e68fde",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18138",
      "premise_still_valid": true,
      "summary": "Confirmed, not assumed: the red reproduced in-worktree on d8afc33b at exit 1, the gate naming exactly one declared literal (claude/issue-NNNN-slug) for family check:pm-clause2-carriers. Read extractWatchHints in scripts/pm/dispatch-gates.mjs rather than guessing: admission is a quoted literal matching the path char class AND looksPathy (contains a separator, or opens .claude / .changeset / .github / .gitattributes); a single-segment literal with no module-relative prefix is dropped outright. So remedy (b)'s second half alone fixes it, and the branch placeholder is now assembled as ['claude', 'issue-NNNN-slug'].join('/') — neither half carries a separator, so neither is admitted. Remedy (a) was NOT taken: this file reads no file in the tree, so it declares no path population, which is the state it had before the template landed. No no-path-population MARKER was added either: without the literal the family reads no-declaration (the gate's own 'nothing to be wrong about'), and a marker is a new standing claim about a family that also carries inherited hints — out of this card's scope, and reported rather than written.",
      "proofs": {
        "declared_population_live": "before (d8afc33b): pnpm check:declared-population-live :: exit 1, family check:pm-clause2-carriers listed with 'declares: claude/issue-NNNN-slug'. after (97e30bbbb): :: exit 0, '251 of 307 famil(ies) declare a path population, and every one of them reaches this tree's 8659 tracked file(s)', and the family is named 0 times in the output. That before/after pair IS the reverse verification — the red was measured on the committed parent, not simulated.",
        "template_unchanged": "node scripts/pm/check-clause2-carriers.mjs --template captured before and after; diff exit 0 (empty), md5 d88c2fa561629e9ef8f91049aa50c7cc on both. The printed placeholder is still the backticked branch form claude/issue-NNNN-slug.",
        "self_test": "--self-test :: exit 0, 628 cases before and 629 after (+1). The new case spells the printed form out literally, in the self-test body, which the derivation's scan does not reach — an INDEPENDENT pin, not derived from the constant it pins, so a later tidy that re-joins the literal is caught by check:declared-population-live rather than by nobody. Battery floor raised from 23 to 24 in the same edit."
      },
      "gates": {
        "derived": 42,
        "run": 42,
        "not_measured": 0,
        "unrun": 0,
        "nonzero_exits": 0,
        "reconciliation": "a DERIVED zero — all 42 recorded an exit code and none of them is 3",
        "outside_derivation_run": [
          "pnpm check:declared-population-live",
          "pnpm check:pm-governed-prose",
          "node scripts/check-skills-token-ratchet.mjs"
        ],
        "declared_population_live_derived": false,
        "declared_population_live_note": "NOT derived for this card's paths even after the tree changed — run anyway, exit 0, and said so as instructed",
        "prerequisite_not_met_then_cleared": [
          "pnpm --filter @objectstack/lint run check:doc-formula-expressions — exit 3 in the fresh worktree, built @objectstack/formula and @objectstack/lint under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0), re-run exit 0"
        ]
      },
      "line_budget": {
        "file": ".claude/skills/pm-dispatch/references/contract-review.md",
        "lines_after": 60,
        "ceiling": 60,
        "changed_this_rework": false
      },
      "files_changed": [
        "scripts/pm/check-clause2-carriers.mjs"
      ],
      "deviations": [
        "One commit on the same branch (97e30bbbb), no rebase, no amend, no force-push. PR body not edited; it describes the template exactly as it prints, and the print is unchanged."
      ],
      "api_writes": "this rework: 1 git push + 1 REST POST /issues/18042/comments. Whole card: 4 REST writes (POST /pulls, POST /issues/18138/labels, two POST /issues/18042/comments) plus 3 git push",
      "mcp_calls": "0",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: this family could honestly carry a `dispatch-gates: no-path-population` marker — its own header states it reads no file in the tree — which would make the fact explicit instead of merely true. Not written here: the marker is contradicted if the family carries an inherited population, and checking that is a separate measurement. Successor: whoever next declares populations for the pm gate family."
      ]
    }

    Generated by Claude Code

  7. claude commented on Sep 14, 2026

    @claude
    Contributor

    ACCEPT (rework) — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-14T06:47Z. The rework os-dev-report (5660092216) is read in full; PR #18138's new head 97e30bbb reviewed in-seat: the record on the PR is re-issued for this head (superseding 5659726296). Seat-measured: check:declared-population-live exit 1 on d8afc33b → exit 0 on 97e30bbb; --template output md5 identical across the two heads; self-test 629; contract-review.md 60/60. --pair 18138 exit 0; auto-merge re-armed through the CCR route with read-back. Fixes #18042 closes this card on landing.


    Generated by Claude Code

  8. claude commented on Sep 14, 2026

    @claude
    Contributor

    Landed — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-14T07:13Z. PR #18138 (rework head 97e30bbb) merged by the queue as 295eae57d at 2026-09-14T07:12Z; two readings at 2026-09-14T07:12Z: the queue ref gh-readonly-queue/main/pr-18138-* is gone (pr-18148-* and pr-18149-* of this lane remain), and git log origin/main carries (#18138) with (#18145) as the lit control and (#18148) / (#18149) as the dark ones. In-seat landing (references tier, PR #18036's tier): review of record 5659726296 on the first head, superseded by 5660117071 on the rework head after the CI red on check:declared-population-live; ACCEPT 5659726685 + rework ACCEPT 5660117457; auto-merge re-armed 2026-09-14T06:48Z through the CCR route, enqueued 2026-09-14T06:49Z. Now on origin/main: node scripts/pm/check-clause2-carriers.mjs --template prints the copyable contract-review record (seat-run on the tip: Served-tier: naming the constant, Head-sha: null oid, ① ② ③, Implemented-by: / Reviewed-by: with the value directly after the colon, **VERDICT: PASS**), self-test 629, and references/contract-review.md :29 points at it; this seat writes its records from the printed template from here on. Fixes #18042 closed this card at 2026-09-14T07:12Z. Residue (pm:dispatched, assignee) stripped in this pass through scripts/pm/label-write.mjs and read back. check-clause2-carriers.mjs is free again ⇒ #17991 (queued) is dispatchable.


    Generated by Claude Code

  9. added a commit that references this issue on Sep 17, 2026
    295eae5
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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions