增强promotion摘要可见性

结论:release URL workflow 已展示 family/source/browser diagnostics 三类 promotion summary 计数,baseline 保持 28/28/131/0,Python remap、tool DB、external user-M hard block 未解锁。
This commit is contained in:
2026-06-18 17:29:59 +08:00
parent 1c94ea4527
commit 56da43b7b1
8 changed files with 117 additions and 7 deletions

View File

@@ -611,6 +611,24 @@ event, and verifies that the source filter DOM re-renders with 2 rose-engine
source rows. This keeps the browser integration path explicit without changing source rows. This keeps the browser integration path explicit without changing
promotion status or CNC semantics. promotion status or CNC semantics.
## 2026-06-18 Release URL Promotion Summary Counts
The release artifact URL workflow summary now exposes the promotion candidate
summary counts as first-class rows:
```text
virtual-hal-promotion-family-count=3 families
virtual-hal-promotion-source-file-count=17 source files
virtual-hal-promotion-browser-diagnostics-count=8 diagnostics-ready
```
These rows are read from the existing browser diagnostics
`virtualHalPromotionCandidateSummary`. They make the current evidence-ready
promotion set easier to inspect from SDK, ini-panel, and browser workflow
surfaces. They do not add virtual HAL runtime capability, do not alter
`promotion-candidates.tsv`, and do not change the inventory baseline or any
hard-block lock.
## Boundary Statement ## Boundary Statement
This analysis relies on the improved virtual HAL only for Web simulation This analysis relies on the improved virtual HAL only for Web simulation

View File

