mandrel 2.36.0 → 2.38.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 +16 -16
- package/.agents/agents/plan-critic.md +1 -1
- package/.agents/docs/SDLC.md +40 -40
- package/.agents/docs/configuration.md +41 -16
- package/.agents/docs/execution-reference.md +3 -3
- package/.agents/docs/quality-gates.md +2 -2
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +5 -5
- package/.agents/rules/changelog-style.md +3 -3
- package/.agents/rules/ci-remediation.md +1 -1
- package/.agents/rules/git-conventions-reference.md +4 -4
- package/.agents/rules/testing-standards.md +56 -0
- package/.agents/schemas/agentrc.schema.json +9 -9
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/apply-quality-bootstrap.js +1 -1
- package/.agents/scripts/audit-to-stories.js +2 -2
- package/.agents/scripts/boot-sweep.js +7 -1
- package/.agents/scripts/check-context-budget.js +195 -103
- package/.agents/scripts/check-cyclomatic.js +112 -42
- package/.agents/scripts/check-doc-links.js +32 -4
- package/.agents/scripts/check-generated-validator.js +202 -0
- package/.agents/scripts/check-lifecycle-lint.js +2 -68
- package/.agents/scripts/check-schema-references.js +2 -28
- package/.agents/scripts/check-test-temp-hygiene.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +1 -1
- package/.agents/scripts/deliver-light.js +10 -10
- package/.agents/scripts/deliver-recover.js +1 -1
- package/.agents/scripts/drain-pending-cleanup.js +19 -2
- package/.agents/scripts/evidence-gate.js +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +5 -5
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +1 -1
- package/.agents/scripts/lib/baselines/components.js +32 -2
- package/.agents/scripts/lib/baselines/env-overrides.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-budget.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bdd-step-index.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -2
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/install-ledger.js +1 -1
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/prompt.js +1 -1
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +2 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +1 -1
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +1 -1
- package/.agents/scripts/lib/cli-args.js +1 -3
- package/.agents/scripts/lib/close-validation/gates.js +1 -1
- package/.agents/scripts/lib/close-validation/process.js +1 -1
- package/.agents/scripts/lib/command-header.js +1 -1
- package/.agents/scripts/lib/config/delivery-routing.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +3 -3
- package/.agents/scripts/lib/config-settings-schema-quality.js +6 -6
- package/.agents/scripts/lib/config-settings-schema.js +49 -9
- package/.agents/scripts/lib/coverage-baseline.js +2 -2
- package/.agents/scripts/lib/cpu-pool.js +90 -10
- package/.agents/scripts/lib/crap-utils.js +6 -2
- package/.agents/scripts/lib/cyclomatic-ceiling.js +28 -6
- package/.agents/scripts/lib/cyclomatic-scope.js +144 -0
- package/.agents/scripts/lib/dependency-version.js +86 -0
- package/.agents/scripts/lib/duplicate-search.js +2 -2
- package/.agents/scripts/lib/errors/index.js +1 -1
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +2 -2
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +5 -5
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +2 -2
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +12 -12
- package/.agents/scripts/lib/findings/route-finding.js +2 -2
- package/.agents/scripts/lib/generated/agentrc-validator.js +16 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/install-cmd-parser.js +1 -1
- package/.agents/scripts/lib/knip-entry-sync.js +1 -57
- package/.agents/scripts/lib/label-constants.js +2 -2
- package/.agents/scripts/lib/label-taxonomy.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +108 -10
- package/.agents/scripts/lib/observability/source-classifier.js +1 -0
- package/.agents/scripts/lib/onboard/init-tail.js +4 -4
- package/.agents/scripts/lib/onboard/scaffold-docs.js +2 -2
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +4 -4
- package/.agents/scripts/lib/orchestration/complexity-gate.js +5 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +3 -3
- package/.agents/scripts/lib/orchestration/docs-digest.js +3 -3
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +2 -2
- package/.agents/scripts/lib/orchestration/light-backstop.js +1 -1
- package/.agents/scripts/lib/orchestration/light-escalation.js +6 -6
- package/.agents/scripts/lib/orchestration/light-suitability.js +19 -19
- package/.agents/scripts/lib/orchestration/plan-context.js +4 -4
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-metrics.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +3 -3
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +6 -6
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +86 -25
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +3 -3
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +6 -6
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +7 -7
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -1
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +3 -3
- package/.agents/scripts/lib/orchestration/pr-watch.js +1 -30
- package/.agents/scripts/lib/orchestration/reassert-status-column.js +2 -2
- package/.agents/scripts/lib/orchestration/remote-verifier.js +2 -2
- package/.agents/scripts/lib/orchestration/resolve-stories.js +4 -4
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +3 -3
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/native.js +9 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -3
- package/.agents/scripts/lib/orchestration/run-epilogue.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +5 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +52 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +3 -3
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +2 -5
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +14 -12
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +34 -9
- package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +6 -4
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +3 -3
- package/.agents/scripts/lib/qa/qa-session.js +1 -1
- package/.agents/scripts/lib/runtime-deps/manifest.js +1 -7
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +3 -58
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +284 -25
- package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
- package/.agents/scripts/lib/source-text/strip-js-comments.js +115 -0
- package/.agents/scripts/lib/story-adjacency.js +2 -2
- package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
- package/.agents/scripts/lib/test-isolate/runner.js +3 -3
- package/.agents/scripts/lib/test-runner-contract.js +14 -6
- package/.agents/scripts/lib/test-tiers.js +135 -18
- package/.agents/scripts/lib/ticket-body-sections.js +1 -1
- package/.agents/scripts/lib/transpile.js +16 -3
- package/.agents/scripts/lib/util/concurrent-map.js +1 -1
- package/.agents/scripts/lib/util/parse-id-list.js +1 -1
- package/.agents/scripts/lib/wave-runner/live-probe.js +4 -4
- package/.agents/scripts/lib/wave-runner/ready-set.js +1 -1
- package/.agents/scripts/lib/workflow-closure.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +170 -32
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +2 -5
- package/.agents/scripts/lint-issue-body.js +3 -3
- package/.agents/scripts/plan-context.js +4 -4
- package/.agents/scripts/plan-critics.js +2 -2
- package/.agents/scripts/plan-persist.js +2 -2
- package/.agents/scripts/plan-run-epilogue.js +2 -2
- package/.agents/scripts/pr-watch-with-update.js +305 -137
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/tickets.js +114 -21
- package/.agents/scripts/quality-preview.js +162 -70
- package/.agents/scripts/resolve-stories.js +3 -3
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/run-test-profile.js +8 -5
- package/.agents/scripts/run-tests.js +79 -14
- package/.agents/scripts/single-story-close.js +2 -4
- package/.agents/scripts/single-story-init.js +10 -6
- package/.agents/scripts/stories-wave-tick.js +8 -8
- package/.agents/skills/core/idea-refinement/SKILL.md +5 -5
- package/.agents/skills/core/scope-triage/SKILL.md +3 -3
- package/.agents/skills/skills.index.json +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -1
- package/.agents/workflows/audit-data-model.md +1 -1
- package/.agents/workflows/audit-documentation.md +2 -2
- package/.agents/workflows/audit-to-stories.md +11 -11
- package/.agents/workflows/git-deliver.md +4 -4
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-review.md +4 -4
- package/.agents/workflows/helpers/deliver-light.md +26 -26
- package/.agents/workflows/helpers/deliver-reference.md +15 -7
- package/.agents/workflows/helpers/deliver-story-reference.md +9 -4
- package/.agents/workflows/helpers/deliver-story.md +6 -7
- package/.agents/workflows/helpers/diagnose.md +2 -2
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/helpers/plan-reference.md +9 -9
- package/.agents/workflows/helpers/qa-core.md +8 -8
- package/.agents/workflows/helpers/worktree-lifecycle.md +6 -6
- package/.agents/workflows/{deliver.md → mandrel-deliver.md} +15 -19
- package/.agents/workflows/{plan.md → mandrel-plan.md} +9 -9
- package/.agents/workflows/memory-consolidate.md +4 -4
- package/.agents/workflows/prototype.md +3 -3
- package/.agents/workflows/qa-assist.md +12 -12
- package/.agents/workflows/qa-explore.md +6 -6
- package/.agents/workflows/qa-run.md +1 -1
- package/README.md +7 -7
- package/docs/CHANGELOG.md +43 -0
- package/lib/cli/doctor.js +1 -1
- package/lib/cli/init.js +1 -1
- package/lib/cli/migrate.js +100 -94
- package/lib/migrations/helpers/retire-agentrc-key.js +200 -0
- package/lib/migrations/index.js +32 -33
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +21 -80
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +15 -87
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +13 -71
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +1 -1
- package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +13 -101
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +13 -100
- package/package.json +7 -3
- package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +0 -32
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +0 -37
- package/.agents/scripts/lib/cli/parse-numeric.js +0 -60
- package/.agents/scripts/lib/close-validation/telemetry.js +0 -79
- package/.agents/scripts/lib/orchestration/label-transitions.js +0 -44
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +0 -147
- package/.agents/scripts/lib/orchestration/phase-runner.js +0 -88
- package/.agents/scripts/lib/orchestration/recut.js +0 -56
|
@@ -487,6 +487,272 @@ async function evaluateGreenWatch({
|
|
|
487
487
|
};
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
+
/**
|
|
491
|
+
* Resolve the knobs, temp root and working directory one watch run needs.
|
|
492
|
+
*
|
|
493
|
+
* Split out of `runPrWatch` because config resolution must not abort the
|
|
494
|
+
* watch: a broken `.agentrc` should degrade to defaults, not turn a CI probe
|
|
495
|
+
* into a crash.
|
|
496
|
+
*
|
|
497
|
+
* @param {{ config?: object, tempRoot?: string, logger: object, flags: object }} params
|
|
498
|
+
* @returns {{ knobs: object, effectiveTempRoot: string, cwd: string }}
|
|
499
|
+
*/
|
|
500
|
+
function resolveWatchContext({ config, tempRoot, logger, flags }) {
|
|
501
|
+
const resolvedConfig =
|
|
502
|
+
config !== undefined ? config : safeResolveConfig(logger);
|
|
503
|
+
const knobs = resolveWatchKnobs({ config: resolvedConfig, flags });
|
|
504
|
+
const effectiveTempRoot =
|
|
505
|
+
tempRoot ?? resolvedConfig?.project?.paths?.tempRoot ?? 'temp';
|
|
506
|
+
return { knobs, effectiveTempRoot, cwd: process.cwd() };
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Build the argument bag for the underlying watch port.
|
|
511
|
+
*
|
|
512
|
+
* Each injectable port is spread in only when supplied so the port keeps its
|
|
513
|
+
* own default — passing `undefined` explicitly would override a default with
|
|
514
|
+
* nothing and break every caller that relies on it.
|
|
515
|
+
*
|
|
516
|
+
* @param {object} params
|
|
517
|
+
* @returns {object}
|
|
518
|
+
*/
|
|
519
|
+
function buildWatchArgs({
|
|
520
|
+
prRef,
|
|
521
|
+
repo,
|
|
522
|
+
cwd,
|
|
523
|
+
knobs,
|
|
524
|
+
ghPrChecksFn,
|
|
525
|
+
ghPrViewFn,
|
|
526
|
+
ghPrUpdateBranchFn,
|
|
527
|
+
sleepFn,
|
|
528
|
+
logger,
|
|
529
|
+
}) {
|
|
530
|
+
return {
|
|
531
|
+
prUrl: prRef,
|
|
532
|
+
repo,
|
|
533
|
+
cwd,
|
|
534
|
+
maxPolls: knobs.maxPolls,
|
|
535
|
+
maxUpdates: knobs.maxUpdates,
|
|
536
|
+
maxResumes: knobs.maxResumes,
|
|
537
|
+
pollIntervalMs: knobs.pollIntervalMs,
|
|
538
|
+
...(ghPrChecksFn ? { ghPrChecksFn } : {}),
|
|
539
|
+
...(ghPrViewFn ? { ghPrViewFn } : {}),
|
|
540
|
+
...(ghPrUpdateBranchFn ? { ghPrUpdateBranchFn } : {}),
|
|
541
|
+
...(sleepFn ? { sleepFn } : {}),
|
|
542
|
+
logger,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* One terminal outcome of the watch: the attach window is spent and no
|
|
548
|
+
* required check ever attached, or the pull request could not be read at all.
|
|
549
|
+
*
|
|
550
|
+
* The two are distinguished structurally — never against `gh`'s stderr prose
|
|
551
|
+
* (Story #4890) — because they route oppositely: CI that has not started is
|
|
552
|
+
* slow (exit 2, keep watching), while an unreadable PR is a `gh` / access
|
|
553
|
+
* fault (exit 1).
|
|
554
|
+
*
|
|
555
|
+
* @param {object} params
|
|
556
|
+
* @returns {number} exit code
|
|
557
|
+
*/
|
|
558
|
+
function reportUnattachedOrError({
|
|
559
|
+
result,
|
|
560
|
+
envelope,
|
|
561
|
+
prNumber,
|
|
562
|
+
knobs,
|
|
563
|
+
logger,
|
|
564
|
+
print,
|
|
565
|
+
}) {
|
|
566
|
+
const notYetStarted = Boolean(
|
|
567
|
+
result.requiredChecksEmpty && result.prResolvable,
|
|
568
|
+
);
|
|
569
|
+
print(
|
|
570
|
+
JSON.stringify({
|
|
571
|
+
...envelope,
|
|
572
|
+
requiredChecksEmpty: Boolean(result.requiredChecksEmpty),
|
|
573
|
+
notYetStarted,
|
|
574
|
+
}),
|
|
575
|
+
);
|
|
576
|
+
if (notYetStarted) {
|
|
577
|
+
logger.warn?.(
|
|
578
|
+
`[pr-watch] no required check has attached to PR #${prNumber} within the ` +
|
|
579
|
+
`${Math.round(knobs.attachWindowMs / 1000)}s attach window (${result.attachRetries} re-resolutions), ` +
|
|
580
|
+
'and the pull request still reads back fine — this is CI that has not started, NOT a red check. ' +
|
|
581
|
+
'Keep polling natively:',
|
|
582
|
+
);
|
|
583
|
+
logger.warn?.('[pr-watch] gh pr checks <pr> --watch');
|
|
584
|
+
return STILL_RUNNING_EXIT_CODE;
|
|
585
|
+
}
|
|
586
|
+
logger.error?.(
|
|
587
|
+
`[pr-watch] could not resolve required checks: ${result.error} — the pull request itself could ` +
|
|
588
|
+
'not be read, so this is a `gh` / access fault rather than CI that has not started.',
|
|
589
|
+
);
|
|
590
|
+
return 1;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Settle a watch whose observed required checks all came back green.
|
|
595
|
+
*
|
|
596
|
+
* Never green on an undercount (Story #4873): the observed set is whatever
|
|
597
|
+
* `gh pr checks --required` returned on the FIRST probe, so a context a
|
|
598
|
+
* ruleset attached later can leave every check we knew about green while
|
|
599
|
+
* GitHub still refuses the merge. An unreconcilable set reports unresolved
|
|
600
|
+
* (exit 2, keep watching) rather than a false green or a false red.
|
|
601
|
+
*
|
|
602
|
+
* @param {object} params
|
|
603
|
+
* @returns {Promise<number>} exit code
|
|
604
|
+
*/
|
|
605
|
+
async function settleGreenWatch({
|
|
606
|
+
result,
|
|
607
|
+
envelope,
|
|
608
|
+
readMergeState,
|
|
609
|
+
storyId,
|
|
610
|
+
prNumber,
|
|
611
|
+
guardPrRef,
|
|
612
|
+
effectiveTempRoot,
|
|
613
|
+
cwd,
|
|
614
|
+
readDigestFn,
|
|
615
|
+
retireDigestFn,
|
|
616
|
+
headShaFn,
|
|
617
|
+
reArmAutoMergeFn,
|
|
618
|
+
blockDeliveryFn,
|
|
619
|
+
logger,
|
|
620
|
+
print,
|
|
621
|
+
}) {
|
|
622
|
+
const reconciliation = reconcileGreenVerdict({
|
|
623
|
+
observedRequired: result.requiredChecks,
|
|
624
|
+
mergeStateStatus: await readMergeState(),
|
|
625
|
+
});
|
|
626
|
+
if (!reconciliation.reconciled) {
|
|
627
|
+
print(JSON.stringify({ ...envelope, reconciliation }));
|
|
628
|
+
logger.warn?.(
|
|
629
|
+
`[pr-watch] withholding the green verdict: ${reconciliation.reason}. ` +
|
|
630
|
+
'Re-run the watch once the repository settles, or inspect branch protection for a context this watch never saw.',
|
|
631
|
+
);
|
|
632
|
+
return STILL_RUNNING_EXIT_CODE;
|
|
633
|
+
}
|
|
634
|
+
const guard = await evaluateGreenWatch({
|
|
635
|
+
storyId,
|
|
636
|
+
prNumber,
|
|
637
|
+
prRef: guardPrRef,
|
|
638
|
+
tempRoot: effectiveTempRoot,
|
|
639
|
+
cwd,
|
|
640
|
+
readDigestFn,
|
|
641
|
+
retireDigestFn,
|
|
642
|
+
headShaFn,
|
|
643
|
+
reArmFn: reArmAutoMergeFn,
|
|
644
|
+
blockFn: blockDeliveryFn,
|
|
645
|
+
logger,
|
|
646
|
+
});
|
|
647
|
+
print(JSON.stringify({ ...envelope, reconciliation, rerunGuard: guard }));
|
|
648
|
+
if (guard.exitCode === 0) {
|
|
649
|
+
logger.info?.('[pr-watch] all required checks green.');
|
|
650
|
+
}
|
|
651
|
+
return guard.exitCode;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Slow-but-not-red: the poll cap AND the resume budget are exhausted with
|
|
656
|
+
* checks still pending and none failed. Never exit 1, never `timed_out` —
|
|
657
|
+
* hand off to the host's interval loop and exit 2.
|
|
658
|
+
*
|
|
659
|
+
* @param {object} params
|
|
660
|
+
* @returns {number} exit code
|
|
661
|
+
*/
|
|
662
|
+
function reportStillRunning({ result, envelope, logger, print }) {
|
|
663
|
+
print(JSON.stringify(envelope));
|
|
664
|
+
const stillPending = Object.entries(result.outcomes)
|
|
665
|
+
.filter(([, v]) => v === 'still-running')
|
|
666
|
+
.map(([k]) => k)
|
|
667
|
+
.join(', ');
|
|
668
|
+
logger.warn?.(
|
|
669
|
+
`[pr-watch] required check(s) still running after ${result.polls} polls + ${result.resumesApplied} resumes: ${stillPending}. Keep polling natively:`,
|
|
670
|
+
);
|
|
671
|
+
logger.warn?.('[pr-watch] gh pr checks <pr> --watch');
|
|
672
|
+
return STILL_RUNNING_EXIT_CODE;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* The failing checks, excluding every non-failing state **and**
|
|
677
|
+
* `still-running`: when the cap fires with a mixed failed+pending map,
|
|
678
|
+
* `promotePendingToStillRunning` has rewritten the pending entries, and a
|
|
679
|
+
* still-running check is slow, not red — including it here would let it become
|
|
680
|
+
* the digest's "primary" failing check and mispoint the diagnosis.
|
|
681
|
+
*
|
|
682
|
+
* @param {Record<string, string>} outcomes
|
|
683
|
+
* @returns {Array<{ name: string, outcome: string }>}
|
|
684
|
+
*/
|
|
685
|
+
function collectFailures(outcomes) {
|
|
686
|
+
return Object.entries(outcomes)
|
|
687
|
+
.filter(
|
|
688
|
+
([, v]) =>
|
|
689
|
+
v !== 'success' &&
|
|
690
|
+
v !== 'neutral' &&
|
|
691
|
+
v !== 'skipped' &&
|
|
692
|
+
v !== 'still-running',
|
|
693
|
+
)
|
|
694
|
+
.map(([name, outcome]) => ({ name, outcome }));
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Genuine red check — exit 1 immediately, disarm auto-merge, write the digest,
|
|
699
|
+
* and surface the fix-loop handoff.
|
|
700
|
+
*
|
|
701
|
+
* @param {object} params
|
|
702
|
+
* @returns {Promise<number>} exit code
|
|
703
|
+
*/
|
|
704
|
+
async function settleRedWatch({
|
|
705
|
+
result,
|
|
706
|
+
envelope,
|
|
707
|
+
storyId,
|
|
708
|
+
prNumber,
|
|
709
|
+
guardPrRef,
|
|
710
|
+
effectiveTempRoot,
|
|
711
|
+
cwd,
|
|
712
|
+
writeDigestFn,
|
|
713
|
+
headShaFn,
|
|
714
|
+
disarmAutoMergeFn,
|
|
715
|
+
blockDeliveryFn,
|
|
716
|
+
logger,
|
|
717
|
+
print,
|
|
718
|
+
}) {
|
|
719
|
+
const failures = collectFailures(result.outcomes);
|
|
720
|
+
const red = failures.map((f) => `${f.name}=${f.outcome}`).join(', ');
|
|
721
|
+
logger.error?.(`[pr-watch] required check(s) not green: ${red}`);
|
|
722
|
+
const redOutcome = await handleRedWatch({
|
|
723
|
+
storyId,
|
|
724
|
+
prNumber,
|
|
725
|
+
prRef: guardPrRef,
|
|
726
|
+
failures,
|
|
727
|
+
tempRoot: effectiveTempRoot,
|
|
728
|
+
cwd,
|
|
729
|
+
writeDigestFn,
|
|
730
|
+
headShaFn,
|
|
731
|
+
disarmFn: disarmAutoMergeFn,
|
|
732
|
+
blockFn: blockDeliveryFn,
|
|
733
|
+
logger,
|
|
734
|
+
});
|
|
735
|
+
print(
|
|
736
|
+
JSON.stringify({
|
|
737
|
+
...envelope,
|
|
738
|
+
classification: classifyFailure(failures[0]?.name),
|
|
739
|
+
rerunGuard: {
|
|
740
|
+
verdict: 'red',
|
|
741
|
+
headSha: redOutcome.headSha,
|
|
742
|
+
autoMergeDisarmed: redOutcome.disarm.disarmed,
|
|
743
|
+
disarmDetail: redOutcome.disarm.detail,
|
|
744
|
+
digestPath: redOutcome.digestPaths?.jsonPath ?? null,
|
|
745
|
+
blocked: redOutcome.blocked,
|
|
746
|
+
},
|
|
747
|
+
}),
|
|
748
|
+
);
|
|
749
|
+
logger.error?.(
|
|
750
|
+
'[pr-watch] a required check failed. Read the digest, reproduce the failure, and apply the smallest fix at source, ' +
|
|
751
|
+
'then push a new commit — re-running the failed job is forbidden (`.agents/rules/ci-remediation.md` § Verifier).',
|
|
752
|
+
);
|
|
753
|
+
return 1;
|
|
754
|
+
}
|
|
755
|
+
|
|
490
756
|
/**
|
|
491
757
|
* Run the watch loop and resolve to the exit code. Exported for tests so
|
|
492
758
|
* the green / red / still-running / BEHIND paths can be exercised with
|
|
@@ -557,21 +823,12 @@ export async function runPrWatch({
|
|
|
557
823
|
if (!Number.isInteger(prNumber) || prNumber < 1)
|
|
558
824
|
throw new TypeError('runPrWatch: --pr requires a positive integer');
|
|
559
825
|
|
|
560
|
-
const
|
|
561
|
-
config
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
flags: {
|
|
565
|
-
pollIntervalMs,
|
|
566
|
-
maxPolls,
|
|
567
|
-
maxResumes,
|
|
568
|
-
maxUpdates,
|
|
569
|
-
attachWindowMs,
|
|
570
|
-
},
|
|
826
|
+
const { knobs, effectiveTempRoot, cwd } = resolveWatchContext({
|
|
827
|
+
config,
|
|
828
|
+
tempRoot,
|
|
829
|
+
logger,
|
|
830
|
+
flags: { pollIntervalMs, maxPolls, maxResumes, maxUpdates, attachWindowMs },
|
|
571
831
|
});
|
|
572
|
-
const effectiveTempRoot =
|
|
573
|
-
tempRoot ?? resolvedConfig?.project?.paths?.tempRoot ?? 'temp';
|
|
574
|
-
const cwd = process.cwd();
|
|
575
832
|
|
|
576
833
|
// `gh` has NO `<owner/repo>#<number>` argument form — it parses that string
|
|
577
834
|
// as a BRANCH NAME, which is why every `--repo` invocation used to fail at
|
|
@@ -596,20 +853,17 @@ export async function runPrWatch({
|
|
|
596
853
|
probeMergeState({ prRef: guardPrRef, prUrl: prRef, repo, cwd, prNumber });
|
|
597
854
|
|
|
598
855
|
const result = await watchWithAttachWindow({
|
|
599
|
-
watchArgs: {
|
|
600
|
-
|
|
856
|
+
watchArgs: buildWatchArgs({
|
|
857
|
+
prRef,
|
|
601
858
|
repo,
|
|
602
859
|
cwd,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
...(ghPrViewFn ? { ghPrViewFn } : {}),
|
|
609
|
-
...(ghPrUpdateBranchFn ? { ghPrUpdateBranchFn } : {}),
|
|
610
|
-
...(sleepFn ? { sleepFn } : {}),
|
|
860
|
+
knobs,
|
|
861
|
+
ghPrChecksFn,
|
|
862
|
+
ghPrViewFn,
|
|
863
|
+
ghPrUpdateBranchFn,
|
|
864
|
+
sleepFn,
|
|
611
865
|
logger,
|
|
612
|
-
},
|
|
866
|
+
}),
|
|
613
867
|
attachWindowMs: knobs.attachWindowMs,
|
|
614
868
|
retryIntervalMs: knobs.pollIntervalMs,
|
|
615
869
|
sleepFn: sleepFn ?? defaultSleep,
|
|
@@ -637,141 +891,55 @@ export async function runPrWatch({
|
|
|
637
891
|
};
|
|
638
892
|
|
|
639
893
|
if (result.requiredChecksEmpty || result.error) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
requiredChecksEmpty: Boolean(result.requiredChecksEmpty),
|
|
649
|
-
notYetStarted,
|
|
650
|
-
}),
|
|
651
|
-
);
|
|
652
|
-
if (notYetStarted) {
|
|
653
|
-
logger.warn?.(
|
|
654
|
-
`[pr-watch] no required check has attached to PR #${prNumber} within the ` +
|
|
655
|
-
`${Math.round(knobs.attachWindowMs / 1000)}s attach window (${result.attachRetries} re-resolutions), ` +
|
|
656
|
-
'and the pull request still reads back fine — this is CI that has not started, NOT a red check. ' +
|
|
657
|
-
'Keep polling natively:',
|
|
658
|
-
);
|
|
659
|
-
logger.warn?.('[pr-watch] gh pr checks <pr> --watch');
|
|
660
|
-
return STILL_RUNNING_EXIT_CODE;
|
|
661
|
-
}
|
|
662
|
-
logger.error?.(
|
|
663
|
-
`[pr-watch] could not resolve required checks: ${result.error} — the pull request itself could ` +
|
|
664
|
-
'not be read, so this is a `gh` / access fault rather than CI that has not started.',
|
|
665
|
-
);
|
|
666
|
-
return 1;
|
|
894
|
+
return reportUnattachedOrError({
|
|
895
|
+
result,
|
|
896
|
+
envelope,
|
|
897
|
+
prNumber,
|
|
898
|
+
knobs,
|
|
899
|
+
logger,
|
|
900
|
+
print,
|
|
901
|
+
});
|
|
667
902
|
}
|
|
668
903
|
|
|
669
904
|
if (result.green) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// repository's own verdict before any green is issued — an unreconcilable
|
|
675
|
-
// set reports unresolved (exit 2, keep watching) rather than a false green
|
|
676
|
-
// or a false red.
|
|
677
|
-
const reconciliation = reconcileGreenVerdict({
|
|
678
|
-
observedRequired: result.requiredChecks,
|
|
679
|
-
mergeStateStatus: await readMergeState(),
|
|
680
|
-
});
|
|
681
|
-
if (!reconciliation.reconciled) {
|
|
682
|
-
print(JSON.stringify({ ...envelope, reconciliation }));
|
|
683
|
-
logger.warn?.(
|
|
684
|
-
`[pr-watch] withholding the green verdict: ${reconciliation.reason}. ` +
|
|
685
|
-
'Re-run the watch once the repository settles, or inspect branch protection for a context this watch never saw.',
|
|
686
|
-
);
|
|
687
|
-
return STILL_RUNNING_EXIT_CODE;
|
|
688
|
-
}
|
|
689
|
-
const guard = await evaluateGreenWatch({
|
|
905
|
+
return await settleGreenWatch({
|
|
906
|
+
result,
|
|
907
|
+
envelope,
|
|
908
|
+
readMergeState,
|
|
690
909
|
storyId,
|
|
691
910
|
prNumber,
|
|
692
|
-
|
|
693
|
-
|
|
911
|
+
guardPrRef,
|
|
912
|
+
effectiveTempRoot,
|
|
694
913
|
cwd,
|
|
695
914
|
readDigestFn,
|
|
696
915
|
retireDigestFn,
|
|
697
916
|
headShaFn,
|
|
698
|
-
|
|
699
|
-
|
|
917
|
+
reArmAutoMergeFn,
|
|
918
|
+
blockDeliveryFn,
|
|
700
919
|
logger,
|
|
920
|
+
print,
|
|
701
921
|
});
|
|
702
|
-
print(JSON.stringify({ ...envelope, reconciliation, rerunGuard: guard }));
|
|
703
|
-
if (guard.exitCode === 0) {
|
|
704
|
-
logger.info?.('[pr-watch] all required checks green.');
|
|
705
|
-
}
|
|
706
|
-
return guard.exitCode;
|
|
707
922
|
}
|
|
708
923
|
|
|
709
|
-
// Slow-but-not-red: the cap AND resume budget are exhausted with checks
|
|
710
|
-
// still pending and none failed. Never exit 1, never `timed_out` — hand
|
|
711
|
-
// off to the host's interval loop and exit 2.
|
|
712
924
|
if (result.stillRunning) {
|
|
713
|
-
|
|
714
|
-
const stillPending = Object.entries(result.outcomes)
|
|
715
|
-
.filter(([, v]) => v === 'still-running')
|
|
716
|
-
.map(([k]) => k)
|
|
717
|
-
.join(', ');
|
|
718
|
-
logger.warn?.(
|
|
719
|
-
`[pr-watch] required check(s) still running after ${result.polls} polls + ${result.resumesApplied} resumes: ${stillPending}. Keep polling natively:`,
|
|
720
|
-
);
|
|
721
|
-
logger.warn?.('[pr-watch] gh pr checks <pr> --watch');
|
|
722
|
-
return STILL_RUNNING_EXIT_CODE;
|
|
925
|
+
return reportStillRunning({ result, envelope, logger, print });
|
|
723
926
|
}
|
|
724
927
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
// fires with a mixed failed+pending map, promotePendingToStillRunning has
|
|
729
|
-
// rewritten the pending entries, and a still-running check is slow, not
|
|
730
|
-
// red — including it here would let it become the digest's "primary"
|
|
731
|
-
// failing check and mispoint the diagnosis at a slow check.
|
|
732
|
-
const failures = Object.entries(result.outcomes)
|
|
733
|
-
.filter(
|
|
734
|
-
([, v]) =>
|
|
735
|
-
v !== 'success' &&
|
|
736
|
-
v !== 'neutral' &&
|
|
737
|
-
v !== 'skipped' &&
|
|
738
|
-
v !== 'still-running',
|
|
739
|
-
)
|
|
740
|
-
.map(([name, outcome]) => ({ name, outcome }));
|
|
741
|
-
const red = failures.map((f) => `${f.name}=${f.outcome}`).join(', ');
|
|
742
|
-
logger.error?.(`[pr-watch] required check(s) not green: ${red}`);
|
|
743
|
-
const redOutcome = await handleRedWatch({
|
|
928
|
+
return await settleRedWatch({
|
|
929
|
+
result,
|
|
930
|
+
envelope,
|
|
744
931
|
storyId,
|
|
745
932
|
prNumber,
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
tempRoot: effectiveTempRoot,
|
|
933
|
+
guardPrRef,
|
|
934
|
+
effectiveTempRoot,
|
|
749
935
|
cwd,
|
|
750
936
|
writeDigestFn,
|
|
751
937
|
headShaFn,
|
|
752
|
-
|
|
753
|
-
|
|
938
|
+
disarmAutoMergeFn,
|
|
939
|
+
blockDeliveryFn,
|
|
754
940
|
logger,
|
|
941
|
+
print,
|
|
755
942
|
});
|
|
756
|
-
print(
|
|
757
|
-
JSON.stringify({
|
|
758
|
-
...envelope,
|
|
759
|
-
classification: classifyFailure(failures[0]?.name),
|
|
760
|
-
rerunGuard: {
|
|
761
|
-
verdict: 'red',
|
|
762
|
-
headSha: redOutcome.headSha,
|
|
763
|
-
autoMergeDisarmed: redOutcome.disarm.disarmed,
|
|
764
|
-
disarmDetail: redOutcome.disarm.detail,
|
|
765
|
-
digestPath: redOutcome.digestPaths?.jsonPath ?? null,
|
|
766
|
-
blocked: redOutcome.blocked,
|
|
767
|
-
},
|
|
768
|
-
}),
|
|
769
|
-
);
|
|
770
|
-
logger.error?.(
|
|
771
|
-
'[pr-watch] a required check failed. Read the digest, reproduce the failure, and apply the smallest fix at source, ' +
|
|
772
|
-
'then push a new commit — re-running the failed job is forbidden (`.agents/rules/ci-remediation.md` § Verifier).',
|
|
773
|
-
);
|
|
774
|
-
return 1;
|
|
775
943
|
}
|
|
776
944
|
|
|
777
945
|
/** Resolve config without letting a config error abort the watch. */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Story #3822 — single source of truth for the post-create board-add
|
|
5
5
|
* step. Issues created through any create path (`createTicket`,
|
|
6
|
-
* `createIssue` — which backs `/plan` persist and the `/plan`
|
|
6
|
+
* `createIssue` — which backs `/mandrel-plan` persist and the `/mandrel-plan`
|
|
7
7
|
* Phase 4 Epic open) must land on the configured Projects V2 board
|
|
8
8
|
* without relying on GitHub's "Auto-add to project" built-in workflow,
|
|
9
9
|
* which is off by default on fresh boards and cannot be enabled via API.
|
|
@@ -156,7 +156,7 @@ export function classifyGithubError(err) {
|
|
|
156
156
|
// Read-path callers (paginateRest, getTicket, getNativeSubIssues, …) all
|
|
157
157
|
// absorb the same jittered exponential backoff on transient GitHub errors
|
|
158
158
|
// through this one primitive, instead of bubbling a one-shot
|
|
159
|
-
// 502/429/ECONNRESET that kills a longer pipeline (e.g. the /deliver
|
|
159
|
+
// 502/429/ECONNRESET that kills a longer pipeline (e.g. the /mandrel-deliver
|
|
160
160
|
// Phase E retro). The network consumers repointed here
|
|
161
161
|
// (branch-protection, labels, projects-v2-graphql) call with no opts, so
|
|
162
162
|
// they adopt these defaults; their retry *classes* (the network blips) are
|