mandrel 1.94.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +328 -28
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +3 -53
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -8
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- package/.agents/workflows/helpers/single-story-deliver.md +0 -557
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dead-exports-knip.js — the knip driver behind the dead-export ratchet.
|
|
3
|
+
*
|
|
4
|
+
* Owns everything about talking to knip and normalising what comes back:
|
|
5
|
+
* spawning it, reading a pre-captured report, and flattening its report into
|
|
6
|
+
* `{ file, symbol }` rows. `check-dead-exports.js` stays a thin CLI over this.
|
|
7
|
+
*
|
|
8
|
+
* @module lib/dead-exports-knip
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import process from 'node:process';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Spawn `npx knip --reporter json --no-progress` and return the parsed
|
|
17
|
+
* envelope. Never throws — the caller logs the error and treats current rows as
|
|
18
|
+
* empty, which surfaces every baseline row as "removed": loud, but safe.
|
|
19
|
+
*
|
|
20
|
+
* `production` adds knip's `--production` flag, which restricts analysis to
|
|
21
|
+
* entry/project patterns carrying the `!` suffix in `knip.json`. The test globs
|
|
22
|
+
* deliberately lack that suffix, so production mode drops them as entry points
|
|
23
|
+
* and an export reachable only from a test reads as dead. Without those
|
|
24
|
+
* suffixes production mode has no entry patterns at all and reports nothing —
|
|
25
|
+
* `knip.json` and this flag are a matched pair.
|
|
26
|
+
*
|
|
27
|
+
* Exported as a hook so tests can stub the spawn without a working knip
|
|
28
|
+
* workspace.
|
|
29
|
+
*
|
|
30
|
+
* @param {{ cwd?: string, spawn?: typeof spawnSync, production?: boolean }} [opts]
|
|
31
|
+
* @returns {{ ok: true, envelope: unknown } | { ok: false, error: string }}
|
|
32
|
+
*/
|
|
33
|
+
export function runKnip({
|
|
34
|
+
cwd = process.cwd(),
|
|
35
|
+
spawn = spawnSync,
|
|
36
|
+
production = false,
|
|
37
|
+
} = {}) {
|
|
38
|
+
const args = ['knip', '--reporter', 'json', '--no-progress'];
|
|
39
|
+
if (production) args.push('--production');
|
|
40
|
+
const result = spawn(process.platform === 'win32' ? 'npx.cmd' : 'npx', args, {
|
|
41
|
+
cwd,
|
|
42
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
43
|
+
encoding: 'utf-8',
|
|
44
|
+
shell: process.platform === 'win32',
|
|
45
|
+
});
|
|
46
|
+
if (result.error) {
|
|
47
|
+
return { ok: false, error: `spawn failed: ${result.error.message}` };
|
|
48
|
+
}
|
|
49
|
+
const stdout = typeof result.stdout === 'string' ? result.stdout : '';
|
|
50
|
+
if (stdout.trim().length === 0) {
|
|
51
|
+
return { ok: false, error: 'knip produced empty stdout' };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return { ok: true, envelope: JSON.parse(stdout) };
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: `knip JSON parse failed: ${err?.message ?? err}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Read a pre-captured knip JSON envelope from disk (the `--knip-output` test
|
|
65
|
+
* seam). Returns the parsed envelope or `null` on failure.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} filePath
|
|
68
|
+
* @returns {unknown}
|
|
69
|
+
*/
|
|
70
|
+
export function readKnipOutput(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Flatten knip's `--reporter json` output into `{ file, symbol }` rows. Knip
|
|
80
|
+
* emits `{ issues: [{ file, exports: [{ name, ... }], ... }, ...] }`. Only
|
|
81
|
+
* `exports` rows are mapped — the ratchet ignores file-, dependency- and
|
|
82
|
+
* duplicate-level issues, which knip surfaces under separate `rules` keys.
|
|
83
|
+
*
|
|
84
|
+
* @param {unknown} knipEnvelope The parsed knip JSON report.
|
|
85
|
+
* @returns {Array<{ file: string, symbol: string }>}
|
|
86
|
+
*/
|
|
87
|
+
export function extractRowsFromKnip(knipEnvelope) {
|
|
88
|
+
const rows = [];
|
|
89
|
+
if (!knipEnvelope || typeof knipEnvelope !== 'object') return rows;
|
|
90
|
+
const issues = Array.isArray(knipEnvelope.issues) ? knipEnvelope.issues : [];
|
|
91
|
+
for (const issue of issues) {
|
|
92
|
+
const file = issue?.file;
|
|
93
|
+
if (typeof file !== 'string' || file.length === 0) continue;
|
|
94
|
+
const exports_ = Array.isArray(issue.exports) ? issue.exports : [];
|
|
95
|
+
for (const e of exports_) {
|
|
96
|
+
const symbol =
|
|
97
|
+
(e && typeof e.name === 'string' && e.name) ||
|
|
98
|
+
(e && typeof e.symbol === 'string' && e.symbol) ||
|
|
99
|
+
null;
|
|
100
|
+
if (!symbol) continue;
|
|
101
|
+
rows.push({ file, symbol });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return rows;
|
|
105
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dead-exports-mode.js — the two passes of the dead-export ratchet.
|
|
3
|
+
*
|
|
4
|
+
* The gate runs twice with different reachability assumptions, and each pass
|
|
5
|
+
* needs a matched triple: which knip invocation to make, which baseline to
|
|
6
|
+
* ratchet against, and how to label its output. Resolving that triple in one
|
|
7
|
+
* place keeps the three from drifting apart — a pass that ran production knip
|
|
8
|
+
* against the default baseline would report every test-only export as newly
|
|
9
|
+
* dead and fail the build for no reason.
|
|
10
|
+
*
|
|
11
|
+
* - **default** — `tests/**` are knip entry points, so an export imported only
|
|
12
|
+
* by a test reads as used. This is the historical gate.
|
|
13
|
+
* - **production** — knip's `--production` drops the test entries, so an export
|
|
14
|
+
* no production code reaches reads as dead. Depends on the `!` pattern
|
|
15
|
+
* suffixes in `knip.json`; without them production mode has no entry points
|
|
16
|
+
* and silently reports nothing.
|
|
17
|
+
*
|
|
18
|
+
* The two ratchet against separate baselines on purpose. This repo sanctions
|
|
19
|
+
* exporting purely for a test (`.agents/rules/test-seams.md`), so the
|
|
20
|
+
* production row set is large and mostly intentional; merging it into the
|
|
21
|
+
* default baseline would destroy that baseline's meaning.
|
|
22
|
+
*
|
|
23
|
+
* @module lib/dead-exports-mode
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
|
|
28
|
+
/** Baseline for the default pass (test entry points included). */
|
|
29
|
+
const DEFAULT_BASELINE = path.join('baselines', 'dead-exports.json');
|
|
30
|
+
|
|
31
|
+
/** Baseline for the `--production` pass (test-only-importer discount). */
|
|
32
|
+
const PRODUCTION_BASELINE = path.join(
|
|
33
|
+
'baselines',
|
|
34
|
+
'dead-exports-production.json',
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the baseline / label / mode-tag triple for a pass.
|
|
39
|
+
*
|
|
40
|
+
* @param {boolean} production
|
|
41
|
+
* @returns {{ mode: 'production'|'default', label: string, baseline: string }}
|
|
42
|
+
*/
|
|
43
|
+
export function resolveDeadExportsMode(production) {
|
|
44
|
+
return production
|
|
45
|
+
? {
|
|
46
|
+
mode: 'production',
|
|
47
|
+
label: 'dead-exports:production',
|
|
48
|
+
baseline: PRODUCTION_BASELINE,
|
|
49
|
+
}
|
|
50
|
+
: { mode: 'default', label: 'dead-exports', baseline: DEFAULT_BASELINE };
|
|
51
|
+
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* dependency-parser.js — Shared Dependency & Metadata Parsing Utilities
|
|
3
3
|
*
|
|
4
4
|
* Canonical implementation of dependency-related regex parsing and
|
|
5
|
-
* ticket metadata extraction
|
|
6
|
-
*
|
|
5
|
+
* ticket metadata extraction, shared by the ticketing provider and the
|
|
6
|
+
* orchestration layer (e.g. providers/github/tickets.js,
|
|
7
|
+
* lib/story-adjacency.js, lib/branch-name-guard.js).
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* - `alwaysLoaded` — `CLAUDE.md` plus the transitive closure of its
|
|
9
9
|
* `@`-import references (`@AGENTS.md`,
|
|
10
|
-
* `@.agents/instructions.md`, the
|
|
11
|
-
*
|
|
10
|
+
* `@.agents/instructions.md`, the always-on rules, …).
|
|
11
|
+
* This is the context every session re-pays
|
|
12
12
|
* on every subagent spawn (instructions.md § 4), so it is
|
|
13
13
|
* the primary budget the context-budget ratchet gates.
|
|
14
14
|
* - `mandatoryRead` — the resolved `project.docsContextFiles` set (prefixed
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* duplicate-search.js — Cross-
|
|
2
|
+
* duplicate-search.js — Cross-Story Duplicate Detection
|
|
3
3
|
*
|
|
4
|
-
* Used by `/plan`
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Used by `/plan` to surface open Stories whose scope overlaps with a
|
|
5
|
+
* seed / seed-file / tickets corpus before new Stories are created.
|
|
6
|
+
* Returns ranked candidates with an overlap score and URL so the host
|
|
7
|
+
* LLM can pause for HITL confirmation.
|
|
8
8
|
*
|
|
9
9
|
* Design notes:
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
10
|
+
* - Prefer `provider.searchIssues` to narrow open Stories server-side
|
|
11
|
+
* (`label:"type::story" state:open` + top seed tokens), capped at
|
|
12
|
+
* ~100 hits, then rank that set. Fall back to `listIssuesByLabel`
|
|
13
|
+
* when search errors, is unavailable, or returns **nothing** (same
|
|
14
|
+
* try/catch pattern as `TicketGateway.getTickets`).
|
|
15
|
+
* - The narrowing query is deliberately weak (Story #4541). GitHub ANDs
|
|
16
|
+
* free-text terms, so the former 8-token query demanded that a candidate
|
|
17
|
+
* contain all eight of a multi-sentence seed's longest words — which no
|
|
18
|
+
* real Story does. Gate #1 duplicate triage therefore reported "no
|
|
19
|
+
* candidates" on every realistic seed while looking perfectly healthy.
|
|
20
|
+
* Search is now a cheap best-case narrowing over a handful of tokens; an
|
|
21
|
+
* empty result is treated as "search did not help", not "no duplicates",
|
|
22
|
+
* and falls through to the label listing whose Jaccard ranker has real
|
|
23
|
+
* recall.
|
|
24
|
+
* - Scoring is intentionally simple (token Jaccard over title + body).
|
|
25
|
+
* It is a triage signal, not a semantic-search replacement.
|
|
26
|
+
* - Provider errors on the list fallback propagate verbatim — the
|
|
27
|
+
* caller is responsible for translating them into a friction comment
|
|
28
|
+
* or operator-visible failure.
|
|
19
29
|
*/
|
|
20
30
|
|
|
31
|
+
import { Logger } from './Logger.js';
|
|
32
|
+
import { TYPE_LABELS } from './label-constants.js';
|
|
33
|
+
|
|
21
34
|
const STOPWORDS = new Set([
|
|
22
35
|
'a',
|
|
23
36
|
'an',
|
|
@@ -75,6 +88,19 @@ const STOPWORDS = new Set([
|
|
|
75
88
|
|
|
76
89
|
const DEFAULT_MIN_SCORE = 0.15;
|
|
77
90
|
const DEFAULT_MAX_RESULTS = 5;
|
|
91
|
+
/**
|
|
92
|
+
* How many seed tokens to pass as free-text search terms.
|
|
93
|
+
*
|
|
94
|
+
* GitHub ANDs these, so the cap is a precision/recall dial pointing the
|
|
95
|
+
* wrong way: every extra token shrinks the candidate set. Three is a
|
|
96
|
+
* best-case narrowing that can plausibly hit on a focused seed; anything
|
|
97
|
+
* beyond that reliably matched nothing on a multi-sentence one (Story
|
|
98
|
+
* #4541). Recall does not rest on this number — an empty result falls back
|
|
99
|
+
* to label-listing plus client-side ranking.
|
|
100
|
+
*/
|
|
101
|
+
const DEFAULT_SEARCH_TOKEN_CAP = 3;
|
|
102
|
+
/** Hard cap on search hits ranked client-side (~one Search API page). */
|
|
103
|
+
const SEARCH_RESULT_CAP = 100;
|
|
78
104
|
|
|
79
105
|
/**
|
|
80
106
|
* Tokenize freeform text into a deduplicated set of meaningful words.
|
|
@@ -92,6 +118,33 @@ export function tokenize(text) {
|
|
|
92
118
|
return new Set(tokens);
|
|
93
119
|
}
|
|
94
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Pick the highest-signal seed tokens for a Search API free-text query.
|
|
123
|
+
* Longer tokens first (specificity proxy), then alphabetical for stability.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} seed
|
|
126
|
+
* @param {number} [maxTokens]
|
|
127
|
+
* @returns {string[]}
|
|
128
|
+
*/
|
|
129
|
+
export function pickSearchTokens(seed, maxTokens = DEFAULT_SEARCH_TOKEN_CAP) {
|
|
130
|
+
const tokens = tokenize(seed);
|
|
131
|
+
return [...tokens]
|
|
132
|
+
.sort((a, b) => b.length - a.length || a.localeCompare(b))
|
|
133
|
+
.slice(0, maxTokens);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build the `/search/issues` query for open Stories overlapping a seed.
|
|
138
|
+
* Repo scoping is left to `provider.searchIssues`.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} seed
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
export function buildOpenStorySearchQuery(seed) {
|
|
144
|
+
const tokens = pickSearchTokens(seed);
|
|
145
|
+
return [`label:"${TYPE_LABELS.STORY}"`, 'state:open', ...tokens].join(' ');
|
|
146
|
+
}
|
|
147
|
+
|
|
95
148
|
/**
|
|
96
149
|
* Compute the Jaccard overlap between two token sets.
|
|
97
150
|
*
|
|
@@ -108,14 +161,14 @@ export function overlapScore(a, b) {
|
|
|
108
161
|
}
|
|
109
162
|
|
|
110
163
|
/**
|
|
111
|
-
* Build the
|
|
112
|
-
*
|
|
164
|
+
* Build the issue URL for an id. The candidate exposes the URL so the
|
|
165
|
+
* HITL pause can render clickable links without a second round-trip.
|
|
113
166
|
*
|
|
114
167
|
* @param {number|string} id
|
|
115
168
|
* @param {{ owner?: string, repo?: string }} [opts]
|
|
116
169
|
* @returns {string}
|
|
117
170
|
*/
|
|
118
|
-
function
|
|
171
|
+
function buildIssueUrl(id, opts = {}) {
|
|
119
172
|
const owner = opts.owner || process.env.GITHUB_OWNER;
|
|
120
173
|
const repo = opts.repo || process.env.GITHUB_REPO;
|
|
121
174
|
if (owner && repo) {
|
|
@@ -125,54 +178,72 @@ function buildEpicUrl(id, opts = {}) {
|
|
|
125
178
|
}
|
|
126
179
|
|
|
127
180
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
181
|
+
* Normalize a provider issue (list or search hit) into the ranking shape.
|
|
182
|
+
*
|
|
183
|
+
* @param {object} issue
|
|
184
|
+
* @returns {{ id: number, title: string, body: string, url?: string }}
|
|
185
|
+
*/
|
|
186
|
+
function normalizeIssue(issue) {
|
|
187
|
+
return {
|
|
188
|
+
id: Number(issue.number ?? issue.id),
|
|
189
|
+
title: issue.title ?? '',
|
|
190
|
+
body: issue.body ?? '',
|
|
191
|
+
url: issue.html_url ?? issue.url ?? undefined,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Rank a pre-fetched open-Story list against a seed corpus.
|
|
197
|
+
* Pure helper shared by the provider-backed search and tests.
|
|
130
198
|
*
|
|
131
199
|
* @param {{
|
|
132
|
-
*
|
|
133
|
-
*
|
|
200
|
+
* seed: string,
|
|
201
|
+
* openStories: Array<{ id:number, title?:string, body?:string, url?:string }>,
|
|
134
202
|
* minScore?: number,
|
|
135
203
|
* maxResults?: number,
|
|
136
204
|
* owner?: string,
|
|
137
205
|
* repo?: string,
|
|
206
|
+
* excludeIds?: Iterable<number|string>,
|
|
138
207
|
* }} args
|
|
139
|
-
* @returns {
|
|
208
|
+
* @returns {Array<{ id: number, title: string, score: number, url: string }>}
|
|
140
209
|
*/
|
|
141
|
-
export
|
|
142
|
-
|
|
143
|
-
|
|
210
|
+
export function rankOpenStoryDuplicates({
|
|
211
|
+
seed,
|
|
212
|
+
openStories,
|
|
144
213
|
minScore = DEFAULT_MIN_SCORE,
|
|
145
214
|
maxResults = DEFAULT_MAX_RESULTS,
|
|
146
215
|
owner,
|
|
147
216
|
repo,
|
|
217
|
+
excludeIds = [],
|
|
148
218
|
}) {
|
|
149
|
-
if (!
|
|
150
|
-
throw new Error(
|
|
151
|
-
'findSimilarOpenEpics: onePager must be a non-empty string',
|
|
152
|
-
);
|
|
219
|
+
if (!seed || typeof seed !== 'string') {
|
|
220
|
+
throw new Error('rankOpenStoryDuplicates: seed must be a non-empty string');
|
|
153
221
|
}
|
|
154
|
-
if (!
|
|
155
|
-
throw new Error('
|
|
222
|
+
if (!Array.isArray(openStories)) {
|
|
223
|
+
throw new Error('rankOpenStoryDuplicates: openStories must be an array');
|
|
156
224
|
}
|
|
157
225
|
|
|
158
|
-
const seedTokens = tokenize(
|
|
226
|
+
const seedTokens = tokenize(seed);
|
|
159
227
|
if (seedTokens.size === 0) return [];
|
|
160
228
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
229
|
+
const excluded = new Set(
|
|
230
|
+
[...excludeIds].map((id) => Number(id)).filter((n) => Number.isFinite(n)),
|
|
231
|
+
);
|
|
164
232
|
|
|
165
233
|
const ranked = [];
|
|
166
|
-
for (const
|
|
167
|
-
const
|
|
234
|
+
for (const story of openStories) {
|
|
235
|
+
const id = Number(story?.id ?? story?.number);
|
|
236
|
+
if (!Number.isFinite(id) || excluded.has(id)) continue;
|
|
237
|
+
const title = story.title || '';
|
|
238
|
+
const corpus = `${title}\n${story.body || ''}`;
|
|
168
239
|
const candidateTokens = tokenize(corpus);
|
|
169
240
|
const score = overlapScore(seedTokens, candidateTokens);
|
|
170
241
|
if (score >= minScore) {
|
|
171
242
|
ranked.push({
|
|
172
|
-
id
|
|
173
|
-
title
|
|
243
|
+
id,
|
|
244
|
+
title,
|
|
174
245
|
score: Number(score.toFixed(4)),
|
|
175
|
-
url:
|
|
246
|
+
url: story.url || buildIssueUrl(id, { owner, repo }),
|
|
176
247
|
});
|
|
177
248
|
}
|
|
178
249
|
}
|
|
@@ -181,9 +252,139 @@ export async function findSimilarOpenEpics({
|
|
|
181
252
|
return ranked.slice(0, maxResults);
|
|
182
253
|
}
|
|
183
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Server-narrowed open-Story fetch via `/search/issues`.
|
|
257
|
+
*
|
|
258
|
+
* @param {object} provider
|
|
259
|
+
* @param {string} seed
|
|
260
|
+
* @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
|
|
261
|
+
*/
|
|
262
|
+
async function fetchOpenStoriesViaSearch(provider, seed) {
|
|
263
|
+
const query = buildOpenStorySearchQuery(seed);
|
|
264
|
+
const hits = await provider.searchIssues({ query });
|
|
265
|
+
if (!Array.isArray(hits)) return [];
|
|
266
|
+
return hits.slice(0, SEARCH_RESULT_CAP).map(normalizeIssue);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Full open-Story backlog via label listing (fallback path).
|
|
271
|
+
*
|
|
272
|
+
* @param {object} provider
|
|
273
|
+
* @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
|
|
274
|
+
*/
|
|
275
|
+
async function fetchOpenStoriesViaList(provider) {
|
|
276
|
+
if (typeof provider.listIssuesByLabel !== 'function') {
|
|
277
|
+
throw new Error(
|
|
278
|
+
'findSimilarOpenStories: provider must implement listIssuesByLabel()',
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const issues = await provider.listIssuesByLabel({
|
|
282
|
+
state: 'open',
|
|
283
|
+
labels: TYPE_LABELS.STORY,
|
|
284
|
+
});
|
|
285
|
+
if (!Array.isArray(issues)) return [];
|
|
286
|
+
return issues.map(normalizeIssue);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Fetch open-Story candidates: try Search API narrowing first, fall back to
|
|
291
|
+
* `listIssuesByLabel` when search errors, is unavailable, or **returns no
|
|
292
|
+
* hits**.
|
|
293
|
+
*
|
|
294
|
+
* The empty-result fallback is the load-bearing one (Story #4541). The
|
|
295
|
+
* narrowing query ANDs seed tokens, so on any real multi-sentence seed it
|
|
296
|
+
* matches nothing — and treating that as an authoritative "no duplicates
|
|
297
|
+
* exist" is what made Gate #1 triage report a clean bill of health while
|
|
298
|
+
* never actually looking. An empty search is evidence about the *query*, not
|
|
299
|
+
* about the backlog.
|
|
300
|
+
*
|
|
301
|
+
* @param {object} provider
|
|
302
|
+
* @param {string} seed
|
|
303
|
+
* @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
|
|
304
|
+
*/
|
|
305
|
+
async function fetchOpenStoryCandidates(provider, seed) {
|
|
306
|
+
if (!provider || typeof provider !== 'object') {
|
|
307
|
+
throw new Error('findSimilarOpenStories: provider is required');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const hasSearch = typeof provider.searchIssues === 'function';
|
|
311
|
+
const hasList = typeof provider.listIssuesByLabel === 'function';
|
|
312
|
+
if (!hasSearch && !hasList) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
'findSimilarOpenStories: provider must implement searchIssues() or listIssuesByLabel()',
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (hasSearch) {
|
|
319
|
+
try {
|
|
320
|
+
const hits = await fetchOpenStoriesViaSearch(provider, seed);
|
|
321
|
+
if (hits.length > 0 || !hasList) return hits;
|
|
322
|
+
Logger.info(
|
|
323
|
+
'[duplicate-search] narrowed search matched no open Stories; ' +
|
|
324
|
+
'falling back to listIssuesByLabel + client-side ranking',
|
|
325
|
+
);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
if (!hasList) throw err;
|
|
328
|
+
const msg = typeof err?.message === 'string' ? err.message : String(err);
|
|
329
|
+
Logger.warn(
|
|
330
|
+
`[duplicate-search] search-based candidate fetch failed (${msg}); ` +
|
|
331
|
+
'falling back to listIssuesByLabel',
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return fetchOpenStoriesViaList(provider);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Find open Stories whose title + body overlap with the supplied seed
|
|
341
|
+
* above a configurable threshold.
|
|
342
|
+
*
|
|
343
|
+
* @param {{
|
|
344
|
+
* seed: string,
|
|
345
|
+
* provider: import('./ITicketingProvider.js').ITicketingProvider,
|
|
346
|
+
* minScore?: number,
|
|
347
|
+
* maxResults?: number,
|
|
348
|
+
* owner?: string,
|
|
349
|
+
* repo?: string,
|
|
350
|
+
* excludeIds?: Iterable<number|string>,
|
|
351
|
+
* }} args
|
|
352
|
+
* @returns {Promise<Array<{ id: number, title: string, score: number, url: string }>>}
|
|
353
|
+
*/
|
|
354
|
+
export async function findSimilarOpenStories({
|
|
355
|
+
seed,
|
|
356
|
+
provider,
|
|
357
|
+
minScore = DEFAULT_MIN_SCORE,
|
|
358
|
+
maxResults = DEFAULT_MAX_RESULTS,
|
|
359
|
+
owner,
|
|
360
|
+
repo,
|
|
361
|
+
excludeIds = [],
|
|
362
|
+
}) {
|
|
363
|
+
if (!seed || typeof seed !== 'string') {
|
|
364
|
+
throw new Error('findSimilarOpenStories: seed must be a non-empty string');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Avoid a network round-trip when the seed cannot produce a score.
|
|
368
|
+
if (tokenize(seed).size === 0) return [];
|
|
369
|
+
|
|
370
|
+
const openStories = await fetchOpenStoryCandidates(provider, seed);
|
|
371
|
+
return rankOpenStoryDuplicates({
|
|
372
|
+
seed,
|
|
373
|
+
openStories,
|
|
374
|
+
minScore,
|
|
375
|
+
maxResults,
|
|
376
|
+
owner,
|
|
377
|
+
repo,
|
|
378
|
+
excludeIds,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
184
382
|
export const __test = {
|
|
185
383
|
STOPWORDS,
|
|
186
384
|
DEFAULT_MIN_SCORE,
|
|
187
385
|
DEFAULT_MAX_RESULTS,
|
|
188
|
-
|
|
386
|
+
DEFAULT_SEARCH_TOKEN_CAP,
|
|
387
|
+
SEARCH_RESULT_CAP,
|
|
388
|
+
buildIssueUrl,
|
|
389
|
+
buildEpicUrl: buildIssueUrl,
|
|
189
390
|
};
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
* failure path is captured in `errors[]`.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
+
import { DEFAULT_FRAMEWORK_REPO } from '../github/framework-repo.js';
|
|
33
34
|
import { META_LABELS } from '../label-constants.js';
|
|
34
|
-
import { DEFAULT_FRAMEWORK_REPO } from '../orchestration/retro/phases/gather-signals.js';
|
|
35
35
|
import {
|
|
36
36
|
contentFingerprint,
|
|
37
37
|
DEFAULT_MAX_FILINGS_PER_RUN,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* session's ledger items (see `.agents/schemas/qa-ledger.schema.json` and
|
|
7
7
|
* `lib/qa/qa-session.js`), the still-untriaged backlog is clustered and each
|
|
8
8
|
* cluster is promoted to a follow-up ticket — a single Story (via `/plan`)
|
|
9
|
-
* for a tight, one-deliverable cluster, or
|
|
9
|
+
* for a tight, one-deliverable cluster, or a multi-Story plan-seed (via `/plan --seed`) for
|
|
10
10
|
* a broad cluster that spans multiple coverage surfaces. Each contributing
|
|
11
11
|
* ledger item then has the resulting `routedTo` issue link written back onto it
|
|
12
12
|
* so a resume run sees the item as filed rather than re-promoting it.
|
|
@@ -32,15 +32,24 @@ import { highestSeverity as highestSeverityOf } from './severity.js';
|
|
|
32
32
|
/** Triaged dispositions, mirrored from the `disposition` enum in the schema. */
|
|
33
33
|
const TRIAGED_DISPOSITIONS = Object.freeze(['file', 'defer', 'dismiss']);
|
|
34
34
|
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The two promotion targets a cluster routes to.
|
|
37
|
+
*
|
|
38
|
+
* `PLAN_SEED` promotes a broad cluster by seeding a multi-Story `/plan`
|
|
39
|
+
* run (`/plan --seed`). Its persisted wire token is the legacy string
|
|
40
|
+
* `'epic'`: archived qa-ledger records (and the distributed
|
|
41
|
+
* `qa-ledger.schema.json` `routedTo.kind` enum) still carry `'epic'`, so
|
|
42
|
+
* the value is kept read-compatible while the write-side constant name is
|
|
43
|
+
* modernised.
|
|
44
|
+
*/
|
|
36
45
|
export const PROMOTION_TARGETS = Object.freeze({
|
|
37
46
|
STORY: 'story',
|
|
38
|
-
|
|
47
|
+
PLAN_SEED: 'epic',
|
|
39
48
|
});
|
|
40
49
|
|
|
41
50
|
/**
|
|
42
51
|
* A cluster of more than this many distinct coverage surfaces is broad enough
|
|
43
|
-
* to warrant
|
|
52
|
+
* to warrant a plan-seed (`/plan --seed`) rather than a single Story
|
|
44
53
|
* (`/plan`). One or two surfaces is a tight, single-deliverable cluster.
|
|
45
54
|
*/
|
|
46
55
|
const EPIC_COVERAGE_THRESHOLD = 2;
|
|
@@ -70,7 +79,7 @@ export function isPromotable(item) {
|
|
|
70
79
|
* Stable cluster key for a ledger item: its `class`. Items sharing a class
|
|
71
80
|
* describe the same kind of signal (a product bug, a tooling-DX gap, …) and
|
|
72
81
|
* merge into one follow-up ticket. A class whose items span many distinct
|
|
73
|
-
* coverage surfaces is broad enough to promote to
|
|
82
|
+
* coverage surfaces is broad enough to promote to a plan-seed (see
|
|
74
83
|
* {@link targetForCluster}); a class confined to one or two surfaces is a
|
|
75
84
|
* single-deliverable Story. Coverage is therefore a *secondary* signal that
|
|
76
85
|
* sizes the cluster rather than splitting it.
|
|
@@ -159,7 +168,7 @@ export function clusterLedgerItems(items) {
|
|
|
159
168
|
/**
|
|
160
169
|
* Decide a cluster's promotion target. A cluster that spans more than
|
|
161
170
|
* {@link EPIC_COVERAGE_THRESHOLD} distinct coverage surfaces is broad enough to
|
|
162
|
-
* warrant
|
|
171
|
+
* warrant a plan-seed (`/plan --seed`); otherwise it is a single-deliverable
|
|
163
172
|
* Story (`/plan`).
|
|
164
173
|
*
|
|
165
174
|
* @param {{ coverages: string[] }} cluster
|
|
@@ -170,7 +179,7 @@ export function targetForCluster(cluster) {
|
|
|
170
179
|
? cluster.coverages.length
|
|
171
180
|
: 0;
|
|
172
181
|
return surfaces > EPIC_COVERAGE_THRESHOLD
|
|
173
|
-
? PROMOTION_TARGETS.
|
|
182
|
+
? PROMOTION_TARGETS.PLAN_SEED
|
|
174
183
|
: PROMOTION_TARGETS.STORY;
|
|
175
184
|
}
|
|
176
185
|
|
|
@@ -224,7 +233,7 @@ function routedToLink(issue, kind) {
|
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
/**
|
|
227
|
-
* Promote the clustered untriaged ledger items into Stories /
|
|
236
|
+
* Promote the clustered untriaged ledger items into Stories / plan-seeds via the
|
|
228
237
|
* shared findings/route logic, then write the resulting `routedTo` issue link
|
|
229
238
|
* back onto each contributing ledger item.
|
|
230
239
|
*
|
|
@@ -233,7 +242,7 @@ function routedToLink(issue, kind) {
|
|
|
233
242
|
* shared `routeFinding` against existing Issues (via the injected search
|
|
234
243
|
* port). This dedups against work already filed.
|
|
235
244
|
* 2. On a `new` decision, open the follow-up ticket through the injected
|
|
236
|
-
* `createStory` (`/plan`) or `
|
|
245
|
+
* `createStory` (`/plan`) or `createPlanSeed` (`/plan --seed`) port,
|
|
237
246
|
* chosen by {@link targetForCluster}. On any other decision, link back to
|
|
238
247
|
* the matched Issue rather than creating a duplicate.
|
|
239
248
|
* 3. Stamp the resolved `routedTo` link onto every contributing ledger item
|
|
@@ -251,8 +260,8 @@ function routedToLink(issue, kind) {
|
|
|
251
260
|
* Optional semantic candidate search, forwarded to `routeFinding`.
|
|
252
261
|
* @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createStory
|
|
253
262
|
* Opens a single Story (`/plan`) for a tight cluster.
|
|
254
|
-
* @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.
|
|
255
|
-
* Opens
|
|
263
|
+
* @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createPlanSeed
|
|
264
|
+
* Opens a plan-seed (`/plan --seed`) run for a broad cluster.
|
|
256
265
|
* @returns {Promise<{
|
|
257
266
|
* promotions: Array<{
|
|
258
267
|
* clusterKey: string,
|
|
@@ -268,7 +277,7 @@ function routedToLink(issue, kind) {
|
|
|
268
277
|
* @throws {Error} when a required create port is missing for a routed cluster.
|
|
269
278
|
*/
|
|
270
279
|
export async function promoteFindings(ledgerItems, ports = {}) {
|
|
271
|
-
const { searchIssues, searchCandidates, createStory,
|
|
280
|
+
const { searchIssues, searchCandidates, createStory, createPlanSeed } = ports;
|
|
272
281
|
if (
|
|
273
282
|
typeof searchCandidates !== 'function' &&
|
|
274
283
|
typeof searchIssues !== 'function'
|
|
@@ -294,10 +303,10 @@ export async function promoteFindings(ledgerItems, ports = {}) {
|
|
|
294
303
|
|
|
295
304
|
if (route.decision === 'new') {
|
|
296
305
|
const createPort =
|
|
297
|
-
target === PROMOTION_TARGETS.
|
|
306
|
+
target === PROMOTION_TARGETS.PLAN_SEED ? createPlanSeed : createStory;
|
|
298
307
|
if (typeof createPort !== 'function') {
|
|
299
308
|
throw new Error(
|
|
300
|
-
`promoteFindings: a ${target === PROMOTION_TARGETS.
|
|
309
|
+
`promoteFindings: a ${target === PROMOTION_TARGETS.PLAN_SEED ? 'createPlanSeed' : 'createStory'} port is required to promote cluster ${cluster.key}`,
|
|
301
310
|
);
|
|
302
311
|
}
|
|
303
312
|
issue = await createPort(cluster);
|