@@ -2611,6 +2611,27 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready` ? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready`
: "not provided", : "not provided",
}, },
{
id: "virtual-hal-promotion-family-count",
label: "Promotion family count",
value: Number.isFinite(promotionCandidateSummary.familyCount)
? `${promotionCandidateSummary.familyCount} families`
: "not provided",
},
{
id: "virtual-hal-promotion-source-file-count",
label: "Promotion source files",
value: Number.isFinite(promotionCandidateSummary.sourceFileCount)
? `${promotionCandidateSummary.sourceFileCount} source files`
: "not provided",
},
{
id: "virtual-hal-promotion-browser-diagnostics-count",
label: "Promotion browser diagnostics",
value: Number.isFinite(promotionCandidateSummary.explicitBrowserDiagnosticsCount)
? `${promotionCandidateSummary.explicitBrowserDiagnosticsCount} diagnostics-ready`
: "not provided",
},
{ {
id: "virtual-hal-promotion-candidate-baseline", id: "virtual-hal-promotion-candidate-baseline",
label: "Candidate inventory baseline", label: "Candidate inventory baseline",

View File

@@ -2634,6 +2634,27 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo
? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready` ? `${promotionCandidateSummary.readyCandidateCount}/${promotionCandidateSummary.candidateCount} ready`
: "not provided", : "not provided",
}, },
{
id: "virtual-hal-promotion-family-count",
label: "Promotion family count",
value: Number.isFinite(promotionCandidateSummary.familyCount)
? `${promotionCandidateSummary.familyCount} families`
: "not provided",
},
{
id: "virtual-hal-promotion-source-file-count",
label: "Promotion source files",
value: Number.isFinite(promotionCandidateSummary.sourceFileCount)
? `${promotionCandidateSummary.sourceFileCount} source files`
: "not provided",
},
{
id: "virtual-hal-promotion-browser-diagnostics-count",
label: "Promotion browser diagnostics",
value: Number.isFinite(promotionCandidateSummary.explicitBrowserDiagnosticsCount)
? `${promotionCandidateSummary.explicitBrowserDiagnosticsCount} diagnostics-ready`
: "not provided",
},
{ {
id: "virtual-hal-promotion-candidate-baseline", id: "virtual-hal-promotion-candidate-baseline",
label: "Candidate inventory baseline", label: "Candidate inventory baseline",

View File

@@ -597,7 +597,7 @@
); );
assertEqual( assertEqual(
releaseArtifactUrlWorkflowRenderResult.rowCount, releaseArtifactUrlWorkflowRenderResult.rowCount,
18, 21,
"workflow overview release artifact URL workflow render rows", "workflow overview release artifact URL workflow render rows",
); );
assertEqual( assertEqual(
@@ -630,6 +630,21 @@
"8/8 ready", "8/8 ready",
"workflow overview release artifact URL workflow candidate count", "workflow overview release artifact URL workflow candidate count",
); );
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value,
"3 families",
"workflow overview release artifact URL workflow family count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value,
"17 source files",
"workflow overview release artifact URL workflow source file count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value,
"8 diagnostics-ready",
"workflow overview release artifact URL workflow browser diagnostics count",
);
assertEqual( assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=28 passed=28 skipped=131 unexpected_fail=0", "executed=28 passed=28 skipped=131 unexpected_fail=0",
@@ -751,6 +766,11 @@
"8/8 ready", "8/8 ready",
"workflow overview fixed URL workflow form candidate count row", "workflow overview fixed URL workflow form candidate count row",
); );
assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="virtual-hal-promotion-source-file-count"]').textContent,
"17 source files",
"workflow overview fixed URL workflow form source file count row",
);
assertEqual( assertEqual(
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent, workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-value="next-command"]').textContent,
"none", "none",

View File

@@ -162,13 +162,25 @@ assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value, readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
"8/8 ready", "8/8 ready",
); );
assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value,
"3 families",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value,
"17 source files",
);
assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value,
"8 diagnostics-ready",
);
assert.equal( assert.equal(
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=28 passed=28 skipped=131 unexpected_fail=0", "executed=28 passed=28 skipped=131 unexpected_fail=0",
); );
assert.deepEqual( assert.deepEqual(
readySummary.rows readySummary.rows
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id))
.map(({ id, value }) => [id, value]), .map(({ id, value }) => [id, value]),
expectedPromotionFamilyRows, expectedPromotionFamilyRows,
); );
@@ -190,6 +202,9 @@ assert.deepEqual(
["virtual-hal-promotion-candidate-ini", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"], ["virtual-hal-promotion-candidate-ini", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"],
["virtual-hal-promotion-candidate-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"], ["virtual-hal-promotion-candidate-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
["virtual-hal-promotion-candidate-count", "8/8 ready"], ["virtual-hal-promotion-candidate-count", "8/8 ready"],
["virtual-hal-promotion-family-count", "3 families"],
["virtual-hal-promotion-source-file-count", "17 source files"],
["virtual-hal-promotion-browser-diagnostics-count", "8 diagnostics-ready"],
["virtual-hal-promotion-candidate-baseline", "executed=28 passed=28 skipped=131 unexpected_fail=0"], ["virtual-hal-promotion-candidate-baseline", "executed=28 passed=28 skipped=131 unexpected_fail=0"],
["saved-session-diagnostics", "ready"], ["saved-session-diagnostics", "ready"],
["next-command", "none"], ["next-command", "none"],

View File

@@ -457,6 +457,9 @@ assert.deepEqual(
["virtual-hal-promotion-candidate-ini", "not provided"], ["virtual-hal-promotion-candidate-ini", "not provided"],
["virtual-hal-promotion-candidate-gcode", "not provided"], ["virtual-hal-promotion-candidate-gcode", "not provided"],
["virtual-hal-promotion-candidate-count", "not provided"], ["virtual-hal-promotion-candidate-count", "not provided"],
["virtual-hal-promotion-family-count", "not provided"],
["virtual-hal-promotion-source-file-count", "not provided"],
["virtual-hal-promotion-browser-diagnostics-count", "not provided"],
["virtual-hal-promotion-candidate-baseline", "not provided"], ["virtual-hal-promotion-candidate-baseline", "not provided"],
["saved-session-diagnostics", "not requested"], ["saved-session-diagnostics", "not requested"],
["next-command", "none"], ["next-command", "none"],

View File

@@ -1680,7 +1680,7 @@ assert.deepEqual(
createProjectReleaseReadinessArtifactValidationSummaryViewModel( createProjectReleaseReadinessArtifactValidationSummaryViewModel(
releaseReadinessArtifactValidation, releaseReadinessArtifactValidation,
).rows ).rows
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id))
.map(({ id, value }) => [id, value]), .map(({ id, value }) => [id, value]),
expectedPromotionFamilyRows, expectedPromotionFamilyRows,
); );
@@ -1751,7 +1751,7 @@ assert.deepEqual(
}), }),
}), }),
).rows ).rows
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id))
.map(({ id, value }) => [id, value]), .map(({ id, value }) => [id, value]),
expectedPromotionFamilyRows, expectedPromotionFamilyRows,
); );

View File

@@ -1939,6 +1939,18 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value, releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
"8/8 ready", "8/8 ready",
); );
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-family-count")?.value,
"3 families",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-source-file-count")?.value,
"17 source files",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-browser-diagnostics-count")?.value,
"8 diagnostics-ready",
);
assert.equal( assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value, releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=28 passed=28 skipped=131 unexpected_fail=0", "executed=28 passed=28 skipped=131 unexpected_fail=0",
@@ -2058,7 +2070,7 @@ assert.equal(
); );
assert.deepEqual( assert.deepEqual(
releaseArtifactUrlWorkflowSummary.rows releaseArtifactUrlWorkflowSummary.rows
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-")) .filter(({ id }) => expectedPromotionFamilyRows.some(([familyId]) => familyId === id))
.map(({ id, value }) => [id, value]), .map(({ id, value }) => [id, value]),
expectedPromotionFamilyRows, expectedPromotionFamilyRows,
); );
@@ -2565,7 +2577,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode, rowsNode: releaseArtifactUrlWorkflowRowsNode,
}); });
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true); assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 22); assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 25);
assert.equal( assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"), releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true, true,
@@ -2583,7 +2595,7 @@ assert.equal(
true, true,
); );
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true); assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 22); assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 25);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({ const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json", artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({ fetchRef: async () => ({