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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* are batched per-Story and surfaced through the same error envelope the
|
|
9
9
|
* decompose loop already uses.
|
|
10
10
|
*
|
|
11
|
-
* Under the
|
|
11
|
+
* Under the 2-tier hierarchy (Epic → Story; Epic #3078 / #3238)
|
|
12
12
|
* the Story is the implementation unit — there is no `type::task` ticket
|
|
13
13
|
* layer — so the gate scans `type === 'story'` tickets and reads the
|
|
14
14
|
* `{ path, assumption }` entries inlined on each Story body.
|
|
@@ -255,7 +255,7 @@ function predecessorMutator(index, path, predecessors) {
|
|
|
255
255
|
* mismatches: object[] // structured payload for downstream tooling
|
|
256
256
|
* }
|
|
257
257
|
*
|
|
258
|
-
* Under the
|
|
258
|
+
* Under the 2-tier hierarchy the Story is the implementation unit, so the
|
|
259
259
|
* gate scans `type === 'story'` tickets and reads the inline
|
|
260
260
|
* `{ path, assumption }` entries on each Story body.
|
|
261
261
|
*
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* planning context tickets (PRD / Tech Spec / Acceptance Spec) linked
|
|
5
5
|
* from the Epic body's `## Planning Artifacts` section.
|
|
6
6
|
*
|
|
7
|
-
* Extracted from `/
|
|
7
|
+
* Extracted from `/deliver` Phase 7.1 prose (the `gh issue close`
|
|
8
8
|
* sequence) so the lifecycle Finalizer listener has a single async
|
|
9
9
|
* helper to call. Reuses `parseLinkedIssues` so the three planning ids
|
|
10
10
|
* are read from the same canonical body shape that `epic-plan` writes
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* request, or locates the existing one when finalize is replaying after
|
|
5
5
|
* a crash between `gh pr create` and the `pr.created` emit.
|
|
6
6
|
*
|
|
7
|
-
* Extracted from `/
|
|
7
|
+
* Extracted from `/deliver` Phase 7.1 prose so the lifecycle
|
|
8
8
|
* Finalizer listener has a single async helper that returns the
|
|
9
9
|
* `{ prNumber, url, created }` envelope it needs to compose
|
|
10
10
|
* downstream emits.
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* the same head branch: the second call short-circuits at the probe and
|
|
27
27
|
* does not attempt to create a duplicate PR. This is the AC-10
|
|
28
28
|
* idempotency contract the Finalizer relies on for cross-process
|
|
29
|
-
* re-runs of `/
|
|
29
|
+
* re-runs of `/deliver`.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import { spawnSync } from 'node:child_process';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `[skip ci]` markers (and the four variant spellings GitHub honours)
|
|
5
5
|
* from a text payload.
|
|
6
6
|
*
|
|
7
|
-
* Story #3165: `/
|
|
7
|
+
* Story #3165: `/deliver` Phase 7 opens a PR squashed into `main`
|
|
8
8
|
* via `gh pr merge --squash`. Story commits legitimately carry
|
|
9
9
|
* `[skip ci]` markers under `delivery.ci.skipForStoryPushes: true` to
|
|
10
10
|
* keep CI from firing on the wave's intermediate pushes. When GitHub
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* - **Operator candidates** — each surface supplies its own ordered
|
|
21
21
|
* candidate list (e.g. `--as` flag → `github.operatorHandle` →
|
|
22
|
-
* `git user.email` for `/
|
|
22
|
+
* `git user.email` for `/deliver`; bare `operatorHandle` for the
|
|
23
23
|
* plan/standalone paths).
|
|
24
24
|
* - **Missing-handle behaviour** — `'null'` (return null; the caller fails
|
|
25
25
|
* closed at acquire time) vs `'throw'` (refuse immediately with surface
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* message via `renderRefusal(result, ticketId)`.
|
|
32
32
|
* - **Liveness anchoring** — the plan/standalone paths have no heartbeat
|
|
33
33
|
* ledger, so they anchor `heartbeatAt` to `now` (fail-closed: every
|
|
34
|
-
* foreign claim reads live). `/
|
|
34
|
+
* foreign claim reads live). `/deliver` threads a real
|
|
35
35
|
* `heartbeatAt` through from the lifecycle ledger, so it opts out of
|
|
36
36
|
* anchoring.
|
|
37
37
|
*
|
|
@@ -91,7 +91,7 @@ export function resolveOperatorFromCandidates({
|
|
|
91
91
|
* same resolved clock value so `isClaimLive` returns true for ANY foreign
|
|
92
92
|
* owner — `acquireLease` then refuses a foreign assignee unless `steal` is
|
|
93
93
|
* set, while unclaimed and self-held tickets still proceed without a write.
|
|
94
|
-
* When unset (`/
|
|
94
|
+
* When unset (`/deliver`), the caller-supplied `heartbeatAt` / `now`
|
|
95
95
|
* pass through untouched.
|
|
96
96
|
*
|
|
97
97
|
* @param {object} opts
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `lib/orchestration/wave-session.js`, which nothing imports outside
|
|
13
13
|
* tests — so every dispatched Story stayed "in-flight" forever and the
|
|
14
14
|
* idle watchdog flagged completed Stories as stalled. The host-LLM driven
|
|
15
|
-
* `/
|
|
15
|
+
* `/deliver` path closes a wave through `epic-execute-record-wave.js`,
|
|
16
16
|
* so that CLI is the correct place to emit the matching dispatch-end per
|
|
17
17
|
* recorded Story.
|
|
18
18
|
*
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* signature is deliberately narrow: only the schema-allowed fields are
|
|
26
26
|
* accepted. The earlier per-child Task id and progress counters
|
|
27
27
|
* were dropped under Epic #3078's
|
|
28
|
-
*
|
|
28
|
+
* 2-tier hard cutover — they would fail strict validation and have no
|
|
29
29
|
* meaning now that the Story is the leaf execution unit with no child
|
|
30
30
|
* tickets. The optional `operator` field (Story #3480) records the handle
|
|
31
31
|
* holding the assignee-as-lease claim; it is included only when supplied so
|
|
@@ -5,7 +5,7 @@ events and performs a single side effect. The canonical close-tail roster
|
|
|
5
5
|
— in registration order — is wired by
|
|
6
6
|
[`index.js`](./index.js) (`buildDefaultListenerChain`), the production
|
|
7
7
|
entrypoint the standalone `lifecycle-emit.js` CLI shells in for
|
|
8
|
-
`/
|
|
8
|
+
`/deliver`'s Phase 6 / 7.5 / 8 / 8.5 markdown invocations:
|
|
9
9
|
|
|
10
10
|
- `ledger-writer.js` — privileged `onEmitted` hook that lands every
|
|
11
11
|
`emitted` record on disk before any listener body runs (MUST be first).
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* This is the bus-level replay defence.
|
|
42
42
|
* 2. The `gh pr view` probe — short-circuits across process
|
|
43
43
|
* boundaries when a prior run already armed auto-merge. This is
|
|
44
|
-
* the recovery defence: `/
|
|
44
|
+
* the recovery defence: `/deliver` restarted on the same PR
|
|
45
45
|
* will see the existing arm and emit `epic.merge.armed` exactly
|
|
46
46
|
* once.
|
|
47
47
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Subscribes to:
|
|
9
9
|
* - `epic.automerge.start` (production path, Story #3901) → the
|
|
10
|
-
* `/
|
|
10
|
+
* `/deliver` Phase 8.5 boundary that the `lifecycle-emit.js`
|
|
11
11
|
* CLI actually fires. This event carries `prUrl` but NO
|
|
12
12
|
* `checkOutcomes` (Phase 8's `pr-watch-with-update.js` has already
|
|
13
13
|
* polled every required check to green before Phase 8.5 runs), so
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// .agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js
|
|
2
2
|
/**
|
|
3
3
|
* BranchCleaner — lifecycle listener that owns post-merge **branch** reap
|
|
4
|
-
* for `/
|
|
4
|
+
* for `/deliver`. Story #2398 (companion to Cleaner, which owns the
|
|
5
5
|
* temp-tree archival half of end-of-Epic cleanup).
|
|
6
6
|
*
|
|
7
7
|
* Subscribes to:
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
* level replays short-circuit.
|
|
50
50
|
* 2. The `gh pr list --head` probe + `openOrLocatePr`'s internal
|
|
51
51
|
* locate path — both defend against cross-process re-runs
|
|
52
|
-
* (`/
|
|
52
|
+
* (`/deliver` restarted on the same branch after a crash).
|
|
53
53
|
*
|
|
54
54
|
* Side-effect firewall: the listener emits on the bus, shells out to
|
|
55
55
|
* `gh`/`git` (via the helpers), and upserts the `epic-handoff`
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* mirrored this roster for an in-session runner was deleted with the
|
|
9
9
|
* dead epic-runner stratum in Story #3908; the host-LLM-drives-CLIs
|
|
10
10
|
* model reaches every close-tail listener through the `lifecycle-emit.js`
|
|
11
|
-
* CLI shells in `/
|
|
11
|
+
* CLI shells in `/deliver`'s Phase 6 / 7.5 / 8 markdown invocations,
|
|
12
12
|
* which call this builder.)
|
|
13
13
|
*
|
|
14
14
|
* Canonical roster (registration order):
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `{ epicId, prNumber, mergeCommitSha, mergedAt, pollAttempts }`.
|
|
21
21
|
* 4. If the budget is exceeded without observing a merge, return
|
|
22
22
|
* a `failed` classification with reason `budget-exceeded` and
|
|
23
|
-
* do NOT emit `epic.merge.confirmed`. The /
|
|
23
|
+
* do NOT emit `epic.merge.confirmed`. The /deliver
|
|
24
24
|
* blocker-handler flow surfaces this via `agent::blocked`.
|
|
25
25
|
*
|
|
26
26
|
* Resume contract (AC of Task #2907): the ledger is the source of
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* webhookEvents entry lifecycle event
|
|
16
16
|
* -------------------- ---------------------
|
|
17
|
-
* `epic-started` (emitted at /
|
|
17
|
+
* `epic-started` (emitted at /deliver kickoff;
|
|
18
18
|
* this listener handles `epic.snapshot.start`)
|
|
19
19
|
* `epic-blocked` `epic.blocked`
|
|
20
20
|
* `epic-complete` `epic.complete`
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Idempotency contract (AC-10): per-instance `Set<string>` of
|
|
24
24
|
* `${event}:${seqId}` keys. A repeat `(event, seqId)` short-circuits
|
|
25
25
|
* without re-polling and emits nothing. Combined with the bus-level
|
|
26
|
-
* replay defence, this is sufficient — re-running `/
|
|
26
|
+
* replay defence, this is sufficient — re-running `/deliver` after
|
|
27
27
|
* a crash will produce a NEW seqId and the listener legitimately
|
|
28
28
|
* re-runs the poll loop (which is itself idempotent: the outcome map
|
|
29
29
|
* always reflects the live GitHub state).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/orchestration/manifest-builder.js — Manifest Building Logic
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 2-tier-only producer. Reads Story tickets from `allTickets` directly,
|
|
5
5
|
* computes Story-scoped waves, and emits the `waves[].stories[]` /
|
|
6
6
|
* Story-only `storyManifest` shape. The pre-Epic-#3163 Task-tier branch
|
|
7
7
|
* has been removed; the framework no longer carries a Task-tier
|
|
@@ -48,7 +48,7 @@ function extractSectionList(body, heading) {
|
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Project a Story ticket into the inline-acceptance/verify shape required by
|
|
51
|
-
* the
|
|
51
|
+
* the 2-tier waves[].stories[] schema. Reads `## Acceptance` /
|
|
52
52
|
* `## Acceptance Criteria` and `## Verify` sections from the body.
|
|
53
53
|
*
|
|
54
54
|
* @param {object} story
|
|
@@ -95,7 +95,7 @@ function projectStoryForWave(story, epicId) {
|
|
|
95
95
|
const AGENT_DONE_LABEL = STATE_LABELS.DONE;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Build the Story-only manifest array used by the
|
|
98
|
+
* Build the Story-only manifest array used by the 2-tier hierarchy path.
|
|
99
99
|
* Reads Story tickets directly from `allTickets`; there is no Task tier
|
|
100
100
|
* to walk. Each entry exposes an empty `tasks: []` to keep downstream
|
|
101
101
|
* consumers (renderers, dispatch helpers) on a single per-Story shape
|
|
@@ -145,7 +145,7 @@ function buildStoryOnlyManifest(stories, epicId) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
|
-
* Build the wave records for a
|
|
148
|
+
* Build the wave records for a 2-tier manifest. Each wave entry exposes a
|
|
149
149
|
* `stories[]` projection (instead of the legacy `tasks[]`) so dispatch
|
|
150
150
|
* consumers can fan Story execution out wave-by-wave without ever seeing
|
|
151
151
|
* a `type::task` ticket.
|
|
@@ -200,7 +200,7 @@ export function buildManifest({
|
|
|
200
200
|
epicTitle: epic?.title ?? '',
|
|
201
201
|
executor: 'claude-code',
|
|
202
202
|
dryRun,
|
|
203
|
-
hierarchy: '
|
|
203
|
+
hierarchy: '2-tier',
|
|
204
204
|
summary: {
|
|
205
205
|
totalStories,
|
|
206
206
|
doneStories,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - "parked" — the Story is genuinely outside the manifest (carved off
|
|
10
10
|
* mid-sprint, no recut lineage). The operator should explicitly
|
|
11
11
|
* adopt it into the current Epic or defer it. Surfaced as a
|
|
12
|
-
* structured comment so `/
|
|
12
|
+
* structured comment so `/deliver` has a single checkpoint.
|
|
13
13
|
*
|
|
14
14
|
* Both categories are informational at the wave-completeness gate — they do
|
|
15
15
|
* not fail closure by themselves. The gate continues to enforce that every
|
|
@@ -81,7 +81,7 @@ export function renderParkedFollowOnsComment(epicId, classification) {
|
|
|
81
81
|
`## 🪝 Parked Follow-Ons & Recuts — Epic #${epicId}`,
|
|
82
82
|
'',
|
|
83
83
|
'Stories created under this Epic that are **not** in the frozen dispatch',
|
|
84
|
-
'manifest. Surfaced here so `/
|
|
84
|
+
'manifest. Surfaced here so `/deliver` can gate on them at the',
|
|
85
85
|
'completeness check.',
|
|
86
86
|
'',
|
|
87
87
|
`- **Recuts** (attributable to a manifest Story): ${recuts.length}`,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* plan-router — given an Epic's current labels, decide which plan-phase CLI
|
|
3
3
|
* should run next.
|
|
4
4
|
*
|
|
5
|
-
* Used by the local `/
|
|
5
|
+
* Used by the local `/plan` wrapper (chains spec → decompose).
|
|
6
6
|
*
|
|
7
7
|
* The router is intentionally stateless. Callers feed the current label set
|
|
8
8
|
* (a string array, usually from `provider.getEpic(id).labels`) and receive a
|
|
@@ -21,7 +21,7 @@ export const PLAN_PHASE_NAMES = Object.freeze({
|
|
|
21
21
|
* path used by the local wrapper; `command` is the slash-command invocation
|
|
22
22
|
* operators fire.
|
|
23
23
|
*
|
|
24
|
-
* Spec and Decompose are served by the unified `/
|
|
24
|
+
* Spec and Decompose are served by the unified `/plan` wrapper with a
|
|
25
25
|
* `--phase` flag — the phase workflows themselves live at
|
|
26
26
|
* `.agents/workflows/helpers/epic-plan-{spec,decompose}.md` and are not
|
|
27
27
|
* directly invokable slash commands.
|
|
@@ -33,20 +33,20 @@ export const PLAN_PHASE_DESCRIPTORS = Object.freeze({
|
|
|
33
33
|
[PLAN_PHASE_NAMES.SPEC]: {
|
|
34
34
|
phase: PLAN_PHASE_NAMES.SPEC,
|
|
35
35
|
script: '.agents/scripts/epic-plan-spec.js',
|
|
36
|
-
command: '/
|
|
36
|
+
command: '/plan --phase spec',
|
|
37
37
|
parkingLabel: AGENT_LABELS.REVIEW_SPEC,
|
|
38
38
|
},
|
|
39
39
|
[PLAN_PHASE_NAMES.DECOMPOSE]: {
|
|
40
40
|
phase: PLAN_PHASE_NAMES.DECOMPOSE,
|
|
41
41
|
script: '.agents/scripts/epic-plan-decompose.js',
|
|
42
|
-
command: '/
|
|
42
|
+
command: '/plan --phase decompose',
|
|
43
43
|
parkingLabel: AGENT_LABELS.READY,
|
|
44
44
|
},
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Given the Epic's current labels, pick the next plan phase to run in the
|
|
49
|
-
* local `/
|
|
49
|
+
* local `/plan` wrapper.
|
|
50
50
|
*
|
|
51
51
|
* Precedence:
|
|
52
52
|
* 1. If the Epic already carries `agent::ready`, there is nothing left to
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* phases/ticket-closure.js — post-merge ticket transition + cascade phase.
|
|
3
3
|
*
|
|
4
4
|
* Transitions the Story to `agent::done`, then runs cascade completion
|
|
5
|
-
* so any parent
|
|
5
|
+
* so any parent Epic-side rollup that is now fully resolved closes too.
|
|
6
6
|
*
|
|
7
|
-
* **
|
|
7
|
+
* **2-tier closure (Story #3127).** Under the 2-tier hierarchy a Story
|
|
8
8
|
* is the leaf unit of execution and has no child tickets — `tasks`
|
|
9
9
|
* arrives as an empty array. `batchTransitionTickets` handles the empty
|
|
10
10
|
* input cleanly (the loop trivially completes), the Story is
|
|
11
11
|
* transitioned alone, and cascade completion walks upward to
|
|
12
|
-
*
|
|
12
|
+
* parent. No branch on hierarchy mode is required here.
|
|
13
13
|
*
|
|
14
14
|
* Notifications are intentionally
|
|
15
15
|
* NOT routed through the per-ticket transitions here — `notificationPhase`
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Cache adapter for the snapshot/DAG envelope produced by
|
|
5
5
|
* `epic-deliver-preflight.js` so `epic-deliver-prepare.js` can skip the
|
|
6
|
-
* second walk of Epic →
|
|
6
|
+
* second walk of Epic → Story when the underlying Epic ticket
|
|
7
7
|
* has not drifted between the two operator invocations.
|
|
8
8
|
*
|
|
9
9
|
* Contract:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `close-validate.end` `emitted` records and groups them by `failedGate`.
|
|
7
7
|
* Returns one finding per gate that appears in **two or more distinct
|
|
8
8
|
* Stories** within the same Epic. Findings are the substrate
|
|
9
|
-
* `/
|
|
9
|
+
* `/deliver` consumes when upserting the cross-Story
|
|
10
10
|
* `recurring-failure-class` structured comment on the Epic ticket.
|
|
11
11
|
*
|
|
12
12
|
* The helper is intentionally pure: no GitHub I/O, no global state. It
|
|
@@ -328,7 +328,7 @@ function followOnBody(s) {
|
|
|
328
328
|
``,
|
|
329
329
|
`Trigger: ${trigger}.`,
|
|
330
330
|
``,
|
|
331
|
-
`Apply the meta::framework-gap label so /
|
|
331
|
+
`Apply the meta::framework-gap label so /plan Phase 0 surfaces it on the next planning pass.`,
|
|
332
332
|
].join('\n');
|
|
333
333
|
}
|
|
334
334
|
|
|
@@ -130,8 +130,8 @@ async function warnIfEpicLooksPopulated({ epicId, provider, logger }) {
|
|
|
130
130
|
* `logger` so the silent failure becomes visible. Probe failure degrades
|
|
131
131
|
* gracefully — the function never throws on the guard alone.
|
|
132
132
|
*
|
|
133
|
-
* **
|
|
134
|
-
* hierarchy (Epic →
|
|
133
|
+
* **2-tier ledgers (Story #3151, Story #3200).** Under the 2-tier
|
|
134
|
+
* hierarchy (Epic → Story; no Task-tier children), `friction`
|
|
135
135
|
* / `parked` / `recuts` / `storyPerfSummaries` are all Story-scoped, so
|
|
136
136
|
* the function continues to produce a non-empty signals report. The
|
|
137
137
|
* empty-walk guard above is **not** triggered for this shape because
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Story #1155 (Epic #1142, 5.40.0) — extracts the helper-driven
|
|
5
5
|
* `epic-retro` invocation into a callable module so the
|
|
6
|
-
* `/
|
|
6
|
+
* `/deliver` runner can fire Phase E without a separate LLM
|
|
7
7
|
* helper turn. (Story #2259, Epic #2172: the legacy deliver-runner
|
|
8
8
|
* CLI was retired once delivery moved entirely into the slash
|
|
9
|
-
* command.) The retro fires before `/
|
|
9
|
+
* command.) The retro fires before `/deliver`'s finalize step
|
|
10
10
|
* opens the PR — the operator's PR-merge is the final human gate, not
|
|
11
11
|
* the retro itself.
|
|
12
12
|
*
|
|
@@ -56,7 +56,7 @@ export { gatherRetroSignals } from './retro/phases/gather-signals.js';
|
|
|
56
56
|
/**
|
|
57
57
|
* Public: compose and post the retro structured comment on the Epic.
|
|
58
58
|
*
|
|
59
|
-
* Story #1290 (Epic #1143) — at /
|
|
59
|
+
* Story #1290 (Epic #1143) — at /deliver Phase 5, the runner invokes
|
|
60
60
|
* the self-healing checks registry with `scope: 'retro'` and
|
|
61
61
|
* `autoFix: false`. The retro is **read-only by construction**: the
|
|
62
62
|
* registry runner enforces the invariant by throwing if any caller flips
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* is enumerated).
|
|
23
23
|
* - `standard` — everything else, including absent/malformed risk envelopes
|
|
24
24
|
* and a `medium` level. Fail toward the middle, never toward
|
|
25
|
-
* `light`: an Epic that skipped `/
|
|
25
|
+
* `light`: an Epic that skipped `/plan` has no risk
|
|
26
26
|
* verdict, and treating it as `light` would under-review
|
|
27
27
|
* unjudged work while `standard` preserves today's behaviour.
|
|
28
28
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* phases/wrong-tree-guard.js — detect worktree/main-checkout edit divergence.
|
|
3
3
|
*
|
|
4
|
-
* Story #3364 — `/single-story-deliver` (and `/
|
|
4
|
+
* Story #3364 — `/single-story-deliver` (and `/deliver`) materializes a
|
|
5
5
|
* per-Story worktree and instructs the agent to `cd` into it before editing.
|
|
6
6
|
* On Windows that guidance is silently insufficient: `cd <workCwd>` steers the
|
|
7
7
|
* Bash tool's working directory, but the path-based Edit/Write tools operate on
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* spec-freshness.js — Tech Spec post-author cross-validation against the
|
|
3
3
|
* current codebase.
|
|
4
4
|
*
|
|
5
|
-
* `/
|
|
5
|
+
* `/plan` Phase 7 authors PRD + Tech Spec from documentation alone.
|
|
6
6
|
* When `project.docsContextFiles` (architecture.md, etc.) drift from
|
|
7
7
|
* the real source tree, the Architect persona happily cites modules and
|
|
8
8
|
* paths that no longer exist. The mismatch only surfaces at delivery time,
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/orchestration/spec-renderer.js — tickets.json → epic-spec.yaml
|
|
3
|
-
* projection (
|
|
3
|
+
* projection (2-tier shape, Story #4041).
|
|
4
4
|
*
|
|
5
5
|
* The decomposer (`epic-plan-decompose-author`) produces a flat array
|
|
6
|
-
* of ticket objects
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `{ epic,
|
|
11
|
-
* `
|
|
6
|
+
* of Story ticket objects keyed by stable `slug`s and linked by
|
|
7
|
+
* `depends_on`. The spec reconciler consumes the declarative
|
|
8
|
+
* structural representation defined by
|
|
9
|
+
* `.agents/schemas/epic-spec.schema.json` — a flat
|
|
10
|
+
* `{ epic, stories: [...] }` shape with Story-level `wave`,
|
|
11
|
+
* `dependsOn`, and inline `acceptance[]` / `verify[]` arrays
|
|
12
12
|
* projected from the decomposer's edges.
|
|
13
13
|
*
|
|
14
|
-
* Under the
|
|
15
|
-
* children — acceptance and verify steps live
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* "Contract Cutovers — No Shim Layer").
|
|
20
|
-
* decomposer input is rejected at index time.
|
|
14
|
+
* Under the 2-tier hierarchy (Story #4041), Stories have no Feature
|
|
15
|
+
* parent and no Task children — acceptance and verify steps live
|
|
16
|
+
* inline on the Story, and Stories attach directly to the Epic. Any
|
|
17
|
+
* `feature` or `task` ticket in the decomposer input is rejected at
|
|
18
|
+
* index time (hard cutover; see `.agents/rules/git-conventions.md` §
|
|
19
|
+
* "Contract Cutovers — No Shim Layer").
|
|
21
20
|
*
|
|
22
21
|
* `renderSpec(tickets, opts)` is the pure projection between those
|
|
23
22
|
* two shapes. It:
|
|
24
23
|
*
|
|
25
|
-
* 1. Indexes the flat array by slug
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* 1. Indexes the flat array by slug. Any unrecognised type
|
|
25
|
+
* (including the historical `feature` / `task`) raises
|
|
26
|
+
* immediately.
|
|
28
27
|
* 2. Filters Story `depends_on` edges down to inter-Story
|
|
29
28
|
* references in the same Epic.
|
|
30
29
|
* 3. Layers Stories into waves via `Graph.assignLayers` (depth in
|
|
@@ -33,9 +32,9 @@
|
|
|
33
32
|
* convention (`build-wave-dag.js` produces the same layering at
|
|
34
33
|
* dispatch time from the live GH state, so the spec's waves are
|
|
35
34
|
* observationally identical to what dispatch will compute).
|
|
36
|
-
* 4. Walks the
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* 4. Walks the Stories in decomposer-declared order, preserving
|
|
36
|
+
* the order the LLM emitted so the reconciler's diff stays
|
|
37
|
+
* human-readable.
|
|
39
38
|
* 5. Strips `agent::*` labels from every entity. The decomposer
|
|
40
39
|
* doesn't normally write them, but they can leak via reverse-
|
|
41
40
|
* bootstrap from live GH state — and the schema forbids them
|
|
@@ -162,7 +161,7 @@ function sanitizeLabels(labels) {
|
|
|
162
161
|
* Convert a decomposer body value into a spec `body` string. The
|
|
163
162
|
* decomposer schema admits two shapes for a Story body:
|
|
164
163
|
*
|
|
165
|
-
* - A short string (preferred under the
|
|
164
|
+
* - A short string (preferred under the 2-tier hierarchy).
|
|
166
165
|
* - A structured object (`{ goal, changes[], acceptance[],
|
|
167
166
|
* verify[] }`) carried over from the legacy 4-tier Task body
|
|
168
167
|
* shape, when the decomposer chooses to inline the Story's
|
|
@@ -215,16 +214,12 @@ function assignNonEmpty(target, key, value) {
|
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* (parents-before-children is guaranteed by the validator's
|
|
222
|
-
* topological sort, but we don't need that invariant here — we walk
|
|
223
|
-
* features in array order, then look up their children by parent_slug
|
|
224
|
-
* in the order the decomposer emitted them).
|
|
217
|
+
* Index the flat ticket array by slug. Returns the ordered Story slug
|
|
218
|
+
* list alongside the lookup map so the renderer can walk Stories in
|
|
219
|
+
* the decomposer's emit order.
|
|
225
220
|
*
|
|
226
|
-
* Under the
|
|
227
|
-
*
|
|
221
|
+
* Under the 2-tier hierarchy (Story #4041), only the `story` type is
|
|
222
|
+
* recognised. Any other type — including the historical `feature` /
|
|
228
223
|
* `task` — falls through to the unknown-type guard and raises
|
|
229
224
|
* immediately; there is no silent drop.
|
|
230
225
|
*
|
|
@@ -232,7 +227,6 @@ function assignNonEmpty(target, key, value) {
|
|
|
232
227
|
*/
|
|
233
228
|
function indexTickets(tickets) {
|
|
234
229
|
const bySlug = new Map();
|
|
235
|
-
const featureSlugs = [];
|
|
236
230
|
const storySlugs = [];
|
|
237
231
|
|
|
238
232
|
for (const t of tickets) {
|
|
@@ -247,15 +241,14 @@ function indexTickets(tickets) {
|
|
|
247
241
|
throw new Error(`[spec-renderer] duplicate slug "${slug}"`);
|
|
248
242
|
}
|
|
249
243
|
bySlug.set(slug, t);
|
|
250
|
-
if (t.type === '
|
|
251
|
-
else if (t.type === 'story') storySlugs.push(slug);
|
|
244
|
+
if (t.type === 'story') storySlugs.push(slug);
|
|
252
245
|
else {
|
|
253
246
|
throw new Error(
|
|
254
247
|
`[spec-renderer] ticket "${slug}" has unknown type "${t.type}"`,
|
|
255
248
|
);
|
|
256
249
|
}
|
|
257
250
|
}
|
|
258
|
-
return { bySlug,
|
|
251
|
+
return { bySlug, storySlugs };
|
|
259
252
|
}
|
|
260
253
|
|
|
261
254
|
/**
|
|
@@ -283,9 +276,8 @@ function layerStories(storySlugs, bySlug) {
|
|
|
283
276
|
* Project the decomposer ticket array into the structural spec object.
|
|
284
277
|
*
|
|
285
278
|
* @param {Array<object>} tickets — flat ticket array as emitted by the
|
|
286
|
-
* decomposer Skill (`type`
|
|
287
|
-
* `
|
|
288
|
-
* `acceptance`, `verify`).
|
|
279
|
+
* decomposer Skill (`type` = story, `slug`, `depends_on`, `title`,
|
|
280
|
+
* `body`, `labels`, `acceptance`, `verify`).
|
|
289
281
|
* @param {object} opts
|
|
290
282
|
* @param {{id: number, title: string, body?: string, labels?: string[]}} opts.epic
|
|
291
283
|
* — Epic descriptor (the decomposer doesn't emit the Epic row; it's
|
|
@@ -297,7 +289,7 @@ function layerStories(storySlugs, bySlug) {
|
|
|
297
289
|
* @param {boolean} [opts.validate=true] — when `false`, skip final
|
|
298
290
|
* schema validation (used by tests that intentionally craft invalid
|
|
299
291
|
* inputs).
|
|
300
|
-
* @returns {object} spec — `{ epic,
|
|
292
|
+
* @returns {object} spec — `{ epic, stories, gates? }` matching
|
|
301
293
|
* `.agents/schemas/epic-spec.schema.json`.
|
|
302
294
|
*/
|
|
303
295
|
function validateRenderSpecInputs(tickets, opts) {
|
|
@@ -318,18 +310,6 @@ function validateRenderSpecInputs(tickets, opts) {
|
|
|
318
310
|
}
|
|
319
311
|
}
|
|
320
312
|
|
|
321
|
-
function bucketStoriesByFeature({ storySlugs, bySlug }) {
|
|
322
|
-
const storiesByFeature = new Map();
|
|
323
|
-
for (const slug of storySlugs) {
|
|
324
|
-
const story = bySlug.get(slug);
|
|
325
|
-
const parent = story.parent_slug;
|
|
326
|
-
if (typeof parent !== 'string' || !bySlug.has(parent)) continue;
|
|
327
|
-
if (!storiesByFeature.has(parent)) storiesByFeature.set(parent, []);
|
|
328
|
-
storiesByFeature.get(parent).push(slug);
|
|
329
|
-
}
|
|
330
|
-
return storiesByFeature;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
313
|
function pickStringArray(primary, fallback) {
|
|
334
314
|
const src = Array.isArray(primary)
|
|
335
315
|
? primary
|
|
@@ -342,7 +322,7 @@ function pickStringArray(primary, fallback) {
|
|
|
342
322
|
}
|
|
343
323
|
|
|
344
324
|
function extractStoryAcceptanceVerify(story) {
|
|
345
|
-
//
|
|
325
|
+
// Stories carry inline
|
|
346
326
|
// acceptance[] / verify[] arrays directly on the ticket. The
|
|
347
327
|
// decomposer may emit these either at the top level of the ticket
|
|
348
328
|
// (preferred) or nested under a structured `body` object (legacy
|
|
@@ -380,20 +360,6 @@ function buildStoryOut({ story, layers, storySet }) {
|
|
|
380
360
|
return out;
|
|
381
361
|
}
|
|
382
362
|
|
|
383
|
-
function buildFeatureOut({ feature, storySlugs, bySlug, layers, storySet }) {
|
|
384
|
-
const stories = storySlugs.map((storySlug) =>
|
|
385
|
-
buildStoryOut({
|
|
386
|
-
story: bySlug.get(storySlug),
|
|
387
|
-
layers,
|
|
388
|
-
storySet,
|
|
389
|
-
}),
|
|
390
|
-
);
|
|
391
|
-
const out = { slug: feature.slug, title: feature.title, stories };
|
|
392
|
-
assignNonEmpty(out, 'body', renderBody(feature.body));
|
|
393
|
-
assignNonEmpty(out, 'labels', sanitizeLabels(feature.labels));
|
|
394
|
-
return out;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
363
|
function buildEpicOut(epic) {
|
|
398
364
|
const out = { id: epic.id, title: epic.title };
|
|
399
365
|
assignNonEmpty(out, 'body', renderBody(epic.body));
|
|
@@ -432,22 +398,19 @@ export function renderSpec(tickets, opts = {}) {
|
|
|
432
398
|
validateRenderSpecInputs(tickets, opts);
|
|
433
399
|
const { epic, gates, schemaPath, validate = true } = opts;
|
|
434
400
|
|
|
435
|
-
const { bySlug,
|
|
401
|
+
const { bySlug, storySlugs } = indexTickets(tickets);
|
|
436
402
|
const { layers } = layerStories(storySlugs, bySlug);
|
|
437
|
-
const storiesByFeature = bucketStoriesByFeature({ storySlugs, bySlug });
|
|
438
403
|
const storySet = new Set(storySlugs);
|
|
439
404
|
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
storySlugs: storiesByFeature.get(featureSlug) ?? [],
|
|
444
|
-
bySlug,
|
|
405
|
+
const stories = storySlugs.map((storySlug) =>
|
|
406
|
+
buildStoryOut({
|
|
407
|
+
story: bySlug.get(storySlug),
|
|
445
408
|
layers,
|
|
446
409
|
storySet,
|
|
447
410
|
}),
|
|
448
411
|
);
|
|
449
412
|
|
|
450
|
-
const spec = { epic: buildEpicOut(epic),
|
|
413
|
+
const spec = { epic: buildEpicOut(epic), stories };
|
|
451
414
|
const gatesOut = buildGatesOut(gates);
|
|
452
415
|
if (gatesOut) spec.gates = gatesOut;
|
|
453
416
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* spec-section-validator.js — Phase 7.5 Tech Spec post-authoring section gate.
|
|
3
3
|
*
|
|
4
|
-
* `/
|
|
4
|
+
* `/plan` Phase 7 authors a Tech Spec from documentation and the PRD.
|
|
5
5
|
* Phase 8.3 (Holistic Consolidation) then reconciles the draft ticket array
|
|
6
6
|
* against the Tech Spec's `## Delivery Slicing` section, which the
|
|
7
7
|
* decompose-author skill uses as the capability-boundary anchor. When the
|