diff --git a/README.md b/README.md index 6248e249..c2708981 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The Enterprise Lab Cockpit provides controlled lab evidence, local reproducibili - Reviewer trust map: [`docs/REVIEWER_TRUST_MAP.md`](docs/REVIEWER_TRUST_MAP.md). - Framing guide: [`docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md`](docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md). - Decision Vector contract: [`docs/ENTERPRISE_LAB_DECISION_VECTOR.md`](docs/ENTERPRISE_LAB_DECISION_VECTOR.md); read-only Dominant Factor Analysis lane: [`docs/ENTERPRISE_LAB_DOMINANT_FACTOR_ANALYSIS.md`](docs/ENTERPRISE_LAB_DOMINANT_FACTOR_ANALYSIS.md); read-only Decision Delta Analysis lane: [`docs/ENTERPRISE_LAB_DECISION_DELTA_ANALYSIS.md`](docs/ENTERPRISE_LAB_DECISION_DELTA_ANALYSIS.md); read-only Decision Replay Snapshot lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_SNAPSHOT.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_SNAPSHOT.md); read-only Decision Replay Reconstruction Trace lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_RECONSTRUCTION_TRACE.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_RECONSTRUCTION_TRACE.md); read-only Decision Replay Capsule lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_CAPSULE.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_CAPSULE.md); read-only Decision Replay Readiness Checklist lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_READINESS_CHECKLIST.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_READINESS_CHECKLIST.md); read-only Decision Replay Evidence Source Map lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_SOURCE_MAP.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_SOURCE_MAP.md); read-only Decision Replay Evidence Boundary Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_BOUNDARY_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_BOUNDARY_SUMMARY.md); read-only Decision Replay Evidence Field Inventory lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_FIELD_INVENTORY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_FIELD_INVENTORY.md); read-only Decision Evidence Null-Safety Summary lane: [`docs/ENTERPRISE_LAB_DECISION_EVIDENCE_NULL_SAFETY_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_EVIDENCE_NULL_SAFETY_SUMMARY.md); read-only Decision Evidence Status Rollup lane: [`docs/ENTERPRISE_LAB_DECISION_EVIDENCE_STATUS_ROLLUP.md`](docs/ENTERPRISE_LAB_DECISION_EVIDENCE_STATUS_ROLLUP.md); read-only Decision Replay Evidence Lane Navigation Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_NAVIGATION_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_NAVIGATION_SUMMARY.md); read-only Decision Replay Evidence Lane Dependency Map lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_MAP.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_MAP.md); read-only Decision Replay Evidence Lane Reference Index lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_REFERENCE_INDEX.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_REFERENCE_INDEX.md); read-only Decision Replay Evidence Lane Dependency Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_SUMMARY.md); read-only Decision Replay Evidence Lane Consistency Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_CONSISTENCY_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_CONSISTENCY_SUMMARY.md); read-only Decision Replay Evidence Reviewer Snapshot lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_SNAPSHOT.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_SNAPSHOT.md); read-only Decision Replay Evidence Reviewer Guidance lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md); read-only Decision Replay Evidence Reviewer Handoff Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_HANDOFF_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_HANDOFF_SUMMARY.md); read-only Decision Replay Evidence Reviewer Closure Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md); read-only Decision Replay Evidence Closure Rollup lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md); read-only Decision Replay Evidence Closure Checklist lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md). +- Read-only Decision Replay Evidence Closure Packet lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md). It is in-response reviewer metadata only, not an export/share/download packet. LoadBalancerPro is becoming **LoadBalancerPro Enterprise Lab**: a Java 17 / Spring Boot lab for adaptive-routing scenarios, deterministic replay, LASE shadow/influence comparison, policy gates, scorecards, evidence export, SRE walkthroughs, and a carefully bounded Production Gateway Candidate track. @@ -759,6 +760,8 @@ The allocation APIs are calculation-only. Scaling recommendations are simulation `POST /api/routing/compare` compares supported routing strategies against caller-provided candidate telemetry. Supported strategy IDs are `ROUND_ROBIN`, `TAIL_LATENCY_POWER_OF_TWO`, `WEIGHTED_LEAST_LOAD`, `WEIGHTED_LEAST_CONNECTIONS`, and `WEIGHTED_ROUND_ROBIN`. It is read-only and recommendation-only: it returns strategy results, explanations, additive Decision Vector evidence, Dominant Factor Analysis, Decision Delta Analysis, Decision Replay Snapshot metadata/fingerprints, Decision Replay Reconstruction Trace evidence steps/fingerprints, Decision Replay Capsule canonical evidence packaging/fingerprints, Decision Replay Readiness Checklist status, Decision Replay Evidence Source Map relationships, Decision Replay Evidence Boundary Summary metadata, Decision Replay Evidence Field Inventory metadata, Decision Evidence Null-Safety Summary metadata, Decision Evidence Status Rollup metadata, Decision Replay Evidence Lane Navigation Summary metadata, Decision Replay Evidence Lane Dependency Map metadata, Decision Replay Evidence Lane Reference Index metadata, Decision Replay Evidence Lane Dependency Summary metadata, Decision Replay Evidence Lane Consistency Summary metadata, Decision Replay Evidence Reviewer Snapshot metadata, Decision Replay Evidence Reviewer Guidance metadata, Decision Replay Evidence Reviewer Handoff Summary metadata, Decision Replay Evidence Reviewer Closure Summary metadata, top-level Decision Replay Evidence Closure Rollup metadata, and top-level Decision Replay Evidence Closure Checklist metadata where available. It does not call `CloudManager` or AWS, does not mutate cloud resources, does not mutate `LoadBalancer` allocation state, does not execute replay, does not perform what-if mutation, does not persist snapshot, trace, capsule, checklist, source-map, boundary-summary, field-inventory, null-safety-summary, status-rollup, lane-navigation, lane-dependency, lane-reference-index, lane-dependency-summary, lane-consistency-summary, reviewer-snapshot, reviewer-guidance, reviewer-handoff, reviewer-closure, reviewer-closure-rollup, or reviewer-closure-checklist state, or audit logs, does not generate a new source-map, boundary-summary, field-inventory, null-safety, status-rollup, lane-navigation, lane-dependency, lane-reference-index, lane-dependency-summary, lane-consistency-summary, reviewer-snapshot, reviewer-guidance, reviewer-handoff, reviewer-closure, reviewer-closure-rollup, or reviewer-closure-checklist fingerprint, does not add upload/share/download/export/PDF/ZIP behavior, and does not alter the capacity-aware or predictive allocation endpoints. +The same compare response can expose top-level `decisionReplayEvidenceReviewerClosurePacket` metadata derived only from returned closure summary, rollup, and checklist data. The packet is read-only in-response reviewer metadata, not an export/share/download packet, and it does not claim replay proof, scoring proof, correctness validation, production readiness, production certification, guaranteed replay, or production validation. + Optional lightweight reverse proxy mode is available at `/proxy/**` when `loadbalancerpro.proxy.enabled=true`. It forwards real HTTP requests to configured local or simulated upstreams, removes the `/proxy` prefix, preserves the method, path suffix, query string, request body, safe headers, upstream status, and upstream body, and adds `X-LoadBalancerPro-Upstream` plus `X-LoadBalancerPro-Strategy` response headers. The first operator-configured proxy foundation supports named `loadbalancerpro.proxy.routes.` entries with path prefixes, per-route strategy selection, and route-specific backend targets without editing Java code; the legacy global upstream list remains supported for existing demos. It reuses the request-level routing strategy registry and skips upstreams configured with `healthy=false`. Optional active health checks can dynamically skip failing local upstreams, optional bounded retries can select another eligible upstream for configured idempotent methods, optional process-local cooldown can temporarily skip repeatedly failing upstreams, and `GET /api/proxy/status` exposes read-only process-local forwarding/failure/retry/cooldown/status-class counters plus effective health state. Open `http://localhost:8080/proxy-status.html` for a no-dependency read-only browser view of that status JSON, upstream table, counters, retry/cooldown state, and local demo curl commands. In prod or cloud-sandbox API-key mode, `/proxy/**` and `GET /api/proxy/status` require `X-API-Key`; in OAuth2 mode they require the configured allocation role, which defaults to `operator`. Local/default API-key mode stays demo-friendly, so keep proxy mode loopback-bound or behind trusted private controls unless deployment-level access control and TLS termination are in place. Strategy-specific local recipes now cover `ROUND_ROBIN`, `WEIGHTED_ROUND_ROBIN`, and health-aware failover selected-upstream evidence with loopback backends only; [`PROXY_DEMO_STACK.md`](docs/PROXY_DEMO_STACK.md) is the single quick-start path for Windows PowerShell, Unix shell, checked-in demo profiles, the Java `ProxyDemoFixtureLauncher`, startup commands, curl verification, status-page review, cleanup, and troubleshooting. [`OPERATOR_PACKAGING.md`](docs/OPERATOR_PACKAGING.md) adds a one-command Maven exec fixture launcher recipe, packaged-jar proxy startup examples, and copy/adapt real-backend property examples under `docs/examples/proxy`. [`OPERATOR_DISTRIBUTION_SMOKE_KIT.md`](docs/OPERATOR_DISTRIBUTION_SMOKE_KIT.md) adds release-free smoke checks for packaged jar startup, Maven exec launcher readiness, proxy profiles, real-backend examples, and packaged static resources. [`LOCAL_ARTIFACT_VERIFICATION.md`](docs/LOCAL_ARTIFACT_VERIFICATION.md) adds release-free local and CI SHA-256, `jar tf`, manifest/resource, static page, demo profile, and launcher-class verification commands, including the `packaged-artifact-smoke` workflow artifact. [`CI_ARTIFACT_CONSUMER_GUIDE.md`](docs/CI_ARTIFACT_CONSUMER_GUIDE.md) shows how to consume JaCoCo, packaged artifact smoke, and SBOM workflow artifacts without creating release assets. [`RELEASE_CANDIDATE_DRY_RUN.md`](docs/RELEASE_CANDIDATE_DRY_RUN.md) turns those checks into one release-free go/no-go packet. The mode is disabled by default, retries and cooldown are disabled by default, non-idempotent retries require explicit opt-in, it does not construct `CloudManager`, does not mutate cloud state, does not persist metrics or cooldown state, and is not a production gateway, benchmark, certification, legal compliance proof, identity proof, TLS terminator, or WebSocket proxy. See [`REVERSE_PROXY_MODE.md`](docs/REVERSE_PROXY_MODE.md), [`REVERSE_PROXY_HEALTH_AND_METRICS.md`](docs/REVERSE_PROXY_HEALTH_AND_METRICS.md), [`REVERSE_PROXY_RESILIENCE.md`](docs/REVERSE_PROXY_RESILIENCE.md), [`PROXY_OPERATOR_STATUS_UI.md`](docs/PROXY_OPERATOR_STATUS_UI.md), [`PROXY_STRATEGY_DEMO_LAB.md`](docs/PROXY_STRATEGY_DEMO_LAB.md), [`PROXY_DEMO_STACK.md`](docs/PROXY_DEMO_STACK.md), [`PROXY_DEMO_FIXTURE_LAUNCHER.md`](docs/PROXY_DEMO_FIXTURE_LAUNCHER.md), [`OPERATOR_PACKAGING.md`](docs/OPERATOR_PACKAGING.md), [`OPERATOR_DISTRIBUTION_SMOKE_KIT.md`](docs/OPERATOR_DISTRIBUTION_SMOKE_KIT.md), [`LOCAL_ARTIFACT_VERIFICATION.md`](docs/LOCAL_ARTIFACT_VERIFICATION.md), [`CI_ARTIFACT_CONSUMER_GUIDE.md`](docs/CI_ARTIFACT_CONSUMER_GUIDE.md), and [`RELEASE_CANDIDATE_DRY_RUN.md`](docs/RELEASE_CANDIDATE_DRY_RUN.md). Open `http://localhost:8080/routing-demo.html` for the Enterprise Lab routing cockpit legacy route. It calls `/api/health`, Actuator readiness, and the existing `/api/routing/compare` endpoint; loads a deterministic controlled lab routing scenario; displays the selected server and reason per strategy; preserves raw JSON; and provides copyable curl, request payload, normalized response-summary blocks, and reviewer/operator proof notes. Import `postman/LoadBalancerPro.postman_collection.json` and run the legacy `Routing Decision Demo` folder for Postman parity. The routing cockpit is local/operator lab review only, not certification, not benchmark proof, not legal compliance proof, and not identity proof. diff --git a/docs/API_CONTRACTS.md b/docs/API_CONTRACTS.md index c26d2d8e..2ea86f4a 100644 --- a/docs/API_CONTRACTS.md +++ b/docs/API_CONTRACTS.md @@ -57,7 +57,7 @@ The performance baseline runner uses the existing API contracts only; it adds no The prior CLI experiment harness remains available as an offline comparison path. Run `--adaptive-routing-experiment=all` through `scripts/smoke/adaptive-routing-experiment.ps1 -Package` to generate ignored `target/adaptive-routing-experiments/` evidence. Run `scripts/smoke/enterprise-lab-workflow.ps1 -Package` to generate the Enterprise Lab scenario catalog JSON, lab run JSON, Markdown scorecard summary, and metadata under ignored `target/enterprise-lab-runs/`. Run `scripts/smoke/controlled-adaptive-routing-policy.ps1 -Package` to generate controlled policy evidence under ignored `target/controlled-adaptive-routing/`. Run `scripts/smoke/enterprise-lab-observability-pack.ps1 -Package` to generate metrics JSON, Prometheus-style sample text, Markdown summary, and manifest under ignored `target/enterprise-lab-observability/`. The default runtime allocation behavior remains unchanged; active-experiment remains explicit, guarded, and lab/evaluation-grade rather than production traffic control or production SLO certification. -Routing comparison responses expose `requestedStrategies`, `candidateCount`, `timestamp`, optional response-level `decisionReplayEvidenceReviewerClosureRollup`, optional response-level `decisionReplayEvidenceReviewerClosureChecklist`, and a `results` array. Each result exposes the strategy id, status, selected server id when one is available, the strategy reason, considered candidates, and score map when the strategy reports scores. A no-healthy-server comparison still returns a controlled result with `chosenServerId` set to `null`, empty candidate/scores collections, and an explanatory reason. +Routing comparison responses expose `requestedStrategies`, `candidateCount`, `timestamp`, optional response-level `decisionReplayEvidenceReviewerClosureRollup`, optional response-level `decisionReplayEvidenceReviewerClosureChecklist`, optional response-level `decisionReplayEvidenceReviewerClosurePacket`, and a `results` array. Each result exposes the strategy id, status, selected server id when one is available, the strategy reason, considered candidates, and score map when the strategy reports scores. A no-healthy-server comparison still returns a controlled result with `chosenServerId` set to `null`, empty candidate/scores collections, and an explanatory reason. Scenario replay responses expose `scenarioId`, `readOnly`, `cloudMutation`, `remediationPlan`, and ordered `steps`. Remediation recommendations are advisory only and must not introduce cloud mutation or execution semantics. @@ -89,3 +89,5 @@ Structured API errors expose `status`, `error`, `message`, `path`, `timestamp`, This repository does not currently include a generated-client build pipeline. Compatibility confidence comes from the OpenAPI path/schema assertions plus representative JSON shape checks. If a future client generator is introduced, keep it lightweight and run it as a contract validation lane without changing release automation or publishing generated artifacts. `POST /api/routing/compare` remains read-only and recommendation-only. Its top-level response can expose additive reviewer metadata `decisionReplayEvidenceReviewerClosureRollup` and `decisionReplayEvidenceReviewerClosureChecklist`, and its routing result objects can expose additive lab explainability fields: `decisionVector`, `dominantFactorAnalysis`, `decisionDeltaAnalysis`, `decisionReplaySnapshot`, `decisionReplayReconstructionTrace`, `decisionReplayCapsule`, `decisionReplayReadinessChecklist`, `decisionReplayEvidenceSourceMap`, `decisionReplayEvidenceBoundarySummary`, `decisionReplayEvidenceFieldInventory`, `decisionReplayEvidenceNullSafetySummary`, `decisionReplayEvidenceStatusRollup`, `decisionReplayEvidenceLaneNavigationSummary`, `decisionReplayEvidenceLaneDependencyMap`, `decisionReplayEvidenceLaneReferenceIndex`, `decisionReplayEvidenceLaneDependencySummary`, `decisionReplayEvidenceLaneConsistencySummary`, `decisionReplayEvidenceReviewerSnapshot`, `decisionReplayEvidenceReviewerGuidance`, `decisionReplayEvidenceReviewerHandoffSummary`, and `decisionReplayEvidenceReviewerClosureSummary`. The reviewer closure rollup and checklist are derived only from already-built `results[].decisionReplayEvidenceReviewerClosureSummary` data and `decisionReplayEvidenceReviewerClosureRollup` data; the replay snapshot, reconstruction trace, replay capsule, replay readiness checklist, evidence source map, evidence boundary summary, evidence field inventory, evidence null-safety summary, evidence status rollup, evidence lane navigation summary, evidence lane dependency map, evidence lane reference index, evidence lane dependency summary, evidence lane consistency summary, evidence reviewer snapshot, evidence reviewer guidance, evidence reviewer handoff summary, and evidence reviewer closure summary are derived from already-built compare evidence and stable analysis/status/navigation/dependency/reviewer metadata; they do not execute replay, perform what-if mutation, persist audit logs, traces, capsules, checklist state, source-map data, boundary-summary data, field-inventory data, null-safety-summary data, status-rollup data, lane-navigation data, lane-dependency data, lane-reference-index data, lane-dependency-summary data, lane-consistency-summary data, reviewer-snapshot data, reviewer-guidance data, reviewer-handoff data, reviewer-closure data, reviewer-closure-rollup data, or reviewer-closure-checklist data, generate a new source-map, boundary-summary, field-inventory, null-safety, status-rollup, lane-navigation, lane-dependency, lane-reference-index, lane-dependency-summary, lane-consistency-summary, reviewer-snapshot, reviewer-guidance, reviewer-handoff, reviewer-closure, reviewer-closure-rollup, or reviewer-closure-checklist fingerprint, recompute scores, retune weights, or add upload/share/download routes or server-side export/PDF/ZIP generation. + +The same top-level response can expose additive reviewer metadata `decisionReplayEvidenceReviewerClosurePacket`. It is derived only from `results[].decisionReplayEvidenceReviewerClosureSummary`, `decisionReplayEvidenceReviewerClosureRollup`, and `decisionReplayEvidenceReviewerClosureChecklist`; it is not an export/share/download packet, does not persist packet data, does not generate a new fingerprint, does not recompute scores, does not execute replay, and does not add server-side export/PDF/ZIP generation. diff --git a/docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md b/docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md index e4f21eb0..7466c632 100644 --- a/docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md +++ b/docs/ENTERPRISE_LAB_COCKPIT_FRAMING.md @@ -113,15 +113,27 @@ routing behavior changes, without proxy behavior changes, without persistence, w upload/share/download/export/PDF/ZIP behavior, without production validation, without correctness validation, without production readiness, without production certification, and without guaranteed replay claims. +Decision Replay Evidence Closure Packet metadata derives only from already-returned +`results[].decisionReplayEvidenceReviewerClosureSummary`, top-level +`decisionReplayEvidenceReviewerClosureRollup`, and top-level `decisionReplayEvidenceReviewerClosureChecklist`. The +read-only `/api/routing/compare` response can expose top-level +`decisionReplayEvidenceReviewerClosurePacket` as additive in-response reviewer metadata without reflection, without +new fingerprint generation, without replay execution, without what-if mutation, without scoring changes, without +routing behavior changes, without proxy behavior changes, without persistence, without telemetry, without +upload/share/download/export/PDF/ZIP behavior, without production validation, without correctness validation, without +production readiness, without production certification, and without guaranteed replay claims. It is not an +export/share/download packet. + The Decision Vector contract keeps unknowns explicit. Exact production scoring is not claimed unless exposed by the API, hidden scoring must not be inferred, ServerScoreCalculator factor contribution extraction is additive for current local calculator components, and candidate factor contribution summaries can attach those calculator explanations to selected and non-selected candidate vectors for controlled lab review. [`ENTERPRISE_LAB_DOMINANT_FACTOR_ANALYSIS.md`](ENTERPRISE_LAB_DOMINANT_FACTOR_ANALYSIS.md) documents the additive read-only lane that identifies largest support, penalty/risk, and absolute-impact contributors from those returned entries only. [`ENTERPRISE_LAB_DECISION_DELTA_ANALYSIS.md`](ENTERPRISE_LAB_DECISION_DELTA_ANALYSIS.md) documents the additive read-only lane that compares the selected candidate with the closest scored non-selected alternative using returned final score gaps and shared finite contribution deltas only. [`ENTERPRISE_LAB_DECISION_REPLAY_SNAPSHOT.md`](ENTERPRISE_LAB_DECISION_REPLAY_SNAPSHOT.md) documents the additive read-only lane that records stable snapshot metadata and a deterministic local fingerprint from already-built compare evidence only. [`ENTERPRISE_LAB_DECISION_REPLAY_RECONSTRUCTION_TRACE.md`](ENTERPRISE_LAB_DECISION_REPLAY_RECONSTRUCTION_TRACE.md) documents the additive read-only lane that records deterministic reconstruction evidence steps and a deterministic local trace fingerprint from already-built compare evidence only. [`ENTERPRISE_LAB_DECISION_REPLAY_CAPSULE.md`](ENTERPRISE_LAB_DECISION_REPLAY_CAPSULE.md) documents the additive read-only lane that records canonical evidence packaging and a deterministic local capsule fingerprint from already-built compare evidence only. [`ENTERPRISE_LAB_DECISION_REPLAY_READINESS_CHECKLIST.md`](ENTERPRISE_LAB_DECISION_REPLAY_READINESS_CHECKLIST.md) documents the additive read-only lane that records lab replay-readiness checklist status from already-built evidence lane statuses and linked fingerprints only. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_SOURCE_MAP.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_SOURCE_MAP.md) documents the additive read-only lane that maps already-built evidence source fields to replay/readiness artifacts without generating a new fingerprint. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_BOUNDARY_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_BOUNDARY_SUMMARY.md) documents the additive read-only lane that summarizes existing lab-only, read-only, and not-proven boundary fields without generating a fingerprint. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_FIELD_INVENTORY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_FIELD_INVENTORY.md) documents the additive read-only lane that inventories already-built evidence field groups without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_EVIDENCE_NULL_SAFETY_SUMMARY.md`](ENTERPRISE_LAB_DECISION_EVIDENCE_NULL_SAFETY_SUMMARY.md) documents the additive read-only lane that summarizes null, missing, unavailable, and no-healthy/failure-path safety without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_EVIDENCE_STATUS_ROLLUP.md`](ENTERPRISE_LAB_DECISION_EVIDENCE_STATUS_ROLLUP.md) documents the additive read-only lane that summarizes already-built evidence lane statuses, selected-candidate presence, candidate counts, and boundary state without reflection, scorecards, quality ranking, or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_NAVIGATION_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_NAVIGATION_SUMMARY.md) documents the additive read-only lane that maps already-built evidence lanes to response field paths, UI section labels, and docs reference labels without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_MAP.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_MAP.md) documents the additive read-only lane that maps already-built evidence lane dependencies and downstream relationships without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_REFERENCE_INDEX.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_REFERENCE_INDEX.md) documents the additive read-only lane that indexes already-built evidence lanes by response field path, UI section label, docs reference label, dependency count, and downstream count without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_DEPENDENCY_SUMMARY.md) documents the additive read-only lane that summarizes the existing lane reference index into dependency-shape counts without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_CONSISTENCY_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_LANE_CONSISTENCY_SUMMARY.md) documents the additive read-only lane that cross-checks existing status rollup, dependency map, reference index, and dependency summary surfaces without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_SNAPSHOT.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_SNAPSHOT.md) documents the additive read-only lane that summarizes existing status rollup, dependency map, reference index, dependency summary, and consistency summary surfaces into reviewer highlights and warnings without reflection or new fingerprint generation. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md) documents the additive read-only lane that derives deterministic reviewer guidance from existing reviewer metadata without reflection or new fingerprint generation. The read-only `/api/routing/compare` response can expose `results[].decisionVector`, `results[].dominantFactorAnalysis`, `results[].decisionDeltaAnalysis`, `results[].decisionReplaySnapshot`, `results[].decisionReplayReconstructionTrace`, `results[].decisionReplayCapsule`, `results[].decisionReplayReadinessChecklist`, `results[].decisionReplayEvidenceSourceMap`, `results[].decisionReplayEvidenceBoundarySummary`, `results[].decisionReplayEvidenceFieldInventory`, `results[].decisionReplayEvidenceNullSafetySummary`, `results[].decisionReplayEvidenceStatusRollup`, `results[].decisionReplayEvidenceLaneNavigationSummary`, `results[].decisionReplayEvidenceLaneDependencyMap`, `results[].decisionReplayEvidenceLaneReferenceIndex`, `results[].decisionReplayEvidenceLaneDependencySummary`, `results[].decisionReplayEvidenceLaneConsistencySummary`, `results[].decisionReplayEvidenceReviewerSnapshot`, and `results[].decisionReplayEvidenceReviewerGuidance` as additive controlled lab evidence while preserving existing fields, routing behavior, scoring behavior, and strategy weights. Replay execution and what-if execution remain future/not implemented, and structured decision logging remains future/not implemented. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md) -documents the additive read-only reviewer closure metadata lane and derives deterministic closure metadata from existing reviewer metadata. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md) documents the additive read-only response-level closure rollup over already-returned reviewer closure summaries. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md) documents the additive read-only response-level closure checklist over already-returned reviewer closure summaries and the closure rollup. It derives only from existing status rollup, +documents the additive read-only reviewer closure metadata lane and derives deterministic closure metadata from existing reviewer metadata. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md) documents the additive read-only response-level closure rollup over already-returned reviewer closure summaries. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md) documents the additive read-only response-level closure checklist over already-returned reviewer closure summaries and the closure rollup. [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md) documents the additive read-only in-response closure packet over already-returned reviewer closure summaries, the closure rollup, and the closure checklist. It derives only from existing status rollup, dependency map, lane reference index, dependency summary, consistency summary, reviewer snapshot, reviewer guidance, and reviewer handoff surfaces. It adds `results[].decisionReplayEvidenceReviewerClosureSummary` and top-level -`decisionReplayEvidenceReviewerClosureRollup` and `decisionReplayEvidenceReviewerClosureChecklist` without changing -routing behavior, recomputing scores, executing replay, performing what-if mutation, persisting closure data, exporting -closure data, generating a new fingerprint, or making production-validation, certification, guaranteed-replay, +`decisionReplayEvidenceReviewerClosureRollup`, `decisionReplayEvidenceReviewerClosureChecklist`, and +`decisionReplayEvidenceReviewerClosurePacket` without changing routing behavior, recomputing scores, executing replay, +performing what-if mutation, persisting closure data, exporting closure data, generating a new fingerprint, or making +production-validation, certification, guaranteed-replay, approval, enforcement, remediation, scorecard, quality-ranking, correctness-validation, or production-readiness claims. ## How to Investigate Surprising Lab Decisions diff --git a/docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md b/docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md new file mode 100644 index 00000000..c72eb47b --- /dev/null +++ b/docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md @@ -0,0 +1,82 @@ +# Enterprise Lab Decision Replay Evidence Closure Packet + +Decision Replay Evidence Closure Packet is an additive read-only response-level reviewer packet for +`POST /api/routing/compare`. It groups already-returned closure summary, closure rollup, and closure checklist +metadata into one in-response reviewer index so reviewers can inspect closure coverage, section status, reviewer +guidance, stripped replay posture, and explicit not-proven boundaries without treating the response as replay proof, +scoring proof, production proof, or an export/share/download artifact. + +The response field is `decisionReplayEvidenceReviewerClosurePacket`. + +## Source + +The closure packet is derived only from existing compare response data: + +- `results[].decisionReplayEvidenceReviewerClosureSummary`; +- `decisionReplayEvidenceReviewerClosureRollup`; +- `decisionReplayEvidenceReviewerClosureChecklist`. + +It does not inspect raw server input, raw request payload data, routing internals, scoring internals, strategy +internals, proxy state, environment variables, system properties, time, random values, or filesystem/process APIs. +It does not use reflection. + +## Packet Sections + +The closure packet includes: + +- packet status: `COMPLETE`, `PARTIAL`, or `UNKNOWN`; +- reviewer-ready boolean; +- packet version `v1`; +- `closureSummary` section with `PASS`, `WARN`, or `UNKNOWN` status; +- `closureRollup` section with `PASS`, `WARN`, or `UNKNOWN` status; +- `closureChecklist` section with `PASS`, `WARN`, or `UNKNOWN` status; +- `scenarioReplayBoundary` section with `PASS`, `WARN`, or `UNKNOWN` status; +- `notProvenBoundaries` section with `PASS`, `WARN`, or `UNKNOWN` status; +- deterministic summary text; +- reviewer guidance strings; +- explicit not-proven boundaries: + - `not replay proof`; + - `not scoring proof`; + - `not correctness validation`; + - `not production readiness`; + - `not production certification`; + - `not guaranteed replay`; + - `not production validation`. + +## Boundaries + +The closure packet: + +- is in-response reviewer metadata only; +- is not an export, share, download, PDF, ZIP, or file-generation packet; +- does not execute replay; +- does not perform what-if mutation; +- does not persist reviewer closure packet data or audit logs server-side; +- does not export, download, upload, or share reviewer closure packet data; +- does not change routing behavior; +- does not change strategy selection; +- does not recompute scores; +- does not retune weights; +- does not infer hidden scoring; +- does not generate a new fingerprint, hash, SHA, or UUID; +- does not use reflection; +- does not add telemetry or storage; +- does not add external calls, scripts, or CDNs; +- does not add server-side export/PDF/ZIP/file generation. + +It is not replay proof, not scoring proof, not correctness validation, not production readiness, not production +certification, not guaranteed replay, and not production validation. + +It is not an approval, remediation, enforcement decision, readiness score, scorecard, quality ranking, correctness +validation, production decision, release gate, registry publication proof, signing proof, governance proof, or +downloadable evidence bundle. + +## No-Healthy And Stripped Replay Paths + +When the normal compare response has no selected candidate, the packet remains safe metadata over the already-returned +closure summary, rollup, and checklist data. It can report `UNKNOWN` with `reviewerReady=false` without inventing +selected candidate evidence, replay execution, score proof, export behavior, or production validation. + +Scenario replay keeps its stripped routing-result posture: reviewer closure metadata fields remain null in embedded +replay results, and this response-level packet is not promoted into scenario replay output as replay proof, +downloadable proof, scoring proof, or production validation. diff --git a/docs/ENTERPRISE_LAB_DECISION_VECTOR.md b/docs/ENTERPRISE_LAB_DECISION_VECTOR.md index 6b235243..0bc22e76 100644 --- a/docs/ENTERPRISE_LAB_DECISION_VECTOR.md +++ b/docs/ENTERPRISE_LAB_DECISION_VECTOR.md @@ -6,7 +6,7 @@ The Enterprise Lab Decision Vector is the structured explanation object for one ## Why the Lab Needs It -The cockpit already explains visible outcomes: selected strategy, selected backend/server, candidate signals, known versus unknown signals, and selected-vs-alternative notes. A Decision Vector gives those explanations a contract so the current read-only dominant-factor lane, selected-vs-closest-alternative decision delta lane, Decision Replay Snapshot lane, Decision Replay Reconstruction Trace lane, Decision Replay Capsule lane, Decision Replay Readiness Checklist lane, Decision Replay Evidence Source Map lane, Decision Replay Evidence Boundary Summary lane, Decision Replay Evidence Field Inventory lane, Decision Evidence Null-Safety Summary lane, Decision Evidence Status Rollup lane, Decision Replay Evidence Lane Navigation Summary lane, Decision Replay Evidence Lane Dependency Map lane, Decision Replay Evidence Lane Reference Index lane, Decision Replay Evidence Lane Dependency Summary lane, Decision Replay Evidence Lane Consistency Summary lane, Decision Replay Evidence Reviewer Snapshot lane, Decision Replay Evidence Reviewer Guidance lane, Decision Replay Evidence Reviewer Handoff Summary lane, Decision Replay Evidence Reviewer Closure Summary lane, response-level Decision Replay Evidence Closure Rollup lane, response-level Decision Replay Evidence Closure Checklist lane, and later separately scoped lab planning work can build without inventing hidden scoring. +The cockpit already explains visible outcomes: selected strategy, selected backend/server, candidate signals, known versus unknown signals, and selected-vs-alternative notes. A Decision Vector gives those explanations a contract so the current read-only dominant-factor lane, selected-vs-closest-alternative decision delta lane, Decision Replay Snapshot lane, Decision Replay Reconstruction Trace lane, Decision Replay Capsule lane, Decision Replay Readiness Checklist lane, Decision Replay Evidence Source Map lane, Decision Replay Evidence Boundary Summary lane, Decision Replay Evidence Field Inventory lane, Decision Evidence Null-Safety Summary lane, Decision Evidence Status Rollup lane, Decision Replay Evidence Lane Navigation Summary lane, Decision Replay Evidence Lane Dependency Map lane, Decision Replay Evidence Lane Reference Index lane, Decision Replay Evidence Lane Dependency Summary lane, Decision Replay Evidence Lane Consistency Summary lane, Decision Replay Evidence Reviewer Snapshot lane, Decision Replay Evidence Reviewer Guidance lane, Decision Replay Evidence Reviewer Handoff Summary lane, Decision Replay Evidence Reviewer Closure Summary lane, response-level Decision Replay Evidence Closure Rollup lane, response-level Decision Replay Evidence Closure Checklist lane, response-level Decision Replay Evidence Closure Packet lane, and later separately scoped lab planning work can build without inventing hidden scoring. A Decision Vector differs from a simple reason string because it separates: @@ -41,6 +41,7 @@ A Decision Vector differs from a simple reason string because it separates: - Decision Replay Evidence Reviewer Closure Summary metadata over the existing status rollup, dependency map, lane reference index, lane dependency summary, lane consistency summary, reviewer snapshot, reviewer guidance, and reviewer handoff surfaces. - Decision Replay Evidence Closure Rollup metadata over already-returned per-result reviewer closure summary fields. - Decision Replay Evidence Closure Checklist metadata over already-returned per-result reviewer closure summary fields and the response-level closure rollup. +- Decision Replay Evidence Closure Packet metadata over already-returned per-result reviewer closure summary fields, the response-level closure rollup, and the response-level closure checklist. - Replay readiness and later separately scoped replay planning gaps. - Lab proof boundaries and production not-proven boundaries. @@ -86,6 +87,7 @@ One Decision Vector represents one controlled lab routing decision. The contract | `decisionReplayEvidenceReviewerClosureSummary` | Additive read-only reviewer closure summary derived only from existing status rollup, dependency map, lane reference index, dependency summary, consistency summary, reviewer snapshot, reviewer guidance, and reviewer handoff metadata; it shows closure status, disposition, bullets, safe conclusions, unresolved boundaries, referenced surfaces, and limitations without replay execution, score recomputation, reflection, production validation, or a new fingerprint. | | `decisionReplayEvidenceReviewerClosureRollup` | Additive read-only top-level reviewer closure rollup derived only from already-returned per-result reviewer closure summaries; it shows result counts, closure summary coverage, complete-with-limitations count, unknown/not-available count, reviewer-ready state, deterministic summary text, and explicit not-proven boundaries without replay execution, score recomputation, reflection, production validation, persistence, telemetry, upload/share/download/export/PDF/ZIP behavior, or a new fingerprint. | | `decisionReplayEvidenceReviewerClosureChecklist` | Additive read-only top-level reviewer closure checklist derived only from already-returned per-result reviewer closure summaries and the response-level closure rollup; it shows checklist status, reviewer-ready state, closure-summary presence, rollup presence, count alignment, scenario replay stripped posture, explicit not-proven boundaries, and deterministic summary text without replay execution, score recomputation, reflection, production validation, persistence, telemetry, upload/share/download/export/PDF/ZIP behavior, or a new fingerprint. | +| `decisionReplayEvidenceReviewerClosurePacket` | Additive read-only top-level reviewer closure packet derived only from already-returned per-result reviewer closure summaries, the response-level closure rollup, and the response-level closure checklist; it shows packet status, reviewer-ready state, packet version, section statuses, reviewer guidance, deterministic summary text, and explicit not-proven boundaries without replay execution, score recomputation, reflection, production validation, persistence, telemetry, upload/share/download/export/PDF/ZIP behavior, or a new fingerprint. It is not an export/share/download packet. | | `replayReadiness` | Contract readiness for future replay; replay execution remains future/not implemented until built. | | `labProofBoundary` | Controlled lab evidence, local reproducibility, same-origin local API responses, and browser-local interpretation. | | `productionNotProvenBoundary` | No production traffic proof, production telemetry proof, production monitoring proof, production certification, live-cloud proof, real-tenant proof, SLA/SLO proof, registry publication, container signing, governance application, or exact production scoring proof. | @@ -688,6 +690,16 @@ summary text, and explicit not-proven boundaries. See [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md) for the focused reviewer contract and safety boundaries. +Decision Replay Evidence Closure Packet metadata is the additive read-only response-level reviewer packet layer derived +only from already-returned per-result reviewer closure summaries, the top-level closure rollup, and the top-level +closure checklist. The top-level `decisionReplayEvidenceReviewerClosurePacket` field summarizes packet status, +reviewer-ready state, packet version, closure-summary, closure-rollup, closure-checklist, scenario replay boundary, +and not-proven-boundary section statuses, reviewer guidance, deterministic summary text, and explicit not-proven +boundaries. See +[`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md) +for the focused reviewer contract and safety boundaries. It is in-response reviewer metadata only, not an +export/share/download packet. + The read-only `/api/routing/compare` response can expose candidate contribution summaries through `results[].decisionVector` without changing scoring behavior, strategy weights, selected backend outcomes, or existing response fields. This does not implement decision replay, what-if execution, strategy plugin @@ -755,6 +767,7 @@ The read-only field includes: - Result-level `decisionReplayEvidenceReviewerHandoffSummary` derived from already-built status rollup, dependency map, lane reference index, dependency summary, consistency summary, reviewer snapshot, and reviewer guidance metadata. - Result-level `decisionReplayEvidenceReviewerClosureSummary` derived from already-built status rollup, dependency map, lane reference index, dependency summary, consistency summary, reviewer snapshot, reviewer guidance, and reviewer handoff metadata. - Response-level `decisionReplayEvidenceReviewerClosureChecklist` derived from already-returned closure summaries and the response-level closure rollup. +- Response-level `decisionReplayEvidenceReviewerClosurePacket` derived from already-returned closure summaries, the response-level closure rollup, and the response-level closure checklist. - Exactness, lab proof, and production not-proven boundaries. - Replay, what-if, and structured logging readiness marked future/not implemented. diff --git a/docs/REVIEWER_TRUST_MAP.md b/docs/REVIEWER_TRUST_MAP.md index a0912520..fdf15ecb 100644 --- a/docs/REVIEWER_TRUST_MAP.md +++ b/docs/REVIEWER_TRUST_MAP.md @@ -250,6 +250,17 @@ audit logs, using reflection, recomputing scores, executing replay, performing w fingerprint, claiming replay proof, claiming scoring proof, claiming correctness validation, claiming production validation, not production certification, not guaranteed replay, and not production readiness. +Decision Replay Evidence Closure Packet metadata is documented in +[`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md). +The read-only `/api/routing/compare` response can expose top-level +`decisionReplayEvidenceReviewerClosurePacket` as deterministic in-response reviewer metadata over already-returned +per-result closure summaries, the closure rollup, and the closure checklist. It reports packet status, reviewer-ready +state, packet version, section statuses, reviewer guidance, deterministic summary text, and explicit not-proven +boundaries without persisting reviewer-closure-packet data or audit logs, using reflection, recomputing scores, +executing replay, performing what-if mutation, generating a new fingerprint, adding export/share/download behavior, +claiming replay proof, claiming scoring proof, claiming correctness validation, claiming production validation, not +production certification, not guaranteed replay, and not production readiness. + Use [`ENTERPRISE_LAB_DECISION_REPLAY_WHAT_IF_PLAN.md`](ENTERPRISE_LAB_DECISION_REPLAY_WHAT_IF_PLAN.md) when reviewers ask how the lab will eventually replay a captured routing decision or test one visible signal mutation. The plan defines the future controlled-lab replay input model, one-mutation what-if model, fixture-only contract seed, safe what-if questions, exactness boundaries, safety boundaries, and phased implementation path. The current contract fixture lane adds static JSON examples under `src/test/resources/enterprise-lab/decision-replay/` and `EnterpriseLabDecisionReplayContractFixtureTest` only. It is fixture-only and deterministic; it does not call scoring, choose a backend, wire a Spring service, or expose runtime behavior. @@ -268,7 +279,7 @@ What can be proven quickly: 2. Operator evidence dashboard: open `http://localhost:8080/operator-evidence-dashboard.html` for local/CI evidence locations, smoke output paths, dry-run artifact metadata, and the local-only `GET /api/enterprise-lab/operator-evidence-summary` response. 3. Evidence timeline: open `http://localhost:8080/evidence-timeline.html` for local/CI evidence stages, generated evidence locations, the dry-run artifact name, and a reusable run template. 4. Evidence export packet: open `http://localhost:8080/evidence-export-packet.html` for the reviewer handoff checklist, packet template, reviewer packet share checklist, browser-local Markdown/JSON packet downloads, browser print/save-as-PDF styling, dashboard links, CI artifact name, not-proven boundaries, and the local-only `GET /api/enterprise-lab/evidence-export-packet` response. The downloads, print flow, and share checklist are generated in the browser and do not send, upload, or create server-side files, PDFs, or share artifacts. -5. Local routing/cockpit behavior: open `http://localhost:8080/routing-demo.html` for the Enterprise Lab Cockpit Monitor, Decision Chain Trace, Why This Backend Was Selected, Why Other Candidates Were Not Selected, Alternative Candidate Evidence, Decision Vector Foundation, Decision Replay Evidence Field Inventory, Decision Evidence Null-Safety Summary, Decision Evidence Status Rollup, Decision Replay Evidence Lane Navigation Summary, Decision Replay Evidence Lane Dependency Map, Decision Replay Evidence Lane Reference Index, Decision Replay Evidence Lane Dependency Summary, Decision Replay Evidence Lane Consistency Summary, Decision Replay Evidence Reviewer Snapshot, Decision Replay Evidence Reviewer Guidance, Decision Replay Evidence Reviewer Handoff Summary, Decision Replay Evidence Reviewer Closure Summary, Decision Replay Evidence Closure Rollup, Candidate Comparison Limits, Candidate Evidence Cards, Candidate Review Questions, Known vs Unknown Signals, Investigation Playbook, How This Routing Decision Was Made, What This Lab Needs to Monitor, How to Reproduce and Explain This Lab Proof, How to Read Lab Edge Cases, Reviewer Workflow Checklist, Evidence Associations, Association Legend, Routing Proof Summary, Scenario Comparison, Reviewer Confidence Signals, Evidence Navigation reviewer path, selected scenario-to-decision mapping, selected strategy/backend, selected-vs-non-selected candidate comparison, structured decision evidence, known versus unknown signal handling, scenario-to-scenario what-changed notes, visible input signal deltas, known versus unknown signals, alternative-candidate limits, null/missing/unavailable evidence handling, evidence lane status rollup handling, lane navigation handling, lane dependency handling, lane reference index handling, lane dependency summary handling, lane consistency summary handling, reviewer snapshot handling, reviewer guidance handling, reviewer handoff handling, reviewer closure handling, reviewer closure rollup handling, degradation/recovery notes, local verification commands, routing-to-evidence dashboard links, copyable decision trace explanation, copyable alternative candidate evidence, copyable structured Decision Vector summary, copyable lab monitor explanation, copyable evidence association summary, copyable end-to-end reviewer walkthrough, and explicit not-proven boundaries; then open `http://localhost:8080/` and `http://localhost:8080/load-balancing-cockpit.html`. +5. Local routing/cockpit behavior: open `http://localhost:8080/routing-demo.html` for the Enterprise Lab Cockpit Monitor, Decision Chain Trace, Why This Backend Was Selected, Why Other Candidates Were Not Selected, Alternative Candidate Evidence, Decision Vector Foundation, Decision Replay Evidence Field Inventory, Decision Evidence Null-Safety Summary, Decision Evidence Status Rollup, Decision Replay Evidence Lane Navigation Summary, Decision Replay Evidence Lane Dependency Map, Decision Replay Evidence Lane Reference Index, Decision Replay Evidence Lane Dependency Summary, Decision Replay Evidence Lane Consistency Summary, Decision Replay Evidence Reviewer Snapshot, Decision Replay Evidence Reviewer Guidance, Decision Replay Evidence Reviewer Handoff Summary, Decision Replay Evidence Reviewer Closure Summary, Decision Replay Evidence Closure Rollup, Decision Replay Evidence Closure Checklist, Decision Replay Evidence Closure Packet, Candidate Comparison Limits, Candidate Evidence Cards, Candidate Review Questions, Known vs Unknown Signals, Investigation Playbook, How This Routing Decision Was Made, What This Lab Needs to Monitor, How to Reproduce and Explain This Lab Proof, How to Read Lab Edge Cases, Reviewer Workflow Checklist, Evidence Associations, Association Legend, Routing Proof Summary, Scenario Comparison, Reviewer Confidence Signals, Evidence Navigation reviewer path, selected scenario-to-decision mapping, selected strategy/backend, selected-vs-non-selected candidate comparison, structured decision evidence, known versus unknown signal handling, scenario-to-scenario what-changed notes, visible input signal deltas, known versus unknown signals, alternative-candidate limits, null/missing/unavailable evidence handling, evidence lane status rollup handling, lane navigation handling, lane dependency handling, lane reference index handling, lane dependency summary handling, lane consistency summary handling, reviewer snapshot handling, reviewer guidance handling, reviewer handoff handling, reviewer closure handling, reviewer closure rollup handling, reviewer closure checklist handling, reviewer closure packet handling, degradation/recovery notes, local verification commands, routing-to-evidence dashboard links, copyable decision trace explanation, copyable alternative candidate evidence, copyable structured Decision Vector summary, copyable lab monitor explanation, copyable evidence association summary, copyable end-to-end reviewer walkthrough, and explicit not-proven boundaries; then open `http://localhost:8080/` and `http://localhost:8080/load-balancing-cockpit.html`. 6. Local proxy forwarding evidence: run `mvn -Dtest=LocalProxyEvidenceExportTest test`, then inspect `target/proxy-evidence/local-proxy-evidence.md` and `target/proxy-evidence/local-proxy-evidence.json`. 7. Private-network profile dry-run evidence: run `mvn -Dtest=PrivateNetworkProxyDryRunEvidenceTest test`, then inspect `target/proxy-evidence/private-network-validation-dry-run.md` and `target/proxy-evidence/private-network-validation-dry-run.json`. 8. Private-network live loopback proof: run `mvn -Dtest=PrivateNetworkLiveValidationExecutorTest test`, then inspect `target/proxy-evidence/private-network-live-loopback-validation.md` and `target/proxy-evidence/private-network-live-loopback-validation.json`. @@ -323,6 +334,7 @@ Safety boundaries preserved by this path: | What closure summary can a reviewer keep after inspecting the evidence? | Decision Replay Evidence Reviewer Closure Summary | [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md), `/routing-demo.html`, `/load-balancing-cockpit.html`, `RoutingDecisionReplayEvidenceReviewerClosureSummaryServiceTest` | Read-only `results[].decisionReplayEvidenceReviewerClosureSummary`, Decision Replay Evidence Reviewer Closure Summary metadata, closure status, closure disposition, reviewer snapshot status, reviewer guidance status, reviewer handoff status, consistency status, lane counts, closure bullets, safe conclusions, unresolved boundaries, evidence surfaces referenced, deterministic summary text, and short limitations | Reviewers can keep compact deterministic closure notes derived from already-exposed reviewer metadata without reflection, raw input inspection, new fingerprinting, scoring, replay execution, replay proof, scoring proof, approval, remediation, enforcement, production-readiness, production-certification, production-validation, scorecard, quality-ranking, correctness-validation, or guaranteed replay claims | Exact production scoring, replay execution, what-if mutation, production certification, guaranteed replay, production validation, approval, remediation, enforcement, production-readiness claims, scorecard claims, quality ranking, correctness validation, upload/share/download route behavior, or server-side export/PDF/ZIP generation | | Does every compare result include reviewer closure metadata? | Decision Replay Evidence Closure Rollup | [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md), `/routing-demo.html`, `/load-balancing-cockpit.html`, `RoutingDecisionReplayEvidenceReviewerClosureRollupServiceTest` | Read-only top-level `decisionReplayEvidenceReviewerClosureRollup`, result count, results with closure summaries, results missing closure summaries, complete-with-limitations count, unknown/not-available count, reviewer-ready state, deterministic summary text, and explicit not-proven boundaries | Reviewers can see closure coverage across the compare response from already-returned per-result reviewer closure metadata without reflection, raw input inspection, new fingerprinting, scoring, replay execution, replay proof, scoring proof, approval, remediation, enforcement, production-readiness, production-certification, production-validation, scorecard, quality-ranking, correctness-validation, or guaranteed replay claims | Exact production scoring, replay execution, what-if mutation, production certification, guaranteed replay, production validation, approval, remediation, enforcement, production-readiness claims, scorecard claims, quality ranking, correctness validation, upload/share/download route behavior, or server-side export/PDF/ZIP generation | | Do closure summary, closure rollup, and not-proven boundaries line up for reviewers? | Decision Replay Evidence Closure Checklist | [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md), `/routing-demo.html`, `/load-balancing-cockpit.html`, `RoutingDecisionReplayEvidenceReviewerClosureChecklistServiceTest` | Read-only top-level `decisionReplayEvidenceReviewerClosureChecklist`, checklist status, reviewer-ready state, closure-summary presence, closure-rollup presence, count alignment, scenario replay stripped posture, not-proven boundary presence, deterministic summary text, and explicit not-proven boundaries | Reviewers can check closure metadata coverage and boundary posture from already-returned closure summaries and closure rollup metadata without reflection, raw input inspection, new fingerprinting, scoring, replay execution, replay proof, scoring proof, production-readiness, production-certification, production-validation, correctness-validation, or guaranteed replay claims | Exact production scoring, replay execution, what-if mutation, production certification, guaranteed replay, production validation, production-readiness claims, correctness validation, upload/share/download route behavior, or server-side export/PDF/ZIP generation | +| What compact packet groups closure summary, rollup, and checklist metadata? | Decision Replay Evidence Closure Packet | [`ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md), `/routing-demo.html`, `/load-balancing-cockpit.html`, `RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest` | Read-only top-level `decisionReplayEvidenceReviewerClosurePacket`, packet status, reviewer-ready state, packet version, closure-summary section, closure-rollup section, closure-checklist section, scenario replay boundary section, not-proven boundary section, reviewer guidance, deterministic summary text, and explicit not-proven boundaries | Reviewers can inspect a compact in-response metadata packet derived from already-returned closure summaries, closure rollup, and closure checklist without reflection, raw input inspection, new fingerprinting, scoring, replay execution, replay proof, scoring proof, production-readiness, production-certification, production-validation, correctness-validation, export/share/download behavior, or guaranteed replay claims | Exact production scoring, replay execution, what-if mutation, production certification, guaranteed replay, production validation, production-readiness claims, correctness validation, upload/share/download route behavior, downloadable packet claims, or server-side export/PDF/ZIP generation | | How will replay and one-signal what-if analysis be planned safely? | Decision Replay / What-If planning lane | [`ENTERPRISE_LAB_DECISION_REPLAY_WHAT_IF_PLAN.md`](ENTERPRISE_LAB_DECISION_REPLAY_WHAT_IF_PLAN.md), `EnterpriseLabDecisionReplayWhatIfPlanDocumentationTest`, `EnterpriseLabDecisionReplayContractFixtureTest`, `src/test/resources/enterprise-lab/decision-replay/` | Planned replay input model, planned one-mutation what-if model, fixture-only decision snapshot/request/result examples, safe what-if questions for latency/error/load/health, exactness boundaries, safety boundaries, and future phases | Reviewers can see how Decision Vectors will support future controlled-lab replay and what-if analysis without inventing hidden scoring, wiring runtime behavior, or implying completion | Completed replay, completed what-if execution, live replay endpoint, production traffic replay, real backend mutation, external telemetry/storage, production certification, live cloud proof, real tenant proof, SLA/SLO proof, registry publication, signing, or server-side export/PDF/ZIP generation | | How do routing decisions connect to reviewer evidence pages? | Static browser routing cockpit association panels | `/routing-demo.html`, `RoutingDecisionDemoTest`, `src/main/resources/static/routing-demo.html` | Evidence Associations, Association Legend, copyable evidence association summary, selected scenario-to-decision mapping, selected strategy/backend mapping, key input signal mapping, scenario comparison delta, evidence page links, export packet handoff link, and local lab-only not-proven boundaries | Reviewers can trace how a controlled lab scenario, routing decision, selected backend, visible signals, scenario delta, evidence pages, timeline, and handoff packet relate without adding server-side exports or external calls | Production traffic proof, production telemetry proof, live cloud proof, real tenant proof, production certification, registry publication, container signing, upload/share endpoint behavior, or server-side export/PDF/ZIP generation | | How should a reviewer walk the routing cockpit end-to-end? | Static browser routing cockpit workflow panels | `/routing-demo.html`, `RoutingDecisionDemoTest`, `src/main/resources/static/routing-demo.html` | Reviewer Workflow Checklist, Reviewer Confidence Signals, copyable end-to-end reviewer walkthrough, Evidence Navigation links, and local lab-only not-proven boundaries | Reviewers can follow a repeatable local lab sequence from controlled lab scenario to routing comparison, proof summary, scenario deltas, evidence dashboards, timeline, and evidence export packet without leaving static/browser-local surfaces | Production deployment certification, live cloud validation, real tenant traffic proof, production SLA/SLO evidence, upload/share endpoint behavior, server-side export/PDF/ZIP generation, registry publication, or container signing | diff --git a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonResponse.java b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonResponse.java index 9208203b..6ddfc096 100644 --- a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonResponse.java +++ b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonResponse.java @@ -9,5 +9,6 @@ public record RoutingComparisonResponse( Instant timestamp, RoutingDecisionReplayEvidenceReviewerClosureRollupResponse decisionReplayEvidenceReviewerClosureRollup, RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse decisionReplayEvidenceReviewerClosureChecklist, + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse decisionReplayEvidenceReviewerClosurePacket, List results) { } diff --git a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonService.java b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonService.java index 405ee30a..8894ac44 100644 --- a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonService.java +++ b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingComparisonService.java @@ -70,6 +70,8 @@ public class RoutingComparisonService { decisionReplayEvidenceReviewerClosureRollupService; private final RoutingDecisionReplayEvidenceReviewerClosureChecklistService decisionReplayEvidenceReviewerClosureChecklistService; + private final RoutingDecisionReplayEvidenceReviewerClosurePacketService + decisionReplayEvidenceReviewerClosurePacketService; private final Clock clock; public RoutingComparisonService() { @@ -118,6 +120,8 @@ private RoutingComparisonService(RoutingStrategyRegistry registry, Clock clock) new RoutingDecisionReplayEvidenceReviewerClosureRollupService(); this.decisionReplayEvidenceReviewerClosureChecklistService = new RoutingDecisionReplayEvidenceReviewerClosureChecklistService(); + this.decisionReplayEvidenceReviewerClosurePacketService = + new RoutingDecisionReplayEvidenceReviewerClosurePacketService(); } public RoutingComparisonResponse compare(RoutingComparisonRequest request) { @@ -221,12 +225,15 @@ private RoutingComparisonResponse toResponse(RoutingComparisonReport report, Lis report.results().stream().map(result -> toResultResponse(result, candidates)).toList(); RoutingDecisionReplayEvidenceReviewerClosureRollupResponse closureRollup = decisionReplayEvidenceReviewerClosureRollupService.rollup(results); + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist = + decisionReplayEvidenceReviewerClosureChecklistService.checklist(results, closureRollup); return new RoutingComparisonResponse( report.requestedStrategies().stream().map(RoutingStrategyId::externalName).toList(), report.candidateCount(), report.timestamp(), closureRollup, - decisionReplayEvidenceReviewerClosureChecklistService.checklist(results, closureRollup), + closureChecklist, + decisionReplayEvidenceReviewerClosurePacketService.packet(results, closureRollup, closureChecklist), results); } diff --git a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketResponse.java b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketResponse.java new file mode 100644 index 00000000..383774f4 --- /dev/null +++ b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketResponse.java @@ -0,0 +1,18 @@ +package com.richmond423.loadbalancerpro.api; + +import java.util.List; + +public record RoutingDecisionReplayEvidenceReviewerClosurePacketResponse( + String status, + boolean reviewerReady, + String packetVersion, + List sections, + String summary, + List reviewerGuidance, + List notProvenBoundaries) { + public RoutingDecisionReplayEvidenceReviewerClosurePacketResponse { + sections = sections == null ? List.of() : List.copyOf(sections); + reviewerGuidance = reviewerGuidance == null ? List.of() : List.copyOf(reviewerGuidance); + notProvenBoundaries = notProvenBoundaries == null ? List.of() : List.copyOf(notProvenBoundaries); + } +} diff --git a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse.java b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse.java new file mode 100644 index 00000000..ccb927d4 --- /dev/null +++ b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse.java @@ -0,0 +1,7 @@ +package com.richmond423.loadbalancerpro.api; + +public record RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse( + String name, + String status, + String description) { +} diff --git a/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketService.java b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketService.java new file mode 100644 index 00000000..7e1f2297 --- /dev/null +++ b/src/main/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketService.java @@ -0,0 +1,226 @@ +package com.richmond423.loadbalancerpro.api; + +import java.util.List; + +public final class RoutingDecisionReplayEvidenceReviewerClosurePacketService { + private static final String STATUS_COMPLETE = "COMPLETE"; + private static final String STATUS_PARTIAL = "PARTIAL"; + private static final String STATUS_UNKNOWN = "UNKNOWN"; + private static final String SECTION_PASS = "PASS"; + private static final String SECTION_WARN = "WARN"; + private static final String SECTION_UNKNOWN = "UNKNOWN"; + private static final String PACKET_VERSION = "v1"; + private static final List NOT_PROVEN_BOUNDARIES = List.of( + "not replay proof", + "not scoring proof", + "not correctness validation", + "not production readiness", + "not production certification", + "not guaranteed replay", + "not production validation"); + + public RoutingDecisionReplayEvidenceReviewerClosurePacketResponse packet( + List results, + RoutingDecisionReplayEvidenceReviewerClosureRollupResponse closureRollup, + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist) { + List safeResults = results == null ? List.of() : results; + SectionCounts sectionCounts = sectionCounts(safeResults); + List sections = List.of( + closureSummarySection(sectionCounts), + closureRollupSection(closureRollup), + closureChecklistSection(closureChecklist), + scenarioReplayBoundarySection(closureChecklist), + notProvenBoundariesSection(closureRollup, closureChecklist)); + boolean reviewerReady = closureRollup != null + && closureChecklist != null + && closureRollup.reviewerReady() + && closureChecklist.reviewerReady() + && allSectionsPass(sections); + String status = status(closureRollup, closureChecklist, reviewerReady, sections); + return new RoutingDecisionReplayEvidenceReviewerClosurePacketResponse( + status, + reviewerReady, + PACKET_VERSION, + sections, + summary(status, reviewerReady, sections), + reviewerGuidance(), + NOT_PROVEN_BOUNDARIES); + } + + private static SectionCounts sectionCounts(List results) { + int resultCount = results.size(); + int withClosureSummary = 0; + for (RoutingComparisonResultResponse result : results) { + if (result != null && result.decisionReplayEvidenceReviewerClosureSummary() != null) { + withClosureSummary++; + } + } + return new SectionCounts(resultCount, withClosureSummary, resultCount - withClosureSummary); + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse closureSummarySection( + SectionCounts counts) { + String status; + if (counts.resultCount() == 0) { + status = SECTION_UNKNOWN; + } else { + status = counts.missingClosureSummary() == 0 ? SECTION_PASS : SECTION_WARN; + } + return section( + "closureSummary", + status, + status.equals(SECTION_PASS) + ? "Per-result reviewer closure summary metadata is present for every compare result." + : "One or more compare results are missing reviewer closure summary metadata."); + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse closureRollupSection( + RoutingDecisionReplayEvidenceReviewerClosureRollupResponse closureRollup) { + String status = statusFromLane(closureRollup == null ? null : closureRollup.status()); + return section( + "closureRollup", + status, + closureRollup == null + ? "Top-level reviewer closure rollup metadata is unavailable." + : "Top-level reviewer closure rollup metadata is present with status " + + closureRollup.status() + "."); + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse closureChecklistSection( + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist) { + String status = statusFromLane(closureChecklist == null ? null : closureChecklist.status()); + return section( + "closureChecklist", + status, + closureChecklist == null + ? "Top-level reviewer closure checklist metadata is unavailable." + : "Top-level reviewer closure checklist metadata is present with status " + + closureChecklist.status() + "."); + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse scenarioReplayBoundarySection( + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist) { + String status = checklistItemStatus(closureChecklist, "scenarioReplayStripped"); + return section( + "scenarioReplayBoundary", + status, + "Scenario replay keeps reviewer closure metadata stripped from embedded routing results; this " + + "packet is compare response metadata only and is not replay proof."); + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse notProvenBoundariesSection( + RoutingDecisionReplayEvidenceReviewerClosureRollupResponse closureRollup, + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist) { + boolean boundariesPresent = closureRollup != null + && closureChecklist != null + && NOT_PROVEN_BOUNDARIES.equals(closureRollup.notProvenBoundaries()) + && NOT_PROVEN_BOUNDARIES.equals(closureChecklist.notProvenBoundaries()); + String status = boundariesPresent ? SECTION_PASS : (closureRollup == null || closureChecklist == null + ? SECTION_UNKNOWN + : SECTION_WARN); + return section( + "notProvenBoundaries", + status, + boundariesPresent + ? "Explicit not-proven boundaries remain present across the closure rollup and checklist." + : "One or more explicit not-proven boundaries are unavailable in the source metadata."); + } + + private static String checklistItemStatus( + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist, + String itemName) { + if (closureChecklist == null || closureChecklist.items() == null) { + return SECTION_UNKNOWN; + } + return closureChecklist.items().stream() + .filter(item -> itemName.equals(item.name())) + .findFirst() + .map(RoutingDecisionReplayEvidenceReviewerClosureChecklistItemResponse::status) + .map(RoutingDecisionReplayEvidenceReviewerClosurePacketService::sectionStatusFromChecklistItem) + .orElse(SECTION_UNKNOWN); + } + + private static String sectionStatusFromChecklistItem(String status) { + if (SECTION_PASS.equals(status) || SECTION_WARN.equals(status)) { + return status; + } + return SECTION_UNKNOWN; + } + + private static String statusFromLane(String laneStatus) { + if (STATUS_COMPLETE.equals(laneStatus)) { + return SECTION_PASS; + } + if (STATUS_PARTIAL.equals(laneStatus)) { + return SECTION_WARN; + } + return SECTION_UNKNOWN; + } + + private static boolean allSectionsPass( + List sections) { + return sections.stream().allMatch(section -> SECTION_PASS.equals(section.status())); + } + + private static String status( + RoutingDecisionReplayEvidenceReviewerClosureRollupResponse closureRollup, + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse closureChecklist, + boolean reviewerReady, + List sections) { + if (closureRollup == null + || closureChecklist == null + || STATUS_UNKNOWN.equals(closureRollup.status()) + || STATUS_UNKNOWN.equals(closureChecklist.status())) { + return STATUS_UNKNOWN; + } + if (reviewerReady && allSectionsPass(sections)) { + return STATUS_COMPLETE; + } + return STATUS_PARTIAL; + } + + private static RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse section( + String name, + String status, + String description) { + return new RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse(name, status, description); + } + + private static String summary( + String status, + boolean reviewerReady, + List sections) { + return "Decision replay evidence reviewer closure packet is " + status + ": " + + sectionSummary(sections) + + "; reviewerReady=" + reviewerReady + + ". This packet is in-response reviewer metadata only, not an export/share/download packet, " + + "and is not replay proof, not scoring proof, not correctness validation, " + + "not production readiness, not production certification, not guaranteed replay, " + + "and not production validation."; + } + + private static List reviewerGuidance() { + return List.of( + "Use this packet as an in-response reviewer index over closure summary, rollup, and checklist " + + "metadata.", + "Inspect per-result closure summaries and the top-level rollup/checklist before treating the " + + "response as reviewer-ready.", + "Treat the not-proven boundaries as explicit limits: this packet is not replay proof, not scoring " + + "proof, not correctness validation, not production readiness, not production " + + "certification, not guaranteed replay, and not production validation."); + } + + private static String sectionSummary( + List sections) { + return sections.stream() + .map(section -> section.name() + "=" + section.status()) + .toList() + .toString(); + } + + private record SectionCounts( + int resultCount, + int withClosureSummary, + int missingClosureSummary) { + } +} diff --git a/src/main/resources/static/load-balancing-cockpit.html b/src/main/resources/static/load-balancing-cockpit.html index 50bd992d..016621f3 100644 --- a/src/main/resources/static/load-balancing-cockpit.html +++ b/src/main/resources/static/load-balancing-cockpit.html @@ -1342,6 +1342,10 @@

Explanation Drill-Down

Decision Replay Evidence Closure Checklist
Run routing comparison to populate response-level Reviewer Closure Checklist Status, Reviewer Closure Checklist Items, Reviewer Ready state, and not-proven boundaries: closureSummaryPresent, closureRollupPresent, countsMatchResultMetadata, scenarioReplayStripped, and notProvenBoundariesPresent. Read-only response-level reviewer closure checklist metadata only; not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, and not production validation.
+
+ Decision Replay Evidence Closure Packet +
Run routing comparison to populate response-level Reviewer Closure Packet Status, Sections, Reviewer Guidance, Reviewer Ready state, and not-proven boundaries: closureSummary, closureRollup, closureChecklist, scenarioReplayBoundary, and notProvenBoundaries. Read-only in-response reviewer closure packet metadata only; not an export/share/download packet, not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, and not production validation.
+
Allocation Math / Capacity Explanation
Run allocation preview to populate capacity math.
@@ -3531,6 +3535,7 @@

10. Safety limitations

renderDecisionReplayEvidenceReviewerClosureExplanation(response); renderDecisionReplayEvidenceReviewerClosureRollupExplanation(response); renderDecisionReplayEvidenceReviewerClosureChecklistExplanation(response); + renderDecisionReplayEvidenceReviewerClosurePacketExplanation(response); return; } const results = response.results || []; @@ -3558,6 +3563,7 @@

10. Safety limitations

renderDecisionReplayEvidenceReviewerClosureExplanation(response); renderDecisionReplayEvidenceReviewerClosureRollupExplanation(response); renderDecisionReplayEvidenceReviewerClosureChecklistExplanation(response); + renderDecisionReplayEvidenceReviewerClosurePacketExplanation(response); return; } rows.innerHTML = results.map((result) => "" @@ -3587,6 +3593,7 @@

10. Safety limitations

renderDecisionReplayEvidenceReviewerClosureExplanation(response); renderDecisionReplayEvidenceReviewerClosureRollupExplanation(response); renderDecisionReplayEvidenceReviewerClosureChecklistExplanation(response); + renderDecisionReplayEvidenceReviewerClosurePacketExplanation(response); } function renderLoadShedding(response) { @@ -3648,6 +3655,7 @@

10. Safety limitations

renderDecisionReplayEvidenceReviewerClosureExplanation(state.routing); renderDecisionReplayEvidenceReviewerClosureRollupExplanation(state.routing); renderDecisionReplayEvidenceReviewerClosureChecklistExplanation(state.routing); + renderDecisionReplayEvidenceReviewerClosurePacketExplanation(state.routing); renderAllocationExplanation(scenario); renderOverloadExplanation(scenario); renderRemediationExplanation(); @@ -4639,6 +4647,39 @@

10. Safety limitations

+ "Boundary: read-only response-level reviewer closure checklist metadata only; not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, not production validation, no upload/share/download, no telemetry, no external calls, and no server-side export/PDF/ZIP generation.

"; } + function renderDecisionReplayEvidenceReviewerClosurePacketExplanation(response) { + const container = document.getElementById( + "decision-replay-evidence-reviewer-closure-packet-explanation"); + if (!response || response.status === "Not available in current API") { + container.textContent = "Run routing comparison to populate response-level Reviewer Closure Packet Status, Sections, Reviewer Guidance, Reviewer Ready state, and not-proven boundaries: closureSummary, closureRollup, closureChecklist, scenarioReplayBoundary, and notProvenBoundaries. Read-only in-response reviewer closure packet metadata only; not an export/share/download packet, not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, and not production validation."; + return; + } + const packet = response.decisionReplayEvidenceReviewerClosurePacket || {}; + if (!packet.summary) { + container.textContent = "Decision Replay Evidence Closure Packet is unavailable; no response-level reviewer closure packet claim is invented. Read-only in-response reviewer closure packet metadata only; not an export/share/download packet, not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, and not production validation."; + return; + } + const sections = Array.isArray(packet.sections) + ? packet.sections.map((section) => + (section.name || "unknown") + "=" + (section.status || "UNKNOWN")).join("; ") + : "none"; + const guidance = Array.isArray(packet.reviewerGuidance) + ? packet.reviewerGuidance.join("; ") + : "none"; + const boundaries = Array.isArray(packet.notProvenBoundaries) + ? packet.notProvenBoundaries.join("; ") + : "not replay proof; not scoring proof; not correctness validation; not production readiness; not production certification; not guaranteed replay; not production validation"; + const text = "Reviewer Closure Packet Status: " + (packet.status || "UNKNOWN") + + "; packetVersion=" + (packet.packetVersion || "unknown") + + "; " + packet.summary + + "; Sections: " + sections + + "; reviewerReady=" + String(Boolean(packet.reviewerReady)) + + "; Reviewer Guidance: " + guidance + + "; Not-Proven Boundaries: " + boundaries; + container.innerHTML = "

" + escapeHtml(text) + "
" + + "Boundary: read-only in-response reviewer closure packet metadata only; not an export/share/download packet, not replay proof, not scoring proof, not correctness validation, not production readiness, not production certification, not guaranteed replay, not production validation, no upload/share/download, no telemetry, no external calls, and no server-side export/PDF/ZIP generation.

"; + } + function formatFinite(value) { const numeric = Number(value); return Number.isFinite(numeric) ? numeric.toFixed(6) : "not returned"; @@ -5264,6 +5305,28 @@

10. Safety limitations

: "not replay proof;not scoring proof;not correctness validation;not production readiness;not production certification;not guaranteed replay;not production validation") ].join("\n") : "- Not run"; + const closurePacket = state.routing && state.routing.decisionReplayEvidenceReviewerClosurePacket + ? state.routing.decisionReplayEvidenceReviewerClosurePacket + : null; + const evidenceReviewerClosurePacketLines = closurePacket + ? [ + "- status: " + (closurePacket.status || "unknown"), + "- reviewerReady: " + String(Boolean(closurePacket.reviewerReady)), + "- packetVersion: " + (closurePacket.packetVersion || "unknown"), + "- sections: " + (Array.isArray(closurePacket.sections) + ? closurePacket.sections.map((section) => + (section.name || "unknown") + "=" + (section.status || "UNKNOWN")).join(";") + : "none"), + "- reviewerGuidance: " + (Array.isArray(closurePacket.reviewerGuidance) + ? closurePacket.reviewerGuidance.join(";") + : "none"), + "- summary: " + (closurePacket.summary || "summary unavailable"), + "- notProvenBoundaries: " + (Array.isArray(closurePacket.notProvenBoundaries) + ? closurePacket.notProvenBoundaries.join(";") + : "not replay proof;not scoring proof;not correctness validation;not production readiness;not production certification;not guaranteed replay;not production validation"), + "- exportShareDownloadPacket: false" + ].join("\n") + : "- Not run"; const allocationLines = state.allocation ? [ "- requestedLoad: " + Number(scenario.allocationRequest.requestedLoad || 0).toFixed(3), @@ -5354,6 +5417,9 @@

10. Safety limitations

"## Decision Replay Evidence Closure Checklist", evidenceReviewerClosureChecklistLines, "", + "## Decision Replay Evidence Closure Packet", + evidenceReviewerClosurePacketLines, + "", "## Allocation Math / Capacity Explanation", allocationLines, "", diff --git a/src/main/resources/static/routing-demo.html b/src/main/resources/static/routing-demo.html index 229bd3ea..8bf100fe 100644 --- a/src/main/resources/static/routing-demo.html +++ b/src/main/resources/static/routing-demo.html @@ -916,9 +916,13 @@

Decision Vector Foundation

Reviewer closure checklist

Decision Replay Evidence Closure Checklist turns closure summary and rollup metadata into response-level reviewer checks without replay execution, scoring proof, correctness validation, production readiness, production certification, or guaranteed replay claims.

+
+ Reviewer closure packet +

Decision Replay Evidence Closure Packet groups closure summary, rollup, and checklist metadata into one in-response reviewer packet without export, download, share, replay proof, scoring proof, correctness validation, production readiness, production certification, or guaranteed replay claims.

+
Read-only API exposure -

The same-origin /api/routing/compare response can return additive decisionVector, dominantFactorAnalysis, decisionDeltaAnalysis, decisionReplaySnapshot, decisionReplayReconstructionTrace, decisionReplayCapsule, decisionReplayReadinessChecklist, decisionReplayEvidenceSourceMap, decisionReplayEvidenceBoundarySummary, decisionReplayEvidenceFieldInventory, decisionReplayEvidenceNullSafetySummary, decisionReplayEvidenceStatusRollup, decisionReplayEvidenceLaneNavigationSummary, decisionReplayEvidenceLaneDependencyMap, decisionReplayEvidenceLaneReferenceIndex, decisionReplayEvidenceLaneDependencySummary, decisionReplayEvidenceLaneConsistencySummary, decisionReplayEvidenceReviewerSnapshot, decisionReplayEvidenceReviewerGuidance, decisionReplayEvidenceReviewerHandoffSummary, decisionReplayEvidenceReviewerClosureSummary, top-level decisionReplayEvidenceReviewerClosureRollup, and top-level decisionReplayEvidenceReviewerClosureChecklist data; if absent, static lab guidance remains available.

+

The same-origin /api/routing/compare response can return additive decisionVector, dominantFactorAnalysis, decisionDeltaAnalysis, decisionReplaySnapshot, decisionReplayReconstructionTrace, decisionReplayCapsule, decisionReplayReadinessChecklist, decisionReplayEvidenceSourceMap, decisionReplayEvidenceBoundarySummary, decisionReplayEvidenceFieldInventory, decisionReplayEvidenceNullSafetySummary, decisionReplayEvidenceStatusRollup, decisionReplayEvidenceLaneNavigationSummary, decisionReplayEvidenceLaneDependencyMap, decisionReplayEvidenceLaneReferenceIndex, decisionReplayEvidenceLaneDependencySummary, decisionReplayEvidenceLaneConsistencySummary, decisionReplayEvidenceReviewerSnapshot, decisionReplayEvidenceReviewerGuidance, decisionReplayEvidenceReviewerHandoffSummary, decisionReplayEvidenceReviewerClosureSummary, top-level decisionReplayEvidenceReviewerClosureRollup, top-level decisionReplayEvidenceReviewerClosureChecklist, and top-level decisionReplayEvidenceReviewerClosurePacket data; if absent, static lab guidance remains available.

Replay and what-if boundary @@ -1558,6 +1562,29 @@

Decision Replay Evidence Closure Checklist

Read-only response-level reviewer closure checklist metadata only; no replay execution, no scoring proof, no correctness validation, no production readiness, no production certification, no guaranteed replay, and no production validation.

+

Decision Replay Evidence Closure Packet

+
+
+ Reviewer Closure Packet Status +

Run Compare strategies to populate response-level reviewer closure packet metadata.

+
+
+ Reviewer Closure Packet Sections +

No response-level reviewer closure packet sections have been returned yet.

+
+
+ Reviewer Closure Packet Guidance +

No response-level reviewer closure packet guidance has been returned yet.

+
+
+ Reviewer Closure Packet Boundaries +

not replay proof; not scoring proof; not correctness validation; not production readiness; not production certification; not guaranteed replay; not production validation

+
+
+ Reviewer Closure Packet Limitations +

Read-only in-response reviewer closure packet metadata only; not an export/share/download packet; no replay execution, no scoring proof, no correctness validation, no production readiness, no production certification, no guaranteed replay, and no production validation.

+
+

Copy boundary: the Decision Vector summary is assembled in the browser from visible local lab state only; no upload/share/download route, no server-side export/PDF/ZIP generation, no external calls, and no telemetry. @@ -1598,6 +1625,8 @@

Copyable Decision Vector summary

decisionReplayEvidenceReviewerClosureRollup: unavailable until /api/routing/compare returns decisionReplayEvidenceReviewerClosureRollup; no response-level reviewer closure claim is invented decisionReplayEvidenceReviewerClosureChecklist: unavailable until /api/routing/compare returns decisionReplayEvidenceReviewerClosureChecklist; no response-level reviewer closure checklist claim is invented decisionReplayEvidenceReviewerClosureChecklistItems: closureSummaryPresent; closureRollupPresent; countsMatchResultMetadata; scenarioReplayStripped; notProvenBoundariesPresent +decisionReplayEvidenceReviewerClosurePacket: unavailable until /api/routing/compare returns decisionReplayEvidenceReviewerClosurePacket; no response-level reviewer closure packet claim is invented +decisionReplayEvidenceReviewerClosurePacketSections: closureSummary; closureRollup; closureChecklist; scenarioReplayBoundary; notProvenBoundaries readOnlyExposure: Decision Vector data is unavailable in this response; static lab guidance remains available. replayReadiness: planned future contract; replay execution is not implemented whatIfReadiness: planned future contract; what-if execution is not implemented @@ -2688,7 +2717,17 @@

6. Safety limitations

evidenceReviewerClosureChecklistBoundaries: document.getElementById( "decision-vector-reviewer-closure-checklist-boundaries"), evidenceReviewerClosureChecklistLimitations: document.getElementById( - "decision-vector-reviewer-closure-checklist-limitations") + "decision-vector-reviewer-closure-checklist-limitations"), + evidenceReviewerClosurePacketStatus: document.getElementById( + "decision-vector-reviewer-closure-packet-status"), + evidenceReviewerClosurePacketSections: document.getElementById( + "decision-vector-reviewer-closure-packet-sections"), + evidenceReviewerClosurePacketGuidance: document.getElementById( + "decision-vector-reviewer-closure-packet-guidance"), + evidenceReviewerClosurePacketBoundaries: document.getElementById( + "decision-vector-reviewer-closure-packet-boundaries"), + evidenceReviewerClosurePacketLimitations: document.getElementById( + "decision-vector-reviewer-closure-packet-limitations") }; const requestJson = document.getElementById("request-json"); const baseUrlInput = document.getElementById("base-url"); @@ -3808,6 +3847,11 @@

6. Safety limitations

&& typeof lastRoutingBody.decisionReplayEvidenceReviewerClosureChecklist === "object" ? lastRoutingBody.decisionReplayEvidenceReviewerClosureChecklist : null; + const evidenceReviewerClosurePacket = lastRoutingBody + && lastRoutingBody.decisionReplayEvidenceReviewerClosurePacket + && typeof lastRoutingBody.decisionReplayEvidenceReviewerClosurePacket === "object" + ? lastRoutingBody.decisionReplayEvidenceReviewerClosurePacket + : null; const selectedStrategy = decisionVector && decisionVector.selectedStrategy ? decisionVector.selectedStrategy : (primary && primary.strategyId ? primary.strategyId : "not run"); @@ -4500,6 +4544,28 @@

6. Safety limitations

? evidenceReviewerClosureChecklist.notProvenBoundaries.join("; ") : "not replay proof; not scoring proof; not correctness validation; not production readiness; not production certification; not guaranteed replay; not production validation"; const reviewerClosureChecklistLimitations = "Read-only response-level reviewer closure checklist metadata only; no replay execution, no scoring proof, no correctness validation, no production readiness, no production certification, no guaranteed replay, and no production validation."; + const reviewerClosurePacketText = evidenceReviewerClosurePacket + ? (evidenceReviewerClosurePacket.summary + || ("Decision replay evidence closure packet is " + + (evidenceReviewerClosurePacket.status || "UNKNOWN") + ".")) + : "Decision Replay Evidence Closure Packet is unavailable; no response-level reviewer closure packet claim is invented."; + const reviewerClosurePacketSections = evidenceReviewerClosurePacket + && Array.isArray(evidenceReviewerClosurePacket.sections) + && evidenceReviewerClosurePacket.sections.length > 0 + ? evidenceReviewerClosurePacket.sections.map((section) => + (section.name || "unknown") + "=" + (section.status || "UNKNOWN")).join("; ") + : "No response-level reviewer closure packet sections have been returned yet."; + const reviewerClosurePacketGuidance = evidenceReviewerClosurePacket + && Array.isArray(evidenceReviewerClosurePacket.reviewerGuidance) + && evidenceReviewerClosurePacket.reviewerGuidance.length > 0 + ? evidenceReviewerClosurePacket.reviewerGuidance.join("; ") + : "No response-level reviewer closure packet guidance has been returned yet."; + const reviewerClosurePacketBoundaries = evidenceReviewerClosurePacket + && Array.isArray(evidenceReviewerClosurePacket.notProvenBoundaries) + && evidenceReviewerClosurePacket.notProvenBoundaries.length > 0 + ? evidenceReviewerClosurePacket.notProvenBoundaries.join("; ") + : "not replay proof; not scoring proof; not correctness validation; not production readiness; not production certification; not guaranteed replay; not production validation"; + const reviewerClosurePacketLimitations = "Read-only in-response reviewer closure packet metadata only; not an export/share/download packet; no replay execution, no scoring proof, no correctness validation, no production readiness, no production certification, no guaranteed replay, and no production validation."; const replayReadiness = decisionVector && decisionVector.replayReadiness ? decisionVector.replayReadiness : "planned future contract; replay execution, what-if execution, and structured decision logging are not implemented here."; @@ -4705,6 +4771,11 @@

6. Safety limitations

reviewerClosureChecklistBoundaries; decisionVectorFields.evidenceReviewerClosureChecklistLimitations.textContent = reviewerClosureChecklistLimitations; + decisionVectorFields.evidenceReviewerClosurePacketStatus.textContent = reviewerClosurePacketText; + decisionVectorFields.evidenceReviewerClosurePacketSections.textContent = reviewerClosurePacketSections; + decisionVectorFields.evidenceReviewerClosurePacketGuidance.textContent = reviewerClosurePacketGuidance; + decisionVectorFields.evidenceReviewerClosurePacketBoundaries.textContent = reviewerClosurePacketBoundaries; + decisionVectorFields.evidenceReviewerClosurePacketLimitations.textContent = reviewerClosurePacketLimitations; outputs.decisionVector.textContent = [ "# Decision Vector Foundation", @@ -4832,6 +4903,11 @@

6. Safety limitations

? reviewerClosureChecklistText + "; items=" + reviewerClosureChecklistItems + "; notProvenBoundaries=" + reviewerClosureChecklistBoundaries : "unavailable until /api/routing/compare returns decisionReplayEvidenceReviewerClosureChecklist; no response-level reviewer closure checklist claim is invented"), + "decisionReplayEvidenceReviewerClosurePacket: " + (evidenceReviewerClosurePacket + ? reviewerClosurePacketText + "; sections=" + reviewerClosurePacketSections + + "; reviewerGuidance=" + reviewerClosurePacketGuidance + + "; notProvenBoundaries=" + reviewerClosurePacketBoundaries + : "unavailable until /api/routing/compare returns decisionReplayEvidenceReviewerClosurePacket; no response-level reviewer closure packet claim is invented"), "selectedVsAlternativeExplanationNotes: " + alternativeCandidateSummary(selectedServer, currentPayload, scenarioMetrics(currentPayload), results), "readOnlyExposure: " + readOnlyExposure, "replayReadiness: " + replayReadiness, diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/ApiContractTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/ApiContractTest.java index 7ba2a83b..17defefe 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/ApiContractTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/ApiContractTest.java @@ -168,7 +168,8 @@ void openApiDocumentExposesCoreApiPathsAndSchemas() throws Exception { assertSchemaProperties(docs, "RoutingComparisonRequest", "strategies", "servers"); assertSchemaProperties(docs, "RoutingComparisonResponse", "requestedStrategies", "candidateCount", "timestamp", "decisionReplayEvidenceReviewerClosureRollup", - "decisionReplayEvidenceReviewerClosureChecklist", "results"); + "decisionReplayEvidenceReviewerClosureChecklist", "decisionReplayEvidenceReviewerClosurePacket", + "results"); assertSchemaProperties(docs, "RoutingComparisonResultResponse", "strategyId", "status", "chosenServerId", "reason", "candidateServersConsidered", "scores", "decisionVector", "dominantFactorAnalysis", "decisionDeltaAnalysis", "decisionReplaySnapshot", @@ -372,6 +373,11 @@ void openApiDocumentExposesCoreApiPathsAndSchemas() throws Exception { "status", "reviewerReady", "items", "summary", "notProvenBoundaries"); assertSchemaProperties(docs, "RoutingDecisionReplayEvidenceReviewerClosureChecklistItemResponse", "name", "status", "description"); + assertSchemaProperties(docs, "RoutingDecisionReplayEvidenceReviewerClosurePacketResponse", + "status", "reviewerReady", "packetVersion", "sections", "summary", "reviewerGuidance", + "notProvenBoundaries"); + assertSchemaProperties(docs, "RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse", + "name", "status", "description"); assertSchemaProperties(docs, "ScenarioReplayRequest", "scenarioId", "servers", "steps"); assertSchemaProperties(docs, "ScenarioReplayStepRequest", "stepId", "type", "requestedLoad", "strategy", "priority", "serverId", "routingStrategies", "currentInFlightRequestCount", @@ -519,6 +525,37 @@ void routingComparisonResponseShapeIsStable() throws Exception { is("not replay proof"))) .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosureChecklist.notProvenBoundaries[6]", is("not production validation"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.status", is("COMPLETE"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.reviewerReady", is(true))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.packetVersion", is("v1"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[0].name", + is("closureSummary"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[0].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[1].name", + is("closureRollup"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[1].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[2].name", + is("closureChecklist"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[2].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[3].name", + is("scenarioReplayBoundary"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[3].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[4].name", + is("notProvenBoundaries"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[4].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.summary", + containsString("not an export/share/download packet"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.reviewerGuidance[0]", + containsString("in-response reviewer index"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.notProvenBoundaries[0]", + is("not replay proof"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.notProvenBoundaries[6]", + is("not production validation"))) .andExpect(jsonPath("$.results").isArray()) .andExpect(jsonPath("$.results[0].strategyId", is("TAIL_LATENCY_POWER_OF_TWO"))) .andExpect(jsonPath("$.results[0].status", is("SUCCESS"))) @@ -1115,6 +1152,23 @@ void routingAllUnhealthyResponseShapeIsStable() throws Exception { is("not replay proof"))) .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosureChecklist.notProvenBoundaries[6]", is("not production validation"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.status", is("UNKNOWN"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.reviewerReady", is(false))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.packetVersion", is("v1"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[0].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[1].status", + is("UNKNOWN"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[2].status", + is("UNKNOWN"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[3].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.sections[4].status", + is("PASS"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.notProvenBoundaries[0]", + is("not replay proof"))) + .andExpect(jsonPath("$.decisionReplayEvidenceReviewerClosurePacket.notProvenBoundaries[6]", + is("not production validation"))) .andExpect(jsonPath("$.results[0].strategyId", is("TAIL_LATENCY_POWER_OF_TWO"))) .andExpect(jsonPath("$.results[0].status", is("SUCCESS"))) .andExpect(jsonPath("$.results[0].chosenServerId", nullValue())) diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabCockpitFramingDocumentationTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabCockpitFramingDocumentationTest.java index 0d609e05..d6187c14 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabCockpitFramingDocumentationTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabCockpitFramingDocumentationTest.java @@ -147,6 +147,7 @@ void reviewerTrustMapAndFramingDocContainExplicitNotDemoBoundary() throws Except assertTrue(framing.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md")); assertTrue(framing.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md")); assertTrue(framing.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md")); + assertTrue(framing.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md")); assertTrue(framing.contains("structured explanation contract for one controlled lab routing decision")); assertTrue(framing.contains("selected candidate vector")); assertTrue(framing.contains("non-selected candidate vectors")); @@ -173,6 +174,7 @@ void reviewerTrustMapAndFramingDocContainExplicitNotDemoBoundary() throws Except assertTrue(framing.contains("`results[].decisionReplayEvidenceReviewerClosureSummary`")); assertTrue(framing.contains("top-level `decisionReplayEvidenceReviewerClosureRollup`")); assertTrue(framing.contains("`decisionReplayEvidenceReviewerClosureChecklist`")); + assertTrue(framing.contains("`decisionReplayEvidenceReviewerClosurePacket`")); assertTrue(framing.contains("deterministic local fingerprint from already-built compare evidence only")); assertTrue(framing.contains("deterministic reconstruction evidence steps")); assertTrue(framing.contains("canonical evidence packaging")); @@ -188,6 +190,9 @@ void reviewerTrustMapAndFramingDocContainExplicitNotDemoBoundary() throws Except assertTrue(framing.contains("derives deterministic closure metadata from existing reviewer metadata")); assertTrue(framing.contains("Decision Replay Evidence Closure Rollup metadata derives only from already-returned")); assertTrue(framing.contains("Decision Replay Evidence Closure Checklist metadata derives only from already-returned")); + assertTrue(framing.contains("Decision Replay Evidence Closure Packet metadata derives only from already-returned")); + assertTrue(framing.contains("It is not an")); + assertTrue(framing.contains("export/share/download packet")); assertTrue(framing.contains("without new fingerprint generation")); assertTrue(framing.contains("without replay execution")); assertTrue(framing.contains("without what-if mutation")); diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionReplayEvidenceClosurePacketDocumentationTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionReplayEvidenceClosurePacketDocumentationTest.java new file mode 100644 index 00000000..4cb1bb78 --- /dev/null +++ b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionReplayEvidenceClosurePacketDocumentationTest.java @@ -0,0 +1,72 @@ +package com.richmond423.loadbalancerpro.api; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +class EnterpriseLabDecisionReplayEvidenceClosurePacketDocumentationTest { + private static final Path DOC = + Path.of("docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md"); + + @Test + void closurePacketDocDefinesReadOnlyDerivedBoundaries() throws Exception { + String doc = Files.readString(DOC, StandardCharsets.UTF_8); + String normalized = doc.toLowerCase(Locale.ROOT); + + assertTrue(doc.contains("# Enterprise Lab Decision Replay Evidence Closure Packet")); + assertTrue(doc.contains("`POST /api/routing/compare`")); + assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosurePacket`")); + assertTrue(doc.contains("`results[].decisionReplayEvidenceReviewerClosureSummary`")); + assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosureRollup`")); + assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosureChecklist`")); + assertTrue(doc.contains("packet version `v1`")); + assertTrue(doc.contains("`closureSummary`")); + assertTrue(doc.contains("`closureRollup`")); + assertTrue(doc.contains("`closureChecklist`")); + assertTrue(doc.contains("`scenarioReplayBoundary`")); + assertTrue(doc.contains("`notProvenBoundaries`")); + assertTrue(doc.contains("PASS")); + assertTrue(doc.contains("WARN")); + assertTrue(doc.contains("UNKNOWN")); + assertTrue(doc.contains("COMPLETE")); + assertTrue(doc.contains("PARTIAL")); + assertTrue(doc.contains("reviewer-ready boolean")); + assertTrue(doc.contains("deterministic summary text")); + assertTrue(doc.contains("reviewer guidance strings")); + assertTrue(doc.contains("`not replay proof`")); + assertTrue(doc.contains("`not scoring proof`")); + assertTrue(doc.contains("`not correctness validation`")); + assertTrue(doc.contains("`not production readiness`")); + assertTrue(doc.contains("`not production certification`")); + assertTrue(doc.contains("`not guaranteed replay`")); + assertTrue(doc.contains("`not production validation`")); + assertTrue(doc.contains("does not inspect raw server input")); + assertTrue(doc.contains("It does not use reflection.")); + assertTrue(doc.contains("is not an export, share, download, PDF, ZIP, or file-generation packet")); + assertTrue(doc.contains("does not execute replay")); + assertTrue(doc.contains("does not perform what-if mutation")); + assertTrue(doc.contains("does not persist reviewer closure packet data or audit logs server-side")); + assertTrue(doc.contains("does not export, download, upload, or share reviewer closure packet data")); + assertTrue(doc.contains("does not change routing behavior")); + assertTrue(doc.contains("does not change strategy selection")); + assertTrue(doc.contains("does not recompute scores")); + assertTrue(doc.contains("does not generate a new fingerprint, hash, SHA, or UUID")); + assertTrue(doc.contains("does not add telemetry or storage")); + assertTrue(doc.contains("does not add external calls, scripts, or CDNs")); + assertTrue(doc.contains("does not add server-side export/PDF/ZIP/file generation")); + assertTrue(doc.contains("Scenario replay keeps its stripped routing-result posture")); + assertTrue(doc.contains("not promoted into scenario replay output as replay proof")); + assertFalse(normalized.contains("production certification is proven")); + assertFalse(normalized.contains("guaranteed replay is proven")); + assertFalse(normalized.contains("quality ranking is proven")); + assertFalse(normalized.contains("approval is granted")); + assertFalse(normalized.contains("correctness validation is proven")); + assertFalse(normalized.contains("downloadable proof is generated")); + } +} diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionVectorDocumentationTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionVectorDocumentationTest.java index 5ac524ce..4d65dc4a 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionVectorDocumentationTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/EnterpriseLabDecisionVectorDocumentationTest.java @@ -214,6 +214,9 @@ void candidateDecisionVectorAndFactorPlaceholderAreBounded() throws Exception { assertTrue(doc.contains("Decision Replay Evidence Closure Checklist metadata")); assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md")); assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosureChecklist`")); + assertTrue(doc.contains("Decision Replay Evidence Closure Packet metadata")); + assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md")); + assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosurePacket`")); assertTrue(doc.contains("\"EXACT_FROM_CALCULATOR\"")); assertTrue(doc.contains("\"NOT_EXPOSED\"")); assertTrue(doc.contains("not production scoring proof")); @@ -282,6 +285,7 @@ void decisionVectorRoadmapIsFutureAndNotImplemented() throws Exception { assertTrue(doc.contains("`results[].decisionReplayEvidenceReviewerClosureSummary`")); assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosureRollup`")); assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosureChecklist`")); + assertTrue(doc.contains("`decisionReplayEvidenceReviewerClosurePacket`")); assertTrue(doc.contains("preserves existing")); assertTrue(doc.contains("`requestedStrategies`, `candidateCount`, `timestamp`, result status")); assertTrue(doc.contains("\"localLabResponsePath\": \"/api/routing/compare\"")); @@ -1212,10 +1216,12 @@ void decisionVectorDocIsLinkedFromReviewerDocs() throws Exception { assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_GUIDANCE.md")); assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md")); assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md")); + assertTrue(doc.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md")); assertTrue(doc.contains("Decision Vector")); } assertTrue(readme.contains("read-only Decision Replay Evidence Reviewer Closure Summary lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_REVIEWER_CLOSURE_SUMMARY.md); read-only Decision Replay Evidence Closure Rollup lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_ROLLUP.md); read-only Decision Replay Evidence Closure Checklist lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_CHECKLIST.md).")); + assertTrue(readme.contains("Read-only Decision Replay Evidence Closure Packet lane: [`docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md`](docs/ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md).")); assertTrue(trustMap.contains("### Decision Vector Contract")); assertTrue(framing.contains("## Decision Vector Contract")); } diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/LoadBalancingCockpitDemoTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/LoadBalancingCockpitDemoTest.java index 04add893..ed07b25c 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/LoadBalancingCockpitDemoTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/LoadBalancingCockpitDemoTest.java @@ -438,6 +438,18 @@ void cockpitPageContainsExpectedSectionsControlsAndEndpointPaths() throws Except assertTrue(page.contains("scenarioReplayStripped")); assertTrue(page.contains("notProvenBoundariesPresent")); assertTrue(page.contains("read-only response-level reviewer closure checklist metadata only")); + assertTrue(page.contains("Decision Replay Evidence Closure Packet")); + assertTrue(page.contains("id=\"decision-replay-evidence-reviewer-closure-packet-explanation\"")); + assertTrue(page.contains("decisionReplayEvidenceReviewerClosurePacket")); + assertTrue(page.contains("Reviewer Closure Packet Status")); + assertTrue(page.contains("Reviewer Closure Packet Status, Sections, Reviewer Guidance")); + assertTrue(page.contains("closureSummary")); + assertTrue(page.contains("closureRollup")); + assertTrue(page.contains("closureChecklist")); + assertTrue(page.contains("scenarioReplayBoundary")); + assertTrue(page.contains("exportShareDownloadPacket: false")); + assertTrue(page.contains("read-only in-response reviewer closure packet metadata only")); + assertTrue(page.contains("not an export/share/download packet")); assertTrue(page.contains("not replay proof")); assertTrue(page.contains("not production certification")); assertTrue(page.contains("not guaranteed replay")); diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/ReviewerTrustMapDocumentationTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/ReviewerTrustMapDocumentationTest.java index bb2fcad0..91260773 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/ReviewerTrustMapDocumentationTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/ReviewerTrustMapDocumentationTest.java @@ -210,6 +210,8 @@ void reviewerDemoPathAnswersFastDemoQuestionsAndLocksEvidenceFiles() throws Exce assertTrue(demoPath.contains("Decision Replay Evidence Reviewer Handoff Summary")); assertTrue(demoPath.contains("Decision Replay Evidence Reviewer Closure Summary")); assertTrue(demoPath.contains("Decision Replay Evidence Closure Rollup")); + assertTrue(demoPath.contains("Decision Replay Evidence Closure Checklist")); + assertTrue(demoPath.contains("Decision Replay Evidence Closure Packet")); assertTrue(demoPath.contains("Candidate Comparison Limits")); assertTrue(demoPath.contains("Candidate Evidence Cards")); assertTrue(demoPath.contains("Candidate Review Questions")); @@ -344,6 +346,11 @@ void routingDemoScenarioComparisonTrustMapEntryIsDiscoverableAndBounded() throws assertTrue(matrix.contains("RoutingDecisionReplayEvidenceReviewerClosureChecklistServiceTest")); assertTrue(matrix.contains("Do closure summary, closure rollup, and not-proven boundaries line up")); assertTrue(matrix.contains("decisionReplayEvidenceReviewerClosureChecklist")); + assertTrue(matrix.contains("Decision Replay Evidence Closure Packet")); + assertTrue(matrix.contains("ENTERPRISE_LAB_DECISION_REPLAY_EVIDENCE_CLOSURE_PACKET.md")); + assertTrue(matrix.contains("RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest")); + assertTrue(matrix.contains("What compact packet groups closure summary, rollup, and checklist metadata?")); + assertTrue(matrix.contains("decisionReplayEvidenceReviewerClosurePacket")); assertTrue(matrix.contains("Structured Decision Evidence")); assertTrue(matrix.contains("EnterpriseLabDecisionVectorDocumentationTest")); assertTrue(matrix.contains("Candidate Decision Vector")); diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionDemoTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionDemoTest.java index 3c9d0290..751e8f31 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionDemoTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionDemoTest.java @@ -920,6 +920,24 @@ void routingDemoDecisionVectorFoundationIsStructuredLocalAndBounded() throws Exc assertTrue(page.contains("countsMatchResultMetadata")); assertTrue(page.contains("scenarioReplayStripped")); assertTrue(page.contains("notProvenBoundariesPresent")); + assertTrue(page.contains("Decision Replay Evidence Closure Packet")); + assertTrue(page.contains("decisionReplayEvidenceReviewerClosurePacket: ")); + assertTrue(page.contains("decisionReplayEvidenceReviewerClosurePacketSections")); + assertTrue(page.contains("Reviewer Closure Packet Status")); + assertTrue(page.contains("Reviewer Closure Packet Sections")); + assertTrue(page.contains("Reviewer Closure Packet Guidance")); + assertTrue(page.contains("Reviewer Closure Packet Boundaries")); + assertTrue(page.contains("Reviewer Closure Packet Limitations")); + assertTrue(page.contains("id=\"decision-vector-reviewer-closure-packet-status\"")); + assertTrue(page.contains("id=\"decision-vector-reviewer-closure-packet-sections\"")); + assertTrue(page.contains("id=\"decision-vector-reviewer-closure-packet-guidance\"")); + assertTrue(page.contains("id=\"decision-vector-reviewer-closure-packet-boundaries\"")); + assertTrue(page.contains("id=\"decision-vector-reviewer-closure-packet-limitations\"")); + assertTrue(page.contains("closureSummary")); + assertTrue(page.contains("closureRollup")); + assertTrue(page.contains("closureChecklist")); + assertTrue(page.contains("scenarioReplayBoundary")); + assertTrue(page.contains("not an export/share/download packet")); assertTrue(page.contains("not production validation")); assertTrue(page.contains("not replay proof")); assertTrue(page.contains("not replay execution")); diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest.java new file mode 100644 index 00000000..ce3c7ebe --- /dev/null +++ b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest.java @@ -0,0 +1,245 @@ +package com.richmond423.loadbalancerpro.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Locale; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +class RoutingDecisionReplayEvidenceReviewerClosurePacketServiceTest { + private final RoutingDecisionReplayEvidenceReviewerClosureChecklistService checklistService = + new RoutingDecisionReplayEvidenceReviewerClosureChecklistService(); + private final RoutingDecisionReplayEvidenceReviewerClosurePacketService service = + new RoutingDecisionReplayEvidenceReviewerClosurePacketService(); + + @Test + void normalCompareResponseIncludesDeterministicReviewerClosurePacket() { + RoutingComparisonResponse response = healthyCompareResponse(); + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse first = service.packet( + response.results(), + response.decisionReplayEvidenceReviewerClosureRollup(), + response.decisionReplayEvidenceReviewerClosureChecklist()); + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse second = service.packet( + response.results(), + response.decisionReplayEvidenceReviewerClosureRollup(), + response.decisionReplayEvidenceReviewerClosureChecklist()); + + assertEquals(first, second); + assertEquals(response.decisionReplayEvidenceReviewerClosurePacket(), first); + assertEquals("COMPLETE", first.status()); + assertTrue(first.reviewerReady()); + assertEquals("v1", first.packetVersion()); + assertEquals(5, first.sections().size()); + assertSection(first, "closureSummary", "PASS"); + assertSection(first, "closureRollup", "PASS"); + assertSection(first, "closureChecklist", "PASS"); + assertSection(first, "scenarioReplayBoundary", "PASS"); + assertSection(first, "notProvenBoundaries", "PASS"); + assertEquals(List.of( + "not replay proof", + "not scoring proof", + "not correctness validation", + "not production readiness", + "not production certification", + "not guaranteed replay", + "not production validation"), first.notProvenBoundaries()); + assertTrue(first.summary().contains("closureSummary=PASS")); + assertTrue(first.summary().contains("not an export/share/download packet")); + assertTrue(first.summary().contains("not replay proof")); + assertTrue(first.reviewerGuidance().get(0).contains("in-response reviewer index")); + assertTrue(first.reviewerGuidance().get(2).contains("not production validation")); + assertThrows(UnsupportedOperationException.class, + () -> first.sections().add(new RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse( + "other", "PASS", "other"))); + assertThrows(UnsupportedOperationException.class, + () -> first.reviewerGuidance().add("other guidance")); + assertThrows(UnsupportedOperationException.class, + () -> first.notProvenBoundaries().add("other boundary")); + } + + @Test + void noHealthyComparePathReturnsUnknownPacketWithoutInventedProof() { + RoutingComparisonResponse response = new RoutingComparisonService().compare(new RoutingComparisonRequest( + List.of("TAIL_LATENCY_POWER_OF_TWO"), + List.of(new RoutingServerStateInput( + "green", + false, + 1, + null, + null, + null, + 10.0, + 20.0, + 30.0, + 0.0, + null, + null)))); + + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse packet = + response.decisionReplayEvidenceReviewerClosurePacket(); + + assertEquals("UNKNOWN", packet.status()); + assertFalse(packet.reviewerReady()); + assertEquals("v1", packet.packetVersion()); + assertSection(packet, "closureSummary", "PASS"); + assertSection(packet, "closureRollup", "UNKNOWN"); + assertSection(packet, "closureChecklist", "UNKNOWN"); + assertSection(packet, "scenarioReplayBoundary", "PASS"); + assertSection(packet, "notProvenBoundaries", "PASS"); + assertTrue(packet.notProvenBoundaries().contains("not replay proof")); + assertTrue(packet.notProvenBoundaries().contains("not production validation")); + assertFalse(packet.toString().contains("reviewerClosurePacketFingerprint")); + assertFalse(packet.toString().contains("production certification is proven")); + assertFalse(packet.toString().contains("guaranteed replay is proven")); + assertFalse(packet.toString().contains("correctness validation is proven")); + } + + @Test + void strippedOrMissingClosureSummaryWarnsWhenPacketSourcesNoLongerAlign() { + RoutingComparisonResponse response = healthyCompareResponse(); + RoutingComparisonResultResponse strippedResult = withoutReviewerClosureSummary(response.results().get(0)); + RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse checklist = checklistService.checklist( + List.of(strippedResult), + response.decisionReplayEvidenceReviewerClosureRollup()); + + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse packet = service.packet( + List.of(strippedResult), + response.decisionReplayEvidenceReviewerClosureRollup(), + checklist); + + assertEquals("PARTIAL", packet.status()); + assertFalse(packet.reviewerReady()); + assertSection(packet, "closureSummary", "WARN"); + assertSection(packet, "closureRollup", "PASS"); + assertSection(packet, "closureChecklist", "WARN"); + assertSection(packet, "scenarioReplayBoundary", "PASS"); + assertSection(packet, "notProvenBoundaries", "PASS"); + assertTrue(packet.summary().contains("closureSummary=WARN")); + } + + @Test + void reviewerClosurePacketResponseDoesNotExposeUnsafeOrProofClaims() throws Exception { + String json = new ObjectMapper().writeValueAsString(healthyCompareResponse() + .decisionReplayEvidenceReviewerClosurePacket()); + String normalized = json.toLowerCase(Locale.ROOT); + + assertTrue(normalized.contains("decision replay evidence reviewer closure packet")); + assertTrue(json.contains("not an export/share/download packet")); + assertTrue(json.contains("not replay proof")); + assertTrue(json.contains("not scoring proof")); + assertTrue(json.contains("not production validation")); + assertFalse(json.contains("reviewerClosurePacketFingerprint")); + assertFalse(json.contains("reviewerClosurePacketHash")); + assertFalse(json.contains("production certification is proven")); + assertFalse(json.contains("guaranteed replay is proven")); + assertFalse(json.contains("correctness validation is proven")); + } + + private static void assertSection( + RoutingDecisionReplayEvidenceReviewerClosurePacketResponse packet, + String name, + String status) { + RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse section = packet.sections().stream() + .filter(candidate -> name.equals(candidate.name())) + .findFirst() + .orElseThrow(); + assertEquals(status, section.status()); + assertFalse(section.description().isBlank()); + } + + private static RoutingComparisonResponse healthyCompareResponse() { + return new RoutingComparisonService().compare(new RoutingComparisonRequest( + List.of("TAIL_LATENCY_POWER_OF_TWO"), + List.of( + new RoutingServerStateInput( + "edge-alpha", + true, + 5, + 100.0, + 100.0, + 2.0, + 20.0, + 40.0, + 80.0, + 0.01, + 1, + new NetworkAwarenessInput( + 0.0, + 0.0, + 0.0, + 4.0, + false, + 0, + 120)), + new RoutingServerStateInput( + "edge-beta", + true, + 28, + 100.0, + 100.0, + 4.0, + 24.0, + 52.0, + 96.0, + 0.02, + 3, + new NetworkAwarenessInput( + 0.01, + 0.01, + 0.0, + 7.0, + false, + 1, + 120)), + new RoutingServerStateInput( + "edge-drain", + false, + 1, + 100.0, + 100.0, + 5.0, + 12.0, + 20.0, + 40.0, + 0.0, + 0, + null)))); + } + + private static RoutingComparisonResultResponse withoutReviewerClosureSummary( + RoutingComparisonResultResponse result) { + return new RoutingComparisonResultResponse( + result.strategyId(), + result.status(), + result.chosenServerId(), + result.reason(), + result.candidateServersConsidered(), + result.scores(), + result.decisionVector(), + result.dominantFactorAnalysis(), + result.decisionDeltaAnalysis(), + result.decisionReplaySnapshot(), + result.decisionReplayReconstructionTrace(), + result.decisionReplayCapsule(), + result.decisionReplayReadinessChecklist(), + result.decisionReplayEvidenceSourceMap(), + result.decisionReplayEvidenceBoundarySummary(), + result.decisionReplayEvidenceFieldInventory(), + result.decisionReplayEvidenceNullSafetySummary(), + result.decisionReplayEvidenceStatusRollup(), + result.decisionReplayEvidenceLaneNavigationSummary(), + result.decisionReplayEvidenceLaneDependencyMap(), + result.decisionReplayEvidenceLaneReferenceIndex(), + result.decisionReplayEvidenceLaneDependencySummary(), + result.decisionReplayEvidenceLaneConsistencySummary(), + result.decisionReplayEvidenceReviewerSnapshot(), + result.decisionReplayEvidenceReviewerGuidance(), + result.decisionReplayEvidenceReviewerHandoffSummary(), + null); + } +} diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionVectorReadOnlyExposureTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionVectorReadOnlyExposureTest.java index 7f28279c..9d3739f1 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionVectorReadOnlyExposureTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingDecisionVectorReadOnlyExposureTest.java @@ -73,6 +73,27 @@ void compareResponseExposesDecisionVectorAdditivelyWithoutBreakingExistingFields assertFalse(closureChecklist.toString().contains("production certification is proven")); assertFalse(closureChecklist.toString().contains("guaranteed replay is proven")); assertFalse(closureChecklist.toString().contains("correctness validation is proven")); + JsonNode closurePacket = response.path("decisionReplayEvidenceReviewerClosurePacket"); + assertEquals("COMPLETE", closurePacket.path("status").asText()); + assertTrue(closurePacket.path("reviewerReady").asBoolean()); + assertEquals("v1", closurePacket.path("packetVersion").asText()); + assertEquals("closureSummary", closurePacket.at("/sections/0/name").asText()); + assertEquals("PASS", closurePacket.at("/sections/0/status").asText()); + assertEquals("closureRollup", closurePacket.at("/sections/1/name").asText()); + assertEquals("PASS", closurePacket.at("/sections/1/status").asText()); + assertEquals("closureChecklist", closurePacket.at("/sections/2/name").asText()); + assertEquals("PASS", closurePacket.at("/sections/2/status").asText()); + assertEquals("scenarioReplayBoundary", closurePacket.at("/sections/3/name").asText()); + assertEquals("PASS", closurePacket.at("/sections/3/status").asText()); + assertEquals("notProvenBoundaries", closurePacket.at("/sections/4/name").asText()); + assertEquals("PASS", closurePacket.at("/sections/4/status").asText()); + assertTrue(closurePacket.path("summary").asText().contains("not an export/share/download packet")); + assertEquals("not replay proof", closurePacket.at("/notProvenBoundaries/0").asText()); + assertEquals("not production validation", closurePacket.at("/notProvenBoundaries/6").asText()); + assertFalse(closurePacket.toString().contains("reviewerClosurePacketFingerprint")); + assertFalse(closurePacket.toString().contains("production certification is proven")); + assertFalse(closurePacket.toString().contains("guaranteed replay is proven")); + assertFalse(closurePacket.toString().contains("correctness validation is proven")); JsonNode result = response.at("/results/0"); assertEquals("TAIL_LATENCY_POWER_OF_TWO", result.path("strategyId").asText()); assertEquals("SUCCESS", result.path("status").asText()); @@ -1077,6 +1098,21 @@ void allUnhealthyResultKeepsExistingFailureShapeAndNoInventedDecisionVector() th assertFalse(closureChecklist.toString().contains("production certification is proven")); assertFalse(closureChecklist.toString().contains("guaranteed replay is proven")); assertFalse(closureChecklist.toString().contains("correctness validation is proven")); + JsonNode closurePacket = response.path("decisionReplayEvidenceReviewerClosurePacket"); + assertEquals("UNKNOWN", closurePacket.path("status").asText()); + assertFalse(closurePacket.path("reviewerReady").asBoolean()); + assertEquals("v1", closurePacket.path("packetVersion").asText()); + assertEquals("PASS", closurePacket.at("/sections/0/status").asText()); + assertEquals("UNKNOWN", closurePacket.at("/sections/1/status").asText()); + assertEquals("UNKNOWN", closurePacket.at("/sections/2/status").asText()); + assertEquals("PASS", closurePacket.at("/sections/3/status").asText()); + assertEquals("PASS", closurePacket.at("/sections/4/status").asText()); + assertEquals("not replay proof", closurePacket.at("/notProvenBoundaries/0").asText()); + assertEquals("not production validation", closurePacket.at("/notProvenBoundaries/6").asText()); + assertFalse(closurePacket.toString().contains("reviewerClosurePacketFingerprint")); + assertFalse(closurePacket.toString().contains("production certification is proven")); + assertFalse(closurePacket.toString().contains("guaranteed replay is proven")); + assertFalse(closurePacket.toString().contains("correctness validation is proven")); assertTrue(result.path("reason").asText().contains("No healthy eligible servers")); } diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingOpenApiContractTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingOpenApiContractTest.java index de5b6c7c..4c991897 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/RoutingOpenApiContractTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/RoutingOpenApiContractTest.java @@ -60,6 +60,8 @@ void openApiDocsCharacterizeInferredRoutingCompareSchemas() throws Exception { "#/components/schemas/RoutingDecisionReplayEvidenceReviewerClosureRollupResponse"); assertRef(required(responseProperties, "/decisionReplayEvidenceReviewerClosureChecklist"), "#/components/schemas/RoutingDecisionReplayEvidenceReviewerClosureChecklistResponse"); + assertRef(required(responseProperties, "/decisionReplayEvidenceReviewerClosurePacket"), + "#/components/schemas/RoutingDecisionReplayEvidenceReviewerClosurePacketResponse"); assertEquals("array", required(responseProperties, "/results/type").asText()); assertRef(required(responseProperties, "/results/items"), "#/components/schemas/RoutingComparisonResultResponse"); @@ -663,6 +665,27 @@ void openApiDocsCharacterizeInferredRoutingCompareSchemas() throws Exception { assertEquals("string", required(reviewerClosureChecklistItemProperties, "/name/type").asText()); assertEquals("string", required(reviewerClosureChecklistItemProperties, "/status/type").asText()); assertEquals("string", required(reviewerClosureChecklistItemProperties, "/description/type").asText()); + + JsonNode reviewerClosurePacketProperties = required(docs, + "/components/schemas/RoutingDecisionReplayEvidenceReviewerClosurePacketResponse/properties"); + assertEquals("string", required(reviewerClosurePacketProperties, "/status/type").asText()); + assertEquals("boolean", required(reviewerClosurePacketProperties, "/reviewerReady/type").asText()); + assertEquals("string", required(reviewerClosurePacketProperties, "/packetVersion/type").asText()); + assertEquals("array", required(reviewerClosurePacketProperties, "/sections/type").asText()); + assertRef(required(reviewerClosurePacketProperties, "/sections/items"), + "#/components/schemas/RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse"); + assertEquals("string", required(reviewerClosurePacketProperties, "/summary/type").asText()); + assertEquals("array", required(reviewerClosurePacketProperties, "/reviewerGuidance/type").asText()); + assertEquals("string", required(reviewerClosurePacketProperties, "/reviewerGuidance/items/type").asText()); + assertEquals("array", required(reviewerClosurePacketProperties, "/notProvenBoundaries/type").asText()); + assertEquals("string", + required(reviewerClosurePacketProperties, "/notProvenBoundaries/items/type").asText()); + + JsonNode reviewerClosurePacketSectionProperties = required(docs, + "/components/schemas/RoutingDecisionReplayEvidenceReviewerClosurePacketSectionResponse/properties"); + assertEquals("string", required(reviewerClosurePacketSectionProperties, "/name/type").asText()); + assertEquals("string", required(reviewerClosurePacketSectionProperties, "/status/type").asText()); + assertEquals("string", required(reviewerClosurePacketSectionProperties, "/description/type").asText()); } private JsonNode openApiDocs() throws Exception { diff --git a/src/test/java/com/richmond423/loadbalancerpro/api/ScenarioReplayControllerTest.java b/src/test/java/com/richmond423/loadbalancerpro/api/ScenarioReplayControllerTest.java index 0b21fe58..cd7c372c 100644 --- a/src/test/java/com/richmond423/loadbalancerpro/api/ScenarioReplayControllerTest.java +++ b/src/test/java/com/richmond423/loadbalancerpro/api/ScenarioReplayControllerTest.java @@ -211,10 +211,13 @@ void failureRecoveryScenarioUpdatesOnlyReplayLocalServerStateAndRoutesDeterminis nullValue())) .andExpect(jsonPath("$.steps[1].decisionReplayEvidenceReviewerClosureRollup").doesNotExist()) .andExpect(jsonPath("$.steps[1].decisionReplayEvidenceReviewerClosureChecklist").doesNotExist()) + .andExpect(jsonPath("$.steps[1].decisionReplayEvidenceReviewerClosurePacket").doesNotExist()) .andExpect(jsonPath("$.steps[1].routingResults[0]" + ".decisionReplayEvidenceReviewerClosureRollup").doesNotExist()) .andExpect(jsonPath("$.steps[1].routingResults[0]" + ".decisionReplayEvidenceReviewerClosureChecklist").doesNotExist()) + .andExpect(jsonPath("$.steps[1].routingResults[0]" + + ".decisionReplayEvidenceReviewerClosurePacket").doesNotExist()) .andExpect(jsonPath("$.steps[2].serverStates[0].healthy", is(true))) .andExpect(jsonPath("$.steps[3].selectedServerId", is("green"))) .andExpect(jsonPath("$.steps[3].routingResults[0].chosenServerId", is("green")))