audit-tools 0.50.9 → 0.50.11

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.
Files changed (49) hide show
  1. package/dist/remediate/index.d.ts.map +1 -1
  2. package/dist/remediate/index.js +2 -2
  3. package/dist/remediate/index.js.map +1 -1
  4. package/dist/remediate/phases/close.d.ts.map +1 -1
  5. package/dist/remediate/phases/close.js +13 -0
  6. package/dist/remediate/phases/close.js.map +1 -1
  7. package/dist/remediate/phases/grounding.d.ts +6 -5
  8. package/dist/remediate/phases/grounding.d.ts.map +1 -1
  9. package/dist/remediate/phases/grounding.js +11 -9
  10. package/dist/remediate/phases/grounding.js.map +1 -1
  11. package/dist/remediate/phases/triage.d.ts.map +1 -1
  12. package/dist/remediate/phases/triage.js +14 -16
  13. package/dist/remediate/phases/triage.js.map +1 -1
  14. package/dist/remediate/steps/contractPipeline.js +11 -11
  15. package/dist/remediate/steps/contractPipeline.js.map +1 -1
  16. package/dist/remediate/steps/dispatch/hostHandoff.d.ts +19 -0
  17. package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
  18. package/dist/remediate/steps/dispatch/hostHandoff.js +94 -30
  19. package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
  20. package/dist/remediate/steps/nextStep.d.ts +1 -1
  21. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  22. package/dist/remediate/steps/nextStep.js +62 -84
  23. package/dist/remediate/steps/nextStep.js.map +1 -1
  24. package/dist/remediate/steps/stepUtils.d.ts +1 -37
  25. package/dist/remediate/steps/stepUtils.d.ts.map +1 -1
  26. package/dist/remediate/steps/stepUtils.js +0 -79
  27. package/dist/remediate/steps/stepUtils.js.map +1 -1
  28. package/dist/remediate/validation/artifacts.js +1 -1
  29. package/dist/remediate/validation/artifacts.js.map +1 -1
  30. package/dist/remediate/validation/contractPipelineGates.d.ts +14 -6
  31. package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
  32. package/dist/remediate/validation/contractPipelineGates.js +35 -36
  33. package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
  34. package/dist/shared/analyzers/acquisitionEngine.d.ts +0 -29
  35. package/dist/shared/analyzers/acquisitionEngine.d.ts.map +1 -1
  36. package/dist/shared/analyzers/acquisitionEngine.js +13 -1
  37. package/dist/shared/analyzers/acquisitionEngine.js.map +1 -1
  38. package/dist/shared/analyzers/binaryAcquisition.d.ts.map +1 -1
  39. package/dist/shared/analyzers/binaryAcquisition.js +6 -2
  40. package/dist/shared/analyzers/binaryAcquisition.js.map +1 -1
  41. package/dist/shared/tooling/commandShape.js +5 -5
  42. package/dist/shared/tooling/exec.d.ts.map +1 -1
  43. package/dist/shared/tooling/exec.js +14 -0
  44. package/dist/shared/tooling/exec.js.map +1 -1
  45. package/dist/shared/validation/findingGrounding.d.ts +7 -2
  46. package/dist/shared/validation/findingGrounding.d.ts.map +1 -1
  47. package/dist/shared/validation/findingGrounding.js +15 -18
  48. package/dist/shared/validation/findingGrounding.js.map +1 -1
  49. package/package.json +1 -1
@@ -15,12 +15,11 @@ import { runTriagePhase } from "../phases/triage.js";
15
15
  import { runClosePhase } from "../phases/close.js";
16
16
  import { validateRemediationPlan } from "../validation/remediationState.js";
17
17
  import { readExtractedPlanIfPresent, } from "./dispatch.js";
18
- import { ingestRemediationHostResults, hostDependencyLevels, precomputeRecoveryTestVerdicts, prepareRemediationHostHandoff, } from "./dispatch/hostHandoff.js";
18
+ import { ingestRemediationHostResults, hostDependencyLevels, permanentlyDeadPendingBlocks, precomputeRecoveryTestVerdicts, prepareRemediationHostHandoff, } from "./dispatch/hostHandoff.js";
19
19
  import { FileLockTimeoutError, withFileLock, } from "../../shared/io/fileLock.js";
