audit-tools 0.30.6 → 0.30.9

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 (87) hide show
  1. package/README.md +229 -0
  2. package/audit-code.mjs +1 -1
  3. package/dist/audit/cli/dispatch/packetPrompt.d.ts.map +1 -1
  4. package/dist/audit/cli/dispatch/packetPrompt.js +1 -0
  5. package/dist/audit/cli/dispatch/packetPrompt.js.map +1 -1
  6. package/dist/audit/cli/dispatch.d.ts.map +1 -1
  7. package/dist/audit/cli/dispatch.js +4 -1
  8. package/dist/audit/cli/dispatch.js.map +1 -1
  9. package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
  10. package/dist/audit/cli/mergeAndIngestCommand.js +76 -35
  11. package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
  12. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  13. package/dist/audit/cli/nextStepCommand.js +8 -3
  14. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  15. package/dist/audit/cli/nextStepHelpers.d.ts +2 -0
  16. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  17. package/dist/audit/cli/nextStepHelpers.js +49 -16
  18. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  19. package/dist/audit/cli/prompts.d.ts +2 -1
  20. package/dist/audit/cli/prompts.d.ts.map +1 -1
  21. package/dist/audit/cli/prompts.js +12 -3
  22. package/dist/audit/cli/prompts.js.map +1 -1
  23. package/dist/audit/cli/rollingAuditDispatch.d.ts.map +1 -1
  24. package/dist/audit/cli/rollingAuditDispatch.js +18 -1
  25. package/dist/audit/cli/rollingAuditDispatch.js.map +1 -1
  26. package/dist/audit/io/toolingManifest.d.ts +1 -1
  27. package/dist/audit/io/toolingManifest.d.ts.map +1 -1
  28. package/dist/audit/io/toolingManifest.js +1 -1
  29. package/dist/audit/io/toolingManifest.js.map +1 -1
  30. package/dist/audit/orchestrator/auditTaskUtils.js +1 -1
  31. package/dist/audit/orchestrator/dependencyMap.js +1 -1
  32. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  33. package/dist/audit/orchestrator/executorRunners.js +6 -9
  34. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  35. package/dist/audit/orchestrator/nextStep.d.ts +5 -9
  36. package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
  37. package/dist/audit/orchestrator/nextStep.js +5 -9
  38. package/dist/audit/orchestrator/nextStep.js.map +1 -1
  39. package/dist/audit/orchestrator/planningExecutors.js +2 -2
  40. package/dist/audit/orchestrator/taskAffinityGraph.js +1 -1
  41. package/dist/audit/types.d.ts +3 -3
  42. package/dist/audit/types.js +1 -1
  43. package/dist/remediate/contractPipeline/derive.d.ts +1 -1
  44. package/dist/remediate/contractPipeline/derive.js +1 -1
  45. package/dist/remediate/dispatch/amendmentClaim.d.ts +7 -0
  46. package/dist/remediate/dispatch/amendmentClaim.d.ts.map +1 -1
  47. package/dist/remediate/dispatch/amendmentClaim.js +7 -0
  48. package/dist/remediate/dispatch/amendmentClaim.js.map +1 -1
  49. package/dist/remediate/dispatch/ownershipRegistry.d.ts +88 -2
  50. package/dist/remediate/dispatch/ownershipRegistry.d.ts.map +1 -1
  51. package/dist/remediate/dispatch/ownershipRegistry.js +224 -11
  52. package/dist/remediate/dispatch/ownershipRegistry.js.map +1 -1
  53. package/dist/remediate/dispatch/ownershipScheduler.d.ts +48 -0
  54. package/dist/remediate/dispatch/ownershipScheduler.d.ts.map +1 -0
  55. package/dist/remediate/dispatch/ownershipScheduler.js +100 -0
  56. package/dist/remediate/dispatch/ownershipScheduler.js.map +1 -0
  57. package/dist/remediate/steps/nextStep.d.ts +17 -2
  58. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  59. package/dist/remediate/steps/nextStep.js +76 -70
  60. package/dist/remediate/steps/nextStep.js.map +1 -1
  61. package/dist/remediate/steps/providerNodeDispatch.d.ts.map +1 -1
  62. package/dist/remediate/steps/providerNodeDispatch.js +16 -1
  63. package/dist/remediate/steps/providerNodeDispatch.js.map +1 -1
  64. package/dist/remediate/validation/contractPipelineGates.js +1 -1
  65. package/dist/shared/engine/obligationEngine.d.ts +1 -1
  66. package/dist/shared/engine/obligationEngine.js +1 -1
  67. package/dist/shared/friction/triage.d.ts +50 -20
  68. package/dist/shared/friction/triage.d.ts.map +1 -1
  69. package/dist/shared/friction/triage.js +70 -23
  70. package/dist/shared/friction/triage.js.map +1 -1
  71. package/dist/shared/index.d.ts +2 -2
  72. package/dist/shared/index.d.ts.map +1 -1
  73. package/dist/shared/index.js +1 -1
  74. package/dist/shared/index.js.map +1 -1
  75. package/dist/shared/quota/scheduler.d.ts +1 -1
  76. package/dist/shared/repair/brokeredDispatch.d.ts +1 -1
  77. package/dist/shared/types/rollingDispatch.d.ts +1 -1
  78. package/package.json +4 -3
  79. package/{audit-code-wrapper-build.mjs → wrapper/audit-code-wrapper-build.mjs} +1 -1
  80. package/{audit-code-wrapper-install-hosts.mjs → wrapper/audit-code-wrapper-install-hosts.mjs} +1 -1
  81. package/{audit-code-wrapper-lib.mjs → wrapper/audit-code-wrapper-lib.mjs} +1 -1
  82. package/README.audit.md +0 -222
  83. package/README.remediate.md +0 -112
  84. /package/{audit-code-wrapper-install-renderers.mjs → wrapper/audit-code-wrapper-install-renderers.mjs} +0 -0
  85. /package/{audit-code-wrapper-io.mjs → wrapper/audit-code-wrapper-io.mjs} +0 -0
  86. /package/{audit-code-wrapper-legacy.mjs → wrapper/audit-code-wrapper-legacy.mjs} +0 -0
  87. /package/{audit-code-wrapper-opencode.mjs → wrapper/audit-code-wrapper-opencode.mjs} +0 -0
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { mkdir, readFile, rename } from "node:fs/promises";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { StateStore } from "../state/store.js";
5
- import { readOptionalJsonFile, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DO_NOT_TOKEN_WRAP_NOTE, DISPATCH_PROMPT_HANDOFF_NOTE, renderQuotaCoverageNudge, coerceJsonObjectArg, createRollingDispatcher, setQuotaStateDir, interpretFreeFormIntent, advance, decideFrictionTriage, planHybridDispatch, readSettledPools, addSettledPool, sourceByPoolId } from "audit-tools/shared";
5
+ import { readOptionalJsonFile, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DO_NOT_TOKEN_WRAP_NOTE, DISPATCH_PROMPT_HANDOFF_NOTE, renderQuotaCoverageNudge, coerceJsonObjectArg, createRollingDispatcher, setQuotaStateDir, interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock, planHybridDispatch, readSettledPools, addSettledPool, sourceByPoolId } from "audit-tools/shared";
6
6
  import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
