docs: refresh M5 live gate aggregator

This commit is contained in:
HWLAB Code Queue
2026-05-22 11:52:11 +00:00
parent f933a81c83
commit 80e8a2f208
5 changed files with 471 additions and 32 deletions
+201 -6
View File
@@ -9,8 +9,13 @@ import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV, SERVICE_IDS } fro
export const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const deprecatedLegacyPublicPort = 6000 + 667;
const deprecatedLegacyPublicEndpoint = `http://74.48.78.17:${deprecatedLegacyPublicPort}`;
const activeBrowserRoot = `${DEV_FRONTEND_ENDPOINT}/`;
const activeApiLiveEndpoint = `${DEV_ENDPOINT}/health/live`;
const deprecatedLegacyPublicEndpoints = Object.freeze([
"http://74.48.78.17:6666",
"http://74.48.78.17:6667"
]);
const latestFrontendRevision = "1e8805664970839b72be40c34636b08f6d18b131";
const levels = Object.freeze(["SOURCE", "LOCAL", "DRY-RUN", "DEV-LIVE", "BLOCKED"]);
const statusRank = Object.freeze({
pass: 0,
@@ -95,6 +100,26 @@ const sourceContracts = Object.freeze([
}
]);
const frontendDevFact = Object.freeze({
revision: latestFrontendRevision,
sources: [
"docs/dev-deploy-apply.md",
"docs/cloud-web-workbench.md",
"web/hwlab-cloud-web/index.html",
"web/hwlab-cloud-web/styles.css"
],
evidence: [
`Cloud Web /health/live accepted revision ${latestFrontendRevision}`,
"Cloud Workbench public browser endpoint is the active frontend route only",
"Frontend load/revision evidence cannot satisfy DB, M3 hardware-loop, M4 agent-loop, or M5 MVP e2e acceptance"
],
commands: [
"node web/hwlab-cloud-web/scripts/check.mjs",
"node scripts/dev-cloud-workbench-smoke.mjs --static"
],
summary: `#99/#108 Cloud Workbench revision ${latestFrontendRevision} is the latest accepted DEV frontend fact, but it is frontend-only evidence.`
});
function issue(id) {
return `pikasTech/HWLAB#${id}`;
}
@@ -356,6 +381,19 @@ function collectM2Evidence(reports) {
const m2EndpointLive = hasCurrentM2EndpointEvidence(m2Smoke);
return [
{
milestone: "M2",
issue: issue(99),
taskId: "cloud-web-workbench-frontend",
lifecycleState: "active",
level: "DEV-LIVE",
status: "pass",
category: "frontend-dev-revision",
sources: frontendDevFact.sources,
commands: frontendDevFact.commands,
evidence: frontendDevFact.evidence,
summary: frontendDevFact.summary
},
reportEvidence({
milestone: "M2",
level: "SOURCE",
@@ -622,7 +660,7 @@ function publicEndpointEvidence(m2Report) {
function isStaleLegacyIngressBlocker(blocker, sourceReport, reports) {
const text = `${blocker.scope ?? ""} ${blocker.summary ?? ""}`;
const staleLegacyPort = text.includes(deprecatedLegacyPublicEndpoint);
const staleLegacyPort = deprecatedLegacyPublicEndpoints.some((endpoint) => text.includes(endpoint));
const stalePreflightEdgeProbe = sourceReport.path === reportPaths.devPreflight &&
blocker.type === "network_blocker" &&
(blocker.scope === "dev-edge" || blocker.scope === "dev-edge-health") &&
@@ -690,6 +728,34 @@ function summarizeMilestone(milestone, highestVisibleLevel, status) {
return `${labels[milestone]}; highest visible level is ${highestVisibleLevel}; status is ${status}.`;
}
function buildMilestoneLevelClassification(milestones) {
const currentLabels = {
M0: "SOURCE",
M1: "LOCAL",
M2: "DEV-LIVE",
M3: "BLOCKED",
M4: "BLOCKED",
M5: "BLOCKED"
};
const notes = {
M0: "Source contract is green at source level only.",
M1: "Local smoke is green; it is not DEV-LIVE.",
M2: "Current public frontend/API route evidence is DEV-LIVE for route/front-end reachability only.",
M3: "DEV-LIVE hardware trusted loop is blocked; local/source shape is not acceptance.",
M4: "DEV-LIVE agent loop is blocked at DB live readiness; local smoke is not acceptance.",
M5: "Dry-run is green, but bounded DEV-LIVE MVP e2e is blocked."
};
return milestones.map((milestone) => ({
milestone: milestone.id,
currentLevel: currentLabels[milestone.id],
status: milestone.status,
strongestEvidenceLevel: milestone.highestVisibleLevel,
liveEvidence: milestone.liveEvidence,
summary: notes[milestone.id]
}));
}
function groupByLevel(evidence, blockers) {
const grouped = Object.fromEntries(levels.map((level) => [level, []]));
for (const item of evidence) {
@@ -836,13 +902,21 @@ function buildCurrentDevLayering(reports, blockers) {
const artifactSourceStates = artifactSourceStateCounts(reports.devArtifacts);
return {
frontendRevision: {
label: "Frontend DEV revision",
status: "pass",
evidenceLevel: "DEV-LIVE",
summary: `${activeBrowserRoot} serves the accepted Cloud Workbench frontend revision ${latestFrontendRevision}; this is browser/frontend evidence only.`,
evidence: frontendDevFact.evidence,
nextRequired: "Keep frontend revision proof separate from DB live readiness, M3 hardware-loop evidence, M4 agent-loop evidence, and M5 acceptance."
},
edgeRoute: {
label: "EDGE/ROUTE live",
status: m2EndpointLive || edgeLive ? "pass" : "blocked",
evidenceLevel: m2EndpointLive || edgeLive ? "DEV-LIVE" : "BLOCKED",
summary: m2EndpointLive
? "16666 browser, 16667 /health, and 16667 /health/live returned accepted HWLAB DEV responses in the active M2 read-only smoke."
: "No active read-only public endpoint report proves both 16666 and 16667.",
? `${activeBrowserRoot}, ${DEV_ENDPOINT}/health, and ${activeApiLiveEndpoint} returned accepted HWLAB DEV responses in the active M2 read-only smoke.`
: `No active read-only public endpoint report proves both ${activeBrowserRoot} and ${activeApiLiveEndpoint}.`,
evidence: publicEndpointEvidence(reports.devM2Smoke),
nextRequired: "Keep this separated from DB readiness, M3/M4 loop evidence, and M5 acceptance."
},
@@ -890,6 +964,60 @@ function buildCurrentDevLayering(reports, blockers) {
};
}
function buildMilestoneBlockerClassification(reports) {
const cloudDb = cloudApiDbStatus(reports);
const m3Live = statusIsPass(reports.devM3Hardware.liveOperation?.status);
const m4Live = statusIsPass(reports.devM4Agent.livePreflight?.status);
return [
{
milestone: "M3",
status: m3Live ? "pass" : "blocked",
currentLevel: m3Live ? "DEV-LIVE" : "BLOCKED",
blockerClass: m3Live ? "cleared" : "hardware-loop-runtime",
dependency: "Real DEV trusted loop through res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1.",
evidence: [
`operationId=${reports.devM3Hardware.liveOperation?.operationId ?? "not_observed"}`,
`traceId=${reports.devM3Hardware.liveOperation?.traceId ?? "not_observed"}`,
`auditId=${reports.devM3Hardware.liveOperation?.auditId ?? "not_observed"}`,
`evidenceId=${reports.devM3Hardware.liveOperation?.evidenceId ?? "not_observed"}`
],
nextRequired: "Run the bounded DEV M3 live smoke only after patch-panel topology can prove operation, trace, audit, and evidence IDs.",
nonPromotionReason: "Public frontend, route, and artifact evidence do not prove the required hardware loop."
},
{
milestone: "M4",
status: m4Live ? "pass" : "blocked",
currentLevel: m4Live ? "DEV-LIVE" : "BLOCKED",
blockerClass: m4Live ? "cleared" : "db-live-readiness",
dependency: "Cloud API /health/live must report DB ready=true, connected=true, and liveDbEvidence=true before live agent scheduling/evidence closure.",
evidence: [
`db.status=${cloudDb.status}`,
`db.ready=${cloudDb.ready}`,
`db.connected=${cloudDb.connected}`,
`db.liveDbEvidence=${cloudDb.liveDbEvidence}`
],
nextRequired: "Repair DB live readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass.",
nonPromotionReason: "Frontend revision and read-only route reachability do not prove DB-backed agent runtime readiness."
},
{
milestone: "M5",
status: "blocked",
currentLevel: "BLOCKED",
blockerClass: "composite-db-m3-m4-live",
dependency: "M5 needs DB live readiness, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage.",
evidence: [
"M5 dry-run is green",
`db.ready=${cloudDb.ready}`,
`m3.live=${m3Live}`,
`m4.live=${m4Live}`
],
nextRequired: "After DB/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations.",
nonPromotionReason: "No frontend, route-only, local, or dry-run evidence is allowed to stand in for bounded DEV-LIVE MVP e2e acceptance."
}
];
}
function artifactSourceStateCounts(artifactReport) {
const counts = { sourcePresent: 0, intentionallyDisabled: 0 };
for (const service of artifactReport.artifactPublish?.services ?? []) {
@@ -964,9 +1092,24 @@ function validateReport(report) {
assert.equal(report.issue, issue(58), "issue");
assert.equal(report.environment, ENVIRONMENT_DEV, "environment");
assert.equal(report.endpoint, DEV_ENDPOINT, "endpoint");
assert.equal(report.frontendEndpoint, DEV_FRONTEND_ENDPOINT, "frontendEndpoint");
assert.equal(report.activeEndpoints.frontend, activeBrowserRoot, "activeEndpoints.frontend");
assert.equal(report.activeEndpoints.apiLive, activeApiLiveEndpoint, "activeEndpoints.apiLive");
assert.equal(report.devOnly, true, "devOnly");
assert.equal(report.prodDisabled, true, "prodDisabled");
assert.deepEqual(Object.keys(report.levels), [...levels], "level keys");
assert.equal(report.latestFrontendDevFact.revision, latestFrontendRevision, "latestFrontendDevFact.revision");
assert.equal(report.latestFrontendDevFact.promotesM3M4M5, false, "frontend fact cannot promote M3/M4/M5");
assert.deepEqual(
report.milestoneLevelClassification.map((item) => item.milestone),
["M0", "M1", "M2", "M3", "M4", "M5"],
"milestoneLevelClassification"
);
assert.deepEqual(
report.milestoneBlockerClassification.map((item) => item.milestone),
["M3", "M4", "M5"],
"milestoneBlockerClassification"
);
assert.ok(report.blockers.length >= 1, "must remain blocked without DEV-LIVE evidence");
assert.equal(report.overall.status, "blocked", "overall status");
assert.ok(report.nextSteps.length >= 1, "next steps required");
@@ -994,8 +1137,10 @@ export async function buildReport() {
];
const blockers = collectBlockers(reports);
const milestones = deriveMilestones(evidence, blockers);
const milestoneLevelClassification = buildMilestoneLevelClassification(milestones);
const dod = buildDoD(reports, milestones, blockers);
const currentDevLayering = buildCurrentDevLayering(reports, blockers);
const milestoneBlockerClassification = buildMilestoneBlockerClassification(reports);
const nextSteps = buildNextSteps(blockers);
const report = {
"$schema": "https://hwlab.pikastech.local/schemas/dev-m5-gate-aggregator-v2.schema.json",
@@ -1008,6 +1153,16 @@ export async function buildReport() {
generatedFromCommit: gitCommit(),
environment: ENVIRONMENT_DEV,
endpoint: DEV_ENDPOINT,
frontendEndpoint: DEV_FRONTEND_ENDPOINT,
activeEndpoints: {
frontend: activeBrowserRoot,
apiLive: activeApiLiveEndpoint
},
deprecatedEndpoints: deprecatedLegacyPublicEndpoints.map((endpoint) => ({
endpoint,
status: "historical/deprecated",
activeGreenEligible: false
})),
devOnly: true,
prodDisabled: true,
reportLifecycle: activeReportLifecycle(
@@ -1041,10 +1196,20 @@ export async function buildReport() {
green: dod.green,
reason: dod.green
? "All #9 DEV DoD checks are green."
: "EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, DB live degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight."
: `Frontend revision ${latestFrontendRevision} and EDGE/ROUTE DEV-LIVE evidence exist on :16666/:16667, but M5 remains blocked by artifact source drift, DB live degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight.`
},
latestFrontendDevFact: {
...frontendDevFact,
issue: issue(99),
supports: [issue(99), issue(108), issue(78)],
endpoint: activeBrowserRoot,
evidenceLevel: "DEV-LIVE",
promotesM3M4M5: false
},
dod,
currentDevLayering,
milestoneLevelClassification,
milestoneBlockerClassification,
milestones,
evidence,
levels: groupByLevel(evidence, blockers),
@@ -1064,6 +1229,11 @@ export function formatCheckSummary(report) {
status: report.overall.status,
green: report.overall.green,
blockers: report.blockers.length,
activeEndpoints: report.activeEndpoints,
frontendRevision: report.latestFrontendDevFact.revision,
m3m4m5: Object.fromEntries(
report.milestoneBlockerClassification.map((item) => [item.milestone, item.status])
),
nextStep: report.nextSteps[0]?.action ?? null,
levels: Object.fromEntries(
Object.entries(report.levels).map(([level, entries]) => [level, entries.length])
@@ -1075,12 +1245,18 @@ export function renderMarkdown(report) {
const layeringLines = Object.entries(report.currentDevLayering)
.map(([, layer]) => `| ${layer.label} | ${layer.status} | ${layer.evidenceLevel} | ${layer.summary} | ${layer.nextRequired} |`)
.join("\n");
const milestoneLevelLines = report.milestoneLevelClassification
.map((item) => `| ${item.milestone} | ${item.currentLevel} | ${item.status} | ${item.strongestEvidenceLevel} | ${item.liveEvidence} | ${item.summary} |`)
.join("\n");
const milestoneLines = report.milestones
.map((milestone) => `| ${milestone.id} | ${milestone.status} | ${milestone.highestVisibleLevel} | ${milestone.liveEvidence} | ${milestone.summary} |`)
.join("\n");
const dodLines = report.dod.checks
.map((check) => `| ${check.id} | ${check.status} | ${check.evidenceLevel} | ${check.summary} |`)
.join("\n");
const blockerClassificationLines = report.milestoneBlockerClassification
.map((item) => `| ${item.milestone} | ${item.status} | ${item.currentLevel} | ${item.blockerClass} | ${item.dependency} | ${item.nextRequired} |`)
.join("\n");
const blockerLines = report.blockers
.map((blocker) => `| ${blocker.priority} | ${blocker.unblockOrder} | ${blocker.type} | ${blocker.scope} | ${blocker.summary} |`)
.join("\n");
@@ -1093,17 +1269,30 @@ export function renderMarkdown(report) {
Status: ${report.overall.status}
Generated from: \`${report.generatedFromCommit}\`
Scope: DEV only, report-only
Active frontend: \`${report.activeEndpoints.frontend}\`
Active API/live: \`${report.activeEndpoints.apiLive}\`
Deprecated public endpoints: ${report.deprecatedEndpoints.map((item) => `\`${item.endpoint}\``).join(", ")}
## Summary
${report.overall.reason}
## Frontend DEV Fact
The latest accepted #99/#108 frontend DEV fact is revision \`${report.latestFrontendDevFact.revision}\` at \`${report.latestFrontendDevFact.endpoint}\`. This is ${report.latestFrontendDevFact.evidenceLevel} browser/frontend evidence only and does not promote M3, M4, or M5.
## Current DEV Layering
| Layer | Status | Evidence level | Current conclusion | Required next proof |
| --- | --- | --- | --- | --- |
${layeringLines}
## M0-M5 Level Classification
| Milestone | Current classification | Status | Strongest evidence | Live evidence | Summary |
| --- | --- | --- | --- | --- | --- |
${milestoneLevelLines}
## Milestones
| Milestone | Status | Highest visible level | Live evidence | Summary |
@@ -1116,6 +1305,12 @@ ${milestoneLines}
| --- | --- | --- | --- |
${dodLines}
## M3/M4/M5 Blocker Classification
| Milestone | Status | Current level | Blocker class | Dependency | Required next proof |
| --- | --- | --- | --- | --- | --- |
${blockerClassificationLines}
## Blockers
| Priority | Order | Type | Scope | Summary |