mandrel 1.93.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/acceptance-critic.md +129 -0
- package/.agents/agents/story-worker.md +161 -0
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +108 -136
- package/.agents/docs/execution-reference.md +44 -22
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +107 -108
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +224 -0
- package/.agents/rules/git-conventions.md +42 -223
- package/.agents/rules/security-baseline.md +5 -0
- package/.agents/rules/testing-standards.md +106 -13
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +71 -201
- 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/acceptance-eval.js +62 -18
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- 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-context-budget.js +62 -5
- 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/diagnose-friction.js +0 -6
- 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/bootstrap/project-bootstrap.js +56 -18
- 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 +160 -22
- package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +103 -0
- package/.agents/scripts/lib/config/explain.js +57 -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 +3 -8
- package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -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 +39 -4
- 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 +112 -3
- package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
- 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-critic-conditions.js +3 -2
- 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 +14 -14
- 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/close-validation.js +1 -0
- 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 +3 -8
- 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/provider-factory.js +1 -1
- 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/post-structured-comment.js +38 -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/sync-claude-agents.js +165 -0
- package/.agents/scripts/update-ticket-state.js +37 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
- package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
- package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +8 -418
- 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 -131
- 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 +52 -40
- 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/mandrel-sync-config.md +1 -1
- 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 +3 -2
- 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 +46 -0
- package/lib/cli/registry.js +95 -0
- package/lib/migrations/index.js +6 -5
- package/package.json +5 -3
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer-mobile.md +0 -120
- package/.agents/personas/engineer-web.md +0 -111
- package/.agents/personas/engineer.md +0 -119
- package/.agents/personas/product.md +0 -94
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/refactorer.md +0 -113
- package/.agents/personas/security-engineer.md +0 -112
- package/.agents/personas/sre.md +0 -86
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/personas/ux-designer.md +0 -95
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- 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 -590
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-decompose.js +0 -54
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-plan-spec.js +0 -64
- 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 -660
- 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 -388
- 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-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
- 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 -278
- 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/skill-capsule-loader.js +0 -109
- 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 -301
- 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/plan-critics.js +0 -199
- package/.agents/scripts/retro-run.js +0 -218
- 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 -335
- package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
- package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
- package/.agents/skills/core/code-simplification/SKILL.md +0 -389
- package/.agents/skills/core/context-engineering/SKILL.md +0 -309
- package/.agents/skills/core/context-engineering/examples.md +0 -58
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
- 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 -413
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
- package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
- package/.agents/skills/core/idea-refinement/examples.md +0 -437
- package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
- package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
- package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
- package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
- package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
- package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
- package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
- package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
- package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
- package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
- package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
- package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
- package/.agents/workflows/helpers/deliver-epic.md +0 -955
- package/.agents/workflows/helpers/deliver-stories.md +0 -440
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
- 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 -351
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bookkeeping-outbox.js — Epic #4476 (M5): buffered GitHub bookkeeping.
|
|
3
|
+
*
|
|
4
|
+
* Delivery bookkeeping — structured-comment upserts (progress, friction,
|
|
5
|
+
* wave-stall, …) and `agent::*` label flips — is human-visible surface that,
|
|
6
|
+
* in an **unattended** (`--yes` / headless) run, no operator is watching in
|
|
7
|
+
* real time. Emitting each one as a live GitHub round-trip mid-run costs an
|
|
8
|
+
* LLM turn per transition for a surface nobody reads until the run finishes.
|
|
9
|
+
*
|
|
10
|
+
* This module lets those non-urgent mutations be **buffered to a local NDJSON
|
|
11
|
+
* outbox** during the run and **reconciled to GitHub once at finalize**.
|
|
12
|
+
* GitHub stays the source of truth *at rest* (post-reconcile); only the
|
|
13
|
+
* per-transition chatter moves off the token stream. The outbox file survives
|
|
14
|
+
* a crash, so crash recovery drains whatever was buffered before the finalize
|
|
15
|
+
* reconcile ran.
|
|
16
|
+
*
|
|
17
|
+
* ## What is NEVER buffered
|
|
18
|
+
* The `agent::blocked` HITL gate (`.agents/instructions.md` §1.J) is the single
|
|
19
|
+
* authoritative runtime pause point. A genuine blocker MUST surface on GitHub
|
|
20
|
+
* **immediately**, not batched to finalize — an operator can only resume a run
|
|
21
|
+
* they can see is blocked. The {@link transitionStateOrBuffer} facade forces
|
|
22
|
+
* `agent::blocked` (and any caller-marked `urgent`) transition through the
|
|
23
|
+
* live path regardless of headless mode.
|
|
24
|
+
*
|
|
25
|
+
* ## Attended runs are unchanged
|
|
26
|
+
* Buffering is gated on the explicit `headless` signal (Story #4427's
|
|
27
|
+
* `--headless`/`--yes` plumbing). An attended run passes `headless: false` and
|
|
28
|
+
* every comment/label posts live exactly as before — byte-for-byte behaviour.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
appendFileSync,
|
|
33
|
+
existsSync,
|
|
34
|
+
mkdirSync,
|
|
35
|
+
readFileSync,
|
|
36
|
+
writeFileSync,
|
|
37
|
+
} from 'node:fs';
|
|
38
|
+
import path from 'node:path';
|
|
39
|
+
|
|
40
|
+
import { runTempDir } from '../config/temp-paths.js';
|
|
41
|
+
import { STATE_LABELS } from './ticketing/reads.js';
|
|
42
|
+
import { upsertStructuredComment } from './ticketing/state.js';
|
|
43
|
+
import { transitionTicketState } from './ticketing/transition.js';
|
|
44
|
+
|
|
45
|
+
/** Canonical basename for the per-run bookkeeping outbox. */
|
|
46
|
+
const OUTBOX_BASENAME = 'bookkeeping-outbox.ndjson';
|
|
47
|
+
|
|
48
|
+
/**
|
|
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.
|
|
54
|
+
*
|
|
55
|
+
* @param {number} epicId
|
|
56
|
+
* @param {object} [config] Resolved config (tempRoot).
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
export function outboxPathFor(epicId, config) {
|
|
60
|
+
return path.join(runTempDir(epicId, config), OUTBOX_BASENAME);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Append one operation record to the outbox, creating the parent dir on
|
|
65
|
+
* demand. Best-effort at the storage layer is the caller's concern; here we
|
|
66
|
+
* let a genuine fs failure propagate so a mis-configured outbox path surfaces
|
|
67
|
+
* loudly in tests.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} outboxPath
|
|
70
|
+
* @param {object} op
|
|
71
|
+
*/
|
|
72
|
+
function appendOp(outboxPath, op) {
|
|
73
|
+
mkdirSync(path.dirname(outboxPath), { recursive: true });
|
|
74
|
+
appendFileSync(outboxPath, `${JSON.stringify(op)}\n`, 'utf8');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Buffer a structured-comment upsert. The op captures everything
|
|
79
|
+
* `upsertStructuredComment` needs at drain time so the reconcile is a pure
|
|
80
|
+
* replay with no re-derivation.
|
|
81
|
+
*
|
|
82
|
+
* @param {{ outboxPath: string, ticketId: number, marker: string,
|
|
83
|
+
* body: string, attrs?: Record<string, string|number>|null,
|
|
84
|
+
* ts?: string }} args
|
|
85
|
+
*/
|
|
86
|
+
export function enqueueComment({
|
|
87
|
+
outboxPath,
|
|
88
|
+
ticketId,
|
|
89
|
+
marker,
|
|
90
|
+
body,
|
|
91
|
+
attrs = null,
|
|
92
|
+
ts = new Date().toISOString(),
|
|
93
|
+
}) {
|
|
94
|
+
appendOp(outboxPath, {
|
|
95
|
+
kind: 'comment',
|
|
96
|
+
ts,
|
|
97
|
+
ticketId,
|
|
98
|
+
marker,
|
|
99
|
+
body,
|
|
100
|
+
...(attrs ? { attrs } : {}),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Buffer an `agent::*` label transition.
|
|
106
|
+
*
|
|
107
|
+
* @param {{ outboxPath: string, ticketId: number, state: string,
|
|
108
|
+
* ts?: string }} args
|
|
109
|
+
*/
|
|
110
|
+
export function enqueueLabel({
|
|
111
|
+
outboxPath,
|
|
112
|
+
ticketId,
|
|
113
|
+
state,
|
|
114
|
+
ts = new Date().toISOString(),
|
|
115
|
+
}) {
|
|
116
|
+
appendOp(outboxPath, { kind: 'label', ts, ticketId, state });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parse the outbox into an ordered array of operation records. Malformed
|
|
121
|
+
* lines are skipped (never throw). A missing / empty outbox yields `[]`.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} outboxPath
|
|
124
|
+
* @returns {Array<object>}
|
|
125
|
+
*/
|
|
126
|
+
export function readOutbox(outboxPath) {
|
|
127
|
+
if (!outboxPath || !existsSync(outboxPath)) return [];
|
|
128
|
+
let raw;
|
|
129
|
+
try {
|
|
130
|
+
raw = readFileSync(outboxPath, 'utf8');
|
|
131
|
+
} catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
if (!raw) return [];
|
|
135
|
+
const ops = [];
|
|
136
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
137
|
+
if (!line) continue;
|
|
138
|
+
try {
|
|
139
|
+
const op = JSON.parse(line);
|
|
140
|
+
if (op && typeof op === 'object') ops.push(op);
|
|
141
|
+
} catch {
|
|
142
|
+
// skip a torn / malformed line — a partial write from a crash is not
|
|
143
|
+
// fatal to the rest of the batch.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return ops;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Drain the outbox to GitHub in FIFO order and clear it on success. Both sink
|
|
151
|
+
* operations are idempotent (comment upserts are marker-scoped;
|
|
152
|
+
* `transitionTicketState` applies one state via the canonical
|
|
153
|
+
* remove-all-then-add path), so a re-run after a partial drain converges. A
|
|
154
|
+
* per-op failure is recorded and the drain continues — a single bad ticket
|
|
155
|
+
* must not strand the rest of the batch. The outbox file is only truncated
|
|
156
|
+
* when EVERY op succeeded, so a crash mid-drain leaves the un-applied
|
|
157
|
+
* remainder for the next reconcile.
|
|
158
|
+
*
|
|
159
|
+
* @param {{ outboxPath: string,
|
|
160
|
+
* provider: import('../ITicketingProvider.js').ITicketingProvider,
|
|
161
|
+
* logger?: { warn?: (m: string) => void } }} args
|
|
162
|
+
* @returns {Promise<{ drained: number, comments: number, labels: number,
|
|
163
|
+
* errors: Array<{ op: object, error: string }>, cleared: boolean }>}
|
|
164
|
+
*/
|
|
165
|
+
export async function reconcileOutbox({ outboxPath, provider, logger }) {
|
|
166
|
+
const ops = readOutbox(outboxPath);
|
|
167
|
+
const result = {
|
|
168
|
+
drained: 0,
|
|
169
|
+
comments: 0,
|
|
170
|
+
labels: 0,
|
|
171
|
+
errors: [],
|
|
172
|
+
cleared: false,
|
|
173
|
+
};
|
|
174
|
+
if (ops.length === 0) {
|
|
175
|
+
result.cleared = true;
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const op of ops) {
|
|
180
|
+
try {
|
|
181
|
+
if (op.kind === 'comment') {
|
|
182
|
+
await upsertStructuredComment(
|
|
183
|
+
provider,
|
|
184
|
+
op.ticketId,
|
|
185
|
+
op.marker,
|
|
186
|
+
op.body,
|
|
187
|
+
op.attrs ?? null,
|
|
188
|
+
);
|
|
189
|
+
result.comments += 1;
|
|
190
|
+
result.drained += 1;
|
|
191
|
+
} else if (op.kind === 'label') {
|
|
192
|
+
await transitionTicketState(provider, op.ticketId, op.state);
|
|
193
|
+
result.labels += 1;
|
|
194
|
+
result.drained += 1;
|
|
195
|
+
} else {
|
|
196
|
+
result.errors.push({ op, error: `unknown op kind "${op.kind}"` });
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
200
|
+
result.errors.push({ op, error: message });
|
|
201
|
+
logger?.warn?.(
|
|
202
|
+
`[bookkeeping-outbox] reconcile op failed (${op.kind} #${op.ticketId}): ${message}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Only truncate when the whole batch landed — otherwise the un-applied
|
|
208
|
+
// ops must survive for the next reconcile (crash-recovery contract).
|
|
209
|
+
if (result.errors.length === 0) {
|
|
210
|
+
try {
|
|
211
|
+
writeFileSync(outboxPath, '', 'utf8');
|
|
212
|
+
result.cleared = true;
|
|
213
|
+
} catch {
|
|
214
|
+
// A clear failure is non-fatal: the ops all landed, and the idempotent
|
|
215
|
+
// sinks make a redundant re-drain a no-op.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Facade: post a structured comment live, OR buffer it to the outbox when the
|
|
223
|
+
* run is headless. Comments are never urgent (the operator reads them at
|
|
224
|
+
* finalize), so headless always buffers when an `outboxPath` is available.
|
|
225
|
+
*
|
|
226
|
+
* @param {{ provider: object, ticketId: number, marker: string, body: string,
|
|
227
|
+
* attrs?: Record<string, string|number>|null, headless?: boolean,
|
|
228
|
+
* outboxPath?: string|null }} args
|
|
229
|
+
* @returns {Promise<{ buffered: boolean }>}
|
|
230
|
+
*/
|
|
231
|
+
export async function postCommentOrBuffer({
|
|
232
|
+
provider,
|
|
233
|
+
ticketId,
|
|
234
|
+
marker,
|
|
235
|
+
body,
|
|
236
|
+
attrs = null,
|
|
237
|
+
headless = false,
|
|
238
|
+
outboxPath = null,
|
|
239
|
+
}) {
|
|
240
|
+
if (headless && outboxPath) {
|
|
241
|
+
enqueueComment({ outboxPath, ticketId, marker, body, attrs });
|
|
242
|
+
return { buffered: true };
|
|
243
|
+
}
|
|
244
|
+
await upsertStructuredComment(provider, ticketId, marker, body, attrs);
|
|
245
|
+
return { buffered: false };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Facade: flip an `agent::*` state live, OR buffer it when headless — EXCEPT
|
|
250
|
+
* `agent::blocked` (and any caller-marked `urgent` flip), which ALWAYS goes
|
|
251
|
+
* live so the HITL gate surfaces immediately (§1.J).
|
|
252
|
+
*
|
|
253
|
+
* @param {{ provider: object, ticketId: number, state: string,
|
|
254
|
+
* headless?: boolean, outboxPath?: string|null,
|
|
255
|
+
* urgent?: boolean }} args
|
|
256
|
+
* @returns {Promise<{ buffered: boolean }>}
|
|
257
|
+
*/
|
|
258
|
+
export async function transitionStateOrBuffer({
|
|
259
|
+
provider,
|
|
260
|
+
ticketId,
|
|
261
|
+
state,
|
|
262
|
+
headless = false,
|
|
263
|
+
outboxPath = null,
|
|
264
|
+
urgent = false,
|
|
265
|
+
}) {
|
|
266
|
+
const mustSurfaceNow = urgent || state === STATE_LABELS.BLOCKED;
|
|
267
|
+
if (headless && outboxPath && !mustSurfaceNow) {
|
|
268
|
+
enqueueLabel({ outboxPath, ticketId, state });
|
|
269
|
+
return { buffered: true };
|
|
270
|
+
}
|
|
271
|
+
await transitionTicketState(provider, ticketId, state);
|
|
272
|
+
return { buffered: false };
|
|
273
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/orchestration/ceremony-routing.js — ceremony-profile + risk-routed
|
|
3
|
+
* acceptance ceremony resolver.
|
|
4
|
+
*
|
|
5
|
+
* The sibling of `review-depth.js` (risk → review depth) and
|
|
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.
|
|
19
|
+
*
|
|
20
|
+
* ## The load-bearing invariant (M4-B acceptance floor — DO NOT VIOLATE)
|
|
21
|
+
*
|
|
22
|
+
* Risk-routing chooses fresh-vs-inline **PER CLUSTER**. It NEVER changes the
|
|
23
|
+
* cluster COUNT. The cluster count is `ceil(totalACs / clusterCeiling)` with
|
|
24
|
+
* the non-disableable `[1, 8]` clamp, owned entirely by
|
|
25
|
+
* `acceptance-clusters.js` and untouched here. A low-risk Story still gets one
|
|
26
|
+
* verdict per cluster — just possibly authored inline instead of by a fresh
|
|
27
|
+
* sub-agent. This module takes the cluster index as an INPUT and returns a
|
|
28
|
+
* decision for that one cluster; it has no way to add or remove clusters.
|
|
29
|
+
*
|
|
30
|
+
* ## Tier rules (per cluster, `standard` profile)
|
|
31
|
+
*
|
|
32
|
+
* - `high` risk → `fresh` (a fresh-context maker-blind spawn).
|
|
33
|
+
* - `medium` risk → `fresh` (fail toward more ceremony, never less —
|
|
34
|
+
* matches `review-depth`'s fail-to-middle).
|
|
35
|
+
* - `low` risk → `inline` (the contract-identical inline critic),
|
|
36
|
+
* UNLESS the maker-checker sampling floor
|
|
37
|
+
* selects this cluster → `fresh`.
|
|
38
|
+
* - missing / unknown → `fresh` (fail-safe: a Story that skipped `/plan`
|
|
39
|
+
* has no risk verdict; treat it as needing
|
|
40
|
+
* the full fresh-context ceremony, exactly
|
|
41
|
+
* as `review-depth` degrades to `standard`
|
|
42
|
+
* and `deriveRiskEnvelope` degrades to
|
|
43
|
+
* review-required).
|
|
44
|
+
*
|
|
45
|
+
* ## Maker-checker sampling floor
|
|
46
|
+
*
|
|
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.
|
|
54
|
+
*
|
|
55
|
+
* Pure and total: inputs in, decision out. No I/O, no throws. `null` /
|
|
56
|
+
* `undefined` / malformed inputs degrade to `fresh` + `full` ceremony.
|
|
57
|
+
*
|
|
58
|
+
* @typedef {'fresh'|'inline'} CeremonyMode
|
|
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}
|
|
76
|
+
*/
|
|
77
|
+
export function normalizeCeremonyProfile(value) {
|
|
78
|
+
if (value === 'minimal' || value === 'standard' || value === 'strict') {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
return 'standard';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Decide whether the sampling floor forces this low-risk cluster fresh.
|
|
86
|
+
*
|
|
87
|
+
* Deterministic in the cluster index: with rate `r` (0 < r ≤ 1) the stride is
|
|
88
|
+
* `round(1 / r)` and every `stride`-th cluster (0-based indices 0, stride,
|
|
89
|
+
* 2·stride, …) is forced fresh, yielding ≈`r` of clusters fresh. `r <= 0`
|
|
90
|
+
* disables the floor (no cluster forced); `r >= 1` forces every cluster.
|
|
91
|
+
*
|
|
92
|
+
* @param {number} clusterIndex Zero-based cluster position (from the fixed
|
|
93
|
+
* `ceil(totalACs / clusterCeiling)` fan-out — an INPUT, never mutated here).
|
|
94
|
+
* @param {number} rate Sampling rate, already clamped into [0, 1] by
|
|
95
|
+
* `getDeliveryRouting`.
|
|
96
|
+
* @returns {boolean} `true` when the floor forces this cluster fresh.
|
|
97
|
+
*/
|
|
98
|
+
export function sampledFresh(clusterIndex, rate) {
|
|
99
|
+
if (typeof rate !== 'number' || !Number.isFinite(rate) || rate <= 0) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (rate >= 1) return true;
|
|
103
|
+
const idx =
|
|
104
|
+
typeof clusterIndex === 'number' &&
|
|
105
|
+
Number.isInteger(clusterIndex) &&
|
|
106
|
+
clusterIndex >= 0
|
|
107
|
+
? clusterIndex
|
|
108
|
+
: 0;
|
|
109
|
+
const stride = Math.max(1, Math.round(1 / rate));
|
|
110
|
+
return idx % stride === 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
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.
|
|
117
|
+
*
|
|
118
|
+
* @param {{
|
|
119
|
+
* overallLevel?: (RiskLevel|string|null|undefined),
|
|
120
|
+
* clusterIndex?: (number|null|undefined),
|
|
121
|
+
* freshCriticSampleRate?: (number|null|undefined),
|
|
122
|
+
* ceremonyProfile?: (CeremonyProfile|string|null|undefined),
|
|
123
|
+
* }} [input]
|
|
124
|
+
* @returns {{
|
|
125
|
+
* mode: CeremonyMode,
|
|
126
|
+
* reason: string,
|
|
127
|
+
* sampled: boolean,
|
|
128
|
+
* profile: CeremonyProfile,
|
|
129
|
+
* }}
|
|
130
|
+
*/
|
|
131
|
+
export function resolveCeremonyForRisk(input = {}) {
|
|
132
|
+
const overallLevel =
|
|
133
|
+
input && typeof input === 'object' ? input.overallLevel : undefined;
|
|
134
|
+
const clusterIndex =
|
|
135
|
+
input && typeof input === 'object' ? input.clusterIndex : undefined;
|
|
136
|
+
const rate =
|
|
137
|
+
input && typeof input === 'object'
|
|
138
|
+
? input.freshCriticSampleRate
|
|
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
|
+
}
|
|
160
|
+
|
|
161
|
+
if (overallLevel === 'high') {
|
|
162
|
+
return {
|
|
163
|
+
mode: 'fresh',
|
|
164
|
+
reason: 'high-risk: fresh-context critic',
|
|
165
|
+
sampled: false,
|
|
166
|
+
profile,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (overallLevel === 'medium') {
|
|
170
|
+
return {
|
|
171
|
+
mode: 'fresh',
|
|
172
|
+
reason: 'medium-risk: fresh-context critic (fail toward more ceremony)',
|
|
173
|
+
sampled: false,
|
|
174
|
+
profile,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (overallLevel === 'low') {
|
|
178
|
+
if (sampledFresh(clusterIndex, rate)) {
|
|
179
|
+
return {
|
|
180
|
+
mode: 'fresh',
|
|
181
|
+
reason:
|
|
182
|
+
'low-risk cluster forced fresh by the maker-checker sampling floor',
|
|
183
|
+
sampled: true,
|
|
184
|
+
profile,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
mode: 'inline',
|
|
189
|
+
reason: 'low-risk: contract-identical inline critic',
|
|
190
|
+
sampled: false,
|
|
191
|
+
profile,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// Missing / unknown / malformed risk → fail-safe fresh + full ceremony,
|
|
195
|
+
// matching how review-depth.js and deriveRiskEnvelope degrade on an
|
|
196
|
+
// unjudged Story.
|
|
197
|
+
return {
|
|
198
|
+
mode: 'fresh',
|
|
199
|
+
reason:
|
|
200
|
+
'risk absent/unknown: fail-safe fresh-context critic + full ceremony',
|
|
201
|
+
sampled: false,
|
|
202
|
+
profile,
|
|
203
|
+
};
|
|
204
|
+
}
|