diff --git a/.agents/skills/babysit/SKILL.md b/.agents/skills/babysit/SKILL.md index 5c8d7b7e..184d97cf 100644 --- a/.agents/skills/babysit/SKILL.md +++ b/.agents/skills/babysit/SKILL.md @@ -47,15 +47,18 @@ formal review list is not a completed review. Check the PR body and trigger comments too. If review fails to start, trigger it once; do not duplicate an active request. -Read every finding the completed review delivered, sort the whole set, then -push one batch. Before acting on any finding, apply the repository's complexity -tripwire; when it fires, pause and report the resumable state instead of -opening another round. +Read every finding the completed review delivered and sort the whole set. Then +apply the repository's complexity tripwire before acting on any item. When it +fires, pause and report the resumable state instead of opening another round. +Push one batch only after the set clears it. Sort each finding into one verb: - **fix** — it breaks correctness, user data, or a stated acceptance criterion in supported use. Fix it this round. +- **fold** — it improves the changed code, breaks nothing, and stays inside the + slice. Fold it into the batch this round is already pushing; when the round + has no batch, defer it rather than opening a round for polish. - **narrow** — it holds only because the change claims an open-ended property. Remove or narrow the claim instead of building machinery to satisfy it. - **defer** — real value, outside this slice. Record it in the reply and in the @@ -66,7 +69,7 @@ Sort each finding into one verb: outside. Reply with evidence and resolve every thread you handled, including every -narrow, defer, and decline. +fold, narrow, defer, and decline. Diagnose red CI without waiting for the review to complete: fix code failures and rerun transient infrastructure failures. Rebase when behind or required, @@ -79,9 +82,9 @@ resolutions. The pull request is green when the current head has green CI, zero unresolved threads, a clean mergeable state, and a completed current-head review whose -findings are all dispositioned. A narrow that changed no file, a defer, and a -decline disposition a finding without a new head, so they need no further -review. +findings are all dispositioned. A narrow that changed no file, a folded finding +carried in an existing batch, a defer, and a decline disposition a finding +without a new head, so they need no further review. Open at most three finding-bearing rounds against one boundary. Every fix and every file-changing narrow creates a new head and therefore opens the next @@ -100,9 +103,9 @@ before the merge command. ## 6. Report -Report the pull request and head, rounds used, the fixes and the narrowed, -deferred, and declined findings with their reasons, rebases, validation, CI and -review results, and the merge result when applicable. In merge mode, include -the prepared next steps or archive readiness. If interrupted or paused, stop -reads and mutations immediately, report the same state as a resumable -checkpoint, and resume from a fresh snapshot. +Report the pull request and head, rounds used, the fixes and the folded, +narrowed, deferred, and declined findings with their reasons, rebases, +validation, CI and review results, and the merge result when applicable. In +merge mode, include the prepared next steps or archive readiness. If +interrupted or paused, stop reads and mutations immediately, report the same +state as a resumable checkpoint, and resume from a fresh snapshot. diff --git a/.agents/skills/babysit/references/merge.md b/.agents/skills/babysit/references/merge.md index da599b39..70d9fbe7 100644 --- a/.agents/skills/babysit/references/merge.md +++ b/.agents/skills/babysit/references/merge.md @@ -3,7 +3,7 @@ Before merging, inspect the addressed issue conversation. Comments added by other threads are a separate input from Codex pull-request reviews: sort every adjunct work item, revision request, and finding that bears on this pull -request with the same four verbs, and let any resulting push restart the gates +request with the same five verbs, and let any resulting push restart the gates of stage 3. Merge a stack dependency-first, then re-audit or restack the later pull requests. @@ -13,11 +13,15 @@ URL. Run `done` so every matching local review reaches Done; zero matching reviews is success. Report a lookup or Markover failure without weakening the verified GitHub merge result. -Then refresh the issue conversation for comments added during the merge before -recommending next steps or closing it, and account for every remaining adjunct -item. If work remains, prepare concrete next steps for the report. If the issue -is complete, perform the remaining state-only housekeeping, verify the issue -and its trackers reflect completion, and prepare an archive-ready conclusion. +When the merged pull request carries a work-intent claim, it completes with the +merge, so set its phase to `completed`. Then refresh the issue conversation for +comments added during the merge before recommending next steps or closing it, +and account for every remaining adjunct item. If work remains, prepare concrete +next steps for the report; the issue's own claim stays as it is. If the issue +is complete, perform the remaining state-only housekeeping, set its claim to +`completed` as well, verify the issue and its trackers reflect completion, and +prepare an archive-ready conclusion. **Complete when:** the merge is verified, matching Markover reviews are Done, -and the issue and its trackers match the real state. +every completed claim says so, and the issue and its trackers match the real +state. diff --git a/.agents/skills/start-issue/SKILL.md b/.agents/skills/start-issue/SKILL.md index 784bdf5d..59204739 100644 --- a/.agents/skills/start-issue/SKILL.md +++ b/.agents/skills/start-issue/SKILL.md @@ -1,183 +1,125 @@ --- name: start-issue -description: Use when starting or taking over a GitHub issue or pull request, starting untracked repository work, or handling a follow-up found after a pull request merged, before implementation. +description: Use before implementation when the user asks to start or take over an issue or pull request, implement untracked repository work, open a tracked pull request for work authorized now, or record a follow-up found after a merge. Reporting or diagnosing a problem is not itself a request to start work. --- # Start Issue -Treat Projects linked to the current repository and its milestones as the -target's tracker set. A Project can provide a kanban status ledger; a milestone -only groups repository issues and pull requests. Always use the work-intent -comment as the change-surface and ownership ledger. Complete the stages in -order. +Starting work means making it visible before changing it: one **work item** on +GitHub, the **tracker** the user reads, and one **claim** other agents can see. +The interview then fixes the **slice boundary** — the evidence that ends this +slice and what it leaves out — so babysit and the tripwire have something +finite to compare against. Complete the stages in order. -## How to respond to initial start-issue prompt +Enter this workflow when the user asks to start, take over, implement untracked +repository work, or record work. Diagnosing a failure, explaining behavior, +and reporting status stay outside it, even when the request names an issue or +pull request. -When the opening prompt names an existing numbered issue or pull request, -resolve its live number, exact GitHub title, and item URL as the first lookup. -Then immediately emit this identity block as the first substantive response: +## 1. Identify the work item + +For an existing numbered issue or pull request, resolve its live number, exact +GitHub title, and item URL as the first lookup, then emit this identity block +as the first substantive response: ```markdown # #52: Open a specific review through a clickable Markover deep link [#52 on github](https://github.com/lastobelus/markover/issues/52) -I’m checking its trackers, existing claim, and inflight overlap before proceeding. +I'm checking its trackers, existing claims, and overlap before proceeding. ``` -For that existing item, treat both identity lines as an emission gate: complete -them before continuing tracker discovery, inflight scanning, or an interview. -Place no decision, question, activity summary, or recommendation before them. +A brief orienting sentence may precede the lookup. No decision, question, +activity summary, or recommendation precedes the identity block. -When no numbered work item exists yet, perform only the routing needed to -create the issue or draft pull request. Immediately after creation, resolve and -emit its identity block before continuing work on that new item. Pre-creation -tracker or delivery-shape questions are exempt from the identity gate. +**No numbered work item yet:** when no open issue or pull request owns the +requested work, including a problem found after a pull request merged, read +[`references/work-item-routing.md`](references/work-item-routing.md) completely +before the first write. Tracker and delivery-shape questions belong before an +item exists, so they precede this block; emit it immediately after creation. -## 1. Orient and select tracking +**Complete when:** the work has one issue or pull request and its live identity +is on screen. -Confirm `gh auth status`, then resolve the current checkout's repository. Keep -all tracking and work-item operations in that repository. Resolve the item type, -number, URL, title, body, relationships, comments, current branch, and attached -trackers. Inspect the repository and GitHub for facts; reserve questions for -decisions. +## 2. Read the ledger -For an existing issue or pull request, inspect both tracker types: +Confirm `gh auth status`, then resolve the current checkout's repository and +keep every tracking and work-item operation there. Read the target once: type, +number, URL, title, body, relationships, comments, current branch, and attached +trackers. ```sh gh issue view ITEM_URL --json milestone,projectItems gh pr view ITEM_URL --json milestone,projectItems ``` -**Untracked or post-merge work:** when no open issue or pull request owns the -requested work, including a problem found after a pull request merged, read -[`references/work-item-routing.md`](references/work-item-routing.md) completely -before deciding which work item to create. +Use every open Project and milestone already attached to the target unless the +user asks to change its tracking. Report an attached closed Project as +historical and leave it out of the tracker set. **Tracker selection:** when the target has no active tracker, an attached Project's identity is incomplete, or the user selects `New Project` or `New Milestone`, read [`references/tracker-selection.md`](references/tracker-selection.md) -completely before the next tracking write. - -**Markover instance selection:** when this run will open, get, or edit a -Markover review, or the user asks to run a development instance, read -[`references/markover-review.md`](references/markover-review.md) completely -before the next Markover command. - -Run only the command matching the item type. Use every open Project and -milestone already attached to the target unless the user asks to change its -tracking. Report attached closed Projects as historical and exclude them from -the tracker set, scans, and status writes. Include one only after the user -explicitly chooses to reactivate it and live data shows it open. Report -conflicting active Project statuses before proceeding. - -For every selected Project, resolve its fields and status options from live -JSON. Use an existing `Status` field and semantically matching `In Progress` -and `Done` options when present. If lifecycle status is absent or ambiguous, -ask the user how that Project represents it and retain the answer as the status -mapping. A milestone has no item-status mapping. - -Establish one stable owner token for this run. Use the agent thread identifier -when available; otherwise generate and retain a unique `start-issue-...` token. -Every independent run uses a different token. +completely before continuing. Treat the selected tracker as part of the tracker +set before the inflight read, even though stage 3 performs the attachment. -**Complete when:** the work has an issue or pull request, its tracker set and -Project status mappings are explicit, its current local and GitHub context is -unambiguous, and this run has a unique owner token. +Resolve each Project's `Status` field and its `In Progress` and `Done` options +from live JSON; a milestone has no status field. When an attached active +Project lacks those options or represents lifecycle differently, ask the user +how that Project represents it and retain the answer as its status mapping. +Report conflicting active Project statuses rather than choosing between them. -## 2. Scan inflight work - -Build the candidate set independently for each selected tracker: - -- For a Project with an `In Progress` mapping, read its live item count, then - query every item in that status with the count as `--limit`. -- For a Project without a status mapping, inspect all of its items for trusted - active work-intent comments. -- For a milestone, use paginated `gh api` reads to inspect its open issues and - pull requests for trusted active work-intent comments. An unmarked milestone - item is not known to be inflight because milestones have no lifecycle field. - -Use tracker identities resolved in stage 1 rather than fixed values. A Project -status query has this parameterized shape: - -```sh -gh project view PROJECT_NUMBER --owner PROJECT_OWNER --format json --jq '.items.totalCount' -gh project item-list PROJECT_NUMBER --owner PROJECT_OWNER --limit PROJECT_ITEM_TOTAL \ - --query 'status:"IN_PROGRESS_OPTION_NAME"' --format json -``` - -For every candidate repository issue or pull request, use paginated `gh api` -reads to inspect all issue comments for this marker, including each comment's -`user.login` and `author_association` (pull-request conversation comments use -the same REST endpoint): +Then read the inflight set once, using live counts as limits: the items each +active Project holds in `In Progress`, each milestone's open issues and pull +requests, and the claim comments those items carry. ```html ``` -Only markers authored with live association `OWNER`, `MEMBER`, or -`COLLABORATOR`, or by a bot identity explicitly allowlisted in repository -guidance, are trusted. Report and ignore every other marker; untrusted comments -never participate in intent discovery, missing-intent resolution, ownership, or -the canonical election. - -An `In Progress` Project draft has no repository comments endpoint. Treat it as -a missing-intent item and inspect its live Project title, body, and fields for -overlap evidence. - -Summarize inflight items, declared touch points and dependencies, tracker -membership, and plausible overlap with the target. Treat a Project item whose -status says `In Progress` but lacks a work-intent comment as unresolved. For a -milestone, trusted comments in `investigating`, `implementing`, `blocked`, or -`review` phase define the known inflight set. - -After the comment checks, refresh every tracker's membership and candidate -query. Compare sorted Project item node IDs and milestone item node IDs with the -prior snapshot. If any total or candidate ID set changed, inspect the changed -set and repeat with refreshed limits. Complete only after two consecutive -snapshots return the same candidate sets. +Judge overlap from what that one pass shows — title, body, declared touch +points, linked pull request, and branch. An item with no claim is not thereby +suspicious; read what it says and move on. Ask the user when overlap is +plausible but unclear, because they are present and a collision is cheap to +resolve. -Re-read every missing-intent Project item after the sets stabilize. If its -marker is still absent, inspect its issue, pull request, or Project draft -content; linked pull requests; changed paths; and local worktree metadata for -enough evidence to assess overlap. Ask the user before claiming the target when -that evidence remains ambiguous. Complete the scan only after each missing -intent has been reconstructed from live evidence or explicitly resolved by the -user. +Read once. When a later read shows the set changed, use the newer read and +report what changed; two reads need not agree before you continue. Never +describe evidence gathered before a claim or a material scope change as a fresh +check. -When the target already has one or more trusted marked comments, read -[`references/existing-claim.md`](references/existing-claim.md) completely -before deciding whether this run is a continuation, handoff, or collision. +**Complete when:** the tracker set and its status mappings are explicit, and +plausible overlap has been assessed or raised with the user. -**Complete when:** every known inflight item in the tracker set has been checked -and the target has no unresolved intent collision. +## 3. Claim it -## 3. Claim the item +**When the target already carries an active claim** — any claim whose phase is +not `completed` — show it to the user and ask whether this run continues it, +takes it over, or belongs on a different item, before attaching or claiming +anything. Add no second claim. Edit another run's claim only after the user +says that run has stopped, and preserve its intent data when taking it over. +One item carries one active intent. -Attach the target to the tracker selected in stage 1 if it is not already -attached. Use the resolved owner, repository, and number: +Attach the target to the tracker set if it is not already attached, and move +each mapped Project to `In Progress`. An already-correct value is a no-op. ```sh gh project item-add PROJECT_NUMBER --owner PROJECT_OWNER --url ITEM_URL gh issue edit ITEM_URL --milestone MILESTONE_TITLE gh pr edit ITEM_URL --milestone MILESTONE_TITLE -``` - -Run only commands required for the selected tracker and item type. An item can -belong to multiple Projects but only one milestone. - -For each selected Project with a status mapping, resolve the Project node ID, -target item node ID, Status field node ID, and option node IDs from live JSON. -Read live field and item counts before using them as `--limit` values. Treat an -item already in the mapped `In Progress` option as a no-op. Otherwise move it -there without changing other fields: - -```sh gh project item-edit --id ITEM_NODE_ID --project-id PROJECT_NODE_ID \ --field-id STATUS_FIELD_NODE_ID \ --single-select-option-id IN_PROGRESS_OPTION_NODE_ID ``` -Create or update one canonical comment using this shape: +Run only the commands the item type and tracker type require, and resolve +every node ID from live JSON — `item-edit` arguments are invalid without them. +An item belongs to many Projects but one milestone. Treat every other Project +field, milestone property, and repository label as read-only; the claim carries +the rest. + +With no active claim on the target, post one claim comment and maintain it by +editing that exact comment ID: ````markdown @@ -188,48 +130,28 @@ phase: investigating summary: "Short description of the intended slice" touch-points: - unknown +done-when: unknown +excludes: [] blocked-by: [] may-block: [] branch: "current branch or unknown" -thread: "this run's owner token" ``` ```` -Use `phase: implementing` instead of `phase: investigating` when the opening -request already resolves every material implementation decision and explicitly -authorizes implementation. Keep the phase truthful; do not transition merely -because the claim exists. - -Use issue or pull-request references in dependency fields. Include a thread -identifier when available. Keep unknown values explicit. Maintain this single -comment by editing its exact GitHub comment ID; routine changes do not create -new comments. - -Immediately re-read all trusted marked comments on the target with their REST -`created_at` timestamps and numeric IDs. The canonical claim is the earliest -`created_at`, breaking a timestamp tie with the smallest numeric ID. Only the -canonical claimant proceeds. A losing claimant edits its own comment to remove -the marker, labels it as a superseded claim, and stops; a later claim can never -displace the established winner. Pause for the user if a trusted losing marker -cannot be demoted by its author. - -At every ownership checkpoint, re-read all target markers. Checkpoints are -before every later work-intent edit, before starting or resuming implementation, -after a wait or interruption, before each commit or push, and before handoff or -completion. Proceed only while this comment remains canonical and its `thread` -equals this run's owner token. Stop on any mismatch before changing the comment -or implementation. - -After the winner is known, repeat the inflight scan from stage 2 without reusing -either pre-claim snapshot. Treat the current canonical target comment as this -run's owned claim, but re-read every item's live intent and apply the same -set-stability and missing-intent rules. Resolve newly visible overlap with the -user before proceeding. - -**Complete when:** the target is attached to its tracker set, mapped Projects -show `In Progress`, the canonical comment is accurate, every visible losing -claim is demoted, this run is the deterministic winner, and the post-claim scan -is stable with no unresolved overlap. +`done-when` is the observable evidence that ends this slice, and `excludes` +names the actors, scenarios, variants, and extensions left outside it. Stage 4 +fills both; babysit reads them as the boundary for triage. Use issue or +pull-request references in dependency fields, keep unknown values explicit, and +keep the phase truthful — `implementing` only after implementation is +authorized. + +After posting, read the target's own claim comments once more — that comment +thread only, not the trackers. When more than one active claim is present, +pause, show the collision, and let the user resolve it before implementation. +Two runs pausing is a good outcome; do not invent a winner. + +**Complete when:** the target is attached, mapped Projects show `In Progress`, +one truthful claim exists, and no unresolved collision remains. ## 4. Interview @@ -237,42 +159,67 @@ When planned slices imply future pull requests, name each uncreated pull request by its relationship to the slice: `slice-3 PR`, `third PR`, or `PR for slice 3`. Reserve `PR #N` for an existing GitHub pull request numbered `N`. -Use a zero-question path when the opening request already resolves acceptance -criteria, scope boundaries, dependencies, touch points, validation, and -meaningful tradeoffs, and explicitly authorizes implementation. Record the -resolved decisions, synchronize the canonical intent, perform the required -fresh final inflight scan, and complete this stage without inventing a question. +When the work promises an open-ended property, such as security, privacy or +sanitization, compatibility breadth, race freedom, provenance, resilience, or +evaluation completeness, resolve its stop condition with the other decisions: +the observable evidence that ends this slice, and the actors, scenarios, +variants, or extensions left outside it. Record that boundary as a decision +when the acceptance criteria already make it finite; otherwise narrow the +promise with the user before authorizing implementation. + +When the user asks whether the complexity is warranted, or doubts that the +design will hold up, answer that as the next decision: name the actor, the +consequence, the ordinary recovery, and the smaller alternative, then take +direction before the scope grows further. + +Use a zero-question path when the opening request, or a routing interview that +preceded item creation, already resolves acceptance criteria, scope boundaries, +dependencies, touch points, validation, and meaningful tradeoffs, and +explicitly authorizes implementation. Record the +resolved decisions, write them into the claim, and complete this stage without +inventing a question. When any material decision remains unresolved, read [`references/interview.md`](references/interview.md) completely and follow its -question, synchronization, and rescan rules. +question and synchronization rules. **Complete when:** acceptance criteria, scope boundaries, dependencies, -touch-points, validation, and meaningful tradeoffs are resolved, and either the -opening request or a later response explicitly confirms the shared -understanding and authorizes implementation. +touch-points, validation, meaningful tradeoffs, and the stop condition of any +open-ended promise are resolved; `done-when` and `excludes` are written into +the claim; and either the opening request or a later response explicitly +confirms the shared understanding and authorizes implementation. + +## 5. Implement and hand off + +Set `phase: implementing` and make the agreed changes. Keep the claim, the +Projects, and the milestone aligned with the real state: -## 5. Implement and maintain intent +- `blocked`: name the concrete blocker in `blocked-by`; mapped Projects stay + `In Progress`. +- `review`: the slice is with babysit or the user; record the handoff in the + summary and leave mapped Projects `In Progress`. +- `completed`: the owned work is finished, and mapped Projects move to `Done`. + A direct pull request completes when it merges. An issue completes when the + issue closes, so a merged pull request that leaves issue work open keeps it + `In Progress`. -After authorization, change the comment to `phase: implementing` if needed and -make the agreed changes. Keep the comment and trackers aligned with these -lifecycle rules: +Keep the milestone attached throughout; its progress changes when the item +closes. -If implementation uncovers a material change to the summary, touch points, -dependencies, or branch, pause before entering the newly added surface. Update -the canonical comment, repeat the inflight scan from stage 2 with fresh -stability snapshots, and resolve newly visible overlap before continuing. +Re-read the claim before resuming after an interruption, and before entering a +surface it does not declare. When implementation materially changes the +summary, touch points, dependencies, or branch, update the claim and reassess +overlap for the newly added surface before working inside it. -- `blocked`: state the concrete blocker in `blocked-by`; keep mapped Projects `In Progress`. -- `review`: record the handoff in the summary; keep mapped Projects `In Progress`. -- `completed`: use only when the item is completed or closed; move mapped Projects to `Done`. +When the user decides something belongs to later work, record it on the owning +durable item before moving on; a follow-on that lives only in a plan or a reply +is lost. Propose the item and get authorization before creating a new one. -Keep the milestone attached throughout the work; its progress changes when the -issue or pull request closes. For Project status changes, reuse the resolved -field and option IDs, and treat an already-correct value as a no-op. Treat every -other Project field, milestone property, and repository label as read-only after -selection; the canonical comment carries the additional coordination detail. +**Markover instance selection:** when this run will open, get, or edit a +Markover review, or the user asks to run a development instance, read +[`references/markover-review.md`](references/markover-review.md) completely +before the next Markover command. **Complete when:** implementation and proportionate verification are finished, -and the final comment, Project statuses, milestone membership, and item state -match the real handoff state. +and the claim, Project statuses, milestone membership, and item state match the +real handoff state. diff --git a/.agents/skills/start-issue/references/existing-claim.md b/.agents/skills/start-issue/references/existing-claim.md deleted file mode 100644 index 57560780..00000000 --- a/.agents/skills/start-issue/references/existing-claim.md +++ /dev/null @@ -1,14 +0,0 @@ -# Existing work-intent claim - -Create no new claim while the target already has a trusted marked comment. -Apply the deterministic winner rule in stage 3 of `SKILL.md`, show the canonical -intent to the user, and ask whether this run is a continuation or handoff. - -Reuse the comment only when its `thread` equals this run's owner token and the -user approves the continuation. For an approved handoff or a legacy null token, -preserve the intent data. Demote the old marker only after its owner acknowledges -relinquishment or the user explicitly confirms that run has stopped, then create -a new marked claim with this run's token in stage 3. - -Keep a separate concurrent effort paused until the user chooses a distinct -issue or pull request. V1 represents one active intent per item. diff --git a/.agents/skills/start-issue/references/interview.md b/.agents/skills/start-issue/references/interview.md index 79f12ee1..99d03ec1 100644 --- a/.agents/skills/start-issue/references/interview.md +++ b/.agents/skills/start-issue/references/interview.md @@ -1,16 +1,23 @@ # Implementation interview -Interview relentlessly about unresolved material implementation decisions and -their dependencies. Ask exactly one question per response and wait for the -answer. Number tracker choices, decisions, and questions in one sequence. +Interview only about unresolved material implementation decisions and their +dependencies, and resolve every one of them before authorization. Ask exactly +one question per response and wait for the answer. Number tracker choices, +decisions, and questions in one sequence. Look up discoverable facts instead of asking for them. Decisions belong to the user. When standards and repository evidence make an answer unusually clear, record it as a numbered decision beside the next actual question. -Before the first interview response for a numbered item, verify that both issue -identity lines required by `SKILL.md` have already been emitted. If not, emit -them before the Decision or Question block. This does not apply to routing +An open-ended promise is resolved only with a stop condition: the observable +evidence that ends this slice and what it leaves out. Derive it from the +acceptance criteria and record it as a numbered decision when they already make +it finite; ask only when the promise is still unbounded, and recommend +narrowing the promise rather than enlarging the slice. + +Before the first interview response for a numbered item, verify that the +identity block required by `SKILL.md` has already been emitted. If not, emit it +before the Decision or Question block. This does not apply to routing questions required before an untracked work item is created. Use this format: @@ -29,13 +36,10 @@ Relevant discovered facts, dependencies, and tradeoffs. > One clear question, preferably yes/no or a small set of choices? ``` -Update the canonical work intent whenever the interview materially changes its -summary, touch points, dependencies, branch, or phase. Keep `phase: -investigating` until the user explicitly confirms shared understanding and -authorizes implementation. +Update the claim whenever the interview materially changes its summary, touch +points, dependencies, branch, or phase, and write the agreed `done-when` and +`excludes` into it before authorization. Keep `phase: investigating` until the +user explicitly confirms shared understanding and authorizes implementation. -After every material intent update, repeat the inflight scan from stage 2 with -fresh stability snapshots and resolve newly visible overlap before asking the -next question. Perform this scan once more after the final material update and -before accepting implementation authorization, even if no further question is -needed. +When an answer brings the work into a surface another claim declares, raise +that overlap with the user before asking the next question. diff --git a/.agents/skills/start-issue/references/markover-review.md b/.agents/skills/start-issue/references/markover-review.md index 0e863e78..12fe3785 100644 --- a/.agents/skills/start-issue/references/markover-review.md +++ b/.agents/skills/start-issue/references/markover-review.md @@ -1,5 +1,9 @@ # Markover instance selection +The root `AGENTS.md` owns the Markover CLI contract, including the +`pullRequestStatus` lookup and the terminal-friendly handoff. This branch adds +instance selection only. + Use canonical for reviews of plans, proposals, and other artifacts produced during the work. An open pull request does not by itself select its development instance. diff --git a/.agents/skills/start-issue/references/work-item-routing.md b/.agents/skills/start-issue/references/work-item-routing.md index 4c6b66d3..0ff27f21 100644 --- a/.agents/skills/start-issue/references/work-item-routing.md +++ b/.agents/skills/start-issue/references/work-item-routing.md @@ -48,8 +48,8 @@ ambiguous. Present the normal tracker choices before the first write. For direct-PR work, follow the bootstrap below. For issue-backed work, create the issue in the current repository before claiming it. When scheduling it for later, attach it, set an unambiguous Project `Todo` or backlog status when -available, report the issue and tracker, and stop. Otherwise return to stages 2 -through 5 in `SKILL.md` with the issue as the target. +available, report the issue and tracker, and stop. Otherwise emit its identity block and +return to stages 2 through 5 in `SKILL.md` with the issue as the target. ## Direct-PR bootstrap @@ -58,10 +58,19 @@ After making the tracker set explicit: 1. State the action in one concise sentence. For a merged-PR follow-up, say `Opening a new PR linked to #42 and adding it to TRACKER_TITLE.` For fresh work, say `Opening one PR and adding it to TRACKER_TITLE.` -2. Scan inflight work in that tracker set before editing. Create a branch, make - the smallest coherent first commit, and open a draft pull request. Include +2. Resolve stage 4's material decisions and the finite slice boundary before + the first commit. That commit is implementation, so it needs the same + authorization every other change does. No pull request exists yet, so these + questions precede the identity block exactly as the tracker choice does. +3. Read inflight work in that tracker set, then create a branch, make the + smallest coherent first commit, and open a draft pull request. Include `Follow-up to #42` in the body when a merged pull request is the source. -3. Immediately attach and claim the draft pull request, then complete the - post-claim scan before continuing implementation. +4. Emit the new pull request's identity block, then continue at stage 3 of + `SKILL.md` with it as the target. The inflight read in step 3 was this path's + stage 2; do not repeat it. Stage 3 attaches the pull request, publishes the + already-resolved claim with `phase: implementing` and the agreed `done-when` + and `excludes`, and performs its collision read. Stage 4 then takes its + zero-question path. The branch and pull request are this path's coordination + point; nothing is claimable until they exist. Create exactly one new work item in this path: the pull request. diff --git a/evals/start-issue/cases.json b/evals/start-issue/cases.json index 9f73440e..524e2061 100644 --- a/evals/start-issue/cases.json +++ b/evals/start-issue/cases.json @@ -100,6 +100,7 @@ "tracker-choice-presented:new-milestone", "question-count:one", "direct-pr-action-announced:tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:selected-tracker", "draft-pr-created:only-work-item", "draft-pr-attached:selected-tracker", @@ -108,6 +109,7 @@ ], "forbiddenActions": [ "delivery-shape-question-asked:clear-single-pr", + "first-commit-made:before-authorization", "issue-created:direct-pr-path", "implementation-continued:before-draft-pr-claim", "work-item-count-created:issue-and-pr" @@ -121,6 +123,7 @@ "tracker-choice-presented:new-milestone", "question-count:one", "direct-pr-action-announced:tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:selected-tracker", "draft-pr-created:only-work-item", "draft-pr-attached:selected-tracker", @@ -134,6 +137,7 @@ "tracker-choice-presented:new-project", "tracker-choice-presented:new-milestone", "question-count:one", + "first-commit-made:before-authorization", "issue-created:direct-pr-path", "draft-pr-created:only-work-item", "implementation-continued:before-draft-pr-claim", @@ -142,12 +146,14 @@ "expectedMissing": [ "delivery-shape-decided:direct-pr", "direct-pr-action-announced:tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:selected-tracker", "draft-pr-attached:selected-tracker", "draft-pr-claimed:before-further-implementation", "work-item-count-created:one-pr" ], "expectedForbidden": [ + "first-commit-made:before-authorization", "issue-created:direct-pr-path", "implementation-continued:before-draft-pr-claim", "work-item-count-created:issue-and-pr" @@ -436,15 +442,15 @@ } }, { - "id": "post-claim-scan-reconstructs-unmarked-items", - "description": "A post-claim scan must rebuild overlap evidence for every still-unmarked Project item instead of reusing the pre-claim reconstruction.", + "id": "changed-ledger-read-uses-newer-state", + "description": "When a later ledger read shows changed state, the workflow adopts the newer evidence, reports the change, and does not seek snapshot convergence.", "provenance": { "kind": "live-thread", "sourceThreadIds": [ "6bcd8df5-d1e2-4ebc-90c4-7c765ffd56af", "2fdb0272-8eb1-4549-a47a-2051b6d37b01" ], - "observation": "Both runs collected fresh post-claim candidate and marker snapshots but reused pre-claim evidence for still-unmarked Project items." + "observation": "Both runs demonstrated that earlier ledger evidence must not be presented as a fresh check after the tracked state changes." }, "observations": { "workItem": { @@ -458,59 +464,40 @@ } ] }, - "scanCheckpoint": "post-claim", - "preClaimReconstructionAvailable": true, - "postClaimState": { - "consecutiveCandidateSetsMatch": true, - "allCandidateMarkersRead": true, - "unmarkedProjectItems": [ - { - "key": "umbrella-issue", - "currentContentAvailable": true - }, - { - "key": "active-feature", - "linkedChangesAvailable": true, - "worktreeMetadataAvailable": true - }, - { - "key": "stale-closed-item", - "currentContentAvailable": true - } - ] - } + "ledgerRead": "single-bounded-pass", + "laterRead": "changed-state-visible" }, "requiredActions": [ - "postclaim-snapshot-collected:fresh", - "postclaim-marker-read:every-candidate", - "postclaim-missing-intent-reconstructed:every-unmarked-project-item", - "postclaim-overlap-assessed:fresh-evidence" + "ledger-read-performed:single-bounded-pass", + "changed-state-adopted:newer-read", + "ledger-change-reported:user-visible" ], "forbiddenActions": [ - "postclaim-missing-intent-reused:preclaim-evidence", - "postclaim-scan-completed:ids-and-markers-only" + "earlier-evidence-presented:as-fresh-check", + "ledger-read-repeated:until-snapshots-agree", + "unmarked-item-reconstructed:exhaustively" ], "controls": { "positive": [ - "postclaim-snapshot-collected:fresh", - "postclaim-marker-read:every-candidate", - "postclaim-missing-intent-reconstructed:every-unmarked-project-item", - "postclaim-overlap-assessed:fresh-evidence" + "ledger-read-performed:single-bounded-pass", + "changed-state-adopted:newer-read", + "ledger-change-reported:user-visible" ], "negative": { "signals": [ - "postclaim-snapshot-collected:fresh", - "postclaim-marker-read:every-candidate", - "postclaim-missing-intent-reused:preclaim-evidence", - "postclaim-scan-completed:ids-and-markers-only" + "ledger-read-performed:single-bounded-pass", + "earlier-evidence-presented:as-fresh-check", + "ledger-read-repeated:until-snapshots-agree", + "unmarked-item-reconstructed:exhaustively" ], "expectedMissing": [ - "postclaim-missing-intent-reconstructed:every-unmarked-project-item", - "postclaim-overlap-assessed:fresh-evidence" + "changed-state-adopted:newer-read", + "ledger-change-reported:user-visible" ], "expectedForbidden": [ - "postclaim-missing-intent-reused:preclaim-evidence", - "postclaim-scan-completed:ids-and-markers-only" + "earlier-evidence-presented:as-fresh-check", + "ledger-read-repeated:until-snapshots-agree", + "unmarked-item-reconstructed:exhaustively" ] } } @@ -548,6 +535,7 @@ "followup-choice-offered:create-issue", "tracker-set-inherited:source-pull-request", "action-announced:source-pr-and-tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:inherited-trackers", "draft-pr-created:follow-up-to-source", "draft-pr-attached:inherited-trackers", @@ -556,6 +544,7 @@ ], "forbiddenActions": [ "issue-created:apply-now", + "first-commit-made:before-authorization", "tracker-choice-question-asked:usable-source-trackers", "implementation-continued:before-draft-pr-claim", "source-pull-request-reopened", @@ -567,6 +556,7 @@ "followup-choice-offered:create-issue", "tracker-set-inherited:source-pull-request", "action-announced:source-pr-and-tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:inherited-trackers", "draft-pr-created:follow-up-to-source", "draft-pr-attached:inherited-trackers", @@ -577,6 +567,7 @@ "signals": [ "followup-choice-offered:apply-now", "followup-choice-offered:create-issue", + "first-commit-made:before-authorization", "tracker-choice-question-asked:usable-source-trackers", "issue-created:apply-now", "draft-pr-created:follow-up-to-source", @@ -586,6 +577,7 @@ "expectedMissing": [ "tracker-set-inherited:source-pull-request", "action-announced:source-pr-and-tracker", + "direct-pr-boundary-resolved:before-first-commit", "prewrite-inflight-scan:inherited-trackers", "draft-pr-attached:inherited-trackers", "draft-pr-claimed:before-further-implementation", @@ -593,6 +585,7 @@ ], "expectedForbidden": [ "issue-created:apply-now", + "first-commit-made:before-authorization", "tracker-choice-question-asked:usable-source-trackers", "implementation-continued:before-draft-pr-claim", "work-item-count-created:issue-and-pr" diff --git a/test/start-issue-evals.test.ts b/test/start-issue-evals.test.ts index 11cd784f..aa9de686 100644 --- a/test/start-issue-evals.test.ts +++ b/test/start-issue-evals.test.ts @@ -73,7 +73,7 @@ test('start-issue corpus covers the twelve coordination branches', () => { 'confirmed-new-project-uses-repository-owner', 'new-milestone-interviews-before-creation', 'multiple-trackers-retain-all-active-attachments', - 'post-claim-scan-reconstructs-unmarked-items', + 'changed-ledger-read-uses-newer-state', 'merged-pr-followup-apply-now-reuses-tracker', 'merged-pr-followup-issue-only-chooses-tracker', 'open-pr-artifact-review-uses-canonical-instance', @@ -83,22 +83,30 @@ test('start-issue corpus covers the twelve coordination branches', () => { assert.equal(new Set(cases.map(({ id }) => id)).size, cases.length) }) +test('start-issue triggers for authorized untracked implementation, not diagnosis', () => { + assert.match( + skillSource, + /^description: .*implement untracked repository work.*Reporting or diagnosing a problem is not itself a request to start work\.$/m + ) + assert.match(skillSource, /asks to start, take over, implement untracked\nrepository work, or record work/) +}) + test('initial response identifies the live issue and title first', () => { assert.match( skillSource, - /## How to respond to initial start-issue prompt[\s\S]*```markdown\n# #52: Open a specific review through a clickable Markover deep link\n\[#52 on github\]\(https:\/\/github\.com\/lastobelus\/markover\/issues\/52\)/ + /## 1\. Identify the work item[\s\S]*```markdown\n# #52: Open a specific review through a clickable Markover deep link\n\[#52 on github\]\(https:\/\/github\.com\/lastobelus\/markover\/issues\/52\)/ ) assert.match( skillSource, - /both identity lines as an emission gate:[\s\S]*before continuing[\s\S]*inflight scanning, or an interview/ + /No decision, question,[\s\S]*precedes the identity block/ ) assert.match( skillSource, - /When no numbered work item exists yet,[\s\S]*Immediately after creation,[\s\S]*emit its identity block/ + /No numbered work item yet:[\s\S]*before the first write[\s\S]*emit it immediately after creation/ ) assert.match( skillSource, - /Pre-creation[\s\S]*tracker or delivery-shape questions are exempt from the identity gate/ + /Tracker and delivery-shape questions belong before an[\s\S]*item exists/ ) assert.doesNotMatch(skillSource, /^# #52—/m) assert.match( @@ -122,7 +130,7 @@ test('branch-only guidance is progressively disclosed', () => { assert.doesNotMatch(skillSource, /^### Follow-ups to merged pull requests$/m) assert.match( skillSource, - /Untracked or post-merge work:[\s\S]*references\/work-item-routing\.md/ + /No numbered work item yet:[\s\S]*references\/work-item-routing\.md/ ) assert.match( skillSource, @@ -132,10 +140,6 @@ test('branch-only guidance is progressively disclosed', () => { skillSource, /material decision remains unresolved[\s\S]*references\/interview\.md/ ) - assert.match( - skillSource, - /target already has one or more trusted marked comments[\s\S]*references\/existing-claim\.md/ - ) assert.match( skillSource, /Markover instance selection:[\s\S]*references\/markover-review\.md/ @@ -144,7 +148,10 @@ test('branch-only guidance is progressively disclosed', () => { assert.match(readReference('work-item-routing.md'), /## Follow-up after merge/) assert.match(readReference('tracker-selection.md'), /## Discover candidates/) assert.match(readReference('interview.md'), /# Implementation interview/) - assert.match(readReference('existing-claim.md'), /# Existing work-intent claim/) + assert.equal( + fs.existsSync(path.join(skillDirectory, 'references', 'existing-claim.md')), + false + ) const markoverReference = readReference('markover-review.md') assert.match(markoverReference, /Use canonical for reviews of plans/) assert.match(markoverReference, /--instance dev open PATH/) @@ -153,6 +160,21 @@ test('branch-only guidance is progressively disclosed', () => { assert.doesNotMatch(markoverReference, /open ''/) }) +test('duplicate claims are detected and handed to the user without an election', () => { + const existingClaim = skillSource.indexOf( + '**When the target already carries an active claim**' + ) + const firstTrackerWrite = skillSource.indexOf('Attach the target to the tracker set') + assert.ok(existingClaim >= 0) + assert.ok(firstTrackerWrite > existingClaim) + assert.match(skillSource, /any claim whose phase is\nnot `completed`/) + assert.match( + skillSource, + /read the target's own claim comments once more[\s\S]*pause, show the collision[\s\S]*do not invent a winner/ + ) + assert.doesNotMatch(skillSource, /owner token|earliest `created_at`|self-demot/i) +}) + test('root guidance owns the terminal-friendly Markover handoff', () => { assert.match(agentsSource, /^`open ''`$/m) assert.match(agentsSource, /best-effort Markdown link and raw review ID/) @@ -207,9 +229,9 @@ test('live provenance stays explanatory rather than becoming fixture input', () } }) -test('post-claim freshness case records both independent live observations', () => { +test('changed-ledger case records both independent live observations', () => { const evaluationCase = cases.find( - ({ id }) => id === 'post-claim-scan-reconstructs-unmarked-items' + ({ id }) => id === 'changed-ledger-read-uses-newer-state' ) assert.ok(evaluationCase) assert.equal(evaluationCase.provenance.kind, 'live-thread') @@ -217,7 +239,10 @@ test('post-claim freshness case records both independent live observations', () '6bcd8df5-d1e2-4ebc-90c4-7c765ffd56af', '2fdb0272-8eb1-4549-a47a-2051b6d37b01' ]) - assert.match(evaluationCase.provenance.observation ?? '', /reused pre-claim/) + assert.match( + evaluationCase.provenance.observation ?? '', + /earlier ledger evidence[\s\S]*fresh check/ + ) }) test('post-merge cases record the issue-and-PR live failure', () => {