mandrel 2.0.0 → 2.1.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 +9 -7
- package/.agents/agents/story-worker.md +41 -46
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +51 -44
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +32 -56
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +14 -16
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/agentrc.schema.json +34 -45
- 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 +22 -66
- 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/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 +31 -13
- 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/code-review.js +58 -168
- 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 +114 -24
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +3 -7
- 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 -75
- 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/planning/authoring-context.js +41 -40
- 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 +3 -12
- 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 +15 -32
- 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 +6 -5
- package/.agents/scripts/lib/templates/decomposer-prompts.js +7 -3
- 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/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 -6
- 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/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 +79 -4
- 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 +98 -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 +21 -13
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +12 -10
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +193 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +184 -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 +40 -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 +12 -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/package.json +1 -1
- 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
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/lifecycle/epic.watch.end.schema.json",
|
|
4
|
-
"title": "epic.watch.end",
|
|
5
|
-
"description": "Emitted by Watcher when required checks settle. checkOutcomes maps check-name → terminal state (success | failure | timed_out | skipped) or the slow-but-not-failed sentinel `still-running` (Story #4358: the poll cap fired with the check still pending after the resume budget was exhausted — distinct from a genuine `failure`). AutomergePredicate subscribes to this event (test-only Watcher path; the production Phase 8.5 boundary is epic.automerge.start).",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["prUrl", "checkOutcomes"],
|
|
8
|
-
"properties": {
|
|
9
|
-
"prUrl": { "type": "string", "format": "uri", "minLength": 1 },
|
|
10
|
-
"checkOutcomes": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"additionalProperties": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"enum": [
|
|
15
|
-
"success",
|
|
16
|
-
"failure",
|
|
17
|
-
"neutral",
|
|
18
|
-
"cancelled",
|
|
19
|
-
"timed_out",
|
|
20
|
-
"still-running",
|
|
21
|
-
"action_required",
|
|
22
|
-
"stale",
|
|
23
|
-
"skipped"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
"additionalProperties": false
|
|
29
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/lifecycle/epic.watch.start.schema.json",
|
|
4
|
-
"title": "epic.watch.start",
|
|
5
|
-
"description": "Emitted by Watcher when required-check polling begins. Required-check names are resolved from GitHub at runtime via gh pr checks, not from .agentrc.json.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["prUrl", "requiredChecks"],
|
|
8
|
-
"properties": {
|
|
9
|
-
"prUrl": { "type": "string", "format": "uri", "minLength": 1 },
|
|
10
|
-
"requiredChecks": {
|
|
11
|
-
"type": "array",
|
|
12
|
-
"items": { "type": "string", "minLength": 1 }
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"additionalProperties": false
|
|
16
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/lifecycle/story.heartbeat.schema.json",
|
|
4
|
-
"title": "story.heartbeat",
|
|
5
|
-
"description": "Emitted by story-phase.js inside a Story's implementation loop. Surfaces the in-progress Story as an inspectable ledger event so /deliver's host-loop reconciler can confirm a long Story is still making forward progress between dispatch and merge — distinct from story.dispatch.start (one per Story per attempt) and story.merged (one per Story per close). The 2-tier hierarchy has no child Task tickets, so the heartbeat carries phase info only. The optional operator field (Story #3480) records the handle holding the assignee-as-lease claim on the Story so the ticket-lease primitive can decide claim liveness from the most-recent heartbeat for a given owner.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["event", "storyId", "epicId", "phase", "timestamp"],
|
|
8
|
-
"properties": {
|
|
9
|
-
"event": { "type": "string", "const": "story.heartbeat" },
|
|
10
|
-
"storyId": { "type": "integer", "minimum": 1 },
|
|
11
|
-
"epicId": { "type": "integer", "minimum": 1 },
|
|
12
|
-
"phase": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"enum": ["init", "implementing", "closing", "blocked", "done"]
|
|
15
|
-
},
|
|
16
|
-
"timestamp": { "type": "string", "format": "date-time" },
|
|
17
|
-
"operator": { "type": "string", "minLength": 1 }
|
|
18
|
-
},
|
|
19
|
-
"additionalProperties": false
|
|
20
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "risk-verdict",
|
|
4
|
-
"title": "Planning Risk Verdict",
|
|
5
|
-
"description": "Planner-authored risk judgment for an Epic, produced by the epic-plan-spec-author Skill as the fourth planning artifact (risk-verdict.json) alongside the PRD, Tech Spec, and Acceptance Spec (Epic #3865). plan-persist.js validates the verdict against this schema, derives the deterministic planningRisk envelope via deriveRiskEnvelope (lib/orchestration/planning-risk.js), and records the verdict as a risk-verdict structured comment plus the epic-plan-state checkpoint riskVerdict field. The model supplies the judgment input (axes); the harness owns the gate decision. A malformed verdict fails closed to the validation-error path — it is never silently coerced.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["axes", "summary"],
|
|
8
|
-
"properties": {
|
|
9
|
-
"axes": {
|
|
10
|
-
"type": "array",
|
|
11
|
-
"description": "Risk axes the planner judges to genuinely apply to the Epic. An empty array asserts no recognized risk axis applies (derives an all-low envelope). Every entry must use the fixed axis vocabulary mirrored from REQUIRED_AXES / NOT_APPLICABLE_AXES in lib/orchestration/planning-risk.js.",
|
|
12
|
-
"items": {
|
|
13
|
-
"type": "object",
|
|
14
|
-
"required": ["axis", "level", "rationale"],
|
|
15
|
-
"properties": {
|
|
16
|
-
"axis": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"enum": [
|
|
19
|
-
"visible-behavior",
|
|
20
|
-
"public-api",
|
|
21
|
-
"security",
|
|
22
|
-
"data-migration",
|
|
23
|
-
"billing",
|
|
24
|
-
"destructive-mutation",
|
|
25
|
-
"critical-workflow",
|
|
26
|
-
"docs-only",
|
|
27
|
-
"test-harness",
|
|
28
|
-
"internal-refactor"
|
|
29
|
-
],
|
|
30
|
-
"description": "Risk axis identifier. The first seven force a required acceptance disposition when present (REQUIRED_AXES); the last three waive the acceptance spec when they are the only signals (NOT_APPLICABLE_AXES)."
|
|
31
|
-
},
|
|
32
|
-
"level": {
|
|
33
|
-
"type": "string",
|
|
34
|
-
"enum": ["low", "medium", "high"],
|
|
35
|
-
"description": "Planner-judged severity of this axis for the Epic's actual change, not keyword presence — e.g. a PRD sentence saying 'out of scope: billing' is not a billing change."
|
|
36
|
-
},
|
|
37
|
-
"rationale": {
|
|
38
|
-
"type": "string",
|
|
39
|
-
"minLength": 1,
|
|
40
|
-
"description": "Concrete, reviewer-readable justification grounded in the PRD / Tech Spec the planner just authored. Never an empty self-attestation."
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
"additionalProperties": false
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"summary": {
|
|
47
|
-
"type": "string",
|
|
48
|
-
"minLength": 1,
|
|
49
|
-
"description": "One-paragraph overall risk narrative for the audit trail (the rationale the retired regex classifier could never produce)."
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"additionalProperties": false
|
|
53
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/story-perf-summary.schema.json",
|
|
4
|
-
"title": "StoryPerfSummary",
|
|
5
|
-
"description": "Payload of the <!-- structured:story-perf-summary --> comment posted once per Story at close (Epic #1030). Replaces the per-Task friction comment fanout and the standalone phase-timings comment.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"additionalProperties": false,
|
|
8
|
-
"required": [
|
|
9
|
-
"kind",
|
|
10
|
-
"storyId",
|
|
11
|
-
"epicId",
|
|
12
|
-
"closedAt",
|
|
13
|
-
"frictionByCategory",
|
|
14
|
-
"phaseTimingsMs",
|
|
15
|
-
"topSlowPhasesVsBaseline",
|
|
16
|
-
"reworkScore",
|
|
17
|
-
"retryDensity"
|
|
18
|
-
],
|
|
19
|
-
"properties": {
|
|
20
|
-
"kind": { "type": "string", "const": "story-perf-summary" },
|
|
21
|
-
"storyId": { "type": "integer", "minimum": 1 },
|
|
22
|
-
"epicId": { "type": "integer", "minimum": 1 },
|
|
23
|
-
"closedAt": { "type": "string", "format": "date-time" },
|
|
24
|
-
"frictionByCategory": {
|
|
25
|
-
"type": "object",
|
|
26
|
-
"description": "Counts of friction signals bucketed by category for this Story.",
|
|
27
|
-
"additionalProperties": { "type": "integer", "minimum": 0 }
|
|
28
|
-
},
|
|
29
|
-
"phaseTimingsMs": {
|
|
30
|
-
"type": "object",
|
|
31
|
-
"description": "Elapsed ms per phase, sourced from phase-timer.js in-process record.",
|
|
32
|
-
"additionalProperties": { "type": "integer", "minimum": 0 }
|
|
33
|
-
},
|
|
34
|
-
"topSlowPhasesVsBaseline": {
|
|
35
|
-
"type": "array",
|
|
36
|
-
"items": {
|
|
37
|
-
"type": "object",
|
|
38
|
-
"additionalProperties": false,
|
|
39
|
-
"required": ["phase", "elapsedMs", "baselineP95Ms", "ratio"],
|
|
40
|
-
"properties": {
|
|
41
|
-
"phase": { "type": "string", "minLength": 1 },
|
|
42
|
-
"elapsedMs": { "type": "integer", "minimum": 0 },
|
|
43
|
-
"baselineP95Ms": { "type": "integer", "minimum": 0 },
|
|
44
|
-
"ratio": { "type": "number", "minimum": 0 }
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"reworkScore": {
|
|
49
|
-
"type": "object",
|
|
50
|
-
"additionalProperties": false,
|
|
51
|
-
"required": ["filesEditedBeyondThreshold"],
|
|
52
|
-
"properties": {
|
|
53
|
-
"filesEditedBeyondThreshold": { "type": "integer", "minimum": 0 },
|
|
54
|
-
"topPath": { "type": ["string", "null"], "minLength": 1 },
|
|
55
|
-
"topPathEdits": { "type": ["integer", "null"], "minimum": 0 }
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"retryDensity": {
|
|
59
|
-
"type": "object",
|
|
60
|
-
"additionalProperties": false,
|
|
61
|
-
"required": ["retries", "uniqueCommands"],
|
|
62
|
-
"properties": {
|
|
63
|
-
"retries": { "type": "integer", "minimum": 0 },
|
|
64
|
-
"uniqueCommands": { "type": "integer", "minimum": 0 }
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
"ghSpawnCount": {
|
|
68
|
-
"type": "integer",
|
|
69
|
-
"minimum": 0,
|
|
70
|
-
"description": "Throw-away measurement field (Story #1795 / Epic #1788): the in-process `gh` spawn counter snapshot at end of close validation. Used to verify the ≥100 fewer-spawns acceptance criterion against the baseline recorded under `temp/epic-1788/`. Removed in a follow-up cleanup commit before the Story merges to the Epic branch."
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* analyze-execution.js — single writer of the structured perf-summary
|
|
5
|
-
* comments (Epic #1030 / Story #1123).
|
|
6
|
-
*
|
|
7
|
-
* Reads NDJSON from `temp/epic-<eid>/stories/story-<sid>/signals.ndjson` and
|
|
8
|
-
* upserts one of two structured comments:
|
|
9
|
-
*
|
|
10
|
-
* - `--story <sid> --epic <eid>` (Story mode): posts
|
|
11
|
-
* `<!-- structured:story-perf-summary -->` on the Story ticket. The
|
|
12
|
-
* payload combines the Story's NDJSON signals with the timing
|
|
13
|
-
* summary written by `post-merge-close.js` to
|
|
14
|
-
* `temp/epic-<eid>/stories/story-<sid>/phase-timings.json` (path overridable
|
|
15
|
-
* via `--phase-timings <path>`).
|
|
16
|
-
*
|
|
17
|
-
* - `--epic <eid>` (Epic mode): rolls up every Story under the Epic by
|
|
18
|
-
* fetching each `story-perf-summary` structured comment from the
|
|
19
|
-
* ticketing provider. Posts the
|
|
20
|
-
* `<!-- structured:epic-perf-report -->` comment on the Epic ticket.
|
|
21
|
-
* Run from the retro composer / `/deliver` Phase 6.0.
|
|
22
|
-
*
|
|
23
|
-
* Both modes are idempotent: `upsertStructuredComment` deletes the prior
|
|
24
|
-
* marker before posting the new one.
|
|
25
|
-
*
|
|
26
|
-
* Exit code is `0` on success, `0` on tolerated soft failures (missing
|
|
27
|
-
* NDJSON for a Story, no children for an Epic) so the close pipelines
|
|
28
|
-
* never block on observability output. Hard failures (bad CLI args,
|
|
29
|
-
* provider error, schema-violating payload) exit non-zero — the call
|
|
30
|
-
* sites in `single-story-close.js` / `helpers/deliver-story.md` treat that
|
|
31
|
-
* as a non-fatal warning.
|
|
32
|
-
*
|
|
33
|
-
* This file is a thin CLI: it wires the I/O readers from
|
|
34
|
-
* `lib/observability/perf-report-readers.js` to the pure renderers in
|
|
35
|
-
* `lib/observability/perf-report-render.js` inside `runStoryMode` /
|
|
36
|
-
* `runEpicMode`, then parses argv and dispatches. The report-formatting
|
|
37
|
-
* and signal-reading logic live in those two modules so report formatting
|
|
38
|
-
* is unit-testable without fixtures (Story #3350).
|
|
39
|
-
*
|
|
40
|
-
* Usage:
|
|
41
|
-
* node .agents/scripts/analyze-execution.js --story <sid> --epic <eid> \
|
|
42
|
-
* [--phase-timings <path>]
|
|
43
|
-
* node .agents/scripts/analyze-execution.js --epic <eid>
|
|
44
|
-
* node .agents/scripts/analyze-execution.js --epic <eid> --plan-metrics-json
|
|
45
|
-
*
|
|
46
|
-
* `--plan-metrics-json` (Epic #4474 PR7 — the G2 extraction surface) is a
|
|
47
|
-
* **local read-only** mode: it reads the per-Epic plan-metrics ledger
|
|
48
|
-
* (`temp/epic-<eid>/plan-metrics.json`), rolls it up with
|
|
49
|
-
* `summarizePlanMetrics`, and prints one compact JSON envelope
|
|
50
|
-
* `{ epicId, planMetrics, summaryLine }` to stdout — no provider call, no
|
|
51
|
-
* comment write, all logs on stderr — so a bench cohort can extract
|
|
52
|
-
* turns-per-plan / per-mode invocation counts / critic skips without
|
|
53
|
-
* scraping GitHub or the human log.
|
|
54
|
-
*
|
|
55
|
-
* @see docs/data-dictionary.md §StoryPerfSummary, §EpicPerfReport
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
import fs from 'node:fs/promises';
|
|
59
|
-
import path from 'node:path';
|
|
60
|
-
import { parseArgs } from 'node:util';
|
|
61
|
-
import { runAsCli } from './lib/cli-utils.js';
|
|
62
|
-
import { signalsFile } from './lib/config/temp-paths.js';
|
|
63
|
-
import { PROJECT_ROOT, resolveConfig } from './lib/config-resolver.js';
|
|
64
|
-
import { Logger, routeAllOutputToStderr } from './lib/Logger.js';
|
|
65
|
-
import { computeBaselineRefreshRate } from './lib/observability/baseline-refresh-rate.js';
|
|
66
|
-
import {
|
|
67
|
-
computeEpicPerfReport,
|
|
68
|
-
computeStoryPerfSummary,
|
|
69
|
-
} from './lib/observability/perf-aggregator.js';
|
|
70
|
-
import {
|
|
71
|
-
aggregateBaselineFrictionFromSignals,
|
|
72
|
-
collectStorySummaries,
|
|
73
|
-
gatherEpicCommitsFromGit,
|
|
74
|
-
readEpicLifecycleEvents,
|
|
75
|
-
readGhSpawnCount,
|
|
76
|
-
readPhaseTimings,
|
|
77
|
-
readStorySignals,
|
|
78
|
-
} from './lib/observability/perf-report-readers.js';
|
|
79
|
-
import {
|
|
80
|
-
EPIC_PERF_TYPE,
|
|
81
|
-
renderEpicBody,
|
|
82
|
-
renderStoryBody,
|
|
83
|
-
STORY_PERF_TYPE,
|
|
84
|
-
} from './lib/observability/perf-report-render.js';
|
|
85
|
-
import {
|
|
86
|
-
readPlanMetrics,
|
|
87
|
-
renderPlanMetricsSummaryLine,
|
|
88
|
-
summarizePlanMetrics,
|
|
89
|
-
} from './lib/orchestration/plan-metrics.js';
|
|
90
|
-
import { upsertStructuredComment } from './lib/orchestration/ticketing.js';
|
|
91
|
-
import { createProvider } from './lib/provider-factory.js';
|
|
92
|
-
|
|
93
|
-
// Re-exported so existing importers (tests, retro composer) keep a stable
|
|
94
|
-
// entry point after the read/render split (Story #3350).
|
|
95
|
-
export { extractStoryPerfSummaryFromComment } from './lib/observability/perf-report-render.js';
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Resolve the wave-execution concurrency cap from the resolved config
|
|
99
|
-
* (`delivery.deliverRunner.concurrencyCap`), falling back to the
|
|
100
|
-
* dispatcher's safe default of 2. Story #3025 / Task #3030.
|
|
101
|
-
*/
|
|
102
|
-
function resolveConcurrencyCap(config) {
|
|
103
|
-
const cap = config?.delivery?.deliverRunner?.concurrencyCap;
|
|
104
|
-
return Number.isInteger(cap) && cap >= 1 ? cap : 2;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Resolve the verify-results concurrency cap from the resolved config
|
|
109
|
-
* (`delivery.deliverRunner.verifyConcurrencyCap`), falling back to 4.
|
|
110
|
-
* Story #3025 / Task #3030.
|
|
111
|
-
*/
|
|
112
|
-
function resolveVerifyConcurrencyCap(config) {
|
|
113
|
-
const cap = config?.delivery?.deliverRunner?.verifyConcurrencyCap;
|
|
114
|
-
return Number.isInteger(cap) && cap >= 1 ? cap : 4;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Story mode: read NDJSON + phase-timings, build the payload, upsert.
|
|
119
|
-
*
|
|
120
|
-
* @param {{
|
|
121
|
-
* storyId: number,
|
|
122
|
-
* epicId: number,
|
|
123
|
-
* phaseTimingsPath?: string|null,
|
|
124
|
-
* provider: object,
|
|
125
|
-
* config: object,
|
|
126
|
-
* logger?: object,
|
|
127
|
-
* now?: () => Date,
|
|
128
|
-
* }} ctx
|
|
129
|
-
* @returns {Promise<{ commentId: number, payload: object }>}
|
|
130
|
-
*/
|
|
131
|
-
export async function runStoryMode(ctx) {
|
|
132
|
-
const { storyId, epicId, provider, config } = ctx;
|
|
133
|
-
const logger = ctx.logger ?? Logger;
|
|
134
|
-
const now = ctx.now ?? (() => new Date());
|
|
135
|
-
|
|
136
|
-
logger.info?.(
|
|
137
|
-
`[analyze-execution] story-mode story=#${storyId} epic=#${epicId}`,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
const events = await readStorySignals(epicId, storyId, config);
|
|
141
|
-
const phaseTiming = await readPhaseTimings(
|
|
142
|
-
epicId,
|
|
143
|
-
storyId,
|
|
144
|
-
config,
|
|
145
|
-
ctx.phaseTimingsPath ?? null,
|
|
146
|
-
);
|
|
147
|
-
const ghSpawnCount = await readGhSpawnCount(epicId, storyId, config);
|
|
148
|
-
|
|
149
|
-
const payload = computeStoryPerfSummary(events, {
|
|
150
|
-
storyId,
|
|
151
|
-
epicId,
|
|
152
|
-
closedAt: now().toISOString(),
|
|
153
|
-
phaseTiming,
|
|
154
|
-
});
|
|
155
|
-
if (ghSpawnCount !== null) {
|
|
156
|
-
// Throw-away measurement field (Story #1795). Surfaces the in-process
|
|
157
|
-
// `gh-exec.getSpawnCount()` snapshot captured at the end of close
|
|
158
|
-
// validation so the Story-close structured comment can be diffed
|
|
159
|
-
// against the baseline recorded under `temp/epic-1788/` for the
|
|
160
|
-
// "≥100 fewer spawns" acceptance criterion.
|
|
161
|
-
payload.ghSpawnCount = ghSpawnCount;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const body = renderStoryBody(payload);
|
|
165
|
-
const result = await upsertStructuredComment(
|
|
166
|
-
provider,
|
|
167
|
-
storyId,
|
|
168
|
-
STORY_PERF_TYPE,
|
|
169
|
-
body,
|
|
170
|
-
);
|
|
171
|
-
logger.info?.(
|
|
172
|
-
`[analyze-execution] story-perf-summary upserted on Story #${storyId} (commentId=${result.commentId})`,
|
|
173
|
-
);
|
|
174
|
-
return { commentId: result.commentId, payload };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Epic mode: collect every Story's perf summary, roll them up, upsert
|
|
179
|
-
* the epic-perf-report comment.
|
|
180
|
-
*
|
|
181
|
-
* @param {{
|
|
182
|
-
* epicId: number,
|
|
183
|
-
* provider: object,
|
|
184
|
-
* config?: object,
|
|
185
|
-
* cwd?: string,
|
|
186
|
-
* windowDays?: number,
|
|
187
|
-
* logger?: object,
|
|
188
|
-
* now?: () => Date,
|
|
189
|
-
* collectSummariesFn?: typeof collectStorySummaries,
|
|
190
|
-
* gatherEpicCommitsFn?: typeof gatherEpicCommitsFromGit,
|
|
191
|
-
* aggregateFrictionFn?: typeof aggregateBaselineFrictionFromSignals,
|
|
192
|
-
* }} ctx
|
|
193
|
-
* @returns {Promise<{ commentId: number, payload: object, baselineRefreshRate: object | null, qualityGateFriction: object | null }>}
|
|
194
|
-
*/
|
|
195
|
-
export async function runEpicMode(ctx) {
|
|
196
|
-
const { epicId, provider } = ctx;
|
|
197
|
-
const logger = ctx.logger ?? Logger;
|
|
198
|
-
const now = ctx.now ?? (() => new Date());
|
|
199
|
-
const windowDays =
|
|
200
|
-
Number.isFinite(ctx.windowDays) && ctx.windowDays > 0
|
|
201
|
-
? Math.floor(ctx.windowDays)
|
|
202
|
-
: 28;
|
|
203
|
-
const collectFn = ctx.collectSummariesFn ?? collectStorySummaries;
|
|
204
|
-
const gatherCommitsFn = ctx.gatherEpicCommitsFn ?? gatherEpicCommitsFromGit;
|
|
205
|
-
const aggregateFrictionFn =
|
|
206
|
-
ctx.aggregateFrictionFn ?? aggregateBaselineFrictionFromSignals;
|
|
207
|
-
|
|
208
|
-
logger.info?.(`[analyze-execution] epic-mode epic=#${epicId}`);
|
|
209
|
-
|
|
210
|
-
const summaries = await collectFn(provider, epicId, logger);
|
|
211
|
-
|
|
212
|
-
// Story #3025 / Task #3030 — forward lifecycle events to the
|
|
213
|
-
// aggregator so the report's `waveParallelism` array is populated
|
|
214
|
-
// (the analyzer used to omit the field, leaving the report at `[]`).
|
|
215
|
-
const readLifecycleEventsFn =
|
|
216
|
-
ctx.readLifecycleEventsFn ?? readEpicLifecycleEvents;
|
|
217
|
-
const lifecycleEvents = ctx.config
|
|
218
|
-
? await readLifecycleEventsFn(epicId, ctx.config, logger)
|
|
219
|
-
: [];
|
|
220
|
-
const concurrencyCap = resolveConcurrencyCap(ctx.config);
|
|
221
|
-
const verifyConcurrencyCap = resolveVerifyConcurrencyCap(ctx.config);
|
|
222
|
-
|
|
223
|
-
// Only forward `events` when we actually have something — passing an
|
|
224
|
-
// empty array suppresses the friction-from-summaries fallback in
|
|
225
|
-
// `computeEpicPerfReport.buildSignalCounts`, which would zero the
|
|
226
|
-
// counts for callers that have summaries but no lifecycle stream
|
|
227
|
-
// (e.g. legacy/test paths without a `config`).
|
|
228
|
-
const reportOpts = {
|
|
229
|
-
epicId,
|
|
230
|
-
generatedAt: now().toISOString(),
|
|
231
|
-
concurrencyCap,
|
|
232
|
-
verifyConcurrencyCap,
|
|
233
|
-
};
|
|
234
|
-
if (lifecycleEvents.length > 0) {
|
|
235
|
-
reportOpts.events = lifecycleEvents;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const payload = computeEpicPerfReport(summaries, reportOpts);
|
|
239
|
-
|
|
240
|
-
// Story #1400 / Task #1427 — baseline-refresh-rate row. Pure reporter
|
|
241
|
-
// operates on a fixture of git-log records gathered by the injected
|
|
242
|
-
// `gatherEpicCommitsFn`. A spawn failure surfaces as `[]` and degrades
|
|
243
|
-
// to the "no Story merges in window" line in the rendered body.
|
|
244
|
-
let baselineRefreshRate = null;
|
|
245
|
-
try {
|
|
246
|
-
const commits = await gatherCommitsFn({
|
|
247
|
-
epicId,
|
|
248
|
-
windowDays,
|
|
249
|
-
cwd: ctx.cwd ?? process.cwd(),
|
|
250
|
-
logger,
|
|
251
|
-
});
|
|
252
|
-
baselineRefreshRate = computeBaselineRefreshRate(commits, {
|
|
253
|
-
windowDays,
|
|
254
|
-
now,
|
|
255
|
-
});
|
|
256
|
-
} catch (err) {
|
|
257
|
-
logger.warn?.(
|
|
258
|
-
`[analyze-execution] baseline-refresh-rate gather failed (non-fatal): ${
|
|
259
|
-
err instanceof Error ? err.message : String(err)
|
|
260
|
-
}`,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Story #1400 / Task #1429 — Quality gate friction block. Aggregates
|
|
265
|
-
// `baseline-refresh-regression` records from the existing
|
|
266
|
-
// signals.ndjson stream (no new file format).
|
|
267
|
-
let qualityGateFriction = null;
|
|
268
|
-
if (ctx.config) {
|
|
269
|
-
try {
|
|
270
|
-
const storyIds = (summaries ?? [])
|
|
271
|
-
.map((s) => s?.storyId)
|
|
272
|
-
.filter((n) => Number.isInteger(n) && n > 0);
|
|
273
|
-
qualityGateFriction = await aggregateFrictionFn({
|
|
274
|
-
epicId,
|
|
275
|
-
storyIds,
|
|
276
|
-
config: ctx.config,
|
|
277
|
-
windowDays,
|
|
278
|
-
now,
|
|
279
|
-
});
|
|
280
|
-
} catch (err) {
|
|
281
|
-
logger.warn?.(
|
|
282
|
-
`[analyze-execution] quality-gate-friction aggregate failed (non-fatal): ${
|
|
283
|
-
err instanceof Error ? err.message : String(err)
|
|
284
|
-
}`,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const body = renderEpicBody(payload, {
|
|
290
|
-
baselineRefreshRate,
|
|
291
|
-
qualityGateFriction,
|
|
292
|
-
});
|
|
293
|
-
const result = await upsertStructuredComment(
|
|
294
|
-
provider,
|
|
295
|
-
epicId,
|
|
296
|
-
EPIC_PERF_TYPE,
|
|
297
|
-
body,
|
|
298
|
-
);
|
|
299
|
-
logger.info?.(
|
|
300
|
-
`[analyze-execution] epic-perf-report upserted on Epic #${epicId} (commentId=${result.commentId}, stories=${summaries.length})`,
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
// Plan-metrics roll-up (#4474 PR1) — surface the plan-CLI invocation
|
|
304
|
-
// ledger (`temp/epic-<id>/plan-metrics.json`) alongside the perf report.
|
|
305
|
-
// Additive and non-fatal: a missing or unreadable ledger yields `null`.
|
|
306
|
-
let planMetrics = null;
|
|
307
|
-
try {
|
|
308
|
-
planMetrics = summarizePlanMetrics(
|
|
309
|
-
await readPlanMetrics(epicId, ctx.config),
|
|
310
|
-
);
|
|
311
|
-
if (planMetrics) {
|
|
312
|
-
logger.info?.(
|
|
313
|
-
`[analyze-execution] ${renderPlanMetricsSummaryLine(planMetrics)}`,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
} catch (err) {
|
|
317
|
-
logger.warn?.(
|
|
318
|
-
`[analyze-execution] plan-metrics read failed (non-fatal): ${
|
|
319
|
-
err instanceof Error ? err.message : String(err)
|
|
320
|
-
}`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
commentId: result.commentId,
|
|
326
|
-
payload,
|
|
327
|
-
baselineRefreshRate,
|
|
328
|
-
qualityGateFriction,
|
|
329
|
-
planMetrics,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function parseCli(argv) {
|
|
334
|
-
const { values } = parseArgs({
|
|
335
|
-
args: argv,
|
|
336
|
-
options: {
|
|
337
|
-
story: { type: 'string' },
|
|
338
|
-
epic: { type: 'string' },
|
|
339
|
-
'phase-timings': { type: 'string' },
|
|
340
|
-
'window-days': { type: 'string' },
|
|
341
|
-
'plan-metrics-json': { type: 'boolean', default: false },
|
|
342
|
-
cwd: { type: 'string' },
|
|
343
|
-
},
|
|
344
|
-
strict: false,
|
|
345
|
-
});
|
|
346
|
-
const story = values.story != null ? Number.parseInt(values.story, 10) : null;
|
|
347
|
-
const epic = values.epic != null ? Number.parseInt(values.epic, 10) : null;
|
|
348
|
-
const windowDays =
|
|
349
|
-
values['window-days'] != null
|
|
350
|
-
? Number.parseInt(values['window-days'], 10)
|
|
351
|
-
: null;
|
|
352
|
-
return {
|
|
353
|
-
storyId: Number.isInteger(story) && story > 0 ? story : null,
|
|
354
|
-
epicId: Number.isInteger(epic) && epic > 0 ? epic : null,
|
|
355
|
-
phaseTimingsPath: values['phase-timings'] ?? null,
|
|
356
|
-
windowDays:
|
|
357
|
-
Number.isInteger(windowDays) && windowDays > 0 ? windowDays : null,
|
|
358
|
-
planMetricsJson: values['plan-metrics-json'] === true,
|
|
359
|
-
cwd: values.cwd ?? null,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* `--plan-metrics-json` — local, read-only G2 extraction surface (Epic
|
|
365
|
-
* #4474 PR7). Reads the per-Epic plan-metrics ledger and prints one
|
|
366
|
-
* compact JSON envelope to stdout. No provider, no comment write; stdout
|
|
367
|
-
* is reserved for the envelope (all logs on stderr). A missing ledger is
|
|
368
|
-
* not an error — it yields `planMetrics: null` so a cohort script can
|
|
369
|
-
* branch on the field instead of the exit code.
|
|
370
|
-
*
|
|
371
|
-
* @param {{ epicId: number, config: object }} input
|
|
372
|
-
* @returns {Promise<{ epicId: number, planMetrics: object|null, summaryLine: string }>}
|
|
373
|
-
*/
|
|
374
|
-
export async function runPlanMetricsJsonMode({ epicId, config }) {
|
|
375
|
-
const planMetrics = summarizePlanMetrics(
|
|
376
|
-
await readPlanMetrics(epicId, config),
|
|
377
|
-
);
|
|
378
|
-
return {
|
|
379
|
-
epicId,
|
|
380
|
-
planMetrics,
|
|
381
|
-
summaryLine: renderPlanMetricsSummaryLine(planMetrics),
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
async function main(argv = process.argv.slice(2)) {
|
|
386
|
-
const args = parseCli(argv);
|
|
387
|
-
|
|
388
|
-
if (!args.epicId) {
|
|
389
|
-
throw new Error(
|
|
390
|
-
'Usage: analyze-execution.js --epic <eid> [--story <sid>] [--phase-timings <path>]',
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const cwd = path.resolve(args.cwd ?? PROJECT_ROOT);
|
|
395
|
-
const config = resolveConfig({ cwd });
|
|
396
|
-
|
|
397
|
-
if (args.planMetricsJson) {
|
|
398
|
-
// Stdout is reserved for the compact envelope — flip every log sink to
|
|
399
|
-
// stderr before any pipeline code runs (Story #2278 discipline).
|
|
400
|
-
routeAllOutputToStderr();
|
|
401
|
-
const result = await runPlanMetricsJsonMode({
|
|
402
|
-
epicId: args.epicId,
|
|
403
|
-
config,
|
|
404
|
-
});
|
|
405
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const provider = createProvider(config);
|
|
410
|
-
|
|
411
|
-
if (args.storyId) {
|
|
412
|
-
// Existence guard: surface a clear log when the Story has no signals
|
|
413
|
-
// yet, but still post the comment with empty arrays so the marker
|
|
414
|
-
// exists for idempotence checks downstream.
|
|
415
|
-
const sigPath = signalsFile(args.epicId, args.storyId, config);
|
|
416
|
-
try {
|
|
417
|
-
await fs.access(sigPath);
|
|
418
|
-
} catch {
|
|
419
|
-
Logger.info?.(
|
|
420
|
-
`[analyze-execution] no signals.ndjson at ${sigPath} — posting empty story-perf-summary`,
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
const result = await runStoryMode({
|
|
424
|
-
storyId: args.storyId,
|
|
425
|
-
epicId: args.epicId,
|
|
426
|
-
phaseTimingsPath: args.phaseTimingsPath,
|
|
427
|
-
provider,
|
|
428
|
-
config,
|
|
429
|
-
});
|
|
430
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const result = await runEpicMode({
|
|
435
|
-
epicId: args.epicId,
|
|
436
|
-
provider,
|
|
437
|
-
config,
|
|
438
|
-
cwd,
|
|
439
|
-
windowDays: args.windowDays ?? undefined,
|
|
440
|
-
});
|
|
441
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
runAsCli(import.meta.url, main, { source: 'analyze-execution' });
|