7
7
  import { groundExtractedFindings } from "../phases/grounding.js";
8
8
  import { runTriagePhase } from "../phases/triage.js";
@@ -10,6 +10,7 @@ import { runClosePhase } from "../phases/close.js";
10
10
  import { validateRemediationPlan } from "../validation/remediationState.js";
11
11
  import { mergeImplementResults, prepareImplementDispatch, readExtractedPlanIfPresent, buildConfirmedPools, executeNodeInWorktree, blockScopesFromPlan, declaredPathsFromPlan, targetedCommandsForBlock, } from "./dispatch.js";
12
12
  import { makeProviderNodeDispatcher } from "./providerNodeDispatch.js";
13
+ import { ownershipSubWaves } from "../dispatch/ownershipScheduler.js";
13
14
  import { prepareHostRollingDispatch, nodeClaimRegistry, nodeSettledPoolsPath } from "./rollingSession.js";
14
15
  import { writeCurrentStep } from "./stepWriter.js";
15
16
  import { dependencyVerifiedComplete } from "./stepUtils.js";
@@ -112,7 +113,10 @@ function resolveArtifactsDir(root, artifactsDir) {
112
113
  return resolve(artifactsDir ?? join(root, ".audit-tools", "remediation"));
113
114
  }
114
115
  function stateRunId(state) {
115
- return state?.plan?.plan_id ?? randomRunId("REMEDIATE");
116
+ // When the plan is absent (fully-green close deleted the state, or complete
117
+ // was persisted without a plan), use the stable fallback "run" so the friction
118
+ // record path is deterministic across multiple next-step calls on the same run.
119
+ return state?.plan?.plan_id ?? "run";
116
120
  }
