mandrel 1.94.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +59 -73
- package/.agents/agents/story-worker.md +12 -13
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +104 -138
- package/.agents/docs/execution-reference.md +17 -20
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +70 -81
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +6 -7
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +54 -214
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/risk-verdict.schema.json +0 -13
- package/.agents/scripts/analyze-execution.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/check-lifecycle-lint.js +6 -9
- package/.agents/scripts/check-prepush-recovery.js +1 -1
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- 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 +5 -5
- 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 -4
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
- package/.agents/scripts/lib/checks/index.js +2 -1
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +16 -7
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +45 -29
- package/.agents/scripts/lib/config/explain.js +55 -36
- package/.agents/scripts/lib/config/limits.js +17 -58
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +1 -1
- package/.agents/scripts/lib/config/runners.js +17 -50
- package/.agents/scripts/lib/config/temp-paths.js +19 -14
- 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 +24 -148
- package/.agents/scripts/lib/config-settings-schema-quality.js +8 -14
- package/.agents/scripts/lib/config-settings-schema.js +52 -38
- 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 +211 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +5 -5
- package/.agents/scripts/lib/framework-version.js +2 -3
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
- package/.agents/scripts/lib/git-utils.js +32 -6
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/label-constants.js +10 -23
- package/.agents/scripts/lib/label-taxonomy.js +9 -43
- package/.agents/scripts/lib/observability/active-story-env.js +1 -1
- package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
- package/.agents/scripts/lib/orchestration/code-review.js +20 -268
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- 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/lifecycle/emit-loop-tick.js +8 -8
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
- 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 +13 -13
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
- 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/phases/code-review.js +15 -12
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
- package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
- package/.agents/scripts/lib/orchestration/ticket-validator.js +2 -7
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
- package/.agents/scripts/lib/planning-corpus.js +16 -11
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/story-adjacency.js +11 -14
- package/.agents/scripts/lib/story-body/story-body.js +124 -70
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
- package/.agents/scripts/lib/validation-evidence.js +1 -1
- package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
- 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 +64 -74
- package/.agents/scripts/plan-persist.js +121 -280
- package/.agents/scripts/plan-run-epilogue.js +97 -0
- package/.agents/scripts/providers/github/issues.js +17 -33
- package/.agents/scripts/providers/github/mappers.js +0 -12
- package/.agents/scripts/providers/github/tickets.js +2 -5
- package/.agents/scripts/resolve-plan-run.js +117 -0
- package/.agents/scripts/signals-view.js +24 -19
- package/.agents/scripts/single-story-close.js +11 -14
- package/.agents/scripts/single-story-confirm-merge.js +39 -23
- package/.agents/scripts/single-story-init.js +29 -20
- package/.agents/scripts/stories-wave-tick.js +6 -6
- package/.agents/scripts/story-plan.js +26 -47
- package/.agents/scripts/update-ticket-state.js +6 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +3 -43
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +3 -3
- package/.agents/workflows/audit-clean-code.md +3 -3
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +5 -5
- package/.agents/workflows/audit-lighthouse.md +3 -3
- package/.agents/workflows/audit-navigability.md +3 -2
- package/.agents/workflows/audit-performance.md +3 -3
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +3 -3
- package/.agents/workflows/audit-security.md +3 -3
- package/.agents/workflows/audit-seo.md +3 -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 +3 -3
- package/.agents/workflows/deliver.md +122 -174
- package/.agents/workflows/git-cleanup.md +3 -4
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +16 -29
- package/.agents/workflows/helpers/code-review.md +70 -193
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
- package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +112 -145
- package/.agents/workflows/qa-assist.md +24 -30
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -2
- package/README.md +9 -8
- package/docs/CHANGELOG.md +30 -0
- package/lib/migrations/index.js +6 -5
- 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-spec.schema.json +0 -153
- 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/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- 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/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/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/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/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/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/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/post-merge-pipeline.js +0 -205
- 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-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/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/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
|
@@ -4,59 +4,30 @@
|
|
|
4
4
|
*
|
|
5
5
|
* All label names are sourced from `label-constants.js` so renames only need
|
|
6
6
|
* to happen in one place. Colors come from `LABEL_COLORS` in the same module.
|
|
7
|
+
*
|
|
8
|
+
* v2 deleted the behavioral persona concept (`.agents/personas/` +
|
|
9
|
+
* `persona::*` labels). Role framing for spawns lives in `.agents/agents/`
|
|
10
|
+
* via `delivery.routing.roleScopedAgents`; QA auth identities live in
|
|
11
|
+
* `qa.personas` — neither is a GitHub label axis.
|
|
7
12
|
*/
|
|
8
13
|
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
14
|
import {
|
|
13
15
|
ACCEPTANCE_LABELS,
|
|
14
16
|
AGENT_LABELS,
|
|
15
17
|
DELIVERY_LABELS,
|
|
16
18
|
LABEL_COLORS,
|
|
17
|
-
PERSONA_LABEL_PREFIX,
|
|
18
19
|
PLANNING_LABELS,
|
|
19
20
|
STATUS_LABELS,
|
|
20
21
|
TYPE_LABELS,
|
|
21
22
|
} from './label-constants.js';
|
|
22
23
|
|
|
23
|
-
const PERSONAS_DIR = path.resolve(
|
|
24
|
-
path.dirname(fileURLToPath(import.meta.url)),
|
|
25
|
-
'..',
|
|
26
|
-
'..',
|
|
27
|
-
'personas',
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Discover persona labels from `.agents/personas/*.md`. The filename
|
|
32
|
-
* (without extension) is the label suffix — this is the same value the
|
|
33
|
-
* context hydrator uses to resolve `persona::<name>` to its markdown file.
|
|
34
|
-
*/
|
|
35
|
-
function buildPersonaLabels() {
|
|
36
|
-
return fs
|
|
37
|
-
.readdirSync(PERSONAS_DIR)
|
|
38
|
-
.filter((f) => f.endsWith('.md'))
|
|
39
|
-
.map((f) => f.slice(0, -3))
|
|
40
|
-
.sort()
|
|
41
|
-
.map((name) => ({
|
|
42
|
-
name: `${PERSONA_LABEL_PREFIX}${name}`,
|
|
43
|
-
color: LABEL_COLORS.PERSONA,
|
|
44
|
-
description: `${name} persona`,
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
24
|
/** @type {Array<{ name: string, color: string, description: string }>} */
|
|
49
25
|
export const LABEL_TAXONOMY = [
|
|
50
26
|
// Type
|
|
51
|
-
{
|
|
52
|
-
name: TYPE_LABELS.EPIC,
|
|
53
|
-
color: LABEL_COLORS.TYPE,
|
|
54
|
-
description: 'Epic-level work item',
|
|
55
|
-
},
|
|
56
27
|
{
|
|
57
28
|
name: TYPE_LABELS.STORY,
|
|
58
29
|
color: LABEL_COLORS.TYPE,
|
|
59
|
-
description: '
|
|
30
|
+
description: 'Story work item',
|
|
60
31
|
},
|
|
61
32
|
|
|
62
33
|
// Agent State
|
|
@@ -95,9 +66,6 @@ export const LABEL_TAXONOMY = [
|
|
|
95
66
|
description: 'Blocked by a dependency',
|
|
96
67
|
},
|
|
97
68
|
|
|
98
|
-
// Persona — dynamically derived from .agents/personas/*.md
|
|
99
|
-
...buildPersonaLabels(),
|
|
100
|
-
|
|
101
69
|
// Acceptance axis — explicit opt-out signal for Epics that
|
|
102
70
|
// intentionally have no acceptance-table coverage (waives the Epic
|
|
103
71
|
// body's ## Acceptance Table section — Story #4324).
|
|
@@ -108,15 +76,13 @@ export const LABEL_TAXONOMY = [
|
|
|
108
76
|
},
|
|
109
77
|
|
|
110
78
|
// Planning axis — operator-applied waivers for the planning → delivery
|
|
111
|
-
// handoff gates.
|
|
112
|
-
//
|
|
113
|
-
// (`runPlanHealthcheck`) returned `ok: false` for a reason the
|
|
114
|
-
// operator has triaged and accepted.
|
|
79
|
+
// handoff gates. `healthcheck-waived` remains for tickets that still
|
|
80
|
+
// carry the historical label; the healthcheck CLI was retired.
|
|
115
81
|
{
|
|
116
82
|
name: PLANNING_LABELS.HEALTHCHECK_WAIVED,
|
|
117
83
|
color: LABEL_COLORS.PLANNING,
|
|
118
84
|
description:
|
|
119
|
-
'
|
|
85
|
+
'Historical operator override for the retired post-plan healthcheck',
|
|
120
86
|
},
|
|
121
87
|
|
|
122
88
|
// Delivery routing — applied by `plan-persist.js` when the risk verdict
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* This module is the single writer/clearer of those vars:
|
|
8
8
|
*
|
|
9
9
|
* - `setActiveStoryEnv({ epicId, storyId, workCwd })` is called from
|
|
10
|
-
* `story-init.js` after the worktree is materialised. It sets the
|
|
10
|
+
* `single-story-init.js` after the worktree is materialised. It sets the
|
|
11
11
|
* vars on the current `process.env` (so any child commands the
|
|
12
12
|
* orchestrator spawns inherit them) and exports them to a sibling
|
|
13
13
|
* `.env.local` inside the worktree. The harness re-spawns the
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* hook-heartbeat.js — Epic #4476 (M5): heartbeats OFF the token stream.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* **LLM obligation**: the delivery workflow
|
|
4
|
+
* Before M5, the `story.heartbeat` / `slice.heartbeat` forward-progress
|
|
5
|
+
* signal that idle-watchdog consumers read from the lifecycle ledger was an
|
|
6
|
+
* **LLM obligation**: the delivery workflow instructed the agent to run
|
|
7
7
|
* `story-phase.js` / `slice-phase.js --event heartbeat` at least once per
|
|
8
|
-
* meaningful step, and every such call
|
|
8
|
+
* meaningful step, and every such call was a full-priced LLM turn re-reading
|
|
9
9
|
* ~100k of cached context just to append one NDJSON line.
|
|
10
10
|
*
|
|
11
11
|
* This module makes that liveness signal a **free byproduct of the agent
|
|
@@ -19,11 +19,10 @@
|
|
|
19
19
|
* mechanism moves off the token stream.
|
|
20
20
|
*
|
|
21
21
|
* ## Robustness contract (mirrors `tool-trace-hook.js`)
|
|
22
|
-
* - **No-op outside an active Story
|
|
23
|
-
* `{ CC_EPIC_ID, CC_STORY_ID }`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* filesystem.
|
|
22
|
+
* - **No-op outside an active Story with an Epic-scoped ledger.** When a
|
|
23
|
+
* valid `{ CC_EPIC_ID, CC_STORY_ID }` pair is not present in the
|
|
24
|
+
* environment (v2 standalone Stories omit `CC_EPIC_ID`), `emitHeartbeatFromHook`
|
|
25
|
+
* returns without touching the filesystem.
|
|
27
26
|
* - **Best-effort.** Every failure is swallowed. A heartbeat is
|
|
28
27
|
* observability, not state; a hook must never block tool execution.
|
|
29
28
|
* - **Throttled across processes.** Command hooks are spawned as fresh
|
|
@@ -39,7 +38,6 @@ import { statSync, utimesSync, writeFileSync } from 'node:fs';
|
|
|
39
38
|
import path from 'node:path';
|
|
40
39
|
|
|
41
40
|
import { epicTempDir } from '../config/temp-paths.js';
|
|
42
|
-
import { emitSliceHeartbeat } from '../orchestration/lifecycle/emit-slice-lifecycle.js';
|
|
43
41
|
import { emitStoryHeartbeat } from '../orchestration/lifecycle/emit-story-heartbeat.js';
|
|
44
42
|
|
|
45
43
|
/**
|
|
@@ -53,25 +51,22 @@ const HEARTBEAT_MIN_INTERVAL_MS = 60_000;
|
|
|
53
51
|
/**
|
|
54
52
|
* Resolve the heartbeat target from the active-Story / active-slice env vars.
|
|
55
53
|
*
|
|
56
|
-
* Precedence: a present `CC_STORY_ID` (fan-out Story child) wins over
|
|
57
|
-
* `CC_SLICE_ID` (single-delivery session) — the two are never set together in
|
|
58
|
-
* practice, but the ordering keeps the resolution deterministic if they were.
|
|
59
|
-
*
|
|
60
54
|
* A `story.heartbeat` requires a parent `epicId` (its schema pins
|
|
61
55
|
* `epicId >= 1`), so a **standalone** Story (`CC_STORY_ID` set, `CC_EPIC_ID`
|
|
62
56
|
* absent) yields `null` — there is no Epic-scoped ledger to write to and the
|
|
63
57
|
* standalone path is not watched by the Epic idle watchdog. The trace hook
|
|
64
58
|
* still records that context's traces; only the heartbeat is skipped.
|
|
65
59
|
*
|
|
60
|
+
* Inert `slice.*` lifecycle (and `CC_SLICE_ID` heartbeats) were removed in
|
|
61
|
+
* the v2 ceremony lock-in — `## Slicing` is prose-only.
|
|
62
|
+
*
|
|
66
63
|
* @param {NodeJS.ProcessEnv} [env]
|
|
67
64
|
* @returns {{ kind: 'story', epicId: number, storyId: number, operator?: string }
|
|
68
|
-
* | { kind: 'slice', epicId: number, sliceId: string, operator?: string }
|
|
69
65
|
* | null}
|
|
70
66
|
*/
|
|
71
67
|
export function resolveHeartbeatTarget(env = process.env) {
|
|
72
68
|
const epicRaw = env.CC_EPIC_ID;
|
|
73
69
|
const storyRaw = env.CC_STORY_ID;
|
|
74
|
-
const sliceRaw = env.CC_SLICE_ID;
|
|
75
70
|
const operator =
|
|
76
71
|
typeof env.CC_OPERATOR === 'string' && env.CC_OPERATOR.length > 0
|
|
77
72
|
? env.CC_OPERATOR
|
|
@@ -93,17 +88,6 @@ export function resolveHeartbeatTarget(env = process.env) {
|
|
|
93
88
|
};
|
|
94
89
|
}
|
|
95
90
|
|
|
96
|
-
if (sliceRaw) {
|
|
97
|
-
if (typeof sliceRaw !== 'string' || sliceRaw.length === 0) return null;
|
|
98
|
-
if (!epicOk) return null;
|
|
99
|
-
return {
|
|
100
|
-
kind: 'slice',
|
|
101
|
-
epicId,
|
|
102
|
-
sliceId: sliceRaw,
|
|
103
|
-
...(operator ? { operator } : {}),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
91
|
return null;
|
|
108
92
|
}
|
|
109
93
|
|
|
@@ -115,11 +99,7 @@ export function resolveHeartbeatTarget(env = process.env) {
|
|
|
115
99
|
* @returns {string}
|
|
116
100
|
*/
|
|
117
101
|
export function heartbeatMarkerName(target) {
|
|
118
|
-
|
|
119
|
-
target.kind === 'story'
|
|
120
|
-
? `story-${target.storyId}`
|
|
121
|
-
: `slice-${String(target.sliceId).replace(/[^A-Za-z0-9._-]/g, '_')}`;
|
|
122
|
-
return `.heartbeat-${key}`;
|
|
102
|
+
return `.heartbeat-story-${target.storyId}`;
|
|
123
103
|
}
|
|
124
104
|
|
|
125
105
|
/**
|
|
@@ -191,26 +171,14 @@ export function emitHeartbeatFromHook({
|
|
|
191
171
|
}
|
|
192
172
|
|
|
193
173
|
const timestamp = now.toISOString();
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
config: config ?? undefined,
|
|
203
|
-
});
|
|
204
|
-
} else {
|
|
205
|
-
res = emitSliceHeartbeat({
|
|
206
|
-
epicId: target.epicId,
|
|
207
|
-
sliceId: target.sliceId,
|
|
208
|
-
phase: 'implementing',
|
|
209
|
-
timestamp,
|
|
210
|
-
...(target.operator ? { operator: target.operator } : {}),
|
|
211
|
-
config: config ?? undefined,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
174
|
+
const res = emitStoryHeartbeat({
|
|
175
|
+
epicId: target.epicId,
|
|
176
|
+
storyId: target.storyId,
|
|
177
|
+
phase: 'implementing',
|
|
178
|
+
timestamp,
|
|
179
|
+
...(target.operator ? { operator: target.operator } : {}),
|
|
180
|
+
config: config ?? undefined,
|
|
181
|
+
});
|
|
214
182
|
touchMarker(markerPath, now);
|
|
215
183
|
return { emitted: true, kind: target.kind, ledgerPath: res?.ledgerPath };
|
|
216
184
|
} catch {
|
|
@@ -57,7 +57,7 @@ function toScanString(value) {
|
|
|
57
57
|
/**
|
|
58
58
|
* Return true when `haystack` contains any framework prefix as a
|
|
59
59
|
* substring. We use `includes` (not `startsWith`) because a command line
|
|
60
|
-
* like `node .agents/scripts/story-init.js` carries the prefix in the
|
|
60
|
+
* like `node .agents/scripts/single-story-init.js` carries the prefix in the
|
|
61
61
|
* middle, and a failing-path like `repo/.agents/foo.js` may carry an
|
|
62
62
|
* absolute prefix.
|
|
63
63
|
*
|
|
@@ -83,13 +83,13 @@ function containsFrameworkPrefix(haystack) {
|
|
|
83
83
|
* Framework-wins: if either input matches a framework prefix, the result
|
|
84
84
|
* is `"framework"` even when the other input looks consumer-shaped. This
|
|
85
85
|
* matters for mixed cases like a consumer test invoking
|
|
86
|
-
* `node .agents/scripts/story-init.js` — that's framework friction even
|
|
86
|
+
* `node .agents/scripts/single-story-init.js` — that's framework friction even
|
|
87
87
|
* though the failing test path lives under the consumer.
|
|
88
88
|
*
|
|
89
89
|
* @param {unknown} failingPath The path of the file or directory the
|
|
90
90
|
* signal blames (e.g. `"tests/foo.test.js"`).
|
|
91
91
|
* @param {unknown} command The command line the signal blames
|
|
92
|
-
* (e.g. `"node .agents/scripts/story-init.js"`).
|
|
92
|
+
* (e.g. `"node .agents/scripts/single-story-init.js"`).
|
|
93
93
|
* @returns {"framework"|"consumer"}
|
|
94
94
|
*/
|
|
95
95
|
export function classifyPathSource(failingPath, command) {
|
|
@@ -37,9 +37,7 @@ import { STUB_MARKER, scaffoldDocs } from './scaffold-docs.js';
|
|
|
37
37
|
*/
|
|
38
38
|
export const PLAN_HANDOFF_TEXT =
|
|
39
39
|
'\n✅ Mandrel is ready. Start your first project:\n\n' +
|
|
40
|
-
' /plan --
|
|
41
|
-
'Or, if you already have a `type::epic` Issue open:\n\n' +
|
|
42
|
-
' /plan <epicId>\n';
|
|
40
|
+
' /plan --seed "<one-line description of what you want to build>"\n';
|
|
43
41
|
|
|
44
42
|
// ---------------------------------------------------------------------------
|
|
45
43
|
// Internal helpers
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-lens-routing.js — risk-envelope → audit-lens roster helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `code-review.js` so the review runner stays focused on
|
|
5
|
+
* executing a review pass, while callers that only need risk→lens mapping
|
|
6
|
+
* (ceremony routing, plan-run epilogue consumers, unit tests) import a
|
|
7
|
+
* smaller pure module.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { selectAuditStrategy } from '../dynamic-workflow/capability.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The axes whose presence (at `high` risk) routes a specific post-delivery
|
|
14
|
+
* audit lens. Mirrors the audit-workflow names under
|
|
15
|
+
* `.agents/workflows/audit-*.md`. Story #3939 broadened the routing so every
|
|
16
|
+
* high-risk REQUIRED axis maps to a lens rather than leaving three of them
|
|
17
|
+
* (`data-migration`, `destructive-mutation`, `billing`) routing nothing:
|
|
18
|
+
*
|
|
19
|
+
* - `security` → `audit-security` (auth/secret boundary)
|
|
20
|
+
* - `public-api` → `audit-architecture` (the canonical
|
|
21
|
+
* architectural axis; a breaking public surface)
|
|
22
|
+
* - `data-migration` → `audit-quality` (migration correctness +
|
|
23
|
+
* regression coverage)
|
|
24
|
+
* - `destructive-mutation` → `audit-security` (irreversible mutation is
|
|
25
|
+
* an auth/abuse boundary; co-routes with
|
|
26
|
+
* `security`, de-duplicated)
|
|
27
|
+
* - `billing` → `audit-privacy` (money + the PII/consent
|
|
28
|
+
* surface that travels with it)
|
|
29
|
+
* - `critical-workflow` → `audit-quality` (the load-bearing path
|
|
30
|
+
* warrants the deepest coverage pass)
|
|
31
|
+
*
|
|
32
|
+
* The `visible-behavior` axis intentionally routes NO lens: it forces the
|
|
33
|
+
* acceptance-spec disposition at plan time and no audit lens maps cleanly to
|
|
34
|
+
* it. Any other axis (or a low/medium-risk axis) contributes no lens.
|
|
35
|
+
*
|
|
36
|
+
* Every key here MUST be a value in the `axis` enum of
|
|
37
|
+
* `.agents/schemas/risk-verdict.schema.json` — the verdict-derived envelope
|
|
38
|
+
* can only ever carry schema-valid axes, so a key absent from that enum is
|
|
39
|
+
* unreachable dead routing (Story #3889 removed the unreachable
|
|
40
|
+
* `architecture` key; the architectural axis is `public-api`).
|
|
41
|
+
*/
|
|
42
|
+
const AXIS_TO_LENS = Object.freeze({
|
|
43
|
+
security: 'audit-security',
|
|
44
|
+
'public-api': 'audit-architecture',
|
|
45
|
+
'data-migration': 'audit-quality',
|
|
46
|
+
'destructive-mutation': 'audit-security',
|
|
47
|
+
billing: 'audit-privacy',
|
|
48
|
+
'critical-workflow': 'audit-quality',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stable output order for routed lenses so the lens list is deterministic
|
|
53
|
+
* regardless of axis ordering in the verdict. Every lens any axis can route
|
|
54
|
+
* appears here exactly once; `resolveAuditLenses` filters this canonical
|
|
55
|
+
* order down to the matched set (Story #3939).
|
|
56
|
+
*/
|
|
57
|
+
const LENS_ORDER = Object.freeze([
|
|
58
|
+
'audit-security',
|
|
59
|
+
'audit-architecture',
|
|
60
|
+
'audit-quality',
|
|
61
|
+
'audit-privacy',
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the set of post-delivery audit lenses a judged risk envelope routes.
|
|
66
|
+
*
|
|
67
|
+
* High-risk axes map to their audit lens via {@link AXIS_TO_LENS}; only axes
|
|
68
|
+
* judged `high` contribute (a `low`/`medium` axis carries no lens). The result
|
|
69
|
+
* is de-duplicated and stably ordered by {@link LENS_ORDER} so an envelope
|
|
70
|
+
* listing the `public-api` axis more than once routes `['audit-architecture']`
|
|
71
|
+
* once, not twice — and two distinct axes routing the same lens (e.g.
|
|
72
|
+
* `security` + `destructive-mutation` both → `audit-security`) collapse to a
|
|
73
|
+
* single entry. A low-risk envelope — or any envelope with no high-risk routed
|
|
74
|
+
* axis — resolves to an empty array (no lens beyond the existing baseline
|
|
75
|
+
* gates).
|
|
76
|
+
*
|
|
77
|
+
* Pure function — no I/O, no side effects.
|
|
78
|
+
*
|
|
79
|
+
* @param {{ axes?: Array<{ axis?: string, level?: string }> }} [envelope]
|
|
80
|
+
* @returns {string[]} Ordered, de-duplicated audit-lens identifiers.
|
|
81
|
+
*/
|
|
82
|
+
export function resolveAuditLenses(envelope = {}) {
|
|
83
|
+
const axes = Array.isArray(envelope?.axes) ? envelope.axes : [];
|
|
84
|
+
const matched = new Set();
|
|
85
|
+
for (const entry of axes) {
|
|
86
|
+
if (!entry || entry.level !== 'high') continue;
|
|
87
|
+
const lens = AXIS_TO_LENS[entry.axis];
|
|
88
|
+
if (lens) matched.add(lens);
|
|
89
|
+
}
|
|
90
|
+
return LENS_ORDER.filter((lens) => matched.has(lens));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build the post-delivery audit-lens execution plan for a judged risk
|
|
95
|
+
* envelope. Each routed lens (see {@link resolveAuditLenses}) is paired with a
|
|
96
|
+
* strategy decision from the **existing** `selectAuditStrategy` engine — no new
|
|
97
|
+
* audit machinery is introduced. A low-risk envelope resolves to an empty
|
|
98
|
+
* `lenses` array and runs no audit beyond the baseline gates (Story #3876).
|
|
99
|
+
*
|
|
100
|
+
* Pure with respect to the injected `selectAuditStrategyFn` (default is the
|
|
101
|
+
* shared dynamic-workflow engine, which is itself pure over its snapshot).
|
|
102
|
+
*
|
|
103
|
+
* @param {{ axes?: Array<{ axis?: string, level?: string }> }} [envelope]
|
|
104
|
+
* @param {{
|
|
105
|
+
* snapshot?: object,
|
|
106
|
+
* forceStrategy?: ('orchestrated'|'sequential'|null),
|
|
107
|
+
* selectAuditStrategyFn?: typeof selectAuditStrategy,
|
|
108
|
+
* }} [opts]
|
|
109
|
+
* @returns {{ lenses: string[], plan: Array<{ lens: string, strategy: string, reason: string, forced: boolean }> }}
|
|
110
|
+
*/
|
|
111
|
+
export function planAuditLenses(envelope = {}, opts = {}) {
|
|
112
|
+
const {
|
|
113
|
+
snapshot = {},
|
|
114
|
+
forceStrategy = null,
|
|
115
|
+
selectAuditStrategyFn = selectAuditStrategy,
|
|
116
|
+
} = opts;
|
|
117
|
+
const lenses = resolveAuditLenses(envelope);
|
|
118
|
+
const plan = lenses.map((lens) => {
|
|
119
|
+
const decision = selectAuditStrategyFn({ snapshot, forceStrategy });
|
|
120
|
+
return {
|
|
121
|
+
lens,
|
|
122
|
+
strategy: decision.strategy,
|
|
123
|
+
reason: decision.reason,
|
|
124
|
+
forced: decision.forced,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
return { lenses, plan };
|
|
128
|
+
}
|
|
@@ -37,24 +37,27 @@ import {
|
|
|
37
37
|
} from 'node:fs';
|
|
38
38
|
import path from 'node:path';
|
|
39
39
|
|
|
40
|
-
import {
|
|
40
|
+
import { runTempDir } from '../config/temp-paths.js';
|
|
41
41
|
import { STATE_LABELS } from './ticketing/reads.js';
|
|
42
42
|
import { upsertStructuredComment } from './ticketing/state.js';
|
|
43
43
|
import { transitionTicketState } from './ticketing/transition.js';
|
|
44
44
|
|
|
45
|
-
/** Canonical basename for the per-
|
|
45
|
+
/** Canonical basename for the per-run bookkeeping outbox. */
|
|
46
46
|
const OUTBOX_BASENAME = 'bookkeeping-outbox.ndjson';
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Resolve the canonical outbox path for
|
|
50
|
-
* `temp/
|
|
49
|
+
* Resolve the canonical outbox path for a run:
|
|
50
|
+
* `temp/run-<id>/bookkeeping-outbox.ndjson`.
|
|
51
|
+
*
|
|
52
|
+
* Uses `runTempDir` directly; callers may still pass an `epicId`-named
|
|
53
|
+
* variable while the broader file/flag rename remains out of this pass.
|
|
51
54
|
*
|
|
52
55
|
* @param {number} epicId
|
|
53
56
|
* @param {object} [config] Resolved config (tempRoot).
|
|
54
57
|
* @returns {string}
|
|
55
58
|
*/
|
|
56
59
|
export function outboxPathFor(epicId, config) {
|
|
57
|
-
return path.join(
|
|
60
|
+
return path.join(runTempDir(epicId, config), OUTBOX_BASENAME);
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/orchestration/ceremony-routing.js — risk-routed
|
|
3
|
-
*
|
|
2
|
+
* lib/orchestration/ceremony-routing.js — ceremony-profile + risk-routed
|
|
3
|
+
* acceptance ceremony resolver.
|
|
4
4
|
*
|
|
5
5
|
* The sibling of `review-depth.js` (risk → review depth) and
|
|
6
|
-
* `
|
|
7
|
-
* planner-judged risk envelope into a
|
|
8
|
-
* single-delivery acceptance critic —
|
|
9
|
-
* contract-identical **inline** critic. It
|
|
10
|
-
* and it does NOT own clustering.
|
|
6
|
+
* `audit-lens-routing.js#resolveAuditLenses` (risk → audit lens): it folds the
|
|
7
|
+
* operator ceremony profile and the planner-judged risk envelope into a
|
|
8
|
+
* per-cluster ceremony decision for the single-delivery acceptance critic —
|
|
9
|
+
* **fresh-context spawn** vs the contract-identical **inline** critic. It
|
|
10
|
+
* does NOT invent a new risk score and it does NOT own clustering.
|
|
11
|
+
*
|
|
12
|
+
* ## Ceremony profiles (`delivery.routing.ceremonyProfile`)
|
|
13
|
+
*
|
|
14
|
+
* - `minimal` — always `inline` (skip fresh critic + sampling floor).
|
|
15
|
+
* Use for tiny N=1 Stories the operator trusts.
|
|
16
|
+
* - `standard` — risk-routed (default). Low → inline (+ sampling floor);
|
|
17
|
+
* medium/high → fresh.
|
|
18
|
+
* - `strict` — always `fresh` regardless of risk.
|
|
11
19
|
*
|
|
12
20
|
* ## The load-bearing invariant (M4-B acceptance floor — DO NOT VIOLATE)
|
|
13
21
|
*
|
|
14
22
|
* Risk-routing chooses fresh-vs-inline **PER CLUSTER**. It NEVER changes the
|
|
15
23
|
* cluster COUNT. The cluster count is `ceil(totalACs / clusterCeiling)` with
|
|
16
24
|
* the non-disableable `[1, 8]` clamp, owned entirely by
|
|
17
|
-
* `acceptance-clusters.js` and untouched here. A low-risk
|
|
25
|
+
* `acceptance-clusters.js` and untouched here. A low-risk Story still gets one
|
|
18
26
|
* verdict per cluster — just possibly authored inline instead of by a fresh
|
|
19
27
|
* sub-agent. This module takes the cluster index as an INPUT and returns a
|
|
20
28
|
* decision for that one cluster; it has no way to add or remove clusters.
|
|
21
29
|
*
|
|
22
|
-
* ## Tier rules (per cluster)
|
|
30
|
+
* ## Tier rules (per cluster, `standard` profile)
|
|
23
31
|
*
|
|
24
32
|
* - `high` risk → `fresh` (a fresh-context maker-blind spawn).
|
|
25
33
|
* - `medium` risk → `fresh` (fail toward more ceremony, never less —
|
|
@@ -27,7 +35,7 @@
|
|
|
27
35
|
* - `low` risk → `inline` (the contract-identical inline critic),
|
|
28
36
|
* UNLESS the maker-checker sampling floor
|
|
29
37
|
* selects this cluster → `fresh`.
|
|
30
|
-
* - missing / unknown → `fresh` (fail-safe:
|
|
38
|
+
* - missing / unknown → `fresh` (fail-safe: a Story that skipped `/plan`
|
|
31
39
|
* has no risk verdict; treat it as needing
|
|
32
40
|
* the full fresh-context ceremony, exactly
|
|
33
41
|
* as `review-depth` degrades to `standard`
|
|
@@ -36,18 +44,42 @@
|
|
|
36
44
|
*
|
|
37
45
|
* ## Maker-checker sampling floor
|
|
38
46
|
*
|
|
39
|
-
* Even at `low` risk
|
|
40
|
-
* 0.2) is forced `fresh` so low risk never
|
|
41
|
-
* selection is **deterministic** in the
|
|
42
|
-
* is stable across re-runs and —
|
|
43
|
-
* it only re-labels which of
|
|
47
|
+
* Even at `low` risk under `standard`, a fraction of clusters
|
|
48
|
+
* (`freshCriticSampleRate`, default 0.2) is forced `fresh` so low risk never
|
|
49
|
+
* means zero independent checking. The selection is **deterministic** in the
|
|
50
|
+
* cluster index (a fixed stride), so it is stable across re-runs and —
|
|
51
|
+
* critically — never changes the cluster count: it only re-labels which of
|
|
52
|
+
* the fixed set of clusters run fresh. Profiles `minimal` and `strict`
|
|
53
|
+
* ignore the sampling floor.
|
|
44
54
|
*
|
|
45
55
|
* Pure and total: inputs in, decision out. No I/O, no throws. `null` /
|
|
46
56
|
* `undefined` / malformed inputs degrade to `fresh` + `full` ceremony.
|
|
47
57
|
*
|
|
48
58
|
* @typedef {'fresh'|'inline'} CeremonyMode
|
|
49
59
|
* @typedef {'low'|'medium'|'high'} RiskLevel
|
|
60
|
+
* @typedef {'minimal'|'standard'|'strict'} CeremonyProfile
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/** @type {readonly CeremonyProfile[]} */
|
|
64
|
+
export const CEREMONY_PROFILES = Object.freeze([
|
|
65
|
+
'minimal',
|
|
66
|
+
'standard',
|
|
67
|
+
'strict',
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Normalize an operator/config ceremony profile. Unknown values degrade to
|
|
72
|
+
* `standard` (fail toward the documented default, not toward less ceremony).
|
|
73
|
+
*
|
|
74
|
+
* @param {unknown} value
|
|
75
|
+
* @returns {CeremonyProfile}
|
|
50
76
|
*/
|
|
77
|
+
export function normalizeCeremonyProfile(value) {
|
|
78
|
+
if (value === 'minimal' || value === 'standard' || value === 'strict') {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
return 'standard';
|
|
82
|
+
}
|
|
51
83
|
|
|
52
84
|
/**
|
|
53
85
|
* Decide whether the sampling floor forces this low-risk cluster fresh.
|
|
@@ -79,16 +111,22 @@ export function sampledFresh(clusterIndex, rate) {
|
|
|
79
111
|
}
|
|
80
112
|
|
|
81
113
|
/**
|
|
82
|
-
* Resolve the acceptance ceremony for one cluster from the
|
|
83
|
-
* and the maker-checker sampling floor. See the module
|
|
84
|
-
* rules and the untouchable cluster-count invariant.
|
|
114
|
+
* Resolve the acceptance ceremony for one cluster from the ceremony profile,
|
|
115
|
+
* judged risk level, and the maker-checker sampling floor. See the module
|
|
116
|
+
* header for the tier rules and the untouchable cluster-count invariant.
|
|
85
117
|
*
|
|
86
118
|
* @param {{
|
|
87
119
|
* overallLevel?: (RiskLevel|string|null|undefined),
|
|
88
120
|
* clusterIndex?: (number|null|undefined),
|
|
89
121
|
* freshCriticSampleRate?: (number|null|undefined),
|
|
122
|
+
* ceremonyProfile?: (CeremonyProfile|string|null|undefined),
|
|
90
123
|
* }} [input]
|
|
91
|
-
* @returns {{
|
|
124
|
+
* @returns {{
|
|
125
|
+
* mode: CeremonyMode,
|
|
126
|
+
* reason: string,
|
|
127
|
+
* sampled: boolean,
|
|
128
|
+
* profile: CeremonyProfile,
|
|
129
|
+
* }}
|
|
92
130
|
*/
|
|
93
131
|
export function resolveCeremonyForRisk(input = {}) {
|
|
94
132
|
const overallLevel =
|
|
@@ -99,12 +137,33 @@ export function resolveCeremonyForRisk(input = {}) {
|
|
|
99
137
|
input && typeof input === 'object'
|
|
100
138
|
? input.freshCriticSampleRate
|
|
101
139
|
: undefined;
|
|
140
|
+
const profile = normalizeCeremonyProfile(
|
|
141
|
+
input && typeof input === 'object' ? input.ceremonyProfile : undefined,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (profile === 'minimal') {
|
|
145
|
+
return {
|
|
146
|
+
mode: 'inline',
|
|
147
|
+
reason: 'ceremonyProfile=minimal: inline critic (no fresh spawn)',
|
|
148
|
+
sampled: false,
|
|
149
|
+
profile,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (profile === 'strict') {
|
|
153
|
+
return {
|
|
154
|
+
mode: 'fresh',
|
|
155
|
+
reason: 'ceremonyProfile=strict: fresh-context critic',
|
|
156
|
+
sampled: false,
|
|
157
|
+
profile,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
102
160
|
|
|
103
161
|
if (overallLevel === 'high') {
|
|
104
162
|
return {
|
|
105
163
|
mode: 'fresh',
|
|
106
164
|
reason: 'high-risk: fresh-context critic',
|
|
107
165
|
sampled: false,
|
|
166
|
+
profile,
|
|
108
167
|
};
|
|
109
168
|
}
|
|
110
169
|
if (overallLevel === 'medium') {
|
|
@@ -112,6 +171,7 @@ export function resolveCeremonyForRisk(input = {}) {
|
|
|
112
171
|
mode: 'fresh',
|
|
113
172
|
reason: 'medium-risk: fresh-context critic (fail toward more ceremony)',
|
|
114
173
|
sampled: false,
|
|
174
|
+
profile,
|
|
115
175
|
};
|
|
116
176
|
}
|
|
117
177
|
if (overallLevel === 'low') {
|
|
@@ -121,21 +181,24 @@ export function resolveCeremonyForRisk(input = {}) {
|
|
|
121
181
|
reason:
|
|
122
182
|
'low-risk cluster forced fresh by the maker-checker sampling floor',
|
|
123
183
|
sampled: true,
|
|
184
|
+
profile,
|
|
124
185
|
};
|
|
125
186
|
}
|
|
126
187
|
return {
|
|
127
188
|
mode: 'inline',
|
|
128
189
|
reason: 'low-risk: contract-identical inline critic',
|
|
129
190
|
sampled: false,
|
|
191
|
+
profile,
|
|
130
192
|
};
|
|
131
193
|
}
|
|
132
194
|
// Missing / unknown / malformed risk → fail-safe fresh + full ceremony,
|
|
133
195
|
// matching how review-depth.js and deriveRiskEnvelope degrade on an
|
|
134
|
-
// unjudged
|
|
196
|
+
// unjudged Story.
|
|
135
197
|
return {
|
|
136
198
|
mode: 'fresh',
|
|
137
199
|
reason:
|
|
138
200
|
'risk absent/unknown: fail-safe fresh-context critic + full ceremony',
|
|
139
201
|
sampled: false,
|
|
202
|
+
profile,
|
|
140
203
|
};
|
|
141
204
|
}
|