Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
179 commits
Select commit Hold shift + click to select a range
ce9e29d
refactor(plugin): remove unused Deep scheduler history
mldangelo-oai Sep 12, 2026
35deeaf
Preserve distinct source remediations in published reports
mldangelo-oai Sep 12, 2026
5113f40
Reject unsupported Deep Scan recovery before ownership changes
mldangelo-oai Sep 12, 2026
88a811f
Read Deep Scan state from one SQLite snapshot
mldangelo-oai Sep 12, 2026
16a456e
Distinguish missing scan usage from reported zero
mldangelo-oai Sep 12, 2026
fca3a93
Honor worker checkpoint heads during result recovery
mldangelo-oai Sep 12, 2026
af83456
Preserve Deep Scan inputs and add execution history schema
mldangelo-oai Sep 12, 2026
99a6074
test(plugin): define shared audit acceptance contract
mldangelo-oai Sep 12, 2026
7f6f385
fix(plugin): keep Deep publication failures replayable
mldangelo-oai Sep 12, 2026
697d689
test: replay accepted sources across reducer batches
mldangelo-oai Sep 12, 2026
844b86d
test: compare migration history across coordinator claims
mldangelo-oai Sep 12, 2026
4683175
Fence Deep publication to the current coordinator and aggregate
mldangelo-oai Sep 12, 2026
81c3af5
test: align publication fixtures with supported recovery
mldangelo-oai Sep 12, 2026
de96630
Read selected Deep Scan finalization before recovery
mldangelo-oai Sep 12, 2026
0663681
refactor: share Codex session construction and stream reduction
mldangelo-oai Sep 12, 2026
0bba867
fix: bind Deep worker model selections to each scan
mldangelo-oai Sep 12, 2026
ce2fcc8
test: exercise Deep lifecycle against installed plugins
mldangelo-oai Sep 12, 2026
6ab9c7b
test: cover managed modes and follow-up child sessions
mldangelo-oai Sep 12, 2026
5f7bbe2
Reserve frozen checkpoint selections and guard requested workflows
mldangelo-oai Sep 12, 2026
69df53e
test(sdk): exercise installed Deep scan lifecycle and recovery
mldangelo-oai Sep 12, 2026
a6a2dd4
test(sdk): allow the shared session module in package checks
mldangelo-oai Sep 12, 2026
a548cea
Reserve original Deep Scan usage ownership metadata
mldangelo-oai Sep 12, 2026
85bcdbe
Freeze checkpoint selections for stopped scan replay
mldangelo-oai Sep 12, 2026
f3fa2e9
Preserve accepted source coverage during stopped recovery
mldangelo-oai Sep 12, 2026
d62c174
Restore original Deep Scan worker settings after coordinator replacement
mldangelo-oai Sep 12, 2026
4606bfd
Preserve accepted source coverage through deep scan publication
mldangelo-oai Sep 12, 2026
a6be9e5
Keep coverage accounting outside reducer model inputs
mldangelo-oai Sep 12, 2026
07e1b72
Isolate real coverage publication fixtures from module mocks
mldangelo-oai Sep 12, 2026
b45c1be
Preserve accepted reducer bytes during coverage recovery
mldangelo-oai Sep 12, 2026
b1fab6b
Gate persisted source coverage by workflow compatibility
mldangelo-oai Sep 12, 2026
b8bd35f
test: cover persisted worker settings and packaged config helper
mldangelo-oai Sep 12, 2026
6cb32c6
refactor: remove the pass-through Codex session wrapper
mldangelo-oai Sep 12, 2026
7501517
Verify versioned reducer snapshots and concurrent bindings
mldangelo-oai Sep 12, 2026
d15f27b
test: verify refreshed worker auth and resumed permission checks
mldangelo-oai Sep 12, 2026
c8b3bcc
refactor: share accepted audit attempts across managed scans
mldangelo-oai Sep 12, 2026
2c249d7
test(sdk): require shared audit module in installed package
mldangelo-oai Sep 12, 2026
6d3d75d
Keep restored worker executable and home paths consistent
mldangelo-oai Sep 12, 2026
75aa3d4
test(sdk): load shared session code in worker shutdown fixtures
mldangelo-oai Sep 12, 2026
1e90097
Recover omitted worker selections from the original native parent
mldangelo-oai Sep 12, 2026
adda049
Preserve receipt ownership for immutable discovery inputs
mldangelo-oai Sep 12, 2026
92ccb9c
Verify material fixes alongside recovered source coverage
mldangelo-oai Sep 12, 2026
ad90b17
fix: replay Deep publication from its selected accepted input
mldangelo-oai Sep 12, 2026
fdebdb5
fix: complete selected Deep results in the enclosing scan
mldangelo-oai Sep 12, 2026
538ad5c
test: cover original native selections and packaged completion helper
mldangelo-oai Sep 12, 2026
578b0fd
fix: preserve explicit cancellation during selected publication
mldangelo-oai Sep 12, 2026
77c6cba
Reconcile scan usage by owned execution and charged response
mldangelo-oai Sep 12, 2026
d7fd7b8
Reconcile native usage with durable execution attribution
mldangelo-oai Sep 12, 2026
04661c9
fix: preserve accepted attempts and compact merge receipts
mldangelo-oai Sep 12, 2026
4b903ba
Count native charged responses and retain usage uncertainty
mldangelo-oai Sep 12, 2026
064fafc
refactor: keep execution projections with scan usage
mldangelo-oai Sep 12, 2026
be95713
Fence publication to committed finalization input
mldangelo-oai Sep 12, 2026
2d61d8e
Recover and publish registered accepted result references
mldangelo-oai Sep 12, 2026
6e91224
Recover Deep Scan workers from accepted checkpoint references
mldangelo-oai Sep 12, 2026
7aeb8ae
fix: stop selected publication on cost limits and client close
mldangelo-oai Sep 12, 2026
9610d25
Test selected publication and stop recovery across process loss
mldangelo-oai Sep 12, 2026
d932ab6
Test cost completion and cancellation ordering with saved selections
mldangelo-oai Sep 12, 2026
f5b72e9
Verify selected terminal causes survive stop and recovery
mldangelo-oai Sep 12, 2026
d0a5e9c
Use committed input attempts in reducer execution context
mldangelo-oai Sep 12, 2026
7b62268
Check persisted reducer content separately from coverage projection
mldangelo-oai Sep 12, 2026
98d419c
Return legacy scan records in budget recovery fixtures
mldangelo-oai Sep 12, 2026
2d3baaa
test: attribute selected publication cost fixtures to native events
mldangelo-oai Sep 12, 2026
7909f93
test: cover managed scan and follow-up service tiers
mldangelo-oai Sep 12, 2026
dcdcd7e
fix: restore completed artifacts after follow-up cancellation
mldangelo-oai Sep 12, 2026
3931039
Cover recovery from recorded immutable inputs
mldangelo-oai Sep 12, 2026
4883ec3
Test original native turn selections and missing recovery history
mldangelo-oai Sep 12, 2026
9b3f703
Verify selected workflow ownership and observer compatibility
mldangelo-oai Sep 12, 2026
4495367
Verify original discovery context and deadline through restart
mldangelo-oai Sep 12, 2026
d3956fb
Recover worker selections from the recorded original owner turn
mldangelo-oai Sep 12, 2026
858da4c
Verify original settings and usage survive continuation handoff
mldangelo-oai Sep 12, 2026
dfe353e
test(sdk): preserve installed Deep result conversations
mldangelo-oai Sep 12, 2026
fd0e3df
test(plugin): account for immutable accepted checkpoint fixtures
mldangelo-oai Sep 12, 2026
8d05203
Recover legacy accepted inputs from frozen merge references
mldangelo-oai Sep 12, 2026
ea56607
Verify selected finalization preserves coverage and material fixes
mldangelo-oai Sep 12, 2026
d93ca5a
Recover missing original selections in existing execution snapshots
mldangelo-oai Sep 12, 2026
3a3bb34
Test selection commit loss before and after stopped replay
mldangelo-oai Sep 12, 2026
1e7ecbd
Recover original selections from native applied settings records
mldangelo-oai Sep 12, 2026
1b41a96
fix(qa): inspect compressed package assets as decoded text
mldangelo-oai Sep 12, 2026
440af39
Enable persisted Deep finalization for new scans
mldangelo-oai Sep 12, 2026
a7a20c4
Align managed audit admission with persisted worker semantics
mldangelo-oai Sep 12, 2026
f2ebd74
Verify accepted bytes during stopped result recovery
mldangelo-oai Sep 12, 2026
4ef3522
Recover committed scan completion and finish selected cost stops
mldangelo-oai Sep 12, 2026
b8147b2
Cover canceled follow-up after recovering a completion receipt
mldangelo-oai Sep 12, 2026
c17bd15
Keep legacy coverage fixtures explicit after workflow activation
mldangelo-oai Sep 12, 2026
261a825
test(sdk): verify persisted Deep package execution state
mldangelo-oai Sep 12, 2026
0a8b31d
test: assert v2 coverage in attempt replay
mldangelo-oai Sep 12, 2026
412fed6
Test complete native selections against legacy turn fields
mldangelo-oai Sep 12, 2026
82248b2
Verify historical settings upgrades and default selected recovery
mldangelo-oai Sep 12, 2026
9ed7afc
Preserve recorded native absent tier with explicit provenance
mldangelo-oai Sep 12, 2026
2b02eab
fix(sdk): recover committed budget completion receipts
mldangelo-oai Sep 12, 2026
c02b33b
test: preserve Node library resolution in worker fixtures
mldangelo-oai Sep 12, 2026
47f4029
fix(sdk): preserve stop errors after completed budget results
mldangelo-oai Sep 12, 2026
56a9ff9
fix(ci): resolve native proof helper dependencies from SDK
mldangelo-oai Sep 12, 2026
6e01a59
Preserve native usage coverage across null token counters
mldangelo-oai Sep 12, 2026
db82e96
test(sdk): match native Windows child executable paths
mldangelo-oai Sep 12, 2026
01fab0e
fix(deep-scan): ignore native compatibility summaries
mldangelo-oai Sep 12, 2026
696cdd1
fix: cancel selected scans without a status read
mldangelo-oai Sep 12, 2026
38ba26d
test: resolve shared SDK imports from MCP dependencies
mldangelo-oai Sep 12, 2026
c846b91
test: detach proof dependency junction with Node
mldangelo-oai Sep 12, 2026
7d640df
fix: reject unsupported stopped scan publication
mldangelo-oai Sep 12, 2026
946c6e2
test: cover supported stopped publication workflows
mldangelo-oai Sep 12, 2026
55f98bf
fix(deep-scan): capture original summary before owner launch
mldangelo-oai Sep 12, 2026
74111b2
fix(deep-scan): preserve uncapped deny glob expansion
mldangelo-oai Sep 12, 2026
051d520
test: include reasoning summary in package contract
mldangelo-oai Sep 12, 2026
36c1585
fix: resolve MCP builds with project dependencies
mldangelo-oai Sep 12, 2026
6732e36
fix: preserve TOML module selection in MCP builds
mldangelo-oai Sep 12, 2026
302e358
fix(deep-scan): persist original settings before recovery
mldangelo-oai Sep 12, 2026
6fb0afe
fix(deep-scan): preserve legacy settings recovery
mldangelo-oai Sep 12, 2026
a68a41d
fix(deep-scan): persist provider selections without definitions
mldangelo-oai Sep 12, 2026
c7ae86a
fix: cancel selected scans without a cleanup state read
mldangelo-oai Sep 12, 2026
20d7cae
test: cover selected cancellation with a lost cleanup state response
mldangelo-oai Sep 12, 2026
7f21c57
fix(deep-scan): keep recovered settings read-only
mldangelo-oai Sep 12, 2026
af499a4
fix: finalize owned cost-limited Deep scans from accepted evidence
mldangelo-oai Sep 12, 2026
2b8182e
fix: preserve budget scope and replay unmerged coverage once
mldangelo-oai Sep 12, 2026
a410b99
Preserve worker usage across Codex home changes
mldangelo-oai Sep 12, 2026
f1a7c63
Include recorded worker homes in scan usage
mldangelo-oai Sep 12, 2026
ac064f1
fix: cancel owned deep scans across resume and selection boundaries
mldangelo-oai Sep 12, 2026
8cd9073
Include inherited SQLite state in worker usage
mldangelo-oai Sep 12, 2026
335a7e9
Read aliased scan session directories once
mldangelo-oai Sep 12, 2026
b04068b
Keep model usage with the most complete session copy
mldangelo-oai Sep 12, 2026
7de2361
fix: recover interrupted budget finalization locally
mldangelo-oai Sep 12, 2026
dc60161
Cancel registered Deep scans before thread initialization
mldangelo-oai Sep 12, 2026
6632cc3
Replay sealed budget results through parent completion
mldangelo-oai Sep 12, 2026
fc3180c
Preserve distinct unmerged coverage surfaces at cost completion
mldangelo-oai Sep 12, 2026
210dcae
Keep complete usage from copied worker rollouts
mldangelo-oai Sep 12, 2026
01f57e1
Preserve event occurrences and expose known cost internally
mldangelo-oai Sep 12, 2026
3f1560d
Enforce known cost bounds without publishing unknown totals
mldangelo-oai Sep 12, 2026
63a8c16
Recover worker usage from recorded session files
mldangelo-oai Sep 12, 2026
ba996ac
Clear resolved rollout lookup warnings
mldangelo-oai Sep 12, 2026
8fd0c1d
Reject unsupported scan protocols before completion writes
mldangelo-oai Sep 12, 2026
1405f83
Check completion replay and budget ownership before writes
mldangelo-oai Sep 12, 2026
f255364
Preserve helper CLI behavior through linked plugin paths
mldangelo-oai Sep 12, 2026
1249ab6
test: align linked helper fixture formatting
mldangelo-oai Sep 12, 2026
5a8d684
Remove redundant worker cancellation branches
mldangelo-oai Sep 12, 2026
3d795c4
Give boolean test cases distinct report names
mldangelo-oai Sep 12, 2026
8f31871
Clarify draft tool metadata guidance
mldangelo-oai Sep 13, 2026
d64a5e6
Clarify ownership in draft tool guidance
mldangelo-oai Sep 13, 2026
8e9c9c3
Keep draft tool tests focused on behavior
mldangelo-oai Sep 13, 2026
3db3e3d
Remove unused coordinator scheduler result bookkeeping
mldangelo-oai Sep 13, 2026
f6f023a
Retain only the latest recovered reducer result and path
mldangelo-oai Sep 13, 2026
b4a0a19
docs: distinguish Deep Scan coverage from completion
mldangelo-oai Sep 13, 2026
65659f9
fix(plugin): expose canonical coverage in completion responses
mldangelo-oai Sep 13, 2026
7be88f1
test: preserve checkpoint publication fault coverage
mldangelo-oai Sep 13, 2026
dac731e
test(plugin): assert completion coverage fixture branches
mldangelo-oai Sep 13, 2026
bbc3b23
Preserve coverage rejection reasons in reports
mldangelo-oai Sep 13, 2026
8e67e07
test: retain frozen checkpoint migration state checks
mldangelo-oai Sep 13, 2026
9074824
Preserve coverage source descriptions during projection
mldangelo-oai Sep 13, 2026
a603a91
Preserve coverage identity when replaying budget drafts
mldangelo-oai Sep 13, 2026
4f04415
Recover repeated coverage projections after interruption
mldangelo-oai Sep 13, 2026
8df806d
Validate Deep Scan writeup files before accepting results
mldangelo-oai Sep 13, 2026
005f5bd
Stream plain saved-logs JSON before aggregate formatting
mldangelo-oai Sep 13, 2026
01b0346
Include saved-logs formatter in package checks
mldangelo-oai Sep 13, 2026
c8aa1ab
Clarify scan draft candidate transitions and preservation
mldangelo-oai Sep 13, 2026
7b81d39
Preserve stale-skills notice in streamed saved-log JSON
mldangelo-oai Sep 13, 2026
4615618
Merge saved-log JSON streaming and compatibility fixes
mldangelo-oai Sep 13, 2026
59abaa7
Fix custom validation compatibility with current scan guidance
mldangelo-oai Sep 14, 2026
60b6ad9
fix: read recovered Deep Scan logs from the recorded Codex home
mldangelo-oai Sep 14, 2026
cbac786
refactor: share recorded scan home lookup
mldangelo-oai Sep 14, 2026
0fa83d8
Fix saved log truncation from copied rollout prefixes
mldangelo-oai Sep 14, 2026
4c0039a
Compare saved rollout extensions after scan attribution
mldangelo-oai Sep 14, 2026
df4b980
Describe empty partial reports without denying saved validation
mldangelo-oai Sep 14, 2026
177a966
test: align budget report assertions with partial summaries
mldangelo-oai Sep 14, 2026
1c6a5d6
Preserve optional finding identities in budget publication
mldangelo-oai Sep 14, 2026
e6a43b8
Preserve accepted findings with generated sibling identities
mldangelo-oai Sep 14, 2026
d3d68a5
test: refresh Windows timings for long scan tests
mldangelo-oai Sep 14, 2026
30c9664
Complete native Deep scans after observers detach
mldangelo-oai Sep 14, 2026
0e89f38
Keep native finalization cases in the publication test helper
mldangelo-oai Sep 14, 2026
d80ca3a
Wait for the owning finalizer in the remote observer control
mldangelo-oai Sep 14, 2026
652298c
Preserve native completion errors for active observers
mldangelo-oai Sep 14, 2026
21f92f6
fix(deep): keep parent finalization with its execution owner
mldangelo-oai Sep 14, 2026
477e57b
test: cover Deep Scan parent finalization process races
mldangelo-oai Sep 14, 2026
621bff5
fix(deep): recover selected parent completion through the existing lease
mldangelo-oai Sep 14, 2026
d8e25f8
fix(deep): defer SDK completion tools and keep heartbeats independent
mldangelo-oai Sep 14, 2026
978ac1d
fix(deep): preserve completion reply redaction and owner guidance
mldangelo-oai Sep 14, 2026
e7470ce
style(deep): wrap selected parent lease conditions
mldangelo-oai Sep 14, 2026
78f144f
test: cover explicit MCP completion during SDK owner turn
mldangelo-oai Sep 14, 2026
c28c457
style(deep): format selected parent cancellation condition
mldangelo-oai Sep 14, 2026
18a7dc9
fix(deep): validate sealed artifacts during completed replay
mldangelo-oai Sep 14, 2026
88738d9
test: preserve completed receipts on lost-response replay
mldangelo-oai Sep 14, 2026
ca53a81
test(deep): preserve sealed replay integrity and SDK completion prompts
mldangelo-oai Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 40 additions & 26 deletions plugins/codex-security/mcp-app/helpers-main.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
import { existsSync, realpathSync } from "node:fs";
import { fileURLToPath } from "node:url";
export { parseCanonicalScanDraft } from "./src/artifact-scan-draft.js";
export { resumeSelectedDeepScan } from "./src/deep-scan/finalization.js";
import { resolveSecurityMdCommand } from "./src/helpers/resolve-security-md";
import { decodePosixBytes } from "./src/helpers/posix-path";
import { windowsBinding } from "./src/native";

