issue-authoring
Draft or refine IDD-ready GitHub issues, roadmap issues, and sub-issues before the normal IDD execution loop begins. Use when a request is too large or ambiguous for one reviewable change, when work needs decomposition or dependency encoding, or when the user asks for issue draft
Install
npx skills add https://github.com/kurone-kito/idd-skill/tree/main/skills/issue-authoring
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kurone-kito-idd-skill@llmmart
git clone https://github.com/kurone-kito/idd-skill.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole kurone-kito/idd-skill collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Issue Authoring
Use this skill to prepare issue-ready work before execution starts. Keep the skill concise and treat the repository docs as the canonical source for the full contract and schema. The canonical source bundle lives in this repository; install copies in the agent-specific skill directory your runtime reads.
Stable Phases
Use two stable phases:
- Intake and Clarification — inspect relevant context, identify
ambiguity, run a secondary critique or explicit self-critique, and
ask only the questions that block safe issue drafting. Keep
clarification bounded; use the repository-local
issueAuthoring.maxClarificationRoundsvalue when available, otherwise default to 3 rounds. Under-clarification stop rule: if, after bounded clarification, you still cannot name the concrete surface to edit or an objective verification for a candidate task, route it toneeds-decisionor ask — do not publish a confidently-vaguereadyissue. Reliability over speed. - Decompose and Draft — restate the request in implementation terms, split it into atomic tasks, classify readiness, reuse existing issues when safe, and draft the smallest issue shape that preserves dependencies and reviewability.
Preserve low-readiness work in stable buckets: ready, deferred, needs-decision, blocked-by-human, and out-of-scope.
Workflow
- Read the bundled contract in references/contract.md.
- Reuse or extend an existing issue before creating a new one — but never edit the body of an actively-claimed or open-PR issue (its claimed agent will not pick the change up); cover it with a follow-up issue instead. See the contract's claim-state precondition.
- Choose the smallest safe output shape:
- orphan issue for one ready autonomous task only when the target
repository discovers orphans (
issue-scope: roadmap-first, the default, via the orphan fallback, ororphan-first) and any configuredorphan-first-policyapproval step can be completed after drafting - roadmap plus sub-issues for multi-task or multi-session work
- stable non-ready buckets for deferred, needs-decision, blocked-by-human, or out-of-scope work
- orphan issue for one ready autonomous task only when the target
repository discovers orphans (
- Prefix-first: resolve the target repository's marker prefix
before emitting any authoring marker —
roadmap-id,blocked-by,autopilot-suitability, oreffort. Use the prefix documented by the target repository's onboarding or IDD docs, and ask the user instead of guessing when the prefix is not discoverable. Never default to this source repository'sidd-skillprefix in an installed bundle. - Keep dependencies machine-readable and minimal:
- roadmap identity via
<!-- <marker-prefix>-roadmap-id: ... --> - active child issues via roadmap task-list links
- issue-to-issue dependencies via
Blocked by #NNN - sequential roadmap dependencies via
<!-- <marker-prefix>-blocked-by: ... -->only when a separate roadmap must close first - keep independent sibling work in roadmap task lists unless a true correctness, availability, or ordering constraint requires a dependency edge
- roadmap identity via
- Before publishing a ready orphan, roadmap, or child body, run the
audit-authored-issuelinter against it as the mechanical pre-publish gate — see Mechanical pre-publish gate in the bundled contract, including the manual fallback forinstructions-onlyinstalls with no helper runtime. Resolve every reported failure before treating the issue as ready. Before newly publishing a body into theneeds-decisionorblocked-by-humanbucket instead, also run the linter, passing--expect-bucket <needs-decision|blocked-by-human>(choose the one matching value) — the same gate section's--expect-bucketflag requires the matchingauthoring-bucketmarker for that publish, closing the gap where a non-ready body would otherwise never be audited at all. - Publish each
readydrafted body directly under the authoring hold once it passes the mechanical gate (step 6) and the critique pass (the Intake and Clarification phase above) — no separate publish approval is needed. Only skip publishing when the current request explicitly asked for a preview instead. Manage the authoring label for each created or updated issue:resolve
issueAuthoring.authoringLabelName, defaulting tostatus:authoringcreate the label with
gh label createbefore first use when the target repository does not already have ittreat label creation or application failure as a publishing blocker
apply the label before updating an existing issue
acquire per-target ownership before editing an existing issue or roadmap; the shared label is a claim-suppression lock, not an owner token. Follow the append-only owner-marker and re-read protocol in references/workflow-boundary.md and stop without editing when ownership or the target snapshot conflicts
for a new Stage 1 set, generate one opaque set ID and reuse it in every owner marker for that set; when resuming an interrupted set, recover and verify its persisted set ID from the exact trusted owner markers and reuse it instead of generating a replacement; never infer set membership from the label alone
when a set includes a parent roadmap, publish a valid roadmap shell under the authoring hold before any child; acquire and verify that roadmap as the set anchor, leaving its
## Trackslist empty only until child issue numbers exist. Without a parent roadmap, use the designated lead target as the anchoracquire and verify the set anchor before publishing or acquiring any child; do not acquire children independently, and stop all edits if any target cannot join that anchor's verified set
before each child acquisition or resume, append and verify a same-owner anchor heartbeat (or reuse one — see the heartbeat-coalescing rule below), re-fetch the anchor's paginated log, then append the child marker and immediately re-fetch both anchor and child. Stop with the label in place if anchor ownership changed between those reads
Heartbeat coalescing (
issueAuthoring.heartbeatCoalesceWindow, defaultPT2M): before appending a heartbeat, replay the target's paginated log; reuse the latest trusted marker instead of appending when it is the same owner/set/session, its mode isacquire/bootstrap/resume/heartbeat, it is younger than the window, and itsbody-sha256matches the just-fetched body — re-fetch and verify the reused marker exactly as a fresh one. This window never applies toacquire,bootstrap,resume,release,release-guard, orrelease-completeappends themselves — only aheartbeatappend may be skippedpersist the anchor's canonical repository/issue identity in every owner marker for the set; the anchor marker points to itself, and a resume must stop if the interrupted set's anchor cannot be proven
immediately before every Stage 1 body or relationship edit, re-fetch both the edited target and the set anchor; require each target's expected owner token independently, plus the same set, anchor, and owning session, and require an unchanged expected target snapshot before editing
immediately before that edit, renew both generations with a trusted same-owner-per-target heartbeat marker (one marker when target and anchor coincide; reuse applies here too), re-fetch and verify both, and stop if renewal or ownership verification fails
create new issues only through a capability-checked publication command that applies the authoring label atomically and carries an exact hidden publication token for target, anchor, set, and session; if that operation is unavailable, stop before creating the issue — never intentionally create an unlabeled issue
the hidden publication token is this exact HTML-first body line.
anchorreuses the opaquetargetvalue only when this new issue is itself the set anchor (self-reference); for a non-anchor child,anchoris instead the set anchor's already-resolved real<owner>/<repo>#<number>reference, not an opaque id (full rule:references/contract.md's "New-issue ownership" section):<!-- <marker-prefix>-authoring-publication: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token> -->The originating Stage 1 hold uses this append-only publication-intent record, whose
anchorfollows the same self-anchor/non-anchor-child rule:<!-- <marker-prefix>-authoring-publication-intent: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token>; journal=<owner>/<repo>#<number>; issue=<owner>/<repo>#<number>|none; actor=<trusted-marker-actor>; state=<pending|member|cleanup|abandoned> -->issueis the returned canonical issue identity ornone. Appendstate=pending; issue=nonebefore creation, then append the returned identity while it remainspending, appendmemberonly after the owner marker is verified, and appendcleanupbefore any safe-close mutation. Appendabandonedonly after closed/label-absent verification. On resume, paginate the hold log and select the latest valid record for the exact token tuple; missing, conflicting, or out-of-order records fail closed, whilependingandcleanupremain recovery holds.journalis the durable record location. For an existing set, use the verified originating Stage 1 hold; for a standalone set with no existing issue or anchor, use the repository-level authoring journal target configured atissueAuthoring.journalIssuein.github/idd/config.json(anowner/repo#numberreference to a pre-existing, durable, comment-only issue) -- an unsetissueAuthoring.journalIssueonly blocks a standalone set; an existing set with a verified Stage 1 hold needs no journal configuration at all. Do not create that journal as part of the same set. If the applicable location cannot be resolved -- no verified Stage 1 hold for an existing set, orissueAuthoring.journalIssueunset or unverifiable for a standalone set -- stop withblocked-by-humanbefore creating any target. On every paginated replay, requireactorto equal the API author and verify that actor is a trusted marker login with the required write-level permission or configured bot/app trust. An untrusted, malformed, or conflicting exact-token record is not valid evidence; fail closed and retain the hold.Generate the opaque
targetid and token before creation because the new issue's own number is not yet known, and generateanchorthe same way only when this new issue is the set anchor itself -- otherwise reuse the anchor's already-resolved real reference, per the self-anchor/non-anchor-child rule above. Before issuing the create, persist those preallocated IDs, the exact token, andstate=pendingin that journal. After a successful create, attach and verify the returned issue identities on that pending record before appending the owner marker. If the pre-create hold write cannot be verified, do not create; if the post-create identity attachment cannot be verified, leave the returned issue held for recovery. Transition tomemberonly after owner-marker verification orabandonedonly after the verified safe close and label removal. On resume, match the exact token and persisted identities; an incomplete scan or state mismatch is recovery.immediately after a new issue is created and labeled, append its
mode=acquireowner marker with the current set ID, then re-fetch the labels, body, and owner comments before treating it as a set memberan atomically labeled publication is not set membership until its owner marker is verified; persist each returned target identity in the journal before appending the marker. On resume, reconcile recorded identities and only issues carrying this set's exact publication token; an incomplete scan or unmarked match is a recovery hold, so never infer membership or completion from the shared label alone
if owner-marker append or verification is uncertain for a new issue, reconcile the returned comment ID and the paginated owner-marker log with bounded retries before closing; if a trusted marker is found, retain the label and recover or reopen the issue as a set member. Otherwise re-fetch labels, body, current
claimed-bystate, and the paginated owner-marker log; if that final read proves no competing claim or owner marker, appendstate=cleanupbefore closing the issue or removing its authoring label. Re-fetch and verify closed/label-absent state, then appendstate=abandoned. If any disposition or cleanup read is uncertain, retainstate=cleanup, leave the issue held, and report the recovery holdif an allegedly atomic create unexpectedly returns an unlabeled issue, re-fetch its labels, body, current
claimed-bystate, and paginated owner-marker log before closing. If a trusted claim or owner marker from another session/set is present, do not close or overwrite the exposed issue; report the ownership conflict and stop. If no competing claim is present, apply and verify the authoring label as a safe hold, then re-fetch its labels, body, currentclaimed-bystate, and paginated owner-marker log again before closing. If that hold or final re-read cannot be verified, leave the issue open and report the recovery hold. Deletion needs admin permission and is not the default recovery pathheld issues under the label ARE the drafts: do in-place body edits, roadmap relationship wiring, and re-lint of already-published bodies on the published issue itself, under the same label
if a session is interrupted before the set is fully wired, leave the label and owner markers in place — the label suppresses Discover and the markers preserve the set identity for a later verified resume
read every target and anchor owner-marker log with paginated retrieval and deterministic comment order; never rely on a single API page
after the release checklist passes and the user explicitly requests release — or, for a single target whose body carried the review-fix-loop-cutoff marker at Stage 1 publication time (never a marker added later), the narrow auto-release exception in Authoring hold and release — preflight and verify or reuse a matching
mode=releasemarker for every target (withsupersedesequal to the current owner token) before removing any label; record its GitHub comment ID, never append a duplicate on retry, append and reconcile an anchor-onlymode=release-guardmarker before the first label removal, keep the set anchor held, and remove it last. Recheck each target's expected owner token independently, plus the shared set/anchor/session, recorded marker, and expected snapshot immediately before each removal. Renew and verify the set anchor heartbeat first (reuse applies here too), re-fetching its current owner, set, anchor, and session; only then renew and verify the target heartbeat when distinct (one marker when they coincide; reuse applies here too). Remove non-anchor labels one at a time and verify the whole set. After the final anchor label removal is verified, reuse or append the anchor-onlymode=release-completemarker and record its comment ID. Reconcile that ID and the paginated anchor log with bounded retries; a successful POST or verification timeout is inconclusive. If the trusted marker is found, keep labels absent and close the set. If a complete fresh read conclusively proves that no trusted marker was appended, restore labels for every target and leave every target generation open. If reads remain inconclusive, keep the release guard and current labels/state in place, leave the set held, and record a recovery hold. Never infer marker absence or roll back from a verification timeout. Discover must treat the reconciled release guard as suppressing every target until the anchor completion marker is found. Treat each release marker, removal, and completion marker as provisional until the durable completion event is reconciled. If a later removal or verification fails, retry a failed post-removal read with a bounded fresh read, restore labels for already processed targets while the owner/set still match, verify the restored set, and leave every target generation open
- Stop at the single approval boundary: release. Publishing under the hold does not by itself authorize starting the IDD execution loop — only the user's explicit release request does, except the narrow review-fix-loop-cutoff auto-release exception in Authoring hold and release.
Reference Routing
- For the bundled contract, output schemas, and discoverability guard: read references/contract.md.
- For the bundled two-stage authoring/release contract and the boundary with the IDD execution loop: read references/workflow-boundary.md.
- For concrete drafting patterns and example prompts: read references/draft-patterns.md.
- When editing this bundle inside the source repository, keep the
bundled references synchronized with the canonical maintenance docs at
repo-root
docs/issue-authoring-skill.mdanddocs/idd-workflow.md(relative links are avoided here since this file is mirrored at a different path depth in.claude/skills/issue-authoring/).
Output Checklist
- Preserve low-readiness work in stable buckets instead of dropping it.
- Keep acceptance criteria explicitly verifiable.
- Keep human-dependent setup, review, and approval work isolated from ready execution issues whenever possible.
- Link every active child issue from its roadmap body.
- Justify each dependency edge and keep independent sibling work as roadmap task-list entries.
- Record reuse or extension decisions when the skill does not create a new issue.
- Avoid widening drafting output beyond the user request without saying so.
- Run the
audit-authored-issuelinter (or its manual fallback ininstructions-onlyinstalls) against every drafted ready body, and against every body newly published intoneeds-decisionorblocked-by-humanwith--expect-bucket; resolve every reported failure before publishing. - Name a concrete surface to edit and an objective verification for
every
readycandidate; route anything else toneeds-decisionor ask instead of guessing (the under-clarification stop rule). - Resolve the target repository's marker prefix before emitting any
authoring marker; never assume this source repository's
idd-skillprefix in an installed bundle (the prefix-first rule).
Files (idd-skill)
-
references
-
contract.md 114.9 KB
# Bundled Issue Authoring Contract This file keeps the `issue-authoring` bundle usable when it is installed or copied outside this repository root. It mirrors the canonical contract in `docs/issue-authoring-skill.md`. That canonical document exists only inside this bundle's source repository — an installed copy of this bundle should not expect it to be present. ## Target marker prefix **Prefix-first**: resolve the target repository's hidden marker prefix before emitting _any_ authoring marker — `roadmap-id`, `blocked-by`, `autopilot-suitability`, or `effort`. Resolve it once, up front, not as an afterthought once a marker is already half-drafted. - Use the prefix documented by the target repository's onboarding or IDD instructions. - In this source repository the prefix is `idd-skill`, but installed bundles must not assume that value elsewhere. Never default to `idd-skill` in an installed bundle; use it only when the target repository actually configured that value. - If the prefix is not discoverable from the repository docs or user context, stop and ask instead of emitting a guessed marker (preventive; no observed incident yet). A prefix the operator already confirmed during an onboarding hearing (Steps 1A-1C of `idd-template/ONBOARDING.md`) counts as a resolved value under "user context" — even before it is committed anywhere in the target repository's tree — and does not require asking again. ## Trigger policy Use this bundle when direct implementation would skip the issue hygiene that the IDD execution loop depends on. Invoke it when one or more of the following are true: - the request is too large or ambiguous for one reviewable change - the likely solution needs decomposition into multiple atomic tasks - dependencies or execution order must be made explicit before work can start safely - the user wants a roadmap, issue breakdown, or parallelizable work plan Skip it when all of the following are true: - the task fits one reviewable change - verification is already clear - no roadmap, dependency marker, or issue split is needed - the user did not ask for issue drafting first ## Stable phases The bundle uses two stable phases. These names mirror the canonical contract and should stay stable for copied bundles. ### 1. Intake and Clarification In this phase, the agent: - inspects the relevant code, docs, and existing issues - identifies assumptions and ambiguity that affect issue quality - runs a secondary critique pass before drafting - asks the user only the questions that block safe issue drafting The critique pass is agent-neutral: use a subagent or rubber-duck reviewer when available, otherwise run an explicit self-critique locally. Clarification must be bounded; use the repository-local `issueAuthoring.maxClarificationRounds` value when available, otherwise default to 3 rounds. If safe drafting is still impossible after that, stop and report the remaining blockers instead of looping indefinitely. **Under-clarification stop rule.** If, after bounded clarification, you still cannot name the concrete surface to edit or an objective verification for a candidate task, route it to `needs-decision` or ask — do not publish a confidently-vague `ready` issue. Reliability over speed. This is distinct from the "Under-specified" specificity band below: that band judges an already-drafted body's wording, while this rule stops publication earlier, during Intake, before a body is even drafted. ### 2. Decompose and Draft In this phase, the agent: - restates the clarified request in implementation-facing terms - splits work into atomic tasks - checks whether each task is suitable for autonomous execution - reuses or extends existing issues before creating new ones - drafts orphan issues, roadmap packages, sub-issues, or non-ready buckets as appropriate ## Readiness buckets Do not silently drop low-confidence or low-readiness work. Route each candidate task into one stable bucket: - **ready**: passes limited scope, clear verification, and autonomous completion - **deferred**: plausible, but priority, timing, or decomposition is not strong enough for execution - **needs-decision**: depends on a product, policy, or design choice - **blocked-by-human**: waits on a person, credential, asset, or outside system - **out-of-scope**: does not belong in the repository or skill scope ## Specificity target Issue drafting should aim for a level of specificity where a middle-tier cloud model can implement the task without drifting. This is a practical drafting heuristic, not a hard model requirement. The goal is to avoid both hidden assumptions that only a top-tier model can infer and step-by-step runbooks that cost too much to author. ### Three specificity bands | Band | Practical signal | Drafting response | | ------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | **Under-specified** | Stable execution likely depends on a frontier cloud model class | Add missing constraints, split scope, or make acceptance criteria more explicit | | **Target** | A middle-tier cloud model class can implement the issue without drifting | Treat this as the preferred drafting target when the execution axes already pass | | **Over-specified** | Even a lightweight local or compact cloud model class could follow the issue mechanically because it has become a runbook | Remove procedural micromanagement while keeping invariants, file anchors, and checks | The capability tiers above are practical heuristics, not a fixed compatibility matrix or runtime requirement. ### How the specificity target interacts with readiness This heuristic does not replace the IDD execution axes: - **Limited scope** still decides whether the work fits one issue or needs a roadmap. - **Clear verification** still decides whether success is objectively checkable. - **Autonomous completion** still decides whether the task can finish without outside coordination. An issue can be specific yet still fail A4 or A4.5 because it is too broad, not verifiable, or blocked on a human decision. Conversely, an issue that passes those gates can still be under-specified if it leaves too much implementation shape implicit. The drafting target is therefore "ready and stable for a middle-tier model," not "maximally detailed." ## Executability gate for code spec-units For a **code spec-unit** — a drafted unit whose acceptance criteria are objectively executable (a script, function, or test the drafted issue itself specifies) — an authoring session can validate the draft before publishing by building a throwaway reference implementation against its own stated acceptance check and accepting the draft only if that reference implementation passes. This reuses the downstream execution loop as an upstream ground-truth gate: it catches drafts whose acceptance criteria are internally inconsistent, non-trivially unsatisfiable, or otherwise not actually implementable, before a downstream executor ever claims the issue. The reference implementation is a discarded validation probe, never published — it does not cross the Publication boundary below; only the drafted issue itself is published, and building the probe does not start the IDD execution loop. This is a **recommended practice for repositories or domains where a drafted code spec-unit has an objectively executable acceptance check**, not a change to this repository's own mechanical `bin/idd-audit-authored-issue.mjs` checks or a new requirement on this repository's own issue-authoring practice — most of this repository's issues are multi-file engineering changes without a single executable acceptance check to validate against. - **Weak-model semantic self-review stays advisory.** When a weak authoring model reviews its own draft for semantic quality, treat the verdict as advisory only, never a hard veto on publishing — mirroring the narrow-question guidance for weak-model judgment calls in `docs/idd-workflow.md`'s Weak-model guardrails section. - **Decompose and draft want different model traits.** Decomposition (judgment about scope and structure) tolerates a verbose reasoning model; the structured draft step (emitting the exact spec/acceptance- criteria block) is more reliable on a leaner, more literal model, whose chain-of-thought does not compete with the drafted block for token budget. Pick the model per sub-task, not per pipeline, when both are available. - **Redraft and re-decomposition are limited rescue mechanisms, not reliable fixes.** A bounded redraft loop tends to re-roll rather than converge, because a weak author regenerates rather than incrementally fixes. Re-decomposing a repeatedly-failing unit is usually low-value too: most authoring failures are hard-but-atomic rather than over-broad, and splitting a hard-but-atomic unit tends to produce incoherent sub-units that do not recompose. Treat a code spec-unit that repeatedly fails this gate as a candidate for `needs-decision` or human/stronger-model authoring instead of looping indefinitely. ## Reuse-first issue policy Before creating any new issue, check whether the work already has a suitable home. **Claim-state precondition (check this first).** Before reusing or extending _any_ existing issue, determine whether it has an **active claim** (its latest valid `claimed-by` comment is newer than the configured `claim-stale-age`; distributed default 24 h) or an **open PR**, or is otherwise actively executing. If so, you **MUST NOT edit its body**: the working agent snapshots the issue body into its B2 plan and treats that plan as authoritative — it never re-reads the body, so a post-claim body edit is silently lost and becomes an implementation gap. You **may** add a separate **comment** (never an edit or append to the body), but **must not** rely on the claimed agent acting on it. Cover the intended change with a **follow-up issue** (or a roadmap track around it), and you **SHOULD** post a cross-reference comment on the claimed issue linking the follow-up. Stale or reclaimable claims (latest `claimed-by` older than `claim-stale-age`) are exempt — the next claimer re-reads the latest body, so editing them is safe. Then apply these checks in order: 1. If an existing open issue already matches the task and only lacks the new schema details, extend that issue instead of cloning it. 2. If an existing open roadmap already owns the initiative, add or refine task-list entries there instead of creating a competing umbrella. 3. If an existing issue is close but too broad, split follow-up work out of it rather than widening the original issue further. When the issue being split is itself a roadmap child, update the parent roadmap's `## Tracks` list in the same authoring action — add the new issue's link and adjust any sequencing notes (a short dated note is the observed good pattern) — subject to the claim-state precondition above applied to the roadmap issue's own claim/PR state, and record the provenance in the new issue's body (e.g., `Split out of #<n>`). 4. If an existing issue has an **active claim**, an open PR, or is otherwise being actively executed, do **not** edit its body or repurpose it (see the claim-state precondition, which exempts stale/reclaimable claims); create a follow-up issue or extend the roadmap around it instead. 5. Create a brand-new issue only when no existing issue can absorb the work without harming ownership, clarity, or reviewability. Report when the bundle reuses, extends, or declines to reuse an issue so a later session can follow the reasoning. **Recent-window scan for just-discovered problems.** The checks above assume the work already has a candidate home to reuse or extend. When instead authoring an ad hoc issue for a problem **just discovered** during the current session — a build-breaking regression noticed mid-session, for example, rather than a task drafted from an existing backlog — run a recent-window duplicate scan immediately before publishing: list the newest issues regardless of state and check whether a concurrent session already authored the same problem. ```sh gh issue list --repo <owner>/<repo> --state all --limit 20 # or, scoped to a recency window: gh issue list --repo <owner>/<repo> --state all --search "created:>=<YYYY-MM-DD>" ``` A hit routes back into the checks above: extend the discovered issue instead of publishing a duplicate. When the race slips past this scan anyway (near-simultaneous discovery), the outcome is **anticipated and self-resolving, not a coordination failure**: both sessions proceed independently through their own claim and implementation cycle; whichever PR merges first wins; the other session manually verifies the fix already landed on the default branch, then closes its own issue and (unmerged) PR as superseded, citing the verifying evidence. This is the same manual verify-then-close judgment call the execution loop's B2.0 supersession re-check (`idd-work.instructions.md`) applies after claim — this scan only adds an earlier, pre-publish checkpoint. A fast enough race can still surface even after B2.0; when it does, it resolves the same way. **Same-shape follow-up chains.** A different case from both checks above: an issue whose own acceptance criteria explicitly ask for a follow-up issue with the same acceptance criteria when the round does not fully complete (a "retry again" pattern, e.g., an iterative measurement or convergence task). Before drafting such a same-shape successor, state what measurable forward progress the just-completed round achieved — a changed metric, a narrowed diagnosis, a newly-tested hypothesis, a newly-discovered and now-fixed blocker. When the round produced no such signal (the same result, the same diagnosis, no new information beyond the predecessor), route to `needs-decision` (or an equivalent hold) instead of authoring another identical-shape issue, and record why the chain paused so a later session or human can see the reasoning. This is a sibling check, not a replacement: the checks above guard against an accidental duplicate; this guards against a correct-but-repeated pattern continuing past the point it stops being useful. ## Output chooser Choose the smallest safe output shape: - **Orphan issue**: one autonomous task can finish the work, no roadmap-level coordination is needed, and the target repository discovers orphans — i.e. `issue-scope` is `roadmap-first` (the default; orphans are picked up as the fallback when no roadmap work is startable) or `orphan-first` (orphans first). If the repository uses `orphan-first-policy: maintainer-approved`, surface the required post-publication maintainer approval step. If the repository sets `issue-scope: roadmap` (roadmap-only) or disables public orphan-first discovery with `orphan-first-policy: public-disabled`, surface that constraint and prefer a roadmap package instead. - **Roadmap plus sub-issues**: the request needs visible sequencing, parallel tracks, multiple ready issues, or multi-session handoff. - **Stable non-ready buckets**: some work is deferred, blocked by a human, waiting on a decision, or outside the repository scope. When the repository keeps the broader issue-author approval gate, surface the same post-publication approval step for orphan issues, roadmaps, and sub-issues whenever the issue author is not self-authorizing under the repository's `maintainer-approval-actors` policy. The configured ready label from `approvalSignals.readyLabelName` (default: `idd:ready`) is accepted according to `approvalSignals.labelFreshnessMode` (`presence-only` by default, optional `event-freshness`), while standalone `IDD ready` comments from a maintainer approval actor must stay fresh against the latest issue content and generated-plan update (or an equivalent draft-stability signal). Until that approval condition is satisfied, route the draft to the approval-needed fallback bucket instead of the normal ready-to-start set. ## Human-dependency isolation Treat unresolved human dependency as a side effect that should be isolated away from ready execution issues whenever possible. - **Front-load** human-dependent work when coding cannot start safely until a person provides a decision, credential, permission, maintainer-only action, external setup, or policy choice, or until an unavailable system becomes usable again. - **Back-load** human-dependent work when the remaining dependency is subjective review, publication choice, optional polish, or another post-implementation judgment that should not block an otherwise autonomous core change. - Keep the central execution issue as close as possible to a pure autonomous unit: clear repository-local scope, no hidden human handoff in the implementation steps or acceptance criteria, and objective verification. - Preserve unavoidable human-dependent work in an explicit stable bucket, dependency edge, or approval-needed hold rather than mixing it into a ready issue. - Route unresolved choices to `needs-decision`, route waiting on people, credentials, maintainer-only actions, or unavailable systems to `blocked-by-human`, use `deferred` when timing or decomposition is not strong enough yet, and keep approval-gated ready work in the approval-needed hold instead of the normal ready-to-start set. - If a task cannot be expressed without unresolved human coordination in the middle of implementation, it is not yet `ready`. This principle complements the execution axes rather than replacing them: it is a practical way to protect autonomous completion and clear verification during issue drafting. ## Hidden human-dependency validation Before publishing a `ready` issue, run a short pre-publication check for hidden human dependency. Treat this as a routing aid, not a rigid wording linter: the question is whether the work still depends on unresolved human action, not whether the draft used one forbidden phrase. Ask these checks: 1. Does implementation require credentials, external access, hardware, or infrastructure that the executing agent cannot already reach? If yes, route the work to `blocked-by-human` unless that dependency can be front-loaded into a separate prerequisite issue. 2. Does any implementation step or acceptance criterion depend on a product, policy, or design decision that has not been made? If yes, route the work to `needs-decision`. 3. Do the acceptance criteria require subjective human approval instead of objective verification? If yes, rewrite the ready issue around measurable checks and back-load the optional review or publication judgment. 4. Does a roadmap narrative hide human-dependent work inside prose while the visible task list presents the item as execution-ready? If yes, preserve that work in an explicit stable bucket, approval-needed hold, or blocking issue instead of burying it in the narrative. 5. Is any dependency marker being used only to group related work or express preference order? If yes, remove the fake blocker and use task-list structure or sequencing notes instead. Keep dependency edges only for true start blockers. Normal post-implementation code review, merge approval, or publication choice does not by itself make an otherwise autonomous issue non-ready. The ready issue should still carry its own objective verification even when a human will look at the result afterward. ## Codebase-fidelity validation Before publishing a `ready` issue, run a short pre-publication check that the spec stays faithful to the existing codebase. Treat this as a routing aid, not a rigid wording linter: A4.5 suitability triage is structural and does not read the codebase, so a spec that contradicts established semantics can still pass that gate and only surface the mismatch in advisory review, costing extra review-fix round-trips. Ask these checks: 1. When an issue reuses an existing identifier or field name, confirm the specified value matches that name's established semantics in the codebase — do not overload a name with a new shape or source. **Remedy**: mint a new, distinctly named field instead of overloading the existing one. Worked example: a candidate issue's acceptance criterion reads "set `retryAttempts` to the elapsed wait time in milliseconds" — `retryAttempts` already means a whole-pass apply attempt _count_ in `audit-pr-cleanup.mts`'s `CleanupAuditReport`, so reusing it for a duration overloads an established name with an incompatible shape. Fix: mint a new field instead, e.g. `retryWaitMs`, and leave `retryAttempts` untouched. 2. Flag values that are mutable at runtime — specify a live read at the point of use rather than a one-time capture at construction. 3. When an issue proposes to **delete, replace, or "align to upstream"** code, first check the target for an intentional-divergence signal — a local change made on purpose to differ from upstream. If one is present, require the issue body to acknowledge that divergence and justify overriding it, rather than silently reverting hardening a consumer added deliberately (blind "resync to upstream" resets are a recurring Discover→plan-cycle waste when the divergence turns out to be intentional). The recommended portable signal is a canonical inline code-comment convention (for example a `do-not-revert:` / `idd-divergence:` marker) — it travels with vendored files and needs no repo-wide label taxonomy. An owner/CODEOWNERS marker or a referenced tracking issue may also serve, but the code-comment convention is the recommended default. Do not hard-code any single consumer's divergence-tracking mechanism. 4. When an issue drafts a **template resync or reimport** (pulling a newer `idd-template/` revision into a repository that already adopted IDD), consult the **upstream target ref's** copy of `docs/customization.md`'s "Documentation lint compatibility" section (added 2026-08-05, commit `6ceaa6dd`) before treating `.markdownlint.yml` / `.markdownlint-cli2.yaml` / `.cspell.config.yml` as unchanged. `docs/customization.md` is itself part of the imported core file set, so it resolves in an installed or adopter context once present — but an adopter whose prior import predates that commit has no such section in their own local copy yet, which is exactly the named gap to document in the resync issue, not a documentation-consultation failure. Either the target ref's `docs/customization.md` section or, from a source checkout of `idd-skill` itself, `idd-template/ONBOARDING.md`'s "Re-importing" section, documents the same named gap: an adopter's own rule customizations for these files need a by-hand merge into the new import rather than an assumed carry-forward, and `idd-onboard.mjs --import` reports a `blockedOverwrites` finding instead of silently keeping a same-named local file as-is, unless the import used `--force`, which still writes the file and reports it in the verdict JSON (`plan` as an `overwrite` entry, `filesChanged`, and `written`) but omits it from `blockedOverwrites` so the import is not blocked (observed 2026-08-12/13 on an adopter repository, `setup.ubuntu`, kurone-kito/idd-skill#2012). 5. When drafting a resync issue's Background, run a mechanical placeholder diff against the **upstream `idd-skill` source repository's** `idd-template/` trees at the two relevant refs — never a tree inside the target/adopter repository itself, which retains no local `idd-template/` directory of its own once IDD is imported. - **Baseline ref.** Use the exact tag or commit SHA actually imported at the adopter's prior onboarding when it is explicitly recorded (for example in the original onboarding PR/commit message) or when the operator can confirm it directly. Do not infer it from the adopter's current file content: neither a diff against the import commit nor a reverse- or forward-substitution content-match against a candidate upstream tree reliably pins a single ref — onboarding substitutes placeholder values and permits legitimate post-substitution edits, an import commit's diff itself only records the already-transformed adopter snapshot, and more than one upstream commit can plausibly yield the same imported subset. When no explicit record or operator confirmation is available, report the baseline as unresolved/ambiguous in the resync issue itself rather than guessing one from content alone. Do not construct `v<iddVersion>` (`.github/idd/config.json`) as the baseline without this check: `iddVersion` is only a coarse signal and can be stale, an adopter still on `0.1.0` has no matching tag at all (`CHANGELOG.md` records that `0.1.0` predates the tag discipline), and an adopter who pinned a raw commit SHA at import time instead of a tag may have no `v<iddVersion>` tag matching what they actually imported either. - **Target ref.** The new release/ref the resync targets. - **Scope.** Intersect both trees with the Step 2 "File list" core file set as it reads **at the target ref** (`idd-template/ONBOARDING.md`'s generated file-list block) plus whichever optional profile artifacts the adopter selected, not the complete `idd-template/` tree — a file such as `idd-template/ONBOARDING.md` itself is never copied into an adopter, so reporting it as changed is noise. Use the target ref's manifest, not the baseline ref's: a file the target release newly added to the core or a selected profile is exactly the kind of change a resync issue must report, and the baseline ref's own manifest predates it. - **Token filter.** Compare each ref's own placeholder table in effect at that ref, not today's list applied to both — a placeholder added or removed between the two refs is itself exactly the kind of token-identity change this check exists to report, and reusing one ref's list for the other's tree would hide that change. At a ref from commit `e55ccd9c` (2026-05-12) onward, that table is Onboarding Reference — Placeholder Values' "Final placeholder meanings" table (`docs/onboarding/placeholders.md`). An older ref has no such file — the placeholders were documented directly inside `idd-template/ONBOARDING.md`'s own "Step 1C — Collect placeholder values" section instead; use that section's list as the allowlist for a baseline ref that old. Never match every `{{...}}`-shaped span unfiltered either way — an unrestricted match also catches ordinary GitHub Actions expressions such as `${{ github.token }}` in an imported workflow file. - **Excluded paths.** Skip `docs/onboarding/placeholders.md`, `docs/customization.md`, and `docs/onboarding/policy-decisions.md` — these deliberately keep `{{...}}` tokens literal to document the placeholder syntax itself, so diffing them misidentifies literal documentation as an outstanding substitution. Compare the actual token identities per changed file, not only the aggregate occurrence count: a same-count one-for-one placeholder swap changes what an adopter must substitute without changing the count. Name any file whose placeholder tokens changed, rather than asserting a file needs no placeholder substitution as an unverified default (observed 2026-08-12/13 on an adopter repository, `setup.ubuntu`, kurone-kito/idd-skill#2012). ## Live-observed claim citation When a drafted issue's Background section (or its `## Goal` / `## Why this matters` equivalent, per the schema in use) asserts a "live observed" runtime behavior claim (as opposed to a claim verifiable by reading static source), cite a concrete, checkable artifact for it — a permalink, a PR/comment/run ID, or an inline reproduction snippet — rather than only a prose description of the observed event. A drafting session that already has the artifact in hand (a PR review thread, a workflow run, an adopter's own report) should cite it directly instead of paraphrasing from memory; a worked example already exists elsewhere in this file: "(observed 2026-08-12/13 on an adopter repository, `setup.ubuntu`, kurone-kito/idd-skill#2012)". This is not a mechanical `audit-authored-issue` gate check: a live-observed claim is prose-level and not reliably machine-detectable without a high false-positive risk. Apply this discipline at drafting time instead, before an unsupported claim ships in one of these sections that another session or reviewer cannot independently re-verify. ## Dependency minimization Encode a dependency edge only when it reflects a true correctness, availability, or ordering constraint. - keep independent sibling tasks as roadmap task-list entries, with short sequencing or parallelization notes when that helps reviewers or later agents - use visible or sequential dependency markers only when the issue cannot start safely until the dependency resolves - do not create an artificial serial chain when sibling tasks could be reviewed and verified independently - do not split one natural, cohesive change into artificial sibling issues only to widen parallel execution - whenever an issue's own narrative states that its work cannot safely start until another named issue resolves, encode `Blocked by #NNN` for that issue rather than leaving the constraint as prose-only sequencing. Discover and A4.5 honor the hard `Blocked by` edge, not a narrative "runs after #NNN" note: A4.5 Actionability inspects the body, not completability, so a narrative-only dependency reports the issue startable the moment its other filters pass, and claiming it then means either violating the asserted constraint or doing the referenced issue's unresolved work first. This rule is general, not limited to a fixed list of cases; the two recurring patterns below are illustrative, not exhaustive - **example — docs or operator-help child that documents behavior implemented by sibling issues**: encode `Blocked by #NNN` for those implementation issues (or otherwise sequence the docs child to run after they merge) so the documentation is written against **shipped** behavior. Describing designed-but-unshipped behavior in the present tense is a recurring advisory-review-thrash pattern; "describe shipped behavior" is a true ordering constraint, so this edge is consistent with the encode-only-a-real-constraint rule above - **example — finalize or verify track whose acceptance criteria assert state produced by sibling implementation tracks**: encode `Blocked by #NNN` on **each** such sibling rather than stating the ordering only in prose. A prose-sequenced finalize track reports startable the moment its build foundation closes, and claiming it then means either failing its acceptance criteria or doing the siblings' unmerged work - once a `Blocked by #NNN` / `Depends on #NNN` reference resolves — the referenced issue closes **with its required outcome verified as delivered**, not merely closed as not-planned, superseded without an equivalent implementation, or later reopened — revisit the blocked issue's own body and remove that reference and its explanatory wait prose, rather than leaving it in place as inert history, but keep any prose identifying the delivered artifact or interface this issue's own scope depends on. A line naming several references (`Blocked by #10, #11`) keeps the still-open ones — remove only the resolved reference, and remove the whole line only once every reference on it has resolved. When the underlying constraint is not actually met, reopen the prerequisite issue or repoint the dependency line at an open replacement issue instead of leaving a stale edge in place: `discover-readiness-check` blocks only on an `OPEN` referenced issue, regardless of why it closed, so retaining the edge alone does not keep the dependent issue blocked. (Observed 2026-08-13 on issue #1994's `Blocked by #1985` note, after #1985 closed; reported in #2002.) This cleanup is more than tidiness: stale wait-explaining prose can trip `checkVerifiability`'s subjective-approval heuristic in `suitability-triage.mts`, which matches either a single line combining a subjective-subject word (`maintainer`, `stakeholder`, `human`, `opinion`, `judgment`/`judgement`, `ux`, or `feel` — not only authority nouns) with a gate word (`approval`, `sign-off`/ `signoff`, `decision`, or `preference`), or a gate word followed within 80 characters — including across lines — by a subjective-subject word. This still fires even though the structural dependency filter already correctly treats the closed reference as unblocking When an issue keeps a dependency edge, justify each dependency edge in the surrounding issue body and confirm that the split still preserves natural cohesion. ## Nested roadmap nodes Use a nested roadmap when one roadmap track needs its own coordination boundary, active child list, or multi-session handoff. A nested roadmap is still a roadmap node, not a normal execution candidate. Authoring rules: - reference the nested roadmap from the parent roadmap task list instead of hiding it in prose - give the nested roadmap its own roadmap marker and `## Tracks` section that links the active child work it coordinates - treat the nested roadmap as a coordination/audit node for discovery and roadmap audit; do not draft it as normal A3/A4/A5 execution work - use two-level or three-level nesting only when the intermediate roadmap has its own active child work or handoff boundary - do not use `Blocked by #NNN` or `<!-- <marker-prefix>-blocked-by: ... -->` only to group leaf issues under an active nested roadmap; reserve those encodings for true execution dependencies or sequential roadmap dependencies between separate roadmaps Validation expectations: - each nested roadmap node is linked from its parent roadmap task list - each nested roadmap node links its own active child work from its body - cycles, duplicate references, and closed intermediate roadmaps with hidden open descendants must be surfaced as validation failures or explicit follow-up notes, not silently normalized away ## Required dependency encoding - Roadmap identity via `<!-- <marker-prefix>-roadmap-id: ... -->` - Active child issues via roadmap task-list links - Issue-to-issue dependencies via `Blocked by #NNN` - Sequential roadmap dependencies via `<!-- <marker-prefix>-blocked-by: ... -->` only when a separate roadmap must close first ## Required draft content ### Candidate files format The `## Candidate files` section is not free-form prose: the `discover-shared-file-overlap` evidence helper parses it as machine input for the A4 Step 2 high-contention shared-file check (see [High-contention shared-file overlap](https://github.com/kurone-kito/idd-skill/blob/main/docs/policy-constants.md#high-contention-shared-files)). Populate it accurately rather than as a loose reading aid for humans. Optional for an orphan or roadmap issue; required for a [child issue under a roadmap](#child-issue-under-a-roadmap) (see [Required draft content](#required-draft-content) below). - List each candidate file path inside backticks, one path (or one bullet) per line — for example `` - `src/scripts/idd-onboard.mts` ``. The parser extracts every backtick-quoted path in the section, including continuation lines of a multi-line bullet. - A bullet with no backticks at all still falls back to its leading path-like token, but backtick-quoting every path is the reliable form and should always be used. - The section ends at the next Markdown heading, **of any level** — even a deeper subheading closes it. Anything inside it that looks like a path — backtick-quoted or a bare bulleted leading token — is parsed as a candidate file, so keep unrelated notes, caveats, or subheadings outside the section. ### Orphan issue - title with a concise user-facing summary - `## Background` or `## Goal` - `## Proposed change` - `## Acceptance criteria` - an autopilot-suitability footer at the end of the body (visible line + `<!-- <marker-prefix>-autopilot-suitability: N -->` marker; see [Autopilot-suitability score](#autopilot-suitability-score)) - an optional effort footer next to it (visible line + `<!-- <marker-prefix>-effort: S|M|L -->` marker; see [Effort hint](#effort-hint)) — a soft Discover selection tie-breaker, fail-safe on absence Validation expectations: - no `<marker-prefix>-roadmap-id` marker - no `<marker-prefix>-blocked-by` marker - acceptance criteria are explicitly verifiable - the issue stays discoverable under the target repository's `issue-scope` setting - exactly one autopilot-suitability footer with an integer 1-5 marker; a score of `1` carries the configured `blocked-by-human` label (default `status:blocked-by-human`), unless an `authoring-bucket: needs-decision` marker substitutes the configured needs-decision label instead (see [Authoring-bucket marker](#authoring-bucket-marker)) - passes the `audit-authored-issue` mechanical pre-publish gate for the `orphan` shape (see [Mechanical pre-publish gate](#mechanical-pre-publish-gate)) ### Roadmap issue - title that describes the umbrella initiative - `## Goal` - `## Background` or `## Why this matters` - `## Tracks` - `## Success criteria` - one `<!-- <marker-prefix>-roadmap-id: <roadmap-id> -->` marker - an autopilot-suitability footer at the end of the body (visible line + `<!-- <marker-prefix>-autopilot-suitability: N -->` marker) - an optional effort footer next to it (visible line + `<!-- <marker-prefix>-effort: S|M|L -->` marker; see [Effort hint](#effort-hint)) — a soft Discover selection tie-breaker, fail-safe on absence Validation expectations: - every active child issue or nested roadmap node is referenced from the roadmap body - the roadmap explains why multiple issues exist - sequencing and blocking are explicit - each dependency edge is justified and preserves natural cohesion - nested roadmap entries stay identifiable as coordination/audit nodes instead of normal execution leaves - exactly one autopilot-suitability footer with an integer 1-5 marker; a score of `1` carries the configured `blocked-by-human` label (default `status:blocked-by-human`), unless an `authoring-bucket: needs-decision` marker substitutes the configured needs-decision label instead (see [Authoring-bucket marker](#authoring-bucket-marker)) - passes the `audit-authored-issue` mechanical pre-publish gate for the `roadmap` shape (see [Mechanical pre-publish gate](#mechanical-pre-publish-gate)) ### Child issue under a roadmap - title with a concrete task summary - `## Background` - `## Proposed change` - `## Acceptance criteria` - `## Candidate files` (see [Candidate files format](#candidate-files-format) above) - optional dependency line or sequential roadmap marker when needed - an autopilot-suitability footer at the end of the body (visible line + `<!-- <marker-prefix>-autopilot-suitability: N -->` marker) - an optional effort footer next to it (visible line + `<!-- <marker-prefix>-effort: S|M|L -->` marker; see [Effort hint](#effort-hint)) — a soft Discover selection tie-breaker, fail-safe on absence Validation expectations: - the issue is referenced from its parent roadmap task list - acceptance criteria are locally verifiable - `## Candidate files` lists the files the child is expected to touch, so the A4 Step 2 high-contention shared-file check (`discover-shared-file-overlap`) can actually engage instead of silently no-opping for lack of input - any dependency marker is resolvable, intentionally chosen, and justified - the issue can be claimed independently without absorbing sibling work - exactly one autopilot-suitability footer with an integer 1-5 marker; a score of `1` carries the configured `blocked-by-human` label (default `status:blocked-by-human`), unless an `authoring-bucket: needs-decision` marker substitutes the configured needs-decision label instead (see [Authoring-bucket marker](#authoring-bucket-marker)) - passes the `audit-authored-issue` mechanical pre-publish gate for the `child` shape (see [Mechanical pre-publish gate](#mechanical-pre-publish-gate)) ## Drafted issue prose language A drafted issue's human-readable prose sections — `## Background` (or `## Goal`/`## Why this matters`), `## Proposed change`, `## Acceptance criteria`, and the roadmap shape's `## Tracks` / `## Success criteria` — follow the target repository's resolved `authoringLanguage` value from `.github/idd/config.json`: - A fixed BCP-47-shaped tag (for example `en`, `ja`, `fr`) makes the drafted prose use that language. - The literal `match-source` matches the operator's live conversational language during an interactive/hearing issue-authoring session. - An absent field defaults to English. See `docs/customization.md`'s Authoring Language section for the full field definition. **Marker/footer carve-out**: this never changes any HTML-comment marker's machine-parsed format, nor any visible-line mirror whose exact wording a mechanical regex parses. Concretely, the autopilot-suitability and effort footers' visible lines (`_Autopilot suitability: N / 5 ...` / `_Effort: S | M | L ...`) must stay in their exact canonical English wording regardless of `authoringLanguage`, since `audit-authored-issue` matches them against a fixed English-phrase regex. ## A4.5 Suitability Gate Alignment When an issue is published and reaches the IDD discover phase, the A4.5 pre-claim gate will evaluate it against seven suitability checks. The authoring skill should catch these issues before publishing: | Check | Authoring Bucket | How to Prevent | | ------------------------ | -------------------- | ------------------------------------------------------------------------------- | | Repository Fit (Check 1) | `out-of-scope` | Ensure issue is scoped to this repository; escalate if it crosses boundaries | | Coherence (Check 2) | `ready` or escalated | Validate issue body against schema before publish | | Safety/trust (Check 3) | `ready` or escalated | Screen issue body for code injection and untrusted markers | | Duplicates (Check 4) | `ready` or escalated | Run reuse-first checks before creating a new issue | | Actionability (Check 5) | `ready` or escalated | Ensure the issue describes concrete work; escalate if blocked by human decision | | Autonomy (Check 6) | `ready` or escalated | Ensure agent can complete without external coordination | | Verifiability (Check 7) | `ready` or escalated | Ensure success is verifiable; escalate if it requires subjective approval | **Check 7 escape-hatch pattern**: an either/or acceptance-criteria bullet where one branch is a substantive change and the other reads as "or document the gap/tradeoff" is not an automatic Check 7 PASS -- the documentation branch must itself name a concrete, checkable requirement, or evaluate it on its own merits and route to `needs-decision`. See `idd-suitability.instructions.md`'s Edge Cases section ("Escape-hatch acceptance criteria") for the full worked example. Pre-publish validation checklist: 1. **Coherence**: Issue body is well-formed, title+description are clear, intent is parseable 2. **Safety**: No code injection, marker injection, or untrusted input in issue body 3. **Uniqueness**: Reuse-first check passed; no duplicate or superseded work 4. **Human dependency isolation**: Ready issues do not hide unresolved decisions, credentials, subjective approvals, or mid-implementation human handoffs 5. **Mechanical audit**: the drafted body passes the `audit-authored-issue` linter for its declared shape (see [Mechanical pre-publish gate](#mechanical-pre-publish-gate)) If any check is uncertain, route the issue to `needs-decision` or `blocked-by-human` during drafting instead of publishing a marginally-ready issue. ## Mechanical pre-publish gate Before publishing a drafted `ready` **orphan, roadmap, or child** body (the shapes the linter supports), run the `audit-authored-issue` linter against it when a helper runtime is available. Before newly publishing a body into the **`needs-decision`** or **`blocked-by-human`** bucket instead, also run it, passing `--expect-bucket <needs-decision|blocked-by-human>` (choose the one matching value): without this, the two mechanical checks below that key off the `authoring-bucket` marker (see [Authoring-bucket marker](#authoring-bucket-marker)) never actually fire in practice, since a non-`ready` body is otherwise never run through this gate at all — exactly the gap that let #2636/#2637 publish without their required label (#2639 follow-up). `--expect-bucket` requires the matching marker to be present, failing when it is absent or disagrees; omit it for a `ready` publish or an edit to an already-published legacy body, where the marker stays optional and fail-safe on absence as documented in that section. `deferred` and `out-of-scope` bodies are not audited by this gate either way. It mechanically re-checks a subset of the structural rules this contract states in prose — the autopilot-suitability marker's exactly-one/coherent-value rule, the one-directional check that a suitability score of `1` (or an `authoring-bucket: blocked-by-human` marker, when present) carries the configured `blocked-by-human` label (it does not check the reverse: a non-`1` score paired with the label still passes), the equivalent one-directional check for `authoring-bucket: needs-decision` and the configured `needsDecisionLabelName` label, markerPrefix consistency across every authoring marker, the declared shape's required section headings, the roadmap-id/blocked-by dependency-marker rules, and visible/hidden line agreement for the suitability and effort footers — so a weak model does not have to hold every rule in its head at once while drafting. The linter also emits one **advisory, warning-severity-only** finding (`prose-dependency`): it flags an issue/PR reference (`#<digits>` or a full GitHub issue/PR URL) that appears near coordination language (for example "before", "after", "once", "until", "predates", "gate"/"gated", "requires", "lands first") with no corresponding encoding for that reference as one of four recognized forms: a `Blocked by #NNN` line, a `Depends on #NNN` line, a task-list checkbox item (`- [ ] #NNN`), or a `Refs #NNN (non-blocking)` line — the same forms `extractBlockedByIssueNumbers` / `extractDependencyIssueNumbers` / `extractNonBlockingReferenceIssueNumbers` already recognize elsewhere in this contract. A task-list checkbox counts regardless of which heading it sits under, so a roadmap's own `## Tracks` membership list already satisfies this — it is not a separate "dependency-only" list. Use `Refs #NNN (non-blocking)` (multi-target: `Refs #NNN, #NNN (non-blocking)`) for a reference that is deliberately informational — a roadmap narrative naming a related, currently-blocked follow-up issue with no ETA, for example — never for a real dependency: unlike the other three forms, `discover-roadmap-graph.mts`'s traversal never enters this reference's target at all, so it cannot become an A1.5 closure-audit blocker, and this check treats it as already-encoded the same as the other three forms (#2236). This catches the pattern this contract's own [Hidden human-dependency validation](#hidden-human-dependency-validation) check 4 warns about in prose — a hard precondition stated only in narrative text, not encoded as a real dependency marker. A full-URL reference is inherently local only when it actually names the current repository, since a cross-repo dependency cannot be encoded with these repository-local markers at all — flagging it would recommend an impossible fix. When the caller supplies the current `owner/repo` (`--current-repo`, defaulting to `$GITHUB_REPOSITORY` in CI), a full-URL reference naming a different repository is never flagged; without that context, a full-URL reference is still flagged by default (unchanged behavior) — unless its issue/PR number happens to already appear as a local `Blocked by` / `Depends on` / task-list marker elsewhere in the body, in which case it is treated as already encoded like any other match. A Markdown link's target may carry trailing content after the issue/PR number and before the link's closing paren — a URL fragment (`#issuecomment-123`), a trailing `/`, or a quoted link title (`"..."` or `'...'`) — and the link is still recognized as one match; without this, the label's own bare `#NNN` would otherwise leak through to the bare-`#` check and be misjudged independently of the link's (possibly cross-repo) target. A local `owner/repo#N` shorthand (e.g. `kurone-kito/idd-skill#4321`) is also recognized, but with the reverse default from the full-URL case above: it is flagged only when `--current-repo` is supplied **and** case-insensitively matches `owner/repo`; naming a different repository, or omitting `--current-repo`, always excludes it, since this shorthand was never recognized at all before and a bare `owner/repo` cannot be assumed local without confirmation. A reference-style Markdown link (`[text][ref]` with a separate `[ref]: target` definition elsewhere in the body) is recognized the same way as the inline-link form above, including the same `currentRepo`-based local/cross-repo comparison; a `ref` with no matching definition is left as literal text and falls through to the bare-`#` check like any other unrecognized shape. A quoted link title may contain a backslash-escaped quote matching its own delimiter (`\"` inside a `"..."` title, or `\'` inside a `'...'` title) without ending the title early and losing the rest of the link. An empty or whitespace-only `--current-repo` (or `$GITHUB_REPOSITORY`) is treated the same as omitting it entirely, rather than as a known repository that can never match. A nested/child list item's reference is evaluated together with its full ancestor chain's coordination-language text instead of being scoped away from it, while a sibling bullet at the same indentation — nested or top-level — still starts its own separate scope, so a tight list (no blank line between sibling items) never reads two consecutive bullets' coordination language as one continuous sentence. This holds for every nested child under a given parent, at any depth — not only the first. A continuation line resuming at an ancestor's own indentation, after a deeper child has already opened, is attributed to that ancestor rather than the deepest open child. A loose list (a blank line between sibling items) preserves the same ancestor scope across the blank line, as long as the following item's own marker is no deeper than whatever is still open at the end of the preceding item — a same-depth sibling, or a resumption at a shallower ancestor's own level — and the preceding item ends in a list marker rather than trailing plain prose (once plain prose follows the last marker, the list reads as already having ended, so the blank line is not bridged). A blank line directly followed by a more deeply indented marker is also not bridged, to avoid grafting an unrelated item onto an open ancestor as a false child. Unlike every other check above, a `prose-dependency` warning never flips `passed` to `false` and never changes the linter's exit code: it prompts the author to either convert the prose into a proper dependency marker or consciously confirm the reference is a mere breadcrumb. ```sh node scripts/audit-authored-issue.mjs --shape <orphan|roadmap|child> \ --marker-prefix <resolved-target-prefix> \ --body-file <path-to-drafted-body> [--label <label>]... \ [--expect-bucket <needs-decision|blocked-by-human>] ``` Or, for npx/package-manager profiles, the equivalent `idd-audit-authored-issue` command. Pass `--stdin` instead of `--body-file` when the drafted body is not yet written to disk. **Always pass `--marker-prefix`** with the prefix resolved under [Target marker prefix](#target-marker-prefix): without it, the linter falls back to reading `.github/idd/config.json` from the current working directory, and if that file is missing or unreadable — for example when running from an installed bundle without a local checkout of the target repository's config — it silently defaults to this source repository's own `idd-skill` prefix, which produces a false pass or fail against the wrong prefix instead of an error. **No helper runtime available (`instructions-only` profile).** The linter cannot run without Node.js and the vendored `scripts/` directory, and an `instructions-only` install is a first-class supported fallback, not a degraded one. Unavailability is never a waiver: manually re-verify the same checks listed above against this contract's prose and the [Draft schemas](#required-draft-content) before publishing. A `passed: false` report (or non-zero exit, or a failed manual re-verification) means the draft is not ready to publish yet, regardless of how complete the narrative reads — fix every reported finding and re-run before treating the issue as `ready`. The linter (or its manual equivalent) is a mechanical structural check, not a substitute for the judgment-based checks above (human-dependency isolation, codebase fidelity, reuse-first) — passing it is necessary, not sufficient, for `ready`. ## Autopilot-suitability score > **Status.** Active contract. Authoring **emits** the score footer > and Discover **ranks and routes** candidates by it (roadmap #759, > fully merged). The score stays advisory: it never bypasses the > A4.5/A5 gates and is fail-safe on absence. Authored issues carry a persisted **autopilot-suitability score** from 1 to 5 (higher = more autopilot-suitable). It is the durable, graded form of the **Autonomous completion** execution axis: the author makes the judgment once, while context is fresh, so the Discover phase can rank and route candidates by a cheap read instead of re-deriving autonomy per candidate. | Score | Meaning | Typical signals | | ----- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | 5 | Autopilot-ideal | Fully specified; deterministic verification (tests/lint/CI/mechanical); no external systems; no human judgment; isolated | | 4 | Strongly autopilot-suitable | Well-specified and verifiable; minor ambiguity resolvable from repo context; no external/human dependency | | 3 | Borderline / mixed | Autopilot can likely finish but with notable judgment, weaker verification, or review-attention risk | | 2 | Mostly human | Agent may draft a partial result; completion needs human judgment, an asset, or review the agent cannot supply | | 1 | Human-only | Interactive credentials, real deployment, subjective/design/product judgment, or external coordination | Scores below the configured discovery floor (`autopilotSuitability.floor`, default `3`) designate **human-oriented issues**: in autopilot runs Discover routes them to humans rather than autopilot. The score is recorded as a **footer at the end of the issue body** — a visible line paired with a hidden, prefix-aware machine marker, mirroring the `claimed-by` convention (visible note + HTML marker): ```text --- _Autopilot suitability: N / 5 -- higher is more autopilot-suitable; below the configured floor is human-oriented._ <!-- {marker-prefix}-autopilot-suitability: N --> ``` Binding rules: - **Authoritative value = the HTML marker**, read prefix-aware via `createMarkerRegex(markerPrefix, "autopilot-suitability")` exactly as `roadmap-id` / `blocked-by` are. `N` is an integer 1-5. The visible line is a human-readable mirror authoring keeps in sync; discovery parses only the marker. - **Authoring marker, not operational marker.** This is body content like `roadmap-id`; it must never be added to `OPERATIONAL_MARKERS` in `scripts/protocol-helpers.mjs` or subjected to F4 minimization. - **One source of truth.** A score of `1` must agree with the configured `blocked-by-human` label (default `status:blocked-by-human`); never publish a contradiction. - **Advisory, never a gate.** The score only ranks/routes candidates. The A4.5 suitability gate and A5 claim safety checks still run unchanged on whatever issue is selected; a high score never bypasses a gate. - **Fail-safe on absence.** A missing, non-integer, or out-of-range marker means "no score": Discover evaluates the issue the normal way and never skips it. Pre-existing issues with no score keep flowing. Backfill is opportunistic: when an existing open issue without a score footer is next edited by the authoring flow, add one. No bulk backfill of the existing backlog is required. The claim-state precondition still applies — never add the footer to the body of an actively-claimed or open-PR issue; defer it to a follow-up instead. ## Effort hint Authored issues may also carry an **effort hint** — an author-time `S | M | L` size estimate, recorded once while context is fresh, that Discover consumes as a **soft selection tie-breaker** so autopilot tends to clear small issues first and leave large ones for a fresh session. Effort is distinct from the autopilot-suitability score: suitability captures _autonomy_ (can an agent finish unattended), while effort captures _size_ (a fully-autonomous issue can still be large). | Hint | Scope | Uncertainty | Example signals | | ------------ | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | **S** Small | Touches one module or a small, contained file set | Little to no open design decision remains once the plan is drafted | One module / a few files; a single reviewable change; little or no review back-and-forth expected | | **M** Medium | Touches a helper plus its callers, or one instr -
draft-patterns.md 23.8 KB
# Draft Patterns Load this file after reading [`contract.md`](contract.md) when you need concrete examples or a quick chooser for output shapes. ## Example triggers - "Break this request into IDD-ready issues before we implement anything." - "Draft a roadmap for this feature and split the ready work." - "Turn this broad request into reviewable orphan issues." - "Check whether an existing issue should be extended instead of opening a new one." ## Output chooser Draft an orphan issue only when one autonomous task can finish the work and the target repository discovers orphans (`issue-scope: roadmap-first`, the default, via the orphan fallback, or `orphan-first`). If the repository uses `orphan-first-policy: maintainer-approved`, include a post-publication approval step after the final issue content is stable. If the repository sets `issue-scope: roadmap` (roadmap-only) or a public repository uses `orphan-first-policy: public-disabled`, draft a roadmap package instead. If the repository keeps the broader secure-by-default issue-author approval gate, use the same post-publication approval note whenever the issue author will not be self-authorizing under the repository's `maintainer-approval-actors` policy. The configured ready label from `approvalSignals.readyLabelName` (default: `idd:ready`) is accepted according to `approvalSignals.labelFreshnessMode` (`presence-only` by default, optional `event-freshness`), while standalone `IDD ready` comments from a maintainer approval actor must stay fresh against the latest issue content and generated-plan update (or an equivalent draft-stability signal). Until that approval condition is satisfied, later discovery should treat the issue as part of the approval-needed fallback bucket instead of the normal ready-to-start set. Draft a roadmap plus sub-issues when the request needs visible sequencing, parallel tracks, or multi-session handoff. Draft only stable non-ready buckets when the work still depends on a human decision, missing asset, or unclear verification. ## Under-clarification stop rule Before drafting a `ready` issue, confirm you can name a concrete surface to edit and an objective verification for it. If, after bounded clarification, you still cannot, route the candidate to `needs-decision` or ask — do not publish a confidently-vague `ready` issue. Reliability over speed. This is an Intake-phase gate on your own confidence, not a wording judgment on an already-written draft — it is distinct from the "Under-specified" band in the next section, which assesses a body that has already been drafted. **Example**: "Improve the error handling in the API layer" fails this check — no concrete surface, no objective verification — even before you consider how detailed to write the body. Ask which endpoint or module, and what the observable failure mode is, or route the request to `needs-decision` instead of guessing at a plausible-sounding scope. ## Specificity target Execution-ready issue drafts should land in a middle band: - **Under-specified**: a high-range model still has to infer the real implementation shape because the issue names only a vague goal, omits the likely surface to edit, or leaves verification too loose. - **Target range**: a solid mid-tier cloud model can hold a stable plan without extra clarification. The issue points at the relevant document, schema, or code surface, explains the constraint, and keeps acceptance criteria verifiable without dictating every edit. - **Over-specified**: even a lightweight model could follow the issue as a script because the body prescribes exact edit order, wording, or implementation steps that reviewers do not need. If a draft feels like it needs a high-range model just to guess the intended change, it is still too vague. If it reads like line-by-line assembly instructions, it is too detailed. ## Specificity checklist before publishing Before you publish a ready issue, confirm: - the title and background name the concrete artifact or surface that should change - a mid-tier cloud model could choose a stable implementation direction without hidden repository archaeology or a follow-up clarification loop - the acceptance criteria verify the outcome, not a step-by-step implementation recipe - candidate files, examples, and notes act as cues rather than an exhaustive script - removing one concrete detail would make the issue feel high-range-only, while adding more detail would start turning it into a lightweight model script ## Mechanical pre-publish gate Before you publish a drafted **ready orphan, roadmap, or child** body, run the `audit-authored-issue` linter against it when a helper runtime is available. It mechanically catches shape and marker mistakes — a missing or duplicated autopilot-suitability footer, a wrong markerPrefix, a missing required heading for the declared shape, a malformed dependency marker — that a confident narrative can otherwise mask: ```sh node scripts/audit-authored-issue.mjs --shape orphan \ --marker-prefix <resolved-target-prefix> --body-file draft.md ``` Before newly publishing a body into the `needs-decision` or `blocked-by-human` bucket instead, also run it, adding `--expect-bucket <needs-decision|blocked-by-human>` (choose the one matching value) — without it, the marker/label checks that key off `authoring-bucket` never fire, since a non-ready body is otherwise never run through this gate at all. Passing `--expect-bucket` also skips the ready-shape-only checks (the suitability footer and required headings) that a bucket body like `#431` below is never expected to carry: ```sh node scripts/audit-authored-issue.mjs --shape orphan \ --marker-prefix <resolved-target-prefix> --body-file draft.md \ --expect-bucket needs-decision \ --label status:needs-decision ``` **Always pass `--marker-prefix`** with the prefix resolved under [contract.md's Target marker prefix](contract.md#target-marker-prefix): without it, the linter falls back to reading `.github/idd/config.json` from the current working directory, and silently defaults to this source repository's own `idd-skill` prefix when that file is missing or unreadable — producing a false pass or fail against the wrong prefix instead of an error. Use `--shape roadmap` or `--shape child` for those shapes, `--stdin` instead of `--body-file` when the draft is not yet on disk, and `--label <name>` (repeatable) to pass proposed labels for the check that a suitability score of `1` carries the configured `blocked-by-human` label (default `status:blocked-by-human`), unless an `authoring-bucket: needs-decision` marker substitutes the configured needs-decision label instead (see [Authoring-bucket marker](contract.md#authoring-bucket-marker)); use `--config <path>` to point at a policy that overrides the label name — this check is also one-directional, it does not flag the reverse, a non-`1` score paired with the label. Fix every reported finding and re-run before publishing; a `passed: false` report means the draft is not ready yet, regardless of how complete the narrative reads. **No helper runtime available (`instructions-only` profile):** the linter cannot run. `instructions-only` is a first-class supported fallback, not a waiver — manually re-verify the same checks against [contract.md's Mechanical pre-publish gate](contract.md#mechanical-pre-publish-gate) before publishing instead. ## Hidden human-dependency quick check Before you publish a `ready` issue, confirm: - the implementation does not still depend on unresolved credentials, access, or unavailable infrastructure - unresolved product, policy, or design choices have been routed to `needs-decision` instead of being buried in implementation steps - acceptance criteria use objective verification, while optional post-implementation review stays optional - roadmap narrative does not hide human-dependent work that belongs in a stable bucket or approval-needed hold - dependency markers represent true start blockers rather than grouping related work ## Codebase-fidelity quick check Before you publish a `ready` issue, confirm: - when the issue reuses an existing identifier or field name, the specified value matches that name's established semantics in the codebase — it does not overload a name with a new shape or source; remedy: mint a new, distinctly named field instead (see [contract.md's worked example](contract.md#codebase-fidelity-validation)) - values that are mutable at runtime are flagged to specify a live read at the point of use rather than a one-time capture at construction ## Example orphan issue - `## Background` or `## Goal` - `## Proposed change` - `## Acceptance criteria` - optional `## Candidate files` — see [contract.md's Candidate files format](contract.md#candidate-files-format) for the exact parse contract before populating it - an autopilot-suitability footer at the end of the body (visible line + `<!-- <marker-prefix>-autopilot-suitability: N -->` marker) Use this shape when the work is narrow enough to pass the IDD viability gate on its own and the target repository can actually discover orphan issues (`issue-scope: roadmap-first`, the default, via the orphan fallback, or `orphan-first`). If the repository sets `issue-scope: roadmap` (roadmap-only), prefer a one-item roadmap package instead of publishing a standalone orphan issue. ## Example roadmap package Roadmap issue: - `## Goal` - `## Background` or `## Why this matters` - `## Tracks` - `## Success criteria` - one `<!-- <marker-prefix>-roadmap-id: ... -->` marker - an autopilot-suitability footer at the end of the body Child issue: - title with a concrete task summary - `## Background` - `## Proposed change` - `## Acceptance criteria` - `## Candidate files` - optional dependency line or sequential roadmap marker when needed - an autopilot-suitability footer at the end of the body Keep ready child issues in the roadmap task list rather than grouping them with hidden dependency markers. ## Nested roadmap chooser note Use a nested roadmap when one roadmap track needs its own coordination boundary, active child list, or multi-session handoff. Keep the parent roadmap pointing to the nested roadmap, and keep the nested roadmap pointing to the leaf execution issues it coordinates. Parent roadmap `## Tracks` excerpt: ```md - [ ] #510 — backend track roadmap ``` Nested roadmap `#510` `## Tracks` excerpt: ```md - [ ] #511 — define the backend contract - [ ] #512 — wire the contract into Discover ``` Treat `#510` as a coordination/audit node, not as a normal execution issue. Do not replace that relationship with `Blocked by #NNN` or `<!-- <marker-prefix>-blocked-by: ... -->` only to group the leaf issues under the parent roadmap. ## Dependency minimization examples ### Natural parallel decomposition Roadmap `## Tracks` excerpt: ```md - [ ] #401 — update the issue-authoring contract - [ ] #402 — add draft-pattern examples _Parallel note: #401 and #402 can proceed independently once the roadmap exists because neither task depends on the other's output._ ``` This is the preferred shape for sibling tasks that can be reviewed and verified independently. The roadmap keeps both tasks visible in its task list, and the short note explains the safe parallelism without adding a fake `Blocked by` edge. **Caveat — shared CI check definitions.** File-disjoint tracks are not automatically execution-order-independent: if one track edits a shared CI check's own workflow _definition_ (e.g. a `.yml` file), any other in-flight track whose CI run relies on that check inherits a hidden ordering dependency, even though the tracks' own edited files never overlap. `gh run rerun` re-resolves against the PR branch's own copy of the workflow file, so a fix merged to `main` on a sibling track stays invisible until the dependent branch pulls it in (see `.github/instructions/idd-ci.instructions.md`'s Rerun mechanics). Note this dependency in the roadmap's parallel note rather than assuming disjoint files always mean safe parallelism. ### Artificial decomposition Bad serial chain: ```md #401 — update the issue-authoring contract #402 — add draft-pattern examples Blocked by #401 ``` This is over-serialized when `#402` can be reviewed and verified without waiting for `#401`. Do not create a serial chain just to make the order feel tidy. Bad split for parallelism: ```md #410 — add one checklist bullet #411 — add one example paragraph #412 — add one anti-pattern sentence ``` This is an artificial split when the three edits form one natural, cohesive authoring change. Do not break a single reviewable task into multiple sibling issues only to widen parallel execution. ### Finalize or verify track that asserts sibling-produced state Anti-pattern — prose sequencing only: ```md #450 — finalize the config, reconcile the docs, verify the combined result Blocked by #440 _Runs after the wiring tracks #441-#445._ ``` The hard `Blocked by #440` names only the build foundation, and the after-the-siblings ordering lives in prose. Once `#440` closes, Discover reports `#450` startable and A4.5 passes it (Actionability inspects the body, not completability) — but its acceptance criteria assert state that only the unmerged `#441`–`#445` produce, so claiming it means failing acceptance or doing the siblings' work. Correct — encode `Blocked by` on each sibling whose output the acceptance criteria depend on: ```md #450 — finalize the config, reconcile the docs, verify the combined result Blocked by #440 Blocked by #441 Blocked by #442 Blocked by #443 Blocked by #444 Blocked by #445 ``` Now Discover and A4.5 defer `#450` until every sibling merges, so it is claimed only when its acceptance criteria can actually pass. **Prefix-first**: resolve `<marker-prefix>` from the target repository's onboarding or IDD docs before emitting any of these markers — `roadmap-id`, `blocked-by`, `autopilot-suitability`, or `effort` — not just the dependency markers shown above. Never default to `idd-skill` in an installed bundle; use it only when the target repository actually configured that prefix. ## Human-dependency isolation examples The examples below show how to move unavoidable human side effects to explicit boundaries rather than hiding them inside a ready execution issue. See [`contract.md`](contract.md) for the routing rules (`needs-decision`, `blocked-by-human`, `deferred`, approval-needed). ### Front-loaded pattern Use this when coding cannot start safely until a person provides a decision, credential, permission, or external setup. **Bad (mixed)**: One execution issue that mixes implementation with an unresolved credential gap: ```md ## Background Add Stripe webhook support. ## Proposed change Implement the `/api/webhooks/stripe` endpoint. ## Acceptance criteria - Stripe webhook secret is configured in production. - Endpoint returns 200 for valid events. ``` This fails the IDD viability gate: the "Stripe webhook secret is configured in production" criterion requires a person to perform an action in an external system before the issue can be verified. The agent cannot autonomously confirm the outcome. **Good (front-loaded)**: Separate the human-dependent setup into its own issue, then write the autonomous implementation issue that depends on it. The `## Tracks` task list should contain only ready execution issues; route the non-ready blocker to a separate section or a stable non-ready bucket. Roadmap body excerpt: ```md ## Non-ready prerequisites - **[blocked-by-human]** #431 — obtain Stripe webhook secret for CI _(must close before #432 can start)_ ## Tracks - [ ] #432 — implement /api/webhooks/stripe endpoint ``` `#431` — `blocked-by-human` issue: ```md ## Background The Stripe webhook endpoint (tracked in #432) needs a test-mode webhook secret in CI so automated tests can verify the signature check. ## Required action 1. Create a test-mode Stripe webhook in the Stripe dashboard. 2. Store the signing secret in the `STRIPE_WEBHOOK_SECRET` repository secret. 3. Reply on this issue with the secret name once added. ## Ready signal Close this issue after confirming the secret is available in CI. <!-- {marker-prefix}-authoring-bucket: blocked-by-human --> ``` `#432` — autonomous execution issue (Blocked by #431): ```md ## Background Parent roadmap: #430 Blocked by #431 ## Proposed change Add POST /api/webhooks/stripe that validates the Stripe-Signature header and dispatches known event types. ## Acceptance criteria - Handler validates the webhook secret sourced from CI `STRIPE_WEBHOOK_SECRET`. - Tests use the Stripe test-mode fixture and pass without manual setup. - `pnpm test` and `pnpm run lint` pass in CI. ## Candidate files - `src/routes/webhooks/stripe.ts` ``` The autonomous issue is fully verifiable in CI once the credential exists. It does not mention a person setting up anything inside its acceptance criteria. ### Back-loaded pattern Use this when post-implementation work is subjective review, publication choice, optional polish, or a sign-off that should not block an otherwise verifiable core change. **Good (back-loaded)**: Separate the implementation from the optional human-gated step: Roadmap body excerpt (the `## Tracks` list contains only the ready execution issue; the deferred step is a narrative note, not a task-list entry): ```md ## Tracks - [ ] #441 — add front-loaded/back-loaded draft-pattern examples ## Deferred The website landing page could be updated to showcase the new examples, but that publication decision requires maintainer approval and is not a blocker for merging #441. Track in a follow-up if approved. ``` `#441` — autonomous execution issue: ```md ## Background Parent roadmap: #440 The draft-patterns reference file does not yet show front-loaded or back-loaded human-dependency isolation patterns. ## Proposed change Add a "Human-dependency isolation examples" section to `skills/issue-authoring/references/draft-patterns.md`. ## Acceptance criteria - The section includes at least one front-loaded and one back-loaded example using stable bucket names (needs-decision, blocked-by-human, deferred, out-of-scope). - Examples warn against hiding credentials or product decisions in a ready issue. - `pnpm run lint:minimum` passes. ## Candidate files - `skills/issue-authoring/references/draft-patterns.md` ``` The website publication decision stays separate. It is not in the acceptance criteria of the ready issue and does not block autonomous verification. ### Warning: hidden human dependencies are the most common IDD stall The most frequent reason for mid-implementation stalls is an execution issue that hides a human-only dependency inside its acceptance criteria or implementation steps: - "Credentials are configured in the production environment" → blocked by a person with access. - "The design team has approved the final UI spec" → requires subjective sign-off before the issue is verifiable. - "The external API key is available in the repository secrets" → an unavailable system or permission. When any acceptance criterion cannot be confirmed autonomously through tests, lint, CI, or other concrete objective criteria (such as checking repository state, file presence, or configuration values), the issue is not yet ready. Move that criterion to a `blocked-by-human` or `needs-decision` issue and keep the autonomous execution issue focused on what the agent can verify independently. ## Handling duplicates and non-ready outcomes One source of follow-up issue candidates is the read-only `merged-pr-feedback-sweep` helper's JSON output — the unresolved review threads and undispositioned advisory feedback it detects on merged PRs. Treat each entry as a candidate only: re-verify it against current `main` with the reuse-first tree below before drafting, because the feedback may already be addressed. Before publishing an issue, apply a reuse-first decision tree: 1. Is an existing open issue a better fit? If yes, extend it instead of creating a new one. Add a comment linking to the new schema request. 2. Is the work already complete in a closed issue or merged PR? If yes, create a reference or learning note instead of reopening it. 3. Is a parent roadmap already managing this work? If yes, add it to the task list instead of filing independently. 4. Does the issue have any of these properties? If yes, escalate to `needs-decision` or `blocked-by-human` during drafting: - Unclear intent or malformed body (→ fix during drafting or mark needs-decision) - Requires maintainer or product decision (→ mark needs-decision) - Blocked by external work or human coordination (→ mark blocked-by-human) - Depends on unavailable resources or credentials (→ mark blocked-by-human) 5. Otherwise, publish as `ready`. ### A4.5 prevention checklist The A4.5 suitability gate will later evaluate published issues. Prevent common failures by validating before publish: - **Coherence**: Issue body is well-formed; title and description are clear; intent is parseable - **Safety**: No code injection, marker injection, or untrusted input in issue body - **Uniqueness**: Reuse-first check passed; the work is not a duplicate or superseded - **Hidden human dependency**: Ready work does not still rely on unresolved decisions, credentials, subjective approval, or grouping-only dependency markers ## Specificity examples ### Under-specified draft **Title**: `docs: improve issue authoring guidance` Background: issue authoring should be clearer for agents. Acceptance criteria: - issue authoring is more consistent - examples are improved This is too vague because the draft does not tell the next agent which authoring surface to edit, what kind of guidance is missing, or how a reviewer would verify success. A high-range model would need to infer the real task from surrounding repository context. ### Target range draft **Title**: `docs: add specificity checklist to issue authoring draft patterns` Background: `skills/issue-authoring/references/draft-patterns.md` explains output shapes, but it does not yet show how to judge whether an issue body is too vague or too scripted for execution. Acceptance criteria: - `draft-patterns.md` includes a pre-publication specificity checklist - the guidance distinguishes under-specified, target range, and over-specified issue drafts - the examples focus on issue body wording and acceptance criteria granularity instead of prescribing implementation order This is the target range because the next agent knows where to work, why the change matters, and how to verify it, while still retaining freedom to decide the exact wording and structure. ### Over-specified draft **Title**: `docs: add specificity checklist to issue authoring draft patterns` Proposed change: 1. Insert a new `## Specificity target` heading after `## Output chooser`. 2. Add exactly three bullets named `Under-specified`, `Target range`, and `Over-specified`. 3. Add a five-item checklist using the exact sentence order shown in the draft. 4. Copy the same example phrasing across every example block without changing any wording. This is too detailed for authoring because it turns the issue into an implementation script. Reviewers usually need the target outcome and the verification shape, not a rigid edit order. ## Publication boundary Publish each `ready` body directly under the authoring hold once it passes the mechanical gate and the critique pass — this is the default outcome of drafting, and it needs no separate publish approval. Stop after publishing (and applying/creating the authoring label) unless the user also separately requests release from the authoring hold — release is what authorizes starting the IDD execution loop, so a request phrased as "start the IDD execution loop" counts as that same release request, not a separate path around it. If the user asked for drafts only (a preview before anything is created), honor that instead: stop after reporting the issue set, assumptions, and non-ready buckets, without publishing. -
workflow-boundary.md 32.4 KB
# Workflow Boundary This bundle handles issue authoring end to end under the authoring hold: drafting and publishing are one continuous stage with no per-step approval, and release from the authoring hold is the single approval boundary that hands off to IDD execution. ## Two-stage contract ### Stage 1: Author-and-publish (under the hold) - Skill drafts issues in the target repository. Each candidate moves through the readiness buckets: `deferred` → `ready` or an escalation bucket (`needs-decision`, `blocked-by-human`, `out-of-scope`) - Before publishing a `ready` body, bundled skill runs the mechanical `audit-authored-issue` gate and the critique pass (both unchanged and still mandatory) - Bundled skill then publishes directly under the configured authoring label (`issueAuthoring.authoringLabelName`, defaulting to `status:authoring`) — **no prior user approval of the drafted body is required**. If the current request asked only for a preview (drafts to look at before anything is created), bundled skill stops after reporting the proposed set instead of publishing; publishing is otherwise the default outcome of drafting, not an opt-in step - If the label does not exist in the target repository, bundled skill creates it with `gh label create` before first use; label creation or application failure blocks publishing - For existing issues, bundled skill applies the authoring label before updating issue content - For new issues, bundled skill requires a capability-checked publication command that creates the issue with the authoring label atomically and carries an exact hidden publication token for target, anchor, set, and session. If the target runtime cannot provide that operation, stop before creating the issue. Never intentionally create an unlabeled issue for the Stage 1 set - The publication token uses this exact HTML-first format. Generate the opaque `target` id and token for both markers below -- the new issue's own number is not yet known. `anchor` in **both** markers depends on the new issue's role in the set: when this new issue is itself the set anchor, `anchor` reuses that same opaque `target` value (self-reference) -- the `<opaque-anchor-id>` placeholder below depicts this self-anchor case only. For a **non-anchor child**, `anchor` is instead the set anchor's already-resolved real `<owner>/<repo>#<number>` reference, not an opaque id, since the anchor already exists with a known number by the time a child is created (see the bundle's canonical contract, `references/contract.md`'s "New-issue ownership" section, for the full rule): ```html <!-- <marker-prefix>-authoring-publication: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token> --> ``` The originating Stage 1 hold uses this append-only publication-intent record, whose `anchor` field follows the same self-anchor/non-anchor-child rule above: ```html <!-- <marker-prefix>-authoring-publication-intent: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token>; journal=<owner>/<repo>#<number>; issue=<owner>/<repo>#<number>|none; actor=<trusted-marker-actor>; state=<pending|member|cleanup|abandoned> --> ``` Append `state=pending; issue=none` before creation, append the returned issue identity while it remains `pending`, append `member` only after owner-marker verification, and append `cleanup` before any safe-close mutation. Append `abandoned` only after closed/label-absent verification. The append-only replay selects the latest valid record for the exact token tuple; missing, conflicting, or out-of-order records fail closed, while `pending` and `cleanup` remain recovery holds. `journal` is the durable record location. For an existing set, use the verified originating Stage 1 hold; for a standalone set with no existing issue or anchor, use the repository-level authoring journal target configured at `issueAuthoring.journalIssue` in `.github/idd/config.json` (an `owner/repo#number` reference to a pre-existing, durable, comment-only issue) -- an unset `issueAuthoring.journalIssue` only blocks a standalone set; an existing set with a verified Stage 1 hold needs no journal configuration at all. Do not create that journal as part of the same set. If the applicable location cannot be resolved -- no verified Stage 1 hold for an existing set, or `issueAuthoring.journalIssue` unset or unverifiable for a standalone set -- stop with `blocked-by-human` before creating any target. On every paginated replay, require `actor` to equal the API author and verify that actor is a trusted marker login with the required write-level permission or configured bot/app trust. An untrusted, malformed, or conflicting exact-token record is not valid evidence; fail closed and retain the hold. Generate the opaque `target` id and publication token before creation because the new issue's own number is not yet known, and generate `anchor` the same way only when this new issue is the set anchor itself -- otherwise reuse the anchor's already-resolved real reference, per the self-anchor/non-anchor-child rule above. Persist those preallocated IDs, the exact token, and `state=pending` in that journal before issuing the create. After a successful create, attach and verify the returned issue identities before appending the owner marker; an unverifiable pre-create write blocks creation, while an unverifiable post-create attachment leaves the issue held for recovery. Only verified owner-marker acquisition changes it to `member`, and only verified safe close changes it to `abandoned` - Immediately after a new issue is created and labeled, bundled skill appends its `mode=acquire` owner marker with the current set ID, then re-fetches labels, body, and owner comments before treating it as a set member - If an allegedly atomic create unexpectedly returns an unlabeled issue, re-fetch its labels, body, current `claimed-by` state, and paginated owner-marker log before closing. If a trusted claim or owner marker from another session or set is present, do not close or overwrite the exposed issue; report the ownership conflict and stop. If no competing claim is present, apply and verify the authoring label as a safe hold, then re-fetch the claim and owner logs again before closing. If that hold or final re-read cannot be verified, leave the issue open and report the recovery hold. Deletion needs admin permission the authoring agent typically lacks (and `docs/permissions.md` forbids for normal IDD), so it is not the default recovery path - If owner-marker append or verification is uncertain for a new issue, reconcile the returned comment ID and the paginated owner-marker log with bounded retries before closing. If a trusted marker is found, retain the label and recover or reopen the issue as a set member. Otherwise re-fetch labels, body, current `claimed-by` state, and the paginated owner-marker log; if that final read proves no competing claim or owner marker, append `state=cleanup` before closing the issue or removing its authoring label. Re-fetch and verify closed/label-absent state, then append `state=abandoned`. If any disposition or cleanup read is uncertain, retain `state=cleanup`, leave the issue held, and report the recovery hold. - An atomically labeled publication is not set membership until its owner marker is verified. Persist each returned target identity in the durable originating Stage 1 hold before appending the marker. On resume, reconcile recorded identities and only issues carrying this set's exact publication token; an incomplete scan or unmarked match is a recovery hold, so never infer membership or completion from the shared label alone. If the final safe-close read proves no competing claim or marker, append `state=cleanup` before closing the issue or removing its authoring label. Re-fetch and verify closed/label-absent state, then append `state=abandoned`; otherwise leave the identity and label held for recovery. - **Per-target ownership is separate from the hold label.** The configured authoring label is a shared claim-suppression lock, not a session lock. Before editing an existing issue or roadmap, the skill must fetch a fresh target snapshot and resolve its complete claim state, including trusted forced-handoff successors and activation-nonce winners, plus its active `claimed-by` and open-PR state. A trusted forced-handoff successor is active even without a new `claimed-by`; any active execution is a conflict, so do not establish the hold. Apply the label if it is absent, then append a hidden owner comment using the resolved marker prefix: ```html <!-- <marker-prefix>-authoring-owner: target=<owner>/<repo>#<number>; anchor=<owner>/<repo>#<number>; mode=acquire|resume|bootstrap|heartbeat|release|release-guard|release-complete; owner=<opaque-owner-token>; set=<opaque-set-id>; session=<opaque-session-id>; body-sha256=<64-lowercase-hex|none>; snapshot-sha256=<64-lowercase-hex|none>; supersedes=<opaque-owner-token|none> --> ``` _Issue-authoring ownership marker. Do not edit or delete._ Use the same `body-sha256` and `snapshot-sha256` semantics as the portable owner protocol: target markers persist the exact fresh body digest, and the anchor-only `release-complete` marker carries the recomputed set snapshot. For `snapshot-sha256`, compute the SHA-256 digest over the UTF-8 bytes of the whole target set's `<owner>/<repo>#<number>:<body-sha256>` lines. Normalize each `<owner>` and `<repo>` component with `NFC(Unicode-default-lowercase(NFC(component)))`, join them with `/`, and serialize each line with that normalized identity. Sort by the identity's unsigned UTF-8 byte sequence (shorter equal prefixes first), then issue number ascending, and join with a single `\n` and no trailing newline. New markers missing these fields are not valid for a new generation; legacy markers are migration input only. A legacy marker cannot prove completion until its target body and required snapshot are re-fetched and recomputed with the canonical algorithm. For this migration check, that algorithm is the SHA-256 digest of the UTF-8 bytes of `<owner>/<repo>#<number>:<body-sha256>` lines after normalizing each owner/repository component with `NFC(Unicode-default-lowercase(NFC(component)))`, joining the normalized components with `/`, sorting by unsigned UTF-8 identity bytes (shorter equal prefixes first) and then issue number ascending, joining with one `\n`, and omitting a trailing newline. When the stored snapshot digest matches that recomputation, the current verification may accept it. Missing, mismatched, or otherwise unverifiable evidence fails closed. This legacy-marker behavior is preventive; no observed incident yet. Append this HTML-first body with a direct JSON `POST` to the issue-comments endpoint; do not rely on `gh issue comment` or `gh api -f body=` for the owner marker. Verify the returned comment ID and body after posting, then re-read the active claim and open-PR state again. If execution began during acquisition, stop without editing and leave the verified hold for explicit recovery. Owner tokens are per target: never compare a child target's `owner` value literally with the anchor's `owner` value. Every owner-marker log read for a target or anchor must use paginated issue-comment retrieval (for example, `gh api --paginate` or an API equivalent) and deterministic GitHub comment order (`created_at`, then comment ID); never rely on a single API page. Resolve the set anchor before appending any target marker. `anchor` records the canonical owner/repository/issue identity of that anchor; the anchor's own marker uses its `target` as the `anchor`, and every other marker in the set repeats the same value. A missing or mismatching `anchor` makes a marker invalid for set membership, resume, or release. A legacy marker without an anchor cannot resume a multi-target set; when no parent roadmap identifies the anchor, stop and bootstrap a fresh explicitly designated anchor instead of choosing a different lead implicitly. Only a trusted target-repository marker actor makes a marker valid: the current authenticated actor after posting and verifying it **and** passing a Write/Maintain/Admin permission check, a configured trusted bot or app, or an explicitly enabled Write/Maintain/Admin collaborator. Comment-only access is insufficient. If permission cannot be verified and no explicit bot/app trust applies, ignore and report the marker; syntax alone never grants ownership. For `acquire`, `bootstrap`, and `resume`, `owner` is a newly generated opaque per-target owner token; `supersedes=none` for `acquire` and `bootstrap`, while `resume` names the prior owner token. For any owner marker needed by a later session, the author's trust must also be re-evaluable from durable policy: `trustedMarkerActors`, a configured trusted bot or app, or an explicitly enabled collaborator whose permission can be re-read. The current-session actor path is provisional and cannot make a historical marker trusted by itself. Without a durable trust source, leave the label and hold in place and report recovery; do not treat the marker as set membership or ownership evidence. For `release`, retain the current owner token in `owner` and set `supersedes` to that same current owner token; `supersedes=none` is invalid for a release marker. For `heartbeat`, retain the current owner, set, and anchor, set `supersedes` to that same owner token, and do not open or close a generation; it only renews the current owner's freshness. `release-guard` is valid only on the set anchor. It retains the anchor's current owner, set, anchor, and session, and sets `supersedes` to that owner token. Append and reconcile it after release-marker preflight but before the first label removal. It is the Discover-visible guard for a provisional set release and does not close any generation. `release-complete` is valid only on the set anchor. It retains the anchor's current owner, set, anchor, and session, and sets `supersedes` to that owner token. Append and verify it only after every target's release marker and label removal has been verified. It is the durable terminal event for the set: a later reapplication of the authoring label must start a fresh set generation rather than resuming the completed set. Within an open generation, the first valid acquisition, bootstrap, or resume marker by GitHub comment order wins. A `resume` marker opens a new generation only for the exact interrupted set and matching prior owner token. A `release` marker must match the current owner and set, but remains provisional while its set release is in progress; an individual label removal never closes that target's generation. During the owning set's own Stage 2 (observed 2026-09-09, kurone-kito/idd-skill#2791), the heartbeat renewal and the pre-removal ownership recheck must treat that set's provisional `mode=release` markers and the anchor's `mode=release-guard` as the expected state rather than as a competing generation; the acquisition-time rule that a target carrying a release marker cannot be re-acquired until that release's `release-complete` is found applies to a later session's fresh acquisition of the child, not to the releasing set's own rechecks or to a resume of the exact interrupted set, which stays the established recovery path when `release-complete` is missing; the releasing set's own rechecks never change the current winner, unlike a valid resume marker for that exact set, which does. Only after a fresh re-read verifies every target's release marker and label removal and the anchor's `release-complete` marker does the set-level release close all target generations, after which a later `acquire` starts a new generation. The active generation's freshness is the GitHub `created_at` of its latest trusted acquisition, bootstrap, resume, or heartbeat marker; a resume marker refreshes that clock, and the label event alone never supersedes a fresh owner marker. The current generation's winner owns the target; any other session must stop without editing and leave the label in place. Do not edit or delete owner comments. - For a new Stage 1 set, generate one opaque set ID and reuse it in every owner marker for that set. When resuming an interrupted set, recover and verify its persisted set ID from the exact trusted owner markers and reuse it instead of generating a replacement. Persist the resolved anchor identity in every marker as well. Before resuming, enumerate the anchor's `## Tracks` and a repository-wide paginated issue-comment scan scoped to trusted owner markers whose exact `anchor` and `set` match; merge the results by comment order and block if enumeration is incomplete. These append-only comments are the durable set, anchor, and target membership record; a resume may include only targets whose valid markers identify that exact set and anchor. Never infer set membership or the anchor from the shared label alone. - A non-anchor target cannot prove that its previous set finished from its local owner-marker log alone. Before accepting a fresh `mode=acquire` for a child whose prior generation has a `mode=release` or `mode=release-guard` marker, follow its exact persisted `anchor` identity and fetch that anchor's paginated owner-marker log. Require a trusted `mode=release-complete` marker for the exact anchor/set/session generation represented by the child's current release marker, including the current anchor owner for that release generation; never accept an older or newer set's completion. Owner tokens are per target, so do not compare the child owner token literally with the anchor owner token. If the completion marker is absent, malformed, or cannot be fetched conclusively, treat the prior release as interrupted: do not acquire the child as a new set, and instead resume that exact set or leave its hold in place. A child log, an absent label, or a session-local read is never completion evidence. Once the anchor completion is reconciled, the old set is closed and a new acquisition may start a new generation. - Acquire one set anchor before acquiring any other target: when the set has a parent roadmap, first publish a valid roadmap shell under the authoring hold, with all required roadmap headings/markers and an empty `## Tracks` list allowed only until child issue numbers exist; then acquire and verify that roadmap as the set anchor. Only after that anchor is verified may the session publish and acquire child targets, and it must wire their real numbers into `## Tracks` before release. When no parent roadmap exists, use the designated lead target as the anchor. The anchor winner serializes acquisition for the whole set; no session may publish or acquire children independently. Before each child acquisition or resume, append and verify a same-owner heartbeat on the anchor, re-fetch the anchor's paginated log, and require its current owner token, set, anchor, and session. Append the child marker only after that validation, then immediately re-fetch both anchor and child and require the same anchor ownership; if either read changes, leave the child hold in place and stop rather than forming a split set. If any target cannot be acquired under that anchor, stop all body and relationship edits, leave labels and append-only markers in place, and require an exact verified resume of that set rather than allowing a split ownership set. After each `acquire`/`resume`/`bootstrap` marker POST, wait the configured `claim.verifySettleDelay`, replay the full paginated log, and choose the winner by deterministic comment order; an immediate local read never authorizes edits. Apply the same settle delay and full paginated replay after every heartbeat before it authorizes an edit or label removal. - **Re-read before every edit.** Immediately before each body or roadmap relationship update, re-fetch both the target and the set anchor (the same fresh snapshot serves both roles when the target is the anchor). Require each target's expected owner token independently, plus the same set, anchor, and owning session, and require the expected body/label snapshot on the edited target to remain unchanged. Also re-read its active `claimed-by` and open-PR state; any active claim or open PR is a conflict. An unexpected change, competing owner, malformed owner marker, or inability to prove a unique owner on either target is a conflict: stop without editing, leave the authoring label in place, and record the safe alternative. - **Renew before every edit.** After that conflict check and immediately before the body or relationship mutation, append and verify a trusted `mode=heartbeat` marker for the set anchor first, then re-fetch and verify its current owner, set, anchor, and session. Only after the anchor renewal succeeds, append and verify the edited target's heartbeat when it is a distinct target, then re-fetch both and require each target's expected owner token independently, plus the same set, anchor, owning session, and expected target snapshot. If either heartbeat cannot be posted or verified, or a newer owner appears, stop without editing. A heartbeat never starts a new generation and never authorizes release. - A target already held by another set is unavailable. A later session may resume only when the invocation identifies the exact interrupted set and the hold is past `issueAuthoring.authoringStaleAge`; it must append a `mode=resume` owner marker with a new owner token and `supersedes` value matching the prior owner token before re-running the same acquisition check. For a stale held target with no valid owner marker, append a trusted `mode=bootstrap` marker with the current set ID, a new owner token, and `supersedes=none`; this starts a new generation and is not evidence of any prior set membership. The first valid bootstrap marker wins. Staleness alone never authorizes takeover: use the latest trusted generation marker's GitHub `created_at` for marked targets, and the label event only for legacy-unowned bootstrap. A competing active marker still stops the session. If the target runtime provides an atomic acquisition helper, use it; otherwise this append-only conflict check is mandatory, including for `instructions-only` installs. - **The held issue IS the draft.** In-place body edits, roadmap relationship wiring (publish/acquire the roadmap anchor first, then publish/acquire children and wire their real issue numbers), and re-lint of already-published bodies all happen on the published issue, under the same label — not in a session-local buffer that a later session cannot see - **Interrupted-session guard.** If a Stage 1 session stops before the set is fully wired and stable, the authoring label stays on every issue it already published, and its owner markers stay in place. The label suppresses Discover while the markers preserve set identity and target membership for a later verified resume; a later session must not infer either from the label alone. ### Stage 2: Release (the single approval boundary) - The user's explicit hold-release request is the only approval this bundle's workflow requires — except the narrow review-fix-loop-cutoff auto-release exception in [Authoring hold and release](contract.md#authoring-hold-and-release) — and it authorizes IDD execution for the released issues - Before removing the authoring label, bundled skill runs a release checklist that absorbs the rigor of the dropped middle step: - every child issue is referenced from its parent roadmap's `## Tracks` list - no unsubstituted placeholder (a leftover `#TBD`, template stand-in, or similar) remains in any published body - the `audit-authored-issue` linter (or its manual fallback under `instructions-only`) is green on every published body in the set - Keep the set anchor held until every other target's label removal is verified, and remove the anchor label last. For every target, first re-fetch owner comments during release-marker preflight. If a valid current-owner/set `mode=release` marker already exists, reuse the earliest matching GitHub comment ID; otherwise append one with `supersedes` equal to the current owner token, re-fetch to verify it, and record its comment ID. Complete that preflight for the whole set before removing any label. A retry of an open generation must reuse the recorded or earliest matching marker and never append an indistinguishable duplicate. Then, before removing any label, append or reuse the anchor-only `mode=release-guard` marker and re-fetch the anchor's paginated owner-marker log with bounded retries, requiring the exact current owner, set, anchor, session, and marker body. If that guard is not found conclusively, leave all labels in place and stop. The guard suppresses Discover for the whole set during the provisional label-removal window; it does not close the set. When this release is proceeding under the narrow review-fix-loop-cutoff auto-release exception in [Authoring hold and release](contract.md#authoring-hold-and-release) instead of an explicit human release request, also verify here -- immediately before the first label removal below, whether that removal is a non-anchor target's or the anchor's own -- that the marked target is the sole member of its authoring set: it carries no `<marker-prefix>-roadmap-id` marker (never a roadmap anchor), and a repository-wide paginated issue-comment scan for trusted owner markers whose exact `set` matches finds no sibling target -- the same repository-wide, fail-closed enumeration the resume procedure above requires, since a sibling's marker lives on the sibling's own issue and never appears in the marked target's own comment log; block on incomplete or inconclusive enumeration the same way. If either condition fails, or the scan cannot be completed, the exception does not authorize removing any label for this release; fall back to the ordinary explicit human release-request precondition for the whole set instead. Then, immediately before each label removal, append and verify the set anchor's `mode=heartbeat` first, re-fetching it and requiring its current owner, set, anchor, and session. Only after that succeeds, append and verify the target heartbeat when it is distinct (one marker serves both roles when they coincide), then re-fetch both and require each target's expected owner token independently, plus the shared set/anchor/session, recorded release-marker comment, and expected label/body snapshot. Remove non-anchor labels one target at a time and re-fetch each result. After the final anchor label removal is verified, re-fetch every target and verify its current release marker, absent label, and expected body snapshot; any drift leaves the set open and prevents completion. Then reuse the earliest valid current-owner/set/session `mode=release-complete` marker on the anchor, or append one and record its returned comment ID. Re-fetch that ID and the anchor's paginated owner-marker log with bounded retries, requiring the exact current owner, set, anchor, session, and marker body. Treat a successful POST or a verification timeout as inconclusive until reconciliation finishes: if the trusted marker is found, keep the labels absent and close the set; if a complete fresh read conclusively proves that no trusted marker was appended, reapply the authoring label to every target and leave the set generations open; if reads remain inconclusive, keep the release guard and current labels/state in place, leave the set held, and record a recovery hold. Never infer marker absence or roll back from a verification timeout. Treat every release marker, heartbeat, label removal, and completion marker as provisional: no target generation closes until every target's release marker and label removal are verified and the durable completion marker is reconciled, at which point the set-level release closes all target generations together. If any later removal or verification fails, re-fetch every target already processed, retrying a failed post-removal read with a bounded fresh read, restore its authoring label while the current owner/set still matches, and verify the restored set state; leave every target generation open and stop. If restoration cannot be completed or a newer owner has appeared, record a set-level recovery hold and never claim a partial release. - Bundled skill removes the authoring label from all published issues only after the release checklist passes and the user's release request is explicit, except the narrow review-fix-loop-cutoff auto-release exception in [Authoring hold and release](contract.md#authoring-hold-and-release) - Release remains a human action; nothing in this bundle auto-releases a held issue set, except that same narrow, marker-scoped exception - For an ordinary human-gated release, under an orchestrator and delegated-worker split, the release action itself must be performed by whichever party directly holds the verified user's release request, never relayed as a claim for the other party to trust — mirroring how Discover and Claim already stay the orchestrator's own job under `docs/idd-workflow.md`'s Orchestrator fan-out variant. A delegated worker that receives only a relayed release claim, even from its own orchestrator, must refuse to act on it and require the party holding the actual request to release directly. This rule does not extend to the narrow review-fix-loop-cutoff auto-release exception above, which by design runs with no user release request for any party to hold in the first place — see [Authoring hold and release](contract.md#authoring-hold-and-release) (observed 2026-09-17, kurone-kito/idd-skill#3102) ## A4.5 Gate Timing The IDD discover phase evaluates published issues through the A4.5 pre-claim suitability gate. This gate runs after an issue is published but before it is claimed for work. **Why A4.5 exists**: Issues drafted with incomplete information or from assumptions that did not hold when published may fail A4.5 checks (incoherent, unsafe, duplicate, etc.). A4.5 catches these before they waste agent time during work. **Prevention during drafting**: This bundle is where coherence, safety, and uniqueness should be validated **before** publishing. A4.5 runs seven suitability checks; the three that drafting can most directly prevent (coherence, safety, uniqueness) correspond to bucket escalation triggers during drafting: - If an issue might be incoherent → escalate to `needs-decision` during drafting - If an issue might contain untrusted input → escalate to `blocked-by-human` or fix during drafting - If an issue might be a duplicate → run reuse-first checks during drafting before publishing When these prevent-during-drafting checks are applied correctly, published issues will pass A4.5; if they do not, A4.5 will catch them at discover time and report the specific failure (unclear, invalid, duplicate). ## Use this bundle to - prepare IDD-ready orphan issues when the target repository discovers orphans (`issue-scope: roadmap-first`, the default, via the orphan fallback, or `orphan-first`), including any required `orphan-first-policy` approval handoff - prepare roadmap packages and child issues when work needs visible sequencing or parallel tracks - surface non-ready buckets instead of guessing through blockers ## Do not use this bundle to - start the Discover -> Claim -> Work loop implicitly - treat bundled references as a replacement for repository execution instructions - publish a body that has not passed the mechanical `audit-authored-issue` gate and the critique pass - remove the authoring label from any issue without an explicit release request, except the narrow review-fix-loop-cutoff auto-release exception in [Authoring hold and release](contract.md#authoring-hold-and-release) ## Handoff to execution Once the authoring label is removed from every issue in a released set — via the user's explicit release request, or, for a single marked target only, the narrow review-fix-loop-cutoff auto-release exception — execution is authorized: the repository's normal entry file and routed `.github/instructions/*.instructions.md` phase files (Discover, Claim, Work) may pick up the released issue(s). This bundle does not itself start that loop.
-
-
SKILL.md 19.8 KB
--- name: issue-authoring description: Draft or refine IDD-ready GitHub issues, roadmap issues, and sub-issues before the normal IDD execution loop begins. Use when a request is too large or ambiguous for one reviewable change, when work needs decomposition or dependency encoding, or when the user asks for issue drafting, roadmap planning, or parallelizable task breakdown. --- # Issue Authoring Use this skill to prepare issue-ready work before execution starts. Keep the skill concise and treat the repository docs as the canonical source for the full contract and schema. The canonical source bundle lives in this repository; install copies in the agent-specific skill directory your runtime reads. ## Stable Phases Use two stable phases: 1. **Intake and Clarification** — inspect relevant context, identify ambiguity, run a secondary critique or explicit self-critique, and ask only the questions that block safe issue drafting. Keep clarification bounded; use the repository-local `issueAuthoring.maxClarificationRounds` value when available, otherwise default to 3 rounds. **Under-clarification stop rule**: if, after bounded clarification, you still cannot name the concrete surface to edit or an objective verification for a candidate task, route it to `needs-decision` or ask — do not publish a confidently-vague `ready` issue. Reliability over speed. 2. **Decompose and Draft** — restate the request in implementation terms, split it into atomic tasks, classify readiness, reuse existing issues when safe, and draft the smallest issue shape that preserves dependencies and reviewability. Preserve low-readiness work in stable buckets: ready, deferred, needs-decision, blocked-by-human, and out-of-scope. ## Workflow 1. Read the bundled contract in [references/contract.md](references/contract.md). 2. Reuse or extend an existing issue before creating a new one — but never edit the body of an actively-claimed or open-PR issue (its claimed agent will not pick the change up); cover it with a follow-up issue instead. See the contract's claim-state precondition. 3. Choose the smallest safe output shape: - orphan issue for one ready autonomous task only when the target repository discovers orphans (`issue-scope: roadmap-first`, the default, via the orphan fallback, or `orphan-first`) and any configured `orphan-first-policy` approval step can be completed after drafting - roadmap plus sub-issues for multi-task or multi-session work - stable non-ready buckets for deferred, needs-decision, blocked-by-human, or out-of-scope work 4. **Prefix-first**: resolve the target repository's marker prefix before emitting any authoring marker — `roadmap-id`, `blocked-by`, `autopilot-suitability`, or `effort`. Use the prefix documented by the target repository's onboarding or IDD docs, and ask the user instead of guessing when the prefix is not discoverable. Never default to this source repository's `idd-skill` prefix in an installed bundle. 5. Keep dependencies machine-readable and minimal: - roadmap identity via `<!-- <marker-prefix>-roadmap-id: ... -->` - active child issues via roadmap task-list links - issue-to-issue dependencies via `Blocked by #NNN` - sequential roadmap dependencies via `<!-- <marker-prefix>-blocked-by: ... -->` only when a separate roadmap must close first - keep independent sibling work in roadmap task lists unless a true correctness, availability, or ordering constraint requires a dependency edge 6. Before publishing a ready orphan, roadmap, or child body, run the `audit-authored-issue` linter against it as the mechanical pre-publish gate — see [Mechanical pre-publish gate](references/contract.md#mechanical-pre-publish-gate) in the bundled contract, including the manual fallback for `instructions-only` installs with no helper runtime. Resolve every reported failure before treating the issue as ready. Before newly publishing a body into the `needs-decision` or `blocked-by-human` bucket instead, also run the linter, passing `--expect-bucket <needs-decision|blocked-by-human>` (choose the one matching value) — the same gate section's `--expect-bucket` flag requires the matching `authoring-bucket` marker for that publish, closing the gap where a non-ready body would otherwise never be audited at all. 7. Publish each `ready` drafted body directly under the authoring hold once it passes the mechanical gate (step 6) and the critique pass (the Intake and Clarification phase above) — no separate publish approval is needed. Only skip publishing when the current request explicitly asked for a preview instead. Manage the authoring label for each created or updated issue: - resolve `issueAuthoring.authoringLabelName`, defaulting to `status:authoring` - create the label with `gh label create` before first use when the target repository does not already have it - treat label creation or application failure as a publishing blocker - apply the label before updating an existing issue - acquire per-target ownership before editing an existing issue or roadmap; the shared label is a claim-suppression lock, not an owner token. Follow the append-only owner-marker and re-read protocol in [references/workflow-boundary.md](references/workflow-boundary.md) and stop without editing when ownership or the target snapshot conflicts - for a new Stage 1 set, generate one opaque set ID and reuse it in every owner marker for that set; when resuming an interrupted set, recover and verify its persisted set ID from the exact trusted owner markers and reuse it instead of generating a replacement; never infer set membership from the label alone - when a set includes a parent roadmap, publish a valid roadmap shell under the authoring hold before any child; acquire and verify that roadmap as the set anchor, leaving its `## Tracks` list empty only until child issue numbers exist. Without a parent roadmap, use the designated lead target as the anchor - acquire and verify the set anchor before publishing or acquiring any child; do not acquire children independently, and stop all edits if any target cannot join that anchor's verified set - before each child acquisition or resume, append and verify a same-owner anchor heartbeat (or reuse one — see the heartbeat-coalescing rule below), re-fetch the anchor's paginated log, then append the child marker and immediately re-fetch both anchor and child. Stop with the label in place if anchor ownership changed between those reads - **Heartbeat coalescing** (`issueAuthoring.heartbeatCoalesceWindow`, default `PT2M`): before appending a heartbeat, replay the target's paginated log; reuse the latest trusted marker instead of appending when it is the same owner/set/session, its mode is `acquire`/`bootstrap`/`resume`/`heartbeat`, it is younger than the window, and its `body-sha256` matches the just-fetched body — re-fetch and verify the reused marker exactly as a fresh one. This window never applies to `acquire`, `bootstrap`, `resume`, `release`, `release-guard`, or `release-complete` appends themselves — only a `heartbeat` append may be skipped - persist the anchor's canonical repository/issue identity in every owner marker for the set; the anchor marker points to itself, and a resume must stop if the interrupted set's anchor cannot be proven - immediately before every Stage 1 body or relationship edit, re-fetch both the edited target and the set anchor; require each target's expected owner token independently, plus the same set, anchor, and owning session, and require an unchanged expected target snapshot before editing - immediately before that edit, renew both generations with a trusted same-owner-per-target heartbeat marker (one marker when target and anchor coincide; reuse applies here too), re-fetch and verify both, and stop if renewal or ownership verification fails - create new issues only through a capability-checked publication command that applies the authoring label atomically and carries an exact hidden publication token for target, anchor, set, and session; if that operation is unavailable, stop before creating the issue — never intentionally create an unlabeled issue - the hidden publication token is this exact HTML-first body line. `anchor` reuses the opaque `target` value only when this new issue is itself the set anchor (self-reference); for a non-anchor child, `anchor` is instead the set anchor's already-resolved real `<owner>/<repo>#<number>` reference, not an opaque id (full rule: `references/contract.md`'s "New-issue ownership" section): ```html <!-- <marker-prefix>-authoring-publication: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token> --> ``` - The originating Stage 1 hold uses this append-only publication-intent record, whose `anchor` follows the same self-anchor/non-anchor-child rule: ```html <!-- <marker-prefix>-authoring-publication-intent: target=<opaque-target-id>; anchor=<opaque-anchor-id>; set=<opaque-set-id>; session=<opaque-session-id>; token=<opaque-publication-token>; journal=<owner>/<repo>#<number>; issue=<owner>/<repo>#<number>|none; actor=<trusted-marker-actor>; state=<pending|member|cleanup|abandoned> --> ``` `issue` is the returned canonical issue identity or `none`. Append `state=pending; issue=none` before creation, then append the returned identity while it remains `pending`, append `member` only after the owner marker is verified, and append `cleanup` before any safe-close mutation. Append `abandoned` only after closed/label-absent verification. On resume, paginate the hold log and select the latest valid record for the exact token tuple; missing, conflicting, or out-of-order records fail closed, while `pending` and `cleanup` remain recovery holds. `journal` is the durable record location. For an existing set, use the verified originating Stage 1 hold; for a standalone set with no existing issue or anchor, use the repository-level authoring journal target configured at `issueAuthoring.journalIssue` in `.github/idd/config.json` (an `owner/repo#number` reference to a pre-existing, durable, comment-only issue) -- an unset `issueAuthoring.journalIssue` only blocks a standalone set; an existing set with a verified Stage 1 hold needs no journal configuration at all. Do not create that journal as part of the same set. If the applicable location cannot be resolved -- no verified Stage 1 hold for an existing set, or `issueAuthoring.journalIssue` unset or unverifiable for a standalone set -- stop with `blocked-by-human` before creating any target. On every paginated replay, require `actor` to equal the API author and verify that actor is a trusted marker login with the required write-level permission or configured bot/app trust. An untrusted, malformed, or conflicting exact-token record is not valid evidence; fail closed and retain the hold. Generate the opaque `target` id and token before creation because the new issue's own number is not yet known, and generate `anchor` the same way only when this new issue is the set anchor itself -- otherwise reuse the anchor's already-resolved real reference, per the self-anchor/non-anchor-child rule above. Before issuing the create, persist those preallocated IDs, the exact token, and `state=pending` in that journal. After a successful create, attach and verify the returned issue identities on that pending record before appending the owner marker. If the pre-create hold write cannot be verified, do not create; if the post-create identity attachment cannot be verified, leave the returned issue held for recovery. Transition to `member` only after owner-marker verification or `abandoned` only after the verified safe close and label removal. On resume, match the exact token and persisted identities; an incomplete scan or state mismatch is recovery. - immediately after a new issue is created and labeled, append its `mode=acquire` owner marker with the current set ID, then re-fetch the labels, body, and owner comments before treating it as a set member - an atomically labeled publication is not set membership until its owner marker is verified; persist each returned target identity in the journal before appending the marker. On resume, reconcile recorded identities and only issues carrying this set's exact publication token; an incomplete scan or unmarked match is a recovery hold, so never infer membership or completion from the shared label alone - if owner-marker append or verification is uncertain for a new issue, reconcile the returned comment ID and the paginated owner-marker log with bounded retries before closing; if a trusted marker is found, retain the label and recover or reopen the issue as a set member. Otherwise re-fetch labels, body, current `claimed-by` state, and the paginated owner-marker log; if that final read proves no competing claim or owner marker, append `state=cleanup` before closing the issue or removing its authoring label. Re-fetch and verify closed/label-absent state, then append `state=abandoned`. If any disposition or cleanup read is uncertain, retain `state=cleanup`, leave the issue held, and report the recovery hold - if an allegedly atomic create unexpectedly returns an unlabeled issue, re-fetch its labels, body, current `claimed-by` state, and paginated owner-marker log before closing. If a trusted claim or owner marker from another session/set is present, do not close or overwrite the exposed issue; report the ownership conflict and stop. If no competing claim is present, apply and verify the authoring label as a safe hold, then re-fetch its labels, body, current `claimed-by` state, and paginated owner-marker log again before closing. If that hold or final re-read cannot be verified, leave the issue open and report the recovery hold. Deletion needs admin permission and is not the default recovery path - held issues under the label ARE the drafts: do in-place body edits, roadmap relationship wiring, and re-lint of already-published bodies on the published issue itself, under the same label - if a session is interrupted before the set is fully wired, leave the label and owner markers in place — the label suppresses Discover and the markers preserve the set identity for a later verified resume - read every target and anchor owner-marker log with paginated retrieval and deterministic comment order; never rely on a single API page - after the release checklist passes and the user explicitly requests release — or, for a single target whose body carried the review-fix-loop-cutoff marker at Stage 1 publication time (never a marker added later), the narrow auto-release exception in [Authoring hold and release](references/contract.md#authoring-hold-and-release) — preflight and verify or reuse a matching `mode=release` marker for every target (with `supersedes` equal to the current owner token) before removing any label; record its GitHub comment ID, never append a duplicate on retry, append and reconcile an anchor-only `mode=release-guard` marker before the first label removal, keep the set anchor held, and remove it last. Recheck each target's expected owner token independently, plus the shared set/anchor/session, recorded marker, and expected snapshot immediately before each removal. Renew and verify the set anchor heartbeat first (reuse applies here too), re-fetching its current owner, set, anchor, and session; only then renew and verify the target heartbeat when distinct (one marker when they coincide; reuse applies here too). Remove non-anchor labels one at a time and verify the whole set. After the final anchor label removal is verified, reuse or append the anchor-only `mode=release-complete` marker and record its comment ID. Reconcile that ID and the paginated anchor log with bounded retries; a successful POST or verification timeout is inconclusive. If the trusted marker is found, keep labels absent and close the set. If a complete fresh read conclusively proves that no trusted marker was appended, restore labels for every target and leave every target generation open. If reads remain inconclusive, keep the release guard and current labels/state in place, leave the set held, and record a recovery hold. Never infer marker absence or roll back from a verification timeout. Discover must treat the reconciled release guard as suppressing every target until the anchor completion marker is found. Treat each release marker, removal, and completion marker as provisional until the durable completion event is reconciled. If a later removal or verification fails, retry a failed post-removal read with a bounded fresh read, restore labels for already processed targets while the owner/set still match, verify the restored set, and leave every target generation open 8. Stop at the single approval boundary: release. Publishing under the hold does not by itself authorize starting the IDD execution loop — only the user's explicit release request does, except the narrow review-fix-loop-cutoff auto-release exception in [Authoring hold and release](references/contract.md#authoring-hold-and-release). ## Reference Routing - For the bundled contract, output schemas, and discoverability guard: read [references/contract.md](references/contract.md). - For the bundled two-stage authoring/release contract and the boundary with the IDD execution loop: read [references/workflow-boundary.md](references/workflow-boundary.md). - For concrete drafting patterns and example prompts: read [references/draft-patterns.md](references/draft-patterns.md). - When editing this bundle inside the source repository, keep the bundled references synchronized with the canonical maintenance docs at repo-root `docs/issue-authoring-skill.md` and `docs/idd-workflow.md` (relative links are avoided here since this file is mirrored at a different path depth in `.claude/skills/issue-authoring/`). ## Output Checklist - Preserve low-readiness work in stable buckets instead of dropping it. - Keep acceptance criteria explicitly verifiable. - Keep human-dependent setup, review, and approval work isolated from ready execution issues whenever possible. - Link every active child issue from its roadmap body. - Justify each dependency edge and keep independent sibling work as roadmap task-list entries. - Record reuse or extension decisions when the skill does not create a new issue. - Avoid widening drafting output beyond the user request without saying so. - Run the `audit-authored-issue` linter (or its manual fallback in `instructions-only` installs) against every drafted ready body, and against every body newly published into `needs-decision` or `blocked-by-human` with `--expect-bucket`; resolve every reported failure before publishing. - Name a concrete surface to edit and an objective verification for every `ready` candidate; route anything else to `needs-decision` or ask instead of guessing (the under-clarification stop rule). - Resolve the target repository's marker prefix before emitting any authoring marker; never assume this source repository's `idd-skill` prefix in an installed bundle (the prefix-first rule).
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.