按text22收束promotion证据

结论:完成 evidence-ready / inventory-ready 双层 promotion candidate 管理,release/SDK gate 已纳入 virtual HAL promotion summary,baseline 保持 28/28/131/0,Python remap、tool DB、external user-M hard block 继续 locked。
This commit is contained in:
2026-06-18 17:20:29 +08:00
parent 014081f1f9
commit 1c94ea4527
29 changed files with 8500 additions and 121 deletions

View File

@@ -70,6 +70,8 @@ import {
VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
@@ -327,8 +329,9 @@ private page state.
files, current Node `PASS` evidence, blocked-family exclusions, target
browser diagnostics evidence, and the required virtual HAL reports for
QtDragon on-abort, PUMA seam-weld, and rose-engine promotion planning.
- `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES` and
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES` plus
- `VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES`,
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES`, and
`VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES` plus
`createVirtualHalSimConfigMacroLoadFixtureReport()` expose non-main
macro/load fixture evidence for virtual HAL diagnostics. The report keeps
`nonMainFixture === true`, separates fixture paths from manifest-backed
@@ -336,7 +339,9 @@ private page state.
`rcone.ngc` or external-offsets `queuebuster.ngc` from being promoted as
standalone browser main programs. Blocked fixture rows such as SilverDragon
`tool_sensor.ngc` remain excluded until their Python UI/process boundary is
proven outside virtual HAL.
proven outside virtual HAL. Audit candidates such as industrial-lathe
`toolchange.ngc` rows stay machine-readable negative evidence until owning
INI and UI/process source evidence is complete.
- `createVirtualHalSourceComplianceReport()` is the machine-readable gate for
the virtual HAL source rule. It maps replacement targets, runtime
capabilities, and HAL pin families back to LinuxCNC source files such as

View File

@@ -7,8 +7,11 @@ export {
VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
@@ -186,6 +189,25 @@ export {
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness,
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,

View File

@@ -125,6 +125,20 @@ export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
}),
]);
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE = Object.freeze({
executed: 28,
passed: 28,
skipped: 131,
unexpectedFail: 0,
});
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = Object.freeze({
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
});
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
Object.freeze({
id: "qtdragon-multi-joint-on-abort",
@@ -137,7 +151,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-multi-joint",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -161,7 +175,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -185,7 +199,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-rotary-axis",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -209,7 +223,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -233,7 +247,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-z-compensation",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -257,7 +271,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
gcodePath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -282,7 +296,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
gcodePath: "linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "represented-by-puma-cube",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "halui-vismach-remap",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -306,7 +320,7 @@ export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
iniPath: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
gcodePath: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "not-promoted",
currentMatrixBrowserStatus: "explicit-browser-diagnostics",
dependencyClass: "hal-process-only",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
@@ -334,6 +348,14 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = Object.freeze([
owningIniPath: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
pairedMainProgram: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
declarationSource: "NGCGUI_SUBFILE",
declarationEvidence: Object.freeze({
sourceFile: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
section: "DISPLAY",
key: "NGCGUI_SUBFILE",
value: "rcone.ngc",
line: 34,
sourceLine: "NGCGUI_SUBFILE = rcone.ngc",
}),
currentNodeInventoryStatus: "PASS",
dependencyClass: "macro-load-non-main",
targetBrowserEvidence: "non-main-fixture-diagnostics",
@@ -353,6 +375,14 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES = Object.freeze([
owningIniPath: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
pairedMainProgram: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
declarationSource: "NGCGUI_SUBFILE",
declarationEvidence: Object.freeze({
sourceFile: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
section: "DISPLAY",
key: "NGCGUI_SUBFILE",
value: "queuebuster.ngc",
line: 40,
sourceLine: "NGCGUI_SUBFILE = queuebuster.ngc",
}),
currentNodeInventoryStatus: "NON_MAIN_CLASS",
dependencyClass: "macro-load-non-main-declared-only",
targetBrowserEvidence: "non-main-fixture-diagnostics",
@@ -370,6 +400,27 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES = Object.freeze(
owningIniPath: "linuxcnc/configs/sim/gscreen/silverdragon/silverdragon.ini",
blockedKind: "PYTHON-UI-PROCESS",
dependencyClass: "macro-load-python-ui-boundary",
boundaryEvidence: Object.freeze({
sourceArtifacts: Object.freeze([
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
]),
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
boundarySummary: Object.freeze({
path: "gscreen/silverdragon/macros/tool_sensor.ngc",
ini: "gscreen/silverdragon/silverdragon.ini",
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini",
}),
iniBoundarySummary: Object.freeze({
ini: "gscreen/silverdragon/silverdragon.ini",
vendored: 0,
reportAvailable: 0,
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini",
}),
}),
reason: "Owning SilverDragon UI machine file is not available in the vendored manifest; promotion would require Python UI process boundary proof outside virtual HAL.",
}),
Object.freeze({
@@ -378,10 +429,94 @@ export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES = Object.freeze(
owningIniPath: "linuxcnc/configs/sim/gmoccapy/6_axis.ini",
blockedKind: "L4-PYTHON-REMAP",
dependencyClass: "macro-load-python-remap-boundary",
boundaryEvidence: Object.freeze({
sourceArtifacts: Object.freeze([
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
]),
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
boundarySummary: Object.freeze({
path: "gmoccapy/macros/on_abort.ngc",
ini: "gmoccapy/6_axis.ini",
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gmoccapy/6_axis.ini",
}),
iniBoundarySummary: Object.freeze({
ini: "gmoccapy/6_axis.ini",
vendored: 0,
reportAvailable: 0,
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gmoccapy/6_axis.ini",
}),
}),
reason: "gmoccapy macro/load rows remain locked behind LinuxCNC-owned Python remap/prolog/epilog runtime proof.",
}),
]);
export const VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES = Object.freeze([
Object.freeze({
id: "gscreen-industrial-lathe-wear-toolchange-audit",
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
owningIniPath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
dependencyClass: "macro-load-ui-family-audit",
auditStatus: "blocked-missing-owning-ini",
promotionAllowed: false,
boundaryEvidence: Object.freeze({
sourceArtifacts: Object.freeze([
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
]),
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
boundarySummary: Object.freeze({
path: "gscreen/industrial_lathe_wear/toolchange.ngc",
ini: "gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
}),
iniBoundarySummary: Object.freeze({
ini: "gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
vendored: 0,
reportAvailable: 0,
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini",
}),
}),
reason: "Generated boundary evidence reports missing vendored owning INI for gscreen industrial_lathe_wear; keep as audit-only until machine files and UI/process declaration have LinuxCNC source evidence.",
}),
Object.freeze({
id: "qtvcp-industrial-lathe-wear-toolchange-audit",
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
owningIniPath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
dependencyClass: "macro-load-ui-family-audit",
auditStatus: "blocked-missing-owning-ini",
promotionAllowed: false,
boundaryEvidence: Object.freeze({
sourceArtifacts: Object.freeze([
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv",
]),
sourceArtifactHashes: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES,
baselineSummary: VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
boundarySummary: Object.freeze({
path: "qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
ini: "qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
}),
iniBoundarySummary: Object.freeze({
ini: "qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
vendored: 0,
reportAvailable: 0,
recommendedBlocked: "UNAVAILABLE",
dependencies: "missing_vendored_ini:configs/sim/qtvcp_screens/industrial_lathe_wear/industrial_lathe_wear.ini",
}),
}),
reason: "Generated boundary evidence reports missing vendored owning INI for qtvcp_screens industrial_lathe_wear; keep as audit-only until machine files and UI/process declaration have LinuxCNC source evidence.",
}),
]);
export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({
simulated: "simulated",
bridgeOnly: "bridge-only",
@@ -2683,6 +2818,10 @@ function normalizeLinuxCncSourcePath(path) {
return parts.join("/");
}
function normalizeSimConfigInventoryPath(path) {
return normalizeLinuxCncSourcePath(path).replace(/^configs\/sim\//, "");
}
function sourceManifestSetFromText(manifestText = "") {
return new Set(String(manifestText)
.split("\n")
@@ -2691,6 +2830,35 @@ function sourceManifestSetFromText(manifestText = "") {
.map(normalizeLinuxCncSourcePath));
}
function isGeneratedBoundaryEvidenceReady(boundaryEvidence = {}, fixturePath = "", owningIniPath = "") {
const boundarySummary = boundaryEvidence.boundarySummary ?? {};
const iniBoundarySummary = boundaryEvidence.iniBoundarySummary ?? {};
const baselineSummary = boundaryEvidence.baselineSummary ?? {};
const sourceArtifactHashes = boundaryEvidence.sourceArtifactHashes ?? {};
const sourceArtifacts = [...(boundaryEvidence.sourceArtifacts ?? [])];
const normalizedFixturePath = normalizeSimConfigInventoryPath(fixturePath);
const normalizedOwningIniPath = normalizeSimConfigInventoryPath(owningIniPath);
return sourceArtifacts.includes("wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv") &&
sourceArtifacts.includes("wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv") &&
Object.entries(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
.every(([artifactPath, expectedHash]) => sourceArtifactHashes[artifactPath] === expectedHash) &&
baselineSummary.executed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.executed &&
baselineSummary.passed === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.passed &&
baselineSummary.skipped === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
baselineSummary.unexpectedFail === VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
normalizeSimConfigInventoryPath(boundarySummary.path) === normalizedFixturePath &&
normalizeSimConfigInventoryPath(boundarySummary.ini) === normalizedOwningIniPath &&
boundarySummary.recommendedBlocked === "UNAVAILABLE" &&
typeof boundarySummary.dependencies === "string" &&
boundarySummary.dependencies.includes("missing_vendored_ini:") &&
normalizeSimConfigInventoryPath(iniBoundarySummary.ini) === normalizedOwningIniPath &&
iniBoundarySummary.vendored === 0 &&
iniBoundarySummary.reportAvailable === 0 &&
iniBoundarySummary.recommendedBlocked === "UNAVAILABLE" &&
typeof iniBoundarySummary.dependencies === "string" &&
iniBoundarySummary.dependencies.includes("missing_vendored_ini:");
}
export function createVirtualHalSimConfigSourceCoverageReport(options = {}) {
const targets = options.targets ?? VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS;
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
@@ -2835,6 +3003,23 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
const blockedCandidateIds = rows.filter((row) => !row.blockedKindExcluded).map((row) => row.id);
const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))];
const missingRequiredReports = [...new Set(rows.flatMap((row) => row.missingRequiredReports))];
const familyRows = [...new Set(rows.map((row) => row.simConfigTarget ?? row.id))].map((familyId) => {
const familyCandidates = rows.filter((row) => (row.simConfigTarget ?? row.id) === familyId);
const completeCount = familyCandidates.filter((row) => row.complete).length;
const explicitBrowserDiagnosticsCount = familyCandidates
.filter((row) => row.targetBrowserEvidence === "explicit-browser-diagnostics").length;
const sourceFiles = [...new Set(familyCandidates.flatMap((row) => row.sourceFiles))];
return {
id: familyId,
candidateIds: familyCandidates.map((row) => row.id),
candidateCount: familyCandidates.length,
completeCount,
explicitBrowserDiagnosticsCount,
explicitBrowserDiagnosticsReady: explicitBrowserDiagnosticsCount === familyCandidates.length,
complete: completeCount === familyCandidates.length,
sourceFiles,
};
});
const complete = rows.length > 0 &&
missingCandidates.length === 0 &&
blockedCandidateIds.length === 0 &&
@@ -2858,9 +3043,11 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
blockedCandidateIds,
missingManifestFiles,
missingRequiredReports,
familyRows,
rows,
summaryRows: [
{ id: "candidates", label: "Promotion candidates", value: `${rows.length}` },
{ id: "families", label: "Promotion families", value: `${familyRows.length}` },
{ id: "blocked-family", label: "Blocked-family exclusion", value: blockedCandidateIds.length === 0 ? "passed" : `blocked ${blockedCandidateIds.length}` },
{ id: "reports", label: "Virtual HAL reports", value: missingRequiredReports.length === 0 ? "complete" : `missing ${missingRequiredReports.length}` },
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
@@ -2872,6 +3059,7 @@ export function createVirtualHalSimConfigPromotionCandidateReport(options = {})
export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
const fixtures = options.fixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES;
const blockedFixtures = options.blockedFixtures ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES;
const auditCandidates = options.auditCandidates ?? VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES;
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
const manifestSet = new Set([
...sourceManifestSetFromText(options.manifestText ?? ""),
@@ -2894,11 +3082,25 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
const statusReady = allowedStatuses.has(fixture.currentNodeInventoryStatus);
const nonMainFixtureReady = fixture.nonMainFixture === true &&
fixture.targetBrowserEvidence === "non-main-fixture-diagnostics";
const declarationEvidence = fixture.declarationEvidence ?? null;
const requiresDeclarationEvidence = typeof fixture.declarationSource === "string" &&
fixture.declarationSource.length > 0;
const declarationEvidenceReady = !requiresDeclarationEvidence || (
declarationEvidence !== null &&
declarationEvidence.sourceFile === fixture.owningIniPath &&
declarationEvidence.key === fixture.declarationSource &&
declarationEvidence.value === fixture.fixturePath.split("/").at(-1) &&
Number.isInteger(declarationEvidence.line) &&
declarationEvidence.line > 0 &&
typeof declarationEvidence.sourceLine === "string" &&
declarationEvidence.sourceLine.includes(`${declarationEvidence.key} = ${declarationEvidence.value}`)
);
const complete = sourceFiles.length > 0 &&
missingManifestFiles.length === 0 &&
blockedKindExcluded &&
statusReady &&
nonMainFixtureReady;
nonMainFixtureReady &&
declarationEvidenceReady;
return {
id: fixture.id,
label: fixture.label,
@@ -2908,6 +3110,9 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
owningIniPath: fixture.owningIniPath,
pairedMainProgram: fixture.pairedMainProgram,
declarationSource: fixture.declarationSource,
declarationEvidence,
declarationEvidenceReady,
requiresDeclarationEvidence,
currentNodeInventoryStatus: fixture.currentNodeInventoryStatus,
dependencyClass: fixture.dependencyClass,
targetBrowserEvidence: fixture.targetBrowserEvidence,
@@ -2924,6 +3129,9 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
});
const missingFixtures = rows.filter((row) => !row.complete).map((row) => row.id);
const blockedFixtureIds = rows.filter((row) => !row.blockedKindExcluded).map((row) => row.id);
const declarationEvidenceViolations = rows
.filter((row) => row.requiresDeclarationEvidence === true && row.declarationEvidenceReady !== true)
.map((row) => row.id);
const standaloneMainViolations = rows
.filter((row) => row.nonMainFixture !== true || row.targetBrowserEvidence !== "non-main-fixture-diagnostics")
.map((row) => row.id);
@@ -2931,6 +3139,8 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
const positiveFixturePaths = new Set(rows.map((row) => normalizeLinuxCncSourcePath(row.fixturePath)));
const blockedRows = blockedFixtures.map((fixture) => {
const excludedFromPositiveFixtures = !positiveFixturePaths.has(normalizeLinuxCncSourcePath(fixture.fixturePath));
const boundaryEvidence = fixture.boundaryEvidence ?? {};
const boundaryEvidenceReady = isGeneratedBoundaryEvidenceReady(boundaryEvidence, fixture.fixturePath, fixture.owningIniPath);
return {
id: fixture.id,
source: "linuxcnc-configs-sim-source-derived-virtual-hal-macro-load-blocked-fixture",
@@ -2939,19 +3149,61 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
blockedKind: fixture.blockedKind,
dependencyClass: fixture.dependencyClass,
excludedFromPositiveFixtures,
boundaryEvidence,
boundaryEvidenceReady,
reason: fixture.reason,
complete: excludedFromPositiveFixtures,
complete: excludedFromPositiveFixtures && boundaryEvidenceReady,
};
});
const blockedFixturePromotionViolations = blockedRows
.filter((row) => row.excludedFromPositiveFixtures !== true)
.map((row) => row.id);
const blockedBoundaryEvidenceViolations = blockedRows
.filter((row) => row.boundaryEvidenceReady !== true)
.map((row) => row.id);
const auditRows = auditCandidates.map((candidate) => {
const excludedFromPositiveFixtures = !positiveFixturePaths.has(normalizeLinuxCncSourcePath(candidate.fixturePath));
const boundaryEvidence = candidate.boundaryEvidence ?? {};
const boundaryEvidenceReady = isGeneratedBoundaryEvidenceReady(boundaryEvidence, candidate.fixturePath, candidate.owningIniPath);
return {
id: candidate.id,
source: "linuxcnc-configs-sim-source-derived-virtual-hal-macro-load-audit-candidate",
fixturePath: candidate.fixturePath,
owningIniPath: candidate.owningIniPath,
dependencyClass: candidate.dependencyClass,
auditStatus: candidate.auditStatus,
promotionAllowed: candidate.promotionAllowed === true,
excludedFromPositiveFixtures,
boundaryEvidence,
boundaryEvidenceReady,
reason: candidate.reason,
complete: excludedFromPositiveFixtures && candidate.promotionAllowed !== true && boundaryEvidenceReady,
};
});
const missingAuditRows = auditRows
.filter((row) => !row.complete)
.map((row) => row.id);
const auditPromotionViolations = auditRows
.filter((row) => row.promotionAllowed !== true && row.excludedFromPositiveFixtures !== true)
.map((row) => row.id);
const auditBoundaryEvidenceViolations = auditRows
.filter((row) => row.boundaryEvidenceReady !== true)
.map((row) => row.id);
const auditPromotionAllowedViolations = auditRows
.filter((row) => row.promotionAllowed === true && row.boundaryEvidenceReady !== true)
.map((row) => row.id);
const complete = rows.length > 0 &&
missingFixtures.length === 0 &&
missingAuditRows.length === 0 &&
blockedFixtureIds.length === 0 &&
declarationEvidenceViolations.length === 0 &&
standaloneMainViolations.length === 0 &&
missingManifestFiles.length === 0 &&
blockedFixturePromotionViolations.length === 0;
blockedFixturePromotionViolations.length === 0 &&
blockedBoundaryEvidenceViolations.length === 0 &&
auditPromotionViolations.length === 0 &&
auditBoundaryEvidenceViolations.length === 0 &&
auditPromotionAllowedViolations.length === 0;
return {
apiName: "linuxcnc-wasm-virtual-hal-sim-config-macro-load-fixture-report",
reportVersion: 1,
@@ -2966,15 +3218,24 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
fixturePaths: rows.map((row) => row.fixturePath),
missingFixtures,
blockedFixtureIds,
declarationEvidenceViolations,
standaloneMainViolations,
blockedFixturePromotionViolations,
blockedBoundaryEvidenceViolations,
missingAuditRows,
auditPromotionViolations,
auditBoundaryEvidenceViolations,
auditPromotionAllowedViolations,
missingManifestFiles,
rows,
blockedRows,
auditRows,
summaryRows: [
{ id: "fixtures", label: "Macro/load fixtures", value: `${rows.length}` },
{ id: "declarations", label: "Declaration evidence", value: declarationEvidenceViolations.length === 0 ? "passed" : `violations ${declarationEvidenceViolations.length}` },
{ id: "non-main", label: "Non-main fixture gate", value: standaloneMainViolations.length === 0 ? "passed" : `violations ${standaloneMainViolations.length}` },
{ id: "blocked-fixture-lock", label: "Blocked fixture lock", value: blockedFixturePromotionViolations.length === 0 ? "passed" : `violations ${blockedFixturePromotionViolations.length}` },
{ id: "blocked-fixture-lock", label: "Blocked fixture lock", value: blockedFixturePromotionViolations.length === 0 && blockedBoundaryEvidenceViolations.length === 0 ? "passed" : "violations" },
{ id: "ui-family-audit", label: "UI-family macro/load audit", value: missingAuditRows.length === 0 && auditPromotionViolations.length === 0 && auditBoundaryEvidenceViolations.length === 0 && auditPromotionAllowedViolations.length === 0 ? "passed" : "violations" },
{ id: "blocked-family", label: "Blocked-family exclusion", value: blockedFixtureIds.length === 0 ? "passed" : `blocked ${blockedFixtureIds.length}` },
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
{ id: "inventory-baseline", label: "Inventory baseline", value: "unchanged" },

View File

@@ -79,6 +79,59 @@ const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = {
unexpectedFail: 0,
};
const DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = {
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
};
function isBoundaryEvidenceBaselineFresh(boundaryEvidence = {}) {
const baseline = objectOrEmpty(boundaryEvidence.baselineSummary);
const hashes = objectOrEmpty(boundaryEvidence.sourceArtifactHashes);
return baseline.executed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.executed &&
baseline.passed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.passed &&
baseline.skipped === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.skipped &&
baseline.unexpectedFail === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail &&
Object.entries(DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES)
.every(([artifactPath, expectedHash]) => hashes[artifactPath] === expectedHash);
}
function createPromotionFamilySummaryText(candidateReport = {}) {
const families = arrayOrEmpty(objectOrEmpty(candidateReport).familyRows);
if (families.length === 0) {
return "not provided";
}
return families.map((family) => {
const label = family.id ?? "unknown";
const completeCount = family.completeCount ?? 0;
const candidateCount = family.candidateCount ?? 0;
const state = family.complete === true && family.explicitBrowserDiagnosticsReady === true ? "ready" : "blocked";
return `${label}: ${completeCount}/${candidateCount} ${state}`;
}).join("; ");
}
function createPromotionFamilySummaryRows(candidateReport = {}) {
return arrayOrEmpty(objectOrEmpty(candidateReport).familyRows).map((family) => {
const sourceFileCount = arrayOrEmpty(family.sourceFiles).length;
const candidateCount = family.candidateCount ?? 0;
const completeCount = family.completeCount ?? 0;
const explicitBrowserDiagnosticsReady = family.explicitBrowserDiagnosticsReady === true;
const ready = family.complete === true && explicitBrowserDiagnosticsReady;
return {
id: family.id ?? "unknown",
label: family.id ?? "Unknown promotion family",
value: `${completeCount}/${candidateCount} ${ready ? "ready" : "blocked"}; sources=${sourceFileCount}; diagnostics=${explicitBrowserDiagnosticsReady ? "ready" : "blocked"}`,
sourceFiles: arrayOrEmpty(family.sourceFiles),
candidateCount,
completeCount,
sourceFileCount,
explicitBrowserDiagnosticsReady,
ready,
};
});
}
const BLOCKED_RUNTIME_FAMILIES = [
"L4-USER-M-PROCESS",
"L4-TOOL-DB",
@@ -152,6 +205,12 @@ const REQUIRED_MACRO_LOAD_FIXTURES = [
{
id: "rose-engine-rcone-macro-load",
fixturePath: "linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
declarationEvidence: {
sourceFile: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
key: "NGCGUI_SUBFILE",
value: "rcone.ngc",
line: 34,
},
sourceFiles: [
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
@@ -161,6 +220,12 @@ const REQUIRED_MACRO_LOAD_FIXTURES = [
{
id: "external-offsets-queuebuster-macro-load",
fixturePath: "linuxcnc/configs/sim/axis/external_offsets/queuebuster.ngc",
declarationEvidence: {
sourceFile: "linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
key: "NGCGUI_SUBFILE",
value: "queuebuster.ngc",
line: 40,
},
sourceFiles: [
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
@@ -181,6 +246,19 @@ const REQUIRED_BLOCKED_MACRO_LOAD_FIXTURES = [
},
];
const REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES = [
{
id: "gscreen-industrial-lathe-wear-toolchange-audit",
fixturePath: "linuxcnc/configs/sim/gscreen/industrial_lathe_wear/toolchange.ngc",
auditStatus: "blocked-missing-owning-ini",
},
{
id: "qtvcp-industrial-lathe-wear-toolchange-audit",
fixturePath: "linuxcnc/configs/sim/qtvcp_screens/industrial_lathe_wear/toolchange.ngc",
auditStatus: "blocked-missing-owning-ini",
},
];
function isVirtualHalSimConfigSourceCoverageReady(report) {
const coverage = objectOrEmpty(report);
return coverage.apiName === "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report" &&
@@ -218,8 +296,14 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
fixtureReport.inventoryBaselineUnchanged === true &&
arrayOrEmpty(fixtureReport.missingFixtures).length === 0 &&
arrayOrEmpty(fixtureReport.blockedFixtureIds).length === 0 &&
arrayOrEmpty(fixtureReport.declarationEvidenceViolations).length === 0 &&
arrayOrEmpty(fixtureReport.standaloneMainViolations).length === 0 &&
arrayOrEmpty(fixtureReport.blockedFixturePromotionViolations).length === 0 &&
arrayOrEmpty(fixtureReport.blockedBoundaryEvidenceViolations).length === 0 &&
arrayOrEmpty(fixtureReport.missingAuditRows).length === 0 &&
arrayOrEmpty(fixtureReport.auditPromotionViolations).length === 0 &&
arrayOrEmpty(fixtureReport.auditBoundaryEvidenceViolations).length === 0 &&
arrayOrEmpty(fixtureReport.auditPromotionAllowedViolations).length === 0 &&
arrayOrEmpty(fixtureReport.missingManifestFiles).length === 0 &&
REQUIRED_MACRO_LOAD_FIXTURES.every((fixture) =>
rows.some((row) =>
@@ -228,6 +312,16 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
row.nonMainFixture === true &&
row.targetBrowserEvidence === "non-main-fixture-diagnostics" &&
row.fixturePath === fixture.fixturePath &&
(
!fixture.declarationEvidence ||
(
row.declarationEvidenceReady === true &&
objectOrEmpty(row.declarationEvidence).sourceFile === fixture.declarationEvidence.sourceFile &&
objectOrEmpty(row.declarationEvidence).key === fixture.declarationEvidence.key &&
objectOrEmpty(row.declarationEvidence).value === fixture.declarationEvidence.value &&
objectOrEmpty(row.declarationEvidence).line === fixture.declarationEvidence.line
)
) &&
fixture.sourceFiles.every((file) => arrayOrEmpty(row.sourceFiles).includes(file))
)
) &&
@@ -237,6 +331,28 @@ function isVirtualHalSimConfigMacroLoadFixtureReportReady(report) {
row.fixturePath === fixture.fixturePath &&
row.blockedKind === fixture.blockedKind &&
row.excludedFromPositiveFixtures === true &&
row.boundaryEvidenceReady === true &&
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
row.complete === true
)
) &&
REQUIRED_MACRO_LOAD_AUDIT_CANDIDATES.every((candidate) =>
arrayOrEmpty(fixtureReport.auditRows).some((row) =>
row.id === candidate.id &&
row.fixturePath === candidate.fixturePath &&
row.auditStatus === candidate.auditStatus &&
row.promotionAllowed === false &&
row.excludedFromPositiveFixtures === true &&
row.boundaryEvidenceReady === true &&
isBoundaryEvidenceBaselineFresh(row.boundaryEvidence) &&
objectOrEmpty(row.boundaryEvidence?.boundarySummary).recommendedBlocked === "UNAVAILABLE" &&
String(objectOrEmpty(row.boundaryEvidence?.boundarySummary).dependencies ?? "").includes("missing_vendored_ini:") &&
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).vendored === 0 &&
objectOrEmpty(row.boundaryEvidence?.iniBoundarySummary).reportAvailable === 0 &&
row.complete === true
)
);
@@ -277,6 +393,23 @@ function isVirtualHalSimConfigPromotionCandidateReportReady(report) {
hasRequiredPromotionCandidates(rows);
}
function isVirtualHalPromotionCandidateSummaryReady(summary) {
const summaryObject = objectOrEmpty(summary);
return summaryObject.apiName === "real-browser-simulation-promotion-candidate-summary" &&
summaryObject.ready === true &&
summaryObject.phase === "ready" &&
summaryObject.candidateCount === 8 &&
summaryObject.readyCandidateCount === 8 &&
summaryObject.familyCount === 3 &&
summaryObject.sourceFileCount === 17 &&
summaryObject.preferredCandidateId === "qtdragon-multi-joint-on-abort" &&
summaryObject.preferredIniPath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" &&
summaryObject.preferredGcodePath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" &&
summaryObject.explicitBrowserDiagnosticsCount === 8 &&
summaryObject.inventoryBaseline === "executed=28 passed=28 skipped=131 unexpected_fail=0" &&
arrayOrEmpty(summaryObject.blockedCandidateIds).length === 0;
}
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
const artifactObject = objectOrEmpty(artifact);
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
@@ -284,6 +417,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
const promotionCandidateSummary = objectOrEmpty(artifactObject.virtualHalPromotionCandidateSummary);
const macroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics);
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
@@ -301,6 +435,9 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined;
const promotionCandidatesReady = !hasPromotionCandidates ||
isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates);
const hasPromotionCandidateSummary = artifactObject.virtualHalPromotionCandidateSummary !== undefined;
const promotionCandidateSummaryReady = hasPromotionCandidateSummary &&
isVirtualHalPromotionCandidateSummaryReady(promotionCandidateSummary);
const hasMacroLoadFixtures = artifactObject.virtualHalSimConfigMacroLoadFixtures !== undefined;
const macroLoadFixturesReady = hasMacroLoadFixtures &&
isVirtualHalSimConfigMacroLoadFixtureReportReady(macroLoadFixtures);
@@ -326,6 +463,7 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
...(promotionCandidateSummaryReady ? [] : ["virtualHalPromotionCandidateSummary"]),
...(macroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
@@ -344,6 +482,8 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
simConfigSourceCoverageReady,
promotionCandidatesReady,
hasPromotionCandidates,
promotionCandidateSummaryReady,
hasPromotionCandidateSummary,
macroLoadFixturesReady,
hasMacroLoadFixtures,
commandScriptFixturesReady,
@@ -385,6 +525,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
label: "Virtual HAL sim-config promotion candidates",
value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided",
},
{
id: "promotion-candidate-summary",
label: "Virtual HAL promotion candidate summary",
value: hasPromotionCandidateSummary ? (promotionCandidateSummaryReady ? "ready" : "missing") : "not provided",
},
{
id: "sim-config-macro-load-fixtures",
label: "Virtual HAL sim-config macro/load fixtures",
@@ -1659,6 +1804,8 @@ export function createProjectReleaseReadinessReport({
observedOutputs = [],
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
virtualHalSimConfigSourceCoverage = null,
virtualHalSimConfigPromotionCandidates = null,
virtualHalSimConfigMacroLoadFixtures = null,
virtualHalMotionControllerMatrix = null,
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
promotedRuntimeFamilies = [],
@@ -1695,6 +1842,14 @@ export function createProjectReleaseReadinessReport({
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage,
);
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
virtualHalSimConfigPromotionCandidates,
);
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
virtualHalSimConfigMacroLoadFixtures,
);
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
);
@@ -1704,6 +1859,8 @@ export function createProjectReleaseReadinessReport({
const ready = releaseGatePassed &&
inventoryReady &&
virtualHalSimConfigSourceCoverageReady &&
virtualHalSimConfigPromotionCandidatesReady &&
virtualHalSimConfigMacroLoadFixturesReady &&
virtualHalMotionControllerMatrixReady &&
blockedRuntimeReady &&
axisScreenshotArtifactsReady;
@@ -1711,6 +1868,8 @@ export function createProjectReleaseReadinessReport({
...(releaseGatePassed ? [] : ["project-release-gate"]),
...(inventoryReady ? [] : ["sim-config-inventory"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtual-hal-sim-config-promotion-candidates"]),
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtual-hal-sim-config-macro-load-fixtures"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
@@ -1730,6 +1889,12 @@ export function createProjectReleaseReadinessReport({
simConfigInventory: inventory,
virtualHalSimConfigSourceCoverage,
virtualHalSimConfigSourceCoverageReady,
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigPromotionCandidatesReady,
virtualHalPromotionFamilySummary,
virtualHalPromotionFamilyRows,
virtualHalSimConfigMacroLoadFixtures,
virtualHalSimConfigMacroLoadFixturesReady,
virtualHalMotionControllerMatrix,
virtualHalMotionControllerMatrixReady,
blockedRuntimeFamilies: blockedFamilies,
@@ -1758,6 +1923,27 @@ export function createProjectReleaseReadinessReport({
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
},
{
id: "virtual-hal-promotion-families",
label: "Virtual HAL promotion families",
value: virtualHalPromotionFamilySummary,
},
...virtualHalPromotionFamilyRows.map((row) => ({
id: `virtual-hal-promotion-family-${row.id}`,
label: `Promotion family ${row.label}`,
value: row.value,
sourceFiles: row.sourceFiles,
})),
{
id: "virtual-hal-sim-config-macro-load-fixtures",
label: "Virtual HAL sim-config macro/load fixtures",
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
@@ -1786,6 +1972,8 @@ export function createProjectReleaseReadinessSummaryViewModel(
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalSimConfigPromotionCandidatesReady = report?.virtualHalSimConfigPromotionCandidatesReady === true;
const virtualHalSimConfigMacroLoadFixturesReady = report?.virtualHalSimConfigMacroLoadFixturesReady === true;
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
@@ -1823,6 +2011,16 @@ export function createProjectReleaseReadinessSummaryViewModel(
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
},
{
id: "virtual-hal-sim-config-macro-load-fixtures",
label: "Virtual HAL sim-config macro/load fixtures",
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
@@ -1910,6 +2108,27 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
label: "Virtual HAL sim-config source coverage",
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
},
{
id: "virtual-hal-sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: validation?.virtualHalSimConfigPromotionCandidatesReady === true ? "ready" : "missing",
},
{
id: "virtual-hal-promotion-families",
label: "Virtual HAL promotion families",
value: validation?.virtualHalPromotionFamilySummary ?? "not provided",
},
...arrayOrEmpty(validation?.virtualHalPromotionFamilyRows).map((row) => ({
id: `virtual-hal-promotion-family-${row.id}`,
label: `Promotion family ${row.label}`,
value: row.value,
sourceFiles: row.sourceFiles,
})),
{
id: "virtual-hal-sim-config-macro-load-fixtures",
label: "Virtual HAL sim-config macro/load fixtures",
value: validation?.virtualHalSimConfigMacroLoadFixturesReady === true ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
@@ -2179,6 +2398,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
let diagnosticsHttpStatus = null;
let diagnosticsFetchError = null;
let diagnosticsValidation = null;
let diagnosticsArtifact = null;
const diagnosticsMissing = [];
if (diagnosticsUrl) {
try {
@@ -2190,7 +2410,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
} else {
diagnosticsFetched = true;
const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
if (requireSavedSessionDiagnostics && diagnosticsValidation.hasSessionDiagnostics !== true) {
@@ -2226,6 +2446,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
jsonWorkflow,
validation: jsonWorkflow.validation,
diagnosticsValidation,
diagnosticsArtifact,
summaryViewModel: jsonWorkflow.summaryViewModel,
actionPlan: jsonWorkflow.actionPlan,
missing,
@@ -2305,6 +2526,8 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
workflow = {},
) {
const missing = arrayOrEmpty(workflow?.missing);
const promotionCandidateSummary = objectOrEmpty(workflow?.diagnosticsArtifact?.virtualHalPromotionCandidateSummary);
const promotionCandidateSummaryReady = promotionCandidateSummary.ready === true;
const ready = workflow?.ready === true;
const statusText = ready ? "Ready" : "Blocked";
const detailText = ready
@@ -2340,6 +2563,17 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
label: "Artifact validation",
value: workflow?.validation?.ready === true ? "ready" : "blocked",
},
{
id: "virtual-hal-promotion-families",
label: "Virtual HAL promotion families",
value: workflow?.validation?.virtualHalPromotionFamilySummary ?? "not provided",
},
...arrayOrEmpty(workflow?.validation?.virtualHalPromotionFamilyRows).map((row) => ({
id: `virtual-hal-promotion-family-${row.id}`,
label: `Promotion family ${row.label}`,
value: row.value,
sourceFiles: row.sourceFiles,
})),
{
id: "browser-diagnostics",
label: "Browser diagnostics",
@@ -2347,6 +2581,41 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
: "not requested",
},
{
id: "virtual-hal-promotion-candidate-summary",
label: "Virtual HAL promotion candidate summary",
value: workflow?.diagnosticsUrl
? (promotionCandidateSummaryReady ? "ready" : "not provided")
: "not requested",
},
{
id: "virtual-hal-promotion-candidate",
label: "Virtual HAL promotion candidate",
value: promotionCandidateSummary.preferredCandidateId ?? "not provided",
},
{
id: "virtual-hal-promotion-candidate-ini",
label: "Candidate INI",
value: promotionCandidateSummary.preferredIniPath ?? "not provided",
},
{
id: "virtual-hal-promotion-candidate-gcode",
label: "Candidate G-code",
value: promotionCandidateSummary.preferredGcodePath ?? "not provided",
},
{
id: "virtual-hal-promotion-candidate-count",
label: "Candidate readiness",
value: Number.isFinite(promotionCandidateSummary.readyCandidateCount) &&
Number.isFinite(promotionCandidateSummary.candidateCount)
? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready`
: "not provided",
},
{
id: "virtual-hal-promotion-candidate-baseline",
label: "Candidate inventory baseline",
value: promotionCandidateSummary.inventoryBaseline ?? "not provided",
},
{
id: "saved-session-diagnostics",
label: "Saved-session diagnostics",
@@ -2466,6 +2735,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
const virtualHalSimConfigPromotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
const virtualHalSimConfigMacroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
@@ -2515,6 +2786,14 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage,
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalSimConfigPromotionCandidatesReady = isVirtualHalSimConfigPromotionCandidateReportReady(
virtualHalSimConfigPromotionCandidates,
) && artifactObject.virtualHalSimConfigPromotionCandidatesReady === true;
const virtualHalPromotionFamilySummary = createPromotionFamilySummaryText(virtualHalSimConfigPromotionCandidates);
const virtualHalPromotionFamilyRows = createPromotionFamilySummaryRows(virtualHalSimConfigPromotionCandidates);
const virtualHalSimConfigMacroLoadFixturesReady = isVirtualHalSimConfigMacroLoadFixtureReportReady(
virtualHalSimConfigMacroLoadFixtures,
) && artifactObject.virtualHalSimConfigMacroLoadFixturesReady === true;
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
) && artifactObject.virtualHalMotionControllerMatrixReady === true;
@@ -2556,6 +2835,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtualHalSimConfigMacroLoadFixtures"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
? []
@@ -2591,6 +2872,10 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
gateResultMatrixReady: matrixReady,
gateActionPlanReady: actionPlanReady,
virtualHalSimConfigSourceCoverageReady,
virtualHalSimConfigPromotionCandidatesReady,
virtualHalPromotionFamilySummary,
virtualHalPromotionFamilyRows,
virtualHalSimConfigMacroLoadFixturesReady,
virtualHalMotionControllerMatrixReady,
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
@@ -2636,6 +2921,27 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: virtualHalSimConfigPromotionCandidatesReady ? "ready" : "missing",
},
{
id: "virtual-hal-promotion-families",
label: "Virtual HAL promotion families",
value: virtualHalPromotionFamilySummary,
},
...virtualHalPromotionFamilyRows.map((row) => ({
id: `virtual-hal-promotion-family-${row.id}`,
label: `Promotion family ${row.label}`,
value: row.value,
sourceFiles: row.sourceFiles,
})),
{
id: "virtual-hal-sim-config-macro-load-fixtures",
label: "Virtual HAL sim-config macro/load fixtures",
value: virtualHalSimConfigMacroLoadFixturesReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",

File diff suppressed because it is too large Load Diff

View File

@@ -101,6 +101,24 @@
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
cursor: pointer;
}
.source-filter-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 8px;
}
.source-filter-controls label {
color: var(--muted);
font: 12px/1.4 "Segoe UI", "Noto Sans", sans-serif;
}
.source-filter-controls select {
width: 100%;
min-height: 32px;
border: 1px solid var(--border);
border-radius: 4px;
padding: 6px 8px;
color: var(--text);
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
}
</style>
</head>
<body>
@@ -135,6 +153,15 @@
<p data-workflow-overview-release-readiness-artifact-gate-execution-summary-status>Waiting for URL workflow gate evidence.</p>
<dl data-workflow-overview-release-readiness-artifact-gate-execution-summary-rows></dl>
</section>
<section class="overview-status" aria-label="Promotion candidate drilldown" data-workflow-overview-promotion-candidate-drilldown>
<p data-workflow-overview-promotion-candidate-drilldown-status>Waiting for promotion candidate evidence.</p>
<dl data-workflow-overview-promotion-candidate-drilldown-rows></dl>
</section>
<section class="overview-status" aria-label="Promotion family source filter" data-workflow-overview-promotion-family-source-filter>
<p data-workflow-overview-promotion-family-source-filter-status>Waiting for promotion family source evidence.</p>
<div class="source-filter-controls" data-workflow-overview-promotion-family-source-filter-controls></div>
<dl data-workflow-overview-promotion-family-source-filter-rows></dl>
</section>
</section>
</main>
<script type="module">
@@ -178,6 +205,21 @@
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness,
@@ -248,8 +290,53 @@
renderState: gateExecutionSummaryRenderState,
documentRef: document,
});
const promotionCandidateDrilldown =
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(summary, {
diagnosticsArtifact: workflow.diagnosticsArtifact,
});
const promotionCandidateDrilldownRenderState =
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(
promotionCandidateDrilldown,
);
const promotionCandidateDrilldownMountResult =
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState({
renderState: promotionCandidateDrilldownRenderState,
documentRef: document,
});
const promotionFamilyRowsRenderState =
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(summary);
const promotionFamilySourceFilter =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
promotionFamilyRowsRenderState,
);
const promotionFamilySourceFilterMountResult =
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState({
viewModel: promotionFamilySourceFilter,
documentRef: document,
});
const promotionFamilySourceFilterBinding =
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls({
renderState: promotionFamilyRowsRenderState,
viewModel: promotionFamilySourceFilter,
documentRef: document,
render: true,
onChange: (changeResult) => {
document.body.dataset.promotionFamilySourceFilterFamily =
changeResult.selectedFamilyId;
document.body.dataset.promotionFamilySourceFilterSourceKind =
changeResult.selectedSourceKind;
document.body.dataset.promotionFamilySourceFilterCount =
String(changeResult.actionPlan.filteredSourceFileCount);
},
});
document.body.dataset.releaseArtifactUrlWorkflowPhase = workflow.phase;
document.body.dataset.releaseArtifactUrlWorkflowReady = workflow.ready ? "true" : "false";
document.body.dataset.promotionCandidateDrilldownReady =
promotionCandidateDrilldown.ready ? "true" : "false";
document.body.dataset.promotionFamilySourceFilterReady =
promotionFamilySourceFilter.ready ? "true" : "false";
document.body.dataset.promotionFamilySourceFilterCount =
String(promotionFamilySourceFilter.filteredSourceFileCount);
return {
workflow,
summary,
@@ -261,6 +348,13 @@
gateExecutionSummary,
gateExecutionSummaryRenderState,
gateExecutionSummaryMountResult,
promotionCandidateDrilldown,
promotionCandidateDrilldownRenderState,
promotionCandidateDrilldownMountResult,
promotionFamilyRowsRenderState,
promotionFamilySourceFilter,
promotionFamilySourceFilterMountResult,
promotionFamilySourceFilterBinding,
};
}
@@ -362,6 +456,69 @@
options ?? { documentRef: document },
)
),
getWorkflowOverviewPromotionFamilyRowsRenderState: (summaryViewModel) => (
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(summaryViewModel)
),
getWorkflowOverviewPromotionFamilySourceFilterViewModel: (renderState, options) => (
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(renderState, options)
),
getWorkflowOverviewPromotionFamilySourceFilterDomContract: (options) => (
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract(options)
),
getWorkflowOverviewPromotionFamilySourceFilterDomReadiness: (options) => (
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness(
options ?? { documentRef: document },
)
),
getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan: (renderState, options) => (
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan(renderState, options)
),
handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange: (options) => (
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange(
options ?? { documentRef: document },
)
),
bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls: (options) => (
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls(
options ?? { documentRef: document },
)
),
renderWorkflowOverviewPromotionFamilySourceFilterState: (viewModel, options) => (
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
viewModel,
options ?? { documentRef: document },
)
),
mountWorkflowOverviewPromotionFamilySourceFilterState: (options) => (
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
options ?? { documentRef: document },
)
),
getWorkflowOverviewPromotionCandidateDrilldownViewModel: (summaryViewModel, options) => (
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(summaryViewModel, options)
),
getWorkflowOverviewPromotionCandidateDrilldownRenderState: (viewModel) => (
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(viewModel)
),
getWorkflowOverviewPromotionCandidateDrilldownDomContract: (options) => (
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract(options)
),
getWorkflowOverviewPromotionCandidateDrilldownDomReadiness: (options) => (
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness(
options ?? { documentRef: document },
)
),
renderWorkflowOverviewPromotionCandidateDrilldownState: (renderState, options) => (
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
renderState,
options ?? { documentRef: document },
)
),
mountWorkflowOverviewPromotionCandidateDrilldownState: (options) => (
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
options ?? { documentRef: document },
)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState: (actionPlan) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan)
),

View File

@@ -683,6 +683,40 @@
font-weight: 700;
margin-right: 4px;
}
.promotion-family-panel {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 4px 7px;
border-top: 1px solid #c4c9cc;
padding-top: 4px;
}
.promotion-family-panel div {
border: 1px solid #b5bcc1;
background: #f1f1f1;
padding: 3px 4px;
}
.promotion-candidate-panel {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 4px 7px;
border-top: 1px solid #c4c9cc;
padding-top: 4px;
}
.promotion-candidate-panel div {
min-height: 27px;
border: 1px solid #b5bcc1;
background: #f7f7f7;
padding: 3px 4px;
}
.promotion-candidate-panel strong {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text);
}
.status-history {
border-top: 1px solid var(--line);
background: #f2f2f2;
@@ -1709,6 +1743,8 @@ M2
<div><span>Last error</span><strong data-diagnostics="last-error">none</strong></div>
<div><span>Readiness</span><strong data-diagnostics="readiness">unchecked</strong></div>
<div><span>Shortcut</span><strong data-diagnostics="shortcut">-</strong></div>
<div class="promotion-family-panel" data-promotion-family-rows></div>
<div class="promotion-candidate-panel" data-promotion-candidate-summary></div>
<div class="tool-table-mini" data-axis-shell="tool-table">
<table aria-label="Loaded tool table">
<thead>
@@ -2553,6 +2589,99 @@ M2
});
}
function renderPromotionFamilyRows(report = createVirtualHalSimConfigPromotionCandidateReport()) {
const container = document.querySelector("[data-promotion-family-rows]");
if (!container) {
return [];
}
const rows = Array.isArray(report.familyRows) ? report.familyRows : [];
container.textContent = "";
document.body.dataset.promotionFamilyReady = rows.every((row) =>
row.complete === true && row.explicitBrowserDiagnosticsReady === true
) ? "true" : "false";
document.body.dataset.promotionFamilyCount = String(rows.length);
for (const row of rows) {
const item = document.createElement("div");
item.dataset.promotionFamilyRow = row.id ?? "unknown";
item.dataset.promotionFamilyReady = row.complete === true && row.explicitBrowserDiagnosticsReady === true ? "true" : "false";
const label = document.createElement("span");
label.textContent = row.id ?? "unknown";
const value = document.createElement("strong");
value.dataset.promotionFamilyValue = row.id ?? "unknown";
value.textContent = `${row.completeCount ?? 0}/${row.candidateCount ?? 0} ready; sources=${row.sourceFiles?.length ?? 0}`;
item.append(label, value);
container.append(item);
}
return rows;
}
function createVirtualHalPromotionCandidateSummary(
report = createVirtualHalSimConfigPromotionCandidateReport(),
) {
const rows = Array.isArray(report.rows) ? report.rows : [];
const familyRows = Array.isArray(report.familyRows) ? report.familyRows : [];
const preferredCandidate = rows.find((row) => row.id === "qtdragon-multi-joint-on-abort") ?? rows[0] ?? null;
const readyRows = rows.filter((row) => row.complete === true);
const blockedRows = rows.filter((row) => row.blockedKind !== "-");
const explicitBrowserRows = rows.filter((row) => row.targetBrowserEvidence === "explicit-browser-diagnostics");
const sourceFiles = [...new Set(rows.flatMap((row) => row.sourceFiles ?? []))];
const ready = report.complete === true &&
readyRows.length === rows.length &&
blockedRows.length === 0 &&
explicitBrowserRows.length === rows.length &&
report.inventoryBaselineUnchanged === true;
return {
apiName: "real-browser-simulation-promotion-candidate-summary",
summaryVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
candidateCount: rows.length,
readyCandidateCount: readyRows.length,
familyCount: familyRows.length,
sourceFileCount: sourceFiles.length,
preferredCandidateId: preferredCandidate?.id ?? null,
preferredIniPath: preferredCandidate?.iniPath ?? null,
preferredGcodePath: preferredCandidate?.gcodePath ?? null,
blockedCandidateIds: blockedRows.map((row) => row.id),
explicitBrowserDiagnosticsCount: explicitBrowserRows.length,
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
sourceFiles,
rows: [
{ id: "candidate-count", label: "Candidates", value: `${readyRows.length}/${rows.length} ready` },
{ id: "family-count", label: "Families", value: `${familyRows.length}` },
{ id: "preferred-candidate", label: "First case", value: preferredCandidate?.id ?? "missing" },
{ id: "source-files", label: "Source files", value: `${sourceFiles.length}` },
{ id: "inventory-baseline", label: "Inventory", value: "28/28 pass; 131 skip unchanged" },
{ id: "blocked-candidates", label: "Hard blocks", value: blockedRows.length === 0 ? "excluded" : blockedRows.map((row) => row.id).join(", ") },
{ id: "browser-evidence", label: "Browser evidence", value: `${explicitBrowserRows.length}/${rows.length} explicit` },
{ id: "preferred-ini", label: "INI", value: preferredCandidate?.iniPath ?? "missing" },
],
};
}
function renderPromotionCandidateSummary(summary = createVirtualHalPromotionCandidateSummary()) {
const container = document.querySelector("[data-promotion-candidate-summary]");
if (!container) {
return summary;
}
container.textContent = "";
document.body.dataset.promotionCandidateReady = summary.ready ? "true" : "false";
document.body.dataset.promotionCandidateCount = String(summary.candidateCount);
document.body.dataset.promotionCandidatePreferred = summary.preferredCandidateId ?? "";
for (const row of summary.rows) {
const item = document.createElement("div");
item.dataset.promotionCandidateSummaryRow = row.id;
const label = document.createElement("span");
label.textContent = row.label;
const value = document.createElement("strong");
value.dataset.promotionCandidateSummaryValue = row.id;
value.textContent = row.value;
item.append(label, value);
container.append(item);
}
return summary;
}
function renderMachineStatusDom(machineStatus) {
const statusMap = {
"spindle-state": machineStatus.spindle.state,
@@ -2591,6 +2720,9 @@ M2
renderVirtualHalDro();
}
renderLimitsHomeState(createVirtualHalLimitsHomeState());
const promotionCandidateReport = createVirtualHalSimConfigPromotionCandidateReport();
renderPromotionFamilyRows(promotionCandidateReport);
renderPromotionCandidateSummary(createVirtualHalPromotionCandidateSummary(promotionCandidateReport));
document.querySelector("[data-axis-machine-control-state]")?.setAttribute("data-state", "ready");
return cloneVirtualHalState();
}
@@ -2642,6 +2774,7 @@ M2
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport(),
virtualHalPromotionCandidateSummary: createVirtualHalPromotionCandidateSummary(),
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport(),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
@@ -4081,6 +4214,8 @@ M2
getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(),
getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(),
getVirtualHalSimConfigPromotionCandidateReport: () => createVirtualHalSimConfigPromotionCandidateReport(),
getVirtualHalPromotionCandidateSummary: () => renderPromotionCandidateSummary(),
getVirtualHalPromotionFamilyRows: () => renderPromotionFamilyRows(),
getVirtualHalSimConfigMacroLoadFixtureReport: () => createVirtualHalSimConfigMacroLoadFixtureReport(),
getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(),
getVirtualHalMotionControllerMatrixReport: () => createVirtualHalMotionControllerMatrixReport(),

View File

@@ -19,6 +19,7 @@ export {
VIRTUAL_HAL_AXES,
VIRTUAL_HAL_AXISUI_PINS,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,