Skip to content

check-half-states: H45's second half — a pm:epic card whose parent lacks pm:epic — needs a new fetch class (the sub-issues parent read) and a pm:epic population kept out of seen #15702

Closed

Description

@os-steve

Filed by the domain:skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve) from the #15667 dev's measured out-of-scope finding (report 5548948273), per that card's disposition for the half it left out. Self-triaged priority:p3, pm:queue, pure code.

What was measured (2026-09-05, live API)

What this card decides and does

Two questions the sweep's contract must answer before the row exists, then the row:

  1. May the sweep take the sub-issues parent read as a fetch class? (Report-only, one call per pm:epic card, bounded by the pm:epic index size.)
  2. The pm:epic enumeration is a separate population — read it, keep it OUT of seen, and judge only the new row over it, so no existing row's input moves.

If both hold, add the row (H45's second half, same class, same report-only contract) with self-test cases in both directions and a live run over both repos in the PR body. ⛔ No existing row's predicate or wording changes; ⛔ no other new fetch class.

Related: #15667 (H45/H46, PR #15690) · #15666 (the pm:epic-on-children rule this row will police).

Activity

  1. claude commented on Sep 5, 2026

    @claude
    Contributor

    Claim: PM loop round 5 — unblocked when #15667 closed (PR #15690 MERGED 04:26Z); the seat answers the card's two contract questions and dispatches the row
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-15702-half-states-h45-parent-half
    Worktree: objectstack-issue-15702
    Domain: domain:skills
    File surface: scripts/pm/check-half-states.mjs only — the second half of H45: a pm:epic card whose parent's labels lack pm:epic (a child marked reserved under a parent that is not delegated), report-only, state class, over a SEPARATE pm:epic population read from the label index and kept out of seen (no existing row's input moves), using the sub-issues parent read (GET /repos/{o}/{r}/issues/{n}/parent, one call per pm:epic card, a 404 meaning parentless) as a new, declared fetch class; self-test cases in both directions; live run over both repos in the PR body. ⛔ No existing row's predicate or wording changes; ⛔ no other new fetch class (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (dispatch-gates.mjs --tier scripts/pm/check-half-states.mjs: no path-derived mandate, read 02:3xZ for the sibling flight on the same file)
    Clause-②: no
    Serial constraints cleared: PR #15690 (#15667, H45/H46) MERGED 04:26Z — the last change to the file; no open PR touches it (scan 04:4xZ). Not governed ⇒ the seat's contract-tier review, then ready + auto-merge once every check is green. The PR carries Fixes #15702.

    Decision re-read (04:4xZ): the two questions the card poses are the seat's to answer on the sweep's contract, and are answered yes and yes above — the read is bounded by the pm:epic index (5 cards today), report-only, and the population is separate by construction. Triage's p3 stands; no ruling owed.


    Generated by Claude Code

  2. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 15702,
      "status": "done",
      "branch": "claude/issue-15702-half-states-h45-parent-half",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15755",
      "premise_still_valid": true,
      "summary": "Both contract questions answered as the seat ruled, and the row landed under H45 as a second sentence (one row id: every registry key matches /^H\\d+$/, so there are no sub-rows to name). New declared fetch class subIssueParentPath(repo, n) = GET /repos/{owner}/{repo}/issues/{n}/parent, one call per pm:epic carrier, its 404 'No parent issue found' counted as an ANSWER (parentless) and any other failure leaving the carrier UNJUDGED. The pm:epic index is enumerated as its own population, read LAST in sweepInto into a loop variable and never folded into `seen`; SEEN_LABEL_PAGES lifts the seven pages that do feed `seen` out of the loop body so the exclusion is checkable rather than re-typed. Reading pinned in the PR body: a top-level carrier is the delegated node when EITHER free signal says so (it heads a subtree, or its body carries the session_... registration, the machine half of the paired delegation signal in SKILL.md's epic section) - the live index refuses both single-signal readings in both directions, so an AND would report three of the five carriers. H45's first half is byte-identical: same predicate, same sentence, same free population. Report-only exit semantics unchanged.",
      "tests": "SELF-TEST: `node scripts/pm/check-half-states.mjs --self-test` -> verdict line 'check-half-states self-test: 2262 cases pass.' exit 0 (2223 before; +39 written, +1 generated by the shared SUMMARY_CLAUSE_ANCHORS coverage loop). Cases cover every shape ruled, both directions. ABLATION (3 legs, no build/dist sits between mutation and run - the self-test imports the same source path the mutation writes, and that is asserted by the blob-hash comparison rather than assumed; implementation was committed FIRST so HEAD is a real restore reference): (1) the parent-lacks-pm:epic branch made to return null -> RED, 4 of 2262; (2) 'pm:epic' added to SEEN_LABEL_PAGES -> RED, 2 of 2262; (3) the delegation-signal OR made an AND -> RED, 2 of 2262. Each leg proved the mutation ON DISK before reading anything - deleted-text and injected-text occurrence counts anchored on the text itself, plus the file's blob hash differing from `git rev-parse HEAD:scripts/pm/check-half-states.mjs` - and proved the restore the same way afterwards (blob hash back to HEAD's AND `git diff HEAD` empty), with an absolute-path trap on EXIT/INT/TERM. One leg was RE-RUN because its first disk proof was invalid: a multi-line grep -c counts LINES, so that reading was declared void and the leg redone with a single-line anchor. GATES: dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 31 commands from the real changeset (1 path, three-dot vs merge base c2a336ca2); all 31 run, 30 exit 0. The one non-zero is `node scripts/check-partof-closing-keyword.mjs` exit 2, whose own text is 'NOT WIRED - neither PR_BODY nor PR_NUMBER is set ... This is a wiring or usage failure, NOT a verdict' = NOT MEASURED, not a red gate; it was then run with the real PR body in PR_BODY and printed 'check:partof-closing-keyword: this PR carries no Part-of/closing-keyword contradiction.' exit 0. `pnpm lint` (eslint . --no-inline-config, the WHOLE repo, not a narrowed run) through the shared lock: 'os-verify-lock: VERDICT command-exit 0 - held the lock 102s'. No vitest pin suite names this script; its --self-test is its suite. This file declares no battery floor (that mechanism lives in scripts/sync-template-versions.mjs), and the count line is computed. LIVE: five interleaved objectstack sweeps (2 pristine out-of-repo, 2 patched, 1 pristine IN PLACE via a swap-and-restore both proven by blob hash) - 18 of 25 rows identical in all five; every mover (H8, H19, H20, H36, H38, H46) is a live PR/branch/seat row and each moves between two PRISTINE runs too. H43 read NOT MEASURED in the two out-of-repo runs (their own words: the governed register is not beside the file) - a defect in my measurement method that the script itself caught; across the in-place pair it reads 5 = 5. objectui: 20 of 22 identical, both movers board rows. Row result: objectstack 5 of 5 parent reads answered, 0 flagged; objectui 0 of 0, 0 flagged. Cost is exactly +1 listing page and +1 read per carrier, shown by the sweep's own counter (objectstack 17 -> 18 pages, objectui 14 -> 15). CI on the draft PR at 3b29a22f6: all 35 check-runs reported - 24 success, 11 skipped, 0 failures, combined state success; 'Live half-state sweep' is among the successes, so the patched script also ran end-to-end on a runner. All measurements above were taken at 3b29a22f6, the branch tip (origin/main merged in first).",
      "mcp_calls": "1 - one directed search_issues, for the out-of-scope duplicate check after the container's /search REST route answered 403; the same call returned an already-closed same-class card as its live control. Everything else (issue and comment reads, the pm:epic index, the parent reads, label write, PR create, check-run polling) went over repo-scoped REST.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #15761: dispatch-gates offers the bare `node scripts/check-partof-closing-keyword.mjs` as a runnable local gate, but that guard takes PR_BODY/PR_NUMBER through the workflow's env: block rather than argv, so the tool's existing 'takes a VALUE FROM THE WORKFLOW / NOT MEASURED' bucket - which correctly caught four argv-carried families on the same run - never sees it, and the command can only ever exit 2 outside CI. Same class as the already-closed #14004 (check-governed-queue-guard), which did not generalise to the env-carried variant. Labelled finding/tooling/domain:devx, no assignee, ungraded."
      ]
    }

    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions