Remove stale dual-observe prerequisites from workflow-authoritative readiness while preserving parity-summary safeguards. - Require the authoritative flag plus clean populated parity summaries for interpreter cutover readiness. - Keep persisted workflowInterpreterDualObserve values inert in runtime tests and documentation. - Update cutover, parity, and graph-executor tests to reflect retired shadow observation behavior. - Add a formatted patch changeset for the operator-facing cutover readiness fix. Files changed: .changeset/fn-6971-workflow-cutover-readiness.md | 7 ++ docs/architecture.md | 2 +- docs/settings-reference.md | 4 +- docs/workflow-steps.md | 19 +++-- .../core/src/__tests__/workflow-cutover.test.ts | 19 +++-- packages/core/src/workflow-cutover.ts | 9 +-- .../workflow-interpreter-cutover.test.ts | 87 +++++++++++++++++++--- .../workflow-interpreter-dual-observe.test.ts | 76 ++++++------------- .../src/__tests__/stepwise-workflow-parity.test.ts | 25 ++++--- .../engine/src/workflow-authoritative-driver.ts | 10 +-- 10 files changed, 152 insertions(+), 106 deletions(-) Fusion-Task-Id: FN-6971 Fusion-Task-Lineage: 363a441d-62e5-403c-9389-75fcf788352a
79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
import type { WorkflowParityDriftReport, WorkflowParitySummary } from "./workflow-parity.js";
|
|
|
|
/**
|
|
* Opt-in authoritative cutover flag for routing the coding lifecycle through the
|
|
* workflow interpreter. The cutover is guarded by rollout-readiness checks and
|
|
* remains reversible by disabling the flag.
|
|
*/
|
|
export const WORKFLOW_INTERPRETER_AUTHORITATIVE_FLAG = "workflowInterpreterAuthoritative" as const;
|
|
|
|
export interface InterpreterCutoverReadinessInput {
|
|
/** Explicit operator opt-in; default runtime remains legacy when false. */
|
|
authoritativeFlagEnabled: boolean;
|
|
/** Aggregated parity signal from the audit trail (for example `store.getWorkflowParitySummary()`). */
|
|
paritySummary?: Pick<WorkflowParitySummary, "observed" | "drift" | "recentDrift"> | null;
|
|
/** Optional unresolved drift reports surfaced directly by the caller. */
|
|
unresolvedDriftReports?: readonly Pick<WorkflowParityDriftReport, "agree" | "diffs">[] | null;
|
|
/** Minimum observed parity runs required before cutover may proceed. Default: 1. */
|
|
minimumObservedRuns?: number;
|
|
}
|
|
|
|
export interface InterpreterCutoverReadinessResult {
|
|
ready: boolean;
|
|
reasons: string[];
|
|
}
|
|
|
|
function normalizeMinimumObservedRuns(value: number | undefined): number {
|
|
if (!Number.isFinite(value)) return 1;
|
|
return Math.max(1, Math.floor(value!));
|
|
}
|
|
|
|
function countUnresolvedDriftReports(
|
|
reports: readonly Pick<WorkflowParityDriftReport, "agree" | "diffs">[] | null | undefined,
|
|
): number {
|
|
if (!reports || reports.length === 0) return 0;
|
|
return reports.filter((report) => report.agree === false || report.diffs.length > 0).length;
|
|
}
|
|
|
|
/**
|
|
* Pure rollout-readiness guard for the interpreter-authoritative cutover.
|
|
* Callers supply explicit parity evidence; this function performs no I/O.
|
|
*
|
|
* FNXC:WorkflowInterpreterCutover 2026-06-23-21:58:
|
|
* workflowInterpreterDualObserve is retired and inert. Authoritative cutover must use the explicit authoritative flag plus clean populated parity summaries as evidence, without reactivating hidden shadow observation.
|
|
*/
|
|
export function evaluateInterpreterCutoverReadiness(
|
|
input: InterpreterCutoverReadinessInput,
|
|
): InterpreterCutoverReadinessResult {
|
|
const reasons: string[] = [];
|
|
const minimumObservedRuns = normalizeMinimumObservedRuns(input.minimumObservedRuns);
|
|
|
|
if (!input.authoritativeFlagEnabled) {
|
|
reasons.push("experimentalFeatures.workflowInterpreterAuthoritative is disabled");
|
|
}
|
|
|
|
const paritySummary = input.paritySummary;
|
|
if (!paritySummary) {
|
|
reasons.push("workflow parity summary unavailable");
|
|
} else {
|
|
if (paritySummary.observed < minimumObservedRuns) {
|
|
reasons.push(
|
|
`workflow parity observation window too small (${paritySummary.observed}/${minimumObservedRuns} observed)`,
|
|
);
|
|
}
|
|
if (paritySummary.drift > 0) {
|
|
reasons.push(`workflow parity drift above zero (${paritySummary.drift} drift events)`);
|
|
}
|
|
}
|
|
|
|
const unresolvedDriftCount = countUnresolvedDriftReports(input.unresolvedDriftReports);
|
|
if (unresolvedDriftCount > 0) {
|
|
reasons.push(`workflow parity has unresolved drift reports (${unresolvedDriftCount})`);
|
|
}
|
|
|
|
return {
|
|
ready: reasons.length === 0,
|
|
reasons,
|
|
};
|
|
}
|