dev-loops 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/agents/dev-loop.md +7 -7
- package/.claude/agents/fixer.md +1 -0
- package/.claude/agents/refiner.md +2 -2
- package/.claude/agents/review.md +2 -2
- package/.claude/commands/loop-enqueue.md +1 -1
- package/.claude/commands/loop-info.md +1 -1
- package/.claude/commands/loop-start-spike.md +1 -1
- package/.claude/skills/copilot-pr-followup/SKILL.md +37 -23
- package/.claude/skills/dev-loop/SKILL.md +6 -6
- package/.claude/skills/docs/acceptance-criteria-verification.md +3 -2
- package/.claude/skills/docs/anti-patterns.md +3 -2
- package/.claude/skills/docs/artifact-authority-contract.md +12 -11
- package/.claude/skills/docs/confirmation-rules.md +1 -0
- package/.claude/skills/docs/contract-style-guide.md +37 -0
- package/.claude/skills/docs/copilot-loop-operations.md +18 -14
- package/.claude/skills/docs/cross-harness-regression-contract.md +2 -2
- package/.claude/skills/docs/debt-remediation-contract.md +1 -1
- package/.claude/skills/docs/epic-tree-refinement-procedure.md +25 -22
- package/.claude/skills/docs/issue-intake-procedure.md +25 -19
- package/.claude/skills/docs/merge-preconditions.md +13 -11
- package/.claude/skills/docs/pr-lifecycle-contract.md +44 -35
- package/.claude/skills/docs/public-dev-loop-contract.md +59 -43
- package/.claude/skills/docs/retrospective-checkpoint-contract.md +18 -5
- package/.claude/skills/docs/spike-mode-contract.md +14 -7
- package/.claude/skills/docs/stop-conditions.md +30 -15
- package/.claude/skills/docs/tracker-first-loop-state.md +13 -8
- package/.claude/skills/docs/validation-policy.md +4 -5
- package/.claude/skills/local-implementation/SKILL.md +48 -142
- package/CHANGELOG.md +6 -0
- package/agents/dev-loop.agent.md +8 -8
- package/agents/fixer.agent.md +1 -0
- package/agents/refiner.agent.md +2 -2
- package/agents/review.agent.md +2 -2
- package/package.json +5 -4
- package/scripts/_core-helpers.mjs +2 -0
- package/scripts/claude/headless-dev-loop.mjs +1 -1
- package/scripts/docs/validate-rule-ownership.mjs +442 -0
- package/scripts/docs/validate-state-machine-conformance.mjs +1092 -0
- package/scripts/github/_review-thread-mutations.mjs +5 -2
- package/scripts/github/create-pr.mjs +119 -10
- package/scripts/github/detect-checkpoint-evidence.mjs +102 -17
- package/scripts/github/list-review-threads.mjs +277 -0
- package/scripts/github/post-gate-findings.mjs +6 -3
- package/scripts/github/request-copilot-review.mjs +92 -26
- package/scripts/github/upsert-checkpoint-verdict.mjs +74 -5
- package/scripts/github/verify-briefing-prefixes.mjs +191 -0
- package/scripts/github/verify-fresh-review-context.mjs +78 -3
- package/scripts/github/wait-pr-checks.mjs +171 -0
- package/scripts/github/write-gate-context.mjs +213 -7
- package/scripts/github/write-gate-findings-log.mjs +54 -2
- package/scripts/loop/copilot-pr-handoff.mjs +29 -2
- package/scripts/loop/detect-change-scope.mjs +2 -2
- package/scripts/loop/detect-copilot-loop-state.mjs +23 -3
- package/scripts/loop/detect-pr-gate-coordination-state.mjs +35 -3
- package/scripts/loop/resolve-dev-loop-startup.mjs +145 -8
- package/scripts/loop/sanctioned-commands.mjs +2 -0
- package/scripts/loop/validate-pr-body-spec.mjs +21 -5
- package/scripts/pages/build-state-atlas.mjs +50 -48
- package/scripts/projects/_resolve-project.mjs +1 -148
- package/scripts/projects/list-queue-items.mjs +3 -377
- package/scripts/projects/move-queue-item.mjs +3 -410
- package/scripts/projects/reorder-queue-item.mjs +3 -22
- package/skills/copilot-pr-followup/SKILL.md +37 -23
- package/skills/dev-loop/SKILL.md +1 -1
- package/skills/docs/acceptance-criteria-verification.md +3 -2
- package/skills/docs/anti-patterns.md +3 -2
- package/skills/docs/artifact-authority-contract.md +12 -11
- package/skills/docs/confirmation-rules.md +1 -0
- package/skills/docs/contract-style-guide.md +37 -0
- package/skills/docs/copilot-loop-operations.md +18 -14
- package/skills/docs/cross-harness-regression-contract.md +2 -2
- package/skills/docs/debt-remediation-contract.md +1 -1
- package/skills/docs/epic-tree-refinement-procedure.md +25 -22
- package/skills/docs/issue-intake-procedure.md +25 -19
- package/skills/docs/merge-preconditions.md +13 -11
- package/skills/docs/pr-lifecycle-contract.md +44 -35
- package/skills/docs/public-dev-loop-contract.md +59 -43
- package/skills/docs/required-rules.json +149 -0
- package/skills/docs/retrospective-checkpoint-contract.md +18 -5
- package/skills/docs/spike-mode-contract.md +14 -7
- package/skills/docs/stop-conditions.md +30 -15
- package/skills/docs/tracker-first-loop-state.md +13 -8
- package/skills/docs/validation-policy.md +4 -5
- package/skills/local-implementation/SKILL.md +48 -142
- package/scripts/docs/validate-no-duplicate-rules.mjs +0 -250
|
@@ -12,25 +12,25 @@ Your job is to provide the callable `dev-loop` public façade and route to the c
|
|
|
12
12
|
|
|
13
13
|
## Handoff envelope mandate (first action)
|
|
14
14
|
|
|
15
|
-
The agent's first action after resolving authoritative state
|
|
15
|
+
The agent's first action after resolving authoritative state MUST be to build the handoff envelope via `buildDevLoopHandoffEnvelope()` from `@dev-loops/core`.
|
|
16
16
|
|
|
17
17
|
The envelope is the primary handoff artifact — it is derived from resolver output, settings, and gate state, and it determines:
|
|
18
18
|
- `requiredReads` — the canonical ordered list of files to load
|
|
19
19
|
- `nextAction` — the bounded task to execute
|
|
20
|
-
- `stopRules` — stop boundaries that
|
|
20
|
+
- `stopRules` — stop boundaries that MUST NOT be crossed without authorization
|
|
21
21
|
- `acceptance` — self-validation criteria for declaring completion
|
|
22
22
|
- `sanctionedCommands` — the operation → wrapper command map (reads/edits/lifecycle), plus the forbidden and orchestrator-owned lists. Carried by DEFAULT on every build so you never re-derive which wrapper performs a GitHub/loop operation. Do NOT restate the map here — the single source of truth is `scripts/loop/sanctioned-commands.mjs`, surfaced verbatim in the envelope.
|
|
23
23
|
|
|
24
24
|
**Construction sequence:**
|
|
25
25
|
|
|
26
|
-
1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.7.
|
|
26
|
+
1. Run the deterministic startup resolver to produce the authoritative state bundle: `npx dev-loops@0.7.2 loop startup --issue <n>` for issues, or `npx dev-loops@0.7.2 loop startup --pr <n>` for PRs.
|
|
27
27
|
2. Pass the resolver output, resolved settings (merged from `.devloops` and `.pi/dev-loop/defaults.yaml`), and current gate state to `buildDevLoopHandoffEnvelope()`.
|
|
28
28
|
3. **Validate the envelope** with `validateHandoffEnvelope()` before consuming any field. If validation returns `ok: false`, reject the handoff with the structured error — do not load requiredReads, do not execute nextAction, do not delegate.
|
|
29
29
|
4. Read the envelope as the first artifact.
|
|
30
30
|
5. Load every path listed in `requiredReads` (in order).
|
|
31
31
|
6. Execute `nextAction` constrained by `stopRules` and `acceptance`.
|
|
32
32
|
|
|
33
|
-
**The agent
|
|
33
|
+
**The agent MUST NOT load skills, route packs, or delegate work before the envelope is built and read.** The derivation contract is [Workflow Handoff Contract](../skills/docs/workflow-handoff-contract.md).
|
|
34
34
|
|
|
35
35
|
Prose task composition is a fallback only when `buildDevLoopHandoffEnvelope()` is unavailable (missing `@dev-loops/core` package) — the handoff contract in `skills/docs/workflow-handoff-contract.md` applies in that fallback case.
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ After the handoff envelope is built and read, load the `dev-loop` skill ([Dev Lo
|
|
|
40
40
|
|
|
41
41
|
When that skill is not available at the expected path, resolve it from the skill installation layout (see the skill's "Skill asset path resolution" section).
|
|
42
42
|
|
|
43
|
-
This entrypoint
|
|
43
|
+
This entrypoint MUST stay thin: do not restate the skill's phase sequencing or workflow policy here. The envelope owns handoff sequencing; the skill owns routed strategy execution procedures.
|
|
44
44
|
|
|
45
45
|
Treat the deterministic public routing contract in [Public Dev Loop Contract](../skills/docs/public-dev-loop-contract.md) and the `dev-loop` skill as the authority for choosing the current execution path. Do not force users to choose internal strategy names up front.
|
|
46
46
|
|
|
@@ -57,9 +57,9 @@ If local facts, GitHub facts, and helper/state-machine output do not agree well
|
|
|
57
57
|
|
|
58
58
|
## Subagent delegation
|
|
59
59
|
|
|
60
|
-
All delegation
|
|
60
|
+
All delegation MUST originate from the handoff envelope: the envelope's `nextAction`, `requiredReads`, `stopRules`, and `acceptance` define the bounded task. The envelope is passed to child subagents as their primary handoff artifact.
|
|
61
61
|
|
|
62
|
-
The pi-subagents skill is parent-only, so delegated subagents do not receive orchestration patterns. This section exists as the minimal locally-enforced subset needed for correct delegation — it is not a restatement of the full policy. The `dev-loop` skill owns all procedural rules; this section only declares the invariants the agent
|
|
62
|
+
The pi-subagents skill is parent-only, so delegated subagents do not receive orchestration patterns. This section exists as the minimal locally-enforced subset needed for correct delegation — it is not a restatement of the full policy. The `dev-loop` skill owns all procedural rules; this section only declares the invariants the agent MUST follow when it cannot defer to the skill:
|
|
63
63
|
- One writer thread; `async: true` default; `context: "fresh"` for reviewers.
|
|
64
64
|
- No child subagent spawning beyond assigned fanout work.
|
|
65
65
|
- Bounded tasks with concrete scope, exit conditions, and validation expectations.
|
package/.claude/agents/fixer.md
CHANGED
|
@@ -26,6 +26,7 @@ You are a focused review-fix agent. You take an existing pull request with revie
|
|
|
26
26
|
|
|
27
27
|
## Review Workflow
|
|
28
28
|
1. Read unresolved review threads and any general review comments.
|
|
29
|
+
- Prefer the deterministic helper `scripts/github/list-review-threads.mjs --unresolved-only` to enumerate threads with their reply/resolve ids, rather than hand-writing a GraphQL query.
|
|
29
30
|
2. Group related comments by file and identify the underlying concern behind each comment.
|
|
30
31
|
3. Decide the best resolution for each concern: exact requested change, better alternative fix, explanation-only resolution, or escalation for expert judgment.
|
|
31
32
|
4. If expert input is needed, stop before editing or resolving the thread and report the question, evidence, and options.
|
|
@@ -38,7 +38,7 @@ For the active phase, require and produce:
|
|
|
38
38
|
- if no explicit definition of done exists, add a `Proposed DoD` subsection before the matrix
|
|
39
39
|
- explicit risks, watchpoints, and unresolved questions
|
|
40
40
|
- validation steps and tests to write first
|
|
41
|
-
- durable decisions that
|
|
41
|
+
- durable decisions that SHOULD be preserved in the phase doc
|
|
42
42
|
- when the phase includes a bounded audit or scan: prioritized findings, the highest-value follow-up candidates, and an explicit statement of what the current phase will not rewrite or broaden
|
|
43
43
|
- When an audit artifact is provided, treat it as a first-class planning input: summarize the audited scope, list prioritized findings, include the highest-value follow-up candidates, and classify each meaningful finding as exactly one of current-phase scope/AC, DoD expectation, explicit non-goal / defer, or risk/watchpoint
|
|
44
44
|
- Do not invent audit findings when no audit artifact was provided
|
|
@@ -83,4 +83,4 @@ Return:
|
|
|
83
83
|
|
|
84
84
|
## Completion quality bar
|
|
85
85
|
- A refinement is complete only when no item in the AC/DoD/Non-goal coverage matrix has status `Partial`, `Unmet`, or `Unverified`.
|
|
86
|
-
- Any `Partial`, `Unmet`, or `Unverified` item means the refinement is still incomplete and
|
|
86
|
+
- Any `Partial`, `Unmet`, or `Unverified` item means the refinement is still incomplete and MUST NOT be presented as ready.
|
package/.claude/agents/review.md
CHANGED
|
@@ -34,7 +34,7 @@ You are in scoped angle-review mode when the invocation supplies a single review
|
|
|
34
34
|
- **Fresh-context guard (mandatory):** run `node scripts/github/verify-fresh-review-context.mjs --scope <angle> --context-path <gate-context-artifact-path>` at startup. If it reports `fresh: false` (exit 1) or any error — including a missing gate-context artifact (`gateContextPresent: false`), which is the fail-closed signal that you're not in the right worktree/head — refuse to proceed and report the failure; do not review on inherited context or without seeded context. "Fresh" here means your context is the **neutral builder artifact + your angle**, and explicitly NOT the main agent's conversation/state or a prior reviewer session's state. The injected neutral bundle is the INTENDED seed (allowed); main-agent or cross-session state bleed still fails closed.
|
|
35
35
|
- **Use the PROVIDED neutral bundle as your base; do NOT re-derive it from scratch:** the gate-context artifact carries the diff (`scope.diffPath`) and a deterministic, neutral adjacent-code bundle in `adjacentCode` (each changed file plus its 1-hop import in/out-edges, with a `stripped`/`truncated`/`missing` manifest). Start from this bundle — it is the build-once, work-deduped seed shared verbatim across all reviewers. Only widen (load more files) per-angle when the bundle genuinely lacks something your angle needs; do not re-build the whole diff/adjacent-code graph yourself.
|
|
36
36
|
- **Single-angle scope:** review ONLY the supplied angle's concern. Read the gate-context artifact for the resolved angle set, change scope (branch, head SHA, touched files), acceptance-criteria pointer, and validation posture. Do not review other angles; the fan-in pass consolidates across angles.
|
|
37
|
-
- **Read the FULL diff, not just hunks:** the gate-context artifact records `scope.diffPath` (a repo-relative path to the complete captured diff) and `scope.changedFiles` (full repo-relative paths). Read the entire diff from `scope.diffPath`; if it is `null` or missing, reconstruct it yourself with `git diff` against the change's base (e.g. `git diff origin/main...HEAD` or the branch/head SHA in `scope`). Do not review off a partial or summarized hunk list — you
|
|
37
|
+
- **Read the FULL diff, not just hunks:** the gate-context artifact records `scope.diffPath` (a repo-relative path to the complete captured diff) and `scope.changedFiles` (full repo-relative paths). Read the entire diff from `scope.diffPath`; if it is `null` or missing, reconstruct it yourself with `git diff` against the change's base (e.g. `git diff origin/main...HEAD` or the branch/head SHA in `scope`). Do not review off a partial or summarized hunk list — you MUST see every changed line for your angle.
|
|
38
38
|
- **Reason about ADJACENT related code from the bundle first:** the `adjacentCode` bundle already includes the changed files' callers, callees, and imports (1-hop). Read those entries before opening anything else. For each changed function, trace the real call paths a defect would flow through, and confirm that caller and callee contracts still match (argument shapes, return shapes, error/null conventions, units). If the bundle truncated or stripped a file you need (see its `truncated`/`stripped` manifest), open that file directly to widen.
|
|
39
39
|
- **Review ADVERSARIALLY — hunt concrete defects:** actively try to break the changed code. Hunt for edge cases, missing or wrong input validation, numeric coercion bugs (NaN, Infinity, floats where integers are assumed, negative values, string-to-number coercion), null/undefined handling, off-by-one and boundary conditions, mismatched caller/callee contracts, and dedup/identity bugs (wrong key, reference vs. value equality, unstable ordering). For every finding, give the concrete `file:line` plus the specific failing scenario or input that triggers the defect — not high-altitude "is there a test?" commentary.
|
|
40
40
|
- **Strictly read-only, but WIDEN SCOPE when needed:** never edit files, stage, commit, push, request reviews, resolve threads, or post PR comments. Findings are returned via the output artifact only; fixes are applied later by the fix cycle, not by this agent. Because you are read-only, you MAY open any additional repository files required to judge your angle (callers, callees, contracts, sibling modules, configs, tests) — widening to read adjacent code is expected and safe. Record every file/module you consulted beyond the briefing's `changedFiles`/`diffPath` in the optional `contextWidened` field on your output artifact.
|
|
@@ -73,7 +73,7 @@ When NOT given an angle scope, behave exactly as the full-PR review agent descri
|
|
|
73
73
|
- Prefer concrete findings with file references and impact over generic style commentary.
|
|
74
74
|
- Distinguish clearly between must-fix findings, lower-severity risks, and informational gaps.
|
|
75
75
|
- If the PR description omits required sections, is too thin to ground review without reconstructing intent from commits, or includes verdict status, evidence, or changelog content, treat that as a first-class review issue.
|
|
76
|
-
- The review verdict
|
|
76
|
+
- The review verdict MUST carry the acceptance-criteria and definition-of-done assessment in explicit markdown verification tables, including status plus concise evidence for each row.
|
|
77
77
|
- For follow-up reviews on the same PR, do not repost full AC/DoD tables: include only delta rows where status or supporting evidence changed, and explicitly note when there are no AC/DoD deltas.
|
|
78
78
|
- When changelog coverage is needed, include a dedicated `## Changelog` section in the review verdict comment so post-merge automation can consume it without reading the PR description.
|
|
79
79
|
|
|
@@ -19,4 +19,4 @@ Add work to the dev-loop queue. Parse `$ARGUMENTS` before acting. The project is
|
|
|
19
19
|
1. **Confirm first — no mutation before approval.** Print a one-line preview of the issue to be created (`About to create issue: "<concise title>"`) and STOP for approval. Do not create the issue, grill, or enqueue anything until the human approves.
|
|
20
20
|
2. **Create the issue.** After approval, create a minimal GitHub issue from the text. The repo has no dedicated create-issue wrapper under `scripts/github/`; the sanctioned create path in this codebase is `gh issue create --repo <owner/repo> --assignee @me --title "<concise title>" --body "<freeform text>"` (the same path used by `skills/docs/issue-intake-procedure.md` and permitted by `skills/docs/main-agent-contract.md`). Title = a concise summary of the text; body = the verbatim freeform text.
|
|
21
21
|
3. **Grill it.** Run `/loop-grill <n> --auto` (delegates to the `loop-grill` skill). The skill writes a `## Grill findings` section back into the issue body and flags any unresolved items there, so they are visible before pickup. Do not re-implement grilling.
|
|
22
|
-
4. **Enqueue.** Run `node scripts/projects/add-queue-item.mjs --repo <owner/repo> --item <n>` to add the grill-annotated issue to the board, then report its Status column as in the numeric path. The `strategy: local-first` default is set repo-wide in `.devloops` (`strategy.default: local-first`), so the new issue is already local-first — this repo has no per-issue local-first label or body tag to apply, and you
|
|
22
|
+
4. **Enqueue.** Run `node scripts/projects/add-queue-item.mjs --repo <owner/repo> --item <n>` to add the grill-annotated issue to the board, then report its Status column as in the numeric path. The `strategy: local-first` default is set repo-wide in `.devloops` (`strategy.default: local-first`), so the new issue is already local-first — this repo has no per-issue local-first label or body tag to apply, and you MUST NOT invent one.
|
|
@@ -4,4 +4,4 @@ argument-hint: "<issue|pr>"
|
|
|
4
4
|
---
|
|
5
5
|
<!-- GENERATED from commands/loop-info.command.md by scripts/claude/generate-claude-assets.mjs — do not edit; edit the source and regenerate. -->
|
|
6
6
|
|
|
7
|
-
Resolve the read-only dev-loop state for `$ARGUMENTS` via the `loop info` shortcut — no full dev-loop run. If `$ARGUMENTS` is an issue number, run `npx dev-loops@0.7.
|
|
7
|
+
Resolve the read-only dev-loop state for `$ARGUMENTS` via the `loop info` shortcut — no full dev-loop run. If `$ARGUMENTS` is an issue number, run `npx dev-loops@0.7.2 loop info --issue $ARGUMENTS`; if it is a PR number, run `npx dev-loops@0.7.2 loop info --pr $ARGUMENTS`. Report the strategy, route, linked PR / branch, CI, and next action. Do not start implementation.
|
|
@@ -11,6 +11,6 @@ Start a spike — a time-boxed exploration whose deliverable is a findings docum
|
|
|
11
11
|
- Run `node scripts/refine/scaffold-spike-file.mjs --question "$ARGUMENTS" --out <path> --json`. It writes `## Question` (from the arg) plus stubbed `## Approach`/`## Findings` so the exploration scaffold passes `validateSpikeExplorationSections`; `## Recommendation` is left for the spike to fill in. It returns `{ ok: true, path, question }`.
|
|
12
12
|
- Then run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` and route the resulting bundle through the `dev-loop` skill.
|
|
13
13
|
|
|
14
|
-
- Pre-authored file (`$ARGUMENTS` begins with `--file <path>`): skip scaffolding and run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` directly, then route. The file
|
|
14
|
+
- Pre-authored file (`$ARGUMENTS` begins with `--file <path>`): skip scaffolding and run `node scripts/loop/resolve-dev-loop-startup.mjs --spike <path>` directly, then route. The file MUST already carry the exploration scaffold (Question/Approach/Findings).
|
|
15
15
|
|
|
16
16
|
The spike runs under the relaxed `gates.spike` profile. When the exploration reaches a `## Recommendation`, conclude it with `node scripts/refine/exit-spike.mjs --spike-file <path> --disposition <discard|graduate> [--plan-file <path>]` per the spike-mode contract — discard leaves nothing behind, graduate emits a plan file into the local-planning flow. No new routing logic here.
|
|
@@ -63,6 +63,8 @@ Max watch timeout: **30 minutes** (from `policy-constants.mjs` COPILOT_REVIEW_WA
|
|
|
63
63
|
```sh
|
|
64
64
|
node <resolved-skill-scripts>/github/request-copilot-review.mjs --help
|
|
65
65
|
node <resolved-skill-scripts>/github/probe-copilot-review.mjs --help
|
|
66
|
+
node <resolved-skill-scripts>/github/list-review-threads.mjs --help
|
|
67
|
+
node <resolved-skill-scripts>/github/wait-pr-checks.mjs --help
|
|
66
68
|
node <resolved-skill-scripts>/loop/detect-copilot-loop-state.mjs --help
|
|
67
69
|
```
|
|
68
70
|
|
|
@@ -86,7 +88,7 @@ Verify all material claims against source, tests, configuration, and CI.
|
|
|
86
88
|
When this skill refers to helper paths such as `scripts/...` or `docs/...`, resolve them from the actual skill installation layout you are running, not from the active target repository checkout.
|
|
87
89
|
|
|
88
90
|
Use this rule:
|
|
89
|
-
- if the skill is installed as a normalized standalone copy, the required bundled contract docs live under the shared `../docs/` directory next to the installed skill directories
|
|
91
|
+
- if the skill is installed as a normalized standalone copy, the required bundled contract docs live under the shared `../docs/` directory next to the installed skill directories. <!-- rule: ASSET-PATH-INSTALLED-NO-ASSUME --> `ASSET-PATH-INSTALLED-NO-ASSUME`: Agents MUST NOT assume helper scripts are bundled unless that installed layout actually contains them.
|
|
90
92
|
- if you are working in the `dev-loops` source repository, this skill file lives under `skills/copilot-pr-followup/`, so source-repo helper scripts live two levels up at `../../scripts/`, while required bundled contract docs live one level up at `../docs/`
|
|
91
93
|
- when in doubt, resolve helper paths relative to this [skill file](./SKILL.md) first, then verify the target file exists before running it
|
|
92
94
|
|
|
@@ -97,7 +99,7 @@ Required bundled runtime contract docs for installed copies of this skill:
|
|
|
97
99
|
- [Copilot Loop Operations](../docs/copilot-loop-operations.md)
|
|
98
100
|
|
|
99
101
|
Read those bundled `../docs/` files from the installed skill layout instead of assuming the source repository checkout is present. If any required bundled contract doc is missing from the installed skill layout, treat that as a packaging/installer bug.
|
|
100
|
-
|
|
102
|
+
<!-- rule: ASSET-PATH-SOURCE-NO-REPO-LOCAL --> `ASSET-PATH-SOURCE-NO-REPO-LOCAL`: Agents MUST NOT assume `scripts/...` is repo-local to the target codebase they are operating on.
|
|
101
103
|
|
|
102
104
|
## Authority and safety rules
|
|
103
105
|
|
|
@@ -115,16 +117,19 @@ Inspect: PR body/title (must satisfy [PR description contract](../docs/copilot-l
|
|
|
115
117
|
|
|
116
118
|
At the issue-assignment seam, use `detect-initial-copilot-pr-state.mjs` and keep waiting when `waiting_for_initial_copilot_implementation`.
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
<!-- rule: COPILOT-FOLLOWUP-REQUEST-HELPER-ONLY -->
|
|
121
|
+
`COPILOT-FOLLOWUP-REQUEST-HELPER-ONLY`: Copilot review requests MUST go through `request-copilot-review.mjs` (see [Operational cookbook](#operational-cookbook)); the agent MUST NOT request Copilot by posting literal `/copilot` or `/copilot re-review` PR comments, and MUST NOT rely solely on `gh pr view --json reviewRequests` to confirm a request. After draft→ready or fix push, explicitly decide whether another pass is desired; if yes, ensure green/credibly green posture first.
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
<!-- rule: COPILOT-FOLLOWUP-REQUEST-BRANCHING -->
|
|
124
|
+
`COPILOT-FOLLOWUP-REQUEST-BRANCHING`: The agent MUST branch on the `request-copilot-review.mjs` machine-readable result exactly as follows, and MUST NOT treat an attempted request as equivalent to a confirmed request:
|
|
121
125
|
- `requested`: if another Copilot pass is actually desired, immediately re-baseline with `detect-copilot-loop-state.mjs` and follow its `nextAction` (enter persistent wait only through `dev-loops loop watch-cycle` or `gh run watch`)
|
|
122
126
|
- `already-requested`: apply the same detector-first rebasing and wait branching as `requested`
|
|
123
127
|
- `suppressed_same_head_clean`: report clean-converged state and stop unless `--force-rerequest-review` bypass is intentionally authorized
|
|
124
128
|
- `unavailable`: report the limitation and stop
|
|
129
|
+
- `blocked_by_copilot_comment`: no request was placed. Delete the violating `violationCommentIds` (or confirm they only quote the rule inside a code span/fenced block, which does not arm the guard) and re-run; do NOT treat this as a placed request and do NOT enter a wait seam
|
|
125
130
|
- non-zero / unexpected failure: stop and report error
|
|
126
131
|
|
|
127
|
-
|
|
132
|
+
Branch on `status`, never on `ok`/exit-code truthiness alone: `ok: true` means the helper ran without error, not that a review was placed. Under `--silent`, the exit code is 0 only for `requested`; every other status (including `blocked_by_copilot_comment`) exits non-zero.
|
|
128
133
|
|
|
129
134
|
### Re-attachment guard (check for existing loop state first)
|
|
130
135
|
|
|
@@ -156,7 +161,8 @@ The outer-loop checkpoint is the canonical re-attachment artifact.
|
|
|
156
161
|
|
|
157
162
|
Start every wait seam with a detector refresh: `detect-copilot-loop-state.mjs --repo <owner/name> --pr <number>`.
|
|
158
163
|
|
|
159
|
-
|
|
164
|
+
<!-- rule: COPILOT-FOLLOWUP-WAIT-TOOLS -->
|
|
165
|
+
`COPILOT-FOLLOWUP-WAIT-TOOLS`: The agent MUST wait only through allowed deterministic tools: `detect-copilot-loop-state.mjs` (one-shot), `dev-loops loop watch-cycle` (persistent), `copilot-pr-handoff.mjs --watch-status` (refresh after timeout/idle), `dev-loops loop watch-ci --repo <owner/name> --pr <number>` (provider-agnostic CI: CircleCI / Actions / external commit-status), `scripts/github/wait-pr-checks.mjs --repo <owner/name> --pr <number>` (same provider-agnostic CI wait, with a direct 0/1/2 process-exit-code contract for shell/scripted callers instead of a JSON `status` field), `gh run watch <run-id> --repo <owner/name>` (Actions-only fallback when the run id is known); otherwise exit and resume later from a fresh detector call.
|
|
160
166
|
|
|
161
167
|
Practical rules: do not poll manually. `waiting_for_copilot_review` → `run-watch-cycle.mjs` or report-and-resume. `waiting_for_ci` with pending/none CI → `dev-loops loop watch-ci --repo <owner/name> --pr <number>` (provider-agnostic; covers CircleCI / Actions / external commit-status) or report-and-resume; `gh run watch <run-id>` is an Actions-only fallback. `dev-loops loop watch-cycle` also auto-routes a `waiting_for_ci` boundary to this CI watcher. Bounded CI exception: zero current-head suites + previous-head green + local `npm run verify` passed → rerun detector with `--local-validation-head-sha` for `crediblyGreen` promotion. `ciStatus=failure` → stop/fix, never wait.
|
|
162
168
|
|
|
@@ -168,6 +174,7 @@ Preferred approach:
|
|
|
168
174
|
- after a successful fix / reply-resolve / re-request cycle, returning to `waiting_for_copilot_review` is a persistence boundary: resume the watcher instead of reporting completion
|
|
169
175
|
- dispatch fix findings to the `fixer` agent; do not run inline fix passes in-watcher
|
|
170
176
|
- do not report completion while unresolved Copilot feedback remains
|
|
177
|
+
- once a watch/probe settles, do not parse its raw output: re-read via `detect-copilot-loop-state.mjs --repo <owner/name> --pr <number>` and `list-review-threads.mjs --repo <owner/name> --pr <number> --unresolved-only` — inline interpreters are barred by `OPS-NO-INLINE-INTERPRETER` in [Copilot loop operations](../docs/copilot-loop-operations.md)
|
|
171
178
|
|
|
172
179
|
### Canonical async dispatch wording
|
|
173
180
|
|
|
@@ -196,6 +203,7 @@ This step covers four responsibilities: the draft gate right before `gh pr ready
|
|
|
196
203
|
When unresolved feedback exists, use a narrow follow-up loop:
|
|
197
204
|
|
|
198
205
|
1. inspect unresolved comments/threads and failing checks
|
|
206
|
+
- enumerate unresolved threads (with the thread/comment ids the reply-resolve helpers below need) via `scripts/github/list-review-threads.mjs --repo <owner/name> --pr <number> --unresolved-only` rather than a hand-written `gh api graphql` query
|
|
199
207
|
2. before the first local file write in each fixer pass on a Copilot-assigned PR, run `node <resolved-skill-scripts>/loop/pre-write-remote-freshness-guard.mjs --branch <headRefName>` as a required fail-closed guard
|
|
200
208
|
- source `<headRefName>` from authoritative PR state (`headRefName`), not from a local branch guess
|
|
201
209
|
- if the guard exits non-zero (`remote_ahead`), stop writing locally, reconcile to the refreshed remote head, then restart the fixer pass
|
|
@@ -209,7 +217,7 @@ When unresolved feedback exists, use a narrow follow-up loop:
|
|
|
209
217
|
- source `<headRefName>` from authoritative PR state (`headRefName`), not from a local branch guess
|
|
210
218
|
- if the guard exits non-zero (`branch_mismatch`), stop and realign to the expected branch before staging or committing
|
|
211
219
|
7. if files changed, push the resolving commit before any thread reply claims the fix is present
|
|
212
|
-
8.
|
|
220
|
+
8. <!-- rule: COPILOT-FOLLOWUP-REPLY-RESOLVE-HELPER --> `COPILOT-FOLLOWUP-REPLY-RESOLVE-HELPER`: When a comment or thread is actually addressed, the agent MUST reply on GitHub with a short resolution note that references the resolving commit SHA or commit URL when applicable, using the deterministic helpers below rather than ad hoc thread mutations:
|
|
213
221
|
- for one thread, must use the deterministic helper `reply-resolve-review-thread.mjs` from the resolved skill scripts directory
|
|
214
222
|
- when the same bounded resolution note applies to multiple matching unresolved threads, use `reply-resolve-review-threads.mjs` instead of ad hoc inline `gh api` / `gh api graphql` mutations
|
|
215
223
|
- when using the single-thread helper, pair `--comment-id` and `--thread-id` from the same fresh PR thread snapshot rather than mixing ids across review rounds
|
|
@@ -226,8 +234,9 @@ When unresolved feedback exists, use a narrow follow-up loop:
|
|
|
226
234
|
- do not stop at a local fix if GitHub-side reply/resolve is authorized
|
|
227
235
|
11. after completing reply/resolve for a pass, verify zero unresolved threads remain via `dev-loops gate capture-threads` before proceeding
|
|
228
236
|
- if the refreshed snapshot reports unresolved threads, re-enter the reply/resolve loop for the missed threads
|
|
229
|
-
12. only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain
|
|
230
|
-
- resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`
|
|
237
|
+
12. <!-- rule: COPILOT-FOLLOWUP-ROUND-CAP --> `COPILOT-FOLLOWUP-ROUND-CAP`: The agent MUST decide whether another Copilot pass is desired, applying the round-cap/signal-gating rules below, only after GitHub-side reply/resolve work is done for the addressed threads and the refreshed thread snapshot proves zero unresolved threads remain
|
|
238
|
+
- resolve the review-round cap from config via `resolveRefinementConfig(config, "maxCopilotRounds")` from `@dev-loops/core/config`; default config ships `maxCopilotRounds: 5`. For a light-dispatched PR, resolve `resolveEffectiveCopilotRoundCap(config, { lightweight: true })` instead — `min(localImplementation.lightMode.maxCopilotRounds ?? 1, maxCopilotRounds)` (default lightweight cap: 1) — see the [Artifact Authority Contract](../docs/artifact-authority-contract.md) lightweight section (issue #1210)
|
|
239
|
+
- for a light-dispatched PR, pass `--lightweight` on every round-cap-consuming helper invocation — `detect-copilot-loop-state.mjs`, `copilot-pr-handoff.mjs`, `detect-pr-gate-coordination-state.mjs`, `request-copilot-review.mjs`, and `upsert-checkpoint-verdict.mjs` — otherwise those tools resolve the full-PR cap and the composed lightweight cap is never enforced
|
|
231
240
|
- **Opt out entirely:** `maxCopilotRounds: 0` disables the external Copilot review gate for the repo — the loop runs `draft_gate → pre_approval_gate` with the local harness only, never requesting or waiting on Copilot. Use this when the repo has no Copilot reviewer configured or prefers local-harness-only review.
|
|
232
241
|
- use the completed Copilot review-round count from `detect-copilot-loop-state.mjs` / `copilot-pr-handoff.mjs` as the current PR's review-round count
|
|
233
242
|
- if completed review rounds have reached the maximum (default: 5), do **not** re-request Copilot review within that concluded cycle
|
|
@@ -250,7 +259,10 @@ Do not treat `fix applied locally` as the end of the loop when the workflow also
|
|
|
250
259
|
|
|
251
260
|
### Mandatory gate-comment command contract
|
|
252
261
|
|
|
253
|
-
|
|
262
|
+
<!-- rule: COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL -->
|
|
263
|
+
`COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL`: For every `draft_gate` or `pre_approval_gate` comment, agents MUST run `upsert-checkpoint-verdict.mjs` and MUST NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments.
|
|
264
|
+
|
|
265
|
+
Per `COPILOT-FOLLOWUP-GATE-COMMENT-CANONICAL` above, run:
|
|
254
266
|
|
|
255
267
|
For a gate that ran via the fan-out/fan-in sub-loop, pass the structured per-angle review results via `--findings-json` (NOT the wall-of-text `--findings-summary`). The operator/loop writes that JSON file from the collected per-angle results — the same per-angle `{angle, verdict, findings}` objects the fan-out reviewers wrote to `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json` that feed `consolidateFanin`. The helper renders a readable per-angle breakdown and derives the single-line `**Findings summary:**` digest itself:
|
|
256
268
|
|
|
@@ -284,19 +296,19 @@ node <resolved-skill-scripts>/github/upsert-checkpoint-verdict.mjs \
|
|
|
284
296
|
|
|
285
297
|
`--execution-mode <fanout_fanin|inline_single_agent>` records how the gate review ran (default `inline_single_agent`). When the gate did not run via the fan-out/fan-in sub-loop ([Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md)), you MUST pass `--execution-mode inline_single_agent --inline-reason "<why>"` — silent inline runs are no longer allowed: inline mode requires a non-empty `--inline-reason` and emits a stderr warning. Because inline is the default mode, a bare call with neither flag now fails with an argument error, so always pass `--execution-mode` explicitly (and `--inline-reason` for inline). The recorded `executionMode` is surfaced by `detect-checkpoint-evidence.mjs` and gated by `gates.requireFanoutEvidence`.
|
|
286
298
|
|
|
287
|
-
Do NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments.
|
|
288
|
-
|
|
289
299
|
`--force --force-reason` on `upsert-checkpoint-verdict.mjs` is a narrow operator-authorized CI override for the helper itself, not the default gate path. Use it only when the helper refuses gate entry solely because the current head is `blocked_needs_user_decision` with `ciStatus="failure"`, and only after the user explicitly authorizes ignoring that current-head CI failure for this one gate-comment upsert. It does **not** bypass stale-head checks, unresolved-thread / unsettled-review refusal, non-draft `draft_gate` refusal, merge conflicts, or other legality checks.
|
|
290
300
|
|
|
291
301
|
### Gate fan-out/fan-in procedure (agent-orchestrated)
|
|
292
302
|
|
|
293
|
-
Both gates run this same checkpoint review chain. It is an **agent-orchestrated skill procedure** — a node script cannot spawn the per-angle reviewers, so the conductor agent drives the fan-out and uses the pure `@dev-loops/core/loop/gate-fanin` helpers only for consolidation, batching, and ledger mapping.
|
|
303
|
+
Both gates run this same checkpoint review chain, owned end-to-end by [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md) (`GATE-EXEC-BUILD-ONCE-SEED`, `GATE-EXEC-BRIEFING-PREFIX`, `GATE-EXEC-SEPARATE-CHAINS`, `GATE-EXEC-POST-BEFORE-FIX`, `GATE-EXEC-REGATE-MANDATORY`, `GATE-EXEC-LIGHT-ESCALATION`); this section owns only this skill's dispatch of that chain. It is an **agent-orchestrated skill procedure** — a node script cannot spawn the per-angle reviewers, so the conductor agent drives the fan-out and uses the pure `@dev-loops/core/loop/gate-fanin` helpers only for consolidation, batching, and ledger mapping.
|
|
294
304
|
|
|
295
|
-
1. **Context (Phase 1)
|
|
296
|
-
2. **Fan-out (Phase 2)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
1. **Context (Phase 1):** build/read the gate-context artifact via `scripts/github/write-gate-context.mjs` (`buildGateContext` resolves the angle set through `resolveGateAnglesDynamic`; mandatory angles always survive). Consume the resulting `tmp/gate-context/<repo-slug>/pr-<N>/<gate>-<headSha>.json` artifact: the FULL diff at `scope.diffPath` plus the top-level adjacent-code bundle (`adjacentCode`) are the build-once seed every reviewer uses verbatim — see `GATE-EXEC-BUILD-ONCE-SEED` for the bundle contract; do not re-derive them per reviewer.
|
|
306
|
+
2. **Fan-out (Phase 2):** plan batches with `planFanoutBatches(angles, cap)` using the `gates.maxFanoutReviewers` cap (default 8); spawn one scoped `review` agent per resolved angle (plain Agent tool), parallel up to the cap, sequential for overflow (record any degradation in the gate evidence). Each reviewer follows [review agent scoped angle-review mode](../../agents/review.agent.md): fresh context, single angle, read-only, writing its per-angle findings artifact to `tmp/gate-reviews/<repo-slug>/pr-<N>/<gate>-<headSha>/<angle>.json`.
|
|
307
|
+
<!-- rule: COPILOT-FOLLOWUP-ADVERSARIAL-BRIEFING -->
|
|
308
|
+
`COPILOT-FOLLOWUP-ADVERSARIAL-BRIEFING`: Each reviewer MUST be briefed to review like an external code reviewer hunting real bugs: read the FULL diff (from `scope.diffPath`, or `git diff` when null) and the bundled adjacent code (callers, callees, imports) rather than re-deriving them, then review adversarially for concrete defects (edge cases, input validation, numeric coercion incl. NaN/Infinity/floats/negatives, null/undefined, boundary conditions, mismatched caller/callee contracts, dedup/identity bugs) with `file:line` + the failing scenario — not process nits like "no test exists". Reviewers MAY widen scope (open adjacent repo files beyond the bundle) only when their angle genuinely needs more, recording that in the optional `contextWidened` field on their findings artifact. The LAYOUT of this briefing (invariant block first, this adversarial angle prompt last) and the `--prefix-hash`/`--prefix-file` sentinel recording are owned by `GATE-EXEC-BRIEFING-PREFIX` — not restated here.
|
|
309
|
+
3. **Fan-in (Phase 3):** before consolidating, run `scripts/github/verify-briefing-prefixes.mjs --head-sha <current_head_sha>` and stop the pass on a fail-closed result (`GATE-EXEC-BRIEFING-PREFIX` owns this check). Then consolidate via `consolidateFanin({ angleResults, blockCleanOnFindingSeverities })` (blocking severities from `resolveGateConfig(config, <configKey>).blockCleanOnFindingSeverities` — `draft` or `preApproval`, not the `<gate>` artifact name). Map with `toFindingsLogShape(...)` and write the disposition ledger via `write-gate-findings-log.mjs` before the visible comment (`GATE-EXEC-DISPOSITION-LEDGER`, `GATE-EXEC-POST-BEFORE-FIX` own that ordering and the ledger's opt-out-proof durability). When `resolveGatePostFindingsComments(config)` is true (default), post via `node <resolved-skill-scripts>/github/post-gate-findings.mjs --repo <owner/name> --pr <number> --gate <gate> --head-sha <current_head_sha> --findings '<toFindingsLogShape JSON>'`.
|
|
310
|
+
4. **Verdict (Phase 4):** post via the [Gate comment command](#mandatory-gate-comment-command-contract) using `--execution-mode fanout_fanin` and `--findings-json <path>` built from the same per-angle artifacts consolidated in Phase 3 — that flag's accepted shapes are owned by the Gate comment command section above; do not restate them here.
|
|
311
|
+
5. **Retry (Phase 5):** on blocking findings, drive each internal fan-out finding through the SAME fix → reply-with-resolving-commit → resolve loop used for external Copilot review comments (Step 7 above); re-run only the `findings_present` angles from the previous pass (context-builder and fan-in always re-run); repeat until the consolidated verdict is `clean` for the current head SHA (`GATE-EXEC-REGATE-MANDATORY`).
|
|
300
312
|
|
|
301
313
|
### Draft gate contract (before marking PR ready for review)
|
|
302
314
|
|
|
@@ -311,8 +323,8 @@ The canonical checkpoint verdict comment contract is [Gate Review Comment Contra
|
|
|
311
323
|
- **Pass criteria:** all configured draft gate angles pass; all findings at severities in `blockCleanOnFindingSeverities` are addressed; validation passes; no unrelated files are included.
|
|
312
324
|
- **Next step after passing:** mark the PR ready for review.
|
|
313
325
|
- **Board status sync (built-in, after ready-for-review):** the In-Progress board move is now performed automatically as a deterministic tail of `ready-for-review.mjs` — marking the PR ready couples the board move to the ready transition (#1069), so no separate `sync-item-status` step is needed. It stays best-effort and NON-FATAL: it uses local `gh` auth (no CI/PAT), exits 0 when the board is not configured / the item is not on the board / the API fails, and never blocks marking the PR ready.
|
|
314
|
-
- **Non-substitution rule:** a clean `draft_gate` comment only authorizes the draft → ready-for-review transition for that head SHA
|
|
315
|
-
- **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract).
|
|
326
|
+
- **Non-substitution rule:** a clean `draft_gate` comment only authorizes the draft → ready-for-review transition for that head SHA; cross-gate non-substitution is owned by `GATE-COMMENT-NON-SUBSTITUTION` in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate that rule.
|
|
327
|
+
- **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Comment field content and validation-reporting format are owned by `GATE-COMMENT-VALIDATION-REPORTING`; the draft-boundary comment requirement is owned by `GATE-COMMENT-DRAFT-REQUIREMENTS`; posting-failure fail-closed behavior is owned by `GATE-COMMENT-FAIL-CLOSED` — all in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate those field/format/fail-closed rules.
|
|
316
328
|
|
|
317
329
|
### Pre-approval gate contract
|
|
318
330
|
|
|
@@ -323,11 +335,12 @@ This is the default pre-approval gate for this workflow boundary. The canonical
|
|
|
323
335
|
- **Execution directive:** run the [Gate fan-out/fan-in procedure](#gate-fan-outfan-in-procedure-agent-orchestrated) (the agent-orchestrated chain defined in [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md)) with the pre-approval gate inspection angles resolved from config. The `acceptance-criteria` angle is mandatory for this gate (see `.devloops` `gates.preApproval.mandatoryAngles`) and always survives dynamic resolution. Retry rule: in subsequent cycles, only re-run reviewers that produced `findings_present` in the previous pass.
|
|
324
336
|
- **Review angles:** resolved at runtime from config via `resolveGateAngles(config, "preApproval")` from `@dev-loops/core/config`. Default config enables all configured pre-approval gate angle families; consumer repos may opt out individual angles via `excludeAngles`.
|
|
325
337
|
- **Persona mapping:** each angle resolves to a reviewer persona via `resolveReviewerRole(config, angle)` from `@dev-loops/core/config`. Include this prompt in each reviewer's briefing so the reviewer knows exactly what to look for.
|
|
326
|
-
- **Pass criteria:** the sub-loop completes with verdict `clean`; all configured angles pass
|
|
338
|
+
- **Pass criteria:** the sub-loop completes with verdict `clean`; all configured angles pass, following the sequential-fallback rule owned by `GATE-EXEC-FANOUT-SEQUENTIAL-FALLBACK` in [Gate Review Sub-Loop Contract](../../docs/gate-review-sub-loop-contract.md).
|
|
327
339
|
- **Acceptance criteria verification:** follow the canonical procedure in [Acceptance Criteria Verification](../docs/acceptance-criteria-verification.md) before posting the `pre_approval_gate` comment. After a clean verification this also ticks the verified PR-body checkboxes via `scripts/github/tick-verified-checkboxes.mjs`, so the merged PR shows checked AC.
|
|
328
340
|
- **Next step after passing:** continue the Step 7 flow and then proceed to the human approval checkpoint below.
|
|
329
|
-
- **Non-substitution rule:** a clean `pre_approval_gate` comment is separate from `draft_gate` evidence
|
|
330
|
-
- **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract).
|
|
341
|
+
- **Non-substitution rule:** a clean `pre_approval_gate` comment governs final-approval readiness for that head SHA and is separate from `draft_gate` evidence; cross-gate non-substitution is owned by `GATE-COMMENT-NON-SUBSTITUTION` in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate that rule.
|
|
342
|
+
- **Required PR comment:** post a visible checkpoint verdict comment using the mandatory [Gate comment command](#mandatory-gate-comment-command-contract). Comment field content and validation-reporting format are owned by `GATE-COMMENT-VALIDATION-REPORTING`; the pre-approval-boundary comment requirement is owned by `GATE-COMMENT-PREAPPROVAL-REQUIREMENTS`; posting-failure fail-closed behavior is owned by `GATE-COMMENT-FAIL-CLOSED` — all in [Gate Review Comment Contract](../../docs/gate-review-comment-contract.md). This skill does not restate those field/format/fail-closed rules.
|
|
343
|
+
- <!-- rule: GATE-SKIP-NOT-RECOVERABLE-BY-CONVERGENCE --> `GATE-SKIP-NOT-RECOVERABLE-BY-CONVERGENCE`: Skipping the gate MUST NOT be treated as recoverable by asserting convergence.
|
|
331
344
|
|
|
332
345
|
### Conflict-resolution gate
|
|
333
346
|
|
|
@@ -433,6 +446,7 @@ Follow [Stop Conditions](../docs/stop-conditions.md). Genuine stops: `blocked` s
|
|
|
433
446
|
|
|
434
447
|
See [Anti-patterns](../docs/anti-patterns.md). Key repo-specific additions:
|
|
435
448
|
- Use `reply-resolve-review-thread.mjs` / `reply-resolve-review-threads.mjs` helpers instead of ad hoc `gh api`/`gh api graphql` thread-mutation commands. Do NOT use `gh pr comment`, `gh api`, or `gh pr review` for gate comments (use `upsert-checkpoint-verdict.mjs`).
|
|
449
|
+
- Use `list-review-threads.mjs` and `wait-pr-checks.mjs` instead of ad hoc `gh api graphql` review-thread queries or `gh pr checks` shell-pipe polling loops.
|
|
436
450
|
- Do not declare merge-ready without visible `pre_approval_gate` comment on current head SHA. Do not declare merge-ready based solely on `mergeable_state: clean` + CI green without gate evidence. CI green + resolved threads alone is insufficient.
|
|
437
451
|
- Do not blind-run `gh pr merge`/`gh pr update-branch`/unapproved rebase when conflicted. Do not dispatch async dev-loop tasks that omit the pre-approval gate requirement.
|
|
438
452
|
- Do not assume generated wiki is authoritative over code or CI.
|
|
@@ -25,7 +25,7 @@ Required installed runtime contract docs are shared bundled copies under `../doc
|
|
|
25
25
|
|
|
26
26
|
> Under the Claude Code harness the dev-loop runs as a single agent: run these steps directly — no read-only boundary and no separate async-subagent dispatch. See [Main Agent Contract](../docs/main-agent-contract.md).
|
|
27
27
|
|
|
28
|
-
Resolve authoritative state via the startup resolver (`npx dev-loops@0.7.
|
|
28
|
+
Resolve authoritative state via the startup resolver (`npx dev-loops@0.7.2 loop startup --issue <n>` for issues, `npx dev-loops@0.7.2 loop startup --pr <n>` for PRs), then immediately build the handoff envelope via `npx dev-loops@0.7.2 loop build-envelope --input <resolver-output.json>`. The envelope determines `requiredReads`, `nextAction`, `stopRules`, and `acceptance` — load only those files, execute only that bounded task. It is the first handoff artifact consumed before loading any route pack. See [Workflow Handoff Contract](../docs/workflow-handoff-contract.md) for the derivation contract.
|
|
29
29
|
|
|
30
30
|
**Retrospective checkpoint gate:** the resolver reads `.pi/dev-loop-retrospective-checkpoint.json` and injects the state. When the checkpoint is `missing` and the repo config `workflow.requireRetrospective` (set via `.devloops` at repo root) is `true`, the resolver returns `needs_reconcile`. Complete or explicitly skip the retrospective before starting.
|
|
31
31
|
|
|
@@ -99,17 +99,17 @@ When `@dev-loops/core` is available again, switch back to the full helper. The f
|
|
|
99
99
|
|
|
100
100
|
## Read-only info shortcut
|
|
101
101
|
|
|
102
|
-
Info/handoff requests can be served directly via `npx dev-loops@0.7.
|
|
103
|
-
- `npx dev-loops@0.7.
|
|
104
|
-
- `npx dev-loops@0.7.
|
|
105
|
-
- `npx dev-loops@0.7.
|
|
102
|
+
Info/handoff requests can be served directly via `npx dev-loops@0.7.2 loop info` (read-only; no full dev-loop run required):
|
|
103
|
+
- `npx dev-loops@0.7.2 loop info --issue <n>` — human-readable issue state summary (strategy, route, linked PR, next action)
|
|
104
|
+
- `npx dev-loops@0.7.2 loop info --pr <n>` — human-readable PR state summary (branch, CI, threads, rounds, action)
|
|
105
|
+
- `npx dev-loops@0.7.2 loop info --issue <n> --json` — machine-readable JSON output
|
|
106
106
|
|
|
107
107
|
## Reading tool output (token-economical convention)
|
|
108
108
|
|
|
109
109
|
When you need a fact from a dev-loops JSON-emitting script, climb this ladder and stop at the first rung that answers the question — never read more output than you need:
|
|
110
110
|
|
|
111
111
|
1. **Prefer the dev-loops subcommand / concise mode.** Use `loop info` or a script's `--concise`/`--summary` mode (e.g. `run-watch-cycle.mjs --concise`, `probe-copilot-review.mjs --concise`) for a human-readable digest. The concise modes surface loop state, Copilot round count, unresolved/actionable thread counts, round-cap-clean eligibility, CI status, next action, and the current round's new Copilot comment bodies.
|
|
112
|
-
2. **`--silent` / `-s` for a yes/no check.** Reads ZERO output: `… --jq '<predicate>' --silent; echo $?` exits `0` for true / `1` for false. Without `--jq`, `--silent` maps the script's success (`ok:true`) to exit `0`, failure to `1
|
|
112
|
+
2. **`--silent` / `-s` for a yes/no check.** Reads ZERO output: `… --jq '<predicate>' --silent; echo $?` exits `0` for true / `1` for false. Without `--jq`, `--silent` maps the script's success (`ok:true`) to exit `0`, failure to `1` — unless the tool documents a stricter `--silent` contract in its own usage text (e.g. `request-copilot-review.mjs` exits `0` only for `status: "requested"`). Example: `probe-copilot-review.mjs --repo o/r --pr N --jq '.status=="idle"' -s`.
|
|
113
113
|
3. **`--jq <filter>` to extract a single field.** `--jq`/`--silent` are a BASE-CLI GUARANTEE across every operator-facing JSON-result dev-loops command — each accepts a gh-style `--jq` filter (jq subset: field access, `.[]`/`.[N]`, `|`, `select(...)`, `==`/`!=`/`<`/`<=`/`>`/`>=`, `length`, `keys`), not just a named subset. (A few scripts are out of scope — e.g. build/smoke tooling, dashboard servers, dormant/unwired adapters, and scripts that write JSON to a file rather than stdout — each carried with its reason in the contract test's exclusion list.) It prints only the filtered value. An invalid filter fails closed (stderr + exit `2`), distinct from a clean predicate-false (silent exit `1`). This is enforced by a contract test (`test/contracts/jq-output-base-guarantee-contract.test.mjs`) that fails the build if a new JSON-emitting command ships without wiring the shared `scripts/lib/jq-output.mjs` emit path.
|
|
114
114
|
4. **Use a dev-loops wrapper for `gh` reads — never an agent-level raw `gh`.** A raw `gh` call is a recorded advisory retro violation (issue #1077: reported, never blocking). If no script covers the read you need, treat it as a tooling gap: file/build a thin wrapper (reuse `scripts/lib/jq-output.mjs`, like the ones below), don't shell out. The reads/edits that already have wrappers:
|
|
115
115
|
- CI run-log tail (a failing PR's job log) → `scripts/github/fetch-ci-logs.mjs --repo <o/r> --pr <n> [--failed-only] [--tail <n>]`, **never raw `gh run view --log`/`--log-failed`**. (`probe-ci-status.mjs` names the failed checks; this returns the LOG.)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Acceptance Criteria Verification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canonical owner for the acceptance-criteria verification procedure run during the `pre_approval_gate`. Referenced from [Copilot PR Follow-up Skill](../copilot-pr-followup/SKILL.md#pre-approval-gate-contract).
|
|
4
4
|
|
|
5
5
|
## Procedure
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<!-- rule: ACCEPT-CRITERIA-VERIFY-AND-REFLECT -->
|
|
8
|
+
`ACCEPT-CRITERIA-VERIFY-AND-REFLECT`: Before posting the `pre_approval_gate` comment, the agent MUST verify every acceptance criteria checklist item in the issue linked to this PR, and MUST reflect the verified items back into both the linked issue body and the PR body (exception: under the lightweight path — `specSource: pr_body` — the step 5 issue-body mirroring is skipped, since the PR body, not the issue body, is the canonical spec surface; see the lightweight fork below):
|
|
8
9
|
|
|
9
10
|
> **Lightweight (PR-body-as-spec) fork:** when the session is lightweight — the resolver output / handoff envelope carries `specSource: pr_body` (`canonicalSpecSource: pr_body`) — a linked issue still exists (`--lightweight` runs on the `--issue` path, so keep the `Closes #N` linkage and all issue-level tracking), but the **PR body**, not the issue body, is the canonical spec-of-record read for AC/DoD. In that case, still resolve the linked issue for tracking, but read the AC/DoD/invariants from the PR body rather than the issue body (skip step 2's issue-body read for spec purposes). Run `node scripts/loop/validate-pr-body-spec.mjs --repo <owner/name> --pr <pr-number> --expected-issue <issue-number>` (reuses `@dev-loops/core/loop/issue-refinement-artifact`) to confirm the body carries the required invariants — it fails closed with a per-section `missing_*` reason if any is absent, in which case post the gate comment with verdict `blocked`. The PR body MUST carry the `Closes #N` linkage (or GitHub's other closing-keyword forms): `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when `--expected-issue` doesn't match. Then continue at step 3, extracting the **Acceptance criteria** checklist items from the PR body itself; step 5's issue-body AC-tick update is unnecessary for lightweight since the issue body is not the spec surface (the PR body is ticked in step 6 as usual). The default issue-backed procedure below is unchanged.
|
|
10
11
|
|
|
@@ -8,11 +8,12 @@ Canonical owner for anti-pattern guidance across all workflow families.
|
|
|
8
8
|
2. **Routing review-only work through local_implementation**: Review-only comparison, synthesis, or consolidation must use `refiner` agent, not `dev-loop` + `local_implementation`.
|
|
9
9
|
3. **Thin placeholder PR descriptions**: Always include change summary, scope/context, acceptance criteria, definition of done, non-goals, and `Closes #N`.
|
|
10
10
|
4. **Merging directly to main without PR**: Use PR-based remote loop when practical.
|
|
11
|
-
5. **Duplicate worktree paths**:
|
|
12
|
-
6. **Main-checkout mutation**:
|
|
11
|
+
5. **Duplicate worktree paths**: See `WORKTREE-DEDUPE` in [Worktree usage guidance](../../docs/worktree-guidance.md#coordination-and-collision-checks).
|
|
12
|
+
6. **Main-checkout mutation**: See `WORKTREE-DEFAULT-USE` in [Worktree usage guidance](../../docs/worktree-guidance.md#default-rule-use-a-worktree-for-mutating-local-work).
|
|
13
13
|
7. **Spelunking tooling internals instead of using the public surface**: Do not read installed package internals, scan tooling source, or run ad-hoc scripts to understand a tool's behavior. Use the CLI, its `--help` subcommands, and `skills/docs/`. Read tool source only when the task is to inspect or change that tool, or when a concrete failure path is inside it and no public CLI/docs path exists. Once a failure is concrete, search changed files for the exact pattern — don't run duplicate broad searches.
|
|
14
14
|
8. **Hand-editing the queue file when a board is configured**: When a GitHub Projects board is configured (`queue.projectNumber`/`queue.boardTitle` in `.devloops`), the board is the authoritative queue MEMBERSHIP and ordering source — not just status. Add work via the board (`dev-loops queue add ... --next-up` to land directly in the normative `Next Up` pickup queue; default lands in Backlog, which is unprioritized intake and never auto-picked), not by hand-editing `.pi/dev-loop-queue.json`; the queue runner reconciles board `Next Up` items into queue entries before each run. See the operator guides under `docs/` (queue board usage/setup) for the workflow.
|
|
15
15
|
9. **Hand-rolling a fan-in wait by polling a subagent transcript or parsing tool JSON with `node -e`/`python3`**: When awaiting fan-out reviewers (gate `draft_gate`/`pre_approval_gate` sub-loops, or any Agent-tool fan-out), NEVER tail/parse another agent's JSONL transcript, NEVER use `node -e`/`python3` to parse tool/subagent JSON, and NEVER `sleep`-poll in a shell loop for completion. These improvisations breach the [internal-tooling-only rule (issue #982)](retrospective-checkpoint-contract.md#internal-tooling-only-rule-issue-982--now-advisory) (no `node -e`/`python3` parsing of tool JSON) and the no-shell-polling rule, and a single improvised wait adds a non-empty entry to the advisory `retrospectiveFindings.rawCallViolations` array (issue #1077: reported to the conductor, **never blocking**). **Sanctioned wait:** rely on the harness completion notification, or read each reviewer's findings artifact at its deterministic output path (see [Checkpoint Review Chain Contract](../../docs/gate-review-sub-loop-contract.md) Phase 2), then join via `consolidateFanin` from `@dev-loops/core/loop/gate-fanin` (Phase 3). See also the dev-loop SKILL "Bounded async task contract".
|
|
16
|
+
10. **Ad hoc `gh api graphql` review-thread queries or `gh pr checks`/shell-pipe CI polling**: Do not hand-write a GraphQL query to enumerate review threads, and do not wrap `gh pr checks` in an `awk`/`grep`/`until`-based shell loop to wait for CI. Enumerate threads (with the ids `reply-resolve-review-thread.mjs` needs) via `scripts/github/list-review-threads.mjs`; block on current-head CI settling via `scripts/github/wait-pr-checks.mjs` (or `dev-loops loop watch-ci` for the JSON-status variant); read the aggregate loop/CI/thread state via `detect-copilot-loop-state.mjs` instead of re-deriving it from raw `gh` output.
|
|
16
17
|
|
|
17
18
|
## Light mode exception
|
|
18
19
|
|
|
@@ -8,7 +8,8 @@ Other repo docs may summarize or link this contract, but they should not redefin
|
|
|
8
8
|
|
|
9
9
|
## Two-tier model
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<!-- rule: ARTIFACT-TWO-TIER-EXCLUSIVE -->
|
|
12
|
+
dev-loops supports two mutually exclusive artifact authority modes. Every work item MUST originate from exactly one authoritative artifact: a GitHub issue or a persisted markdown plan file. Work MUST NOT originate from a PR or a direct local change unless explicitly requested.
|
|
12
13
|
|
|
13
14
|
The shipped extension default selects local-planning; see [Shipped default posture](#shipped-default-posture) below. The mode names that follow describe the two tiers; "default" in their headings refers to the local-first code-level default in `BUILT_IN_DEFAULTS`.
|
|
14
15
|
|
|
@@ -19,7 +20,7 @@ The shipped extension default selects local-planning; see [Shipped default postu
|
|
|
19
20
|
Artifacts:
|
|
20
21
|
- **Planning artifact:** GitHub issue (title, body, labels, assignees, acceptance criteria)
|
|
21
22
|
- **Execution artifact:** GitHub PR (linked to issue; created during implementation)
|
|
22
|
-
- **No local duplicate:**
|
|
23
|
+
- <!-- rule: ARTIFACT-TRACKER-FIRST-NO-DUP --> **No local duplicate:** A tracker-first session MUST NOT create `docs/phases/phase-<n>.md` for the same session when a GitHub issue is the canonical spec
|
|
23
24
|
|
|
24
25
|
Key contract:
|
|
25
26
|
- GitHub issue state is authoritative — not local notes or chat context
|
|
@@ -40,7 +41,7 @@ Artifacts:
|
|
|
40
41
|
Key contract:
|
|
41
42
|
- The markdown plan file is the canonical spec — not a duplicate of a tracker issue
|
|
42
43
|
- GitHub issues may still be used for tracking or linking, but the plan file is authoritative for scope and acceptance criteria
|
|
43
|
-
- A tracker-backed local implementation session (GitHub issue as canonical spec)
|
|
44
|
+
- A tracker-backed local implementation session (GitHub issue as canonical spec) is bound by [ARTIFACT-TRACKER-FIRST-NO-DUP](#tracker-first) above — see [Public Dev Loop Contract](public-dev-loop-contract.md) "Tracker-backed local implementation input-source contract"
|
|
44
45
|
|
|
45
46
|
### Lightweight (PR-body-as-spec)
|
|
46
47
|
|
|
@@ -51,11 +52,10 @@ Artifacts:
|
|
|
51
52
|
- **No committed plan doc:** no `docs/phases/*.md` is created for the session
|
|
52
53
|
|
|
53
54
|
Key contract:
|
|
54
|
-
- The PR body
|
|
55
|
-
- The PR body MUST also carry the `Closes #N` linkage (GitHub's other closing keywords count too); `validate-pr-body-spec` fails closed with `missing_closing_issue_reference` without it, and with `closes_wrong_issue` when an `--expected-issue` is given and doesn't match — the lightweight path's issue-tracking state must never silently diverge from PR state (issue #1181).
|
|
55
|
+
- <!-- rule: ARTIFACT-LIGHTWEIGHT-BODY-INVARIANTS --> The PR body MUST carry the same invariants a durable spec would: **Objective/why, in-scope + explicit non-goals, testable acceptance criteria, definition of done, open questions/risks** — unconditionally, whether or not the work is tracker-backed. The `Closes #N` linkage (GitHub's other closing keywords count too) is conditional on artifact backing (operator ruling, issue #1210): REQUIRED when the work originates from a GitHub issue (`--issue --lightweight`), ABSENT BY DESIGN when the PR is the sole artifact with no backing issue (`--lightweight` alone, issue-less PR-first) — an issue-less PR body MUST NOT carry a closing reference to an issue that doesn't back it. `scripts/loop/validate-pr-body-spec.mjs` (reusing the generic markdown logic of `@dev-loops/core/loop/issue-refinement-artifact`, `validatePrBodySpec`) validates these and fails closed with a distinct reason per violated invariant — `missing_closing_issue_reference` without the linkage in tracker-backed mode, `closes_wrong_issue` when an `--expected-issue` is given and doesn't match, `unexpected_closing_issue_reference` when a closing reference is present under explicit issue-less mode (`--no-issue`) — so the lightweight path's issue-tracking state never silently diverges from PR state (issue #1181).
|
|
56
56
|
- This flips the promotion invariant below (P4, "the PR body carries the committed plan-doc **path**"): under lightweight there is no committed plan doc — the PR body **is** the spec, not a pointer to one.
|
|
57
57
|
- The explicit `--lightweight` flag is the primary, deterministic trigger. The secondary heuristic (chore/fix commit type + no `--plan-file` + small change) is a documented manual signal for when to reach for the flag; it is not an automatic selector.
|
|
58
|
-
- `--lightweight`
|
|
58
|
+
- <!-- rule: ARTIFACT-LIGHTWEIGHT-PLAN-FILE-EXCLUSIVE --> `--lightweight` MUST be rejected when combined with `--plan-file` (they are opposites: `--plan-file` commits a durable plan doc as the spec, `--lightweight` makes the PR body the spec). It composes with `--issue` (tracker-backed) or stands alone (issue-less PR-first, #1210 — gated on `localImplementation.lightMode` being enabled and the change scope staying within its threshold); it MUST be rejected when combined with any other mode flag (`--pr`, `--input`, `--spike`).
|
|
59
59
|
- Pre-approval acceptance-criteria verification reads the AC/DoD/invariants directly from the PR body rather than a linked issue body; see [Acceptance Criteria Verification](acceptance-criteria-verification.md).
|
|
60
60
|
|
|
61
61
|
### Mode selection table
|
|
@@ -64,7 +64,7 @@ Key contract:
|
|
|
64
64
|
|---|---|---|---|
|
|
65
65
|
| Tracker-first | GitHub issue | Yes | `strategy.default: github-first` |
|
|
66
66
|
| Local-planning (shipped default) | Markdown plan file | No | `strategy.default: local-first` |
|
|
67
|
-
| Lightweight (PR-body-as-spec) | GitHub PR description |
|
|
67
|
+
| Lightweight (PR-body-as-spec) | GitHub PR description | Conditional — `--issue` when tracker-backed; absent for issue-less PR-first (#1210), gated on `localImplementation.lightMode` + change-scope threshold | modifier: `--lightweight` (`canonicalSpecSource: pr_body`) |
|
|
68
68
|
|
|
69
69
|
`inputSource.default` further disambiguates local-first startup:
|
|
70
70
|
| inputSource | Meaning |
|
|
@@ -106,13 +106,14 @@ The effective default for a consumer comes from the config-merge layering in `pa
|
|
|
106
106
|
|
|
107
107
|
With nothing but the shipped package in place, the extension layer resolves `strategy.default` to `local-first`, so the shipped default posture is local-planning (epic #947, decision #7). A repo opts back into tracker-first by setting `strategy.default: github-first` in its own `.devloops`.
|
|
108
108
|
|
|
109
|
-
Two legacy repo-local layers also exist under `.pi/dev-loop/` (the package no longer ships a `.pi/dev-loop/defaults.yaml`). They differ in how they load: `.pi/dev-loop/defaults.*` is always applied when present, between the extension defaults and `.devloops`; `.pi/dev-loop/settings.*` (and the older `overrides.*`) load only when no `.devloops` is present — when `.devloops` exists it is authoritative and those files are ignored (with a deprecation warning).
|
|
109
|
+
Two legacy repo-local layers also exist under `.pi/dev-loop/` (the package no longer ships a `.pi/dev-loop/defaults.yaml`). They differ in how they load, per the precedence list above: `.pi/dev-loop/defaults.*` is always applied when present, between the extension defaults and `.devloops`; `.pi/dev-loop/settings.*` (and the older `overrides.*`) load only when no `.devloops` is present — when `.devloops` exists it is authoritative and those files are ignored (with a deprecation warning).
|
|
110
110
|
|
|
111
111
|
### Explicit non-knobs
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
113
|
+
<!-- rule: ARTIFACT-STRATEGY-ENUM-FAIL-CLOSED -->
|
|
114
|
+
`ARTIFACT-STRATEGY-ENUM-FAIL-CLOSED`: The strategy enum MUST accept only `github-first` or `local-first` and MUST fail closed on any other value (`packages/core/src/config/config.mjs`). These are not valid artifact authority mode selectors:
|
|
115
|
+
- `strategy.default: copilot` — not a valid mode
|
|
116
|
+
- Free-form string values — MUST fail closed
|
|
116
117
|
- Omitting `strategy.default` from every layer — resolves to `local-first` from `BUILT_IN_DEFAULTS`
|
|
117
118
|
|
|
118
119
|
## Local-first plan-file flow end to end
|
|
@@ -4,6 +4,7 @@ Canonical owner for agent confirmation / authorization rules across all workflow
|
|
|
4
4
|
|
|
5
5
|
## Core rule
|
|
6
6
|
|
|
7
|
+
<!-- rule: CONFIRM-CORE-EXPLICIT -->
|
|
7
8
|
Before any state-changing action, get explicit confirmation unless the latest user message already clearly authorizes that exact action.
|
|
8
9
|
|
|
9
10
|
## What counts as confirmation
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Contract style guide
|
|
2
|
+
|
|
3
|
+
Canonical owner for contract style, rule IDs, and definitional discipline.
|
|
4
|
+
|
|
5
|
+
A short orientation paragraph introduces the document; normative rules live only in the rule table.
|
|
6
|
+
|
|
7
|
+
## Terms
|
|
8
|
+
|
|
9
|
+
| Term | Definition |
|
|
10
|
+
|---|---|
|
|
11
|
+
| <!-- term: gate:pre_approval_gate --> `pre_approval_gate` | Review gate that checks changed contract rules against the registry before approval. |
|
|
12
|
+
|
|
13
|
+
## Rule ownership
|
|
14
|
+
|
|
15
|
+
| Rule ID | Rule |
|
|
16
|
+
|---|---|
|
|
17
|
+
| <!-- rule: STYLE-RFC2119-KEYWORDS --> `STYLE-RFC2119-KEYWORDS` | Normative contract text MUST use RFC-2119 keywords (`MUST`, `MUST NOT`, `SHALL`, `SHALL NOT`, `SHOULD`, `MAY`) when declaring requirements. |
|
|
18
|
+
| <!-- rule: STYLE-SINGLE-OWNER --> `STYLE-SINGLE-OWNER` | Each normative rule MUST have one single owner: exactly one source document defines the rule and every other document references that rule by ID instead of restating it. |
|
|
19
|
+
| <!-- rule: STYLE-RULE-MARKER --> `STYLE-RULE-MARKER` | A rule definition MUST place an HTML marker immediately before the rule body using `<!-- rule: AREA-TOPIC-NNN -->`; markers are invisible in rendered Markdown and MUST pass through mirror generation verbatim. |
|
|
20
|
+
| <!-- rule: STYLE-ID-SCHEME --> `STYLE-ID-SCHEME` | Rule IDs MUST be stable uppercase identifiers using the descriptive `AREA-TOPIC-NNN` shape or a short descriptive suffix when clearer. |
|
|
21
|
+
| <!-- rule: STYLE-ID-STABILITY --> `STYLE-ID-STABILITY` | A rule ID is stable forever: semantic changes MUST retire the old ID and mint a new ID through a dedicated semantic-change issue, never silent mutation. |
|
|
22
|
+
| <!-- rule: STYLE-REFERENCE-BY-ID --> `STYLE-REFERENCE-BY-ID` | A non-owner document MUST reference an owned rule by ID, preferably with a link to the owner, and MUST NOT duplicate the owned rule text. |
|
|
23
|
+
| <!-- rule: STYLE-DEFINED-TERMS --> `STYLE-DEFINED-TERMS` | State tokens, reason codes, and gate names MUST have exactly one canonical definition before annotated rule text uses them. |
|
|
24
|
+
| <!-- rule: STYLE-TERM-MARKER --> `STYLE-TERM-MARKER` | A canonical term definition MUST use `<!-- term: kind:value -->`, where `kind` is `state`, `reason`, or `gate`. |
|
|
25
|
+
| <!-- rule: STYLE-PRECEDENCE-CONFLICT --> `STYLE-PRECEDENCE-CONFLICT` | When two contract statements conflict, the document that owns the referenced rule ID wins until a semantic-change issue retires or replaces the rule. |
|
|
26
|
+
| <!-- rule: STYLE-CANONICAL-OPENER --> `STYLE-CANONICAL-OPENER` | A contract document SHOULD open with `Canonical owner for X` and SHOULD end with a Cross-references section. |
|
|
27
|
+
| <!-- rule: STYLE-CONTRADICTION-LENS --> `STYLE-CONTRADICTION-LENS` | A contract-touching PR MUST include an RFC-2119 contradiction check in `pre_approval_gate`: every added or changed rule is checked against the rule registry for opposing modality or a weaker restatement. |
|
|
28
|
+
| <!-- rule: STYLE-LEXICAL-SCAN-LIMIT --> `STYLE-LEXICAL-SCAN-LIMIT` | The ownership validator's contradiction scan is lexical only; behavioral contradictions belong to the L2/L3 harness and semantic contradictions belong to the gate contradiction lens. |
|
|
29
|
+
|
|
30
|
+
## Reference syntax
|
|
31
|
+
|
|
32
|
+
Use `<!-- rule-ref: RULE-ID -->` next to a cross-reference when a machine-checkable reference is useful. Markdown links may also use the rule ID as link text.
|
|
33
|
+
|
|
34
|
+
## Cross-references
|
|
35
|
+
|
|
36
|
+
- [Stop conditions](stop-conditions.md)
|
|
37
|
+
- State-machine conformance + invariant harness (`scripts/docs/validate-state-machine-conformance.mjs`) — L2 (doc ↔ code) conformance and L3 (completeness/safety/liveness) graph invariants; its header comment documents the registration path for adding a machine
|