@dev-loops/core 1.0.1 → 1.0.2-slim.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/package.json +5 -2
- package/src/analysis/diff-analyzer.mjs +85 -137
- package/src/claude/asset-generation.mjs +7 -7
- package/src/claude/hook-decisions.mjs +167 -50
- package/src/config/config.mjs +388 -787
- package/src/config/extension-defaults.yaml +14 -9
- package/src/github/comment-id-guard.mjs +39 -1
- package/src/github/copilot-helpers.mjs +90 -158
- package/src/github/gh.mjs +49 -0
- package/src/loop/bash-command-classify.mjs +34 -49
- package/src/loop/commit-msg-guard.mjs +1 -1
- package/src/loop/conductor-routing.mjs +15 -23
- package/src/loop/copilot-loop-state.mjs +46 -94
- package/src/loop/gate-carry-forward.mjs +46 -22
- package/src/loop/gate-evidence-reconcile.mjs +75 -0
- package/src/loop/gate-fanin.mjs +266 -435
- package/src/loop/handoff-envelope.mjs +21 -21
- package/src/loop/issue-refinement-artifact.mjs +449 -284
- package/src/loop/lifecycle-state.mjs +10 -21
- package/src/loop/pr-gate-coordination.mjs +49 -49
- package/src/loop/queue-board-sync.mjs +16 -82
- package/src/loop/review-dispatch-plan.mjs +61 -122
- package/src/loop/review-lineage.mjs +19 -44
- package/src/loop/spec-authority.mjs +729 -0
- package/src/loop/steering.mjs +16 -68
- package/src/loop/ui-e2e-scoping.mjs +1 -0
- package/src/loop/worktree-guard.mjs +55 -0
- package/src/projects/list-queue-items.mjs +16 -175
- package/src/projects/move-queue-item.mjs +16 -171
- package/src/projects/projects-access.mjs +202 -0
- package/src/security/secret-scan.mjs +13 -1
|
@@ -123,7 +123,7 @@ register(INTERNAL_DEV_LOOP_STRATEGY.FINAL_APPROVAL, "default", {
|
|
|
123
123
|
register(INTERNAL_DEV_LOOP_STRATEGY.LOCAL_IMPLEMENTATION, "default", {
|
|
124
124
|
criteria: [
|
|
125
125
|
{ id: "phase-ac", must: "All phase acceptance criteria from the active phase doc are satisfied.", severity: "required" },
|
|
126
|
-
{ id: "verify-green", must: "`
|
|
126
|
+
{ id: "verify-green", must: "`bun run verify` passes with no failures.", severity: "required" },
|
|
127
127
|
],
|
|
128
128
|
evidence: ["commands-run", "validation-output", "changed-files"],
|
|
129
129
|
maxFinalizationTurns: 6,
|
|
@@ -131,14 +131,14 @@ register(INTERNAL_DEV_LOOP_STRATEGY.LOCAL_IMPLEMENTATION, "default", {
|
|
|
131
131
|
activeNoticeAfterMs: DEFAULT_ACTIVE_NOTICE_MS,
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
// local_implementation · spike run (SPIKE-RELAXED-GATE-PROFILE
|
|
134
|
+
// local_implementation · spike run (SPIKE-RELAXED-GATE-PROFILE): a
|
|
135
135
|
// spike-mode spin resolves the relaxed `spike` gate profile instead of the
|
|
136
136
|
// default local-implementation gate. Kept as its own acceptance key so the
|
|
137
137
|
// generic default can stay approach-agnostic.
|
|
138
138
|
register(INTERNAL_DEV_LOOP_STRATEGY.LOCAL_IMPLEMENTATION, "spike", {
|
|
139
139
|
criteria: [
|
|
140
140
|
{ id: "spike-recorded", must: "The spike exploration and its recommendation are recorded (spike file + summary).", severity: "required" },
|
|
141
|
-
{ id: "verify-green", must: "`
|
|
141
|
+
{ id: "verify-green", must: "`bun run verify` passes with no failures.", severity: "required" },
|
|
142
142
|
],
|
|
143
143
|
evidence: ["commands-run", "validation-output", "changed-files"],
|
|
144
144
|
maxFinalizationTurns: 6,
|
|
@@ -305,7 +305,7 @@ function deriveRequiredReads(bundle, resolverOutput) {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
// ---------------------------------------------------------------------------
|
|
308
|
-
// specSource derivation
|
|
308
|
+
// specSource derivation — lightweight PR-body-as-spec
|
|
309
309
|
// ---------------------------------------------------------------------------
|
|
310
310
|
|
|
311
311
|
/**
|
|
@@ -440,7 +440,7 @@ function deriveCwd(bundle, options = {}) {
|
|
|
440
440
|
}
|
|
441
441
|
|
|
442
442
|
/** Repo-relative root for loop-owned worktrees. The `dev-loops/` namespace */
|
|
443
|
-
/** marks them so cleanup can only ever remove its own
|
|
443
|
+
/** marks them so cleanup can only ever remove its own. */
|
|
444
444
|
export const WORKTREE_NAMESPACE = "tmp/worktrees/dev-loops";
|
|
445
445
|
|
|
446
446
|
/**
|
|
@@ -470,7 +470,7 @@ function flattenSlugSegment(s) {
|
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
function buildWorktreeSlug(artifact, kind) {
|
|
473
|
-
// Canonical naming is namespaced + no branch suffix
|
|
473
|
+
// Canonical naming is namespaced + no branch suffix so the path
|
|
474
474
|
// is recomputable from the issue/PR number alone (cleanup can find it).
|
|
475
475
|
if (kind === DEV_LOOP_TARGET_KIND.ISSUE && Number.isInteger(artifact.issue) && artifact.issue > 0) {
|
|
476
476
|
return `issue-${artifact.issue}`;
|
|
@@ -511,7 +511,7 @@ function normalizeGateState(gateState) {
|
|
|
511
511
|
|
|
512
512
|
|
|
513
513
|
/**
|
|
514
|
-
* Normalize the structured retrospective findings
|
|
514
|
+
* Normalize the structured retrospective findings.
|
|
515
515
|
*
|
|
516
516
|
* The retrospective is advisory: it never blocks merge or any lifecycle
|
|
517
517
|
* transition. Its findings travel in the handoff envelope (the conductor's
|
|
@@ -553,7 +553,7 @@ function resolveSubGate(strategy, gateState) {
|
|
|
553
553
|
return "default";
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
-
/** True when the resolver output identifies a spike-mode run
|
|
556
|
+
/** True when the resolver output identifies a spike-mode run. */
|
|
557
557
|
function isSpikeRun(resolverOutput) {
|
|
558
558
|
return Boolean(resolverOutput && resolverOutput.spikeIntakeState);
|
|
559
559
|
}
|
|
@@ -593,7 +593,7 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
593
593
|
if (!repo) throw new Error("handoff-envelope: repo slug is required (owner/name)");
|
|
594
594
|
|
|
595
595
|
const gs = normalizeGateState(gateState);
|
|
596
|
-
// SPIKE-RELAXED-GATE-PROFILE
|
|
596
|
+
// SPIKE-RELAXED-GATE-PROFILE: a spike-mode spin (startup resolver
|
|
597
597
|
// result carrying `spikeIntakeState`) resolves the relaxed `spike` gate
|
|
598
598
|
// profile instead of the default local-implementation gate. The spike
|
|
599
599
|
// marker lives at the TOP level of the resolver output (the bundle does not
|
|
@@ -603,7 +603,7 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
603
603
|
: resolveSubGate(strategy, gs);
|
|
604
604
|
// Normalize each source independently, then fall back on the normalized result
|
|
605
605
|
// (not the raw value): a present-but-invalid gateState value must NOT shadow a
|
|
606
|
-
// valid options.retrospectiveFindings fallback
|
|
606
|
+
// valid options.retrospectiveFindings fallback.
|
|
607
607
|
const retrospectiveFindings = normalizeRetrospectiveFindings(gateState?.retrospectiveFindings)
|
|
608
608
|
?? normalizeRetrospectiveFindings(options.retrospectiveFindings);
|
|
609
609
|
|
|
@@ -613,7 +613,7 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
613
613
|
const gateConfig = deriveGateConfig(settings, subGate);
|
|
614
614
|
const derivedCwd = deriveCwd(bundle, { repoRoot: options.repoRoot, worktreeCwd: options.worktreeCwd });
|
|
615
615
|
const template = lookupAcceptanceTemplate(strategy, subGate);
|
|
616
|
-
// Lightweight PR-body-as-spec
|
|
616
|
+
// Lightweight PR-body-as-spec: retarget the phase-doc criterion
|
|
617
617
|
// text to the PR description. Null/phase_doc leaves the criteria untouched, so
|
|
618
618
|
// the non-lightweight path stays byte-identical.
|
|
619
619
|
const specSource = deriveSpecSource(bundle, resolverOutput);
|
|
@@ -623,7 +623,7 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
623
623
|
? { ...options.overrides }
|
|
624
624
|
: undefined;
|
|
625
625
|
|
|
626
|
-
// Sanctioned operation → wrapper command map
|
|
626
|
+
// Sanctioned operation → wrapper command map. Core is
|
|
627
627
|
// consumer-agnostic: it carries whatever map the consumer supplies (the
|
|
628
628
|
// `loop build-envelope` CLI injects this repo's scripts/... paths) so every
|
|
629
629
|
// spawned subagent receives it by DEFAULT. Core defines the SHAPE only —
|
|
@@ -632,7 +632,7 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
632
632
|
? options.sanctionedCommands
|
|
633
633
|
: undefined;
|
|
634
634
|
|
|
635
|
-
// Surface the *effective* async-start posture alongside the *configured* one
|
|
635
|
+
// Surface the *effective* async-start posture alongside the *configured* one. The
|
|
636
636
|
// configured `asyncStartMode` is echoed verbatim from settings (back-compat), but the contract
|
|
637
637
|
// is relaxed at validation time under the Claude harness (resolveEffectiveAsyncStartMode →
|
|
638
638
|
// "allowed" when CLAUDECODE=1). Without surfacing the effective value, a `required` envelope
|
|
@@ -686,21 +686,21 @@ export function buildDevLoopHandoffEnvelope(resolverOutput, settings, gateState
|
|
|
686
686
|
envelope.sanctionedCommands = sanctionedCommands;
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
-
// Advisory retrospective findings
|
|
689
|
+
// Advisory retrospective findings. Optional structured
|
|
690
690
|
// field carrying the check-retro-tooling.mjs JSON output to the conductor. Never a
|
|
691
691
|
// gate — the conductor surfaces these as an advisory PR comment, not a block.
|
|
692
692
|
if (retrospectiveFindings) {
|
|
693
693
|
envelope.retrospectiveFindings = retrospectiveFindings;
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
-
// Canonical spec source
|
|
696
|
+
// Canonical spec source. Optional: only set when the resolver
|
|
697
697
|
// marks a lightweight PR-body-as-spec session, so the default (phase-doc) path
|
|
698
698
|
// carries no specSource field and its envelope stays byte-identical.
|
|
699
699
|
if (specSource) {
|
|
700
700
|
envelope.specSource = specSource;
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
-
//
|
|
703
|
+
// The ONLY per-round-varying block, kept LAST. Every field here changes
|
|
704
704
|
// between builds/rounds (the timestamp, the head SHA, CI status, thread/round
|
|
705
705
|
// counts); isolating them as the envelope's tail keeps everything above a
|
|
706
706
|
// byte-stable prefix that a fresh reviewer spawn can cache-READ instead of
|
|
@@ -931,7 +931,7 @@ export function validateHandoffEnvelope(envelope) {
|
|
|
931
931
|
});
|
|
932
932
|
}
|
|
933
933
|
|
|
934
|
-
// ----- asyncStartEffective (required field; the harness-resolved posture
|
|
934
|
+
// ----- asyncStartEffective (required field; the harness-resolved posture) -----
|
|
935
935
|
if (envelope.asyncStartEffective === undefined || envelope.asyncStartEffective === null) {
|
|
936
936
|
errors.push({
|
|
937
937
|
field: "asyncStartEffective",
|
|
@@ -946,7 +946,7 @@ export function validateHandoffEnvelope(envelope) {
|
|
|
946
946
|
});
|
|
947
947
|
}
|
|
948
948
|
|
|
949
|
-
// ----- retrospectiveFindings (optional, advisory
|
|
949
|
+
// ----- retrospectiveFindings (optional, advisory) -----
|
|
950
950
|
if (envelope.retrospectiveFindings !== undefined && envelope.retrospectiveFindings !== null) {
|
|
951
951
|
const rf = envelope.retrospectiveFindings;
|
|
952
952
|
if (typeof rf !== "object" || Array.isArray(rf)) {
|
|
@@ -968,7 +968,7 @@ export function validateHandoffEnvelope(envelope) {
|
|
|
968
968
|
}
|
|
969
969
|
}
|
|
970
970
|
|
|
971
|
-
// ----- specSource (optional —
|
|
971
|
+
// ----- specSource (optional — lightweight PR-body-as-spec) -----
|
|
972
972
|
if (envelope.specSource !== undefined && envelope.specSource !== null) {
|
|
973
973
|
const validSources = [CANONICAL_SPEC_SOURCE.PHASE_DOC, CANONICAL_SPEC_SOURCE.PR_BODY];
|
|
974
974
|
if (typeof envelope.specSource !== "string" || !validSources.includes(envelope.specSource)) {
|
|
@@ -980,8 +980,8 @@ export function validateHandoffEnvelope(envelope) {
|
|
|
980
980
|
}
|
|
981
981
|
}
|
|
982
982
|
|
|
983
|
-
// ----- gateState.derivedAt (informational, warn on missing) —
|
|
984
|
-
// volatile timestamp
|
|
983
|
+
// ----- gateState.derivedAt (informational, warn on missing) — the
|
|
984
|
+
// volatile timestamp lives in the gateState tail so the rest stays byte-stable -----
|
|
985
985
|
if (typeof envelope.gateState?.derivedAt !== "string" || !envelope.gateState.derivedAt.trim()) {
|
|
986
986
|
warnings.push({ field: "gateState.derivedAt", reason: "should be an ISO 8601 timestamp" });
|
|
987
987
|
}
|