117
121
  function defaultInputCandidates(root) {
118
122
  // Prefer the canonical machine contract (audit-findings.json) over its
@@ -245,8 +249,14 @@ function blockedByUnsatisfiedDependency(state) {
245
249
  * prerequisite is skipped/blocked, not merely pending) is NOT placed in any
246
250
  * level — it is dead-ended by `handlePlanning`.
247
251
  *
248
- * Pure and deterministic: levels and the nodes within a level are ordered by
249
- * block_id so the interposed-rebuild boundaries are stable across runs.
252
+ * Pure and deterministic: level MEMBERSHIP is fixed by the dependency predicate
253
+ * (INV-RS-01) and is independent of node order; the interposed-rebuild boundaries
254
+ * are therefore stable across runs without an in-level numeric sort. In-level
255
+ * admission ORDER is no longer decided here — it is owned by the file-ownership
256
+ * scheduler (`ownershipSubWaves`, INV-SOO-04/08), which applies its own explicit
257
+ * block_id tie-break AFTER the disjointness filter. The numeric
258
+ * `block_id.localeCompare` admission ordering that used to live here was removed
259
+ * atomically with that change (INV-SOO-04).
250
260
  */
251
261
  export function rollingDependencyLevels(state) {
252
262
  const plan = state.plan;
@@ -254,9 +264,7 @@ export function rollingDependencyLevels(state) {
254
264
  if (!plan || !items)
255
265
  return [];
256
266
  const blockById = new Map(plan.blocks.map((b) => [b.block_id, b]));
257
- const pendingBlocks = plan.blocks
258
- .filter((b) => b.items.some((id) => items[id]?.status === "pending"))
259
- .sort((a, b) => a.block_id.localeCompare(b.block_id));
267
+ const pendingBlocks = plan.blocks.filter((b) => b.items.some((id) => items[id]?.status === "pending"));
260
268
  // A dependency edge is "completable" only when the dep node is itself pending
261
269
  // (will be satisfied by some level) or already verified-complete. A skipped /
262
270
  // blocked dependency makes the dependent permanently ineligible — such nodes
@@ -340,23 +348,34 @@ export async function driveRollingDispatch(levels, options) {
340
348
  }
341
349
  }
342
350
  const blockByPacketId = new Map(level.map((b) => [b.block_id, b]));
343
- const packets = level.map((block) => ({
344
- id: block.block_id,
345
- payload: { block_id: block.block_id },
346
- estimatedTokens: estimateTokens(block),
347
- complexity: 0.5,
348
- }));
349
- const dispatcher = createRollingDispatcher({
350
- confirmedPools: options.confirmedPools,
351
- sessionConfig: options.sessionConfig,
352
- dispatchPacket: async (packet, slot) => {
353
- const block = blockByPacketId.get(packet.payload.block_id);
354
- return options.dispatchNode(block, slot);
355
- },
356
- });
357
- dispatcher.enqueue(packets);
358
- const results = await dispatcher.run();
359
- out.levels.push({ blockIds: level.map((b) => b.block_id), results });
351
+ const scopeForBlock = options.scopeForBlock ?? ((b) => b.touched_files ?? []);
352
+ // File-ownership-disjoint admission (INV-SOO): split the level into ordered
353
+ // sub-waves, each a maximal file-disjoint subset (deterministic block_id
354
+ // tie-break AFTER the disjointness filter, INV-SOO-08). Same-file nodes land
355
+ // in successive sub-waves (serialize, INV-SOO-01/02) and a freed file becomes
356
+ // schedulable for the next sub-wave; different-file nodes share a sub-wave and
357
+ // parallelize up to the quota cap the dispatcher enforces (INV-SOO-03/05).
358
+ const subWaves = ownershipSubWaves(level.map((b) => ({ block_id: b.block_id, write_paths: scopeForBlock(b) })), options.root);
359
+ const levelResults = [];
360
+ for (const wave of subWaves) {
361
+ const packets = wave.map((node) => ({
362
+ id: node.block_id,
363
+ payload: { block_id: node.block_id },
364
+ estimatedTokens: estimateTokens(blockByPacketId.get(node.block_id)),
365
+ complexity: 0.5,
366
+ }));
367
+ const dispatcher = createRollingDispatcher({
368
+ confirmedPools: options.confirmedPools,
369
+ sessionConfig: options.sessionConfig,
370
+ dispatchPacket: async (packet, slot) => {
371
+ const block = blockByPacketId.get(packet.payload.block_id);
372
+ return options.dispatchNode(block, slot);
373
+ },
374
+ });
375
+ dispatcher.enqueue(packets);
376
+ levelResults.push(...(await dispatcher.run()));
377
+ }
378
+ out.levels.push({ blockIds: level.map((b) => b.block_id), results: levelResults });
360
379
  }
361
380
  return out;
362
381
  }