20
20
  import { AUDIT_FINDINGS_FILENAME, AUDIT_REPORT_FILENAME, auditArtifactsDir, auditFindingsPath, auditReportPath, promotedAuditFindingsPath, promotedAuditReportPath, remediationArtifactsDir, } from "../../shared/io/auditToolsPaths.js";
21
21
  import { callerWorkingDirectory, discoverRepoRoot, resolveRepoRoot, } from "../../shared/io/repoRoot.js";
22
22
  import { writeCurrentStep } from "./stepWriter.js";
23
- import { dependencyAwaitingClarification, dependencyVerifiedComplete, } from "./stepUtils.js";
24
23
  import { isTerminalStatus, isVerifiedCompleteStatus, } from "../state/itemStatus.js";
25
24
  import { deduplicateCrossLensFindings, fixupBlocksAfterDedup, } from "../dedup/crossLensDedup.js";
26
25
  import { checkAffectedFileIntegrity } from "../utils/fileIntegrity.js";
@@ -219,7 +218,7 @@ function suppliedInputMatchesRun(inputResolution, manifest) {
219
218
  return false;
220
219
  return true;
221
220
  }
222
- export { dependencyVerifiedComplete, classifyFindingRisk, } from "./stepUtils.js";
221
+ export { classifyFindingRisk } from "./stepUtils.js";
223
222
  export { isTerminalStatus, isVerifiedCompleteStatus };
224
223
  export { hostDependencyLevels };
