Skip to content

Fix #90 and let agents report skill problems to this repository #96

Closed

Description

@DocksDocks

Goal

Fix the plan-lifecycle defect in #90: a reopen after a partial-merge close can race GitHub's own close and leave the plan issue closed with reason REOPENED, a state that GitHub refuses to change. Give agents that use a skill from this repository a documented way to report a problem with that skill as a GitHub issue here, with a template that explains the problem well and keeps private data out of the public issue. Close the open items from the advisor check of #89 and #95.

Scope: the plan-lifecycle helper, contract, and skills; the shipped docks and plan-lifecycle skills; the test-contract registry; and releases of the changed plugins after the merge, each with its own question.

Yolo: the user delegated every decision except the merge to the agent.

Mode: plan-and-implement

Research

Open issues

How the reopen works today

How agents would report a skill problem

Advisor follow-ups from #89 and #95

  • scripts/config/test-contracts.json lists "toolchains": ["node-24"] for all 17 contracts. Since Run the repository's author tooling on Bun 1.4.4 #88, 16 of them run on Bun (bun()/bunOk() in scripts/ci.mjs, bun test for the unit files). Only scripts/tests/plan-cli.mjs runs on Node, and its fake gh runs on Bun. scripts/tests/test-contracts.mjs only checks that the field is a sorted string array, so nothing caught the drift.
  • Corrections to the Cut the gate to 7 s and a gh round trip from each plan write #95 run summary: the remote branch 89-optimize-the-gate-and-the-plan-helper-after-the-bun-switch stays on GitHub (the repository does not delete merged branches; delete_branch_on_merge=false); status writes now also run the post-write label check, and a status write now creates a missing phase label where it used to fail inside gh issue edit; plugin users get the faster helper only with a plan-lifecycle release, which steps 5 and 6 of this plan schedule.
  • No official GitHub source states a 100-label cap per issue (web search), so Cut the gate to 7 s and a gh round trip from each plan write #95's fix that takes removal ids from the paged snapshot stays as merged.

