mandrel 2.31.0 → 2.33.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 +13 -17
- package/.agents/agents/acceptance-critic.md +1 -2
- package/.agents/docs/SDLC.md +12 -9
- package/.agents/docs/agentrc-reference.json +63 -58
- package/.agents/docs/configuration.md +275 -227
- package/.agents/docs/execution-reference.md +13 -14
- package/.agents/docs/quality-gates.md +195 -23
- package/.agents/instructions.md +2 -5
- package/.agents/rules/git-conventions-reference.md +27 -27
- package/.agents/rules/git-conventions.md +4 -2
- package/.agents/rules/known-tooling-behavior.md +66 -30
- package/.agents/rules/testing-standards.md +35 -71
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +1955 -1410
- package/.agents/schemas/lifecycle/README.md +21 -14
- package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/scripts/README.md +16 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/audit-to-stories.js +160 -41
- package/.agents/scripts/bootstrap.js +28 -26
- package/.agents/scripts/check-baseline-drift.js +73 -13
- package/.agents/scripts/check-baseline-scope.js +362 -0
- package/.agents/scripts/check-dead-exports.js +9 -1
- package/.agents/scripts/check-gherkin-corpus.js +508 -0
- package/.agents/scripts/check-knip-entries.js +159 -0
- package/.agents/scripts/check-lifecycle-lint.js +108 -124
- package/.agents/scripts/check-schema-references.js +1 -1
- package/.agents/scripts/diagnose-friction.js +7 -4
- package/.agents/scripts/generate-config-docs.js +263 -171
- package/.agents/scripts/install-matrix-assert.js +0 -1
- package/.agents/scripts/lib/ITicketingProvider.js +0 -58
- package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
- package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +81 -35
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
- package/.agents/scripts/lib/baselines/envelope.js +41 -60
- package/.agents/scripts/lib/baselines/git-base.js +30 -37
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
- package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
- package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
- package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
- package/.agents/scripts/lib/bdd-step-index.js +326 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
- package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
- package/.agents/scripts/lib/checks/loop-health.js +9 -37
- package/.agents/scripts/lib/child-exec.js +193 -0
- package/.agents/scripts/lib/cli/standard-args.js +1 -1
- package/.agents/scripts/lib/cli-args.js +64 -0
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/close-validation/runner.js +3 -3
- package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
- package/.agents/scripts/lib/config/commands.js +3 -5
- package/.agents/scripts/lib/config/explain.js +5 -7
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
- package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
- package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
- package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
- package/.agents/scripts/lib/config/gates/index.js +5 -2
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
- package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
- package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
- package/.agents/scripts/lib/config/gates/shared.js +58 -9
- package/.agents/scripts/lib/config/github.js +0 -1
- package/.agents/scripts/lib/config/limits.js +3 -48
- package/.agents/scripts/lib/config/qa.js +105 -0
- package/.agents/scripts/lib/config/runners.js +38 -16
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -56
- package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
- package/.agents/scripts/lib/config-settings-schema.js +386 -39
- package/.agents/scripts/lib/crap-baseline-join.js +126 -9
- package/.agents/scripts/lib/crap-utils.js +84 -520
- package/.agents/scripts/lib/dead-exports-knip.js +79 -10
- package/.agents/scripts/lib/degraded-mode.js +2 -2
- package/.agents/scripts/lib/dependency-parser.js +20 -7
- package/.agents/scripts/lib/doc-tiers.js +3 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
- package/.agents/scripts/lib/findings/provenance-field.js +135 -0
- package/.agents/scripts/lib/findings/route-finding.js +57 -8
- package/.agents/scripts/lib/fs-walk.js +52 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/git-utils.js +16 -36
- package/.agents/scripts/lib/knip-config-resolver.js +181 -0
- package/.agents/scripts/lib/knip-entry-sync.js +508 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
- package/.agents/scripts/lib/observability/signal-validator.js +5 -85
- package/.agents/scripts/lib/observability/signals-writer.js +19 -62
- package/.agents/scripts/lib/observability/source-classifier.js +5 -7
- package/.agents/scripts/lib/observability/terse-result.js +3 -3
- package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
- package/.agents/scripts/lib/orchestration/code-review.js +2 -2
- package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
- package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
- package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +7 -26
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +116 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
- package/.agents/scripts/lib/signals/detectors/common.js +1 -1
- package/.agents/scripts/lib/signals/index.js +8 -6
- package/.agents/scripts/lib/signals/schema.js +20 -25
- package/.agents/scripts/lib/signals/write.js +8 -8
- package/.agents/scripts/lib/story-body/footer-block.js +97 -0
- package/.agents/scripts/lib/story-body/story-body.js +18 -81
- package/.agents/scripts/lib/temp-retention.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
- package/.agents/scripts/lib/ticket-body-sections.js +4 -5
- package/.agents/scripts/lib/wave-runner/footprint.js +306 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
- package/.agents/scripts/lib/worktree-manager.js +2 -3
- package/.agents/scripts/lint-label-vocabulary.js +2 -24
- package/.agents/scripts/pr-watch-with-update.js +7 -5
- package/.agents/scripts/providers/github/blocked-by-add.js +25 -10
- package/.agents/scripts/providers/github/cache.js +2 -2
- package/.agents/scripts/providers/github/comments.js +6 -28
- package/.agents/scripts/providers/github/compose.js +0 -15
- package/.agents/scripts/providers/github/errors.js +10 -27
- package/.agents/scripts/providers/github/request-helpers.js +1 -2
- package/.agents/scripts/providers/github/sub-issues.js +10 -218
- package/.agents/scripts/providers/github.js +4 -7
- package/.agents/scripts/prune-baseline-orphans.js +181 -0
- package/.agents/scripts/resolve-stories.js +21 -7
- package/.agents/scripts/run-lint.js +61 -61
- package/.agents/scripts/run-test-profile.js +6 -6
- package/.agents/scripts/run-verify.js +48 -30
- package/.agents/scripts/single-story-close.js +20 -0
- package/.agents/scripts/single-story-init.js +12 -35
- package/.agents/scripts/stories-wave-tick.js +192 -9
- package/.agents/scripts/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +1 -11
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +28 -2
- package/.agents/workflows/helpers/audit-lens-core.md +11 -12
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
- package/.agents/workflows/helpers/code-review.md +3 -8
- package/.agents/workflows/helpers/deliver-reference.md +30 -2
- package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
- package/.agents/workflows/helpers/plan-reference.md +76 -0
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
- package/.agents/workflows/mandrel-update.md +10 -10
- package/.agents/workflows/qa-assist.md +15 -20
- package/.agents/workflows/qa-explore.md +9 -8
- package/README.md +1 -1
- package/docs/CHANGELOG.md +58 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
- package/package.json +14 -5
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
- package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
- package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
- package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
- package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
- package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
- package/.agents/scripts/check-gherkin-placeholders.js +0 -663
- package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
- package/.agents/scripts/lib/audit-suite/cli.js +0 -64
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
- package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
- package/.agents/scripts/lib/crap-baseline-index.js +0 -46
- package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
- package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
- package/.agents/scripts/lib/mutation/config-detector.js +0 -119
- package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
- package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
- package/.agents/scripts/lib/observability/active-story-env.js +0 -170
- package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
- package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
- package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
- package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
- package/.agents/scripts/lib/planning-corpus.js +0 -37
- package/.agents/scripts/lib/qa/coverage-report.js +0 -181
- package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
- package/.agents/scripts/lib/signals/detectors/index.js +0 -14
- package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
- package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
- package/.agents/scripts/lib/signals/read.js +0 -268
- package/.agents/scripts/lib/signals/span-tree.js +0 -291
- package/.agents/scripts/lib/story-lifecycle.js +0 -194
- package/.agents/scripts/lib/story-plan.js +0 -379
- package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
- package/.agents/scripts/lib/util/phase-timer.js +0 -163
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
- package/.agents/scripts/lint-baseline.js +0 -507
- package/.agents/scripts/providers/github/prs.js +0 -103
- package/.agents/scripts/signals-view.js +0 -309
- package/.agents/scripts/story-plan.js +0 -370
- package/.agents/scripts/sync-branch-from-base.js +0 -149
- package/.agents/scripts/validate-docs-freshness.js +0 -314
- package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
- package/.agents/workflows/helpers/signals.md +0 -112
|
@@ -54,7 +54,6 @@ import {
|
|
|
54
54
|
import { getStoryBranch, gitSpawn, gitSync } from './lib/git-utils.js';
|
|
55
55
|
import { Logger } from './lib/Logger.js';
|
|
56
56
|
import { TYPE_LABELS } from './lib/label-constants.js';
|
|
57
|
-
import { setActiveStoryEnv } from './lib/observability/active-story-env.js';
|
|
58
57
|
import { emitTerseResult } from './lib/observability/terse-result.js';
|
|
59
58
|
import {
|
|
60
59
|
executeFastForward,
|
|
@@ -492,10 +491,9 @@ export function seedStoryBranch({ cwd, storyBranch, baseBranch, progress }) {
|
|
|
492
491
|
}
|
|
493
492
|
|
|
494
493
|
/**
|
|
495
|
-
* Provision a worktree (or check out the branch in single-tree mode)
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
* (owns the worktree/single-tree routing + setActiveStoryEnv call).
|
|
494
|
+
* Provision a worktree (or check out the branch in single-tree mode).
|
|
495
|
+
* Returns the resolved `workCwd`, `worktreeCreated`, and `installStatus`.
|
|
496
|
+
* Exported for testing (owns the worktree/single-tree routing).
|
|
499
497
|
*
|
|
500
498
|
* @param {object} opts
|
|
501
499
|
* @param {object} opts.runtime
|
|
@@ -541,23 +539,6 @@ export async function provisionWorktree({
|
|
|
541
539
|
gitSync(cwd, 'checkout', storyBranch);
|
|
542
540
|
}
|
|
543
541
|
|
|
544
|
-
try {
|
|
545
|
-
// v2 Stories are standalone — no parent Epic. The helper omits
|
|
546
|
-
// CC_EPIC_ID from env + file; the trace hook keys its standalone-trace
|
|
547
|
-
// branch on CC_EPIC_ID being absent.
|
|
548
|
-
setActiveStoryEnv({
|
|
549
|
-
storyId,
|
|
550
|
-
workCwd,
|
|
551
|
-
logger: {
|
|
552
|
-
warn: (m) => progress('ENV', `⚠️ ${m}`),
|
|
553
|
-
},
|
|
554
|
-
});
|
|
555
|
-
} catch (err) {
|
|
556
|
-
Logger.error(
|
|
557
|
-
`[single-story-init] ⚠️ Failed to set active-Story env: ${err?.message ?? err}`,
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
542
|
return { workCwd, worktreeCreated, installStatus };
|
|
562
543
|
}
|
|
563
544
|
|
|
@@ -572,13 +553,11 @@ export async function runSingleStoryInit({
|
|
|
572
553
|
injectedConfig,
|
|
573
554
|
injectedSweep,
|
|
574
555
|
// Story #3483: lets tests drive the lease preflight deterministically.
|
|
575
|
-
// `injectedAcquireLease` swaps the guard
|
|
576
|
-
//
|
|
577
|
-
//
|
|
578
|
-
// heartbeat ledger, so a foreign assignee blocks unless stolen.
|
|
556
|
+
// `injectedAcquireLease` swaps the guard. `steal` forcibly transfers a
|
|
557
|
+
// foreign claim — the lease fails closed (audit #3513), so a foreign
|
|
558
|
+
// assignee blocks unless stolen.
|
|
579
559
|
injectedAcquireLease,
|
|
580
560
|
steal = false,
|
|
581
|
-
leaseNow,
|
|
582
561
|
injectedVerifyRemote,
|
|
583
562
|
// Story #4620: swap the git-touching provisioning steps so the
|
|
584
563
|
// early-flip-then-rollback ordering is unit-testable without a real worktree.
|
|
@@ -652,13 +631,12 @@ export async function runSingleStoryInit({
|
|
|
652
631
|
`Standalone Story: "${story.title}" → branch ${storyBranch} from ${baseBranch}.`,
|
|
653
632
|
);
|
|
654
633
|
|
|
655
|
-
// Story #3483 — lease preflight. Take an exclusive
|
|
656
|
-
//
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
// proceed. Skipped under --dry-run (no assignee mutation).
|
|
634
|
+
// Story #3483 — lease preflight. Take an exclusive claim on the Story
|
|
635
|
+
// ticket before any git mutation so two concurrent standalone runs cannot
|
|
636
|
+
// both drive the same Story. The guard fails closed (audit #3513): a
|
|
637
|
+
// foreign assignee aborts init (naming the current owner) unless --steal
|
|
638
|
+
// forcibly transfers it. Unclaimed / self-held claims proceed. Skipped
|
|
639
|
+
// under --dry-run (no assignee mutation).
|
|
662
640
|
let workCwd = cwd;
|
|
663
641
|
let worktreeCreated = false;
|
|
664
642
|
let installStatus = { status: 'skipped', reason: 'dry-run' };
|
|
@@ -670,7 +648,6 @@ export async function runSingleStoryInit({
|
|
|
670
648
|
storyId,
|
|
671
649
|
config,
|
|
672
650
|
steal: stealRequested,
|
|
673
|
-
now: leaseNow,
|
|
674
651
|
});
|
|
675
652
|
progress(
|
|
676
653
|
'LEASE',
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
* inFlight: number,
|
|
56
56
|
* cycleError: string | null,
|
|
57
57
|
* wedged: { reason, stories: [{ id, unmetBlockers }] } | null,
|
|
58
|
-
* inFlightReservation: { available, withheld: [{ id, blockedBy, reason }], note }
|
|
58
|
+
* inFlightReservation: { available, withheld: [{ id, blockedBy, reason, source, paths }], note },
|
|
59
|
+
* footprintGuard: { mode, withheld: [{ id, blockedBy, scope, source, paths }], advisory, note }
|
|
59
60
|
* }
|
|
60
61
|
*
|
|
61
62
|
* `inFlightReservation` reports the cross-beat half of the co-dispatch guard
|
|
@@ -70,6 +71,17 @@
|
|
|
70
71
|
* `available: false` rather than an empty — and therefore indistinguishable —
|
|
71
72
|
* result.
|
|
72
73
|
*
|
|
74
|
+
* `footprintGuard` reports the **beat-local** half, which until Story #5044 was
|
|
75
|
+
* reported nowhere: a same-beat overlap skip was a bare `continue` inside
|
|
76
|
+
* `planReadySet`, so the Story vanished from `ready[]` with no field anywhere
|
|
77
|
+
* naming the collision. Every entry in either report now also carries the
|
|
78
|
+
* colliding `paths` and a `source` tag — `declared-overlap` when both Stories'
|
|
79
|
+
* `changes[]` named the path (intended serialization: two Stories really do
|
|
80
|
+
* rewrite the same generated baseline) versus `scraped-overlap` when only the
|
|
81
|
+
* text evidence produced it. `mode` names the `footprintGuard` config value;
|
|
82
|
+
* under `advisory` the collisions are detected and listed in `advisory[]` but
|
|
83
|
+
* dispatch follows the declared `depends_on` edges alone.
|
|
84
|
+
*
|
|
73
85
|
* Probe mode adds fields the caller can no longer compute for itself:
|
|
74
86
|
* `done: number[]` (the resolved done set, in-set ∪ satisfied foreign
|
|
75
87
|
* blockers), `epilogueDue: boolean` (true exactly when every listed Story
|
|
@@ -111,19 +123,24 @@ import { readFileSync } from 'node:fs';
|
|
|
111
123
|
import { parseArgs } from 'node:util';
|
|
112
124
|
|
|
113
125
|
import { runAsCli } from './lib/cli-utils.js';
|
|
114
|
-
import { getRunners, resolveConfig } from './lib/config-resolver.js';
|
|
126
|
+
import { getPaths, getRunners, resolveConfig } from './lib/config-resolver.js';
|
|
115
127
|
import { detectCycle } from './lib/Graph.js';
|
|
116
128
|
import { Logger } from './lib/Logger.js';
|
|
117
129
|
import { AGENT_LABELS } from './lib/label-constants.js';
|
|
118
130
|
import { parseIds } from './lib/orchestration/resolve-stories.js';
|
|
119
131
|
import { buildStoryAdjacency } from './lib/story-adjacency.js';
|
|
120
132
|
import { expandIdList } from './lib/util/parse-id-list.js';
|
|
133
|
+
import { OVERLAP_SOURCES } from './lib/wave-runner/footprint.js';
|
|
121
134
|
import {
|
|
122
135
|
createProbeContext,
|
|
123
136
|
probeLiveState,
|
|
124
137
|
validateProbeFlags,
|
|
125
138
|
} from './lib/wave-runner/live-probe.js';
|
|
126
|
-
import {
|
|
139
|
+
import {
|
|
140
|
+
GUARD_MODES,
|
|
141
|
+
planReadySet,
|
|
142
|
+
WITHHOLD_SCOPES,
|
|
143
|
+
} from './lib/wave-runner/ready-set.js';
|
|
127
144
|
|
|
128
145
|
/**
|
|
129
146
|
* Exit code for a wedged run — deliberately distinct from the cycle exit (2)
|
|
@@ -214,7 +231,29 @@ Output envelope:
|
|
|
214
231
|
"wedged": null,
|
|
215
232
|
"inFlightReservation": {
|
|
216
233
|
"available": true,
|
|
217
|
-
"withheld": [
|
|
234
|
+
"withheld": [
|
|
235
|
+
{
|
|
236
|
+
"id": 4951,
|
|
237
|
+
"blockedBy": 4949,
|
|
238
|
+
"reason": "in-flight-earlier-beat",
|
|
239
|
+
"source": "declared-overlap",
|
|
240
|
+
"paths": ["lib/shared.js"]
|
|
241
|
+
}
|
|
242
|
+
],
|
|
243
|
+
"note": "..."
|
|
244
|
+
},
|
|
245
|
+
"footprintGuard": {
|
|
246
|
+
"mode": "enforce",
|
|
247
|
+
"withheld": [
|
|
248
|
+
{
|
|
249
|
+
"id": 4952,
|
|
250
|
+
"blockedBy": 4951,
|
|
251
|
+
"scope": "beat",
|
|
252
|
+
"source": "scraped-overlap",
|
|
253
|
+
"paths": ["lib/other.js"]
|
|
254
|
+
}
|
|
255
|
+
],
|
|
256
|
+
"advisory": [],
|
|
218
257
|
"note": "..."
|
|
219
258
|
}
|
|
220
259
|
}
|
|
@@ -225,6 +264,15 @@ blocking id — so an unfilled slot is explained rather than mysterious. It need
|
|
|
225
264
|
the in-flight Stories' footprints, which only --probe-live has: under --dag the
|
|
226
265
|
report is { available: false } and selection de-conflicts within the beat only.
|
|
227
266
|
|
|
267
|
+
footprintGuard names each Story withheld from THIS beat by a peer already
|
|
268
|
+
admitted on it — the half that used to be an unreported skip — and every
|
|
269
|
+
entry in either report carries the colliding paths plus a source tag
|
|
270
|
+
(declared-overlap when both changes[] declarations named the path, else
|
|
271
|
+
scraped-overlap from the text evidence). Its "mode" echoes
|
|
272
|
+
delivery.deliverRunner.footprintGuard: under "advisory" the collisions are
|
|
273
|
+
detected and listed in "advisory" but never withhold, and dispatch follows the
|
|
274
|
+
declared depends_on edges alone.
|
|
275
|
+
|
|
228
276
|
Exit codes:
|
|
229
277
|
0 - Success, ready set emitted
|
|
230
278
|
1 - Invalid input (missing/malformed DAG, invalid --concurrency/--in-flight/--done)
|
|
@@ -258,6 +306,7 @@ function inputErrorResult(message, concurrencyCap = null, inFlightValue = 0) {
|
|
|
258
306
|
cycleError: null,
|
|
259
307
|
wedged: null,
|
|
260
308
|
inFlightReservation: null,
|
|
309
|
+
footprintGuard: null,
|
|
261
310
|
inputError: message,
|
|
262
311
|
},
|
|
263
312
|
exitCode: 1,
|
|
@@ -299,10 +348,18 @@ const RESERVATION_REASONS = Object.freeze({
|
|
|
299
348
|
* and no later beat of this run will clear it. `foreignHeldIds` splits the two
|
|
300
349
|
* so each carries its own reason (Story #4960).
|
|
301
350
|
*
|
|
351
|
+
* Each entry also carries the **colliding paths** and an
|
|
352
|
+
* `OVERLAP_SOURCES` tag (Story #5044). A withhold that names no path is one an
|
|
353
|
+
* operator cannot act on, and `declared-overlap` vs `scraped-overlap` is the
|
|
354
|
+
* difference between "these two Stories both declared this generated baseline,
|
|
355
|
+
* serializing them is the point" and "one Story's body happened to mention a
|
|
356
|
+
* path the other declared" — the same unfilled slot for two very different
|
|
357
|
+
* reasons.
|
|
358
|
+
*
|
|
302
359
|
* @param {object[]|null|undefined} inFlightRecords
|
|
303
|
-
* @param {Array<{id: number, blockedBy: number}>} withheld
|
|
360
|
+
* @param {Array<{id: number, blockedBy: number, source?: string, paths?: string[]}>} withheld
|
|
304
361
|
* @param {Iterable<number>} [foreignHeldIds] Ids held by a foreign lease.
|
|
305
|
-
* @returns {{ available: boolean, withheld: Array<{id: number, blockedBy: number, reason: string}>, note: string|null }}
|
|
362
|
+
* @returns {{ available: boolean, withheld: Array<{id: number, blockedBy: number, reason: string, source: string, paths: string[]}>, note: string|null }}
|
|
306
363
|
*/
|
|
307
364
|
export function buildReservationReport(
|
|
308
365
|
inFlightRecords,
|
|
@@ -326,10 +383,13 @@ export function buildReservationReport(
|
|
|
326
383
|
}
|
|
327
384
|
const foreign = new Set(foreignHeldIds);
|
|
328
385
|
const classified = withheld.map((w) => ({
|
|
329
|
-
|
|
386
|
+
id: w.id,
|
|
387
|
+
blockedBy: w.blockedBy,
|
|
330
388
|
reason: foreign.has(w.blockedBy)
|
|
331
389
|
? RESERVATION_REASONS.FOREIGN_LEASE
|
|
332
390
|
: RESERVATION_REASONS.EARLIER_BEAT,
|
|
391
|
+
source: w.source ?? OVERLAP_SOURCES.DECLARED,
|
|
392
|
+
paths: w.paths ?? [],
|
|
333
393
|
}));
|
|
334
394
|
return {
|
|
335
395
|
available: true,
|
|
@@ -338,6 +398,87 @@ export function buildReservationReport(
|
|
|
338
398
|
};
|
|
339
399
|
}
|
|
340
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Report the **beat-local** half of the footprint guard, plus the guard mode
|
|
403
|
+
* itself (Story #5044).
|
|
404
|
+
*
|
|
405
|
+
* Until now a same-beat overlap skip was an anonymous `continue` inside
|
|
406
|
+
* `planReadySet`: the Story simply did not appear in `ready[]` and no field
|
|
407
|
+
* anywhere said why. That is the same unexplained-unfilled-slot failure the
|
|
408
|
+
* cross-beat `inFlightReservation` report was built to remove, left standing on
|
|
409
|
+
* the other half of the guard — and it is how a plan whose siblings collided
|
|
410
|
+
* only on machine-generated footer text ran fully serial without leaving a
|
|
411
|
+
* trace to notice.
|
|
412
|
+
*
|
|
413
|
+
* `withheld` and `advisory` are disjoint by construction: under `enforce` every
|
|
414
|
+
* detection withheld, under `advisory` none did. Reporting them as separate
|
|
415
|
+
* lists rather than one flagged list means a consumer counting withheld
|
|
416
|
+
* dispatches never has to inspect a boolean to get the count right.
|
|
417
|
+
*
|
|
418
|
+
* @param {Array<object>} footprintWithholds The kernel's complete ledger.
|
|
419
|
+
* @param {'enforce'|'advisory'} mode
|
|
420
|
+
* @returns {{ mode: string, withheld: object[], advisory: object[], note: string|null }}
|
|
421
|
+
*/
|
|
422
|
+
export function buildFootprintGuardReport(footprintWithholds, mode) {
|
|
423
|
+
const ledger = Array.isArray(footprintWithholds) ? footprintWithholds : [];
|
|
424
|
+
const project = ({ id, blockedBy, scope, source, paths }) => ({
|
|
425
|
+
id,
|
|
426
|
+
blockedBy,
|
|
427
|
+
scope,
|
|
428
|
+
source,
|
|
429
|
+
paths,
|
|
430
|
+
});
|
|
431
|
+
const beat = ledger
|
|
432
|
+
.filter((w) => w.scope === WITHHOLD_SCOPES.BEAT && w.enforced)
|
|
433
|
+
.map(project);
|
|
434
|
+
const advisory = ledger.filter((w) => !w.enforced).map(project);
|
|
435
|
+
return {
|
|
436
|
+
mode,
|
|
437
|
+
withheld: beat,
|
|
438
|
+
advisory,
|
|
439
|
+
note: footprintGuardNote(beat, advisory, mode),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Render the operator-facing note for the beat-local guard, naming the
|
|
445
|
+
* colliding path(s) for every entry. `null` when the guard neither withheld nor
|
|
446
|
+
* waved anything through.
|
|
447
|
+
*
|
|
448
|
+
* @param {object[]} beat
|
|
449
|
+
* @param {object[]} advisory
|
|
450
|
+
* @param {string} mode
|
|
451
|
+
* @returns {string|null}
|
|
452
|
+
*/
|
|
453
|
+
function footprintGuardNote(beat, advisory, mode) {
|
|
454
|
+
const detail = (entries) =>
|
|
455
|
+
entries
|
|
456
|
+
.map(
|
|
457
|
+
(w) =>
|
|
458
|
+
`#${w.id} ← #${w.blockedBy} on ${w.paths.join(', ')} (${w.source})`,
|
|
459
|
+
)
|
|
460
|
+
.join('; ');
|
|
461
|
+
if (beat.length > 0) {
|
|
462
|
+
return (
|
|
463
|
+
`${beat.length} Story(ies) withheld from THIS beat because their file ` +
|
|
464
|
+
`footprint overlaps a peer already admitted on it — ${detail(beat)}. ` +
|
|
465
|
+
`Each is still eligible and re-admits on a later beat once its peer ` +
|
|
466
|
+
`lands. A ${OVERLAP_SOURCES.SCRAPED} source means the collision came ` +
|
|
467
|
+
`from path evidence in the Story text rather than from either ` +
|
|
468
|
+
`changes[] declaration.`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
if (advisory.length > 0) {
|
|
472
|
+
return (
|
|
473
|
+
`footprintGuard is '${mode}': ${advisory.length} footprint collision(s) ` +
|
|
474
|
+
`were detected and NOT enforced — ${detail(advisory)}. Dispatch followed ` +
|
|
475
|
+
`the declared depends_on edges alone. Set ` +
|
|
476
|
+
`delivery.deliverRunner.footprintGuard: 'enforce' to serialize these.`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
|
|
341
482
|
/**
|
|
342
483
|
* Render the operator-facing reservation note, one sentence per reason class
|
|
343
484
|
* present. Neither class is a failure or a wedge, but they clear by different
|
|
@@ -600,6 +741,29 @@ export function resolveCapPrecedence({ cwd, config, override } = {}) {
|
|
|
600
741
|
};
|
|
601
742
|
}
|
|
602
743
|
|
|
744
|
+
/**
|
|
745
|
+
* Resolve the two footprint-guard inputs from the same config seam the cap
|
|
746
|
+
* comes from (Story #5044).
|
|
747
|
+
*
|
|
748
|
+
* `tempRoot` is threaded rather than hardcoded because the evidence scrape must
|
|
749
|
+
* exclude the project's *configured* scratch root: a consumer that sets
|
|
750
|
+
* `project.paths.tempRoot: '.scratch'` would otherwise have every sibling
|
|
751
|
+
* citing a report under it collide, which is the exact defect this Story
|
|
752
|
+
* removes for the default `temp/`.
|
|
753
|
+
*
|
|
754
|
+
* @param {object} [opts]
|
|
755
|
+
* @param {string} [opts.cwd] Repo root for config resolution.
|
|
756
|
+
* @param {object} [opts.config] Pre-resolved config (test injection).
|
|
757
|
+
* @returns {{ footprintGuard: 'enforce'|'advisory', tempRoot: string }}
|
|
758
|
+
*/
|
|
759
|
+
export function resolveFootprintGuardSettings({ cwd, config } = {}) {
|
|
760
|
+
const resolved = config ?? resolveConfig({ cwd });
|
|
761
|
+
return {
|
|
762
|
+
footprintGuard: getRunners(resolved).deliverRunner.footprintGuard,
|
|
763
|
+
tempRoot: getPaths(resolved).tempRoot,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
603
767
|
/**
|
|
604
768
|
* Build the per-beat ready-set envelope from a validated DAG.
|
|
605
769
|
*
|
|
@@ -648,6 +812,8 @@ export function buildReadySetEnvelope(
|
|
|
648
812
|
inFlight = 0,
|
|
649
813
|
inFlightRecords = null,
|
|
650
814
|
foreignHeldIds = [],
|
|
815
|
+
footprintGuard = GUARD_MODES.ENFORCE,
|
|
816
|
+
tempRoot,
|
|
651
817
|
},
|
|
652
818
|
) {
|
|
653
819
|
const totalStories = nodes.length;
|
|
@@ -668,6 +834,11 @@ export function buildReadySetEnvelope(
|
|
|
668
834
|
// which Stories a reservation withheld (Story #4950). Never omitted on a
|
|
669
835
|
// resolved beat: an absent report reads exactly like an empty one.
|
|
670
836
|
inFlightReservation: buildReservationReport(inFlightRecords, []),
|
|
837
|
+
// The beat-local half of the same guard, plus the mode it ran in (Story
|
|
838
|
+
// #5044). Also never omitted: a same-beat skip used to be reported nowhere
|
|
839
|
+
// at all, which is precisely how an evidence-widening artifact could
|
|
840
|
+
// serialize a whole run unnoticed.
|
|
841
|
+
footprintGuard: buildFootprintGuardReport([], footprintGuard),
|
|
671
842
|
};
|
|
672
843
|
|
|
673
844
|
if (totalStories === 0) {
|
|
@@ -714,7 +885,7 @@ export function buildReadySetEnvelope(
|
|
|
714
885
|
return rec;
|
|
715
886
|
});
|
|
716
887
|
|
|
717
|
-
const { selected,
|
|
888
|
+
const { selected, footprintWithholds, guardMode } = planReadySet({
|
|
718
889
|
stories: records,
|
|
719
890
|
doneIds,
|
|
720
891
|
inFlight,
|
|
@@ -723,13 +894,21 @@ export function buildReadySetEnvelope(
|
|
|
723
894
|
// contract (an array) while `base.inFlightReservation` reports that the
|
|
724
895
|
// reservation itself was unavailable rather than merely empty.
|
|
725
896
|
inFlightRecords: inFlightRecords ?? [],
|
|
897
|
+
footprintGuard,
|
|
898
|
+
tempRoot,
|
|
726
899
|
});
|
|
727
900
|
const ready = selected.map((rec) => rec.id);
|
|
901
|
+
// The cross-beat report reads the enforced in-flight slice of the kernel's
|
|
902
|
+
// ledger rather than the legacy `withheldByInFlight` list, so it carries the
|
|
903
|
+
// colliding paths and the overlap source through to the operator.
|
|
728
904
|
const reservation = buildReservationReport(
|
|
729
905
|
inFlightRecords,
|
|
730
|
-
|
|
906
|
+
footprintWithholds.filter(
|
|
907
|
+
(w) => w.scope === WITHHOLD_SCOPES.IN_FLIGHT && w.enforced,
|
|
908
|
+
),
|
|
731
909
|
foreignHeldIds,
|
|
732
910
|
);
|
|
911
|
+
const guardReport = buildFootprintGuardReport(footprintWithholds, guardMode);
|
|
733
912
|
|
|
734
913
|
// Wedge detection (Story #4540). `ready: []` is normal while work is in
|
|
735
914
|
// flight — the loop is simply waiting. But ready-empty AND nothing in
|
|
@@ -750,6 +929,7 @@ export function buildReadySetEnvelope(
|
|
|
750
929
|
ready,
|
|
751
930
|
wedged: wedge,
|
|
752
931
|
inFlightReservation: reservation,
|
|
932
|
+
footprintGuard: guardReport,
|
|
753
933
|
},
|
|
754
934
|
exitCode: WEDGED_EXIT_CODE,
|
|
755
935
|
};
|
|
@@ -761,6 +941,7 @@ export function buildReadySetEnvelope(
|
|
|
761
941
|
ready,
|
|
762
942
|
wedged: null,
|
|
763
943
|
inFlightReservation: reservation,
|
|
944
|
+
footprintGuard: guardReport,
|
|
764
945
|
},
|
|
765
946
|
exitCode: 0,
|
|
766
947
|
};
|
|
@@ -896,6 +1077,7 @@ export function runStoriesWaveTick({
|
|
|
896
1077
|
capPrecedence,
|
|
897
1078
|
doneIds,
|
|
898
1079
|
inFlight: inFlightValue,
|
|
1080
|
+
...resolveFootprintGuardSettings({ cwd, config }),
|
|
899
1081
|
});
|
|
900
1082
|
}
|
|
901
1083
|
|
|
@@ -1001,6 +1183,7 @@ export async function runProbedStoriesWaveTick({
|
|
|
1001
1183
|
// ...and the only mode that can tell a foreign lease-holder apart from
|
|
1002
1184
|
// this run's own earlier-beat dispatch (Story #4960).
|
|
1003
1185
|
foreignHeldIds: foreignHeld.map((h) => h.id),
|
|
1186
|
+
...resolveFootprintGuardSettings({ cwd, config }),
|
|
1004
1187
|
});
|
|
1005
1188
|
|
|
1006
1189
|
const done = [...doneIds].sort((a, b) => a - b);
|