mandrel 1.58.0 → 1.60.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 +100 -98
- package/.agents/docs/SDLC.md +140 -141
- package/.agents/docs/configuration.md +16 -16
- package/.agents/docs/workflows.md +7 -8
- 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/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/audit-rules.json +20 -0
- 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 +1 -1
- package/.agents/scripts/acceptance-eval.js +21 -4
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/assert-branch.js +1 -3
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/bootstrap.js +1 -1
- package/.agents/scripts/check-arch-cycles.js +360 -0
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/coverage-capture.js +24 -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 +11 -9
- package/.agents/scripts/epic-deliver-prepare.js +13 -5
- package/.agents/scripts/epic-execute-record-wave.js +5 -5
- 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 +2 -2
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/git-rebase-and-resolve.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +40 -24
- 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/baselines/kinds/coverage.js +33 -149
- package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
- package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
- package/.agents/scripts/lib/baselines/writer.js +1 -1
- 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 +1 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/close-validation/commands.js +188 -0
- package/.agents/scripts/lib/close-validation/gates.js +235 -0
- package/.agents/scripts/lib/close-validation/process.js +101 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
- package/.agents/scripts/lib/close-validation/runner.js +325 -0
- package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/quality.js +6 -6
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-resolver.js +2 -5
- 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/coverage-capture.js +147 -4
- package/.agents/scripts/lib/cpu-pool.js +14 -0
- package/.agents/scripts/lib/crap-utils.js +6 -11
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- 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/git-utils.js +24 -22
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +3 -8
- package/.agents/scripts/lib/maintainability-engine.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +4 -187
- package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
- package/.agents/scripts/lib/orchestration/code-review.js +95 -82
- 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 +14 -37
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
- 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-decompose/phases/planning-artifacts.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
- 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 +28 -8
- 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 +13 -41
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
- 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 +6 -5
- 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 +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
- 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 +8 -8
- 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/notification.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
- package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
- 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/review-providers/codex.js +5 -60
- package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
- 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/auto-refresh-runner.js +451 -503
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
- package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
- 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 +4 -35
- 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 +44 -73
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
- package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
- 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/project-root.js +17 -0
- 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-adjacency.js +76 -0
- 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 +9 -9
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/transpile.js +93 -0
- package/.agents/scripts/lib/wave-runner/tick.js +4 -153
- package/.agents/scripts/lib/workers/crap-worker.js +1 -1
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
- 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 +114 -10
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +10 -1
- package/.agents/scripts/run-tests.js +24 -4
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +13 -10
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +162 -26
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +2 -2
- 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/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +2 -2
- 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 +226 -0
- 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/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
- 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 +28 -39
- 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 +12 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/onboard.md +21 -20
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +17 -20
- package/docs/CHANGELOG.md +1149 -0
- package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
- package/lib/cli/__tests__/update-reexec.test.js +513 -0
- package/lib/cli/init.js +338 -0
- package/lib/cli/update.js +413 -52
- package/package.json +3 -1
- package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
- package/.agents/scripts/lib/close-validation.js +0 -897
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
- package/.agents/scripts/lib/task-utils.js +0 -26
- package/.agents/scripts/story-deliver-prepare.js +0 -267
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
* Extracted from `manifest-formatter.js` (Story #1849 Task #1869). The
|
|
10
10
|
* shape projection used to be inlined in the formatter; pulling it out
|
|
11
11
|
* isolates the spec → manifest projection from the Markdown renderer and
|
|
12
|
-
* lets the per-
|
|
12
|
+
* lets the per-story guard cascade live behind a single
|
|
13
13
|
* private predicate (`validateSpecShape`) so the orchestrator function's
|
|
14
14
|
* CRAP score drops below 12.
|
|
15
15
|
*
|
|
16
|
-
* Story #3413 (
|
|
16
|
+
* Story #3413 (2-tier cutover, final): the residual per-Task projection
|
|
17
17
|
* (the Task projector, the per-Story Task array, and the Task-count
|
|
18
|
-
* rollup) has been deleted. The walker (`
|
|
18
|
+
* rollup) has been deleted. The walker (`projectStories`) counts
|
|
19
19
|
* Stories directly, and `summary` carries Story-tier counts only
|
|
20
20
|
* (`totalStories` / `doneStories` / `progressPercent`), matching the
|
|
21
21
|
* canonical producer in `lib/orchestration/manifest-builder.js`.
|
|
22
22
|
*
|
|
23
23
|
* Story-level status surfaces on each `storyEntry.status` so downstream
|
|
24
|
-
* renderers read the Story's own `agent::*` label directly — the
|
|
24
|
+
* renderers read the Story's own `agent::*` label directly — the
|
|
25
25
|
* "Stories are first-class lifecycle units" invariant.
|
|
26
26
|
*
|
|
27
27
|
* No fs / network access; pure transform. Caller supplies `state` from
|
|
@@ -38,8 +38,7 @@ import { AGENT_LABELS } from '../label-constants.js';
|
|
|
38
38
|
* "is this thing iterable / object-shaped?" decisions.
|
|
39
39
|
*
|
|
40
40
|
* `level` describes which spec node we are validating:
|
|
41
|
-
* - `'
|
|
42
|
-
* - `'stories'` → a feature's `stories` array
|
|
41
|
+
* - `'stories'` → the spec-level `stories` array
|
|
43
42
|
* - `'story'` → a single Story object (must be a non-null object)
|
|
44
43
|
*
|
|
45
44
|
* Returns `true` when the node satisfies the shape contract for that
|
|
@@ -52,7 +51,6 @@ import { AGENT_LABELS } from '../label-constants.js';
|
|
|
52
51
|
*/
|
|
53
52
|
function validateSpecShape(level, value) {
|
|
54
53
|
switch (level) {
|
|
55
|
-
case 'features':
|
|
56
54
|
case 'stories':
|
|
57
55
|
return Array.isArray(value);
|
|
58
56
|
case 'story':
|
|
@@ -102,8 +100,8 @@ function buildResolvers(state) {
|
|
|
102
100
|
* Private: project a single spec Story into a manifest Story entry. The
|
|
103
101
|
* Story's status is resolved directly from the Story-level label
|
|
104
102
|
* (`state.mapping[slug].lastObservedAgentState`) and surfaces on
|
|
105
|
-
* `storyEntry.status` —
|
|
106
|
-
*
|
|
103
|
+
* `storyEntry.status` — Stories carry their own lifecycle state and are
|
|
104
|
+
* leaves with no child tickets. Caller
|
|
107
105
|
* filters non-object stories with `validateSpecShape('story', ...)`
|
|
108
106
|
* before invoking.
|
|
109
107
|
*
|
|
@@ -129,16 +127,17 @@ function projectStory(story, resolvers) {
|
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
/**
|
|
132
|
-
* Private: walk every
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
130
|
+
* Private: walk every Story in a spec and collect the per-story
|
|
131
|
+
* projections + Story-tier roll-up counters. Keeps the loop machinery
|
|
132
|
+
* out of `buildManifestFromSpec` so the entry point reads as a straight
|
|
133
|
+
* assembly of the result envelope.
|
|
136
134
|
*
|
|
137
|
-
* Under the
|
|
138
|
-
* rollup counts Stories directly:
|
|
139
|
-
* Story and `doneStories` is the
|
|
135
|
+
* Under the 2-tier hierarchy (Story #4041) Stories are direct Epic
|
|
136
|
+
* children and leaves, so the rollup counts Stories directly:
|
|
137
|
+
* `totalStories` is every projected Story and `doneStories` is the
|
|
138
|
+
* subset carrying `agent::done`.
|
|
140
139
|
*
|
|
141
|
-
* @param {object[]}
|
|
140
|
+
* @param {object[]} stories
|
|
142
141
|
* @param {{ resolveId: Function, resolveStatus: Function }} resolvers
|
|
143
142
|
* @returns {{
|
|
144
143
|
* storyManifest: object[],
|
|
@@ -147,23 +146,18 @@ function projectStory(story, resolvers) {
|
|
|
147
146
|
* waveSet: Set<number>,
|
|
148
147
|
* }}
|
|
149
148
|
*/
|
|
150
|
-
function
|
|
149
|
+
function projectStories(stories, resolvers) {
|
|
151
150
|
const storyManifest = [];
|
|
152
151
|
let totalStories = 0;
|
|
153
152
|
let doneStories = 0;
|
|
154
153
|
const waveSet = new Set();
|
|
155
|
-
for (const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
storyManifest.push(storyEntry);
|
|
163
|
-
totalStories++;
|
|
164
|
-
if (storyEntry.status === AGENT_LABELS.DONE) doneStories++;
|
|
165
|
-
if (wave >= 0) waveSet.add(wave);
|
|
166
|
-
}
|
|
154
|
+
for (const story of stories) {
|
|
155
|
+
if (!validateSpecShape('story', story)) continue;
|
|
156
|
+
const { storyEntry, wave } = projectStory(story, resolvers);
|
|
157
|
+
storyManifest.push(storyEntry);
|
|
158
|
+
totalStories++;
|
|
159
|
+
if (storyEntry.status === AGENT_LABELS.DONE) doneStories++;
|
|
160
|
+
if (wave >= 0) waveSet.add(wave);
|
|
167
161
|
}
|
|
168
162
|
return { storyManifest, totalStories, doneStories, waveSet };
|
|
169
163
|
}
|
|
@@ -199,12 +193,12 @@ export function buildManifestFromSpec(spec, opts = {}) {
|
|
|
199
193
|
spec?.epic && typeof spec.epic.id === 'number' ? spec.epic.id : null;
|
|
200
194
|
const epicTitle =
|
|
201
195
|
spec?.epic && typeof spec.epic.title === 'string' ? spec.epic.title : '';
|
|
202
|
-
const
|
|
203
|
-
? spec.
|
|
196
|
+
const stories = validateSpecShape('stories', spec?.stories)
|
|
197
|
+
? spec.stories
|
|
204
198
|
: [];
|
|
205
199
|
|
|
206
|
-
const { storyManifest, totalStories, doneStories, waveSet } =
|
|
207
|
-
|
|
200
|
+
const { storyManifest, totalStories, doneStories, waveSet } = projectStories(
|
|
201
|
+
stories,
|
|
208
202
|
resolvers,
|
|
209
203
|
);
|
|
210
204
|
|
|
@@ -144,7 +144,6 @@ function renderManifestHeader(manifest) {
|
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
146
|
* Private: emit the Wave Summary table plus the per-wave H2 sections.
|
|
147
|
-
* Filters Feature containers out of the wave-eligible set.
|
|
148
147
|
*
|
|
149
148
|
* @param {object} manifest
|
|
150
149
|
* @returns {string[]}
|
|
@@ -155,7 +154,7 @@ function renderManifestBody(manifest) {
|
|
|
155
154
|
manifest.stories ||
|
|
156
155
|
manifest.summary?.stories ||
|
|
157
156
|
[];
|
|
158
|
-
const waveEligible = allItems
|
|
157
|
+
const waveEligible = allItems;
|
|
159
158
|
const lines = [];
|
|
160
159
|
const waveBlock = renderWaveSections(waveEligible);
|
|
161
160
|
if (waveBlock) lines.push(waveBlock);
|
|
@@ -165,7 +164,7 @@ function renderManifestBody(manifest) {
|
|
|
165
164
|
if (nestedBlock) lines.push(nestedBlock);
|
|
166
165
|
}
|
|
167
166
|
// Cross-Story concurrency hazards block — only emitted when the caller
|
|
168
|
-
// attaches `concurrencyFindings` to the manifest (i.e. `/
|
|
167
|
+
// attaches `concurrencyFindings` to the manifest (i.e. `/plan`
|
|
169
168
|
// Phase 9 dispatcher dry-run forwards the validator's findings array).
|
|
170
169
|
// Absent for live progress-reporter manifests where the block would
|
|
171
170
|
// duplicate Story-level state already shown above.
|
|
@@ -178,7 +177,7 @@ function renderManifestBody(manifest) {
|
|
|
178
177
|
|
|
179
178
|
/**
|
|
180
179
|
* Private: emit the agent-telemetry trailer (friction count + recent
|
|
181
|
-
* friction list) when the manifest carries one. Under the
|
|
180
|
+
* friction list) when the manifest carries one. Under the 2-tier
|
|
182
181
|
* hierarchy (Epic #3163) friction records are Story-scoped, so each
|
|
183
182
|
* recent-friction item is keyed by its `storyId`.
|
|
184
183
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* re-exports every name here so existing call-sites' import paths stay
|
|
9
9
|
* unchanged.
|
|
10
10
|
*
|
|
11
|
-
* Post-
|
|
11
|
+
* Post-2-tier (Story #3194 / #3413, Epic #3163): the helpers consume the
|
|
12
12
|
* Story-only manifest shape. Stories carry their lifecycle state on a
|
|
13
13
|
* top-level `status` field (the parent Story's `agent::*` label) — the
|
|
14
14
|
* old per-Story Task array, the per-Task id indirection, and the
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the only HTML the manifest emits by AC — every other section is plain
|
|
17
17
|
* Markdown.
|
|
18
18
|
*
|
|
19
|
-
* @param {number|string} epicId the Epic id used to substitute `/
|
|
19
|
+
* @param {number|string} epicId the Epic id used to substitute `/deliver` examples.
|
|
20
20
|
* @returns {string}
|
|
21
21
|
*/
|
|
22
22
|
export function renderProceduresAndLegendDetails(epicId) {
|
|
@@ -28,13 +28,13 @@ export function renderProceduresAndLegendDetails(epicId) {
|
|
|
28
28
|
lines.push('### Operating Procedures');
|
|
29
29
|
lines.push('');
|
|
30
30
|
lines.push(
|
|
31
|
-
`1. **Deliver**: Run \`/
|
|
31
|
+
`1. **Deliver**: Run \`/deliver ${epicId}\`. The runner iterates waves in order, fans Stories out in parallel via \`/deliver\`, and only pauses when the Epic flips to \`agent::blocked\`.`,
|
|
32
32
|
);
|
|
33
33
|
lines.push(
|
|
34
|
-
'2. **Resume (granular, optional)**: Re-running `/
|
|
34
|
+
'2. **Resume (granular, optional)**: Re-running `/deliver` resumes from the checkpointed wave. To re-drive a single Story, run `/deliver <storyId>`. Re-runs are checkpoint-idempotent.',
|
|
35
35
|
);
|
|
36
36
|
lines.push(
|
|
37
|
-
`3. **Close**: \`/
|
|
37
|
+
`3. **Close**: \`/deliver ${epicId}\` runs close-validation, code-review, retro, and PR-create in its tail. Operators merge the PR via the GitHub UI.`,
|
|
38
38
|
);
|
|
39
39
|
lines.push('');
|
|
40
40
|
lines.push('### Symbol legend');
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Presentation-only: emits the per-wave `## <emoji> Wave N` H2 sections
|
|
5
5
|
* (with nested per-Story H3 headings) that sit beneath the Wave Summary
|
|
6
|
-
* table in the dispatch manifest. Under the
|
|
6
|
+
* table in the dispatch manifest. Under the 2-tier hierarchy (Epic
|
|
7
7
|
* #3163) Stories are leaves with no child Task tickets, so each wave
|
|
8
8
|
* counts Stories (done/total) and renders one H3 per Story. The TOC
|
|
9
9
|
* links emitted by `renderWaveSections` jump directly into the H2
|
|
@@ -85,7 +85,7 @@ function pickWaveTail(status, waveIdx, sortedWaves, storyCount) {
|
|
|
85
85
|
/**
|
|
86
86
|
* Group Stories into per-wave buckets and accumulate per-wave Story
|
|
87
87
|
* totals. Pure helper — keeps the bookkeeping outside the main render
|
|
88
|
-
* loop. Under the
|
|
88
|
+
* loop. Under the 2-tier hierarchy (Epic #3163) Stories are leaves with
|
|
89
89
|
* no child Task tickets, so each wave's `total` / `done` counts Stories
|
|
90
90
|
* (a Story is "done" when it carries `agent::done`) — the unit
|
|
91
91
|
* `deriveWaveStatus` consumes.
|
|
@@ -122,11 +122,8 @@ export function renderNestedWaveSections(storyManifest) {
|
|
|
122
122
|
if (!validateWaveSection('storyManifest', storyManifest)) return '';
|
|
123
123
|
if (storyManifest.length === 0) return '';
|
|
124
124
|
|
|
125
|
-
const waveStories = storyManifest.filter(
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
const featureItems = storyManifest.filter(
|
|
129
|
-
(s) => validateWaveSection('story', s) && s.type === 'feature',
|
|
125
|
+
const waveStories = storyManifest.filter((s) =>
|
|
126
|
+
validateWaveSection('story', s),
|
|
130
127
|
);
|
|
131
128
|
|
|
132
129
|
const { waveGroups, waveStats } = groupStoriesByWave(waveStories);
|
|
@@ -158,22 +155,6 @@ export function renderNestedWaveSections(storyManifest) {
|
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
|
|
161
|
-
if (featureItems.length > 0) {
|
|
162
|
-
lines.push('## Feature Containers');
|
|
163
|
-
lines.push('');
|
|
164
|
-
lines.push(
|
|
165
|
-
'> Features are organizational groupings and are **not directly executable**.',
|
|
166
|
-
);
|
|
167
|
-
lines.push('> Execute the Stories within each Feature instead.');
|
|
168
|
-
lines.push('');
|
|
169
|
-
lines.push('| Feature | Title |');
|
|
170
|
-
lines.push('| :--- | :--- |');
|
|
171
|
-
for (const f of featureItems) {
|
|
172
|
-
lines.push(`| #${f.storyId} | ${f.storySlug} |`);
|
|
173
|
-
}
|
|
174
|
-
lines.push('');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
158
|
return lines.join('\n');
|
|
178
159
|
}
|
|
179
160
|
|
|
@@ -139,7 +139,7 @@ export async function postParkedFollowOnsComment(manifest, provider) {
|
|
|
139
139
|
|
|
140
140
|
const storyManifest = manifest.storyManifest ?? [];
|
|
141
141
|
const manifestStoryIds = storyManifest
|
|
142
|
-
.filter((s) => s.
|
|
142
|
+
.filter((s) => s.storyId !== '__ungrouped__')
|
|
143
143
|
.map((s) => Number(s.storyId))
|
|
144
144
|
.filter((n) => Number.isFinite(n));
|
|
145
145
|
|
|
@@ -83,9 +83,7 @@ export function printStoryDispatchTable(storyManifest, opts = {}) {
|
|
|
83
83
|
const log = opts.logger?.log ?? ((line) => Logger.info(line));
|
|
84
84
|
if (!storyManifest || storyManifest.length === 0) return;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
const stories = storyManifest.filter((s) => s.type !== 'feature');
|
|
88
|
-
const features = storyManifest.filter((s) => s.type === 'feature');
|
|
86
|
+
const stories = storyManifest;
|
|
89
87
|
|
|
90
88
|
log('\n┌─────────┬──────────────────────────────────────┬──────┐');
|
|
91
89
|
log('│ 📋 STORY DISPATCH TABLE │');
|
|
@@ -106,14 +104,7 @@ export function printStoryDispatchTable(storyManifest, opts = {}) {
|
|
|
106
104
|
log('└─────────┴──────────────────────────────────────┴──────┘');
|
|
107
105
|
log('');
|
|
108
106
|
log(' 💡 Stories in the same [Wave] can be executed in parallel.');
|
|
109
|
-
log(' 💡 Use /
|
|
107
|
+
log(' 💡 Use /deliver #[Story ID] to execute a Story.');
|
|
110
108
|
|
|
111
|
-
if (features.length > 0) {
|
|
112
|
-
log('');
|
|
113
|
-
log(' 📦 Feature Containers (not directly executable):');
|
|
114
|
-
for (const f of features) {
|
|
115
|
-
log(` #${f.storyId} — ${f.storySlug}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
109
|
log('');
|
|
119
110
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/project-root.js — side-effect-free leaf module for the repository
|
|
3
|
+
* root path.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `config-resolver.js` (Story #3993) so the ~10 importers
|
|
6
|
+
* that need only the path constant no longer transitively load the
|
|
7
|
+
* stateful config subsystem (module-global caches + `.env` load side
|
|
8
|
+
* effect). `config-resolver.js` re-exports this constant, so its barrel
|
|
9
|
+
* surface is unchanged.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
// scripts/lib/ → scripts/ → .agents/ → project root
|
|
17
|
+
export const PROJECT_ROOT = path.resolve(__dirname, '../../..');
|
|
@@ -98,7 +98,7 @@ export const EVENT_KINDS = Object.freeze({
|
|
|
98
98
|
// Story #3819 — per-criterion acceptance self-eval signal emitted by
|
|
99
99
|
// acceptance-eval.js. One record per Story per eval-loop terminus,
|
|
100
100
|
// carrying which acceptance items needed rework and the round count, so
|
|
101
|
-
// the retro and /
|
|
101
|
+
// the retro and /plan Phase 0 feedback fetch can see acceptance
|
|
102
102
|
// churn alongside friction/hotspot data.
|
|
103
103
|
ACCEPTANCE_EVAL: 'acceptance-eval',
|
|
104
104
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* lib/spec/index.js — public surface for the spec I/O module.
|
|
3
3
|
*
|
|
4
4
|
* Re-exports the loader (and, in Wave 1, future spec utilities) so
|
|
5
|
-
* downstream consumers — the reconciler, the rewritten /
|
|
5
|
+
* downstream consumers — the reconciler, the rewritten /plan, the
|
|
6
6
|
* wave-runner — can `import * from '../lib/spec/index.js'` without
|
|
7
7
|
* reaching into individual submodules.
|
|
8
8
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - `temp/epic-<epic-id>/<epic-id>.state.json` — slug→issue mapping (observed)
|
|
9
9
|
*
|
|
10
10
|
* Both files live under the per-Epic ephemeral tree `temp/epic-<id>/`
|
|
11
|
-
* (already gitignored) so /
|
|
11
|
+
* (already gitignored) so /plan reruns don't churn a tracked path
|
|
12
12
|
* and so concurrent Epics never collide on a single shared directory.
|
|
13
13
|
* Tests inject `opts.epicsDir` to point at a sandbox; the default for
|
|
14
14
|
* production callers is derived from `lib/config/temp-paths.js#epicTempDir`.
|
|
@@ -382,7 +382,7 @@ export function writeState(epicId, state, opts = {}) {
|
|
|
382
382
|
* the schema from the file itself.
|
|
383
383
|
*
|
|
384
384
|
* Story #1498 / Task #1525 introduced this writer so the rewritten
|
|
385
|
-
* `/
|
|
385
|
+
* `/plan` halves can persist the spec from the decomposer's
|
|
386
386
|
* ticket-array projection (`renderSpec`) without reaching into raw
|
|
387
387
|
* `js-yaml` calls scattered across the planning scripts.
|
|
388
388
|
*
|
|
@@ -108,7 +108,7 @@ export function sha256Hex(input) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
* Hash a spec entry (a
|
|
111
|
+
* Hash a spec entry (a Story object) deterministically.
|
|
112
112
|
* The entry is canonicalised (recursive key-sort), serialised, and
|
|
113
113
|
* sha256'd. Equivalent entries (any key order, equivalent nested order
|
|
114
114
|
* for the object-valued fields) hash to the same digest.
|
|
@@ -126,10 +126,9 @@ export function hashSpecEntry(entry) {
|
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
128
|
* Iterate every slug-bearing entity in the spec. Yields tuples of
|
|
129
|
-
* `[slug, entry]` where `entry` is the raw object as authored in
|
|
130
|
-
* spec
|
|
131
|
-
*
|
|
132
|
-
* mapping iteration.
|
|
129
|
+
* `[slug, entry]` where `entry` is the raw Story object as authored in
|
|
130
|
+
* the spec. Order follows `spec.stories[]` — the natural read order,
|
|
131
|
+
* useful for deterministic mapping iteration.
|
|
133
132
|
*
|
|
134
133
|
* Exported for tests; the reconciler diff path uses `projectMapping`
|
|
135
134
|
* (below) rather than this generator directly.
|
|
@@ -138,17 +137,9 @@ export function hashSpecEntry(entry) {
|
|
|
138
137
|
* @returns {Generator<[string, object]>}
|
|
139
138
|
*/
|
|
140
139
|
export function* iterSpecEntries(spec) {
|
|
141
|
-
if (!spec || !Array.isArray(spec.
|
|
142
|
-
for (const
|
|
143
|
-
if (
|
|
144
|
-
if (!Array.isArray(feature?.stories)) continue;
|
|
145
|
-
for (const story of feature.stories) {
|
|
146
|
-
if (story?.slug) yield [story.slug, story];
|
|
147
|
-
if (!Array.isArray(story?.tasks)) continue;
|
|
148
|
-
for (const task of story.tasks) {
|
|
149
|
-
if (task?.slug) yield [task.slug, task];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
140
|
+
if (!spec || !Array.isArray(spec.stories)) return;
|
|
141
|
+
for (const story of spec.stories) {
|
|
142
|
+
if (story?.slug) yield [story.slug, story];
|
|
152
143
|
}
|
|
153
144
|
}
|
|
154
145
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/story-adjacency.js — the single story-level adjacency builder.
|
|
3
|
+
*
|
|
4
|
+
* All three wave-computation wrappers bottom out in the shared
|
|
5
|
+
* `lib/Graph.js` kernel (`detectCycle` / `assignLayers` /
|
|
6
|
+
* `computeWaves`), but each historically re-implemented the step that
|
|
7
|
+
* turns a list of Story records into the `Map<storyId, number[]>`
|
|
8
|
+
* adjacency the kernel consumes. This module is now the one home for
|
|
9
|
+
* that step; the consumers are:
|
|
10
|
+
*
|
|
11
|
+
* - `lib/orchestration/epic-runner/phases/build-wave-dag.js`
|
|
12
|
+
* (`buildStoryDag` → `computeWaves`)
|
|
13
|
+
* - `lib/orchestration/dispatch-pipeline.js`
|
|
14
|
+
* (`buildStoryDispatchGraph` → `computeStoryWaves`)
|
|
15
|
+
* - `stories-wave-tick.js` (`buildAdjacency` → `assignLayers`)
|
|
16
|
+
*
|
|
17
|
+
* Dependency source order (must stay aligned with manifest-builder.js so
|
|
18
|
+
* the dispatch manifest and runtime wave scheduling never disagree):
|
|
19
|
+
* 1. Canonical: `blocked by #NNN` / `depends on #NNN` parsed from the
|
|
20
|
+
* Story body via `parseBlockedBy` (the same parser the dispatcher
|
|
21
|
+
* uses).
|
|
22
|
+
* 2. Fallback: an explicit `dependencies` (ticket shape) or
|
|
23
|
+
* `dependsOn` (operator-DAG shape) array on the Story record.
|
|
24
|
+
*
|
|
25
|
+
* @module lib/story-adjacency
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { parseBlockedBy } from './dependency-parser.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build a story-level adjacency map (`Map<storyId, dependencyIds[]>`)
|
|
32
|
+
* from an ordered list of Story records.
|
|
33
|
+
*
|
|
34
|
+
* Each record contributes one adjacency entry keyed by
|
|
35
|
+
* `Number(record.id ?? record.number)`. Dependencies are the deduped
|
|
36
|
+
* union of body-parsed `blocked by` references and the record's
|
|
37
|
+
* explicit `dependencies` / `dependsOn` array, with self-edges and
|
|
38
|
+
* non-integer ids always dropped.
|
|
39
|
+
*
|
|
40
|
+
* @param {Array<{id?: number|string, number?: number, body?: string,
|
|
41
|
+
* dependencies?: Array<number|string>, dependsOn?: Array<number|string>}>} stories
|
|
42
|
+
* Story records (live ticket payloads, fixture tickets, or operator
|
|
43
|
+
* DAG nodes).
|
|
44
|
+
* @param {object} [opts]
|
|
45
|
+
* @param {boolean} [opts.dropForeign=true] When true (the default,
|
|
46
|
+
* matching the Epic-scoped wrappers), edges pointing at ids outside
|
|
47
|
+
* the supplied story set are dropped so the DAG stays closed over the
|
|
48
|
+
* scheduled set. `stories-wave-tick.js` passes `false` to preserve its
|
|
49
|
+
* historical operator-DAG contract, where a dependency on an id absent
|
|
50
|
+
* from the input still deepens the dependent's layer.
|
|
51
|
+
* @returns {Map<number, number[]>}
|
|
52
|
+
*/
|
|
53
|
+
export function buildStoryAdjacency(stories, { dropForeign = true } = {}) {
|
|
54
|
+
const records = Array.isArray(stories) ? stories : [];
|
|
55
|
+
const storyIds = new Set(records.map((s) => Number(s?.id ?? s?.number)));
|
|
56
|
+
const adjacency = new Map();
|
|
57
|
+
for (const s of records) {
|
|
58
|
+
const id = Number(s?.id ?? s?.number);
|
|
59
|
+
const fromBody = parseBlockedBy(s?.body ?? '');
|
|
60
|
+
const fromField = Array.isArray(s?.dependencies)
|
|
61
|
+
? s.dependencies.map(Number)
|
|
62
|
+
: Array.isArray(s?.dependsOn)
|
|
63
|
+
? s.dependsOn.map(Number)
|
|
64
|
+
: [];
|
|
65
|
+
const merged = [...new Set([...fromBody, ...fromField])]
|
|
66
|
+
.map(Number)
|
|
67
|
+
.filter(
|
|
68
|
+
(dep) =>
|
|
69
|
+
Number.isInteger(dep) &&
|
|
70
|
+
dep !== id &&
|
|
71
|
+
(!dropForeign || storyIds.has(dep)),
|
|
72
|
+
);
|
|
73
|
+
adjacency.set(id, merged);
|
|
74
|
+
}
|
|
75
|
+
return adjacency;
|
|
76
|
+
}
|
|
@@ -21,7 +21,7 @@ import { resolveStoryHierarchy } from '../story-lifecycle.js';
|
|
|
21
21
|
* @param {number} deps.input.storyId
|
|
22
22
|
* @param {number|null} [deps.input.recutOf]
|
|
23
23
|
* @param {boolean} [deps.input.dryRun]
|
|
24
|
-
* @returns {Promise<{ story: object, body: string, epicId: number,
|
|
24
|
+
* @returns {Promise<{ story: object, body: string, epicId: number, parentId: number|null }>}
|
|
25
25
|
*/
|
|
26
26
|
export async function resolveContext({ provider, logger, input }) {
|
|
27
27
|
const { storyId, recutOf = null, dryRun = false } = input;
|
|
@@ -43,7 +43,7 @@ export async function resolveContext({ provider, logger, input }) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
let body = story.body ?? '';
|
|
46
|
-
const { epicId,
|
|
46
|
+
const { epicId, parentId } = resolveStoryHierarchy(body);
|
|
47
47
|
|
|
48
48
|
if (!epicId) {
|
|
49
49
|
throw new Error(
|
|
@@ -88,5 +88,5 @@ export async function resolveContext({ provider, logger, input }) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
return { story, body, epicId,
|
|
91
|
+
return { story, body, epicId, parentId };
|
|
92
92
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* state-transitioner.js — Stage 6 of the story-init pipeline.
|
|
3
3
|
*
|
|
4
4
|
* Flips the Story ticket to `agent::executing` at init time. Under the
|
|
5
|
-
*
|
|
6
|
-
* lifecycle — `/
|
|
5
|
+
* 2-tier hierarchy the Story has inline acceptance and no child Task
|
|
6
|
+
* lifecycle — `/deliver` runs a single Story-implementation phase.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import {
|
|
@@ -14,7 +14,7 @@ import { fetchChildTickets } from '../story-lifecycle.js';
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Detect whether a Story body carries inline acceptance criteria, the
|
|
17
|
-
* structural signal that the Story is authored in the
|
|
17
|
+
* structural signal that the Story is authored in the 2-tier
|
|
18
18
|
* (Story-with-inline-acceptance) shape and therefore should not be expected
|
|
19
19
|
* to enumerate child Task tickets. Recognises both `## Acceptance` and
|
|
20
20
|
* `## Acceptance Criteria` headings, with at least one list bullet under
|
|
@@ -67,15 +67,15 @@ function sortTasksByDependencies(tasks) {
|
|
|
67
67
|
* @param {object} deps.input
|
|
68
68
|
* @param {number} deps.input.storyId
|
|
69
69
|
* @param {string} [deps.input.storyBody] Story body — used to detect
|
|
70
|
-
* whether the Story carries inline acceptance (
|
|
70
|
+
* whether the Story carries inline acceptance (2-tier shape) so the
|
|
71
71
|
* empty-Task-list path is treated as expected rather than as a warning.
|
|
72
72
|
*
|
|
73
73
|
* Task #3154 (Epic #3078) deleted the `planning.hierarchy` flag; the
|
|
74
|
-
*
|
|
75
|
-
* inline acceptance + zero Tasks resolves to `'
|
|
74
|
+
* 2-tier vs 4-tier mode is now derived entirely from the ticket shape —
|
|
75
|
+
* inline acceptance + zero Tasks resolves to `'2-tier'`, otherwise
|
|
76
76
|
* `'4-tier'`.
|
|
77
77
|
*
|
|
78
|
-
* @returns {Promise<{ sortedTasks: Array<object>, mode: '
|
|
78
|
+
* @returns {Promise<{ sortedTasks: Array<object>, mode: '2-tier'|'4-tier' }>}
|
|
79
79
|
*/
|
|
80
80
|
export async function buildTaskGraph({ provider, logger, input }) {
|
|
81
81
|
const { storyId, storyBody = '' } = input;
|
|
@@ -85,13 +85,13 @@ export async function buildTaskGraph({ provider, logger, input }) {
|
|
|
85
85
|
const tasks = await fetchChildTickets(provider, storyId);
|
|
86
86
|
|
|
87
87
|
const inlineAcceptance = hasInlineAcceptance(storyBody);
|
|
88
|
-
const mode = tasks.length === 0 && inlineAcceptance ? '
|
|
88
|
+
const mode = tasks.length === 0 && inlineAcceptance ? '2-tier' : '4-tier';
|
|
89
89
|
|
|
90
90
|
if (tasks.length === 0) {
|
|
91
91
|
if (inlineAcceptance) {
|
|
92
92
|
progress(
|
|
93
93
|
'TASKS',
|
|
94
|
-
`Story #${storyId} has inline acceptance — no child Tasks expected (
|
|
94
|
+
`Story #${storyId} has inline acceptance — no child Tasks expected (2-tier shape).`,
|
|
95
95
|
);
|
|
96
96
|
} else {
|
|
97
97
|
warn(
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* (branch bootstrap, merge, cascade, notifications). Expanding this module to
|
|
11
11
|
* cover those would over-abstract — they are genuinely different concerns.
|
|
12
12
|
*
|
|
13
|
-
* Under the
|
|
13
|
+
* Under the 2-tier hierarchy (Epic #3078) Stories have no child tickets, so
|
|
14
14
|
* `fetchChildTickets` typically returns `[]` — but the helper remains in
|
|
15
15
|
* place so legacy callers still resolve the Story's direct children cleanly.
|
|
16
16
|
*/
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
* Parse the `Epic: #N` and `parent: #N` references from a Story body.
|
|
25
25
|
*
|
|
26
26
|
* @param {string} body Raw Story body Markdown.
|
|
27
|
-
* @returns {{ epicId: number|null,
|
|
27
|
+
* @returns {{ epicId: number|null, parentId: number|null }}
|
|
28
28
|
*/
|
|
29
29
|
export function resolveStoryHierarchy(body) {
|
|
30
30
|
const source = body ?? '';
|
|
@@ -32,25 +32,25 @@ export function resolveStoryHierarchy(body) {
|
|
|
32
32
|
const parentMatch = source.match(/(?:^\s*parent:\s*#(\d+))/im);
|
|
33
33
|
return {
|
|
34
34
|
epicId: epicMatch ? Number.parseInt(epicMatch[1], 10) : null,
|
|
35
|
-
|
|
35
|
+
parentId: parentMatch ? Number.parseInt(parentMatch[1], 10) : null,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Fetch the Story's direct child tickets via the provider.
|
|
41
41
|
*
|
|
42
|
-
* Under the
|
|
42
|
+
* Under the 2-tier hierarchy (Epic #3078) Stories no longer enumerate
|
|
43
43
|
* child Task tickets — acceptance criteria and verification steps live
|
|
44
|
-
* inline on the Story body, and decomposers emit only Epic /
|
|
45
|
-
*
|
|
44
|
+
* inline on the Story body, and decomposers emit only Epic / Story
|
|
45
|
+
* issues. For those Stories this helper resolves to an empty
|
|
46
46
|
* array because `provider.getSubTickets(storyId)` itself returns no
|
|
47
47
|
* rows. The helper is retained as a thin pass-through so the three
|
|
48
|
-
* orchestration callers (`story-
|
|
48
|
+
* orchestration callers (`story-init` prepare, `task-graph-builder`,
|
|
49
49
|
* and `locked-pipeline`) keep a single, named seam for sub-ticket
|
|
50
50
|
* hydration that is easy to mock in tests and to instrument in the
|
|
51
51
|
* provider layer.
|
|
52
52
|
*
|
|
53
|
-
* Legacy ticket trees that were planned before the
|
|
53
|
+
* Legacy ticket trees that were planned before the 2-tier cutover may
|
|
54
54
|
* still carry sub-tickets (PRD/Tech-Spec links recorded as direct
|
|
55
55
|
* children, for example). The helper deliberately does **not** filter
|
|
56
56
|
* those out — the caller is responsible for classifying anything that
|
|
@@ -59,7 +59,7 @@ export function resolveStoryHierarchy(body) {
|
|
|
59
59
|
*
|
|
60
60
|
* @param {object} provider ITicketingProvider instance.
|
|
61
61
|
* @param {number} storyId Story ticket number to hydrate children for.
|
|
62
|
-
* @returns {Promise<object[]>} Array of child tickets (empty under
|
|
62
|
+
* @returns {Promise<object[]>} Array of child tickets (empty under 2-tier).
|
|
63
63
|
*/
|
|
64
64
|
export async function fetchChildTickets(provider, storyId) {
|
|
65
65
|
return provider.getSubTickets(storyId);
|