Skip to content

[finding] check-clause2-carriers.mjs --pair answers C3 / exit 4 on a LEGITIMATELY cleared clause-② pair — the completed state (declaration outlives the label) is indistinguishable from the fail-open it hunts #14155

Closed

Description

@os-support-ai

Filed by the director seat during the landing window of PR #13864 / card #13657. ⛔ Recording only; no severity asserted; the tool behaved as written and is report-only.

The measurement

Sequence on one pair, 2026-09-01:

  1. In-seat contract review at tier ruled PASS at head 9af92aa3 (PR Refuse an undeclared field a before-hook writes — the post-hook half of the declared-field door, one envelope on every driver #13864 comment 5491487878), and needs:contract-review was removed from both carriers in the same stroke, with read-modify-write and a clean diff on read-back — the legitimate clear, exactly per the carrier discipline.
  2. --pair 13864 immediately before the clear: ✓ "the clause-② declaration is readable in the fixed spelling and both carriers agree" (exit 0).
  3. --pair 13864 immediately after the clear: ✗ C3 — "card The undeclared-field door sits in FRONT of the hooks, so a key a beforeInsert hook writes has no door at all — and the drivers then disagree (memory stores it, SQL throws a raw statement error) #13657 declares Clause-②: yes while NEITHER it nor its delivering open PR carries needs:contract-review" (exit 4).

Why this is structural, not a one-off

A Clause-②: yes declaration is history — it stays on the thread forever. The label is state — a completed review clears it, on both carriers, by rule. So every clause-② pair that completes its review lands in exactly C3's trigger shape: declared yes, label on neither carrier. The protocol itself already names this completed state legible to a human — "PASS + 无标 + head 未动 = 已清标非被剥" (references/contract-review.md) — but C3 does not read verdict comments, so the tool cannot tell the completed state from the never-gated one it hunts.

Two consequences, either of which is worth fixing:

  • The in-seat landing check names --pair as its machine reading with "4 不一致 ⛔ 不作干净". Read literally, no clause-② pair can ever pass the landing check after its own legitimate clear — the check can only be satisfied in the window between review-PASS and label-clear, which is the wrong order.
  • Once a pair lands, its C3 row recurs on every future live sweep, so the sweep's signal degrades as cleared pairs accumulate.

What is NOT claimed

  • ⛔ Not that C3 is wrong to exist — the fail-open it hunts (a content-limb yes nobody gated) is real and was measured on the live board; the miss is only that the cleared state shares its shape.
  • ⛔ No fix prescribed. Candidate directions, costs unweighed: C3 could stand down when a tier verdict comment (PASS, anchored to the current head) exists on the thread; or --pair could answer a distinct state/exit for "cleared with verdict on file"; or the landing check's wording could bound --pair to the pre-clear window. Choosing is the tool owner's call.

Dedup note: the repo search endpoint returned zero hits this session even for terms known to exist in open issues (recorded in the round report), so dedup ran degraded; if this duplicates an existing card, merge freely. Refs: #13922 (the tool's own filing card) · #13914 (the declaration-limb spelling gap, closed) · PR #13864 (where measured).

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 1, 2026
  2. claude commented on Sep 1, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 14155,
      "status": "done",
      "branch": "claude/issue-14155-pair-cleared-state",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14174",
      "premise_still_valid": true,
      "premise_checks": {
        "card_premise": "CONFIRMED live, not recalled. Old tool, real board, this session: `--pair 13864` exited 4 with the C3 row on card #13657 — the card's measurement reproduced exactly. New tool, same pair, same minute: exit 0.",
        "A_no_governed_prose_change": "HOLDS. `references/contract-review.md` (60 lines, at its ratcheted ceiling) quotes only the command and the 0/4/3 register; the register keeps its shape and every quoted meaning stays true — a cleared pair is still 双肢可读且一致 (declaration readable, both carriers agreeing on absence). Exit 2 already existed for --pair and was already un-quoted there, so the UNJUDGED widening needs no prose edit either. Diff is scripts/pm/** only; the .md is untouched and check-skill-line-ratchet passes with headroom 0.",
        "B_label_events_readable": "HOLDS. GET /repos/OWNER/REPO/issues/NUMBER/events returns labeled/unlabeled rows with label.name, actor.login and created_at, for the CARD and the PR alike (a PR is an issue on this endpoint). Measured on the filing pair: card #13657 hung 2026-08-31T16:55:54Z by os-warren, cleared 2026-09-01T08:54:47Z by os-support-ai; PR #13864 hung 15:19:53Z by claude[bot], cleared 08:54:56Z — nine seconds apart, the dual-carrier stroke signature the tool's own DUAL_CARRIER text already names.",
        "C_head_motion_readable": "HOLDS, with a named boundary — the minimal-fix fallback was NOT needed. The head sha is already in the PR row the sweep holds, so one GET /repos/OWNER/REPO/commits/SHA yields commit.committer.date. On the filing pair: head 9af92aa3 dated 2026-09-01T08:16:59Z against a first removal of 08:54:47Z, i.e. unmoved since the clear — matching the card's account that the review PASSed at that head. Boundary named at the reading site: this is commit metadata, not a push timestamp, so a force-push landing a deliberately backdated commit would read as unmoved. The unforgeable alternative (head_ref_force_pushed) exists only on the timeline endpoint, which caps at 250 events and truncates SILENTLY — a capped read backing a CLEAN verdict is the fail-open shape this family refuses, so the exact uncapped reading was preferred with its hole documented. The human PASS conjunct closes it.",
        "D_cost_bounded": "HOLDS. Streams are fetched only for pairs already in C3's candidate shape, head commit only once both carriers read cleared. Live full sweep at final HEAD: 9 pairs from 11 open PRs, 8.15s wall, 0 UNJUDGED, exit 0."
      },
      "summary": "C3 and --pair now read the needs:contract-review label event stream on both carriers, so a completed review is distinguishable from a gate that never ran. Four states: never hung (C3 fires as before, exit 4); hung-then-cleared on both with the head unmoved (the completed state — clean, exit 0); hung-then-cleared with the head moved since (adverse, the re-hang-owed state); bound on one carrier only (adverse — one removal where a legitimate clear leaves two, the strip signature the file's own DUAL_CARRIER text names). An unreadable stream or head commit is UNJUDGED, never clean. No verdict comment is read (the PASS conjunct stays human, per #12409), no spelling is relaxed, and the tool still writes nothing. Two implementation readings worth carrying forward: the events endpoint returns rows OLDEST FIRST, so a short read loses the removal and reads as a live hang or a never-hung gate — the reader pages to exhaustion and hands null otherwise; and the gate-label vocabulary is imported from check-half-states.mjs rather than restated, so this row cannot disagree with H31/H35 about what a gate event is.",
      "files": [
        "scripts/pm/check-clause2-carriers.mjs — +391 / -23 (1 file changed; the only path in the diff)"
      ],
      "tests": "All at final HEAD 260a96e4d, exit codes captured BEFORE any pipe (redirect, then read $?).\n\nSELF-TEST — `node scripts/pm/check-clause2-carriers.mjs --self-test` EXIT=0, verdict line: '✓ check-clause2-carriers self-test: 98 cases pass (fixed-spelling reader, the four declaration states, the 2026-08-31 seven-pair replay, the four gate-binding states replayed from the 2026-09-01 clear, and the exit register).' Grew 64 → 98 cases; the new fixtures replay the measured 2026-09-01 clear verbatim.\n\nLIVE, the card's own measurement — `--pair 13864`: NEW tool EXIT=0, '✓ ... the clause-② declaration is readable in the fixed spelling and both carriers agree.' Full sweep EXIT=0: '9 card/PR pair(s) derived from 11 open PR(s) ... 6 clause-② finding(s), 0 pair(s) UNJUDGED' — all 6 are pre-existing C2 rows; zero C3 rows, where cleared pairs previously accumulated them.\n\nREVERSE VERIFICATION, two legs, each mutate → PROVE on disk → run → restore → PROVE restored. Restore proven both times by `git hash-object` equalling the HEAD blob 73396155432329370ad6ea888b08cd565ffc6dff AND `git diff HEAD` empty; script carried `trap restore EXIT INT TERM` with absolute paths.\n  Leg 1 (old code, live pair): mutation confirmed on disk by counting the new marker (gateBindingState occurrences = 0, expected 0) and an old-only sentence (present). Result EXIT=4 — '✗ C3 — card #13657 declares `Clause-②: yes` while NEITHER it nor its delivering open PR #13864 carries `needs:contract-review` ...'. This IS the card's finding, reproduced.\n  Leg 2 (do the new cases bind?): kept the new tests, broke only the completed-state early return; mutation confirmed on disk by anchor counts (before=1, after=0, injected=1). Self-test EXIT=1, '✗ check-clause2-carriers self-test: 2 of 98 case(s) failed', naming the completed-state cases. Restored tree re-run: EXIT=0, 98 pass — the measurement was retaken on the real tree.\n\nDERIVED GATE UNION — `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no hand-built path list; script derives from merge base 682d03ba7, three-dot). 15 commands; re-derived at final HEAD and byte-identical to the first derivation. Results: 14 PASS(0) — check-ci-filter-parity ('OK: all 129 declared cross-package glob(s) ... are covered'), check-cross-package-test-inputs, check-shard-attestation ('✓ ... 2 aggregate gate(s) count 3 declared leg(s)'), bare-root-worklist --self-test, check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:parse-guard, check:pm-clause2-carriers, check:pm-dispatch-gates, check:pnpm-filter-targets, check:watch-hint-literal. Plus check:nul-bytes PASS(0) (any edit owes it).\n  ⚠ check-test-completeness is NOT MEASURED, not green and not red: EXIT=3 PREREQUISITE NOT MET, and the gate's own text names this exact case — 'Arrived here from the gate family dispatch-gates.mjs derives? ... There is no local log to hand it, so the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix.' It grades a saved `turbo run test` log CI tees. First batch also showed exit 3 on ci-filter-parity and shard-attestation — that was a missing `pnpm install` in the fresh worktree (their own text said 'Nothing was measured ... Fix: pnpm install'); after install both are the PASS(0) above, so no exit-3 was recorded as a failure.\n  Also run because the diff edits a gate script: check-skill-line-ratchet (the only other file naming this tool) EXIT=0.\n\nREPO-WIDE LINT — `pnpm lint` (= `eslint . --no-inline-config`) at final HEAD, EXIT=0, 65s under the shared verify lock. Full repo scan, NOT a narrowed run, so no narrowing evidence is owed.\n\nHeavy runs went through `scripts/pm/os-verify-lock.sh` with a stable slot; lock verdict lines read, never bare $?.",
      "mcp_calls": "0 — every GitHub read and write went through the repo-scoped REST channel (probed green at the start of the run: GET /repos/objectstack-ai/objectstack HTTP 200). No MCP GitHub tool was called; `gh` is absent in this container.",
      "open_questions": [],
      "out_of_scope_findings": [],
      "notes": [
        "skip-changeset applied via the additive endpoint (POST .../issues/14174/labels, HTTP 200) and read back after the labeler window: labels are ['size/m','skip-changeset'] — the label survived the size-labeler's group write. Diff is scripts/pm/** only and publishes nothing from any package, so no changeset file.",
        "PR body read back in full: every line I wrote survived byte-for-byte, table intact, zero angle-bracket fragments, nothing eaten by the sanitizer. The platform then APPENDED a second session-URL footer under a --- separator. That is another actor's write on my PR, so it is reported, not 'corrected' — and editing the body would additionally downgrade the session-URL footer to bare.",
        "PR #14174 is DRAFT and stays draft; nothing was flipped ready, queued, or armed. The diff touches no governed surface (scripts/pm/** is not on the GOVERNED_SURFACES register).",
        "Reported at draft-PR time per protocol; CI convergence is the PM's read, not a wait I performed. No card other than #14155 was folded in.",
        "Worktree /home/user/objectstack-issue-14155 is left in place pending PM review of this report; teardown on request."
      ]
    }

    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

    Labels

    domain:skillsDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions