fix(sql): remove colon prefix from SQL comments to prevent bind param errors, archive trace-progressive-ui

SQLAlchemy text() parses :param patterns in SQL comments as bind
parameters. When EventFetcher replaces the WHERE clause via string
substitution, orphaned :container_id in comments causes
"A value is required for bind parameter 'container_id'" errors.

Changes:
- Remove colon prefix from parameter names in SQL comments for
  lot_history, lot_rejects, lot_holds, lot_materials
- Archive trace-progressive-ui change (22/22 tasks complete)
- Sync delta specs to main: add trace-staged-api, progressive-trace-ux,
  merge api-safety-hygiene (+2 requirements)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
egg
2026-02-12 16:53:54 +08:00
parent 519f8ae2f4
commit 5a47bc87d8
14 changed files with 182 additions and 5 deletions

View File

@@ -103,3 +103,27 @@ Route and service test files SHALL exist and cover core behaviors.
- **WHEN** pytest discovers tests
- **THEN** `tests/test_mid_section_defect_service.py` SHALL contain tests for date validation, pagination logic, and loss reasons caching
### Requirement: Staged trace API endpoints SHALL apply rate limiting
The `/api/trace/seed-resolve`, `/api/trace/lineage`, and `/api/trace/events` endpoints SHALL apply per-client rate limiting using the existing `configured_rate_limit` mechanism.
#### Scenario: Seed-resolve rate limit exceeded
- **WHEN** a client sends more than 10 requests to `/api/trace/seed-resolve` within 60 seconds
- **THEN** the endpoint SHALL return HTTP 429 with a `Retry-After` header
#### Scenario: Lineage rate limit exceeded
- **WHEN** a client sends more than 10 requests to `/api/trace/lineage` within 60 seconds
- **THEN** the endpoint SHALL return HTTP 429 with a `Retry-After` header
#### Scenario: Events rate limit exceeded
- **WHEN** a client sends more than 15 requests to `/api/trace/events` within 60 seconds
- **THEN** the endpoint SHALL return HTTP 429 with a `Retry-After` header
### Requirement: Mid-section defect analysis endpoint SHALL internally use staged pipeline
The existing `/api/mid-section-defect/analysis` endpoint SHALL internally delegate to the staged trace pipeline while maintaining full backward compatibility.
#### Scenario: Analysis endpoint backward compatibility
- **WHEN** a client calls `GET /api/mid-section-defect/analysis` with existing query parameters
- **THEN** the response JSON structure SHALL be identical to pre-refactoring output
- **THEN** existing rate limiting (6/min analysis, 15/min detail, 3/min export) SHALL remain unchanged
- **THEN** existing distributed lock behavior SHALL remain unchanged

View File

@@ -0,0 +1,64 @@
## ADDED Requirements
### Requirement: useTraceProgress composable SHALL orchestrate staged fetching with reactive state
`useTraceProgress` SHALL provide a shared composable for sequential stage fetching with per-stage reactive state updates.
#### Scenario: Normal three-stage fetch sequence
- **WHEN** `useTraceProgress` is invoked with profile and params
- **THEN** it SHALL execute seed-resolve → lineage → events sequentially
- **THEN** after each stage completes, `current_stage` and `completed_stages` reactive refs SHALL update immediately
- **THEN** `stage_results` SHALL accumulate results from completed stages
#### Scenario: Stage failure does not block completed results
- **WHEN** the lineage stage fails after seed-resolve has completed
- **THEN** seed-resolve results SHALL remain visible and accessible
- **THEN** the error SHALL be captured in stage-specific error state
- **THEN** subsequent stages (events) SHALL NOT execute
### Requirement: mid-section-defect SHALL render progressively as stages complete
The mid-section-defect page SHALL display partial results as each trace stage completes.
#### Scenario: Seed lots visible before lineage completes
- **WHEN** seed-resolve stage completes (≤3s for ≥10 seed lots)
- **THEN** the seed lots list SHALL be rendered immediately
- **THEN** lineage and events sections SHALL show skeleton placeholders
#### Scenario: KPI/charts visible after events complete
- **WHEN** lineage and events stages complete
- **THEN** KPI cards and charts SHALL render with fade-in animation
- **THEN** no layout shift SHALL occur (skeleton placeholders SHALL have matching dimensions)
#### Scenario: Detail table pagination unchanged
- **WHEN** the user requests detail data
- **THEN** the existing detail endpoint with pagination SHALL be used (not the staged API)
### Requirement: query-tool lineage tab SHALL load on-demand
The query-tool lineage tab SHALL load lineage data for individual lots on user interaction, not batch-load all lots.
#### Scenario: User clicks a lot to view lineage
- **WHEN** the user clicks a lot card to expand lineage information
- **THEN** lineage SHALL be fetched via `/api/trace/lineage` for that single lot's container IDs
- **THEN** response time SHALL be ≤3s for the individual lot
#### Scenario: Multiple lots expanded
- **WHEN** the user expands lineage for multiple lots
- **THEN** each lot's lineage SHALL be fetched independently (not batch)
- **THEN** already-fetched lineage data SHALL be preserved (not re-fetched)
### Requirement: Both pages SHALL display a stage progress indicator
Both mid-section-defect and query-tool SHALL display a progress indicator showing the current trace stage.
#### Scenario: Progress indicator during staged fetch
- **WHEN** a trace query is in progress
- **THEN** a progress indicator SHALL display the current stage (seed → lineage → events)
- **THEN** completed stages SHALL be visually distinct from pending/active stages
- **THEN** the indicator SHALL replace the existing single loading spinner
### Requirement: Legacy static query-tool.js SHALL be removed
The pre-Vite static file `src/mes_dashboard/static/js/query-tool.js` (3056L, 126KB) SHALL be deleted as dead code.
#### Scenario: Dead code removal verification
- **WHEN** `static/js/query-tool.js` is deleted
- **THEN** grep for `static/js/query-tool.js` SHALL return zero results across the codebase
- **THEN** `query_tool.html` template SHALL continue to function via `frontend_asset('query-tool.js')` which resolves to the Vite-built bundle
- **THEN** `frontend/src/query-tool/main.js` (Vue 3 Vite entry) SHALL remain unaffected

