audit-tools 0.35.0 → 0.35.1
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/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +4 -2
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/extractors/analyzers/candidates.d.ts.map +1 -1
- package/dist/audit/extractors/analyzers/candidates.js +45 -54
- package/dist/audit/extractors/analyzers/candidates.js.map +1 -1
- package/dist/audit/orchestrator/executors.d.ts +2 -0
- package/dist/audit/orchestrator/executors.d.ts.map +1 -1
- package/dist/audit/orchestrator/executors.js +8 -2
- package/dist/audit/orchestrator/executors.js.map +1 -1
- package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
- package/dist/audit/orchestrator/nextStep.js +2 -2
- package/dist/audit/orchestrator/nextStep.js.map +1 -1
- package/dist/audit/orchestrator/staleness.d.ts.map +1 -1
- package/dist/audit/orchestrator/staleness.js +3 -3
- package/dist/audit/orchestrator/staleness.js.map +1 -1
- package/dist/audit/providers/index.d.ts +12 -6
- package/dist/audit/providers/index.d.ts.map +1 -1
- package/dist/audit/providers/index.js +24 -14
- package/dist/audit/providers/index.js.map +1 -1
- package/dist/audit/quota/hostLimits.d.ts +4 -6
- package/dist/audit/quota/hostLimits.d.ts.map +1 -1
- package/dist/audit/quota/hostLimits.js +5 -8
- package/dist/audit/quota/hostLimits.js.map +1 -1
- package/dist/audit/validation/sessionConfig.d.ts.map +1 -1
- package/dist/audit/validation/sessionConfig.js +27 -29
- package/dist/audit/validation/sessionConfig.js.map +1 -1
- package/dist/remediate/providers/index.d.ts +12 -7
- package/dist/remediate/providers/index.d.ts.map +1 -1
- package/dist/remediate/providers/index.js +24 -15
- package/dist/remediate/providers/index.js.map +1 -1
- package/dist/remediate/quota/hostLimits.d.ts +4 -6
- package/dist/remediate/quota/hostLimits.d.ts.map +1 -1
- package/dist/remediate/quota/hostLimits.js +5 -8
- package/dist/remediate/quota/hostLimits.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +1 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +165 -138
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/sessionConfigLoad.d.ts.map +1 -1
- package/dist/remediate/steps/sessionConfigLoad.js +12 -3
- package/dist/remediate/steps/sessionConfigLoad.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +3 -8
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/shared/index.d.ts +3 -1
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +2 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/providers/agyProvider.d.ts.map +1 -1
- package/dist/shared/providers/agyProvider.js +12 -26
- package/dist/shared/providers/agyProvider.js.map +1 -1
- package/dist/shared/providers/auditorSources.d.ts.map +1 -1
- package/dist/shared/providers/auditorSources.js +0 -6
- package/dist/shared/providers/auditorSources.js.map +1 -1
- package/dist/shared/providers/orchestratorBindings.d.ts +42 -0
- package/dist/shared/providers/orchestratorBindings.d.ts.map +1 -0
- package/dist/shared/providers/orchestratorBindings.js +30 -0
- package/dist/shared/providers/orchestratorBindings.js.map +1 -0
- package/dist/shared/providers/providerFactory.d.ts.map +1 -1
- package/dist/shared/providers/providerFactory.js +3 -5
- package/dist/shared/providers/providerFactory.js.map +1 -1
- package/dist/shared/providers/providerPathGuard.d.ts.map +1 -1
- package/dist/shared/providers/providerPathGuard.js +18 -21
- package/dist/shared/providers/providerPathGuard.js.map +1 -1
- package/dist/shared/quota/hostLimits.d.ts +18 -0
- package/dist/shared/quota/hostLimits.d.ts.map +1 -1
- package/dist/shared/quota/hostLimits.js +13 -0
- package/dist/shared/quota/hostLimits.js.map +1 -1
- package/dist/shared/quota/limits.d.ts +6 -9
- package/dist/shared/quota/limits.d.ts.map +1 -1
- package/dist/shared/quota/limits.js +37 -24
- package/dist/shared/quota/limits.js.map +1 -1
- package/dist/shared/quota/scheduler.d.ts.map +1 -1
- package/dist/shared/quota/scheduler.js +2 -11
- package/dist/shared/quota/scheduler.js.map +1 -1
- package/dist/shared/types/sessionConfig.d.ts +3 -3
- package/dist/shared/types/sessionConfig.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/audit/providers/agyProvider.d.ts +0 -11
- package/dist/audit/providers/agyProvider.d.ts.map +0 -1
- package/dist/audit/providers/agyProvider.js +0 -18
- package/dist/audit/providers/agyProvider.js.map +0 -1
- package/dist/audit/providers/claudeCodeProvider.d.ts +0 -11
- package/dist/audit/providers/claudeCodeProvider.d.ts.map +0 -1
- package/dist/audit/providers/claudeCodeProvider.js +0 -23
- package/dist/audit/providers/claudeCodeProvider.js.map +0 -1
- package/dist/audit/providers/claudeWorkerProvider.d.ts +0 -10
- package/dist/audit/providers/claudeWorkerProvider.d.ts.map +0 -1
- package/dist/audit/providers/claudeWorkerProvider.js +0 -15
- package/dist/audit/providers/claudeWorkerProvider.js.map +0 -1
- package/dist/audit/providers/opencodeProvider.d.ts +0 -2
- package/dist/audit/providers/opencodeProvider.d.ts.map +0 -1
- package/dist/audit/providers/opencodeProvider.js +0 -7
- package/dist/audit/providers/opencodeProvider.js.map +0 -1
- package/dist/remediate/providers/agyProvider.d.ts +0 -12
- package/dist/remediate/providers/agyProvider.d.ts.map +0 -1
- package/dist/remediate/providers/agyProvider.js +0 -19
- package/dist/remediate/providers/agyProvider.js.map +0 -1
- package/dist/remediate/providers/claudeCodeProvider.d.ts +0 -12
- package/dist/remediate/providers/claudeCodeProvider.d.ts.map +0 -1
- package/dist/remediate/providers/claudeCodeProvider.js +0 -24
- package/dist/remediate/providers/claudeCodeProvider.js.map +0 -1
- package/dist/remediate/providers/claudeWorkerProvider.d.ts +0 -11
- package/dist/remediate/providers/claudeWorkerProvider.d.ts.map +0 -1
- package/dist/remediate/providers/claudeWorkerProvider.js +0 -16
- package/dist/remediate/providers/claudeWorkerProvider.js.map +0 -1
- package/dist/remediate/providers/opencodeProvider.d.ts +0 -2
- package/dist/remediate/providers/opencodeProvider.d.ts.map +0 -1
- package/dist/remediate/providers/opencodeProvider.js +0 -7
- package/dist/remediate/providers/opencodeProvider.js.map +0 -1
|
@@ -3,7 +3,18 @@ import { existsSync, statSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, rename } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { StateStore } from "../state/store.js";
|
|
6
|
-
|
|
6
|
+
// IO / validation / rendering helpers
|
|
7
|
+
import { discardOnSchemaVersionMismatch, buildSelfSpawnExclusion, readOptionalJsonFile, readValidatedRepoSessionIntent, stagedAndUntracked, writeJsonFile, writeTextFile, buildAuditDeliverablePair, formatValidationIssues, isRecord, withFsRetry, RunLogger, DISPATCH_PROMPT_HANDOFF_NOTE, renderHostScratchNote, hostScratchDir, renderQuotaCoverageNudge, renderTokenBudgetView, coerceJsonObjectArg,
|
|
8
|
+
// rolling engine + quota
|
|
9
|
+
driveRolling, resolveLedgerBudgets, setQuotaStateDir, detectHostDispatchWall, admissionBlockedOnBudget, classifyEmptyGrantCause, reconcileAdmissionLeasesFromQuotaFile, buildQuotaPausedTerminal, HostSessionQuotaSource, quotaPoolKey,
|
|
10
|
+
// obligation engine + intent
|
|
11
|
+
interpretFreeFormIntent, advance, decideFrictionTriage, buildFrictionTriageBlock,
|
|
12
|
+
// dispatch planning / provider classification
|
|
13
|
+
planHybridDispatch, readSettledPools, addSettledPool, isPoolSettlingOutcome, isInProcessWorkerProvider, sourceByPoolId, classifyProvider, selectDispatchDriver, renderDispatchDriverInstruction, resolveHostProviderName, resolveHostDispatchProviderName, resolveHostDispatchCapability as sharedResolveHostDispatchCapability, resolveAutonomousMode, resolveRollingEngineFlag,
|
|
14
|
+
// friction capture
|
|
15
|
+
captureStepBoundaryFriction, captureZeroCapacityFriction, captureCostDriftFriction, captureCreditExhaustionFriction, captureQuotaUnclassifiedFriction, captureModelUnavailableFriction, capturePacketTooLargeFriction, createDispatchDecisionLog,
|
|
16
|
+
// domain constants
|
|
17
|
+
LENSES, SEVERITIES, } from "audit-tools/shared";
|
|
7
18
|
import { readRemediationAccessMemory, computeBlockContinuityScores } from "../state/accessMemory.js";
|
|
8
19
|
import { applyPlanPipeline, buildCoverageLedger } from "../phases/plan.js";
|
|
9
20
|
import { groundExtractedFindings } from "../phases/grounding.js";
|
|
@@ -1019,12 +1030,12 @@ function normalizeExtractedPlan(value) {
|
|
|
1019
1030
|
}
|
|
1020
1031
|
async function saveStateForPlan(artifactsDir, existing, plan, planCoverage) {
|
|
1021
1032
|
const items = {};
|
|
1033
|
+
const blockIds = blockIdsByFinding(plan);
|
|
1022
1034
|
for (const finding of plan.findings) {
|
|
1023
|
-
const block = plan.blocks.find((candidate) => candidate.items.includes(finding.id));
|
|
1024
1035
|
items[finding.id] = {
|
|
1025
1036
|
finding_id: finding.id,
|
|
1026
1037
|
status: "pending",
|
|
1027
|
-
block_id:
|
|
1038
|
+
block_id: blockIds.get(finding.id) ?? "UNKNOWN",
|
|
1028
1039
|
};
|
|
1029
1040
|
}
|
|
1030
1041
|
const state = {
|
|
@@ -1063,11 +1074,20 @@ function stripPlanTimeBookkeeping(value) {
|
|
|
1063
1074
|
function findingCarryForwardKey(finding) {
|
|
1064
1075
|
return JSON.stringify(stripPlanTimeBookkeeping(finding));
|
|
1065
1076
|
}
|
|
1077
|
+
// FIRST-wins on multi-block membership: `fixupBlocksAfterDedup` can leave a
|
|
1078
|
+
// dedup survivor in more than one block's items, and historically the two
|
|
1079
|
+
// consumers disagreed (saveStateForPlan scanned blocks first-wins while this
|
|
1080
|
+
// map overwrote last-wins), so the same plan could yield different block_ids
|
|
1081
|
+
// per path. First-wins is pinned as the single semantic. The root question —
|
|
1082
|
+
// whether a finding should ever belong to two blocks at all — is tracked in
|
|
1083
|
+
// docs/backlog/open-bugs.md.
|
|
1066
1084
|
function blockIdsByFinding(plan) {
|
|
1067
1085
|
const byFinding = new Map();
|
|
1068
1086
|
for (const block of plan.blocks) {
|
|
1069
1087
|
for (const id of block.items) {
|
|
1070
|
-
byFinding.
|
|
1088
|
+
if (!byFinding.has(id)) {
|
|
1089
|
+
byFinding.set(id, block.block_id);
|
|
1090
|
+
}
|
|
1071
1091
|
}
|
|
1072
1092
|
}
|
|
1073
1093
|
return byFinding;
|
|
@@ -1989,6 +2009,7 @@ async function handlePendingExtractedPlan(root, artifactsDir, existing, extracte
|
|
|
1989
2009
|
// way declined findings are recorded; their payloads recover at close from the
|
|
1990
2010
|
// unfiltered intake source.
|
|
1991
2011
|
const filterDisp = await readOptionalJsonFile(reviewFilterDispositionsPath(artifactsDir));
|
|
2012
|
+
const pipelinedBlockIds = blockIdsByFinding(pipelined);
|
|
1992
2013
|
const coverage = filterDisp
|
|
1993
2014
|
? buildCoverageLedger({
|
|
1994
2015
|
planId: pipelined.plan_id,
|
|
@@ -2015,8 +2036,7 @@ async function handlePendingExtractedPlan(root, artifactsDir, existing, extracte
|
|
|
2015
2036
|
{
|
|
2016
2037
|
finding_id: finding.id,
|
|
2017
2038
|
status: "pending",
|
|
2018
|
-
block_id:
|
|
2019
|
-
?.block_id ?? "UNKNOWN",
|
|
2039
|
+
block_id: pipelinedBlockIds.get(finding.id) ?? "UNKNOWN",
|
|
2020
2040
|
},
|
|
2021
2041
|
])),
|
|
2022
2042
|
});
|
|
@@ -2370,31 +2390,49 @@ async function handleReadyIntakeContractPipeline(root, artifactsDir, options) {
|
|
|
2370
2390
|
if (!intake.summary || !isIntakeReady(intake.summary)) {
|
|
2371
2391
|
return null;
|
|
2372
2392
|
}
|
|
2393
|
+
// Resolve the manifest sources ONCE for the whole step (risk signal, Path A,
|
|
2394
|
+
// and the pipeline source inputs all consume the same snapshot), and read the
|
|
2395
|
+
// structured-audit source file at most once via a memoized reader (an
|
|
2396
|
+
// unreadable source memoizes `undefined`; extractAuditFindings on undefined
|
|
2397
|
+
// yields the empty set, so consumers degrade exactly as before).
|
|
2398
|
+
const manifestSources = intake.manifest
|
|
2399
|
+
? resolveManifestSources(root, intake.manifest).resolved
|
|
2400
|
+
: [];
|
|
2401
|
+
const auditSource = intake.summary.source_type === "structured_audit"
|
|
2402
|
+
? manifestSources.find((s) => s.type === "structured_audit")
|
|
2403
|
+
: undefined;
|
|
2404
|
+
let auditFindingsCache;
|
|
2405
|
+
const readAuditFindingsOnce = async () => {
|
|
2406
|
+
if (!auditSource) {
|
|
2407
|
+
return undefined;
|
|
2408
|
+
}
|
|
2409
|
+
if (!auditFindingsCache) {
|
|
2410
|
+
let value;
|
|
2411
|
+
try {
|
|
2412
|
+
value = JSON.parse(await readFile(auditSource.path, "utf8"));
|
|
2413
|
+
}
|
|
2414
|
+
catch {
|
|
2415
|
+
value = undefined;
|
|
2416
|
+
}
|
|
2417
|
+
auditFindingsCache = { value };
|
|
2418
|
+
}
|
|
2419
|
+
return auditFindingsCache.value;
|
|
2420
|
+
};
|
|
2373
2421
|
// Slice 2 — compute & persist the shared intake risk/complexity signal the
|
|
2374
2422
|
// self-scaling dials (Slices 3/4) will read. Idempotent: recorded once from
|
|
2375
2423
|
// intake-available data only (affected_files + goals + path-risk patterns), so
|
|
2376
|
-
// a later escalate-on-evidence raise is never clobbered. The
|
|
2377
|
-
//
|
|
2378
|
-
// where the top-level summary.affected_files is
|
|
2379
|
-
// per-finding) — it unions the per-finding
|
|
2380
|
-
// patterns actually fire (fail-closed: a
|
|
2381
|
-
// `low`). No behavior keys on it yet —
|
|
2424
|
+
// a later escalate-on-evidence raise is never clobbered. The audit-source read
|
|
2425
|
+
// happens only on the run that actually computes (memoized above), and for
|
|
2426
|
+
// structured_audit — where the top-level summary.affected_files is
|
|
2427
|
+
// legitimately empty (paths live per-finding) — it unions the per-finding
|
|
2428
|
+
// affected files so the path-risk patterns actually fire (fail-closed: a
|
|
2429
|
+
// risky-subsystem audit must not land `low`). No behavior keys on it yet —
|
|
2430
|
+
// this establishes the source of truth.
|
|
2382
2431
|
await ensureIntakeRiskSignal(artifactsDir, async () => {
|
|
2383
2432
|
const summary = intake.summary;
|
|
2384
2433
|
const affectedFiles = summary.affected_files.map((f) => f.path);
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
if (auditSource) {
|
|
2388
|
-
try {
|
|
2389
|
-
const parsed = JSON.parse(await readFile(auditSource.path, "utf8"));
|
|
2390
|
-
affectedFiles.push(...distinctAffectedFiles(extractAuditFindings(parsed)));
|
|
2391
|
-
}
|
|
2392
|
-
catch {
|
|
2393
|
-
// Unreadable audit source — leave the summary-derived list; an empty
|
|
2394
|
-
// list with non-empty goals still rates conservatively via intent.
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2434
|
+
const parsed = await readAuditFindingsOnce();
|
|
2435
|
+
affectedFiles.push(...distinctAffectedFiles(extractAuditFindings(parsed)));
|
|
2398
2436
|
return { affectedFiles, goals: summary.goals };
|
|
2399
2437
|
});
|
|
2400
2438
|
const pipeline = shouldEnterContractPipeline(artifactsDir, intake.summary.source_type);
|
|
@@ -2419,118 +2457,109 @@ async function handleReadyIntakeContractPipeline(root, artifactsDir, options) {
|
|
|
2419
2457
|
// ledger is built over the originals (every audit finding → exactly one
|
|
2420
2458
|
// disposition). The gate may halt to collect the user's decision.
|
|
2421
2459
|
let reviewSourceSwap;
|
|
2422
|
-
if (
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2460
|
+
if (auditSource) {
|
|
2461
|
+
const auditFindings = await readAuditFindingsOnce();
|
|
2462
|
+
const originals = extractAuditFindings(auditFindings);
|
|
2463
|
+
if (originals.length > 0) {
|
|
2464
|
+
const checkpoint = await readOptionalJsonFile(join(artifactsDir, "intent_checkpoint.json"));
|
|
2465
|
+
const filter = await runFindingFilterPass(originals, {
|
|
2466
|
+
root,
|
|
2467
|
+
checkpoint: checkpoint ?? undefined,
|
|
2468
|
+
evidenceGrounding: true,
|
|
2469
|
+
});
|
|
2470
|
+
const gate = await runReviewApprovalGate(root, artifactsDir, filter.survivors,
|
|
2471
|
+
// Autonomy resolves from the PERSISTED session config (hoisted load
|
|
2472
|
+
// above) → env → attended default, never from the bare CLI options.
|
|
2473
|
+
resolveAutonomousMode({ sessionConfig: sessionConfigForDispatch }));
|
|
2474
|
+
if (gate.kind === "halt") {
|
|
2475
|
+
return gate.step;
|
|
2428
2476
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2477
|
+
// Persist the filter dispositions so coverage is built over the originals.
|
|
2478
|
+
await persistReviewFilterDispositions(artifactsDir, originals, filter);
|
|
2479
|
+
// Lean path = the `low` risk tier's realization (D-68 — the standalone lean
|
|
2480
|
+
// fast-path folded into the self-scaling dial; its two mechanisms now live in
|
|
2481
|
+
// `riskSignal.ts` (light-review) + `contractPipeline.ts` (lean plan builder)).
|
|
2482
|
+
// A run skips the heavy contract DESIGN loop and
|
|
2483
|
+
// synthesizes the extracted plan directly IFF its effective risk tier is `low`;
|
|
2484
|
+
// the plan→implement→close machinery (per-node verify-before-merge + the final
|
|
2485
|
+
// whole-repo gate) is the retained safety net. Runs only here — on Path A
|
|
2486
|
+
// (structured_audit), the only intake with a pre-existing finding set to judge.
|
|
2487
|
+
//
|
|
2488
|
+
// First fold the APPROVED set's finding-level risk (grounding / confidence /
|
|
2489
|
+
// coupling / systemic / architecture / count — the finding-QUALITY dimension the
|
|
2490
|
+
// intake path/breadth/intent signal doesn't see) INTO the shared risk signal as
|
|
2491
|
+
// escalate-on-evidence. This makes the tier the SINGLE classifier: there is no
|
|
2492
|
+
// separate fast-path boolean that can DISAGREE with it (a grounded handful
|
|
2493
|
+
// touching a risk subsystem stays `high` and takes the full pipeline, instead of
|
|
2494
|
+
// bypassing it as the old parallel `evaluateFastPath` allowed).
|
|
2495
|
+
const findingEvidence = findingRiskEvidence(gate.approved);
|
|
2496
|
+
let riskSignal = await readIntakeRiskSignal(artifactsDir);
|
|
2497
|
+
if (findingEvidence && riskSignal) {
|
|
2498
|
+
const raised = escalateRiskSignal(riskSignal, findingEvidence);
|
|
2499
|
+
// escalateRiskSignal returns the SAME reference when the evidence does not
|
|
2500
|
+
// raise the tier — only persist on an actual change (no byte-identical rewrite).
|
|
2501
|
+
if (raised !== riskSignal) {
|
|
2502
|
+
riskSignal = raised;
|
|
2503
|
+
await writeIntakeRiskSignal(artifactsDir, riskSignal);
|
|
2504
|
+
}
|
|
2431
2505
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
// Autonomy resolves from the PERSISTED session config (hoisted load
|
|
2442
|
-
// above) → env → attended default, never from the bare CLI options.
|
|
2443
|
-
resolveAutonomousMode({ sessionConfig: sessionConfigForDispatch }));
|
|
2444
|
-
if (gate.kind === "halt") {
|
|
2445
|
-
return gate.step;
|
|
2506
|
+
// T1 slice 3b — the lean tier is NOT zero-scrutiny: a `low`-tier run first runs
|
|
2507
|
+
// a bounded LIGHT adversarial review over the approved findings (the floor,
|
|
2508
|
+
// never off — `adversarialDepthForTier("low") === "light"`). A clear verdict
|
|
2509
|
+
// proceeds to the lean plan; a verdict that surfaces a real concern escalates
|
|
2510
|
+
// the risk signal and routes to the full pipeline below.
|
|
2511
|
+
if (gate.approved.length > 0 && riskSignal?.tier === "low") {
|
|
2512
|
+
const review = await runLeanLightReviewGate(root, artifactsDir, gate.approved);
|
|
2513
|
+
if (review.kind === "halt") {
|
|
2514
|
+
return review.step;
|
|
2446
2515
|
}
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
// (structured_audit), the only intake with a pre-existing finding set to judge.
|
|
2457
|
-
//
|
|
2458
|
-
// First fold the APPROVED set's finding-level risk (grounding / confidence /
|
|
2459
|
-
// coupling / systemic / architecture / count — the finding-QUALITY dimension the
|
|
2460
|
-
// intake path/breadth/intent signal doesn't see) INTO the shared risk signal as
|
|
2461
|
-
// escalate-on-evidence. This makes the tier the SINGLE classifier: there is no
|
|
2462
|
-
// separate fast-path boolean that can DISAGREE with it (a grounded handful
|
|
2463
|
-
// touching a risk subsystem stays `high` and takes the full pipeline, instead of
|
|
2464
|
-
// bypassing it as the old parallel `evaluateFastPath` allowed).
|
|
2465
|
-
const findingEvidence = findingRiskEvidence(gate.approved);
|
|
2466
|
-
let riskSignal = await readIntakeRiskSignal(artifactsDir);
|
|
2467
|
-
if (findingEvidence && riskSignal) {
|
|
2468
|
-
const raised = escalateRiskSignal(riskSignal, findingEvidence);
|
|
2469
|
-
// escalateRiskSignal returns the SAME reference when the evidence does not
|
|
2470
|
-
// raise the tier — only persist on an actual change (no byte-identical rewrite).
|
|
2471
|
-
if (raised !== riskSignal) {
|
|
2472
|
-
riskSignal = raised;
|
|
2473
|
-
await writeIntakeRiskSignal(artifactsDir, riskSignal);
|
|
2474
|
-
}
|
|
2516
|
+
if (review.kind === "escalate") {
|
|
2517
|
+
// Escalate-on-evidence: raise the signal to at least `medium` so the
|
|
2518
|
+
// full pipeline's adversarial depth is `full` (see slice 3a), then
|
|
2519
|
+
// fall through to the full pipeline.
|
|
2520
|
+
await writeIntakeRiskSignal(artifactsDir, escalateRiskSignal(riskSignal, {
|
|
2521
|
+
tier: "medium",
|
|
2522
|
+
reason: `lean light review surfaced a concern: ${review.concerns.join("; ")}`,
|
|
2523
|
+
}));
|
|
2524
|
+
process.stderr.write(`[remediate-code] Lean light review escalated (${review.concerns.join("; ")}); routing to the full contract pipeline.\n`);
|
|
2475
2525
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
return review.step;
|
|
2526
|
+
else {
|
|
2527
|
+
// Clear verdict → proceed with the lean plan.
|
|
2528
|
+
const leanPlan = buildLeanExtractedPlan(gate.approved, randomRunId("LEAN"));
|
|
2529
|
+
await writeJsonFile(intakePaths(artifactsDir).extractedPlan, leanPlan);
|
|
2530
|
+
process.stderr.write(`[remediate-code] Lean fast path (risk tier low): ${riskSignal.rationale.join("; ")}; light review clear. Routing to plan→implement.\n`);
|
|
2531
|
+
const planned = await handlePendingExtractedPlan(root, artifactsDir, { status: "pending" }, leanPlan);
|
|
2532
|
+
if (planned) {
|
|
2533
|
+
return planned;
|
|
2485
2534
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
tier: "medium",
|
|
2492
|
-
reason: `lean light review surfaced a concern: ${review.concerns.join("; ")}`,
|
|
2493
|
-
}));
|
|
2494
|
-
process.stderr.write(`[remediate-code] Lean light review escalated (${review.concerns.join("; ")}); routing to the full contract pipeline.\n`);
|
|
2495
|
-
}
|
|
2496
|
-
else {
|
|
2497
|
-
// Clear verdict → proceed with the lean plan.
|
|
2498
|
-
const leanPlan = buildLeanExtractedPlan(gate.approved, randomRunId("LEAN"));
|
|
2499
|
-
await writeJsonFile(intakePaths(artifactsDir).extractedPlan, leanPlan);
|
|
2500
|
-
process.stderr.write(`[remediate-code] Lean fast path (risk tier low): ${riskSignal.rationale.join("; ")}; light review clear. Routing to plan→implement.\n`);
|
|
2501
|
-
const planned = await handlePendingExtractedPlan(root, artifactsDir, { status: "pending" }, leanPlan);
|
|
2502
|
-
if (planned) {
|
|
2503
|
-
return planned;
|
|
2504
|
-
}
|
|
2505
|
-
// Defensive: a deterministically-built lean plan should always
|
|
2506
|
-
// normalize. If it somehow didn't, handlePendingExtractedPlan removed
|
|
2507
|
-
// the file; fall through to the full pipeline (the safety net) rather
|
|
2508
|
-
// than stalling the run.
|
|
2509
|
-
process.stderr.write("[remediate-code] Lean fast-path plan failed to materialize; falling back to the contract pipeline.\n");
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
// Seed the pipeline with the approved survivors only. When that set is
|
|
2513
|
-
// narrower than the originals (anything filtered or declined), route the
|
|
2514
|
-
// seed AND the pipeline's source inputs at a filtered file so a removed
|
|
2515
|
-
// finding can never re-enter via the raw audit-findings.json (tool-enforced).
|
|
2516
|
-
const approvedPayload = isRecord(auditFindings)
|
|
2517
|
-
? { ...auditFindings, findings: gate.approved }
|
|
2518
|
-
: { findings: gate.approved };
|
|
2519
|
-
let seedSourcePath = auditSource.path;
|
|
2520
|
-
if (gate.approved.length < originals.length) {
|
|
2521
|
-
await mkdir(contractPipelineDir(artifactsDir), { recursive: true });
|
|
2522
|
-
seedSourcePath = join(contractPipelineDir(artifactsDir), "approved-findings.json");
|
|
2523
|
-
await writeJsonFile(seedSourcePath, approvedPayload);
|
|
2524
|
-
reviewSourceSwap = { from: auditSource.path, to: seedSourcePath };
|
|
2525
|
-
}
|
|
2526
|
-
try {
|
|
2527
|
-
await writePathASeedFromFindings(artifactsDir, seedSourcePath, approvedPayload);
|
|
2528
|
-
}
|
|
2529
|
-
catch {
|
|
2530
|
-
// If the seed cannot be written, the pipeline still runs; the LLM
|
|
2531
|
-
// phases use the source files from sourcePaths.
|
|
2535
|
+
// Defensive: a deterministically-built lean plan should always
|
|
2536
|
+
// normalize. If it somehow didn't, handlePendingExtractedPlan removed
|
|
2537
|
+
// the file; fall through to the full pipeline (the safety net) rather
|
|
2538
|
+
// than stalling the run.
|
|
2539
|
+
process.stderr.write("[remediate-code] Lean fast-path plan failed to materialize; falling back to the contract pipeline.\n");
|
|
2532
2540
|
}
|
|
2533
2541
|
}
|
|
2542
|
+
// Seed the pipeline with the approved survivors only. When that set is
|
|
2543
|
+
// narrower than the originals (anything filtered or declined), route the
|
|
2544
|
+
// seed AND the pipeline's source inputs at a filtered file so a removed
|
|
2545
|
+
// finding can never re-enter via the raw audit-findings.json (tool-enforced).
|
|
2546
|
+
const approvedPayload = isRecord(auditFindings)
|
|
2547
|
+
? { ...auditFindings, findings: gate.approved }
|
|
2548
|
+
: { findings: gate.approved };
|
|
2549
|
+
let seedSourcePath = auditSource.path;
|
|
2550
|
+
if (gate.approved.length < originals.length) {
|
|
2551
|
+
await mkdir(contractPipelineDir(artifactsDir), { recursive: true });
|
|
2552
|
+
seedSourcePath = join(contractPipelineDir(artifactsDir), "approved-findings.json");
|
|
2553
|
+
await writeJsonFile(seedSourcePath, approvedPayload);
|
|
2554
|
+
reviewSourceSwap = { from: auditSource.path, to: seedSourcePath };
|
|
2555
|
+
}
|
|
2556
|
+
try {
|
|
2557
|
+
await writePathASeedFromFindings(artifactsDir, seedSourcePath, approvedPayload);
|
|
2558
|
+
}
|
|
2559
|
+
catch {
|
|
2560
|
+
// If the seed cannot be written, the pipeline still runs; the LLM
|
|
2561
|
+
// phases use the source files from sourcePaths.
|
|
2562
|
+
}
|
|
2534
2563
|
}
|
|
2535
2564
|
}
|
|
2536
2565
|
const paths = intakePaths(artifactsDir);
|
|
@@ -2538,14 +2567,12 @@ async function handleReadyIntakeContractPipeline(root, artifactsDir, options) {
|
|
|
2538
2567
|
if (existsSync(paths.brief)) {
|
|
2539
2568
|
sourcePaths.add(paths.brief);
|
|
2540
2569
|
}
|
|
2541
|
-
|
|
2542
|
-
for
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
: source.path);
|
|
2548
|
-
}
|
|
2570
|
+
for (const source of manifestSources) {
|
|
2571
|
+
// Swap the raw audit-findings.json for the approved-only filtered file so a
|
|
2572
|
+
// declined finding can never re-enter the pipeline as a source input.
|
|
2573
|
+
sourcePaths.add(reviewSourceSwap && source.path === reviewSourceSwap.from
|
|
2574
|
+
? reviewSourceSwap.to
|
|
2575
|
+
: source.path);
|
|
2549
2576
|
}
|
|
2550
2577
|
// Resolve the independent-critic dispatch capability from the SAME handshake
|
|
2551
2578
|
// (`resolveHostDispatchCapability`) implement dispatch uses — never a manual
|