mandrel 1.57.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 +954 -0
- package/.agents/docs/SDLC.md +1420 -0
- package/.agents/docs/agentrc-reference.json +278 -0
- package/.agents/docs/configuration.md +1040 -0
- package/.agents/docs/workflows.md +59 -0
- package/.agents/instructions.md +384 -0
- package/.agents/personas/architect.md +107 -0
- package/.agents/personas/devops-engineer.md +36 -0
- package/.agents/personas/engineer-mobile.md +119 -0
- package/.agents/personas/engineer-web.md +110 -0
- package/.agents/personas/engineer.md +90 -0
- package/.agents/personas/product.md +88 -0
- package/.agents/personas/project-manager.md +110 -0
- package/.agents/personas/qa-engineer.md +91 -0
- package/.agents/personas/refactorer.md +110 -0
- package/.agents/personas/security-engineer.md +112 -0
- package/.agents/personas/sre.md +86 -0
- package/.agents/personas/technical-writer.md +100 -0
- package/.agents/personas/ux-designer.md +95 -0
- package/.agents/rules/api-conventions.md +75 -0
- package/.agents/rules/changelog-style.md +238 -0
- package/.agents/rules/gherkin-standards.md +146 -0
- package/.agents/rules/git-conventions.md +146 -0
- package/.agents/rules/orchestration-error-handling.md +35 -0
- package/.agents/rules/security-baseline.md +92 -0
- package/.agents/rules/shell-conventions.md +70 -0
- package/.agents/rules/test-seams.md +59 -0
- package/.agents/rules/testing-standards.md +177 -0
- package/.agents/runtime-deps.json +18 -0
- package/.agents/schemas/acceptance-eval-verdict.schema.json +93 -0
- package/.agents/schemas/agentrc.schema.json +1583 -0
- package/.agents/schemas/audit-results.schema.json +69 -0
- package/.agents/schemas/audit-rules.json +134 -0
- package/.agents/schemas/audit-rules.schema.json +69 -0
- package/.agents/schemas/baselines/baseline-envelope.schema.json +44 -0
- package/.agents/schemas/baselines/bundle-size.schema.json +47 -0
- package/.agents/schemas/baselines/coverage.schema.json +50 -0
- package/.agents/schemas/baselines/crap.schema.json +52 -0
- package/.agents/schemas/baselines/duplication.schema.json +62 -0
- package/.agents/schemas/baselines/lighthouse.schema.json +59 -0
- package/.agents/schemas/baselines/lint.schema.json +47 -0
- package/.agents/schemas/baselines/maintainability.schema.json +71 -0
- package/.agents/schemas/baselines/mutation.schema.json +52 -0
- package/.agents/schemas/crap-baseline.schema.json +57 -0
- package/.agents/schemas/crap-report.schema.json +102 -0
- package/.agents/schemas/dispatch-manifest.json +232 -0
- package/.agents/schemas/epic-perf-report.schema.json +89 -0
- package/.agents/schemas/epic-spec.schema.json +183 -0
- package/.agents/schemas/friction-event.schema.json +56 -0
- package/.agents/schemas/lifecycle/README.md +18 -0
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +13 -0
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +13 -0
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +13 -0
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +13 -0
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +13 -0
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +18 -0
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +13 -0
- package/.agents/schemas/lifecycle/code-review.end.schema.json +30 -0
- package/.agents/schemas/lifecycle/code-review.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +14 -0
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +13 -0
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +13 -0
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.complete.schema.json +13 -0
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +13 -0
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +13 -0
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +14 -0
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +17 -0
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +15 -0
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +18 -0
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +16 -0
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +28 -0
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +16 -0
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +15 -0
- package/.agents/schemas/lifecycle/ledger-record.schema.json +59 -0
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +18 -0
- package/.agents/schemas/lifecycle/pr.created.schema.json +14 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +16 -0
- package/.agents/schemas/lifecycle/retro.start.schema.json +12 -0
- package/.agents/schemas/lifecycle/story.blocked.schema.json +13 -0
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +17 -0
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +15 -0
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +20 -0
- package/.agents/schemas/lifecycle/story.merged.schema.json +13 -0
- package/.agents/schemas/mi-report.schema.json +58 -0
- package/.agents/schemas/model-attribution.schema.json +49 -0
- package/.agents/schemas/qa-finding.schema.json +133 -0
- package/.agents/schemas/qa-ledger.schema.json +89 -0
- package/.agents/schemas/risk-verdict.schema.json +53 -0
- package/.agents/schemas/signal-event.schema.json +58 -0
- package/.agents/schemas/skill.schema.json +31 -0
- package/.agents/schemas/skills-index.schema.json +81 -0
- package/.agents/schemas/story-perf-summary.schema.json +73 -0
- package/.agents/schemas/validation-evidence.schema.json +78 -0
- package/.agents/scripts/README.md +93 -0
- package/.agents/scripts/acceptance-eval.js +284 -0
- package/.agents/scripts/acceptance-spec-reconciler.js +556 -0
- package/.agents/scripts/agents-bootstrap-github.js +634 -0
- package/.agents/scripts/analyze-execution.js +369 -0
- package/.agents/scripts/assert-branch.js +83 -0
- package/.agents/scripts/audit-labels-bootstrap.js +253 -0
- package/.agents/scripts/audit-to-stories.js +257 -0
- package/.agents/scripts/bootstrap.js +1378 -0
- package/.agents/scripts/check-baselines.js +81 -0
- package/.agents/scripts/check-dead-exports.js +311 -0
- package/.agents/scripts/check-doc-links.js +401 -0
- package/.agents/scripts/check-gherkin-placeholders.js +663 -0
- package/.agents/scripts/check-lifecycle-doc-drift.js +402 -0
- package/.agents/scripts/check-lifecycle-lint.js +379 -0
- package/.agents/scripts/check-prepush-recovery.js +90 -0
- package/.agents/scripts/check-windows-git-perf.js +138 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +67 -0
- package/.agents/scripts/coverage-capture.js +112 -0
- package/.agents/scripts/detect-merges.js +111 -0
- package/.agents/scripts/diagnose-friction.js +257 -0
- package/.agents/scripts/diagnose.js +240 -0
- package/.agents/scripts/dispatcher.js +295 -0
- package/.agents/scripts/drain-pending-cleanup.js +147 -0
- package/.agents/scripts/epic-audit-prepare.js +419 -0
- package/.agents/scripts/epic-audit-recheck.js +241 -0
- package/.agents/scripts/epic-deliver-note-intervention.js +192 -0
- package/.agents/scripts/epic-deliver-preflight.js +407 -0
- package/.agents/scripts/epic-deliver-prepare.js +383 -0
- package/.agents/scripts/epic-execute-record-wave.js +463 -0
- package/.agents/scripts/epic-plan-clarity.js +201 -0
- package/.agents/scripts/epic-plan-decompose.js +79 -0
- package/.agents/scripts/epic-plan-healthcheck.js +363 -0
- package/.agents/scripts/epic-plan-spec-validate.js +111 -0
- package/.agents/scripts/epic-plan-spec.js +198 -0
- package/.agents/scripts/epic-reconcile.js +637 -0
- package/.agents/scripts/evidence-gate.js +235 -0
- package/.agents/scripts/generate-config-docs.js +516 -0
- package/.agents/scripts/generate-lifecycle-docs.js +224 -0
- package/.agents/scripts/generate-skills-index.js +252 -0
- package/.agents/scripts/generate-workflows-doc.js +168 -0
- package/.agents/scripts/git-cleanup.js +124 -0
- package/.agents/scripts/git-pr-quality-gate.js +203 -0
- package/.agents/scripts/git-rebase-and-resolve.js +234 -0
- package/.agents/scripts/hierarchy-gate.js +176 -0
- package/.agents/scripts/hydrate-context.js +179 -0
- package/.agents/scripts/install-matrix-assert.js +282 -0
- package/.agents/scripts/lib/Graph.js +326 -0
- package/.agents/scripts/lib/ITicketingProvider.js +349 -0
- package/.agents/scripts/lib/Logger.js +194 -0
- package/.agents/scripts/lib/audit-suite/cli.js +64 -0
- package/.agents/scripts/lib/audit-suite/findings.js +164 -0
- package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +32 -0
- package/.agents/scripts/lib/audit-suite/frontmatter.js +110 -0
- package/.agents/scripts/lib/audit-suite/index.js +22 -0
- package/.agents/scripts/lib/audit-suite/runner.js +233 -0
- package/.agents/scripts/lib/audit-suite/selector.js +235 -0
- package/.agents/scripts/lib/audit-suite/substitutions.js +124 -0
- package/.agents/scripts/lib/audit-suite/workflow-loader.js +49 -0
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +130 -0
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +114 -0
- package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +93 -0
- package/.agents/scripts/lib/audit-to-stories/group-findings.js +265 -0
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +246 -0
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +160 -0
- package/.agents/scripts/lib/auto-refresh-baselines.js +308 -0
- package/.agents/scripts/lib/baseline-loader.js +0 -0
- package/.agents/scripts/lib/baseline-schema-registry.js +69 -0
- package/.agents/scripts/lib/baseline-snapshot.js +716 -0
- package/.agents/scripts/lib/baselines/component-matcher.js +21 -0
- package/.agents/scripts/lib/baselines/components.js +126 -0
- package/.agents/scripts/lib/baselines/diff-scope-cli.js +203 -0
- package/.agents/scripts/lib/baselines/duplication-scanner.js +220 -0
- package/.agents/scripts/lib/baselines/env-overrides.js +129 -0
- package/.agents/scripts/lib/baselines/envelope.js +368 -0
- package/.agents/scripts/lib/baselines/exit-codes.js +89 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/kernel.js +111 -0
- package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +220 -0
- package/.agents/scripts/lib/baselines/kinds/bundle-size.js +157 -0
- package/.agents/scripts/lib/baselines/kinds/coverage.js +194 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +555 -0
- package/.agents/scripts/lib/baselines/kinds/duplication.js +197 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +185 -0
- package/.agents/scripts/lib/baselines/kinds/lint.js +172 -0
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +340 -0
- package/.agents/scripts/lib/baselines/kinds/mutation.js +153 -0
- package/.agents/scripts/lib/baselines/path-canon.js +279 -0
- package/.agents/scripts/lib/baselines/preview-gates.js +298 -0
- package/.agents/scripts/lib/baselines/reader.js +321 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +733 -0
- package/.agents/scripts/lib/baselines/scope.js +291 -0
- package/.agents/scripts/lib/baselines/writer.js +312 -0
- package/.agents/scripts/lib/bdd-runner-detect.js +417 -0
- package/.agents/scripts/lib/bdd-scenario-scanner.js +310 -0
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +202 -0
- package/.agents/scripts/lib/bootstrap/branch-protection.js +222 -0
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +171 -0
- package/.agents/scripts/lib/bootstrap/commit-push.js +146 -0
- package/.agents/scripts/lib/bootstrap/gh-list.js +153 -0
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +306 -0
- package/.agents/scripts/lib/bootstrap/hitl-confirm.js +89 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +174 -0
- package/.agents/scripts/lib/bootstrap/manifest.js +272 -0
- package/.agents/scripts/lib/bootstrap/merge-methods.js +108 -0
- package/.agents/scripts/lib/bootstrap/preflight.js +195 -0
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +801 -0
- package/.agents/scripts/lib/bootstrap/prompt.js +480 -0
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +370 -0
- package/.agents/scripts/lib/bootstrap/summary.js +75 -0
- package/.agents/scripts/lib/bootstrap/workflow-audit.js +256 -0
- package/.agents/scripts/lib/branch-name-guard.js +98 -0
- package/.agents/scripts/lib/c8-cli-path.js +21 -0
- package/.agents/scripts/lib/changed-files.js +184 -0
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +104 -0
- package/.agents/scripts/lib/checks/core-bare-clean.js +48 -0
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +54 -0
- package/.agents/scripts/lib/checks/index.js +288 -0
- package/.agents/scripts/lib/checks/push-hook-parity.js +106 -0
- package/.agents/scripts/lib/checks/stale-origin-epic.js +49 -0
- package/.agents/scripts/lib/checks/state.js +558 -0
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +186 -0
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +182 -0
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +92 -0
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +81 -0
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +55 -0
- package/.agents/scripts/lib/cli/parse-numeric.js +60 -0
- package/.agents/scripts/lib/cli/standard-args.js +351 -0
- package/.agents/scripts/lib/cli-args.js +286 -0
- package/.agents/scripts/lib/cli-utils.js +69 -0
- package/.agents/scripts/lib/close-validation/projections/head-sha.js +44 -0
- package/.agents/scripts/lib/close-validation/projections/inputs.js +86 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +286 -0
- package/.agents/scripts/lib/close-validation.js +897 -0
- package/.agents/scripts/lib/codebase-snapshot.js +513 -0
- package/.agents/scripts/lib/command-header.js +33 -0
- package/.agents/scripts/lib/config/acceptance-eval.js +95 -0
- package/.agents/scripts/lib/config/baselines.js +60 -0
- package/.agents/scripts/lib/config/ci.js +30 -0
- package/.agents/scripts/lib/config/commands.js +36 -0
- package/.agents/scripts/lib/config/defaults.js +119 -0
- package/.agents/scripts/lib/config/explain.js +348 -0
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +23 -0
- package/.agents/scripts/lib/config/gates/coverage.schema.js +18 -0
- package/.agents/scripts/lib/config/gates/crap.schema.js +33 -0
- package/.agents/scripts/lib/config/gates/duplication.schema.js +26 -0
- package/.agents/scripts/lib/config/gates/index.js +36 -0
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +23 -0
- package/.agents/scripts/lib/config/gates/lint.schema.js +9 -0
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +20 -0
- package/.agents/scripts/lib/config/gates/mutation.schema.js +12 -0
- package/.agents/scripts/lib/config/gates/shared.js +117 -0
- package/.agents/scripts/lib/config/github.js +122 -0
- package/.agents/scripts/lib/config/lifecycle.js +40 -0
- package/.agents/scripts/lib/config/limits.js +211 -0
- package/.agents/scripts/lib/config/paths.js +73 -0
- package/.agents/scripts/lib/config/preflight.js +58 -0
- package/.agents/scripts/lib/config/quality.js +665 -0
- package/.agents/scripts/lib/config/retro.js +77 -0
- package/.agents/scripts/lib/config/runners.js +105 -0
- package/.agents/scripts/lib/config/runtime.js +167 -0
- package/.agents/scripts/lib/config/shared.js +46 -0
- package/.agents/scripts/lib/config/sync-agentrc.js +243 -0
- package/.agents/scripts/lib/config/temp-paths.js +373 -0
- package/.agents/scripts/lib/config/validate-orchestration.js +81 -0
- package/.agents/scripts/lib/config/worktree-isolation.js +80 -0
- package/.agents/scripts/lib/config-resolver.js +298 -0
- package/.agents/scripts/lib/config-schema-shared.js +32 -0
- package/.agents/scripts/lib/config-schema.js +20 -0
- package/.agents/scripts/lib/config-settings-schema-delivery.js +332 -0
- package/.agents/scripts/lib/config-settings-schema-quality.js +165 -0
- package/.agents/scripts/lib/config-settings-schema.js +420 -0
- package/.agents/scripts/lib/coverage-baseline.js +352 -0
- package/.agents/scripts/lib/coverage-capture.js +195 -0
- package/.agents/scripts/lib/coverage-utils.js +239 -0
- package/.agents/scripts/lib/cpu-pool.js +223 -0
- package/.agents/scripts/lib/crap-engine.js +119 -0
- package/.agents/scripts/lib/crap-utils.js +479 -0
- package/.agents/scripts/lib/degraded-mode.js +69 -0
- package/.agents/scripts/lib/dependency-parser.js +129 -0
- package/.agents/scripts/lib/duplicate-search.js +189 -0
- package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +70 -0
- package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +197 -0
- package/.agents/scripts/lib/dynamic-workflow/capability.js +396 -0
- package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +80 -0
- package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +72 -0
- package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +90 -0
- package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +43 -0
- package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +83 -0
- package/.agents/scripts/lib/env-loader.js +52 -0
- package/.agents/scripts/lib/epic-merge-lock.js +239 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +142 -0
- package/.agents/scripts/lib/epic-plan-ideation.js +228 -0
- package/.agents/scripts/lib/error-redactor.js +125 -0
- package/.agents/scripts/lib/errors/index.js +67 -0
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +230 -0
- package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +207 -0
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +421 -0
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +480 -0
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +229 -0
- package/.agents/scripts/lib/findings/classify-finding.js +195 -0
- package/.agents/scripts/lib/findings/promote-finding.js +353 -0
- package/.agents/scripts/lib/findings/route-finding.js +283 -0
- package/.agents/scripts/lib/findings/semantic-issue-search.js +179 -0
- package/.agents/scripts/lib/findings/severity.js +102 -0
- package/.agents/scripts/lib/gates/baseline-store.js +106 -0
- package/.agents/scripts/lib/gates/friction.js +43 -0
- package/.agents/scripts/lib/gh-exec.js +553 -0
- package/.agents/scripts/lib/git/cached-fetch.js +0 -0
- package/.agents/scripts/lib/git/sync-from-base.js +162 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +213 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +353 -0
- package/.agents/scripts/lib/git-merge-orchestrator.js +261 -0
- package/.agents/scripts/lib/git-utils.js +363 -0
- package/.agents/scripts/lib/github-url.js +29 -0
- package/.agents/scripts/lib/install-cmd-parser.js +51 -0
- package/.agents/scripts/lib/issue-link-parser.js +74 -0
- package/.agents/scripts/lib/json-utils.js +60 -0
- package/.agents/scripts/lib/label-constants.js +169 -0
- package/.agents/scripts/lib/label-taxonomy.js +200 -0
- package/.agents/scripts/lib/maintainability-engine.js +164 -0
- package/.agents/scripts/lib/maintainability-utils.js +343 -0
- package/.agents/scripts/lib/mandrel-catalog.js +170 -0
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +238 -0
- package/.agents/scripts/lib/mutation/config-detector.js +119 -0
- package/.agents/scripts/lib/mutation/stryker-runner.js +306 -0
- package/.agents/scripts/lib/mutation/survivor-report.js +160 -0
- package/.agents/scripts/lib/notifications/notifier.js +75 -0
- package/.agents/scripts/lib/observability/active-story-env.js +182 -0
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +221 -0
- package/.agents/scripts/lib/observability/perf-aggregator.js +887 -0
- package/.agents/scripts/lib/observability/perf-report-readers.js +319 -0
- package/.agents/scripts/lib/observability/perf-report-render.js +182 -0
- package/.agents/scripts/lib/observability/signals-writer.js +296 -0
- package/.agents/scripts/lib/observability/source-classifier.js +103 -0
- package/.agents/scripts/lib/observability/tool-trace-hook.js +417 -0
- package/.agents/scripts/lib/onboard/detect-stack.js +300 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +128 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +173 -0
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +275 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +131 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +80 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +132 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +142 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +149 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +158 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +56 -0
- package/.agents/scripts/lib/orchestration/code-review.js +652 -0
- package/.agents/scripts/lib/orchestration/column-sync.js +286 -0
- package/.agents/scripts/lib/orchestration/context-envelope.js +280 -0
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +581 -0
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +88 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +188 -0
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +144 -0
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +206 -0
- package/.agents/scripts/lib/orchestration/doc-reader.js +94 -0
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +473 -0
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +310 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +167 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +151 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +74 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +78 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +72 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +155 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +321 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +75 -0
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +86 -0
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +235 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +197 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +48 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +94 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +414 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +55 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +105 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +235 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +120 -0
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +118 -0
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +295 -0
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +186 -0
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +50 -0
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +146 -0
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +110 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +392 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +217 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +235 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +69 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +32 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +291 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +175 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +37 -0
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +127 -0
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +400 -0
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +285 -0
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +66 -0
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +797 -0
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +619 -0
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +335 -0
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +230 -0
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +363 -0
- package/.agents/scripts/lib/orchestration/error-journal.js +139 -0
- package/.agents/scripts/lib/orchestration/file-assumption-enum.js +31 -0
- package/.agents/scripts/lib/orchestration/file-assumptions.js +506 -0
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +116 -0
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +241 -0
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +489 -0
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +88 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +219 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +309 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +99 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +123 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +57 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +114 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +426 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +84 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +365 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +72 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +69 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +214 -0
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +137 -0
- package/.agents/scripts/lib/orchestration/label-transitions.js +43 -0
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +309 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +147 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +155 -0
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +226 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +69 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +378 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +248 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +527 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +259 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +278 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +355 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +647 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +331 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +140 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +421 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +168 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +668 -0
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +322 -0
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +114 -0
- package/.agents/scripts/lib/orchestration/manifest-builder.js +216 -0
- package/.agents/scripts/lib/orchestration/model-attribution.js +390 -0
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +147 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +87 -0
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +63 -0
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +86 -0
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +212 -0
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +213 -0
- package/.agents/scripts/lib/orchestration/planning-risk.js +155 -0
- package/.agents/scripts/lib/orchestration/planning-state-manager.js +318 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +56 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +33 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +78 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +68 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +118 -0
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +396 -0
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +205 -0
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +47 -0
- package/.agents/scripts/lib/orchestration/preflight-cache.js +164 -0
- package/.agents/scripts/lib/orchestration/reassert-status-column.js +202 -0
- package/.agents/scripts/lib/orchestration/reconciler.js +137 -0
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +152 -0
- package/.agents/scripts/lib/orchestration/recut.js +56 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +127 -0
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +94 -0
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +448 -0
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +335 -0
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +133 -0
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +57 -0
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +275 -0
- package/.agents/scripts/lib/orchestration/retro-proposals.js +395 -0
- package/.agents/scripts/lib/orchestration/retro-runner.js +171 -0
- package/.agents/scripts/lib/orchestration/review-depth.js +93 -0
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +363 -0
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +205 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +805 -0
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +73 -0
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +396 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +373 -0
- package/.agents/scripts/lib/orchestration/review-providers/types.js +89 -0
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +107 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +159 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +194 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +81 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +190 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +70 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +106 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +42 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +73 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +225 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +315 -0
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +149 -0
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +110 -0
- package/.agents/scripts/lib/orchestration/spec-freshness.js +320 -0
- package/.agents/scripts/lib/orchestration/spec-renderer.js +456 -0
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +80 -0
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +797 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +163 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +152 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +387 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +266 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +48 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +67 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +161 -0
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +117 -0
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +86 -0
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +147 -0
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +142 -0
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +62 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +221 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +123 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +216 -0
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +636 -0
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +198 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +105 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +222 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +220 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +291 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +234 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +110 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +86 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +112 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +157 -0
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +434 -0
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +290 -0
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +643 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +67 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +391 -0
- package/.agents/scripts/lib/orchestration/ticket-lease.js +358 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +783 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +367 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +691 -0
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +723 -0
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +474 -0
- package/.agents/scripts/lib/orchestration/ticketing/state.js +559 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +55 -0
- package/.agents/scripts/lib/orchestration/wave-marker.js +28 -0
- package/.agents/scripts/lib/orchestration/wave-record-io.js +277 -0
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +189 -0
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +423 -0
- package/.agents/scripts/lib/path-security.js +25 -0
- package/.agents/scripts/lib/plan-phase-cleanup.js +125 -0
- package/.agents/scripts/lib/preflight-runner.js +196 -0
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +95 -0
- package/.agents/scripts/lib/presentation/manifest-builder.js +245 -0
- package/.agents/scripts/lib/presentation/manifest-formatter.js +243 -0
- package/.agents/scripts/lib/presentation/manifest-helpers.js +213 -0
- package/.agents/scripts/lib/presentation/manifest-persistence.js +262 -0
- package/.agents/scripts/lib/presentation/manifest-procedures.js +55 -0
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +252 -0
- package/.agents/scripts/lib/presentation/manifest-renderer.js +188 -0
- package/.agents/scripts/lib/presentation/manifest-story-views.js +119 -0
- package/.agents/scripts/lib/provider-factory.js +80 -0
- package/.agents/scripts/lib/push-epic-retry.js +209 -0
- package/.agents/scripts/lib/qa/console-allowlist.js +151 -0
- package/.agents/scripts/lib/qa/coverage-report.js +181 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +296 -0
- package/.agents/scripts/lib/qa/propose-missing-test.js +95 -0
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +296 -0
- package/.agents/scripts/lib/qa/qa-session.js +197 -0
- package/.agents/scripts/lib/qa/redact-evidence.js +245 -0
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +190 -0
- package/.agents/scripts/lib/qa/resolve-selection.js +373 -0
- package/.agents/scripts/lib/runtime-deps/ensure-installed.js +100 -0
- package/.agents/scripts/lib/runtime-deps/manifest.js +96 -0
- package/.agents/scripts/lib/runtime-deps/preflight.js +78 -0
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +202 -0
- package/.agents/scripts/lib/signals/detectors/common.js +36 -0
- package/.agents/scripts/lib/signals/detectors/hotspot.js +298 -0
- package/.agents/scripts/lib/signals/detectors/index.js +14 -0
- package/.agents/scripts/lib/signals/detectors/retry.js +289 -0
- package/.agents/scripts/lib/signals/detectors/rework.js +204 -0
- package/.agents/scripts/lib/signals/index.js +39 -0
- package/.agents/scripts/lib/signals/read.js +268 -0
- package/.agents/scripts/lib/signals/schema.js +225 -0
- package/.agents/scripts/lib/signals/span-tree.js +290 -0
- package/.agents/scripts/lib/signals/write.js +19 -0
- package/.agents/scripts/lib/single-story/confirm-merge.js +201 -0
- package/.agents/scripts/lib/single-story/story-merged-notify.js +126 -0
- package/.agents/scripts/lib/single-story-sweep/protection.js +274 -0
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +169 -0
- package/.agents/scripts/lib/single-story-sweep.js +329 -0
- package/.agents/scripts/lib/skills/parse-skill.js +202 -0
- package/.agents/scripts/lib/skills/walk-skill-files.js +56 -0
- package/.agents/scripts/lib/spec/index.js +36 -0
- package/.agents/scripts/lib/spec/loader.js +425 -0
- package/.agents/scripts/lib/spec/state.js +217 -0
- package/.agents/scripts/lib/story-body/story-body.js +743 -0
- package/.agents/scripts/lib/story-init/blocker-validator.js +68 -0
- package/.agents/scripts/lib/story-init/branch-initializer.js +422 -0
- package/.agents/scripts/lib/story-init/context-resolver.js +92 -0
- package/.agents/scripts/lib/story-init/donor-precheck.js +207 -0
- package/.agents/scripts/lib/story-init/hierarchy-tracer.js +36 -0
- package/.agents/scripts/lib/story-init/state-transitioner.js +80 -0
- package/.agents/scripts/lib/story-init/task-graph-builder.js +114 -0
- package/.agents/scripts/lib/story-init/transition-summary.js +34 -0
- package/.agents/scripts/lib/story-lifecycle.js +186 -0
- package/.agents/scripts/lib/story-plan.js +246 -0
- package/.agents/scripts/lib/task-utils.js +26 -0
- package/.agents/scripts/lib/templates/decomposer-prompts.js +168 -0
- package/.agents/scripts/lib/test-env.js +30 -0
- package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +52 -0
- package/.agents/scripts/lib/test-isolate/list-files.js +90 -0
- package/.agents/scripts/lib/test-isolate/parse-tap.js +75 -0
- package/.agents/scripts/lib/test-isolate/runner.js +483 -0
- package/.agents/scripts/lib/test-profile/parse-tap.js +136 -0
- package/.agents/scripts/lib/test-profile/render-report.js +45 -0
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +35 -0
- package/.agents/scripts/lib/test-tiers.js +94 -0
- package/.agents/scripts/lib/util/concurrent-map.js +59 -0
- package/.agents/scripts/lib/util/phase-timer-state.js +72 -0
- package/.agents/scripts/lib/util/phase-timer.js +163 -0
- package/.agents/scripts/lib/util/poll-loop.js +86 -0
- package/.agents/scripts/lib/util/with-timeout.js +32 -0
- package/.agents/scripts/lib/validation-evidence.js +323 -0
- package/.agents/scripts/lib/wave-runner/tick.js +665 -0
- package/.agents/scripts/lib/wave-runner/wave-checkpoint.js +91 -0
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +19 -0
- package/.agents/scripts/lib/workers/crap-worker.js +197 -0
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +137 -0
- package/.agents/scripts/lib/workers/maintainability-worker.js +79 -0
- package/.agents/scripts/lib/workspace-provisioner.js +189 -0
- package/.agents/scripts/lib/worktree/bootstrapper.js +48 -0
- package/.agents/scripts/lib/worktree/inspector.js +140 -0
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +118 -0
- package/.agents/scripts/lib/worktree/lifecycle/drift-detection.js +62 -0
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +276 -0
- package/.agents/scripts/lib/worktree/lifecycle/gc.js +49 -0
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +178 -0
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +264 -0
- package/.agents/scripts/lib/worktree/lifecycle/precheck.js +100 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +588 -0
- package/.agents/scripts/lib/worktree/lifecycle/registry-sync.js +124 -0
- package/.agents/scripts/lib/worktree/lifecycle/shared.js +26 -0
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +40 -0
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +349 -0
- package/.agents/scripts/lib/worktree-manager.js +243 -0
- package/.agents/scripts/lifecycle-diff.js +206 -0
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +194 -0
- package/.agents/scripts/lifecycle-emit.js +479 -0
- package/.agents/scripts/lint-baseline.js +507 -0
- package/.agents/scripts/lint-label-vocabulary.js +237 -0
- package/.agents/scripts/loc-delta.js +205 -0
- package/.agents/scripts/notify.js +307 -0
- package/.agents/scripts/package.json +3 -0
- package/.agents/scripts/post-structured-comment.js +127 -0
- package/.agents/scripts/pr-watch-with-update.js +152 -0
- package/.agents/scripts/providers/github/auth.js +65 -0
- package/.agents/scripts/providers/github/board-add.js +63 -0
- package/.agents/scripts/providers/github/branch-protection.js +186 -0
- package/.agents/scripts/providers/github/cache.js +72 -0
- package/.agents/scripts/providers/github/comments.js +131 -0
- package/.agents/scripts/providers/github/compose.js +111 -0
- package/.agents/scripts/providers/github/errors.js +242 -0
- package/.agents/scripts/providers/github/issues.js +242 -0
- package/.agents/scripts/providers/github/labels.js +179 -0
- package/.agents/scripts/providers/github/mappers.js +126 -0
- package/.agents/scripts/providers/github/merge-methods.js +82 -0
- package/.agents/scripts/providers/github/project-board.js +47 -0
- package/.agents/scripts/providers/github/projects-v2-graphql.js +472 -0
- package/.agents/scripts/providers/github/prs.js +103 -0
- package/.agents/scripts/providers/github/request-helpers.js +110 -0
- package/.agents/scripts/providers/github/sub-issues.js +369 -0
- package/.agents/scripts/providers/github/tickets.js +381 -0
- package/.agents/scripts/providers/github/transient-retry.js +62 -0
- package/.agents/scripts/providers/github.js +157 -0
- package/.agents/scripts/quality-preview.js +327 -0
- package/.agents/scripts/quality-watch.js +223 -0
- package/.agents/scripts/render-manifest.js +143 -0
- package/.agents/scripts/resync-status-column.js +176 -0
- package/.agents/scripts/retro-run.js +167 -0
- package/.agents/scripts/run-audit-suite.js +97 -0
- package/.agents/scripts/run-coverage.js +103 -0
- package/.agents/scripts/run-lint.js +94 -0
- package/.agents/scripts/run-test-profile.js +126 -0
- package/.agents/scripts/run-tests.js +185 -0
- package/.agents/scripts/run-verify.js +56 -0
- package/.agents/scripts/select-audits.js +155 -0
- package/.agents/scripts/signals-view.js +294 -0
- package/.agents/scripts/single-story-close.js +83 -0
- package/.agents/scripts/single-story-confirm-merge.js +183 -0
- package/.agents/scripts/single-story-init.js +692 -0
- package/.agents/scripts/stories-wave-tick.js +415 -0
- package/.agents/scripts/story-close.js +246 -0
- package/.agents/scripts/story-deliver-prepare.js +267 -0
- package/.agents/scripts/story-init.js +516 -0
- package/.agents/scripts/story-phase.js +327 -0
- package/.agents/scripts/story-plan.js +284 -0
- package/.agents/scripts/sync-agentrc.js +71 -0
- package/.agents/scripts/sync-branch-from-base.js +138 -0
- package/.agents/scripts/sync-claude-commands.js +151 -0
- package/.agents/scripts/test-isolate.js +222 -0
- package/.agents/scripts/test-wrapper.js +108 -0
- package/.agents/scripts/update-coverage-baseline.js +129 -0
- package/.agents/scripts/update-crap-baseline.js +177 -0
- package/.agents/scripts/update-duplication-baseline.js +134 -0
- package/.agents/scripts/update-maintainability-baseline.js +183 -0
- package/.agents/scripts/update-mutation-baseline.js +189 -0
- package/.agents/scripts/update-ticket-state.js +107 -0
- package/.agents/scripts/validate-docs-freshness.js +259 -0
- package/.agents/scripts/validate-skills.js +278 -0
- package/.agents/scripts/wave-tick.js +335 -0
- package/.agents/skills/core/analyze-execution/SKILL.md +98 -0
- package/.agents/skills/core/api-and-interface-design/SKILL.md +327 -0
- package/.agents/skills/core/baseline-refresh/SKILL.md +181 -0
- package/.agents/skills/core/browser-testing-with-devtools/SKILL.md +352 -0
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +274 -0
- package/.agents/skills/core/ci-cd-and-automation/examples.md +211 -0
- package/.agents/skills/core/code-review-and-quality/SKILL.md +421 -0
- package/.agents/skills/core/code-simplification/SKILL.md +389 -0
- package/.agents/skills/core/context-engineering/SKILL.md +309 -0
- package/.agents/skills/core/context-engineering/examples.md +58 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +338 -0
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +250 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +79 -0
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +323 -0
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +145 -0
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +425 -0
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +393 -0
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +357 -0
- package/.agents/skills/core/git-workflow-and-versioning/SKILL.md +352 -0
- package/.agents/skills/core/hydrate-context/SKILL.md +118 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +317 -0
- package/.agents/skills/core/idea-refinement/examples.md +437 -0
- package/.agents/skills/core/idea-refinement/frameworks.md +135 -0
- package/.agents/skills/core/idea-refinement/refinement-criteria.md +155 -0
- package/.agents/skills/core/idea-refinement/scripts/idea-refine.sh +15 -0
- package/.agents/skills/core/incremental-implementation/SKILL.md +271 -0
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +213 -0
- package/.agents/skills/core/knowledge-transfer/SKILL.md +175 -0
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +117 -0
- package/.agents/skills/core/performance-optimization/SKILL.md +314 -0
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +277 -0
- package/.agents/skills/core/property-based-testing/SKILL.md +148 -0
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +105 -0
- package/.agents/skills/core/refactoring-discipline/SKILL.md +111 -0
- package/.agents/skills/core/scope-triage/SKILL.md +127 -0
- package/.agents/skills/core/security-and-hardening/SKILL.md +400 -0
- package/.agents/skills/core/shipping-and-launch/SKILL.md +328 -0
- package/.agents/skills/core/spec-driven-development/SKILL.md +252 -0
- package/.agents/skills/core/test-driven-development/SKILL.md +475 -0
- package/.agents/skills/core/using-agent-skills/SKILL.md +232 -0
- package/.agents/skills/skills.index.json +596 -0
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +31 -0
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +51 -0
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +48 -0
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +31 -0
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +33 -0
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +31 -0
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +51 -0
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +54 -0
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +29 -0
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +30 -0
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +57 -0
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +9 -0
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +48 -0
- package/.agents/skills/stack/frontend/astro/SKILL.md +62 -0
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +30 -0
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +29 -0
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +50 -0
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +58 -0
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +34 -0
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +51 -0
- package/.agents/skills/stack/qa/gherkin-authoring/SKILL.md +257 -0
- package/.agents/skills/stack/qa/gherkin-authoring/examples/invoice-issue.feature +41 -0
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +199 -0
- package/.agents/skills/stack/qa/playwright/SKILL.md +50 -0
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +188 -0
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +142 -0
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +220 -0
- package/.agents/skills/stack/qa/vitest/SKILL.md +51 -0
- package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +68 -0
- package/.agents/starter-agentrc.json +22 -0
- package/.agents/templates/agent-protocol.md +72 -0
- package/.agents/templates/docs/architecture.md +30 -0
- package/.agents/templates/docs/decisions.md +24 -0
- package/.agents/templates/epic-from-idea.md +21 -0
- package/.agents/templates/single-story-body.md +17 -0
- package/.agents/workflows/agents-update.md +415 -0
- package/.agents/workflows/audit-architecture.md +312 -0
- package/.agents/workflows/audit-clean-code.md +179 -0
- package/.agents/workflows/audit-dependencies.md +91 -0
- package/.agents/workflows/audit-devops.md +110 -0
- package/.agents/workflows/audit-lighthouse.md +260 -0
- package/.agents/workflows/audit-performance.md +161 -0
- package/.agents/workflows/audit-privacy.md +104 -0
- package/.agents/workflows/audit-quality.md +191 -0
- package/.agents/workflows/audit-security.md +156 -0
- package/.agents/workflows/audit-seo.md +118 -0
- package/.agents/workflows/audit-sre.md +139 -0
- package/.agents/workflows/audit-to-stories.md +257 -0
- package/.agents/workflows/audit-ux-ui.md +102 -0
- package/.agents/workflows/epic-deliver.md +864 -0
- package/.agents/workflows/epic-plan.md +998 -0
- package/.agents/workflows/explain.md +118 -0
- package/.agents/workflows/git-cleanup.md +250 -0
- package/.agents/workflows/git-commit-all.md +15 -0
- package/.agents/workflows/git-merge-pr.md +377 -0
- package/.agents/workflows/git-pr-all.md +278 -0
- package/.agents/workflows/git-push.md +60 -0
- package/.agents/workflows/helpers/_merge-conflict-template.md +54 -0
- package/.agents/workflows/helpers/acceptance-self-eval.md +74 -0
- package/.agents/workflows/helpers/agents-sync-config.md +129 -0
- package/.agents/workflows/helpers/code-quality-guardrails.md +101 -0
- package/.agents/workflows/helpers/code-review.md +370 -0
- package/.agents/workflows/helpers/diagnose.md +117 -0
- package/.agents/workflows/helpers/epic-audit.md +295 -0
- package/.agents/workflows/helpers/epic-deliver-story.md +370 -0
- package/.agents/workflows/helpers/epic-plan-decompose.md +199 -0
- package/.agents/workflows/helpers/epic-plan-spec.md +184 -0
- package/.agents/workflows/helpers/epic-testing.md +125 -0
- package/.agents/workflows/helpers/parallel-tooling.md +88 -0
- package/.agents/workflows/helpers/signals.md +112 -0
- package/.agents/workflows/helpers/single-story-deliver.md +636 -0
- package/.agents/workflows/helpers/worktree-lifecycle.md +317 -0
- package/.agents/workflows/onboard.md +207 -0
- package/.agents/workflows/qa-assist.md +293 -0
- package/.agents/workflows/qa-explore.md +350 -0
- package/.agents/workflows/qa-run-harness.md +288 -0
- package/.agents/workflows/story-deliver.md +327 -0
- package/.agents/workflows/story-plan.md +233 -0
- package/LICENSE +21 -0
- package/README.md +193 -0
- package/bin/mandrel.js +56 -0
- package/bin/postinstall.js +195 -0
- package/lib/cli/__tests__/migrate.test.js +268 -0
- package/lib/cli/__tests__/sync-local-zone.test.js +247 -0
- package/lib/cli/__tests__/sync.test.js +372 -0
- package/lib/cli/__tests__/update-major.test.js +217 -0
- package/lib/cli/__tests__/update.test.js +696 -0
- package/lib/cli/__tests__/version-check.test.js +398 -0
- package/lib/cli/doctor.js +124 -0
- package/lib/cli/explain.js +107 -0
- package/lib/cli/migrate.js +260 -0
- package/lib/cli/registry.js +830 -0
- package/lib/cli/sync-commands.js +50 -0
- package/lib/cli/sync.js +200 -0
- package/lib/cli/uninstall.js +795 -0
- package/lib/cli/update.js +854 -0
- package/lib/cli/version-check.js +206 -0
- package/lib/migrations/README.md +69 -0
- package/lib/migrations/__tests__/index.test.js +216 -0
- package/lib/migrations/index.js +164 -0
- package/package.json +105 -0
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance signal aggregator (Epic #1030 / Story #1123; rewrite under
|
|
3
|
+
* Epic #1181 / Story #1438 / Task #1460).
|
|
4
|
+
*
|
|
5
|
+
* Pure functions that turn the per-Story `signals.ndjson` stream into the
|
|
6
|
+
* structured payloads posted by `analyze-execution.js`:
|
|
7
|
+
*
|
|
8
|
+
* - `computeStoryPerfSummary(events, opts)` → `<!-- structured:story-perf-summary -->`
|
|
9
|
+
* - `computeEpicPerfReport(perStorySummaries, opts)` → `<!-- structured:epic-perf-report -->`
|
|
10
|
+
*
|
|
11
|
+
* Plus streaming counterparts that consume the canonical
|
|
12
|
+
* `lib/signals/read` iterator directly, so the aggregator owns its own
|
|
13
|
+
* NDJSON ingestion through the shared reader (Task #1460 AC):
|
|
14
|
+
*
|
|
15
|
+
* - `computeStoryPerfSummaryFromStore({ storyId, epicId, config? })`
|
|
16
|
+
* - `computeEpicPerfReportFromStore({ epicId, perStorySummaries, config? })`
|
|
17
|
+
*
|
|
18
|
+
* Schemas:
|
|
19
|
+
* - `.agents/schemas/story-perf-summary.schema.json`
|
|
20
|
+
* - `.agents/schemas/epic-perf-report.schema.json`
|
|
21
|
+
*
|
|
22
|
+
* Robustness contract:
|
|
23
|
+
* - Both helpers tolerate empty / partial input. Empty streams produce a
|
|
24
|
+
* well-formed payload with zeroed counters and empty arrays so the
|
|
25
|
+
* analyzer can still upsert a comment without throwing.
|
|
26
|
+
* - Malformed events (missing `kind`, non-object payload) are silently
|
|
27
|
+
* skipped; the caller is responsible for reading them off the wire and
|
|
28
|
+
* deciding whether to log. The aggregator never throws on bad data.
|
|
29
|
+
* - Numeric fields are floored to non-negative integers so the schemas
|
|
30
|
+
* (`integer`, `minimum: 0`) hold by construction.
|
|
31
|
+
*
|
|
32
|
+
* NDJSON ingestion discipline (Epic #1181):
|
|
33
|
+
* - Field-name literals for event `kind` come from
|
|
34
|
+
* `lib/signals/schema.js` so writer ↔ reader names stay in lockstep.
|
|
35
|
+
* - All file I/O for `signals.ndjson` goes through `lib/signals/read.js`
|
|
36
|
+
* (no direct `readFileSync` / `createReadStream` on signals.ndjson in
|
|
37
|
+
* this module). A grep gate in `tests/lib/checks/` enforces this on
|
|
38
|
+
* CI.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { isObject } from '../json-utils.js';
|
|
42
|
+
import { read as readSignals } from '../signals/read.js';
|
|
43
|
+
import { EVENT_KINDS } from '../signals/schema.js';
|
|
44
|
+
|
|
45
|
+
const FRICTION_KIND = EVENT_KINDS.FRICTION;
|
|
46
|
+
const HOTSPOT_KIND = EVENT_KINDS.HOTSPOT;
|
|
47
|
+
const REWORK_KIND = EVENT_KINDS.REWORK;
|
|
48
|
+
const RETRY_KIND = EVENT_KINDS.RETRY;
|
|
49
|
+
const SIGNAL_COUNT_KINDS = Object.freeze([
|
|
50
|
+
EVENT_KINDS.FRICTION,
|
|
51
|
+
EVENT_KINDS.HOTSPOT,
|
|
52
|
+
EVENT_KINDS.REWORK,
|
|
53
|
+
EVENT_KINDS.CHURN,
|
|
54
|
+
EVENT_KINDS.IDLE,
|
|
55
|
+
EVENT_KINDS.RETRY,
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
function nonNegativeInt(v) {
|
|
59
|
+
const n = Number(v);
|
|
60
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
61
|
+
return Math.floor(n);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function nonNegativeNumber(v) {
|
|
65
|
+
const n = Number(v);
|
|
66
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
67
|
+
return n;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Pull friction-by-category counts off a list of NDJSON events. Keys are
|
|
72
|
+
* the `details.category` strings; values ≥ 0 integers.
|
|
73
|
+
*
|
|
74
|
+
* @param {Iterable<object>} events
|
|
75
|
+
* @returns {Object<string, number>}
|
|
76
|
+
*/
|
|
77
|
+
function frictionByCategory(events) {
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const evt of events) {
|
|
80
|
+
if (!isObject(evt) || evt.kind !== FRICTION_KIND) continue;
|
|
81
|
+
const category =
|
|
82
|
+
isObject(evt.details) && typeof evt.details.category === 'string'
|
|
83
|
+
? evt.details.category
|
|
84
|
+
: 'Unknown';
|
|
85
|
+
out[category] = (out[category] ?? 0) + 1;
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Build the `topSlowPhasesVsBaseline` array. We accept hotspot signals
|
|
92
|
+
* carrying `{ phase, elapsedMs, baselineP95Ms, ratio }` in `details` and
|
|
93
|
+
* surface them sorted by ratio descending. The hotspot detector is a
|
|
94
|
+
* future Epic-#1030 Story; until it lands the input list is empty and
|
|
95
|
+
* this returns `[]`.
|
|
96
|
+
*
|
|
97
|
+
* @param {Iterable<object>} events
|
|
98
|
+
* @param {{ limit?: number }} [opts]
|
|
99
|
+
* @returns {Array<{phase: string, elapsedMs: number, baselineP95Ms: number, ratio: number}>}
|
|
100
|
+
*/
|
|
101
|
+
function topSlowPhasesVsBaseline(events, opts = {}) {
|
|
102
|
+
const limit = Number.isInteger(opts.limit) && opts.limit > 0 ? opts.limit : 5;
|
|
103
|
+
const rows = [];
|
|
104
|
+
for (const evt of events) {
|
|
105
|
+
if (!isObject(evt) || evt.kind !== HOTSPOT_KIND) continue;
|
|
106
|
+
const d = isObject(evt.details) ? evt.details : {};
|
|
107
|
+
const phase =
|
|
108
|
+
typeof evt.phase === 'string' && evt.phase.length > 0
|
|
109
|
+
? evt.phase
|
|
110
|
+
: typeof d.phase === 'string' && d.phase.length > 0
|
|
111
|
+
? d.phase
|
|
112
|
+
: null;
|
|
113
|
+
if (!phase) continue;
|
|
114
|
+
rows.push({
|
|
115
|
+
phase,
|
|
116
|
+
elapsedMs: nonNegativeInt(d.elapsedMs),
|
|
117
|
+
baselineP95Ms: nonNegativeInt(d.baselineP95Ms),
|
|
118
|
+
ratio: nonNegativeNumber(d.ratio),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
rows.sort((a, b) => b.ratio - a.ratio);
|
|
122
|
+
return rows.slice(0, limit);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Build the `reworkScore` object: `{ filesEditedBeyondThreshold, topPath?,
|
|
127
|
+
* topPathEdits? }`. We aggregate `kind: 'rework'` signals whose details
|
|
128
|
+
* carry a `path` and an `edits` count. When the input has no rework
|
|
129
|
+
* signals we return the zero-shape: `{ filesEditedBeyondThreshold: 0 }`.
|
|
130
|
+
*
|
|
131
|
+
* @param {Iterable<object>} events
|
|
132
|
+
* @returns {{ filesEditedBeyondThreshold: number, topPath?: string|null, topPathEdits?: number|null }}
|
|
133
|
+
*/
|
|
134
|
+
function reworkScore(events) {
|
|
135
|
+
const editsByPath = new Map();
|
|
136
|
+
for (const evt of events) {
|
|
137
|
+
if (!isObject(evt) || evt.kind !== REWORK_KIND) continue;
|
|
138
|
+
const d = isObject(evt.details) ? evt.details : {};
|
|
139
|
+
const p = typeof d.path === 'string' && d.path.length > 0 ? d.path : null;
|
|
140
|
+
if (!p) continue;
|
|
141
|
+
const edits = nonNegativeInt(d.edits);
|
|
142
|
+
editsByPath.set(p, Math.max(editsByPath.get(p) ?? 0, edits));
|
|
143
|
+
}
|
|
144
|
+
if (editsByPath.size === 0) {
|
|
145
|
+
return { filesEditedBeyondThreshold: 0 };
|
|
146
|
+
}
|
|
147
|
+
let topPath = null;
|
|
148
|
+
let topPathEdits = 0;
|
|
149
|
+
for (const [p, n] of editsByPath) {
|
|
150
|
+
if (n > topPathEdits) {
|
|
151
|
+
topPath = p;
|
|
152
|
+
topPathEdits = n;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
filesEditedBeyondThreshold: editsByPath.size,
|
|
157
|
+
topPath,
|
|
158
|
+
topPathEdits,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Build the `retryDensity` object: `{ retries, uniqueCommands }`. Sums
|
|
164
|
+
* `kind: 'retry'` signals; `uniqueCommands` is the number of distinct
|
|
165
|
+
* `details.command` strings observed. Zero-shape on empty input.
|
|
166
|
+
*
|
|
167
|
+
* @param {Iterable<object>} events
|
|
168
|
+
* @returns {{ retries: number, uniqueCommands: number }}
|
|
169
|
+
*/
|
|
170
|
+
function retryDensity(events) {
|
|
171
|
+
let retries = 0;
|
|
172
|
+
const commands = new Set();
|
|
173
|
+
for (const evt of events) {
|
|
174
|
+
if (!isObject(evt) || evt.kind !== RETRY_KIND) continue;
|
|
175
|
+
const d = isObject(evt.details) ? evt.details : {};
|
|
176
|
+
retries += 1;
|
|
177
|
+
if (typeof d.command === 'string' && d.command.length > 0) {
|
|
178
|
+
commands.add(d.command);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { retries, uniqueCommands: commands.size };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Convert a phase-timer summary `{ phases: [{ name, elapsedMs }, ...] }`
|
|
186
|
+
* into the flat `{ <name>: <ms> }` map the schema wants. Last entry wins
|
|
187
|
+
* if a phase appears twice (mark/finish boundaries).
|
|
188
|
+
*
|
|
189
|
+
* @param {{ phases?: Array<{ name: string, elapsedMs: number }> } | null | undefined} timing
|
|
190
|
+
* @returns {Object<string, number>}
|
|
191
|
+
*/
|
|
192
|
+
function phaseTimingsMs(timing) {
|
|
193
|
+
if (!isObject(timing) || !Array.isArray(timing.phases)) return {};
|
|
194
|
+
const out = {};
|
|
195
|
+
for (const p of timing.phases) {
|
|
196
|
+
if (!isObject(p)) continue;
|
|
197
|
+
if (typeof p.name !== 'string' || p.name.length === 0) continue;
|
|
198
|
+
out[p.name] = nonNegativeInt(p.elapsedMs);
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Compute the StoryPerfSummary payload from a list of NDJSON events
|
|
205
|
+
* sampled out of `temp/epic-<eid>/stories/story-<sid>/signals.ndjson` plus an
|
|
206
|
+
* optional phase-timer summary.
|
|
207
|
+
*
|
|
208
|
+
* @param {Iterable<object>} events
|
|
209
|
+
* @param {{ storyId: number, epicId: number, closedAt?: string, phaseTiming?: object|null }} opts
|
|
210
|
+
* @returns {object} StoryPerfSummary payload (schema: story-perf-summary)
|
|
211
|
+
*/
|
|
212
|
+
export function computeStoryPerfSummary(events, opts) {
|
|
213
|
+
if (!isObject(opts)) {
|
|
214
|
+
throw new TypeError('computeStoryPerfSummary: opts is required');
|
|
215
|
+
}
|
|
216
|
+
const storyId = Number(opts.storyId);
|
|
217
|
+
const epicId = Number(opts.epicId);
|
|
218
|
+
if (!Number.isInteger(storyId) || storyId < 1) {
|
|
219
|
+
throw new RangeError(
|
|
220
|
+
`computeStoryPerfSummary: storyId must be a positive integer (got ${opts.storyId})`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
if (!Number.isInteger(epicId) || epicId < 1) {
|
|
224
|
+
throw new RangeError(
|
|
225
|
+
`computeStoryPerfSummary: epicId must be a positive integer (got ${opts.epicId})`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const closedAt =
|
|
229
|
+
typeof opts.closedAt === 'string' && opts.closedAt.length > 0
|
|
230
|
+
? opts.closedAt
|
|
231
|
+
: new Date().toISOString();
|
|
232
|
+
|
|
233
|
+
// Materialise the iterable so each helper can scan independently.
|
|
234
|
+
const evtArr = [];
|
|
235
|
+
for (const e of events ?? []) {
|
|
236
|
+
if (isObject(e) && typeof e.kind === 'string') evtArr.push(e);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
kind: 'story-perf-summary',
|
|
241
|
+
storyId,
|
|
242
|
+
epicId,
|
|
243
|
+
closedAt,
|
|
244
|
+
frictionByCategory: frictionByCategory(evtArr),
|
|
245
|
+
phaseTimingsMs: phaseTimingsMs(opts.phaseTiming),
|
|
246
|
+
topSlowPhasesVsBaseline: topSlowPhasesVsBaseline(evtArr),
|
|
247
|
+
reworkScore: reworkScore(evtArr),
|
|
248
|
+
retryDensity: retryDensity(evtArr),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Compute the EpicPerfReport payload from a list of per-Story summaries
|
|
254
|
+
* (each shaped like `computeStoryPerfSummary`'s return value) plus an
|
|
255
|
+
* optional list of raw events for signal-count rollup.
|
|
256
|
+
*
|
|
257
|
+
* `signalCounts` rolls up across **events**, not summaries — a Story's
|
|
258
|
+
* `frictionByCategory` only carries friction (the named slice the schema
|
|
259
|
+
* surfaces), but the Epic-level rollup wants every kind. When `opts.events`
|
|
260
|
+
* is absent we fall back to summing each summary's friction count and
|
|
261
|
+
* leave the other kinds at 0.
|
|
262
|
+
*
|
|
263
|
+
* @param {Iterable<object>} perStorySummaries
|
|
264
|
+
* @param {{ epicId: number, generatedAt?: string, events?: Iterable<object>, waveParallelism?: Array<object>, topHotspots?: Array<object> }} opts
|
|
265
|
+
* @returns {object} EpicPerfReport payload (schema: epic-perf-report)
|
|
266
|
+
*/
|
|
267
|
+
/**
|
|
268
|
+
* Predicate / collector: walk a `perStorySummaries` iterable and emit
|
|
269
|
+
* only the entries whose `kind === 'story-perf-summary'`. Extracted from
|
|
270
|
+
* `computeEpicPerfReport` so the input-validation cascade is independently
|
|
271
|
+
* testable and the parent stays straight-line. Returns `[]` for nullish
|
|
272
|
+
* inputs, which matches the parent's prior behaviour.
|
|
273
|
+
*
|
|
274
|
+
* @param {Iterable<object>|null|undefined} perStorySummaries
|
|
275
|
+
* @returns {object[]}
|
|
276
|
+
*/
|
|
277
|
+
export function collectValidStorySamples(perStorySummaries) {
|
|
278
|
+
const out = [];
|
|
279
|
+
if (!perStorySummaries) return out;
|
|
280
|
+
for (const s of perStorySummaries) {
|
|
281
|
+
if (isObject(s) && s.kind === 'story-perf-summary') out.push(s);
|
|
282
|
+
}
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Build the `signalCounts` block. When `events` is supplied we roll up
|
|
288
|
+
* across every kind in `SIGNAL_COUNT_KINDS`; otherwise we sum the
|
|
289
|
+
* per-Story friction counts so the legacy summary-only path keeps the
|
|
290
|
+
* same friction total.
|
|
291
|
+
*
|
|
292
|
+
* @param {Iterable<object>|null|undefined} events
|
|
293
|
+
* @param {object[]} summaries
|
|
294
|
+
* @returns {{friction: number, hotspot: number, rework: number, churn: number, idle: number, retry: number}}
|
|
295
|
+
*/
|
|
296
|
+
function buildSignalCounts(events, summaries) {
|
|
297
|
+
const counts = {
|
|
298
|
+
friction: 0,
|
|
299
|
+
hotspot: 0,
|
|
300
|
+
rework: 0,
|
|
301
|
+
churn: 0,
|
|
302
|
+
idle: 0,
|
|
303
|
+
retry: 0,
|
|
304
|
+
};
|
|
305
|
+
if (events) {
|
|
306
|
+
for (const evt of events) {
|
|
307
|
+
if (!isObject(evt) || typeof evt.kind !== 'string') continue;
|
|
308
|
+
if (SIGNAL_COUNT_KINDS.includes(evt.kind)) {
|
|
309
|
+
counts[evt.kind] += 1;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return counts;
|
|
313
|
+
}
|
|
314
|
+
for (const s of summaries) {
|
|
315
|
+
if (!isObject(s.frictionByCategory)) continue;
|
|
316
|
+
for (const v of Object.values(s.frictionByCategory)) {
|
|
317
|
+
counts.friction += nonNegativeInt(v);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return counts;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Aggregate the `topHotspots` block from per-Story samples: group each
|
|
325
|
+
* story's `topSlowPhasesVsBaseline` rows by phase, count occurrences,
|
|
326
|
+
* average the ratio, then sort by `occurrences desc, avgRatio desc` and
|
|
327
|
+
* cap at 5. Extracted from `computeEpicPerfReport` so the parent stays
|
|
328
|
+
* straight-line; callers that pass `opts.topHotspots` skip this helper
|
|
329
|
+
* entirely.
|
|
330
|
+
*
|
|
331
|
+
* @param {object[]} summaries
|
|
332
|
+
* @returns {Array<{phase: string, occurrences: number, avgRatio: number}>}
|
|
333
|
+
*/
|
|
334
|
+
function aggregateTopHotspots(summaries) {
|
|
335
|
+
const acc = new Map();
|
|
336
|
+
for (const s of summaries) {
|
|
337
|
+
const arr = Array.isArray(s.topSlowPhasesVsBaseline)
|
|
338
|
+
? s.topSlowPhasesVsBaseline
|
|
339
|
+
: [];
|
|
340
|
+
for (const row of arr) {
|
|
341
|
+
if (!isObject(row) || typeof row.phase !== 'string') continue;
|
|
342
|
+
const rec = acc.get(row.phase) ?? {
|
|
343
|
+
phase: row.phase,
|
|
344
|
+
occurrences: 0,
|
|
345
|
+
ratioSum: 0,
|
|
346
|
+
};
|
|
347
|
+
rec.occurrences += 1;
|
|
348
|
+
rec.ratioSum += nonNegativeNumber(row.ratio);
|
|
349
|
+
acc.set(row.phase, rec);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return [...acc.values()]
|
|
353
|
+
.map((r) => ({
|
|
354
|
+
phase: r.phase,
|
|
355
|
+
occurrences: r.occurrences,
|
|
356
|
+
avgRatio: r.occurrences > 0 ? r.ratioSum / r.occurrences : 0,
|
|
357
|
+
}))
|
|
358
|
+
.sort((a, b) => b.occurrences - a.occurrences || b.avgRatio - a.avgRatio)
|
|
359
|
+
.slice(0, 5);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Default verify-concurrency cap when the caller does not override it.
|
|
364
|
+
* Mirrors the default for `delivery.deliverRunner.verifyConcurrencyCap`
|
|
365
|
+
* in `.agentrc.json` (Epic #3019 Tech Spec §1.4).
|
|
366
|
+
*/
|
|
367
|
+
const DEFAULT_VERIFY_CONCURRENCY_CAP = 4;
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Default wave-execution concurrency cap used when the caller does not
|
|
371
|
+
* supply `concurrencyCap` to {@link computeWaveParallelismRows}. The
|
|
372
|
+
* project default (`delivery.deliverRunner.concurrencyCap`) is 2 today;
|
|
373
|
+
* the value here is the safe fallback for offline / test contexts.
|
|
374
|
+
*/
|
|
375
|
+
const DEFAULT_WAVE_CONCURRENCY_CAP = 2;
|
|
376
|
+
|
|
377
|
+
function tsOf(evt) {
|
|
378
|
+
return evt?.ts ?? evt?.timestamp ?? null;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function tsToMs(ts) {
|
|
382
|
+
if (typeof ts !== 'string') return null;
|
|
383
|
+
const n = Date.parse(ts);
|
|
384
|
+
return Number.isFinite(n) ? n : null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function storyIdOf(evt) {
|
|
388
|
+
const raw = evt?.story ?? evt?.storyId;
|
|
389
|
+
const n = Number(raw);
|
|
390
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Materialise an event iterable into an array of `{ evt, ms }` records,
|
|
395
|
+
* parsing each event's timestamp **exactly once** (Story #3343). Events
|
|
396
|
+
* with a non-string `kind` are dropped (matching the prior inline guard);
|
|
397
|
+
* the `ms` field is `null` when the timestamp is missing or unparseable so
|
|
398
|
+
* downstream passes can skip it without re-parsing.
|
|
399
|
+
*
|
|
400
|
+
* @param {Iterable<object>} events
|
|
401
|
+
* @returns {Array<{ evt: object, ms: number|null }>}
|
|
402
|
+
*/
|
|
403
|
+
function materialiseTimedEvents(events) {
|
|
404
|
+
const out = [];
|
|
405
|
+
for (const evt of events ?? []) {
|
|
406
|
+
if (isObject(evt) && typeof evt.kind === 'string') {
|
|
407
|
+
out.push({ evt, ms: tsToMs(tsOf(evt)) });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return out;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Index Story state-transition windows from pre-timed events: first
|
|
415
|
+
* `agent::executing` → last terminal (`agent::done` | `agent::blocked` |
|
|
416
|
+
* `agent::failed`). Reuses the per-event `ms` parsed by
|
|
417
|
+
* {@link materialiseTimedEvents}. Extracted from
|
|
418
|
+
* {@link computeWaveParallelismRows} (Story #3343).
|
|
419
|
+
*
|
|
420
|
+
* @param {Array<{ evt: object, ms: number|null }>} timedEvents
|
|
421
|
+
* @returns {Map<number, { startMs: number|null, endMs: number|null }>}
|
|
422
|
+
*/
|
|
423
|
+
function indexStoryWindows(timedEvents) {
|
|
424
|
+
const storyWindows = new Map();
|
|
425
|
+
for (const { evt, ms } of timedEvents) {
|
|
426
|
+
if (evt.kind !== 'state-transition') continue;
|
|
427
|
+
const sid = storyIdOf(evt);
|
|
428
|
+
if (sid == null) continue;
|
|
429
|
+
if (ms == null) continue;
|
|
430
|
+
const to =
|
|
431
|
+
(isObject(evt.details) && evt.details.to) ?? evt.to ?? evt.toState;
|
|
432
|
+
const rec = storyWindows.get(sid) ?? { startMs: null, endMs: null };
|
|
433
|
+
if (to === 'agent::executing') {
|
|
434
|
+
if (rec.startMs == null || ms < rec.startMs) rec.startMs = ms;
|
|
435
|
+
} else if (
|
|
436
|
+
to === 'agent::done' ||
|
|
437
|
+
to === 'agent::blocked' ||
|
|
438
|
+
to === 'agent::failed'
|
|
439
|
+
) {
|
|
440
|
+
if (rec.endMs == null || ms > rec.endMs) rec.endMs = ms;
|
|
441
|
+
}
|
|
442
|
+
storyWindows.set(sid, rec);
|
|
443
|
+
}
|
|
444
|
+
return storyWindows;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Bucket `wave-start` / `wave-complete` events by index from pre-timed
|
|
449
|
+
* events. Reuses the per-event `ms` parsed by
|
|
450
|
+
* {@link materialiseTimedEvents}. Extracted from
|
|
451
|
+
* {@link computeWaveParallelismRows} (Story #3343).
|
|
452
|
+
*
|
|
453
|
+
* @param {Array<{ evt: object, ms: number|null }>} timedEvents
|
|
454
|
+
* @returns {Map<number, { startMs: number|null, endMs: number|null, stories: number[] }>}
|
|
455
|
+
*/
|
|
456
|
+
function bucketWaves(timedEvents) {
|
|
457
|
+
const waves = new Map();
|
|
458
|
+
for (const { evt, ms } of timedEvents) {
|
|
459
|
+
if (ms == null) continue;
|
|
460
|
+
if (evt.kind === 'wave-start') {
|
|
461
|
+
const idx = Number(evt.index);
|
|
462
|
+
if (!Number.isInteger(idx) || idx < 0) continue;
|
|
463
|
+
const storiesField = Array.isArray(evt.stories) ? evt.stories : [];
|
|
464
|
+
const storyIds = storiesField
|
|
465
|
+
.map((s) => {
|
|
466
|
+
const n = Number(isObject(s) ? (s.id ?? s.storyId) : s);
|
|
467
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
468
|
+
})
|
|
469
|
+
.filter((n) => n != null);
|
|
470
|
+
const rec = waves.get(idx) ?? {
|
|
471
|
+
startMs: null,
|
|
472
|
+
endMs: null,
|
|
473
|
+
stories: [],
|
|
474
|
+
};
|
|
475
|
+
if (rec.startMs == null || ms < rec.startMs) rec.startMs = ms;
|
|
476
|
+
rec.stories = storyIds;
|
|
477
|
+
waves.set(idx, rec);
|
|
478
|
+
} else if (evt.kind === 'wave-complete') {
|
|
479
|
+
const idx = Number(evt.index);
|
|
480
|
+
if (!Number.isInteger(idx) || idx < 0) continue;
|
|
481
|
+
const rec = waves.get(idx) ?? {
|
|
482
|
+
startMs: null,
|
|
483
|
+
endMs: null,
|
|
484
|
+
stories: [],
|
|
485
|
+
};
|
|
486
|
+
if (rec.endMs == null || ms > rec.endMs) rec.endMs = ms;
|
|
487
|
+
waves.set(idx, rec);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return waves;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Largest value `< hi` that is `>= lo` in a sorted ascending array, or
|
|
495
|
+
* `null` when the half-open window `[lo, hi)` contains no element. Pure
|
|
496
|
+
* binary search — used by {@link fillMissingWaveEnds} to find a wave's
|
|
497
|
+
* fallback terminator without re-scanning the whole event array per wave.
|
|
498
|
+
*
|
|
499
|
+
* @param {number[]} sortedMs ascending
|
|
500
|
+
* @param {number} lo inclusive lower bound
|
|
501
|
+
* @param {number} hi exclusive upper bound (may be Infinity)
|
|
502
|
+
* @returns {number|null}
|
|
503
|
+
*/
|
|
504
|
+
function maxInWindow(sortedMs, lo, hi) {
|
|
505
|
+
// Find the first index with value >= hi (upper bound), then step back to
|
|
506
|
+
// the last element strictly below hi.
|
|
507
|
+
let left = 0;
|
|
508
|
+
let right = sortedMs.length;
|
|
509
|
+
while (left < right) {
|
|
510
|
+
const mid = (left + right) >> 1;
|
|
511
|
+
if (sortedMs[mid] < hi) left = mid + 1;
|
|
512
|
+
else right = mid;
|
|
513
|
+
}
|
|
514
|
+
const candidate = left - 1; // last index with value < hi
|
|
515
|
+
if (candidate < 0) return null;
|
|
516
|
+
const val = sortedMs[candidate];
|
|
517
|
+
return val >= lo ? val : null;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Fill the `endMs` of any wave that never observed a `wave-complete`:
|
|
522
|
+
* each such wave's terminator is the max event timestamp in the half-open
|
|
523
|
+
* window `[startMs, nextStartMs)`, where `nextStartMs` is the start of the
|
|
524
|
+
* next wave by ascending index (or `Infinity` for the last wave).
|
|
525
|
+
*
|
|
526
|
+
* Replaces the prior O(waves × events) nested scan with a single sort of
|
|
527
|
+
* the already-parsed timestamps plus one binary search per gap-wave
|
|
528
|
+
* (Story #3343). Output is byte-identical to the prior implementation.
|
|
529
|
+
*
|
|
530
|
+
* @param {Array<[number, { startMs: number|null, endMs: number|null, stories: number[] }]>} orderedWaves sorted by index asc
|
|
531
|
+
* @param {Array<{ evt: object, ms: number|null }>} timedEvents
|
|
532
|
+
* @returns {void} mutates the wave records in `orderedWaves` in place
|
|
533
|
+
*/
|
|
534
|
+
function fillMissingWaveEnds(orderedWaves, timedEvents) {
|
|
535
|
+
const needsFill = orderedWaves.some(
|
|
536
|
+
([, rec]) => rec.endMs == null && rec.startMs != null,
|
|
537
|
+
);
|
|
538
|
+
if (!needsFill) return;
|
|
539
|
+
const sortedMs = [];
|
|
540
|
+
for (const { ms } of timedEvents) {
|
|
541
|
+
if (ms != null) sortedMs.push(ms);
|
|
542
|
+
}
|
|
543
|
+
sortedMs.sort((a, b) => a - b);
|
|
544
|
+
for (let i = 0; i < orderedWaves.length; i += 1) {
|
|
545
|
+
const [, rec] = orderedWaves[i];
|
|
546
|
+
if (rec.endMs != null) continue;
|
|
547
|
+
const startMs = rec.startMs;
|
|
548
|
+
if (startMs == null) continue;
|
|
549
|
+
const nextStartMs =
|
|
550
|
+
i + 1 < orderedWaves.length ? orderedWaves[i + 1][1].startMs : Infinity;
|
|
551
|
+
const maxMs = maxInWindow(sortedMs, startMs, nextStartMs);
|
|
552
|
+
rec.endMs = maxMs == null ? startMs : Math.max(startMs, maxMs);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Compute per-wave parallelism rows from a chronological iterable of
|
|
558
|
+
* lifecycle events (Task #3028, Epic #3019 / Story #3025).
|
|
559
|
+
*
|
|
560
|
+
* Wave windows are bracketed by `wave-start` (carrying `index` +
|
|
561
|
+
* `stories[]`) and the matching `wave-complete` (same `index`). When no
|
|
562
|
+
* `wave-complete` is observed for a wave, the wave's wallClockMs falls
|
|
563
|
+
* back to the timestamp of the last in-wave event observed (so partial
|
|
564
|
+
* runs still emit a row). When `wave-start` is missing entirely we emit
|
|
565
|
+
* no row for that wave.
|
|
566
|
+
*
|
|
567
|
+
* Per-Story durations within a wave come from `state-transition` events
|
|
568
|
+
* (`agent::executing` → `agent::done`) for the Story IDs the
|
|
569
|
+
* `wave-start` payload enumerated. We bracket the **first**
|
|
570
|
+
* `executing` transition and the **last** terminal transition (`done`,
|
|
571
|
+
* `blocked`, or `failed`) per Story; if a Story is missing one boundary
|
|
572
|
+
* its contribution to `summedStoryMs` is 0.
|
|
573
|
+
*
|
|
574
|
+
* Field contract (per the extended `epic-perf-report.schema.json`):
|
|
575
|
+
* - `waveIndex`: integer ≥ 0, from `wave-start.index`
|
|
576
|
+
* - `storyCount`: integer ≥ 0, number of Stories in the wave (from
|
|
577
|
+
* `wave-start.stories`). Added under Story #3850.
|
|
578
|
+
* - `wallClockMs`: integer ≥ 0, `(waveEnd - waveStart)` in ms
|
|
579
|
+
* - `summedStoryMs`: integer ≥ 0, Σ per-Story `(end - start)`
|
|
580
|
+
* - `utilisation`: `summedStoryMs / (wallClockMs * effectiveCap)`,
|
|
581
|
+
* where `effectiveCap = min(storyCount, concurrencyCap)`, clamped
|
|
582
|
+
* to `[0, 1]`. Zero when `wallClockMs === 0` or effectiveCap is 0.
|
|
583
|
+
* Using the effective (not raw) cap means a fully-busy 1-Story wave
|
|
584
|
+
* scores 1.0 rather than `1/cap`, eliminating false-positive
|
|
585
|
+
* `low-utilisation` signals on serialized/narrow waves (Story #3850).
|
|
586
|
+
* - `capBinding`: true when `summedStoryMs / wallClockMs >=
|
|
587
|
+
* concurrencyCap`, false otherwise (and false when wallClockMs
|
|
588
|
+
* is 0). Still uses the raw cap so the signal fires when the
|
|
589
|
+
* configured parallelism ceiling is actually saturated.
|
|
590
|
+
* - `verifyConcurrencyCap`: forwarded from `opts.verifyConcurrencyCap`
|
|
591
|
+
* (or the project default 4) so the post-merge close comment can
|
|
592
|
+
* attribute saturation back to the cap value in force at the time.
|
|
593
|
+
*
|
|
594
|
+
* @param {Iterable<object>} events
|
|
595
|
+
* @param {{
|
|
596
|
+
* concurrencyCap?: number,
|
|
597
|
+
* verifyConcurrencyCap?: number,
|
|
598
|
+
* }} [opts]
|
|
599
|
+
* @returns {Array<{
|
|
600
|
+
* waveIndex: number,
|
|
601
|
+
* storyCount: number,
|
|
602
|
+
* wallClockMs: number,
|
|
603
|
+
* summedStoryMs: number,
|
|
604
|
+
* utilisation: number,
|
|
605
|
+
* capBinding: boolean,
|
|
606
|
+
* verifyConcurrencyCap: number,
|
|
607
|
+
* }>}
|
|
608
|
+
*/
|
|
609
|
+
export function computeWaveParallelismRows(events, opts = {}) {
|
|
610
|
+
const concurrencyCap =
|
|
611
|
+
Number.isInteger(opts.concurrencyCap) && opts.concurrencyCap >= 1
|
|
612
|
+
? opts.concurrencyCap
|
|
613
|
+
: DEFAULT_WAVE_CONCURRENCY_CAP;
|
|
614
|
+
const verifyConcurrencyCap =
|
|
615
|
+
Number.isInteger(opts.verifyConcurrencyCap) &&
|
|
616
|
+
opts.verifyConcurrencyCap >= 1
|
|
617
|
+
? opts.verifyConcurrencyCap
|
|
618
|
+
: DEFAULT_VERIFY_CONCURRENCY_CAP;
|
|
619
|
+
|
|
620
|
+
// Materialise the iterable once, parsing each event's timestamp a
|
|
621
|
+
// single time so every downstream pass reuses the same `ms` value
|
|
622
|
+
// (Story #3343). Events are typically a few thousand per Epic at most.
|
|
623
|
+
const timedEvents = materialiseTimedEvents(events);
|
|
624
|
+
|
|
625
|
+
// Index Story state-transition windows: first `agent::executing` →
|
|
626
|
+
// last terminal (`agent::done` | `agent::blocked` | `agent::failed`).
|
|
627
|
+
const storyWindows = indexStoryWindows(timedEvents);
|
|
628
|
+
|
|
629
|
+
// Bucket wave-start / wave-complete events by index, then fill any wave
|
|
630
|
+
// that never saw `wave-complete` via a single sorted-timestamp sweep
|
|
631
|
+
// (replacing the prior O(waves × events) nested scan).
|
|
632
|
+
const waves = bucketWaves(timedEvents);
|
|
633
|
+
const orderedWaves = [...waves.entries()].sort((a, b) => a[0] - b[0]);
|
|
634
|
+
fillMissingWaveEnds(orderedWaves, timedEvents);
|
|
635
|
+
|
|
636
|
+
// Build rows.
|
|
637
|
+
const rows = [];
|
|
638
|
+
for (const [idx, rec] of orderedWaves) {
|
|
639
|
+
if (rec.startMs == null) continue;
|
|
640
|
+
const wallClockMs = Math.max(
|
|
641
|
+
0,
|
|
642
|
+
Math.floor((rec.endMs ?? rec.startMs) - rec.startMs),
|
|
643
|
+
);
|
|
644
|
+
const storyCount = rec.stories.length;
|
|
645
|
+
let summedStoryMs = 0;
|
|
646
|
+
for (const sid of rec.stories) {
|
|
647
|
+
const w = storyWindows.get(sid);
|
|
648
|
+
if (!w || w.startMs == null || w.endMs == null) continue;
|
|
649
|
+
const dur = w.endMs - w.startMs;
|
|
650
|
+
if (Number.isFinite(dur) && dur > 0) summedStoryMs += Math.floor(dur);
|
|
651
|
+
}
|
|
652
|
+
// Use the effective cap — min(storyCount, concurrencyCap) — as the
|
|
653
|
+
// utilisation denominator so a fully-busy 1-Story wave scores 1.0
|
|
654
|
+
// instead of 1/cap. This eliminates false-positive `low-utilisation`
|
|
655
|
+
// signals on serialized / narrow-wave Epics (Story #3850).
|
|
656
|
+
// capBinding still uses the raw concurrencyCap because it signals that
|
|
657
|
+
// the configured parallelism ceiling is genuinely saturated.
|
|
658
|
+
const effectiveCap = Math.min(storyCount, concurrencyCap);
|
|
659
|
+
let utilisation = 0;
|
|
660
|
+
let capBinding = false;
|
|
661
|
+
if (wallClockMs > 0 && effectiveCap > 0) {
|
|
662
|
+
utilisation = clamp(summedStoryMs / (wallClockMs * effectiveCap), 0, 1);
|
|
663
|
+
}
|
|
664
|
+
if (wallClockMs > 0 && concurrencyCap > 0) {
|
|
665
|
+
capBinding = summedStoryMs / wallClockMs >= concurrencyCap;
|
|
666
|
+
}
|
|
667
|
+
rows.push({
|
|
668
|
+
waveIndex: idx,
|
|
669
|
+
storyCount,
|
|
670
|
+
wallClockMs,
|
|
671
|
+
summedStoryMs,
|
|
672
|
+
utilisation,
|
|
673
|
+
capBinding,
|
|
674
|
+
verifyConcurrencyCap,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return rows;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Clamp `n` to the inclusive range [lo, hi]. Returns `lo` for NaN /
|
|
683
|
+
* non-finite inputs so the coercer stays well-behaved on garbage.
|
|
684
|
+
*
|
|
685
|
+
* @param {number} n
|
|
686
|
+
* @param {number} lo
|
|
687
|
+
* @param {number} hi
|
|
688
|
+
* @returns {number}
|
|
689
|
+
*/
|
|
690
|
+
function clamp(n, lo, hi) {
|
|
691
|
+
if (!Number.isFinite(n)) return lo;
|
|
692
|
+
if (n < lo) return lo;
|
|
693
|
+
if (n > hi) return hi;
|
|
694
|
+
return n;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Coerce a single waveParallelism input row into the schema-canonical
|
|
699
|
+
* shape `{ waveIndex, storyCount, wallClockMs, summedStoryMs, utilisation,
|
|
700
|
+
* capBinding, verifyConcurrencyCap }` (Story #3025; storyCount added
|
|
701
|
+
* Story #3850).
|
|
702
|
+
*
|
|
703
|
+
* - Numeric fields are floored to non-negative integers (or clamped
|
|
704
|
+
* numbers for utilisation) so the JSON-schema `integer` / `minimum: 0`
|
|
705
|
+
* constraints hold by construction.
|
|
706
|
+
* - `utilisation` is clamped to `[0, 1]` per the Tech Spec
|
|
707
|
+
* contract (§1.1).
|
|
708
|
+
* - `capBinding` is coerced to boolean.
|
|
709
|
+
* - `verifyConcurrencyCap` falls back to the project default (4) when the
|
|
710
|
+
* caller omits it or supplies a non-positive integer; the schema
|
|
711
|
+
* requires `minimum: 1` so 0 is not a valid carrier value.
|
|
712
|
+
* - `storyCount` falls back to 0 when absent (older payloads predating
|
|
713
|
+
* Story #3850 do not carry the field).
|
|
714
|
+
*
|
|
715
|
+
* Exported for unit-testing the coercer in isolation.
|
|
716
|
+
*
|
|
717
|
+
* @param {object | null | undefined} row
|
|
718
|
+
* @returns {{ waveIndex: number, storyCount: number, wallClockMs: number, summedStoryMs: number, utilisation: number, capBinding: boolean, verifyConcurrencyCap: number }}
|
|
719
|
+
*/
|
|
720
|
+
export function coerceWaveParallelismRow(row) {
|
|
721
|
+
const src = isObject(row) ? row : {};
|
|
722
|
+
const cap = Number(src.verifyConcurrencyCap);
|
|
723
|
+
const verifyConcurrencyCap =
|
|
724
|
+
Number.isInteger(cap) && cap >= 1 ? cap : DEFAULT_VERIFY_CONCURRENCY_CAP;
|
|
725
|
+
return {
|
|
726
|
+
waveIndex: nonNegativeInt(src.waveIndex),
|
|
727
|
+
storyCount: nonNegativeInt(src.storyCount),
|
|
728
|
+
wallClockMs: nonNegativeInt(src.wallClockMs),
|
|
729
|
+
summedStoryMs: nonNegativeInt(src.summedStoryMs),
|
|
730
|
+
utilisation: clamp(nonNegativeNumber(src.utilisation), 0, 1),
|
|
731
|
+
capBinding: Boolean(src.capBinding),
|
|
732
|
+
verifyConcurrencyCap,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
export function computeEpicPerfReport(perStorySummaries, opts) {
|
|
737
|
+
if (!isObject(opts)) {
|
|
738
|
+
throw new TypeError('computeEpicPerfReport: opts is required');
|
|
739
|
+
}
|
|
740
|
+
const epicId = Number(opts.epicId);
|
|
741
|
+
if (!Number.isInteger(epicId) || epicId < 1) {
|
|
742
|
+
throw new RangeError(
|
|
743
|
+
`computeEpicPerfReport: epicId must be a positive integer (got ${opts.epicId})`,
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
const generatedAt =
|
|
747
|
+
typeof opts.generatedAt === 'string' && opts.generatedAt.length > 0
|
|
748
|
+
? opts.generatedAt
|
|
749
|
+
: new Date().toISOString();
|
|
750
|
+
|
|
751
|
+
const summaries = collectValidStorySamples(perStorySummaries);
|
|
752
|
+
|
|
753
|
+
// signalCounts: prefer the raw-event roll-up; fall back to friction-only
|
|
754
|
+
// when the caller did not pass events.
|
|
755
|
+
const signalCounts = buildSignalCounts(opts.events, summaries);
|
|
756
|
+
|
|
757
|
+
const topHotspots = Array.isArray(opts.topHotspots)
|
|
758
|
+
? opts.topHotspots
|
|
759
|
+
: aggregateTopHotspots(summaries);
|
|
760
|
+
|
|
761
|
+
// mostFrictionStories: per-Story friction count, sorted desc, capped.
|
|
762
|
+
const mostFrictionStories = summaries
|
|
763
|
+
.map((s) => {
|
|
764
|
+
const counts = isObject(s.frictionByCategory)
|
|
765
|
+
? Object.values(s.frictionByCategory).reduce(
|
|
766
|
+
(acc, v) => acc + nonNegativeInt(v),
|
|
767
|
+
0,
|
|
768
|
+
)
|
|
769
|
+
: 0;
|
|
770
|
+
return {
|
|
771
|
+
storyId: nonNegativeInt(s.storyId),
|
|
772
|
+
frictionCount: counts,
|
|
773
|
+
};
|
|
774
|
+
})
|
|
775
|
+
.filter((row) => row.storyId > 0)
|
|
776
|
+
.sort((a, b) => b.frictionCount - a.frictionCount)
|
|
777
|
+
.slice(0, 5);
|
|
778
|
+
|
|
779
|
+
let waveParallelism;
|
|
780
|
+
if (Array.isArray(opts.waveParallelism)) {
|
|
781
|
+
waveParallelism = opts.waveParallelism.map((row) =>
|
|
782
|
+
coerceWaveParallelismRow(row),
|
|
783
|
+
);
|
|
784
|
+
} else if (opts.events) {
|
|
785
|
+
// Derive rows from the raw lifecycle event stream when the caller
|
|
786
|
+
// hands us the events but no pre-computed array (Story #3025 /
|
|
787
|
+
// Task #3028).
|
|
788
|
+
waveParallelism = computeWaveParallelismRows(opts.events, {
|
|
789
|
+
concurrencyCap: opts.concurrencyCap,
|
|
790
|
+
verifyConcurrencyCap: opts.verifyConcurrencyCap,
|
|
791
|
+
});
|
|
792
|
+
} else {
|
|
793
|
+
waveParallelism = [];
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return {
|
|
797
|
+
kind: 'epic-perf-report',
|
|
798
|
+
epicId,
|
|
799
|
+
generatedAt,
|
|
800
|
+
signalCounts,
|
|
801
|
+
waveParallelism,
|
|
802
|
+
topHotspots,
|
|
803
|
+
mostFrictionStories,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// ---------------------------------------------------------------------------
|
|
808
|
+
// Streaming entry-points (Epic #1181 / Story #1438 / Task #1460)
|
|
809
|
+
// ---------------------------------------------------------------------------
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Streaming variant of `computeStoryPerfSummary` that ingests events
|
|
813
|
+
* directly from `lib/signals/read.js` rather than expecting the caller
|
|
814
|
+
* to materialise the iterable upstream. The aggregation logic is
|
|
815
|
+
* identical — we collect the events through the shared reader and then
|
|
816
|
+
* delegate to `computeStoryPerfSummary`.
|
|
817
|
+
*
|
|
818
|
+
* Use this when the caller is the analyzer and already has the
|
|
819
|
+
* `{ epicId, storyId, config }` triple; use the pure
|
|
820
|
+
* `computeStoryPerfSummary(events, opts)` when the caller already
|
|
821
|
+
* holds an in-memory event array (tests, mock injections).
|
|
822
|
+
*
|
|
823
|
+
* @param {{
|
|
824
|
+
* storyId: number,
|
|
825
|
+
* epicId: number,
|
|
826
|
+
* closedAt?: string,
|
|
827
|
+
* phaseTiming?: object|null,
|
|
828
|
+
* config?: object,
|
|
829
|
+
* }} opts
|
|
830
|
+
* @returns {Promise<object>} StoryPerfSummary payload.
|
|
831
|
+
*/
|
|
832
|
+
export async function computeStoryPerfSummaryFromStore(opts) {
|
|
833
|
+
if (!opts || typeof opts !== 'object') {
|
|
834
|
+
throw new TypeError('computeStoryPerfSummaryFromStore: opts is required');
|
|
835
|
+
}
|
|
836
|
+
const { storyId, epicId, config } = opts;
|
|
837
|
+
const events = [];
|
|
838
|
+
for await (const evt of readSignals({
|
|
839
|
+
epic: Number(epicId),
|
|
840
|
+
story: Number(storyId),
|
|
841
|
+
config,
|
|
842
|
+
})) {
|
|
843
|
+
events.push(evt);
|
|
844
|
+
}
|
|
845
|
+
return computeStoryPerfSummary(events, {
|
|
846
|
+
storyId,
|
|
847
|
+
epicId,
|
|
848
|
+
closedAt: opts.closedAt,
|
|
849
|
+
phaseTiming: opts.phaseTiming,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Streaming variant of `computeEpicPerfReport` that ingests the
|
|
855
|
+
* raw-event roll-up directly from `lib/signals/read.js` (across every
|
|
856
|
+
* Story under the Epic). Per-Story summaries are still passed in by
|
|
857
|
+
* the caller — those are the canonical per-Story payloads upserted
|
|
858
|
+
* onto each Story ticket and not derivable from the raw stream alone
|
|
859
|
+
* (they fold in phase-timer data).
|
|
860
|
+
*
|
|
861
|
+
* @param {{
|
|
862
|
+
* epicId: number,
|
|
863
|
+
* perStorySummaries?: Iterable<object>,
|
|
864
|
+
* generatedAt?: string,
|
|
865
|
+
* waveParallelism?: Array<object>,
|
|
866
|
+
* topHotspots?: Array<object>,
|
|
867
|
+
* config?: object,
|
|
868
|
+
* }} opts
|
|
869
|
+
* @returns {Promise<object>} EpicPerfReport payload.
|
|
870
|
+
*/
|
|
871
|
+
export async function computeEpicPerfReportFromStore(opts) {
|
|
872
|
+
if (!opts || typeof opts !== 'object') {
|
|
873
|
+
throw new TypeError('computeEpicPerfReportFromStore: opts is required');
|
|
874
|
+
}
|
|
875
|
+
const { epicId, perStorySummaries, config } = opts;
|
|
876
|
+
const events = [];
|
|
877
|
+
for await (const evt of readSignals({ epic: Number(epicId), config })) {
|
|
878
|
+
events.push(evt);
|
|
879
|
+
}
|
|
880
|
+
return computeEpicPerfReport(perStorySummaries ?? [], {
|
|
881
|
+
epicId,
|
|
882
|
+
generatedAt: opts.generatedAt,
|
|
883
|
+
events,
|
|
884
|
+
waveParallelism: opts.waveParallelism,
|
|
885
|
+
topHotspots: opts.topHotspots,
|
|
886
|
+
});
|
|
887
|
+
}
|