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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* preflight-cache.js — Epic #3019 / Story #3027.
|
|
3
|
+
*
|
|
4
|
+
* Cache adapter for the snapshot/DAG envelope produced by
|
|
5
|
+
* `epic-deliver-preflight.js` so `epic-deliver-prepare.js` can skip the
|
|
6
|
+
* second walk of Epic → Feature → Story when the underlying Epic ticket
|
|
7
|
+
* has not drifted between the two operator invocations.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - Cache file lives at `<cwd>/temp/epic-<epicId>/preflight-snapshot.json`.
|
|
11
|
+
* - Envelope shape:
|
|
12
|
+
* {
|
|
13
|
+
* epicId: number,
|
|
14
|
+
* baseSha: string, // deterministic fingerprint of the Epic
|
|
15
|
+
* // snapshot returned by provider.getTicket
|
|
16
|
+
* capturedAt: string, // ISO-8601 timestamp
|
|
17
|
+
* epic: object, // the snapshot from runSnapshotPhase
|
|
18
|
+
* stories: object[], // the open Story tickets from runBuildWaveDagPhase
|
|
19
|
+
* waves: Array<Array<object>>, // wave DAG (ids + labels preserved)
|
|
20
|
+
* }
|
|
21
|
+
* - `computeBaseSha(epic)` derives the cache key from the same provider
|
|
22
|
+
* call (`getTicket(epicId)`) used to walk the hierarchy. The hash
|
|
23
|
+
* covers the Epic's id, body, labels (sorted), and (when present)
|
|
24
|
+
* `updatedAt` — the same fields that drive Story discovery and the
|
|
25
|
+
* acceptance-spec gate.
|
|
26
|
+
* - Cache miss is signalled by `readCache` returning `null`. Callers
|
|
27
|
+
* MUST fall back to a fresh snapshot/DAG pass.
|
|
28
|
+
* - `writeCache` creates the parent directory recursively and writes
|
|
29
|
+
* atomically (write to `<path>.tmp` then rename).
|
|
30
|
+
*
|
|
31
|
+
* The module is pure JS / node:fs — no provider dependency — so tests
|
|
32
|
+
* can exercise it without spinning up a GitHub mock.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { createHash } from 'node:crypto';
|
|
36
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
37
|
+
import path from 'node:path';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the on-disk path for an Epic's preflight cache.
|
|
41
|
+
*
|
|
42
|
+
* @param {{ epicId: number, cwd?: string }} args
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
export function preflightCachePath({ epicId, cwd }) {
|
|
46
|
+
if (!Number.isInteger(epicId) || epicId <= 0) {
|
|
47
|
+
throw new TypeError(
|
|
48
|
+
'preflightCachePath: epicId must be a positive integer',
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const root = cwd ?? process.cwd();
|
|
52
|
+
return path.join(root, 'temp', `epic-${epicId}`, 'preflight-snapshot.json');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Stable string fingerprint of an Epic snapshot. The hash is keyed on the
|
|
57
|
+
* exact fields `runSnapshotPhase` reads (`getTicket(epicId)` return value)
|
|
58
|
+
* so that any drift the snapshot phase would observe forces a cache miss.
|
|
59
|
+
*
|
|
60
|
+
* Labels are sorted to absorb GitHub's non-deterministic label order
|
|
61
|
+
* across responses. The hash is sha256; we return the full hex digest so
|
|
62
|
+
* the cache key is collision-resistant for the lifetime of a delivery.
|
|
63
|
+
*
|
|
64
|
+
* @param {{ id?: number|string, number?: number|string, body?: string, labels?: string[], updatedAt?: string }} epic
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
export function computeBaseSha(epic) {
|
|
68
|
+
if (!epic || typeof epic !== 'object') {
|
|
69
|
+
throw new TypeError('computeBaseSha: epic snapshot must be an object');
|
|
70
|
+
}
|
|
71
|
+
const id = epic.id ?? epic.number ?? null;
|
|
72
|
+
const body = typeof epic.body === 'string' ? epic.body : '';
|
|
73
|
+
const labels = Array.isArray(epic.labels)
|
|
74
|
+
? [...epic.labels].map(String).sort()
|
|
75
|
+
: [];
|
|
76
|
+
const updatedAt = typeof epic.updatedAt === 'string' ? epic.updatedAt : null;
|
|
77
|
+
const payload = JSON.stringify({ id, body, labels, updatedAt });
|
|
78
|
+
return createHash('sha256').update(payload).digest('hex');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read the cached preflight envelope from disk. Returns `null` when the
|
|
83
|
+
* file is missing or unreadable — callers MUST treat that as a cache
|
|
84
|
+
* miss and fall back to a fresh snapshot/DAG pass.
|
|
85
|
+
*
|
|
86
|
+
* The function intentionally swallows `ENOENT` and JSON-parse errors so
|
|
87
|
+
* a stale or partially-written file does not poison `epic-deliver-prepare`.
|
|
88
|
+
*
|
|
89
|
+
* @param {{ epicId: number, cwd?: string }} args
|
|
90
|
+
* @returns {Promise<object | null>}
|
|
91
|
+
*/
|
|
92
|
+
export async function readPreflightCache({ epicId, cwd }) {
|
|
93
|
+
const cachePath = preflightCachePath({ epicId, cwd });
|
|
94
|
+
let raw;
|
|
95
|
+
try {
|
|
96
|
+
raw = await readFile(cachePath, 'utf8');
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err && err.code === 'ENOENT') return null;
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(raw);
|
|
103
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
104
|
+
if (parsed.epicId !== epicId) return null;
|
|
105
|
+
if (typeof parsed.baseSha !== 'string' || !parsed.baseSha) return null;
|
|
106
|
+
if (!parsed.epic || typeof parsed.epic !== 'object') return null;
|
|
107
|
+
if (!Array.isArray(parsed.stories)) return null;
|
|
108
|
+
if (!Array.isArray(parsed.waves)) return null;
|
|
109
|
+
return parsed;
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Persist the preflight envelope to disk. Writes atomically via tmp +
|
|
117
|
+
* rename so concurrent readers never observe a partial file.
|
|
118
|
+
*
|
|
119
|
+
* @param {{ epicId: number, baseSha: string, epic: object, stories: object[], waves: Array<Array<object>>, cwd?: string, capturedAt?: string }} args
|
|
120
|
+
* @returns {Promise<{ cachePath: string, baseSha: string }>}
|
|
121
|
+
*/
|
|
122
|
+
export async function writePreflightCache({
|
|
123
|
+
epicId,
|
|
124
|
+
baseSha,
|
|
125
|
+
epic,
|
|
126
|
+
stories,
|
|
127
|
+
waves,
|
|
128
|
+
cwd,
|
|
129
|
+
capturedAt,
|
|
130
|
+
}) {
|
|
131
|
+
if (!Number.isInteger(epicId) || epicId <= 0) {
|
|
132
|
+
throw new TypeError(
|
|
133
|
+
'writePreflightCache: epicId must be a positive integer',
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (typeof baseSha !== 'string' || !baseSha) {
|
|
137
|
+
throw new TypeError(
|
|
138
|
+
'writePreflightCache: baseSha must be a non-empty string',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (!epic || typeof epic !== 'object') {
|
|
142
|
+
throw new TypeError('writePreflightCache: epic must be an object');
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(stories)) {
|
|
145
|
+
throw new TypeError('writePreflightCache: stories must be an array');
|
|
146
|
+
}
|
|
147
|
+
if (!Array.isArray(waves)) {
|
|
148
|
+
throw new TypeError('writePreflightCache: waves must be an array');
|
|
149
|
+
}
|
|
150
|
+
const cachePath = preflightCachePath({ epicId, cwd });
|
|
151
|
+
await mkdir(path.dirname(cachePath), { recursive: true });
|
|
152
|
+
const envelope = {
|
|
153
|
+
epicId,
|
|
154
|
+
baseSha,
|
|
155
|
+
capturedAt: capturedAt ?? new Date().toISOString(),
|
|
156
|
+
epic,
|
|
157
|
+
stories,
|
|
158
|
+
waves,
|
|
159
|
+
};
|
|
160
|
+
const tmp = `${cachePath}.tmp`;
|
|
161
|
+
await writeFile(tmp, `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
|
|
162
|
+
await rename(tmp, cachePath);
|
|
163
|
+
return { cachePath, baseSha };
|
|
164
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reassert-status-column — re-fire `ColumnSync` against a ticket's
|
|
3
|
+
* current label set so the GitHub Projects v2 Status column matches the
|
|
4
|
+
* orchestrator's view (Story #2845, hardened in Story #2876).
|
|
5
|
+
*
|
|
6
|
+
* Why a dedicated helper:
|
|
7
|
+
* `transitionTicketState` already calls `syncProjectStatusColumn` at
|
|
8
|
+
* label-flip time, but the GitHub built-in `Pull request merged` /
|
|
9
|
+
* `Pull request linked to issue` workflows fire ~minutes after the
|
|
10
|
+
* PR auto-merges and overwrite Status. The orchestrator's close path
|
|
11
|
+
* has long-since exited by then, so "the last write" is the bot's.
|
|
12
|
+
* This helper is invoked AFTER the merge confirmation step (via the
|
|
13
|
+
* `resync-status-column.js` CLI the `/single-story-deliver` workflow
|
|
14
|
+
* doc calls) to reassert authority and win the race deterministically.
|
|
15
|
+
*
|
|
16
|
+
* Operators who disable the conflicting bot workflows (via the
|
|
17
|
+
* `--reap-conflicting-workflows` bootstrap flag) get the same outcome
|
|
18
|
+
* without needing the re-sync. The helper is defense-in-depth and is
|
|
19
|
+
* cheap to fire — Story #2876 added a bounded poll-and-retry loop so
|
|
20
|
+
* the defense actually works against an asynchronous bot overwrite
|
|
21
|
+
* that lands after the initial mutation. Without the loop, a one-shot
|
|
22
|
+
* mutation routinely lost the race (reproduced on Story #2871 /
|
|
23
|
+
* PR #2872).
|
|
24
|
+
*
|
|
25
|
+
* Surface:
|
|
26
|
+
* - {@link reassertStatusColumn} — read the ticket's current labels,
|
|
27
|
+
* pick the canonical column via {@link columnForLabels}, push it
|
|
28
|
+
* via `ColumnSync.sync`, then poll the live Status for a bounded
|
|
29
|
+
* window and re-fire on drift. Returns the sync envelope augmented
|
|
30
|
+
* with `attempts`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { ColumnSync, columnForLabels } from './column-sync.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Default total mutation attempts (including the initial sync). At
|
|
37
|
+
* `pollDelayMs = 5000`, four attempts cover a ~15 s defense window —
|
|
38
|
+
* long enough to outlast the typical GitHub built-in workflow fire
|
|
39
|
+
* timing without ballooning the close-path wall clock.
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_POLL_ATTEMPTS = 4;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Default delay between drift checks (ms). Five seconds is a sweet
|
|
45
|
+
* spot: short enough that the operator doesn't perceive the close
|
|
46
|
+
* path as hung, long enough that one re-fire usually wins the race.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_POLL_DELAY_MS = 5000;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Production sleep function. Tests inject `sleepFn: () => Promise.resolve()`
|
|
52
|
+
* so the poll loop runs instantly.
|
|
53
|
+
*
|
|
54
|
+
* @param {number} ms
|
|
55
|
+
* @returns {Promise<void>}
|
|
56
|
+
*/
|
|
57
|
+
function defaultSleep(ms) {
|
|
58
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Re-assert the Status column for a single ticket. Reads the ticket's
|
|
63
|
+
* current labels so the caller doesn't have to pass them (the typical
|
|
64
|
+
* use site is a post-merge CLI that knows the ticket id only).
|
|
65
|
+
*
|
|
66
|
+
* Returns the same envelope shape as `ColumnSync.sync`, augmented with
|
|
67
|
+
* `attempts` (the number of mutations actually fired):
|
|
68
|
+
* - `{ status: 'synced', column, attempts }` — the mutation landed
|
|
69
|
+
* and the drift-check confirmed Status matches the target. If
|
|
70
|
+
* drift was detected and re-fired, `attempts` reflects the total
|
|
71
|
+
* mutation count.
|
|
72
|
+
* - `{ status: 'drifted', column, attempts }` — the helper exhausted
|
|
73
|
+
* `pollAttempts` and the live Status still didn't match the
|
|
74
|
+
* target. Returned, not thrown — the caller decides whether to
|
|
75
|
+
* escalate. `column` carries the target column the helper was
|
|
76
|
+
* trying to set.
|
|
77
|
+
* - `{ status: 'skipped', reason }` — no-op for an enumerated reason
|
|
78
|
+
* (`no-matching-label`, `no-project`, `no-meta`, `no-option-<col>`,
|
|
79
|
+
* `not-on-project`). Skip paths short-circuit before the poll loop.
|
|
80
|
+
*
|
|
81
|
+
* Throws when the provider is unusable. Other failures (GraphQL,
|
|
82
|
+
* network) propagate from `ColumnSync.sync` and `provider.getTicket` —
|
|
83
|
+
* callers wrap in try/catch if they want best-effort semantics.
|
|
84
|
+
*
|
|
85
|
+
* @param {{
|
|
86
|
+
* provider: { getTicket: Function, graphql: Function, owner: string, repo: string, projectNumber?: number|null },
|
|
87
|
+
* ticketId: number,
|
|
88
|
+
* logger?: { info: Function, warn: Function },
|
|
89
|
+
* pollAttempts?: number,
|
|
90
|
+
* pollDelayMs?: number,
|
|
91
|
+
* sleepFn?: (ms: number) => Promise<void>,
|
|
92
|
+
* }} args
|
|
93
|
+
* @returns {Promise<{ status: string, column?: string, reason?: string, attempts?: number }>}
|
|
94
|
+
*/
|
|
95
|
+
export async function reassertStatusColumn(args) {
|
|
96
|
+
const {
|
|
97
|
+
provider,
|
|
98
|
+
ticketId,
|
|
99
|
+
logger,
|
|
100
|
+
pollAttempts = DEFAULT_POLL_ATTEMPTS,
|
|
101
|
+
pollDelayMs = DEFAULT_POLL_DELAY_MS,
|
|
102
|
+
sleepFn = defaultSleep,
|
|
103
|
+
} = args ?? {};
|
|
104
|
+
if (!provider || typeof provider.getTicket !== 'function') {
|
|
105
|
+
throw new TypeError(
|
|
106
|
+
'reassertStatusColumn requires a provider with getTicket',
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (typeof provider.graphql !== 'function') {
|
|
110
|
+
throw new TypeError(
|
|
111
|
+
'reassertStatusColumn requires a provider with graphql (for ColumnSync)',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (!Number.isInteger(ticketId) || ticketId <= 0) {
|
|
115
|
+
throw new TypeError(
|
|
116
|
+
'reassertStatusColumn requires a positive integer ticketId',
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
if (!Number.isInteger(pollAttempts) || pollAttempts <= 0) {
|
|
120
|
+
throw new TypeError(
|
|
121
|
+
'reassertStatusColumn requires a positive integer pollAttempts',
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const ticket = await provider.getTicket(ticketId);
|
|
126
|
+
const labels = Array.isArray(ticket?.labels) ? ticket.labels : [];
|
|
127
|
+
const targetColumn = columnForLabels(labels);
|
|
128
|
+
if (!targetColumn) {
|
|
129
|
+
return { status: 'skipped', reason: 'no-matching-label' };
|
|
130
|
+
}
|
|
131
|
+
const sync = new ColumnSync({ provider, logger: logger ?? console });
|
|
132
|
+
|
|
133
|
+
// First attempt — always fires through ColumnSync.sync so the skip
|
|
134
|
+
// paths (no-project / no-meta / no-option-<col> / not-on-project)
|
|
135
|
+
// short-circuit before we enter the poll loop.
|
|
136
|
+
const initial = await sync.sync(ticketId, labels);
|
|
137
|
+
if (initial.status !== 'synced') {
|
|
138
|
+
return initial;
|
|
139
|
+
}
|
|
140
|
+
let attempts = 1;
|
|
141
|
+
let lastEnvelope = { ...initial, attempts };
|
|
142
|
+
|
|
143
|
+
// Drift loop — read live Status, re-fire if it doesn't match the
|
|
144
|
+
// target column. Bounded by pollAttempts so a hostile bot can't
|
|
145
|
+
// stall the close path indefinitely.
|
|
146
|
+
for (let i = 1; i < pollAttempts; i += 1) {
|
|
147
|
+
await sleepFn(pollDelayMs);
|
|
148
|
+
let current;
|
|
149
|
+
try {
|
|
150
|
+
current = await sync.readCurrentColumn(ticketId);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logger?.warn?.(
|
|
153
|
+
`[reassertStatusColumn] drift-check #${i} failed (transient): ${
|
|
154
|
+
err?.message ?? err
|
|
155
|
+
}. Continuing.`,
|
|
156
|
+
);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (current === targetColumn) {
|
|
160
|
+
// Sticky confirmation — return the existing envelope.
|
|
161
|
+
return lastEnvelope;
|
|
162
|
+
}
|
|
163
|
+
logger?.info?.(
|
|
164
|
+
`[reassertStatusColumn] drift detected on attempt ${i} (current=${
|
|
165
|
+
current ?? '<null>'
|
|
166
|
+
} target=${targetColumn}); re-firing.`,
|
|
167
|
+
);
|
|
168
|
+
try {
|
|
169
|
+
const next = await sync.sync(ticketId, labels);
|
|
170
|
+
attempts += 1;
|
|
171
|
+
// Skip envelopes during the retry loop are unexpected once we've
|
|
172
|
+
// already synced once; preserve their shape but stop retrying.
|
|
173
|
+
if (next.status !== 'synced') {
|
|
174
|
+
return { ...next, attempts };
|
|
175
|
+
}
|
|
176
|
+
lastEnvelope = { ...next, attempts };
|
|
177
|
+
} catch (err) {
|
|
178
|
+
logger?.warn?.(
|
|
179
|
+
`[reassertStatusColumn] re-fire attempt ${i} failed (transient): ${
|
|
180
|
+
err?.message ?? err
|
|
181
|
+
}. Continuing.`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Exhausted the poll budget — final drift read to report the
|
|
187
|
+
// honest outcome to the caller.
|
|
188
|
+
let final;
|
|
189
|
+
try {
|
|
190
|
+
final = await sync.readCurrentColumn(ticketId);
|
|
191
|
+
} catch {
|
|
192
|
+
final = null;
|
|
193
|
+
}
|
|
194
|
+
if (final === targetColumn) {
|
|
195
|
+
return lastEnvelope;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
status: 'drifted',
|
|
199
|
+
column: targetColumn,
|
|
200
|
+
attempts,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/orchestration/reconciler.js — Ticket Hierarchy Reconciliation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Logger } from '../Logger.js';
|
|
6
|
+
import { AGENT_LABELS, TYPE_LABELS } from '../label-constants.js';
|
|
7
|
+
import { concurrentMap } from '../util/concurrent-map.js';
|
|
8
|
+
import { STATE_LABELS } from './ticketing.js';
|
|
9
|
+
|
|
10
|
+
const AGENT_DONE_LABEL = STATE_LABELS.DONE;
|
|
11
|
+
|
|
12
|
+
// Inlined parent-id parser. The reconciler only needs the direct parent
|
|
13
|
+
// reference scraped from a ticket body's `parent: #N` trailer; pulling the
|
|
14
|
+
// helper inline keeps reconciler.js self-contained for the 3-tier
|
|
15
|
+
// hierarchy walk (Stories → Features).
|
|
16
|
+
const PARENT_ID_PATTERN = /^parent:\s*#(\d+)/m;
|
|
17
|
+
|
|
18
|
+
function parseParentIdFromBody(body) {
|
|
19
|
+
const match = (body ?? '').match(PARENT_ID_PATTERN);
|
|
20
|
+
return match ? Number.parseInt(match[1], 10) : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Cap=4 — bounded parallelism for ticket-update fan-outs. Reconciliation
|
|
24
|
+
// iterates an Epic-sized set of independent GitHub mutations; cap matches
|
|
25
|
+
// the established pattern across the orchestration layer (wave-gate,
|
|
26
|
+
// progress-reporter, sub-issue link reconcile) and stays well under the
|
|
27
|
+
// secondary rate-limit ceiling observed for issue PATCHes.
|
|
28
|
+
const RECONCILE_CONCURRENCY = 4;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reconcile the ticket hierarchy bottom-up (Stories → Features).
|
|
32
|
+
*
|
|
33
|
+
* Walks every Story and Feature under the Epic; if all children of a
|
|
34
|
+
* container are done, closes the container and applies `agent::done`.
|
|
35
|
+
*
|
|
36
|
+
* Epic auto-closure is intentionally excluded — Epics close only through
|
|
37
|
+
* the formal `/epic-deliver` workflow.
|
|
38
|
+
*
|
|
39
|
+
* Per-ticket provider failures are logged and swallowed so a single bad
|
|
40
|
+
* ticket cannot halt reconciliation across the rest of the graph.
|
|
41
|
+
*
|
|
42
|
+
* @param {import('../ITicketingProvider.js').ITicketingProvider} provider Ticketing provider.
|
|
43
|
+
* @param {number} _epicId Epic id (reserved — currently unused; kept for call-site stability).
|
|
44
|
+
* @param {object} _epic Epic ticket record (reserved — currently unused).
|
|
45
|
+
* @param {object[]} allTickets Every ticket under the Epic.
|
|
46
|
+
* @param {boolean} dryRun When true, mutate nothing.
|
|
47
|
+
* @returns {Promise<void>}
|
|
48
|
+
*/
|
|
49
|
+
export async function reconcileHierarchy(
|
|
50
|
+
provider,
|
|
51
|
+
_epicId,
|
|
52
|
+
_epic,
|
|
53
|
+
allTickets,
|
|
54
|
+
dryRun,
|
|
55
|
+
) {
|
|
56
|
+
const ticketMap = new Map(allTickets.map((t) => [t.id, t]));
|
|
57
|
+
|
|
58
|
+
const childrenOf = new Map();
|
|
59
|
+
for (const ticket of allTickets) {
|
|
60
|
+
const parentId = parseParentIdFromBody(ticket.body);
|
|
61
|
+
if (parentId != null) {
|
|
62
|
+
if (!childrenOf.has(parentId)) childrenOf.set(parentId, []);
|
|
63
|
+
childrenOf.get(parentId).push(ticket.id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isDone(ticketId) {
|
|
68
|
+
const t = ticketMap.get(ticketId);
|
|
69
|
+
if (!t) return false;
|
|
70
|
+
return (
|
|
71
|
+
t.state === 'closed' ||
|
|
72
|
+
(t.labelSet ?? new Set(t.labels)).has(AGENT_DONE_LABEL)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function shouldClose(id) {
|
|
77
|
+
const ticket = ticketMap.get(id);
|
|
78
|
+
if (!ticket || ticket.state === 'closed') return null;
|
|
79
|
+
const children = childrenOf.get(id) ?? [];
|
|
80
|
+
if (children.length === 0) return null;
|
|
81
|
+
if (!children.every((cid) => isDone(cid))) return null;
|
|
82
|
+
return ticket;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function applyClose(id, typeName, ticket) {
|
|
86
|
+
try {
|
|
87
|
+
await provider.updateTicket(id, {
|
|
88
|
+
labels: {
|
|
89
|
+
add: [AGENT_DONE_LABEL],
|
|
90
|
+
remove: [AGENT_LABELS.READY, AGENT_LABELS.EXECUTING],
|
|
91
|
+
},
|
|
92
|
+
state: 'closed',
|
|
93
|
+
state_reason: 'completed',
|
|
94
|
+
});
|
|
95
|
+
ticket.state = 'closed';
|
|
96
|
+
Logger.info(`✅ ${typeName} #${id} closed and marked agent::done.`);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
Logger.warn(`Failed to close ${typeName} #${id}: ${err.message}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function maybeClose(id, typeName) {
|
|
103
|
+
const ticket = shouldClose(id);
|
|
104
|
+
if (!ticket) return;
|
|
105
|
+
Logger.info(
|
|
106
|
+
`All children of ${typeName} #${id} "${ticket.title}" are done. Closing...`,
|
|
107
|
+
);
|
|
108
|
+
if (dryRun) {
|
|
109
|
+
Logger.info(
|
|
110
|
+
`[DRY-RUN] Would close ${typeName} #${id} and set agent::done.`,
|
|
111
|
+
);
|
|
112
|
+
ticket.state = 'closed';
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await applyClose(id, typeName, ticket);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const storyIds = allTickets
|
|
119
|
+
.filter((t) => (t.labelSet ?? new Set(t.labels)).has(TYPE_LABELS.STORY))
|
|
120
|
+
.map((t) => t.id);
|
|
121
|
+
const featureIds = allTickets
|
|
122
|
+
.filter((t) => (t.labelSet ?? new Set(t.labels)).has(TYPE_LABELS.FEATURE))
|
|
123
|
+
.map((t) => t.id);
|
|
124
|
+
|
|
125
|
+
// cap=4 — Stories are container leaves of this reconcile (their children
|
|
126
|
+
// are Tasks already settled by reconcileClosedTasks); independent close
|
|
127
|
+
// mutations can fan out without ordering. Features stay sequential because
|
|
128
|
+
// a Feature may parent another Feature, and bottom-up close depends on
|
|
129
|
+
// child-Feature state already being mutated in the same pass.
|
|
130
|
+
await concurrentMap(storyIds, (id) => maybeClose(id, 'Story'), {
|
|
131
|
+
concurrency: RECONCILE_CONCURRENCY,
|
|
132
|
+
});
|
|
133
|
+
for (const id of featureIds) await maybeClose(id, 'Feature');
|
|
134
|
+
|
|
135
|
+
// EXCLUSION: Epic auto-closure removed.
|
|
136
|
+
// The Epic ticket now stays open until the formal /epic-deliver workflow is executed.
|
|
137
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// .agents/scripts/lib/orchestration/recurring-failure-detector.js
|
|
2
|
+
/**
|
|
3
|
+
* recurring-failure-detector.js — Story #3062 / Epic #3051.
|
|
4
|
+
*
|
|
5
|
+
* Pure helper that scans a per-Epic lifecycle ledger for
|
|
6
|
+
* `close-validate.end` `emitted` records and groups them by `failedGate`.
|
|
7
|
+
* Returns one finding per gate that appears in **two or more distinct
|
|
8
|
+
* Stories** within the same Epic. Findings are the substrate
|
|
9
|
+
* `/epic-deliver` consumes when upserting the cross-Story
|
|
10
|
+
* `recurring-failure-class` structured comment on the Epic ticket.
|
|
11
|
+
*
|
|
12
|
+
* The helper is intentionally pure: no GitHub I/O, no global state. It
|
|
13
|
+
* accepts either a pre-parsed `records[]` array (the hot path used by
|
|
14
|
+
* `wave-tick` once it has already read the ledger for its in-flight
|
|
15
|
+
* reconciliation) or a `ledgerPath` string (the convenience path that
|
|
16
|
+
* reads + parses NDJSON on its own). When both are supplied,
|
|
17
|
+
* `records` wins — the caller already paid the parse cost.
|
|
18
|
+
*
|
|
19
|
+
* Finding shape (sorted lexicographically by `gate` for determinism):
|
|
20
|
+
*
|
|
21
|
+
* { gate: string, storyIds: number[], firstSeenAt: string, lastSeenAt: string }
|
|
22
|
+
*
|
|
23
|
+
* - `storyIds` is the deduplicated, ascending list of Story IDs that
|
|
24
|
+
* recorded a `close-validate.end` with `ok: false` and this gate.
|
|
25
|
+
* - `firstSeenAt` / `lastSeenAt` are ISO-8601 strings pulled from the
|
|
26
|
+
* ledger record's top-level `ts` (set by `LedgerWriter.buildEmitted`),
|
|
27
|
+
* which is the canonical wall-clock for the event.
|
|
28
|
+
*
|
|
29
|
+
* @module lib/orchestration/recurring-failure-detector
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Detect recurring failure classes across Stories within one Epic.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} epicId Positive integer — the Epic the ledger belongs
|
|
38
|
+
* to. Used only for validation (the helper does not filter records by
|
|
39
|
+
* epicId; the ledger is already epic-scoped on disk).
|
|
40
|
+
* @param {object} [opts]
|
|
41
|
+
* @param {Array<object>} [opts.records] Pre-parsed NDJSON records. Each
|
|
42
|
+
* entry is the object emitted by `LedgerWriter.buildEmitted`
|
|
43
|
+
* (`{ kind, seqId, ts, event, payload }`).
|
|
44
|
+
* @param {string} [opts.ledgerPath] Filesystem path to `lifecycle.ndjson`.
|
|
45
|
+
* Read + parsed only when `records` is absent. A missing file returns
|
|
46
|
+
* an empty findings array (no throw).
|
|
47
|
+
* @returns {Array<{gate: string, storyIds: number[], firstSeenAt: string, lastSeenAt: string}>}
|
|
48
|
+
*/
|
|
49
|
+
export function detectRecurringFailures(epicId, opts = {}) {
|
|
50
|
+
if (!Number.isInteger(epicId) || epicId <= 0) {
|
|
51
|
+
throw new TypeError(
|
|
52
|
+
'detectRecurringFailures: epicId must be a positive integer',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const records = resolveRecords(opts);
|
|
56
|
+
if (records.length === 0) return [];
|
|
57
|
+
|
|
58
|
+
/** @type {Map<string, {storyIds: Set<number>, firstSeenAt: string, lastSeenAt: string}>} */
|
|
59
|
+
const byGate = new Map();
|
|
60
|
+
for (const record of records) {
|
|
61
|
+
const finding = extractFailedGateRecord(record);
|
|
62
|
+
if (!finding) continue;
|
|
63
|
+
const { gate, storyId, ts } = finding;
|
|
64
|
+
const bucket = byGate.get(gate);
|
|
65
|
+
if (!bucket) {
|
|
66
|
+
byGate.set(gate, {
|
|
67
|
+
storyIds: new Set([storyId]),
|
|
68
|
+
firstSeenAt: ts,
|
|
69
|
+
lastSeenAt: ts,
|
|
70
|
+
});
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
bucket.storyIds.add(storyId);
|
|
74
|
+
if (ts < bucket.firstSeenAt) bucket.firstSeenAt = ts;
|
|
75
|
+
if (ts > bucket.lastSeenAt) bucket.lastSeenAt = ts;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const findings = [];
|
|
79
|
+
for (const [gate, bucket] of byGate) {
|
|
80
|
+
if (bucket.storyIds.size < 2) continue;
|
|
81
|
+
findings.push({
|
|
82
|
+
gate,
|
|
83
|
+
storyIds: Array.from(bucket.storyIds).sort((a, b) => a - b),
|
|
84
|
+
firstSeenAt: bucket.firstSeenAt,
|
|
85
|
+
lastSeenAt: bucket.lastSeenAt,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
findings.sort((a, b) => (a.gate < b.gate ? -1 : a.gate > b.gate ? 1 : 0));
|
|
89
|
+
return findings;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the records array from caller opts. Pre-parsed `records` wins
|
|
94
|
+
* over a `ledgerPath` so callers that already loaded the file (e.g.
|
|
95
|
+
* `wave-tick.js` doing its in-flight reconciliation) don't pay the parse
|
|
96
|
+
* cost twice. A missing ledger file is treated as "no records" — the
|
|
97
|
+
* tick must remain stateless and must not throw when nothing has
|
|
98
|
+
* happened yet on this Epic.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} opts
|
|
101
|
+
* @returns {Array<object>}
|
|
102
|
+
*/
|
|
103
|
+
function resolveRecords(opts) {
|
|
104
|
+
if (Array.isArray(opts.records)) return opts.records;
|
|
105
|
+
if (typeof opts.ledgerPath !== 'string' || opts.ledgerPath.length === 0) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
if (!existsSync(opts.ledgerPath)) return [];
|
|
109
|
+
let raw;
|
|
110
|
+
try {
|
|
111
|
+
raw = readFileSync(opts.ledgerPath, 'utf8');
|
|
112
|
+
} catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
if (!raw) return [];
|
|
116
|
+
const out = [];
|
|
117
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
118
|
+
if (!line) continue;
|
|
119
|
+
try {
|
|
120
|
+
out.push(JSON.parse(line));
|
|
121
|
+
} catch {
|
|
122
|
+
// Malformed line — skip; the ledger is append-only NDJSON and a
|
|
123
|
+
// partial write at process death is the only realistic source of
|
|
124
|
+
// un-parseable lines. The next tick will see the completed line.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Decide whether a ledger record is a `close-validate.end` `emitted`
|
|
132
|
+
* payload carrying a failed gate, and if so extract the
|
|
133
|
+
* `(gate, storyId, ts)` triple the bucketing loop consumes. Returns
|
|
134
|
+
* `null` for any record that doesn't satisfy every guard.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} record
|
|
137
|
+
* @returns {{gate: string, storyId: number, ts: string} | null}
|
|
138
|
+
*/
|
|
139
|
+
function extractFailedGateRecord(record) {
|
|
140
|
+
if (!record || record.kind !== 'emitted') return null;
|
|
141
|
+
if (record.event !== 'close-validate.end') return null;
|
|
142
|
+
const payload = record.payload;
|
|
143
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
144
|
+
if (payload.ok !== false) return null;
|
|
145
|
+
const gate = payload.failedGate;
|
|
146
|
+
if (typeof gate !== 'string' || gate.length === 0) return null;
|
|
147
|
+
const storyId = payload.storyId;
|
|
148
|
+
if (!Number.isInteger(storyId) || storyId <= 0) return null;
|
|
149
|
+
const ts = typeof record.ts === 'string' && record.ts ? record.ts : '';
|
|
150
|
+
if (!ts) return null;
|
|
151
|
+
return { gate, storyId, ts };
|
|
152
|
+
}
|