Decisions

  1. Add reopen <n> --pr <pr> to plan.mjs and make it the contract's only reopen path after a partial merge. It reads the pull request (merged, mergedAt, closingIssuesReferences) and the plan for write.
    • When the pull request is not merged, it refuses.
    • When the pull request does not close the plan, it prints that nothing needs a reopen, exits 0, and writes nothing, so the manager can run it after every partial merge.
    • Otherwise it polls until it observes both facts: the plan reads CLOSED, and its latest ClosedEvent comes from that pull request (or from a commit associated with it) and is not older than mergedAt. The pause grows from 250 ms and doubles up to 2 s, for at most 34 reads (about 60 s); the bound counts reads, not wall time.
    • A plan that reads OPEN with such a close event was reopened after that close: the command prints plan #<n> is open: <status> and writes nothing. A close by another closer after the merge is refused.
    • After both observations it waits a full 10 s on its own clock, the wait that avoided the state in plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90. It then reads ownership, state, and the close event again, right before the write. It writes only when the plan still reads CLOSED with reason COMPLETED and the same close event; a plan that now reads OPEN prints the open status, and a foreign owner is refused.
    • The reopen is one gh api graphql request: reopenIssue, addComment with the contract's comment, and, for an unassigned plan, addAssigneesToAssignable with the acting login, so the claim lands in the same write as the contract requires (Status, ownership, and frozen state). The last field returns the stored state and reason, and the command prints plan #<n> reopened: <status>.
    • A plan that reads CLOSED with reason REOPENED is refused with the stuck-state message: before the reopen, after it, and when GitHub answers the reopen with an error (plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90: "Could not reopen the issue").
    • The waits block the process with Atomics.wait, which Node allows on the main thread (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait). The plan-CLI cases load a test-only --import hook that makes those waits return at once, as scripts/tests/unit/agent-score-vacuity.test.mjs loads its fixture hook, so the shipped helper carries no test switch, and the read bound makes the timeout case fast.
    • Rejected: contract prose only (plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90 suggestion 1), because an agent would hand-roll the polling each time and a missed wait is permanent. Rejected: a fixed sleep after the merge, because it does not prove that the close happened. Rejected: a settle time computed from the event's createdAt, because the local clock can drift from GitHub's and shrink the wait to nothing. Rejected: gh issue reopen --comment, because it cannot claim an unassigned plan in the same write.
  2. show prints advice for a plan that GitHub left CLOSED with reason REOPENED: GitHub left this issue closed with reason REOPENED and refuses every state change; ask GitHub Support to repair it, and record step results in an issue comment until then (plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90 suggestion 2). The derived status stays unreadable, so the contract's status table does not change.
  3. The contract documents the fallback (plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90 suggestion 3): do not retry the reopen; record the remaining step results in one issue comment; report the plan as stuck; ask the repository owner to contact GitHub Support. The contract's command table, advice list, and refusal list gain the new command, advice, and refusals. plan-manager/SKILL.md names reopen where it now names a hand reopen.
  4. Add the docks skill report-skill-issue (productivity/, user-invocable: true). It confirms that the problem is in the skill, collects the plugin version and the skill's metadata.updated and content_hash, searches for a duplicate, drafts the title <plugin>:<skill>: <symptom> and a fixed-section body, gets the user's approval of the exact text, and files it with gh issue create --repo DocksDocks/docks --body-file. Without gh, it gives the prefilled URL.
    • Its constraints: the repository is public; file only after approval, which ends the turn in a headless run; leave out secrets (name the variable), private repository, organization, and customer names, hostnames, machine paths, proprietary code, and private plan titles. A security problem is never filed publicly; the user gets the draft to send to the repository owner privately.
    • The template follows plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90's structure: Summary, Skill, What the skill says, What happened, Expected, How to reproduce, Workaround, Suggested change.
    • Rejected: a .github/ISSUE_TEMPLATE form, because agents file through gh issue create, and a second copy of the template would drift.
  5. Every shipped skill of both plugins ends with one byte-identical ## Report a problem with this skill section that points to report-skill-issue. For a runtime without docks, the section says to give the user a draft only and to file nothing, because the publication safeguards (public destination, redaction, security reports, exact-text approval) live in the skill. plugins/docks/skills/AGENTS.md owns the rule and its verify command, and plugins/plan-lifecycle/skills/AGENTS.md points to it.
    • Rejected: description text, because it costs listing budget in every session. Rejected: a SessionStart hook, because it injects context into every session. Rejected: a new validator, because the repository checks its other byte-identical paragraph with a grep. Rejected: the reviewer agent wrappers and the project-local .agents/skills, because the wrappers load the canonical skill and project-local skills are fixed in place.
  6. Correct the registry: each contract lists the toolchain of the runner that the gate uses for it. That is bun-1 for the 16 Bun suites and bun-1 plus node-24 for repo-plan-cli, whose fake gh runs on Bun. scripts/tests/test-contracts.mjs derives the expected token from how scripts/ci.mjs runs each suite (node()/nodeOk() give node-<major> from the node-version in .github/workflows/ci.yml; bun()/bunOk() and bun test give bun-<major> from packageManager). It fails when a contract omits its runner's token, so a later runtime change cannot leave the registry stale. Rejected: renaming the selection kinds node-script and node-test-file, because they name the selection shape and a rename changes no behavior.
  7. Release both plugins after the merge, each with its own question: docks 0.21.0 (new skill) and plan-lifecycle 0.16.0 (new command), as minor bumps under 0.x semantic versioning. A plan-lifecycle release also ships the Optimize the gate and the plan helper after the Bun switch #89 helper changes. plugins/plan-lifecycle/compatibility.json stays at minimum_docks_major: 0, because the plan-lifecycle pointer works without docks.

Request corrections

Asked: "check open issues as we have some new issues that came". Did: fix #90, the only open issue, and explain #91 to #94 as #89's closed probe issues. Reason: gh issue list --state open lists only #90.

