# WCS Real Queue History Replay Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. **Goal:** Build a reproducible, read-only offline replay of FT546's real SRGV queue history, test at least 200 original records, scan the complete 17,136-row history through database aggregates, and produce a Chinese-readable report that separates database facts from SHADOW inference. **Architecture:** Use the SQL Server MCP only to create versioned JSON snapshots and aggregate scan evidence; Maven tests never connect to SQL Server. A focused `com.ft546.wcs.shadow.history` package loads the snapshot, validates and normalizes paths, reconstructs inferred lifecycle-overlap windows, projects node/undirected-edge resources, performs deterministic replay, and writes machine-readable evidence. The final Excel workbook is generated from those verified evidence files. **Tech Stack:** Java 8, Maven, Jackson 2.17.2, JUnit 5.10.2, SQL Server read-only MCP, `@oai/artifact-tool` for the final `.xlsx`. **Status (2026-07-31):** Completed and independently reviewed. Final evidence run: `outputs/wcs-shadow-history-replay-20260731-0945-af0b52d`; Maven verification: 355 tests, 0 failures, 0 errors, 0 skipped. ## Global Constraints - All database access is single-statement `SELECT`/CTE and read-only; no stored procedures or mutations. - Preserve original `guid`, `task_no`, `agv_id`, site IDs, and paths in internal test evidence. - Never store database credentials, IP addresses, tokens, or connection strings. - `start_date` may be null; inferred activity interval is `[create_date, finish_date)`. - Empty or unknown paths produce `INSUFFICIENT_DATA`, never a synthetic `GRANT`. - Historical completion is a database fact; replay decisions are current-model inference and never an accuracy comparison. - Do not modify `ruoyi-*` production modules. - Keep the existing 60 synthetic safety cases and every existing test passing. - Output wording must say “真实历史队列离线回放”, not “现场 SHADOW 验收”. --- ## File Structure **Create** - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueRecord.java` — immutable raw snapshot row. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueDataset.java` — manifest plus records and map sites. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueLoader.java` — classpath JSON loading and SHA/count validation. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalPath.java` — parsed dates, ordered nodes, resource projection, and issues. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueNormalizer.java` — raw-row validation and normalization. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayCategory.java` — stable result enum. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayResult.java` — one readable replay result. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayAnalyzer.java` — overlap reconstruction and deterministic conflict decisions. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayReportWriter.java` — summary JSON and CSV evidence. - `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayApplication.java` — CLI entry point. - `javaTest/src/test/resources/shadow/history/manifest.json` — extraction metadata and hashes. - `javaTest/src/test/resources/shadow/history/full-scan-summary.json` — database-side aggregate scan of all rows. - `javaTest/src/test/resources/shadow/history/queue-history-200.json` — deterministic sample with at least 200 original rows. - `javaTest/src/test/resources/shadow/history/map-sites.json` — only map sites referenced by the sample. - `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueLoaderTest.java` - `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueNormalizerTest.java` - `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalReplayAnalyzerTest.java` - `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueDatabaseReplayTest.java` **Modify** - `javaTest/README.md` — commands, data provenance, readable field meanings, and evidence boundary. **Generated, not committed** - `outputs/wcs-shadow-history-replay-/source/queue-history-full.json` - `outputs/wcs-shadow-history-replay-/summary.json` - `outputs/wcs-shadow-history-replay-/replay-results.csv` - `outputs/wcs-shadow-history-replay-/data-quality-issues.csv` - `outputs/wcs-shadow-history-replay-/readable-report.xlsx` - `outputs/wcs-shadow-history-replay-/manifest.sha256` --- ### Task 1: Extract and Freeze Read-Only Database Evidence **Files:** - Create: `javaTest/src/test/resources/shadow/history/full-scan-summary.json` - Create: `javaTest/src/test/resources/shadow/history/queue-history-200.json` - Create: `javaTest/src/test/resources/shadow/history/map-sites.json` - Create: `javaTest/src/test/resources/shadow/history/manifest.json` **Interfaces:** - Consumes: `FT546-wcs.dbo.wcs_srgv_queue_finish`, `FT546-wcs.dbo.wcs_srgv_map`. - Produces: JSON arrays with original database fields and a manifest containing `database`, `table`, `extractedAt`, `totalRows`, `sampleRows`, `queryId`, and SHA-256 values. - [x] **Step 1: Run the complete-history aggregate scan** Use the SQL Server read-only tool with: ```sql SELECT COUNT(*) AS total_rows, MIN(create_date) AS min_create_date, MAX(create_date) AS max_create_date, MIN(finish_date) AS min_finish_date, MAX(finish_date) AS max_finish_date, SUM(CASE WHEN path_info IS NULL OR LTRIM(RTRIM(path_info)) = '' THEN 1 ELSE 0 END) AS missing_path_rows, SUM(CASE WHEN create_date IS NULL OR finish_date IS NULL THEN 1 ELSE 0 END) AS missing_interval_rows, SUM(CASE WHEN finish_date < create_date THEN 1 ELSE 0 END) AS reversed_interval_rows, COUNT(DISTINCT agv_id) AS distinct_agv, COUNT(DISTINCT start_site) AS distinct_start_site, COUNT(DISTINCT end_site) AS distinct_end_site FROM dbo.wcs_srgv_queue_finish ``` Expected: one row; `total_rows >= 17136`; query is read-only. - [x] **Step 2: Select a deterministic representative sample** Use one CTE query. The final `ORDER BY` is mandatory for reproducibility: ```sql WITH eligible AS ( SELECT guid, task_no, task_type, priority, cargo, issued, execution_status, cargo_pick_site, cargo_place_site, start_site, end_site, the_floor, trun_path, up_down_site, create_date, start_date, finish_date, agv_id, default02, default03, default04, default05, default06, default07, default08, path_info, ROW_NUMBER() OVER ( PARTITION BY agv_id, task_type ORDER BY create_date, finish_date, guid ) AS stratum_seq FROM dbo.wcs_srgv_queue_finish WHERE path_info IS NOT NULL AND LTRIM(RTRIM(path_info)) <> '' AND create_date IS NOT NULL AND finish_date IS NOT NULL ) SELECT TOP 240 * FROM eligible ORDER BY CASE WHEN stratum_seq <= 30 THEN 0 ELSE 1 END, stratum_seq, agv_id, task_type, create_date, finish_date, guid ``` Expected: 240 original rows when available, never fewer than 200. - [x] **Step 3: Export the complete history through deterministic read-only pages** Use pages of 500 rows and change only the two numeric bounds for each page: ```sql WITH ordered AS ( SELECT ROW_NUMBER() OVER ( ORDER BY create_date, finish_date, guid, task_no, agv_id ) AS snapshot_row, guid, task_no, task_type, priority, cargo, issued, execution_status, cargo_pick_site, cargo_place_site, start_site, end_site, the_floor, trun_path, up_down_site, create_date, start_date, finish_date, agv_id, default02, default03, default04, default05, default06, default07, default08, path_info FROM dbo.wcs_srgv_queue_finish ) SELECT * FROM ordered WHERE snapshot_row BETWEEN 1 AND 500 ORDER BY snapshot_row ``` Continue with `501 AND 1000` until the returned page is empty. Stream pages into `outputs/wcs-shadow-history-replay-/source/queue-history-full.json`; do not route the complete payload through chat text. The final row count must equal the aggregate scan exactly. - [x] **Step 4: Export referenced map sites** Read the full non-sensitive map projection, then retain only site IDs referenced by the sample and complete snapshot: ```sql SELECT site_id, x, y, z, axy, warehouse, space_area FROM dbo.wcs_srgv_map WHERE site_id IS NOT NULL ORDER BY site_id, z, y, x ``` Do not query credentials or device IP fields. - [x] **Step 5: Write snapshot files and hashes** Write JSON with UTF-8, stable property order, and two-space indentation. Compute: ```text SHA-256(queue-history-200.json) SHA-256(map-sites.json) SHA-256(full-scan-summary.json) ``` `manifest.json` must contain the exact hashes and row counts. The run-directory source manifest additionally contains the complete snapshot hash. It must not contain connection strings. - [x] **Step 6: Verify extraction evidence** Run: ```bash grep -RniE "password|token|jdbc:|server=" javaTest/src/test/resources/shadow/history ``` Expected: no matches. - [x] **Step 7: Commit only the bounded immutable test snapshot** ```bash git add javaTest/src/test/resources/shadow/history git commit -m "test: add real queue history replay snapshot" ``` --- ### Task 2: Load and Validate the Versioned Snapshot **Files:** - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueRecord.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueDataset.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueLoader.java` - Test: `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueLoaderTest.java` **Interfaces:** - Produces: `HistoricalQueueDataset HistoricalQueueLoader.load(ClassLoader loader, String basePath)`. - Produces: `HistoricalQueueDataset HistoricalQueueLoader.load(Path basePath)` for the complete run-directory snapshot. - `HistoricalQueueDataset.getRecords()` returns an unmodifiable `List`. - `HistoricalQueueDataset.getMapSites()` returns an unmodifiable `Set`. - [x] **Step 1: Write failing loader tests** ```java @Test void loadsVersionedDatabaseSnapshotAndPreservesOriginalIds() { HistoricalQueueDataset data = new HistoricalQueueLoader().load( getClass().getClassLoader(), "shadow/history"); assertTrue(data.getRecords().size() >= 200); assertEquals(data.getManifestSampleRows(), data.getRecords().size()); assertFalse(data.getRecords().get(0).getTaskNo().isEmpty()); assertFalse(data.getRecords().get(0).getAgvId().isEmpty()); } @Test void rejectsHashMismatch() { assertThrows(IllegalArgumentException.class, () -> HistoricalQueueLoader.verifySha256("wrong", "abc".getBytes(StandardCharsets.UTF_8))); } ``` - [x] **Step 2: Run loader tests and confirm RED** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalQueueLoaderTest test ``` Expected: compilation failure because history classes do not exist. - [x] **Step 3: Implement immutable raw records and dataset** `HistoricalQueueRecord` must expose exact string values for IDs/sites/dates and retain all extracted columns. Constructor arguments are JSON-mapped with Jackson `@JsonCreator` and `@JsonProperty`. `HistoricalQueueDataset` must defensively copy: ```java this.records = Collections.unmodifiableList(new ArrayList(records)); this.mapSites = Collections.unmodifiableSet(new LinkedHashSet(mapSites)); ``` - [x] **Step 4: Implement loader and SHA validation** The loader reads the four classpath resources, verifies every hash before deserializing, and checks: ```java if (manifestSampleRows != records.size()) { throw new IllegalArgumentException("history sample row count mismatch"); } if (records.size() < 200) { throw new IllegalArgumentException("history sample must contain at least 200 rows"); } ``` - [x] **Step 5: Run loader tests and confirm GREEN** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalQueueLoaderTest test ``` Expected: all loader tests pass. - [x] **Step 6: Commit** ```bash git add javaTest/src/main/java/com/ft546/wcs/shadow/history javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueLoaderTest.java git commit -m "feat: load versioned queue history snapshots" ``` --- ### Task 3: Normalize Dates and Real Paths Fail-Closed **Files:** - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalPath.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueNormalizer.java` - Test: `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueNormalizerTest.java` **Interfaces:** - Produces: `HistoricalPath HistoricalQueueNormalizer.normalize(HistoricalQueueRecord record, Set mapSites)`. - `HistoricalPath` exposes `getNodes()`, `getResources()`, `getCreatedAt()`, `getFinishedAt()`, `getIssues()`, and `isReplayable()`. - [x] **Step 1: Write failing normalization tests** ```java @Test void parsesTrailingCommaAndProjectsUndirectedEdges() { HistoricalPath path = normalizer.normalize(record("A,B,C,"), set("A", "B", "C")); assertEquals(Arrays.asList("A", "B", "C"), path.getNodes()); assertTrue(path.getResources().contains(ResourceKey.node("B"))); assertTrue(path.getResources().contains(ResourceKey.edge("B", "A"))); } @Test void unknownMapSiteIsNotReplayable() { HistoricalPath path = normalizer.normalize(record("A,UNKNOWN,"), set("A")); assertFalse(path.isReplayable()); assertTrue(path.getIssues().contains("UNKNOWN_MAP_SITE:UNKNOWN")); } @Test void reversedIntervalIsNotReplayable() { HistoricalPath path = normalizer.normalize( recordWithTimes("2026-07-21 14:05:00", "2026-07-21 14:04:59", "A,B,"), set("A", "B")); assertFalse(path.isReplayable()); assertTrue(path.getIssues().contains("REVERSED_INTERVAL")); } ``` - [x] **Step 2: Run normalization tests and confirm RED** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalQueueNormalizerTest test ``` Expected: compilation failure because normalizer classes do not exist. - [x] **Step 3: Implement strict timestamp parsing** Use: ```java DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss") .withResolverStyle(ResolverStyle.STRICT); ``` Parse into `LocalDateTime`. Null, blank, invalid, or reversed intervals add structured issues and prevent replay. - [x] **Step 4: Implement path normalization and resource projection** Rules: ```text trim tokens drop empty trailing tokens keep original order flag adjacent duplicate as ADJACENT_DUPLICATE_NODE: flag missing map site as UNKNOWN_MAP_SITE: project every node and every adjacent undirected edge do not create CONFLICT or lift resources ``` Empty paths add `EMPTY_PATH` and return `isReplayable() == false`. - [x] **Step 5: Run tests and confirm GREEN** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalQueueNormalizerTest test ``` Expected: all normalization tests pass. - [x] **Step 6: Commit** ```bash git add javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalPath.java javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalQueueNormalizer.java javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueNormalizerTest.java git commit -m "feat: normalize real queue paths fail closed" ``` --- ### Task 4: Reconstruct Overlap Windows and Deterministic Replay **Files:** - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayCategory.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayResult.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayAnalyzer.java` - Test: `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalReplayAnalyzerTest.java` **Interfaces:** - Produces: `List HistoricalReplayAnalyzer.analyze(List paths)`. - Produces: `boolean HistoricalReplayAnalyzer.overlaps(HistoricalPath left, HistoricalPath right)`. - Stable ordering: priority ascending, `createdAt`, task number, AGV ID, GUID. - [x] **Step 1: Write failing overlap and conflict tests** ```java @Test void touchingIntervalsDoNotOverlap() { assertFalse(analyzer.overlaps(path("10:00:00", "10:01:00"), path("10:01:00", "10:02:00"))); } @Test void detectsOppositeUseOfSameUndirectedEdge() { List results = analyzer.analyze(Arrays.asList( path("V1", "T1", "A,B", "10:00:00", "10:02:00"), path("V2", "T2", "B,A", "10:00:30", "10:03:00"))); assertTrue(results.stream().anyMatch( result -> result.getCategory() == HistoricalReplayCategory.POTENTIAL_OPPOSITE_EDGE)); } @Test void identicalInputProducesIdenticalResults() { List first = analyzer.analyze(sample); List second = analyzer.analyze(sample); assertEquals(first, second); } ``` - [x] **Step 2: Run analyzer tests and confirm RED** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest test ``` Expected: compilation failure because analyzer types do not exist. - [x] **Step 3: Implement half-open overlap semantics** ```java return left.getCreatedAt().isBefore(right.getFinishedAt()) && right.getCreatedAt().isBefore(left.getFinishedAt()); ``` Non-replayable paths return `INSUFFICIENT_DATA` before conflict checks. - [x] **Step 4: Implement conflict classification** For overlapping records: 1. Same `agv_id` → `SAME_VEHICLE_OVERLAP`. 2. Reverse adjacent node order on a shared normalized edge → `POTENTIAL_OPPOSITE_EDGE`. 3. Shared node → `POTENTIAL_NODE_CONFLICT`. 4. No resource intersection → `NO_CONFLICT`. The result stores both original record IDs, shared resource keys, inferred interval, category, and Chinese explanation. - [x] **Step 5: Implement deterministic replay decision** Sort candidates once. Maintain an in-memory `ResourceLedger` with node and edge capacity 1. Replayable candidates become: ```text REPLAY_GRANT when every projected resource fits REPLAY_HOLD when a projected resource is occupied in the inferred window INSUFFICIENT_DATA when normalization blocked replay ``` Do not synthesize YIELD because no signed safe waiting points exist. - [x] **Step 6: Run analyzer tests and confirm GREEN** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest test ``` Expected: all analyzer tests pass. - [x] **Step 7: Commit** ```bash git add javaTest/src/main/java/com/ft546/wcs/shadow/history javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalReplayAnalyzerTest.java git commit -m "feat: replay inferred historical traffic windows" ``` --- ### Task 5: Execute the Real Database Snapshot Test Matrix **Files:** - Create: `javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueDatabaseReplayTest.java` **Interfaces:** - Consumes: `HistoricalQueueLoader`, `HistoricalQueueNormalizer`, `HistoricalReplayAnalyzer`. - Produces: Surefire-counted parameterized tests using original task and vehicle IDs. - [x] **Step 1: Write the real-data parameterized test** ```java @ParameterizedTest(name = "{index} task={0} agv={1} {2}->{3}") @MethodSource("realHistoryCases") void replaysRealHistoryRecord(String taskNo, String agvId, String startSite, String endSite, HistoricalPath path) { assertFalse(taskNo.trim().isEmpty()); assertFalse(agvId.trim().isEmpty()); assertNotNull(path); if (path.isReplayable()) { assertFalse(path.getNodes().isEmpty()); assertFalse(path.getResources().isEmpty()); } else { assertFalse(path.getIssues().isEmpty()); } } ``` `realHistoryCases()` must expose every snapshot row, so Surefire counts at least 200 independent real-data cases. - [x] **Step 2: Add aggregate acceptance tests** Assert: ```java assertTrue(dataset.getRecords().size() >= 200); assertTrue(dataset.getRecords().stream().map(HistoricalQueueRecord::getAgvId).distinct().count() >= 3); assertTrue(dataset.getRecords().stream().map(HistoricalQueueRecord::getTaskType).distinct().count() >= 3); assertEquals(firstReplay, secondReplay); assertEquals(17136, dataset.getFullScanTotalRows()); ``` If the fresh extraction count differs from 17,136, use the manifest's exact count and assert it matches `full-scan-summary.json`; do not hardcode a stale number. - [x] **Step 3: Run real-data tests and confirm GREEN** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalQueueDatabaseReplayTest test ``` Expected: at least 200 parameterized cases plus aggregate checks; failures and errors are 0. - [x] **Step 4: Run the existing synthetic safety matrix** ```bash mvn -f javaTest/pom.xml -Dtest=ShadowSimulationDataTest test ``` Expected: 60 tests, failures 0, errors 0. - [x] **Step 5: Commit** ```bash git add javaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueDatabaseReplayTest.java git commit -m "test: replay real FT546 queue history" ``` --- ### Task 6: Generate Machine-Readable and Chinese-Readable Evidence **Files:** - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayReportWriter.java` - Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayApplication.java` - Modify: `javaTest/README.md` **Interfaces:** - Produces: `void HistoricalReplayReportWriter.write(Path outputDir, HistoricalQueueDataset dataset, List paths, List results)`. - CLI usage: `HistoricalReplayApplication `. - [x] **Step 1: Write a failing writer test inside `HistoricalReplayAnalyzerTest`** ```java @TempDir Path temp; @Test void writesReadableEvidenceWithOriginalIdsAndBoundaryText() throws Exception { writer.write(temp, dataset, paths, results); String csv = new String(Files.readAllBytes(temp.resolve("replay-results.csv")), StandardCharsets.UTF_8); String summary = new String(Files.readAllBytes(temp.resolve("summary.json")), StandardCharsets.UTF_8); assertTrue(csv.contains(dataset.getRecords().get(0).getTaskNo())); assertTrue(csv.contains(dataset.getRecords().get(0).getAgvId())); assertTrue(summary.contains("真实历史队列离线回放")); assertTrue(summary.contains("不是现场 SHADOW 验收")); } ``` - [x] **Step 2: Run the writer test and confirm RED** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest#writesReadableEvidenceWithOriginalIdsAndBoundaryText test ``` Expected: compilation failure because writer does not exist. - [x] **Step 3: Implement CSV and JSON evidence** `replay-results.csv` columns: ```text 创建时间,完成时间,车辆,任务号,起点,终点,历史状态,路径, 同期车辆,同期任务,共享资源,SHADOW推演,分类,中文原因,数据口径 ``` `data-quality-issues.csv` columns: ```text GUID,车辆,任务号,问题类型,原始路径,是否影响回放,处理方式 ``` CSV must use RFC 4180 quoting and UTF-8 BOM so Excel opens Chinese text correctly. - [x] **Step 4: Implement summary and SHA manifest** `summary.json` includes: ```text database/table/extractedAt/sourceRowCount/sampleRowCount/replayableRows min/max timestamps/vehicles/taskTypes/mapSites qualityIssueCounts/categoryCounts/replayGrant/replayHold deterministic=true/mode=REAL_HISTORY_OFFLINE_REPLAY boundary="不是现场 SHADOW 验收" ``` `manifest.sha256` covers every generated file except itself, sorted by filename. - [x] **Step 5: Implement CLI** ```java public static void main(String[] args) throws Exception { if (args.length != 2) { throw new IllegalArgumentException( "usage: HistoricalReplayApplication "); } // load, normalize, analyze twice, verify equality, write } ``` - [x] **Step 6: Document the workflow** Add to `javaTest/README.md`: ```bash mvn clean test mvn exec:java \ -Dexec.mainClass=com.ft546.wcs.shadow.history.HistoricalReplayApplication \ -Dexec.args="../outputs/wcs-shadow-history-replay-local/source ../outputs/wcs-shadow-history-replay-local" ``` Explain original IDs are retained and the report distinguishes database facts from inference. - [x] **Step 7: Run writer tests and CLI** ```bash mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest test mvn -f javaTest/pom.xml exec:java \ -Dexec.mainClass=com.ft546.wcs.shadow.history.HistoricalReplayApplication \ -Dexec.args="../outputs/wcs-shadow-history-replay-local/source ../outputs/wcs-shadow-history-replay-local" ``` Expected: evidence files exist, hashes verify, and CLI exits 0. - [x] **Step 8: Commit** ```bash git add javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayReportWriter.java javaTest/src/main/java/com/ft546/wcs/shadow/history/HistoricalReplayApplication.java javaTest/README.md git commit -m "feat: report real history replay evidence" ``` --- ### Task 7: Create and Verify the Readable Excel Workbook **Files:** - Generate: `outputs/wcs-shadow-history-replay-/readable-report.xlsx` **Interfaces:** - Consumes: verified `summary.json`, `replay-results.csv`, `data-quality-issues.csv`. - Produces: one workbook with sheets `测试总览`, `真实任务明细`, `潜在冲突`, `数据问题`, `来源与口径`. - [x] **Step 1: Run a fresh full Maven verification** ```bash mvn -f javaTest/pom.xml clean test ``` Expected: build success, every original test plus at least 200 real-history parameterized cases, failures 0, errors 0. - [x] **Step 2: Generate fresh evidence** Use a run ID containing the current date/time and Git short SHA. Never overwrite an earlier run. - [x] **Step 3: Build the workbook with `@oai/artifact-tool`** Workbook requirements: - KPI cards: source rows, sampled rows, replayable rows, quality issues, potential conflicts. - Original IDs and full paths preserved in detail. - Filters, frozen header rows, wrapped Chinese explanations. - Formula-driven category counts and pass/replay totals. - One compact category-distribution chart. - Amber boundary banner: “真实历史队列离线回放,不是现场 SHADOW 验收”. - [x] **Step 4: Verify workbook values and formulas** Inspect: ```text 测试总览!A1:O30 真实任务明细!A1:O20 来源与口径!A1:D20 ``` Scan for: ```text #REF! #DIV/0! #VALUE! #NAME? #N/A ``` Expected: no formula errors; KPI counts reconcile to JSON/CSV. - [x] **Step 5: Render and visually inspect every sheet** Check Chinese text, task numbers, vehicle IDs, full paths, chart, wrapped reason text, and boundary banner. Fix clipping before export. - [x] **Step 6: Verify manifest** Recalculate SHA-256 for all run-directory evidence except `manifest.sha256` and compare line-by-line. - [x] **Step 7: Commit implementation and tests** Do not commit generated `outputs/` evidence unless repository policy explicitly tracks evidence. Confirm: ```bash git status --short git log -7 --oneline ``` Expected: source/resources/docs changes committed; generated output remains an external artifact. --- ## Final Verification Checklist - [x] SQL operations were read-only. - [x] Full database aggregate scan row count is captured. - [x] Snapshot contains at least 200 original records and 3 original AGV IDs. - [x] Original task, vehicle, site, and path values are visible. - [x] Missing/unknown data is fail-closed. - [x] Replay is deterministic. - [x] Existing 60 synthetic safety cases still pass. - [x] Full Maven suite passes with failures/errors 0. - [x] Excel totals reconcile to machine-readable evidence. - [x] Report clearly states it is not现场 SHADOW/HIL/ACTIVE evidence. - [x] `ruoyi-*` production code is unchanged.