mandrel 1.58.0 → 1.60.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 +100 -98
- package/.agents/docs/SDLC.md +140 -141
- package/.agents/docs/configuration.md +16 -16
- package/.agents/docs/workflows.md +7 -8
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/audit-rules.json +20 -0
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +1 -1
- package/.agents/scripts/acceptance-eval.js +21 -4
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/assert-branch.js +1 -3
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/bootstrap.js +1 -1
- package/.agents/scripts/check-arch-cycles.js +360 -0
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/coverage-capture.js +24 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +11 -9
- package/.agents/scripts/epic-deliver-prepare.js +13 -5
- package/.agents/scripts/epic-execute-record-wave.js +5 -5
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +2 -2
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/git-rebase-and-resolve.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +40 -24
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
- package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
- package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
- package/.agents/scripts/lib/baselines/writer.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/close-validation/commands.js +188 -0
- package/.agents/scripts/lib/close-validation/gates.js +235 -0
- package/.agents/scripts/lib/close-validation/process.js +101 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
- package/.agents/scripts/lib/close-validation/runner.js +325 -0
- package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/quality.js +6 -6
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-resolver.js +2 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/coverage-capture.js +147 -4
- package/.agents/scripts/lib/cpu-pool.js +14 -0
- package/.agents/scripts/lib/crap-utils.js +6 -11
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/git-utils.js +24 -22
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +3 -8
- package/.agents/scripts/lib/maintainability-engine.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +4 -187
- package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
- package/.agents/scripts/lib/orchestration/code-review.js +95 -82
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
- package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
- package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
- package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
- package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/project-root.js +17 -0
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-adjacency.js +76 -0
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +9 -9
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/transpile.js +93 -0
- package/.agents/scripts/lib/wave-runner/tick.js +4 -153
- package/.agents/scripts/lib/workers/crap-worker.js +1 -1
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +114 -10
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +10 -1
- package/.agents/scripts/run-tests.js +24 -4
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +13 -10
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +162 -26
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +2 -2
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +2 -2
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +226 -0
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +12 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/onboard.md +21 -20
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +17 -20
- package/docs/CHANGELOG.md +1149 -0
- package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
- package/lib/cli/__tests__/update-reexec.test.js +513 -0
- package/lib/cli/init.js +338 -0
- package/lib/cli/update.js +413 -52
- package/package.json +3 -1
- package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
- package/.agents/scripts/lib/close-validation.js +0 -897
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
- package/.agents/scripts/lib/task-utils.js +0 -26
- package/.agents/scripts/story-deliver-prepare.js +0 -267
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* `isCoverageFresh` and decide whether to delegate to `runCapture`.
|
|
12
12
|
*/
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import crypto from 'node:crypto';
|
|
14
15
|
import fs from 'node:fs';
|
|
15
16
|
import path from 'node:path';
|
|
16
17
|
|
|
@@ -65,10 +66,130 @@ export function newestSourceMtime(cwd, targetDirs, io = {}) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
69
|
+
* Resolve the capture-stamp path that sits next to the coverage artifact.
|
|
70
|
+
* The stamp persists the content digest of the CRAP-target sources at the
|
|
71
|
+
* moment coverage was last captured, so freshness can be decided by content
|
|
72
|
+
* rather than mtime (mtime churns on branch switches / checkouts even when
|
|
73
|
+
* content is unchanged).
|
|
74
|
+
*
|
|
75
|
+
* @param {string} cwd Absolute repo root.
|
|
76
|
+
* @param {string} coveragePath Repo-relative coverage artifact path.
|
|
77
|
+
* @returns {string} Absolute stamp path (`<coverage-dir>/.capture-stamp.json`).
|
|
78
|
+
*/
|
|
79
|
+
export function captureStampPath(cwd, coveragePath) {
|
|
80
|
+
return path.join(
|
|
81
|
+
path.dirname(path.resolve(cwd, coveragePath)),
|
|
82
|
+
'.capture-stamp.json',
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const SOURCE_EXT_RE = /\.(?:js|mjs)$/;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Compute a stable content digest of the `.js`/`.mjs` sources under
|
|
90
|
+
* `targetDirs`: the `git ls-files -s` listing (mode + blob SHA + path) of
|
|
91
|
+
* tracked content, plus the on-disk bytes of any dirty working-tree files.
|
|
92
|
+
* Checkout/branch churn leaves blob SHAs untouched, so the digest only moves
|
|
93
|
+
* when content actually changes.
|
|
94
|
+
*
|
|
95
|
+
* Returns `null` when the digest cannot be computed (git unavailable, not a
|
|
96
|
+
* repo, empty target list) so callers can fall back to the mtime heuristic.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} cwd Absolute repo root.
|
|
99
|
+
* @param {string[]} targetDirs Repo-relative directories to digest.
|
|
100
|
+
* @param {{ spawnSync?: typeof spawnSync, readFileSync?: typeof fs.readFileSync }} [io]
|
|
101
|
+
* @returns {string | null} Hex SHA-256 digest, or null when unavailable.
|
|
102
|
+
*/
|
|
103
|
+
export function computeContentDigest(cwd, targetDirs, io = {}) {
|
|
104
|
+
const spawn = io.spawnSync ?? spawnSync;
|
|
105
|
+
const readFileSync = io.readFileSync ?? fs.readFileSync;
|
|
106
|
+
const dirs = (targetDirs ?? []).filter(
|
|
107
|
+
(d) => typeof d === 'string' && d.length > 0,
|
|
108
|
+
);
|
|
109
|
+
if (dirs.length === 0) return null;
|
|
110
|
+
|
|
111
|
+
const git = (...args) => {
|
|
112
|
+
const res = spawn('git', args, { cwd, encoding: 'utf8' });
|
|
113
|
+
if (res?.error || res?.status !== 0) {
|
|
114
|
+
throw res?.error ?? new Error(res?.stderr || `git ${args[0]} failed`);
|
|
115
|
+
}
|
|
116
|
+
return res.stdout ?? '';
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const hash = crypto.createHash('sha256');
|
|
121
|
+
const tracked = git('ls-files', '-s', '--', ...dirs)
|
|
122
|
+
.split('\n')
|
|
123
|
+
.filter((line) => SOURCE_EXT_RE.test(line.trimEnd()));
|
|
124
|
+
hash.update(tracked.join('\n'));
|
|
125
|
+
|
|
126
|
+
// Dirty working-tree files are not represented by their index blob SHA,
|
|
127
|
+
// so fold in their on-disk bytes (or absence) explicitly.
|
|
128
|
+
const dirty = git('status', '--porcelain', '--', ...dirs)
|
|
129
|
+
.split('\n')
|
|
130
|
+
.filter((line) => line.length > 3);
|
|
131
|
+
for (const line of dirty) {
|
|
132
|
+
let file = line.slice(3).trim();
|
|
133
|
+
if (file.includes(' -> ')) file = file.split(' -> ').pop();
|
|
134
|
+
file = file.replace(/^"|"$/g, '');
|
|
135
|
+
if (!SOURCE_EXT_RE.test(file)) continue;
|
|
136
|
+
hash.update(`\0${file}\0`);
|
|
137
|
+
try {
|
|
138
|
+
hash.update(readFileSync(path.resolve(cwd, file)));
|
|
139
|
+
} catch {
|
|
140
|
+
hash.update('<absent>');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return hash.digest('hex');
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Persist the capture stamp next to the coverage artifact. Best-effort: a
|
|
151
|
+
* write failure returns `false` rather than throwing — the worst case is a
|
|
152
|
+
* fall back to the mtime heuristic on the next freshness check.
|
|
153
|
+
*
|
|
154
|
+
* @param {{
|
|
155
|
+
* cwd: string,
|
|
156
|
+
* coveragePath: string,
|
|
157
|
+
* digest: string,
|
|
158
|
+
* writeFileSync?: typeof fs.writeFileSync,
|
|
159
|
+
* }} opts
|
|
160
|
+
* @returns {boolean} True when the stamp was written.
|
|
161
|
+
*/
|
|
162
|
+
export function writeCaptureStamp({
|
|
163
|
+
cwd,
|
|
164
|
+
coveragePath,
|
|
165
|
+
digest,
|
|
166
|
+
writeFileSync = fs.writeFileSync,
|
|
167
|
+
}) {
|
|
168
|
+
if (typeof digest !== 'string' || digest.length === 0) return false;
|
|
169
|
+
try {
|
|
170
|
+
writeFileSync(
|
|
171
|
+
captureStampPath(cwd, coveragePath),
|
|
172
|
+
`${JSON.stringify({ digest, capturedAt: new Date().toISOString() }, null, 2)}\n`,
|
|
173
|
+
);
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Decide whether the existing coverage artifact is "fresh".
|
|
182
|
+
*
|
|
183
|
+
* Primary test (content-aware, Story #3982): when a capture stamp exists
|
|
184
|
+
* next to the artifact, compare its persisted digest against the current
|
|
185
|
+
* content digest of `targetDirs`. Equal digests → fresh; different → stale.
|
|
186
|
+
* Branch switches and checkouts that bump mtimes without changing content
|
|
187
|
+
* no longer invalidate coverage.
|
|
188
|
+
*
|
|
189
|
+
* Fallback (stamp absent / unreadable / digest unavailable): the original
|
|
190
|
+
* mtime heuristic — artifact at least as new as the newest source file
|
|
191
|
+
* under `targetDirs`. Missing files, missing target dirs, or any IO error
|
|
192
|
+
* resolve to `false` so the caller captures rather than trusting stale data.
|
|
72
193
|
*
|
|
73
194
|
* @param {{
|
|
74
195
|
* coveragePath: string,
|
|
@@ -77,6 +198,8 @@ export function newestSourceMtime(cwd, targetDirs, io = {}) {
|
|
|
77
198
|
* statSync?: typeof fs.statSync,
|
|
78
199
|
* readdirSync?: typeof fs.readdirSync,
|
|
79
200
|
* existsSync?: typeof fs.existsSync,
|
|
201
|
+
* readFileSync?: typeof fs.readFileSync,
|
|
202
|
+
* computeDigest?: typeof computeContentDigest,
|
|
80
203
|
* }} opts
|
|
81
204
|
* @returns {{ fresh: boolean, reason: 'missing' | 'stale' | 'fresh' | 'no-sources' }}
|
|
82
205
|
*/
|
|
@@ -87,10 +210,30 @@ export function isCoverageFresh({
|
|
|
87
210
|
statSync = fs.statSync,
|
|
88
211
|
readdirSync = fs.readdirSync,
|
|
89
212
|
existsSync = fs.existsSync,
|
|
213
|
+
readFileSync = fs.readFileSync,
|
|
214
|
+
computeDigest = computeContentDigest,
|
|
90
215
|
}) {
|
|
91
216
|
const absCoverage = path.resolve(cwd, coveragePath);
|
|
92
217
|
if (!existsSync(absCoverage)) return { fresh: false, reason: 'missing' };
|
|
93
218
|
|
|
219
|
+
const stampPath = captureStampPath(cwd, coveragePath);
|
|
220
|
+
if (existsSync(stampPath)) {
|
|
221
|
+
let stamp = null;
|
|
222
|
+
try {
|
|
223
|
+
stamp = JSON.parse(readFileSync(stampPath, 'utf8'));
|
|
224
|
+
} catch {
|
|
225
|
+
// Corrupt/unreadable stamp → fall through to the mtime heuristic.
|
|
226
|
+
}
|
|
227
|
+
if (typeof stamp?.digest === 'string' && stamp.digest.length > 0) {
|
|
228
|
+
const current = computeDigest(cwd, targetDirs);
|
|
229
|
+
if (typeof current === 'string' && current.length > 0) {
|
|
230
|
+
return current === stamp.digest
|
|
231
|
+
? { fresh: true, reason: 'fresh' }
|
|
232
|
+
: { fresh: false, reason: 'stale' };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
94
237
|
let coverageMtime;
|
|
95
238
|
try {
|
|
96
239
|
coverageMtime = statSync(absCoverage).mtimeMs;
|
|
@@ -54,6 +54,20 @@ import { Worker } from 'node:worker_threads';
|
|
|
54
54
|
/** Default factory: spawn a real `worker_threads.Worker`. */
|
|
55
55
|
const defaultWorkerFactory = (script, options) => new Worker(script, options);
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Pool-vs-serial cutover for `runOnPool` callers.
|
|
59
|
+
*
|
|
60
|
+
* Below this batch size the pool's worker spawn overhead dominates, so
|
|
61
|
+
* callers fall back to in-process serial scoring. Tuned against the test
|
|
62
|
+
* suite's tmpdir fixtures (n=2 stays serial; the full repo n≈200–470
|
|
63
|
+
* takes the pool path). Single-sourced here so the maintainability
|
|
64
|
+
* baseline scan (`maintainability-utils.js`), the CRAP scanner
|
|
65
|
+
* (`crap-utils.js`), and the native review provider
|
|
66
|
+
* (`review-providers/native.js`) cannot silently desynchronize on a
|
|
67
|
+
* retune.
|
|
68
|
+
*/
|
|
69
|
+
export const POOL_SERIAL_THRESHOLD = 8;
|
|
70
|
+
|
|
57
71
|
/**
|
|
58
72
|
* @template TItem, TResult
|
|
59
73
|
* @param {string|URL} workerScript - File URL or path to the worker entry.
|
|
@@ -6,22 +6,17 @@ import {
|
|
|
6
6
|
coverageForMethodInEntry,
|
|
7
7
|
findCoverageEntry,
|
|
8
8
|
} from './coverage-utils.js';
|
|
9
|
-
import { runOnPool } from './cpu-pool.js';
|
|
9
|
+
import { POOL_SERIAL_THRESHOLD, runOnPool } from './cpu-pool.js';
|
|
10
10
|
import { crapFormula } from './crap-engine.js';
|
|
11
11
|
import { Logger } from './Logger.js';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
scanDirectory,
|
|
15
|
-
transpileIfNeeded,
|
|
16
|
-
} from './maintainability-utils.js';
|
|
12
|
+
import { scanDirectory } from './maintainability-utils.js';
|
|
13
|
+
import { resolveTsTranspilerVersion, transpileIfNeeded } from './transpile.js';
|
|
17
14
|
|
|
18
15
|
const CRAP_WORKER_URL = new URL('./workers/crap-worker.js', import.meta.url);
|
|
19
16
|
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
// stay serial and remain byte-identical to the pre-pool output.
|
|
24
|
-
const SERIAL_THRESHOLD = 8;
|
|
17
|
+
// Pool-vs-serial cutover — single-sourced in cpu-pool.js (see the
|
|
18
|
+
// POOL_SERIAL_THRESHOLD docstring for the tuning rationale).
|
|
19
|
+
const SERIAL_THRESHOLD = POOL_SERIAL_THRESHOLD;
|
|
25
20
|
// 1.1.0 — TypeScript support landed in 5.29.0. Bumped from 1.0.0 because
|
|
26
21
|
// the scanner now emits CRAP rows for TS/TSX paths that the previous
|
|
27
22
|
// kernel could never reach. The CRAP formula and per-method scoring
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* duplicate-search.js — Cross-Epic Duplicate Detection
|
|
3
3
|
*
|
|
4
|
-
* Used by `/
|
|
4
|
+
* Used by `/plan` Phase 2 (s-plan-ideation) to surface open Epics
|
|
5
5
|
* whose scope overlaps with a sharpened one-pager before a new Epic is
|
|
6
6
|
* created. Returns ranked candidates with an overlap score and URL so
|
|
7
7
|
* the host LLM can pause for HITL confirmation.
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* Both paths MUST emit the identical per-lens report contract
|
|
28
28
|
* (`{{auditOutputDir}}/<lens>-results.md`), so downstream consumers
|
|
29
|
-
* (`/
|
|
29
|
+
* (`/deliver` Phase 4 epic-audit, `audit-to-stories`) are agnostic to
|
|
30
30
|
* which path produced it.
|
|
31
31
|
*
|
|
32
32
|
* ## Why this is capability-degradation, not a contract shim
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// .agents/scripts/lib/dynamic-workflow/documentation-report-contract.js
|
|
2
|
+
import { assertSectionsContract } from './report-contract-core.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The `audit-documentation` report contract (Story #4024).
|
|
6
|
+
*
|
|
7
|
+
* This is the **single source of truth** for the report shape that BOTH the
|
|
8
|
+
* sequential lens (`.agents/workflows/audit-documentation.md` Step 3) and the
|
|
9
|
+
* orchestrated dynamic-workflow path
|
|
10
|
+
* (`.claude/workflows/audit-documentation.workflow.js`) MUST emit to
|
|
11
|
+
* `{{auditOutputDir}}/audit-documentation-results.md`. Keeping it here lets
|
|
12
|
+
* the contract-tier test assert report conformance against one definition
|
|
13
|
+
* rather than re-deriving headings from prose in two places.
|
|
14
|
+
*
|
|
15
|
+
* @module dynamic-workflow/documentation-report-contract
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The artifact filename the lens writes under `auditOutputDir`. */
|
|
19
|
+
export const REPORT_ARTIFACT_BASENAME = 'audit-documentation-results.md';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The required top-level (`##`) section headings, in document order, that the
|
|
23
|
+
* lens markdown's Step 3 template defines. A conformant report MUST contain
|
|
24
|
+
* each of these headings; the orchestrated path assembles its verified
|
|
25
|
+
* findings into exactly this skeleton.
|
|
26
|
+
*/
|
|
27
|
+
export const REQUIRED_SECTIONS = Object.freeze([
|
|
28
|
+
'Executive Summary',
|
|
29
|
+
'Target Set Coverage',
|
|
30
|
+
'Detailed Findings',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/** The H1 title the report opens with. */
|
|
34
|
+
export const REPORT_TITLE = 'Documentation Audit Report';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The required field labels inside each `### <finding>` block under
|
|
38
|
+
* `## Detailed Findings`. Mirrors the strict per-finding structure in the
|
|
39
|
+
* lens template.
|
|
40
|
+
*/
|
|
41
|
+
export const FINDING_FIELDS = Object.freeze([
|
|
42
|
+
'Category',
|
|
43
|
+
'Impact',
|
|
44
|
+
'Current State',
|
|
45
|
+
'Recommendation & Rationale',
|
|
46
|
+
'Agent Prompt',
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The finding categories the per-finding `Category` field draws from. These
|
|
51
|
+
* mirror the lens's Step 3 template enumeration: deterministic-gate findings
|
|
52
|
+
* (Generator Drift, Link Integrity) plus semantic-verification findings
|
|
53
|
+
* (Broken Instruction, Stale Description, Missing Coverage).
|
|
54
|
+
*/
|
|
55
|
+
export const FINDING_CATEGORIES = Object.freeze([
|
|
56
|
+
'Broken Instruction',
|
|
57
|
+
'Stale Description',
|
|
58
|
+
'Missing Coverage',
|
|
59
|
+
'Generator Drift',
|
|
60
|
+
'Link Integrity',
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The impact buckets the Executive Summary and per-finding `Impact` field draw
|
|
65
|
+
* from. The lens speaks in High/Medium/Low; this list is the canonical
|
|
66
|
+
* taxonomy the downstream `audit-to-stories` consumer maps onto Story
|
|
67
|
+
* priority.
|
|
68
|
+
*/
|
|
69
|
+
export const IMPACT_LEVELS = Object.freeze(['High', 'Medium', 'Low']);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Assert that a rendered markdown report conforms to the contract: it has the
|
|
73
|
+
* H1 title and every required `##` section heading. Returns a structured
|
|
74
|
+
* result rather than throwing, so callers (tests, the orchestrated path's
|
|
75
|
+
* self-check) can report precisely which sections are missing.
|
|
76
|
+
*
|
|
77
|
+
* Pure function — string analysis only.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} markdown - The rendered report body.
|
|
80
|
+
* @returns {{ conformant: boolean, missingSections: string[], hasTitle: boolean }}
|
|
81
|
+
*/
|
|
82
|
+
export function assertReportContract(markdown) {
|
|
83
|
+
return assertSectionsContract(markdown, {
|
|
84
|
+
title: REPORT_TITLE,
|
|
85
|
+
requiredSections: REQUIRED_SECTIONS,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Verdict rule: `clear` requires **both** (a) ≥ 4 of 5 canonical sections
|
|
11
11
|
* present, **and** (b) the **Acceptance Criteria** section present. The
|
|
12
12
|
* Acceptance-Criteria requirement is load-bearing: a downstream
|
|
13
|
-
* `/
|
|
13
|
+
* `/deliver` start gate and the close-time acceptance-spec reconciler
|
|
14
14
|
* both assume the Epic carries acceptance criteria, so a gate that passed an
|
|
15
15
|
* Epic with no Acceptance Criteria (the pre-Story-#3910 `≥ 4 of 5` behaviour)
|
|
16
16
|
* advertised a clarity guarantee it did not provide. AC is now a required
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* memory-freshness.js — walker + verifier for the `/
|
|
2
|
+
* memory-freshness.js — walker + verifier for the `/plan` Phase 0
|
|
3
3
|
* memory-freshness pre-flight. Story #2557 / Epic #2547. Tech Spec #2550.
|
|
4
4
|
*
|
|
5
5
|
* Walks every `.md` file under a memory directory (typically
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* prior-feedback-fetcher.js — gh-CLI-backed fetcher for open meta feedback
|
|
3
|
-
* issues that feed the `/
|
|
3
|
+
* issues that feed the `/plan` Phase 0 planner context.
|
|
4
4
|
*
|
|
5
5
|
* Story #2554 / Epic #2547. Tech Spec #2550 specifies that the fetcher MUST
|
|
6
6
|
* return open issues carrying the `meta::framework-gap` and
|
|
@@ -51,7 +51,7 @@ export const FINDING_CLASSES = Object.freeze([
|
|
|
51
51
|
/**
|
|
52
52
|
* Class → label-set routing table. Each class maps to exactly one label set.
|
|
53
53
|
* `tooling-dx` is the framework-gap path: it carries `meta::framework-gap` so
|
|
54
|
-
* the `/
|
|
54
|
+
* the `/plan` Phase 0 feedback fetcher surfaces it to the planner.
|
|
55
55
|
*/
|
|
56
56
|
const CLASS_TO_LABELS = Object.freeze({
|
|
57
57
|
'product-bug': [FOCUS_LABELS.PRODUCT],
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* tail of the exploratory-QA Triage path: once an operator has dispositioned a
|
|
6
6
|
* session's ledger items (see `.agents/schemas/qa-ledger.schema.json` and
|
|
7
7
|
* `lib/qa/qa-session.js`), the still-untriaged backlog is clustered and each
|
|
8
|
-
* cluster is promoted to a follow-up ticket — a single Story (via `/
|
|
9
|
-
* for a tight, one-deliverable cluster, or an Epic (via `/
|
|
8
|
+
* cluster is promoted to a follow-up ticket — a single Story (via `/plan`)
|
|
9
|
+
* for a tight, one-deliverable cluster, or an Epic (via `/plan --idea`) for
|
|
10
10
|
* a broad cluster that spans multiple coverage surfaces. Each contributing
|
|
11
11
|
* ledger item then has the resulting `routedTo` issue link written back onto it
|
|
12
12
|
* so a resume run sees the item as filed rather than re-promoting it.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Pure orchestration: **no network I/O lives here.** Every GitHub side-effect
|
|
24
24
|
* (issue search, ticket creation) flows through INJECTED PORTS so the unit test
|
|
25
25
|
* runs with no network. Production wires the ports to the GitHub provider /
|
|
26
|
-
* `/
|
|
26
|
+
* `/plan` / `/plan` surfaces; tests pass in-memory stubs.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { fingerprintFinding, routeFinding } from './route-finding.js';
|
|
@@ -40,8 +40,8 @@ export const PROMOTION_TARGETS = Object.freeze({
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* A cluster of more than this many distinct coverage surfaces is broad enough
|
|
43
|
-
* to warrant an Epic (`/
|
|
44
|
-
* (`/
|
|
43
|
+
* to warrant an Epic (`/plan --idea`) rather than a single Story
|
|
44
|
+
* (`/plan`). One or two surfaces is a tight, single-deliverable cluster.
|
|
45
45
|
*/
|
|
46
46
|
const EPIC_COVERAGE_THRESHOLD = 2;
|
|
47
47
|
|
|
@@ -159,8 +159,8 @@ export function clusterLedgerItems(items) {
|
|
|
159
159
|
/**
|
|
160
160
|
* Decide a cluster's promotion target. A cluster that spans more than
|
|
161
161
|
* {@link EPIC_COVERAGE_THRESHOLD} distinct coverage surfaces is broad enough to
|
|
162
|
-
* warrant an Epic (`/
|
|
163
|
-
* Story (`/
|
|
162
|
+
* warrant an Epic (`/plan --idea`); otherwise it is a single-deliverable
|
|
163
|
+
* Story (`/plan`).
|
|
164
164
|
*
|
|
165
165
|
* @param {{ coverages: string[] }} cluster
|
|
166
166
|
* @returns {'story'|'epic'}
|
|
@@ -233,7 +233,7 @@ function routedToLink(issue, kind) {
|
|
|
233
233
|
* shared `routeFinding` against existing Issues (via the injected search
|
|
234
234
|
* port). This dedups against work already filed.
|
|
235
235
|
* 2. On a `new` decision, open the follow-up ticket through the injected
|
|
236
|
-
* `createStory` (`/
|
|
236
|
+
* `createStory` (`/plan`) or `createEpic` (`/plan --idea`) port,
|
|
237
237
|
* chosen by {@link targetForCluster}. On any other decision, link back to
|
|
238
238
|
* the matched Issue rather than creating a duplicate.
|
|
239
239
|
* 3. Stamp the resolved `routedTo` link onto every contributing ledger item
|
|
@@ -250,9 +250,9 @@ function routedToLink(issue, kind) {
|
|
|
250
250
|
* @param {(finding: object) => Promise<Array<{ number: number, state: string, title?: string, body?: string }>>} [ports.searchCandidates]
|
|
251
251
|
* Optional semantic candidate search, forwarded to `routeFinding`.
|
|
252
252
|
* @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createStory
|
|
253
|
-
* Opens a single Story (`/
|
|
253
|
+
* Opens a single Story (`/plan`) for a tight cluster.
|
|
254
254
|
* @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createEpic
|
|
255
|
-
* Opens an Epic (`/
|
|
255
|
+
* Opens an Epic (`/plan --idea`) for a broad cluster.
|
|
256
256
|
* @returns {Promise<{
|
|
257
257
|
* promotions: Array<{
|
|
258
258
|
* clusterKey: string,
|
|
@@ -231,25 +231,28 @@ export function __setSleep(fn, opts = {}) {
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* immediately
|
|
234
|
+
* Shared bounded retry loop for git commands that can hit packed-refs lock
|
|
235
|
+
* contention. Only contention signatures trigger a retry — non-contention
|
|
236
|
+
* failures surface immediately, and success short-circuits the loop.
|
|
237
237
|
*
|
|
238
238
|
* Backoff schedule: 250ms, 500ms, 1000ms (3 retries → 4 attempts total).
|
|
239
239
|
* Deliberately no global lock — a mutex would erase the parallelism the
|
|
240
|
-
* worktree-isolation model is designed to enable.
|
|
240
|
+
* worktree-isolation model is designed to enable. The schedule and the
|
|
241
|
+
* jitter policy (`_sleep` / `_jitterFactor` seams) live only here so a
|
|
242
|
+
* backoff tuning change has a single point of application.
|
|
241
243
|
*
|
|
242
244
|
* @param {string} cwd
|
|
243
|
-
* @param {
|
|
245
|
+
* @param {string[]} argvPrefix - Leading git argv (e.g. `['fetch']`).
|
|
246
|
+
* @param {string[]} args - Trailing arguments (e.g. `['origin']`).
|
|
244
247
|
* @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
|
|
245
248
|
*/
|
|
246
|
-
|
|
249
|
+
async function gitWithContentionRetry(cwd, argvPrefix, args) {
|
|
247
250
|
const backoff = [250, 500, 1000];
|
|
248
251
|
let attempt = 0;
|
|
249
252
|
let last;
|
|
250
253
|
for (;;) {
|
|
251
254
|
attempt++;
|
|
252
|
-
last = gitSpawn(cwd,
|
|
255
|
+
last = gitSpawn(cwd, ...argvPrefix, ...args);
|
|
253
256
|
if (last.status === 0) return { ...last, attempts: attempt };
|
|
254
257
|
if (!isPackedRefsContention(last.stderr))
|
|
255
258
|
return { ...last, attempts: attempt };
|
|
@@ -260,6 +263,18 @@ export async function gitFetchWithRetry(cwd, ...args) {
|
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Run `git fetch …` with the bounded packed-refs-contention retry loop
|
|
268
|
+
* (see `gitWithContentionRetry`).
|
|
269
|
+
*
|
|
270
|
+
* @param {string} cwd
|
|
271
|
+
* @param {...string} args - Arguments after `fetch` (e.g. `'origin'`).
|
|
272
|
+
* @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
|
|
273
|
+
*/
|
|
274
|
+
export function gitFetchWithRetry(cwd, ...args) {
|
|
275
|
+
return gitWithContentionRetry(cwd, ['fetch'], args);
|
|
276
|
+
}
|
|
277
|
+
|
|
263
278
|
/**
|
|
264
279
|
* Run `git pull --rebase …` with the same bounded retry loop as
|
|
265
280
|
* `gitFetchWithRetry`. Packed-refs contention can occur during pulls
|
|
@@ -269,21 +284,8 @@ export async function gitFetchWithRetry(cwd, ...args) {
|
|
|
269
284
|
* @param {...string} args - Arguments after `pull --rebase` (e.g. `'origin', 'main'`).
|
|
270
285
|
* @returns {Promise<{ status: number, stdout: string, stderr: string, attempts: number }>}
|
|
271
286
|
*/
|
|
272
|
-
export
|
|
273
|
-
|
|
274
|
-
let attempt = 0;
|
|
275
|
-
let last;
|
|
276
|
-
for (;;) {
|
|
277
|
-
attempt++;
|
|
278
|
-
last = gitSpawn(cwd, 'pull', '--rebase', ...args);
|
|
279
|
-
if (last.status === 0) return { ...last, attempts: attempt };
|
|
280
|
-
if (!isPackedRefsContention(last.stderr))
|
|
281
|
-
return { ...last, attempts: attempt };
|
|
282
|
-
if (attempt > backoff.length) return { ...last, attempts: attempt };
|
|
283
|
-
const base = backoff[attempt - 1];
|
|
284
|
-
const jitter = Math.floor(Math.random() * base * _jitterFactor);
|
|
285
|
-
await _sleep(base + jitter);
|
|
286
|
-
}
|
|
287
|
+
export function gitPullWithRetry(cwd, ...args) {
|
|
288
|
+
return gitWithContentionRetry(cwd, ['pull', '--rebase'], args);
|
|
287
289
|
}
|
|
288
290
|
|
|
289
291
|
/**
|
|
@@ -76,7 +76,6 @@ export function isValidTransition(fromState, toState) {
|
|
|
76
76
|
|
|
77
77
|
export const TYPE_LABELS = {
|
|
78
78
|
EPIC: 'type::epic',
|
|
79
|
-
FEATURE: 'type::feature',
|
|
80
79
|
STORY: 'type::story',
|
|
81
80
|
};
|
|
82
81
|
|
|
@@ -104,7 +103,7 @@ export const CONTEXT_LABELS = {
|
|
|
104
103
|
export const CONTEXT_ACCEPTANCE_SPEC = CONTEXT_LABELS.ACCEPTANCE_SPEC;
|
|
105
104
|
|
|
106
105
|
/**
|
|
107
|
-
* Acceptance-axis labels for opt-out signalling on Stories
|
|
106
|
+
* Acceptance-axis labels for opt-out signalling on Stories that
|
|
108
107
|
* intentionally have no acceptance-spec coverage. Separate namespace from
|
|
109
108
|
* `context::` because it expresses absence rather than a linked context
|
|
110
109
|
* ticket.
|
|
@@ -120,7 +119,7 @@ export const ACCEPTANCE_NA = ACCEPTANCE_LABELS.N_A;
|
|
|
120
119
|
* loop). `meta::framework-gap` is applied to issues that surface a defect or
|
|
121
120
|
* missing capability in the framework itself; `meta::consumer-improvement`
|
|
122
121
|
* is applied to issues that surface improvements to a consumer project
|
|
123
|
-
* (workflow tweaks, ergonomic asks, doc polish). The `/
|
|
122
|
+
* (workflow tweaks, ergonomic asks, doc polish). The `/plan` Phase 0
|
|
124
123
|
* fetcher (see `lib/feedback-loop/prior-feedback-fetcher.js`) reads open
|
|
125
124
|
* issues carrying either label and surfaces them to the planner so retro
|
|
126
125
|
* signals are routed into durable substrates rather than lost in chat.
|
|
@@ -133,7 +132,7 @@ export const META_LABELS = {
|
|
|
133
132
|
/**
|
|
134
133
|
* Planning-axis labels (Epic #2880 F7). Currently scoped to the
|
|
135
134
|
* `planning::healthcheck-waived` operator-applied waiver, which is the
|
|
136
|
-
* documented escape hatch for the `/
|
|
135
|
+
* documented escape hatch for the `/plan` Phase 10 readiness
|
|
137
136
|
* healthcheck (`epic-plan-healthcheck.js`). The persist half of
|
|
138
137
|
* `epic-plan-decompose.js` refuses to flip an Epic to `agent::ready`
|
|
139
138
|
* when the healthcheck returned `ok: false` unless this label is
|
|
@@ -53,15 +53,10 @@ export const LABEL_TAXONOMY = [
|
|
|
53
53
|
color: LABEL_COLORS.TYPE,
|
|
54
54
|
description: 'Epic-level work item',
|
|
55
55
|
},
|
|
56
|
-
{
|
|
57
|
-
name: TYPE_LABELS.FEATURE,
|
|
58
|
-
color: LABEL_COLORS.TYPE,
|
|
59
|
-
description: 'Feature under an Epic',
|
|
60
|
-
},
|
|
61
56
|
{
|
|
62
57
|
name: TYPE_LABELS.STORY,
|
|
63
58
|
color: LABEL_COLORS.TYPE,
|
|
64
|
-
description: 'User story under
|
|
59
|
+
description: 'User story under an Epic',
|
|
65
60
|
},
|
|
66
61
|
|
|
67
62
|
// Agent State
|
|
@@ -75,7 +70,7 @@ export const LABEL_TAXONOMY = [
|
|
|
75
70
|
name: AGENT_LABELS.READY,
|
|
76
71
|
color: LABEL_COLORS.AGENT,
|
|
77
72
|
description:
|
|
78
|
-
'Parking state — frozen dispatch manifest exists; awaiting local /
|
|
73
|
+
'Parking state — frozen dispatch manifest exists; awaiting local /deliver',
|
|
79
74
|
},
|
|
80
75
|
{
|
|
81
76
|
name: AGENT_LABELS.EXECUTING,
|
|
@@ -120,7 +115,7 @@ export const LABEL_TAXONOMY = [
|
|
|
120
115
|
description: 'Acceptance Specification (Gherkin scenarios)',
|
|
121
116
|
},
|
|
122
117
|
|
|
123
|
-
// Acceptance axis — explicit opt-out signal for Stories
|
|
118
|
+
// Acceptance axis — explicit opt-out signal for Stories that
|
|
124
119
|
// intentionally have no acceptance-spec coverage.
|
|
125
120
|
{
|
|
126
121
|
name: ACCEPTANCE_LABELS.N_A,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import escomplex from 'typhonjs-escomplex';
|
|
3
|
-
import { transpileIfNeeded } from './
|
|
3
|
+
import { transpileIfNeeded } from './transpile.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Calculates the maintainability score of a JavaScript source file or string.
|