@@ -543,12 +562,18 @@ export async function driveRollingImplementDispatch(options) {
543
562
  }
544
563
  return result;
545
564
  };
565
+ // Declared write-scope per block, from the dispatch plan — the same authority
566
+ // the merge-time write-scope gate reads — for file-ownership-disjoint admission
567
+ // (INV-SOO). A block with no plan scope falls back to its touched_files.
568
+ const writePathsByBlock = new Map(allBlockScopes.map((s) => [s.block_id, s.write_paths]));
546
569
  await driveRollingDispatch(levels, {
547
570
  confirmedPools,
548
571
  sessionConfig: options.sessionConfig ?? {},
549
572
  dispatchNode: dispatchNodeWithWorktree,
550
573
  rebuildSharedBetweenLevels: options.rebuildSharedBetweenLevels,
551
574
  quotaStateDir: artifactsDir,
575
+ root,
576
+ scopeForBlock: (block) => writePathsByBlock.get(block.block_id) ?? block.touched_files ?? [],
552
577
  });
553
578
  // Deterministic merge: same path the wave flow uses (tolerant remap, write-scope
554
579
  // gate against each verified branch, lost-update detection). Worktrees are
@@ -996,56 +1021,36 @@ async function forceReplanFromExistingIntake(root, artifactsDir, previous, store
996
1021
  }
