devflow-kit 3.2.0 → 3.4.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 +34 -0
- package/README.md +1 -1
- package/dist/agents/code.md +330 -0
- package/{src/assets → dist}/agents/design.md +11 -1
- package/{src/assets → dist}/agents/diagnose.md +17 -13
- package/dist/agents/git.md +29 -56
- package/{src/assets → dist}/agents/knowledge.md +8 -5
- package/{src/assets → dist}/agents/research.md +21 -2
- package/{src/assets → dist}/agents/review.md +18 -7
- package/{src/assets → dist}/agents/scrutinize.md +35 -11
- package/dist/agents/skim.md +148 -0
- package/{src/assets → dist}/agents/triage.md +8 -2
- package/dist/cli/commands/init.js +62 -0
- package/dist/cli/commands/learning.js +63 -8
- package/dist/cli/commands/uninstall.js +42 -1
- package/dist/commands/bug-analysis.md +30 -8
- package/dist/commands/code-review.md +142 -61
- package/dist/commands/debug.md +15 -13
- package/dist/commands/dynamic-build.md +176 -130
- package/dist/commands/dynamic-plan.md +31 -19
- package/dist/commands/dynamic-profile.md +27 -13
- package/dist/commands/dynamic-tickets.md +28 -14
- package/dist/commands/explore.md +16 -14
- package/dist/commands/implement.md +163 -158
- package/dist/commands/plan.md +37 -24
- package/dist/commands/release.md +69 -4
- package/dist/commands/research.md +33 -11
- package/dist/commands/resolve.md +61 -47
- package/dist/commands/self-review.md +50 -32
- package/dist/core/agent-models.js +53 -7
- package/dist/core/assets.js +55 -10
- package/dist/core/claude-md-audit.js +190 -0
- package/dist/core/feature-switch.js +20 -1
- package/dist/core/flags.js +28 -0
- package/dist/core/fs-atomic.js +8 -3
- package/dist/core/learning-tuning-config.js +8 -0
- package/dist/core/learning-variants.js +213 -0
- package/dist/core/manifest.js +62 -0
- package/dist/core/mds-variants.js +38 -1
- package/dist/core/plugins.js +78 -11
- package/{src/assets → dist/learning-off}/agents/code.md +77 -28
- package/dist/learning-off/agents/design.md +119 -0
- package/dist/learning-off/agents/diagnose.md +210 -0
- package/dist/learning-off/agents/knowledge.md +90 -0
- package/dist/learning-off/agents/research.md +149 -0
- package/dist/learning-off/agents/review.md +228 -0
- package/dist/learning-off/agents/scrutinize.md +117 -0
- package/{src/assets → dist/learning-off}/agents/skim.md +5 -10
- package/dist/learning-off/agents/triage.md +163 -0
- package/dist/learning-off/commands/bug-analysis.md +420 -0
- package/dist/learning-off/commands/code-review.md +525 -0
- package/dist/learning-off/commands/debug.md +294 -0
- package/dist/learning-off/commands/dynamic-build.md +1255 -0
- package/dist/learning-off/commands/dynamic-plan.md +424 -0
- package/dist/learning-off/commands/dynamic-profile.md +214 -0
- package/dist/learning-off/commands/dynamic-tickets.md +632 -0
- package/dist/learning-off/commands/explore.md +210 -0
- package/dist/learning-off/commands/implement.md +808 -0
- package/dist/learning-off/commands/plan.md +664 -0
- package/dist/learning-off/commands/release.md +310 -0
- package/dist/learning-off/commands/research.md +222 -0
- package/dist/learning-off/commands/resolve.md +837 -0
- package/dist/learning-off/commands/self-review.md +266 -0
- package/dist/skills/git/references/tracker/_contract.md +33 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/github/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +12 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +1 -1
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +14 -2
- package/dist/skills/git/references/tracker/linear/associate-release.md +1 -1
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +14 -2
- package/dist/targets/claude-code/installer.js +72 -36
- package/dist/targets/claude-code/language-stamp.js +185 -0
- package/dist/targets/claude-code/learning-install.js +489 -0
- package/package.json +1 -1
- package/src/assets/agents/code.mds +339 -0
- package/src/assets/agents/design.mds +149 -0
- package/src/assets/agents/diagnose.mds +225 -0
- package/src/assets/agents/evaluate.md +12 -25
- package/src/assets/agents/git.mds +29 -56
- package/src/assets/agents/knowledge.mds +125 -0
- package/src/assets/agents/learning.md +4 -6
- package/src/assets/agents/research.mds +176 -0
- package/src/assets/agents/review.mds +286 -0
- package/src/assets/agents/scrutinize.mds +132 -0
- package/src/assets/agents/simplify.md +20 -0
- package/src/assets/agents/skim.mds +161 -0
- package/src/assets/agents/synthesize.md +12 -0
- package/src/assets/agents/test.md +2 -2
- package/src/assets/agents/triage.mds +194 -0
- package/src/assets/agents/validate.md +15 -8
- package/src/assets/commands/_partials/_compliance.mds +5 -4
- package/src/assets/commands/_partials/_decisions.mds +31 -0
- package/src/assets/commands/_partials/_engine.mds +29 -24
- package/src/assets/commands/_partials/_knowledge.mds +26 -13
- package/src/assets/commands/_partials/_plan_contract.mds +1 -1
- package/src/assets/commands/_partials/_preamble.mds +33 -9
- package/src/assets/commands/_partials/_publication.mds +5 -4
- package/src/assets/commands/_partials/_settings.mds +13 -5
- package/src/assets/commands/_partials/_wave.mds +16 -6
- package/src/assets/commands/bug-analysis.mds +24 -2
- package/src/assets/commands/code-review.mds +148 -45
- package/src/assets/commands/debug.mds +17 -1
- package/src/assets/commands/dynamic-build.mds +144 -65
- package/src/assets/commands/dynamic-plan.mds +36 -6
- package/src/assets/commands/dynamic-profile.mds +9 -1
- package/src/assets/commands/dynamic-tickets.mds +16 -2
- package/src/assets/commands/explore.mds +27 -1
- package/src/assets/commands/implement.mds +171 -138
- package/src/assets/commands/plan.mds +47 -8
- package/src/assets/commands/{release.md → release.mds} +27 -24
- package/src/assets/commands/research.mds +28 -4
- package/src/assets/commands/resolve.mds +69 -17
- package/src/assets/commands/self-review.mds +43 -13
- package/src/assets/mds/tracker/_common.mds +1 -1
- package/src/assets/mds/tracker/_contract.mds +72 -0
- package/src/assets/mds/tracker/_github.mds +13 -2
- package/src/assets/mds/tracker/_jira.mds +17 -5
- package/src/assets/mds/tracker/_linear.mds +17 -5
- package/src/assets/mds/tracker/_mcp.mds +2 -2
- package/src/assets/mds/tracker/_steps.mds +97 -0
- package/src/assets/rules/context-economy.md +10 -0
- package/src/assets/rules/go.md +1 -0
- package/src/assets/rules/java.md +1 -0
- package/src/assets/rules/python.md +1 -0
- package/src/assets/rules/rust.md +1 -0
- package/src/assets/rules/typescript.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +636 -0
- package/src/assets/scripts/claude-md-audit.cjs +611 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +2 -3
- package/src/assets/scripts/hooks/background-memory-update +290 -9
- package/src/assets/scripts/hooks/json-helper.cjs +13 -5
- package/src/assets/scripts/hooks/json-parse +34 -10
- package/src/assets/scripts/hooks/session-start-context +315 -7
- package/src/assets/scripts/release-trace.cjs +27 -10
- package/src/assets/skills/accessibility/SKILL.md +1 -1
- package/src/assets/skills/apply-decisions/SKILL.md +12 -82
- package/src/assets/skills/apply-feature-knowledge/SKILL.md +9 -43
- package/src/assets/skills/architecture/SKILL.md +1 -1
- package/src/assets/skills/boundary-validation/SKILL.md +1 -1
- package/src/assets/skills/complexity/SKILL.md +1 -1
- package/src/assets/skills/compliance/SKILL.md +1 -1
- package/src/assets/skills/consistency/SKILL.md +1 -1
- package/src/assets/skills/database/SKILL.md +1 -1
- package/src/assets/skills/dependencies/SKILL.md +1 -1
- package/src/assets/skills/dependency-research/SKILL.md +3 -6
- package/src/assets/skills/design-review/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +1 -1
- package/src/assets/skills/documentation/SKILL.md +1 -1
- package/src/assets/skills/feature-knowledge/SKILL.md +43 -12
- package/src/assets/skills/gap-analysis/SKILL.md +1 -1
- package/src/assets/skills/git/SKILL.md +1 -1
- package/src/assets/skills/go/SKILL.md +1 -1
- package/src/assets/skills/java/SKILL.md +1 -1
- package/src/assets/skills/patterns/SKILL.md +1 -1
- package/src/assets/skills/performance/SKILL.md +1 -1
- package/src/assets/skills/python/SKILL.md +1 -1
- package/src/assets/skills/qa/SKILL.md +1 -3
- package/src/assets/skills/quality-gates/SKILL.md +10 -13
- package/src/assets/skills/quality-gates/references/report-template.md +20 -20
- package/src/assets/skills/react/SKILL.md +1 -1
- package/src/assets/skills/regression/SKILL.md +1 -1
- package/src/assets/skills/reliability/SKILL.md +1 -1
- package/src/assets/skills/research-codebase/SKILL.md +1 -1
- package/src/assets/skills/research-competitor/SKILL.md +1 -1
- package/src/assets/skills/research-external/SKILL.md +1 -1
- package/src/assets/skills/research-technology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/rust/SKILL.md +1 -1
- package/src/assets/skills/security/SKILL.md +1 -1
- package/src/assets/skills/software-design/SKILL.md +1 -1
- package/src/assets/skills/test-driven-development/SKILL.md +9 -29
- package/src/assets/skills/testing/SKILL.md +1 -1
- package/src/assets/skills/typescript/SKILL.md +1 -1
- package/src/assets/skills/ui-design/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/SKILL.md +3 -55
- package/src/assets/skills/worktree-support/references/discovery.md +48 -0
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
|
@@ -3,7 +3,7 @@ description: Dependency-aware build engine — implement, review, and verify a s
|
|
|
3
3
|
argument-hint: "[ticket | issue-url | plan-doc]"
|
|
4
4
|
output-dir: dist/commands
|
|
5
5
|
---
|
|
6
|
-
@import { authoring_preamble } from "./_partials/_preamble.mds"
|
|
6
|
+
@import { authoring_preamble, authoring_decisions } from "./_partials/_preamble.mds"
|
|
7
7
|
@import { evidence_policy } from "./_partials/_evidence_policy.mds"
|
|
8
8
|
@import { docs_root } from "./_partials/_docs_root.mds"
|
|
9
9
|
@import { agent_roster, agent_caveats } from "./_partials/_roster.mds"
|
|
@@ -13,8 +13,14 @@ output-dir: dist/commands
|
|
|
13
13
|
@import { issue_ref_grammar, issue_capture_contract } from "./_partials/_tracker.mds"
|
|
14
14
|
@import "./_partials/_publication.mds" as pub
|
|
15
15
|
@import "./_partials/_compliance.mds" as compliance
|
|
16
|
+
@import "./_partials/_settings.mds" as settings
|
|
16
17
|
{{authoring_preamble()}}
|
|
17
18
|
|
|
19
|
+
### Settings line
|
|
20
|
+
|
|
21
|
+
{{settings.settings_resolve()}}
|
|
22
|
+
|
|
23
|
+
{{authoring_decisions()}}
|
|
18
24
|
---
|
|
19
25
|
|
|
20
26
|
## dynamic-build — author and run a build workflow
|
|
@@ -69,10 +75,12 @@ Pass it as `complianceFrameworks` when invoking the workflow; the engine hands i
|
|
|
69
75
|
|
|
70
76
|
`{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.
|
|
71
77
|
|
|
78
|
+
<!-- learning:on -->
|
|
72
79
|
**1. Apply decisions context**
|
|
73
80
|
|
|
74
|
-
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded
|
|
81
|
+
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Code agent prompts.
|
|
75
82
|
|
|
83
|
+
<!-- learning:end -->
|
|
76
84
|
**2. Read budget**
|
|
77
85
|
|
|
78
86
|
Note the `budget` value from the Workflow tool context (or default to "medium" if not provided). This governs Review agent roster size and verification vote count.
|
|
@@ -150,7 +158,9 @@ const TICKET = args.ticket || args[0] || "see task description";
|
|
|
150
158
|
const PLAN = args.plan || null;
|
|
151
159
|
const CRITERIA = args.criteria || null;
|
|
152
160
|
const TEST_PLAN = args.testPlan || null; // the plan's checked TP lines, one string (Pre-authoring step 4)
|
|
161
|
+
<!-- learning:on -->
|
|
153
162
|
const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
|
|
163
|
+
<!-- learning:end -->
|
|
154
164
|
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
|
|
155
165
|
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
|
|
156
166
|
const APPLY_CONVENTIONS = String(args.applyConventions) === "false" ? "false" : "true";
|
|
@@ -187,15 +197,18 @@ if (BRANCH === "(none)") {
|
|
|
187
197
|
|
|
188
198
|
// Phase 2: Implement
|
|
189
199
|
await phase("implement", () =>
|
|
190
|
-
agent(`
|
|
200
|
+
agent(`OPERATION: implement
|
|
201
|
+
Implement the following ticket on branch ${BRANCH}:
|
|
191
202
|
|
|
192
203
|
${TICKET}
|
|
193
204
|
|
|
194
205
|
${PLAN ? `Implementation plan:\n${PLAN}` : "No plan provided — use best judgment."}
|
|
195
206
|
|
|
207
|
+
<!-- learning:on -->
|
|
196
208
|
Relevant architectural decisions (apply devflow:apply-decisions algorithm):
|
|
197
209
|
${DECISIONS_CONTEXT}
|
|
198
210
|
|
|
211
|
+
<!-- learning:end -->
|
|
199
212
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
200
213
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
201
214
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
@@ -208,39 +221,54 @@ After implementing, commit your changes with a conventional-commit message and r
|
|
|
208
221
|
- Any open questions or blockers`, { agentType: "Code" })
|
|
209
222
|
);
|
|
210
223
|
|
|
211
|
-
// Phase 3: Gate 1 — post-code pipeline
|
|
224
|
+
// Phase 3: Gate 1 #1 — post-code pipeline: Simplify agent → Scrutinize agent → Validate agent.
|
|
225
|
+
// Validate runs last and unconditionally, so its one full run covers every commit the pass made (see gate1_postcode()).
|
|
212
226
|
const gate1 = await phase("gate1", async () => {
|
|
213
|
-
|
|
227
|
+
await agent(`Simplify and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Simplify" });
|
|
228
|
+
|
|
229
|
+
const scrutiny = await agent(`9-pillar self-review of recent changes on branch ${BRANCH}. Fix P0/P1 issues and commit the fixes.
|
|
230
|
+
Return: {"status": "PASS" | "FIXED" | "BLOCKED"}`, { agentType: "Scrutinize" });
|
|
231
|
+
// Anything but an explicit PASS or FIXED is a stop: a Scrutinize that returned no status, or one this skeleton does not know, has not accepted the code.
|
|
232
|
+
if (!["PASS", "FIXED"].includes(scrutiny?.status)) {
|
|
233
|
+
return { verdict: "ESCALATED", type: "scrutiny-blocked", reason: scrutiny?.status === "BLOCKED" ? "Scrutinize agent returned BLOCKED: a P0 issue cannot be fixed in scope" : "Scrutinize agent returned no recognised status: counted as BLOCKED" };
|
|
234
|
+
}
|
|
235
|
+
|
|
214
236
|
const validation = await agent(`Run build, typecheck, lint, and tests on branch ${BRANCH}.
|
|
215
237
|
Follow your Running commands block for every build and test command.
|
|
216
|
-
|
|
238
|
+
Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
|
|
217
239
|
|
|
218
|
-
|
|
219
|
-
|
|
240
|
+
// Anything but an explicit PASS is a failure: a Validate that returned no verdict has not validated the branch.
|
|
241
|
+
if (validation?.verdict !== "PASS") {
|
|
242
|
+
let failureDetails = validation?.details || "Validate agent returned no PASS verdict";
|
|
243
|
+
// Up to 2 fix attempts, each followed by a Validate re-run
|
|
220
244
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
221
|
-
await agent(`
|
|
222
|
-
${
|
|
245
|
+
await agent(`OPERATION: validation-fix
|
|
246
|
+
Fix the validation failures on branch ${BRANCH}:
|
|
247
|
+
${failureDetails}
|
|
248
|
+
<!-- learning:on -->
|
|
249
|
+
DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
|
|
250
|
+
<!-- learning:end -->
|
|
223
251
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
224
252
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
225
253
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
226
254
|
Commit fixes with conventional-commit message.`, { agentType: "Code" });
|
|
227
|
-
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH}
|
|
228
|
-
|
|
229
|
-
if (
|
|
255
|
+
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} after fix attempt ${attempt} (follow your Running commands block).
|
|
256
|
+
Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
|
|
257
|
+
if (recheck?.verdict === "PASS") break;
|
|
258
|
+
failureDetails = recheck?.details || failureDetails;
|
|
259
|
+
if (attempt === 2) return { verdict: "ESCALATED", type: "validation-exhausted", reason: "Validation exhausted after 2 Code agent fix attempts" };
|
|
230
260
|
}
|
|
231
261
|
}
|
|
232
262
|
|
|
233
|
-
await agent(`Simplify and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Simplify" });
|
|
234
|
-
|
|
235
|
-
const scrutiny = await agent(`9-pillar self-review of recent changes on branch ${BRANCH}. Report any code you changed and your findings.`, { agentType: "Scrutinize" });
|
|
236
|
-
|
|
237
|
-
if (scrutiny.codeChanged) {
|
|
238
|
-
await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (Scrutinize agent made changes). Report: PASS or FAIL.`, { agentType: "Validate" });
|
|
239
|
-
}
|
|
240
|
-
|
|
241
263
|
return { verdict: "PASS" };
|
|
242
264
|
});
|
|
243
265
|
|
|
266
|
+
// Gate 1 #1 stop, before Gate 2: an exhausted Validate or a BLOCKED Scrutinize leaves a broken or unfinished build,
|
|
267
|
+
// so Gate 2 and the review pass never run on it — a correctness stop, like the ticket-link and branch stops.
|
|
268
|
+
if (gate1.verdict === "ESCALATED") {
|
|
269
|
+
return { ticket: TICKET, branch: BRANCH, verdict: "ESCALATED", issueId: ISSUE_NUMBER, issuePrLink: ISSUE_PR_LINK, escalations: [{ type: gate1.type, description: gate1.reason }] };
|
|
270
|
+
}
|
|
271
|
+
|
|
244
272
|
// Phase 4: Gate 2 — acceptance gate (once, before review pass)
|
|
245
273
|
const gate2 = await phase("gate2", async () => {
|
|
246
274
|
if (!PLAN && !CRITERIA && !TEST_PLAN) {
|
|
@@ -249,21 +277,22 @@ const gate2 = await phase("gate2", async () => {
|
|
|
249
277
|
|
|
250
278
|
let evalVerdict = "SKIPPED";
|
|
251
279
|
if (PLAN) {
|
|
252
|
-
const
|
|
253
|
-
|
|
280
|
+
const evaluation = await agent(`Acceptance and scope check on branch ${BRANCH}. Answer both questions:
|
|
281
|
+
1. Does the implementation satisfy each numbered criterion, INCLUDING negative criteria (what it must NOT do)?
|
|
282
|
+
2. Did the Code agent introduce any unplanned changes, smuggled anti-features, or drift from the plan's intent?
|
|
254
283
|
Plan: ${PLAN}
|
|
255
284
|
Criteria: ${CRITERIA || "see plan"}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
Plan: ${PLAN}
|
|
259
|
-
Report: PASS or FAIL with rationale.`, { agentType: "Evaluate" }),
|
|
260
|
-
]);
|
|
261
|
-
evalVerdict = panel.every(p => p.verdict === "PASS") ? "PASS" : "FAIL";
|
|
285
|
+
Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answer is no, naming the failing criterion or the drift.`, { agentType: "Evaluate" });
|
|
286
|
+
evalVerdict = evaluation?.verdict === "PASS" ? "PASS" : "FAIL";
|
|
262
287
|
if (evalVerdict === "FAIL") {
|
|
263
288
|
// fix-and-continue — no re-evaluate, no inline Gate 1. The final Gate 1 (#2)
|
|
264
289
|
// after the review pass is the build gate before the branch is handed back.
|
|
265
|
-
await agent(`
|
|
266
|
-
|
|
290
|
+
await agent(`OPERATION: alignment-fix
|
|
291
|
+
Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
|
|
292
|
+
${evaluation?.rationale || "see the Evaluate agent's report"}
|
|
293
|
+
<!-- learning:on -->
|
|
294
|
+
DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
|
|
295
|
+
<!-- learning:end -->
|
|
267
296
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
268
297
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
269
298
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
@@ -282,8 +311,12 @@ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scen
|
|
|
282
311
|
testVerdict = testResult.verdict;
|
|
283
312
|
if (testVerdict === "FAIL") {
|
|
284
313
|
// fix-and-continue — no re-test, no inline Gate 1.
|
|
285
|
-
await agent(`
|
|
314
|
+
await agent(`OPERATION: qa-fix
|
|
315
|
+
Fix the failing acceptance test scenarios on branch ${BRANCH}:
|
|
286
316
|
${testResult.failures}
|
|
317
|
+
<!-- learning:on -->
|
|
318
|
+
DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
|
|
319
|
+
<!-- learning:end -->
|
|
287
320
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
288
321
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
289
322
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
@@ -309,7 +342,7 @@ const reviewResult = await phase("review", async () => {
|
|
|
309
342
|
const chunkSize = 5;
|
|
310
343
|
const reviewThunks = reviewFocuses.map(focus =>
|
|
311
344
|
() => agent(`${focus.charAt(0).toUpperCase() + focus.slice(1)} review. ${reviewScope}
|
|
312
|
-
Focus discipline: ${focus}. Return: {"focus": "${focus}", "reviewed": true, "filesExamined": ["<path>"], "findings": [{"file": "<path-or-null>", "description": "<one-sentence issue>", "severity": "critical|high|medium|low"}]}`, { agentType: "Review" })
|
|
345
|
+
Focus discipline: ${focus}. Return: {"focus": "${focus}", "reviewed": true, "filesExamined": ["<path>"], "findings": [{"file": "<path-or-null>", "line": <number-or-null>, "description": "<one-sentence issue>", "severity": "critical|high|medium|low"}]}`, { agentType: "Review" })
|
|
313
346
|
);
|
|
314
347
|
|
|
315
348
|
let rawReviews = [];
|
|
@@ -326,7 +359,7 @@ Focus discipline: ${focus}. Return: {"focus": "${focus}", "reviewed": true, "fil
|
|
|
326
359
|
const focus = reviewFocuses[i];
|
|
327
360
|
if (!r || r.reviewed !== true) {
|
|
328
361
|
// Dead Review agent: retry once sequentially
|
|
329
|
-
const retry = await agent(`Retry ${focus} review. ${reviewScope} Focus: ${focus} discipline. Return: {"focus": "${focus}", "reviewed": true, "filesExamined": ["<path>"], "findings": [{"file": "<path-or-null>", "description": "<one-sentence issue>", "severity": "critical|high|medium|low"}]}`, { agentType: "Review" });
|
|
362
|
+
const retry = await agent(`Retry ${focus} review. ${reviewScope} Focus: ${focus} discipline. Return: {"focus": "${focus}", "reviewed": true, "filesExamined": ["<path>"], "findings": [{"file": "<path-or-null>", "line": <number-or-null>, "description": "<one-sentence issue>", "severity": "critical|high|medium|low"}]}`, { agentType: "Review" });
|
|
330
363
|
if (!retry || retry.reviewed !== true) {
|
|
331
364
|
coverageGaps.push(focus);
|
|
332
365
|
log(`review coverage incomplete: ${focus}`);
|
|
@@ -391,13 +424,21 @@ ${JSON.stringify(allFindings.map((f, i) => ({ index: i, description: f.descripti
|
|
|
391
424
|
// Distinct-file groups are paced in chunks of FIX_CHUNK — same pacing bar as the Review spawn
|
|
392
425
|
// path — to avoid launching all Code agents at once on a large branch (reliability: explicit bound).
|
|
393
426
|
const FIX_CHUNK = 5;
|
|
394
|
-
|
|
427
|
+
// issue-fix SCOPE per finding: CRITICAL or HIGH takes the Careful protocol (failing regression test first); anything else is Standard.
|
|
428
|
+
const CAREFUL_SEVERITIES = new Set(["critical", "high"]);
|
|
429
|
+
const fixThunks = Object.values(fileGroups).map(findings => async () => {
|
|
395
430
|
const chunkResults = [];
|
|
396
431
|
for (let i = 0; i < findings.length; i += MAX_BATCH) {
|
|
397
432
|
const chunk = findings.slice(i, i + MAX_BATCH);
|
|
398
|
-
const r = await agent(`
|
|
399
|
-
|
|
400
|
-
|
|
433
|
+
const r = await agent(`OPERATION: issue-fix
|
|
434
|
+
Fix the following confirmed review findings on branch ${BRANCH}:
|
|
435
|
+
ISSUES:
|
|
436
|
+
${chunk.map((f, n) => `${n + 1}. ${f.file ? `${f.file}${f.line ? `:${f.line}` : ""} — ` : ""}${f.description} (${f.severity})`).join("\n")}
|
|
437
|
+
SCOPE: ${chunk.map((f, n) => `${n + 1}=${CAREFUL_SEVERITIES.has(String(f.severity).toLowerCase()) ? "Careful" : "Standard"}`).join(", ")}
|
|
438
|
+
PUSH: false
|
|
439
|
+
<!-- learning:on -->
|
|
440
|
+
DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
|
|
441
|
+
<!-- learning:end -->
|
|
401
442
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
402
443
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
403
444
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
@@ -436,38 +477,43 @@ Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<
|
|
|
436
477
|
return { survivingFindings: notAddressed, fixedFindings, coverageGaps };
|
|
437
478
|
});
|
|
438
479
|
|
|
439
|
-
// Phase 5.5: Gate 1 #2 — FINAL post-fix gate (
|
|
480
|
+
// Phase 5.5: Gate 1 #2 — FINAL post-fix gate (Simplify agent → Scrutinize agent → full Validate agent, Validate last and unconditional).
|
|
440
481
|
// Runs ONCE, after the review pass. Inside the pass the Code agent self-verified its own fixes;
|
|
441
482
|
// this is the build gate before the branch is handed back. See gate1_postcode() cadence.
|
|
442
483
|
const gate1Final = await phase("gate1-final", async () => {
|
|
484
|
+
await agent(`Simplify and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Simplify" });
|
|
485
|
+
|
|
486
|
+
const scrutiny = await agent(`9-pillar self-review of recent changes on branch ${BRANCH}. Fix P0/P1 issues and commit the fixes.
|
|
487
|
+
Return: {"status": "PASS" | "FIXED" | "BLOCKED"}`, { agentType: "Scrutinize" });
|
|
488
|
+
if (!["PASS", "FIXED"].includes(scrutiny?.status)) {
|
|
489
|
+
return { verdict: "ESCALATED", type: "scrutiny-blocked", reason: scrutiny?.status === "BLOCKED" ? "Final Gate 1 Scrutinize agent returned BLOCKED: a P0 issue cannot be fixed in scope" : "Final Gate 1 Scrutinize agent returned no recognised status: counted as BLOCKED" };
|
|
490
|
+
}
|
|
491
|
+
|
|
443
492
|
const validation = await agent(`Run build, typecheck, lint, and tests on branch ${BRANCH} (final gate after all fixing).
|
|
444
493
|
Follow your Running commands block for every build and test command.
|
|
445
|
-
|
|
494
|
+
Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
|
|
446
495
|
|
|
447
|
-
if (validation
|
|
448
|
-
let failureDetails = validation
|
|
496
|
+
if (validation?.verdict !== "PASS") {
|
|
497
|
+
let failureDetails = validation?.details || "Validate agent returned no PASS verdict";
|
|
449
498
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
450
|
-
await agent(`
|
|
499
|
+
await agent(`OPERATION: validation-fix
|
|
500
|
+
Fix the final validation failures on branch ${BRANCH}:
|
|
451
501
|
${failureDetails}
|
|
502
|
+
<!-- learning:on -->
|
|
503
|
+
DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
|
|
504
|
+
<!-- learning:end -->
|
|
452
505
|
ISSUE_NUMBER: ${ISSUE_NUMBER}
|
|
453
506
|
ISSUE_PR_LINK: ${ISSUE_PR_LINK}
|
|
454
507
|
COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
|
|
455
508
|
Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code" });
|
|
456
|
-
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (follow your Running commands block).
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
509
|
+
const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (final gate, after fix attempt ${attempt}; follow your Running commands block).
|
|
510
|
+
Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
|
|
511
|
+
if (recheck?.verdict === "PASS") break;
|
|
512
|
+
failureDetails = recheck?.details || failureDetails;
|
|
513
|
+
if (attempt === 2) return { verdict: "ESCALATED", type: "validation-exhausted", reason: "Final Gate 1 validation exhausted after 2 Code agent fix attempts" };
|
|
460
514
|
}
|
|
461
515
|
}
|
|
462
516
|
|
|
463
|
-
await agent(`Simplify and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Simplify" });
|
|
464
|
-
|
|
465
|
-
const scrutiny = await agent(`9-pillar self-review of recent changes on branch ${BRANCH}. Report any code you changed and your findings.`, { agentType: "Scrutinize" });
|
|
466
|
-
|
|
467
|
-
if (scrutiny.codeChanged) {
|
|
468
|
-
await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (Scrutinize agent made changes; follow your Running commands block). Report: PASS or FAIL.`, { agentType: "Validate" });
|
|
469
|
-
}
|
|
470
|
-
|
|
471
517
|
return { verdict: "PASS" };
|
|
472
518
|
});
|
|
473
519
|
|
|
@@ -496,7 +542,7 @@ Write a concise report. Present only surviving findings as outstanding — never
|
|
|
496
542
|
survivingFindings: reviewResult.survivingFindings || [], fixedFindings: reviewResult.fixedFindings || [],
|
|
497
543
|
reviewCoverage: { failedFocuses: coverageGaps, complete: coverageGaps.length === 0 },
|
|
498
544
|
escalations: [
|
|
499
|
-
...(gate1Final.verdict === "ESCALATED" ? [{ type: "validation-exhausted", description: gate1Final.reason || "final Gate 1 escalated" }] : []),
|
|
545
|
+
...(gate1Final.verdict === "ESCALATED" ? [{ type: gate1Final.type || "validation-exhausted", description: gate1Final.reason || "final Gate 1 escalated" }] : []),
|
|
500
546
|
...coverageGaps.map(focus => ({ type: "review-coverage-incomplete", description: `review coverage incomplete: ${focus}` })),
|
|
501
547
|
],
|
|
502
548
|
gate2, report,
|
|
@@ -540,7 +586,7 @@ When WAVE mode is detected, author a workflow that wraps the single-ticket engin
|
|
|
540
586
|
|
|
541
587
|
**Wave workflow structure (author after the SINGLE engine blocks above):**
|
|
542
588
|
|
|
543
|
-
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
|
|
589
|
+
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 the workflow spawns the Validate agent (build + test) and undoes a red merge. Escalations accumulate in a list; the final report lists all of them.
|
|
544
590
|
|
|
545
591
|
Wave skeleton — compact reference (see `wave_loop()` doctrine for full semantics):
|
|
546
592
|
|
|
@@ -551,11 +597,12 @@ Wave skeleton — compact reference (see `wave_loop()` doctrine for full semanti
|
|
|
551
597
|
const WAVE_TICKETS = [...remainingTickets]; // the pre-fetch's refs, in input order — the wave block's row order
|
|
552
598
|
const results = {}; // ticketId → its row of the workflow's return; a ticket with no entry never ran
|
|
553
599
|
// A row from an engine result: the fields step 3 after the workflow renders
|
|
554
|
-
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 });
|
|
600
|
+
const rowOf = (ticketId, r, merged, treeEqual = null) => ({ ticket: ticketId, ran: true, verdict: r?.verdict || r?.overallVerdict || null, merged, treeEqual, issuePrLink: r?.issuePrLink || "(none)", evaluateVerdict: r?.gate2?.evaluateVerdict, testVerdict: r?.gate2?.testVerdict, surviving: r?.survivingFindings?.length, coverageComplete: r?.reviewCoverage?.complete });
|
|
555
601
|
const MAX_ROUNDS = Math.max(10, remainingTickets.length * 2 + 5); // heuristic; always finite
|
|
556
|
-
const
|
|
602
|
+
const MERGE_SHA = /^[0-9a-f]{40}$/; // a merge commit as git prints it: the only Git-reported value that reaches a later prompt
|
|
603
|
+
const waveState = { quarantined: [], round: 0, halted: null };
|
|
557
604
|
let reAskedThisDeadlock = false; // re-ask guard: re-ask once on empty ready-set, then escalate
|
|
558
|
-
while (remainingTickets.length > 0 && waveState.round < MAX_ROUNDS) {
|
|
605
|
+
while (remainingTickets.length > 0 && waveState.round < MAX_ROUNDS && waveState.halted === null) {
|
|
559
606
|
waveState.round++;
|
|
560
607
|
|
|
561
608
|
// Design agent reader (opus) applies vacuous-truth rule: a ticket with no unmet dependencies is ALWAYS ready.
|
|
@@ -584,14 +631,44 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
|
|
|
584
631
|
try {
|
|
585
632
|
// ticketId is the ISSUE_REF the pre-fetch heading printed — this ticket's OWN reference: the engine's `ticket` (its TICKET)
|
|
586
633
|
// 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)
|
|
634
|
+
<!-- learning:on -->
|
|
587
635
|
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 });
|
|
636
|
+
<!-- learning:off -->
|
|
637
|
+
const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
|
|
638
|
+
<!-- learning:end -->
|
|
588
639
|
// Check both verdict (engine_output_schema) and overallVerdict (SINGLE skeleton alias).
|
|
589
640
|
// PASS and UNVERIFIED merge; PARTIAL, FAIL, ESCALATED (the ticket-link and branch stops included) or no verdict quarantine.
|
|
590
641
|
if (["PASS", "UNVERIFIED"].includes(engineResult.verdict || engineResult.overallVerdict)) {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
642
|
+
// Git merges locally and validates nothing: the workflow owns the post-merge Validate (D-WAVE-MERGE-VALIDATE).
|
|
643
|
+
const merge = await agent(`Merge ${engineResult.branch} to ${INTEGRATION_BRANCH} locally. Include ticket ID ${ticketId} in the merge commit message. Do not push, and run no build or test.
|
|
644
|
+
Return: {"merged": true, "mergeSha": "<40-hex merge commit>", "treeEqual": <true when mergeSha^{tree} equals the tree of ${engineResult.branch}'s head>} — or {"merged": false, "reason": "<why>"} when no merge is kept.`, { agentType: "Git" });
|
|
645
|
+
let kept = false;
|
|
646
|
+
let reason = merge?.reason || "merge failed";
|
|
647
|
+
if (merge?.merged === true && !MERGE_SHA.test(String(merge.mergeSha))) {
|
|
648
|
+
// A merge may sit on the integration branch with no SHA to validate or undo it by: stop taking merges.
|
|
649
|
+
waveState.halted = { ticket: ticketId, mergeSha: null, reason: "merge reported without a 40-hex mergeSha; the integration branch may hold an unvalidated merge" };
|
|
650
|
+
reason = waveState.halted.reason;
|
|
651
|
+
} else if (merge?.merged === true) {
|
|
652
|
+
// A merge counts as kept only when this Validate returns PASS. It always runs: treeEqual is recorded, never a reason to skip.
|
|
653
|
+
const post = await agent(`Run build, typecheck, lint, and tests on ${INTEGRATION_BRANCH} after merging ${engineResult.branch} (merge commit ${merge.mergeSha}).
|
|
654
|
+
Follow your Running commands block for every build and test command.
|
|
655
|
+
Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
|
|
656
|
+
if (post?.verdict === "PASS") {
|
|
657
|
+
kept = true;
|
|
658
|
+
} else {
|
|
659
|
+
const undo = await agent(`Undo the merge ${merge.mergeSha} on ${INTEGRATION_BRANCH}, only when no remote branch contains ${merge.mergeSha} and the HEAD of ${INTEGRATION_BRANCH} still equals ${merge.mergeSha}. On the integration branch run exactly this one command and no other that changes a branch or a remote: git reset --keep ${merge.mergeSha}^1
|
|
660
|
+
Return: {"undone": true} — or {"undone": false, "reason": "<why>"} when you did not undo it.`, { agentType: "Git" });
|
|
661
|
+
if (undo?.undone === true) {
|
|
662
|
+
reason = "post-merge build red; merge undone";
|
|
663
|
+
} else {
|
|
664
|
+
// A merge that stays red would poison every later ticket: stop taking merges and rounds, and name the red HEAD.
|
|
665
|
+
reason = `post-merge build red; merge not undone (${undo?.reason || "no reason given"})`;
|
|
666
|
+
waveState.halted = { ticket: ticketId, mergeSha: merge.mergeSha, reason };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
results[ticketId] = rowOf(ticketId, engineResult, kept, merge?.merged === true && typeof merge.treeEqual === "boolean" ? merge.treeEqual : null);
|
|
671
|
+
if (!kept) waveState.quarantined.push({ ticket: ticketId, reason });
|
|
595
672
|
} else {
|
|
596
673
|
results[ticketId] = rowOf(ticketId, engineResult, false);
|
|
597
674
|
waveState.quarantined.push({ ticket: ticketId, reason: engineResult.escalations?.[0]?.description || "engine fail/escalated" });
|
|
@@ -602,11 +679,12 @@ Return: {"merged": true} — or {"merged": false, "reason": "<why>"} when the me
|
|
|
602
679
|
waveState.quarantined.push({ ticket: ticketId, reason: `engine crash: ${String(err)}` });
|
|
603
680
|
}
|
|
604
681
|
remainingTickets = remainingTickets.filter(t => t !== ticketId);
|
|
682
|
+
if (waveState.halted !== null) break; // a red integration HEAD takes no further merge in this round
|
|
605
683
|
}
|
|
606
684
|
}
|
|
607
685
|
|
|
608
686
|
// After the wave report is written: one row per wave ticket, in input order. No entry ⇒ never ran (cascade, deadlock, MAX_ROUNDS).
|
|
609
|
-
return { tickets: WAVE_TICKETS.map(t => results[t] || { ticket: t, ran: false, verdict: null, merged: false, issuePrLink: "(none)" }), quarantined: waveState.quarantined };
|
|
687
|
+
return { tickets: WAVE_TICKETS.map(t => results[t] || { ticket: t, ran: false, verdict: null, merged: false, treeEqual: null, issuePrLink: "(none)" }), quarantined: waveState.quarantined, halted: waveState.halted };
|
|
610
688
|
```
|
|
611
689
|
|
|
612
690
|
---
|
|
@@ -630,6 +708,7 @@ A workflow cannot pause mid-run. After the build/wave workflow returns, you (the
|
|
|
630
708
|
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.
|
|
631
709
|
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.
|
|
632
710
|
- **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.
|
|
711
|
+
- **Halted** (the workflow returned a non-null `halted`: a post-merge build it could not undo, or a merge reported with no SHA): the integration HEAD may be red. Record `Wave PR: skipped (integration HEAD red, wave halted at <halted.ticket>)`, name `halted.mergeSha` (or `none reported`) and `halted.reason` as the red integration HEAD in the run summary, and go to step 4 with no wave PR.
|
|
633
712
|
- **Nothing merged** (no entry has `merged: true`): record `Wave PR: skipped (nothing merged)` and go to step 4.
|
|
634
713
|
- Otherwise compose (a) and then (b).
|
|
635
714
|
|
|
@@ -3,13 +3,21 @@ description: Parallel wave planning — plan-challenge every ticket, produce acc
|
|
|
3
3
|
argument-hint: "[tickets-dir | issue-list]"
|
|
4
4
|
output-dir: dist/commands
|
|
5
5
|
---
|
|
6
|
-
@import { authoring_preamble } from "./_partials/_preamble.mds"
|
|
6
|
+
@import { authoring_preamble, authoring_decisions } from "./_partials/_preamble.mds"
|
|
7
7
|
@import { docs_root } from "./_partials/_docs_root.mds"
|
|
8
8
|
@import { agent_roster, agent_caveats } from "./_partials/_roster.mds"
|
|
9
9
|
@import { acceptance_criteria_contract } from "./_partials/_plan_contract.mds"
|
|
10
10
|
@import { issue_ref_grammar, issue_capture_contract } from "./_partials/_tracker.mds"
|
|
11
|
+
@import "./_partials/_settings.mds" as settings
|
|
11
12
|
{{authoring_preamble()}}
|
|
12
13
|
|
|
14
|
+
<!-- learning:on -->
|
|
15
|
+
### Settings line
|
|
16
|
+
|
|
17
|
+
{{settings.settings_resolve()}}
|
|
18
|
+
|
|
19
|
+
<!-- learning:end -->
|
|
20
|
+
{{authoring_decisions()}}
|
|
13
21
|
---
|
|
14
22
|
|
|
15
23
|
## dynamic-plan — author and run a parallel planning + plan-challenge workflow
|
|
@@ -44,10 +52,12 @@ Before authoring, verify:
|
|
|
44
52
|
|
|
45
53
|
Pass `{worktree}` to the workflow as its `root` argument.
|
|
46
54
|
|
|
55
|
+
<!-- learning:on -->
|
|
47
56
|
**1. Apply decisions context**
|
|
48
57
|
|
|
49
|
-
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded
|
|
58
|
+
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note verbatim ADR/PF IDs to inject into Design agent prompts.
|
|
50
59
|
|
|
60
|
+
<!-- learning:end -->
|
|
51
61
|
**2. Read the preference profile**
|
|
52
62
|
|
|
53
63
|
Check whether `~/.devflow/preference-profile.md` exists:
|
|
@@ -112,7 +122,9 @@ export const meta = {
|
|
|
112
122
|
// AskUserQuestion happens at the command boundary after this workflow returns — NOT here.
|
|
113
123
|
|
|
114
124
|
const ticketSource = args.ticketSource || args[0] || "see task description";
|
|
125
|
+
<!-- learning:on -->
|
|
115
126
|
const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
|
|
127
|
+
<!-- learning:end -->
|
|
116
128
|
const PREFERENCE_PROFILE = args.preferenceProfile || ""; // injected before authoring
|
|
117
129
|
const TP_CONTRACT = args.tpContract || ""; // the "Test-plan line (TP)" contract below — its paragraph and four bullets — passed verbatim as tpContract when invoking the workflow, never pasted into this script (it holds backticks)
|
|
118
130
|
const slug = args.slug || "wave";
|
|
@@ -133,7 +145,9 @@ const plans = await phase("plan-parallel", () =>
|
|
|
133
145
|
parallel((tickets || []).map(ticket => () =>
|
|
134
146
|
agent(`Write an implementation plan for this ticket.
|
|
135
147
|
Ticket: ${JSON.stringify(ticket)}
|
|
148
|
+
<!-- learning:on -->
|
|
136
149
|
Decisions context (apply devflow:apply-decisions; a plan file can be posted to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
|
|
150
|
+
<!-- learning:end -->
|
|
137
151
|
The plan must cover: approach overview, affected files and modules, key design decisions, implementation sequence (what to build first), risks and mitigations, and any open questions you cannot resolve from the ticket alone.
|
|
138
152
|
Write a thorough but tight plan — every section must earn its place for a Code agent who has no other context.
|
|
139
153
|
Return: { ticketTitle, planMarkdown, openDecisions (array of genuine unknowns requiring user input) }.`, { agentType: "Design" })
|
|
@@ -149,7 +163,6 @@ const challenged = await phase("plan-challenge", () =>
|
|
|
149
163
|
|
|
150
164
|
Plan under review: ${JSON.stringify(plan)}
|
|
151
165
|
Ticket: ${JSON.stringify((tickets || [])[i])}
|
|
152
|
-
Decisions context: ${DECISIONS_CONTEXT}
|
|
153
166
|
|
|
154
167
|
Produce:
|
|
155
168
|
1. List of improvements / gaps / edge cases / side-effects identified.
|
|
@@ -160,7 +173,7 @@ Produce:
|
|
|
160
173
|
- its verification method is its method: a test committed to the suite is ci; a command run and read (a load test, a script) is local; a step performed and observed is manual;
|
|
161
174
|
- the paths it exercises, from the plan's affected files, are its files: globs.
|
|
162
175
|
Setup and expected outcome never go in a line: give them per TP in testScenarios.
|
|
163
|
-
4. A list of genuine design decisions that require user input (not settled by the plan
|
|
176
|
+
4. A list of genuine design decisions that require user input (not settled by the plan or the preference profile).
|
|
164
177
|
|
|
165
178
|
Test-plan line contract:
|
|
166
179
|
${TP_CONTRACT}
|
|
@@ -178,7 +191,9 @@ Return: { ticketTitle, improvements (array), acceptanceCriteria (array of number
|
|
|
178
191
|
const crossCritic = await phase("cross-plan-critic", () =>
|
|
179
192
|
agent(`Cross-plan conflict audit.
|
|
180
193
|
All ticket plans + challenged criteria: ${JSON.stringify(challenged)}
|
|
194
|
+
<!-- learning:on -->
|
|
181
195
|
Decisions context: ${DECISIONS_CONTEXT}
|
|
196
|
+
<!-- learning:end -->
|
|
182
197
|
|
|
183
198
|
Audit:
|
|
184
199
|
1. API conflicts: two tickets defining the same API differently (different signatures, return types, error codes).
|
|
@@ -186,7 +201,11 @@ Audit:
|
|
|
186
201
|
3. Undeclared dependencies: ticket A's plan implicitly requires something ticket B provides, but no dependency is declared.
|
|
187
202
|
4. Scope overlap: two tickets both claim ownership of the same module or behavior.
|
|
188
203
|
|
|
204
|
+
<!-- learning:on -->
|
|
189
205
|
For each conflict found: state which two tickets are involved, describe the conflict precisely, and propose a resolution aligned with the decisions ledger.
|
|
206
|
+
<!-- learning:off -->
|
|
207
|
+
For each conflict found: state which two tickets are involved, describe the conflict precisely, and propose a resolution.
|
|
208
|
+
<!-- learning:end -->
|
|
190
209
|
|
|
191
210
|
Return: { conflicts (array of {tickets, conflictDescription, proposedResolution}), planAmendments (array of {ticketTitle, amendment}) }.`, { agentType: "Design" })
|
|
192
211
|
);
|
|
@@ -199,10 +218,9 @@ const resolved = await phase("preference-resolve", () =>
|
|
|
199
218
|
Preference profile: ${PREFERENCE_PROFILE || "(none — no profile found)"}
|
|
200
219
|
Open decisions from plan-challenge: ${JSON.stringify((challenged || []).flatMap(c => c.openDecisions || []))}
|
|
201
220
|
Cross-plan conflicts needing resolution: ${JSON.stringify((crossCritic && crossCritic.conflicts) || [])}
|
|
202
|
-
Decisions context (ADRs/PFs already settled): ${DECISIONS_CONTEXT}
|
|
203
221
|
|
|
204
222
|
For each open decision:
|
|
205
|
-
- If the preference profile
|
|
223
|
+
- If the preference profile settles it clearly: auto-resolve and note the rationale.
|
|
206
224
|
- If not settled: add to the DECISIONS-NEEDED list for the user.
|
|
207
225
|
|
|
208
226
|
Return: { autoResolved (array of {decision, resolution, source}), decisionsNeeded (array of {decision, context, options}) }.`, { agentType: "Synthesize" })
|
|
@@ -224,14 +242,26 @@ For each ticket, write ${OUTDIR}/{ticket-slug}-plan.md containing:
|
|
|
224
242
|
- ## Acceptance Criteria (numbered, positive + negative)
|
|
225
243
|
- ## Test Plan — the challenger's testPlan lines, verbatim, one per line, and nothing else: no prose, no blank line between them, no setup or outcome
|
|
226
244
|
- ## Test Scenarios — one line per TP, in TP order: TP-n: its setup, then its expected outcome, from testScenarios
|
|
245
|
+
<!-- learning:on -->
|
|
227
246
|
- ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source, naming a recorded decision in words, never by ID)
|
|
247
|
+
<!-- learning:off -->
|
|
248
|
+
- ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source)
|
|
249
|
+
<!-- learning:end -->
|
|
228
250
|
|
|
229
251
|
Then write ${OUTDIR}/DECISIONS-NEEDED.md:
|
|
252
|
+
<!-- learning:on -->
|
|
230
253
|
- ## Auto-Resolved Decisions — list each silently-resolved decision as: decision → resolution → source (preference profile / ADR-NNN), so auto-resolution is auditable and reversible. If none, write "None."
|
|
254
|
+
<!-- learning:off -->
|
|
255
|
+
- ## Auto-Resolved Decisions — list each silently-resolved decision as: decision → resolution → source (preference profile), so auto-resolution is auditable and reversible. If none, write "None."
|
|
256
|
+
<!-- learning:end -->
|
|
231
257
|
- ## Decisions Needed
|
|
232
258
|
- One section per open decision: what the decision is, why it matters, what options exist.
|
|
233
259
|
- Include cross-plan conflicts that were not auto-resolved.
|
|
260
|
+
<!-- learning:on -->
|
|
234
261
|
- If no decisions needed, write: "No open decisions — all settled by preference profile or existing ADRs."
|
|
262
|
+
<!-- learning:off -->
|
|
263
|
+
- If no decisions needed, write: "No open decisions — all settled by the preference profile."
|
|
264
|
+
<!-- learning:end -->
|
|
235
265
|
|
|
236
266
|
Return: { planPaths (array), decisionsNeededPath (string), decisionsNeededCount (number) }.`, { agentType: "Synthesize" })
|
|
237
267
|
);
|
|
@@ -3,9 +3,17 @@ description: Decision-preference profile distiller — mine past session transcr
|
|
|
3
3
|
argument-hint: "[--dry-run]"
|
|
4
4
|
output-dir: dist/commands
|
|
5
5
|
---
|
|
6
|
-
@import { authoring_preamble } from "./_partials/_preamble.mds"
|
|
6
|
+
@import { authoring_preamble, authoring_decisions } from "./_partials/_preamble.mds"
|
|
7
|
+
@import "./_partials/_settings.mds" as settings
|
|
7
8
|
{{authoring_preamble()}}
|
|
8
9
|
|
|
10
|
+
<!-- learning:on -->
|
|
11
|
+
### Settings line
|
|
12
|
+
|
|
13
|
+
{{settings.settings_resolve()}}
|
|
14
|
+
|
|
15
|
+
<!-- learning:end -->
|
|
16
|
+
{{authoring_decisions()}}
|
|
9
17
|
---
|
|
10
18
|
|
|
11
19
|
## dynamic-profile — distill a decision-preference profile from past sessions
|
|
@@ -3,14 +3,22 @@ description: Generalized ticket-factory — turn an initiative or spec into a re
|
|
|
3
3
|
argument-hint: "[initiative | spec-doc]"
|
|
4
4
|
output-dir: dist/commands
|
|
5
5
|
---
|
|
6
|
-
@import { authoring_preamble } from "./_partials/_preamble.mds"
|
|
6
|
+
@import { authoring_preamble, authoring_decisions } from "./_partials/_preamble.mds"
|
|
7
7
|
@import { agent_roster, agent_caveats } from "./_partials/_roster.mds"
|
|
8
8
|
@import { ticket_body_template } from "./_partials/_ticket_template.mds"
|
|
9
9
|
@import { factory_shape } from "./_partials/_factory.mds"
|
|
10
10
|
@import { evidence_policy } from "./_partials/_evidence_policy.mds"
|
|
11
11
|
@import { docs_root } from "./_partials/_docs_root.mds"
|
|
12
|
+
@import "./_partials/_settings.mds" as settings
|
|
12
13
|
{{authoring_preamble()}}
|
|
13
14
|
|
|
15
|
+
<!-- learning:on -->
|
|
16
|
+
### Settings line
|
|
17
|
+
|
|
18
|
+
{{settings.settings_resolve()}}
|
|
19
|
+
|
|
20
|
+
<!-- learning:end -->
|
|
21
|
+
{{authoring_decisions()}}
|
|
14
22
|
---
|
|
15
23
|
|
|
16
24
|
## dynamic-tickets — author and run a ticket-factory workflow
|
|
@@ -53,10 +61,12 @@ Before you write the workflow script:
|
|
|
53
61
|
|
|
54
62
|
Pass `{worktree}` to the workflow as its `root` argument.
|
|
55
63
|
|
|
64
|
+
<!-- learning:on -->
|
|
56
65
|
**1. Apply decisions context**
|
|
57
66
|
|
|
58
|
-
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded
|
|
67
|
+
Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Design agent prompts.
|
|
59
68
|
|
|
69
|
+
<!-- learning:end -->
|
|
60
70
|
**2. Read and distill the initiative**
|
|
61
71
|
|
|
62
72
|
Read or note the user's input:
|
|
@@ -93,7 +103,9 @@ export const meta = {
|
|
|
93
103
|
const candidates = args.candidates || []; // [{title, summary, wave, dependsOn}]
|
|
94
104
|
const initiative = args.initiative || args[0] || "see task description";
|
|
95
105
|
const constraints = args.constraints || "";
|
|
106
|
+
<!-- learning:on -->
|
|
96
107
|
const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
|
|
108
|
+
<!-- learning:end -->
|
|
97
109
|
const slug = args.slug || initiative.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 40);
|
|
98
110
|
const ts = new Date().toISOString().slice(0,16).replace(/[-:T]/g, (c) => c === 'T' ? '_' : c === ':' ? '' : c);
|
|
99
111
|
const ROOT = args.root; // {worktree} from Pre-authoring setup: the checkout's toplevel, never cwd
|
|
@@ -105,7 +117,9 @@ const drafts = await phase("draft", () =>
|
|
|
105
117
|
agent(`Draft ticket for initiative: "${initiative}"
|
|
106
118
|
Ticket: ${JSON.stringify(c)}
|
|
107
119
|
Constraints: ${constraints}
|
|
120
|
+
<!-- learning:on -->
|
|
108
121
|
Decisions context (apply devflow:apply-decisions; the ticket is filed to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
|
|
122
|
+
<!-- learning:end -->
|
|
109
123
|
Write the ticket body following the ticket_body_template structure (Wave/Depends-on header, Summary, Scope with In/Out + anti-features, Invariants, numbered Acceptance Criteria with at least one negative criterion, Open Questions).
|
|
110
124
|
Return a JSON object with: title (string), summary (string), wave (number), dependsOn (array), bodyMarkdown (string), openQuestions (array).`, { agentType: "Design" })
|
|
111
125
|
))
|