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
|
@@ -3,7 +3,10 @@ import { detectCycle } from '../Graph.js';
|
|
|
3
3
|
import { gitSpawn } from '../git-utils.js';
|
|
4
4
|
|
|
5
5
|
import { Logger } from '../Logger.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
parse as parseStoryBody,
|
|
8
|
+
StoryBodyParseError,
|
|
9
|
+
} from '../story-body/story-body.js';
|
|
7
10
|
import { validateStoryFileAssumptions } from './file-assumptions.js';
|
|
8
11
|
import {
|
|
9
12
|
computeConflictFindings,
|
|
@@ -11,14 +14,9 @@ import {
|
|
|
11
14
|
} from './ticket-validator-conflicts.js';
|
|
12
15
|
import {
|
|
13
16
|
computeSizingFindings,
|
|
14
|
-
DEFAULT_TASK_SIZING,
|
|
15
17
|
renderHardFindingError,
|
|
16
18
|
} from './ticket-validator-sizing.js';
|
|
17
19
|
|
|
18
|
-
// Re-exported for callers that want the constants without reaching into the
|
|
19
|
-
// sizing helper module directly.
|
|
20
|
-
export { DEFAULT_TASK_SIZING };
|
|
21
|
-
|
|
22
20
|
/**
|
|
23
21
|
* Regex matching code-asset paths the freshness gate cares about. The three
|
|
24
22
|
* roots — `.agents/scripts`, `lib`, and `tests` — cover the executable surface
|
|
@@ -48,6 +46,95 @@ function collectPathsFromText(text, paths) {
|
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Parse a Story's serialized markdown body, translating a
|
|
51
|
+
* `StoryBodyParseError` into a `ValidationError` that names the offending
|
|
52
|
+
* **section** and **entry** (Story #4541).
|
|
53
|
+
*
|
|
54
|
+
* `StoryBodyParseError` already carries `field` (the section the parser was
|
|
55
|
+
* reading) and `raw` (the entry text that failed); this lifts both into an
|
|
56
|
+
* operator-legible message and a structured `violation` payload so an
|
|
57
|
+
* authoring loop can point at the exact bullet instead of re-deriving it
|
|
58
|
+
* from a downstream freshness miss.
|
|
59
|
+
*
|
|
60
|
+
* @param {object} story Story whose `body` is a non-empty markdown string.
|
|
61
|
+
* @returns {object} The structured body.
|
|
62
|
+
* @throws {ValidationError} `code: 'story-body-unparseable'`.
|
|
63
|
+
*/
|
|
64
|
+
function parseStoryBodyOrThrow(story) {
|
|
65
|
+
try {
|
|
66
|
+
return parseStoryBody(story.body).body;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (!(err instanceof StoryBodyParseError)) throw err;
|
|
69
|
+
const slug = story.slug ?? '<unknown>';
|
|
70
|
+
const section = err.field ?? 'body';
|
|
71
|
+
const entry = err.raw ?? null;
|
|
72
|
+
const entryLine = entry === null ? '' : `\n entry: ${entry}`;
|
|
73
|
+
const violation = { slug, section, entry, reason: err.message };
|
|
74
|
+
const error = new ValidationError(
|
|
75
|
+
`Cross-Validation Failed: Story "${slug}" has an unparseable body — ` +
|
|
76
|
+
`the ## ${section} section could not be read: ${err.message}` +
|
|
77
|
+
`${entryLine}\n\nFix the offending entry; this is a malformed body, ` +
|
|
78
|
+
'not a stale path reference.',
|
|
79
|
+
{ violations: [violation] },
|
|
80
|
+
);
|
|
81
|
+
error.code = 'story-body-unparseable';
|
|
82
|
+
error.violations = [violation];
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Refuse the plan when any Story's serialized body cannot be parsed, before
|
|
89
|
+
* either git-probe gate runs (Story #4541). Ordering matters: the freshness
|
|
90
|
+
* gate consults `body.changes` for its net-new whitelist, so an unparseable
|
|
91
|
+
* body used to reach the operator as a freshness miss naming declared paths.
|
|
92
|
+
*
|
|
93
|
+
* @param {{ tickets: object[] }} opts
|
|
94
|
+
* @throws {ValidationError} `code: 'story-body-unparseable'` on the first
|
|
95
|
+
* offending Story.
|
|
96
|
+
*/
|
|
97
|
+
function assertStoryBodiesParse({ tickets }) {
|
|
98
|
+
for (const story of (tickets ?? []).filter((t) => t.type === 'story')) {
|
|
99
|
+
if (typeof story.body !== 'string' || story.body.trim().length === 0) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
parseStoryBodyOrThrow(story);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolve every acceptance line a Story declares, across both authoring
|
|
108
|
+
* shapes (Story #4541).
|
|
109
|
+
*
|
|
110
|
+
* The canonical shape is a **serialized string body** with the criteria at
|
|
111
|
+
* the ticket's **top level** — the machine contract persist syncs into the
|
|
112
|
+
* body. `validateAcceptanceSubjectPrefix` used to read `body.acceptance` on
|
|
113
|
+
* an object body only, so on every real plan it scanned nothing and the gate
|
|
114
|
+
* silently passed. Union both sources (deduplicated) so the gate fires on
|
|
115
|
+
* whichever surface the author used.
|
|
116
|
+
*
|
|
117
|
+
* @param {object} story
|
|
118
|
+
* @returns {string[]}
|
|
119
|
+
*/
|
|
120
|
+
function resolveAcceptanceLines(story) {
|
|
121
|
+
const lines = new Set();
|
|
122
|
+
if (Array.isArray(story?.acceptance)) {
|
|
123
|
+
for (const item of story.acceptance) lines.add(String(item ?? ''));
|
|
124
|
+
}
|
|
125
|
+
const body = story?.body;
|
|
126
|
+
let bodyAcceptance = null;
|
|
127
|
+
if (typeof body === 'string' && body.trim().length > 0) {
|
|
128
|
+
bodyAcceptance = parseStoryBodyOrThrow(story).acceptance;
|
|
129
|
+
} else if (body !== null && typeof body === 'object') {
|
|
130
|
+
bodyAcceptance = body.acceptance;
|
|
131
|
+
}
|
|
132
|
+
if (Array.isArray(bodyAcceptance)) {
|
|
133
|
+
for (const item of bodyAcceptance) lines.add(String(item ?? ''));
|
|
134
|
+
}
|
|
135
|
+
return [...lines];
|
|
136
|
+
}
|
|
137
|
+
|
|
51
138
|
function collectTaskPathReferences(task) {
|
|
52
139
|
const paths = new Set();
|
|
53
140
|
const body = task.body;
|
|
@@ -87,7 +174,7 @@ function collectTaskPathReferences(task) {
|
|
|
87
174
|
* path out of the prose.
|
|
88
175
|
* 3. **Object form** — `{ path: "<path>", assumption: "creates" | ... }`,
|
|
89
176
|
* introduced by Story #2636 as the canonical declaration shape and
|
|
90
|
-
* documented in `
|
|
177
|
+
* documented in `lib/templates/decomposer-prompts.js`. The path is
|
|
91
178
|
* trusted verbatim.
|
|
92
179
|
*
|
|
93
180
|
* Only `body.changes` (and `body.references`) is consulted —
|
|
@@ -105,15 +192,16 @@ function collectTaskChangesPaths(task) {
|
|
|
105
192
|
// arrays before scanning. Without this, a string body causes the
|
|
106
193
|
// object-form branch below to fall through on every item, leaving the
|
|
107
194
|
// freshness gate blind to declared paths.
|
|
195
|
+
//
|
|
196
|
+
// Story #4541: a parse failure is NOT swallowed here. Swallowing it
|
|
197
|
+
// returned an empty whitelist, so a single malformed `## Changes` entry
|
|
198
|
+
// surfaced downstream as "files do not exist at main" naming the very
|
|
199
|
+
// paths the Story *had* declared — a misdiagnosis that cost two authoring
|
|
200
|
+
// round-trips. `assertStoryBodiesParse` runs before the freshness gate and
|
|
201
|
+
// owns that failure with a named error; the throw here is the same error
|
|
202
|
+
// for any caller that drives `validateAcFreshness` directly.
|
|
108
203
|
if (typeof body === 'string' && body.trim().length > 0) {
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
parsed = parseStoryBody(body).body;
|
|
112
|
-
} catch {
|
|
113
|
-
// Unparseable body — no paths to whitelist; the freshness gate will
|
|
114
|
-
// catch any real references in the text scan below.
|
|
115
|
-
return paths;
|
|
116
|
-
}
|
|
204
|
+
const parsed = parseStoryBodyOrThrow(task);
|
|
117
205
|
for (const arrName of ['changes', 'references']) {
|
|
118
206
|
const arr = parsed[arrName];
|
|
119
207
|
if (!Array.isArray(arr)) continue;
|
|
@@ -312,11 +400,16 @@ const SUBJECT_PREFIX_RE = /Commit subject begins with ['"`]([^'"`]+):['"`]/g;
|
|
|
312
400
|
* the form `baseline-refresh` is rejected because no Conventional-Commits
|
|
313
401
|
* type starts with that token.
|
|
314
402
|
*
|
|
315
|
-
* Only
|
|
403
|
+
* Only acceptance criteria are scanned; `body.goal` / `body.verify` /
|
|
316
404
|
* `body.changes` are not commit-subject prescriptions by convention and
|
|
317
405
|
* scanning them would surface false positives from prose that happens to
|
|
318
406
|
* quote a forbidden prefix while explaining why it's forbidden.
|
|
319
407
|
*
|
|
408
|
+
* Both authoring shapes are covered (Story #4541): the canonical top-level
|
|
409
|
+
* `acceptance[]` on a serialized string body, and the pre-serialize
|
|
410
|
+
* `body.acceptance[]` object shape. Scanning only the latter made the gate
|
|
411
|
+
* inert on every real plan.
|
|
412
|
+
*
|
|
320
413
|
* @param {object} opts
|
|
321
414
|
* @param {object[]} opts.tickets - Validated ticket hierarchy.
|
|
322
415
|
* @throws {ValidationError} when one or more Story acceptance items
|
|
@@ -329,11 +422,7 @@ export function validateAcceptanceSubjectPrefix({ tickets }) {
|
|
|
329
422
|
const violations = [];
|
|
330
423
|
const stories = (tickets ?? []).filter((t) => t.type === 'story');
|
|
331
424
|
for (const story of stories) {
|
|
332
|
-
const
|
|
333
|
-
if (body === null || typeof body !== 'object') continue;
|
|
334
|
-
if (!Array.isArray(body.acceptance)) continue;
|
|
335
|
-
for (const item of body.acceptance) {
|
|
336
|
-
const line = String(item ?? '');
|
|
425
|
+
for (const line of resolveAcceptanceLines(story)) {
|
|
337
426
|
// Reset the global regex between iterations.
|
|
338
427
|
SUBJECT_PREFIX_RE.lastIndex = 0;
|
|
339
428
|
let match = SUBJECT_PREFIX_RE.exec(line);
|
|
@@ -386,8 +475,8 @@ function renderMissLine({ slug, path }) {
|
|
|
386
475
|
* The returned tickets array carries two extra non-array properties:
|
|
387
476
|
* - `findings` — structured sizing findings (hard + soft) keyed by the
|
|
388
477
|
* three-layer sizing model. The bounded re-decomposition loop in
|
|
389
|
-
*
|
|
390
|
-
*
|
|
478
|
+
* `/plan` reads `findings.filter(f => f.severity === 'hard')` to decide
|
|
479
|
+
* whether to re-prompt.
|
|
391
480
|
* - `errors` — human-readable strings, one per hard finding. Non-empty
|
|
392
481
|
* `errors[]` is the AC-visible "block normalization" signal; the legacy
|
|
393
482
|
* hierarchy/cycle/freshness checks continue to throw, so callers that
|
|
@@ -399,7 +488,7 @@ function renderMissLine({ slug, path }) {
|
|
|
399
488
|
* @param {string} [opts.baseBranchRef] - When set, runs `validateAcFreshness` against this ref.
|
|
400
489
|
* @param {Function} [opts.gitRunner] - Optional git probe override.
|
|
401
490
|
* @param {string} [opts.cwd] - Repo cwd (forwarded to the freshness gate).
|
|
402
|
-
* @param {object} [opts.
|
|
491
|
+
* @param {object} [opts.modelCapacity] - Programmatic override of `DEFAULT_MODEL_CAPACITY` (tests only — not read from `.agentrc.json`).
|
|
403
492
|
* @param {object} [opts.conflictPolicy] - Severity controls for cross-Story conflict findings.
|
|
404
493
|
* @param {boolean} [opts.conflictPolicy.failOnSharedEditors=false] - Upgrade `shared-editor` findings to `hard`.
|
|
405
494
|
* @param {boolean} [opts.conflictPolicy.requireExplicitCrossStoryDeps=false] - Upgrade `implicit-cross-story-dep` findings to `hard`.
|
|
@@ -545,6 +634,14 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
|
|
|
545
634
|
|
|
546
635
|
assertAcyclic(slugAdjacency);
|
|
547
636
|
|
|
637
|
+
// Story #4541 — refuse an unparseable Story body up front, with a named
|
|
638
|
+
// error pointing at the offending section + entry. Must precede both the
|
|
639
|
+
// subject-prefix scan and the freshness gate: each parses the body, and
|
|
640
|
+
// the freshness gate's net-new whitelist comes from `body.changes`, so a
|
|
641
|
+
// malformed body used to surface as a stale-path miss naming the paths the
|
|
642
|
+
// Story had legitimately declared.
|
|
643
|
+
assertStoryBodiesParse({ tickets });
|
|
644
|
+
|
|
548
645
|
// Reject any Task acceptance item that prescribes a non-Conventional-Commits
|
|
549
646
|
// subject prefix (e.g. legacy "Commit subject begins with 'baseline-refresh:'"
|
|
550
647
|
// from pre-Epic-#2501 planner output). Runs before the freshness gate so
|
|
@@ -614,7 +711,7 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
|
|
|
614
711
|
|
|
615
712
|
const sizingFindings = computeSizingFindings({
|
|
616
713
|
stories,
|
|
617
|
-
|
|
714
|
+
capacity: opts.modelCapacity,
|
|
618
715
|
});
|
|
619
716
|
// Cross-Story path-conflict pass observes the story-level depends_on
|
|
620
717
|
// graph. Findings are appended to the same `findings` array consumed by
|
|
@@ -653,6 +750,7 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
|
|
|
653
750
|
|
|
654
751
|
// Internal helpers exposed for unit tests; not part of the public surface.
|
|
655
752
|
export const _internal = {
|
|
753
|
+
assertStoryBodiesParse,
|
|
656
754
|
indexTicketsBySlug,
|
|
657
755
|
assertAllTicketsAreStories,
|
|
658
756
|
assertEveryStoryHasInlineContract,
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { Logger } from '../../Logger.js';
|
|
23
|
-
import { TYPE_LABELS } from '../../label-constants.js';
|
|
24
23
|
import { ALL_STATES, STATE_LABELS } from './reads.js';
|
|
25
24
|
import {
|
|
26
25
|
postStructuredComment,
|
|
@@ -290,30 +289,6 @@ async function processCascadeParentLocked(
|
|
|
290
289
|
);
|
|
291
290
|
if (!allDone) return { cascadedTo, failed };
|
|
292
291
|
|
|
293
|
-
// EXCLUSION: Epics do not auto-close via cascade. Epics close via
|
|
294
|
-
// formal /deliver (its own machinery handles branch merges,
|
|
295
|
-
// PR-driven `Closes #N` auto-close, and a recovery transition in
|
|
296
|
-
// `epic-deliver-finalize.js`).
|
|
297
|
-
//
|
|
298
|
-
// Legacy planning tickets (pre-#4324 `context::*` artifacts on
|
|
299
|
-
// historical Epics) DO close via cascade (Story #1951): leaving
|
|
300
|
-
// them open as native sub-issues of the Epic blocks GitHub from
|
|
301
|
-
// honoring the Epic's `Closes #N` footer. New Epics carry their
|
|
302
|
-
// planning content on the Epic body itself, so this branch is
|
|
303
|
-
// purely a legacy-hygiene path.
|
|
304
|
-
//
|
|
305
|
-
// Reuse the parentSnapshot from the idempotency check above — it is
|
|
306
|
-
// a fresh read (cache was invalidated before the getTicket call) and
|
|
307
|
-
// the parent's type label is invariant within a single cascade lock
|
|
308
|
-
// hold, so a second getTicket is wasteful and redundant.
|
|
309
|
-
const isEpic = parentSnapshot.labels.includes(TYPE_LABELS.EPIC);
|
|
310
|
-
if (isEpic) {
|
|
311
|
-
logger.warn(
|
|
312
|
-
`[Ticketing] Cascade reached Epic #${parentId}. Skipping auto-close (Epics close via the operator's PR merge).`,
|
|
313
|
-
);
|
|
314
|
-
return { cascadedTo, failed };
|
|
315
|
-
}
|
|
316
|
-
|
|
317
292
|
// Retry the parent transition on transient `gh` failures (rate limit,
|
|
318
293
|
// 5xx, transport timeouts). Permanent failures fall through to the
|
|
319
294
|
// outer catch on the first attempt so the operator sees the real
|
|
@@ -382,7 +357,7 @@ async function processCascadeParentLocked(
|
|
|
382
357
|
* unset so the module-level {@link Logger} is used.
|
|
383
358
|
* @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
|
|
384
359
|
*/
|
|
385
|
-
|
|
360
|
+
async function cascadeCompletion(provider, ticketId, opts = {}) {
|
|
386
361
|
const ticket = await provider.getTicket(ticketId);
|
|
387
362
|
|
|
388
363
|
// Determine if this ticket is agent::done
|
|
@@ -390,43 +365,14 @@ export async function cascadeCompletion(provider, ticketId, opts = {}) {
|
|
|
390
365
|
return { cascadedTo: [], failed: [] };
|
|
391
366
|
}
|
|
392
367
|
|
|
368
|
+
// Story #4545 — one strategy, not three. The `parent: #N` body footer and
|
|
369
|
+
// the native sub-issue link were both written by `createTicket`, the
|
|
370
|
+
// Epic-hierarchy write surface deleted in the same Story; with no writer,
|
|
371
|
+
// the body regex could only ever miss and the native lookup could only
|
|
372
|
+
// ever spend an API call to learn the same. The operator-settable `blocks`
|
|
373
|
+
// annotation is the one parent edge that can still exist.
|
|
393
374
|
const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
|
|
394
|
-
|
|
395
|
-
// Fallback: parse `parent: #NNN` from the body when `blocks` syntax isn't used (C-5).
|
|
396
|
-
let parsedParents = parentIds;
|
|
397
|
-
if (!parsedParents || parsedParents.length === 0) {
|
|
398
|
-
const parentMatch = ticket.body
|
|
399
|
-
? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
|
|
400
|
-
: [];
|
|
401
|
-
parsedParents = parentMatch.map((m) => Number.parseInt(m[1], 10));
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Story #2982 — third fallback: GitHub's native Sub-Issues API. The
|
|
405
|
-
// resume reconciler can strip the `parent: #N` orchestrator footer
|
|
406
|
-
// from a Story body (see Issue 2 in #2982); without the body marker
|
|
407
|
-
// the cascade silently returned `{ cascadedTo: [], failed: [] }` and
|
|
408
|
-
// left intermediate parent tickets stranded OPEN. The native link is
|
|
409
|
-
// independent of body text, so consult it when the first two
|
|
410
|
-
// strategies came back empty.
|
|
411
|
-
if (
|
|
412
|
-
parsedParents.length === 0 &&
|
|
413
|
-
typeof provider._getNativeParent === 'function' &&
|
|
414
|
-
ticket.nodeId
|
|
415
|
-
) {
|
|
416
|
-
try {
|
|
417
|
-
const nativeParent = await provider._getNativeParent(
|
|
418
|
-
ticket.nodeId,
|
|
419
|
-
ticketId,
|
|
420
|
-
);
|
|
421
|
-
if (typeof nativeParent === 'number') {
|
|
422
|
-
parsedParents = [nativeParent];
|
|
423
|
-
}
|
|
424
|
-
} catch (err) {
|
|
425
|
-
Logger.warn(
|
|
426
|
-
`[cascadeCompletion] native parent lookup failed for #${ticketId}: ${err.message}`,
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
375
|
+
const parsedParents = Array.isArray(parentIds) ? parentIds : [];
|
|
430
376
|
|
|
431
377
|
if (parsedParents.length === 0) {
|
|
432
378
|
return { cascadedTo: [], failed: [] };
|
|
@@ -562,22 +508,18 @@ export async function cascadeParentState(provider, ticketId, opts = {}) {
|
|
|
562
508
|
}
|
|
563
509
|
|
|
564
510
|
/**
|
|
565
|
-
* Resolve the parent issue ids for a ticket
|
|
566
|
-
* annotations
|
|
567
|
-
*
|
|
511
|
+
* Resolve the parent issue ids for a ticket from its `blocks:` dependency
|
|
512
|
+
* annotations. Mirrors the resolution path used by {@link cascadeCompletion}
|
|
513
|
+
* — see there for why the `parent: #NNN` body fallback is gone (Story #4545).
|
|
568
514
|
*
|
|
569
515
|
* @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
|
|
570
|
-
* @param {object}
|
|
516
|
+
* @param {object} _ticket
|
|
571
517
|
* @param {number} ticketId
|
|
572
518
|
* @returns {Promise<number[]>}
|
|
573
519
|
*/
|
|
574
|
-
async function resolveParentIds(provider,
|
|
520
|
+
async function resolveParentIds(provider, _ticket, ticketId) {
|
|
575
521
|
const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
|
|
576
|
-
|
|
577
|
-
const parentMatch = ticket?.body
|
|
578
|
-
? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
|
|
579
|
-
: [];
|
|
580
|
-
return parentMatch.map((m) => Number.parseInt(m[1], 10));
|
|
522
|
+
return Array.isArray(parentIds) ? parentIds : [];
|
|
581
523
|
}
|
|
582
524
|
|
|
583
525
|
/**
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { AGENT_LABELS } from '../../label-constants.js';
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
const WAVE_MARKER_RE = /^wave-([0-9]{1,3})-(start|end)$/;
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Canonical agent-state label triad used by every state mutator. Kept on
|
|
@@ -23,7 +24,7 @@ export const STATE_LABELS = {
|
|
|
23
24
|
READY: AGENT_LABELS.READY,
|
|
24
25
|
EXECUTING: AGENT_LABELS.EXECUTING,
|
|
25
26
|
// Story #2144 — intermediate state held by a Story between successful
|
|
26
|
-
// close-preflight and a confirmed merge into `
|
|
27
|
+
// close-preflight and a confirmed Story PR merge into `main`. Included in
|
|
27
28
|
// the state enum so `transitionTicketState` can apply the label via
|
|
28
29
|
// the canonical one-state-at-a-time path (which removes every other
|
|
29
30
|
// `agent::*` label in the same call) and so the read-side `ALL_STATES`
|
|
@@ -72,35 +73,24 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
|
|
|
72
73
|
'verification-results',
|
|
73
74
|
'retro',
|
|
74
75
|
'retro-partial',
|
|
76
|
+
// v2 Story closeout — actionable follow-ups from friction signals
|
|
77
|
+
'follow-ups',
|
|
78
|
+
// v2 plan-run epilogue artifacts (posted on the primary Story)
|
|
79
|
+
'plan-run-audit-roster',
|
|
80
|
+
'plan-run-sibling-coherence',
|
|
75
81
|
'epic-run-state',
|
|
76
82
|
'epic-run-progress',
|
|
77
83
|
'epic-plan-state',
|
|
78
84
|
'parked-follow-ons',
|
|
79
|
-
|
|
80
|
-
// Story #566 — per-phase wall-clock summary posted by story-close
|
|
81
|
-
// and consumed by the epic-runner progress reporter to surface median /
|
|
82
|
-
// p95 phase timings across completed stories.
|
|
85
|
+
// Story #566 — per-phase wall-clock summary posted by single-story-close.js.
|
|
83
86
|
'phase-timings',
|
|
84
87
|
// Story #831 — story-init upserts a `story-init` comment that
|
|
85
88
|
// surfaces `dependenciesInstalled` (and the underlying installStatus) so
|
|
86
89
|
// downstream workflow steps don't have to infer install state from
|
|
87
90
|
// node_modules presence.
|
|
88
91
|
'story-init',
|
|
89
|
-
// Story #
|
|
90
|
-
//
|
|
91
|
-
// the epic-runner progress reporter both read this comment to derive
|
|
92
|
-
// Story-level state without re-fetching ticket labels.
|
|
93
|
-
'story-run-progress',
|
|
94
|
-
// Story #1123 — analyze-execution.js upserts perf summaries at close
|
|
95
|
-
// time. Story-mode posts `story-perf-summary` on each Story; Epic-mode
|
|
96
|
-
// posts `epic-perf-report` on the Epic. Both replace the legacy
|
|
97
|
-
// per-Task `friction` fan-out and the standalone `phase-timings`
|
|
98
|
-
// surface (Epic #1030).
|
|
99
|
-
'story-perf-summary',
|
|
100
|
-
'epic-perf-report',
|
|
101
|
-
// Story #2128 — Phase 6 Epic Clarity Gate. `epic-plan-clarity.js` upserts
|
|
102
|
-
// a `clarity-gate-update` comment on the Epic when the operator approves
|
|
103
|
-
// a sharpened body rewrite, recording the persistence event for audit.
|
|
92
|
+
// Story #2128 — Phase 6 Epic Clarity Gate (CLI retired). Historical
|
|
93
|
+
// `clarity-gate-update` comments may still exist on older tickets.
|
|
104
94
|
'clarity-gate-update',
|
|
105
95
|
// Story #2635 — Tech Spec freshness check. `plan-persist.js`
|
|
106
96
|
// upserts a `spec-freshness` comment on the Epic listing any
|
|
@@ -126,40 +116,34 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
|
|
|
126
116
|
// ticket. Re-invocations upsert the same marker rather than appending
|
|
127
117
|
// duplicates.
|
|
128
118
|
'epic-handoff',
|
|
129
|
-
// Story #2899 (Epic #2880, F13) — `epic-deliver-preflight.js`
|
|
130
|
-
// `delivery-preflight` comment on the Epic at the start of
|
|
131
|
-
//
|
|
119
|
+
// Story #2899 (Epic #2880, F13) — the deleted `epic-deliver-preflight.js`
|
|
120
|
+
// upserted a `delivery-preflight` comment on the Epic at the start of
|
|
121
|
+
// pre-v2 Epic `/deliver` Phase 1, surfacing estimated story count, install cost,
|
|
132
122
|
// wave count, GitHub API request volume, Claude quota burn, and any
|
|
133
123
|
// threshold breaches against `delivery.preflight.max*`. One entry per
|
|
134
124
|
// Epic; re-runs replace prior content.
|
|
135
125
|
'delivery-preflight',
|
|
136
|
-
// Story #3062 (Epic #3051) — `wave-tick.js`
|
|
126
|
+
// Story #3062 (Epic #3051) — the deleted `wave-tick.js` upserted a
|
|
137
127
|
// `recurring-failure-class` comment on the Epic when the cross-Story
|
|
138
128
|
// detector finds two or more Stories that hit the same `failedGate` in
|
|
139
129
|
// `close-validate.end`. One entry per Epic; re-ticks with the same
|
|
140
130
|
// findings upsert in place (`upsertStructuredComment` diffs by body).
|
|
141
131
|
'recurring-failure-class',
|
|
142
|
-
// Story #3061 (Epic #3051) — the
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
132
|
+
// Story #3061 (Epic #3051) — the pre-v2 `/deliver` idle-watchdog prose
|
|
133
|
+
// instructed the parent host LLM to upsert a `wave-stall` comment on the
|
|
134
|
+
// Epic whenever an in-flight Story had been silent for longer than the
|
|
135
|
+
// configured cadence. The deleted `wave-tick.js --check-idle` CLI emitted
|
|
136
|
+
// the matching envelope; registering the kind here keeps historical
|
|
147
137
|
// the documented remediation actually executable
|
|
148
138
|
// (assertValidStructuredCommentType would otherwise throw).
|
|
149
139
|
'wave-stall',
|
|
150
|
-
// Story #
|
|
151
|
-
// comment
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
// comment on the Epic at lease-acquire time, recording the claiming
|
|
158
|
-
// operator and the claim timestamp. `/plan` emits no
|
|
159
|
-
// `story.heartbeat`, so this claim-time is the liveness signal that
|
|
160
|
-
// makes the documented `--steal` contract decidable: a foreign claim
|
|
161
|
-
// older than the lease TTL is reclaimed automatically; a fresh one
|
|
162
|
-
// refuses with the claim age. One entry per Epic; re-acquires upsert
|
|
140
|
+
// Story #4019 — the plan-tier lease guard upserts a `plan-lease`
|
|
141
|
+
// comment at lease-acquire time, recording the claiming operator and the
|
|
142
|
+
// claim timestamp. Nothing emits a per-run liveness beat, so this
|
|
143
|
+
// claim-time is the only age signal a reader has when reasoning about the
|
|
144
|
+
// documented `--steal` contract. The guard itself fails closed regardless
|
|
145
|
+
// (it anchors liveness to `now`), so a stranded claim is cleared with
|
|
146
|
+
// `--steal`, not by TTL expiry. One entry per ticket; re-acquires upsert
|
|
163
147
|
// in place.
|
|
164
148
|
'plan-lease',
|
|
165
149
|
// Story #4415 (Epic #4406) — the feedback-loop graduators
|
|
@@ -171,15 +155,21 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
|
|
|
171
155
|
// `graduator="audit-results|code-review"` attr so the two graduators
|
|
172
156
|
// upsert independent comments; re-runs upsert in place.
|
|
173
157
|
'cross-repo-deferred',
|
|
174
|
-
// Epic #4474 (PR3) — `plan-persist.js` upserts a single
|
|
175
|
-
// comment on the
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
// comment (whose claimed consumer did not exist — the live manifest is
|
|
179
|
-
// written at deliver time by `wave-record-io.js`) and the Phase 12
|
|
180
|
-
// notify round-trip. One entry per Epic; a --force/--resume re-persist
|
|
181
|
-
// upserts in place.
|
|
158
|
+
// Epic #4474 (PR3) / v2 Stage 3 — `plan-persist.js` upserts a single
|
|
159
|
+
// `plan-summary` comment on the primary Story at terminal persist
|
|
160
|
+
// success, carrying risk / routing receipts and the depends_on order
|
|
161
|
+
// table. One entry per plan; a re-persist upserts in place.
|
|
182
162
|
'plan-summary',
|
|
163
|
+
// v2 Stage 3 — flat Story persist checkpoint on every created Story
|
|
164
|
+
// (replaces epic-plan-state for new plans). plan-summary stays primary-only.
|
|
165
|
+
'story-plan-state',
|
|
166
|
+
// Story #4535 — `plan-persist.js` upserts a `superseded-by` comment on
|
|
167
|
+
// each `/plan --tickets` source issue at persist time, naming the single
|
|
168
|
+
// Story that claims it (plus any per-supersede note the plan authored),
|
|
169
|
+
// immediately before closing the issue as `not_planned`. Keying off this
|
|
170
|
+
// marker rather than a bare `postComment` is what makes a re-run
|
|
171
|
+
// non-double-commenting. One entry per source issue.
|
|
172
|
+
'superseded-by',
|
|
183
173
|
]);
|
|
184
174
|
|
|
185
175
|
export const WAVE_TYPE_PATTERN = WAVE_MARKER_RE;
|
|
@@ -265,8 +255,8 @@ export function structuredCommentMarker(type, attrs = null) {
|
|
|
265
255
|
* recent `findStructuredComment` / `upsertStructuredComment` call.
|
|
266
256
|
* Story #1795 — every Epic run owns its process for the duration of
|
|
267
257
|
* the run, so a single seed-then-reuse window saves one
|
|
268
|
-
* `getTicketComments` per repeat upsert on the hot path (
|
|
269
|
-
* `
|
|
258
|
+
* `getTicketComments` per repeat upsert on the hot path (`story-init`,
|
|
259
|
+
* `verification-results`, etc).
|
|
270
260
|
*
|
|
271
261
|
* Lifecycle:
|
|
272
262
|
* - First call to `findStructuredComment(provider, t, type, attrs)`
|
|
@@ -352,8 +342,8 @@ export function structuredCommentCacheKey(ticketId, type, attrs) {
|
|
|
352
342
|
* array returned by the most recent `provider.getTicketComments(ticketId)`
|
|
353
343
|
* call. Story #2465 — `findStructuredComment` is invoked back-to-back for
|
|
354
344
|
* different `type` discriminators against the same ticket (e.g.
|
|
355
|
-
* `story-
|
|
356
|
-
*
|
|
345
|
+
* `story-init` + `verification-results` + a `friction` probe during a
|
|
346
|
+
* single close). Without this cache each lookup pays a full
|
|
357
347
|
* pagination round-trip even when the prior call already fetched the
|
|
358
348
|
* same comments. The structured-comment-id cache short-circuits *repeat*
|
|
359
349
|
* lookups for the same `(type, attrs)` tuple but does not help across
|
|
@@ -37,6 +37,10 @@ import {
|
|
|
37
37
|
eventSeverity,
|
|
38
38
|
renderTransitionMessage,
|
|
39
39
|
} from '../../notifications/notifier.js';
|
|
40
|
+
import {
|
|
41
|
+
emitRuntimeFriction,
|
|
42
|
+
RUNTIME_FRICTION_CATEGORIES,
|
|
43
|
+
} from '../../observability/runtime-friction.js';
|
|
40
44
|
import { ColumnSync } from '../column-sync.js';
|
|
41
45
|
import {
|
|
42
46
|
ALL_STATES,
|
|
@@ -145,11 +149,11 @@ async function loadTicketSnapshot(provider, opts, ticketId) {
|
|
|
145
149
|
/**
|
|
146
150
|
* Mirror the post-flip label set onto the GitHub Projects v2 Status
|
|
147
151
|
* column. Story #2548 — wiring this here makes every caller of
|
|
148
|
-
* `transitionTicketState` (story-init
|
|
149
|
-
* the LabelTransitioner lifecycle listener, the
|
|
150
|
-
* batch transitions) update the board automatically.
|
|
151
|
-
* sync was only wired from the epic-runner against
|
|
152
|
-
* Stories and Tasks never had their `agent::executing` /
|
|
152
|
+
* `transitionTicketState` (`single-story-init.js`, `single-story-close.js`,
|
|
153
|
+
* `story-phase.js`, the LabelTransitioner lifecycle listener, the
|
|
154
|
+
* update-ticket-state CLI, batch transitions) update the board automatically.
|
|
155
|
+
* Prior to #2548 the sync was only wired from the deleted epic-runner against
|
|
156
|
+
* the Epic ticket, so Stories and Tasks never had their `agent::executing` /
|
|
153
157
|
* `agent::blocked` flips reflected on the board.
|
|
154
158
|
*
|
|
155
159
|
* Best-effort: a project-board misconfig, missing scope, or transient
|
|
@@ -279,6 +283,46 @@ function dispatchTransitionNotification(args) {
|
|
|
279
283
|
});
|
|
280
284
|
}
|
|
281
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Emit a runtime-derived `friction` signal when a ticket parks at
|
|
288
|
+
* `agent::blocked` (Story #4578). No-op for every other target state.
|
|
289
|
+
*
|
|
290
|
+
* Why here: `agent::blocked` is the single runtime HITL pause point
|
|
291
|
+
* (`.agents/instructions.md` § 1.J), and this is its canonical mutator — so
|
|
292
|
+
* one hook catches every block regardless of which path drove it (the
|
|
293
|
+
* `merge.unlanded` and `merge.flip-failed` paths in
|
|
294
|
+
* `single-story-close/phases/confirm-merge.js`, the review-block path, an
|
|
295
|
+
* operator's `update-ticket-state.js`, a Story worker giving up). Before
|
|
296
|
+
* this, a block was only ever a *label* — it left no trace in the friction
|
|
297
|
+
* stream the retro reads, so a run could park a worker and still produce a
|
|
298
|
+
* zero-signal roll-up.
|
|
299
|
+
*
|
|
300
|
+
* This is also why the terminal-envelope hook deliberately skips `blocked`
|
|
301
|
+
* (see `frictionForTerminal`): the two would otherwise count one incident
|
|
302
|
+
* twice.
|
|
303
|
+
*
|
|
304
|
+
* Best-effort and awaited: `emitRuntimeFriction` swallows its own failures
|
|
305
|
+
* and resolves `false`, so this can neither throw nor block the transition.
|
|
306
|
+
* It is awaited rather than fire-and-forget because CLI entry points exit
|
|
307
|
+
* via `process.exit` as soon as `main` resolves (`cli-utils.runAsCli` with
|
|
308
|
+
* `propagateExitCode`), which would discard a still-pending append.
|
|
309
|
+
*
|
|
310
|
+
* @param {number} ticketId
|
|
311
|
+
* @param {string} newState
|
|
312
|
+
* @param {{ config?: object }} opts
|
|
313
|
+
* @returns {Promise<void>}
|
|
314
|
+
*/
|
|
315
|
+
async function emitBlockedFriction(ticketId, newState, opts) {
|
|
316
|
+
if (newState !== STATE_LABELS.BLOCKED) return;
|
|
317
|
+
await emitRuntimeFriction({
|
|
318
|
+
storyId: ticketId,
|
|
319
|
+
category: RUNTIME_FRICTION_CATEGORIES.STORY_BLOCKED,
|
|
320
|
+
tool: 'transitionTicketState',
|
|
321
|
+
details: { toState: newState },
|
|
322
|
+
config: opts?.config,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
282
326
|
/**
|
|
283
327
|
* Transitions a ticket's label to the new state.
|
|
284
328
|
* Removes other agent:: state labels.
|
|
@@ -361,6 +405,10 @@ export async function transitionTicketState(
|
|
|
361
405
|
_ticketSnapshot: ticketSnapshot,
|
|
362
406
|
});
|
|
363
407
|
|
|
408
|
+
// Story #4578 — derive a friction signal from the block, at the point the
|
|
409
|
+
// runtime already knows. Best-effort; never blocks the transition.
|
|
410
|
+
await emitBlockedFriction(ticketId, newState, opts);
|
|
411
|
+
|
|
364
412
|
// Story #2548 — mirror the new state onto the Projects v2 Status
|
|
365
413
|
// column. Best-effort; never blocks the transition.
|
|
366
414
|
// Story #3645 — thread the DIP seam so callers can inject a stub.
|
|
@@ -460,14 +508,25 @@ export async function toggleTasklistCheckbox(
|
|
|
460
508
|
/**
|
|
461
509
|
* Post a structured comment to a ticket.
|
|
462
510
|
*
|
|
511
|
+
* Returns whatever the provider's `postComment` resolved to (Story #4543).
|
|
512
|
+
* Previously the result was swallowed, which made it impossible for a caller
|
|
513
|
+
* to reference the comment it had just written — the terminal envelope's
|
|
514
|
+
* `blocked.frictionCommentId` pointer needs exactly that, so an operator can
|
|
515
|
+
* be sent straight to the remediation instead of told to go find it. Callers
|
|
516
|
+
* that don't need the id simply ignore the return, so this is additive.
|
|
517
|
+
*
|
|
518
|
+
* The shape is provider-defined and may be `undefined` for providers that do
|
|
519
|
+
* not surface one; callers must treat the id as best-effort.
|
|
520
|
+
*
|
|
463
521
|
* @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
|
|
464
522
|
* @param {number} ticketId
|
|
465
523
|
* @param {'progress'|'friction'|'notification'} type
|
|
466
524
|
* @param {string} payload
|
|
525
|
+
* @returns {Promise<unknown>} The provider's `postComment` result.
|
|
467
526
|
*/
|
|
468
527
|
export async function postStructuredComment(provider, ticketId, type, payload) {
|
|
469
528
|
assertValidStructuredCommentType(type);
|
|
470
|
-
await provider.postComment(ticketId, {
|
|
529
|
+
const posted = await provider.postComment(ticketId, {
|
|
471
530
|
type,
|
|
472
531
|
body: payload,
|
|
473
532
|
});
|
|
@@ -475,4 +534,5 @@ export async function postStructuredComment(provider, ticketId, type, payload) {
|
|
|
475
534
|
// `findStructuredComment` against this ticket re-fetches and sees the
|
|
476
535
|
// freshly-posted comment.
|
|
477
536
|
invalidateRawCommentsCache(provider, ticketId);
|
|
537
|
+
return posted;
|
|
478
538
|
}
|