mandrel 1.94.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +328 -28
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +3 -53
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -8
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- package/.agents/workflows/helpers/single-story-deliver.md +0 -557
|
@@ -1,57 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Story sizing model —
|
|
3
|
-
* declaration that lifts the hard file-width rejection. Co-located with
|
|
4
|
-
* `ticket-validator.js`, but kept as its own module so the validator's
|
|
5
|
-
* primary file stays under the maintainability ceiling. The validator
|
|
6
|
-
* imports `computeSizingFindings` and `renderHardFindingError` and stitches
|
|
7
|
-
* the result onto its return value as `findings` / `errors`.
|
|
2
|
+
* Story sizing model — v2 model-capacity split advisory.
|
|
8
3
|
*
|
|
9
|
-
* `DEFAULT_TASK_SIZING`
|
|
4
|
+
* Per-Story file/AC ceilings (`DEFAULT_TASK_SIZING.softFiles` /
|
|
5
|
+
* `hardFiles` / `softAcceptanceCount`) are gone. A Story is sized by
|
|
6
|
+
* **cohesion first**; the numeric backstop only asks whether the authored
|
|
7
|
+
* Story ticket itself is pathologically verbose. See `docs/roadmap.md`
|
|
8
|
+
* § v2.0.0 design decision 2.
|
|
9
|
+
*
|
|
10
|
+
* Co-located with `ticket-validator.js`, but kept as its own module so the
|
|
11
|
+
* validator's primary file stays under the maintainability ceiling. The
|
|
12
|
+
* validator imports `computeSizingFindings` and `renderHardFindingError` and
|
|
13
|
+
* stitches the result onto its return value as `findings` / `errors`.
|
|
14
|
+
*
|
|
15
|
+
* `DEFAULT_MODEL_CAPACITY` is the **single source of truth** for the capacity
|
|
10
16
|
* thresholds. The decomposer prompt template
|
|
11
|
-
* (`.agents/scripts/lib/templates/decomposer-prompts.js`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* its threshold sentence from this constant so the two surfaces cannot drift.
|
|
15
|
-
* Keep it in lockstep with `.agents/schemas/agentrc.schema.json`
|
|
16
|
-
* (`$defs.taskSizing`) and the JS mirror in
|
|
17
|
-
* `.agents/scripts/lib/config-settings-schema.js`.
|
|
17
|
+
* (`.agents/scripts/lib/templates/decomposer-prompts.js`) generates its
|
|
18
|
+
* threshold sentence from this constant rather than restating divergent
|
|
19
|
+
* numbers, so the two surfaces cannot drift.
|
|
18
20
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* -
|
|
23
|
-
* `
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `wide` declaration carrying a one-line human-readable reason. Declaring
|
|
28
|
-
* `wide` with a reason lifts the `hardFiles` rejection; no Story is
|
|
29
|
-
* rejected for width when `wide` is declared.
|
|
30
|
-
* - Cohesion is the primary heuristic (one Story = one coherent change with
|
|
31
|
-
* one reason to exist); the numeric ceilings are a backstop. See the
|
|
32
|
-
* decomposer prompt and authoring SKILL.
|
|
21
|
+
* Capacity model:
|
|
22
|
+
* - Plan-time **session mass** = authored tokens only (`estimateTokens` of
|
|
23
|
+
* the Story's goal/reason/acceptance/verify/slicing/spec/change paths).
|
|
24
|
+
* - Soft / hard ceilings are **absolute** authored-token counts (no
|
|
25
|
+
* `maxTokenBudget` denominator — that envelope was retired).
|
|
26
|
+
* - The optional `wide` declaration lifts the hard session-mass rejection.
|
|
27
|
+
* - Cohesion remains the primary heuristic; capacity is a pathological-
|
|
28
|
+
* verbosity backstop only (not an operator-tunable agentrc knob).
|
|
33
29
|
*/
|
|
34
30
|
|
|
35
31
|
import { parse as parseStoryBody } from '../story-body/story-body.js';
|
|
32
|
+
import { estimateTokens } from './context-envelope.js';
|
|
36
33
|
|
|
37
34
|
/**
|
|
38
35
|
* Normalize a Story's `body` to the structured object the sizing layers
|
|
39
36
|
* score, mirroring `validateAcFreshness` / `collectStoryAssumptionEntries`
|
|
40
37
|
* (Story #3302) and `resolveStructuredBody` in `task-body-validator.js`.
|
|
41
38
|
*
|
|
42
|
-
* The decomposer emits `body` as the canonical serialized **string**
|
|
43
|
-
* (`decomposer-prompts.js`), but the sizing layers historically read
|
|
44
|
-
* `story.body` only when it was already an object — so on the production
|
|
45
|
-
* string shape `changes` / `wide` fell through to empty and the `hardFiles`
|
|
46
|
-
* / unanchored-constant backstops emitted nothing. A defensive parse here
|
|
47
|
-
* restores parity:
|
|
48
|
-
* - **string body** → parsed via `parseStoryBody`; an unparseable string
|
|
49
|
-
* yields `null` (the gate degrades to "no structured signal", never
|
|
50
|
-
* throws mid-validation).
|
|
51
|
-
* - **object body** → returned verbatim (a caller may pass the
|
|
52
|
-
* pre-serialize shape directly; `parse` round-trips it).
|
|
53
|
-
* - **null / other** → `null`.
|
|
54
|
-
*
|
|
55
39
|
* @param {object} story
|
|
56
40
|
* @returns {object|null}
|
|
57
41
|
*/
|
|
@@ -71,13 +55,8 @@ function resolveStoryBody(story) {
|
|
|
71
55
|
|
|
72
56
|
/**
|
|
73
57
|
* Resolve the acceptance-criteria array for a Story, preferring the
|
|
74
|
-
* authoritative top-level `story.acceptance`
|
|
75
|
-
*
|
|
76
|
-
* structured body's `acceptance`. Reading the top-level array makes the
|
|
77
|
-
* acceptance ceiling correct regardless of body shape — a string body whose
|
|
78
|
-
* structured `acceptance` is only reachable after a parse, or an object body
|
|
79
|
-
* (Story #4271). Falls back to `resolveStoryBody(story).acceptance` only when
|
|
80
|
-
* the top-level array is absent.
|
|
58
|
+
* authoritative top-level `story.acceptance` over the structured body's
|
|
59
|
+
* `acceptance`.
|
|
81
60
|
*
|
|
82
61
|
* @param {object} story
|
|
83
62
|
* @returns {unknown[]}
|
|
@@ -89,17 +68,20 @@ function resolveAcceptance(story) {
|
|
|
89
68
|
}
|
|
90
69
|
|
|
91
70
|
/**
|
|
92
|
-
* Resolve the
|
|
93
|
-
*
|
|
94
|
-
* inside the structured body (the shape `story-body.js` parses out). Prefer the
|
|
95
|
-
* top-level array, falling back to the resolved body's `depends_on`. Returns
|
|
96
|
-
* only non-empty string slugs — mirroring the `story-body.js` normalization so
|
|
97
|
-
* a blank / non-string edge never counts as a real dependency.
|
|
71
|
+
* Resolve the verify-commands array for a Story, preferring the top-level
|
|
72
|
+
* `story.verify` over the structured body's `verify`.
|
|
98
73
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
74
|
+
* @param {object} story
|
|
75
|
+
* @returns {unknown[]}
|
|
76
|
+
*/
|
|
77
|
+
function resolveVerify(story) {
|
|
78
|
+
if (Array.isArray(story?.verify)) return story.verify;
|
|
79
|
+
const body = resolveStoryBody(story);
|
|
80
|
+
return Array.isArray(body?.verify) ? body.verify : [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the `depends_on` edge list for a Story.
|
|
103
85
|
*
|
|
104
86
|
* @param {object} story
|
|
105
87
|
* @returns {string[]}
|
|
@@ -113,127 +95,72 @@ function resolveDependsOn(story) {
|
|
|
113
95
|
: [];
|
|
114
96
|
}
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// merge rule. Emitted as a `soft` advisory only; never a rejection. A tiny
|
|
135
|
-
// ORPHAN Story (no `depends_on`) stays silent — small orthogonal slices are
|
|
136
|
-
// legitimate.
|
|
137
|
-
mergeCandidateMaxFiles: 3,
|
|
138
|
-
mergeCandidateMaxAcceptance: 4,
|
|
98
|
+
/**
|
|
99
|
+
* Framework capacity constant — not operator-tunable via `.agentrc.json`
|
|
100
|
+
* (same collapse pattern as `maxTickets` / Story #4163).
|
|
101
|
+
*
|
|
102
|
+
* Absolute authored-token ceilings (no delivery-envelope denominator):
|
|
103
|
+
*
|
|
104
|
+
* - Soft 30_000: cohesion check when the ticket itself is already a
|
|
105
|
+
* substantial document. Normal capability Stories stay silent.
|
|
106
|
+
* - Hard 75_000: reject Spec novels unless `wide`.
|
|
107
|
+
* - Merge candidate 1_500: thin `depends_on` fragment (soft only).
|
|
108
|
+
*
|
|
109
|
+
* Cohesion / split policy / conflict advisories remain the primary sizing
|
|
110
|
+
* signal; these ceilings catch pathological ticket verbosity only.
|
|
111
|
+
*/
|
|
112
|
+
export const DEFAULT_MODEL_CAPACITY = Object.freeze({
|
|
113
|
+
softSessionTokens: 30000,
|
|
114
|
+
hardSessionTokens: 75000,
|
|
115
|
+
mergeCandidateMaxSessionTokens: 1500,
|
|
139
116
|
});
|
|
140
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Resolve capacity ceilings, shallow-merging an optional override bag
|
|
120
|
+
* (tests / programmatic only). Pure; used by the validator and the
|
|
121
|
+
* decomposer prompt.
|
|
122
|
+
*
|
|
123
|
+
* @param {object} [capacity]
|
|
124
|
+
* @returns {{ softSessionTokens: number, hardSessionTokens: number, mergeCandidateMaxSessionTokens: number }}
|
|
125
|
+
*/
|
|
126
|
+
export function resolveCapacityCeilings(capacity = DEFAULT_MODEL_CAPACITY) {
|
|
127
|
+
const merged = { ...DEFAULT_MODEL_CAPACITY, ...(capacity ?? {}) };
|
|
128
|
+
return {
|
|
129
|
+
softSessionTokens: merged.softSessionTokens,
|
|
130
|
+
hardSessionTokens: merged.hardSessionTokens,
|
|
131
|
+
mergeCandidateMaxSessionTokens: merged.mergeCandidateMaxSessionTokens,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
141
135
|
/**
|
|
142
136
|
* `DELIVERABLE_GRANULARITY_GUIDANCE` is the **single source of truth** for the
|
|
143
137
|
* deliverable-granularity definition of a Story (Story #3777). It is stated
|
|
144
|
-
* ONCE here and consumed by BOTH the decomposer prompt template
|
|
145
|
-
*
|
|
146
|
-
* the string verbatim) AND the authoring SKILL
|
|
147
|
-
* (`.agents/skills/core/epic-plan-decompose-author/SKILL.md`, whose prose
|
|
148
|
-
* mirrors these sentences). The SKILL cannot import JS, so the
|
|
149
|
-
* `epic-plan-decompose-author` smoke test and the `ticket-decomposer` prompt
|
|
150
|
-
* test both assert the canonical phrasing on each surface — a divergent
|
|
151
|
-
* restatement fails those gates. This reuses the #3760 single-source
|
|
152
|
-
* mechanism (one constant, two surfaces, drift-gated by tests).
|
|
153
|
-
*
|
|
154
|
-
* The definition: a Story is a **capability slice a frontier model delivers
|
|
155
|
-
* and self-verifies in one pass** — a shippable slice a reviewer would accept
|
|
156
|
-
* as a single PR — not a single module or file. Module-level slices fold into
|
|
157
|
-
* the capability they belong to, and a Story whose only consumer is one
|
|
158
|
-
* sibling Story is merged into that sibling (single-consumer merge rule).
|
|
159
|
-
*
|
|
160
|
-
* The `envelopeFloor` sentence is the **soft** complement to the sizing
|
|
161
|
-
* section's envelope framing (Story #4313): the delivery envelope
|
|
162
|
-
* (`maxTokenBudget`) bounds the TOP of a Story, but under-utilizing it is
|
|
163
|
-
* itself a merge signal. It is deliberately prose-only — an illustrative
|
|
164
|
-
* fraction, not a threshold constant, and no validator finding backs it (the
|
|
165
|
-
* mechanical backstop is the `merge-candidate` finding). It names the
|
|
166
|
-
* per-Story delivery-session cost (hydration, branch, PR, review, CI) so the
|
|
167
|
-
* "models one-shot bigger things now" planning assumption is stated, not
|
|
168
|
-
* implicit.
|
|
138
|
+
* ONCE here and consumed by BOTH the decomposer prompt template and the
|
|
139
|
+
* authoring SKILL.
|
|
169
140
|
*/
|
|
170
141
|
export const DELIVERABLE_GRANULARITY_GUIDANCE = Object.freeze({
|
|
171
|
-
// The one-sentence definition of Story granularity.
|
|
172
142
|
definition:
|
|
173
143
|
'A Story is a **capability slice a frontier model delivers and self-verifies in one pass** — a shippable slice a reviewer would accept as a single PR, a capability or user-visible surface, **not a single module or file**. Fold module-level slices into the capability they belong to rather than emitting one Story per module.',
|
|
174
|
-
// The single-consumer merge rule.
|
|
175
144
|
singleConsumerRule:
|
|
176
145
|
'**Single-consumer merge rule.** A Story whose only consumer is one sibling Story should be **merged into that sibling** rather than emitted separately — a single-consumer downstream slice is not its own unit of work.',
|
|
177
|
-
// The envelope-floor heuristic (soft; no validator finding — Story #4313).
|
|
178
146
|
envelopeFloor:
|
|
179
|
-
'**
|
|
147
|
+
'**Thin dependent slices are a merge signal.** A Story that is neither parallel-deliverable nor orthogonal to its siblings — especially a short `depends_on` fragment whose only job is to feed one consumer — should be **merged into its consumer**. Modern frontier models one-shot capability-sized changes, so a chain of small dependent Stories needlessly pays a full delivery session (branch, PR, review, CI) per link. Merge such links up unless a parallelism or orthogonality reason justifies the separate slice.',
|
|
180
148
|
});
|
|
181
149
|
|
|
182
150
|
/**
|
|
183
151
|
* `AUTHORING_ALTITUDE_GUIDANCE` is the **single source of truth** for the
|
|
184
152
|
* binding-vs-advisory authoring altitude (Epic #4131 F8) and the New-File
|
|
185
|
-
* Contract (Story #4272).
|
|
186
|
-
* decomposer prompt template
|
|
187
|
-
* (`.agents/scripts/lib/templates/decomposer-prompts.js`, which interpolates
|
|
188
|
-
* the strings verbatim into the rendered system prompt) AND the authoring
|
|
189
|
-
* SKILL (`.agents/skills/core/epic-plan-decompose-author/SKILL.md`, whose
|
|
190
|
-
* prose mirrors these sentences). The SKILL cannot import JS, so the
|
|
191
|
-
* `ticket-decomposer` prompt test asserts the canonical phrasing on both
|
|
192
|
-
* surfaces — a divergent restatement fails that gate. This reuses the #3777
|
|
193
|
-
* single-source mechanism (one constant, two surfaces, drift-gated by tests).
|
|
194
|
-
*
|
|
195
|
-
* The altitude: `acceptance[]` / `verify[]` are the **binding contract** (the
|
|
196
|
-
* sole definition of "done"); `changes[]` / `references[]` are an **advisory
|
|
197
|
-
* implementation sketch** the executor MAY revise. Author acceptance to assert
|
|
198
|
-
* the **outcome** independent of file layout — never pin an incidental helper
|
|
199
|
-
* name or private path into an acceptance item. The advisory sketch is still
|
|
200
|
-
* validated (base-branch probes, New-File Contract) and never licenses
|
|
201
|
-
* skipping `acceptance[]` / `verify[]` or any `rules/security-baseline.md` MUST.
|
|
153
|
+
* Contract (Story #4272).
|
|
202
154
|
*/
|
|
203
155
|
export const AUTHORING_ALTITUDE_GUIDANCE = Object.freeze({
|
|
204
|
-
// The binding-vs-advisory altitude statement.
|
|
205
156
|
altitude:
|
|
206
157
|
'**Binding contract vs advisory sketch.** `acceptance[]` and `verify[]` are the Story\'s **binding contract** — the executor MUST satisfy them exactly, and they are the only definition of "done." `changes[]` and `references[]` are an **advisory implementation sketch**: your best prediction of the file footprint, which the executor MAY revise when the real codebase diverges from the sketch. Author `acceptance[]` / `verify[]` to assert the **outcome** independent of any one file layout — never pin an incidental implementation detail (an internal helper name, a private file path) into an acceptance item that the advisory `changes[]` is free to reshape; assert the observable behaviour instead.',
|
|
207
|
-
// The advisory-does-not-mean-unvalidated caveat.
|
|
208
158
|
advisoryCaveat:
|
|
209
159
|
"**Advisory does not mean unvalidated.** `changes[]` paths still pass the base-branch file-assumption probes (a `creates` against an existing path still fails), the New-File Contract still holds, and the executor's latitude to revise the approach never licenses skipping `acceptance[]` / `verify[]` or relaxing any `rules/security-baseline.md` MUST.",
|
|
210
|
-
// The New-File Contract.
|
|
211
160
|
newFileContract:
|
|
212
161
|
'**New-File Contract.** Any path named in a Story\'s `goal`, `acceptance`, or `verify` that does NOT already exist on `main` MUST also appear in that Story\'s `changes[]` with `assumption: "creates"`; otherwise the freshness validator rejects the decompose — even when the Story is the one authoring the file.',
|
|
213
162
|
});
|
|
214
163
|
|
|
215
|
-
/**
|
|
216
|
-
* Configuration-constant phrase patterns the `unanchored-constant` heuristic
|
|
217
|
-
* scans Story acceptance criteria for. Each entry matches the *kind* of
|
|
218
|
-
* tunable an implementing agent would otherwise have to context-hop to the
|
|
219
|
-
* Tech Spec (or an ADR) to resolve: a retention window, a rate limit, a
|
|
220
|
-
* timeout, an age cutoff, a bounded window, a per-unit cap, or a quota.
|
|
221
|
-
*
|
|
222
|
-
* The list is the single source of truth for the heuristic and intentionally
|
|
223
|
-
* narrow — it targets the small set of config constants that recur across
|
|
224
|
-
* Stories and that an author can almost always inline as a concrete value at
|
|
225
|
-
* authoring time. Anchored with `\b` word boundaries so substrings inside
|
|
226
|
-
* unrelated prose ("maximum", "timeouts handled elsewhere") don't over-match.
|
|
227
|
-
*
|
|
228
|
-
* Covered phrases (acceptance criterion of Story #3855):
|
|
229
|
-
* - `retention window`
|
|
230
|
-
* - `rate limit` / `rate-limit`
|
|
231
|
-
* - `timeout`
|
|
232
|
-
* - `older than`
|
|
233
|
-
* - `within N` (a bounded count/duration window)
|
|
234
|
-
* - `max ... per ...` (a per-unit cap, e.g. "max requests per second")
|
|
235
|
-
* - `quota`
|
|
236
|
-
*/
|
|
237
164
|
const UNANCHORED_CONSTANT_PATTERNS = Object.freeze([
|
|
238
165
|
/\bretention window\b/i,
|
|
239
166
|
/\brate[ -]limit\b/i,
|
|
@@ -244,25 +171,8 @@ const UNANCHORED_CONSTANT_PATTERNS = Object.freeze([
|
|
|
244
171
|
/\bquota\b/i,
|
|
245
172
|
]);
|
|
246
173
|
|
|
247
|
-
/**
|
|
248
|
-
* Matches a concrete numeric value anywhere in an acceptance criterion. A
|
|
249
|
-
* bare digit run is enough to count as "anchored": `"older than 90 days"`,
|
|
250
|
-
* `"5 req/s"`, `"within 30 minutes"`, and `"max 100 per hour"` all carry a
|
|
251
|
-
* digit, so they are not flagged. Spelled-out small numbers are intentionally
|
|
252
|
-
* NOT treated as anchors — the heuristic nudges authors toward an explicit
|
|
253
|
-
* numeral the implementing agent can copy verbatim.
|
|
254
|
-
*/
|
|
255
174
|
const CONCRETE_VALUE_RE = /\d/;
|
|
256
175
|
|
|
257
|
-
/**
|
|
258
|
-
* Soft, advisory `unanchored-constant` finding (Story #3855). Surfaces a
|
|
259
|
-
* Story acceptance criterion that references a configuration constant
|
|
260
|
-
* (retention window, rate limit, timeout, threshold) without stating a
|
|
261
|
-
* concrete numeric value inline — so an implementing agent doesn't have to
|
|
262
|
-
* context-hop to the Tech Spec or guess. Advisory only: `normalizeTickets`
|
|
263
|
-
* still returns successfully and the finding rides the advisory nudges array
|
|
264
|
-
* alongside the sizing soft findings.
|
|
265
|
-
*/
|
|
266
176
|
function makeUnanchoredConstant(slug, criterion) {
|
|
267
177
|
return {
|
|
268
178
|
kind: 'unanchored-constant',
|
|
@@ -273,21 +183,8 @@ function makeUnanchoredConstant(slug, criterion) {
|
|
|
273
183
|
};
|
|
274
184
|
}
|
|
275
185
|
|
|
276
|
-
/**
|
|
277
|
-
* Scan a single Story's acceptance criteria for unanchored configuration
|
|
278
|
-
* constants. A criterion trips the finding when it matches one of
|
|
279
|
-
* `UNANCHORED_CONSTANT_PATTERNS` and carries NO concrete numeric value
|
|
280
|
-
* (`CONCRETE_VALUE_RE`) — the digit is the signal the author already inlined
|
|
281
|
-
* the value, so a criterion like `"older than 90 days"` is left alone while
|
|
282
|
-
* `"older than the retention window"` is flagged. One finding per offending
|
|
283
|
-
* criterion.
|
|
284
|
-
*/
|
|
285
186
|
function computeUnanchoredConstantFindings(story) {
|
|
286
187
|
const out = [];
|
|
287
|
-
// Read the authoritative top-level `story.acceptance` (the binding
|
|
288
|
-
// contract), falling back to the structured body's acceptance only when the
|
|
289
|
-
// top-level array is absent. This is correct regardless of body shape —
|
|
290
|
-
// string or object (Story #4271).
|
|
291
188
|
const acceptance = resolveAcceptance(story);
|
|
292
189
|
for (const item of acceptance) {
|
|
293
190
|
const criterion = String(item ?? '');
|
|
@@ -302,19 +199,6 @@ function computeUnanchoredConstantFindings(story) {
|
|
|
302
199
|
return out;
|
|
303
200
|
}
|
|
304
201
|
|
|
305
|
-
/**
|
|
306
|
-
* Soft, advisory `missing-reason-to-exist` finding (Story #4273). Surfaces a
|
|
307
|
-
* Story whose body carries no non-empty `reason_to_exist` — the
|
|
308
|
-
* machine-checkable form of the cohesion rule (**one Story = one coherent
|
|
309
|
-
* change with one reason to exist**). `reason_to_exist` is marked REQUIRED by
|
|
310
|
-
* the decomposer prompt and is the field the `epic-plan-consolidate` critic
|
|
311
|
-
* gates on, but that critic is an honor-system LLM check with no runtime
|
|
312
|
-
* backstop. This deterministic finding is the cheap backstop.
|
|
313
|
-
*
|
|
314
|
-
* Severity is `soft` (not a hard reject) so existing `reason_to_exist`-less
|
|
315
|
-
* standalone / audit Stories are surfaced as an advisory nudge rather than
|
|
316
|
-
* blocked — matching the `unanchored-constant` finding's advisory contract.
|
|
317
|
-
*/
|
|
318
202
|
function makeMissingReasonToExist(slug) {
|
|
319
203
|
return {
|
|
320
204
|
kind: 'missing-reason-to-exist',
|
|
@@ -325,17 +209,6 @@ function makeMissingReasonToExist(slug) {
|
|
|
325
209
|
};
|
|
326
210
|
}
|
|
327
211
|
|
|
328
|
-
/**
|
|
329
|
-
* Emit a soft `missing-reason-to-exist` finding when the Story body resolves
|
|
330
|
-
* to no non-empty `reason_to_exist`. The body parser
|
|
331
|
-
* (`story-body/story-body.js`) already normalizes `reason_to_exist` to a
|
|
332
|
-
* non-empty trimmed string or `null`, so reading `body.reason_to_exist` after
|
|
333
|
-
* `resolveStoryBody` is correct regardless of body shape — a serialized
|
|
334
|
-
* **string** body (the production decomposer shape) or an object body
|
|
335
|
-
* (Story #4271). A body that fails to parse resolves to `null` and trips the
|
|
336
|
-
* finding, which is the right advisory signal: the author should re-emit a
|
|
337
|
-
* parseable body carrying the field. One finding per Story.
|
|
338
|
-
*/
|
|
339
212
|
function computeMissingReasonToExistFinding(story) {
|
|
340
213
|
const body = resolveStoryBody(story);
|
|
341
214
|
const reason = body?.reason_to_exist;
|
|
@@ -343,82 +216,31 @@ function computeMissingReasonToExistFinding(story) {
|
|
|
343
216
|
return hasReason ? [] : [makeMissingReasonToExist(story.slug)];
|
|
344
217
|
}
|
|
345
218
|
|
|
346
|
-
|
|
347
|
-
* Soft, advisory `merge-candidate` finding (Story #4312). Surfaces a Story that
|
|
348
|
-
* looks like a dependent fragment rather than a capability slice — the
|
|
349
|
-
* machine-checkable form of the single-consumer merge rule
|
|
350
|
-
* (`DELIVERABLE_GRANULARITY_GUIDANCE.singleConsumerRule`). Never a rejection:
|
|
351
|
-
* `severity: 'soft'`, so it rides the advisory `findings[]` channel and never
|
|
352
|
-
* enters the hard `errors[]` array.
|
|
353
|
-
*
|
|
354
|
-
* The rendered message names the depended-on sibling slug(s) and recommends
|
|
355
|
-
* merging into the consumer, mirroring the tone of the `wide-undeclared` nudge.
|
|
356
|
-
*/
|
|
357
|
-
function makeMergeCandidate(slug, fileCount, acceptanceCount, dependsOn) {
|
|
219
|
+
function makeMergeCandidate(slug, sessionMass, dependsOn) {
|
|
358
220
|
const siblings = dependsOn.map((d) => `"${d}"`).join(', ');
|
|
359
221
|
return {
|
|
360
222
|
kind: 'merge-candidate',
|
|
361
223
|
severity: 'soft',
|
|
362
224
|
ticketSlug: slug,
|
|
363
|
-
|
|
364
|
-
acceptanceCount,
|
|
225
|
+
sessionMass,
|
|
365
226
|
dependsOn,
|
|
366
|
-
message: `Story "${slug}" is a thin dependent slice (
|
|
227
|
+
message: `Story "${slug}" is a thin dependent slice (estimated session mass ${sessionMass} tokens) that depends on sibling(s) ${siblings}. A Story whose only role is to feed one sibling is not its own unit of work — consider merging it into the consumer (single-consumer merge rule) rather than shipping it as a separate slice.`,
|
|
367
228
|
};
|
|
368
229
|
}
|
|
369
230
|
|
|
370
231
|
/**
|
|
371
|
-
*
|
|
372
|
-
* heuristic (Story #4312): footprint ≤ `mergeCandidateMaxFiles` declared
|
|
373
|
-
* `changes[]` files AND ≤ `mergeCandidateMaxAcceptance` acceptance items AND at
|
|
374
|
-
* least one `depends_on` edge to a sibling. All three conditions MUST hold — a
|
|
375
|
-
* tiny ORPHAN Story (no `depends_on`) stays silent because small orthogonal
|
|
376
|
-
* slices are legitimate. Glob entries mark the footprint as unknown-width, so a
|
|
377
|
-
* glob-carrying Story is never a merge candidate.
|
|
378
|
-
*
|
|
379
|
-
* @param {object} story
|
|
380
|
-
* @param {{ fileCount: number, hasGlobs: boolean }} changesAnalysis
|
|
381
|
-
* @param {number} acceptanceCount
|
|
382
|
-
* @param {object} sizing
|
|
383
|
-
* @returns {object[]}
|
|
384
|
-
*/
|
|
385
|
-
function computeMergeCandidateFinding(
|
|
386
|
-
story,
|
|
387
|
-
changesAnalysis,
|
|
388
|
-
acceptanceCount,
|
|
389
|
-
sizing,
|
|
390
|
-
) {
|
|
391
|
-
if (changesAnalysis.hasGlobs) return [];
|
|
392
|
-
const dependsOn = resolveDependsOn(story);
|
|
393
|
-
if (dependsOn.length === 0) return [];
|
|
394
|
-
if (changesAnalysis.fileCount > sizing.mergeCandidateMaxFiles) return [];
|
|
395
|
-
if (acceptanceCount > sizing.mergeCandidateMaxAcceptance) return [];
|
|
396
|
-
return [
|
|
397
|
-
makeMergeCandidate(
|
|
398
|
-
story.slug,
|
|
399
|
-
changesAnalysis.fileCount,
|
|
400
|
-
acceptanceCount,
|
|
401
|
-
dependsOn,
|
|
402
|
-
),
|
|
403
|
-
];
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Returns true when a `changes[]` entry is a glob pattern. Handles both the
|
|
408
|
-
* canonical PathEntry object form `{ path, assumption }` and legacy strings.
|
|
232
|
+
* Returns true when a `changes[]` entry is a glob pattern.
|
|
409
233
|
*/
|
|
410
234
|
function isGlobBullet(bullet) {
|
|
411
|
-
const s = bullet?.path
|
|
235
|
+
const s = bullet?.path;
|
|
412
236
|
return typeof s === 'string' && s.includes('*');
|
|
413
237
|
}
|
|
414
238
|
|
|
415
239
|
/**
|
|
416
|
-
* Extract the path
|
|
417
|
-
* canonical PathEntry object form (path used directly) and the legacy
|
|
418
|
-
* `"<path>: <verb>"` string form (slices on the first colon).
|
|
240
|
+
* Extract the path from a single object-form `changes` entry.
|
|
419
241
|
*/
|
|
420
242
|
function extractChangeBulletPath(bullet) {
|
|
421
|
-
const s =
|
|
243
|
+
const s = bullet?.path ?? null;
|
|
422
244
|
if (!s) return null;
|
|
423
245
|
const colonIdx = s.indexOf(':');
|
|
424
246
|
if (colonIdx <= 0) return /[\\/.]/.test(s) ? s.trim() : null;
|
|
@@ -427,44 +249,91 @@ function extractChangeBulletPath(bullet) {
|
|
|
427
249
|
}
|
|
428
250
|
|
|
429
251
|
/**
|
|
430
|
-
* Analyse the `changes[]` array and return
|
|
431
|
-
* - `fileCount` — number of unique non-glob path-shaped heads
|
|
432
|
-
* - `hasGlobs` — true when at least one bullet is a glob pattern
|
|
433
|
-
*
|
|
434
|
-
* Handles both the canonical PathEntry object form `{ path, assumption }` and
|
|
435
|
-
* the legacy string form via the underlying helpers.
|
|
252
|
+
* Analyse the `changes[]` array and return fileCount / hasGlobs / path texts.
|
|
436
253
|
*/
|
|
437
254
|
function analyseChanges(changes) {
|
|
438
255
|
const paths = new Set();
|
|
256
|
+
const pathTexts = [];
|
|
439
257
|
let hasGlobs = false;
|
|
440
258
|
for (const bullet of changes) {
|
|
441
259
|
if (isGlobBullet(bullet)) {
|
|
442
260
|
hasGlobs = true;
|
|
261
|
+
const globText = bullet?.path ?? '';
|
|
262
|
+
if (globText) pathTexts.push(String(globText));
|
|
443
263
|
continue;
|
|
444
264
|
}
|
|
445
265
|
const path = extractChangeBulletPath(bullet);
|
|
446
|
-
if (path)
|
|
266
|
+
if (path) {
|
|
267
|
+
paths.add(path);
|
|
268
|
+
pathTexts.push(path);
|
|
269
|
+
}
|
|
447
270
|
}
|
|
448
|
-
return { fileCount: paths.size, hasGlobs };
|
|
271
|
+
return { fileCount: paths.size, hasGlobs, pathTexts };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Estimate the plan-time session mass (tokens) for a Story.
|
|
276
|
+
*
|
|
277
|
+
* Session mass is **authored prose only** — `estimateTokens` of the Story's
|
|
278
|
+
* goal / reason / acceptance / verify / slicing / spec / change-path text.
|
|
279
|
+
* File count and AC count do not add delivery-cost proxies (those recreated
|
|
280
|
+
* the retired count ceilings). Glob entries contribute their text but do not
|
|
281
|
+
* imply known width.
|
|
282
|
+
*
|
|
283
|
+
* @param {object} story
|
|
284
|
+
* @param {object} [_capacity] Unused; retained so call sites that still pass
|
|
285
|
+
* a capacity bag keep working. Mass no longer depends on capacity knobs.
|
|
286
|
+
* @returns {{ sessionMass: number, authoredTokens: number, acceptanceCount: number, fileCount: number, hasGlobs: boolean, changesAnalysis: object }}
|
|
287
|
+
*/
|
|
288
|
+
export function estimateStorySessionMass(
|
|
289
|
+
story,
|
|
290
|
+
_capacity = DEFAULT_MODEL_CAPACITY,
|
|
291
|
+
) {
|
|
292
|
+
const body = resolveStoryBody(story);
|
|
293
|
+
const acceptance = resolveAcceptance(story);
|
|
294
|
+
const verify = resolveVerify(story);
|
|
295
|
+
const changes = Array.isArray(body?.changes) ? body.changes : [];
|
|
296
|
+
const changesAnalysis = analyseChanges(changes);
|
|
297
|
+
|
|
298
|
+
const authoredParts = [
|
|
299
|
+
body?.goal,
|
|
300
|
+
body?.reason_to_exist,
|
|
301
|
+
typeof body?.spec === 'string' ? body.spec : '',
|
|
302
|
+
...acceptance.map((a) => String(a ?? '')),
|
|
303
|
+
...verify.map((v) => String(v ?? '')),
|
|
304
|
+
typeof body?.slicing === 'string' ? body.slicing : '',
|
|
305
|
+
...changesAnalysis.pathTexts,
|
|
306
|
+
].filter((p) => typeof p === 'string' && p.length > 0);
|
|
307
|
+
|
|
308
|
+
const authoredTokens = estimateTokens(authoredParts.join('\n'));
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
sessionMass: authoredTokens,
|
|
312
|
+
authoredTokens,
|
|
313
|
+
acceptanceCount: acceptance.length,
|
|
314
|
+
fileCount: changesAnalysis.fileCount,
|
|
315
|
+
hasGlobs: changesAnalysis.hasGlobs,
|
|
316
|
+
changesAnalysis,
|
|
317
|
+
};
|
|
449
318
|
}
|
|
450
319
|
|
|
451
|
-
function makeOversized(slug,
|
|
320
|
+
function makeOversized(slug, observed, ceiling) {
|
|
452
321
|
return {
|
|
453
322
|
kind: 'oversized-task',
|
|
454
323
|
severity: 'hard',
|
|
455
324
|
ticketSlug: slug,
|
|
456
|
-
field,
|
|
325
|
+
field: 'sessionMass',
|
|
457
326
|
observed,
|
|
458
327
|
ceiling,
|
|
459
328
|
};
|
|
460
329
|
}
|
|
461
330
|
|
|
462
|
-
function
|
|
331
|
+
function makeSoftSessionPressure(slug, observed, soft) {
|
|
463
332
|
return {
|
|
464
|
-
kind: 'soft-
|
|
333
|
+
kind: 'soft-session-pressure',
|
|
465
334
|
severity: 'soft',
|
|
466
335
|
ticketSlug: slug,
|
|
467
|
-
field,
|
|
336
|
+
field: 'sessionMass',
|
|
468
337
|
observed,
|
|
469
338
|
soft,
|
|
470
339
|
};
|
|
@@ -472,9 +341,7 @@ function makeSoftWidth(slug, field, observed, soft) {
|
|
|
472
341
|
|
|
473
342
|
/**
|
|
474
343
|
* Returns true when the Story declares itself `wide` with a non-empty reason.
|
|
475
|
-
* A `wide` declaration lifts the
|
|
476
|
-
* change states why it is broad rather than being silently capped. The
|
|
477
|
-
* reason is the only payload — there is no longer a profile enum to match.
|
|
344
|
+
* A `wide` declaration lifts the hard session-mass rejection.
|
|
478
345
|
*/
|
|
479
346
|
function isDeclaredWide(wide) {
|
|
480
347
|
return (
|
|
@@ -485,108 +352,59 @@ function isDeclaredWide(wide) {
|
|
|
485
352
|
);
|
|
486
353
|
}
|
|
487
354
|
|
|
355
|
+
function computeMergeCandidateFinding(story, mass, ceilings) {
|
|
356
|
+
if (mass.hasGlobs) return [];
|
|
357
|
+
const dependsOn = resolveDependsOn(story);
|
|
358
|
+
if (dependsOn.length === 0) return [];
|
|
359
|
+
if (mass.sessionMass > ceilings.mergeCandidateMaxSessionTokens) return [];
|
|
360
|
+
return [makeMergeCandidate(story.slug, mass.sessionMass, dependsOn)];
|
|
361
|
+
}
|
|
362
|
+
|
|
488
363
|
/**
|
|
489
|
-
* Compute the hard + soft
|
|
490
|
-
* - acceptance ceiling (`maxAcceptance`) + soft warn (`softAcceptanceCount`)
|
|
491
|
-
* - file-width ceiling (`hardFiles`) + soft warn (`softFiles`)
|
|
492
|
-
* - the optional `wide` declaration, which lifts the hard file ceiling
|
|
493
|
-
* - glob-awareness (glob entries are unknown-width; numeric ceiling skipped)
|
|
494
|
-
*
|
|
495
|
-
* Cohesion is the primary authoring heuristic (see the decomposer prompt and
|
|
496
|
-
* SKILL); these numeric findings are the backstop. A wide Story declares
|
|
497
|
-
* `wide` with a reason instead of being rejected for width.
|
|
364
|
+
* Compute the hard + soft capacity findings for a single Story.
|
|
498
365
|
*/
|
|
499
|
-
function computeStorySizingFindings(story,
|
|
366
|
+
function computeStorySizingFindings(story, capacity, ceilings) {
|
|
500
367
|
const out = [];
|
|
501
|
-
// Story #4271: normalize the body so the canonical serialized **string**
|
|
502
|
-
// shape the decomposer emits is scored at parity with the pre-serialize
|
|
503
|
-
// object shape. The acceptance ceiling reads the authoritative top-level
|
|
504
|
-
// `story.acceptance` (the binding contract), not `body.acceptance`.
|
|
505
368
|
const body = resolveStoryBody(story);
|
|
506
|
-
const acceptance = resolveAcceptance(story);
|
|
507
|
-
const changes = Array.isArray(body?.changes) ? body.changes : [];
|
|
508
369
|
const declaredWide = isDeclaredWide(body?.wide ?? null);
|
|
509
|
-
const
|
|
370
|
+
const mass = estimateStorySessionMass(story, capacity);
|
|
510
371
|
|
|
511
|
-
// Soft, advisory: flag acceptance criteria that reference a configuration
|
|
512
|
-
// constant without inlining a concrete value (Story #3855). Independent of
|
|
513
|
-
// the numeric sizing layers below — purely an authoring nudge.
|
|
514
372
|
out.push(...computeUnanchoredConstantFindings(story));
|
|
515
|
-
|
|
516
|
-
// Soft, advisory: flag a Story body that carries no non-empty
|
|
517
|
-
// `reason_to_exist` (Story #4273). The decomposer prompt marks the field
|
|
518
|
-
// REQUIRED and the consolidate critic gates on it, but that critic has no
|
|
519
|
-
// runtime backstop — this deterministic finding is the cheap backstop.
|
|
520
|
-
// Independent of the numeric sizing layers below.
|
|
521
373
|
out.push(...computeMissingReasonToExistFinding(story));
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
//
|
|
525
|
-
//
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
)
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
// Acceptance mass is advisory-only (Story #4312's under-size heuristic is
|
|
537
|
-
// the merge signal; the former hard `maxAcceptance` rejection is removed).
|
|
538
|
-
// A long binding contract is a re-check-cohesion nudge, never by itself a
|
|
539
|
-
// decomposition error — cohesion and the delivery envelope govern Story
|
|
540
|
-
// size, and the delivery-schedule simulation in the decomposer prompt owns
|
|
541
|
-
// the fragmentation/consolidation judgment.
|
|
542
|
-
if (acceptance.length > sizing.softAcceptanceCount) {
|
|
543
|
-
out.push(
|
|
544
|
-
makeSoftWidth(
|
|
545
|
-
story.slug,
|
|
546
|
-
'acceptance',
|
|
547
|
-
acceptance.length,
|
|
548
|
-
sizing.softAcceptanceCount,
|
|
549
|
-
),
|
|
550
|
-
);
|
|
374
|
+
out.push(...computeMergeCandidateFinding(story, mass, ceilings));
|
|
375
|
+
|
|
376
|
+
// Glob entries mark the change footprint as unknown-width. A non-wide Story
|
|
377
|
+
// carrying globs still gets an informational nudge to declare `wide` when
|
|
378
|
+
// the authored mass alone does not already trip a capacity finding.
|
|
379
|
+
if (mass.hasGlobs && !declaredWide) {
|
|
380
|
+
out.push({
|
|
381
|
+
kind: 'wide-undeclared',
|
|
382
|
+
severity: 'soft',
|
|
383
|
+
ticketSlug: story.slug,
|
|
384
|
+
reason: 'glob-changes',
|
|
385
|
+
});
|
|
551
386
|
}
|
|
552
387
|
|
|
553
|
-
const {
|
|
554
|
-
|
|
555
|
-
// Glob entries mark the Story as unknown-width: a glob cannot be bounded by
|
|
556
|
-
// the numeric ceiling, so skip it. A non-wide Story carrying globs gets an
|
|
557
|
-
// informational nudge to either narrow the change or declare `wide`.
|
|
558
|
-
if (hasGlobs) {
|
|
559
|
-
if (!declaredWide) {
|
|
560
|
-
out.push({
|
|
561
|
-
kind: 'wide-undeclared',
|
|
562
|
-
severity: 'soft',
|
|
563
|
-
ticketSlug: story.slug,
|
|
564
|
-
reason: 'glob-changes',
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
return out;
|
|
568
|
-
}
|
|
388
|
+
const { softSessionTokens, hardSessionTokens } = ceilings;
|
|
569
389
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
makeOversized(story.slug, 'fileCount', fileCount, sizing.hardFiles),
|
|
574
|
-
);
|
|
575
|
-
} else if (fileCount > sizing.softFiles) {
|
|
390
|
+
if (mass.sessionMass > hardSessionTokens && !declaredWide) {
|
|
391
|
+
out.push(makeOversized(story.slug, mass.sessionMass, hardSessionTokens));
|
|
392
|
+
} else if (mass.sessionMass > softSessionTokens) {
|
|
576
393
|
if (declaredWide) {
|
|
577
|
-
// Declared-wide Stories still surface the width as advisory signal.
|
|
578
394
|
out.push(
|
|
579
|
-
|
|
395
|
+
makeSoftSessionPressure(
|
|
396
|
+
story.slug,
|
|
397
|
+
mass.sessionMass,
|
|
398
|
+
softSessionTokens,
|
|
399
|
+
),
|
|
580
400
|
);
|
|
581
401
|
} else {
|
|
582
|
-
// Wide footprint with no `wide` declaration: informational nudge to
|
|
583
|
-
// either merge the Story down (cohesion) or declare `wide` with a reason.
|
|
584
402
|
out.push({
|
|
585
403
|
kind: 'wide-undeclared',
|
|
586
404
|
severity: 'soft',
|
|
587
405
|
ticketSlug: story.slug,
|
|
588
|
-
|
|
589
|
-
|
|
406
|
+
sessionMass: mass.sessionMass,
|
|
407
|
+
softSessionTokens,
|
|
590
408
|
});
|
|
591
409
|
}
|
|
592
410
|
}
|
|
@@ -596,23 +414,20 @@ function computeStorySizingFindings(story, sizing) {
|
|
|
596
414
|
|
|
597
415
|
/**
|
|
598
416
|
* Compute the full structured findings array for a normalized ticket
|
|
599
|
-
* hierarchy.
|
|
600
|
-
* `findings`; the AC-visible `errors[]` channel is the rendered
|
|
601
|
-
* subset where `severity === 'hard'`.
|
|
602
|
-
*
|
|
603
|
-
* 2-tier (Epic #3238): each Story is its own implementation unit and
|
|
604
|
-
* carries the `body` (acceptance / changes / wide) that the sizing layers
|
|
605
|
-
* score. There is no Task tier, so findings are computed directly over
|
|
606
|
-
* `stories`.
|
|
417
|
+
* hierarchy.
|
|
607
418
|
*
|
|
608
|
-
* @param {{ stories: object[],
|
|
419
|
+
* @param {{ stories: object[], capacity?: object }} input
|
|
609
420
|
* @returns {object[]}
|
|
610
421
|
*/
|
|
611
|
-
export function computeSizingFindings({ stories,
|
|
612
|
-
const merged = {
|
|
422
|
+
export function computeSizingFindings({ stories, capacity }) {
|
|
423
|
+
const merged = {
|
|
424
|
+
...DEFAULT_MODEL_CAPACITY,
|
|
425
|
+
...(capacity ?? {}),
|
|
426
|
+
};
|
|
427
|
+
const ceilings = resolveCapacityCeilings(merged);
|
|
613
428
|
const findings = [];
|
|
614
429
|
for (const story of stories ?? []) {
|
|
615
|
-
findings.push(...computeStorySizingFindings(story, merged));
|
|
430
|
+
findings.push(...computeStorySizingFindings(story, merged, ceilings));
|
|
616
431
|
}
|
|
617
432
|
return findings;
|
|
618
433
|
}
|
|
@@ -622,7 +437,7 @@ export function computeSizingFindings({ stories, sizing }) {
|
|
|
622
437
|
*/
|
|
623
438
|
export function renderHardFindingError(finding) {
|
|
624
439
|
if (finding.kind === 'oversized-task') {
|
|
625
|
-
return `
|
|
440
|
+
return `Story "${finding.ticketSlug}" exceeds the session-capacity ceiling: observed ${finding.observed} estimated tokens, max ${finding.ceiling}. Size the Story to what one guarded session can deliver and self-verify, or declare \`wide\` with a one-line reason.`;
|
|
626
441
|
}
|
|
627
|
-
return `
|
|
442
|
+
return `Story "${finding.ticketSlug}" tripped hard finding ${finding.kind}.`;
|
|
628
443
|
}
|