225
224
  function documentableFindings(state) {
@@ -228,41 +227,50 @@ function documentableFindings(state) {
228
227
  return state.plan.findings.filter((finding) => state.items?.[finding.id]?.status === "pending");
229
228
  }
230
229
  /**
231
- * Blocks eligible for the next host handoff: those with pending
232
- * work AND every dependency VERIFIED-COMPLETE (INV-RS-01 — a SKIP or blocked
233
- * dependency never makes a dependent eligible). This dependency-frontier gate
234
- * replaced the old `dependenciesSatisfied` (any-terminal) check so a block whose
235
- * prerequisite was skipped/blocked is held back until its required surface lands.
230
+ * The dispatch frontier: the level-0 draw of the SAME dependency/phase
231
+ * partition the host workload builder emits from ({@link hostDependencyLevels}).
232
+ * The guard and the builder reading ONE computation is what keeps the
233
+ * "Cannot prepare an empty remediation host workload" throw unreachable from
234
+ * the scheduler: whenever this is non-empty, the builder derives the identical
235
+ * non-empty set (the 2026-08-23 empty-frontier incident — the retired
236
+ * edge-only predicate ignored the phase barrier and dependency existence, so
237
+ * the guard dispatched frontiers the builder refused, and next-step died
238
+ * instead of pausing).
236
239
  */
237
- function implementableBlocks(state) {
238
- if (!state.plan || !state.items)
239
- return [];
240
- return state.plan.blocks.filter((block) => dependencyVerifiedComplete(block, state) &&
241
- block.items.some((findingId) => {
242
- const item = state.items?.[findingId];
243
- return item?.status === "pending";
244
- }));
240
+ function dispatchFrontier(state) {
241
+ return hostDependencyLevels(state)[0] ?? [];
245
242
  }
246
243
  /**
247
- * Pending nodes that are genuinely DEAD-ENDED: at least one dependency did not
248
- * reach a verified-complete disposition and never will (a prerequisite was
249
- * skipped or blocked, or the edges are cyclic). Once no eligible block remains,
250
- * these are marked `blocked` — their upstream surface never landed — rather than
251
- * looping forever.
252
- *
253
- * A node held only by an UNANSWERED WORKER QUESTION is deliberately excluded
254
- * (`dependencyAwaitingClarification`): since the clarification round is deferred
255
- * to the end of the implement phase, such a node reaches this sweep while its
256
- * answer is still outstanding, and blocking it would report "upstream failed"
257
- * for what is really "awaiting an answer". It stays `pending` and is re-decided
258
- * once the answer lands.
244
+ * The ONE dead-end sweep, shared by the planning→implementing transition and
245
+ * the implementing obligation: marks the pending items of every permanently
246
+ * dead block `blocked` with the INV-RS-01 reason, and reports whether anything
247
+ * changed. Deadness is the workload boundary's own liveness analysis
248
+ * ({@link permanentlyDeadPendingBlocks}), so a node held by an unanswered
249
+ * clarification — or by a lower phase that is merely still working — is NEVER
250
+ * mis-reported as an upstream failure (the 175cfb89 pin).
259
251
  */
260
- function blockedByUnsatisfiedDependency(state) {
261
- if (!state.plan || !state.items)
262
- return [];
263
- return state.plan.blocks.filter((block) => !dependencyVerifiedComplete(block, state) &&
264
- !dependencyAwaitingClarification(block, state) &&
265
- block.items.some((findingId) => state.items?.[findingId]?.status === "pending"));
252
+ function sweepPermanentlyDeadBlocks(state) {
253
+ let changed = false;
254
+ const now = new Date().toISOString();
255
+ for (const block of permanentlyDeadPendingBlocks(state)) {
256
+ for (const findingId of block.items) {
257
+ const it = state.items?.[findingId];
258
+ if (!it || it.status !== "pending")
259
+ continue;
260
+ it.status = "blocked";
261
+ it.started_at ??= now;
262
+ it.completed_at = now;
263
+ it.failure_reason =
264
+ it.failure_reason ??
265
+ "A prerequisite can never reach a verified-complete disposition " +
266
+ "(a dependency or lower-phase block was skipped, blocked, or abandoned; " +
267
+ "a declared dependency resolves to no block; or the dependencies are " +
268
+ "cyclic); the host handoff will not expose this node against an " +
269
+ "upstream surface that never landed (INV-RS-01).";
270
+ changed = true;
271
+ }
272
+ }
273
+ return changed;
266
274
  }
267
275
  /**
268
276
  * Whether any item is paused on a worker question that has not been answered yet.
@@ -668,7 +676,11 @@ export async function recoverIngestHostResults(options) {
668
676
  if (requiredTestVerdicts === "unsupported_retired_state") {
669
677
  throw new Error("Remediation state uses a retired dispatch shape and cannot cross the host handoff boundary.");
670
678
  }
671
- // ── Phase 2: locked, spawn-free ──────────────────────────────────────────
679
+ // ── Phase 2: locked; children are async + deadline-bounded only ──────────
680
+ // The long-running required tests were precomputed in Phase 1, outside the
681
+ // lock. What still spawns under the hold is the corroboration git probes —
682
+ // async on the tracked twin with the shared deadline (INV-SSF), so the
683
+ // hold's heartbeat keeps beating through every probe.
672
684
  let ingested;
673
685
  await store.mutate(async (state) => {
674
686
  if (!state) {
@@ -1985,7 +1997,7 @@ async function handlePlanning(root, artifactsDir, state, store) {
1985
1997
  }
1986
1998
  // Document phase dissolved (N-R13): planning transitions directly to
1987
1999
  // implementing, and the host workload reads finding context directly.
1988
- const implementBlocks = implementableBlocks(state);
2000
+ const implementBlocks = dispatchFrontier(state);
1989
2001
  if (implementBlocks.length > 0) {
1990
2002
  if (state.plan) {
1991
2003
  const integrity = await checkAffectedFileIntegrity(root, state.plan.findings);
@@ -2020,28 +2032,15 @@ async function handlePlanning(root, artifactsDir, state, store) {
2020
2032
  }
2021
2033
  }
2022
2034
  // Transition directly to implementing — no separate document round.
2023
- // Any pending item outside every attainable host dependency frontier is
2035
+ // Any pending item that can never enter the host dependency frontier is
2024
2036
  // dead-ended (INV-RS-01): a prerequisite was skipped/blocked, so its
2025
2037
  // verified-complete edge can never be satisfied — never dispatch a dependent
2026
2038
  // against an upstream surface that did not land. Mark it blocked so the run
2027
2039
  // advances to close rather than looping forever. A node that is merely
2028
- // waiting on a still-running prerequisite is NOT here (it would appear in a
2029
- // later eligible pass); only nodes with a permanently-unsatisfiable edge are.
2040
+ // waiting on a still-running prerequisite — or on a clarification answer —
2041
+ // is NOT dead; the liveness analysis behind the sweep holds it pending.
2030
2042
  if (implementBlocks.length === 0) {
2031
- for (const block of blockedByUnsatisfiedDependency(state)) {
2032
- for (const findingId of block.items) {
2033
- const it = state.items?.[findingId];
2034
- if (!it || it.status !== "pending")
2035
- continue;
2036
- it.status = "blocked";
2037
- it.failure_reason =
2038
- it.failure_reason ??
2039
- "A dependency node did not reach a verified-complete disposition " +
2040
- "(a prerequisite was skipped, blocked, or the dependencies are cyclic); " +
2041
- "the host handoff will not expose this node against an upstream " +
2042
- "surface that never landed (INV-RS-01).";
2043
- }
2044
- }
2043
+ sweepPermanentlyDeadBlocks(state);
2045
2044
  }
2046
2045
  state.status = "implementing";
2047
2046
  await store.saveState(state);
@@ -3232,7 +3231,7 @@ export function buildMainObligations(ctx) {
3232
3231
  derive: (state) => state != null &&
3233
3232
  (state.status === "implementing" || state.status === "triage") &&
3234
3233
  hasUnansweredClarification(state) &&
3235
- implementableBlocks(state).length === 0
3234
+ dispatchFrontier(state).length === 0
3236
3235
  ? "missing"
3237
3236
  : "satisfied",
3238
3237
  execute: async (state) => {
@@ -3247,9 +3246,9 @@ export function buildMainObligations(ctx) {
3247
3246
  derive: (state) => state?.status === "implementing" ? "missing" : "satisfied",
3248
3247
  execute: async (state) => {
3249
3248
  const s = requireState(state);
3250
- // Pending implementable blocks dispatch; triage only runs once every item
3251
- // has left "pending".
3252
- const pendingBlocks = implementableBlocks(s);
3249
+ // A non-empty dispatch frontier dispatches; triage only runs once every
3250
+ // item has left "pending".
3251
+ const pendingBlocks = dispatchFrontier(s);
3253
3252
  if (pendingBlocks.length > 0) {
3254
3253
  // Per-phase boundary gate (T3): before opening a phase P > 0, run the
3255
3254
  // whole-repo suite once over the just-landed foundations. A red PAUSES
@@ -3273,33 +3272,12 @@ export function buildMainObligations(ctx) {
3273
3272
  runLogger,
3274
3273
  });
3275
3274
  }
3276
- // Dead-end pending nodes whose dependency never reached verified-complete
3277
- // (INV-RS-01) so the implementing→triage loop can't livelock; transition
3278
- // so the engine re-scans on the updated state.
3279
- const deadEnded = blockedByUnsatisfiedDependency(s);
3280
- if (deadEnded.length > 0) {
3281
- const now = new Date().toISOString();
3282
- let changed = false;
3283
- for (const block of deadEnded) {
3284
- for (const findingId of block.items) {
3285
- const it = s.items?.[findingId];
3286
- if (!it || it.status !== "pending")
3287
- continue;
3288
- it.status = "blocked";
3289
- it.started_at ??= now;
3290
- it.completed_at = now;
3291
- it.failure_reason =
3292
- it.failure_reason ??
3293
- "A dependency node did not reach a verified-complete disposition " +
3294
- "(a prerequisite was skipped, blocked, or the dependencies are cyclic); " +
3295
- "the host handoff will not expose this node (INV-RS-01).";
3296
- changed = true;
3297
- }
3298
- }
3299
- if (changed) {
3300
- await store.saveState(s);
3301
- return { kind: "transition", state: s };
3302
- }
3275
+ // Dead-end pending nodes that can never enter the frontier (INV-RS-01)
3276
+ // so the implementing→triage loop can't livelock; transition so the
3277
+ // engine re-scans on the updated state.
3278
+ if (sweepPermanentlyDeadBlocks(s)) {
3279
+ await store.saveState(s);
3280
+ return { kind: "transition", state: s };
3303
3281
  }
3304
3282
  return handleImplementing(root, artifactsDir, s, runLogger, store, options);
3305
3283
  },