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
|
@@ -1,119 +1,24 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { minimatch } from 'minimatch';
|
|
5
4
|
import { canonicalise as canonicalisePath } from './baselines/path-canon.js';
|
|
6
|
-
import {
|
|
7
|
-
write as writeBaselineEnvelope,
|
|
8
|
-
writeFile as writeBaselineFile,
|
|
9
|
-
} from './baselines/writer.js';
|
|
10
|
-
import { runOnPool } from './cpu-pool.js';
|
|
5
|
+
import { POOL_SERIAL_THRESHOLD, runOnPool } from './cpu-pool.js';
|
|
11
6
|
import { Logger } from './Logger.js';
|
|
12
7
|
import { calculateForFile } from './maintainability-engine.js';
|
|
13
8
|
|
|
14
|
-
const require = createRequire(import.meta.url);
|
|
15
|
-
|
|
16
9
|
const MAINTAINABILITY_WORKER_URL = new URL(
|
|
17
10
|
'./workers/maintainability-worker.js',
|
|
18
11
|
import.meta.url,
|
|
19
12
|
);
|
|
20
13
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
// fixtures (n=2 stays serial; the full repo n≈470 takes the pool path).
|
|
25
|
-
const SERIAL_THRESHOLD = 8;
|
|
14
|
+
// Pool-vs-serial cutover — single-sourced in cpu-pool.js (see the
|
|
15
|
+
// POOL_SERIAL_THRESHOLD docstring for the tuning rationale).
|
|
16
|
+
const SERIAL_THRESHOLD = POOL_SERIAL_THRESHOLD;
|
|
26
17
|
|
|
27
18
|
const JS_EXTS = new Set(['.js', '.mjs', '.cjs']);
|
|
28
19
|
const TS_EXTS = new Set(['.ts', '.tsx', '.mts', '.cts']);
|
|
29
20
|
const SUPPORTED_EXTS = new Set([...JS_EXTS, ...TS_EXTS]);
|
|
30
21
|
|
|
31
|
-
let _ts = null;
|
|
32
|
-
let _tsLoadFailed = false;
|
|
33
|
-
|
|
34
|
-
function loadTypeScript() {
|
|
35
|
-
if (_ts) return _ts;
|
|
36
|
-
if (_tsLoadFailed) return null;
|
|
37
|
-
try {
|
|
38
|
-
_ts = require('typescript');
|
|
39
|
-
return _ts;
|
|
40
|
-
} catch {
|
|
41
|
-
_tsLoadFailed = true;
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Resolve the `typescript` package version, used to stamp baselines so
|
|
48
|
-
* consumers can detect transpiler drift. Returns `'0.0.0'` when the
|
|
49
|
-
* dependency is unresolvable — callers treat that sentinel as "unknown
|
|
50
|
-
* environment" and may refuse to persist a baseline that includes TS rows.
|
|
51
|
-
*
|
|
52
|
-
* @returns {string}
|
|
53
|
-
*/
|
|
54
|
-
export function resolveTsTranspilerVersion() {
|
|
55
|
-
const ts = loadTypeScript();
|
|
56
|
-
if (ts && typeof ts.version === 'string') return ts.version;
|
|
57
|
-
return '0.0.0';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function isTypeScriptPath(filePath) {
|
|
61
|
-
return TS_EXTS.has(path.extname(String(filePath)).toLowerCase());
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Pre-transpile TypeScript or TSX sources to JavaScript that the
|
|
66
|
-
* Esprima-based escomplex kernel can parse. Returns the input unchanged
|
|
67
|
-
* for `.js` / `.mjs` / `.cjs` paths.
|
|
68
|
-
*
|
|
69
|
-
* Type annotations introduce no control flow, so the transpiled output
|
|
70
|
-
* scores identically to the original TS for cyclomatic complexity,
|
|
71
|
-
* Halstead volume, and the maintainability index. `.tsx` uses the
|
|
72
|
-
* `react-jsx` emit so JSX expressions become function calls escomplex
|
|
73
|
-
* can read; `.preserve` would leave JSX in the output and Esprima would
|
|
74
|
-
* choke on it.
|
|
75
|
-
*
|
|
76
|
-
* On transpile failure the helper returns `null` — callers treat that
|
|
77
|
-
* as "skip this file" rather than crashing the scan.
|
|
78
|
-
*
|
|
79
|
-
* @param {string} filePath
|
|
80
|
-
* @param {string} source
|
|
81
|
-
* @returns {string|null}
|
|
82
|
-
*/
|
|
83
|
-
export function transpileIfNeeded(filePath, source) {
|
|
84
|
-
if (!isTypeScriptPath(filePath)) return source;
|
|
85
|
-
const ts = loadTypeScript();
|
|
86
|
-
if (!ts) {
|
|
87
|
-
Logger.warn(
|
|
88
|
-
`[Maintainability] ⚠ typescript package not resolvable; cannot score ${filePath}. ` +
|
|
89
|
-
"Install with 'npm install --save-dev typescript' (peer dep, >=5.0.0).",
|
|
90
|
-
);
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const result = ts.transpileModule(source, {
|
|
95
|
-
compilerOptions: {
|
|
96
|
-
target: ts.ScriptTarget.ESNext,
|
|
97
|
-
module: ts.ModuleKind.ESNext,
|
|
98
|
-
isolatedModules: true,
|
|
99
|
-
noEmitHelpers: true,
|
|
100
|
-
importHelpers: false,
|
|
101
|
-
removeComments: false,
|
|
102
|
-
jsx: ts.JsxEmit.ReactJSX,
|
|
103
|
-
sourceMap: false,
|
|
104
|
-
},
|
|
105
|
-
fileName: path.basename(filePath),
|
|
106
|
-
reportDiagnostics: false,
|
|
107
|
-
});
|
|
108
|
-
return result.outputText;
|
|
109
|
-
} catch (err) {
|
|
110
|
-
Logger.warn(
|
|
111
|
-
`[Maintainability] ⚠ TS transpile failed for ${filePath}: ${err?.message ?? err}; skipping.`,
|
|
112
|
-
);
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
22
|
/**
|
|
118
23
|
* @returns {boolean} True when the path's extension is one the engines score.
|
|
119
24
|
*/
|
|
@@ -121,94 +26,6 @@ function isSupportedSourceFile(filePath) {
|
|
|
121
26
|
return SUPPORTED_EXTS.has(path.extname(String(filePath)).toLowerCase());
|
|
122
27
|
}
|
|
123
28
|
|
|
124
|
-
/**
|
|
125
|
-
* Loads the current maintainability baseline from disk. The on-disk path is
|
|
126
|
-
* resolved by the caller via {@link getBaselines}; passing it explicitly
|
|
127
|
-
* removes the silent-default behaviour the framework dropped in Epic #730
|
|
128
|
-
* Story 5.5.
|
|
129
|
-
*
|
|
130
|
-
* @param {string} baselinePath Repo-relative or absolute path to the baseline
|
|
131
|
-
* JSON. Required.
|
|
132
|
-
* @returns {Record<string, number>}
|
|
133
|
-
*/
|
|
134
|
-
/**
|
|
135
|
-
* Story #1895: project the canonical maintainability envelope back to the
|
|
136
|
-
* legacy flat `{ path: mi }` map so existing gate consumers keep working
|
|
137
|
-
* without churn — Story #1912 will replace this shim with the shared
|
|
138
|
-
* reader. Returns the parsed input unchanged when it doesn't look like an
|
|
139
|
-
* envelope (legacy flat shape stays flat).
|
|
140
|
-
*/
|
|
141
|
-
function projectMaintainabilityEnvelopeToFlat(parsed) {
|
|
142
|
-
if (
|
|
143
|
-
!parsed ||
|
|
144
|
-
typeof parsed !== 'object' ||
|
|
145
|
-
Array.isArray(parsed) ||
|
|
146
|
-
!Array.isArray(parsed.rows) ||
|
|
147
|
-
typeof parsed.$schema !== 'string'
|
|
148
|
-
) {
|
|
149
|
-
return parsed;
|
|
150
|
-
}
|
|
151
|
-
const flat = {};
|
|
152
|
-
for (const row of parsed.rows) {
|
|
153
|
-
if (row && typeof row.path === 'string' && typeof row.mi === 'number') {
|
|
154
|
-
flat[row.path] = row.mi;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return flat;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function getBaseline(baselinePath) {
|
|
161
|
-
if (typeof baselinePath !== 'string' || baselinePath.length === 0) {
|
|
162
|
-
throw new TypeError(
|
|
163
|
-
'maintainability-utils.getBaseline: baselinePath is required (Epic #730 ' +
|
|
164
|
-
'Story 5.5 — callers resolve the path via getBaselines(config).maintainability.path).',
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
const abs = path.isAbsolute(baselinePath)
|
|
168
|
-
? baselinePath
|
|
169
|
-
: path.resolve(process.cwd(), baselinePath);
|
|
170
|
-
if (!fs.existsSync(abs)) return {};
|
|
171
|
-
try {
|
|
172
|
-
const parsed = JSON.parse(fs.readFileSync(abs, 'utf-8'));
|
|
173
|
-
return projectMaintainabilityEnvelopeToFlat(parsed);
|
|
174
|
-
} catch (err) {
|
|
175
|
-
Logger.warn(`[Maintainability] Failed to parse baseline: ${err.message}`);
|
|
176
|
-
return {};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Saves a new maintainability baseline to disk at `baselinePath`.
|
|
182
|
-
*
|
|
183
|
-
* Accepts the legacy flat `{ path: mi }` shape for backwards compatibility
|
|
184
|
-
* with existing callers (`regenerateMainFromTree`, refresh helpers). The
|
|
185
|
-
* map is transformed into the canonical envelope shape (`$schema`,
|
|
186
|
-
* `kernelVersion`, `generatedAt`, `rollup`, `rows`) via the shared
|
|
187
|
-
* `lib/baselines/writer.js` pipeline before being persisted, so every
|
|
188
|
-
* write produces a file that round-trips through `lib/baselines/reader.js`
|
|
189
|
-
* without schema errors.
|
|
190
|
-
*
|
|
191
|
-
* @param {Record<string, number>} baseline path→MI flat map.
|
|
192
|
-
* @param {string} baselinePath Required — caller supplies via getBaselines().
|
|
193
|
-
*/
|
|
194
|
-
export function saveBaseline(baseline, baselinePath) {
|
|
195
|
-
if (typeof baselinePath !== 'string' || baselinePath.length === 0) {
|
|
196
|
-
throw new TypeError(
|
|
197
|
-
'maintainability-utils.saveBaseline: baselinePath is required.',
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
const abs = path.isAbsolute(baselinePath)
|
|
201
|
-
? baselinePath
|
|
202
|
-
: path.resolve(process.cwd(), baselinePath);
|
|
203
|
-
|
|
204
|
-
const rows = Object.entries(baseline ?? {}).map(([p, mi]) => ({
|
|
205
|
-
path: p,
|
|
206
|
-
mi,
|
|
207
|
-
}));
|
|
208
|
-
const envelope = writeBaselineEnvelope({ kind: 'maintainability', rows });
|
|
209
|
-
writeBaselineFile(abs, envelope);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
29
|
const IGNORED_DIRS = new Set([
|
|
213
30
|
'node_modules',
|
|
214
31
|
'.git',
|
|
@@ -16,6 +16,7 @@ import { storyArtifactPath } from '../config/temp-paths.js';
|
|
|
16
16
|
import { gitSpawn } from '../git-utils.js';
|
|
17
17
|
import { Logger } from '../Logger.js';
|
|
18
18
|
import { read as readSignals } from '../signals/read.js';
|
|
19
|
+
import { concurrentMap } from '../util/concurrent-map.js';
|
|
19
20
|
import { extractStoryPerfSummaryFromComment } from './perf-report-render.js';
|
|
20
21
|
import { forEachLine } from './signals-writer.js';
|
|
21
22
|
|
|
@@ -154,30 +155,38 @@ export async function collectStorySummaries(provider, epicId, logger) {
|
|
|
154
155
|
),
|
|
155
156
|
);
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
// Bounded-parallel comment fetch (Story #3990). Each Story's comment
|
|
159
|
+
// thread is an independent paginated REST read through a fresh `gh`
|
|
160
|
+
// spawn, so a serial loop pays N sequential round-trips. concurrentMap
|
|
161
|
+
// preserves input→output index, keeping summaries in Story order; the
|
|
162
|
+
// per-Story try/catch stays inside the mapper so one failed fetch
|
|
163
|
+
// degrades to warn-and-skip without aborting the batch (mirrors
|
|
164
|
+
// verifySingleResult in lib/orchestration/wave-record-io.js, #3024).
|
|
165
|
+
const perStory = await concurrentMap(
|
|
166
|
+
storyTickets,
|
|
167
|
+
async (ticket) => {
|
|
168
|
+
const id = Number(ticket.id ?? ticket.number);
|
|
169
|
+
if (!Number.isInteger(id) || id < 1) return null;
|
|
170
|
+
let comments;
|
|
171
|
+
try {
|
|
172
|
+
comments = (await provider.getTicketComments(id)) ?? [];
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logger.warn?.(
|
|
175
|
+
`[analyze-execution] getTicketComments(${id}) failed: ${
|
|
176
|
+
err instanceof Error ? err.message : String(err)
|
|
177
|
+
}`,
|
|
178
|
+
);
|
|
179
|
+
return null;
|
|
177
180
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
for (const c of comments) {
|
|
182
|
+
const parsed = extractStoryPerfSummaryFromComment(c?.body);
|
|
183
|
+
if (parsed) return parsed;
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
},
|
|
187
|
+
{ concurrency: 4 },
|
|
188
|
+
);
|
|
189
|
+
return perStory.filter((s) => s !== null);
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
/**
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Acceptance self-eval decision core (Story #3819).
|
|
3
3
|
*
|
|
4
|
-
* Pure
|
|
5
|
-
*
|
|
4
|
+
* Pure reducer that turns one round's critic verdict plus the resolved
|
|
5
|
+
* round cap into the loop's next action. The CLI wrapper
|
|
6
6
|
* (`acceptance-eval.js`) owns the file reads, schema validation, signal
|
|
7
7
|
* emission, and ticket transitions; this module owns the *decision* so it
|
|
8
8
|
* can be unit-tested in isolation.
|
|
9
9
|
*
|
|
10
|
+
* ## Round derivation (Story #4019)
|
|
11
|
+
*
|
|
12
|
+
* The round number is **derived from the signals ledger**, not from the
|
|
13
|
+
* critic's self-reported `verdict.round`: every prior round appended one
|
|
14
|
+
* `acceptance-eval` signal to the Story's `signals.ndjson`, so the
|
|
15
|
+
* current round is `count(prior signals) + 1`. This survives a subagent
|
|
16
|
+
* restart (the ledger is on disk) and removes the critic's scratch value
|
|
17
|
+
* from the cap enforcement path — a critic that always reports `round: 1`
|
|
18
|
+
* can no longer defeat the bounded-loop guarantee.
|
|
19
|
+
*
|
|
10
20
|
* ## The three terminal actions
|
|
11
21
|
*
|
|
12
22
|
* - `proceed` — every criterion is `met`. The Story may flip to
|
|
@@ -29,6 +39,10 @@
|
|
|
29
39
|
* possible action is `block`.
|
|
30
40
|
*/
|
|
31
41
|
|
|
42
|
+
import { readFileSync } from 'node:fs';
|
|
43
|
+
|
|
44
|
+
import { signalsFile } from '../config/temp-paths.js';
|
|
45
|
+
|
|
32
46
|
/**
|
|
33
47
|
* Verdicts that clear a criterion. Anything else (`partial`, `unmet`, or
|
|
34
48
|
* an unrecognised value) is treated as not-yet-met and triggers rework.
|
|
@@ -87,11 +101,16 @@ function partitionCriteria(criteria) {
|
|
|
87
101
|
* Decide the next loop action from a single round's verdict.
|
|
88
102
|
*
|
|
89
103
|
* @param {object} args
|
|
90
|
-
* @param {{
|
|
104
|
+
* @param {{ criteria?: Array<object> }} args.verdict
|
|
91
105
|
* A verdict already validated against the acceptance-eval-verdict schema.
|
|
106
|
+
* Its `round` field, when present, is ignored — the round is supplied by
|
|
107
|
+
* the caller (derived from the signals ledger; Story #4019).
|
|
92
108
|
* @param {number} args.maxRounds
|
|
93
109
|
* The resolved (already-clamped) redraft ceiling from
|
|
94
110
|
* `getAcceptanceEval(config).maxRounds`.
|
|
111
|
+
* @param {number} [args.round]
|
|
112
|
+
* The current round number, derived via `deriveAcceptanceEvalRound`.
|
|
113
|
+
* Defaults to 1 when absent or invalid.
|
|
95
114
|
* @returns {{
|
|
96
115
|
* decision: 'proceed' | 'redraft' | 'block',
|
|
97
116
|
* round: number,
|
|
@@ -102,10 +121,9 @@ function partitionCriteria(criteria) {
|
|
|
102
121
|
* capReached: boolean,
|
|
103
122
|
* }}
|
|
104
123
|
*/
|
|
105
|
-
export function decideAcceptanceEval({ verdict, maxRounds }) {
|
|
124
|
+
export function decideAcceptanceEval({ verdict, maxRounds, round: roundIn }) {
|
|
106
125
|
const cap = effectiveCap(maxRounds);
|
|
107
|
-
const round =
|
|
108
|
-
Number.isInteger(verdict?.round) && verdict.round >= 1 ? verdict.round : 1;
|
|
126
|
+
const round = Number.isInteger(roundIn) && roundIn >= 1 ? roundIn : 1;
|
|
109
127
|
const { metCount, notMet } = partitionCriteria(verdict?.criteria);
|
|
110
128
|
const totalCriteria = metCount + notMet.length;
|
|
111
129
|
const allMet = notMet.length === 0;
|
|
@@ -134,7 +152,7 @@ export function decideAcceptanceEval({ verdict, maxRounds }) {
|
|
|
134
152
|
/**
|
|
135
153
|
* Build the per-criterion acceptance-eval signal payload for the retro /
|
|
136
154
|
* feedback substrate. Carries which acceptance items needed rework and the
|
|
137
|
-
* round count so `/
|
|
155
|
+
* round count so `/plan` Phase 0 feedback fetch and the retro can
|
|
138
156
|
* surface acceptance churn. PII-free by construction — it carries only
|
|
139
157
|
* acceptance-item indices, verdicts, and the terminal decision.
|
|
140
158
|
*
|
|
@@ -171,3 +189,59 @@ export function buildAcceptanceEvalSignal({
|
|
|
171
189
|
},
|
|
172
190
|
};
|
|
173
191
|
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Derive the current acceptance-eval round for a Story by counting the
|
|
195
|
+
* `acceptance-eval` signals already appended to the Story's
|
|
196
|
+
* `signals.ndjson` (Story #4019). Round = prior-signal count + 1, so the
|
|
197
|
+
* first run reports round 1 and each completed round (which appends one
|
|
198
|
+
* signal via `acceptance-eval.js`) advances the derived round by one.
|
|
199
|
+
*
|
|
200
|
+
* The derivation is restart-safe: the ledger lives on disk, so a subagent
|
|
201
|
+
* that dies mid-loop and restarts still observes every prior round. A
|
|
202
|
+
* missing or malformed ledger degrades to round 1 (no prior rounds), and
|
|
203
|
+
* malformed lines are skipped — observability corruption never wedges the
|
|
204
|
+
* gate.
|
|
205
|
+
*
|
|
206
|
+
* @param {object} args
|
|
207
|
+
* @param {number|null} args.epicId Parent Epic ID, or `null` for a
|
|
208
|
+
* standalone Story (routes to `<tempRoot>/standalone/stories/...`).
|
|
209
|
+
* @param {number} args.storyId
|
|
210
|
+
* @param {object} [args.config] Resolved config (tempRoot resolution).
|
|
211
|
+
* @param {(p: string) => string} [args.readFile] Injectable reader (tests).
|
|
212
|
+
* @param {(eid: number|null, sid: number, config?: object) => string} [args.signalsPathResolver]
|
|
213
|
+
* Injectable path resolver (tests). Defaults to `signalsFile`.
|
|
214
|
+
* @returns {number} The 1-based current round.
|
|
215
|
+
*/
|
|
216
|
+
export function deriveAcceptanceEvalRound({
|
|
217
|
+
epicId,
|
|
218
|
+
storyId,
|
|
219
|
+
config,
|
|
220
|
+
readFile = (p) => readFileSync(p, 'utf8'),
|
|
221
|
+
signalsPathResolver = signalsFile,
|
|
222
|
+
}) {
|
|
223
|
+
let text;
|
|
224
|
+
try {
|
|
225
|
+
text = readFile(signalsPathResolver(epicId ?? null, storyId, config));
|
|
226
|
+
} catch (_err) {
|
|
227
|
+
// No ledger yet → no prior rounds.
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let priorRounds = 0;
|
|
232
|
+
for (const line of String(text).split('\n')) {
|
|
233
|
+
const trimmed = line.trim();
|
|
234
|
+
if (trimmed === '') continue;
|
|
235
|
+
let record;
|
|
236
|
+
try {
|
|
237
|
+
record = JSON.parse(trimmed);
|
|
238
|
+
} catch (_err) {
|
|
239
|
+
continue; // Malformed line — skip, never throw.
|
|
240
|
+
}
|
|
241
|
+
if (!record || typeof record !== 'object') continue;
|
|
242
|
+
if (record.kind !== 'acceptance-eval') continue;
|
|
243
|
+
if (record.storyId !== storyId) continue;
|
|
244
|
+
priorRounds += 1;
|
|
245
|
+
}
|
|
246
|
+
return priorRounds + 1;
|
|
247
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Story #1155 (Epic #1142, 5.40.0) — extracted the helper-driven
|
|
5
5
|
* `epic-code-review` invocation into a callable module so the
|
|
6
|
-
* `/
|
|
6
|
+
* `/deliver` runner can run Phase D without spawning a child
|
|
7
7
|
* process or routing through an LLM-driven helper.
|
|
8
8
|
*
|
|
9
9
|
* Story #2831 (Epic #2815, Pluggable Code Review) — refactored to load
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* chain are unchanged.
|
|
17
17
|
*
|
|
18
18
|
* Public API:
|
|
19
|
-
* - `runCodeReview({
|
|
19
|
+
* - `runCodeReview({ scope, ticketId, provider, logger, bus, ... })` →
|
|
20
20
|
* `{ status, severity, posted, report, halted, blockerReason }`.
|
|
21
21
|
*
|
|
22
22
|
* Behaviour:
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
* - Always posts the structured `code-review` comment on the Epic
|
|
26
26
|
* issue (the adapter never posts; the orchestrator owns persistence).
|
|
27
27
|
* - Treats severity.critical > 0 as a halting blocker — the merged
|
|
28
|
-
* `/
|
|
28
|
+
* `/deliver` runner consults `halted` and refuses to advance
|
|
29
29
|
* to Phase E (retro) when set.
|
|
30
30
|
*
|
|
31
31
|
* Halting on critical findings is the in-process replacement for the
|
|
32
|
-
* helper's "operator must remediate before /
|
|
32
|
+
* helper's "operator must remediate before /deliver" gate.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { resolveConfig } from '../config-resolver.js';
|
|
@@ -116,7 +116,7 @@ function resolveTaskSizing(config) {
|
|
|
116
116
|
*
|
|
117
117
|
* Best-effort and total: a missing/unparseable checkpoint, an absent
|
|
118
118
|
* `planningRisk` field, a read failure, or a malformed `overallLevel` all
|
|
119
|
-
* degrade to `standard` (never throws). So an Epic that skipped `/
|
|
119
|
+
* degrade to `standard` (never throws). So an Epic that skipped `/plan`
|
|
120
120
|
* (no checkpoint) still gets a passing `standard` review with no new failure
|
|
121
121
|
* mode. The producer resolves depth from the judged risk alone (the diff is
|
|
122
122
|
* not yet enumerated at this point); the full risk + diff-width combination is
|
|
@@ -302,90 +302,106 @@ function buildCodeReviewEndPayload({ epicId, result, durationMs }) {
|
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
/**
|
|
305
|
-
* Resolve the
|
|
306
|
-
* `
|
|
307
|
-
|
|
308
|
-
|
|
305
|
+
* Resolve the project base branch fallback used when a caller omits
|
|
306
|
+
* `baseRef`.
|
|
307
|
+
*/
|
|
308
|
+
function resolveConfigBase(config) {
|
|
309
|
+
return (
|
|
310
|
+
config?.project?.baseBranch ?? config?.agentSettings?.baseBranch ?? 'main'
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Positive-integer override, else the supplied default. */
|
|
315
|
+
function resolveCommentTargetId(commentTargetId, fallback) {
|
|
316
|
+
return Number.isInteger(commentTargetId) && commentTargetId > 0
|
|
317
|
+
? commentTargetId
|
|
318
|
+
: fallback;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Resolve the Story-scope envelope from the parameterized
|
|
323
|
+
* `{ scope: 'story', ticketId, baseRef, headRef, commentTargetId }` shape.
|
|
309
324
|
*
|
|
310
|
-
* @param {{
|
|
311
|
-
* epicId?: number,
|
|
312
|
-
* scope?: 'epic'|'story',
|
|
313
|
-
* ticketId?: number,
|
|
314
|
-
* baseBranch?: string|null,
|
|
315
|
-
* baseRef?: string|null,
|
|
316
|
-
* headRef?: string|null,
|
|
317
|
-
* commentTargetId?: number|null,
|
|
318
|
-
* }} opts
|
|
319
|
-
* @param {object} config
|
|
320
325
|
* @returns {{
|
|
321
|
-
* scope: '
|
|
326
|
+
* scope: 'story',
|
|
322
327
|
* ticketId: number,
|
|
323
328
|
* baseRef: string,
|
|
324
329
|
* headRef: string,
|
|
325
330
|
* commentTargetId: number,
|
|
326
|
-
* epicIdForLedger:
|
|
331
|
+
* epicIdForLedger: null,
|
|
327
332
|
* }}
|
|
328
333
|
*/
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
if (typeof opts.headRef !== 'string' || opts.headRef.length === 0) {
|
|
342
|
-
throw new TypeError(
|
|
343
|
-
'runCodeReview: headRef is required (non-empty string) when scope="story".',
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
const baseRef = opts.baseRef ?? opts.baseBranch ?? configBase;
|
|
347
|
-
const commentTargetId =
|
|
348
|
-
Number.isInteger(opts.commentTargetId) && opts.commentTargetId > 0
|
|
349
|
-
? opts.commentTargetId
|
|
350
|
-
: opts.ticketId;
|
|
351
|
-
return {
|
|
352
|
-
scope: 'story',
|
|
353
|
-
ticketId: opts.ticketId,
|
|
354
|
-
baseRef,
|
|
355
|
-
headRef: opts.headRef,
|
|
356
|
-
commentTargetId,
|
|
357
|
-
epicIdForLedger: null,
|
|
358
|
-
};
|
|
334
|
+
function resolveStoryScope(opts, config) {
|
|
335
|
+
if (!Number.isInteger(opts.ticketId) || opts.ticketId <= 0) {
|
|
336
|
+
throw new TypeError(
|
|
337
|
+
'runCodeReview: ticketId is required (positive integer) when scope="story".',
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (typeof opts.headRef !== 'string' || opts.headRef.length === 0) {
|
|
341
|
+
throw new TypeError(
|
|
342
|
+
'runCodeReview: headRef is required (non-empty string) when scope="story".',
|
|
343
|
+
);
|
|
359
344
|
}
|
|
345
|
+
return {
|
|
346
|
+
scope: 'story',
|
|
347
|
+
ticketId: opts.ticketId,
|
|
348
|
+
baseRef: opts.baseRef ?? resolveConfigBase(config),
|
|
349
|
+
headRef: opts.headRef,
|
|
350
|
+
commentTargetId: resolveCommentTargetId(
|
|
351
|
+
opts.commentTargetId,
|
|
352
|
+
opts.ticketId,
|
|
353
|
+
),
|
|
354
|
+
epicIdForLedger: null,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
360
357
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Resolve the Epic-scope envelope from the parameterized
|
|
360
|
+
* `{ scope: 'epic', ticketId, baseRef, headRef, commentTargetId }` shape.
|
|
361
|
+
* `headRef` defaults to `epic/<ticketId>` and `baseRef` to the project
|
|
362
|
+
* base branch.
|
|
363
|
+
*
|
|
364
|
+
* @returns {{
|
|
365
|
+
* scope: 'epic',
|
|
366
|
+
* ticketId: number,
|
|
367
|
+
* baseRef: string,
|
|
368
|
+
* headRef: string,
|
|
369
|
+
* commentTargetId: number,
|
|
370
|
+
* epicIdForLedger: number,
|
|
371
|
+
* }}
|
|
372
|
+
*/
|
|
373
|
+
function resolveEpicScope(opts, config) {
|
|
374
|
+
if (!Number.isInteger(opts.ticketId) || opts.ticketId <= 0) {
|
|
367
375
|
throw new TypeError(
|
|
368
|
-
'runCodeReview:
|
|
376
|
+
'runCodeReview: ticketId is required (positive integer) when scope="epic".',
|
|
369
377
|
);
|
|
370
378
|
}
|
|
371
|
-
const baseRef = opts.baseRef ?? opts.baseBranch ?? configBase;
|
|
372
|
-
const headRef = opts.headRef ?? `epic/${effectiveEpicId}`;
|
|
373
|
-
const commentTargetId =
|
|
374
|
-
Number.isInteger(opts.commentTargetId) && opts.commentTargetId > 0
|
|
375
|
-
? opts.commentTargetId
|
|
376
|
-
: effectiveEpicId;
|
|
377
379
|
return {
|
|
378
380
|
scope: 'epic',
|
|
379
|
-
ticketId:
|
|
380
|
-
baseRef,
|
|
381
|
-
headRef
|
|
382
|
-
commentTargetId
|
|
383
|
-
|
|
381
|
+
ticketId: opts.ticketId,
|
|
382
|
+
baseRef: opts.baseRef ?? resolveConfigBase(config),
|
|
383
|
+
headRef: opts.headRef ?? `epic/${opts.ticketId}`,
|
|
384
|
+
commentTargetId: resolveCommentTargetId(
|
|
385
|
+
opts.commentTargetId,
|
|
386
|
+
opts.ticketId,
|
|
387
|
+
),
|
|
388
|
+
epicIdForLedger: opts.ticketId,
|
|
384
389
|
};
|
|
385
390
|
}
|
|
386
391
|
|
|
387
392
|
/**
|
|
388
|
-
*
|
|
393
|
+
* Dispatch the parameterized scope envelope
|
|
394
|
+
* (`{ scope, ticketId, baseRef, headRef, commentTargetId }`) to the
|
|
395
|
+
* matching pure resolver. `scope` defaults to `'epic'`.
|
|
396
|
+
*/
|
|
397
|
+
function resolveScopeEnvelope(opts, config) {
|
|
398
|
+
return opts.scope === 'story'
|
|
399
|
+
? resolveStoryScope(opts, config)
|
|
400
|
+
: resolveEpicScope(opts, config);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* In-process wrapper that the `/deliver` runner and the
|
|
389
405
|
* `/single-story-deliver` close path consume.
|
|
390
406
|
*
|
|
391
407
|
* Story #2252 — emits `code-review.start` immediately on entry and
|
|
@@ -407,26 +423,23 @@ function resolveScopeEnvelope(opts, config) {
|
|
|
407
423
|
* `scope: 'epic'` because the `code-review.end` schema requires
|
|
408
424
|
* `epicId` and the ledger only spans Epic lifecycles.
|
|
409
425
|
*
|
|
410
|
-
* Argument
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
* rendered header ("Story #N").
|
|
426
|
+
* Argument shape (parameterized, Epic or Story):
|
|
427
|
+
* `{ scope, ticketId, baseRef, headRef, [commentTargetId],
|
|
428
|
+
* provider, bus }`
|
|
429
|
+
* `scope` defaults to `'epic'`; `baseRef` defaults to the project base
|
|
430
|
+
* branch and (Epic scope only) `headRef` defaults to `epic/<ticketId>`.
|
|
431
|
+
* For `scope === 'story'`, `commentTargetId` overrides the post
|
|
432
|
+
* target (e.g. PR number) while `ticketId` continues to label the
|
|
433
|
+
* rendered header ("Story #N").
|
|
419
434
|
*
|
|
420
435
|
* @param {{
|
|
421
|
-
* epicId?: number,
|
|
422
436
|
* scope?: 'epic'|'story',
|
|
423
|
-
* ticketId
|
|
437
|
+
* ticketId: number,
|
|
424
438
|
* baseRef?: string|null,
|
|
425
439
|
* headRef?: string|null,
|
|
426
440
|
* commentTargetId?: number|null,
|
|
427
441
|
* provider: object,
|
|
428
442
|
* logger?: { info?: Function, warn?: Function, error?: Function, fatal?: Function, createProgress?: Function },
|
|
429
|
-
* baseBranch?: string|null,
|
|
430
443
|
* planningRisk?: { overallLevel?: ('low'|'medium'|'high'), axes?: Array<{ axis?: string, level?: string }> }|null,
|
|
431
444
|
* changedFileCount?: number|null,
|
|
432
445
|
* storyId?: number|null,
|