mandrel 1.59.0 → 1.61.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/README.md +86 -44
- package/.agents/docs/SDLC.md +135 -141
- package/.agents/docs/configuration.md +77 -20
- package/.agents/docs/quality-gates.md +796 -0
- package/.agents/docs/workflows.md +6 -9
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +2 -2
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/agents-bootstrap-github.js +23 -119
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +6 -6
- package/.agents/scripts/epic-deliver-prepare.js +1 -1
- package/.agents/scripts/epic-execute-record-wave.js +4 -4
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +7 -11
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +47 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +7 -9
- package/.agents/scripts/lib/bootstrap/manifest.js +21 -1
- package/.agents/scripts/lib/bootstrap/merge-methods.js +31 -16
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +32 -11
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/detect-package-manager.js +72 -0
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/errors/index.js +4 -4
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +5 -10
- package/.agents/scripts/lib/onboard/detect-stack.js +10 -10
- package/.agents/scripts/lib/onboard/init-tail.js +218 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +18 -3
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +5 -5
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +9 -25
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +26 -13
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +6 -21
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +2 -2
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +2 -2
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +5 -12
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +8 -8
- package/.agents/scripts/lib/orchestration/ticketing/state.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +2 -2
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/runtime-deps/preflight.js +6 -6
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +8 -8
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/wave-runner/tick.js +1 -1
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +5 -2
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +4 -4
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +5 -5
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +13 -16
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +1 -1
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +16 -31
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +2 -2
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/agents-sync-config.md +3 -2
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +43 -43
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +13 -13
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +11 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/plan.md +131 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +19 -39
- package/bin/mandrel.js +235 -16
- package/docs/CHANGELOG.md +1173 -0
- package/lib/cli/doctor.js +45 -3
- package/lib/cli/init.js +97 -36
- package/lib/cli/registry.js +41 -145
- package/lib/cli/sync.js +122 -23
- package/lib/cli/uninstall.js +42 -7
- package/lib/cli/update.js +524 -210
- package/lib/cli/version-helpers.js +59 -0
- package/package.json +7 -6
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/workflows/onboard.md +0 -208
- package/lib/cli/__tests__/migrate.test.js +0 -268
- package/lib/cli/__tests__/sync-local-zone.test.js +0 -247
- package/lib/cli/__tests__/sync.test.js +0 -372
- package/lib/cli/__tests__/update-major.test.js +0 -217
- package/lib/cli/__tests__/update.test.js +0 -696
- package/lib/cli/__tests__/version-check.test.js +0 -398
- package/lib/migrations/__tests__/index.test.js +0 -216
|
@@ -2,7 +2,6 @@ import { LIMITS_DEFAULTS } from '../config/limits.js';
|
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_TASK_SIZING,
|
|
4
4
|
DELIVERABLE_GRANULARITY_GUIDANCE,
|
|
5
|
-
SOFT_STORIES_PER_FEATURE,
|
|
6
5
|
} from '../orchestration/ticket-validator-sizing.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -13,22 +12,23 @@ import {
|
|
|
13
12
|
* resolved value; importing it here means a fallback path (no caller-supplied
|
|
14
13
|
* value) still tracks the framework default in `lib/config/limits.js`.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* asks the planner to
|
|
15
|
+
* 2-tier is the only published hierarchy after Story #4041 removed the
|
|
16
|
+
* Feature tier: the prompt emits Stories only (direct Epic children) and
|
|
17
|
+
* asks the planner to carry acceptance/verify as top-level ticket arrays.
|
|
19
18
|
*/
|
|
20
19
|
export function renderDecomposerSystemPrompt({
|
|
21
20
|
maxTickets = LIMITS_DEFAULTS.maxTickets,
|
|
22
21
|
} = {}) {
|
|
23
|
-
return
|
|
22
|
+
return render2TierPrompt({ maxTickets });
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
|
-
*
|
|
28
|
-
* layer. Acceptance criteria and verification commands live inline
|
|
29
|
-
* Story body so the executing agent has everything it needs in one
|
|
26
|
+
* 2-tier prompt (Story #4041). Decomposes to Stories only — no Feature and
|
|
27
|
+
* no Task layer. Acceptance criteria and verification commands live inline
|
|
28
|
+
* on the Story body so the executing agent has everything it needs in one
|
|
29
|
+
* ticket. Thematic grouping lives as prose in the Epic body / Tech Spec.
|
|
30
30
|
*/
|
|
31
|
-
function
|
|
31
|
+
function render2TierPrompt({ maxTickets }) {
|
|
32
32
|
// Sizing thresholds are sourced from the single DEFAULT_TASK_SIZING constant
|
|
33
33
|
// (ticket-validator-sizing.js) so the prompt and the validator cannot drift.
|
|
34
34
|
const { softFiles, hardFiles, maxAcceptance, softAcceptanceCount } =
|
|
@@ -40,16 +40,16 @@ function render3TierPrompt({ maxTickets }) {
|
|
|
40
40
|
const { definition: granularityDefinition, singleConsumerRule } =
|
|
41
41
|
DELIVERABLE_GRANULARITY_GUIDANCE;
|
|
42
42
|
return `You are an expert Senior Project Manager and Orchestrator.
|
|
43
|
-
Your job is to take a Product Requirements Document (PRD) and a Technical Specification and decompose them into a
|
|
43
|
+
Your job is to take a Product Requirements Document (PRD) and a Technical Specification and decompose them into a flat list of Story tickets for an AI Agent to execute.
|
|
44
44
|
|
|
45
45
|
### HIERARCHY RULES:
|
|
46
|
-
1. **
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
46
|
+
1. **Stories**: Specific user-facing or architectural user stories (e.g., "Implement JWT Token Exchange").
|
|
47
|
+
- Every Story attaches directly to the Epic — there is NO Feature tier and NO Task layer in this hierarchy.
|
|
48
|
+
- **Story-Level Execution**: Each Story will be executed end-to-end on a single branch by a single agent. Acceptance criteria and verification commands live as top-level \`acceptance[]\` / \`verify[]\` arrays on the Story ticket (see STORY BODY SCHEMA below).
|
|
49
|
+
- Thematic grouping is prose in the Epic body / Tech Spec, never a ticket.
|
|
50
50
|
|
|
51
51
|
### LABEL CONVENTIONS:
|
|
52
|
-
- Every ticket must have
|
|
52
|
+
- 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.
|
|
53
53
|
- Every ticket must have a \`persona::[engineer|architect|qa-engineer|engineer-web|etc]\` label indicating WHO should execute it.
|
|
54
54
|
|
|
55
55
|
### OUTPUT FORMAT:
|
|
@@ -58,43 +58,52 @@ You MUST respond ONLY with a valid JSON array of objects. No prose, no markdown
|
|
|
58
58
|
### JSON SCHEMA:
|
|
59
59
|
[
|
|
60
60
|
{
|
|
61
|
-
"slug": "
|
|
62
|
-
"type": "
|
|
61
|
+
"slug": "hyphen-case-id",
|
|
62
|
+
"type": "story",
|
|
63
63
|
"title": "Short descriptive title",
|
|
64
|
-
"body": <string
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
64
|
+
"body": <string — see STORY BODY SCHEMA below>,
|
|
65
|
+
"acceptance": ["<testable, observable criterion>", ...],
|
|
66
|
+
"verify": ["<exact command or test path> (<tier>)", ...],
|
|
67
|
+
"labels": ["type::story", "persona::..."],
|
|
68
|
+
"depends_on": ["slug-of-blocking-dependency"] (optional array of Story slugs that block execution)
|
|
68
69
|
}
|
|
69
70
|
]
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
For Features, \`body\` is a brief string under 2 sentences. Features are navigational — the work happens at the Story level — so dense bodies waste output budget.
|
|
72
|
+
**Slug format**: \`^[a-z0-9][a-z0-9-]*\$\` — hyphen-case only. Underscores are rejected by the validator.
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
### STORY BODY SCHEMA (REQUIRED FOR EVERY STORY):
|
|
75
|
+
\`body\` MUST be a **string** — the serialized markdown produced by \`serialize()\` from \`lib/story-body/story-body.js\`. Do NOT emit \`body\` as a JSON object: an object body throws \`StoryBodyParseError\` in the reconciler (Story #3302) and is discarded by the GitHub provider, producing an empty issue body. Stories are consumed by non-interactive sub-agents that must self-verify from the Story ticket alone — so the ticket must carry everything an agent needs to execute and self-verify.
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
- **Operational/compliance work gets its own Feature.** Retention purges, audit-log sweeps, scheduled cleanups, observability emitters, and other operational/compliance chores serve operators and compliance, not end users. Place them in a dedicated \`compliance-and-observability\` or \`data-retention\` Feature — do NOT fold them into a user-facing Feature (e.g. notification flows) merely because they share a cron/scheduled execution shape with user-facing Stories.
|
|
78
|
-
- **Smell test:** if the only thing two Stories have in common is *how* they run (a scheduled job, a request interceptor, a build step) rather than *whom* they serve or *what* capability they deliver, they are mis-grouped. Re-home the operational/compliance Story into its own capability Feature.
|
|
77
|
+
The \`acceptance[]\` and \`verify[]\` arrays live at the **top level** of the Story ticket object (not nested inside \`body\`). The validator reads \`story.acceptance\` and \`story.verify\` directly — nesting them inside the body makes them invisible to the validator and the decompose is rejected.
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
The serialized \`body\` string renders these markdown sections (in order):
|
|
80
|
+
|
|
81
|
+
## Goal
|
|
82
|
+
<one sentence — why this Story exists within the Epic>
|
|
83
|
+
|
|
84
|
+
## Changes
|
|
85
|
+
- {"path": "<file path>", "assumption": "creates" | "refactors-existing" | "deletes"}
|
|
86
|
+
- ...
|
|
87
|
+
|
|
88
|
+
## Acceptance
|
|
89
|
+
- [ ] <testable, observable criterion>
|
|
90
|
+
- ...
|
|
91
|
+
|
|
92
|
+
## Verify
|
|
93
|
+
- <exact command or test path> (<tier>)
|
|
94
|
+
- ...
|
|
95
|
+
|
|
96
|
+
## References
|
|
97
|
+
- {"path": "<read-only dependency path>", "assumption": "exists"}
|
|
98
|
+
- ...
|
|
90
99
|
|
|
91
100
|
#### STORY BODY RULES:
|
|
92
101
|
|
|
93
|
-
- **goal
|
|
94
|
-
- **changes
|
|
95
|
-
- **acceptance
|
|
96
|
-
- **verify
|
|
97
|
-
- **estimated_test_files** (optional): Integer estimate of how many test files this Story creates or modifies. Omit when the number is not estimable. Informational only — it does not gate the decompose.
|
|
102
|
+
- **goal** (in body string): One sentence stating WHY this story exists within the Epic.
|
|
103
|
+
- **changes** (in body string): Each entry is an object \`{ path, assumption }\` where \`assumption\` is one of \`creates | refactors-existing | deletes\`. Acceptable path shapes include explicit files (\`src/components/Foo.tsx\`), glob patterns (\`tests/e2e/*.spec.ts\`, \`**/*.astro\`), and module identifiers that resolve to files. Use \`refactors-existing\` for in-place edits to a file already on \`main\`; \`creates\` for net-new files; \`deletes\` for removals.
|
|
104
|
+
- **acceptance** (top-level array on the ticket object): Items MUST be observable from outside the agent. Acceptable shapes: a specific command exits 0, a file exists at a given path, a snapshot test matches, a \`data-testid\` resolves under a given selector, a row count in a fixture matches. UNACCEPTABLE: "verify by reading the diff", "looks good", "matches the spec" — push these down into a \`verify\` command instead.
|
|
105
|
+
- **verify** (top-level array on the ticket object): Each entry MUST name a testing tier in parentheses, drawn from \`unit\` / \`contract\` / \`e2e\` / \`validate\`. Example: \`npm run test -- src/x.test.ts (unit)\`, \`npm run validate (validate)\`. Stories with zero verify entries SHOULD fail validation; if a story is genuinely unverifiable in isolation (e.g., a copy edit auditor will eyeball), the literal entry \`manual:<reason>\` is allowed so the absence is intentional, not lazy. Manual entries without a reason are rejected.
|
|
106
|
+
- **estimated_test_files** (optional, encoded in the \`<!-- meta: {...} -->\` comment appended to the serialized body string — NOT a top-level ticket field): Integer estimate of how many test files this Story creates or modifies. Omit when the number is not estimable. Informational only — it does not gate the decompose.
|
|
98
107
|
|
|
99
108
|
#### STORY SIZING — COHESION FIRST (the numeric ceiling is only a backstop):
|
|
100
109
|
|
|
@@ -104,10 +113,8 @@ The primary question is **cohesion, not count**: *is this one coherent change wi
|
|
|
104
113
|
|
|
105
114
|
- **One Story = one coherent change with one reason to exist.** If you cannot state that reason in a sentence, the Story is probably two Stories — or two Stories that should be one.
|
|
106
115
|
- ${singleConsumerRule}
|
|
107
|
-
- **Split independent, parallelizable work** into sibling Stories
|
|
116
|
+
- **Split independent, parallelizable work** into sibling Stories — but only when the pieces genuinely have separate reasons to exist.
|
|
108
117
|
- **Declare \`wide\` with a one-line reason when a change is legitimately broad** (a cohesive cutover that spans many files for one reason). Declaring \`wide\` lifts the hard file-width ceiling — see below.
|
|
109
|
-
- **Every Feature MUST decompose into at least TWO Stories.** A Feature with a single Story is the work of a Story, not a Feature — collapse it (drop the Feature wrapper and attach its lone Story to a sibling Feature, or merge the Feature into another). The validator HARD-rejects a Feature with fewer than two Stories.
|
|
110
|
-
- **Features typically decompose into ≤${SOFT_STORIES_PER_FEATURE} Stories; otherwise split into a sibling Feature.** A Feature stretching past ${SOFT_STORIES_PER_FEATURE} Stories is a sign the Feature scope is two features.
|
|
111
118
|
|
|
112
119
|
**Numeric backstop (validator-enforced).** These thresholds are sourced from the single \`DEFAULT_TASK_SIZING\` constant in \`ticket-validator-sizing.js\` — there is no second copy to drift:
|
|
113
120
|
|
|
@@ -117,7 +124,7 @@ The primary question is **cohesion, not count**: *is this one coherent change wi
|
|
|
117
124
|
|
|
118
125
|
#### \`wide\` DECLARATION (optional — for legitimately broad changes):
|
|
119
126
|
|
|
120
|
-
A Story whose footprint is legitimately broad declares \`
|
|
127
|
+
A Story whose footprint is legitimately broad declares \`wide\` carrying a one-line human-readable reason. Encode it in the \`<!-- meta: {"wide": {"reason": "..."}} -->\` comment that \`serialize()\` appends to the body string — it is NOT a top-level ticket field:
|
|
121
128
|
|
|
122
129
|
\`\`\`json
|
|
123
130
|
"wide": { "reason": "hard contract cutover: migrate every <X> call site in one PR" }
|
|
@@ -139,11 +146,11 @@ Declaring \`wide\` with a non-empty reason **lifts the \`hardFiles\` rejection**
|
|
|
139
146
|
- Stories that touch user-visible copy, brand assets, or visual style MUST cite the relevant section of \`docs/style-guide.md\` in \`acceptance\` (e.g. \`"acceptance": ["Hero copy matches docs/style-guide.md §3 (voice & tone)"]\`). If \`docs/style-guide.md\` does not exist or has no relevant section, state that explicitly: \`"acceptance": ["docs/style-guide.md absent — copy reviewed against the inline brand brief in PRD §2"]\`. Silence on style sourcing is a smell.
|
|
140
147
|
|
|
141
148
|
### WAVE-0 BDD SCAFFOLD STORY (features-first; emit when the Acceptance Spec has \`new\`-disposition rows):
|
|
142
|
-
The Acceptance Spec's AC table (columns \`AC ID | Outcome | Feature File | Scenario | Disposition\`) tags each row's \`Disposition\` with one of \`new | updated | unchanged\`. A \`new\` row names a \`.feature\` file + scenario that does NOT yet exist on \`main\`. The framework is features-first: implementing Stories reference those \`.feature\` paths in their \`verify[]\` lines, so the files MUST already exist when those Stories run — otherwise verification fails mid-delivery on a missing file.
|
|
149
|
+
The Acceptance Spec's AC table (columns \`AC ID | Outcome | Feature File | Scenario | Disposition\`) tags each row's \`Disposition\` with one of \`new | updated | unchanged\`. A \`new\` row names a \`.feature\` file + scenario that does NOT yet exist on \`main\`. The framework is features-first: implementing Stories reference those \`.feature\` paths in their \`verify[]\` lines, so the files MUST already exist when those Stories run — otherwise verification fails mid-delivery on a missing file. (These Gherkin \`.feature\` files are BDD artifacts, unrelated to any ticket tier.)
|
|
143
150
|
|
|
144
151
|
When the Acceptance Spec contains **one or more \`Disposition: new\` rows**, you MUST emit **exactly one** dedicated wave-0 scaffold Story whose sole job is to create the \`.feature\` files with \`@skip\`-tagged scenarios BEFORE any implementation Story runs:
|
|
145
152
|
|
|
146
|
-
- **goal**: contains the literal token \`bdd-scaffold\` (e.g. "bdd-scaffold: create the @skip-tagged feature files the implementation Stories verify against
|
|
153
|
+
- **goal**: contains the literal token \`bdd-scaffold\` (e.g. "bdd-scaffold: create the @skip-tagged feature files the implementation Stories verify against").
|
|
147
154
|
- **depends_on**: EMPTY (\`[]\`) — it runs first, in wave 0.
|
|
148
155
|
- **changes**: one entry per distinct \`.feature\` file named in a \`new\` row, each \`{ "path": "<feature file path>", "assumption": "creates" }\`.
|
|
149
156
|
- **acceptance**: MUST assert (a) every new \`.feature\` file exists, and (b) every new scenario within them carries an \`@skip\` tag. Keep these observable (a grep/validate command exits 0, a file exists at a path).
|
|
@@ -153,16 +160,16 @@ When the Acceptance Spec contains **one or more \`Disposition: new\` rows**, you
|
|
|
153
160
|
When the Acceptance Spec contains **zero \`new\`-disposition rows** (every row is \`updated\` or \`unchanged\`), do NOT emit a scaffold Story — there is nothing to create.
|
|
154
161
|
|
|
155
162
|
### SCOPE-OVERLAP FLAGGING (docs/runbook downstream of config work):
|
|
156
|
-
When a "docs update" / "runbook" / "README" Story appears downstream of an earlier Story in the same Epic whose AC already covers updating the same document (e.g. a "config + runbook" Story followed by a "docs" Story touching the same runbook), the downstream Story's deliverable may be fully absorbed by the earlier Story. Flag the risk directly in the Story \`
|
|
163
|
+
When a "docs update" / "runbook" / "README" Story appears downstream of an earlier Story in the same Epic whose AC already covers updating the same document (e.g. a "config + runbook" Story followed by a "docs" Story touching the same runbook), the downstream Story's deliverable may be fully absorbed by the earlier Story. Flag the risk directly in the Story's top-level \`acceptance\` array by appending an item of the form:
|
|
157
164
|
"Scope verification note: this story's deliverable may already be satisfied by Story #<slug-or-id>'s AC — before implementing, \`git diff main -- <path>\` against the upstream Story branch and confirm whether a substantive edit is still required, or whether only a cross-reference remains."
|
|
158
165
|
This prevents the executing agent from redoing work the upstream Story already merged.
|
|
159
166
|
|
|
160
|
-
CRITICAL: Dependencies should follow execution blockers.
|
|
161
|
-
IMPORTANT DEPENDENCY RULE:
|
|
167
|
+
CRITICAL: Dependencies should follow execution blockers. Stories attach directly to the Epic — never emit a 'parent_slug' field.
|
|
168
|
+
IMPORTANT DEPENDENCY RULE: Story-to-Story dependencies are expressed via \`depends_on\` (one Story depends_on another Story's slug). Use this to express execution ordering across the plan.
|
|
162
169
|
|
|
163
170
|
### REVIEWABILITY BUDGET (Story #2798):
|
|
164
171
|
\`maxTickets = ${maxTickets}\` is a **reviewability budget**, not a hard authoring cap. It marks the count of tickets a human operator can comfortably review in one planning pass; emitting more than this overflows the operator's review window. Default behaviour:
|
|
165
172
|
- **Stay at or under the budget when possible.** Merge narrow, single-module stories into larger, cohesive capability stories before splitting; small Stories should merge back into siblings rather than spawn their own container.
|
|
166
|
-
- **Do NOT truncate or over-compress to fit.** If the plan genuinely needs more tickets than the budget, emit the full plan anyway and add a compact \`over_budget_rationale\`
|
|
173
|
+
- **Do NOT truncate or over-compress to fit.** If the plan genuinely needs more tickets than the budget, emit the full plan anyway and add a compact \`over_budget_rationale\` note inside the FIRST Story's \`## Goal\` section explaining (a) why the plan exceeds the budget and (b) what was already merged to keep the count down. The operator will then either accept the plan by re-running the decompose with the explicit \`--allow-over-budget\` override flag, or push back and ask for a re-scope.
|
|
167
174
|
- **Never stop mid-array.** Always emit complete JSON — partial arrays are rejected by the validator.`;
|
|
168
175
|
}
|
|
@@ -37,7 +37,7 @@ import { WaveRunnerError } from './wave-runner-error.js';
|
|
|
37
37
|
* totalWaves: number
|
|
38
38
|
*
|
|
39
39
|
* Wave grouping comes from the checkpoint's `state.plan` (the GH-derived
|
|
40
|
-
* dependency-DAG grouping originally seeded by /
|
|
40
|
+
* dependency-DAG grouping originally seeded by /plan).
|
|
41
41
|
*
|
|
42
42
|
* @typedef {object} WaveTickArgs
|
|
43
43
|
* @property {number | { id: number }} epic
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { spawnSync } from 'node:child_process';
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
+
import { detectPackageManager } from '../detect-package-manager.js';
|
|
23
24
|
|
|
24
25
|
function sleepSync(ms) {
|
|
25
26
|
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
@@ -110,10 +111,12 @@ export function selectInstallCommand(strategy, wtPath, fsLike = fs) {
|
|
|
110
111
|
if (strategy === 'pnpm-store') {
|
|
111
112
|
return { cmd: 'pnpm', args: ['install', '--frozen-lockfile'] };
|
|
112
113
|
}
|
|
113
|
-
|
|
114
|
+
// Shared lockfile probe (Story #4048 B3 — one implementation per concept).
|
|
115
|
+
const pm = detectPackageManager(wtPath, (p) => fsLike.existsSync(p)) ?? 'npm';
|
|
116
|
+
if (pm === 'pnpm') {
|
|
114
117
|
return { cmd: 'pnpm', args: ['install', '--frozen-lockfile'] };
|
|
115
118
|
}
|
|
116
|
-
if (
|
|
119
|
+
if (pm === 'yarn') {
|
|
117
120
|
return { cmd: 'yarn', args: ['install', '--frozen-lockfile'] };
|
|
118
121
|
}
|
|
119
122
|
return { cmd: 'npm', args: ['ci'] };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Thin host-loop CLI that appends a single `story.dispatch.start`
|
|
7
7
|
* NDJSON record to `temp/epic-<id>/lifecycle.ndjson`. Invoked by
|
|
8
|
-
* `/
|
|
8
|
+
* `/deliver` Phase 2 immediately BEFORE each per-Story Agent
|
|
9
9
|
* tool call so the lifecycle ledger durably records every dispatch
|
|
10
10
|
* attempt. The `wave-tick.js` reconciler (Story #2891 Task #2901)
|
|
11
11
|
* then derives `nextAction['in-flight']` from this ledger to surface
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Replaces the three single-purpose emit shims
|
|
8
8
|
* (`epic-deliver-finalize.js`, `epic-deliver-automerge.js`,
|
|
9
|
-
* `epic-deliver-cleanup.js`) that the `/
|
|
9
|
+
* `epic-deliver-cleanup.js`) that the `/deliver` workflow markdown
|
|
10
10
|
* invoked in Phase 6, 7.5, and 8. Those shims each did exactly one
|
|
11
11
|
* thing: construct a bus and emit one event. Collapsing them into a
|
|
12
12
|
* single argv-driven CLI lets the workflow stay declarative ("fire
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Story #3822 — single source of truth for the post-create board-add
|
|
5
5
|
* step. Issues created through any create path (`createTicket`,
|
|
6
|
-
* `createIssue` — which backs `/
|
|
6
|
+
* `createIssue` — which backs `/plan` persist and the `/plan`
|
|
7
7
|
* Phase 4 Epic open) must land on the configured Projects V2 board
|
|
8
8
|
* without relying on GitHub's "Auto-add to project" built-in workflow,
|
|
9
9
|
* which is off by default on fresh boards and cannot be enabled via API.
|
|
@@ -109,7 +109,7 @@ export function classifyGithubError(err) {
|
|
|
109
109
|
// callers (paginateRest, getTicket, getNativeSubIssues, …) absorb the same
|
|
110
110
|
// jittered exponential backoff on transient GitHub errors instead of
|
|
111
111
|
// bubbling a one-shot 502/429/ECONNRESET that kills a longer pipeline
|
|
112
|
-
// (e.g. the /
|
|
112
|
+
// (e.g. the /deliver Phase E retro).
|
|
113
113
|
|
|
114
114
|
export const TRANSIENT_RETRY_DEFAULTS = Object.freeze({
|
|
115
115
|
maxAttempts: 6,
|
|
@@ -57,7 +57,7 @@ export function issueToEpic(issue) {
|
|
|
57
57
|
export function subIssueNodeToTicket(node) {
|
|
58
58
|
// Story #3097 (Wave-0 additive, Epic #3078 Strategy B) — return `null`
|
|
59
59
|
// for absent sub-issue nodes instead of dereferencing properties on
|
|
60
|
-
// `null`/`undefined`. In
|
|
60
|
+
// `null`/`undefined`. In 2-tier mode a Story can legitimately have zero
|
|
61
61
|
// Task children, which surfaces as an empty / missing sub-issue node
|
|
62
62
|
// when callers iterate the GraphQL response and pass each entry through
|
|
63
63
|
// this mapper. The legacy 4-tier path also benefits — a transient
|
|
@@ -83,7 +83,7 @@ export function subIssueNodeToTicket(node) {
|
|
|
83
83
|
* any null/undefined entries. Story #3097 (Wave-0 additive, Epic #3078
|
|
84
84
|
* Strategy B) — gives callers a single Storyless-tolerant entry point so
|
|
85
85
|
* the existing per-node mappers can stay strict for the 4-tier path while
|
|
86
|
-
* the
|
|
86
|
+
* the 2-tier path (Storyless: a Story with zero child Tasks) gets a
|
|
87
87
|
* well-defined empty-array result.
|
|
88
88
|
*
|
|
89
89
|
* @param {Array<object|null|undefined>|null|undefined} nodes
|
|
@@ -42,8 +42,8 @@ import {
|
|
|
42
42
|
const SEARCH_PAGE_CAP = 10;
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* Compose the final markdown body for a created ticket. Under the
|
|
46
|
-
* hierarchy (Epic →
|
|
45
|
+
* Compose the final markdown body for a created ticket. Under the 2-tier
|
|
46
|
+
* hierarchy (Epic → Story), `body` is always a string supplied
|
|
47
47
|
* by the caller (the decomposer, the spec planner, or the reconciler-apply
|
|
48
48
|
* engine). This helper appends the canonical orchestrator footer
|
|
49
49
|
* (`parent: #<n>` / `Epic: #<m>` / `blocked by #<x>`) byte-stable with the
|
|
@@ -361,8 +361,8 @@ export class TicketGateway {
|
|
|
361
361
|
/**
|
|
362
362
|
* Create a **bare** issue — no `parent: #N` footer composition and no
|
|
363
363
|
* sub-issue link. Serves the standalone create paths that bypass
|
|
364
|
-
* `createTicket`'s Story-shaped body rendering: the `/
|
|
365
|
-
* persist step and the `/
|
|
364
|
+
* `createTicket`'s Story-shaped body rendering: the `/plan`
|
|
365
|
+
* persist step and the `/plan` Phase 4 Epic open
|
|
366
366
|
* (`openEpicFromOnePager`'s `createIssue` port).
|
|
367
367
|
*
|
|
368
368
|
* After the POST, the new issue is added to the configured Project V2
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* resync-status-column.js — re-assert the GitHub Projects v2 Status
|
|
6
6
|
* column for a ticket after auto-merge has fired (Story #2845).
|
|
7
7
|
*
|
|
8
|
-
* The `/single-story-deliver` and `/
|
|
8
|
+
* The `/single-story-deliver` and `/deliver` workflow docs call
|
|
9
9
|
* this CLI after Step 5 confirms `state: "MERGED"` so the orchestrator
|
|
10
10
|
* wins the race against the GitHub built-in `Pull request merged`
|
|
11
11
|
* workflow, which would otherwise overwrite Status to whatever value
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/* node:coverage ignore file */
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* retro-run.js — execute the `/
|
|
5
|
+
* retro-run.js — execute the `/deliver` Phase 6 retro from a CLI.
|
|
6
6
|
*
|
|
7
|
-
* Phase 6 of `/
|
|
7
|
+
* Phase 6 of `/deliver` posts the Epic retro by invoking the
|
|
8
8
|
* in-process retro module (`lib/orchestration/retro-runner.js`'s
|
|
9
9
|
* `runRetro`). That module is a library entry point — it has no CLI
|
|
10
10
|
* wrapper and hard-requires both a GitHub `provider` and a lifecycle
|
|
@@ -64,7 +64,7 @@ const tasks = [
|
|
|
64
64
|
// only the canonical `<axis>::<value>` separator from
|
|
65
65
|
// `lib/label-constants.js` appears. Closes the drift gap that
|
|
66
66
|
// let the original `type/epic` typo land at
|
|
67
|
-
// `.agents/workflows/epic
|
|
67
|
+
// `.agents/workflows/helpers/plan-epic.md:49`.
|
|
68
68
|
name: 'label-vocabulary',
|
|
69
69
|
cmd: 'node',
|
|
70
70
|
args: ['.agents/scripts/lint-label-vocabulary.js'],
|
|
@@ -139,7 +139,7 @@ export function makeGhRunner(cwd) {
|
|
|
139
139
|
export function assertDeliverableStory(story, storyId) {
|
|
140
140
|
if (!story.labels.includes(TYPE_LABELS.STORY)) {
|
|
141
141
|
throw new Error(
|
|
142
|
-
`Issue #${storyId} is not a Story (labels: ${story.labels.join(', ')}). Use /
|
|
142
|
+
`Issue #${storyId} is not a Story (labels: ${story.labels.join(', ')}). Use /deliver or /deliver for Epic-attached work.`,
|
|
143
143
|
);
|
|
144
144
|
}
|
|
145
145
|
if (story.state === 'closed') {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* stories-wave-tick.js — DAG/wave engine for the top-level /
|
|
4
|
+
* stories-wave-tick.js — DAG/wave engine for the top-level /deliver workflow.
|
|
5
5
|
*
|
|
6
6
|
* Consumes an operator-supplied dependency DAG of standalone Story IDs and
|
|
7
7
|
* emits ordered execution waves. Analogous to wave-tick.js but for standalone
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
* }
|
|
27
27
|
*
|
|
28
28
|
* The per-wave concurrency cap is resolved from the same config seam
|
|
29
|
-
* `/
|
|
29
|
+
* `/deliver` uses — `resolveConfig` + `getRunners` reading
|
|
30
30
|
* `delivery.deliverRunner.concurrencyCap` (default 3) — so a
|
|
31
31
|
* `.agentrc.local.json` override is honored. A `--concurrency <n>` CLI flag
|
|
32
32
|
* overrides the config-resolved value for that run only. This puts both the
|
|
33
|
-
* standalone (`/
|
|
33
|
+
* standalone (`/deliver`) and Epic (`/deliver`) delivery paths on
|
|
34
34
|
* one deterministic config source.
|
|
35
35
|
*
|
|
36
36
|
* On cycle detection, exits with code 2 and sets cycleError in the envelope.
|
|
@@ -149,7 +149,7 @@ export function buildAdjacency(nodes) {
|
|
|
149
149
|
/**
|
|
150
150
|
* Resolve the per-wave concurrency cap.
|
|
151
151
|
*
|
|
152
|
-
* Mirrors the `/
|
|
152
|
+
* Mirrors the `/deliver` seam (`epic-deliver-prepare.js`): resolve the
|
|
153
153
|
* project config (which deep-merges `.agentrc.local.json` over `.agentrc.json`)
|
|
154
154
|
* then read `delivery.deliverRunner.concurrencyCap` via `getRunners` (default
|
|
155
155
|
* 3). An explicit `override` (the `--concurrency <n>` CLI flag) wins over
|
|
@@ -177,7 +177,7 @@ export function resolveConcurrencyCap({ cwd, config, override } = {}) {
|
|
|
177
177
|
*
|
|
178
178
|
* Uses detectCycle from Graph.js to validate the DAG before computing
|
|
179
179
|
* layers via assignLayers. Returns the wave envelope, carrying the resolved
|
|
180
|
-
* per-wave `concurrencyCap` so the `/
|
|
180
|
+
* per-wave `concurrencyCap` so the `/deliver` workflow dispatches
|
|
181
181
|
* `min(wave.stories.length, concurrencyCap)` from a deterministic field rather
|
|
182
182
|
* than from recalled prose.
|
|
183
183
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 5. branch-initializer — materialise the story branch (single-tree
|
|
16
16
|
* checkout or isolated worktree).
|
|
17
17
|
* 6. state-transitioner — flip the Story to `agent::executing`. Under
|
|
18
|
-
* the
|
|
18
|
+
* the 2-tier hierarchy the Story has inline
|
|
19
19
|
* acceptance and no child Task lifecycle.
|
|
20
20
|
*
|
|
21
21
|
* Usage:
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* 0 — Initialization complete. Agent can start implementation.
|
|
26
26
|
* 1 — Blocked or error (details in stderr).
|
|
27
27
|
*
|
|
28
|
-
* @see .agents/workflows/
|
|
28
|
+
* @see .agents/workflows/helpers/deliver-stories.md
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
import path from 'node:path';
|
|
@@ -141,7 +141,7 @@ export async function runStoryInit({
|
|
|
141
141
|
progress('INIT', `Initializing Story #${storyId}...`);
|
|
142
142
|
|
|
143
143
|
// Stage 1 — context.
|
|
144
|
-
const { story, body, epicId,
|
|
144
|
+
const { story, body, epicId, parentId } = await resolveContext({
|
|
145
145
|
provider,
|
|
146
146
|
logger: stageLogger,
|
|
147
147
|
input: { storyId, recutOf, dryRun },
|
|
@@ -154,10 +154,7 @@ export async function runStoryInit({
|
|
|
154
154
|
input: { epicId },
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
progress(
|
|
158
|
-
'CONTEXT',
|
|
159
|
-
`Epic: #${epicId}, Feature/Parent: #${featureId ?? 'none'}`,
|
|
160
|
-
);
|
|
157
|
+
progress('CONTEXT', `Epic: #${epicId}, Parent: #${parentId ?? 'none'}`);
|
|
161
158
|
progress(
|
|
162
159
|
'CONTEXT',
|
|
163
160
|
`PRD: #${prdId ?? 'none'}, Tech Spec: #${techSpecId ?? 'none'}`,
|
|
@@ -192,8 +189,8 @@ export async function runStoryInit({
|
|
|
192
189
|
progress('BLOCKERS', '✅ All blockers resolved');
|
|
193
190
|
|
|
194
191
|
// Stage 4 — task graph. Pass the Story body so buildTaskGraph can detect
|
|
195
|
-
// the inline-acceptance
|
|
196
|
-
// hierarchy flag,
|
|
192
|
+
// the inline-acceptance 2-tier shape. After Task #3154 collapsed the
|
|
193
|
+
// hierarchy flag, 2-tier is the only supported shape.
|
|
197
194
|
const { sortedTasks, mode: hierarchyMode } = await buildTaskGraph({
|
|
198
195
|
provider,
|
|
199
196
|
logger: stageLogger,
|
|
@@ -327,7 +324,7 @@ export async function runStoryInit({
|
|
|
327
324
|
worktreeCreated,
|
|
328
325
|
installStatus,
|
|
329
326
|
sortedTasks,
|
|
330
|
-
|
|
327
|
+
parentId,
|
|
331
328
|
prdId,
|
|
332
329
|
techSpecId,
|
|
333
330
|
dryRun,
|
|
@@ -500,7 +497,7 @@ function buildStoryInitResult({
|
|
|
500
497
|
worktreeCreated,
|
|
501
498
|
installStatus,
|
|
502
499
|
sortedTasks,
|
|
503
|
-
|
|
500
|
+
parentId,
|
|
504
501
|
prdId,
|
|
505
502
|
techSpecId,
|
|
506
503
|
dryRun,
|
|
@@ -514,11 +511,11 @@ function buildStoryInitResult({
|
|
|
514
511
|
storyBranch,
|
|
515
512
|
epicBranch,
|
|
516
513
|
storyTitle: story.title,
|
|
517
|
-
// Hierarchy mode resolved by buildTaskGraph.
|
|
514
|
+
// Hierarchy mode resolved by buildTaskGraph. 2-tier is the only
|
|
518
515
|
// supported shape after Task #3154 deleted the `planning.hierarchy`
|
|
519
516
|
// flag; this is retained as a constant marker for downstream
|
|
520
517
|
// consumers and persisted artefacts.
|
|
521
|
-
hierarchy: hierarchy ?? '
|
|
518
|
+
hierarchy: hierarchy ?? '2-tier',
|
|
522
519
|
worktreeEnabled,
|
|
523
520
|
workCwd,
|
|
524
521
|
worktreeCreated,
|
|
@@ -534,7 +531,7 @@ function buildStoryInitResult({
|
|
|
534
531
|
labels: t.labels,
|
|
535
532
|
dependencies: t.dependsOn ?? parseBlockedBy(t.body ?? ''),
|
|
536
533
|
})),
|
|
537
|
-
context: {
|
|
534
|
+
context: { parentId, prdId, techSpecId },
|
|
538
535
|
dryRun,
|
|
539
536
|
};
|
|
540
537
|
}
|
|
@@ -591,10 +588,10 @@ export function renderStoryInitCommentBody(result) {
|
|
|
591
588
|
epicId: result.epicId,
|
|
592
589
|
storyBranch: result.storyBranch,
|
|
593
590
|
epicBranch: result.epicBranch,
|
|
594
|
-
// Hierarchy mode is always `'
|
|
591
|
+
// Hierarchy mode is always `'2-tier'` after Task #3154 deleted the
|
|
595
592
|
// `planning.hierarchy` flag. Persisted so resumed runs can read it
|
|
596
593
|
// without re-resolving anything in the worker.
|
|
597
|
-
hierarchy: result.hierarchy ?? '
|
|
594
|
+
hierarchy: result.hierarchy ?? '2-tier',
|
|
598
595
|
worktreeEnabled: result.worktreeEnabled,
|
|
599
596
|
workCwd: result.workCwd,
|
|
600
597
|
worktreeCreated: result.worktreeCreated,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* story-phase.js — phase snapshot + heartbeat writer (
|
|
4
|
+
* story-phase.js — phase snapshot + heartbeat writer (2-tier).
|
|
5
5
|
*
|
|
6
6
|
* Replaces the deleted per-Task progress writer from the 4-tier era
|
|
7
|
-
* (removed under #3157). `/
|
|
7
|
+
* (removed under #3157). `/deliver` calls this CLI at each Story-
|
|
8
8
|
* level phase transition (init → implementing → closing → done, or any
|
|
9
9
|
* → blocked). Each call:
|
|
10
10
|
*
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* longer posts a `story-run-progress` comment (the redundant mid-flight
|
|
15
15
|
* progress surface was deleted); the snapshot is render-only.
|
|
16
16
|
* 2. Appends one `story.heartbeat` lifecycle record to
|
|
17
|
-
* `temp/epic-<epicId>/lifecycle.ndjson` so `/
|
|
17
|
+
* `temp/epic-<epicId>/lifecycle.ndjson` so `/deliver`'s
|
|
18
18
|
* §2e Idle Watchdog (`wave-tick.js --check-idle 30`) can confirm
|
|
19
19
|
* forward progress without polling the Story comment.
|
|
20
20
|
*
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*
|
|
36
36
|
* `renderedBody` is the markdown body upserted onto the Story so the
|
|
37
37
|
* caller can relay it to chat verbatim (mirrors the contract the deleted
|
|
38
|
-
* per-Task progress writer exposed and that `/
|
|
38
|
+
* per-Task progress writer exposed and that `/deliver` Step 1 / 3
|
|
39
39
|
* already documents).
|
|
40
40
|
*/
|
|
41
41
|
|
|
@@ -175,7 +175,7 @@ async function resolveStoryBranch({ provider, storyId }) {
|
|
|
175
175
|
* lease-owner handle the SAME way the lease primitive does
|
|
176
176
|
* (`normalizeOperatorHandle(github.operatorHandle)`) and stamps it as
|
|
177
177
|
* `operator` so `latestHeartbeatForOwner({ epicId, owner })` resolves a real
|
|
178
|
-
* heartbeat — without it `isClaimLive(null)` is false and /
|
|
178
|
+
* heartbeat — without it `isClaimLive(null)` is false and /deliver
|
|
179
179
|
* silently reclaims a live foreign claim (audit #3513). The field is attached
|
|
180
180
|
* only when a handle resolves, preserving the "omit when absent" shape for
|
|
181
181
|
* repos that have not configured `github.operatorHandle`. A failed append is
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/* node:coverage ignore file */
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* story-plan.js — Local `/
|
|
5
|
+
* story-plan.js — Local `/plan` wrapper.
|
|
6
6
|
*
|
|
7
|
-
* Standalone counterpart to `/
|
|
7
|
+
* Standalone counterpart to `/plan` for Stories that do **not**
|
|
8
8
|
* attach to an Epic. The script is deliberately a thin CLI around the
|
|
9
9
|
* pure helpers in `lib/story-plan.js`:
|
|
10
10
|
*
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* GitHub. Echoes the rendered body and the `gh` argv it would
|
|
25
25
|
* have run.
|
|
26
26
|
*
|
|
27
|
-
* Mirrors the `/
|
|
27
|
+
* Mirrors the `/plan` pattern: deterministic Node I/O wrappers
|
|
28
28
|
* with HITL gating handled by the host LLM in chat. No external LLM
|
|
29
29
|
* APIs are called from this script.
|
|
30
30
|
*/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Use this from workflow markdown when the operator needs to sync a
|
|
8
8
|
* working branch with `origin/<baseBranch>` before opening a PR — for
|
|
9
|
-
* example as a pre-Phase-6 step in `/
|
|
9
|
+
* example as a pre-Phase-6 step in `/deliver` so the Epic→main PR
|
|
10
10
|
* opens with the latest `main` commits already integrated.
|
|
11
11
|
*
|
|
12
12
|
* For `/single-story-deliver`, the sync runs in-process inside
|
|
@@ -201,7 +201,7 @@ export function renderFreshnessFailureMessage(epicId) {
|
|
|
201
201
|
return (
|
|
202
202
|
`[docs-freshness] ❌ Documentation freshness gate FAILED for Epic #${epicId}.\n\n` +
|
|
203
203
|
`Update each failing file so its commit message or body references #${epicId}, ` +
|
|
204
|
-
`then re-run /
|
|
204
|
+
`then re-run /deliver.`
|
|
205
205
|
);
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Reads the `epic-run-state` checkpoint plus fresh Story labels and
|
|
8
8
|
* prints one `WaveTickResult` envelope. The slash-command operator
|
|
9
|
-
* (`/
|
|
9
|
+
* (`/deliver`) consumes the envelope to decide whether to dispatch
|
|
10
10
|
* the next wave, observe in-flight stories, or finalize the Epic.
|
|
11
11
|
*
|
|
12
12
|
* Usage:
|