audit-tools 0.51.10 → 0.52.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/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
- package/dist/audit/orchestrator/executorRunners.js +14 -3
- package/dist/audit/orchestrator/executorRunners.js.map +1 -1
- package/dist/audit/orchestrator/resultBaseline.d.ts +1 -1
- package/dist/audit/orchestrator/synthesisExecutors.d.ts +10 -3
- package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
- package/dist/audit/orchestrator/synthesisExecutors.js +5 -4
- package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
- package/dist/audit/reporting/synthesis.d.ts +7 -3
- package/dist/audit/reporting/synthesis.d.ts.map +1 -1
- package/dist/audit/reporting/synthesis.js +13 -2
- package/dist/audit/reporting/synthesis.js.map +1 -1
- package/dist/remediate/phases/close.d.ts +11 -4
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +78 -14
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts +19 -35
- package/dist/remediate/phases/closeVerifyHeadEvidence.d.ts.map +1 -1
- package/dist/remediate/phases/closeVerifyHeadEvidence.js +38 -12
- package/dist/remediate/phases/closeVerifyHeadEvidence.js.map +1 -1
- package/dist/remediate/phases/closeVerifyLandingGates.d.ts +85 -0
- package/dist/remediate/phases/closeVerifyLandingGates.d.ts.map +1 -0
- package/dist/remediate/phases/closeVerifyLandingGates.js +48 -0
- package/dist/remediate/phases/closeVerifyLandingGates.js.map +1 -0
- package/dist/remediate/state/types.d.ts +31 -4
- package/dist/remediate/state/types.d.ts.map +1 -1
- package/dist/remediate/state/types.js +12 -1
- package/dist/remediate/state/types.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +23 -1
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +122 -10
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.js +11 -7
- package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.js +41 -5
- package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +23 -7
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/types/options.d.ts +6 -0
- package/dist/remediate/types/options.d.ts.map +1 -1
- package/dist/shared/contentKey.d.ts +4 -4
- package/dist/shared/contentKey.js +3 -3
- package/dist/shared/git.d.ts +31 -0
- package/dist/shared/git.d.ts.map +1 -1
- package/dist/shared/git.js +105 -0
- package/dist/shared/git.js.map +1 -1
- package/dist/shared/index.d.ts +7 -5
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +5 -4
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/prompts.d.ts +14 -3
- package/dist/shared/prompts.d.ts.map +1 -1
- package/dist/shared/prompts.js +17 -4
- package/dist/shared/prompts.js.map +1 -1
- package/dist/shared/reporting/auditDeliverable.d.ts +8 -3
- package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -1
- package/dist/shared/reporting/auditDeliverable.js +10 -3
- package/dist/shared/reporting/auditDeliverable.js.map +1 -1
- package/dist/shared/tooling/landingGates.d.ts +118 -0
- package/dist/shared/tooling/landingGates.d.ts.map +1 -0
- package/dist/shared/tooling/landingGates.js +314 -0
- package/dist/shared/tooling/landingGates.js.map +1 -0
- package/dist/shared/tooling/projectTestAdmission.d.ts +16 -0
- package/dist/shared/tooling/projectTestAdmission.d.ts.map +1 -1
- package/dist/shared/tooling/projectTestAdmission.js +73 -10
- package/dist/shared/tooling/projectTestAdmission.js.map +1 -1
- package/dist/shared/tooling/testCommand.d.ts.map +1 -1
- package/dist/shared/tooling/testCommand.js +10 -0
- package/dist/shared/tooling/testCommand.js.map +1 -1
- package/dist/shared/types/finding.d.ts +56 -0
- package/dist/shared/types/finding.d.ts.map +1 -1
- package/dist/shared/types/finding.js +27 -0
- package/dist/shared/types/finding.js.map +1 -1
- package/dist/shared/types/intentCheckpoint.d.ts +2 -2
- package/dist/shared/validation/findingsReport.d.ts +13 -2
- package/dist/shared/validation/findingsReport.d.ts.map +1 -1
- package/dist/shared/validation/findingsReport.js +19 -2
- package/dist/shared/validation/findingsReport.js.map +1 -1
- package/docs/audit-pkg/product.md +2 -4
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// sites-pinned: tests/remediate/contract-pipeline.test.ts
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// sites-pinned: tests/remediate/contract-pipeline.test.ts, tests/remediate/dc3.test.ts
|
|
2
|
+
// contract-pipeline: the promotion computes no finding field FindingSchema would drop.
|
|
3
|
+
// dc3: the fan-out wording (needs, not mechanism) and the TRANSPORT report for a
|
|
4
|
+
// partially returned wave.
|
|
4
5
|
/**
|
|
5
6
|
* Contract-pipeline gate for ALL remediation starts (both paths).
|
|
6
7
|
*
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
import { existsSync } from "node:fs";
|
|
26
27
|
import { mkdir, readFile, rename, rm } from "node:fs/promises";
|
|
27
28
|
import { isAbsolute, join, resolve } from "node:path";
|
|
28
|
-
import { writeJsonFile, readOptionalJsonFile, formatValidationIssues, hashContent, isRecord, withFsRetry, projectApprovedFindings, captureStepBoundaryFriction, climbOutOfAuditTools, partitionCommandsByDeclaredShape, splitSequentialCommandChain, normalizeRepoPath, repoRelativePath, toPosixPath, neutralProjectFacts, } from "audit-tools/shared";
|
|
29
|
+
import { writeJsonFile, readOptionalJsonFile, formatValidationIssues, hashContent, isRecord, withFsRetry, auditReadOf, projectApprovedFindings, captureStepBoundaryFriction, climbOutOfAuditTools, partitionCommandsByDeclaredShape, splitSequentialCommandChain, normalizeRepoPath, repoRelativePath, toPosixPath, neutralProjectFacts, } from "audit-tools/shared";
|
|
29
30
|
import { createStepEmissionScaffold, } from "../../shared/steps/stepEmissionScaffold.js";
|
|
30
31
|
import { OBLIGATION_KIND_PRIORITY, } from "../contractPipeline/obligationKinds.js";
|
|
31
32
|
import { CP_ARTIFACT_NAMES, contractArtifactExists, contractArtifactFilePath, contractInputFilePath, contractPipelineDir, detectStaleArtifacts, dropReviewSnapshot, envelopePayload, envelopeSemanticHash, isEnvelope, pathASeedFilePath, payloadSemanticHash, readContractArtifact, stampToolCreatedAt, writeContractArtifact, writeDerivedContractArtifact, } from "../contractPipeline/artifactStore.js";
|
|
@@ -1498,6 +1499,48 @@ ${outputPaths.map((p, i) => `${i + 1}. \`${p}\` (${phases[i]})`).join("\n")}`;
|
|
|
1498
1499
|
stopCondition: `Stop after writing all ${phases.length} collapsed artifacts (${phases.join(", ")}) and running next-step once.`,
|
|
1499
1500
|
});
|
|
1500
1501
|
}
|
|
1502
|
+
/**
|
|
1503
|
+
* The TRANSPORT-failure report for a re-emitted module wave, or `""` on a first
|
|
1504
|
+
* dispatch.
|
|
1505
|
+
*
|
|
1506
|
+
* A shard absent after dispatch is NOT a refusal of the work and NOT a contract
|
|
1507
|
+
* the worker got wrong — it is an item that was published and did not come back.
|
|
1508
|
+
* The two used to be indistinguishable: the re-emitted wave was byte-identical
|
|
1509
|
+
* to the first one, so a host that lost items mid-output saw only "run this
|
|
1510
|
+
* wave again", and nothing named the loss. Naming it is what lets a host
|
|
1511
|
+
* respond to the right thing (re-deliver the missing items, or report that it
|
|
1512
|
+
* cannot run them) instead of re-running a wave whose other shards already
|
|
1513
|
+
* exist.
|
|
1514
|
+
*
|
|
1515
|
+
* The classification is stated in the prompt because that is the channel the
|
|
1516
|
+
* host actually reads — the re-emission IS the delivery. It is a RE-EMISSION
|
|
1517
|
+
* report, so the absence is derived from the shards on disk, never from a
|
|
1518
|
+
* worker's claim that it wrote one.
|
|
1519
|
+
*/
|
|
1520
|
+
function transportReport(absent, total) {
|
|
1521
|
+
// A PARTIAL return is the transport fact. All-absent is not reported, and the
|
|
1522
|
+
// reason is that NOTHING PERSISTED CAN TELL THE TWO APART. Searched, so the
|
|
1523
|
+
// claim is not an assumption: no dispatch marker exists under the artifacts
|
|
1524
|
+
// dir (the wave directory holds only the shards `scanModuleShards` reads, so a
|
|
1525
|
+
// wave emitted-and-lost leaves the same entries as one never emitted); the
|
|
1526
|
+
// rejected-payload archive covers AGGREGATED artifacts, never a shard, so an
|
|
1527
|
+
// unparseable shard is simply absent; `current-step.json` is overwritten by
|
|
1528
|
+
// EVERY emission and is a request, not a receipt — the tool cannot observe
|
|
1529
|
+
// that a host read it; and this writer has no `runLogger` seam at all, so no
|
|
1530
|
+
// emission is recorded in the run log either. Inventing the distinction would
|
|
1531
|
+
// mean guessing, and guessing here fires a transport failure on every ordinary
|
|
1532
|
+
// first emission — a report that is wrong on the normal case is one a host
|
|
1533
|
+
// learns to ignore, which costs more than the distinguishable half is worth.
|
|
1534
|
+
//
|
|
1535
|
+
// So the report is scoped to what it can actually ASSERT: some items came back
|
|
1536
|
+
// and some did not. That case needs no dispatch record, because the returned
|
|
1537
|
+
// shards ARE the record that a wave was published.
|
|
1538
|
+
if (absent.length === 0 || absent.length === total)
|
|
1539
|
+
return "";
|
|
1540
|
+
const list = absent.map((name) => `\`${name}\``).join(", ");
|
|
1541
|
+
return `> **TRANSPORT failure — ${absent.length} of ${total} items did not return.** The previous wave published ${list}, and no valid shard exists for ${absent.length === 1 ? "it" : "them"} on disk. This is a DELIVERY failure, not a refusal: the item was published and did not come back, which says nothing about whether the work is right. Re-deliver ${absent.length === 1 ? "that item" : "those items"} — the other ${total - absent.length} shard(s) are already present and must NOT be re-run. If the host cannot deliver ${absent.length === 1 ? "it" : "them"} independently, say so rather than serializing the whole wave through one context.
|
|
1542
|
+
`;
|
|
1543
|
+
}
|
|
1501
1544
|
/**
|
|
1502
1545
|
* DC-3: fan a parallel phase out to one bounded item per module. The host owns
|
|
1503
1546
|
* grouping, concurrency, and execution choices; this tool supplies only the
|
|
@@ -1511,6 +1554,15 @@ async function writeParallelModuleWaveStep(ctx, phase) {
|
|
|
1511
1554
|
if (modules.length <= 1) {
|
|
1512
1555
|
return writeContractPhaseStep(ctx, phase);
|
|
1513
1556
|
}
|
|
1557
|
+
// THE TRANSPORT REPORT. This wave is emitted either as the FIRST dispatch (no
|
|
1558
|
+
// shard on disk yet — an empty missing set) or as a RE-emission after a
|
|
1559
|
+
// previous wave came back with shards absent. The two are different facts and
|
|
1560
|
+
// used to render identically: a host that lost two of nine items mid-output
|
|
1561
|
+
// got the same undiagnosed "run this wave" it got the first time, so the only
|
|
1562
|
+
// thing that ever noticed the loss was a repeat of the same prompt. The set is
|
|
1563
|
+
// read from disk here rather than threaded through the plan, because the plan
|
|
1564
|
+
// is a pure classifier and the shard scan is a filesystem fact.
|
|
1565
|
+
const absent = (await scanModuleShards(ctx.artifactsDir, phase, modules)).missing;
|
|
1514
1566
|
const inputArtifact = "module_decomposition";
|
|
1515
1567
|
const inputPaths = ["goal_spec", "context_bundle", "module_decomposition"].map((key) => `- \`${ctx.artifactPaths[key]}\` (${key})`);
|
|
1516
1568
|
const moduleLines = modules
|
|
@@ -1535,17 +1587,31 @@ async function writeParallelModuleWaveStep(ctx, phase) {
|
|
|
1535
1587
|
const taskVerb = "draft its module contract";
|
|
1536
1588
|
const cwdNote = `\n> Set the shell/tool working directory to \`${ctx.root}\` before running any commands.\n`;
|
|
1537
1589
|
const nextCommand = loaderCommand("next-step");
|
|
1590
|
+
// THE STEP STATES WHAT IT NEEDS, NOT A MECHANISM. It used to say "dispatch one
|
|
1591
|
+
// sub-agent PER MODULE" — a mechanism the host may not have (in-process
|
|
1592
|
+
// subagents are not universal, and the fallback is a shell-out lane this tool
|
|
1593
|
+
// neither knows nor sizes for). Two of nine such dispatches died mid-output
|
|
1594
|
+
// and only the step's presence check noticed. What the work actually requires
|
|
1595
|
+
// is stated instead: N INDEPENDENT CONTEXTS with no shared authorship. A host
|
|
1596
|
+
// with subagents dispatches N of them; a host without runs the items however
|
|
1597
|
+
// it can, as long as no single context drafts both sides of a seam — which is
|
|
1598
|
+
// the property the seam-reconciliation gate downstream depends on.
|
|
1538
1599
|
const prompt = `# Per-Module Contract Drafting (${modules.length} modules)
|
|
1539
1600
|
|
|
1540
|
-
This phase publishes one bounded item per module. Complete all ${modules.length} items below; the host owns how they are grouped or executed.
|
|
1601
|
+
This phase publishes one bounded item per module. Complete all ${modules.length} items below; the host owns how they are grouped or executed.
|
|
1602
|
+
${transportReport(absent, modules.length)}
|
|
1603
|
+
|
|
1604
|
+
**What this work needs:** ${modules.length} independent contexts, one per module — no shared authorship. Each module's contract must be drafted by a context that has NOT drafted any module it seams against. That independence is the input the seam reconciliation relies on: a single context drafting both sides of a seam reconciles the seam against itself and reports no mismatch, however mismatched the interfaces are. The host chooses the mechanism; if it has no way to run ${modules.length} independent contexts, say so rather than serializing them through one — a serialized draft is worse than a re-emitted wave, because it produces a wrong aggregate that nothing downstream can detect.
|
|
1605
|
+
|
|
1606
|
+
Each item reads only its module's file scope, then writes ONLY that module's contract shard — no item owns both sides of a seam, and no item writes the aggregated artifact.
|
|
1541
1607
|
${cwdNote}
|
|
1542
|
-
## Shared Inputs (every
|
|
1608
|
+
## Shared Inputs (every item may read these)
|
|
1543
1609
|
|
|
1544
1610
|
${inputPaths.join("\n")}
|
|
1545
1611
|
|
|
1546
|
-
## Per-Module Assignments — one
|
|
1612
|
+
## Per-Module Assignments — one independent context each
|
|
1547
1613
|
|
|
1548
|
-
For each module,
|
|
1614
|
+
For each module, an independent context reads its file scope from \`${inputArtifact}\` and ${taskVerb}, writing the result to the module's shard path:
|
|
1549
1615
|
|
|
1550
1616
|
${moduleLines}
|
|
1551
1617
|
|
|
@@ -1555,13 +1621,13 @@ Each shard must be a single JSON object of this shape (the orchestrator merges a
|
|
|
1555
1621
|
${perModuleSchema}
|
|
1556
1622
|
\`\`\`
|
|
1557
1623
|
|
|
1558
|
-
## After
|
|
1624
|
+
## After Every Item Finishes
|
|
1559
1625
|
|
|
1560
1626
|
Once every module's shard above has been written (all ${modules.length}), run:
|
|
1561
1627
|
|
|
1562
1628
|
\`${nextCommand}\`
|
|
1563
1629
|
|
|
1564
|
-
The orchestrator verifies every module shard is present, merges them into \`${PHASE_TO_ARTIFACT[phase]}\`, and advances. If any shard is missing, this same wave is re-emitted for the missing modules — never a partial aggregate.
|
|
1630
|
+
The orchestrator verifies every module shard is present, merges them into \`${PHASE_TO_ARTIFACT[phase]}\`, and advances. If any shard is missing, this same wave is re-emitted for the missing modules — never a partial aggregate. Do not re-run items whose shard is already present.
|
|
1565
1631
|
|
|
1566
1632
|
**Stop after the per-module shards are written and you run next-step.** Do not edit source files. Do not write the aggregated artifact. Do not advance further.
|
|
1567
1633
|
`;
|
|
@@ -3360,6 +3426,52 @@ export async function collectPathARefusals(artifactsDir) {
|
|
|
3360
3426
|
seedPresent: pathASeed !== undefined,
|
|
3361
3427
|
}).refusals;
|
|
3362
3428
|
}
|
|
3429
|
+
/**
|
|
3430
|
+
* What the AUDIT read, for the run whose Path-A seed lives in `artifactsDir` —
|
|
3431
|
+
* the value plan application stamps onto `state.plan.audit_read`.
|
|
3432
|
+
*
|
|
3433
|
+
* Read by the TOOL from the seed's own source report, which passed the strict
|
|
3434
|
+
* shared validator (`projectApprovedFindings`) before it may answer. It never
|
|
3435
|
+
* rides `extracted-plan.json`: that file is host-writable, and the close
|
|
3436
|
+
* phase's evidence leg turns this commit into terminal dispositions, so a
|
|
3437
|
+
* host-supplied value would let a host author its own `refuted`.
|
|
3438
|
+
*
|
|
3439
|
+
* BOUND TO THE SEED'S OWN DIGEST. The seed recorded a sha256 of the source
|
|
3440
|
+
* report when it was built (`source_digests`); the bytes read here must hash to
|
|
3441
|
+
* it, so a report swapped afterwards — even for another VALID report — answers
|
|
3442
|
+
* `null`. The file is read ONCE and the same bytes are hashed and parsed. A seed
|
|
3443
|
+
* that carries no digest for its source binds nothing, and an unbound commit is
|
|
3444
|
+
* not one this function will vouch for.
|
|
3445
|
+
*
|
|
3446
|
+
* `null` — "no commit is known" — when there is no seed (the run did not start
|
|
3447
|
+
* from a findings report), the source is unbound, changed, unreadable or
|
|
3448
|
+
* invalid, or the report itself states `null`.
|
|
3449
|
+
*/
|
|
3450
|
+
export async function readSeedAuditRead(artifactsDir) {
|
|
3451
|
+
const pathASeed = await readOptionalJsonFile(pathASeedFilePath(artifactsDir));
|
|
3452
|
+
if (!pathASeed)
|
|
3453
|
+
return null;
|
|
3454
|
+
const bound = (pathASeed.source_digests ?? []).find((entry) => entry?.path === pathASeed.audit_findings_path);
|
|
3455
|
+
if (typeof bound?.sha256 !== "string")
|
|
3456
|
+
return null;
|
|
3457
|
+
let source;
|
|
3458
|
+
try {
|
|
3459
|
+
const bytes = await readFile(pathASeed.audit_findings_path);
|
|
3460
|
+
if (hashContent(bytes) !== bound.sha256)
|
|
3461
|
+
return null;
|
|
3462
|
+
source = JSON.parse(bytes.toString("utf8"));
|
|
3463
|
+
}
|
|
3464
|
+
catch {
|
|
3465
|
+
return null;
|
|
3466
|
+
}
|
|
3467
|
+
try {
|
|
3468
|
+
projectApprovedFindings(source);
|
|
3469
|
+
}
|
|
3470
|
+
catch {
|
|
3471
|
+
return null;
|
|
3472
|
+
}
|
|
3473
|
+
return auditReadOf(source);
|
|
3474
|
+
}
|
|
3363
3475
|
/**
|
|
3364
3476
|
* Convert a completed ImplementationDAG into the extracted-plan.json format
|
|
3365
3477
|
* that the existing handlePendingExtractedPlan/applyPlanPipeline path consumes.
|