devflow-kit 2.4.0 → 3.0.0
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/CHANGELOG.md +229 -0
- package/README.md +111 -18
- package/dist/agents/git.md +822 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/ambient.js +160 -145
- package/dist/cli/commands/attribution-prompts.js +1 -1
- package/dist/cli/commands/capture.js +29 -55
- package/dist/cli/commands/compliance-prompts.js +1 -1
- package/dist/cli/commands/compliance.js +48 -55
- package/dist/cli/commands/context.js +17 -32
- package/dist/cli/commands/debug.js +65 -26
- package/dist/cli/commands/flags.js +3 -3
- package/dist/cli/commands/hud.js +34 -10
- package/dist/cli/commands/init-seed.js +61 -27
- package/dist/cli/commands/init.js +649 -240
- package/dist/cli/commands/install-report.js +200 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +35 -37
- package/dist/cli/commands/learning.js +79 -57
- package/dist/cli/commands/legacy-hooks.js +11 -14
- package/dist/cli/commands/memory.js +134 -135
- package/dist/cli/commands/prompt-io.js +4 -4
- package/dist/cli/commands/proxy.js +23 -41
- package/dist/cli/commands/security.js +81 -29
- package/dist/cli/commands/skills.js +71 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +277 -0
- package/dist/cli/commands/uninstall.js +520 -169
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +58 -14
- package/dist/commands/code-review.md +110 -32
- package/dist/commands/debug.md +55 -11
- package/dist/commands/dynamic-build.md +344 -73
- package/dist/commands/dynamic-plan.md +77 -27
- package/dist/commands/dynamic-profile.md +25 -11
- package/dist/commands/dynamic-tickets.md +76 -15
- package/dist/commands/explore.md +37 -7
- package/dist/commands/implement.md +314 -62
- package/dist/commands/plan.md +146 -32
- package/dist/commands/release.md +64 -17
- package/dist/commands/research.md +34 -8
- package/dist/commands/resolve.md +196 -68
- package/dist/commands/self-review.md +45 -9
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/compliance-compose.js +27 -27
- package/dist/core/evidence-policy.js +363 -0
- package/dist/core/feature-config.js +200 -65
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +34 -6
- package/dist/core/fs-atomic.js +27 -0
- package/dist/core/hook-log-dirs.js +104 -0
- package/dist/core/learning-tuning-config.js +5 -3
- package/dist/core/ledger-root.js +102 -0
- package/dist/core/manifest.js +38 -10
- package/dist/core/mds-variants.js +798 -0
- package/dist/core/migrations.js +49 -23
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +361 -12
- package/dist/core/project-paths.js +1 -18
- package/dist/core/proxy-log.js +8 -6
- package/dist/core/proxy-state.js +11 -8
- package/dist/core/reference-sweep.js +136 -0
- package/dist/core/same-location.js +25 -0
- package/dist/core/tracker.js +494 -0
- package/dist/hud/components/config-counts.js +15 -4
- package/dist/hud/components/learning-counts.js +14 -0
- package/dist/hud/config.js +2 -1
- package/dist/hud/cost-history.js +2 -4
- package/dist/hud/git.js +52 -7
- package/dist/hud/index.js +7 -9
- package/dist/skills/git/references/decision-markers.md +19 -0
- package/dist/skills/git/references/learn-conventions.md +56 -0
- package/dist/skills/git/references/pr/check-ci-status.md +14 -0
- package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
- package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
- package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
- package/dist/skills/git/references/pr/post-review-summary.md +42 -0
- package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
- package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
- package/dist/skills/git/references/pr/validate-branch.md +18 -0
- package/dist/skills/git/references/publication-gate.md +13 -0
- package/dist/skills/git/references/tracker/_mcp.md +153 -0
- package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
- package/dist/skills/git/references/tracker/github/create-release.md +11 -0
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
- package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
- package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
- package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
- package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
- package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
- package/dist/skills/git/references/trust-rule.md +7 -0
- package/dist/targets/claude-code/claude-paths.js +59 -57
- package/dist/targets/claude-code/compliance-install.js +49 -65
- package/dist/targets/claude-code/hooks.js +108 -3
- package/dist/targets/claude-code/installer.js +1187 -32
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +366 -151
- package/dist/targets/claude-code/tracker-install.js +134 -0
- package/package.json +8 -6
- package/src/assets/agents/code.md +45 -6
- package/src/assets/agents/design.md +2 -1
- package/src/assets/agents/git.mds +825 -0
- package/src/assets/agents/knowledge.md +3 -3
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/review.md +3 -1
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +474 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_compliance.mds +19 -1
- package/src/assets/commands/_partials/_decisions.mds +15 -3
- package/src/assets/commands/_partials/_docs_root.mds +35 -0
- package/src/assets/commands/_partials/_engine.mds +13 -11
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_factory.mds +1 -1
- package/src/assets/commands/_partials/_knowledge.mds +27 -9
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +2 -2
- package/src/assets/commands/_partials/_publication.mds +8 -2
- package/src/assets/commands/_partials/_settings.mds +28 -0
- package/src/assets/commands/_partials/_ticket_template.mds +3 -2
- package/src/assets/commands/_partials/_tracker.mds +18 -0
- package/src/assets/commands/_partials/_wave.mds +16 -10
- package/src/assets/commands/bug-analysis.mds +31 -19
- package/src/assets/commands/code-review.mds +67 -41
- package/src/assets/commands/debug.mds +13 -7
- package/src/assets/commands/dynamic-build.mds +274 -66
- package/src/assets/commands/dynamic-plan.mds +50 -23
- package/src/assets/commands/dynamic-profile.mds +24 -11
- package/src/assets/commands/dynamic-tickets.mds +63 -16
- package/src/assets/commands/explore.mds +4 -5
- package/src/assets/commands/implement.mds +234 -67
- package/src/assets/commands/plan.mds +91 -33
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/research.mds +11 -9
- package/src/assets/commands/resolve.mds +150 -78
- package/src/assets/commands/self-review.mds +24 -25
- package/src/assets/mds/git/_pr.mds +331 -0
- package/src/assets/mds/git/_references.mds +135 -0
- package/src/assets/mds/tracker/_common.mds +156 -0
- package/src/assets/mds/tracker/_github.mds +472 -0
- package/src/assets/mds/tracker/_jira.mds +407 -0
- package/src/assets/mds/tracker/_linear.mds +449 -0
- package/src/assets/mds/tracker/_mcp.mds +305 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +40 -19
- package/src/assets/scripts/hooks/capture-prompt +18 -8
- package/src/assets/scripts/hooks/capture-question +18 -8
- package/src/assets/scripts/hooks/capture-turn +27 -13
- package/src/assets/scripts/hooks/debug-trace +11 -6
- package/src/assets/scripts/hooks/ensure-devflow-init +33 -6
- package/src/assets/scripts/hooks/ensure-proxy +9 -8
- package/src/assets/scripts/hooks/ensure-root-gitignore +236 -60
- package/src/assets/scripts/hooks/git-marker +48 -0
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +228 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -20
- package/src/assets/scripts/hooks/log-paths +80 -0
- package/src/assets/scripts/hooks/memory-worker +22 -13
- package/src/assets/scripts/hooks/pre-compact-memory +44 -15
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +146 -28
- package/src/assets/scripts/hooks/resolve-project-root +101 -7
- package/src/assets/scripts/hooks/session-start-context +534 -20
- package/src/assets/scripts/hooks/session-start-memory +38 -15
- package/src/assets/scripts/lib/project-config.cjs +633 -0
- package/src/assets/scripts/pr-evidence.cjs +1961 -0
- package/src/assets/scripts/redact-secrets.cjs +490 -62
- package/src/assets/scripts/release-trace.cjs +1143 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +1145 -0
- package/src/assets/scripts/resolve-settings.cjs +1054 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +4 -2
- package/src/assets/skills/docs-framework/SKILL.md +11 -10
- package/src/assets/skills/docs-framework/references/patterns.md +10 -17
- package/src/assets/skills/gap-analysis/SKILL.md +2 -2
- package/src/assets/skills/git/SKILL.md +8 -78
- package/src/assets/skills/git/references/github-api.md +179 -141
- package/src/assets/skills/git/references/patterns.md +11 -6
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/references/patterns.md +6 -61
- package/src/assets/skills/review-methodology/references/violations.md +14 -22
- package/src/assets/skills/worktree-support/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +29 -0
- package/src/targets/claude-code/templates/managed-settings.json +25 -9
- package/src/assets/agents/git.md +0 -938
|
@@ -4,13 +4,16 @@ argument-hint: "[ticket | issue-url | plan-doc]"
|
|
|
4
4
|
output-dir: dist/commands
|
|
5
5
|
---
|
|
6
6
|
@import { authoring_preamble } from "./_partials/_preamble.mds"
|
|
7
|
-
@import {
|
|
7
|
+
@import { evidence_policy } from "./_partials/_evidence_policy.mds"
|
|
8
|
+
@import { docs_root } from "./_partials/_docs_root.mds"
|
|
8
9
|
@import { agent_roster, agent_caveats } from "./_partials/_roster.mds"
|
|
9
10
|
@import { gate1_postcode, gate2_acceptance, evaluator_panel, implement_bundle, review_pass, concurrency_doctrine, build_execution_doctrine, engine_output_schema, engine_invariants } from "./_partials/_engine.mds"
|
|
10
11
|
@import { wave_loop, branch_merge_model, merge_doctrine, escalation_model } from "./_partials/_wave.mds"
|
|
11
12
|
@import { acceptance_criteria_contract } from "./_partials/_plan_contract.mds"
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
@import { issue_ref_grammar, issue_capture_contract } from "./_partials/_tracker.mds"
|
|
14
|
+
@import "./_partials/_publication.mds" as pub
|
|
15
|
+
@import "./_partials/_compliance.mds" as compliance
|
|
16
|
+
{{authoring_preamble()}}
|
|
14
17
|
|
|
15
18
|
---
|
|
16
19
|
|
|
@@ -18,14 +21,14 @@ output-dir: dist/commands
|
|
|
18
21
|
|
|
19
22
|
This command instructs you to construct and run a Claude Code dynamic Workflow that implements, reviews, and verifies one ticket or a full wave of tickets, reusing devflow's existing agents via `agentType`.
|
|
20
23
|
|
|
21
|
-
{agent_roster()}
|
|
24
|
+
{{agent_roster()}}
|
|
22
25
|
|
|
23
|
-
{agent_caveats()}
|
|
26
|
+
{{agent_caveats()}}
|
|
24
27
|
|
|
25
28
|
---
|
|
26
29
|
|
|
27
30
|
**Requires:** ticket or task description; optional plan document and acceptance criteria from `/devflow:dynamic-plan`
|
|
28
|
-
**Produces:** implemented and reviewed branch per ticket; wave run report at
|
|
31
|
+
**Produces:** implemented and reviewed branch per ticket; wave run report at `{integration worktree root}/.devflow/docs/waves/{slug}/{ts}/wave-report.md`
|
|
29
32
|
|
|
30
33
|
---
|
|
31
34
|
|
|
@@ -35,8 +38,8 @@ Before authoring, verify:
|
|
|
35
38
|
|
|
36
39
|
1. **Workflow tool available:** if the `Workflow` tool is not in your available tools, STOP and tell the user: "The Workflow tool is not available in this session. dynamic-build requires Claude Code's dynamic workflow runtime."
|
|
37
40
|
2. **`agentType` support:** confirmed available (spike F5, 2026-06-11). If spawned agents return no results, check that devflow is installed (`devflow init` has been run).
|
|
38
|
-
3. **
|
|
39
|
-
4. **No-remote path:** if the repo has no remote, skip
|
|
41
|
+
3. **Tracker paths:** an issue reference or URL in the input is read through the Git agent, which resolves the configured tracker and its access itself and reports `TRACEABILITY: DEGRADED ({reason})` when it cannot read one — then note it and fall back to the issue text the user provided. No tracker CLI is checked here.
|
|
42
|
+
4. **No-remote path:** if the repo has no remote, skip the remote-dependent steps (the wave PR and its evidence) and proceed with local branch operations only; the Git agent reports DEGRADED for any tracker step it cannot reach.
|
|
40
43
|
|
|
41
44
|
---
|
|
42
45
|
|
|
@@ -44,12 +47,27 @@ Before authoring, verify:
|
|
|
44
47
|
|
|
45
48
|
Before you write the workflow script:
|
|
46
49
|
|
|
47
|
-
**0. Resolve
|
|
50
|
+
**0. Resolve the evidence policy**
|
|
51
|
+
|
|
52
|
+
**Produces:** EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL
|
|
53
|
+
|
|
54
|
+
{{evidence_policy()}}
|
|
55
|
+
|
|
56
|
+
Author the resolved `ISSUE_REQUIRED` and `APPLY_CONVENTIONS` into the workflow script as constants — pass them as `issueRequired` and `applyConventions` when invoking the workflow — and pass both to the Git `setup-task` spawn.
|
|
57
|
+
|
|
58
|
+
**0b. Resolve the compliance lens**
|
|
59
|
+
|
|
60
|
+
**Produces:** COMPLIANCE_FRAMEWORKS
|
|
61
|
+
|
|
62
|
+
{{compliance.compliance_lens()}}
|
|
63
|
+
|
|
64
|
+
Pass it as `complianceFrameworks` when invoking the workflow; the engine hands it to every Code agent.
|
|
48
65
|
|
|
49
|
-
|
|
50
|
-
Reuse this result for every ticket in the wave.
|
|
66
|
+
**0c. Resolve the docs root**
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
{{docs_root()}}
|
|
69
|
+
|
|
70
|
+
`{integration worktree root}` is the toplevel of the checkout the integration branch is checked out in: `{worktree}` unless the wave runs in a linked worktree.
|
|
53
71
|
|
|
54
72
|
**1. Apply decisions context**
|
|
55
73
|
|
|
@@ -62,7 +80,8 @@ Note the `budget` value from the Workflow tool context (or default to "medium" i
|
|
|
62
80
|
**3. Detect mode: SINGLE or WAVE**
|
|
63
81
|
|
|
64
82
|
- **SINGLE mode:** input is one ticket, one issue, one task description, or one plan document
|
|
65
|
-
- **WAVE mode:** input is a set of
|
|
83
|
+
- **WAVE mode:** input is a set of tracker issues (wave labels, milestone, issue list), or the user says "wave" / "all tickets in wave N"
|
|
84
|
+
- **A `/devflow:dynamic-tickets` ticket directory** (`{worktree}/.devflow/docs/tickets/{slug}/{ts}/`) is WAVE input: in each ticket file (every `.md` there but `tracking-issue.md`), the `**Issue:**` line directly after `**Depends on:**` is one raw `ISSUE_REFS` token, forwarded to the wave's pre-fetch verbatim — never rendered, normalised or re-derived. A ticket file with no `**Issue:**` line, or more than one, contributes no token; name it in the run summary as `not filed`.
|
|
66
85
|
|
|
67
86
|
When ambiguous, ask the user before authoring: "Is this a single ticket or a wave of tickets?"
|
|
68
87
|
|
|
@@ -70,24 +89,41 @@ When ambiguous, ask the user before authoring: "Is this a single ticket or a wav
|
|
|
70
89
|
|
|
71
90
|
Check for (in priority order):
|
|
72
91
|
- A plan document passed as input (path or inline)
|
|
73
|
-
-
|
|
92
|
+
- An issue body (fetched via the Git agent using `OPERATION: fetch-issue`)
|
|
74
93
|
- The current working context (recent `/devflow:dynamic-plan` output)
|
|
75
94
|
- An in-context task description
|
|
76
95
|
|
|
96
|
+
{{issue_capture_contract()}}
|
|
97
|
+
|
|
77
98
|
Extract or note:
|
|
78
99
|
- Implementation plan (for Code agent prompt and Evaluate agent)
|
|
79
|
-
- Acceptance criteria
|
|
100
|
+
- Acceptance criteria (for Gate 2) — pass them as `criteria` when invoking the workflow
|
|
101
|
+
- The test plan (for Gate 2) — a plan's TP lines, passed only once they pass this check. Copy the plan's `## Test Plan` section byte for byte into a fresh `mktemp` file with the Write tool, never through an interpolated shell string, and run:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node "$HOME/.devflow/scripts/verify-evidence.cjs" check tp <that file>; echo "exit=$?"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Only `exit=0` passes it: pass the section's TP lines, as one string, as `testPlan` when invoking the workflow — a test plan alone still runs the Test agent. Any other result, or a plan with no `## Test Plan` section: omit `testPlan` and note `Test plan: missing or malformed` in the run summary. Nothing is repaired, and a test plan in any other shape — an older JSON one included — is never passed.
|
|
108
|
+
|
|
109
|
+
In WAVE mode, run this step once per ticket and author the results as `plans`, keyed by the ticket's reference: each ticket gets its own `plan`, `criteria` and checked `testPlan` — never one test plan for the whole wave. Keep each ticket's checked TP lines: step 3 after the workflow builds the wave test plan from them.
|
|
80
110
|
|
|
81
111
|
If none found: build proceeds Gate-1-only (Gate 2 skipped with a note). Never refuse to build; never fabricate criteria.
|
|
82
112
|
|
|
83
113
|
**5. Resolve tracking-issue number (optional)**
|
|
84
114
|
|
|
85
115
|
Check, in priority order:
|
|
86
|
-
- An explicit issue
|
|
87
|
-
- The
|
|
116
|
+
- An explicit candidate issue reference or issue URL in the user's input (e.g. `#42`, `42`, or `https://github.com/…/issues/42`)
|
|
117
|
+
- The `**Issue:**` line directly after the H1 of the ticket set's `tracking-issue.md` (written by `/devflow:dynamic-tickets`' filing step; the file is at `{worktree}/.devflow/docs/tickets/{slug}/{ts}/tracking-issue.md`), as a raw token — only when the file holds exactly one `**Issue:**` line
|
|
88
118
|
- Otherwise: none
|
|
89
119
|
|
|
90
|
-
|
|
120
|
+
{{issue_ref_grammar()}}
|
|
121
|
+
|
|
122
|
+
If a number is found, record it as the command-level `ISSUE_NUMBER`:
|
|
123
|
+
- **SINGLE mode:** it is the ticket's own reference. Pass it as `issueNumber: <number>` when invoking the workflow; the engine hands it to setup-task as `ISSUE_INPUT`. If none is found, pass nothing.
|
|
124
|
+
- **WAVE mode:** it is the tracking issue. Only steps 2 and 3 after the workflow use it: step 2 for the wave report, step 3 for the wave block's tracking line. It never reaches a ticket: each ticket's engine gets that ticket's own reference as `issueInput`.
|
|
125
|
+
|
|
126
|
+
Never pass `ISSUE_PR_LINK` into the workflow. The engine binds each ticket's `ISSUE_NUMBER` and `ISSUE_PR_LINK` from that ticket's own setup-task Output (`### Handoff Values`), never from the token it passed in, and hands both to that ticket's Code agents. No `- **PR link line**:` captured ⇒ `ISSUE_PR_LINK` is `"(none)"` and the Code agent emits the `## Related Issues` heading with no reference.
|
|
91
127
|
|
|
92
128
|
---
|
|
93
129
|
|
|
@@ -105,22 +141,44 @@ export const meta = {
|
|
|
105
141
|
// SINGLE mode: one ticket, one branch, full engine
|
|
106
142
|
|
|
107
143
|
const TICKET = args.ticket || args[0] || "see task description";
|
|
108
|
-
const BRANCH = args.branch || `ticket/${TICKET.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`;
|
|
109
144
|
const PLAN = args.plan || null;
|
|
110
145
|
const CRITERIA = args.criteria || null;
|
|
146
|
+
const TEST_PLAN = args.testPlan || null; // the plan's checked TP lines, one string (Pre-authoring step 4)
|
|
111
147
|
const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
|
|
112
|
-
const
|
|
113
|
-
const
|
|
148
|
+
const ISSUE_INPUT = args.issueInput || args.issueNumber || "(none)"; // this ticket's OWN raw reference (Pre-authoring step 5; a wave passes issueInput) — setup-task's input, never a Code agent's
|
|
149
|
+
const ISSUE_REQUIRED = String(args.issueRequired) === "false" ? "false" : "true"; // Pre-authoring step 0; only an explicit false turns it off — absent or unrecognised fails closed, like the resolver
|
|
150
|
+
const APPLY_CONVENTIONS = String(args.applyConventions) === "false" ? "false" : "true";
|
|
151
|
+
const COMPLIANCE_FRAMEWORKS = /^(?:off|none|[a-z][a-z0-9-]{0,15}(?:,[a-z][a-z0-9-]{0,15}){0,7})$/.test(String(args.complianceFrameworks)) ? String(args.complianceFrameworks) : "off"; // Pre-authoring step 0b; any other shape is no lens
|
|
114
152
|
|
|
115
|
-
// Phase 1: Git setup — declare the operation; the agent owns the process
|
|
116
|
-
await phase("setup", () =>
|
|
153
|
+
// Phase 1: Git setup — declare the operation; the agent owns the process, the branch name included
|
|
154
|
+
const setup = await phase("setup", () =>
|
|
117
155
|
agent(`OPERATION: setup-task
|
|
118
156
|
BASE_BRANCH: ${args.baseBranch || "HEAD"}
|
|
119
157
|
TASK_DESCRIPTION: ${TICKET}
|
|
120
|
-
|
|
121
|
-
|
|
158
|
+
ISSUE_REQUIRED: ${ISSUE_REQUIRED}
|
|
159
|
+
APPLY_CONVENTIONS: ${APPLY_CONVENTIONS}
|
|
160
|
+
${ISSUE_INPUT !== "(none)" ? "ISSUE_INPUT: " + ISSUE_INPUT : ""}
|
|
161
|
+
Return: {"branch": "<the - **Branch name**: value under your ### Branch, or (none)>", "issueId": "<the - **Issue ID**: value under your ### Handoff Values, or (none)>", "prLinkLine": "<the - **PR link line**: value, or (none)>"}`, { agentType: "Git" })
|
|
122
162
|
);
|
|
123
163
|
|
|
164
|
+
// The ticket's own Handoff Values, read from setup-task's Output — never derived from ISSUE_INPUT.
|
|
165
|
+
// An Issue ID is a bare number or a KEY-number, per the provider; any other value — "none", blank, prose — is no capture, so the stop fails closed.
|
|
166
|
+
const ISSUE_ID_SHAPE = /^(?:[1-9][0-9]{0,8}|[A-Z][A-Z0-9_]{0,9}-[1-9][0-9]{0,8})$/;
|
|
167
|
+
const ISSUE_NUMBER = ISSUE_ID_SHAPE.test(String(setup?.issueId ?? "")) ? setup.issueId : "(none)"; // the captured Issue ID; "(none)" ⇒ none was captured
|
|
168
|
+
const ISSUE_PR_LINK = setup?.prLinkLine || "(none)"; // "(none)" ⇒ Code emits the heading with no reference
|
|
169
|
+
// The branch setup-task created, as it reported it: every later phase and the merge use it. The engine never names one.
|
|
170
|
+
const BRANCH = typeof setup?.branch === "string" && setup.branch.trim() !== "" && setup.branch.trim() !== "(none)" ? setup.branch : "(none)";
|
|
171
|
+
|
|
172
|
+
// Ticket-link stop, before implement. The workflow cannot ask, so it never records an exception: it stops.
|
|
173
|
+
if (ISSUE_REQUIRED === "true" && ISSUE_NUMBER === "(none)") {
|
|
174
|
+
return { ticket: TICKET, branch: BRANCH, verdict: "ESCALATED", issueId: "(none)", issuePrLink: ISSUE_PR_LINK, escalations: [{ type: "ticket-link-missing", description: "setup-task captured no Issue ID while issues are required — link or create this ticket's issue, then re-run" }] };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Branch stop, before implement: with no branch there is nothing to build on or merge — a correctness stop, not a shape gate.
|
|
178
|
+
if (BRANCH === "(none)") {
|
|
179
|
+
return { ticket: TICKET, branch: BRANCH, verdict: "ESCALATED", issueId: ISSUE_NUMBER, issuePrLink: ISSUE_PR_LINK, escalations: [{ type: "branch-missing", description: "setup-task reported no branch name — check its Output, then re-run" }] };
|
|
180
|
+
}
|
|
181
|
+
|
|
124
182
|
// Phase 2: Implement
|
|
125
183
|
await phase("implement", () =>
|
|
126
184
|
agent(`Implement the following ticket on branch ${BRANCH}:
|
|
@@ -133,6 +191,8 @@ Relevant architectural decisions (apply devflow:apply-decisions algorithm):
|
|
|
133
191
|
${DECISIONS_CONTEXT}
|
|
134
192
|
|
|
135
193
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
194
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
195
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
136
196
|
|
|
137
197
|
When you build or run tests to verify your work, use your "Long-running commands" discipline (background-Bash + Monitor poll) for anything that may run silent >120s, and prefer package-scoped commands.
|
|
138
198
|
|
|
@@ -155,6 +215,8 @@ Report: PASS or FAIL with details.`, { agentType: "Validate" });
|
|
|
155
215
|
await agent(`Fix the validation failures on branch ${BRANCH}:
|
|
156
216
|
${validation.details}
|
|
157
217
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
218
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
219
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
158
220
|
Commit fixes with conventional-commit message.`, { agentType: "Code" });
|
|
159
221
|
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH}. Report: PASS or FAIL.`, { agentType: "Validate" });
|
|
160
222
|
if (recheck.verdict === "PASS") break;
|
|
@@ -175,8 +237,8 @@ Commit fixes with conventional-commit message.`, { agentType: "Code" });
|
|
|
175
237
|
|
|
176
238
|
// Phase 4: Gate 2 — acceptance gate (once, before review pass)
|
|
177
239
|
const gate2 = await phase("gate2", async () => {
|
|
178
|
-
if (!PLAN && !CRITERIA) {
|
|
179
|
-
return { evaluateVerdict: "SKIPPED", testVerdict: "SKIPPED", skipReasons: ["No plan and no
|
|
240
|
+
if (!PLAN && !CRITERIA && !TEST_PLAN) {
|
|
241
|
+
return { evaluateVerdict: "SKIPPED", testVerdict: "SKIPPED", skipReasons: ["No plan, no criteria and no test plan provided"] };
|
|
180
242
|
}
|
|
181
243
|
|
|
182
244
|
let evalVerdict = "SKIPPED";
|
|
@@ -197,23 +259,28 @@ Report: PASS or FAIL with rationale.`, { agentType: "Evaluate" }),
|
|
|
197
259
|
await agent(`Fix the alignment issues identified by the Evaluate agent panel on branch ${BRANCH}:
|
|
198
260
|
${panel.filter(p => p.verdict === "FAIL").map(p => p.rationale).join("\n")}
|
|
199
261
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
262
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
263
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
200
264
|
Self-verify your fix compiles (background-Bash + Monitor for any build >120s — see your "Long-running commands" discipline). Commit fixes.`, { agentType: "Code" });
|
|
201
265
|
evalVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
|
|
202
266
|
}
|
|
203
267
|
}
|
|
204
268
|
|
|
205
269
|
let testVerdict = "SKIPPED";
|
|
206
|
-
if (CRITERIA) {
|
|
270
|
+
if (CRITERIA || TEST_PLAN) {
|
|
207
271
|
const testResult = await agent(`Run scenario-based acceptance tests on branch ${BRANCH} against these criteria:
|
|
208
|
-
${CRITERIA}
|
|
272
|
+
${CRITERIA || "(none)"}
|
|
273
|
+
TEST_PLAN: ${TEST_PLAN || "(none)"}
|
|
209
274
|
For any test/build command that may run silent >120s, use the background-Bash + Monitor poll procedure (your "Long-running commands" discipline) so you never trip the 180s watchdog.
|
|
210
|
-
Cover: functionality, API contracts, performance. Report: PASS or FAIL per scenario.`, { agentType: "Test" });
|
|
275
|
+
Cover: functionality, API contracts, performance, and cover every TEST_PLAN scenario. Report: PASS or FAIL per scenario.`, { agentType: "Test" });
|
|
211
276
|
testVerdict = testResult.verdict;
|
|
212
277
|
if (testVerdict === "FAIL") {
|
|
213
278
|
// fix-and-continue — no re-test, no inline Gate 1.
|
|
214
279
|
await agent(`Fix the failing acceptance test scenarios on branch ${BRANCH}:
|
|
215
280
|
${testResult.failures}
|
|
216
281
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
282
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
283
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
217
284
|
Self-verify your fix compiles and the scenarios pass (background-Bash + Monitor for any build/test >120s). Commit fixes.`, { agentType: "Code" });
|
|
218
285
|
testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
|
|
219
286
|
}
|
|
@@ -326,6 +393,8 @@ ${JSON.stringify(allFindings.map((f, i) => ({ index: i, description: f.descripti
|
|
|
326
393
|
${chunk.map(f => `- ${f.description} (${f.severity})`).join("\n")}
|
|
327
394
|
|
|
328
395
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
396
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
397
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
329
398
|
Fix all findings in this batch. Self-verify your fix compiles (background-Bash + Monitor for any build >120s — see your "Long-running commands" discipline). Commit with conventional-commit message.
|
|
330
399
|
Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<description of any finding that could not be fixed>"]}`, { agentType: "Code" });
|
|
331
400
|
chunkResults.push({ chunk, result: r });
|
|
@@ -375,6 +444,8 @@ Report: PASS or FAIL with details.`, { agentType: "Validate" });
|
|
|
375
444
|
await agent(`Fix the final validation failures on branch ${BRANCH}:
|
|
376
445
|
${failureDetails}
|
|
377
446
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
447
|
+
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
448
|
+
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
378
449
|
Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code" });
|
|
379
450
|
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (background+Monitor for long commands). Report: PASS or FAIL.`, { agentType: "Validate" });
|
|
380
451
|
if (recheck.verdict === "PASS") break;
|
|
@@ -395,15 +466,17 @@ Self-verify your fix compiles. Commit fixes with conventional-commit message.`,
|
|
|
395
466
|
});
|
|
396
467
|
|
|
397
468
|
// PASS requires survivingFindings.length === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED"
|
|
469
|
+
// and no FAIL-FIXED Gate 2 verdict: fixes applied but never re-run report UNVERIFIED, never PASS
|
|
398
470
|
const coverageGaps = reviewResult.coverageGaps || [];
|
|
399
|
-
const
|
|
471
|
+
const gate2Unverified = [gate2.evaluateVerdict, gate2.testVerdict].includes("FAIL-FIXED");
|
|
472
|
+
const overallVerdict = (reviewResult.survivingFindings?.length || 0) === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED" ? (gate2Unverified ? "UNVERIFIED" : "PASS") : "PARTIAL";
|
|
400
473
|
|
|
401
|
-
// Phase 6: Report
|
|
402
|
-
return phase("report", () =>
|
|
403
|
-
agent(`Synthesize the build run for ticket ${TICKET} on branch ${BRANCH}:
|
|
474
|
+
// Phase 6: Report — the phase returns the engine result (engine_output_schema); its verdict is overallVerdict
|
|
475
|
+
return phase("report", async () => {
|
|
476
|
+
const report = await agent(`Synthesize the build run for ticket ${TICKET} on branch ${BRANCH}:
|
|
404
477
|
- Implementation summary
|
|
405
478
|
- Gate 1 (#1 post-implementation) result
|
|
406
|
-
- Gate 2 result: ${JSON.stringify(gate2)}
|
|
479
|
+
- Gate 2 result: ${JSON.stringify(gate2)} (render FAIL-FIXED as "UNVERIFIED (fixes applied, not re-run)", never PASS)
|
|
407
480
|
- Review: single pass (full branch diff)
|
|
408
481
|
- Final Gate 1 (#2 post-fix): ${JSON.stringify(gate1Final)}
|
|
409
482
|
- Findings disposition:
|
|
@@ -411,29 +484,39 @@ return phase("report", () =>
|
|
|
411
484
|
- SURVIVING: ${reviewResult.survivingFindings?.length || 0} findings not addressed (fix Code agent failed or deferred): ${JSON.stringify(reviewResult.survivingFindings)}
|
|
412
485
|
- Overall verdict: ${overallVerdict}
|
|
413
486
|
|
|
414
|
-
Write a concise report. Present only surviving findings as outstanding — never present FIXED findings as outstanding. The branch is ready for user review — do NOT merge to main.`, { agentType: "Synthesize" })
|
|
415
|
-
|
|
487
|
+
Write a concise report. Present only surviving findings as outstanding — never present FIXED findings as outstanding. The branch is ready for user review — do NOT merge to main.`, { agentType: "Synthesize" });
|
|
488
|
+
return {
|
|
489
|
+
ticket: TICKET, branch: BRANCH, verdict: overallVerdict, issueId: ISSUE_NUMBER, issuePrLink: ISSUE_PR_LINK,
|
|
490
|
+
survivingFindings: reviewResult.survivingFindings || [], fixedFindings: reviewResult.fixedFindings || [],
|
|
491
|
+
reviewCoverage: { failedFocuses: coverageGaps, complete: coverageGaps.length === 0 },
|
|
492
|
+
escalations: [
|
|
493
|
+
...(gate1Final.verdict === "ESCALATED" ? [{ type: "validation-exhausted", description: gate1Final.reason || "final Gate 1 escalated" }] : []),
|
|
494
|
+
...coverageGaps.map(focus => ({ type: "review-coverage-incomplete", description: `review coverage incomplete: ${focus}` })),
|
|
495
|
+
],
|
|
496
|
+
gate2, report,
|
|
497
|
+
};
|
|
498
|
+
});
|
|
416
499
|
```
|
|
417
500
|
|
|
418
|
-
{concurrency_doctrine()}
|
|
501
|
+
{{concurrency_doctrine()}}
|
|
419
502
|
|
|
420
|
-
{build_execution_doctrine()}
|
|
503
|
+
{{build_execution_doctrine()}}
|
|
421
504
|
|
|
422
|
-
{implement_bundle()}
|
|
505
|
+
{{implement_bundle()}}
|
|
423
506
|
|
|
424
|
-
{gate1_postcode()}
|
|
507
|
+
{{gate1_postcode()}}
|
|
425
508
|
|
|
426
|
-
{gate2_acceptance()}
|
|
509
|
+
{{gate2_acceptance()}}
|
|
427
510
|
|
|
428
|
-
{evaluator_panel()}
|
|
511
|
+
{{evaluator_panel()}}
|
|
429
512
|
|
|
430
|
-
{acceptance_criteria_contract()}
|
|
513
|
+
{{acceptance_criteria_contract()}}
|
|
431
514
|
|
|
432
|
-
{review_pass()}
|
|
515
|
+
{{review_pass()}}
|
|
433
516
|
|
|
434
|
-
{engine_invariants()}
|
|
517
|
+
{{engine_invariants()}}
|
|
435
518
|
|
|
436
|
-
{engine_output_schema()}
|
|
519
|
+
{{engine_output_schema()}}
|
|
437
520
|
|
|
438
521
|
---
|
|
439
522
|
|
|
@@ -441,22 +524,28 @@ Write a concise report. Present only surviving findings as outstanding — never
|
|
|
441
524
|
|
|
442
525
|
When WAVE mode is detected, author a workflow that wraps the single-ticket engine with the wave loop:
|
|
443
526
|
|
|
444
|
-
{wave_loop()}
|
|
527
|
+
{{wave_loop()}}
|
|
445
528
|
|
|
446
|
-
{branch_merge_model()}
|
|
529
|
+
{{branch_merge_model()}}
|
|
447
530
|
|
|
448
|
-
{merge_doctrine()}
|
|
531
|
+
{{merge_doctrine()}}
|
|
449
532
|
|
|
450
|
-
{escalation_model()}
|
|
533
|
+
{{escalation_model()}}
|
|
451
534
|
|
|
452
535
|
**Wave workflow structure (author after the SINGLE engine blocks above):**
|
|
453
536
|
|
|
454
|
-
The wave workflow uses the same phases as SINGLE but wraps them in a wave loop. The integration branch is `wave/<initiative>` — the initiative slug, referenced below as
|
|
537
|
+
The wave workflow uses the same phases as SINGLE but wraps them in a wave loop. The integration branch is `wave/<initiative>` — the initiative slug, referenced below as `{slug}` — (or the user's current branch). Each ticket works on the branch setup-task created. The Git agent manages worktrees for parallel-eligible tickets. After every merge: Validate agent (build + test). Escalations accumulate in a list; the final report lists all of them.
|
|
455
538
|
|
|
456
539
|
Wave skeleton — compact reference (see `wave_loop()` doctrine for full semantics):
|
|
457
540
|
|
|
458
541
|
```js
|
|
459
542
|
// Wave round loop: Design agent reader → per-ticket try/catch → cascade quarantine via next reader
|
|
543
|
+
// runSingleTicketEngine(args) is the SINGLE skeleton above as a function of its OWN args: the wave's
|
|
544
|
+
// args.issueNumber (the tracking issue, Pre-authoring step 5) is never in its scope.
|
|
545
|
+
const WAVE_TICKETS = [...remainingTickets]; // the pre-fetch's refs, in input order — the wave block's row order
|
|
546
|
+
const results = {}; // ticketId → its row of the workflow's return; a ticket with no entry never ran
|
|
547
|
+
// A row from an engine result: the fields step 3 after the workflow renders
|
|
548
|
+
const rowOf = (ticketId, r, merged) => ({ ticket: ticketId, ran: true, verdict: r?.verdict || r?.overallVerdict || null, merged, issuePrLink: r?.issuePrLink || "(none)", evaluateVerdict: r?.gate2?.evaluateVerdict, testVerdict: r?.gate2?.testVerdict, surviving: r?.survivingFindings?.length, coverageComplete: r?.reviewCoverage?.complete });
|
|
460
549
|
const MAX_ROUNDS = Math.max(10, remainingTickets.length * 2 + 5); // heuristic; always finite
|
|
461
550
|
const waveState = { quarantined: [], round: 0 };
|
|
462
551
|
let reAskedThisDeadlock = false; // re-ask guard: re-ask once on empty ready-set, then escalate
|
|
@@ -477,7 +566,7 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
|
|
|
477
566
|
{ agentType: "Design" }
|
|
478
567
|
);
|
|
479
568
|
|
|
480
|
-
const ready = waveRead?.ready || [];
|
|
569
|
+
const ready = (waveRead?.ready || []).filter(t => remainingTickets.includes(t)); // only the pre-fetch's own refs: a ready ID the reader invents never reaches an engine
|
|
481
570
|
if (ready.length === 0) {
|
|
482
571
|
if (reAskedThisDeadlock) break; // second empty read → declare deadlock with named blockers, break
|
|
483
572
|
reAskedThisDeadlock = true; // re-ask once with vacuous-truth rule quoted verbatim
|
|
@@ -487,20 +576,31 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
|
|
|
487
576
|
|
|
488
577
|
for (const ticketId of ready) {
|
|
489
578
|
try {
|
|
490
|
-
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
579
|
+
// ticketId is the ISSUE_REF the pre-fetch heading printed — this ticket's OWN reference: the engine's `ticket` (its TICKET)
|
|
580
|
+
// and its setup-task ISSUE_INPUT; the engine returns the branch that setup-task created, merged below; plans[ticketId] is its own plan, criteria and checked testPlan (Pre-authoring step 4)
|
|
581
|
+
const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), decisionsContext: DECISIONS_CONTEXT, issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
|
|
582
|
+
// Check both verdict (engine_output_schema) and overallVerdict (SINGLE skeleton alias).
|
|
583
|
+
// PASS and UNVERIFIED merge; PARTIAL, FAIL, ESCALATED (the ticket-link and branch stops included) or no verdict quarantine.
|
|
584
|
+
if (["PASS", "UNVERIFIED"].includes(engineResult.verdict || engineResult.overallVerdict)) {
|
|
585
|
+
const merge = await agent(`Merge ${engineResult.branch} to ${INTEGRATION_BRANCH}. Include ticket ID ${ticketId} in the merge commit message. Run Validate agent (build + test) after merge.
|
|
586
|
+
Return: {"merged": true} — or {"merged": false, "reason": "<why>"} when the merge or the post-merge build failed and the merge was not kept.`, { agentType: "Git" });
|
|
587
|
+
results[ticketId] = rowOf(ticketId, engineResult, merge?.merged === true);
|
|
588
|
+
if (merge?.merged !== true) waveState.quarantined.push({ ticket: ticketId, reason: merge?.reason || "merge or post-merge build failed" });
|
|
494
589
|
} else {
|
|
590
|
+
results[ticketId] = rowOf(ticketId, engineResult, false);
|
|
495
591
|
waveState.quarantined.push({ ticket: ticketId, reason: engineResult.escalations?.[0]?.description || "engine fail/escalated" });
|
|
496
592
|
}
|
|
497
593
|
} catch (err) {
|
|
498
594
|
// One ticket's crash/stall never kills the wave — quarantine it; cascade propagates to dependents via the next reader round
|
|
595
|
+
results[ticketId] = rowOf(ticketId, null, false);
|
|
499
596
|
waveState.quarantined.push({ ticket: ticketId, reason: `engine crash: ${String(err)}` });
|
|
500
597
|
}
|
|
501
598
|
remainingTickets = remainingTickets.filter(t => t !== ticketId);
|
|
502
599
|
}
|
|
503
600
|
}
|
|
601
|
+
|
|
602
|
+
// After the wave report is written: one row per wave ticket, in input order. No entry ⇒ never ran (cascade, deadlock, MAX_ROUNDS).
|
|
603
|
+
return { tickets: WAVE_TICKETS.map(t => results[t] || { ticket: t, ran: false, verdict: null, merged: false, issuePrLink: "(none)" }), quarantined: waveState.quarantined };
|
|
504
604
|
```
|
|
505
605
|
|
|
506
606
|
---
|
|
@@ -509,24 +609,132 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
|
|
|
509
609
|
|
|
510
610
|
A workflow cannot pause mid-run. After the build/wave workflow returns, you (the main model) surface anything that needs a human decision — all at once:
|
|
511
611
|
|
|
512
|
-
1. Read the wave report (
|
|
513
|
-
2. **Post wave-report as tracking-issue comment** (WAVE mode only — skip this step entirely in SINGLE mode; SINGLE runs produce no wave report): In WAVE mode, if a tracking-issue number was resolved in Pre-authoring step 5 (`ISSUE_NUMBER` is not `(none)`) AND
|
|
612
|
+
1. Read the wave report (`{integration worktree root}/.devflow/docs/waves/{slug}/{ts}/wave-report.md`) for its **escalations** (quarantined/blocked tickets and why), and read any `DECISIONS-NEEDED.md` left by a prior `/devflow:dynamic-plan` run for this initiative.
|
|
613
|
+
2. **Post wave-report as tracking-issue comment** (WAVE mode only — skip this step entirely in SINGLE mode; SINGLE runs produce no wave report): In WAVE mode, if a tracking-issue number was resolved in Pre-authoring step 5 (`ISSUE_NUMBER` is not `(none)`) AND `{integration worktree root}/.devflow/docs/waves/{slug}/{ts}/wave-report.md` exists, define `WAVE_ID` as the timestamped wave directory slug (the `{ts}` component, e.g. `2026-08-20_1730`), then spawn:
|
|
514
614
|
```
|
|
515
615
|
Agent(subagent_type="Git"):
|
|
516
616
|
"OPERATION: post-wave-report
|
|
517
|
-
TRACKING_ISSUE:
|
|
518
|
-
WAVE_REPORT_PATH: .devflow/docs/waves
|
|
519
|
-
WAVE_ID:
|
|
520
|
-
WORKTREE_PATH:
|
|
617
|
+
TRACKING_ISSUE: {ISSUE_NUMBER}
|
|
618
|
+
WAVE_REPORT_PATH: .devflow/docs/waves/{slug}/{ts}/wave-report.md
|
|
619
|
+
WAVE_ID: {WAVE_ID}
|
|
620
|
+
WORKTREE_PATH: {integration worktree root}"
|
|
521
621
|
```
|
|
522
|
-
The Git agent deduplicates via marker
|
|
622
|
+
The Git agent deduplicates via its own marker — it skips if a report for this `WAVE_ID` is already posted. The marker's format belongs to the operation; this caller passes `WAVE_ID` and never restates the literal. On API failure it degrades gracefully (`TRACEABILITY: DEGRADED ({reason})`) and continues — never blocks the post-wave step. This comment is the evidence surface for the PR-less integration-branch path; no other PR machinery is invented.
|
|
523
623
|
|
|
524
624
|
In WAVE mode, if no tracking-issue number was resolved in Pre-authoring step 5: state `TRACEABILITY: DEGRADED (no tracking issue for this run)` in the run summary and skip — never skip silently.
|
|
525
|
-
3.
|
|
526
|
-
|
|
625
|
+
3. **Compose the wave PR inputs** (WAVE mode only — skip this step entirely in SINGLE mode). The workflow returned `tickets`: one entry per wave ticket, in its input order, each `{ticket, ran, verdict, merged, issuePrLink, evaluateVerdict, testVerdict, surviving, coverageComplete}`. Every value in it is agent-reported and treated as untrusted: nothing below is repaired, and no reference is ever composed from a number.
|
|
626
|
+
- **Branch check.** Run `git -C "{integration worktree root}" branch --show-current`. Only a name matching `^wave/[a-z0-9][a-z0-9-]{0,59}$` opens a wave PR, and its part after `wave/` is the `{slug}` below. Any other name: record `TRACEABILITY: DEGRADED (not a wave branch)` and go to step 4 with no wave PR.
|
|
627
|
+
- **Nothing merged** (no entry has `merged: true`): record `Wave PR: skipped (nothing merged)` and go to step 4.
|
|
628
|
+
- Otherwise compose (a) and then (b).
|
|
629
|
+
|
|
630
|
+
**(a) The wave block.** Write it with the Write tool, byte for byte, to a fresh `mktemp` file — never through an interpolated shell string — in exactly this shape: the two headings with one blank line between them, the tracking line and then the related lines in row order under the first, and the header and separator verbatim directly under the second:
|
|
631
|
+
|
|
632
|
+
```markdown
|
|
633
|
+
## Related Issues
|
|
634
|
+
Refs {tracking ref}
|
|
635
|
+
{one related line per row that has one}
|
|
636
|
+
|
|
637
|
+
## Wave Evidence
|
|
638
|
+
| T | Ticket | Verdict | Evaluate | Test | Surviving | Coverage |
|
|
639
|
+
|---|---|---|---|---|---|---|
|
|
640
|
+
| T{k} | {ticket} | {verdict} | {evaluate} | {test} | {surviving} | {coverage} |
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
One row per `tickets` entry, `T1` … `Tn` in order. Each entry maps to exactly one row, by these rules:
|
|
644
|
+
- **Verdict** — one of `PASS | UNVERIFIED | QUARANTINED | BLOCKED`. `ran: false` (cascade, deadlock, MAX_ROUNDS) ⇒ `BLOCKED`. `merged: true` with `verdict` `PASS` ⇒ `PASS`. `merged: true` with `verdict` `UNVERIFIED` ⇒ `UNVERIFIED`: the row stays flagged, and its TP lines join the wave test plan. Anything else that ran — PARTIAL, FAIL, ESCALATED, no verdict, an engine crash, a failed merge or a red post-merge build — ⇒ `QUARANTINED`.
|
|
645
|
+
- **Evaluate, Test** — the entry's `evaluateVerdict` and `testVerdict` when it is `PASS`, `FAIL`, `FAIL-FIXED` or `SKIPPED`, else `—`. **Surviving** — its `surviving` count when it is 0–999, else `—`. **Coverage** — `complete` or `incomplete` from `coverageComplete`, else `—`. A `BLOCKED` row is `—` in all four.
|
|
646
|
+
- **Ticket and related line** — the entry's captured `issuePrLink` is the only source of a closing reference. When it is not `(none)`: a `PASS` or `UNVERIFIED` row's related line is `issuePrLink` verbatim; a `QUARANTINED` row's is `issuePrLink` with a leading `Closes ` replaced by `Refs `; and the Ticket cell is the reference that line names (its text after `Closes ` or `Refs `). When it is `(none)`: no related line, and the Ticket cell is `(none)` on a `PASS` or `UNVERIFIED` row; on any other row it is the entry's `ticket` when that is a `#N` or `KEY-N` reference, else `(none)`.
|
|
647
|
+
|
|
648
|
+
**Tracking line** — only when Pre-authoring step 5 resolved a tracking issue whose token is, as a whole, a `#N` or `KEY-N` reference: `Refs ` and that token, verbatim, as the first line under `## Related Issues`. Never `Closes` — the tracking issue outlives the wave. Any other token (a bare number, a URL), or none ⇒ no tracking line: nothing is composed from a number.
|
|
649
|
+
|
|
650
|
+
Then check it:
|
|
651
|
+
|
|
652
|
+
```bash
|
|
653
|
+
node "$HOME/.devflow/scripts/verify-evidence.cjs" check wave <that file>; echo "exit=$?"
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Only `exit=0` admits the file's text, verbatim, as `PR_WAVE_BLOCK`. Any other result: no wave PR — record `Wave PR: not opened (wave block refused: <the code on stderr>)` and go to step 4.
|
|
657
|
+
|
|
658
|
+
**Required link** — only when `EVIDENCE_POLICY` is `required`: a `PASS` or `UNVERIFIED` row whose Ticket is `(none)` — a merged ticket whose setup-task captured an Issue ID but no link line, so the wave PR would close nothing for it — ⇒ `Wave PR: BLOCKED (no ticket link for T<k>, …)`, with no wave PR question and the remedy "link or create those tickets' issues, then re-run"; there is no exception. Under `standard` such a row stays as the Ticket rule above renders it.
|
|
659
|
+
|
|
660
|
+
**(b) The wave test plan.** Take the checked TP lines each merged row's ticket was given in Pre-authoring step 4, in row order; renumber them `TP-1`, `TP-2`, … and prefix each scenario with its row's `T<k>: `. A line is never shortened or reworded: one whose prefixed scenario would pass 200 characters leaves its ticket with no usable test plan. Write `## Test Plan` and those lines, and nothing else, to `"{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"` with the Write tool, then run (the path double-quoted; one rewrite from the same lines after a refusal, never a second):
|
|
661
|
+
|
|
662
|
+
```bash
|
|
663
|
+
node "$HOME/.devflow/scripts/verify-evidence.cjs" check tp "{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"; echo "exit=$?"
|
|
664
|
+
node "$HOME/.devflow/scripts/verify-evidence.cjs" render --plan "{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"; echo "exit=$?"
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Only when both exit 0 is `PR_TEST_PLAN_BLOCK` the render's stdout, byte for byte without its `exit=` line; in every other case it is `(none)`.
|
|
668
|
+
|
|
669
|
+
**Required plan** — only when `EVIDENCE_POLICY` is `required`: a merged ticket with no usable test plan ⇒ `Wave PR: BLOCKED (no test plan for T<k>, …)`, more than 200 lines in all ⇒ `Wave PR: BLOCKED (test plan over 200 lines)`, and a plan that did not check and render ⇒ `Wave PR: BLOCKED (test plan malformed)` — each with no wave PR question and the remedy "run `/devflow:dynamic-plan` for those tickets and re-run, or ship them through `/implement`"; no exception is offered here. Otherwise the block is optional: a ticket with no usable test plan is left out of it and named in the summary, and `(none)` is passed when no line remains.
|
|
670
|
+
4. Surface ALL of them — escalations AND open decisions — to the user in ONE batched `AskUserQuestion` (never one-at-a-time). `_wave.mds`'s escalation model already quarantines-and-continues; this batches the surfacing so the user answers everything in a single pass.
|
|
671
|
+
- **The wave PR question.** Only when step 3 composed `PR_WAVE_BLOCK`, the batch gains exactly one question: "Open the wave PR from wave/{slug}? It links {n} merged tickets ({u} UNVERIFIED) and references {q} quarantined." It has exactly two options: open it, or don't. The counts come from the checked block's rows: `{n}` PASS and UNVERIFIED, `{u}` UNVERIFIED, `{q}` QUARANTINED.
|
|
672
|
+
- A `ticket-link-missing` escalation carries its remedy: link or create that ticket's issue, then re-run. There is no per-ticket exception.
|
|
673
|
+
- **Headless** — `AskUserQuestion` is unavailable, or no answer comes — is a decline: nothing is created and nothing is pushed.
|
|
674
|
+
5. If `~/.devflow/preference-profile.md` was absent, note in your summary: "no preference profile found — N decisions surfaced that a profile might have auto-resolved; consider `/devflow:dynamic-profile`."
|
|
675
|
+
6. **Wave PR** — only after an explicit "open" in step 4. Spawn:
|
|
676
|
+
```
|
|
677
|
+
Agent(subagent_type="Git"):
|
|
678
|
+
"OPERATION: ensure-pr-ready
|
|
679
|
+
WORKTREE_PATH: {integration worktree root}
|
|
680
|
+
PR_DESCRIPTION_GUIDANCE: {counts only — the wave slug and the merged, quarantined and blocked counts; never an issue title or body}
|
|
681
|
+
APPLY_CONVENTIONS: {APPLY_CONVENTIONS}
|
|
682
|
+
PR_WAVE_BLOCK: {PR_WAVE_BLOCK verbatim}
|
|
683
|
+
PR_TEST_PLAN_BLOCK: {PR_TEST_PLAN_BLOCK verbatim, or (none)}"
|
|
684
|
+
```
|
|
685
|
+
The Git agent pastes each block only behind its own check. Report its `**PR**` line and any `TRACEABILITY: DEGRADED ({reason})` lines. The wave PR is opened here and nowhere else; it is never merged, and main is never touched — the user merges.
|
|
527
686
|
|
|
528
687
|
Do NOT ask questions mid-workflow — that is impossible (F4). The workflow only WRITES the report; you read it and ask.
|
|
529
688
|
|
|
689
|
+
7. **Wave PR evidence** — only when step 6 reported the wave PR, after it; it never blocks, and every outcome below goes into the run summary. Take `{n}` from step 6's `- **PR**: #{n}` line when `{n}` matches `^[1-9][0-9]{0,9}$` as a whole; no such line ⇒ record `TRACEABILITY: DEGRADED (wave PR number not captured)` and skip this step. `PR_TEST_PLAN_BLOCK` `(none)` ⇒ record `Wave evidence: skipped (no wave test plan)` and skip it.
|
|
690
|
+
|
|
691
|
+
**(a) Test the wave.** Spawn one Test agent on the integration worktree with the wave test plan — the TP lines of the `## Test Plan` section step 3(b) wrote to `{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md`:
|
|
692
|
+
|
|
693
|
+
```
|
|
694
|
+
Agent(subagent_type="Test"):
|
|
695
|
+
"ORIGINAL_REQUEST: the merged tickets of wave/{slug}, as the wave test plan names them
|
|
696
|
+
FILES_CHANGED: {the files wave/{slug} changes against the - **Base**: branch step 6 reported}
|
|
697
|
+
TEST_PLAN: {the TP lines of the wave evidence file's ## Test Plan section}
|
|
698
|
+
WORKTREE_PATH: {integration worktree root}
|
|
699
|
+
Cover every TEST_PLAN line on the integration branch as it stands. Report PASS or FAIL with evidence."
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Nothing is fixed here, PASS or FAIL: the wave is done and its PR is open. Report the Test agent's Status.
|
|
703
|
+
|
|
704
|
+
**(b) Claims.** Append its TP claims, PASS or FAIL alike, to the `## Claims` section of `"{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"` — the file's last section, created when absent. Append only: never edit or remove a claim. Each line is `/implement`'s TP claim, keyed to the 40-hex `HEAD:` the Test agent's report shows:
|
|
705
|
+
|
|
706
|
+
```
|
|
707
|
+
- TP-<n> <PASS|FAIL|SKIP> sha:<head> by:test exit:<0-255>
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
One line per `### Test Plan Evidence` row whose TP is in the wave test plan, with the row's outcome; the line ends at `by:test` when the row's Exit is not a number from 0 to 255. A report whose `HEAD:` is not a single 40-hex SHA — a reported before/after change included — gets no claim: record `Wave evidence: no claims (HEAD not one SHA)`.
|
|
711
|
+
|
|
712
|
+
**(c) Push** the integration branch once — never force, no retry — so every claim's SHA is in the PR:
|
|
713
|
+
|
|
714
|
+
```bash
|
|
715
|
+
git -C "{integration worktree root}" push origin HEAD; echo "exit=$?"
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Any result but `exit=0`, a rejected non-fast-forward push included ⇒ record `TRACEABILITY: DEGRADED (evidence push failed)` and refresh anyway.
|
|
719
|
+
|
|
720
|
+
**(d) Refresh.** Resolve the publication value for the integration worktree:
|
|
721
|
+
|
|
722
|
+
{{pub.publication_gate()}}
|
|
723
|
+
|
|
724
|
+
Then spawn:
|
|
725
|
+
|
|
726
|
+
```
|
|
727
|
+
Agent(subagent_type="Git"):
|
|
728
|
+
"OPERATION: update-pr-evidence
|
|
729
|
+
PR_NUMBER: {n}
|
|
730
|
+
EVIDENCE_FILE: .devflow/docs/evidence-wave-{slug}.md
|
|
731
|
+
REVIEW_PUBLICATION: {REVIEW_PUBLICATION resolved above, or auto}
|
|
732
|
+
WORKTREE_PATH: {integration worktree root}
|
|
733
|
+
Update the wave PR's test-plan block and post its evidence comment."
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
`update-pr-evidence` decides what each publication value means for the evidence comment. Report its `## PR Evidence` block — its `EVIDENCE` line and its `**Body**:` / `**Comment**:` line — or its `TRACEABILITY: DEGRADED ({reason})` line; a spawn that returns neither ⇒ `TRACEABILITY: DEGRADED (evidence refresh failed)`. Whatever it returns, the run ends here.
|
|
737
|
+
|
|
530
738
|
---
|
|
531
739
|
|
|
532
740
|
### Maintenance note
|