Engineering standards

  • Step 1 reopen_spec: tdd-workflow (spec tests before the helper code; red baseline below), test-coverage principles, code-clarity, commit-discipline.
  • Step 2 reopen_helper: fix-workflow (plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90 is a reported defect; the reproduction is step 1's red baseline), code-clarity, commit-discipline.
  • Step 3 report_skill: write-skill (description rules, trigger collisions, score), code-clarity, commit-discipline.
  • Step 4 registry_toolchains: tdd-workflow (the cross-check is written first and fails on the stale registry), code-clarity, commit-discipline.
  • Before code review round 1: code-review self-review with code-clarity.

Steps

# Id Task Files Depends Effect Status Done when
1 reopen_spec Write the plan-CLI spec cases for reopen and the stuck-state advice: waits for the pull request's close, then reopens with the contract comment and claims an unassigned plan; nothing to reopen when the pull request does not close the plan; refuses an unmerged pull request; refuses a close by another closer after the merge; refuses GitHub's CLOSED/REOPENED state before and after the reopen, and show advises on it; prints the open status when the plan was already reopened; refuses after the bounded reads when GitHub never closes the plan. Add the test-only --import hook that skips the waits. Teach the fake gh the reopenIssue and addComment mutations, a delayed close after a set number of reads, a reopen that GitHub leaves closed, createdAt on close events, and a pullRequest query with closingIssuesReferences scripts/tests/plan-cli/, scripts/tests/fixtures/gh-stub/gh - local done the new cases fail on the current helper because reopen and the advice do not exist (red baseline in Verification Results), and the other plan-CLI cases still pass
2 reopen_helper Implement reopen <n> --pr <pr> and the show advice (decisions 1 and 2) in plan.mjs; update the contract's partial pull request steps, command table, advice list, refusal list, and fallback (decision 3); name reopen in plan-manager/SKILL.md; backfill the plan-lifecycle content hashes plugins/plan-lifecycle/skills/productivity/plan-manager/scripts/plan.mjs, plugins/plan-lifecycle/skills/productivity/plan-manager/references/plan-contract.md, plugins/plan-lifecycle/skills/productivity/plan-manager/SKILL.md 1 local done node scripts/tests/plan-cli.mjs passes with the step 1 spec files unchanged (git diff -- <spec files> prints nothing); bun run test:coverage reports at least 99% line coverage for plan.mjs
3 report_skill Add plugins/docks/skills/productivity/report-skill-issue/SKILL.md (decision 4); end every shipped skill of both plugins with the byte-identical report section (decision 5); add the rule and verify command to plugins/docks/skills/AGENTS.md and a pointer to plugins/plan-lifecycle/skills/AGENTS.md; list the skill in both READMEs; backfill the content hashes of both plugins plugins/docks/skills/, plugins/plan-lifecycle/skills/, README.md, plugins/docks/README.md 2 local done the gaps of a fresh agent without the skill are recorded first (write-skill step 0); a5, a6, a11, and a12 hold; bun scripts/ci.mjs --plugin docks and bun scripts/ci.mjs --plugin plan-lifecycle exit 0
4 registry_toolchains Write the toolchain cross-check in scripts/tests/test-contracts.mjs first, then correct the 17 toolchains values (decision 6) scripts/tests/test-contracts.mjs, scripts/config/test-contracts.json - local done the cross-check fails on the stale registry (red baseline), and after the correction bun scripts/tests/test-contracts.mjs passes
5 release_docks After the merge and only with its own question: run bun scripts/release.mjs --plugin docks minor from a clean main plugins/docks/.claude-plugin/plugin.json, plugins/docks/.codex-plugin/plugin.json, .claude-plugin/marketplace.json 3 release done the docks--v0.21.0 tag CI passes and gh release view docks--v0.21.0 shows the release, or the step is skipped with the user's answer recorded
6 release_plan_lifecycle After the merge and only with its own question: run bun scripts/release.mjs --plugin plan-lifecycle minor from a clean main plugins/plan-lifecycle/.claude-plugin/plugin.json, plugins/plan-lifecycle/.codex-plugin/plugin.json, .claude-plugin/marketplace.json 2, 3, 5 release done the plan-lifecycle--v0.16.0 tag CI passes and gh release view plan-lifecycle--v0.16.0 shows the release, or the step is skipped with the user's answer recorded

Acceptance

ID Command Expected
a1 bun scripts/ci.mjs exits 0 for the repo-wide checks and both plugins (root AGENTS.md: the full gate for repo-wide and multi-plugin changes)
a2 bun scripts/tests/ci-plugin-targeting.mjs exits 0 (the non-unit targeting contract, .github/AGENTS.md)
a3 node scripts/tests/plan-cli.mjs exits 0, including every reopen case
a4 bun run test:coverage runs under Node and reports at least 99% line coverage for plan.mjs
a5 for f in plugins/docks/skills/*/*/SKILL.md plugins/plan-lifecycle/skills/*/*/SKILL.md; do sed -n '/^## Report a problem with this skill$/,$p' "$f" | sha256sum; done | sort | uniq -c one line whose count equals the number of shipped skills (30), and the hash is not that of empty input
a6 node plugins/docks/skills/productivity/write-skill/scripts/skill-guard.mjs score --per-file plugins/docks/skills productivity/report-skill-issue scores at least 14, and no skill falls below its category floor
a7 In a temporary worktree of the branch (the yolo Commits rule for worktree paths), set repo-plan-cli to list only bun-1 in scripts/config/test-contracts.json and run bun scripts/tests/test-contracts.mjs there; then run it in the checkout the worktree run fails naming repo-plan-cli; the checkout run passes
a8 On GitHub, with the branch helper: show 89 (archived; its closure read now includes createdAt), and reopen 94 --pr 95 on the closed probe issue #94, which #95 does not close show succeeds; reopen prints that pull request #95 does not close plan #94 and exits 0; #94 stays closed as not planned with no labels
a9 A dry run of report-skill-issue on #90's facts, without filing the draft has every template section, the title plan-lifecycle:plan-manager: <symptom>, and no private repository name or plan title
a10 pull request checks every check passes
a11 Trigger check from write-skill (Behavioral checks): 9 should-trigger and 9 near-miss queries, a third of each held back; for each query a fresh sonic subagent gets the names and descriptions of every docks and plan-lifecycle skill and names the skill it would load, or none every held-back near-miss routes away from report-skill-issue (the near-misses include the three sibling routes: fix-workflow, skill-maintenance, and write-skill), and the held-back should-trigger queries load it with at most one miss
a12 Baseline check from write-skill: 3 realistic prompts about a wrong instruction in a docks or plan-lifecycle skill, each given to a fresh sonic subagent without the skill and to one with it; assertions written before the "with" outputs are read the "with" runs close the gaps that the "without" runs show (template sections, redaction, a security problem kept private, approval before filing) and break nothing that the "without" runs got right

Do not touch

Open questions

Verification Results

Local runs: Linux x64, Node v24.22.1, Bun 1.4.4. Node test runs set TMPDIR to the review scratch (the workstation limitation recorded in #87).

Step 4 registry_toolchains (commit 6bbf8b30)

  • Red baseline, step registry_toolchains: bun scripts/tests/test-contracts.mjs: 16 failed, 1 passed (the cross-check named the 16 Bun suites, for example repo-author-tooling.toolchains must list bun-1: scripts/ci.mjs runs scripts/tests/author-tooling.mjs with bun; repo-plan-cli passed).
  • After the registry change, with the check unchanged since the red baseline: PASS test_contracts contracts=17 discovered=17 selected=17.

Steps 1 and 2 reopen_spec, reopen_helper (commit 347f0a58)

  • Red baseline, step reopen_spec: node --test scripts/tests/plan-cli/*.test.mjs: 13 failed, 48 passed. Every failure is an assertion on the helper's usage error (reopen is not a command yet); the 48 existing cases pass. The spec files are staged.
  • After the helper change, with the spec files unchanged since the red baseline (git diff on the four files was empty): node scripts/tests/plan-cli.mjs: 61 passed, 0 failed.
  • Beyond the spec, the helper refuses two more cases, and the contract lists both refusals: plan #<n> changed during the settle wait; run reopen again (the read after the 10 s wait shows a different close) and plan issue state differs after reopen (the stored state still reads closed after the write). The pull request query also checks the repository of each closing reference, so an issue with the same number in another repository does not count.

Review fixes to reopen (commits 6a3ef0e8, b7fc4fef)

  • Self-review: a plan closed before its partial pull request merged got GitHub has not closed plan #<n> for pull request #<pr> yet; run reopen again once it has, which loops forever, because GitHub cannot close a closed issue. After the last read, such a plan now gets plan #<n> was closed by <closer> before pull request #<pr> merged; reopen it by hand only if that close was a mistake, and the contract lists it. Red baseline: the new case failed with the old message, and the other 13 reopen cases passed.
  • Code review round 1: writeReopen did not recheck the owner at its pre-write read. It now refuses an assignee other than the acting login there and claims only when that read shows no assignee. The fake gh now lands due scheduled changes on issue view, as its own comment says. Red baseline: the new case exited 0 and wrote, and the other 62 passed. After the fix: 63 passed.

Step 3 report_skill (commits 8542d798, 093c13f0, a55d88f5)

  • Baseline, "without" arm (recorded before the skill existed): three fresh sonic agents had no target repository or gh command, no plugin version or runtime, no quote, no fixed sections, no duplicate search, no approval step, and one would report a security problem publicly. Right already: private names and the secret value left out, rotation advised.
  • a12, "with" arm, round 1: A held every applicable assertion; B skipped the duplicate search (and so missed the open plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90) and named no command or account; C wrote its private draft without the template sections. The skill then gained: every stage in order and no skipped search, also in a text-only run; the private draft uses every section; the pre-filing check lists the search and the target, command, and account; the prefilled link files nothing by itself.
  • a12, round 2 (fresh agents, same prompts). Correction: this round was first recorded as a pass, and that was wrong. B3 ran the search, found plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90, and drafted a comment there, but it quoted the already-fixed plan-manager text from the development checkout under the installed version 0.15.0, so the comment blamed the fix. C3 quoted the security skill's instruction not to output .env and still drafted a defect report. A3 and B3 took the content hash from the checkout, not from the installed copy. The probe setup pointed the agents at the checkout, but the skill did not say which copy to read either. The skill then gained (commit a55d88f5): read the skill, its version, and its hash from the copy that the runtime loaded (or the installed copy, said in the draft), never from a checkout or another copy; a quote that does not say what the user reports, or says the opposite, gets no defect report.
  • a12, round 3 (fresh agents, same prompts; the setup serves installed skills through skill:// and names the checkout as the development copy): A4 read the installed fix-workflow (0.20.0, updated 2026-09-24, hash 655b1d447db2), ran the search, and drafted a new issue with every section, the command, and the prefilled link, with approval first. B4 read the installed plan-manager (0.15.0, updated 2026-10-21, hash 37cfc75fba99), quoted its text ("After a partial merge, reopen the issue as the contract states."), found plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90 through the issue search, and drafted a redacted comment there with only the new fact; it gave the issue link and filed nothing. C4 read the installed security skill (0.20.0, hash 11afe1a4f593), quoted the instruction that contradicts the claim, drafted no defect report, advised rotation first, and pointed to another source. Assertion 3 and the private draft of assertion 7 do not apply to C4, because the quoted skill text refutes the claim; every other applicable assertion holds. Pass.
  • a11 trigger check (one fresh sonic agent per query, given only the 30 names and descriptions): 9 of 9 report requests loaded report-skill-issue, including the 3 held back; 9 of 9 near-misses routed elsewhere, including the 3 held back (skill-maintenance, none, plan-manager) and the sibling routes fix-workflow and write-skill. Pass.
  • Two dry runs encoded the prefilled link by hand (one link body differed from the shown draft, one was empty), so the link section now gives a Node and a Python command (commit 093c13f0). Both commands round-trip a body with backticks, &, $, quotes, %, #, and non-ASCII text. A repeat dry run produced a link whose body equals the shown draft and its body file byte for byte (2178 characters).

Acceptance (at a55d88f5 unless noted)

Activity

  1. self-assigned this
    on Oct 23, 2026
  2. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Plan review round 1

    Plan-review: repair

    • research_gap - Research decision 1; plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90, “Workaround that worked” - The successful workaround waited approximately 10 seconds after observing both CLOSED and the matching close event. Decision 1 requires only the event and measures settling from createdAt, which GitHub documents as event creation time, not stabilization. These are not equivalent safeguards - Require both observations, then a full elapsed settle interval before reopening; distinguish a confirmed subsequent reopen from a close still being observed.

    • research_gap - Research decision 1; plugins/plan-lifecycle/skills/productivity/plan-manager/references/plan-contract.md:138-140; plan.mjs — readPlanIssue, writeIssueIfUnchanged - “No claim, because the reopen writes no body” contradicts the contract: mutations claim unassigned plans, including bodyless writes. The proposed sequence also lacks a fresh ownership check after waiting - Preserve claim-on-mutation behavior and re-read ownership, state, and closure immediately before reopening. Keep genuine no-op outcomes mutation-free.

    • security_risk - Research decisions 4–5; step 3 - The standalone reporting skill prohibits private-data disclosure and public security reports, but the no-docks fallback specifies only a minimum draft and approval rule. That fallback cannot load the missing skill’s safeguards - Make the byte-identical section explicitly carry the public-destination warning, redaction requirements, security-report prohibition, and exact-text approval gate; otherwise restrict that fallback to a private draft without publication.

    • goal_fit - Acceptance a9; plugins/docks/skills/AGENTS.md:41,115 - A single report dry run does not cover the mandatory manual sibling-collision check or baseline check on the smallest targeted model/runtime. The full gate’s mechanical collision audit does not replace these - Include the required three near-miss routing checks and fresh-instance without/with-skill baseline comparison in Acceptance.

    • research_gap - Steps 5–6, Files; scripts/lib/plugin-release.mjs — runGenericPluginRelease - Both release steps declare none, although the command writes and commits the selected plugin’s Claude manifest, Codex manifest, and .claude-plugin/marketplace.json - List those three paths for each release. Retain the existing dependency from step 6 to step 5 for the shared catalog.

    • research_gap - Acceptance a7; scripts/tests/test-contracts.mjs — REPO, readJson, registry initialization - The validator always reads the registry relative to its own script location; the proposed command provides no way to select a standalone scratch registry - Specify running the copied validator from a complete scratch repository containing the altered registry, so the negative check actually exercises the intended input.

  3. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Plan review round 2

    Plan-review: pass

    None.

  4. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Self-review

    Scope: full candidate diff since the merge base with main (e49e144d..6a3ef0e8, 6 commits), reviewed with code-review and code-clarity. Two-axis mode: the spec source is this issue.

    Standards

    MEDIUM · Correctness · plugins/plan-lifecycle/skills/productivity/plan-manager/scripts/plan.mjs — reopenPlan (introduced; fixed in 6a3ef0e8)
    Evidence: for a plan that was closed before its partial pull request merged, reopen read the plan 34 times and then printed GitHub has not closed plan #<n> for pull request #<pr> yet; run reopen again once it has.
    Why it is a problem: GitHub cannot close an issue that is already closed, so that merge never adds a close. An agent that follows the message retries forever.
    Fix: after the last read, a plan that still reads closed gets plan #<n> was closed by <closer> before pull request #<pr> merged; reopen it by hand only if that close was a mistake, and the contract lists the refusal. The helper still polls first, so a short lag between the issue state and its timeline cannot cause a false refusal. Red baseline: the new case failed with the old message, and the other 13 reopen cases passed. After the fix: node scripts/tests/plan-cli.mjs 62 passed, plan.mjs line coverage 99.68%, and bun scripts/ci.mjs --plugin plan-lifecycle passed.

    LOW · Correctness (kept by design) · plugins/plan-lifecycle/skills/productivity/plan-manager/scripts/plan.mjs — writeReopen
    Evidence: reopenIssue, addComment, and addAssigneesToAssignable are root fields of one mutation, and GraphQL runs every root field even when an earlier one fails.
    Why it matters: if GitHub rejects the reopen, the Reopened: ... comment and the claim still land on an issue that stays closed.
    Disposition: kept. Decision 1 and the spec require one request with the contract comment, so that the claim lands in the same write as the reopen. The write runs only after the plan reads closed by the pull request, a 10 s settle wait, and a fresh read, so this path needs a GitHub failure on top. The helper then prints the stuck-state refusal, which names the real state of the issue.

    Spec

    No findings.

    • Step 1: the 13 spec cases came first (red baseline recorded) and stayed byte-identical through step 2.
    • Step 2: the helper follows decision 1 (merged check, closing reference with repository match, polling bound and schedule, close by the pull request or one of its commits, 10 s settle, fresh read, one write with the exact comment and a claim for an unassigned plan, every refusal), decision 2 (show advice, status stays unreadable), and decision 3 (contract fallback). plan-manager/SKILL.md names reopen <n> --pr <pr> in Landing and reporting and in the BAD/GOOD pair.
    • Step 3: the skill covers every point of decision 4. The section is byte-identical in all 30 shipped skills (a5), and both AGENTS.md nodes carry the rule and its pointer. Both READMEs list the skill.
    • Step 4: the cross-check derives the runner from the gate and fails on the stale registry (red baseline), and the registry follows decision 6.
    • Acceptance a1 to a9, a11, and a12 are recorded in Verification Results; a10 waits for the pull request. After 6a3ef0e8 the full gate runs again before the push.
    • Do not touch: compatibility.json, the frozen pre-GitHub archive, other plugins' manifests, and plan-lifecycle: reopen right after a partial merge can leave the issue closed with reason REOPENED #90's text are unchanged.
  5. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Code review round 1

    Code-review: pass

    Standards

    • [MEDIUM] Bug - plugins/plan-lifecycle/skills/productivity/plan-manager/scripts/plan.mjs:1139 - If another agent claims the plan after the final snapshot, preWriteIssue can return that foreign assignee, but writeReopen discards everything except id and proceeds to reopen, comment, and claim using stale ownership. This bypasses the foreign-owner refusal. Carry the acting viewer ID into this function, recheck the returned assignees, and derive the claim from that latest read before constructing the mutation.

    Spec

    None.

  6. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Code review round 2

    Code-review: pass

    Standards

    None.

    Spec

    None.

  7. DocksDocks commented on Oct 23, 2026

    @DocksDocks
    OwnerAuthor

    Code review round 3

    Code-review: pass

    Standards

    None.

    Spec

    None.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions