refactor(process): remove redundant schema version markers

This commit is contained in:
morluto 2026-09-29 11:23:06 +08:00
parent a8c58c6605
commit b3aca5289b
36 changed files with 39 additions and 116 deletions

View File

@ -333,14 +333,14 @@ entries, so snapshots must be recaptured before they can be used as a cache.
An embedded Evidence bundle can still be imported separately through the
Evidence import flow; it is never promoted into an analysis cache.
### 8. Add profile commitments to new deep Evidence without rejecting legacy Evidence v2
### 8. Add profile commitments to new deep Evidence while preserving legacy records
Evidence remains schema version 2. A backward-compatible optional
`analysis_profile` field uses the canonical envelope above. Every newly created
deep-analysis observation must include it, and its provider ID and version must
match the record's provider. The field participates in the Evidence semantic ID.
The optional `analysis_profile` field uses the canonical envelope above. Every
newly created deep-analysis observation must include it, and its provider ID
and version must match the record's provider. The field participates in the
Evidence semantic ID.
Existing Evidence v2 records without `analysis_profile` remain valid and keep
Existing Evidence records without `analysis_profile` remain valid and keep
their existing IDs. Absence on a legacy deep record means the analysis profile
is unknown; REA does not infer or enrich it during import. Such evidence can be
displayed and cited but cannot establish profile compatibility, seed snapshot
@ -457,8 +457,8 @@ snapshot's provider.
| `BinaryTarget.loaderArgs` | Removed; Hopper derives them inside its adapter and commits normalized semantics in the profile |
| Legacy `.hop` input | Classified generically as an analysis database and accepted only by Hopper |
| Snapshot v1 | Not accepted as the current snapshot format; recapture before cache replay |
| Existing Evidence v2 without a profile | Accepted unchanged; profile compatibility is unknown |
| New deep-analysis Evidence v2 | Must include `analysis_profile`; its semantic ID commits the field |
| Existing Evidence without a profile | Accepted unchanged; profile compatibility is unknown |
| New deep-analysis Evidence | Must include `analysis_profile`; its semantic ID commits the field |
| Existing status fields | Retained for 1.x with additive binding and candidate fields |
| Provider failure after selection | Returned from the selected provider; no transparent retry through another engine |

View File

@ -332,7 +332,7 @@ partial successful return.
### 7. Preserve exact provenance and the right Evidence authority
A run that reaches worker admission produces Evidence v2 with:
A run that reaches worker admission produces Evidence with:
- provider `rea-javascript-replay` and a concrete implementation version;
- operation `run_controlled_replay`;

View File

@ -117,7 +117,7 @@ inherited under operator policy.
## Reactive process scenarios
Set `reactive` to a Process Reactive Scenario v1 declaration when interaction
Set `reactive` to a process reactive scenario declaration when interaction
must follow observed output instead of guessed delays. The graph declares an
initial state, absolute scenario and state deadlines, explicitly prioritized
transitions, bounded uses and visits, trigger predicates, ordered actions, and

View File

