mandrel 2.0.0 → 2.2.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 +59 -28
- package/.agents/agents/acceptance-critic.md +20 -9
- package/.agents/agents/story-worker.md +45 -48
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +60 -46
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +33 -57
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +17 -19
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +10 -0
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
- package/.agents/schemas/agentrc.schema.json +35 -46
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +24 -68
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/audit-suite/selector.js +323 -23
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
- package/.agents/scripts/lib/checks/index.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli-args.js +23 -2
- package/.agents/scripts/lib/close-validation/gates.js +13 -13
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
- package/.agents/scripts/lib/config/delivery-routing.js +7 -6
- package/.agents/scripts/lib/config/explain.js +10 -16
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +15 -25
- package/.agents/scripts/lib/config/quality.js +11 -14
- package/.agents/scripts/lib/config/runners.js +8 -21
- package/.agents/scripts/lib/config/temp-paths.js +18 -56
- package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
- package/.agents/scripts/lib/config-settings-schema.js +48 -22
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/duplicate-search.js +38 -7
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -189
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
- package/.agents/scripts/lib/git-utils.js +0 -14
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +0 -15
- package/.agents/scripts/lib/label-taxonomy.js +1 -12
- package/.agents/scripts/lib/observability/active-story-env.js +42 -163
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +2 -2
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
- package/.agents/scripts/lib/orchestration/change-set.js +103 -0
- package/.agents/scripts/lib/orchestration/code-review.js +70 -191
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
- package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
- package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
- package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
- package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
- package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
- package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -286
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +8 -7
- package/.agents/scripts/lib/story-body/story-body.js +81 -13
- package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/ticket-body-sections.js +0 -14
- package/.agents/scripts/lib/validation-evidence.js +31 -59
- package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/plan-context.js +38 -7
- package/.agents/scripts/plan-critics.js +203 -0
- package/.agents/scripts/plan-persist.js +145 -35
- package/.agents/scripts/plan-run-epilogue.js +83 -38
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +1 -11
- package/.agents/scripts/providers/github/mappers.js +5 -0
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -153
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/quality-preview.js +13 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +9 -10
- package/.agents/scripts/single-story-close.js +173 -18
- package/.agents/scripts/single-story-confirm-merge.js +288 -15
- package/.agents/scripts/single-story-init.js +6 -10
- package/.agents/scripts/stories-wave-tick.js +380 -53
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/update-ticket-state.js +8 -50
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
- package/.agents/skills/core/scope-triage/SKILL.md +3 -0
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/workflows/audit-architecture.md +3 -4
- package/.agents/workflows/audit-clean-code.md +4 -4
- package/.agents/workflows/audit-documentation.md +4 -5
- package/.agents/workflows/audit-lighthouse.md +8 -0
- package/.agents/workflows/audit-navigability.md +10 -0
- package/.agents/workflows/audit-performance.md +2 -3
- package/.agents/workflows/audit-quality.md +8 -9
- package/.agents/workflows/audit-security.md +1 -2
- package/.agents/workflows/audit-seo.md +10 -0
- package/.agents/workflows/audit-ux-ui.md +7 -0
- package/.agents/workflows/deliver.md +133 -45
- package/.agents/workflows/git-cleanup.md +2 -2
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
- package/.agents/workflows/helpers/code-review.md +14 -12
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +209 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +239 -19
- package/.agents/workflows/qa-assist.md +6 -6
- package/.agents/workflows/qa-explore.md +3 -3
- package/.agents/workflows/qa-run.md +1 -5
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +62 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +17 -9
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
- package/package.json +2 -2
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -53
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
- package/.agents/scripts/resolve-plan-run.js +0 -117
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/orchestration/change-set.js — the **one** Story change-set enumerator
|
|
3
|
+
* (Story #4593).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* A single Story delivery used to enumerate `git diff --name-only
|
|
8
|
+
* <base>...<head>` three to four separate times: once for the ceremony
|
|
9
|
+
* derivation in `helpers/deliver-story`, once inside `runCodeReview` (to derive
|
|
10
|
+
* the review depth), once for the Story-scope local-lens roster, and once more
|
|
11
|
+
* per fresh acceptance critic. Every one of those consumers must agree about
|
|
12
|
+
* *what changed* — ceremony level and review depth both flow from
|
|
13
|
+
* `deriveChangeLevel`, so two enumerations straddling a commit could route the
|
|
14
|
+
* same Story two different ways. Computing the set once and injecting it closes
|
|
15
|
+
* that window and drops the redundant git calls.
|
|
16
|
+
*
|
|
17
|
+
* ## Contract
|
|
18
|
+
*
|
|
19
|
+
* {@link computeChangeSet} is **total**: it never throws. A diff it cannot
|
|
20
|
+
* enumerate (git failure, missing ref, spawn error) yields
|
|
21
|
+
* `{ files: null, enumerated: false }` — the neutral "diff unknown" signal that
|
|
22
|
+
* `deriveChangeLevel` / `resolveDepth` already fail safe on (`standard` depth, a
|
|
23
|
+
* `fresh` critic). `null` is deliberately distinct from `[]`: an empty array is
|
|
24
|
+
* the *fact* that nothing changed, whereas `null` is the *absence* of evidence,
|
|
25
|
+
* and only the latter must never buy a change less checking.
|
|
26
|
+
*
|
|
27
|
+
* The returned `files` are trimmed, de-duplicated, and sorted, so two consumers
|
|
28
|
+
* comparing the same change set compare byte-identical lists. The refs the set
|
|
29
|
+
* was computed against ride along on the envelope so a downstream consumer can
|
|
30
|
+
* report (or assert) its provenance rather than re-deriving it.
|
|
31
|
+
*
|
|
32
|
+
* @typedef {{
|
|
33
|
+
* baseRef: string,
|
|
34
|
+
* headRef: string,
|
|
35
|
+
* files: string[]|null,
|
|
36
|
+
* enumerated: boolean,
|
|
37
|
+
* }} ChangeSet
|
|
38
|
+
*
|
|
39
|
+
* @typedef {typeof gitSpawn} GitSpawnFn
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { gitSpawn } from '../git-utils.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Trim, drop empties, de-duplicate, and sort raw `git diff --name-only` output.
|
|
46
|
+
* Pure.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} stdout
|
|
49
|
+
* @returns {string[]}
|
|
50
|
+
*/
|
|
51
|
+
function normalizeFileList(stdout) {
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
for (const line of stdout.split('\n')) {
|
|
54
|
+
const file = line.trim();
|
|
55
|
+
if (file.length > 0) seen.add(file);
|
|
56
|
+
}
|
|
57
|
+
return [...seen].sort();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Compute the change set for the `baseRef...headRef` diff **once**, for every
|
|
62
|
+
* consumer that needs to know what a Story touched.
|
|
63
|
+
*
|
|
64
|
+
* Total — never throws; see the module header for the `null` vs `[]` contract.
|
|
65
|
+
*
|
|
66
|
+
* @param {{
|
|
67
|
+
* baseRef: string,
|
|
68
|
+
* headRef: string,
|
|
69
|
+
* cwd?: string,
|
|
70
|
+
* gitSpawnFn?: typeof gitSpawn,
|
|
71
|
+
* }} args
|
|
72
|
+
* @returns {ChangeSet}
|
|
73
|
+
*/
|
|
74
|
+
export function computeChangeSet({
|
|
75
|
+
baseRef,
|
|
76
|
+
headRef,
|
|
77
|
+
cwd = process.cwd(),
|
|
78
|
+
gitSpawnFn = gitSpawn,
|
|
79
|
+
} = {}) {
|
|
80
|
+
const unknown = { baseRef, headRef, files: null, enumerated: false };
|
|
81
|
+
if (typeof baseRef !== 'string' || baseRef.length === 0) return unknown;
|
|
82
|
+
if (typeof headRef !== 'string' || headRef.length === 0) return unknown;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const result = gitSpawnFn(
|
|
86
|
+
cwd,
|
|
87
|
+
'diff',
|
|
88
|
+
'--name-only',
|
|
89
|
+
`${baseRef}...${headRef}`,
|
|
90
|
+
);
|
|
91
|
+
if (!result || result.status !== 0 || typeof result.stdout !== 'string') {
|
|
92
|
+
return unknown;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
baseRef,
|
|
96
|
+
headRef,
|
|
97
|
+
files: normalizeFileList(result.stdout),
|
|
98
|
+
enumerated: true,
|
|
99
|
+
};
|
|
100
|
+
} catch {
|
|
101
|
+
return unknown;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -11,12 +11,15 @@
|
|
|
11
11
|
* adapter's `runReview()` to collect a `Finding[]`, render the
|
|
12
12
|
* structured-comment body via `findings-renderer`, and post the
|
|
13
13
|
* comment through the GitHub provider here (the adapter is post-free
|
|
14
|
-
* by design).
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* by design).
|
|
15
|
+
*
|
|
16
|
+
* v2.0.0 removed the Epic tier. The Epic-scope envelope and the
|
|
17
|
+
* Epic-scoped `code-review.start` / `.end` lifecycle emits (whose schema
|
|
18
|
+
* requires `epicId`) went with it; Story scope is the only scope and the
|
|
19
|
+
* module no longer touches the lifecycle bus.
|
|
17
20
|
*
|
|
18
21
|
* Public API:
|
|
19
|
-
* - `runCodeReview({
|
|
22
|
+
* - `runCodeReview({ ticketId, headRef, provider, logger, ... })` →
|
|
20
23
|
* `{ status, severity, posted, report, halted, blockerReason }`.
|
|
21
24
|
*
|
|
22
25
|
* Behaviour:
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
* a single `native` chain entry when `delivery.codeReview.providers`
|
|
25
28
|
* is unset or empty.
|
|
26
29
|
* - Always posts the unified `verification-results` structured comment on
|
|
27
|
-
* the
|
|
30
|
+
* the target issue/PR (the adapter never posts; the orchestrator owns
|
|
28
31
|
* persistence). Story #4411 (Epic #4405) unified the former
|
|
29
32
|
* `code-review` and `audit-results` findings contracts into this one
|
|
30
33
|
* `verification-results` marker.
|
|
@@ -38,8 +41,8 @@
|
|
|
38
41
|
|
|
39
42
|
import { hasSurvivingCritical } from '../audit-suite/findings.js';
|
|
40
43
|
import { resolveConfig } from '../config-resolver.js';
|
|
41
|
-
import {
|
|
42
|
-
import { resolveDepth } from './review-depth.js';
|
|
44
|
+
import { computeChangeSet } from './change-set.js';
|
|
45
|
+
import { deriveChangeLevel, resolveDepth } from './review-depth.js';
|
|
43
46
|
import {
|
|
44
47
|
countBySeverity,
|
|
45
48
|
renderFindings,
|
|
@@ -49,84 +52,19 @@ import { upsertStructuredComment } from './ticketing.js';
|
|
|
49
52
|
|
|
50
53
|
/**
|
|
51
54
|
* Review depth tiers, ordered light → standard → deep. The depth is resolved
|
|
52
|
-
* by the shared {@link resolveDepth} resolver from
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
55
|
+
* by the shared {@link resolveDepth} resolver from two observable properties of
|
|
56
|
+
* the diff under review — whether its changed files touch a registered
|
|
57
|
+
* sensitive path ({@link deriveChangeLevel}) and their mechanical count — then
|
|
58
|
+
* threaded into the review provider's `runReview` input so a sensitive *or*
|
|
59
|
+
* wide-footprint change gets a deeper pass than a small, unremarkable one.
|
|
60
|
+
* Depth is an **input** signal only — it never changes the
|
|
57
61
|
* `{ status, severity, posted, report, halted, blockerReason }` output
|
|
58
|
-
* envelope nor the `
|
|
59
|
-
* extended by Story #3938).
|
|
62
|
+
* envelope nor the `verification-results` structured-comment body (Story #3876,
|
|
63
|
+
* extended by Story #3938; re-based off the diff by Story #4542).
|
|
60
64
|
*
|
|
61
65
|
* @typedef {import('./review-depth.js').ReviewDepth} ReviewDepth
|
|
62
66
|
*/
|
|
63
67
|
|
|
64
|
-
/**
|
|
65
|
-
* Count the files changed in the `baseRef...headRef` diff via
|
|
66
|
-
* `git diff --name-only`. Returns the file count, or `null` when the diff
|
|
67
|
-
* cannot be enumerated (git failure, missing ref). A `null` count is the
|
|
68
|
-
* neutral "width unknown" signal {@link resolveDepth} tolerates without
|
|
69
|
-
* downgrading or escalating the depth. Best-effort — never throws.
|
|
70
|
-
*
|
|
71
|
-
* @param {{ baseRef: string, headRef: string, gitSpawnFn?: typeof gitSpawn }} args
|
|
72
|
-
* @returns {number|null}
|
|
73
|
-
*/
|
|
74
|
-
function countChangedFiles({ baseRef, headRef, gitSpawnFn = gitSpawn }) {
|
|
75
|
-
try {
|
|
76
|
-
const result = gitSpawnFn(
|
|
77
|
-
process.cwd(),
|
|
78
|
-
'diff',
|
|
79
|
-
`${baseRef}...${headRef}`,
|
|
80
|
-
'--name-only',
|
|
81
|
-
);
|
|
82
|
-
if (!result || result.status !== 0 || typeof result.stdout !== 'string') {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
return result.stdout
|
|
86
|
-
.trim()
|
|
87
|
-
.split('\n')
|
|
88
|
-
.filter((line) => line.length > 0).length;
|
|
89
|
-
} catch {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Build the `code-review.end` payload from the normalized result envelope.
|
|
96
|
-
* The runner result may carry a `report` field (full markdown body) which
|
|
97
|
-
* is NOT included in the lifecycle payload: the schema's
|
|
98
|
-
* `additionalProperties: false` forbids it, and the body can carry inline
|
|
99
|
-
* severity counts that drift from the structured `severity` field. The
|
|
100
|
-
* lifecycle ledger is the structured surface; the report is GitHub's
|
|
101
|
-
* surface (posted via the structured comment).
|
|
102
|
-
*
|
|
103
|
-
* Story #2839: `epicId` is preserved verbatim in the lifecycle payload so
|
|
104
|
-
* the `code-review.end` schema (additionalProperties: false, requires
|
|
105
|
-
* `epicId`) stays unchanged. Story-scope invocations never reach this
|
|
106
|
-
* helper — `runCodeReview` short-circuits the bus emit for `scope: 'story'`
|
|
107
|
-
* because story-scope review sits outside the Epic lifecycle ledger.
|
|
108
|
-
*/
|
|
109
|
-
function buildCodeReviewEndPayload({ epicId, result, durationMs }) {
|
|
110
|
-
const payload = {
|
|
111
|
-
epicId,
|
|
112
|
-
status: result.status,
|
|
113
|
-
};
|
|
114
|
-
if (result.severity && typeof result.severity === 'object') {
|
|
115
|
-
payload.severity = {
|
|
116
|
-
critical: result.severity.critical ?? 0,
|
|
117
|
-
high: result.severity.high ?? 0,
|
|
118
|
-
medium: result.severity.medium ?? 0,
|
|
119
|
-
suggestion: result.severity.suggestion ?? 0,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (typeof result.halted === 'boolean') payload.halted = result.halted;
|
|
123
|
-
if (typeof result.posted === 'boolean') payload.posted = result.posted;
|
|
124
|
-
if (Number.isFinite(durationMs) && durationMs >= 0) {
|
|
125
|
-
payload.durationMs = Math.floor(durationMs);
|
|
126
|
-
}
|
|
127
|
-
return payload;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
68
|
/**
|
|
131
69
|
* Resolve the project base branch fallback used when a caller omits
|
|
132
70
|
* `baseRef`.
|
|
@@ -152,7 +90,6 @@ function resolveCommentTargetId(commentTargetId, fallback) {
|
|
|
152
90
|
* baseRef: string,
|
|
153
91
|
* headRef: string,
|
|
154
92
|
* commentTargetId: number,
|
|
155
|
-
* epicIdForLedger: null,
|
|
156
93
|
* }}
|
|
157
94
|
*/
|
|
158
95
|
function resolveStoryScope(opts, config) {
|
|
@@ -175,53 +112,16 @@ function resolveStoryScope(opts, config) {
|
|
|
175
112
|
opts.commentTargetId,
|
|
176
113
|
opts.ticketId,
|
|
177
114
|
),
|
|
178
|
-
epicIdForLedger: null,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Resolve the Epic-scope envelope from the parameterized
|
|
184
|
-
* `{ scope: 'epic', ticketId, baseRef, headRef, commentTargetId }` shape.
|
|
185
|
-
* `headRef` defaults to `epic/<ticketId>` and `baseRef` to the project
|
|
186
|
-
* base branch.
|
|
187
|
-
*
|
|
188
|
-
* @returns {{
|
|
189
|
-
* scope: 'epic',
|
|
190
|
-
* ticketId: number,
|
|
191
|
-
* baseRef: string,
|
|
192
|
-
* headRef: string,
|
|
193
|
-
* commentTargetId: number,
|
|
194
|
-
* epicIdForLedger: number,
|
|
195
|
-
* }}
|
|
196
|
-
*/
|
|
197
|
-
function resolveEpicScope(opts, config) {
|
|
198
|
-
if (!Number.isInteger(opts.ticketId) || opts.ticketId <= 0) {
|
|
199
|
-
throw new TypeError(
|
|
200
|
-
'runCodeReview: ticketId is required (positive integer) when scope="epic".',
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
scope: 'epic',
|
|
205
|
-
ticketId: opts.ticketId,
|
|
206
|
-
baseRef: opts.baseRef ?? resolveConfigBase(config),
|
|
207
|
-
headRef: opts.headRef ?? `epic/${opts.ticketId}`,
|
|
208
|
-
commentTargetId: resolveCommentTargetId(
|
|
209
|
-
opts.commentTargetId,
|
|
210
|
-
opts.ticketId,
|
|
211
|
-
),
|
|
212
|
-
epicIdForLedger: opts.ticketId,
|
|
213
115
|
};
|
|
214
116
|
}
|
|
215
117
|
|
|
216
118
|
/**
|
|
217
|
-
*
|
|
218
|
-
* (`{ scope, ticketId, baseRef, headRef, commentTargetId }`)
|
|
219
|
-
*
|
|
119
|
+
* Resolve the scope envelope
|
|
120
|
+
* (`{ scope, ticketId, baseRef, headRef, commentTargetId }`). v2.0.0
|
|
121
|
+
* removed the Epic tier, so `'story'` is the only scope.
|
|
220
122
|
*/
|
|
221
123
|
function resolveScopeEnvelope(opts, config) {
|
|
222
|
-
return opts
|
|
223
|
-
? resolveStoryScope(opts, config)
|
|
224
|
-
: resolveEpicScope(opts, config);
|
|
124
|
+
return resolveStoryScope(opts, config);
|
|
225
125
|
}
|
|
226
126
|
|
|
227
127
|
/**
|
|
@@ -239,38 +139,30 @@ function resolveScopeEnvelope(opts, config) {
|
|
|
239
139
|
* derived from the `Finding[]` returned by the adapter (no separate
|
|
240
140
|
* severity field on the runner result).
|
|
241
141
|
*
|
|
242
|
-
* Story #2839 (Epic #2815) —
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* caller as `halted: true`. Lifecycle bus emits are confined to
|
|
247
|
-
* `scope: 'epic'` because the `code-review.end` schema requires
|
|
248
|
-
* `epicId` and the ledger only spans Epic lifecycles.
|
|
142
|
+
* Story #2839 (Epic #2815) — the Story closer requests a Story-scope
|
|
143
|
+
* review against `main`, posts the structured findings comment to the PR
|
|
144
|
+
* (via `commentTargetId`), and surfaces critical findings to the caller
|
|
145
|
+
* as `halted: true`.
|
|
249
146
|
*
|
|
250
|
-
* Argument shape
|
|
251
|
-
* `{
|
|
252
|
-
*
|
|
253
|
-
* `
|
|
254
|
-
*
|
|
255
|
-
* For `scope === 'story'`, `commentTargetId` overrides the post
|
|
256
|
-
* target (e.g. PR number) while `ticketId` continues to label the
|
|
257
|
-
* rendered header ("Story #N").
|
|
147
|
+
* Argument shape:
|
|
148
|
+
* `{ ticketId, baseRef, headRef, [commentTargetId], provider }`
|
|
149
|
+
* `baseRef` defaults to the project base branch; `headRef` is required.
|
|
150
|
+
* `commentTargetId` overrides the post target (e.g. PR number) while
|
|
151
|
+
* `ticketId` continues to label the rendered header ("Story #N").
|
|
258
152
|
*
|
|
259
153
|
* @param {{
|
|
260
|
-
* scope?: '
|
|
154
|
+
* scope?: 'story',
|
|
261
155
|
* ticketId: number,
|
|
262
156
|
* baseRef?: string|null,
|
|
263
157
|
* headRef?: string|null,
|
|
264
158
|
* commentTargetId?: number|null,
|
|
265
159
|
* provider: object,
|
|
266
160
|
* logger?: { info?: Function, warn?: Function, error?: Function, fatal?: Function, createProgress?: Function },
|
|
267
|
-
*
|
|
161
|
+
* changedFiles?: string[]|null,
|
|
268
162
|
* changedFileCount?: number|null,
|
|
269
163
|
* storyId?: number|null,
|
|
270
|
-
* bus?: object|null,
|
|
271
|
-
* now?: () => number,
|
|
272
164
|
* reviewProvider?: { runReview: Function },
|
|
273
|
-
* gitSpawnFn?:
|
|
165
|
+
* gitSpawnFn?: import('./change-set.js').GitSpawnFn,
|
|
274
166
|
* resolveConfigFn?: typeof resolveConfig,
|
|
275
167
|
* createReviewProviderFn?: typeof createReviewProvider,
|
|
276
168
|
* upsertCommentFn?: typeof upsertStructuredComment,
|
|
@@ -307,24 +199,47 @@ function resolveProviderName(codeReviewConfig) {
|
|
|
307
199
|
return 'native';
|
|
308
200
|
}
|
|
309
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Resolve the change set the depth derivation reads (Story #4593).
|
|
204
|
+
*
|
|
205
|
+
* `opts.changedFiles` is an injection with three distinct states,
|
|
206
|
+
* and the difference is load-bearing: an **array** is the change set to use
|
|
207
|
+
* verbatim; an explicit **null** is a caller (`runStoryReviewCore`) reporting
|
|
208
|
+
* that it already tried and the diff is unenumerable — re-running git here would
|
|
209
|
+
* only fail again, so it degrades straight to the fail-safe tier; **absent**
|
|
210
|
+
* means no caller enumerated at all, so the shared {@link computeChangeSet}
|
|
211
|
+
* enumerator runs as the fallback (standalone CLI use). On the close path the
|
|
212
|
+
* spine always injects, so the diff is enumerated exactly once per delivery and
|
|
213
|
+
* this pillar can never disagree with the lens pass about what changed.
|
|
214
|
+
*/
|
|
215
|
+
function resolveInjectedChangedFiles({ opts, baseRef, headRef }) {
|
|
216
|
+
if (opts.changedFiles === undefined) {
|
|
217
|
+
return computeChangeSet({ baseRef, headRef, gitSpawnFn: opts.gitSpawnFn })
|
|
218
|
+
.files;
|
|
219
|
+
}
|
|
220
|
+
return Array.isArray(opts.changedFiles) ? opts.changedFiles : null;
|
|
221
|
+
}
|
|
222
|
+
|
|
310
223
|
/**
|
|
311
224
|
* Build the provider `runReview` input, resolving the review depth from the
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* input-only signal (light → standard → deep) and never touches the
|
|
315
|
-
* envelope or the posted comment.
|
|
316
|
-
*
|
|
225
|
+
* diff under review: its changed files derive the change level (sensitive path
|
|
226
|
+
* touched or not — Story #4542) and their count supplies the width. The depth
|
|
227
|
+
* is an input-only signal (light → standard → deep) and never touches the
|
|
228
|
+
* output envelope or the posted comment. An unenumerable diff → `standard`.
|
|
229
|
+
* Story #4075 — extracted from `runCodeReview`.
|
|
317
230
|
*/
|
|
318
231
|
function buildReviewInput({ opts, scope, ticketId, baseRef, headRef }) {
|
|
232
|
+
const changedFiles = resolveInjectedChangedFiles({ opts, baseRef, headRef });
|
|
319
233
|
const changedFileCount =
|
|
320
234
|
typeof opts.changedFileCount === 'number'
|
|
321
235
|
? opts.changedFileCount
|
|
322
|
-
:
|
|
236
|
+
: (changedFiles?.length ?? null);
|
|
323
237
|
// v2 Stage 2: review depth uses DEFAULT_DIFF_WIDTH (mechanical file count
|
|
324
238
|
// of the diff under review). It is deliberately decoupled from the
|
|
325
239
|
// planning model-capacity advisory (`DEFAULT_MODEL_CAPACITY`).
|
|
240
|
+
const { level } = deriveChangeLevel({ changedFiles });
|
|
326
241
|
const depth = resolveDepth({
|
|
327
|
-
|
|
242
|
+
derivedLevel: level,
|
|
328
243
|
changedFileCount,
|
|
329
244
|
});
|
|
330
245
|
return {
|
|
@@ -418,7 +333,7 @@ async function executeReviewPipeline({ opts, config, envelope }) {
|
|
|
418
333
|
injectedReviewProvider ?? createReviewProviderFn(codeReviewConfig);
|
|
419
334
|
|
|
420
335
|
logger?.info?.(
|
|
421
|
-
`[code-review] Running ${providerName} adapter for
|
|
336
|
+
`[code-review] Running ${providerName} adapter for Story #${ticketId} (${baseRef}...${headRef})...`,
|
|
422
337
|
);
|
|
423
338
|
|
|
424
339
|
const reviewInput = buildReviewInput({
|
|
@@ -477,49 +392,13 @@ async function executeReviewPipeline({ opts, config, envelope }) {
|
|
|
477
392
|
}
|
|
478
393
|
|
|
479
394
|
export async function runCodeReview(opts = {}) {
|
|
480
|
-
const {
|
|
395
|
+
const { resolveConfigFn = resolveConfig } = opts;
|
|
481
396
|
|
|
482
397
|
const config = resolveConfigFn();
|
|
483
398
|
const envelope = resolveScopeEnvelope(opts, config);
|
|
484
|
-
const { scope } = envelope;
|
|
485
|
-
|
|
486
|
-
// Epic-scope lifecycle ledger requires `bus`; Story-scope sits outside
|
|
487
|
-
// the Epic lifecycle so the bus is optional there. A caller without a
|
|
488
|
-
// bus on the Story path still gets the full review semantics — only the
|
|
489
|
-
// `code-review.start`/`.end` events are suppressed.
|
|
490
|
-
if (scope === 'epic' && (!bus || typeof bus.emit !== 'function')) {
|
|
491
|
-
throw new TypeError('runCodeReview: bus is required (object with emit()).');
|
|
492
|
-
}
|
|
493
|
-
const ledgerEnabled =
|
|
494
|
-
scope === 'epic' && bus && typeof bus.emit === 'function';
|
|
495
|
-
|
|
496
|
-
const startedAt = typeof now === 'function' ? now() : Date.now();
|
|
497
|
-
// Emit the matched `code-review.end` boundary; the ledger must always
|
|
498
|
-
// show a start/end pair (even on adapter throw, where `result` is the
|
|
499
|
-
// canonical `{ status: 'invalid' }`).
|
|
500
|
-
const emitEnd = async (result) => {
|
|
501
|
-
if (!ledgerEnabled) return;
|
|
502
|
-
const endedAt = typeof now === 'function' ? now() : Date.now();
|
|
503
|
-
await bus.emit(
|
|
504
|
-
'code-review.end',
|
|
505
|
-
buildCodeReviewEndPayload({
|
|
506
|
-
epicId: envelope.epicIdForLedger,
|
|
507
|
-
result,
|
|
508
|
-
durationMs: Math.max(0, endedAt - startedAt),
|
|
509
|
-
}),
|
|
510
|
-
);
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
if (ledgerEnabled) {
|
|
514
|
-
await bus.emit('code-review.start', { epicId: envelope.epicIdForLedger });
|
|
515
|
-
}
|
|
516
399
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
} catch (err) {
|
|
522
|
-
await emitEnd({ status: 'invalid' });
|
|
523
|
-
throw err;
|
|
524
|
-
}
|
|
400
|
+
// No lifecycle-bus emit: the `code-review.start` / `.end` ledger pair was
|
|
401
|
+
// Epic-scoped (its schema requires `epicId`), and v2.0.0 removed the Epic
|
|
402
|
+
// tier. Story-scope review sits outside the lifecycle ledger entirely.
|
|
403
|
+
return executeReviewPipeline({ opts, config, envelope });
|
|
525
404
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* reconcile — dispatching it is pure token spend for a no-op. This module
|
|
13
13
|
* computes that decision **deterministically**, off the same two inputs the
|
|
14
14
|
* critic itself reads (the draft array and the Epic body's Delivery Slicing
|
|
15
|
-
* table), so
|
|
15
|
+
* table), so the planning workflow can skip the sub-agent
|
|
16
16
|
* dispatch when it is provably safe to.
|
|
17
17
|
*
|
|
18
18
|
* **Fail-open by design.** Every ambiguous case — a missing or unparseable
|
|
@@ -123,7 +123,7 @@ export function parseDeliverySlicingTable(epicBody) {
|
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* True when `story` is the recognized wave-0 BDD scaffold Story — identified
|
|
126
|
-
* by the literal `bdd-scaffold` goal token the
|
|
126
|
+
* by the literal `bdd-scaffold` goal token the decomposer prompt
|
|
127
127
|
* skill's WAVE-0 BDD SCAFFOLD STORY section requires. Scaffold Stories are
|
|
128
128
|
* not a Delivery Slicing slice, so they are excluded from the count
|
|
129
129
|
* comparison — BDD-adopting consumer repos still benefit from the
|
|
@@ -145,7 +145,7 @@ function isBddScaffoldStory(story) {
|
|
|
145
145
|
*
|
|
146
146
|
* @param {object} input
|
|
147
147
|
* @param {object[]} input.draftStories - The draft `tickets.json` array
|
|
148
|
-
* (
|
|
148
|
+
* (the decomposer's output) — raw Story ticket objects with
|
|
149
149
|
* top-level `slug` / `depends_on` / `body` (serialized string).
|
|
150
150
|
* @param {string} input.epicBody - The Epic body carrying the folded Tech
|
|
151
151
|
* Spec sections (`## Delivery Slicing` onward).
|