27 KiB
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_datemay be null; inferred activity interval is[create_date, finish_date).- Empty or unknown paths produce
INSUFFICIENT_DATA, never a syntheticGRANT. - 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.javajavaTest/src/test/java/com/ft546/wcs/shadow/HistoricalQueueNormalizerTest.javajavaTest/src/test/java/com/ft546/wcs/shadow/HistoricalReplayAnalyzerTest.javajavaTest/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-<runId>/source/queue-history-full.jsonoutputs/wcs-shadow-history-replay-<runId>/summary.jsonoutputs/wcs-shadow-history-replay-<runId>/replay-results.csvoutputs/wcs-shadow-history-replay-<runId>/data-quality-issues.csvoutputs/wcs-shadow-history-replay-<runId>/readable-report.xlsxoutputs/wcs-shadow-history-replay-<runId>/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. -
Step 1: Run the complete-history aggregate scan
Use the SQL Server read-only tool with:
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.
- Step 2: Select a deterministic representative sample
Use one CTE query. The final ORDER BY is mandatory for reproducibility:
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.
- 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:
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-<runId>/source/queue-history-full.json; do not
route the complete payload through chat text. The final row count must equal the
aggregate scan exactly.
- 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:
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.
- Step 5: Write snapshot files and hashes
Write JSON with UTF-8, stable property order, and two-space indentation. Compute:
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.
- Step 6: Verify extraction evidence
Run:
grep -RniE "password|token|jdbc:|server=" javaTest/src/test/resources/shadow/history
Expected: no matches.
- Step 7: Commit only the bounded immutable test snapshot
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 unmodifiableList<HistoricalQueueRecord>. -
HistoricalQueueDataset.getMapSites()returns an unmodifiableSet<String>. -
Step 1: Write failing loader tests
@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)));
}
- Step 2: Run loader tests and confirm RED
mvn -f javaTest/pom.xml -Dtest=HistoricalQueueLoaderTest test
Expected: compilation failure because history classes do not exist.
- 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:
this.records = Collections.unmodifiableList(new ArrayList<HistoricalQueueRecord>(records));
this.mapSites = Collections.unmodifiableSet(new LinkedHashSet<String>(mapSites));
- Step 4: Implement loader and SHA validation
The loader reads the four classpath resources, verifies every hash before deserializing, and checks:
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");
}
- Step 5: Run loader tests and confirm GREEN
mvn -f javaTest/pom.xml -Dtest=HistoricalQueueLoaderTest test
Expected: all loader tests pass.
- Step 6: Commit
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<String> mapSites). -
HistoricalPathexposesgetNodes(),getResources(),getCreatedAt(),getFinishedAt(),getIssues(), andisReplayable(). -
Step 1: Write failing normalization tests
@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"));
}
- Step 2: Run normalization tests and confirm RED
mvn -f javaTest/pom.xml -Dtest=HistoricalQueueNormalizerTest test
Expected: compilation failure because normalizer classes do not exist.
- Step 3: Implement strict timestamp parsing
Use:
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.
- Step 4: Implement path normalization and resource projection
Rules:
trim tokens
drop empty trailing tokens
keep original order
flag adjacent duplicate as ADJACENT_DUPLICATE_NODE:<site>
flag missing map site as UNKNOWN_MAP_SITE:<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.
- Step 5: Run tests and confirm GREEN
mvn -f javaTest/pom.xml -Dtest=HistoricalQueueNormalizerTest test
Expected: all normalization tests pass.
- Step 6: Commit
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<HistoricalReplayResult> HistoricalReplayAnalyzer.analyze(List<HistoricalPath> paths). -
Produces:
boolean HistoricalReplayAnalyzer.overlaps(HistoricalPath left, HistoricalPath right). -
Stable ordering: priority ascending,
createdAt, task number, AGV ID, GUID. -
Step 1: Write failing overlap and conflict tests
@Test
void touchingIntervalsDoNotOverlap() {
assertFalse(analyzer.overlaps(path("10:00:00", "10:01:00"),
path("10:01:00", "10:02:00")));
}
@Test
void detectsOppositeUseOfSameUndirectedEdge() {
List<HistoricalReplayResult> 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<HistoricalReplayResult> first = analyzer.analyze(sample);
List<HistoricalReplayResult> second = analyzer.analyze(sample);
assertEquals(first, second);
}
- Step 2: Run analyzer tests and confirm RED
mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest test
Expected: compilation failure because analyzer types do not exist.
- Step 3: Implement half-open overlap semantics
return left.getCreatedAt().isBefore(right.getFinishedAt())
&& right.getCreatedAt().isBefore(left.getFinishedAt());
Non-replayable paths return INSUFFICIENT_DATA before conflict checks.
- Step 4: Implement conflict classification
For overlapping records:
- Same
agv_id→SAME_VEHICLE_OVERLAP. - Reverse adjacent node order on a shared normalized edge →
POTENTIAL_OPPOSITE_EDGE. - Shared node →
POTENTIAL_NODE_CONFLICT. - No resource intersection →
NO_CONFLICT.
The result stores both original record IDs, shared resource keys, inferred interval, category, and Chinese explanation.
- Step 5: Implement deterministic replay decision
Sort candidates once. Maintain an in-memory ResourceLedger with node and edge capacity 1. Replayable candidates become:
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.
- Step 6: Run analyzer tests and confirm GREEN
mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest test
Expected: all analyzer tests pass.
- Step 7: Commit
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.
-
Step 1: Write the real-data parameterized test
@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.
- Step 2: Add aggregate acceptance tests
Assert:
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.
- Step 3: Run real-data tests and confirm GREEN
mvn -f javaTest/pom.xml -Dtest=HistoricalQueueDatabaseReplayTest test
Expected: at least 200 parameterized cases plus aggregate checks; failures and errors are 0.
- Step 4: Run the existing synthetic safety matrix
mvn -f javaTest/pom.xml -Dtest=ShadowSimulationDataTest test
Expected: 60 tests, failures 0, errors 0.
- Step 5: Commit
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<HistoricalPath> paths, List<HistoricalReplayResult> results). -
CLI usage:
HistoricalReplayApplication <snapshotDir> <outputDir>. -
Step 1: Write a failing writer test inside
HistoricalReplayAnalyzerTest
@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 验收"));
}
- Step 2: Run the writer test and confirm RED
mvn -f javaTest/pom.xml -Dtest=HistoricalReplayAnalyzerTest#writesReadableEvidenceWithOriginalIdsAndBoundaryText test
Expected: compilation failure because writer does not exist.
- Step 3: Implement CSV and JSON evidence
replay-results.csv columns:
创建时间,完成时间,车辆,任务号,起点,终点,历史状态,路径,
同期车辆,同期任务,共享资源,SHADOW推演,分类,中文原因,数据口径
data-quality-issues.csv columns:
GUID,车辆,任务号,问题类型,原始路径,是否影响回放,处理方式
CSV must use RFC 4180 quoting and UTF-8 BOM so Excel opens Chinese text correctly.
- Step 4: Implement summary and SHA manifest
summary.json includes:
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.
- Step 5: Implement CLI
public static void main(String[] args) throws Exception {
if (args.length != 2) {
throw new IllegalArgumentException(
"usage: HistoricalReplayApplication <snapshotDir> <outputDir>");
}
// load, normalize, analyze twice, verify equality, write
}
- Step 6: Document the workflow
Add to javaTest/README.md:
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.
- Step 7: Run writer tests and CLI
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.
- Step 8: Commit
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-<runId>/readable-report.xlsx
Interfaces:
-
Consumes: verified
summary.json,replay-results.csv,data-quality-issues.csv. -
Produces: one workbook with sheets
测试总览,真实任务明细,潜在冲突,数据问题,来源与口径. -
Step 1: Run a fresh full Maven verification
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.
- Step 2: Generate fresh evidence
Use a run ID containing the current date/time and Git short SHA. Never overwrite an earlier run.
- 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 验收”.
-
Step 4: Verify workbook values and formulas
Inspect:
测试总览!A1:O30
真实任务明细!A1:O20
来源与口径!A1:D20
Scan for:
#REF! #DIV/0! #VALUE! #NAME? #N/A
Expected: no formula errors; KPI counts reconcile to JSON/CSV.
- 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.
- Step 6: Verify manifest
Recalculate SHA-256 for all run-directory evidence except manifest.sha256 and compare line-by-line.
- Step 7: Commit implementation and tests
Do not commit generated outputs/ evidence unless repository policy explicitly tracks evidence. Confirm:
git status --short
git log -7 --oneline
Expected: source/resources/docs changes committed; generated output remains an external artifact.
Final Verification Checklist
- SQL operations were read-only.
- Full database aggregate scan row count is captured.
- Snapshot contains at least 200 original records and 3 original AGV IDs.
- Original task, vehicle, site, and path values are visible.
- Missing/unknown data is fail-closed.
- Replay is deterministic.
- Existing 60 synthetic safety cases still pass.
- Full Maven suite passes with failures/errors 0.
- Excel totals reconcile to machine-readable evidence.
- Report clearly states it is not现场 SHADOW/HIL/ACTIVE evidence.
ruoyi-*production code is unchanged.