View File

@@ -0,0 +1,89 @@
## ADDED Requirements
### Requirement: Staged trace API SHALL expose seed-resolve endpoint
`POST /api/trace/seed-resolve` SHALL resolve seed lots based on the provided profile and parameters.
#### Scenario: query_tool profile seed resolve
- **WHEN** request body contains `{ "profile": "query_tool", "params": { "resolve_type": "lot_id", "values": [...] } }`
- **THEN** the endpoint SHALL call existing lot resolve logic and return `{ "stage": "seed-resolve", "seeds": [...], "seed_count": N, "cache_key": "trace:{hash}" }`
- **THEN** each seed object SHALL contain `container_id`, `container_name`, and `lot_id`
#### Scenario: mid_section_defect profile seed resolve
- **WHEN** request body contains `{ "profile": "mid_section_defect", "params": { "date_range": [...], "workcenter": "..." } }`
- **THEN** the endpoint SHALL call TMTT detection logic and return seed lots in the same response format
#### Scenario: Empty seed result
- **WHEN** seed resolution finds no matching lots
- **THEN** the endpoint SHALL return HTTP 200 with `{ "stage": "seed-resolve", "seeds": [], "seed_count": 0, "cache_key": "trace:{hash}" }`
- **THEN** the error code `SEED_RESOLVE_EMPTY` SHALL NOT be used for empty results (reserved for resolution failures)
#### Scenario: Invalid profile
- **WHEN** request body contains an unrecognized `profile` value
- **THEN** the endpoint SHALL return HTTP 400 with `{ "error": "...", "code": "INVALID_PROFILE" }`
### Requirement: Staged trace API SHALL expose lineage endpoint
`POST /api/trace/lineage` SHALL resolve lineage graph for provided container IDs using `LineageEngine`.
#### Scenario: Normal lineage resolution
- **WHEN** request body contains `{ "profile": "query_tool", "container_ids": [...] }`
- **THEN** the endpoint SHALL call `LineageEngine.resolve_full_genealogy()` and return `{ "stage": "lineage", "ancestors": {...}, "merges": {...}, "total_nodes": N }`
#### Scenario: Lineage result caching with idempotency
- **WHEN** two requests with the same `container_ids` set (regardless of order) arrive
- **THEN** the cache key SHALL be computed as `trace:lineage:{sorted_cids_hash}`
- **THEN** the second request SHALL return cached result from L2 Redis (TTL = 300s)
#### Scenario: Lineage timeout
- **WHEN** lineage resolution exceeds 10 seconds
- **THEN** the endpoint SHALL return HTTP 504 with `{ "error": "...", "code": "LINEAGE_TIMEOUT" }`
### Requirement: Staged trace API SHALL expose events endpoint
`POST /api/trace/events` SHALL query events for specified domains using `EventFetcher`.
#### Scenario: Normal events query
- **WHEN** request body contains `{ "profile": "query_tool", "container_ids": [...], "domains": ["history", "materials"] }`
- **THEN** the endpoint SHALL return `{ "stage": "events", "results": { "history": { "data": [...], "count": N }, "materials": { "data": [...], "count": N } }, "aggregation": null }`
#### Scenario: mid_section_defect profile with aggregation
- **WHEN** request body contains `{ "profile": "mid_section_defect", "container_ids": [...], "domains": ["upstream_history"] }`
- **THEN** the endpoint SHALL automatically run aggregation logic after event fetching
- **THEN** the response `aggregation` field SHALL contain the aggregated results (not null)
#### Scenario: Partial domain failure
- **WHEN** one domain query fails while others succeed
- **THEN** the endpoint SHALL return HTTP 200 with `{ "error": "...", "code": "EVENTS_PARTIAL_FAILURE" }`
- **THEN** the response SHALL include successfully fetched domains in `results` and list failed domains in `failed_domains`
### Requirement: All staged trace endpoints SHALL apply rate limiting and caching
Every `/api/trace/*` endpoint SHALL use `configured_rate_limit()` and L2 Redis caching.
#### Scenario: Rate limit exceeded on any trace endpoint
- **WHEN** a client exceeds the configured request budget for a trace endpoint
- **THEN** the endpoint SHALL return HTTP 429 with a `Retry-After` header
- **THEN** the body SHALL contain `{ "error": "...", "meta": { "retry_after_seconds": N } }`
#### Scenario: Cache hit on trace endpoint
- **WHEN** a request matches a cached result in L2 Redis (TTL = 300s)
- **THEN** the cached result SHALL be returned without executing backend logic
- **THEN** Oracle DB connection pool SHALL NOT be consumed
### Requirement: cache_key parameter SHALL be used for logging correlation only
The optional `cache_key` field in request bodies SHALL be used solely for logging and tracing correlation.
#### Scenario: cache_key provided in request
- **WHEN** a request includes `cache_key` from a previous stage response
- **THEN** the value SHALL be logged for correlation purposes
- **THEN** the value SHALL NOT influence cache lookup or rate limiting logic
#### Scenario: cache_key omitted in request
- **WHEN** a request omits the `cache_key` field
- **THEN** the endpoint SHALL function normally without any degradation
### Requirement: Existing `GET /api/mid-section-defect/analysis` SHALL remain compatible
The existing analysis endpoint (GET method) SHALL internally delegate to the staged pipeline while maintaining identical external behavior.
#### Scenario: Legacy analysis endpoint invocation
- **WHEN** a client calls `GET /api/mid-section-defect/analysis` with existing query parameters
- **THEN** the endpoint SHALL internally execute seed-resolve → lineage → events + aggregation
- **THEN** the response format SHALL be identical to the pre-refactoring output
- **THEN** a golden test SHALL verify output equivalence

