From f87a72732cf199f89a625b27d6966895f2361c6d Mon Sep 17 00:00:00 2001 From: mes123456 Date: Mon, 7 Sep 2026 11:47:42 +0800 Subject: [PATCH] test(web): enforce complete one-pass smoke closure --- my-tools/show_blender_web_scheduler.py | 2 ++ .../WBV2/feature-completeness-audit.json | 1 + tools/web/audit-wbv2-feature-completeness.mjs | 3 ++- tools/web/check-wbv2-feature-smoke-plan.mjs | 23 ++++++++++++++++++- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/my-tools/show_blender_web_scheduler.py b/my-tools/show_blender_web_scheduler.py index 086b648e3..c3524718d 100755 --- a/my-tools/show_blender_web_scheduler.py +++ b/my-tools/show_blender_web_scheduler.py @@ -430,6 +430,7 @@ def format_progress(progress: dict, fast_path: dict | None = None) -> list[str]: ledger = fast_path.get("failureLedger", {}) planner = fast_path.get("autoPlanner", {}) product = progress.get("productMigration", {}) + smoke = progress.get("completenessAudit", {}).get("smoke", {}) product_estimate = product.get("estimatedRemainingMinutes") return [ "", @@ -439,6 +440,7 @@ def format_progress(progress: dict, fast_path: dict | None = None) -> list[str]: f" implementation estimate: {product_estimate if product_estimate is not None else 'learning'} minutes remaining; verification={product.get('verificationPolicy') or 'deferred'}", f" source traceability reference: {progress.get('verifiedUnits', 0)}/{progress.get('totalUnits', 0)} bound; excluded from product completion denominator", f" completeness audit: {progress.get('completenessAudit', {}).get('status', 'MISSING')} blocked={sum(item.get('status') == 'BLOCKED' for item in progress.get('completenessAudit', {}).get('actualMissingOrBlocked', []))} behavioral-unverified={progress.get('completenessAudit', {}).get('behavioralEvidence', {}).get('unverifiedUnits', 0)}", + f" one-pass feature smoke: resolved={smoke.get('resolved', 0)}/{smoke.get('totalFeatures', 0)} attempts={smoke.get('attempts', 0)} retries={smoke.get('retries', 0)} unresolved={len(smoke.get('unresolved', []))}", f" task plan: {plan.get('done', 0)}/{plan.get('tasks', 0)} done, active={','.join(plan.get('active', [])) or 'none'}, blocked={plan.get('blocked', 0)}", f" old verification estimate: remaining={estimate_text}, total={total_text} (reference only; strategy retired)", f" observed bottleneck: stage={progress.get('bottleneck', {}).get('stage') or 'n/a'} median={progress.get('bottleneck', {}).get('medianDurationMs') or 0}ms command={progress.get('bottleneck', {}).get('label') or 'n/a'}", diff --git a/tests/golden/WBV2/feature-completeness-audit.json b/tests/golden/WBV2/feature-completeness-audit.json index 8c6403e05..3b12212b2 100644 --- a/tests/golden/WBV2/feature-completeness-audit.json +++ b/tests/golden/WBV2/feature-completeness-audit.json @@ -64,6 +64,7 @@ "totalFeatures": 34, "attempts": 34, "passed": 31, + "resolved": 34, "initialFailed": [ { "featureId": "WEBUI-OPERATOR-PALETTE", diff --git a/tools/web/audit-wbv2-feature-completeness.mjs b/tools/web/audit-wbv2-feature-completeness.mjs index 599a175f6..1c932080b 100644 --- a/tools/web/audit-wbv2-feature-completeness.mjs +++ b/tools/web/audit-wbv2-feature-completeness.mjs @@ -58,6 +58,7 @@ const audit = { totalFeatures: smokePlan.cases.length, attempts: smokeReport.cases.reduce((sum, entry) => sum + Number(entry.attempts ?? 0), 0), passed: smokeReport.cases.filter((entry) => entry.status === "PASS").length, + resolved: smokeReport.cases.filter((entry) => entry.status === "PASS").length + focusedPasses.size, initialFailed: failedSmoke, unresolved: unresolvedSmoke, focusedFix: smokeFix.fixes, @@ -80,4 +81,4 @@ const audit = { }; const outputPath = path.join(root, "tests/golden/WBV2/feature-completeness-audit.json"); fs.writeFileSync(outputPath, `${JSON.stringify(audit, null, 2)}\n`); -process.stdout.write(`wbv2-feature-completeness-audit status=${audit.status} modules=${audit.implementation.workflowModules.implemented}/${audit.implementation.workflowModules.total} tasks=${audit.implementation.productTasks.implemented}/${audit.implementation.productTasks.total} routes=${audit.routeCoverage.classifiedCount}/${audit.routeCoverage.unitCount} verified=${audit.behavioralEvidence.verifiedUnits} unverified=${audit.behavioralEvidence.unverifiedUnits} smokePassed=${audit.smoke.passed}/${audit.smoke.totalFeatures} blocked=${audit.actualMissingOrBlocked.filter((item) => item.status === "BLOCKED").length}\n`); +process.stdout.write(`wbv2-feature-completeness-audit status=${audit.status} modules=${audit.implementation.workflowModules.implemented}/${audit.implementation.workflowModules.total} tasks=${audit.implementation.productTasks.implemented}/${audit.implementation.productTasks.total} routes=${audit.routeCoverage.classifiedCount}/${audit.routeCoverage.unitCount} verified=${audit.behavioralEvidence.verifiedUnits} unverified=${audit.behavioralEvidence.unverifiedUnits} smokeResolved=${audit.smoke.resolved}/${audit.smoke.totalFeatures} blocked=${audit.actualMissingOrBlocked.filter((item) => item.status === "BLOCKED").length}\n`); diff --git a/tools/web/check-wbv2-feature-smoke-plan.mjs b/tools/web/check-wbv2-feature-smoke-plan.mjs index 04108682c..7b41515a8 100644 --- a/tools/web/check-wbv2-feature-smoke-plan.mjs +++ b/tools/web/check-wbv2-feature-smoke-plan.mjs @@ -21,6 +21,27 @@ assert.equal(new Set(plan.cases.map((item) => item.featureId)).size, plan.cases. assert.equal(new Set(plan.cases.map((item) => item.smokeId)).size, plan.cases.length); assert.ok(plan.cases.every((item) => item.runOnce === true && item.maxAttempts === 1)); assert.equal(report.cases.length, plan.cases.length); +assert.ok(report.cases.every((item) => item.attempts === 1), "every feature must have exactly one smoke attempt"); +const initialPassed = report.cases.filter((item) => item.status === "PASS"); +const initialFailed = report.cases.filter((item) => item.status !== "PASS"); +const focusedPassed = focused.fixes.filter((item) => item.status === "PASS"); +assert.deepEqual( + focusedPassed.map((item) => item.featureId).sort(), + initialFailed.map((item) => item.featureId).sort(), + "focused fixes must resolve exactly the initial smoke failures", +); assert.equal(focused.retries, 0); +const resolved = initialPassed.length + focusedPassed.length; +assert.equal(manifest.status, "pass"); +assert.deepEqual(manifest.summary, { + totalFeatures: plan.cases.length, + smokeAttempts: plan.cases.length, + initialPassed: initialPassed.length, + focusedFixPassed: focusedPassed.length, + blocked: 0, + retries: 0, + remainingBlocker: null, +}); +assert.equal(resolved, plan.cases.length); for (const artifact of [manifest.plan, manifest.report, manifest.focusedFixReport, manifest.planDocument]) assert.equal(sha(artifact.path), artifact.sha256, `smoke artifact hash mismatch ${artifact.path}`); -process.stdout.write(`wbv2-feature-smoke-plan-ok features=${plan.cases.length} attempts=${manifest.summary.smokeAttempts} initialPassed=${manifest.summary.initialPassed} focusedFixPassed=${manifest.summary.focusedFixPassed} blocked=${manifest.summary.blocked} retries=${focused.retries}\n`); +process.stdout.write(`wbv2-feature-smoke-plan-ok features=${plan.cases.length} attempts=${manifest.summary.smokeAttempts} initialPassed=${manifest.summary.initialPassed} focusedFixPassed=${manifest.summary.focusedFixPassed} resolved=${resolved}/${plan.cases.length} blocked=${manifest.summary.blocked} retries=${focused.retries}\n`);