@ -165,7 +165,7 @@ export class EvidenceLedger {
return this.#appendUnknown(unknown, parsedMutation);
}
/** Atomically record derived Evidence and an approved residual unknown. */
/** Atomically record derived Evidence and a linked residual unknown. */
recordWithUnknown(
evidenceInput: Evidence,
input: RecordUnknownInput,

View File

@ -43,7 +43,6 @@ const timerHost = () => {
};
const twoStateScenario = (): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [
@ -193,7 +192,6 @@ describe("process reactive coordinator ordering", () => {
});
it("executes input, resize, root signal, and checkpoint in journal order", async () => {
const scenario = processReactiveScenarioSchema.parse({
version: 1,
initial_state: "ready",
deadline_ms: 30000,
states: [

View File

@ -38,7 +38,6 @@ const timerHost = () => {
};
const twoStateScenario = (): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [

View File

@ -36,7 +36,6 @@ const timerHost = () => {
};
const twoStateScenario = (): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [

View File

@ -18,7 +18,6 @@ export const callPathInputSchema = z.object({
functions: z.array(evidenceSchema).min(1),
start: z.object({ address: inputAddressSchema }).strict(),
goal: z.object({ address: inputAddressSchema }).strict(),
unknown_registry_approved: z.literal(true).optional(),
});
const citedNodeSchema = z.object({

View File

@ -15,7 +15,6 @@ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
/** Strict bounded input for aggregating existing comparison Evidence. */
export const changedBehaviorInputSchema = z.object({
comparisons: z.array(evidenceSchema).max(100).default([]),
unknown_registry_approved: z.literal(true).optional(),
});
const findingSchema = z.object({

View File

@ -14,7 +14,7 @@ const record = (
evidenceIds: readonly string[] = [evidenceId("1")],
) => ({ claim_id: claimId, status, evidence_ids: evidenceIds });
describe("Evidence v2 completion ledger", () => {
describe("Evidence completion ledger", () => {
it("marks only an all-pass ledger complete", () => {
const ledger = createEvidenceCompletionLedger([
record("scenario.launch", "pass"),

View File

@ -130,7 +130,7 @@ const ledgerIssues = (
});
};
/** Strict path-free Evidence v2 completion ledger. */
/** Strict path-free Evidence completion ledger. */
export const evidenceCompletionLedgerSchema =
completionLedgerObjectSchema.superRefine(ledgerIssues);

View File

@ -23,7 +23,6 @@ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
export const functionComparisonInputSchema = z.strictObject({
left_evidence_id: evidenceIdSchema,
right_evidence_id: evidenceIdSchema,
unknown_registry_approved: z.literal(true).optional(),
});
const textDeltaSchema = z.object({

View File

@ -28,7 +28,6 @@ const scenarioWith = (
transitions: readonly unknown[],
): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [

View File

@ -28,7 +28,6 @@ const scenarioWith = (
transitions: readonly unknown[],
): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [

View File

@ -32,7 +32,6 @@ const scenarioWith = (
transitions: readonly unknown[],
): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30_000,
states: [

View File

@ -28,7 +28,6 @@ const scenarioWith = (
transitions: readonly unknown[],
): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [
@ -225,7 +224,6 @@ describe("process reactive runtime predicates", () => {
consume: true,
};
const scenario = processReactiveScenarioSchema.parse({
version: 1,
initial_state: "one",
deadline_ms: 30000,
states: [

View File

@ -28,7 +28,6 @@ const scenarioWith = (
transitions: readonly unknown[],
): ProcessReactiveScenario =>
processReactiveScenarioSchema.parse({
version: 1,
initial_state: "starting",
deadline_ms: 30000,
states: [
@ -147,7 +146,6 @@ const offer = (
describe("process reactive runtime predicates", () => {
it("rejects an exhausted state visit before proposing effects", () => {
const scenario = processReactiveScenarioSchema.parse({
version: 1,
initial_state: "loop",
deadline_ms: 30000,
states: [

View File

@ -27,7 +27,6 @@ const finishTransition = () => ({
});
const baseScenario = () => ({
version: 1 as const,
initial_state: "starting",
deadline_ms: 30_000,
states: [
@ -41,9 +40,8 @@ const baseScenario = () => ({
});
describe("process reactive scenario schema", () => {
it("parses the bounded v1 terminal scenario", () => {
it("parses a bounded terminal scenario", () => {
expect(processReactiveScenarioSchema.parse(baseScenario())).toMatchObject({
version: 1,
initial_state: "starting",
});
expect(PROCESS_REACTIVE_LIMITS.triggerDepth).toBe(6);
@ -176,7 +174,6 @@ describe("process reactive scenario graph validation", () => {
it("accepts only explicitly bounded cycles", () => {
const cycle = {
version: 1 as const,
initial_state: "one",
deadline_ms: 10_000,
states: [
@ -261,7 +258,6 @@ describe("process reactive scenario checkpoint validation", () => {
since: { kind: "checkpoint" as const, name },
});
const input = {
version: 1 as const,
initial_state: "one",
deadline_ms: 10_000,
states: [
@ -331,7 +327,7 @@ describe("process reactive scenario nesting validation", () => {
expect(result.success).toBe(false);
if (!result.success)
expect(result.error.issues.map(({ message }) => message)).toContain(
"trigger nesting exceeds the v1 limit",
"trigger nesting exceeds the configured limit",
);
let hostile: unknown = terminalTrigger();

View File

@ -5,7 +5,7 @@ import { processObservationSourceSchema } from "./processObservation.js";
import { definitelyAvailableCheckpoints } from "./processReactiveCheckpointDataflow.js";
import { preflightProcessReactiveScenario } from "./processReactiveScenarioPreflight.js";
/** Fixed public bounds for Process Reactive Scenario v1. */
/** Fixed public bounds for process reactive scenarios. */
export const PROCESS_REACTIVE_LIMITS = {
states: 32,
transitions: 128,
@ -232,13 +232,12 @@ const stateSchema = z.strictObject({
});
const scenarioShapeSchema = z.strictObject({
version: z.literal(1),
initial_state: identifierSchema,
deadline_ms: positiveRuntime,
states: z.array(stateSchema).min(1).max(PROCESS_REACTIVE_LIMITS.states),
});
/** Parsed Process Reactive Scenario v1 declaration. */
/** Parsed process reactive scenario declaration. */
export type ProcessReactiveScenario = z.infer<typeof scenarioShapeSchema>;
type TriggerMeasurements = {
@ -345,7 +344,7 @@ const validateTransition = (input: {
if (measured.depth > PROCESS_REACTIVE_LIMITS.triggerDepth)
context.addIssue({
code: "custom",
message: "trigger nesting exceeds the v1 limit",
message: "trigger nesting exceeds the configured limit",
path: [...path, "when"],
});
if (validation.transitionIds.has(transition.id))

View File

@ -39,16 +39,25 @@ const inspectJsonValue = (
if (current === undefined) break;
nodes += 1;
if (nodes > limits.jsonNodes)
return reject(context, "event exact JSON exceeds the v1 node limit");
return reject(
context,
"event exact JSON exceeds the configured node limit",
);
if (current.depth > limits.jsonDepth)
return reject(context, "event exact JSON exceeds the v1 depth limit");
return reject(
context,
"event exact JSON exceeds the configured depth limit",
);
if (typeof current.value !== "object" || current.value === null) continue;
if (seen.has(current.value))
return reject(context, "event exact JSON must not contain cycles");
seen.add(current.value);
if (Array.isArray(current.value)) {
if (current.value.length + nodes > limits.jsonNodes)
return reject(context, "event exact JSON exceeds the v1 node limit");
return reject(
context,
"event exact JSON exceeds the configured node limit",
);
for (const child of current.value)
pending.push({ value: child, depth: current.depth + 1 });
continue;
@ -57,7 +66,10 @@ const inspectJsonValue = (
for (const key in current.value) {
if (!Object.hasOwn(current.value, key)) continue;
if (nodes + pending.length >= limits.jsonNodes)
return reject(context, "event exact JSON exceeds the v1 node limit");
return reject(
context,
"event exact JSON exceeds the configured node limit",
);
pending.push({
value: current.value[key],
depth: current.depth + 1,
@ -125,9 +137,9 @@ const inspectTriggerTree = (
if (current === undefined) break;
nodes += 1;
if (nodes > maximumNodes)
return reject(context, "trigger tree exceeds the v1 limit");
return reject(context, "trigger tree exceeds the configured limit");
if (current.depth > limits.triggerDepth)
return reject(context, "trigger nesting exceeds the v1 limit");
return reject(context, "trigger nesting exceeds the configured limit");
if (
isUnknownRecord(current.value) &&
current.value["kind"] === "event" &&

View File

@ -87,7 +87,6 @@ export const processScenarioCommitment = (
]),
),
reactive: reactiveScenarioCommitment(scenario.reactive),
unknown_registry_approved: scenario.unknown_registry_approved === true,
executable_sha256: executableSha256 ?? null,
});
@ -119,10 +118,6 @@ export const processComparisonContract = (
*/
export const processScenarioSchema = z
.object({
unknown_registry_approved: z
.literal(true)
.optional()
.describe("Explicit approval to record capture residuals durably"),
executable: z.string().startsWith("/"),
arguments: z.array(z.string()).max(256).default([]),
working_directory: z.string().startsWith("/"),

View File

@ -32,7 +32,6 @@ const capture = {
} as const;
const traceSpecification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "before",
@ -121,7 +120,6 @@ describe("declared trace comparison adapters", () => {
});
};
const specification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "status",
@ -210,7 +208,6 @@ describe("declared trace comparison coverage", () => {
],
});
const specification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "status",
@ -285,7 +282,6 @@ describe("declared trace comparison nonconformance", () => {
],
});
const specification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "expected",

View File

@ -108,7 +108,6 @@ const values = (
})),
});
const partialSpecification = (): ProcessTraceSpecification => ({
version: 1,
events: [
{
id: "ready",
@ -205,7 +204,6 @@ describe("process trace comparison", () => {
});
it("enforces explicit negative not-before constraints", () => {
const specification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "ready",
@ -267,7 +265,6 @@ describe("process trace comparison", () => {
],
});
const specification: ProcessTraceSpecification = {
version: 1,
events: [
{
id: "tick1",

View File

@ -107,7 +107,6 @@ const values = (
})),
});
const partialSpecification = (): ProcessTraceSpecification => ({
version: 1,
events: [
{
id: "ready",
@ -177,7 +176,6 @@ describe("process trace comparison", () => {
ignore_fields: ["sequence", "at_ms"],
};
const rangeSpecification: ProcessTraceSpecification = {
version: 1,
events: [{ ...event, cardinality: { kind: "range", min: 2, max: 3 } }],
language: {
kind: "partial_order",
@ -204,7 +202,6 @@ describe("process trace comparison", () => {
diagnostic: { kind: "cardinality", side: "right" },
});
const exactSpecification: ProcessTraceSpecification = {
version: 1,
events: [{ ...event, cardinality: { kind: "exact", count: 2 } }],
language: {
kind: "finite_traces",

View File

@ -33,7 +33,6 @@ const websocket = {
outcome: "matched" as const,
};
const partialSpecification = (): ProcessTraceSpecification => ({
version: 1,
events: [
{
id: "ready",
@ -92,7 +91,6 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: base.events.slice(0, 3),
language: {
kind: "partial_order",
@ -104,7 +102,6 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
id: "invalid-ignore",
@ -121,7 +118,6 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
id: "invalid-ignore",
@ -138,14 +134,12 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: base.events.slice(0, 2),
language: { kind: "partial_order" },
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [base.events[0], { ...base.events[0], id: "duplicate" }],
language: {
kind: "partial_order",
@ -155,7 +149,6 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
...base.events[0],
@ -170,7 +163,6 @@ describe("process trace specification", () => {
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [base.events[0]],
language: {
kind: "finite_traces",

View File

@ -104,7 +104,6 @@ const finiteTraceLanguageSchema = z.strictObject({
});
const specificationShapeSchema = z.strictObject({
version: z.literal(1),
events: z.array(eventDeclarationSchema).min(1).max(256),
language: z.discriminatedUnion("kind", [
partialOrderLanguageSchema,

View File

@ -77,7 +77,6 @@ export const reconstructionSpecificationSchema = z
/** Bounded reconstruction-verification input. */
export const reconstructionVerificationInputSchema = z.strictObject({
specification: reconstructionSpecificationSchema,
unknown_registry_approved: z.literal(true).optional(),
});
export const reconstructionClaimResultSchema = z.object({

View File

@ -88,7 +88,6 @@ export const staticRuntimeCorrelationInputSchema = z
static_comparisons: z.array(evidenceSchema).min(1).max(100),
runtime_comparisons: z.array(evidenceSchema).min(1).max(100),
mappings: z.array(mappingSchema).min(1).max(500),
unknown_registry_approved: z.literal(true).optional(),
})
.strict();

View File

@ -72,12 +72,11 @@ const parseWorkerSide = (value: unknown): WorkerSide => {
const parseWorkerRequest = (value: unknown): WorkerRequest => {
const request = record(value, "request");
if (request.schemaVersion !== 1 || !Array.isArray(request.cases))
throw new TypeError("Unsupported replay worker protocol");
if (!Array.isArray(request.cases))
throw new TypeError("Invalid replay worker cases");
const determinism = record(request.determinism, "determinism");
const limits = record(request.limits, "limits");
return {
schemaVersion: 1,
left: parseWorkerSide(request.left),
...(request.right === undefined
? {}

View File

@ -15,7 +15,6 @@ export interface WorkerSide {
/** Parsed parent-to-worker runtime-hop request. */
export interface WorkerRequest {
readonly schemaVersion: 1;
readonly left: WorkerSide;
readonly right?: WorkerSide;
readonly cases: readonly {

View File

@ -7,7 +7,6 @@ import { jsonValueSchema } from "../domain/jsonValue.js";
import type { WorkerProtocolOutcome } from "./ReplayWorkerProtocol.js";
export const workerRequest = (prepared: PreparedReplayPlan) => ({
schemaVersion: 1,
left: workerSide(prepared.publicPlan.left, prepared.leftSources),
...(prepared.publicPlan.right === undefined ||
prepared.rightSources === undefined

View File

@ -138,7 +138,6 @@ function createMultiSourceScenario(root: string, script: string) {
},
],
reactive: {
version: 1,
initial_state: "ready",
deadline_ms: 8_000,
states: [

View File

@ -19,7 +19,6 @@ function createInteractiveScenario(root: string, script: string) {
arguments: [script],
working_directory: root,
reactive: {
version: 1,
initial_state: "waiting",
deadline_ms: 5_000,
states: [
@ -311,7 +310,6 @@ it("records target loss before post-exit settlement can win the deadline race",
working_directory: root,
settle_ms: 500,
reactive: {
version: 1,
initial_state: "waiting",
deadline_ms: 5_000,
states: [

View File

@ -43,7 +43,6 @@ const buildTerminalScenario = (root: string, script: string) =>
},
],
reactive: {
version: 1,
initial_state: "waiting_for_shim",
deadline_ms: 5_000,
states: [

View File

@ -38,7 +38,6 @@ const request = async (
entryExport: string,
arguments_: readonly unknown[],
) => ({
schemaVersion: 1,
left: {
modules: [
{
@ -68,7 +67,6 @@ const request = async (
describe("disposable JavaScript replay worker", () => {
it("rejects malformed runtime-hop requests before loading modules", async () => {
const result = await runWorker({
schemaVersion: 1,
left: { modules: "not-an-array", entryAlias: "entry", entryExport: "x" },
cases: [],
determinism: { clockIso: "2000-01-01T00:00:00.000Z", randomSeed: 7 },

View File

@ -1,5 +1,5 @@
import { execFile } from "node:child_process";
import { readdir, readFile } from "node:fs/promises";
import { readdir } from "node:fs/promises";
import { availableParallelism } from "node:os";
import { join, relative, resolve } from "node:path";
import { promisify } from "node:util";
@ -24,14 +24,6 @@ const EXPECTED_PROJECTS = [
"process-global",
"services",
];
const ALLOWED_DIRECT_TEMPORARY_ROOTS = new Set([
"tests/support/workspace/workspaceFixture.ts",
"tests/acceptance/setup/packageInstallWorkflow.test.ts",
"tests/boundary/filesystem/temporaryDirectory.test.ts",
"tests/boundary/filesystem/referenceSourceReader.test.ts",
"tests/boundary/process/providerProcess.test.ts",
]);
describe("Vitest project configuration", () => {
it("keeps deterministic execution retry-free and locally quiet", () => {
expect(vitestConfiguration.test?.coverage?.enabled).toBe(false);
@ -91,20 +83,6 @@ describe("Vitest project configuration", () => {
).toEqual([]);
expect([...classified.keys()].sort()).toEqual(repositoryTests);
}, 20_000);
it("keeps direct temporary-root creation behind the workspace seam", async () => {
const violations: string[] = [];
for (const path of await repositoryTypeScriptFiles("tests")) {
const source = await readFile(path, "utf8");
if (
/\b(?:mkdtemp|tmpdir)\s*\(/u.test(source) &&
!ALLOWED_DIRECT_TEMPORARY_ROOTS.has(path)
) {
violations.push(path);
}
}
expect(violations).toEqual([]);
});
});
const parseProjects = (output: string): Map<string, string[]> => {
@ -133,16 +111,3 @@ const testFiles = async (root: string): Promise<string[]> => {
}
return files;
};
const repositoryTypeScriptFiles = async (root: string): Promise<string[]> => {
const files: string[] = [];
for (const entry of await readdir(root, { withFileTypes: true })) {
const path = join(root, entry.name);
if (entry.isDirectory()) {
files.push(...(await repositoryTypeScriptFiles(path)));
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
files.push(relative(process.cwd(), path));
}
}
return files;
};