mandrel 1.92.0 → 1.94.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/.agents/agents/acceptance-critic.md +129 -0
- package/.agents/agents/retro.md +42 -0
- package/.agents/agents/story-worker.md +162 -0
- package/.agents/docs/configuration.md +7 -1
- package/.agents/docs/execution-reference.md +27 -2
- package/.agents/instructions.md +43 -33
- package/.agents/personas/engineer.md +26 -112
- package/.agents/personas/security-engineer.md +1 -2
- package/.agents/rules/git-conventions-reference.md +225 -0
- package/.agents/rules/git-conventions.md +25 -200
- package/.agents/rules/security-baseline.md +5 -0
- package/.agents/rules/testing-standards.md +106 -13
- package/.agents/schemas/agentrc.schema.json +31 -1
- package/.agents/schemas/lifecycle/slice.end.schema.json +21 -0
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +20 -0
- package/.agents/schemas/lifecycle/slice.start.schema.json +17 -0
- package/.agents/scripts/acceptance-eval.js +62 -18
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bookkeeping-reconcile.js +117 -0
- package/.agents/scripts/check-context-budget.js +62 -5
- package/.agents/scripts/diagnose-friction.js +0 -6
- package/.agents/scripts/epic-deliver-prepare.js +272 -10
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
- package/.agents/scripts/lib/close-validation/gates.js +159 -21
- package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
- package/.agents/scripts/lib/config/delivery-routing.js +87 -0
- package/.agents/scripts/lib/config/explain.js +2 -0
- package/.agents/scripts/lib/config-resolver.js +1 -1
- package/.agents/scripts/lib/config-settings-schema-delivery.js +37 -3
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -0
- package/.agents/scripts/lib/doc-tiers.js +37 -2
- package/.agents/scripts/lib/observability/active-story-env.js +111 -2
- package/.agents/scripts/lib/observability/hook-heartbeat.js +219 -0
- package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +270 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +141 -0
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -124
- package/.agents/scripts/lib/orchestration/deliver-route.js +173 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +233 -0
- package/.agents/scripts/lib/orchestration/file-assumptions.js +68 -7
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +270 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +83 -2
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +6 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +189 -3
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +99 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +38 -1
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +16 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +1 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +19 -2
- package/.agents/scripts/lib/provider-factory.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
- package/.agents/scripts/plan-context.js +28 -10
- package/.agents/scripts/post-structured-comment.js +38 -0
- package/.agents/scripts/slice-phase.js +361 -0
- package/.agents/scripts/sync-claude-agents.js +165 -0
- package/.agents/scripts/update-ticket-state.js +31 -0
- package/.agents/scripts/wave-tick.js +138 -9
- package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
- package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +5 -5
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +8 -8
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +4 -4
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +26 -56
- package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +2 -8
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
- package/.agents/skills/skills.index.json +11 -381
- package/.agents/workflows/deliver.md +47 -4
- package/.agents/workflows/helpers/acceptance-self-eval.md +38 -13
- package/.agents/workflows/helpers/deliver-epic-reference.md +18 -5
- package/.agents/workflows/helpers/deliver-epic-single.md +331 -0
- package/.agents/workflows/helpers/deliver-epic.md +51 -8
- package/.agents/workflows/helpers/deliver-stories.md +15 -5
- package/.agents/workflows/helpers/epic-deliver-story.md +12 -3
- package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
- package/.agents/workflows/helpers/plan-epic-reference.md +19 -8
- package/.agents/workflows/helpers/plan-epic.md +95 -27
- package/.agents/workflows/helpers/scope-triage-gate.md +9 -0
- package/.agents/workflows/mandrel-update.md +1 -1
- package/.agents/workflows/plan.md +16 -4
- package/docs/CHANGELOG.md +23 -0
- package/lib/cli/registry.js +95 -0
- package/package.json +4 -2
- package/.agents/personas/engineer-mobile.md +0 -120
- package/.agents/personas/engineer-web.md +0 -111
- package/.agents/personas/product.md +0 -94
- package/.agents/personas/refactorer.md +0 -113
- package/.agents/personas/sre.md +0 -86
- package/.agents/personas/ux-designer.md +0 -95
- package/.agents/scripts/epic-plan-decompose.js +0 -54
- package/.agents/scripts/epic-plan-spec.js +0 -64
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
- package/.agents/scripts/plan-critics.js +0 -227
- package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
- package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
- package/.agents/skills/core/code-simplification/SKILL.md +0 -389
- package/.agents/skills/core/context-engineering/SKILL.md +0 -309
- package/.agents/skills/core/context-engineering/examples.md +0 -58
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
- package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
- package/.agents/skills/core/idea-refinement/examples.md +0 -437
- package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
- package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
- package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
- package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
- package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
- package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
- package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
- package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
- package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
- package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
- package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
- package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-critics-evaluate.js — shared critic-dispatch evaluation for the
|
|
3
|
+
* collapsed /plan flow (#4496 fix 6; extracted from the `plan-critics.js`
|
|
4
|
+
* CLI so the persist surface folds the same evaluation in as a pre-write
|
|
5
|
+
* phase).
|
|
6
|
+
*
|
|
7
|
+
* Two consumers:
|
|
8
|
+
* - `plan-persist.js` (via `runPlanPersist`) — evaluates the dispatch
|
|
9
|
+
* conditions as a deterministic pre-write phase, prints the verdicts,
|
|
10
|
+
* and records every skip on the plan-metrics ledger, so the headless
|
|
11
|
+
* path never pays a standalone CLI turn for the same decision.
|
|
12
|
+
* - `plan-critics.js` — the standalone CLI survives one release as a
|
|
13
|
+
* thin shim over this module for the attended pre-gate evaluation
|
|
14
|
+
* (the verdict folds into gate #2's view before the persist runs).
|
|
15
|
+
*
|
|
16
|
+
* Pure evaluation: no file I/O, no GitHub calls, no ledger writes — the
|
|
17
|
+
* callers own artifact loading and skip recording.
|
|
18
|
+
*
|
|
19
|
+
* @module lib/orchestration/plan-critics-evaluate
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { getLimits } from '../config-resolver.js';
|
|
23
|
+
import {
|
|
24
|
+
evaluateConsolidationDispatch,
|
|
25
|
+
evaluatePremortemDispatch,
|
|
26
|
+
} from './plan-critic-conditions.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the planning risk heuristics list from the canonical config
|
|
30
|
+
* block (same resolution `plan-context.js` and the decompose context use).
|
|
31
|
+
*
|
|
32
|
+
* @param {object} config
|
|
33
|
+
* @returns {string[]}
|
|
34
|
+
*/
|
|
35
|
+
function resolveRiskHeuristics(config = {}) {
|
|
36
|
+
if (Array.isArray(config.planning?.riskHeuristics)) {
|
|
37
|
+
return config.planning.riskHeuristics;
|
|
38
|
+
}
|
|
39
|
+
return config.agentSettings?.planning?.riskHeuristics || [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Evaluate the consolidation + pre-mortem critic dispatch conditions over
|
|
44
|
+
* the authored planning artifacts (design §4 / #4474 PR6 conditions,
|
|
45
|
+
* unchanged):
|
|
46
|
+
*
|
|
47
|
+
* - Consolidation: skipped outright when `tickets` is null/absent (the
|
|
48
|
+
* single-delivery shape authors no draft tickets); otherwise the
|
|
49
|
+
* deterministic precondition + size/divergence conditions.
|
|
50
|
+
* - Pre-mortem: risk verdict overall level high, OR ticket count at least
|
|
51
|
+
* half `maxTickets`, OR any `planning.riskHeuristics` phrase matching
|
|
52
|
+
* the plan text.
|
|
53
|
+
*
|
|
54
|
+
* @param {{
|
|
55
|
+
* techSpecContent: string,
|
|
56
|
+
* riskVerdict: { summary?: string },
|
|
57
|
+
* tickets?: Array<object>|null,
|
|
58
|
+
* config?: object,
|
|
59
|
+
* }} args
|
|
60
|
+
* @returns {{
|
|
61
|
+
* consolidation: { critic: string, dispatch: boolean, reasons: string[] },
|
|
62
|
+
* premortem: { critic: string, dispatch: boolean, reasons: string[] },
|
|
63
|
+
* }}
|
|
64
|
+
*/
|
|
65
|
+
export function evaluatePlanCritics({
|
|
66
|
+
techSpecContent,
|
|
67
|
+
riskVerdict,
|
|
68
|
+
tickets = null,
|
|
69
|
+
config = {},
|
|
70
|
+
}) {
|
|
71
|
+
const ticketList = Array.isArray(tickets) ? tickets : null;
|
|
72
|
+
const consolidation =
|
|
73
|
+
ticketList === null
|
|
74
|
+
? {
|
|
75
|
+
critic: 'consolidation',
|
|
76
|
+
dispatch: false,
|
|
77
|
+
reasons: [
|
|
78
|
+
'single-delivery shape — no draft tickets exist to consolidate.',
|
|
79
|
+
],
|
|
80
|
+
}
|
|
81
|
+
: evaluateConsolidationDispatch({
|
|
82
|
+
draftStories: ticketList,
|
|
83
|
+
specText: techSpecContent,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const premortem = evaluatePremortemDispatch({
|
|
87
|
+
riskVerdict,
|
|
88
|
+
ticketCount: ticketList?.length ?? 0,
|
|
89
|
+
maxTickets: getLimits(config).maxTickets,
|
|
90
|
+
riskHeuristics: resolveRiskHeuristics(config),
|
|
91
|
+
planText: [
|
|
92
|
+
techSpecContent ?? '',
|
|
93
|
+
ticketList ? JSON.stringify(ticketList) : '',
|
|
94
|
+
riskVerdict?.summary ?? '',
|
|
95
|
+
].join('\n'),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return { consolidation, premortem };
|
|
99
|
+
}
|
|
@@ -130,6 +130,7 @@ import {
|
|
|
130
130
|
read as readPlanState,
|
|
131
131
|
write as writePlanState,
|
|
132
132
|
} from '../epic-plan-state-store.js';
|
|
133
|
+
import { evaluatePlanCritics } from '../plan-critics-evaluate.js';
|
|
133
134
|
import {
|
|
134
135
|
appendCriticSkip,
|
|
135
136
|
readPlanMetrics,
|
|
@@ -426,9 +427,10 @@ export async function runPlanPersist({
|
|
|
426
427
|
let validated = null;
|
|
427
428
|
let amendPartition = null;
|
|
428
429
|
let reachability = null;
|
|
430
|
+
let gateSet = null;
|
|
429
431
|
if (mode !== 'single') {
|
|
430
432
|
amendPartition = mode === 'amend' ? partitionAmendTickets(tickets) : null;
|
|
431
|
-
|
|
433
|
+
gateSet = mode === 'amend' ? buildMergedTicketSet(tickets) : tickets;
|
|
432
434
|
const maxTickets = getLimits(config).maxTickets;
|
|
433
435
|
if (gateSet.length > maxTickets && !allowOverBudget) {
|
|
434
436
|
throw new Error(
|
|
@@ -533,6 +535,40 @@ export async function runPlanPersist({
|
|
|
533
535
|
);
|
|
534
536
|
}
|
|
535
537
|
|
|
538
|
+
// ---- Step 4.7: folded critic dispatch evaluation (#4496 fix 6 — the
|
|
539
|
+
// former standalone `plan-critics.js` turn). Deterministic and git-local,
|
|
540
|
+
// still zero provider calls: the verdicts are printed (and returned on
|
|
541
|
+
// the result) as part of the pre-write phase, and every skip decision is
|
|
542
|
+
// appended to the plan-metrics ledger exactly as the standalone CLI did,
|
|
543
|
+
// so under-firing stays auditable without a separate invocation on the
|
|
544
|
+
// headless path. Advisory by construction — the deterministic validators
|
|
545
|
+
// above remain the unchanged hard gates. ----
|
|
546
|
+
const critics = evaluatePlanCritics({
|
|
547
|
+
techSpecContent,
|
|
548
|
+
riskVerdict,
|
|
549
|
+
tickets: gateSet,
|
|
550
|
+
config,
|
|
551
|
+
});
|
|
552
|
+
for (const decision of [critics.consolidation, critics.premortem]) {
|
|
553
|
+
Logger.info(
|
|
554
|
+
`[plan-persist] critic ${decision.critic}: ` +
|
|
555
|
+
`${decision.dispatch ? 'dispatch' : 'skip'} — ` +
|
|
556
|
+
decision.reasons.join('; '),
|
|
557
|
+
);
|
|
558
|
+
if (!decision.dispatch) {
|
|
559
|
+
// Best-effort by contract — a failed append never fails the persist.
|
|
560
|
+
await appendCriticSkip(
|
|
561
|
+
{
|
|
562
|
+
critic: decision.critic,
|
|
563
|
+
reasons: decision.reasons,
|
|
564
|
+
cli: 'plan-persist',
|
|
565
|
+
epicId: requestedEpicId,
|
|
566
|
+
},
|
|
567
|
+
config,
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
536
572
|
// ---- Step 5: ideation fold / Epic resolution (first provider call). ----
|
|
537
573
|
const { epicId, epic, created } = await resolveTargetEpic({
|
|
538
574
|
epicId: requestedEpicId,
|
|
@@ -902,6 +938,7 @@ export async function runPlanPersist({
|
|
|
902
938
|
freshness,
|
|
903
939
|
healthcheck,
|
|
904
940
|
reachability,
|
|
941
|
+
critics,
|
|
905
942
|
reconcile,
|
|
906
943
|
specPath: specFilePath,
|
|
907
944
|
waveTable,
|
|
@@ -89,10 +89,16 @@ function renderWaveTableLines(waveTable) {
|
|
|
89
89
|
* record `{ deliveryShape, sliceCount, routingReasons }` (Epic #4474 PR4)
|
|
90
90
|
* for the spec-only mode.
|
|
91
91
|
*
|
|
92
|
+
* Every auto-waiver the persist derived is printed WITH its reason
|
|
93
|
+
* (#4496 fix 2) — e.g. the no-BDD-runner acceptance-disposition waiver
|
|
94
|
+
* (`planningRisk.acceptanceWaivedReason`) — so the summary is
|
|
95
|
+
* self-explanatory and a headless reader never has to re-derive a persist
|
|
96
|
+
* outcome from framework source.
|
|
97
|
+
*
|
|
92
98
|
* @param {{
|
|
93
99
|
* epicId: number,
|
|
94
100
|
* ticketCount: number,
|
|
95
|
-
* planningRisk: { overallLevel?: string, gateDecision?: string },
|
|
101
|
+
* planningRisk: { overallLevel?: string, gateDecision?: string, acceptanceDisposition?: string, acceptanceWaivedReason?: string },
|
|
96
102
|
* reviewRouting: { decision?: string },
|
|
97
103
|
* freshness?: { stale?: number, ambiguous?: number },
|
|
98
104
|
* healthcheck?: { ok?: boolean, waived?: boolean, skipped?: boolean },
|
|
@@ -137,6 +143,14 @@ export function buildPlanSummaryCommentBody({
|
|
|
137
143
|
? `- Single-delivery plan (\`delivery::single\`): no Story tree — the Delivery Slicing table is the audit trail.`
|
|
138
144
|
: `- ${ticketCount} Story ticket(s) persisted across ${waveTable.length} wave(s).`;
|
|
139
145
|
|
|
146
|
+
// Auto-waivers always ship with their reason (#4496 fix 2): the summary
|
|
147
|
+
// is authoritative, so the line must be self-explanatory on its own.
|
|
148
|
+
const waiverLines = planningRisk?.acceptanceWaivedReason
|
|
149
|
+
? [
|
|
150
|
+
`- ⚠️ Acceptance disposition auto-waived to \`not-applicable\` — ${planningRisk.acceptanceWaivedReason}`,
|
|
151
|
+
]
|
|
152
|
+
: [];
|
|
153
|
+
|
|
140
154
|
const amendLines = amend
|
|
141
155
|
? [
|
|
142
156
|
`- Amend delta: ${amend.created.length} added, ${amend.recreated.length} modified (closed + recreated), ${amend.closed.length} closed, ${amend.keptCount} kept untouched.`,
|
|
@@ -176,6 +190,7 @@ export function buildPlanSummaryCommentBody({
|
|
|
176
190
|
headLine,
|
|
177
191
|
...amendLines,
|
|
178
192
|
`- Risk: ${planningRisk?.overallLevel ?? 'unknown'} · ${planningRisk?.gateDecision ?? 'unknown'} (review routing: ${reviewRouting?.decision ?? 'unknown'}).`,
|
|
193
|
+
...waiverLines,
|
|
179
194
|
freshnessLine,
|
|
180
195
|
healthcheckLine,
|
|
181
196
|
// G2 measurement receipt (Epic #4474 PR1/PR7): the plan-CLI invocation
|
|
@@ -140,6 +140,7 @@ export async function runPreMergeGates({
|
|
|
140
140
|
config,
|
|
141
141
|
epicBranch,
|
|
142
142
|
cwd: worktreePath || cwd,
|
|
143
|
+
log: (m) => logger.info?.(m),
|
|
143
144
|
});
|
|
144
145
|
const gateCount = Array.isArray(gates) ? gates.length : 0;
|
|
145
146
|
// Story #2250 — emit `close-validate.start` only when both an epicId
|
|
@@ -272,7 +272,7 @@ export function validateAcFreshness({
|
|
|
272
272
|
* Epic #2501 introduced this guard after the legacy `baseline-refresh`
|
|
273
273
|
* leading-token prescription created a wave of commit-msg hook failures
|
|
274
274
|
* across story-deliver sub-agents. See
|
|
275
|
-
* `.agents/skills/core/
|
|
275
|
+
* `.agents/skills/core/gates-and-baselines/SKILL.md` for the canonical refresh
|
|
276
276
|
* shape (Conventional-Commits subject + `baseline-refresh: true` body
|
|
277
277
|
* trailer).
|
|
278
278
|
*/
|
|
@@ -589,8 +589,25 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
|
|
|
589
589
|
gitRunner: sharedGitRunner,
|
|
590
590
|
cwd: opts.cwd,
|
|
591
591
|
});
|
|
592
|
+
// Auto-normalizations (#4496 fix 5) get their own prefix so the logged
|
|
593
|
+
// warning is self-explanatory; everything else on the warnings channel
|
|
594
|
+
// is a legacy-shape deprecation nudge.
|
|
595
|
+
const normalizationWarnings = new Set(
|
|
596
|
+
(assumptionReport.normalizations ?? []).map((n) => n.path),
|
|
597
|
+
);
|
|
592
598
|
for (const warning of assumptionReport.warnings) {
|
|
593
|
-
|
|
599
|
+
const isNormalization = warning.includes('auto-normalized to "creates"');
|
|
600
|
+
Logger.warn(
|
|
601
|
+
`[ticket-validator] ${isNormalization ? 'assumption-normalized' : 'assumption-deprecation'}: ${warning}`,
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
if (normalizationWarnings.size > 0) {
|
|
605
|
+
Logger.warn(
|
|
606
|
+
`[ticket-validator] ${normalizationWarnings.size} refactors-existing ` +
|
|
607
|
+
'declaration(s) on base-untracked path(s) auto-normalized to ' +
|
|
608
|
+
'"creates" — the gate proceeds; update the plan declarations at ' +
|
|
609
|
+
'the next amend.',
|
|
610
|
+
);
|
|
594
611
|
}
|
|
595
612
|
assumptionErrors = assumptionReport.errors;
|
|
596
613
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Accepts the canonical resolved config object (the wrapper returned by
|
|
5
5
|
* `resolveConfig()` with `config.github` populated). The legacy
|
|
6
6
|
* `orchestration`-shaped argument is no longer supported as part of the
|
|
7
|
-
* Epic #2880 hard cutover; see `.agents/rules/git-conventions.md#contract-cutovers-—-no-shim-layer`.
|
|
7
|
+
* Epic #2880 hard cutover; see `.agents/rules/git-conventions-reference.md#contract-cutovers-—-no-shim-layer`.
|
|
8
8
|
*
|
|
9
9
|
* @see docs/v5-implementation-plan.md Sprint 1B
|
|
10
10
|
*/
|
|
@@ -88,7 +88,7 @@ Your job is to take an Epic (including its inline User Stories) and a Technical
|
|
|
88
88
|
|
|
89
89
|
### LABEL CONVENTIONS:
|
|
90
90
|
- Every ticket must have the \`type::story\` label. No other type label is allowed — the retired Feature and Task tiers have no labels under this hierarchy.
|
|
91
|
-
- Every ticket must have a \`persona::[engineer|architect|qa-engineer|engineer
|
|
91
|
+
- Every ticket must have a \`persona::[engineer|architect|qa-engineer|security-engineer|etc]\` label indicating WHO should execute it.
|
|
92
92
|
|
|
93
93
|
### OUTPUT FORMAT:
|
|
94
94
|
You MUST respond ONLY with a valid JSON array of objects. No prose, no markdown blocks.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* stdout-pure JSON envelope. The PR7 cutover retired the delegate CLIs —
|
|
12
12
|
* this is the only emit-context surface.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* Three entry forms (exactly one is required):
|
|
15
15
|
*
|
|
16
16
|
* --epic <id> Existing-Epic mode. Envelope carries `epic`,
|
|
17
17
|
* `clarity` (Epic Clarity Gate rubric), `replan`
|
|
@@ -23,6 +23,15 @@
|
|
|
23
23
|
* dup search). No clarity score: the ideation path
|
|
24
24
|
* is definitionally clear.
|
|
25
25
|
*
|
|
26
|
+
* --seed "<text>" Headless ideation entry (#4496 fix 1) — neither
|
|
27
|
+
* the Epic nor the one-pager exists yet. The dup
|
|
28
|
+
* search runs off the raw seed text, and the
|
|
29
|
+
* envelope additively carries `seed`, `scopeTriage`
|
|
30
|
+
* (the scope-triage rubric applied CLI-side — fix 6)
|
|
31
|
+
* and `onePagerSpec`, so the one-pager sections are
|
|
32
|
+
* authored in the same batched write as the spec
|
|
33
|
+
* artifacts.
|
|
34
|
+
*
|
|
26
35
|
* Flags:
|
|
27
36
|
* --pretty Pretty-print the JSON envelope.
|
|
28
37
|
* --full-context Bypass the planning-context budget (unbounded body).
|
|
@@ -58,10 +67,11 @@ import { createProvider } from './lib/provider-factory.js';
|
|
|
58
67
|
* captured output is exactly one `JSON.parse`-able payload.
|
|
59
68
|
*
|
|
60
69
|
* @param {{
|
|
61
|
-
* mode: 'epic'|'one-pager',
|
|
70
|
+
* mode: 'epic'|'one-pager'|'seed',
|
|
62
71
|
* epicId?: number,
|
|
63
72
|
* onePagerPath?: string,
|
|
64
73
|
* onePagerContent?: string,
|
|
74
|
+
* seedText?: string,
|
|
65
75
|
* provider: object,
|
|
66
76
|
* config: object,
|
|
67
77
|
* settings: object,
|
|
@@ -77,6 +87,7 @@ export async function emitPlanContext({
|
|
|
77
87
|
epicId,
|
|
78
88
|
onePagerPath,
|
|
79
89
|
onePagerContent,
|
|
90
|
+
seedText,
|
|
80
91
|
provider,
|
|
81
92
|
config,
|
|
82
93
|
settings,
|
|
@@ -90,6 +101,7 @@ export async function emitPlanContext({
|
|
|
90
101
|
epicId,
|
|
91
102
|
onePagerPath,
|
|
92
103
|
onePagerContent,
|
|
104
|
+
seedText,
|
|
93
105
|
provider,
|
|
94
106
|
config,
|
|
95
107
|
settings,
|
|
@@ -108,6 +120,7 @@ async function main() {
|
|
|
108
120
|
options: {
|
|
109
121
|
epic: { type: 'string' },
|
|
110
122
|
'one-pager': { type: 'string' },
|
|
123
|
+
seed: { type: 'string' },
|
|
111
124
|
pretty: { type: 'boolean', default: false },
|
|
112
125
|
'full-context': { type: 'boolean', default: false },
|
|
113
126
|
},
|
|
@@ -117,12 +130,16 @@ async function main() {
|
|
|
117
130
|
const hasEpic = typeof values.epic === 'string' && values.epic.length > 0;
|
|
118
131
|
const hasOnePager =
|
|
119
132
|
typeof values['one-pager'] === 'string' && values['one-pager'].length > 0;
|
|
120
|
-
|
|
133
|
+
const hasSeed = typeof values.seed === 'string' && values.seed.length > 0;
|
|
134
|
+
const entryForms = [hasEpic, hasOnePager, hasSeed].filter(Boolean).length;
|
|
135
|
+
if (entryForms !== 1) {
|
|
121
136
|
throw new Error(
|
|
122
|
-
'Pass exactly one of --epic <id
|
|
123
|
-
'(--epic: existing-Epic mode; --one-pager: ideation mode
|
|
137
|
+
'Pass exactly one of --epic <id>, --one-pager <path> or --seed "<text>". ' +
|
|
138
|
+
'(--epic: existing-Epic mode; --one-pager: ideation mode; ' +
|
|
139
|
+
'--seed: headless ideation entry.)',
|
|
124
140
|
);
|
|
125
141
|
}
|
|
142
|
+
const mode = hasEpic ? 'epic' : hasOnePager ? 'one-pager' : 'seed';
|
|
126
143
|
|
|
127
144
|
let epicId;
|
|
128
145
|
if (hasEpic) {
|
|
@@ -159,21 +176,22 @@ async function main() {
|
|
|
159
176
|
const provider = createProvider(config);
|
|
160
177
|
|
|
161
178
|
// Plan-metrics ledger (#4474 PR1): stamp entry/exit + mode so the folded
|
|
162
|
-
// emit surface is measured against the 12-phase baseline. One-pager
|
|
163
|
-
//
|
|
164
|
-
// (epicId null) exactly like `story-plan.js`.
|
|
179
|
+
// emit surface is measured against the 12-phase baseline. One-pager and
|
|
180
|
+
// seed modes have no Epic yet, so the record routes to the standalone
|
|
181
|
+
// stream (epicId null) exactly like `story-plan.js`.
|
|
165
182
|
await recordPlanInvocation(
|
|
166
183
|
{
|
|
167
184
|
cli: 'plan-context',
|
|
168
|
-
mode
|
|
185
|
+
mode,
|
|
169
186
|
epicId: hasEpic ? epicId : null,
|
|
170
187
|
config,
|
|
171
188
|
},
|
|
172
189
|
() =>
|
|
173
190
|
emitPlanContext({
|
|
174
|
-
mode
|
|
191
|
+
mode,
|
|
175
192
|
epicId,
|
|
176
193
|
onePagerPath: hasOnePager ? values['one-pager'] : undefined,
|
|
194
|
+
seedText: hasSeed ? values.seed : undefined,
|
|
177
195
|
provider,
|
|
178
196
|
config,
|
|
179
197
|
settings,
|
|
@@ -22,6 +22,10 @@ import fs from 'node:fs/promises';
|
|
|
22
22
|
import { parseArgs } from 'node:util';
|
|
23
23
|
import { runAsCli } from './lib/cli-utils.js';
|
|
24
24
|
import { resolveConfig } from './lib/config-resolver.js';
|
|
25
|
+
import {
|
|
26
|
+
outboxPathFor,
|
|
27
|
+
postCommentOrBuffer,
|
|
28
|
+
} from './lib/orchestration/bookkeeping-outbox.js';
|
|
25
29
|
import {
|
|
26
30
|
assertValidStructuredCommentType,
|
|
27
31
|
upsertStructuredComment,
|
|
@@ -37,6 +41,11 @@ Flags:
|
|
|
37
41
|
retro, epic-run-state, wave-0-start) (required).
|
|
38
42
|
--body-file Path to a file containing the markdown body (required).
|
|
39
43
|
--provider Provider name (default: inferred from .agentrc.json github block).
|
|
44
|
+
--buffer Headless mode: buffer this upsert to the per-Epic bookkeeping
|
|
45
|
+
outbox instead of posting live. Requires --epic. Drained to
|
|
46
|
+
GitHub once at finalize by bookkeeping-reconcile.js (Epic
|
|
47
|
+
#4476). Omit for attended runs (posts live, unchanged).
|
|
48
|
+
--epic <id> Epic id owning the outbox (required with --buffer).
|
|
40
49
|
--help Show this message.
|
|
41
50
|
`;
|
|
42
51
|
|
|
@@ -64,6 +73,8 @@ export function parseArgv(argv) {
|
|
|
64
73
|
marker: { type: 'string' },
|
|
65
74
|
'body-file': { type: 'string' },
|
|
66
75
|
provider: { type: 'string' },
|
|
76
|
+
buffer: { type: 'boolean' },
|
|
77
|
+
epic: { type: 'string' },
|
|
67
78
|
help: { type: 'boolean' },
|
|
68
79
|
},
|
|
69
80
|
strict: false,
|
|
@@ -115,6 +126,33 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
115
126
|
: config;
|
|
116
127
|
const provider = createProvider(effectiveConfig);
|
|
117
128
|
|
|
129
|
+
// Headless buffering (Epic #4476 M5): route the upsert to the per-Epic
|
|
130
|
+
// outbox instead of a live GitHub round-trip. Requires --epic to locate the
|
|
131
|
+
// outbox; finalize's bookkeeping-reconcile.js drains it once. Attended runs
|
|
132
|
+
// (no --buffer) are unchanged.
|
|
133
|
+
if (values.buffer) {
|
|
134
|
+
const epicId = Number.parseInt(values.epic ?? '', 10);
|
|
135
|
+
if (!Number.isInteger(epicId) || epicId <= 0) {
|
|
136
|
+
process.stderr.write(
|
|
137
|
+
'[post-structured-comment] --buffer requires --epic <id>.\n',
|
|
138
|
+
);
|
|
139
|
+
process.exit(2);
|
|
140
|
+
}
|
|
141
|
+
assertValidStructuredCommentType(values.marker);
|
|
142
|
+
const { buffered } = await postCommentOrBuffer({
|
|
143
|
+
provider,
|
|
144
|
+
ticketId,
|
|
145
|
+
marker: values.marker,
|
|
146
|
+
body,
|
|
147
|
+
headless: true,
|
|
148
|
+
outboxPath: outboxPathFor(epicId, config),
|
|
149
|
+
});
|
|
150
|
+
process.stdout.write(
|
|
151
|
+
`${JSON.stringify({ success: true, ticketId, type: values.marker, buffered })}\n`,
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
118
156
|
const envelope = await runPostStructuredComment({
|
|
119
157
|
ticketId,
|
|
120
158
|
type: values.marker,
|