View File

@@ -2,8 +2,8 @@
-- Retrieves complete production history for a LOT
--
-- Parameters:
-- :container_id - CONTAINERID to query (16-char hex)
-- {{ WORKCENTER_FILTER }} - Optional workcenter name filter (replaced by service)
-- container_id - CONTAINERID to query (16-char hex)
-- WORKCENTER_FILTER - Optional workcenter name filter (replaced by service)
--
-- Output columns:
-- PJ_TYPE - Product type (from DW_MES_CONTAINER)

View File

@@ -2,7 +2,7 @@
-- Retrieves HOLD/RELEASE history for a LOT
--
-- Parameters:
-- :container_id - CONTAINERID to query (16-char hex)
-- container_id - CONTAINERID to query (16-char hex)
--
-- Note: Uses HOLDTXNDATE/RELEASETXNDATE (NOT TXNDATETIME)
-- NULL RELEASETXNDATE means currently HOLD

View File

@@ -2,7 +2,7 @@
-- Retrieves material consumption records for a LOT
--
-- Parameters:
-- :container_id - CONTAINERID to query (16-char hex)
-- container_id - CONTAINERID to query (16-char hex)
--
-- Note: Uses MATERIALPARTNAME (NOT MATERIALNAME)
-- Uses QTYCONSUMED (NOT CONSUMEQTY)

View File

@@ -2,7 +2,7 @@
-- Retrieves reject (defect) records for a LOT
--
-- Parameters:
-- :container_id - CONTAINERID to query (16-char hex)
-- container_id - CONTAINERID to query (16-char hex)
--
-- Note: Uses LOSSREASONNAME (NOT REJECTREASONNAME)
-- Uses TXNDATE (NOT TXNDATETIME)