let commandLine = process.argv.slice(2);
if (process.platform === "win32") {
const original = windowsBinding().windowsArguments();
commandLine = original
.slice(original.length - commandLine.length)
.map((argument) => argument.toString("utf16le"));
}
let posixHome = process.env.HOME;
if (commandLine[0] === "--helper") {
// Importing the bundled helper from the SDK does not invoke its CLI adapter.
const entryPath = import.meta.url.startsWith("file:") ? fileURLToPath(import.meta.url) : import.meta.url;
const invokedPath = process.argv[1];
if (
invokedPath && existsSync(invokedPath)
&& realpathSync(invokedPath) === realpathSync(entryPath)
) runHelper();

function runHelper(): void {
let commandLine = process.argv.slice(2);
if (process.platform === "win32") {
commandLine = commandLine.slice(1);
const original = windowsBinding().windowsArguments();
commandLine = original
.slice(original.length - commandLine.length)
.map((argument) => argument.toString("utf16le"));
}
let posixHome = process.env.HOME;
if (commandLine[0] === "--helper") {
if (process.platform === "win32") {
commandLine = commandLine.slice(1);
} else {
const [homeSet, home, ...args] = decodePosixBytes(
Buffer.from(commandLine[1] ?? "", "hex"),
)
.split("\0")
.slice(0, -1);
posixHome = homeSet ? home : undefined;
commandLine = args;
}
}
const [command, ...args] = commandLine;
if (command === "resolve-security-md") {
process.exitCode = resolveSecurityMdCommand(args, posixHome);
} else {
const [homeSet, home, ...args] = decodePosixBytes(
Buffer.from(commandLine[1] ?? "", "hex"),
)
.split("\0")
.slice(0, -1);
posixHome = homeSet ? home : undefined;
commandLine = args;
console.error(
"Usage: launch_codex_security_mcp[.cmd] --helper resolve-security-md [options]",
);
process.exitCode = 2;
}
}
const [command, ...args] = commandLine;
if (command === "resolve-security-md") {
process.exitCode = resolveSecurityMdCommand(args, posixHome);
} else {
console.error(
"Usage: launch_codex_security_mcp[.cmd] --helper resolve-security-md [options]",
);
process.exitCode = 2;
}
2 changes: 2 additions & 0 deletions plugins/codex-security/mcp-app/scripts/build_mcp_app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export async function buildMcpApp({ output }) {
loader: { ".md": "text" },
logLevel: "info",
logOverride: { "empty-import-meta": "silent" },
nodePaths: [join(root, "node_modules")],
outfile: bundle,
platform: "node",
target: "node20"
Expand Down Expand Up @@ -108,5 +109,6 @@ const runtimeModule = new Module(loaderPath);
runtimeModule.filename = loaderPath;
runtimeModule.paths = Module._nodeModulePaths(dirname(loaderPath));
runtimeModule._compile(runtimeSource, loaderPath);
export default runtimeModule.exports;
`;
}
132 changes: 107 additions & 25 deletions plugins/codex-security/mcp-app/server.ts

Large diffs are not rendered by default.

32 changes: 26 additions & 6 deletions plugins/codex-security/mcp-app/src/artifact-deep-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { join } from "node:path";
import { dirname, join, relative, sep } from "node:path";
import type { ZodType } from "zod/v4";
import commonSchema from "../../schemas/definitions/artifact-common.schema.json";
import reducerSchema from "../../schemas/tools/deep-reducer.schema.json";
Expand All @@ -20,7 +20,9 @@ import {
type DeepScanArtifacts
} from "./deep-scan/artifacts.js";
import {
deepReductionForPersistence,
parseDeepReduction,
projectDiscoveryCoverage,
reconcileDeepReduction,
type DeepReductionInput,
type DeepReductionSources,
Expand Down Expand Up @@ -54,6 +56,18 @@ interface BoundReducer {
/** Read the findings and scan context assigned to this reducer. */
export async function getCodexSecurityDeepReducerInputs(
context: ArtifactContext
): Promise<DeepReductionSources> {
const inputs = await readDeepReductionSources(context);
const { sourceCoverage: _coverage, ...previous } = inputs.previous ?? {};
return {
discoveries: inputs.discoveries.map(({ workerId, result }) => ({ workerId, result })),
previous: inputs.previous === null ? null : previous as DeepReductionInput,
};
}

/** Capture host coverage alongside the reducer's immutable finding inputs. */
export async function readDeepReductionSources(
context: ArtifactContext
): Promise<DeepReductionSources> {
return withLogicalReducerErrors(context, async () => {
const bound = bindDeepReducer(context);
Expand All @@ -73,8 +87,13 @@ export async function getCodexSecurityDeepReducerInputs(
sourceFindingIds: [`${worker.id}:${index}`],
},
}));
const { coverage: _coverage, ...reduction } = result;
return { workerId: worker.id, result: reduction };
const { coverage, ...reduction } = result;
return {
workerId: worker.id,
...(worker.attempt === undefined ? {} : { attempt: worker.attempt }),
coverage: projectDiscoveryCoverage(coverage, worker, relative(bound.artifacts.scanDir, worker.artifactDir ?? dirname(worker.resultPath)).split(sep).join("/")),
result: reduction,
};
}));
const previous = await readPreviousReduction(bound);
const scanId = bound.scanId ?? previous?.scanId ?? discoveries[0]?.result.scanId;
Expand Down Expand Up @@ -107,7 +126,7 @@ export async function recordCodexSecurityDeepReduction(
const submitted = deepReductionInputSchema.parse(input);
let reduction = parseDeepReduction(submitted);
if (reduction.complete === false) throw new Error("Deep reduction is only a checkpoint, not a complete result.");
const inputs = await getCodexSecurityDeepReducerInputs(context);
const inputs = await readDeepReductionSources(context);
const expectedScanId = bound.scanId
?? inputs.previous?.scanId
?? inputs.discoveries[0]?.result.scanId;
Expand All @@ -116,8 +135,9 @@ export async function recordCodexSecurityDeepReduction(
}
reduction = reconcileDeepReduction(reduction, inputs.discoveries, inputs.previous);

await saveScanDraftCheckpoint(context, reduction);
await writeJsonAtomic(bound.resultPath, reduction);
const persisted = deepReductionForPersistence(reduction, bound.state.persistSourceCoverage);
await saveScanDraftCheckpoint(context, persisted);
await writeJsonAtomic(bound.resultPath, persisted);
return {
findingCount: reduction.findings.length,
consumedWorkerIds: bound.state.claimedWorkers.map((worker) => worker.id)
Expand Down
4 changes: 4 additions & 0 deletions plugins/codex-security/mcp-app/src/artifact-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import { dirname, isAbsolute, join, resolve, sep } from "node:path";
export interface DeepReducerWorkerContext {
id: string;
resultPath: string;
/** Original output owner for relative evidence, including accepted checkpoints. */
artifactDir?: string;
attempt?: number;
}

export interface DeepReducerContext {
scanRoot: string;
claimedWorkers: DeepReducerWorkerContext[];
previousReducerResultPath?: string;
persistSourceCoverage?: boolean;
}

/**
Expand Down
116 changes: 71 additions & 45 deletions plugins/codex-security/mcp-app/src/artifact-scan-draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { dirname, join, sep } from "node:path";
import type * as z from "zod/v4";
import commonSchema from "../../schemas/definitions/artifact-common.schema.json";
import scanDraftDocument from "../../schemas/tools/scan-draft.schema.json";
import scanManifestDocument from "../../schemas/scan-manifest.schema.json";
import type { ArtifactContext } from "./artifact-context.js";
import type { RunArtifactWorkbench } from "./artifact-context.js";
import {
Expand All @@ -17,17 +18,10 @@ import {
type SchemaDocument,
} from "./artifact-schema-loader.js";

type JsonObject = Record<string, unknown>;
import type { ScanDraftInput } from "../../../../sdk/typescript/src/accepted-audit.js";
export type { ScanDraftInput } from "../../../../sdk/typescript/src/accepted-audit.js";

export interface ScanDraftInput {
scanId: string;
complete?: boolean;
handoffClaimToken?: string;
scope?: JsonObject;
threatModel?: JsonObject;
findings: JsonObject[];
coverage: JsonObject;
}
type JsonObject = Record<string, unknown>;

export interface CompletedScanInput {
scanId: string;
Expand Down Expand Up @@ -55,6 +49,12 @@ interface PreparedScanDraft {
coverage: JsonObject;
}

/** Host-selected Deep aggregate, separate from model-authored draft fields. */
export interface DeepScanPublication {
coordinatorGeneration?: number;
resultPath: string | null;
}

type PublishScanDraft = (
draft: PreparedScanDraft,
expectedDigest: string | undefined,
Expand All @@ -69,6 +69,19 @@ export const scanDraftInputSchema = loadArtifactZodSchema(
"scanDraftInput",
) as z.ZodType<ScanDraftInput>;

// Sealed documents retain the existing public ID contract; live drafts require UUIDs.
const canonicalScanDraftInputSchema = loadArtifactZodSchema(
[commonSchema, {
...scanDraftDocument,
$defs: {
...scanDraftDocument.$defs,
scanId: scanManifestDocument.properties.scan.properties.id,
},
}] as SchemaDocument[],
scanDraftDocument.$id,
"scanDraftInput",
) as z.ZodType<ScanDraftInput>;

export const completedScanInputSchema = loadArtifactZodSchema(
schemaDocuments,
scanDraftDocument.$id,
Expand Down Expand Up @@ -165,6 +178,7 @@ export async function recordCodexSecurityScanDraftViaWorkbench(
input: ScanDraftInput,
runWorkbench: RunArtifactWorkbench,
signal?: AbortSignal,
publication?: DeepScanPublication,
): Promise<ScanDraftResult> {
return recordCodexSecurityScanDraft(
context,
Expand All @@ -184,7 +198,10 @@ export async function recordCodexSecurityScanDraftViaWorkbench(
const { handoffClaimToken: _claim, ...snapshot } = checkpoint;
await Promise.all([
replaceArtifactJson(checkpointPath, snapshot),
replaceArtifactJson(draftPath, draft),
replaceArtifactJson(draftPath, {
...draft,
...(publication === undefined ? {} : { deepScanPublication: publication }),
}),
]);
const arguments_ = [
"write-scan-draft",
Expand Down Expand Up @@ -548,32 +565,9 @@ async function readPreviousScanDraft(
const manifest = parseJsonObject(contents[0]!, "previous scan draft manifest");
const findings = parseJsonObject(contents[1]!, "previous scan draft findings");
const coverage = parseJsonObject(contents[2]!, "previous scan draft coverage");
const scan = requireObject(manifest.scan, "previous scan draft.scan");
const semanticScope = isObject(scan.scope) ? { ...scan.scope } : undefined;
if (semanticScope) {
delete semanticScope.includePaths;
delete semanticScope.excludePaths;
}
const semanticCoverage = { ...coverage };
for (const field of ["documentType", "schemaVersion", "scanId", "mode", "includePaths", "excludePaths", "receiptRefs", "inventoryStrategy"]) delete semanticCoverage[field];
return {
digest,
input: parsePersistedScanDraft({
scanId: context.scanId,
...(scan.complete === false ? { complete: false } : {}),
...(semanticScope && Object.keys(semanticScope).length > 0
? { scope: semanticScope }
: {}),
...(isObject(scan.threatModel)
? { threatModel: structuredClone(scan.threatModel) }
: {}),
findings: (findings.findings as JsonObject[]).map((finding) => {
const semantic = { ...finding };
for (const field of ["findingId", "occurrenceId", "fingerprints"]) delete semantic[field];
return semantic;
}),
coverage: semanticCoverage,
}),
input: parseCanonicalScanDraft({ scanId: context.scanId, manifest, findings, coverage }),
};
}

Expand Down Expand Up @@ -973,8 +967,35 @@ export async function getCodexSecurityCompletedScan(
return { scanId: parsed.scanId, manifest, findings, coverage };
}

/** Project canonical documents through the same semantic parser as worker drafts. */
export function parseCanonicalScanDraft(input: {
scanId?: string;
manifest: JsonObject;
findings: JsonObject;
coverage: JsonObject;
}): ScanDraftInput {
const scan = requireObject(input.manifest.scan, "scan draft manifest.scan");
for (const scanId of [scan.id, input.findings.scanId, input.coverage.scanId]) {
if (scanId !== undefined && scanId !== input.scanId) {
throw new Error("scan draft: canonical documents belong to a different scan.");
}
}
return parsePersistedCheckpoint({
scanId: input.scanId,
...(scan.complete === undefined ? {} : { complete: scan.complete }),
...(scan.scope === undefined ? {} : { scope: scan.scope }),
...(scan.threatModel === undefined ? {} : { threatModel: scan.threatModel }),
findings: input.findings.findings,
coverage: input.coverage,
}, canonicalScanDraftInputSchema);
}

export function parseScanDraft(input: ScanDraftInput): ScanDraftInput {
const parsed = scanDraftInputSchema.parse(input);
return parseSemanticScanDraft(input, scanDraftInputSchema);
}

function parseSemanticScanDraft(input: unknown, schema: z.ZodType<ScanDraftInput>): ScanDraftInput {
const parsed = schema.parse(input);
validateFindingSemantics(parsed.findings);
validateCoverageSemantics(parsed.coverage);
return parsed;
Expand All @@ -984,18 +1005,23 @@ export function parseScanDraft(input: ScanDraftInput): ScanDraftInput {
export function parsePersistedScanDraft(
input: Record<string, unknown>
): ScanDraftInput {
return parsePersistedDraft(input, scanDraftInputSchema);
}

function parsePersistedDraft(input: Record<string, unknown>, schema: z.ZodType<ScanDraftInput>): ScanDraftInput {
const compatible = structuredClone(input);
if (!Array.isArray(compatible.findings)) {
return parseScanDraft(compatible as unknown as ScanDraftInput);
}
for (const finding of compatible.findings) {
if (!isObject(finding)) continue;
normalizePersistedFindingDetails(finding);
if (Array.isArray(compatible.findings)) {
for (const finding of compatible.findings) {
if (isObject(finding)) normalizePersistedFindingDetails(finding);
}
}
return parseScanDraft(compatible as unknown as ScanDraftInput);
return parseSemanticScanDraft(compatible, schema);
}

function parsePersistedCheckpoint(input: Record<string, unknown>): ScanDraftInput {
function parsePersistedCheckpoint(
input: Record<string, unknown>,
schema = scanDraftInputSchema,
): ScanDraftInput {
const compatible = structuredClone(input);
if (isObject(compatible.scope)) {
delete compatible.scope.includePaths;
Expand All @@ -1022,7 +1048,7 @@ function parsePersistedCheckpoint(input: Record<string, unknown>): ScanDraftInpu
delete finding.fingerprints;
}
}
return parsePersistedScanDraft(compatible);
return parsePersistedDraft(compatible, schema);
}

function normalizePersistedFindingDetails(finding: JsonObject): void {
Expand Down
Loading
Loading