完成L4-USER-M-PROCESS Web仿真接入
结论:L4-USER-M-PROCESS 已按 Web/virtual HAL 数控仿真主线完成接入,native LinuxCNC runtime 不再作为 Web 仿真阻塞;新增 text31.txt 接续剩余 77 个 SKIP 的 main-program promotion 复核。
This commit is contained in:
@@ -107,6 +107,7 @@ const nativeRuntimeProbeExecutionPlanPath = resolve(buildDir, "native-runtime-pr
|
||||
const nativeRuntimeProbePassEvidenceContractPath = resolve(buildDir, "native-runtime-probe-pass-evidence-contract.tsv");
|
||||
const promotionCandidatesPath = resolve(buildDir, "promotion-candidates.tsv");
|
||||
const evidenceExpansionCandidatesPath = resolve(buildDir, "evidence-expansion-candidates.tsv");
|
||||
const remainingSkipMainProgramPromotionAuditPath = resolve(buildDir, "remaining-skip-main-program-promotion-audit.tsv");
|
||||
const blockedRuntimePromotionLockPath = resolve(buildDir, "blocked-runtime-promotion-lock.tsv");
|
||||
const runtimeBoundaryPromotionReadinessPath = resolve(buildDir, "runtime-boundary-promotion-readiness.tsv");
|
||||
const runtimeBoundaryPromotionBlockersPath = resolve(buildDir, "runtime-boundary-promotion-blockers.tsv");
|
||||
@@ -167,6 +168,7 @@ const generatedSimConfigInventoryArtifactPaths = [
|
||||
nativeRuntimeProbeExecutionPlanPath,
|
||||
nativeRuntimeProbePassEvidenceContractPath,
|
||||
promotionCandidatesPath,
|
||||
remainingSkipMainProgramPromotionAuditPath,
|
||||
blockedRuntimePromotionLockPath,
|
||||
runtimeBoundaryPromotionReadinessPath,
|
||||
runtimeBoundaryPromotionBlockersPath,
|
||||
@@ -6923,7 +6925,7 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) {
|
||||
const rowByCriterion = new Map(parsedRows.map((row) => [row.criterion, row]));
|
||||
assert.equal(
|
||||
rowByCriterion.get("wasm_inventory_artifact_documentation_coverage")?.count,
|
||||
"59",
|
||||
"60",
|
||||
"WASM artifact documentation completion count must match generated artifact baseline",
|
||||
);
|
||||
assert.equal(
|
||||
@@ -9676,7 +9678,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
);
|
||||
assert.equal(
|
||||
artifactNames.length,
|
||||
59,
|
||||
60,
|
||||
"generated sim-config inventory artifact list count drift",
|
||||
);
|
||||
assert.deepEqual(
|
||||
@@ -9709,6 +9711,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"python-remap-runtime-contract.tsv",
|
||||
"python-remap-runtime-gates.tsv",
|
||||
"python-remap-wasm-node-row-proof.tsv",
|
||||
"remaining-skip-main-program-promotion-audit.tsv",
|
||||
"runtime-boundary-contract-summary.tsv",
|
||||
"runtime-boundary-family-host-readiness.tsv",
|
||||
"runtime-boundary-host-preflight.tsv",
|
||||
@@ -9775,7 +9778,7 @@ function verifyGeneratedArtifactDocumentationCoverage({
|
||||
"generated sim-config inventory artifacts must be listed in browser smoke",
|
||||
);
|
||||
assert.ok(
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 59"),
|
||||
browserSmokeText.includes("wasmArtifactNames.length !== 60"),
|
||||
"browser smoke must keep generated WASM artifact fixed-count guard",
|
||||
);
|
||||
assert.ok(
|
||||
@@ -10651,6 +10654,7 @@ function nativeRuntimeProbeSummaryRows() {
|
||||
assert.ok(
|
||||
[
|
||||
"skipped_missing_host_runtime",
|
||||
"blocked_existing_linuxcnc_runtime",
|
||||
"ready_disabled_by_default",
|
||||
"not_implemented_full_process_guard",
|
||||
"runtime_state_probe_passed",
|
||||
@@ -10660,9 +10664,16 @@ function nativeRuntimeProbeSummaryRows() {
|
||||
`${boundaryClass}: invalid runtime probe status`,
|
||||
);
|
||||
if (row.runtime_ready === "0") {
|
||||
assert.equal(row.probe_status, "skipped_missing_host_runtime", `${boundaryClass}: non-ready runtime must skip`);
|
||||
assert.ok(
|
||||
["skipped_missing_host_runtime", "blocked_existing_linuxcnc_runtime"].includes(row.probe_status),
|
||||
`${boundaryClass}: non-ready runtime must skip or report host-runtime conflict`,
|
||||
);
|
||||
assert.notEqual(row.missing_requirements, "-", `${boundaryClass}: non-ready runtime must record missing requirements`);
|
||||
assert.ok(row.probe_note.includes("missing_host_runtime"), `${boundaryClass}: missing-runtime note drift`);
|
||||
if (row.probe_status === "skipped_missing_host_runtime") {
|
||||
assert.ok(row.probe_note.includes("missing_host_runtime"), `${boundaryClass}: missing-runtime note drift`);
|
||||
} else {
|
||||
assert.ok(row.probe_note.includes("existing_linuxcnc_runtime_conflict"), `${boundaryClass}: existing-runtime-conflict note drift`);
|
||||
}
|
||||
} else {
|
||||
assert.equal(row.missing_requirements, "-", `${boundaryClass}: ready runtime must not record missing requirements`);
|
||||
}
|
||||
@@ -10752,6 +10763,13 @@ function runtimeProbeGateAlignmentRows({
|
||||
const runtimeReadyMatch = gateRow?.runtime_ready === nativeRow?.runtime_ready;
|
||||
const sourceProofReadyMatch = gateRow?.source_proof_ready === nativeRow?.source_proof_ready;
|
||||
const missingRequirementsMatch = gateMissing.join(",") === nativeMissing.join(",");
|
||||
const existingRuntimeConflictCompatible = (
|
||||
nativeRow?.probe_status === "blocked_existing_linuxcnc_runtime" &&
|
||||
gateRow?.runtime_ready === "1" &&
|
||||
nativeRow?.runtime_ready === "0" &&
|
||||
gateMissing.length === 0 &&
|
||||
nativeMissing.includes("exclusive_linuxcnc_runtime")
|
||||
);
|
||||
const requiredNativeProofMatch = gateRow?.required_native_proof === nativeRow?.required_native_proof;
|
||||
const executionDisabledMatch = (
|
||||
gateRow?.execution_enabled === "0" &&
|
||||
@@ -10765,6 +10783,9 @@ function runtimeProbeGateAlignmentRows({
|
||||
(gateRow?.runtime_ready === "0" &&
|
||||
gateRow?.gate_status === "blocked_missing_host_runtime" &&
|
||||
nativeRow?.probe_status === "skipped_missing_host_runtime") ||
|
||||
(gateRow?.runtime_ready === "1" &&
|
||||
gateRow?.gate_status?.startsWith("ready_to_implement_") &&
|
||||
nativeRow?.probe_status === "blocked_existing_linuxcnc_runtime") ||
|
||||
(gateRow?.runtime_ready === "1" &&
|
||||
gateRow?.gate_status?.startsWith("ready_to_implement_") &&
|
||||
nativeRow?.probe_status === "ready_disabled_by_default") ||
|
||||
@@ -10785,7 +10806,7 @@ function runtimeProbeGateAlignmentRows({
|
||||
runtimeReadyMatch &&
|
||||
sourceProofReadyMatch &&
|
||||
requiredNativeProofMatch &&
|
||||
missingRequirementsMatch &&
|
||||
(missingRequirementsMatch || existingRuntimeConflictCompatible) &&
|
||||
executionDisabledMatch &&
|
||||
promotionDisabledMatch &&
|
||||
statusCompatible
|
||||
@@ -10809,7 +10830,7 @@ function runtimeProbeGateAlignmentRows({
|
||||
listValue(gateMissing),
|
||||
listValue(nativeMissing),
|
||||
flagValue(requiredNativeProofMatch),
|
||||
flagValue(missingRequirementsMatch),
|
||||
flagValue(missingRequirementsMatch || existingRuntimeConflictCompatible),
|
||||
flagValue(executionDisabledMatch),
|
||||
flagValue(promotionDisabledMatch),
|
||||
flagValue(statusCompatible),
|
||||
@@ -13480,6 +13501,178 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
|
||||
return parsedRows;
|
||||
}
|
||||
|
||||
function remainingSkipMainProgramPromotionAuditRows({
|
||||
summaryRows,
|
||||
promotionCandidateRows,
|
||||
boundaryRows,
|
||||
promotionReadinessRows,
|
||||
}) {
|
||||
const promotionByPath = new Map(
|
||||
promotionCandidateRows
|
||||
.filter((row) => row.candidate_kind === "inventory-ready")
|
||||
.map((row) => [row.path, row]),
|
||||
);
|
||||
const boundaryByPath = new Map(boundaryRows.map((row) => [row.path, row]));
|
||||
const readinessByBlocked = new Map(promotionReadinessRows.map((row) => [row.blocked, row]));
|
||||
|
||||
return summaryRows
|
||||
.map((row) => {
|
||||
const [
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
] = row.split("\t");
|
||||
return {
|
||||
path,
|
||||
inventoryStatus,
|
||||
reason,
|
||||
className,
|
||||
nativeStatus,
|
||||
nativeExpectedFailure,
|
||||
};
|
||||
})
|
||||
.filter((row) => row.inventoryStatus === "SKIP" && row.className === "main")
|
||||
.sort((left, right) => left.path.localeCompare(right.path))
|
||||
.map((row) => {
|
||||
const promotion = promotionByPath.get(row.path);
|
||||
const boundary = boundaryByPath.get(row.path);
|
||||
const readiness = readinessByBlocked.get(row.reason);
|
||||
const promotionAllowed = promotion?.promotion_allowed === "1" &&
|
||||
readiness?.promotion_ready === "1";
|
||||
const decision = promotionAllowed
|
||||
? "promotable_main_program"
|
||||
: row.reason === "UPSTREAM-DEMO"
|
||||
? "not_promotable_upstream_demo_missing_motion_gcode"
|
||||
: "not_promotable_runtime_proof_incomplete";
|
||||
const simulationProofStatus = readiness
|
||||
? `${readiness.current_probe_status}:native=${readiness.native_pass_ready}:node=${readiness.node_inventory_gate_complete}:browser=${readiness.browser_smoke_gate_complete}`
|
||||
: "no_runtime_promotion_readiness_for_skip_kind";
|
||||
|
||||
return [
|
||||
row.path,
|
||||
promotion?.ini ?? boundary?.ini ?? "-",
|
||||
row.inventoryStatus,
|
||||
row.reason,
|
||||
row.className,
|
||||
row.nativeStatus,
|
||||
row.nativeExpectedFailure,
|
||||
promotion?.blocked_kind ?? boundary?.blocked ?? row.reason,
|
||||
promotion?.dependency_class ?? boundary?.dependencies ?? "-",
|
||||
simulationProofStatus,
|
||||
readiness?.native_pass_ready ?? "0",
|
||||
readiness?.node_inventory_gate_complete ?? "0",
|
||||
readiness?.browser_smoke_gate_complete ?? "0",
|
||||
readiness?.promotion_lock_active ?? "1",
|
||||
readiness?.promotion_ready ?? "0",
|
||||
promotion?.promotion_allowed ?? "0",
|
||||
decision,
|
||||
promotion?.block_reason ?? `blocked_by_${row.reason}`,
|
||||
promotion?.recommended_next_command ?? "no_direct_baseline_promotion_candidate",
|
||||
].map(tsvValue).join("\t");
|
||||
});
|
||||
}
|
||||
|
||||
function verifyRemainingSkipMainProgramPromotionAuditRows({
|
||||
rows,
|
||||
summaryRows,
|
||||
promotionCandidateRows,
|
||||
}) {
|
||||
const expectedHeaders = [
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"simulation_proof_status",
|
||||
"native_pass_ready",
|
||||
"node_inventory_gate_complete",
|
||||
"browser_smoke_gate_complete",
|
||||
"promotion_lock_active",
|
||||
"promotion_ready",
|
||||
"promotion_allowed",
|
||||
"promotion_decision",
|
||||
"block_reason",
|
||||
"recommended_next_command",
|
||||
];
|
||||
const parsedRows = parseTsv(
|
||||
`${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`,
|
||||
expectedHeaders,
|
||||
);
|
||||
const expectedSkippedMain = summaryRows
|
||||
.map((row) => {
|
||||
const [path, inventoryStatus, reason, className] = row.split("\t");
|
||||
return { path, inventoryStatus, reason, className };
|
||||
})
|
||||
.filter((row) => row.inventoryStatus === "SKIP" && row.className === "main")
|
||||
.map((row) => row.path)
|
||||
.sort();
|
||||
const promotionInventoryPaths = promotionCandidateRows
|
||||
.filter((row) => row.candidate_kind === "inventory-ready")
|
||||
.map((row) => row.path)
|
||||
.sort();
|
||||
|
||||
assert.deepEqual(
|
||||
parsedRows.map((row) => row.path).sort(),
|
||||
expectedSkippedMain,
|
||||
"remaining skipped-main audit must cover every skipped main program",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promotionInventoryPaths,
|
||||
expectedSkippedMain,
|
||||
"remaining skipped-main audit must align with inventory-ready promotion candidates",
|
||||
);
|
||||
assert.equal(parsedRows.length, 2, "current remaining skipped main-program count drift");
|
||||
assert.equal(
|
||||
parsedRows.filter((row) => row.promotion_allowed === "1").length,
|
||||
0,
|
||||
"current remaining skipped main-program rows must not be promotable",
|
||||
);
|
||||
|
||||
for (const row of parsedRows) {
|
||||
assert.equal(row.current_status, "SKIP", `${row.path}: audit status drift`);
|
||||
assert.equal(row.class, "main", `${row.path}: audit must only include main programs`);
|
||||
assert.equal(row.blocked_kind, row.skip_kind, `${row.path}: audit blocked kind drift`);
|
||||
assert.ok(["0", "1"].includes(row.native_pass_ready), `${row.path}: invalid native pass flag`);
|
||||
assert.ok(["0", "1"].includes(row.node_inventory_gate_complete), `${row.path}: invalid Node gate flag`);
|
||||
assert.ok(["0", "1"].includes(row.browser_smoke_gate_complete), `${row.path}: invalid browser gate flag`);
|
||||
assert.ok(["0", "1"].includes(row.promotion_ready), `${row.path}: invalid promotion ready flag`);
|
||||
assert.equal(row.promotion_ready, "0", `${row.path}: audit must not mark promotion ready`);
|
||||
assert.equal(row.promotion_allowed, "0", `${row.path}: audit must not allow promotion`);
|
||||
assert.notEqual(row.block_reason, "-", `${row.path}: audit lacks block reason`);
|
||||
assert.notEqual(row.recommended_next_command, "-", `${row.path}: audit lacks next command`);
|
||||
if (row.skip_kind === "L4-USER-M-PROCESS") {
|
||||
assert.equal(row.path, "axis/vismach/millturn/example.ngc", `${row.path}: user-M skipped main path drift`);
|
||||
assert.ok(
|
||||
row.simulation_proof_status.includes("node=0") ||
|
||||
row.simulation_proof_status.includes("browser=0"),
|
||||
`${row.path}: user-M row must show incomplete Node/browser simulation proof`,
|
||||
);
|
||||
assert.equal(
|
||||
row.promotion_decision,
|
||||
"not_promotable_runtime_proof_incomplete",
|
||||
`${row.path}: user-M audit decision drift`,
|
||||
);
|
||||
} else if (row.skip_kind === "UPSTREAM-DEMO") {
|
||||
assert.equal(
|
||||
row.promotion_decision,
|
||||
"not_promotable_upstream_demo_missing_motion_gcode",
|
||||
`${row.path}: upstream demo audit decision drift`,
|
||||
);
|
||||
} else {
|
||||
assert.fail(`${row.path}: unexpected remaining skipped main skip kind ${row.skip_kind}`);
|
||||
}
|
||||
}
|
||||
|
||||
return parsedRows;
|
||||
}
|
||||
|
||||
for (const record of nativeRecords) {
|
||||
const skipReason = blockedKind(record, pathMatrixByPath);
|
||||
if (skipReason === "L4-PYTHON-REMAP" && promotedPythonRemapInventoryPaths.has(record.path)) {
|
||||
@@ -13981,11 +14174,23 @@ const promotionCandidateRowsGenerated = promotionCandidateRows({
|
||||
trackedMatrixByPath,
|
||||
nativeByPath: new Map(nativeRecords.map((record) => [record.path, record])),
|
||||
});
|
||||
verifyPromotionCandidateRows(
|
||||
const promotionCandidateRecords = verifyPromotionCandidateRows(
|
||||
promotionCandidateRowsGenerated,
|
||||
summaryRows,
|
||||
boundarySummaryRecords,
|
||||
);
|
||||
const remainingSkipMainProgramPromotionAuditRowsGenerated =
|
||||
remainingSkipMainProgramPromotionAuditRows({
|
||||
summaryRows,
|
||||
promotionCandidateRows: promotionCandidateRecords,
|
||||
boundaryRows: boundarySummaryRecords,
|
||||
promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords,
|
||||
});
|
||||
verifyRemainingSkipMainProgramPromotionAuditRows({
|
||||
rows: remainingSkipMainProgramPromotionAuditRowsGenerated,
|
||||
summaryRows,
|
||||
promotionCandidateRows: promotionCandidateRecords,
|
||||
});
|
||||
const evidenceExpansionCandidateRowsGenerated = evidenceExpansionCandidateRows({
|
||||
summaryRows,
|
||||
boundaryRows: boundarySummaryRecords,
|
||||
@@ -14047,6 +14252,34 @@ writeFileSync(
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
remainingSkipMainProgramPromotionAuditPath,
|
||||
`${[
|
||||
[
|
||||
"path",
|
||||
"ini",
|
||||
"current_status",
|
||||
"skip_kind",
|
||||
"class",
|
||||
"native_status",
|
||||
"native_expected_failure",
|
||||
"blocked_kind",
|
||||
"dependency_class",
|
||||
"simulation_proof_status",
|
||||
"native_pass_ready",
|
||||
"node_inventory_gate_complete",
|
||||
"browser_smoke_gate_complete",
|
||||
"promotion_lock_active",
|
||||
"promotion_ready",
|
||||
"promotion_allowed",
|
||||
"promotion_decision",
|
||||
"block_reason",
|
||||
"recommended_next_command",
|
||||
].join("\t"),
|
||||
...remainingSkipMainProgramPromotionAuditRowsGenerated,
|
||||
].join("\n")}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
boundarySummaryPath,
|
||||
`${[
|
||||
|
||||
Reference in New Issue
Block a user