997
1022
  async function presentReportStep(root, artifactsDir, state) {
998
1023
  const reportPath = join(dirname(artifactsDir), "remediation-report.md");
999
- // Tool-emitted terminal friction-TRIAGE close-out, FOLDED into present_report so it
1000
- // is one bounded step (parity with audit-code; single-sourced in `audit-tools/shared`).
1001
- // MANDATORY + BLOCKING: stays "dispose" until every captured mechanical event AND
1002
- // every surfaced agent-feedback reflection carries a keep|discard|annotate
1003
- // disposition; an empty set (zero events AND zero reflections) is trivially
1004
- // "disposed". Never couples to any repo's backlog doc.
1005
- const triage = await decideRemediateFrictionCloseout(artifactsDir, state);
1006
- const pendingBlock = triage.action === "dispose"
1007
- ? `
1008
-
1009
- ## Triage captured friction (BLOCKING close-out)
1010
-
1011
- This run captured friction that MUST be disposed before it can present complete.
1012
- For EACH item below, record a disposition (\`keep\`, \`discard\`, or \`annotate\`)
1013
- against its id into the run's friction record at \`${triage.recordPath}\` (append to
1014
- \`dispositions[]\` as \`{ "target_id": "<id>", "disposition": "keep|discard|annotate",
1015
- optional "annotation": "..." }\`). This is the run's own record under its artifacts
1016
- dir — do NOT edit any repository's tracking/backlog doc.
1017
-
1018
- ${triage.pending
1019
- .map((subject) => `- \`${subject.id}\` (${subject.source}) — ${subject.note}`)
1020
- .join("\n")}
1021
- `
1022
- : `
1023
-
1024
- ## Run friction triage
1025
-
1026
- No captured friction or agent-feedback reflections required disposition this run
1027
- (empty set → trivially disposed).
1028
- `;
1024
+ // Terminal friction-TRIAGE close-out, folded into present_report (single-sourced in
1025
+ // `audit-tools/shared`). MANDATORY + BLOCKING: stays "dispose" until every mechanical
1026
+ // event + reflection is disposed AND ≥1 open observation written. Never trivially
1027
+ // satisfied by an empty event set — the host must actively confirm the friction state.
1028
+ //
1029
+ // When `artifactsDir` was deleted by a fully-green close (close.ts rm -rf on a
1030
+ // green run), there is nowhere to persist the friction record and no mechanical
1031
+ // events to triage skip the triage entirely and go straight to complete.
1032
+ const artifactsDirExists = existsSync(artifactsDir);
1033
+ const triage = artifactsDirExists
1034
+ ? await decideRemediateFrictionCloseout(artifactsDir, state)
1035
+ : null;
1036
+ const frictionBlock = triage ? buildFrictionTriageBlock(triage) : "";
1037
+ const isBlocked = triage?.action === "dispose";
1029
1038
  return writeCurrentStep({
1030
1039
  stepKind: "present_report",
1031
- status: "complete",
1040
+ status: isBlocked ? "ready" : "complete",
1032
1041
  runId: stateRunId(state),
1033
1042
  repoRoot: root,
1034
1043
  artifactsDir,
1035
- prompt: `
1036
- # Present Remediation Report
1037
-
1038
- Read \`${reportPath}\` and summarize the remediation outcome for the user.
1039
- Mention the resolved, ignored, and deemed-inappropriate counts plus the closing action.
1040
- ${pendingBlock}
1041
- Stop after presenting the summary (and disposing captured friction, if any).
1042
- `,
1044
+ prompt: isBlocked
1045
+ ? `# Remediation Run Friction Triage\n\nComplete friction triage before presenting the report.\n${frictionBlock}`
1046
+ : `# Present Remediation Report\n\nRead \`${reportPath}\` and summarize the remediation outcome.\nMention resolved, ignored, and deemed-inappropriate counts plus the closing action.\n${frictionBlock}`,
1043
1047
  allowedCommands: [],
1044
- stopCondition: "Stop after presenting the remediation report summary and disposing any captured run friction.",
1048
+ stopCondition: isBlocked
1049
+ ? "Complete friction triage (write dispositions and open_observations), then call next-step again."
1050
+ : "Present the remediation report summary and stop.",
1045
1051
  artifactPaths: {
1046
1052
  final_report: reportPath,
1047
- // Surface the run's friction record (resolved through the shared path module).
1048
- friction_record: triage.recordPath,
1053
+ ...(triage ? { friction_record: triage.recordPath } : {}),
1049
1054
  },
1050
1055
  });
1051
1056
  }
@@ -2363,10 +2368,11 @@ async function handleClosing(root, artifactsDir, state, runLogger, store) {
2363
2368
  // Close-complete CROSSES the engine boundary: `complete` is a pre-intake
2364
2369
  // obligation, unreachable from a main-engine transition. Emit the durable
2365
2370
  // report directly, passing exactly what the original recursion reloaded — the
2366
- // artifact dir is DELETED on a fully-green close (reload → null → randomRunId)
2367
- // and PRESERVED on a not-green complete (reload → the saved complete state →
2368
- // its plan_id). `store.loadState()` reproduces both, so present_report is
2369
- // identical to the cascade. (Regression-locked in next-step-implement-dispatch.)
2371
+ // artifact dir is DELETED on a fully-green close (reload → null → stateRunId
2372
+ // falls back to "run") and PRESERVED on a not-green complete (reload → the
2373
+ // saved complete state → its plan_id). `store.loadState()` reproduces both, so
2374
+ // present_report is identical to the cascade.
2375
+ // (Regression-locked in next-step-implement-dispatch.)
2370
2376
  return {
2371
2377
  kind: "emit",
2372
2378
  step: await handleComplete(root, artifactsDir, await store.loadState()),