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:
@@ -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
|
||||
|
||||
|
||||
64
openspec/specs/progressive-trace-ux/spec.md
Normal file
64
openspec/specs/progressive-trace-ux/spec.md
Normal 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
|
||||
89
openspec/specs/trace-staged-api/spec.md
Normal file
89
openspec/specs/trace-staged-api/spec.md
Normal 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
|
||||
Reference in New Issue
Block a user