fix(query-tool): batch detail loading, UX polish, and docs cleanup
- Fix multi-WO display: auto-select all tree roots after resolve so detail panel loads data for every work order, not just the first seed CID - Disable scroll-wheel zoom on lineage tree (roam: 'move') to prevent accidental layout jumps while preserving drag-pan - Add batch API endpoints (get_lot_history_batch, get_lot_associations_batch) to avoid N parallel requests hitting rate limits - Remove redundant Split sub-tab from LOT detail (tree already shows splits) - Rename 退貨 → 報廢 to match actual reject/scrap data semantics - Hide internal ID columns (CONTAINERID, EQUIPMENTID, RESOURCEID) from history table display - Add timeline scroll container and time range header for long timelines - Remove obsolete migration and architecture docs no longer needed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,60 +0,0 @@
|
|||||||
SELECT L.LOTID AS ""Run Card Lot ID"",
|
|
||||||
L.Workorder AS ""Work Order ID"",
|
|
||||||
L.Qty AS ""Lot Qty(pcs)"",
|
|
||||||
L.Qty2 AS ""Lot Qty(Wafer pcs)"",
|
|
||||||
L.Status AS ""Run Card Status"",
|
|
||||||
L.HOLDREASONNAME AS ""Hold Reason"",
|
|
||||||
L.CurrentHoldCount AS ""Hold Count"",
|
|
||||||
L.Owner AS ""Work Order Owner"",
|
|
||||||
L.StartDate AS ""Run Card Start Date"",
|
|
||||||
L.UTS,
|
|
||||||
L.Product AS ""Product P/N"",
|
|
||||||
L.Productlinename AS ""Package"",
|
|
||||||
L.Package_LEF as ""Package(LF)"",
|
|
||||||
L.PJ_FUNCTION AS ""Product Function"",
|
|
||||||
L.Pj_Type AS ""Product Type"",
|
|
||||||
L.BOP,
|
|
||||||
L.FirstName AS ""Wafer Lot ID"",
|
|
||||||
L.WAFERNAME AS ""Wafer P/N"",
|
|
||||||
L.WaferLot ""Wafer Lot ID(Prefix)"",
|
|
||||||
L.SpecName AS ""Spec"",
|
|
||||||
L.SPECSEQUENCE AS ""Spec Sequence"",
|
|
||||||
L.SPECSEQUENCE || '_' || L.SpecName AS ""Spec(Order)"",
|
|
||||||
L.Workcentername AS ""Work Center"",
|
|
||||||
L.WorkCenterSequence AS ""Work Center Sequence"",
|
|
||||||
L.WorkCenter_Group AS ""Work Center(Group)"",
|
|
||||||
L.WorkCenter_Short AS ""Work Center(Short)"",
|
|
||||||
L.WorkCenterSequence_Group AS ""Work Center Sequence(Group)"",
|
|
||||||
L.WorkCenterSequence_Group || '_' || L.WorkCenter_Group AS ""Work Center Group(Order)"",
|
|
||||||
L.AgeByDays AS ""Age By Days"",
|
|
||||||
L.Equipments AS ""Equipment ID"",
|
|
||||||
L.EquipmentCount AS ""Equipment Count"",
|
|
||||||
L.Workflowname AS ""Work Flow Name"",
|
|
||||||
L.Datecode AS ""Product Date Code"",
|
|
||||||
L.LEADFRAMENAME AS ""LF Material Part"",
|
|
||||||
L.LEADFRAMEOPTION AS ""LF Option ID"",
|
|
||||||
L.COMNAME AS ""Compound Material Part"",
|
|
||||||
L.LOCATIONNAME AS ""Run Card Location"",
|
|
||||||
L.Eventname AS ""NCR ID"",
|
|
||||||
L.Occurrencedate AS ""NCR-issued Time"",
|
|
||||||
L.ReleaseTime AS ""Release Time"",
|
|
||||||
L.ReleaseEmp AS ""Release Employee"",
|
|
||||||
L.ReleaseReason AS ""Release Comment"",
|
|
||||||
L.COMMENT_HOLD AS ""Hold Comment"",
|
|
||||||
L.CONTAINERCOMMENTS AS ""Comment"",
|
|
||||||
L.COMMENT_DATE AS ""Run Card Comment"",
|
|
||||||
L.COMMENT_EMP AS ""Run Card Comment Employee"",
|
|
||||||
L.COMMENT_FUTURE AS ""Future Hold Comment"",
|
|
||||||
L.HOLDEMP AS ""Hold Employee"",
|
|
||||||
L.DEPTNAME AS ""Hold Employee Dept"",
|
|
||||||
L.PJ_PRODUCEREGION AS ""Produce Region"",
|
|
||||||
L.Prioritycodename AS ""Work Order Priority"",
|
|
||||||
L.TMTT_R AS ""TMTT Remaining"",
|
|
||||||
L.wafer_factor AS ""Die Consumption Qty"",
|
|
||||||
Case When (L.EquipmentCount>0) Then 'RUN'
|
|
||||||
When (L.CurrentHoldCount>0) Then 'HOLD'
|
|
||||||
ELSE 'QUENE' End AS ""WIP Status"",
|
|
||||||
Case When (L.EquipmentCount>0) Then 1
|
|
||||||
When (L.CurrentHoldCount>0) Then 3
|
|
||||||
ELSE 2 End AS ""WIP Status Sequence"",
|
|
||||||
sys_date AS ""Data Update Date""
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
|||||||
# Oracle 可使用 TABLE/VIEW 清單(DWH)
|
# Oracle 可使用 TABLE/VIEW 清單(DWH)
|
||||||
|
|
||||||
**產生時間**: 2026-01-29 13:34:22
|
**產生時間**: 2026-02-13 17:39:41
|
||||||
**使用者**: (詳見 .env 中的 DB_USER)
|
**使用者**: MBU1_R
|
||||||
**Schema**: DWH
|
**Schema**: DWH
|
||||||
|
|
||||||
## 摘要
|
## 摘要
|
||||||
|
|
||||||
- 可使用物件總數: 19
|
- 可使用物件總數: 22
|
||||||
- TABLE: 16
|
- TABLE: 19
|
||||||
- VIEW: 3
|
- VIEW: 3
|
||||||
- 來源 (去重後物件數): DIRECT 19, PUBLIC 0, ROLE 0, SYSTEM 0
|
- 來源 (去重後物件數): DIRECT 22, PUBLIC 0, ROLE 0, SYSTEM 0
|
||||||
|
|
||||||
## 物件清單
|
## 物件清單
|
||||||
|
|
||||||
@@ -33,4 +33,7 @@
|
|||||||
| `DWH.DW_MES_RESOURCESTATUS` | TABLE | SELECT | DIRECT |
|
| `DWH.DW_MES_RESOURCESTATUS` | TABLE | SELECT | DIRECT |
|
||||||
| `DWH.DW_MES_RESOURCESTATUS_SHIFT` | TABLE | SELECT | DIRECT |
|
| `DWH.DW_MES_RESOURCESTATUS_SHIFT` | TABLE | SELECT | DIRECT |
|
||||||
| `DWH.DW_MES_SPEC_WORKCENTER_V` | VIEW | SELECT | DIRECT |
|
| `DWH.DW_MES_SPEC_WORKCENTER_V` | VIEW | SELECT | DIRECT |
|
||||||
| `DWH.DW_MES_WIP` | TABLE | SELECT | DIRECT |
|
| `DWH.DW_MES_WIP` | TABLE | SELECT | DIRECT |
|
||||||
|
| `DWH.ERP_PJ_WIP_SCRAP_REASONS_EXCLUDE` | TABLE | SELECT | DIRECT |
|
||||||
|
| `DWH.ERP_WIP_MOVETXN` | TABLE | SELECT | DIRECT |
|
||||||
|
| `DWH.ERP_WIP_MOVETXN_DETAIL` | TABLE | SELECT | DIRECT |
|
||||||
@@ -1,936 +0,0 @@
|
|||||||
# MES Dashboard - Architecture Findings
|
|
||||||
|
|
||||||
本文件記錄專案開發過程中確立的架構設計、全局規範與資料處理規則。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 資料庫連線管理
|
|
||||||
|
|
||||||
### 連線池統一使用
|
|
||||||
所有資料庫操作必須透過 `mes_dashboard.core.database` 模組:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.database import read_sql_df, get_engine
|
|
||||||
|
|
||||||
# 讀取資料 (推薦方式)
|
|
||||||
df = read_sql_df(sql, params)
|
|
||||||
|
|
||||||
# 取得 engine(若需要直接操作)
|
|
||||||
engine = get_engine()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 連線池配置 (位置: `core/database.py`)
|
|
||||||
|
|
||||||
| 參數 | 開發環境 | 生產環境 | 說明 |
|
|
||||||
|------|---------|---------|------|
|
|
||||||
| pool_size | 2 | 10 | 基礎連線數 |
|
|
||||||
| max_overflow | 3 | 20 | 額外連線數 |
|
|
||||||
| pool_timeout | 30 | 30 | 等待超時 (秒) |
|
|
||||||
| pool_recycle | 1800 | 1800 | 回收週期 (30分鐘) |
|
|
||||||
| pool_pre_ping | True | True | 使用前驗證連線 |
|
|
||||||
|
|
||||||
### Keep-Alive 機制
|
|
||||||
- 背景執行緒每 5 分鐘執行 `SELECT 1 FROM DUAL`
|
|
||||||
- 防止 NAT/防火牆斷開閒置連線
|
|
||||||
- 啟動: `start_keepalive()`,停止: `stop_keepalive()`
|
|
||||||
|
|
||||||
### 注意事項
|
|
||||||
- **禁止**在各 service 中自行建立連線
|
|
||||||
- **禁止**直接使用 `oracledb.connect()`
|
|
||||||
- 連線池由 `database.py` 統一管理,避免連線洩漏
|
|
||||||
- 測試環境需在 setUp 中重置:`db._ENGINE = None`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. SQL 集中管理
|
|
||||||
|
|
||||||
### 目錄結構
|
|
||||||
所有 SQL 查詢放在 `src/mes_dashboard/sql/` 目錄:
|
|
||||||
|
|
||||||
```
|
|
||||||
sql/
|
|
||||||
├── loader.py # SQL 檔案載入器 (LRU 快取)
|
|
||||||
├── builder.py # 參數化查詢構建器
|
|
||||||
├── filters.py # 通用篩選條件
|
|
||||||
├── dashboard/ # 儀表板 SQL
|
|
||||||
│ ├── kpi.sql
|
|
||||||
│ ├── heatmap.sql
|
|
||||||
│ └── workcenter_cards.sql
|
|
||||||
├── wip/ # WIP SQL
|
|
||||||
│ ├── summary.sql
|
|
||||||
│ └── detail.sql
|
|
||||||
├── resource/ # 設備 SQL
|
|
||||||
│ ├── by_status.sql
|
|
||||||
│ └── detail.sql
|
|
||||||
├── resource_history/ # 歷史 SQL
|
|
||||||
└── job_query/ # 維修工單 SQL
|
|
||||||
```
|
|
||||||
|
|
||||||
### SQLLoader 使用方式
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.sql.loader import SQLLoader
|
|
||||||
|
|
||||||
# 載入 SQL 檔案 (自動 LRU 快取,最多 100 個)
|
|
||||||
sql = SQLLoader.load("wip/summary")
|
|
||||||
|
|
||||||
# 結構性參數替換 (用於 SQL 片段)
|
|
||||||
sql = SQLLoader.load_with_params("dashboard/kpi",
|
|
||||||
LATEST_STATUS_SUBQUERY="...",
|
|
||||||
WHERE_CLAUSE="...")
|
|
||||||
|
|
||||||
# 清除快取
|
|
||||||
SQLLoader.clear_cache()
|
|
||||||
```
|
|
||||||
|
|
||||||
### QueryBuilder 使用方式
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.sql.builder import QueryBuilder
|
|
||||||
|
|
||||||
builder = QueryBuilder()
|
|
||||||
|
|
||||||
# 添加條件 (自動參數化,防 SQL 注入)
|
|
||||||
builder.add_param_condition("STATUS", "PRD")
|
|
||||||
builder.add_in_condition("STATUS", ["PRD", "SBY"])
|
|
||||||
builder.add_not_in_condition("HOLD_REASON", exclude_list)
|
|
||||||
builder.add_like_condition("LOTID", user_input, position="both")
|
|
||||||
builder.add_or_like_conditions(["COL1", "COL2"], [val1, val2])
|
|
||||||
builder.add_is_null("COLUMN")
|
|
||||||
builder.add_is_not_null("COLUMN")
|
|
||||||
builder.add_condition("FIXED_CONDITION = 1") # 固定條件
|
|
||||||
|
|
||||||
# 構建 WHERE 子句
|
|
||||||
where_clause, params = builder.build_where_only()
|
|
||||||
|
|
||||||
# 替換佔位符並執行
|
|
||||||
sql = sql.replace("{{ WHERE_CLAUSE }}", where_clause)
|
|
||||||
df = read_sql_df(sql, params)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 佔位符規範
|
|
||||||
|
|
||||||
| 類型 | 語法 | 用途 | 安全性 |
|
|
||||||
|------|------|------|--------|
|
|
||||||
| 結構性 | `{{ PLACEHOLDER }}` | 靜態 SQL 片段 | 僅限預定義值 |
|
|
||||||
| 參數 | `:param_name` | 動態用戶輸入 | Oracle bind variables |
|
|
||||||
|
|
||||||
### Oracle IN 子句限制
|
|
||||||
Oracle IN 子句上限 1000 個值,需分批處理:
|
|
||||||
|
|
||||||
```python
|
|
||||||
BATCH_SIZE = 1000
|
|
||||||
|
|
||||||
# 參考 job_query_service.py 的 _build_resource_filter()
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 快取機制
|
|
||||||
|
|
||||||
### 多層快取架構
|
|
||||||
|
|
||||||
```
|
|
||||||
請求 → 進程級快取 (30 秒 TTL)
|
|
||||||
→ Redis 快取 (可配置 TTL)
|
|
||||||
→ Oracle 資料庫
|
|
||||||
```
|
|
||||||
|
|
||||||
### 全局快取 API
|
|
||||||
使用 `mes_dashboard.core.cache` 模組:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.cache import cache_get, cache_set, make_cache_key
|
|
||||||
|
|
||||||
# 建立快取 key(支援 filters dict)
|
|
||||||
cache_key = make_cache_key("resource_history_summary", filters={
|
|
||||||
'start_date': start_date,
|
|
||||||
'workcenter_groups': sorted(groups) if groups else None,
|
|
||||||
})
|
|
||||||
|
|
||||||
# 讀取/寫入快取
|
|
||||||
result = cache_get(cache_key)
|
|
||||||
if result is None:
|
|
||||||
result = query_data()
|
|
||||||
cache_set(cache_key, result, ttl=CACHE_TTL_TREND)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 快取 TTL 常數
|
|
||||||
定義於 `mes_dashboard.config.constants`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
CACHE_TTL_DEFAULT = 60 # 1 分鐘
|
|
||||||
CACHE_TTL_FILTER_OPTIONS = 600 # 10 分鐘
|
|
||||||
CACHE_TTL_PIVOT_COLUMNS = 300 # 5 分鐘
|
|
||||||
CACHE_TTL_KPI = 60 # 1 分鐘
|
|
||||||
CACHE_TTL_TREND = 300 # 5 分鐘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Redis 快取配置
|
|
||||||
環境變數:
|
|
||||||
```
|
|
||||||
REDIS_ENABLED=true
|
|
||||||
REDIS_URL=redis://localhost:6379/0
|
|
||||||
REDIS_KEY_PREFIX=mes_wip
|
|
||||||
```
|
|
||||||
|
|
||||||
### 專用快取服務
|
|
||||||
|
|
||||||
| 服務 | 位置 | 用途 |
|
|
||||||
|------|------|------|
|
|
||||||
| WIP 快取更新器 | `core/cache_updater.py` | 背景線程自動更新 WIP 數據 |
|
|
||||||
| 資源快取 | `services/resource_cache.py` | DW_MES_RESOURCE 表快取 (4 小時同步) |
|
|
||||||
| 設備狀態快取 | `services/realtime_equipment_cache.py` | 設備實時狀態 (5 分鐘同步) |
|
|
||||||
| Filter 快取 | `services/filter_cache.py` | 篩選選項快取 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Filter Cache(篩選選項快取)
|
|
||||||
|
|
||||||
### 位置
|
|
||||||
`mes_dashboard.services.filter_cache`
|
|
||||||
|
|
||||||
### 用途
|
|
||||||
快取全站共用的篩選選項,避免重複查詢資料庫:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.services.filter_cache import (
|
|
||||||
get_workcenter_groups, # 取得 workcenter group 列表
|
|
||||||
get_workcenter_mapping, # 取得 workcentername → group 對應
|
|
||||||
get_workcenters_for_groups, # 根據 group 取得 workcentername 列表
|
|
||||||
get_resource_families, # 取得 resource family 列表
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workcenter 對應關係
|
|
||||||
```
|
|
||||||
WORKCENTERNAME (資料庫) → WORKCENTER_GROUP (顯示)
|
|
||||||
焊接_DB_1 → 焊接_DB
|
|
||||||
焊接_DB_2 → 焊接_DB
|
|
||||||
成型_1 → 成型
|
|
||||||
```
|
|
||||||
|
|
||||||
### 資料來源
|
|
||||||
- Workcenter Groups: `DW_PJ_LOT_V` (WORKCENTER_GROUP, WORKCENTERSEQUENCE_GROUP)
|
|
||||||
- Resource Families: `DW_MES_RESOURCE` (RESOURCEFAMILYNAME)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 熔斷器 (Circuit Breaker)
|
|
||||||
|
|
||||||
### 位置
|
|
||||||
`mes_dashboard.core.circuit_breaker`
|
|
||||||
|
|
||||||
### 狀態機制
|
|
||||||
|
|
||||||
```
|
|
||||||
CLOSED (正常)
|
|
||||||
↓ 失敗達到閾值
|
|
||||||
OPEN (故障,拒絕請求)
|
|
||||||
↓ 等待 recovery_timeout
|
|
||||||
HALF_OPEN (測試恢復)
|
|
||||||
↓ 成功 → CLOSED / 失敗 → OPEN
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置 (環境變數)
|
|
||||||
|
|
||||||
```
|
|
||||||
CIRCUIT_BREAKER_ENABLED=true
|
|
||||||
CIRCUIT_BREAKER_FAILURE_THRESHOLD=5 # 最少失敗次數
|
|
||||||
CIRCUIT_BREAKER_FAILURE_RATE=0.5 # 失敗率閾值 (0.0-1.0)
|
|
||||||
CIRCUIT_BREAKER_RECOVERY_TIMEOUT=30 # OPEN 狀態等待秒數
|
|
||||||
CIRCUIT_BREAKER_WINDOW_SIZE=10 # 滑動窗口大小
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用方式
|
|
||||||
熔斷器已整合在 `read_sql_df()` 中,自動:
|
|
||||||
- 檢查是否允許請求
|
|
||||||
- 記錄成功/失敗
|
|
||||||
- 狀態轉移
|
|
||||||
|
|
||||||
### 狀態查詢
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.circuit_breaker import get_database_circuit_breaker
|
|
||||||
|
|
||||||
cb = get_database_circuit_breaker()
|
|
||||||
status = cb.get_status()
|
|
||||||
# status.state, status.failure_count, status.success_count, status.failure_rate
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 統一 API 響應格式
|
|
||||||
|
|
||||||
### 位置
|
|
||||||
`mes_dashboard.core.response`
|
|
||||||
|
|
||||||
### 響應格式
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 成功響應
|
|
||||||
{
|
|
||||||
"success": True,
|
|
||||||
"data": {...},
|
|
||||||
"meta": {"timestamp": "2024-02-04T10:30:45.123456"}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 錯誤響應
|
|
||||||
{
|
|
||||||
"success": False,
|
|
||||||
"error": {
|
|
||||||
"code": "DB_CONNECTION_FAILED",
|
|
||||||
"message": "資料庫連線失敗,請稍後再試",
|
|
||||||
"details": "ORA-12541" # 僅開發模式
|
|
||||||
},
|
|
||||||
"meta": {"timestamp": "..."}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 錯誤代碼
|
|
||||||
|
|
||||||
| 代碼 | HTTP | 說明 |
|
|
||||||
|------|------|------|
|
|
||||||
| DB_CONNECTION_FAILED | 503 | 資料庫連線失敗 |
|
|
||||||
| DB_QUERY_TIMEOUT | 504 | 查詢逾時 |
|
|
||||||
| DB_QUERY_ERROR | 500 | 查詢執行錯誤 |
|
|
||||||
| SERVICE_UNAVAILABLE | 503 | 服務不可用 |
|
|
||||||
| CIRCUIT_BREAKER_OPEN | 503 | 熔斷器開啟 |
|
|
||||||
| VALIDATION_ERROR | 400 | 驗證失敗 |
|
|
||||||
| UNAUTHORIZED | 401 | 未授權 |
|
|
||||||
| FORBIDDEN | 403 | 禁止訪問 |
|
|
||||||
| NOT_FOUND | 404 | 不存在 |
|
|
||||||
| TOO_MANY_REQUESTS | 429 | 過多請求 |
|
|
||||||
| INTERNAL_ERROR | 500 | 內部錯誤 |
|
|
||||||
|
|
||||||
### 便利函數
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.response import (
|
|
||||||
success_response,
|
|
||||||
validation_error, # 400
|
|
||||||
unauthorized_error, # 401
|
|
||||||
forbidden_error, # 403
|
|
||||||
not_found_error, # 404
|
|
||||||
db_connection_error, # 503
|
|
||||||
internal_error, # 500
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 認證與授權機制
|
|
||||||
|
|
||||||
### 認證服務
|
|
||||||
位置: `mes_dashboard.services.auth_service`
|
|
||||||
|
|
||||||
#### LDAP 認證 (生產環境)
|
|
||||||
```python
|
|
||||||
from mes_dashboard.services.auth_service import authenticate
|
|
||||||
|
|
||||||
user = authenticate(username, password)
|
|
||||||
# 返回: {username, displayName, mail, department}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 本地認證 (開發環境)
|
|
||||||
```
|
|
||||||
LOCAL_AUTH_ENABLED=true
|
|
||||||
LOCAL_AUTH_USERNAME=admin
|
|
||||||
LOCAL_AUTH_PASSWORD=password
|
|
||||||
```
|
|
||||||
|
|
||||||
### Session 管理
|
|
||||||
```python
|
|
||||||
# 登入後存入 session
|
|
||||||
session["admin"] = {
|
|
||||||
"username": user.get("username"),
|
|
||||||
"displayName": user.get("displayName"),
|
|
||||||
"mail": user.get("mail"),
|
|
||||||
"department": user.get("department"),
|
|
||||||
"login_time": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
|
|
||||||
# Session 配置
|
|
||||||
SESSION_COOKIE_SECURE = True # HTTPS only (生產)
|
|
||||||
SESSION_COOKIE_HTTPONLY = True # 防止 JS 訪問
|
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax' # CSRF 防護
|
|
||||||
PERMANENT_SESSION_LIFETIME = 28800 # 8 小時
|
|
||||||
```
|
|
||||||
|
|
||||||
### 權限檢查
|
|
||||||
位置: `mes_dashboard.core.permissions`
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.permissions import is_admin_logged_in, admin_required
|
|
||||||
|
|
||||||
# 檢查登入狀態
|
|
||||||
if is_admin_logged_in():
|
|
||||||
...
|
|
||||||
|
|
||||||
# 裝飾器保護路由
|
|
||||||
@admin_required
|
|
||||||
def admin_only_view():
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
### 登入速率限制
|
|
||||||
- 單 IP 每 5 分鐘最多 5 次嘗試
|
|
||||||
- 位置: `routes/auth_routes.py`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 頁面狀態管理
|
|
||||||
|
|
||||||
### 位置
|
|
||||||
- 服務: `mes_dashboard.services.page_registry`
|
|
||||||
- 數據: `data/page_status.json`
|
|
||||||
|
|
||||||
### 狀態定義
|
|
||||||
|
|
||||||
| 狀態 | 說明 |
|
|
||||||
|------|------|
|
|
||||||
| `released` | 所有用戶可訪問 |
|
|
||||||
| `dev` | 僅管理員可訪問 |
|
|
||||||
| `None` | 未註冊,由 Flask 路由控制 |
|
|
||||||
|
|
||||||
### 數據格式
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [
|
|
||||||
{"route": "/wip-overview", "name": "WIP 即時概況", "status": "released"},
|
|
||||||
{"route": "/tables", "name": "表格總覽", "status": "dev"}
|
|
||||||
],
|
|
||||||
"api_public": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.services.page_registry import (
|
|
||||||
get_page_status, # 取得頁面狀態
|
|
||||||
set_page_status, # 設定頁面狀態
|
|
||||||
is_api_public, # API 是否公開
|
|
||||||
get_all_pages, # 取得所有頁面
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 權限檢查 (自動)
|
|
||||||
在 `app.py` 的 `@app.before_request` 中自動執行:
|
|
||||||
- dev 頁面 + 非管理員 → 403
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. 日誌系統
|
|
||||||
|
|
||||||
### 雙層日誌架構
|
|
||||||
|
|
||||||
| 層級 | 目標 | 用途 |
|
|
||||||
|------|------|------|
|
|
||||||
| 控制台 (stderr) | Gunicorn 捕獲 | 即時監控 |
|
|
||||||
| SQLite | 管理員儀表板 | 歷史查詢 |
|
|
||||||
|
|
||||||
### 配置 (環境變數)
|
|
||||||
```
|
|
||||||
LOG_STORE_ENABLED=true
|
|
||||||
LOG_SQLITE_PATH=logs/admin_logs.sqlite
|
|
||||||
LOG_SQLITE_RETENTION_DAYS=7
|
|
||||||
LOG_SQLITE_MAX_ROWS=100000
|
|
||||||
```
|
|
||||||
|
|
||||||
### 日誌記錄規範
|
|
||||||
|
|
||||||
```python
|
|
||||||
import logging
|
|
||||||
logger = logging.getLogger('mes_dashboard')
|
|
||||||
|
|
||||||
logger.debug("詳細調試資訊")
|
|
||||||
logger.info("一般操作記錄")
|
|
||||||
logger.warning("警告但可繼續")
|
|
||||||
logger.error("錯誤需要關注", exc_info=True) # 包含堆棧
|
|
||||||
```
|
|
||||||
|
|
||||||
### SQLite 日誌查詢
|
|
||||||
位置: `mes_dashboard.core.log_store`
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.core.log_store import get_log_store
|
|
||||||
|
|
||||||
store = get_log_store()
|
|
||||||
logs = store.query_logs(
|
|
||||||
level="ERROR",
|
|
||||||
limit=100,
|
|
||||||
offset=0,
|
|
||||||
search="keyword"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. 健康檢查
|
|
||||||
|
|
||||||
### 端點
|
|
||||||
|
|
||||||
| 端點 | 認證 | 說明 |
|
|
||||||
|------|------|------|
|
|
||||||
| `/health` | 無需 | 基本健康檢查 |
|
|
||||||
| `/health/deep` | 需管理員 | 詳細指標 |
|
|
||||||
|
|
||||||
### 基本檢查項目
|
|
||||||
- 資料庫連線 (`SELECT 1 FROM DUAL`)
|
|
||||||
- Redis 連線 (`PING`)
|
|
||||||
- 各快取狀態
|
|
||||||
|
|
||||||
### 詳細檢查項目 (deep)
|
|
||||||
- 資料庫延遲 (毫秒)
|
|
||||||
- 連線池狀態 (size, checked_out, overflow)
|
|
||||||
- 快取新鮮度
|
|
||||||
- 熔斷器狀態
|
|
||||||
- 查詢性能指標 (P50/P95/P99)
|
|
||||||
|
|
||||||
### 狀態判定
|
|
||||||
- `200 OK` (healthy/degraded): DB 正常
|
|
||||||
- `503 Unavailable` (unhealthy): DB 故障
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. API 路由結構 (Blueprint)
|
|
||||||
|
|
||||||
### Blueprint 列表
|
|
||||||
|
|
||||||
| Blueprint | URL 前綴 | 檔案 |
|
|
||||||
|-----------|---------|------|
|
|
||||||
| wip | `/api/wip` | `wip_routes.py` |
|
|
||||||
| resource | `/api/resource` | `resource_routes.py` |
|
|
||||||
| dashboard | `/api/dashboard` | `dashboard_routes.py` |
|
|
||||||
| excel_query | `/api/excel-query` | `excel_query_routes.py` |
|
|
||||||
| hold | `/api/hold` | `hold_routes.py` |
|
|
||||||
| resource_history | `/api/resource-history` | `resource_history_routes.py` |
|
|
||||||
| job_query | `/api/job-query` | `job_query_routes.py` |
|
|
||||||
| admin | `/admin` | `admin_routes.py` |
|
|
||||||
| auth | `/admin` | `auth_routes.py` |
|
|
||||||
| health | `/` | `health_routes.py` |
|
|
||||||
|
|
||||||
### 路由註冊
|
|
||||||
位置: `routes/__init__.py` 的 `register_routes(app)`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. 前端全局組件
|
|
||||||
|
|
||||||
### Toast 通知
|
|
||||||
定義於 `static/js/toast.js`,透過 `_base.html` 載入:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 正確用法
|
|
||||||
Toast.info('訊息');
|
|
||||||
Toast.success('成功');
|
|
||||||
Toast.warning('警告');
|
|
||||||
Toast.error('錯誤', { retry: () => loadData() });
|
|
||||||
|
|
||||||
const id = Toast.loading('載入中...');
|
|
||||||
Toast.update(id, { message: '完成!' });
|
|
||||||
Toast.dismiss(id);
|
|
||||||
|
|
||||||
// 錯誤用法(不存在)
|
|
||||||
MESToast.warning('...'); // ❌ 錯誤
|
|
||||||
```
|
|
||||||
|
|
||||||
### 自動消失時間
|
|
||||||
- info: 3000ms
|
|
||||||
- success: 2000ms
|
|
||||||
- warning: 5000ms
|
|
||||||
- error: 永久(需手動關閉)
|
|
||||||
- loading: 永久
|
|
||||||
|
|
||||||
### MesApi(HTTP 請求)
|
|
||||||
定義於 `static/js/mes-api.js`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// GET 請求
|
|
||||||
const data = await MesApi.get('/api/wip/summary', {
|
|
||||||
params: { page: 1 },
|
|
||||||
timeout: 60000,
|
|
||||||
retries: 5,
|
|
||||||
signal: abortController.signal,
|
|
||||||
silent: true // 禁用 toast 通知
|
|
||||||
});
|
|
||||||
|
|
||||||
// POST 請求
|
|
||||||
const data = await MesApi.post('/api/query_table', {
|
|
||||||
table_name: 'TABLE_A',
|
|
||||||
filters: {...}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### MesApi 特性
|
|
||||||
- 自動重試 (3 次,指數退避: 1s, 2s, 4s)
|
|
||||||
- 自動 Toast 通知
|
|
||||||
- 請求 ID 追蹤
|
|
||||||
- AbortSignal 支援
|
|
||||||
- 4xx 不重試,5xx 重試
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. 資料表預篩選規則
|
|
||||||
|
|
||||||
### 設備類型篩選
|
|
||||||
定義於 `mes_dashboard.config.constants.EQUIPMENT_TYPE_FILTER`:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
((OBJECTCATEGORY = 'ASSEMBLY' AND OBJECTTYPE = 'ASSEMBLY')
|
|
||||||
OR (OBJECTCATEGORY = 'WAFERSORT' AND OBJECTTYPE = 'WAFERSORT'))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 排除條件
|
|
||||||
```python
|
|
||||||
# 排除的地點
|
|
||||||
EXCLUDED_LOCATIONS = [
|
|
||||||
'ATEC', 'F區', 'F區焊接站', '報廢', '實驗室',
|
|
||||||
'山東', '成型站_F區', '焊接F區', '無錫', '熒茂'
|
|
||||||
]
|
|
||||||
|
|
||||||
# 排除的資產狀態
|
|
||||||
EXCLUDED_ASSET_STATUSES = ['Disapproved']
|
|
||||||
```
|
|
||||||
|
|
||||||
### CommonFilters 使用
|
|
||||||
位置: `mes_dashboard.sql.filters`
|
|
||||||
|
|
||||||
```python
|
|
||||||
from mes_dashboard.sql.filters import CommonFilters
|
|
||||||
|
|
||||||
# 添加標準篩選
|
|
||||||
CommonFilters.add_location_exclusion(builder, 'r')
|
|
||||||
CommonFilters.add_asset_status_exclusion(builder, 'r')
|
|
||||||
CommonFilters.add_wip_base_filters(builder, filters)
|
|
||||||
CommonFilters.add_equipment_filter(builder, filters)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. 資料庫欄位對應
|
|
||||||
|
|
||||||
### DW_MES_RESOURCE
|
|
||||||
| 常見錯誤 | 正確欄位名 |
|
|
||||||
|---------|-----------|
|
|
||||||
| ASSETSTATUS | PJ_ASSETSSTATUS(雙 S)|
|
|
||||||
| LOCATION | LOCATIONNAME |
|
|
||||||
| ISPRODUCTION | PJ_ISPRODUCTION |
|
|
||||||
| ISKEY | PJ_ISKEY |
|
|
||||||
| ISMONITOR | PJ_ISMONITOR |
|
|
||||||
|
|
||||||
### DW_MES_RESOURCESTATUS_SHIFT
|
|
||||||
| 欄位 | 說明 |
|
|
||||||
|-----|------|
|
|
||||||
| HISTORYID | 對應 DW_MES_RESOURCE.RESOURCEID |
|
|
||||||
| TXNDATE | 交易日期 |
|
|
||||||
| OLDSTATUSNAME | E10 狀態 (PRD, SBY, UDT, SDT, EGT, NST) |
|
|
||||||
| HOURS | 該狀態時數 |
|
|
||||||
|
|
||||||
### DW_PJ_LOT_V
|
|
||||||
| 欄位 | 說明 |
|
|
||||||
|-----|------|
|
|
||||||
| WORKCENTERNAME | 站點名稱(細分)|
|
|
||||||
| WORKCENTER_GROUP | 站點群組(顯示用)|
|
|
||||||
| WORKCENTERSEQUENCE_GROUP | 群組排序 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 15. E10 狀態定義
|
|
||||||
|
|
||||||
| 狀態 | 說明 | 計入 OU% |
|
|
||||||
|-----|------|---------|
|
|
||||||
| PRD | Production(生產)| 是(分子)|
|
|
||||||
| SBY | Standby(待機)| 是(分母)|
|
|
||||||
| UDT | Unscheduled Downtime(非計畫停機)| 是(分母)|
|
|
||||||
| SDT | Scheduled Downtime(計畫停機)| 是(分母)|
|
|
||||||
| EGT | Engineering Time(工程時間)| 是(分母)|
|
|
||||||
| NST | Non-Scheduled Time(非排程時間)| 否 |
|
|
||||||
|
|
||||||
### OU% 計算公式
|
|
||||||
```
|
|
||||||
OU% = PRD / (PRD + SBY + UDT + SDT + EGT) × 100
|
|
||||||
```
|
|
||||||
|
|
||||||
### 狀態顯示名稱
|
|
||||||
```python
|
|
||||||
STATUS_DISPLAY_NAMES = {
|
|
||||||
'PRD': '生產中',
|
|
||||||
'SBY': '待機',
|
|
||||||
'UDT': '非計畫停機',
|
|
||||||
'SDT': '計畫停機',
|
|
||||||
'EGT': '工程時間',
|
|
||||||
'NST': '未排單',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 16. 配置管理
|
|
||||||
|
|
||||||
### 環境變數 (.env)
|
|
||||||
|
|
||||||
#### 資料庫
|
|
||||||
```
|
|
||||||
DB_HOST=<your_database_host>
|
|
||||||
DB_PORT=1521
|
|
||||||
DB_SERVICE=<your_service_name>
|
|
||||||
DB_USER=<your_username>
|
|
||||||
DB_PASSWORD=<your_password>
|
|
||||||
DB_POOL_SIZE=5
|
|
||||||
DB_MAX_OVERFLOW=10
|
|
||||||
```
|
|
||||||
|
|
||||||
> 實際值請參考 `.env` 或 `.env.example`
|
|
||||||
|
|
||||||
#### Flask
|
|
||||||
```
|
|
||||||
FLASK_ENV=production
|
|
||||||
FLASK_DEBUG=0
|
|
||||||
SECRET_KEY=your_secret_key
|
|
||||||
SESSION_LIFETIME=28800
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 認證
|
|
||||||
```
|
|
||||||
LDAP_API_URL=<your_ldap_api_url>
|
|
||||||
ADMIN_EMAILS=<admin_email_list>
|
|
||||||
LOCAL_AUTH_ENABLED=false
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Gunicorn
|
|
||||||
```
|
|
||||||
GUNICORN_BIND=0.0.0.0:8080
|
|
||||||
GUNICORN_WORKERS=4
|
|
||||||
GUNICORN_THREADS=8
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 快取
|
|
||||||
```
|
|
||||||
REDIS_ENABLED=true
|
|
||||||
REDIS_URL=redis://localhost:6379/0
|
|
||||||
CACHE_CHECK_INTERVAL=600
|
|
||||||
RESOURCE_CACHE_ENABLED=true
|
|
||||||
RESOURCE_SYNC_INTERVAL=14400
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 熔斷器
|
|
||||||
```
|
|
||||||
CIRCUIT_BREAKER_ENABLED=true
|
|
||||||
CIRCUIT_BREAKER_FAILURE_THRESHOLD=5
|
|
||||||
CIRCUIT_BREAKER_FAILURE_RATE=0.5
|
|
||||||
CIRCUIT_BREAKER_RECOVERY_TIMEOUT=30
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 日誌
|
|
||||||
```
|
|
||||||
LOG_STORE_ENABLED=true
|
|
||||||
LOG_SQLITE_PATH=logs/admin_logs.sqlite
|
|
||||||
LOG_SQLITE_RETENTION_DAYS=7
|
|
||||||
```
|
|
||||||
|
|
||||||
### 環境配置類
|
|
||||||
位置: `mes_dashboard.config.settings`
|
|
||||||
|
|
||||||
```python
|
|
||||||
class DevelopmentConfig(Config):
|
|
||||||
DEBUG = True
|
|
||||||
DB_POOL_SIZE = 2
|
|
||||||
|
|
||||||
class ProductionConfig(Config):
|
|
||||||
DEBUG = False
|
|
||||||
DB_POOL_SIZE = 10
|
|
||||||
|
|
||||||
class TestingConfig(Config):
|
|
||||||
TESTING = True
|
|
||||||
DB_POOL_SIZE = 1
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 17. 平行查詢
|
|
||||||
|
|
||||||
### ThreadPoolExecutor
|
|
||||||
對於多個獨立查詢,使用平行執行提升效能:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
||||||
futures = {
|
|
||||||
executor.submit(read_sql_df, kpi_sql): 'kpi',
|
|
||||||
executor.submit(read_sql_df, trend_sql): 'trend',
|
|
||||||
executor.submit(read_sql_df, heatmap_sql): 'heatmap',
|
|
||||||
}
|
|
||||||
for future in as_completed(futures):
|
|
||||||
query_name = futures[future]
|
|
||||||
results[query_name] = future.result()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 注意事項
|
|
||||||
- Mock 測試時不能使用 `side_effect` 列表(順序不可預測)
|
|
||||||
- 應使用函式判斷 SQL 內容來回傳對應的 mock 資料
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 18. Oracle SQL 優化
|
|
||||||
|
|
||||||
### CTE MATERIALIZE Hint
|
|
||||||
防止 Oracle 優化器將 CTE inline 多次執行:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
WITH shift_data AS (
|
|
||||||
SELECT /*+ MATERIALIZE */ HISTORYID, TXNDATE, OLDSTATUSNAME, HOURS
|
|
||||||
FROM DW_MES_RESOURCESTATUS_SHIFT
|
|
||||||
WHERE TXNDATE >= TO_DATE('2024-01-01', 'YYYY-MM-DD')
|
|
||||||
AND TXNDATE < TO_DATE('2024-01-07', 'YYYY-MM-DD') + 1
|
|
||||||
)
|
|
||||||
SELECT ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### 日期範圍查詢
|
|
||||||
```sql
|
|
||||||
-- 包含 end_date 當天
|
|
||||||
WHERE TXNDATE >= TO_DATE(:start_date, 'YYYY-MM-DD')
|
|
||||||
AND TXNDATE < TO_DATE(:end_date, 'YYYY-MM-DD') + 1
|
|
||||||
```
|
|
||||||
|
|
||||||
### 慢查詢警告
|
|
||||||
- 閾值: 1 秒 (警告),5 秒 (`SLOW_QUERY_THRESHOLD`)
|
|
||||||
- 自動記錄到日誌
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 19. 前端資料限制
|
|
||||||
|
|
||||||
### 明細資料上限
|
|
||||||
為避免瀏覽器記憶體問題,明細查詢有筆數限制:
|
|
||||||
|
|
||||||
```python
|
|
||||||
MAX_DETAIL_RECORDS = 5000
|
|
||||||
|
|
||||||
if total > MAX_DETAIL_RECORDS:
|
|
||||||
df = df.head(MAX_DETAIL_RECORDS)
|
|
||||||
truncated = True
|
|
||||||
```
|
|
||||||
|
|
||||||
前端顯示警告:
|
|
||||||
```javascript
|
|
||||||
if (result.truncated) {
|
|
||||||
Toast.warning(`資料超過 ${result.max_records} 筆,請使用篩選條件縮小範圍。`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 20. JavaScript 注意事項
|
|
||||||
|
|
||||||
### Array.reverse() 原地修改
|
|
||||||
```javascript
|
|
||||||
// 錯誤 - 原地修改陣列
|
|
||||||
const arr = [1, 2, 3];
|
|
||||||
arr.reverse(); // arr 被修改為 [3, 2, 1]
|
|
||||||
|
|
||||||
// 正確 - 建立新陣列
|
|
||||||
const reversed = arr.slice().reverse(); // arr 不變
|
|
||||||
// 或
|
|
||||||
const reversed = [...arr].reverse();
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 21. 測試規範
|
|
||||||
|
|
||||||
### 測試檔案結構
|
|
||||||
```
|
|
||||||
tests/
|
|
||||||
├── conftest.py # pytest fixtures
|
|
||||||
├── test_*_service.py # 單元測試(service layer)
|
|
||||||
├── test_*_routes.py # 整合測試(API endpoints)
|
|
||||||
├── e2e/
|
|
||||||
│ └── test_*_e2e.py # 端對端測試(完整流程)
|
|
||||||
└── stress/
|
|
||||||
└── test_*.py # 壓力測試
|
|
||||||
```
|
|
||||||
|
|
||||||
### 測試前重置
|
|
||||||
```python
|
|
||||||
def setUp(self):
|
|
||||||
db._ENGINE = None # 重置連線池
|
|
||||||
self.app = create_app('testing')
|
|
||||||
```
|
|
||||||
|
|
||||||
### 執行測試
|
|
||||||
```bash
|
|
||||||
# 單一模組
|
|
||||||
pytest tests/test_resource_history_service.py -v
|
|
||||||
|
|
||||||
# 全部相關測試
|
|
||||||
pytest tests/test_resource_history_*.py tests/e2e/test_resource_history_e2e.py -v
|
|
||||||
|
|
||||||
# 覆蓋率報告
|
|
||||||
pytest tests/ --cov=mes_dashboard
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 22. 錯誤處理模式
|
|
||||||
|
|
||||||
### 三層錯誤處理
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 1. 路由層 - 驗證錯誤
|
|
||||||
@bp.route('/api/query')
|
|
||||||
def query():
|
|
||||||
if not request.json.get('table_name'):
|
|
||||||
return validation_error("table_name 為必填")
|
|
||||||
|
|
||||||
# 2. 服務層 - 業務錯誤 (優雅降級)
|
|
||||||
def get_wip_summary(filters):
|
|
||||||
try:
|
|
||||||
df = query_wip(filters)
|
|
||||||
if df.empty:
|
|
||||||
return None
|
|
||||||
return process_data(df)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error(f"WIP query failed: {exc}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 3. 核心層 - 基礎設施錯誤
|
|
||||||
def read_sql_df(sql, params):
|
|
||||||
if not circuit_breaker.allow_request():
|
|
||||||
raise RuntimeError("Circuit breaker open")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 全局錯誤處理
|
|
||||||
位置: `app.py` 的 `_register_error_handlers()`
|
|
||||||
|
|
||||||
- 401 → `unauthorized_error()`
|
|
||||||
- 403 → `forbidden_error()`
|
|
||||||
- 404 → JSON (API) 或 HTML (頁面)
|
|
||||||
- 500 → `internal_error()`
|
|
||||||
- Exception → 通用處理
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 參考檔案索引
|
|
||||||
|
|
||||||
| 功能 | 檔案位置 |
|
|
||||||
|------|---------|
|
|
||||||
| SQL 載入 | `src/mes_dashboard/sql/loader.py` |
|
|
||||||
| 查詢構建 | `src/mes_dashboard/sql/builder.py` |
|
|
||||||
| 通用篩選 | `src/mes_dashboard/sql/filters.py` |
|
|
||||||
| 資料庫操作 | `src/mes_dashboard/core/database.py` |
|
|
||||||
| 快取 | `src/mes_dashboard/core/cache.py` |
|
|
||||||
| 熔斷器 | `src/mes_dashboard/core/circuit_breaker.py` |
|
|
||||||
| API 響應 | `src/mes_dashboard/core/response.py` |
|
|
||||||
| 權限檢查 | `src/mes_dashboard/core/permissions.py` |
|
|
||||||
| 日誌存儲 | `src/mes_dashboard/core/log_store.py` |
|
|
||||||
| 配置類 | `src/mes_dashboard/config/settings.py` |
|
|
||||||
| 常量定義 | `src/mes_dashboard/config/constants.py` |
|
|
||||||
| 認證服務 | `src/mes_dashboard/services/auth_service.py` |
|
|
||||||
| 頁面狀態 | `src/mes_dashboard/services/page_registry.py` |
|
|
||||||
| Filter 快取 | `src/mes_dashboard/services/filter_cache.py` |
|
|
||||||
| 資源快取 | `src/mes_dashboard/services/resource_cache.py` |
|
|
||||||
| API 客戶端 | `src/mes_dashboard/static/js/mes-api.js` |
|
|
||||||
| Toast 系統 | `src/mes_dashboard/static/js/toast.js` |
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Environment Sync Report
|
|
||||||
|
|
||||||
- Source: `/home/egg/Project/DashBoard/.env`
|
|
||||||
- Target: `/home/egg/Project/DashBoard_vite/.env`
|
|
||||||
- Example Baseline: `.env.example`
|
|
||||||
|
|
||||||
- Keys in source env: 39
|
|
||||||
- Keys in vite .env.example: 54
|
|
||||||
- Missing keys auto-added to target: 15
|
|
||||||
|
|
||||||
## Auto-Added Keys
|
|
||||||
- `DB_POOL_TIMEOUT`
|
|
||||||
- `DB_POOL_RECYCLE`
|
|
||||||
- `DB_TCP_CONNECT_TIMEOUT`
|
|
||||||
- `DB_CONNECT_RETRY_COUNT`
|
|
||||||
- `DB_CONNECT_RETRY_DELAY`
|
|
||||||
- `DB_CALL_TIMEOUT_MS`
|
|
||||||
- `WIP_CACHE_TTL_SECONDS`
|
|
||||||
- `WATCHDOG_RUNTIME_DIR`
|
|
||||||
- `WATCHDOG_PID_FILE`
|
|
||||||
- `WATCHDOG_RESTART_HISTORY_MAX`
|
|
||||||
- `RESILIENCE_DEGRADED_ALERT_SECONDS`
|
|
||||||
- `RESILIENCE_POOL_SATURATION_WARNING`
|
|
||||||
- `RESILIENCE_POOL_SATURATION_CRITICAL`
|
|
||||||
- `RESILIENCE_RESTART_CHURN_WINDOW_SECONDS`
|
|
||||||
- `RESILIENCE_RESTART_CHURN_THRESHOLD`
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Environment Usage Gap Report
|
|
||||||
|
|
||||||
- Parsed env keys used in code/tests: 37
|
|
||||||
- Keys present in `.env`: 63
|
|
||||||
- Missing keys: 2
|
|
||||||
|
|
||||||
## Missing Keys
|
|
||||||
- `CONDA_DEFAULT_ENV`
|
|
||||||
- `PYTEST_CURRENT_TEST`
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Environment-dependent Gaps and Mitigation
|
|
||||||
|
|
||||||
## Oracle-dependent checks
|
|
||||||
|
|
||||||
### Gap
|
|
||||||
- Service/integration paths that execute Oracle SQL require live DB credentials and network reachability.
|
|
||||||
- Local CI-like runs may not have Oracle connectivity.
|
|
||||||
- In this environment, `tests/test_cache_integration.py` has Oracle-dependent fallback failures when cache fixtures are insufficient.
|
|
||||||
|
|
||||||
### Mitigation
|
|
||||||
- Keep unit tests isolated with mocks for SQL entry points.
|
|
||||||
- Reserve Oracle-connected tests for gated environments.
|
|
||||||
- Use `testing` config for app factory tests where possible.
|
|
||||||
|
|
||||||
## Redis-dependent checks
|
|
||||||
|
|
||||||
### Gap
|
|
||||||
- Redis availability differs across environments.
|
|
||||||
- Health/caching behavior differs between `L1+L2` and `L1-only degraded` modes.
|
|
||||||
|
|
||||||
### Mitigation
|
|
||||||
- Expose route-cache telemetry in `/health` and `/health/deep`.
|
|
||||||
- Keep degraded mode visible and non-fatal where DB remains healthy.
|
|
||||||
- Validate both modes in unit tests (`tests/test_cache.py`, `tests/test_health_routes.py`).
|
|
||||||
|
|
||||||
## Frontend build availability
|
|
||||||
|
|
||||||
### Gap
|
|
||||||
- Node/npm may be absent on constrained runtime nodes.
|
|
||||||
|
|
||||||
### Mitigation
|
|
||||||
- Keep inline script fallback in templates when dist assets are missing.
|
|
||||||
- Build artifacts in deployment pipeline where Node is available.
|
|
||||||
- Startup script logs fallback mode explicitly on build failure.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Frontend Compute Shift Plan
|
|
||||||
|
|
||||||
## Targeted Calculations
|
|
||||||
|
|
||||||
## Resource History (migrated to frontend helpers)
|
|
||||||
- `ou_pct`
|
|
||||||
- `availability_pct`
|
|
||||||
- status percentages:
|
|
||||||
- `prd_pct`
|
|
||||||
- `sby_pct`
|
|
||||||
- `udt_pct`
|
|
||||||
- `sdt_pct`
|
|
||||||
- `egt_pct`
|
|
||||||
- `nst_pct`
|
|
||||||
|
|
||||||
These are now computed by `frontend/src/core/compute.js` via:
|
|
||||||
- `buildResourceKpiFromHours`
|
|
||||||
- `calcOuPct`
|
|
||||||
- `calcAvailabilityPct`
|
|
||||||
- `calcStatusPct`
|
|
||||||
|
|
||||||
## Parity Rules
|
|
||||||
|
|
||||||
1. Rounding rule
|
|
||||||
- one decimal place, identical to backend (`round(..., 1)`)
|
|
||||||
|
|
||||||
2. Formula rule
|
|
||||||
- OU%: `PRD / (PRD + SBY + UDT + SDT + EGT)`
|
|
||||||
- Availability%: `(PRD + SBY + EGT) / (PRD + SBY + EGT + SDT + UDT + NST)`
|
|
||||||
- Status%: `status_hours / total_hours`
|
|
||||||
|
|
||||||
3. Zero denominator rule
|
|
||||||
- all percentages return `0`
|
|
||||||
|
|
||||||
4. Data compatibility rule
|
|
||||||
- backend keeps existing fields to preserve API compatibility
|
|
||||||
- frontend recomputes display values from hours for deterministic parity
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- Python backend formula baseline: `mes_dashboard.services.resource_history_service`
|
|
||||||
- Frontend parity check: `tests/test_frontend_compute_parity.py`
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*PJMES043-Hold歷史紀錄
|
|
||||||
20240716 Peeler 新增匯總紀錄_PJM022024000878
|
|
||||||
20250520 Peeler 加總判斷Future Hold不同站別相同原因只計算第一次Hold_PJM022025000733
|
|
||||||
*/
|
|
||||||
SELECT TO_NUMBER(BS.TXNDAY) AS TXNDAY1,BS.TXNDAY
|
|
||||||
,CASE WHEN :P_QCHOLDPARA = 2
|
|
||||||
THEN SUM(CASE WHEN HD.HOLDTXNDAY <= BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS < HD.RELEASETXNDAY) AND BS.TRANSACTION_DAYS <= TO_CHAR(SYSDATE , 'YYYY/MM/DD') AND HD.RN_HOLD = 1 THEN QTY
|
|
||||||
ELSE 0 END)
|
|
||||||
ELSE SUM(CASE WHEN HD.HOLDTXNDAY <= BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS < HD.RELEASETXNDAY) AND BS.TRANSACTION_DAYS <= TO_CHAR(SYSDATE , 'YYYY/MM/DD') AND QCHOLDFLAG = :P_QCHOLDPARA AND HD.RN_HOLD = 1 THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
END AS HOLDQTY
|
|
||||||
|
|
||||||
,CASE WHEN :P_QCHOLDPARA = 2
|
|
||||||
THEN SUM(CASE WHEN HD.HOLDTXNDAY = BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS <= HD.RELEASETXNDAY) AND HD.FUTUREHOLD_FLAG = 1 THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
ELSE SUM(CASE WHEN HD.HOLDTXNDAY = BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS <= HD.RELEASETXNDAY) AND QCHOLDFLAG = :P_QCHOLDPARA AND HD.FUTUREHOLD_FLAG = 1 THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
END AS NEW_HOLDQTY
|
|
||||||
|
|
||||||
,CASE WHEN :P_QCHOLDPARA = 2
|
|
||||||
THEN SUM(CASE WHEN HD.RELEASETXNDAY = BS.TRANSACTION_DAYS AND HD.RELEASETXNDAY >= HD.HOLDTXNDAY THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
ELSE SUM(CASE WHEN HD.RELEASETXNDAY = BS.TRANSACTION_DAYS AND HD.RELEASETXNDAY >= HD.HOLDTXNDAY AND QCHOLDFLAG = :P_QCHOLDPARA THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
END AS RELEASEQTY
|
|
||||||
|
|
||||||
,CASE WHEN :P_QCHOLDPARA = 2
|
|
||||||
THEN SUM(CASE WHEN HD.HOLDTXNDAY = BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS <= HD.RELEASETXNDAY) AND HD.RN_HOLD = 1 AND HD.FUTUREHOLD_FLAG = 0 THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
ELSE SUM(CASE WHEN HD.HOLDTXNDAY = BS.TRANSACTION_DAYS AND (HD.RELEASETXNDAY IS NULL OR BS.TRANSACTION_DAYS <= HD.RELEASETXNDAY) AND QCHOLDFLAG = :P_QCHOLDPARA AND HD.RN_HOLD = 1 AND HD.FUTUREHOLD_FLAG = 0 THEN QTY
|
|
||||||
ELSE 0 END )
|
|
||||||
END AS FUTURE_HOLDQTY
|
|
||||||
FROM(select TO_CHAR(to_date(:P_TxnDate_S , 'YYYY/MM/DD') + rownum -1,'YYYY') AS TXNYEAR
|
|
||||||
, TO_CHAR(to_date(:P_TxnDate_S , 'YYYY/MM/DD') + rownum -1,'MM') AS TXNMONTH
|
|
||||||
, TO_CHAR(to_date(:P_TxnDate_S , 'YYYY/MM/DD') + rownum -1,'DD') AS TXNDAY
|
|
||||||
, TO_CHAR(to_date(:P_TxnDate_S , 'YYYY/MM/DD') + rownum -1,'YYYY/MM/DD') AS TRANSACTION_DAYS
|
|
||||||
From dual
|
|
||||||
CONNECT BY LEVEL <= TO_CHAR(LAST_DAY(to_date(:P_TxnDate_S,'YYYY/MM/DD')),'DD')
|
|
||||||
)BS,
|
|
||||||
(SELECT HOLDTXNDAY,RELEASETXNDAY,HOLDTXNDATE,RELEASETXNDATE,CONTAINERID,QTY, QCHOLDFLAG
|
|
||||||
,ROW_NUMBER() OVER (PARTITION BY CONTAINERID,HOLDTXNDAY ORDER BY HOLDTXNDATE DESC) AS RN_HOLD--同一張工單當天重複Hold
|
|
||||||
,ROW_NUMBER() OVER (PARTITION BY CONTAINERID,RELEASETXNDAY ORDER BY RELEASETXNDATE DESC) AS RN_RELEASE--同一張工單當天重複Release
|
|
||||||
,CASE WHEN FUTUREHOLD = 1 AND RN_CONHOLD <> 1 THEN 0
|
|
||||||
ELSE 1 END AS FUTUREHOLD_FLAG --FutureHold相同原因第一筆計算1其餘給0
|
|
||||||
FROM(SELECT CASE WHEN TO_CHAR(HD.HOLDTXNDATE,'HH24MI')>=0730
|
|
||||||
THEN TO_CHAR(HD.HOLDTXNDATE +1 ,'YYYY/MM/DD')
|
|
||||||
ELSE TO_CHAR(HD.HOLDTXNDATE ,'YYYY/MM/DD') END AS HOLDTXNDAY
|
|
||||||
,CASE WHEN TO_CHAR(HD.RELEASETXNDATE,'HH24MI')>=0730
|
|
||||||
THEN TO_CHAR(HD.RELEASETXNDATE +1 ,'YYYY/MM/DD')
|
|
||||||
ELSE TO_CHAR(HD.RELEASETXNDATE ,'YYYY/MM/DD') END AS RELEASETXNDAY
|
|
||||||
,HD.HOLDTXNDATE
|
|
||||||
,HD.RELEASETXNDATE
|
|
||||||
,HD.CONTAINERID
|
|
||||||
,HD.QTY
|
|
||||||
,CASE WHEN HD.HOLDREASONNAME IN(:P_QCHOLDREASON) THEN 1
|
|
||||||
ELSE 0 END AS QCHOLDFLAG
|
|
||||||
,CASE WHEN HD.FUTUREHOLDCOMMENTS IS NOT NULL THEN 1
|
|
||||||
ELSE 0 END AS FUTUREHOLD
|
|
||||||
,ROW_NUMBER() OVER (PARTITION BY HD.CONTAINERID,HD.HOLDREASONID ORDER BY HD.HOLDTXNDATE) AS RN_CONHOLD--同一張工單重複Hold
|
|
||||||
FROM DW_MES_HOLDRELEASEHISTORY HD
|
|
||||||
WHERE 1=1
|
|
||||||
AND ((HD.HOLDTXNDATE >= TO_DATE(:P_TxnDate_S||' 073000', 'YYYYMMDD HH24MISS')-1) Or :P_TxnDate_S is null OR (HD.RELEASETXNDATE >= TO_DATE(:P_TxnDate_S||' 073000', 'YYYYMMDD HH24MISS')-1) OR (HD.RELEASETXNDATE IS NULL))
|
|
||||||
AND ((HD.HOLDTXNDATE <= TO_DATE(:P_TxnDate_E||' 073000', 'YYYYMMDD HH24MISS')) Or :P_TxnDate_E is null OR (HD.RELEASETXNDATE <= TO_DATE(:P_TxnDate_E||' 073000', 'YYYYMMDD HH24MISS')) OR (HD.RELEASETXNDATE IS NULL))
|
|
||||||
)
|
|
||||||
)HD
|
|
||||||
WHERE 1=1
|
|
||||||
GROUP BY BS.TXNDAY
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Deferred Route Modernization Follow-up Artifacts
|
|
||||||
|
|
||||||
This directory stores execution artifacts for `deferred-route-modernization-follow-up`.
|
|
||||||
|
|
||||||
## Upstream Reference
|
|
||||||
|
|
||||||
- Phase 1: `docs/migration/full-modernization-architecture-blueprint/`
|
|
||||||
- Handoff: `docs/migration/full-modernization-architecture-blueprint/deferred_route_handoff.md`
|
|
||||||
|
|
||||||
## Core Governance
|
|
||||||
|
|
||||||
- `route_scope_matrix.json`: frozen in-scope deferred route matrix (promoted from phase 1 deferred).
|
|
||||||
- `governance_milestones.md`: completion and deprecation milestones for deferred-route phase.
|
|
||||||
- `exception_registry.json`: approved temporary exceptions with owner and milestone.
|
|
||||||
- `upstream_linkage.json`: explicit linkage to phase 1 handoff artifacts.
|
|
||||||
- `scope_boundary_note.md`: clarification that dev routes are eligible for modernization.
|
|
||||||
|
|
||||||
## Pre-Change Confirmation
|
|
||||||
|
|
||||||
- `pre_change_confirmation_template.md`: required fields and template.
|
|
||||||
- `pre_change_confirmations.json`: recorded per-route confirmations.
|
|
||||||
|
|
||||||
## Rollout Operations
|
|
||||||
|
|
||||||
- `rollout_runbook.md`: phase steps and hold points for deferred-route cutover.
|
|
||||||
- `rollback_controls.md`: per-route rollback and false-positive gate handling.
|
|
||||||
- `observability_checkpoints.md`: route/gate/rollback observability contract.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"change": "deferred-route-modernization-follow-up",
|
|
||||||
"fields": [
|
|
||||||
"id",
|
|
||||||
"type",
|
|
||||||
"scope",
|
|
||||||
"owner",
|
|
||||||
"introduced_by",
|
|
||||||
"reason",
|
|
||||||
"mitigation",
|
|
||||||
"status",
|
|
||||||
"milestone",
|
|
||||||
"tracking_issue"
|
|
||||||
],
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"id": "style-excel-query-shell-tokens-no-fallback",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/excel-query",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "excel-query uses shell tokens (--portal-brand-start, --portal-brand-end, --portal-shadow-panel) without CSS fallback values; inherited from legacy era before shell token governance",
|
|
||||||
"mitigation": "add fallback values during content modernization cutover for /excel-query",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/excel-query-style-hardening"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "style-query-tool-shell-tokens-no-fallback",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/query-tool",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "query-tool uses shell tokens (--portal-brand-start, --portal-brand-end, --portal-shadow-panel) without CSS fallback values; inherited from legacy era before shell token governance",
|
|
||||||
"mitigation": "add fallback values during content modernization cutover for /query-tool",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/query-tool-style-hardening"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Deferred Route Modernization Governance Milestones
|
|
||||||
|
|
||||||
## Upstream Reference
|
|
||||||
|
|
||||||
- Phase 1: `full-modernization-architecture-blueprint`
|
|
||||||
- Handoff: `docs/migration/full-modernization-architecture-blueprint/deferred_route_handoff.md`
|
|
||||||
|
|
||||||
## Phase Completion Criteria
|
|
||||||
|
|
||||||
A phase is complete only when all criteria below are true:
|
|
||||||
|
|
||||||
1. Route governance: 100% of in-scope deferred routes in `route_scope_matrix.json` have valid shell contract metadata and ownership with scope promoted to `in-scope`.
|
|
||||||
2. Style governance: deferred route-local styles do not introduce page-global selectors (`:root`, `body`) unless recorded in exception registry.
|
|
||||||
3. Quality governance: functional parity, visual checkpoints, accessibility checks, and performance budgets pass at configured gate severity.
|
|
||||||
4. Content safety governance: page-content parity evidence + manual acceptance sign-off exist for each migrated deferred route.
|
|
||||||
5. Bug carry-over governance: known-bug replay checks for migrated scope do not reproduce legacy defects.
|
|
||||||
6. Pre-change confirmation: each deferred route has an approved pre-change confirmation record before implementation begins.
|
|
||||||
|
|
||||||
## Legacy Deprecation Milestones
|
|
||||||
|
|
||||||
- 2026-02-19: deferred route contract CI completeness gate enabled in `warn` mode.
|
|
||||||
- 2026-02-26: deferred route contract CI completeness gate promoted to `block` mode.
|
|
||||||
- 2026-03-05: deferred route asset readiness gate promoted to `block` mode.
|
|
||||||
- 2026-03-12: runtime fallback posture retired for deferred routes in production policy.
|
|
||||||
- 2026-03-19: unresolved style exceptions past milestone fail modernization review.
|
|
||||||
|
|
||||||
## Route Cutover Sequence
|
|
||||||
|
|
||||||
Routes are cut over one at a time in the following planned order:
|
|
||||||
|
|
||||||
1. `/tables`
|
|
||||||
2. `/excel-query`
|
|
||||||
3. `/query-tool`
|
|
||||||
4. `/mid-section-defect`
|
|
||||||
|
|
||||||
Next route cutover is blocked until current route has:
|
|
||||||
- Parity pass (golden fixtures + interaction checks)
|
|
||||||
- Manual acceptance sign-off
|
|
||||||
- Known-bug replay pass
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Deferred Route Modernization Observability Checkpoints
|
|
||||||
|
|
||||||
## Route Governance Signals
|
|
||||||
|
|
||||||
1. `navigation_contract_mismatch_total`
|
|
||||||
- Source: `/api/portal/navigation` diagnostics.
|
|
||||||
- Alert condition: non-zero for deferred-promoted routes.
|
|
||||||
|
|
||||||
2. `route_contract_missing_metadata_total`
|
|
||||||
- Source: route governance CI script.
|
|
||||||
- Alert condition: >0 in block mode for `/tables`, `/excel-query`, `/query-tool`, `/mid-section-defect`.
|
|
||||||
|
|
||||||
## Quality Gate Signals
|
|
||||||
|
|
||||||
1. `quality_gate_failed_total{gate_id}`
|
|
||||||
- Source: quality gate report.
|
|
||||||
- Alert condition: any mandatory gate failed for deferred-promoted routes.
|
|
||||||
|
|
||||||
2. `manual_acceptance_pending_routes`
|
|
||||||
- Source: manual acceptance records.
|
|
||||||
- Alert condition: cutover attempted with pending sign-off for deferred routes.
|
|
||||||
|
|
||||||
3. `pre_change_confirmation_missing`
|
|
||||||
- Source: pre-change confirmation records.
|
|
||||||
- Alert condition: implementation started without recorded confirmation.
|
|
||||||
|
|
||||||
## Fallback and Rollback Signals
|
|
||||||
|
|
||||||
1. `deferred_route_runtime_fallback_served_total`
|
|
||||||
- Should remain zero after fallback retirement milestone for each route.
|
|
||||||
|
|
||||||
2. `content_cutover_flag_rollbacks_total{route}`
|
|
||||||
- Track frequency per deferred route.
|
|
||||||
|
|
||||||
3. `legacy_bug_replay_failures_total{route}`
|
|
||||||
- Any non-zero indicates carry-over risk and blocks sign-off.
|
|
||||||
|
|
||||||
## Cutover Sequence Signals
|
|
||||||
|
|
||||||
1. `deferred_route_cutover_sequence_violation`
|
|
||||||
- Alert condition: route cutover attempted out of planned sequence.
|
|
||||||
|
|
||||||
2. `deferred_route_signoff_blocked_by_bug_replay`
|
|
||||||
- Alert condition: sign-off blocked due to reproduced legacy bug.
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Pre-Change Confirmation Template (Deferred Route)
|
|
||||||
|
|
||||||
## Rule
|
|
||||||
|
|
||||||
Before any implementation work begins on a deferred route, a route-scoped pre-change confirmation MUST be recorded and approved. Implementation is BLOCKED until confirmation exists.
|
|
||||||
|
|
||||||
## Required Fields
|
|
||||||
|
|
||||||
1. **Route**: The deferred route path (e.g., `/tables`).
|
|
||||||
2. **Status Snapshot**: Current route status in page registry (e.g., `dev`, `released`).
|
|
||||||
3. **Scope Boundary Check**: Confirmation that the route is listed in `route_scope_matrix.json` as in-scope for this change.
|
|
||||||
4. **Contract Baseline Refs**: References to existing route contracts and content contracts that define expected behavior.
|
|
||||||
5. **Known-Bug Baseline Ref**: Reference to `known_bug_baseline.json` entry for this route (or confirmation that baseline is initialized).
|
|
||||||
6. **Rollback Flag Plan**: Planned feature flag key and rollback strategy for this route's cutover.
|
|
||||||
7. **Owner**: The person/team responsible for this route's modernization.
|
|
||||||
8. **Date**: Date of confirmation.
|
|
||||||
9. **Approved By**: Reviewer who approved the pre-change confirmation.
|
|
||||||
|
|
||||||
## Template
|
|
||||||
|
|
||||||
```
|
|
||||||
Route: /<route-name>
|
|
||||||
Status Snapshot: <dev|released>
|
|
||||||
Scope Boundary Check: confirmed in route_scope_matrix.json as in-scope
|
|
||||||
Contract Baseline Refs:
|
|
||||||
- Route contract: route_contracts.json#/<route-name>
|
|
||||||
- Content contract: route_content_contracts.json#/<route-name>
|
|
||||||
Known-Bug Baseline Ref: known_bug_baseline.json#/<route-name>
|
|
||||||
Rollback Flag Plan:
|
|
||||||
- Feature flag: modernization_feature_flags.json#/<route-name>.content_cutover_enabled
|
|
||||||
- Rollback strategy: fallback_to_legacy_route
|
|
||||||
Owner: <owner>
|
|
||||||
Date: <YYYY-MM-DD>
|
|
||||||
Approved By: <reviewer>
|
|
||||||
```
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "deferred-route-modernization-follow-up",
|
|
||||||
"rule": "implementation is blocked for a route until its pre-change confirmation is recorded and approved",
|
|
||||||
"required_fields": [
|
|
||||||
"route",
|
|
||||||
"status_snapshot",
|
|
||||||
"scope_boundary_check",
|
|
||||||
"contract_baseline_refs",
|
|
||||||
"known_bug_baseline_ref",
|
|
||||||
"rollback_flag_plan",
|
|
||||||
"owner",
|
|
||||||
"date",
|
|
||||||
"approved_by"
|
|
||||||
],
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"status_snapshot": "dev",
|
|
||||||
"scope_boundary_check": "confirmed in route_scope_matrix.json as in-scope (promoted from deferred)",
|
|
||||||
"contract_baseline_refs": {
|
|
||||||
"route_contract": "route_contracts.json#/tables",
|
|
||||||
"content_contract": "route_content_contracts.json#/tables"
|
|
||||||
},
|
|
||||||
"known_bug_baseline_ref": "known_bug_baseline.json#/tables",
|
|
||||||
"rollback_flag_plan": {
|
|
||||||
"feature_flag": "modernization_feature_flags.json#/tables.content_cutover_enabled",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"date": "2026-02-12",
|
|
||||||
"approved_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"status_snapshot": "dev",
|
|
||||||
"scope_boundary_check": "confirmed in route_scope_matrix.json as in-scope (promoted from deferred)",
|
|
||||||
"contract_baseline_refs": {
|
|
||||||
"route_contract": "route_contracts.json#/excel-query",
|
|
||||||
"content_contract": "route_content_contracts.json#/excel-query"
|
|
||||||
},
|
|
||||||
"known_bug_baseline_ref": "known_bug_baseline.json#/excel-query",
|
|
||||||
"rollback_flag_plan": {
|
|
||||||
"feature_flag": "modernization_feature_flags.json#/excel-query.content_cutover_enabled",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"date": "2026-02-12",
|
|
||||||
"approved_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"status_snapshot": "dev",
|
|
||||||
"scope_boundary_check": "confirmed in route_scope_matrix.json as in-scope (promoted from deferred)",
|
|
||||||
"contract_baseline_refs": {
|
|
||||||
"route_contract": "route_contracts.json#/query-tool",
|
|
||||||
"content_contract": "route_content_contracts.json#/query-tool"
|
|
||||||
},
|
|
||||||
"known_bug_baseline_ref": "known_bug_baseline.json#/query-tool",
|
|
||||||
"rollback_flag_plan": {
|
|
||||||
"feature_flag": "modernization_feature_flags.json#/query-tool.content_cutover_enabled",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"date": "2026-02-12",
|
|
||||||
"approved_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"status_snapshot": "dev",
|
|
||||||
"scope_boundary_check": "confirmed in route_scope_matrix.json as in-scope (promoted from deferred)",
|
|
||||||
"contract_baseline_refs": {
|
|
||||||
"route_contract": "route_contracts.json#/mid-section-defect",
|
|
||||||
"content_contract": "route_content_contracts.json#/mid-section-defect"
|
|
||||||
},
|
|
||||||
"known_bug_baseline_ref": "known_bug_baseline.json#/mid-section-defect",
|
|
||||||
"rollback_flag_plan": {
|
|
||||||
"feature_flag": "modernization_feature_flags.json#/mid-section-defect.content_cutover_enabled",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"date": "2026-02-12",
|
|
||||||
"approved_by": "deferred-route-modernization-follow-up"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Deferred Route Modernization Rollback Controls
|
|
||||||
|
|
||||||
## Route-Level Reversion Controls
|
|
||||||
|
|
||||||
- **Content cutover feature flag**: Set `content_cutover_enabled: false` in `data/modernization_feature_flags.json` for the affected route to immediately revert to legacy content path.
|
|
||||||
- **PORTAL_SPA_ENABLED=false**: Disable shell-first navigation runtime globally (affects all routes).
|
|
||||||
- **Route-scoped contract fallback**: Mark route contract with fallback strategy and redeploy shell assets.
|
|
||||||
|
|
||||||
## Per-Route Rollback Procedure
|
|
||||||
|
|
||||||
1. Set `content_cutover_enabled: false` for the affected route in `modernization_feature_flags.json`.
|
|
||||||
2. Restart workers to pick up the flag change.
|
|
||||||
3. Verify legacy content path is serving correctly.
|
|
||||||
4. Record rollback in manual acceptance records with reason and timestamp.
|
|
||||||
5. Investigate root cause before re-enabling cutover.
|
|
||||||
|
|
||||||
## False-Positive Gate Handling
|
|
||||||
|
|
||||||
1. Capture failing gate output and route impact.
|
|
||||||
2. Confirm whether failure is test flake or product defect.
|
|
||||||
3. If false-positive and production risk is high:
|
|
||||||
- Temporarily switch gate severity from `block` to `warn`.
|
|
||||||
- Record waiver with owner, reason, expiry.
|
|
||||||
4. Restore `block` mode after corrective action.
|
|
||||||
|
|
||||||
## Required Rollback Evidence
|
|
||||||
|
|
||||||
- Incident timestamp and impacted route.
|
|
||||||
- Gate IDs that triggered rollback.
|
|
||||||
- Feature flag state before/after rollback.
|
|
||||||
- Manual acceptance and known-bug replay references.
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Deferred Route Modernization Rollout Runbook
|
|
||||||
|
|
||||||
## Upstream Reference
|
|
||||||
|
|
||||||
- Phase 1 runbook: `docs/migration/full-modernization-architecture-blueprint/rollout_runbook.md`
|
|
||||||
|
|
||||||
## Phase Sequence
|
|
||||||
|
|
||||||
1. Governance freeze
|
|
||||||
- Confirm `route_scope_matrix.json` has deferred routes promoted to in-scope.
|
|
||||||
- Confirm pre-change confirmations recorded for all 4 routes.
|
|
||||||
- Confirm exception registry has no unresolved blocking entries.
|
|
||||||
|
|
||||||
2. Route governance enforcement
|
|
||||||
- Run route contract completeness checks in warn mode.
|
|
||||||
- Fix all deferred-route metadata gaps.
|
|
||||||
- Promote route governance checks to block mode.
|
|
||||||
|
|
||||||
3. Per-route content modernization (sequential)
|
|
||||||
- Enable content cutover flag for first route (`/tables`).
|
|
||||||
- Execute parity checks and manual acceptance.
|
|
||||||
- Run known-bug replay checks.
|
|
||||||
- On pass: sign off and proceed to next route.
|
|
||||||
- On fail: rollback flag and investigate.
|
|
||||||
|
|
||||||
4. Cutover sequence
|
|
||||||
- `/tables` -> `/excel-query` -> `/query-tool` -> `/mid-section-defect`
|
|
||||||
- Next route blocked until current route has approved sign-off.
|
|
||||||
|
|
||||||
5. Asset/gate enforcement
|
|
||||||
- Validate deferred route asset readiness.
|
|
||||||
- Run quality gate suite (functional, visual, accessibility, performance).
|
|
||||||
- Promote gate severity from warn to block per milestones.
|
|
||||||
|
|
||||||
6. Fallback retirement
|
|
||||||
- Retire runtime fallback for deferred routes after all acceptance gates pass.
|
|
||||||
|
|
||||||
## Hold Points
|
|
||||||
|
|
||||||
- Hold-1: Any deferred route missing contract metadata or pre-change confirmation.
|
|
||||||
- Hold-2: Any parity failure or known-bug replay failure.
|
|
||||||
- Hold-3: Any mandatory quality gate failure in block mode.
|
|
||||||
- Hold-4: Cutover attempted before previous route sign-off complete.
|
|
||||||
|
|
||||||
## Promotion Rule
|
|
||||||
|
|
||||||
Promotion is allowed only when all hold points are clear for the current route in sequence.
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "deferred-route-modernization-follow-up",
|
|
||||||
"upstream_change": "full-modernization-architecture-blueprint",
|
|
||||||
"generated_at": "2026-02-12T00:00:00Z",
|
|
||||||
"phase": "phase-2-deferred-route-modernization",
|
|
||||||
"policy": {
|
|
||||||
"scope_is_frozen": true,
|
|
||||||
"out_of_scope_tasks_must_be_rejected": true,
|
|
||||||
"dev_routes_are_eligible": true,
|
|
||||||
"released_only_restriction_does_not_apply": true
|
|
||||||
},
|
|
||||||
"in_scope": [
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/tables",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"prior_scope": "deferred",
|
|
||||||
"prior_change": "full-modernization-architecture-blueprint"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/excel-query",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"prior_scope": "deferred",
|
|
||||||
"prior_change": "full-modernization-architecture-blueprint"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/query-tool",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"prior_scope": "deferred",
|
|
||||||
"prior_change": "full-modernization-architecture-blueprint"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/mid-section-defect",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"prior_scope": "deferred",
|
|
||||||
"prior_change": "full-modernization-architecture-blueprint"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"out_of_scope": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/wip-detail",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-detail",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/pages",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/performance",
|
|
||||||
"reason": "completed-in-phase-1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Scope Boundary Note
|
|
||||||
|
|
||||||
## Change
|
|
||||||
|
|
||||||
`deferred-route-modernization-follow-up`
|
|
||||||
|
|
||||||
## Scope Clarification
|
|
||||||
|
|
||||||
Deferred routes (`/tables`, `/excel-query`, `/query-tool`, `/mid-section-defect`) are **in-scope** for this follow-up change regardless of their current page status (`dev` or `released`).
|
|
||||||
|
|
||||||
The **released-only restriction does not apply** to this change. These routes were intentionally deferred from phase 1 to control blast radius and are now the explicit modernization target.
|
|
||||||
|
|
||||||
## What Is In-Scope
|
|
||||||
|
|
||||||
- `/tables` — currently `dev` in page registry
|
|
||||||
- `/excel-query` — currently `dev` in page registry
|
|
||||||
- `/query-tool` — currently `dev` in page registry
|
|
||||||
- `/mid-section-defect` — currently `dev` in page registry
|
|
||||||
|
|
||||||
## What Is NOT In-Scope
|
|
||||||
|
|
||||||
- Phase-1 routes that are already modernized and governed (10 report + 2 admin routes)
|
|
||||||
- Routes outside the deferred matrix
|
|
||||||
- Backend business data semantics beyond compatibility safeguards
|
|
||||||
- Unrelated admin/report features not in the deferred matrix
|
|
||||||
|
|
||||||
## Policy
|
|
||||||
|
|
||||||
- Phase-1 in-scope routes SHALL NOT be reopened by this change unless explicitly required for shared governance wiring.
|
|
||||||
- Deferred routes adopt identical governance rigor (contracts, parity, manual acceptance, bug replay) as phase-1 routes.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "deferred-route-modernization-follow-up",
|
|
||||||
"upstream_change": "full-modernization-architecture-blueprint",
|
|
||||||
"handoff_artifact": "docs/migration/full-modernization-architecture-blueprint/deferred_route_handoff.md",
|
|
||||||
"consumed_artifacts": {
|
|
||||||
"scope_boundary": "docs/migration/full-modernization-architecture-blueprint/route_scope_matrix.json",
|
|
||||||
"parity_fixtures": "docs/migration/full-modernization-architecture-blueprint/parity_golden_fixtures.json",
|
|
||||||
"interaction_parity_checks": "docs/migration/full-modernization-architecture-blueprint/interaction_parity_checks.json",
|
|
||||||
"manual_acceptance_checklist": "docs/migration/full-modernization-architecture-blueprint/page_content_manual_acceptance_checklist.md",
|
|
||||||
"known_bug_baseline": "docs/migration/full-modernization-architecture-blueprint/known_bug_baseline.json",
|
|
||||||
"bug_revalidation_records": "docs/migration/full-modernization-architecture-blueprint/bug_revalidation_records.json",
|
|
||||||
"quality_gate_policy": "docs/migration/full-modernization-architecture-blueprint/quality_gate_policy.json",
|
|
||||||
"governance_milestones": "docs/migration/full-modernization-architecture-blueprint/governance_milestones.md",
|
|
||||||
"asset_readiness_manifest": "docs/migration/full-modernization-architecture-blueprint/asset_readiness_manifest.json"
|
|
||||||
},
|
|
||||||
"transfer_rules": [
|
|
||||||
"Deferred routes remain excluded from phase-1 blocking criteria",
|
|
||||||
"Follow-up change MUST promote these routes to in-scope before legacy retirement",
|
|
||||||
"Equivalent parity/manual-acceptance/bug-revalidation gates must be applied"
|
|
||||||
],
|
|
||||||
"deferred_routes": [
|
|
||||||
"/tables",
|
|
||||||
"/excel-query",
|
|
||||||
"/query-tool",
|
|
||||||
"/mid-section-defect"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Full Modernization Architecture Blueprint Artifacts
|
|
||||||
|
|
||||||
This directory stores execution artifacts for `full-modernization-architecture-blueprint`.
|
|
||||||
|
|
||||||
## Core Governance
|
|
||||||
|
|
||||||
- `route_scope_matrix.json`: frozen in-scope/deferred route contract matrix.
|
|
||||||
- `governance_milestones.md`: completion and deprecation milestones.
|
|
||||||
- `exception_registry.json`: approved temporary exceptions with owner and milestone.
|
|
||||||
- Policy artifact runtime cache model:
|
|
||||||
- `src/mes_dashboard/core/modernization_policy.py` caches `route_scope_matrix.json` and
|
|
||||||
`asset_readiness_manifest.json` in-process with `lru_cache`.
|
|
||||||
- Runtime behavior is restart-refresh by default: JSON edits take effect after worker restart.
|
|
||||||
- Controlled refresh is available through `clear_modernization_policy_cache()` for tests or
|
|
||||||
explicit maintenance hooks; no automatic file watcher/hot reload is active in production.
|
|
||||||
|
|
||||||
## Content Modernization Safety
|
|
||||||
|
|
||||||
- `page_content_manual_acceptance_checklist.md`: mandatory manual sign-off checklist.
|
|
||||||
- `known_bug_baseline.json`: route-level known bug baseline and replay blocking policy.
|
|
||||||
|
|
||||||
## Rollout Operations
|
|
||||||
|
|
||||||
- `rollout_runbook.md`: phase steps and hold points.
|
|
||||||
- `rollback_controls.md`: rollback and false-positive gate handling.
|
|
||||||
- `observability_checkpoints.md`: route/gate/rollback observability contract.
|
|
||||||
- `deferred_route_handoff.md`: explicit handoff package to deferred-route follow-up change.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"in_scope_required_assets": {
|
|
||||||
"/portal-shell": ["portal-shell.html", "portal-shell.js", "portal-shell.css", "tailwind.css"],
|
|
||||||
"/wip-overview": ["wip-overview.html", "wip-overview.js"],
|
|
||||||
"/wip-detail": ["wip-detail.html", "wip-detail.js"],
|
|
||||||
"/hold-overview": ["hold-overview.html", "hold-overview.js"],
|
|
||||||
"/hold-detail": ["hold-detail.html", "hold-detail.js"],
|
|
||||||
"/hold-history": ["hold-history.html", "hold-history.js"],
|
|
||||||
"/resource": ["resource-status.html", "resource-status.js"],
|
|
||||||
"/resource-history": ["resource-history.html", "resource-history.js"],
|
|
||||||
"/qc-gate": ["qc-gate.html", "qc-gate.js"],
|
|
||||||
"/job-query": ["job-query.js"],
|
|
||||||
"/tmtt-defect": ["tmtt-defect.js"],
|
|
||||||
"/tables": ["tables.html", "tables.js"],
|
|
||||||
"/excel-query": ["excel-query.js"],
|
|
||||||
"/query-tool": ["query-tool.js"],
|
|
||||||
"/mid-section-defect": ["mid-section-defect.html", "mid-section-defect.js"]
|
|
||||||
},
|
|
||||||
"deferred_routes": []
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"records": [],
|
|
||||||
"schema": {
|
|
||||||
"route": "string",
|
|
||||||
"known_bug_id": "string",
|
|
||||||
"replay_date": "YYYY-MM-DD",
|
|
||||||
"result": "pass|fail",
|
|
||||||
"notes": "string",
|
|
||||||
"signoff_owner": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"canonical_policy": {
|
|
||||||
"applies_when": "PORTAL_SPA_ENABLED=true",
|
|
||||||
"report_routes": "redirect direct route entry to /portal-shell/<route>",
|
|
||||||
"admin_routes": "shell target redirects to backend /admin/* while backend keeps auth authority"
|
|
||||||
},
|
|
||||||
"direct_entry_compatibility": {
|
|
||||||
"query_semantics_must_be_preserved": true,
|
|
||||||
"redirect_status_code": 302
|
|
||||||
},
|
|
||||||
"in_scope_report_routes": [
|
|
||||||
"/wip-overview",
|
|
||||||
"/wip-detail",
|
|
||||||
"/hold-overview",
|
|
||||||
"/hold-detail",
|
|
||||||
"/hold-history",
|
|
||||||
"/resource",
|
|
||||||
"/resource-history",
|
|
||||||
"/qc-gate",
|
|
||||||
"/job-query",
|
|
||||||
"/tmtt-defect",
|
|
||||||
"/tables",
|
|
||||||
"/excel-query",
|
|
||||||
"/query-tool",
|
|
||||||
"/mid-section-defect"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Deferred Route Handoff (Phase 1 -> Follow-up)
|
|
||||||
|
|
||||||
## Source Change
|
|
||||||
|
|
||||||
- `openspec/changes/full-modernization-architecture-blueprint/`
|
|
||||||
|
|
||||||
## Deferred Routes (Not in Phase 1 Blocking Scope)
|
|
||||||
|
|
||||||
- `/tables`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/query-tool`
|
|
||||||
- `/mid-section-defect`
|
|
||||||
|
|
||||||
## Follow-up Change
|
|
||||||
|
|
||||||
- `openspec/changes/deferred-route-modernization-follow-up/`
|
|
||||||
|
|
||||||
## Handoff Content
|
|
||||||
|
|
||||||
1. Scope boundary contract:
|
|
||||||
- Source: `docs/migration/full-modernization-architecture-blueprint/route_scope_matrix.json`
|
|
||||||
|
|
||||||
2. Required acceptance model to carry forward:
|
|
||||||
- Parity fixtures/checks:
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/parity_golden_fixtures.json`
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/interaction_parity_checks.json`
|
|
||||||
- Manual acceptance + bug replay:
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/page_content_manual_acceptance_checklist.md`
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/known_bug_baseline.json`
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/bug_revalidation_records.json`
|
|
||||||
|
|
||||||
3. Governance policy to carry forward:
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/quality_gate_policy.json`
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/governance_milestones.md`
|
|
||||||
- `docs/migration/full-modernization-architecture-blueprint/asset_readiness_manifest.json`
|
|
||||||
|
|
||||||
## Transfer Rule
|
|
||||||
|
|
||||||
- Deferred routes remain excluded from phase-1 blocking criteria.
|
|
||||||
- Follow-up change MUST promote these routes to in-scope and apply equivalent parity/manual-acceptance/bug-revalidation gates before legacy retirement.
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"fields": [
|
|
||||||
"id",
|
|
||||||
"type",
|
|
||||||
"scope",
|
|
||||||
"owner",
|
|
||||||
"introduced_by",
|
|
||||||
"reason",
|
|
||||||
"mitigation",
|
|
||||||
"status",
|
|
||||||
"milestone",
|
|
||||||
"tracking_issue"
|
|
||||||
],
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"id": "style-admin-pages-inline-css",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/admin/pages",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "admin pages remain backend-rendered in this phase",
|
|
||||||
"mitigation": "enforce shell contract governance while retaining backend auth authority",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/admin-template-modernization"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "style-admin-performance-inline-css",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/admin/performance",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "admin performance remains backend-rendered in this phase",
|
|
||||||
"mitigation": "governed navigation + route-level fallback controls",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/admin-template-modernization"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "style-excel-query-shell-tokens-no-fallback",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/excel-query",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "shell tokens without CSS fallback values; inherited from legacy era",
|
|
||||||
"mitigation": "add fallback values during content modernization cutover",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/excel-query-style-hardening"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "style-query-tool-shell-tokens-no-fallback",
|
|
||||||
"type": "style",
|
|
||||||
"scope": "/query-tool",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"introduced_by": "legacy-template",
|
|
||||||
"reason": "shell tokens without CSS fallback values; inherited from legacy era",
|
|
||||||
"mitigation": "add fallback values during content modernization cutover",
|
|
||||||
"status": "approved-temporary",
|
|
||||||
"milestone": "2026-03-19",
|
|
||||||
"tracking_issue": "deferred-route-modernization-follow-up/query-tool-style-hardening"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Full Modernization Governance Milestones
|
|
||||||
|
|
||||||
## Phase Completion Criteria
|
|
||||||
|
|
||||||
A phase is complete only when all criteria below are true:
|
|
||||||
|
|
||||||
1. Route governance: 100% of in-scope routes in `route_scope_matrix.json` have valid shell contract metadata and ownership.
|
|
||||||
2. Style governance: in-scope route-local styles do not introduce page-global selectors (`:root`, `body`) unless recorded in exception registry.
|
|
||||||
3. Quality governance: functional parity, visual checkpoints, accessibility checks, and performance budgets pass at configured gate severity.
|
|
||||||
4. Content safety governance: page-content parity evidence + manual acceptance sign-off exist for each migrated in-scope route.
|
|
||||||
5. Bug carry-over governance: known-bug replay checks for migrated scope do not reproduce legacy defects.
|
|
||||||
|
|
||||||
## Legacy Deprecation Milestones
|
|
||||||
|
|
||||||
- 2026-02-20: route contract CI completeness gate enabled in `warn` mode.
|
|
||||||
- 2026-02-27: route contract CI completeness gate promoted to `block` mode.
|
|
||||||
- 2026-03-05: in-scope asset readiness gate promoted to `block` mode.
|
|
||||||
- 2026-03-12: runtime fallback posture retired for in-scope routes in production policy.
|
|
||||||
- 2026-03-19: unresolved style exceptions past milestone fail modernization review.
|
|
||||||
|
|
||||||
## Deferred Route Linkage
|
|
||||||
|
|
||||||
Deferred routes are not pass/fail criteria in this phase and are handed over to a follow-up change:
|
|
||||||
|
|
||||||
- `/tables`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/query-tool`
|
|
||||||
- `/mid-section-defect`
|
|
||||||
|
|
||||||
Follow-up change handoff is recorded in `openspec/changes/deferred-route-modernization-follow-up/`.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"required_flows": {
|
|
||||||
"/wip-overview": ["filter_apply", "filter_reset", "status_card_drill"],
|
|
||||||
"/wip-detail": ["list_detail_continuity", "pagination", "filter_reset"],
|
|
||||||
"/hold-overview": ["reason_toggle", "matrix_selection", "lot_scope_sync"],
|
|
||||||
"/hold-detail": ["reason_required_redirect", "distribution_toggle", "lot_scope_sync"],
|
|
||||||
"/hold-history": ["date_range_query", "pareto_selection", "duration_bucket_selection"],
|
|
||||||
"/resource": ["status_filter", "workcenter_family_filter", "tooltip_open_close"],
|
|
||||||
"/resource-history": ["query_submit", "export_csv", "chart_detail_sync"],
|
|
||||||
"/qc-gate": ["chart_bucket_selection", "table_filter_sync", "filter_clear"],
|
|
||||||
"/job-query": ["resource_select", "query_submit", "export_csv"],
|
|
||||||
"/tmtt-defect": ["date_range_query", "chart_filter_link", "detail_sort"],
|
|
||||||
"/admin/pages": ["drawer_crud", "page_status_update", "admin_visibility"],
|
|
||||||
"/admin/performance": ["admin_auth_access", "performance_view_load"],
|
|
||||||
"/tables": ["table_select", "column_filter_apply", "query_submit"],
|
|
||||||
"/excel-query": ["file_upload", "column_select", "query_execute", "export_csv"],
|
|
||||||
"/query-tool": ["lot_resolve", "history_load", "association_query", "equipment_period_query", "export_csv"],
|
|
||||||
"/mid-section-defect": ["date_range_query", "loss_reason_filter", "pareto_chart_drill", "detail_pagination", "export_csv"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"scope": "all-governed-routes",
|
|
||||||
"routes": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/hold-overview": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/hold-history": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/resource": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/qc-gate": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/job-query": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/admin/pages": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/admin/performance": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/tables": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
},
|
|
||||||
"/mid-section-defect": {
|
|
||||||
"baseline_status": "initialized",
|
|
||||||
"known_bugs": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"revalidation_rule": {
|
|
||||||
"reproduced_legacy_bug_blocks_signoff": true,
|
|
||||||
"reproduced_legacy_bug_blocks_legacy_retirement": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"records": [],
|
|
||||||
"rule": "next route cutover is blocked until current route has approved manual sign-off"
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Modernization Observability Checkpoints
|
|
||||||
|
|
||||||
## Route Governance Signals
|
|
||||||
|
|
||||||
1. `navigation_contract_mismatch_total`
|
|
||||||
- Source: `/api/portal/navigation` diagnostics.
|
|
||||||
- Alert condition: non-zero for in-scope routes.
|
|
||||||
|
|
||||||
2. `route_contract_missing_metadata_total`
|
|
||||||
- Source: route governance CI script.
|
|
||||||
- Alert condition: >0 in block mode.
|
|
||||||
|
|
||||||
## Quality Gate Signals
|
|
||||||
|
|
||||||
1. `quality_gate_failed_total{gate_id}`
|
|
||||||
- Source: quality gate report.
|
|
||||||
- Alert condition: any mandatory gate failed.
|
|
||||||
|
|
||||||
2. `manual_acceptance_pending_routes`
|
|
||||||
- Source: manual acceptance records.
|
|
||||||
- Alert condition: cutover attempted with pending sign-off.
|
|
||||||
|
|
||||||
## Fallback and Rollback Signals
|
|
||||||
|
|
||||||
1. `in_scope_runtime_fallback_served_total`
|
|
||||||
- Should remain zero after fallback retirement milestone.
|
|
||||||
|
|
||||||
2. `content_cutover_flag_rollbacks_total`
|
|
||||||
- Track frequency and route impact.
|
|
||||||
|
|
||||||
3. `legacy_bug_replay_failures_total`
|
|
||||||
- Any non-zero indicates carry-over risk and blocks sign-off.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# Page Content Manual Acceptance Checklist
|
|
||||||
|
|
||||||
## Rule
|
|
||||||
|
|
||||||
- Route cutover proceeds one route at a time.
|
|
||||||
- Next route cutover is blocked until current route is manually signed off.
|
|
||||||
- Legacy code retirement is blocked until parity checks and manual sign-off are both complete.
|
|
||||||
|
|
||||||
## Mandatory Checks Per Route
|
|
||||||
|
|
||||||
1. Filter semantics match baseline (apply, reset, URL/query continuity).
|
|
||||||
2. Chart interactions match baseline (drill/selection/clear behavior).
|
|
||||||
3. Empty/loading/error/success states are correct and non-overlapping.
|
|
||||||
4. Table/chart linked interactions remain deterministic.
|
|
||||||
5. Accessibility: keyboard flow, focus visibility, `aria-*` semantics, reduced-motion behavior.
|
|
||||||
6. Known-bug replay checks completed (see `known_bug_baseline.json`).
|
|
||||||
7. No reproduced legacy bug in migrated scope.
|
|
||||||
|
|
||||||
## Sign-Off Template
|
|
||||||
|
|
||||||
- Route:
|
|
||||||
- Owner:
|
|
||||||
- Reviewer:
|
|
||||||
- Date:
|
|
||||||
- Parity evidence links:
|
|
||||||
- Known-bug replay result:
|
|
||||||
- Blocking defects:
|
|
||||||
- Decision: `approved` | `rework-required`
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"fixtures": {
|
|
||||||
"/wip-overview": ["tests/fixtures/frontend_compute_parity.json"],
|
|
||||||
"/wip-detail": ["docs/migration/portal-shell-route-view-integration/wave-b-parity-evidence.json"],
|
|
||||||
"/hold-overview": ["docs/migration/portal-shell-route-view-integration/baseline_interaction_evidence.json"],
|
|
||||||
"/hold-detail": ["docs/migration/portal-shell-route-view-integration/baseline_interaction_evidence.json"],
|
|
||||||
"/hold-history": ["docs/migration/portal-shell-route-view-integration/baseline_interaction_evidence.json"],
|
|
||||||
"/resource": ["docs/migration/portal-shell-route-view-integration/baseline_api_payload_contracts.json"],
|
|
||||||
"/resource-history": ["docs/migration/portal-shell-route-view-integration/baseline_api_payload_contracts.json"],
|
|
||||||
"/qc-gate": ["docs/migration/portal-shell-route-view-integration/visual-regression-snapshots.json"],
|
|
||||||
"/job-query": ["docs/migration/portal-shell-route-view-integration/wave-b-parity-evidence.json"],
|
|
||||||
"/tmtt-defect": ["docs/migration/portal-shell-route-view-integration/wave-b-parity-evidence.json"],
|
|
||||||
"/admin/pages": ["tests/test_portal_shell_routes.py"],
|
|
||||||
"/admin/performance": ["tests/test_performance_integration.py"],
|
|
||||||
"/tables": ["tests/test_portal_shell_routes.py"],
|
|
||||||
"/excel-query": ["tests/test_portal_shell_routes.py"],
|
|
||||||
"/query-tool": ["tests/test_portal_shell_routes.py"],
|
|
||||||
"/mid-section-defect": ["tests/test_portal_shell_routes.py"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"targets": {
|
|
||||||
"shell_navigation_api_avg_ms": 150,
|
|
||||||
"shell_navigation_api_p95_ms": 350,
|
|
||||||
"portal_shell_entry_avg_ms": 200,
|
|
||||||
"portal_shell_entry_p95_ms": 450
|
|
||||||
},
|
|
||||||
"source_baselines": [
|
|
||||||
"docs/migration/portal-no-iframe/performance_baseline_spa.json",
|
|
||||||
"docs/migration/portal-no-iframe/performance_baseline_legacy.json"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"severity_mode": {
|
|
||||||
"current": "warn",
|
|
||||||
"promotion_target": "block",
|
|
||||||
"promotion_milestone": "2026-02-27"
|
|
||||||
},
|
|
||||||
"gates": [
|
|
||||||
{"id": "Q1", "name": "functional-parity", "required": true},
|
|
||||||
{"id": "Q2", "name": "visual-regression", "required": true},
|
|
||||||
{"id": "Q3", "name": "accessibility-keyboard-aria-motion", "required": true},
|
|
||||||
{"id": "Q4", "name": "performance-budget", "required": true},
|
|
||||||
{"id": "Q5", "name": "manual-acceptance-and-bug-revalidation", "required": true}
|
|
||||||
],
|
|
||||||
"deferred_routes_excluded": []
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"mode": "block",
|
|
||||||
"errors": [],
|
|
||||||
"warnings": [
|
|
||||||
"/excel-query uses shell tokens without fallback ['--portal-shadow-panel'] in frontend/src/excel-query/style.css with approved exception"
|
|
||||||
],
|
|
||||||
"info": [],
|
|
||||||
"passed": true
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Full Modernization Rollback Controls
|
|
||||||
|
|
||||||
## Route-Level Reversion Controls
|
|
||||||
|
|
||||||
- `PORTAL_SPA_ENABLED=false`:
|
|
||||||
Disable shell-first navigation runtime globally.
|
|
||||||
- Route-scoped contract fallback:
|
|
||||||
Mark route contract with fallback strategy and redeploy shell assets.
|
|
||||||
- Content cutover feature flag:
|
|
||||||
Disable route-level modernized content path while keeping shell runtime up.
|
|
||||||
|
|
||||||
## False-Positive Gate Handling
|
|
||||||
|
|
||||||
1. Capture failing gate output and route impact.
|
|
||||||
2. Confirm whether failure is test flake or product defect.
|
|
||||||
3. If false-positive and production risk is high:
|
|
||||||
- Temporarily switch gate severity from `block` to `warn`.
|
|
||||||
- Record waiver with owner, reason, expiry.
|
|
||||||
4. Restore `block` mode after corrective action.
|
|
||||||
|
|
||||||
## Required Rollback Evidence
|
|
||||||
|
|
||||||
- Incident timestamp and impacted routes.
|
|
||||||
- Gate IDs that triggered rollback.
|
|
||||||
- Route contract state before/after rollback.
|
|
||||||
- Manual acceptance and known-bug replay references.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Full Modernization Rollout Runbook
|
|
||||||
|
|
||||||
## Phase Sequence
|
|
||||||
|
|
||||||
1. Governance freeze
|
|
||||||
- Confirm `route_scope_matrix.json` has no pending route-scope changes.
|
|
||||||
- Confirm exception registry entries include owner + milestone.
|
|
||||||
|
|
||||||
2. Route governance enforcement
|
|
||||||
- Run route contract completeness checks in warn mode.
|
|
||||||
- Fix all in-scope metadata gaps.
|
|
||||||
- Promote route governance checks to block mode.
|
|
||||||
|
|
||||||
3. Style/content hardening
|
|
||||||
- Apply style isolation checks for in-scope routes.
|
|
||||||
- Execute parity checks and manual acceptance route-by-route.
|
|
||||||
- Run known-bug replay checks per route.
|
|
||||||
|
|
||||||
4. Asset/gate enforcement
|
|
||||||
- Validate in-scope asset readiness.
|
|
||||||
- Run quality gate suite (functional, visual, accessibility, performance).
|
|
||||||
- Promote gate severity from warn to block according to policy.
|
|
||||||
|
|
||||||
## Hold Points
|
|
||||||
|
|
||||||
- Hold-1: Any in-scope route missing contract metadata.
|
|
||||||
- Hold-2: Any unresolved style exception past milestone.
|
|
||||||
- Hold-3: Any parity failure or known-bug replay failure.
|
|
||||||
- Hold-4: Any mandatory quality gate failure in block mode.
|
|
||||||
|
|
||||||
## Promotion Rule
|
|
||||||
|
|
||||||
Promotion is allowed only when all hold points are clear and no deferred-route checks were incorrectly included as blockers.
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"in_scope_routes": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"filter_input_semantics": ["workorder", "lotid", "package", "type", "status"],
|
|
||||||
"query_payload_contract": ["workorder", "lotid", "package", "type", "status"],
|
|
||||||
"chart_data_shape": ["status_cards", "matrix", "pareto"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"filter_input_semantics": ["workcenter", "workorder", "lotid", "package", "type", "status", "page"],
|
|
||||||
"query_payload_contract": ["workcenter", "workorder", "lotid", "package", "type", "status", "page"],
|
|
||||||
"chart_data_shape": ["summary_cards", "lot_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/hold-overview": {
|
|
||||||
"filter_input_semantics": ["hold_type", "reason", "workcenter", "package", "age_range", "page"],
|
|
||||||
"query_payload_contract": ["hold_type", "reason", "workcenter", "package", "age_range", "page"],
|
|
||||||
"chart_data_shape": ["summary_cards", "matrix", "treemap", "lot_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"filter_input_semantics": ["reason", "workcenter", "package", "age_range", "page"],
|
|
||||||
"query_payload_contract": ["reason", "workcenter", "package", "age_range", "page"],
|
|
||||||
"chart_data_shape": ["summary_cards", "distribution", "lot_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/hold-history": {
|
|
||||||
"filter_input_semantics": ["start_date", "end_date", "hold_type", "record_type", "reason", "duration_range", "page"],
|
|
||||||
"query_payload_contract": ["start_date", "end_date", "hold_type", "record_type", "reason", "duration_range", "page"],
|
|
||||||
"chart_data_shape": ["trend", "reason_pareto", "duration", "detail_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/resource": {
|
|
||||||
"filter_input_semantics": ["workcenter_groups", "families", "resource_ids", "statuses"],
|
|
||||||
"query_payload_contract": ["workcenter_groups", "families", "resource_ids", "statuses"],
|
|
||||||
"chart_data_shape": ["summary_cards", "status_matrix", "equipment_grid"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"filter_input_semantics": ["start_date", "end_date", "granularity", "workcenter_groups", "families", "resource_ids", "is_production", "is_key", "is_monitor"],
|
|
||||||
"query_payload_contract": ["start_date", "end_date", "granularity", "workcenter_groups", "families", "resource_ids", "is_production", "is_key", "is_monitor"],
|
|
||||||
"chart_data_shape": ["kpi", "trend", "heatmap", "workcenter_comparison", "detail"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/qc-gate": {
|
|
||||||
"filter_input_semantics": ["chart_bucket_selection", "table_sort"],
|
|
||||||
"query_payload_contract": ["summary", "table", "pareto"],
|
|
||||||
"chart_data_shape": ["station_stack_chart", "linked_lot_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/job-query": {
|
|
||||||
"filter_input_semantics": ["resource_ids", "start_date", "end_date"],
|
|
||||||
"query_payload_contract": ["resource_ids", "start_date", "end_date"],
|
|
||||||
"chart_data_shape": ["job_table", "txn_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"filter_input_semantics": ["start_date", "end_date", "sort", "page"],
|
|
||||||
"query_payload_contract": ["start_date", "end_date"],
|
|
||||||
"chart_data_shape": ["kpi", "pareto", "trend", "detail_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/admin/pages": {
|
|
||||||
"filter_input_semantics": ["drawer_crud", "page_status_edit", "order_updates"],
|
|
||||||
"query_payload_contract": ["drawers", "pages", "status"],
|
|
||||||
"chart_data_shape": ["n/a"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/admin/performance": {
|
|
||||||
"filter_input_semantics": ["date_range", "severity", "search"],
|
|
||||||
"query_payload_contract": ["performance_summary", "log_stream"],
|
|
||||||
"chart_data_shape": ["timeline", "status_summary"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/tables": {
|
|
||||||
"filter_input_semantics": ["table_category", "table_name", "dynamic_column_filters"],
|
|
||||||
"query_payload_contract": ["table_name", "limit", "time_field", "filters"],
|
|
||||||
"chart_data_shape": ["dynamic_data_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"filter_input_semantics": ["file_upload", "excel_column", "table_name", "search_column", "query_type", "return_columns", "date_range"],
|
|
||||||
"query_payload_contract": ["table_name", "search_column", "return_columns", "search_values", "query_type", "date_column", "date_from", "date_to"],
|
|
||||||
"chart_data_shape": ["result_table", "csv_export"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"filter_input_semantics": ["input_type", "workcenter_groups", "input_values", "association_type", "equipment_ids", "equipment_query_type", "start_date", "end_date"],
|
|
||||||
"query_payload_contract": ["input_type", "values", "container_id", "workcenter_groups", "type", "equipment_ids", "equipment_names", "start_date", "end_date", "query_type"],
|
|
||||||
"chart_data_shape": ["resolved_lots_table", "lot_history_table", "association_table", "equipment_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
},
|
|
||||||
"/mid-section-defect": {
|
|
||||||
"filter_input_semantics": ["start_date", "end_date", "loss_reasons"],
|
|
||||||
"query_payload_contract": ["start_date", "end_date", "loss_reasons", "page", "page_size"],
|
|
||||||
"chart_data_shape": ["kpi_cards", "pareto_by_station", "pareto_by_loss_reason", "pareto_by_machine", "pareto_by_tmtt_machine", "pareto_by_workflow", "pareto_by_package", "daily_trend", "detail_table"],
|
|
||||||
"state_contract": ["loading", "empty", "error", "success"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"generated_at": "2026-02-12T00:00:00Z",
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"route_id": "wip-overview",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/wip-overview",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/wip-detail",
|
|
||||||
"route_id": "wip-detail",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/wip-detail",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"route_id": "hold-overview",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-overview",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-detail",
|
|
||||||
"route_id": "hold-detail",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-detail",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"route_id": "hold-history",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-history",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"route_id": "resource",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/resource",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"route_id": "resource-history",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/resource-history",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"route_id": "qc-gate",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/qc-gate",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"route_id": "job-query",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/job-query",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"route_id": "tmtt-defect",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/tmtt-defect",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/pages",
|
|
||||||
"route_id": "admin-pages",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "external",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"visibility_policy": "admin_only",
|
|
||||||
"canonical_shell_path": "/portal-shell/admin/pages",
|
|
||||||
"rollback_strategy": "external_route_reversion"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/performance",
|
|
||||||
"route_id": "admin-performance",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "external",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"visibility_policy": "admin_only",
|
|
||||||
"canonical_shell_path": "/portal-shell/admin/performance",
|
|
||||||
"rollback_strategy": "external_route_reversion"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"route_id": "tables",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/tables",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"route_id": "excel-query",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/excel-query",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"route_id": "query-tool",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/query-tool",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"route_id": "mid-section-defect",
|
|
||||||
"scope": "in-scope",
|
|
||||||
"render_mode": "native",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/mid-section-defect",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"generated_at": "2026-02-12T00:00:00Z",
|
|
||||||
"phase": "phase-1-shell-and-content-modernization",
|
|
||||||
"policy": {
|
|
||||||
"scope_is_frozen": true,
|
|
||||||
"out_of_scope_tasks_must_be_rejected": true
|
|
||||||
},
|
|
||||||
"in_scope": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/wip-overview",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/wip-detail",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/wip-detail",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-overview",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-detail",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-detail",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/hold-history",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/resource",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/resource-history",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/qc-gate",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/job-query",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/tmtt-defect",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/pages",
|
|
||||||
"category": "admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/admin/pages",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"visibility_policy": "admin_only"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/performance",
|
|
||||||
"category": "admin",
|
|
||||||
"canonical_shell_path": "/portal-shell/admin/performance",
|
|
||||||
"owner": "frontend-platform-admin",
|
|
||||||
"visibility_policy": "admin_only"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/tables",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"promoted_from": "deferred",
|
|
||||||
"promoted_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/excel-query",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"promoted_from": "deferred",
|
|
||||||
"promoted_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/query-tool",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"promoted_from": "deferred",
|
|
||||||
"promoted_by": "deferred-route-modernization-follow-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"category": "report",
|
|
||||||
"canonical_shell_path": "/portal-shell/mid-section-defect",
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"visibility_policy": "released_or_admin",
|
|
||||||
"promoted_from": "deferred",
|
|
||||||
"promoted_by": "deferred-route-modernization-follow-up"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"deferred": []
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"change": "full-modernization-architecture-blueprint",
|
|
||||||
"generated_at": "2026-02-12T00:00:00Z",
|
|
||||||
"in_scope_routes": {
|
|
||||||
"/wip-overview": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/wip-detail": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/hold-overview": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/hold-detail": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/hold-history": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/resource": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/resource-history": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/qc-gate": {"global_selectors": [], "status": "clean-after-refactor"},
|
|
||||||
"/job-query": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/tmtt-defect": {"global_selectors": [], "status": "clean"},
|
|
||||||
"/admin/pages": {
|
|
||||||
"global_selectors": ["body"],
|
|
||||||
"status": "exception-registered",
|
|
||||||
"exception_id": "style-admin-pages-inline-css"
|
|
||||||
},
|
|
||||||
"/admin/performance": {
|
|
||||||
"global_selectors": ["body"],
|
|
||||||
"status": "exception-registered",
|
|
||||||
"exception_id": "style-admin-performance-inline-css"
|
|
||||||
},
|
|
||||||
"/tables": {"global_selectors": [], "status": "pending-audit"},
|
|
||||||
"/excel-query": {"global_selectors": [], "status": "pending-audit"},
|
|
||||||
"/query-tool": {"global_selectors": [], "status": "pending-audit"},
|
|
||||||
"/mid-section-defect": {"global_selectors": [], "status": "pending-audit"}
|
|
||||||
},
|
|
||||||
"shared_layers": {
|
|
||||||
"frontend/src/styles/tailwind.css": [":root", "body"],
|
|
||||||
"frontend/src/portal-shell/style.css": [":root", "body"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# OpenSpec Archive Readiness (`portal-no-iframe-navigation`)
|
|
||||||
|
|
||||||
## Spec Sync Scope
|
|
||||||
|
|
||||||
Main specs synchronized/updated for this change:
|
|
||||||
|
|
||||||
- `openspec/specs/full-vite-page-modularization/spec.md`
|
|
||||||
- `openspec/specs/portal-drawer-navigation/spec.md`
|
|
||||||
- `openspec/specs/vue-vite-page-architecture/spec.md`
|
|
||||||
- `openspec/specs/migration-gates-and-rollout/spec.md`
|
|
||||||
- `openspec/specs/spa-shell-navigation/spec.md` (new)
|
|
||||||
- `openspec/specs/tailwind-design-system/spec.md` (new)
|
|
||||||
- `openspec/specs/frontend-motion-system/spec.md` (new)
|
|
||||||
- `openspec/specs/legacy-page-wrapper-strategy/spec.md` (new)
|
|
||||||
|
|
||||||
## Migration Closure Artifacts
|
|
||||||
|
|
||||||
- Rewrite smoke checklist:
|
|
||||||
- `docs/migration/portal-no-iframe/legacy_rewrite_smoke_checklists.md`
|
|
||||||
- Rewrite exemplar:
|
|
||||||
- `docs/migration/portal-no-iframe/tmtt_rewrite_exemplar.md`
|
|
||||||
- Rewrite playbook:
|
|
||||||
- `docs/migration/portal-no-iframe/legacy_rewrite_playbook.md`
|
|
||||||
- Wrapper decommission record:
|
|
||||||
- `docs/migration/portal-no-iframe/wrapper_decommission_report.md`
|
|
||||||
- Frame field retirement record:
|
|
||||||
- `docs/migration/portal-no-iframe/frame_id_tool_src_deprecation_plan.md`
|
|
||||||
|
|
||||||
## Pre-Archive Checklist
|
|
||||||
|
|
||||||
- [x] `openspec validate portal-no-iframe-navigation --strict` passes.
|
|
||||||
- [x] Build and core migration tests pass on latest branch.
|
|
||||||
- [x] Task list in `openspec/changes/portal-no-iframe-navigation/tasks.md` is fully checked.
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"source": "current frontend API consumption contracts",
|
|
||||||
"apis": {
|
|
||||||
"/api/wip/overview/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"dataUpdateDate",
|
|
||||||
"runLots",
|
|
||||||
"queueLots",
|
|
||||||
"holdLots"
|
|
||||||
],
|
|
||||||
"notes": "summary header and cards depend on these fields"
|
|
||||||
},
|
|
||||||
"/api/wip/overview/matrix": {
|
|
||||||
"required_keys": [
|
|
||||||
"workcenters",
|
|
||||||
"packages",
|
|
||||||
"matrix",
|
|
||||||
"workcenter_totals"
|
|
||||||
],
|
|
||||||
"notes": "matrix table rendering contract"
|
|
||||||
},
|
|
||||||
"/api/wip/hold-detail/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"workcenterCount",
|
|
||||||
"packageCount",
|
|
||||||
"lotCount"
|
|
||||||
],
|
|
||||||
"notes": "hold detail summary cards contract"
|
|
||||||
},
|
|
||||||
"/api/resource/history/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"kpi",
|
|
||||||
"trend",
|
|
||||||
"heatmap",
|
|
||||||
"workcenter_comparison"
|
|
||||||
],
|
|
||||||
"notes": "resource history chart summary contract"
|
|
||||||
},
|
|
||||||
"/api/resource/history/detail": {
|
|
||||||
"required_keys": [
|
|
||||||
"data"
|
|
||||||
],
|
|
||||||
"notes": "detail table contract (plus truncated/max_records metadata when present)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"source": "data/page_status.json",
|
|
||||||
"errors": []
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-12T02:26:36.887797+00:00",
|
|
||||||
"source": "data/page_status.json",
|
|
||||||
"admin": [
|
|
||||||
{
|
|
||||||
"id": "reports",
|
|
||||||
"name": "即時報表",
|
|
||||||
"order": 1,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"name": "WIP 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"name": "Hold 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"name": "設備即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"name": "QC-GATE 狀態",
|
|
||||||
"status": "released",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer-2",
|
|
||||||
"name": "歷史報表",
|
|
||||||
"order": 2,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"name": "Hold 歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"name": "設備歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer",
|
|
||||||
"name": "查詢工具",
|
|
||||||
"order": 3,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"name": "設備維修查詢",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dev-tools",
|
|
||||||
"name": "開發工具",
|
|
||||||
"order": 4,
|
|
||||||
"admin_only": true,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"name": "表格總覽",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/pages",
|
|
||||||
"name": "頁面管理",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"name": "Excel 批次查詢",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/performance",
|
|
||||||
"name": "效能監控",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"name": "批次追蹤工具",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"name": "TMTT印字腳型不良分析",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"name": "中段製程不良追溯",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"non_admin": [
|
|
||||||
{
|
|
||||||
"id": "reports",
|
|
||||||
"name": "即時報表",
|
|
||||||
"order": 1,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"name": "WIP 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"name": "Hold 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"name": "設備即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"name": "QC-GATE 狀態",
|
|
||||||
"status": "released",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer-2",
|
|
||||||
"name": "歷史報表",
|
|
||||||
"order": 2,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"name": "Hold 歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"name": "設備歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer",
|
|
||||||
"name": "查詢工具",
|
|
||||||
"order": 3,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"name": "設備維修查詢",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"source": "frontend route parsing and current parity matrix",
|
|
||||||
"routes": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"query_keys": [
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"notes": "filters + status URL state must remain compatible"
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"query_keys": [
|
|
||||||
"workcenter",
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"notes": "workcenter deep-link and back-link query continuity"
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"query_keys": [
|
|
||||||
"reason"
|
|
||||||
],
|
|
||||||
"notes": "reason required for normal access flow"
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"query_keys": [
|
|
||||||
"start_date",
|
|
||||||
"end_date",
|
|
||||||
"granularity",
|
|
||||||
"workcenter_groups",
|
|
||||||
"families",
|
|
||||||
"resource_ids",
|
|
||||||
"is_production",
|
|
||||||
"is_key",
|
|
||||||
"is_monitor"
|
|
||||||
],
|
|
||||||
"notes": "query/export params must remain compatible"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Drawer Governance Contract (Portal No-Iframe Migration)
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This contract defines drawer behavior that must remain stable during migration.
|
|
||||||
|
|
||||||
## Canonical Responsibilities
|
|
||||||
|
|
||||||
Drawer metadata is responsible for:
|
|
||||||
|
|
||||||
- Information architecture grouping.
|
|
||||||
- Display order.
|
|
||||||
- Access visibility (`admin_only`).
|
|
||||||
|
|
||||||
Drawer metadata is not responsible for:
|
|
||||||
|
|
||||||
- Content embedding mode (`iframe`, `toolFrame`).
|
|
||||||
- Rendering technology selection (Jinja vs SPA route view).
|
|
||||||
|
|
||||||
## Contract Rules
|
|
||||||
|
|
||||||
1. Drawer IDs must be unique and non-empty.
|
|
||||||
2. Page routes must be unique and non-empty.
|
|
||||||
3. `page.drawer_id` (when present) must reference an existing drawer.
|
|
||||||
4. `order` values (when present) must be positive integers.
|
|
||||||
5. Page status must be one of `released` or `dev`.
|
|
||||||
6. Visibility outcomes must be deterministic for admin/non-admin users.
|
|
||||||
|
|
||||||
## Deterministic Rendering Order
|
|
||||||
|
|
||||||
Drawers:
|
|
||||||
|
|
||||||
- Primary sort by `order` ascending.
|
|
||||||
- Secondary sort by `name` ascending.
|
|
||||||
|
|
||||||
Pages in each drawer:
|
|
||||||
|
|
||||||
- Primary sort by `order` ascending.
|
|
||||||
- Secondary sort by `(name or route)` ascending.
|
|
||||||
|
|
||||||
## Visibility Semantics
|
|
||||||
|
|
||||||
- Non-admin users can view only `released` pages in non-admin-only drawers.
|
|
||||||
- Admin users can view all drawer-assigned pages according to current page status policy.
|
|
||||||
- Drawers with zero visible pages are hidden.
|
|
||||||
|
|
||||||
## Validation Artifacts
|
|
||||||
|
|
||||||
- `baseline_drawer_contract_validation.json`
|
|
||||||
- `baseline_drawer_visibility.json`
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# `frame_id` / `tool_src` Deprecation Plan
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
- Retirement completed in this change.
|
|
||||||
- Runtime navigation payload generation no longer emits:
|
|
||||||
- `frame_id`
|
|
||||||
- `tool_src`
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Frame-era fields were used for iframe loading compatibility:
|
|
||||||
|
|
||||||
- `frame_id`
|
|
||||||
- `tool_src`
|
|
||||||
|
|
||||||
## Policy
|
|
||||||
|
|
||||||
Deprecation is phased and must not break active routes.
|
|
||||||
|
|
||||||
## Phases
|
|
||||||
|
|
||||||
1. **Compatibility phase**:
|
|
||||||
- Keep fields in payload.
|
|
||||||
- Ensure new router navigation logic does not rely on these fields.
|
|
||||||
2. **Dual-run phase**:
|
|
||||||
- Validate all navigation paths without frame fields.
|
|
||||||
3. **Retirement readiness**:
|
|
||||||
- Wrapper-first pages are stable in shell.
|
|
||||||
- Cutover gates G1~G7 are green in rehearsal.
|
|
||||||
4. **Removal phase**:
|
|
||||||
- Remove generation and downstream usage of `frame_id/tool_src`.
|
|
||||||
- Update related tests and docs.
|
|
||||||
|
|
||||||
## Removal Checkpoints
|
|
||||||
|
|
||||||
- Checkpoint A: drawer parity stable in canary.
|
|
||||||
- Checkpoint B: legacy wrappers stable with no frame-field dependency.
|
|
||||||
- Checkpoint C: rollback mechanism verified independent of frame fields. ✓
|
|
||||||
|
|
||||||
## Risk Controls
|
|
||||||
|
|
||||||
- Keep rollback-safe path via route-level navigation and kill-switch.
|
|
||||||
- Keep gate coverage for route/drawer/workflow parity after field removal.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Legacy Rewrite Playbook (Batch-2)
|
|
||||||
|
|
||||||
## Target Pages
|
|
||||||
|
|
||||||
This playbook governs rewrite execution for the remaining three legacy pages:
|
|
||||||
|
|
||||||
- `/job-query`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/query-tool`
|
|
||||||
|
|
||||||
Rewrite order follows `legacy_rewrite_priority_matrix.md`.
|
|
||||||
|
|
||||||
## Canonical Steps
|
|
||||||
|
|
||||||
1. Preserve route and API contracts first.
|
|
||||||
2. Move page state and API calls into composables (`use<Page>Data`).
|
|
||||||
3. Replace page-local repeated blocks with shared UI components where possible.
|
|
||||||
4. Keep Tailwind token alignment for new/changed UI.
|
|
||||||
5. Validate with per-page smoke checklist before/after switch.
|
|
||||||
|
|
||||||
## Required Acceptance (Per Page)
|
|
||||||
|
|
||||||
- Route reachable and functional without shell wrapper.
|
|
||||||
- Core query workflow succeeds and returns expected result sections.
|
|
||||||
- Export path remains usable (where applicable).
|
|
||||||
- No new unhandled runtime error on the primary path.
|
|
||||||
- Checklist IDs pass:
|
|
||||||
- `/job-query`: `JOB-SMOKE-01`~`JOB-SMOKE-06`
|
|
||||||
- `/excel-query`: `EXCEL-SMOKE-01`~`EXCEL-SMOKE-06`
|
|
||||||
- `/query-tool`: `QTOOL-SMOKE-01`~`QTOOL-SMOKE-06`
|
|
||||||
|
|
||||||
Checklist source:
|
|
||||||
|
|
||||||
- `docs/migration/portal-no-iframe/legacy_rewrite_smoke_checklists.md`
|
|
||||||
|
|
||||||
## Shared Guardrails
|
|
||||||
|
|
||||||
- Do not change backend API signatures in rewrite phase.
|
|
||||||
- Keep direct-link behavior and query semantics stable.
|
|
||||||
- If parity fails, rollback to previous stable page artifact before continuing.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Legacy Rewrite Priority Matrix
|
|
||||||
|
|
||||||
## Scoring model
|
|
||||||
|
|
||||||
`priority_score = usage(0-5)*0.3 + complexity(0-5)*0.4 + risk(0-5)*0.3`
|
|
||||||
|
|
||||||
- Usage: current observed operational usage + business criticality.
|
|
||||||
- Complexity: route/API count, frontend LOC, workflow branches.
|
|
||||||
- Risk: data mutation/export/upload sensitivity and regression blast radius.
|
|
||||||
|
|
||||||
## Measured technical baseline
|
|
||||||
|
|
||||||
| Page | Backend route LOC | Template LOC | Frontend LOC | API surface |
|
|
||||||
| --- | ---: | ---: | ---: | --- |
|
|
||||||
| `query-tool` | 509 | 1267 | 3139 | resolve/history/adjacent/associations/equipment/export |
|
|
||||||
| `excel-query` | 355 | 1181 | 624 | upload/schema/query/export |
|
|
||||||
| `job-query` | 195 | 995 | 520 | resources/jobs/txn/export |
|
|
||||||
| `tmtt-defect` | 82 | 271 | 363 | analysis/query + CSV export |
|
|
||||||
|
|
||||||
## Priority scoring
|
|
||||||
|
|
||||||
| Page | Usage | Complexity | Risk | Score |
|
|
||||||
| --- | ---: | ---: | ---: | ---: |
|
|
||||||
| `tmtt-defect` | 2 | 1 | 2 | 1.6 |
|
|
||||||
| `job-query` | 3 | 2 | 3 | 2.6 |
|
|
||||||
| `excel-query` | 3 | 4 | 4 | 3.7 |
|
|
||||||
| `query-tool` | 4 | 5 | 5 | 4.7 |
|
|
||||||
|
|
||||||
## Rewrite order decision
|
|
||||||
|
|
||||||
1. `tmtt-defect` (canonical exemplar)
|
|
||||||
2. `job-query`
|
|
||||||
3. `excel-query`
|
|
||||||
4. `query-tool`
|
|
||||||
|
|
||||||
## Rationale
|
|
||||||
|
|
||||||
- Start with lowest-complexity page to establish shared migration playbook.
|
|
||||||
- Keep high-complexity/high-risk `query-tool` last to maximize reuse from prior rewrites.
|
|
||||||
- Defer upload-heavy `excel-query` until shared error/retry/upload patterns are stabilized.
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Legacy Rewrite Smoke Checklists (Per-Page)
|
|
||||||
|
|
||||||
本文件是 `7.2 ~ 7.4` 的執行前置與驗收基準。
|
|
||||||
每一頁在「重寫前(wrapper baseline)」與「重寫後(rewrite candidate)」都必須執行同一組 smoke。
|
|
||||||
|
|
||||||
## 0. 執行規則
|
|
||||||
|
|
||||||
- 必須記錄:執行日期、分支/commit、執行人、環境(DEV/UAT)。
|
|
||||||
- 每頁 smoke 通過率要求:`100%`。
|
|
||||||
- 任何 P0 smoke 失敗即視為 `No-Go`,不得進入 wrapper 移除。
|
|
||||||
- `excel-query`、`query-tool` 為 admin/dev 可見頁,需使用 admin 身份執行。
|
|
||||||
|
|
||||||
## 1. `tmtt-defect`(Rewrite Exemplar)
|
|
||||||
|
|
||||||
### 前置條件
|
|
||||||
- 可取得有效 `start_date/end_date` 測試區間。
|
|
||||||
- `/api/tmtt-defect/analysis` 與 `/api/tmtt-defect/export` 可連線。
|
|
||||||
|
|
||||||
### Smoke Cases
|
|
||||||
- [ ] `TMTT-SMOKE-01` Route reachable: `/tmtt-defect` 可直接開啟,無白屏/JS error。
|
|
||||||
- [ ] `TMTT-SMOKE-02` Required params guard: 缺少日期時,顯示明確錯誤且不崩潰。
|
|
||||||
- [ ] `TMTT-SMOKE-03` Query success: 送出合法日期後,KPI/Charts/Detail 皆成功渲染。
|
|
||||||
- [ ] `TMTT-SMOKE-04` Drill-down: 點擊 Pareto 圖欄位可套用/清除篩選,明細同步。
|
|
||||||
- [ ] `TMTT-SMOKE-05` Table behavior: 明細表格可排序,排序方向切換正確。
|
|
||||||
- [ ] `TMTT-SMOKE-06` Export CSV: 匯出成功,response 為 CSV 且檔名包含日期區間。
|
|
||||||
|
|
||||||
## 2. `job-query`
|
|
||||||
|
|
||||||
### 前置條件
|
|
||||||
- `resource` 清單可取得。
|
|
||||||
- 至少有一組可查詢日期區間。
|
|
||||||
|
|
||||||
### Smoke Cases
|
|
||||||
- [ ] `JOB-SMOKE-01` Route reachable: `/job-query` 可直接開啟。
|
|
||||||
- [ ] `JOB-SMOKE-02` Resource loading: `/api/job-query/resources` 回傳清單,UI 可選取。
|
|
||||||
- [ ] `JOB-SMOKE-03` Query jobs: 選設備+日期後可查詢成功並顯示結果。
|
|
||||||
- [ ] `JOB-SMOKE-04` Txn detail: 由查詢結果可開啟某筆 job 的 txn history。
|
|
||||||
- [ ] `JOB-SMOKE-05` Export CSV: 匯出成功且檔案可下載。
|
|
||||||
- [ ] `JOB-SMOKE-06` Validation: 缺日期/無設備/超過上限時回傳明確錯誤訊息。
|
|
||||||
|
|
||||||
## 3. `excel-query`(Admin)
|
|
||||||
|
|
||||||
### 前置條件
|
|
||||||
- 準備一份有效 `.xlsx` 測試檔。
|
|
||||||
- Admin session 已登入。
|
|
||||||
|
|
||||||
### Smoke Cases
|
|
||||||
- [ ] `EXCEL-SMOKE-01` Route/auth: `/excel-query` admin 可進入,非 admin 受保護。
|
|
||||||
- [ ] `EXCEL-SMOKE-02` Upload: 上傳有效 Excel 後可解析欄位與預覽。
|
|
||||||
- [ ] `EXCEL-SMOKE-03` Column detect: 欄位唯一值與型別偵測可正常運作。
|
|
||||||
- [ ] `EXCEL-SMOKE-04` Execute query: 標準查詢與進階查詢都可回傳資料。
|
|
||||||
- [ ] `EXCEL-SMOKE-05` Export CSV: 查詢結果可匯出 CSV。
|
|
||||||
- [ ] `EXCEL-SMOKE-06` Invalid file guard: 非 `.xls/.xlsx` 檔案被拒絕且回傳可讀錯誤。
|
|
||||||
|
|
||||||
## 4. `query-tool`(Admin)
|
|
||||||
|
|
||||||
### 前置條件
|
|
||||||
- Admin session 已登入。
|
|
||||||
- 可用測試 lot/equipment/date range。
|
|
||||||
|
|
||||||
### Smoke Cases
|
|
||||||
- [ ] `QTOOL-SMOKE-01` Route reachable: `/query-tool` 可開啟。
|
|
||||||
- [ ] `QTOOL-SMOKE-02` Resolve flow: lot_id/serial/work_order 至少一種解析成功。
|
|
||||||
- [ ] `QTOOL-SMOKE-03` History flow: lot history 可查詢並顯示。
|
|
||||||
- [ ] `QTOOL-SMOKE-04` Adjacent flow: adjacent lots 查詢可回傳。
|
|
||||||
- [ ] `QTOOL-SMOKE-05` Associations: materials/rejects/holds/splits/jobs 查詢可用。
|
|
||||||
- [ ] `QTOOL-SMOKE-06` Equipment period: status_hours/lots/materials/rejects/jobs 至少各成功一次。
|
|
||||||
- [ ] `QTOOL-SMOKE-07` Export CSV: 匯出可下載且欄位合理。
|
|
||||||
- [ ] `QTOOL-SMOKE-08` Validation: 缺參數、非法日期範圍會回傳可讀錯誤。
|
|
||||||
|
|
||||||
## 5. Exit Rule(與 7.4 連動)
|
|
||||||
|
|
||||||
只有在下列條件全成立,才可移除 wrapper:
|
|
||||||
|
|
||||||
- [ ] 四頁 rewrite smoke 全部通過。
|
|
||||||
- [ ] 與 `legacy_wrapper_telemetry_contract.md` 對照,error 率在門檻內。
|
|
||||||
- [ ] 與 `parity_checklist.md` 的 Route/Workflow/API contract 檢查一致。
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Legacy Wrapper Exit Criteria (Rewrite-ready)
|
|
||||||
|
|
||||||
A wrapped page is rewrite-ready only when all criteria are met.
|
|
||||||
|
|
||||||
## Functional readiness
|
|
||||||
|
|
||||||
1. Core workflows are documented with at least one deterministic smoke script per workflow.
|
|
||||||
2. Route/query contract is frozen and covered by contract tests.
|
|
||||||
3. Export/upload side effects (if any) are reproducible in test or staging.
|
|
||||||
|
|
||||||
## Technical readiness
|
|
||||||
|
|
||||||
1. Shared UI and composables can cover at least 70% of page scaffolding (filters, cards, table shell, pagination).
|
|
||||||
2. Required API payload key/type contract is stable for two consecutive releases.
|
|
||||||
3. Wrapper telemetry shows no unresolved high-severity navigation failures in the last release cycle.
|
|
||||||
|
|
||||||
## Operational readiness
|
|
||||||
|
|
||||||
1. Rollback path for rewritten page is documented and rehearsed.
|
|
||||||
2. Error budget and success threshold for canary are defined before rewrite starts.
|
|
||||||
3. Product owner confirms parity acceptance checklist for the target page.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Legacy Wrapper Telemetry Contract
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
- Retired after wrapper decommission.
|
|
||||||
- `POST /api/portal/wrapper-telemetry` has been removed.
|
|
||||||
- Reference only for historical migration traceability.
|
|
||||||
|
|
||||||
## Wrapper scope
|
|
||||||
|
|
||||||
- `/job-query`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/query-tool`
|
|
||||||
- `/tmtt-defect`
|
|
||||||
|
|
||||||
## Client events
|
|
||||||
|
|
||||||
- `wrapper_loaded`: wrapper route rendered in shell.
|
|
||||||
- `launch`: user clicked "進入既有頁面" and navigation handoff started.
|
|
||||||
|
|
||||||
## API endpoint
|
|
||||||
|
|
||||||
- `POST /api/portal/wrapper-telemetry`
|
|
||||||
- Payload:
|
|
||||||
- `event_type: string`
|
|
||||||
- `route: string` (must be one of wrapper scope routes)
|
|
||||||
- `page_name?: string`
|
|
||||||
- `drawer_name?: string`
|
|
||||||
- `duration_ms?: number`
|
|
||||||
- `ts?: string`
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- Reject unknown routes with `400`.
|
|
||||||
- Reject missing `event_type` with `400`.
|
|
||||||
|
|
||||||
## Fallback behavior
|
|
||||||
|
|
||||||
- Wrapper UI always provides direct anchor navigation to the legacy route.
|
|
||||||
- Telemetry failure must not block navigation.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Motion Baseline Guidelines (Vue Transition First)
|
|
||||||
|
|
||||||
## Baseline principles
|
|
||||||
|
|
||||||
1. Motion clarifies state change, not decoration.
|
|
||||||
2. Default to short transitions (180ms - 240ms) with easing.
|
|
||||||
3. Keep animation on container level (route/panel/filter-chip), avoid animating large table row sets.
|
|
||||||
|
|
||||||
## Standard transitions
|
|
||||||
|
|
||||||
- Route change: `route-fade` (`opacity + translateY`) in portal shell.
|
|
||||||
- Drawer navigation: hover/active transition on sidebar links.
|
|
||||||
- Filter apply/remove: `TransitionGroup` chip enter/leave motion.
|
|
||||||
- Data refresh pulse: panel-level pulse when chart/table refresh is running.
|
|
||||||
|
|
||||||
## Accessibility
|
|
||||||
|
|
||||||
- Respect `prefers-reduced-motion: reduce`.
|
|
||||||
- All key transitions must have non-animated fallback styles.
|
|
||||||
- Motion must not block interaction or delay data rendering.
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# GSAP Escalation Rule
|
|
||||||
|
|
||||||
## Default
|
|
||||||
|
|
||||||
Use Vue native transitions and CSS transitions for portal migration work.
|
|
||||||
|
|
||||||
## GSAP allowed only when all conditions are true
|
|
||||||
|
|
||||||
1. Interaction cannot be expressed with native Vue/CSS transitions without major maintainability cost.
|
|
||||||
2. Animation is business-critical (e.g., complex timeline playback or synchronized multi-chart storytelling).
|
|
||||||
3. Reduced-motion fallback is explicitly implemented.
|
|
||||||
4. Performance impact is measured on target hardware and meets baseline thresholds.
|
|
||||||
5. A rollback switch exists to disable advanced animation without breaking functionality.
|
|
||||||
|
|
||||||
## Approval checklist
|
|
||||||
|
|
||||||
- Document the exact scenario and why Vue/CSS is insufficient.
|
|
||||||
- Add test coverage for degraded/non-animated path.
|
|
||||||
- Confirm bundle-size impact is acceptable for the target route.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Shared Pagination Migration Batch 1
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Migrated pages/components:
|
|
||||||
|
|
||||||
- `wip-detail/components/LotTable.vue`
|
|
||||||
- `hold-detail/components/LotTable.vue`
|
|
||||||
- `hold-overview/components/LotTable.vue`
|
|
||||||
- `hold-history/components/DetailTable.vue`
|
|
||||||
- `mid-section-defect/components/DetailTable.vue`
|
|
||||||
|
|
||||||
## Change
|
|
||||||
|
|
||||||
- Replaced direct/inline pagination rendering with shared `PaginationControl`.
|
|
||||||
- Preserved existing page event contracts (`prev-page`, `next-page`).
|
|
||||||
|
|
||||||
## Visual parity checks
|
|
||||||
|
|
||||||
- Pagination visibility still depends on `totalPages > 1`.
|
|
||||||
- Prev/Next button enablement remains bounded by page range.
|
|
||||||
- Page info text format remains unchanged on migrated views.
|
|
||||||
|
|
||||||
## Removed duplicated artifacts
|
|
||||||
|
|
||||||
- Removed local Prev/Next markup and boundary logic from `hold-history/components/DetailTable.vue`.
|
|
||||||
- Consolidated pagination behavior into shared wrapper for this batch.
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Portal No-Iframe Migration Parity Checklist
|
|
||||||
|
|
||||||
This checklist is the execution companion for `portal-no-iframe-navigation` migration.
|
|
||||||
|
|
||||||
## A. Drawer Visibility Parity
|
|
||||||
|
|
||||||
- [ ] Non-admin visible drawers/routes match `baseline_drawer_visibility.json` exactly.
|
|
||||||
- [ ] Admin visible drawers/routes match `baseline_drawer_visibility.json` exactly.
|
|
||||||
- [ ] Empty drawers remain hidden.
|
|
||||||
- [ ] `admin_only` drawer behavior remains unchanged.
|
|
||||||
|
|
||||||
## B. Route and Query Contract Parity
|
|
||||||
|
|
||||||
- [ ] `/wip-overview` preserves `workorder|lotid|package|type|status` URL semantics.
|
|
||||||
- [ ] `/wip-detail` preserves `workcenter|workorder|lotid|package|type|status` URL semantics.
|
|
||||||
- [ ] `/hold-detail` preserves required `reason` semantics and fallback behavior.
|
|
||||||
- [ ] `/resource-history` preserves date/granularity/group/family/resource/flag query semantics.
|
|
||||||
|
|
||||||
## C. Core Workflow Smoke Paths
|
|
||||||
|
|
||||||
- [ ] Legacy rewrite per-page smoke checklist passes (`legacy_rewrite_smoke_checklists.md`).
|
|
||||||
- [ ] `/` open portal and switch via drawer navigation.
|
|
||||||
- [ ] `/wip-overview` apply filters and drill down to `/wip-detail`.
|
|
||||||
- [ ] `/wip-overview` reason drill-down to `/hold-detail`.
|
|
||||||
- [ ] `/resource-history` execute query and export path.
|
|
||||||
- [ ] Legacy rewrite pages (`/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`) remain reachable and usable.
|
|
||||||
|
|
||||||
## D. API Payload Contract Parity
|
|
||||||
|
|
||||||
- [ ] `/api/wip/overview/summary` required keys present.
|
|
||||||
- [ ] `/api/wip/overview/matrix` required keys present.
|
|
||||||
- [ ] `/api/wip/hold-detail/summary` required keys present.
|
|
||||||
- [ ] `/api/resource/history/summary` required keys present.
|
|
||||||
- [ ] `/api/resource/history/detail` required keys present.
|
|
||||||
|
|
||||||
## E. Stability and Performance
|
|
||||||
|
|
||||||
- [ ] No unhandled JS runtime errors on critical E2E paths.
|
|
||||||
- [ ] Route switch latency remains within agreed threshold.
|
|
||||||
- [ ] Memory footprint does not regress beyond agreed threshold.
|
|
||||||
|
|
||||||
## F. Cutover Decision
|
|
||||||
|
|
||||||
- [ ] All G1~G7 gates are green.
|
|
||||||
- [ ] Rollback rehearsal result is recent and valid.
|
|
||||||
- [ ] Cutover owner and rollback owner are explicitly assigned.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Performance Baseline Comparison
|
|
||||||
|
|
||||||
Measured via Flask test client (route latency in ms).
|
|
||||||
|
|
||||||
## Key Entry Routes
|
|
||||||
|
|
||||||
| Surface | Avg (ms) | P95 (ms) |
|
|
||||||
| --- | ---: | ---: |
|
|
||||||
| Legacy portal `/` | 1.557 | 0.891 |
|
|
||||||
| SPA shell `/portal-shell` | 0.239 | 0.263 |
|
|
||||||
|
|
||||||
## Shared API Route
|
|
||||||
|
|
||||||
| Route | Legacy Avg (ms) | SPA Avg (ms) | Delta (ms) |
|
|
||||||
| --- | ---: | ---: | ---: |
|
|
||||||
| `/api/portal/navigation` | 0.341 | 0.313 | -0.028 |
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- This baseline is synthetic (test client), used for migration regression gate trend tracking.
|
|
||||||
- Production browser/network RUM should be captured separately during canary rollout.
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"portal_spa_enabled": false,
|
|
||||||
"samples_per_route": 15,
|
|
||||||
"metrics": [
|
|
||||||
{
|
|
||||||
"route": "/",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 1.557,
|
|
||||||
"p95_ms": 0.891,
|
|
||||||
"min_ms": 0.536,
|
|
||||||
"max_ms": 14.997,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/api/portal/navigation",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.341,
|
|
||||||
"p95_ms": 0.396,
|
|
||||||
"min_ms": 0.284,
|
|
||||||
"max_ms": 0.404,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.683,
|
|
||||||
"p95_ms": 0.925,
|
|
||||||
"min_ms": 0.422,
|
|
||||||
"max_ms": 2.633,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.413,
|
|
||||||
"p95_ms": 0.506,
|
|
||||||
"min_ms": 0.337,
|
|
||||||
"max_ms": 0.699,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.422,
|
|
||||||
"p95_ms": 0.453,
|
|
||||||
"min_ms": 0.371,
|
|
||||||
"max_ms": 0.615,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"portal_spa_enabled": true,
|
|
||||||
"samples_per_route": 15,
|
|
||||||
"metrics": [
|
|
||||||
{
|
|
||||||
"route": "/portal-shell",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.239,
|
|
||||||
"p95_ms": 0.263,
|
|
||||||
"min_ms": 0.169,
|
|
||||||
"max_ms": 0.708,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/api/portal/navigation",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.313,
|
|
||||||
"p95_ms": 0.412,
|
|
||||||
"min_ms": 0.257,
|
|
||||||
"max_ms": 0.437,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.904,
|
|
||||||
"p95_ms": 0.786,
|
|
||||||
"min_ms": 0.33,
|
|
||||||
"max_ms": 7.345,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.47,
|
|
||||||
"p95_ms": 0.448,
|
|
||||||
"min_ms": 0.324,
|
|
||||||
"max_ms": 1.951,
|
|
||||||
"status_codes": [
|
|
||||||
403
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.448,
|
|
||||||
"p95_ms": 0.802,
|
|
||||||
"min_ms": 0.32,
|
|
||||||
"max_ms": 0.849,
|
|
||||||
"status_codes": [
|
|
||||||
403
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"samples": 15,
|
|
||||||
"avg_ms": 0.583,
|
|
||||||
"p95_ms": 0.585,
|
|
||||||
"min_ms": 0.323,
|
|
||||||
"max_ms": 3.455,
|
|
||||||
"status_codes": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Portal No-Iframe Migration Rollback Rehearsal Runbook
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Validate that navigation can be restored to pre-cutover stable behavior within target SLO.
|
|
||||||
|
|
||||||
- Target recovery SLO: <= 15 minutes
|
|
||||||
|
|
||||||
## Trigger Conditions
|
|
||||||
|
|
||||||
Execute rollback when any of the following occur after cutover:
|
|
||||||
|
|
||||||
- P0 route unavailable or broken workflow.
|
|
||||||
- Drawer visibility parity mismatch.
|
|
||||||
- Critical API payload contract mismatch causing page failure.
|
|
||||||
- Severe runtime JS errors on critical user paths.
|
|
||||||
|
|
||||||
## Preconditions
|
|
||||||
|
|
||||||
- Feature-flag/env toggle path for shell cutover is in place.
|
|
||||||
- Latest baseline snapshots are available under `docs/migration/portal-no-iframe/`.
|
|
||||||
- On-call owner and rollback owner are assigned.
|
|
||||||
|
|
||||||
## Rehearsal Steps
|
|
||||||
|
|
||||||
1. Enable new navigation mode in staging/canary.
|
|
||||||
2. Execute parity checklist (`parity_checklist.md`) on critical routes.
|
|
||||||
3. Force simulated rollback trigger (toggle off new mode).
|
|
||||||
4. Re-run critical smoke checks:
|
|
||||||
- portal load
|
|
||||||
- drawer visibility
|
|
||||||
- wip overview/detail flow
|
|
||||||
- resource history query path
|
|
||||||
5. Record elapsed recovery time and failures.
|
|
||||||
|
|
||||||
## Verification Criteria
|
|
||||||
|
|
||||||
- Toggle change takes effect without manual code rollback.
|
|
||||||
- Critical routes recover to expected behavior.
|
|
||||||
- Recovery time is within SLO.
|
|
||||||
- No residual hard-failure state remains.
|
|
||||||
|
|
||||||
## Post-Rehearsal Record
|
|
||||||
|
|
||||||
- Date:
|
|
||||||
- Environment:
|
|
||||||
- Operator:
|
|
||||||
- Trigger reason:
|
|
||||||
- Recovery duration:
|
|
||||||
- Issues found:
|
|
||||||
- Follow-up actions:
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# Rollback Strategy (Shell / Router / Wrapper)
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Shell entry failures (`/portal-shell`, route guards, navigation API)
|
|
||||||
- Legacy route integration failures (`job-query`, `excel-query`, `query-tool`, `tmtt-defect`)
|
|
||||||
|
|
||||||
## Immediate actions
|
|
||||||
|
|
||||||
1. Flip `PORTAL_SPA_ENABLED=false`.
|
|
||||||
2. Confirm `/` portal route responds and sidebar route links render.
|
|
||||||
3. Verify core routes (`/wip-overview`, `/resource`, `/qc-gate`) return 2xx.
|
|
||||||
4. Verify legacy routes (`/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`) return 2xx.
|
|
||||||
|
|
||||||
## Validation checkpoints (<=15 minutes)
|
|
||||||
|
|
||||||
- `GET /api/portal/navigation` returns deterministic drawer/page list.
|
|
||||||
- No spike in 5xx for portal and legacy routes.
|
|
||||||
- Smoke flows for one P0 page and one legacy page pass.
|
|
||||||
|
|
||||||
## Legacy route fallback
|
|
||||||
|
|
||||||
- If a legacy route fails hard, temporarily hide it from drawer config (`page_status.json`) and announce maintenance route.
|
|
||||||
|
|
||||||
## Post-rollback follow-up
|
|
||||||
|
|
||||||
- Capture failed gate(s), timestamp, and impacted routes.
|
|
||||||
- Generate incident summary with fix candidate and rehearsal re-entry criteria.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Phased Rollout Plan (Canary)
|
|
||||||
|
|
||||||
## Feature switch
|
|
||||||
|
|
||||||
- Primary switch: `PORTAL_SPA_ENABLED`
|
|
||||||
- Canary enabled users/groups are routed to `/portal-shell`; others remain on `/` route-based portal.
|
|
||||||
|
|
||||||
## Phases
|
|
||||||
|
|
||||||
1. Phase A (Internal): dev/admin users only, 1 day.
|
|
||||||
2. Phase B (Canary): 10-20% target users, 2-3 days.
|
|
||||||
3. Phase C (Broad): 50% users if gates are green for 24h.
|
|
||||||
4. Phase D (Full): 100% after cutover gates pass.
|
|
||||||
|
|
||||||
## Success thresholds
|
|
||||||
|
|
||||||
- Route availability (P0): >= 99.9% 2xx/3xx.
|
|
||||||
- Client runtime error rate on critical paths: 0 unhandled exceptions.
|
|
||||||
- Drawer parity drift: 0 mismatches (admin/non-admin route sets).
|
|
||||||
- Wrapper launch success (`launch` telemetry): >= 99%.
|
|
||||||
|
|
||||||
## Error thresholds (rollback trigger)
|
|
||||||
|
|
||||||
- P0 route availability < 99.5% in any 30-minute window.
|
|
||||||
- Any critical workflow smoke failure.
|
|
||||||
- Drawer parity mismatch count > 0 after deployment.
|
|
||||||
- Wrapper telemetry error rate >= 2% sustained 30 minutes.
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Shared UI Component Contracts
|
|
||||||
|
|
||||||
## `PaginationControl`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-ui/components/PaginationControl.vue`
|
|
||||||
|
|
||||||
- Props:
|
|
||||||
- `page?: number` (legacy compatibility)
|
|
||||||
- `modelValue?: number`
|
|
||||||
- `totalPages: number`
|
|
||||||
- `infoText?: string`
|
|
||||||
- `visible?: boolean`
|
|
||||||
- Emits:
|
|
||||||
- `update:modelValue(number)`
|
|
||||||
- `change(number)`
|
|
||||||
- `prev(number)`
|
|
||||||
- `next(number)`
|
|
||||||
- Compatibility:
|
|
||||||
- Supports legacy usage (`:page`, `@prev`, `@next`) for migration-safe replacement.
|
|
||||||
|
|
||||||
## `SectionCard`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-ui/components/SectionCard.vue`
|
|
||||||
|
|
||||||
- Slots:
|
|
||||||
- `header`
|
|
||||||
- default body
|
|
||||||
- `footer`
|
|
||||||
- Purpose:
|
|
||||||
- Normalize page section container structure and spacing.
|
|
||||||
|
|
||||||
## `FilterToolbar`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-ui/components/FilterToolbar.vue`
|
|
||||||
|
|
||||||
- Slots:
|
|
||||||
- default filter controls
|
|
||||||
- `actions`
|
|
||||||
- Purpose:
|
|
||||||
- Shared filter layout shell with consistent spacing and action alignment.
|
|
||||||
|
|
||||||
## `StatusBadge`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-ui/components/StatusBadge.vue`
|
|
||||||
|
|
||||||
- Props:
|
|
||||||
- `tone: neutral | success | warning | danger`
|
|
||||||
- `text: string`
|
|
||||||
- Purpose:
|
|
||||||
- Replace repeated local badge/status color snippets.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Shared Composables Contracts
|
|
||||||
|
|
||||||
## `useAutoRefresh`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-composables/useAutoRefresh.js`
|
|
||||||
|
|
||||||
- Current behavior wraps existing `wip-shared` implementation.
|
|
||||||
- Purpose: single import path for all page modules before deeper implementation merge.
|
|
||||||
|
|
||||||
## `useAutocomplete`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-composables/useAutocomplete.js`
|
|
||||||
|
|
||||||
- Current behavior wraps existing `wip-shared` implementation.
|
|
||||||
- Purpose: single import path to normalize field/autocomplete interactions.
|
|
||||||
|
|
||||||
## `usePaginationState`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-composables/usePaginationState.js`
|
|
||||||
|
|
||||||
- State: `page`, `perPage`, `total`, `totalPages`
|
|
||||||
- Derived: `hasPrev`, `hasNext`
|
|
||||||
- Methods: `setFromPayload`, `reset`
|
|
||||||
|
|
||||||
## `useQueryState`
|
|
||||||
|
|
||||||
File: `frontend/src/shared-composables/useQueryState.js`
|
|
||||||
|
|
||||||
- `readQueryState(keys)`
|
|
||||||
- `writeQueryState(nextState)`
|
|
||||||
- Purpose: unify URL query read/write semantics across pages.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Tailwind Design Tokens Mapping
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Map existing portal visual language into a stable token set for phased migration.
|
|
||||||
|
|
||||||
## Color tokens
|
|
||||||
|
|
||||||
- `brand.500` / `brand.600` / `brand.700`: primary brand actions and active navigation states.
|
|
||||||
- `accent.500`: gradient accent endpoint for shell headers.
|
|
||||||
- `surface.app` / `surface.card` / `surface.muted`: app background, card surfaces, muted blocks.
|
|
||||||
- `stroke.soft` / `stroke.panel`: border hierarchy.
|
|
||||||
- `state.success` / `state.warning` / `state.danger` / `state.neutral`: status dots and health states.
|
|
||||||
|
|
||||||
## Typography tokens
|
|
||||||
|
|
||||||
- `fontFamily.sans`: `Noto Sans TC`, `Microsoft JhengHei`, system fallback.
|
|
||||||
|
|
||||||
## Layout tokens
|
|
||||||
|
|
||||||
- `spacing.shell`: outer shell padding.
|
|
||||||
- `spacing.panel`: panel interior spacing.
|
|
||||||
- `spacing.nav`: sidebar item horizontal spacing.
|
|
||||||
- `spacing.block`: vertical rhythm baseline.
|
|
||||||
|
|
||||||
## Radius and elevation tokens
|
|
||||||
|
|
||||||
- `borderRadius.shell`: shell and main card radius.
|
|
||||||
- `borderRadius.card`: smaller control/card radius.
|
|
||||||
- `boxShadow.soft`: light containers (sidebar).
|
|
||||||
- `boxShadow.panel`: content panel container.
|
|
||||||
- `boxShadow.shell`: header gradient card emphasis.
|
|
||||||
|
|
||||||
## Z-index token
|
|
||||||
|
|
||||||
- `zIndex.popup`: status popup / overlay layer.
|
|
||||||
|
|
||||||
## Migration note
|
|
||||||
|
|
||||||
Tokens are intentionally aligned with current portal values to minimize visual drift during iframe decommission.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Tailwind Migration Guide (Portal No-iframe)
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Move distributed page CSS toward a token-driven Tailwind system without breaking existing portal behavior.
|
|
||||||
|
|
||||||
## Step-by-step
|
|
||||||
|
|
||||||
1. Keep existing route/page behavior unchanged.
|
|
||||||
2. Replace repeated layout wrappers with Tailwind utilities first (`grid`, `flex`, spacing, radius, shadows).
|
|
||||||
3. Replace repeated visual primitives with shared component classes from `@layer components`.
|
|
||||||
4. Move hard-coded colors/spacing to tokens in `tailwind.config.js` and `tailwind.css`.
|
|
||||||
5. Remove obsolete page-local CSS only after visual parity is verified.
|
|
||||||
|
|
||||||
## Recommended migration order
|
|
||||||
|
|
||||||
1. Shell and shared navigation blocks
|
|
||||||
2. Filter bars and KPI card rows
|
|
||||||
3. Shared table containers and pagination controls
|
|
||||||
4. Page-specific edge states and empty/error banners
|
|
||||||
|
|
||||||
## Parity checks per batch
|
|
||||||
|
|
||||||
- Drawer visibility and route links stay unchanged.
|
|
||||||
- Existing URL/query semantics remain compatible.
|
|
||||||
- No new runtime style conflicts in non-admin/admin views.
|
|
||||||
|
|
||||||
## Do / Don’t
|
|
||||||
|
|
||||||
- Do: prefer composable utility classes and shared Vue components.
|
|
||||||
- Do: keep style changes scoped to one route family per batch.
|
|
||||||
- Don’t: introduce new long inline `<style>` blocks in templates.
|
|
||||||
- Don’t: mix unrelated refactors with migration styling tasks.
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Tailwind Style Governance (Migration Phase)
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Applies to all new frontend work under `frontend/src/**` during iframe removal migration.
|
|
||||||
- Existing page-local CSS can remain temporarily, but new large page-local blocks are disallowed.
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
1. New shared UI styles must be authored in Tailwind layers (`base`, `components`, `utilities`) under `frontend/src/styles/tailwind.css`.
|
|
||||||
2. Reusable patterns (cards, filter bars, badge groups, table shells) must use component classes or Vue components, not copy-pasted CSS.
|
|
||||||
3. Page-specific CSS additions over 40 lines require an explicit migration note in the PR and an issue to move them into shared layers.
|
|
||||||
4. Token values must come from `tailwind.config.js` or CSS variables in `tailwind.css`; hard-coded new color scales are disallowed.
|
|
||||||
5. Motion/accessibility styles must support reduced-motion fallback and avoid forced animation on critical data refresh paths.
|
|
||||||
|
|
||||||
## Review Checklist
|
|
||||||
|
|
||||||
- New files import `frontend/src/styles/tailwind.css` through the entry module.
|
|
||||||
- No new iframe-targeting selectors are introduced.
|
|
||||||
- Shared classes/components are reused before adding page-local CSS.
|
|
||||||
- Token naming remains stable (`brand`, `surface`, `stroke`, `state`, spacing/radius/shadow/z-index).
|
|
||||||
|
|
||||||
## Exceptions
|
|
||||||
|
|
||||||
- Bugfix hotfixes may temporarily bypass these rules only if release risk is high.
|
|
||||||
- Every exception must include an expiry task in `openspec/changes/portal-no-iframe-navigation/tasks.md`.
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# `tmtt-defect` Rewrite Exemplar
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Route: `/tmtt-defect`
|
|
||||||
- Goal: establish the first canonical legacy rewrite pattern with:
|
|
||||||
- Vue SFC composition
|
|
||||||
- shared UI layer reuse
|
|
||||||
- Tailwind token layer coexistence
|
|
||||||
- no iframe / no wrapper dependency
|
|
||||||
|
|
||||||
## Implemented Structure
|
|
||||||
|
|
||||||
- Entry: `frontend/src/tmtt-defect/main.js`
|
|
||||||
- Page container: `frontend/src/tmtt-defect/App.vue`
|
|
||||||
- Data state/composable: `frontend/src/tmtt-defect/composables/useTmttDefectData.js`
|
|
||||||
- Reusable page components:
|
|
||||||
- `frontend/src/tmtt-defect/components/TmttKpiCards.vue`
|
|
||||||
- `frontend/src/tmtt-defect/components/TmttChartCard.vue`
|
|
||||||
- `frontend/src/tmtt-defect/components/TmttDetailTable.vue`
|
|
||||||
- Shared UI usage:
|
|
||||||
- `frontend/src/shared-ui/components/FilterToolbar.vue`
|
|
||||||
- `frontend/src/shared-ui/components/SectionCard.vue`
|
|
||||||
- `frontend/src/shared-ui/components/StatusBadge.vue`
|
|
||||||
- Backend template mount shell: `src/mes_dashboard/templates/tmtt_defect.html`
|
|
||||||
|
|
||||||
## Behavioral Parity
|
|
||||||
|
|
||||||
The rewrite keeps current route and API contracts:
|
|
||||||
|
|
||||||
- Query API: `GET /api/tmtt-defect/analysis`
|
|
||||||
- Export API: `GET /api/tmtt-defect/export`
|
|
||||||
- Sort/filter/detail behavior preserved on result table
|
|
||||||
|
|
||||||
Smoke coverage references:
|
|
||||||
|
|
||||||
- `TMTT-SMOKE-01` ~ `TMTT-SMOKE-06` in `legacy_rewrite_smoke_checklists.md`
|
|
||||||
|
|
||||||
## Verification Snapshot
|
|
||||||
|
|
||||||
- `npm --prefix frontend run build` passed
|
|
||||||
- `pytest -q tests/test_template_integration.py tests/test_portal_shell_routes.py tests/test_cutover_gates.py tests/test_app_factory.py` passed
|
|
||||||
|
|
||||||
This page is the baseline implementation that remaining legacy rewrites follow.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# UI Pattern Inventory (WIP / Resource / Hold / QC)
|
|
||||||
|
|
||||||
## Duplicated patterns observed
|
|
||||||
|
|
||||||
1. Filter bars:
|
|
||||||
- `hold-overview/components/FilterBar.vue`
|
|
||||||
- `hold-history/components/FilterBar.vue`
|
|
||||||
- `resource-status/components/FilterBar.vue`
|
|
||||||
- `resource-history/components/FilterBar.vue`
|
|
||||||
- `mid-section-defect/components/FilterBar.vue`
|
|
||||||
2. KPI/Summary cards:
|
|
||||||
- `wip-overview/components/SummaryCards.vue`
|
|
||||||
- `wip-detail/components/SummaryCards.vue`
|
|
||||||
- `hold-detail/components/SummaryCards.vue`
|
|
||||||
- `hold-history/components/SummaryCards.vue`
|
|
||||||
- `resource-status/components/SummaryCards.vue`
|
|
||||||
- `resource-history/components/KpiCards.vue`
|
|
||||||
- `mid-section-defect/components/KpiCards.vue`
|
|
||||||
3. Table + pagination shells:
|
|
||||||
- `wip-detail/components/LotTable.vue`
|
|
||||||
- `hold-detail/components/LotTable.vue`
|
|
||||||
- `hold-overview/components/LotTable.vue`
|
|
||||||
- `hold-history/components/DetailTable.vue`
|
|
||||||
- `mid-section-defect/components/DetailTable.vue`
|
|
||||||
- `qc-gate/components/LotTable.vue`
|
|
||||||
4. Multi-select and query controls:
|
|
||||||
- `resource-shared/components/MultiSelect.vue`
|
|
||||||
- `mid-section-defect/components/MultiSelect.vue`
|
|
||||||
5. Repeated status/badge presentation logic:
|
|
||||||
- WIP/Hold status class mapping and local badge styles in multiple tables/cards.
|
|
||||||
|
|
||||||
## Consolidation targets
|
|
||||||
|
|
||||||
- Shared UI layer (`frontend/src/shared-ui/components`)
|
|
||||||
- Shared composables layer (`frontend/src/shared-composables`)
|
|
||||||
- Tailwind tokenized styles (`frontend/src/styles/tailwind.css`)
|
|
||||||
|
|
||||||
## First migration batch completed
|
|
||||||
|
|
||||||
- Unified pagination rendering for WIP/Hold/Mid-section detail tables through `PaginationControl` wrapper.
|
|
||||||
- Auto-refresh and autocomplete imports migrated to `shared-composables` entry points.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Wrapper Decommission Report
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
Legacy shell wrapper mode has been decommissioned after rewrite milestone validation.
|
|
||||||
|
|
||||||
## Changes Applied
|
|
||||||
|
|
||||||
- Removed shell wrapper route branch:
|
|
||||||
- `frontend/src/portal-shell/router.js`
|
|
||||||
- `frontend/src/portal-shell/App.vue`
|
|
||||||
- Removed wrapper-specific frontend artifacts:
|
|
||||||
- deleted `frontend/src/portal-shell/constants.js`
|
|
||||||
- deleted `frontend/src/portal-shell/views/LegacyWrapperView.vue`
|
|
||||||
- Removed backend wrapper telemetry endpoint:
|
|
||||||
- deleted `/api/portal/wrapper-telemetry` in `src/mes_dashboard/app.py`
|
|
||||||
|
|
||||||
## Operational Outcome
|
|
||||||
|
|
||||||
- Portal shell navigation now uses direct page-bridge behavior only.
|
|
||||||
- Legacy page access remains available via direct routes.
|
|
||||||
- Wrapper telemetry contract is retired.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
- Route and template integration tests updated and passing.
|
|
||||||
- Cutover gate tests remain green after wrapper removal.
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"source": "frontend API contracts observed in report modules",
|
|
||||||
"apis": {
|
|
||||||
"/api/wip/overview/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"dataUpdateDate",
|
|
||||||
"runLots",
|
|
||||||
"queueLots",
|
|
||||||
"holdLots"
|
|
||||||
],
|
|
||||||
"notes": "WIP summary cards"
|
|
||||||
},
|
|
||||||
"/api/wip/overview/matrix": {
|
|
||||||
"required_keys": [
|
|
||||||
"workcenters",
|
|
||||||
"packages",
|
|
||||||
"matrix",
|
|
||||||
"workcenter_totals"
|
|
||||||
],
|
|
||||||
"notes": "WIP matrix table"
|
|
||||||
},
|
|
||||||
"/api/wip/hold-detail/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"workcenterCount",
|
|
||||||
"packageCount",
|
|
||||||
"lotCount"
|
|
||||||
],
|
|
||||||
"notes": "Hold detail KPI cards"
|
|
||||||
},
|
|
||||||
"/api/hold-overview/matrix": {
|
|
||||||
"required_keys": [
|
|
||||||
"rows",
|
|
||||||
"totals"
|
|
||||||
],
|
|
||||||
"notes": "Hold overview matrix interaction"
|
|
||||||
},
|
|
||||||
"/api/hold-history/list": {
|
|
||||||
"required_keys": [
|
|
||||||
"rows",
|
|
||||||
"summary"
|
|
||||||
],
|
|
||||||
"notes": "Hold history table and summary sync"
|
|
||||||
},
|
|
||||||
"/api/resource/status": {
|
|
||||||
"required_keys": [
|
|
||||||
"rows",
|
|
||||||
"summary"
|
|
||||||
],
|
|
||||||
"notes": "Realtime resource status table"
|
|
||||||
},
|
|
||||||
"/api/resource/history/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"kpi",
|
|
||||||
"trend",
|
|
||||||
"heatmap",
|
|
||||||
"workcenter_comparison"
|
|
||||||
],
|
|
||||||
"notes": "Resource history charts"
|
|
||||||
},
|
|
||||||
"/api/resource/history/detail": {
|
|
||||||
"required_keys": [
|
|
||||||
"data"
|
|
||||||
],
|
|
||||||
"notes": "Resource history detail table"
|
|
||||||
},
|
|
||||||
"/api/qc-gate/summary": {
|
|
||||||
"required_keys": [
|
|
||||||
"summary",
|
|
||||||
"table",
|
|
||||||
"pareto"
|
|
||||||
],
|
|
||||||
"notes": "QC-GATE chart/table linked view"
|
|
||||||
},
|
|
||||||
"/api/tmtt-defect/analysis": {
|
|
||||||
"required_keys": [
|
|
||||||
"kpi",
|
|
||||||
"pareto",
|
|
||||||
"trend",
|
|
||||||
"detail"
|
|
||||||
],
|
|
||||||
"notes": "TMTT chart/table analysis payload"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"source": "data/page_status.json",
|
|
||||||
"errors": []
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-12T02:26:36.887797+00:00",
|
|
||||||
"source": "data/page_status.json",
|
|
||||||
"admin": [
|
|
||||||
{
|
|
||||||
"id": "reports",
|
|
||||||
"name": "即時報表",
|
|
||||||
"order": 1,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"name": "WIP 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"name": "Hold 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"name": "設備即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"name": "QC-GATE 狀態",
|
|
||||||
"status": "released",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer-2",
|
|
||||||
"name": "歷史報表",
|
|
||||||
"order": 2,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"name": "Hold 歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"name": "設備歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer",
|
|
||||||
"name": "查詢工具",
|
|
||||||
"order": 3,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"name": "設備維修查詢",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dev-tools",
|
|
||||||
"name": "開發工具",
|
|
||||||
"order": 4,
|
|
||||||
"admin_only": true,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/tables",
|
|
||||||
"name": "表格總覽",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/pages",
|
|
||||||
"name": "頁面管理",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/excel-query",
|
|
||||||
"name": "Excel 批次查詢",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/admin/performance",
|
|
||||||
"name": "效能監控",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/query-tool",
|
|
||||||
"name": "批次追蹤工具",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"name": "TMTT印字腳型不良分析",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/mid-section-defect",
|
|
||||||
"name": "中段製程不良追溯",
|
|
||||||
"status": "dev",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"non_admin": [
|
|
||||||
{
|
|
||||||
"id": "reports",
|
|
||||||
"name": "即時報表",
|
|
||||||
"order": 1,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"name": "WIP 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"name": "Hold 即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource",
|
|
||||||
"name": "設備即時概況",
|
|
||||||
"status": "released",
|
|
||||||
"order": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"name": "QC-GATE 狀態",
|
|
||||||
"status": "released",
|
|
||||||
"order": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer-2",
|
|
||||||
"name": "歷史報表",
|
|
||||||
"order": 2,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/hold-history",
|
|
||||||
"name": "Hold 歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route": "/resource-history",
|
|
||||||
"name": "設備歷史績效",
|
|
||||||
"status": "released",
|
|
||||||
"order": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "drawer",
|
|
||||||
"name": "查詢工具",
|
|
||||||
"order": 3,
|
|
||||||
"admin_only": false,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"route": "/job-query",
|
|
||||||
"name": "設備維修查詢",
|
|
||||||
"status": "released",
|
|
||||||
"order": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,706 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"capture_scope": [
|
|
||||||
"/wip-overview",
|
|
||||||
"/wip-detail",
|
|
||||||
"/hold-overview",
|
|
||||||
"/hold-detail",
|
|
||||||
"/hold-history",
|
|
||||||
"/resource",
|
|
||||||
"/resource-history",
|
|
||||||
"/qc-gate",
|
|
||||||
"/job-query",
|
|
||||||
"/excel-query",
|
|
||||||
"/query-tool",
|
|
||||||
"/tmtt-defect"
|
|
||||||
],
|
|
||||||
"routes": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/wip-overview",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/wip-overview/App.vue",
|
|
||||||
"frontend/src/wip-overview/components/FilterPanel.vue",
|
|
||||||
"frontend/src/wip-overview/components/MatrixTable.vue",
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue",
|
|
||||||
"frontend/src/wip-overview/components/StatusCards.vue",
|
|
||||||
"frontend/src/wip-overview/components/SummaryCards.vue",
|
|
||||||
"frontend/src/wip-overview/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-overview/components/MatrixTable.vue",
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": false,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-overview/App.vue",
|
|
||||||
"frontend/src/wip-overview/components/FilterPanel.vue",
|
|
||||||
"frontend/src/wip-overview/components/StatusCards.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-overview/App.vue",
|
|
||||||
"frontend/src/wip-overview/components/MatrixTable.vue"
|
|
||||||
],
|
|
||||||
"has_matrix_interaction": true
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/wip/overview/hold",
|
|
||||||
"/api/wip/overview/matrix",
|
|
||||||
"/api/wip/overview/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/wip-detail",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/wip-detail/App.vue",
|
|
||||||
"frontend/src/wip-detail/components/FilterPanel.vue",
|
|
||||||
"frontend/src/wip-detail/components/LotDetailPanel.vue",
|
|
||||||
"frontend/src/wip-detail/components/LotTable.vue",
|
|
||||||
"frontend/src/wip-detail/components/SummaryCards.vue",
|
|
||||||
"frontend/src/wip-detail/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-detail/components/LotTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": false,
|
|
||||||
"has_pagination": true,
|
|
||||||
"sort_hint_files": [],
|
|
||||||
"pagination_hint_files": [
|
|
||||||
"frontend/src/wip-detail/App.vue",
|
|
||||||
"frontend/src/wip-detail/components/LotTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": false,
|
|
||||||
"has_tooltip_logic": false,
|
|
||||||
"legend_hint_files": [],
|
|
||||||
"tooltip_hint_files": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [
|
|
||||||
"workcenter",
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/wip-detail/App.vue",
|
|
||||||
"frontend/src/wip-detail/components/FilterPanel.vue",
|
|
||||||
"frontend/src/wip-detail/components/SummaryCards.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/wip/detail/",
|
|
||||||
"/api/wip/lot/",
|
|
||||||
"/api/wip/meta/workcenters"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/hold-overview": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/hold-overview",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/FilterBar.vue",
|
|
||||||
"frontend/src/hold-overview/components/FilterIndicator.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldMatrix.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue",
|
|
||||||
"frontend/src/hold-overview/components/LotTable.vue",
|
|
||||||
"frontend/src/hold-overview/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-overview/components/HoldMatrix.vue",
|
|
||||||
"frontend/src/hold-overview/components/LotTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": true,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/LotTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/FilterBar.vue",
|
|
||||||
"frontend/src/hold-overview/components/FilterIndicator.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldMatrix.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldTreeMap.vue",
|
|
||||||
"frontend/src/hold-overview/components/LotTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/FilterIndicator.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldMatrix.vue"
|
|
||||||
],
|
|
||||||
"has_matrix_interaction": true
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/hold-overview/lots",
|
|
||||||
"/api/hold-overview/matrix",
|
|
||||||
"/api/hold-overview/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/hold-detail",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/hold-detail/App.vue",
|
|
||||||
"frontend/src/hold-detail/components/AgeDistribution.vue",
|
|
||||||
"frontend/src/hold-detail/components/DistributionTable.vue",
|
|
||||||
"frontend/src/hold-detail/components/LotTable.vue",
|
|
||||||
"frontend/src/hold-detail/components/SummaryCards.vue",
|
|
||||||
"frontend/src/hold-detail/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-detail/components/DistributionTable.vue",
|
|
||||||
"frontend/src/hold-detail/components/LotTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": false,
|
|
||||||
"has_pagination": true,
|
|
||||||
"sort_hint_files": [],
|
|
||||||
"pagination_hint_files": [
|
|
||||||
"frontend/src/hold-detail/App.vue",
|
|
||||||
"frontend/src/hold-detail/components/LotTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": false,
|
|
||||||
"has_tooltip_logic": false,
|
|
||||||
"legend_hint_files": [],
|
|
||||||
"tooltip_hint_files": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [
|
|
||||||
"reason"
|
|
||||||
],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-detail/App.vue",
|
|
||||||
"frontend/src/hold-detail/components/LotTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/wip/hold-detail/distribution",
|
|
||||||
"/api/wip/hold-detail/lots",
|
|
||||||
"/api/wip/hold-detail/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/hold-history": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/hold-history",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/hold-history/App.vue",
|
|
||||||
"frontend/src/hold-history/components/DailyTrend.vue",
|
|
||||||
"frontend/src/hold-history/components/DetailTable.vue",
|
|
||||||
"frontend/src/hold-history/components/DurationChart.vue",
|
|
||||||
"frontend/src/hold-history/components/FilterBar.vue",
|
|
||||||
"frontend/src/hold-history/components/FilterIndicator.vue",
|
|
||||||
"frontend/src/hold-history/components/ReasonPareto.vue",
|
|
||||||
"frontend/src/hold-history/components/RecordTypeFilter.vue",
|
|
||||||
"frontend/src/hold-history/components/SummaryCards.vue",
|
|
||||||
"frontend/src/hold-history/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-history/components/DetailTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": false,
|
|
||||||
"has_pagination": true,
|
|
||||||
"sort_hint_files": [],
|
|
||||||
"pagination_hint_files": [
|
|
||||||
"frontend/src/hold-history/App.vue",
|
|
||||||
"frontend/src/hold-history/components/DetailTable.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-history/components/DailyTrend.vue",
|
|
||||||
"frontend/src/hold-history/components/DurationChart.vue",
|
|
||||||
"frontend/src/hold-history/components/ReasonPareto.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/hold-history/components/DailyTrend.vue",
|
|
||||||
"frontend/src/hold-history/components/ReasonPareto.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/hold-history/components/DailyTrend.vue",
|
|
||||||
"frontend/src/hold-history/components/DurationChart.vue",
|
|
||||||
"frontend/src/hold-history/components/ReasonPareto.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/hold-history/App.vue",
|
|
||||||
"frontend/src/hold-history/components/FilterBar.vue",
|
|
||||||
"frontend/src/hold-history/components/FilterIndicator.vue",
|
|
||||||
"frontend/src/hold-history/components/RecordTypeFilter.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/hold-history/duration",
|
|
||||||
"/api/hold-history/list",
|
|
||||||
"/api/hold-history/reason-pareto",
|
|
||||||
"/api/hold-history/trend"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/resource": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/resource-status",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/resource-status/App.vue",
|
|
||||||
"frontend/src/resource-status/components/EquipmentCard.vue",
|
|
||||||
"frontend/src/resource-status/components/EquipmentGrid.vue",
|
|
||||||
"frontend/src/resource-status/components/FilterBar.vue",
|
|
||||||
"frontend/src/resource-status/components/FloatingTooltip.vue",
|
|
||||||
"frontend/src/resource-status/components/MatrixSection.vue",
|
|
||||||
"frontend/src/resource-status/components/StatusHeader.vue",
|
|
||||||
"frontend/src/resource-status/components/SummaryCards.vue",
|
|
||||||
"frontend/src/resource-status/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/resource-status/App.vue",
|
|
||||||
"frontend/src/resource-status/components/MatrixSection.vue"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": false,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/resource-status/App.vue",
|
|
||||||
"frontend/src/resource-status/components/FloatingTooltip.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/resource-status/App.vue",
|
|
||||||
"frontend/src/resource-status/components/EquipmentGrid.vue",
|
|
||||||
"frontend/src/resource-status/components/FilterBar.vue",
|
|
||||||
"frontend/src/resource-status/components/MatrixSection.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/resource-status/App.vue",
|
|
||||||
"frontend/src/resource-status/components/MatrixSection.vue",
|
|
||||||
"frontend/src/resource-status/components/SummaryCards.vue"
|
|
||||||
],
|
|
||||||
"has_matrix_interaction": true
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/resource/status",
|
|
||||||
"/api/resource/status/options",
|
|
||||||
"/api/resource/status/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/resource-history",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/resource-history/App.vue",
|
|
||||||
"frontend/src/resource-history/components/ComparisonChart.vue",
|
|
||||||
"frontend/src/resource-history/components/DetailSection.vue",
|
|
||||||
"frontend/src/resource-history/components/FilterBar.vue",
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue",
|
|
||||||
"frontend/src/resource-history/components/KpiCards.vue",
|
|
||||||
"frontend/src/resource-history/components/StackedChart.vue",
|
|
||||||
"frontend/src/resource-history/components/TrendChart.vue",
|
|
||||||
"frontend/src/resource-history/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/resource-history/App.vue",
|
|
||||||
"frontend/src/resource-history/components/ComparisonChart.vue",
|
|
||||||
"frontend/src/resource-history/components/DetailSection.vue",
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/resource-history/components/ComparisonChart.vue",
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue",
|
|
||||||
"frontend/src/resource-history/components/StackedChart.vue",
|
|
||||||
"frontend/src/resource-history/components/TrendChart.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/resource-history/components/StackedChart.vue",
|
|
||||||
"frontend/src/resource-history/components/TrendChart.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/resource-history/components/ComparisonChart.vue",
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue",
|
|
||||||
"frontend/src/resource-history/components/StackedChart.vue",
|
|
||||||
"frontend/src/resource-history/components/TrendChart.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [
|
|
||||||
"start_date",
|
|
||||||
"end_date",
|
|
||||||
"granularity",
|
|
||||||
"workcenter_groups",
|
|
||||||
"families",
|
|
||||||
"resource_ids",
|
|
||||||
"is_production",
|
|
||||||
"is_key",
|
|
||||||
"is_monitor"
|
|
||||||
],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/resource-history/App.vue",
|
|
||||||
"frontend/src/resource-history/components/FilterBar.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue"
|
|
||||||
],
|
|
||||||
"has_matrix_interaction": true
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/resource/history/detail",
|
|
||||||
"/api/resource/history/export",
|
|
||||||
"/api/resource/history/options",
|
|
||||||
"/api/resource/history/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/qc-gate": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/qc-gate",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/qc-gate/App.vue",
|
|
||||||
"frontend/src/qc-gate/components/LotTable.vue",
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue",
|
|
||||||
"frontend/src/qc-gate/composables/useQcGateData.js",
|
|
||||||
"frontend/src/qc-gate/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/qc-gate/components/LotTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/qc-gate/components/LotTable.vue",
|
|
||||||
"frontend/src/qc-gate/composables/useQcGateData.js"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/qc-gate/App.vue",
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/qc-gate/App.vue",
|
|
||||||
"frontend/src/qc-gate/components/LotTable.vue",
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue",
|
|
||||||
"frontend/src/qc-gate/composables/useQcGateData.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/qc-gate/summary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/job-query": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/job-query",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/job-query/App.vue",
|
|
||||||
"frontend/src/job-query/composables/useJobQueryData.js",
|
|
||||||
"frontend/src/job-query/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/job-query/App.vue",
|
|
||||||
"frontend/src/job-query/main.js"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/job-query/main.js"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": false,
|
|
||||||
"has_tooltip_logic": false,
|
|
||||||
"legend_hint_files": [],
|
|
||||||
"tooltip_hint_files": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/job-query/App.vue",
|
|
||||||
"frontend/src/job-query/composables/useJobQueryData.js",
|
|
||||||
"frontend/src/job-query/main.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/job-query/export",
|
|
||||||
"/api/job-query/jobs",
|
|
||||||
"/api/job-query/resources",
|
|
||||||
"/api/job-query/txn/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/excel-query",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/excel-query/App.vue",
|
|
||||||
"frontend/src/excel-query/composables/useExcelQueryData.js",
|
|
||||||
"frontend/src/excel-query/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/excel-query/App.vue",
|
|
||||||
"frontend/src/excel-query/main.js"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/excel-query/main.js"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": false,
|
|
||||||
"has_tooltip_logic": false,
|
|
||||||
"legend_hint_files": [],
|
|
||||||
"tooltip_hint_files": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/excel-query/App.vue",
|
|
||||||
"frontend/src/excel-query/composables/useExcelQueryData.js",
|
|
||||||
"frontend/src/excel-query/main.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/excel-query/column-type",
|
|
||||||
"/api/excel-query/column-values",
|
|
||||||
"/api/excel-query/execute",
|
|
||||||
"/api/excel-query/execute-advanced",
|
|
||||||
"/api/excel-query/export-csv",
|
|
||||||
"/api/excel-query/table-metadata",
|
|
||||||
"/api/excel-query/tables",
|
|
||||||
"/api/excel-query/upload"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/query-tool",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/query-tool/App.vue",
|
|
||||||
"frontend/src/query-tool/composables/useQueryToolData.js",
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/query-tool/App.vue",
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/query-tool/App.vue",
|
|
||||||
"frontend/src/query-tool/composables/useQueryToolData.js",
|
|
||||||
"frontend/src/query-tool/main.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/query-tool/adjacent-lots",
|
|
||||||
"/api/query-tool/equipment-list",
|
|
||||||
"/api/query-tool/equipment-period",
|
|
||||||
"/api/query-tool/export-csv",
|
|
||||||
"/api/query-tool/lot-associations",
|
|
||||||
"/api/query-tool/lot-history",
|
|
||||||
"/api/query-tool/resolve",
|
|
||||||
"/api/query-tool/workcenter-groups"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"capture_method": "static_source_analysis",
|
|
||||||
"source_dir": "frontend/src/tmtt-defect",
|
|
||||||
"source_files": [
|
|
||||||
"frontend/src/tmtt-defect/App.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttChartCard.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttDetailTable.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttKpiCards.vue",
|
|
||||||
"frontend/src/tmtt-defect/composables/useTmttDefectData.js",
|
|
||||||
"frontend/src/tmtt-defect/main.js"
|
|
||||||
],
|
|
||||||
"table": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/tmtt-defect/components/TmttDetailTable.vue"
|
|
||||||
],
|
|
||||||
"has_sort_logic": true,
|
|
||||||
"has_pagination": false,
|
|
||||||
"sort_hint_files": [
|
|
||||||
"frontend/src/tmtt-defect/App.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttDetailTable.vue",
|
|
||||||
"frontend/src/tmtt-defect/composables/useTmttDefectData.js"
|
|
||||||
],
|
|
||||||
"pagination_hint_files": []
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/tmtt-defect/components/TmttChartCard.vue"
|
|
||||||
],
|
|
||||||
"has_legend_logic": true,
|
|
||||||
"has_tooltip_logic": true,
|
|
||||||
"legend_hint_files": [
|
|
||||||
"frontend/src/tmtt-defect/components/TmttChartCard.vue"
|
|
||||||
],
|
|
||||||
"tooltip_hint_files": [
|
|
||||||
"frontend/src/tmtt-defect/components/TmttChartCard.vue"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"required_query_keys": [],
|
|
||||||
"component_files": [
|
|
||||||
"frontend/src/tmtt-defect/App.vue",
|
|
||||||
"frontend/src/tmtt-defect/composables/useTmttDefectData.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"component_files": [],
|
|
||||||
"has_matrix_interaction": false
|
|
||||||
},
|
|
||||||
"api_endpoints": [
|
|
||||||
"/api/tmtt-defect/analysis",
|
|
||||||
"/api/tmtt-defect/export"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"routes": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"query_keys": [
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "filter URL sync + status drill-down to detail"
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"query_keys": [
|
|
||||||
"workcenter",
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "workcenter deep-link + list/detail continuity"
|
|
||||||
},
|
|
||||||
"/hold-overview": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "summary/matrix/lot interactions must remain stable"
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"query_keys": [
|
|
||||||
"reason"
|
|
||||||
],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "requires reason; missing reason redirects"
|
|
||||||
},
|
|
||||||
"/hold-history": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "trend/pareto/duration/table interactions"
|
|
||||||
},
|
|
||||||
"/resource": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "status summary + table filtering semantics"
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"query_keys": [
|
|
||||||
"start_date",
|
|
||||||
"end_date",
|
|
||||||
"granularity",
|
|
||||||
"workcenter_groups",
|
|
||||||
"families",
|
|
||||||
"resource_ids",
|
|
||||||
"is_production",
|
|
||||||
"is_key",
|
|
||||||
"is_monitor"
|
|
||||||
],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "date/granularity/group/family/resource/flags contract"
|
|
||||||
},
|
|
||||||
"/qc-gate": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "chart-table linked filtering parity"
|
|
||||||
},
|
|
||||||
"/job-query": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "resource/date query + txn detail + export"
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "upload/detect/query/export workflow"
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "resolve/history/associations/equipment-period workflows"
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"query_keys": [],
|
|
||||||
"render_mode": "native",
|
|
||||||
"notes": "analysis + chart interactions + CSV export"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T17:48:00+08:00",
|
|
||||||
"change": "portal-shell-route-view-integration",
|
|
||||||
"release_blocked": false,
|
|
||||||
"policy": {
|
|
||||||
"block_on_any_failed_gate": true,
|
|
||||||
"block_on_incomplete_smoke_evidence": true,
|
|
||||||
"block_on_critical_parity_failure": true
|
|
||||||
},
|
|
||||||
"gates": [
|
|
||||||
{
|
|
||||||
"id": "G1",
|
|
||||||
"name": "route_availability",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"tests/test_portal_shell_routes.py",
|
|
||||||
"tests/test_cutover_gates.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G2",
|
|
||||||
"name": "drawer_parity_and_admin_visibility",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"tests/test_portal_shell_routes.py",
|
|
||||||
"tests/test_route_view_migration_baseline.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G3",
|
|
||||||
"name": "smoke_evidence_completeness",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"docs/migration/portal-shell-route-view-integration/wave-a-smoke-evidence.json",
|
|
||||||
"docs/migration/portal-shell-route-view-integration/wave-b-native-smoke-evidence.json",
|
|
||||||
"tests/test_cutover_gates.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G4",
|
|
||||||
"name": "no_iframe_shell_content",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"frontend/tests/portal-shell-no-iframe.test.js",
|
|
||||||
"tests/stress/test_frontend_stress.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G5",
|
|
||||||
"name": "route_query_compatibility",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"frontend/tests/portal-shell-route-query-compat.test.js",
|
|
||||||
"tests/test_wip_hold_pages_integration.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G6",
|
|
||||||
"name": "table_chart_filter_interaction_matrix_parity",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"docs/migration/portal-shell-route-view-integration/wave-b-parity-evidence.json",
|
|
||||||
"frontend/tests/portal-shell-parity-table-chart-matrix.test.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "G7",
|
|
||||||
"name": "rollback_and_kill_switch_readiness",
|
|
||||||
"status": "pass",
|
|
||||||
"block_on_fail": true,
|
|
||||||
"sources": [
|
|
||||||
"docs/migration/portal-shell-route-view-integration/rollback-rehearsal-shell-route-view.md",
|
|
||||||
"docs/migration/portal-shell-route-view-integration/kill-switch-operations.md",
|
|
||||||
"tests/test_cutover_gates.py"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Final Migration State: No-Iframe Full Cutover
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Final State Summary
|
|
||||||
|
|
||||||
- Shell navigation runs as Vue Router SPA under `/portal-shell`.
|
|
||||||
- All target routes are `render_mode=native`:
|
|
||||||
- `/wip-overview`, `/wip-detail`, `/hold-overview`, `/hold-detail`, `/hold-history`, `/resource`, `/resource-history`, `/qc-gate`, `/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`.
|
|
||||||
- Shell content path does not use iframe embedding.
|
|
||||||
- `PageBridgeView` runtime host and wrapper telemetry endpoint are decommissioned.
|
|
||||||
|
|
||||||
## Contract State
|
|
||||||
|
|
||||||
- Source of truth remains:
|
|
||||||
- `docs/migration/portal-shell-route-view-integration/route_migration_contract.json`
|
|
||||||
- `docs/migration/portal-shell-route-view-integration/baseline_route_query_contracts.json`
|
|
||||||
- Navigation API diagnostics remain active for contract mismatch observability.
|
|
||||||
|
|
||||||
## Evidence Index
|
|
||||||
|
|
||||||
- Wave A smoke evidence: `wave-a-smoke-evidence.json`
|
|
||||||
- Wave B smoke evidence: `wave-b-native-smoke-evidence.json`
|
|
||||||
- Wave B parity evidence: `wave-b-parity-evidence.json`
|
|
||||||
- Gate report: `cutover-gates-report.json`
|
|
||||||
- Visual snapshots: `visual-regression-snapshots.json`
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# Final Parity Audit and Archive-Readiness Checklist
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Gate Readiness
|
|
||||||
|
|
||||||
- [x] G1 route availability pass
|
|
||||||
- [x] G2 drawer/admin visibility parity pass
|
|
||||||
- [x] G3 smoke evidence completeness pass
|
|
||||||
- [x] G4 no-iframe shell content pass
|
|
||||||
- [x] G5 route/query compatibility pass
|
|
||||||
- [x] G6 table/chart/filter/interaction/matrix parity pass
|
|
||||||
- [x] G7 rollback + kill-switch readiness pass
|
|
||||||
|
|
||||||
## Functional Parity
|
|
||||||
|
|
||||||
- [x] Wave A pages verified in shell native route-view
|
|
||||||
- [x] Wave B rewritten pages verified in shell native route-view
|
|
||||||
- [x] Table column/sort/pagination semantics preserved
|
|
||||||
- [x] Chart series/legend/tooltip/link semantics preserved
|
|
||||||
- [x] Matrix selection/highlight/drill semantics preserved
|
|
||||||
- [x] Zero-value and empty-state semantics preserved
|
|
||||||
|
|
||||||
## Operational Readiness
|
|
||||||
|
|
||||||
- [x] Rollout plan documented
|
|
||||||
- [x] Full/partial rollback rehearsal documented
|
|
||||||
- [x] Kill-switch instructions documented
|
|
||||||
- [x] Observability dashboard/report documented
|
|
||||||
|
|
||||||
## Cleanup Readiness
|
|
||||||
|
|
||||||
- [x] PageBridge runtime host removed
|
|
||||||
- [x] Wrapper telemetry endpoint removed
|
|
||||||
- [x] Wrapper-phase smoke checklist replaced with native evidence
|
|
||||||
- [x] Migration docs updated to final no-iframe state
|
|
||||||
|
|
||||||
## Archive Readiness Decision
|
|
||||||
|
|
||||||
- Result: READY FOR ARCHIVE
|
|
||||||
- Blocking issues: none
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Kill-Switch Operations: Shell Route-View Migration
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Provide a rapid, operator-safe mechanism to recover service usability when severe regressions occur after shell cutover.
|
|
||||||
|
|
||||||
## Trigger Conditions
|
|
||||||
|
|
||||||
- Critical route failures on shell core paths (`/portal-shell`, `/api/portal/navigation`).
|
|
||||||
- Multiple P0 smoke failures across Wave A/Wave B pages.
|
|
||||||
- Sustained health regression (`/health` degraded/unhealthy beyond threshold).
|
|
||||||
|
|
||||||
## Kill-Switch Command
|
|
||||||
|
|
||||||
- Set `PORTAL_SPA_ENABLED=false` in deployment environment.
|
|
||||||
- Restart application workers.
|
|
||||||
|
|
||||||
## Verification Checklist (must complete in order)
|
|
||||||
|
|
||||||
1. `GET /` responds and routes to legacy portal.
|
|
||||||
2. `GET /api/portal/navigation` responds 200 and drawer payload is valid JSON.
|
|
||||||
3. `GET /health` reports no new critical errors after rollback.
|
|
||||||
4. Critical page routes remain reachable: `/wip-overview`, `/resource`, `/qc-gate`, `/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`.
|
|
||||||
|
|
||||||
## Page-level Partial Kill-Switch
|
|
||||||
|
|
||||||
- If issue is route-scoped, patch affected route contract to fallback strategy and redeploy frontend shell assets only.
|
|
||||||
- Keep unaffected routes in native mode to avoid global disruption.
|
|
||||||
|
|
||||||
## Escalation
|
|
||||||
|
|
||||||
- If kill-switch does not restore stable behavior within 15 minutes, escalate to full rollback runbook and incident bridge.
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Migration Observability Dashboard/Report
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Monitoring Scope
|
|
||||||
|
|
||||||
- Route errors: shell route 4xx/5xx, unknown-route fallback count, dynamic module load errors.
|
|
||||||
- Health regressions: `/health` and `/health/frontend-shell` status transitions (healthy/degraded/unhealthy).
|
|
||||||
- Wrapper fallback usage: expected to remain zero after full native decommission; any non-zero signal is incident-worthy.
|
|
||||||
|
|
||||||
## Key Metrics
|
|
||||||
|
|
||||||
1. `shell_route_error_rate_5m`
|
|
||||||
- Definition: 4xx/5xx ratio for `/portal-shell/*` routes over 5 minutes.
|
|
||||||
- Threshold: warning at 0.5%, critical at 1.0%.
|
|
||||||
|
|
||||||
2. `navigation_contract_mismatch_total`
|
|
||||||
- Definition: count of `contract_mismatch_routes` emitted by `/api/portal/navigation` diagnostics.
|
|
||||||
- Threshold: must be 0.
|
|
||||||
|
|
||||||
3. `shell_health_degraded_ratio_15m`
|
|
||||||
- Definition: degraded/unhealthy health polls over 15 minutes.
|
|
||||||
- Threshold: warning at 5%, critical at 10%.
|
|
||||||
|
|
||||||
4. `native_module_load_error_total`
|
|
||||||
- Definition: native route module load failures captured by client telemetry/logs.
|
|
||||||
- Threshold: must be 0 for stable rollout.
|
|
||||||
|
|
||||||
5. `wrapper_fallback_usage_total`
|
|
||||||
- Definition: fallback-to-wrapper invocation count after decommission.
|
|
||||||
- Threshold: must be 0.
|
|
||||||
|
|
||||||
## Dashboard Panels
|
|
||||||
|
|
||||||
- Panel A: Route errors by route id and render mode.
|
|
||||||
- Panel B: Health summary state timeline with error/warning counts.
|
|
||||||
- Panel C: Route contract mismatch and unknown-route fallback trend.
|
|
||||||
- Panel D: Wave A/Wave B smoke pass trend and gate pass/fail timeline.
|
|
||||||
- Panel E: Wrapper fallback usage (target line at zero).
|
|
||||||
|
|
||||||
## Operational Notes
|
|
||||||
|
|
||||||
- During canary/partial rollout, all panels must stay within threshold before progressing.
|
|
||||||
- Any critical threshold breach forces hold or rollback per rollout plan.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Pre/Post Parity Report (Table/Chart/Filter/Interaction/Matrix)
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Routes: `/wip-overview`, `/wip-detail`, `/hold-overview`, `/hold-detail`, `/hold-history`, `/resource`, `/resource-history`, `/qc-gate`, `/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`.
|
|
||||||
|
|
||||||
## Method
|
|
||||||
|
|
||||||
- Pre-migration baseline:
|
|
||||||
- `baseline_interaction_evidence.json`
|
|
||||||
- `baseline_route_query_contracts.json`
|
|
||||||
- `baseline_api_payload_contracts.json`
|
|
||||||
- Post-migration verification:
|
|
||||||
- Frontend tests (`portal-shell-*`)
|
|
||||||
- Backend tests (`test_route_view_migration_baseline.py`, `test_cutover_gates.py`, Wave B native smoke)
|
|
||||||
- Visual snapshot fingerprints (`visual-regression-snapshots.json`)
|
|
||||||
|
|
||||||
## Page-by-Page Outcome
|
|
||||||
|
|
||||||
| Route | Table | Chart | Filter | Interaction | Matrix | Outcome |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| `/wip-overview` | pass | pass | pass | pass | pass | parity maintained |
|
|
||||||
| `/wip-detail` | pass | n/a | pass | pass | n/a | parity maintained |
|
|
||||||
| `/hold-overview` | pass | n/a | pass | pass | pass | parity maintained |
|
|
||||||
| `/hold-detail` | pass | pass | pass | pass | pass | parity maintained |
|
|
||||||
| `/hold-history` | pass | pass | pass | pass | n/a | parity maintained |
|
|
||||||
| `/resource` | pass | n/a | pass | pass | pass | parity maintained |
|
|
||||||
| `/resource-history` | pass | pass | pass | pass | n/a | parity maintained |
|
|
||||||
| `/qc-gate` | pass | pass | n/a | pass | pass | parity maintained |
|
|
||||||
| `/job-query` | pass | n/a | pass | pass | n/a | parity maintained |
|
|
||||||
| `/excel-query` | pass | n/a | pass | pass | n/a | parity maintained |
|
|
||||||
| `/query-tool` | pass | n/a | pass | pass | n/a | parity maintained |
|
|
||||||
| `/tmtt-defect` | pass | pass | pass | pass | n/a | parity maintained |
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
- Critical parity regressions: 0
|
|
||||||
- Routes blocked by gates: 0
|
|
||||||
- Wrapper fallback usage expected: 0 (post-decommission policy)
|
|
||||||
- Release/Archive recommendation: APPROVED
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Rollback Rehearsal: Shell Route-View Migration
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Recovery SLO
|
|
||||||
|
|
||||||
- Target recovery time: 15 minutes from trigger to restored stable path.
|
|
||||||
|
|
||||||
## Full Rollback Rehearsal
|
|
||||||
|
|
||||||
1. Trigger criteria
|
|
||||||
- Any G1~G7 gate failure after promotion.
|
|
||||||
- P0 user-facing regression on shell navigation or report interaction.
|
|
||||||
|
|
||||||
2. Steps
|
|
||||||
- Set environment variable `PORTAL_SPA_ENABLED=false`.
|
|
||||||
- Restart application workers.
|
|
||||||
- Verify `/` returns legacy portal path and `/api/portal/navigation` remains healthy.
|
|
||||||
- Confirm critical routes are reachable directly (`/wip-overview`, `/resource`, `/qc-gate`, `/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`).
|
|
||||||
|
|
||||||
3. Validation
|
|
||||||
- Run `pytest tests/test_cutover_gates.py::test_g7_rollback_gate_has_recovery_slo_and_kill_switch_steps -q`.
|
|
||||||
- Confirm `/health` and `/health/frontend-shell` return expected statuses.
|
|
||||||
|
|
||||||
## Partial Rollback Rehearsal (Page-level)
|
|
||||||
|
|
||||||
1. Trigger criteria
|
|
||||||
- Regression isolated to one or a subset of pages.
|
|
||||||
|
|
||||||
2. Steps
|
|
||||||
- Patch affected page contracts in `frontend/src/portal-shell/routeContracts.js` to temporary legacy fallback strategy.
|
|
||||||
- Rebuild frontend bundle and deploy only affected shell assets.
|
|
||||||
- Keep shell navigation enabled for unaffected routes.
|
|
||||||
|
|
||||||
3. Validation
|
|
||||||
- Re-run Wave B native smoke suite for unaffected pages.
|
|
||||||
- Ensure route-level fallback preserves service usability.
|
|
||||||
|
|
||||||
## Rehearsal Result (2026-02-11)
|
|
||||||
|
|
||||||
- Full rollback drill: PASS (estimated 11 minutes).
|
|
||||||
- Partial rollback drill: PASS (single-page contract patch + redeploy).
|
|
||||||
- Open issues: none.
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# Shell Route-View Cutover Rollout Plan
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
## Objectives
|
|
||||||
|
|
||||||
- Complete no-iframe shell cutover with zero P0 regressions.
|
|
||||||
- Keep rollback recovery under 15 minutes.
|
|
||||||
- Enforce G1~G7 gate pass before each promotion step.
|
|
||||||
|
|
||||||
## Phased Rollout
|
|
||||||
|
|
||||||
1. Phase 0: Preflight (0%)
|
|
||||||
- Run `npm --prefix frontend run build` and `npm --prefix frontend test`.
|
|
||||||
- Run gate suite: `pytest tests/test_cutover_gates.py tests/test_route_view_migration_baseline.py -q`.
|
|
||||||
- Validate `cutover-gates-report.json` is all-pass.
|
|
||||||
|
|
||||||
2. Phase 1: Canary (10%)
|
|
||||||
- Enable `PORTAL_SPA_ENABLED=true` on one canary instance.
|
|
||||||
- Track 30 minutes of route error rate, health summary status, and JS runtime errors.
|
|
||||||
- Hold point: any critical gate regression or error-rate spike > 2x baseline blocks progression.
|
|
||||||
|
|
||||||
3. Phase 2: Partial (50%)
|
|
||||||
- Expand SPA shell to half of instances.
|
|
||||||
- Monitor dashboard metrics for at least 60 minutes.
|
|
||||||
- Hold point: unresolved P0/P1 on Wave A/B smoke pages.
|
|
||||||
|
|
||||||
4. Phase 3: Full (100%)
|
|
||||||
- Enable SPA shell on all instances.
|
|
||||||
- Keep heightened monitoring window for 24 hours.
|
|
||||||
- Keep rollback kill-switch ready during the full window.
|
|
||||||
|
|
||||||
## Thresholds
|
|
||||||
|
|
||||||
- HTTP 5xx on shell routes: < 1.0% (5-min window).
|
|
||||||
- `/health` degraded/unhealthy ratio: < 5% of polls.
|
|
||||||
- JS runtime errors (`pageerror`/uncaught): zero critical occurrences.
|
|
||||||
- Smoke evidence completeness: 100% routes pass, zero unresolved critical failures.
|
|
||||||
|
|
||||||
## Hold Points
|
|
||||||
|
|
||||||
- H1: Preflight gate mismatch.
|
|
||||||
- H2: Canary route errors exceed threshold.
|
|
||||||
- H3: Partial rollout parity mismatch (table/chart/filter/matrix/interactions).
|
|
||||||
- H4: Health summary or admin entry regression.
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"description": "Route-level migration contract freeze for shell route-view integration.",
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"route_id": "wip-overview",
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"page_name": "WIP 即時概況",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/wip-overview"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "wip-detail",
|
|
||||||
"route": "/wip-detail",
|
|
||||||
"page_name": "WIP 詳細列表",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [
|
|
||||||
"workcenter",
|
|
||||||
"workorder",
|
|
||||||
"lotid",
|
|
||||||
"package",
|
|
||||||
"type",
|
|
||||||
"status"
|
|
||||||
],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/wip-detail"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "hold-overview",
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"page_name": "Hold 即時概況",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/hold-overview"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "hold-detail",
|
|
||||||
"route": "/hold-detail",
|
|
||||||
"page_name": "Hold 詳細查詢",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [
|
|
||||||
"reason"
|
|
||||||
],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/hold-detail"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "hold-history",
|
|
||||||
"route": "/hold-history",
|
|
||||||
"page_name": "Hold 歷史報表",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/hold-history"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "resource",
|
|
||||||
"route": "/resource",
|
|
||||||
"page_name": "設備即時狀況",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/resource-status"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "resource-history",
|
|
||||||
"route": "/resource-history",
|
|
||||||
"page_name": "設備歷史績效",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [
|
|
||||||
"start_date",
|
|
||||||
"end_date",
|
|
||||||
"granularity",
|
|
||||||
"workcenter_groups",
|
|
||||||
"families",
|
|
||||||
"resource_ids",
|
|
||||||
"is_production",
|
|
||||||
"is_key",
|
|
||||||
"is_monitor"
|
|
||||||
],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/resource-history"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "qc-gate",
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"page_name": "QC-GATE 狀態",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/qc-gate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "job-query",
|
|
||||||
"route": "/job-query",
|
|
||||||
"page_name": "設備維修查詢",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/job-query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "excel-query",
|
|
||||||
"route": "/excel-query",
|
|
||||||
"page_name": "Excel 查詢工具",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/excel-query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "query-tool",
|
|
||||||
"route": "/query-tool",
|
|
||||||
"page_name": "Query Tool",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/query-tool"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"route_id": "tmtt-defect",
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"page_name": "TMTT Defect",
|
|
||||||
"render_mode": "native",
|
|
||||||
"required_query_keys": [],
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"rollback_strategy": "fallback_to_legacy_route",
|
|
||||||
"source_dir": "frontend/src/tmtt-defect"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Route Migration Contract Freeze
|
|
||||||
|
|
||||||
Generated at: `2026-02-11T07:44:03+00:00`
|
|
||||||
|
|
||||||
This contract freezes route ownership and migration mode for shell cutover governance.
|
|
||||||
|
|
||||||
| Route ID | Route | Mode | Required Query Keys | Owner | Rollback Strategy |
|
|
||||||
| --- | --- | --- | --- | --- | --- |
|
|
||||||
| `wip-overview` | `/wip-overview` | `native` | `workorder, lotid, package, type, status` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `wip-detail` | `/wip-detail` | `native` | `workcenter, workorder, lotid, package, type, status` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `hold-overview` | `/hold-overview` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `hold-detail` | `/hold-detail` | `native` | `reason` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `hold-history` | `/hold-history` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `resource` | `/resource` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `resource-history` | `/resource-history` | `native` | `start_date, end_date, granularity, workcenter_groups, families, resource_ids, is_production, is_key, is_monitor` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `qc-gate` | `/qc-gate` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `job-query` | `/job-query` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `excel-query` | `/excel-query` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `query-tool` | `/query-tool` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `tmtt-defect` | `/tmtt-defect` | `native` | `-` | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
|
|
||||||
## Validation Rules
|
|
||||||
|
|
||||||
- Missing route definitions are treated as blocking contract errors.
|
|
||||||
- Duplicate route definitions are rejected.
|
|
||||||
- `render_mode` MUST be `native` or `wrapper`.
|
|
||||||
- `owner` and `rollback_strategy` MUST be non-empty.
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T07:44:03+00:00",
|
|
||||||
"errors": []
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Route Parity Matrix (Shell Route-View Integration)
|
|
||||||
|
|
||||||
Generated at: `2026-02-11T07:44:03+00:00`
|
|
||||||
|
|
||||||
| Route | Mode | Required Query Keys | Table / Filter Focus | Chart / Matrix Focus | Owner | Rollback |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| `/wip-overview` | `native` | `workorder, lotid, package, type, status` | table_files=2; sort=N; pagination=N | chart_files=1; legend=Y; tooltip=Y; matrix=Y | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/wip-detail` | `native` | `workcenter, workorder, lotid, package, type, status` | table_files=1; sort=N; pagination=Y | chart_files=0; legend=N; tooltip=N; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/hold-overview` | `native` | `-` | table_files=2; sort=Y; pagination=Y | chart_files=1; legend=Y; tooltip=Y; matrix=Y | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/hold-detail` | `native` | `reason` | table_files=2; sort=N; pagination=Y | chart_files=0; legend=N; tooltip=N; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/hold-history` | `native` | `-` | table_files=1; sort=N; pagination=Y | chart_files=3; legend=Y; tooltip=Y; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/resource` | `native` | `-` | table_files=0; sort=Y; pagination=N | chart_files=0; legend=N; tooltip=Y; matrix=Y | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/resource-history` | `native` | `start_date, end_date, granularity, workcenter_groups, families, resource_ids, is_production, is_key, is_monitor` | table_files=0; sort=Y; pagination=N | chart_files=4; legend=Y; tooltip=Y; matrix=Y | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/qc-gate` | `native` | `-` | table_files=1; sort=Y; pagination=N | chart_files=2; legend=Y; tooltip=Y; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/job-query` | `native` | `-` | table_files=2; sort=Y; pagination=N | chart_files=0; legend=N; tooltip=N; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/excel-query` | `native` | `-` | table_files=2; sort=Y; pagination=N | chart_files=0; legend=N; tooltip=N; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/query-tool` | `native` | `-` | table_files=2; sort=Y; pagination=N | chart_files=0; legend=Y; tooltip=Y; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
| `/tmtt-defect` | `native` | `-` | table_files=1; sort=Y; pagination=N | chart_files=1; legend=Y; tooltip=Y; matrix=N | `frontend-mes-reporting` | `fallback_to_legacy_route` |
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Matrix and chart/table links are validated further in per-page smoke and parity tests.
|
|
||||||
- All target routes are in native mode; no iframe/wrapper runtime host remains in shell content path.
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-12T12:40:00+08:00",
|
|
||||||
"description": "Critical visual-state snapshots for chart/table/matrix routes.",
|
|
||||||
"critical_diff_policy": {
|
|
||||||
"block_release": true,
|
|
||||||
"severity": "critical"
|
|
||||||
},
|
|
||||||
"snapshots": [
|
|
||||||
{
|
|
||||||
"id": "wip-overview-matrix-default",
|
|
||||||
"route": "/wip-overview",
|
|
||||||
"state": "matrix-default",
|
|
||||||
"files": [
|
|
||||||
"frontend/src/wip-overview/App.vue",
|
|
||||||
"frontend/src/wip-overview/components/MatrixTable.vue",
|
|
||||||
"frontend/src/wip-overview/components/ParetoSection.vue",
|
|
||||||
"frontend/src/wip-overview/style.css"
|
|
||||||
],
|
|
||||||
"fingerprint": "2f1710ac75c5253bc4057bec7ce3b036089d12bc2abead8cf82d39c498dce961"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "hold-overview-matrix-selected",
|
|
||||||
"route": "/hold-overview",
|
|
||||||
"state": "matrix-selected",
|
|
||||||
"files": [
|
|
||||||
"frontend/src/hold-overview/App.vue",
|
|
||||||
"frontend/src/hold-overview/components/HoldMatrix.vue",
|
|
||||||
"frontend/src/hold-overview/style.css"
|
|
||||||
],
|
|
||||||
"fingerprint": "f2ca1666f50afb4f922b522cdf739685ce068911a17d6e6c285244770f451f2c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "qc-gate-chart-table-linked",
|
|
||||||
"route": "/qc-gate",
|
|
||||||
"state": "chart-table-linked",
|
|
||||||
"files": [
|
|
||||||
"frontend/src/qc-gate/App.vue",
|
|
||||||
"frontend/src/qc-gate/components/LotTable.vue",
|
|
||||||
"frontend/src/qc-gate/components/QcGateChart.vue",
|
|
||||||
"frontend/src/qc-gate/style.css"
|
|
||||||
],
|
|
||||||
"fingerprint": "13e000938f5fc398a9abf2c62b3e64dd0c4742ba87b20d144c18befd57e2e1f4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "resource-history-chart-detail",
|
|
||||||
"route": "/resource-history",
|
|
||||||
"state": "chart-detail-sync",
|
|
||||||
"files": [
|
|
||||||
"frontend/src/resource-history/App.vue",
|
|
||||||
"frontend/src/resource-history/components/TrendChart.vue",
|
|
||||||
"frontend/src/resource-history/components/StackedChart.vue",
|
|
||||||
"frontend/src/resource-history/components/HeatmapChart.vue",
|
|
||||||
"frontend/src/resource-history/components/DetailSection.vue",
|
|
||||||
"frontend/src/resource-history/style.css"
|
|
||||||
],
|
|
||||||
"fingerprint": "385910e89f10f016f7973e97be30a697a396a71ea470c1bcfe028a2c6daa4cc9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "tmtt-defect-pareto-detail",
|
|
||||||
"route": "/tmtt-defect",
|
|
||||||
"state": "pareto-detail-filtered",
|
|
||||||
"files": [
|
|
||||||
"frontend/src/tmtt-defect/App.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttChartCard.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttDetailTable.vue",
|
|
||||||
"frontend/src/tmtt-defect/components/TmttKpiCards.vue",
|
|
||||||
"frontend/src/tmtt-defect/style.css"
|
|
||||||
],
|
|
||||||
"fingerprint": "141d712008d33887a103a5a5133543d527be208c2a5700d16f4c045ce13bb166"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# Portal Shell Route-View Migration: Wave A Smoke Checklist
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
Scope: Native shell routes (`/wip-overview`, `/wip-detail`, `/hold-overview`, `/hold-detail`, `/hold-history`, `/resource`, `/resource-history`, `/qc-gate`)
|
|
||||||
|
|
||||||
## Checklist Fields
|
|
||||||
|
|
||||||
Each page must provide and pass the following fields before cutover:
|
|
||||||
|
|
||||||
- Entry path
|
|
||||||
- Required query params
|
|
||||||
- Key interaction path
|
|
||||||
- Error path
|
|
||||||
- Export path (if applicable)
|
|
||||||
- Table checkpoint
|
|
||||||
- Chart checkpoint
|
|
||||||
- Filter checkpoint
|
|
||||||
- Interaction checkpoint
|
|
||||||
- Matrix checkpoint
|
|
||||||
- Expected outcomes
|
|
||||||
|
|
||||||
## Wave A Per-Page Checklist
|
|
||||||
|
|
||||||
| Page | Entry Path | Required Query Params | Key Interaction | Error Path | Export Path | Table Checkpoint | Chart Checkpoint | Filter Checkpoint | Interaction Checkpoint | Matrix Checkpoint | Expected Outcome | Status |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| WIP Overview | `/portal-shell/wip-overview` | `workorder, lotid, package, type, status` (optional) | Drill-down from matrix to detail view | Simulate `/api/wip/overview/*` failure and verify error banner | N/A | Matrix row/column totals equal summary counts | Pareto chart legend/tooltip/cumulative line remain aligned | Query filters update URL and survive refresh | Click status cards toggles status scope and reloads matrix | Workcenter x Package matrix selection drives detail navigation | Route remains in shell; query state and selection scope remain deterministic | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| WIP Detail | `/portal-shell/wip-detail` | `workcenter` required; `workorder, lotid, package, type, status` optional | Open lot detail, paginate, return to overview | Simulate `/api/wip/detail/*` failure and verify fallback message | N/A | Pagination continuity across page switch and refresh | N/A | URL keeps list/detail filter context | Back-link keeps overview query state intact | N/A | Detail/list continuity preserved without leaving shell runtime | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| Hold Overview | `/portal-shell/hold-overview` | `hold_type, reason, workcenter, package, page` (optional) | Change hold type/reason and matrix selection | Simulate `/api/hold-overview/*` failure and verify error banner | N/A | Lot list paging/filter text matches matrix scope | N/A | `hold_type/reason` and matrix query stay in URL | Matrix toggle clear/reselect behavior remains stable | Matrix workcenter/package selection scopes lots correctly | Type/reason query semantics preserved after refresh | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| Hold Detail | `/portal-shell/hold-detail` | `reason` required; `workcenter, package, age_range, page` optional | Toggle age/workcenter/package filters and page lots | Missing `reason` redirects to `/portal-shell/wip-overview` | N/A | Lot table page transitions preserve filter scope | Age distribution and distribution tables remain visually consistent | URL continuity for `reason/age/workcenter/package/page` | Clear filters restores default scope without stale highlights | Distribution filter selection matches lot-table scope | Reason/type semantics and back-navigation remain compatible | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| Hold History | `/portal-shell/hold-history` | `start_date, end_date, hold_type, record_type, reason, duration_range, page` | Toggle reason pareto + duration buckets and paginate | Simulate `/api/hold-history/*` failure and verify error banner | N/A | Detail table count/pagination matches active filters | Trend, pareto, duration charts keep tooltip + selected state | Date + record-type changes preserve query contract | Reason/duration toggles only affect dependent data as expected | N/A | Date/record-type parity maintained across refresh/re-entry | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| Resource Status | `/portal-shell/resource` | none | Matrix/status filter with tooltip drill inspection | Simulate `/api/resource/status*` failure and verify cache/error text | N/A | Equipment grid rows match active filters | N/A | Group/family/machine filters prune invalid selections deterministically | Tooltip open/close and row expansion remain stable | Status matrix + summary card filters compose correctly | Summary/detail parity preserved under filter combinations | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| Resource History | `/portal-shell/resource-history` | `start_date, end_date, granularity, workcenter_groups, families, resource_ids, is_production, is_key, is_monitor` | Query then export CSV under narrowed filters | Simulate summary/detail API failure and verify query error path | `/api/resource/history/export?...` | Detail section hierarchy rows stay aligned after query | Trend/stacked/heatmap/comparison charts show correct axes + tooltips | URL query reflects active filters and survives refresh | Query button always reflects current form state | N/A | Summary/detail/export parity preserved with shell route-view | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
| QC Gate | `/portal-shell/qc-gate` | none | Click chart segment to filter LOT table, then clear | Simulate data load failure and verify error banner | N/A | LOT table rows match active chart segment scope | Bar stack tooltip and segment highlighting remain consistent | N/A | Chart click toggles linked table scope without stale state | Chart bucket selection and table highlight stay synchronized | Chart-table linked interaction parity preserved in shell | Automated: pass / Manual: waived (covered by parity gates) |
|
|
||||||
|
|
||||||
## Zero-Value / Empty-State Mandatory Checks
|
|
||||||
|
|
||||||
Apply these checks for every page above:
|
|
||||||
|
|
||||||
- KPI zero values (`0`) must render as valid values, not blank/hidden placeholders.
|
|
||||||
- Table empty result must show an explicit empty state and keep column structure stable.
|
|
||||||
- Matrix empty state must keep headers/axis labels visible with deterministic zero rendering.
|
|
||||||
- Chart empty series must render empty-state/fallback text without throwing runtime errors.
|
|
||||||
- Filter combinations that produce zero rows must keep user-selected filters and query params intact.
|
|
||||||
|
|
||||||
## Current Automated Evidence
|
|
||||||
|
|
||||||
- `npm --prefix frontend test`
|
|
||||||
- includes `frontend/tests/portal-shell-wave-a-smoke.test.js`
|
|
||||||
- includes `frontend/tests/portal-shell-wave-a-chart-lifecycle.test.js`
|
|
||||||
- validates Wave A native mapping, route registration expectations, and deep-link query path behavior in shell runtime.
|
|
||||||
- `npm --prefix frontend run build`
|
|
||||||
- validates all Wave A native modules compile and bundle in shell build pipeline.
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T17:45:00+08:00",
|
|
||||||
"scope": "wave-a-native",
|
|
||||||
"routes": [
|
|
||||||
"/wip-overview",
|
|
||||||
"/wip-detail",
|
|
||||||
"/hold-overview",
|
|
||||||
"/hold-detail",
|
|
||||||
"/hold-history",
|
|
||||||
"/resource",
|
|
||||||
"/resource-history",
|
|
||||||
"/qc-gate"
|
|
||||||
],
|
|
||||||
"execution": {
|
|
||||||
"automated_runs": [
|
|
||||||
{
|
|
||||||
"command": "npm --prefix frontend test",
|
|
||||||
"status": "pass",
|
|
||||||
"evidence": [
|
|
||||||
"frontend/tests/portal-shell-wave-a-smoke.test.js",
|
|
||||||
"frontend/tests/portal-shell-wave-a-chart-lifecycle.test.js",
|
|
||||||
"frontend/tests/portal-shell-parity-table-chart-matrix.test.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pytest tests/test_route_view_migration_baseline.py tests/test_portal_shell_routes.py tests/test_cutover_gates.py -q",
|
|
||||||
"status": "pass",
|
|
||||||
"evidence": [
|
|
||||||
"tests/test_route_view_migration_baseline.py",
|
|
||||||
"tests/test_portal_shell_routes.py",
|
|
||||||
"tests/test_cutover_gates.py"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"manual_replay": "waived",
|
|
||||||
"waiver_reason": "Coverage upgraded to deterministic CI gates for table/chart/filter/interaction/matrix semantics"
|
|
||||||
},
|
|
||||||
"pages": {
|
|
||||||
"/wip-overview": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "pass",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/wip-detail": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/hold-overview": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "pass",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/hold-detail": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "pass",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/hold-history": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/resource": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "pass",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/resource-history": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/qc-gate": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "n/a",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "pass",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Portal Shell Route-View Migration: Wave B Native Smoke Checklist
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
Scope: Native shell routes (`/job-query`, `/excel-query`, `/query-tool`, `/tmtt-defect`)
|
|
||||||
|
|
||||||
## Execution Rules
|
|
||||||
|
|
||||||
- Wave B routes are now `native` and must remain no-iframe in shell content area.
|
|
||||||
- Any P0 smoke failure blocks release until resolved.
|
|
||||||
- `/excel-query` and `/query-tool` smoke must run under admin session.
|
|
||||||
|
|
||||||
## Per-Page Native Smoke Checklist
|
|
||||||
|
|
||||||
| Page | Shell Entry Path | Required Query Params | Key Interaction | Error Path | Export Path | Table Checkpoint | Chart Checkpoint | Filter Checkpoint | Interaction Checkpoint | Matrix Checkpoint | Expected Outcome | Automated Evidence |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| Job Query | `/portal-shell/job-query` | `resource_ids, start_date, end_date` | Resource load -> query jobs -> open txn detail | Missing resource/date returns validation error | `/api/job-query/export` | Jobs/Txn table columns keep API order and empty-state text | N/A | Date/resource/search filters sync to URL | Selected job row loads txn table with stable state | N/A | Query/search/export remain usable in native route-view | `tests/test_portal_shell_wave_b_native_smoke.py::test_job_query_native_smoke_query_search_export`; `frontend/tests/portal-shell-wave-b-native-smoke.test.js` |
|
|
||||||
| Excel Query (Admin) | `/portal-shell/excel-query` | `table_name, search_column, return_columns` (+ upload) | Upload Excel -> detect type -> execute advanced query | Invalid file / missing required args returns validation error | `/api/excel-query/export-csv` | Result table columns and row count match response payload | N/A | Table/query/date filters sync to URL and persist on refresh | Upload/query/export flow keeps success/error feedback contract | N/A | Upload/detect/query/export parity preserved after native cutover | `tests/test_portal_shell_wave_b_native_smoke.py::test_excel_query_native_smoke_upload_detect_query_export`; `frontend/tests/portal-shell-wave-b-native-smoke.test.js` |
|
|
||||||
| Query Tool (Admin) | `/portal-shell/query-tool` | `input_type`, optional `workcenter_groups`, `equipment_ids`, date range | Resolve -> history -> associations -> equipment-period query | Missing input/container/type triggers deterministic errors | `/api/query-tool/export-csv` | Resolved/history/association/equipment tables stay query-consistent | N/A | Batch/equipment filters sync to URL with multi-value keys | Selection and association state transitions remain deterministic | N/A | Resolve/history/association/equipment workflows remain native-stable | `tests/test_portal_shell_wave_b_native_smoke.py::test_query_tool_native_smoke_resolve_history_association`; `frontend/tests/portal-shell-wave-b-native-smoke.test.js` |
|
|
||||||
| TMTT Defect | `/portal-shell/tmtt-defect` | `start_date, end_date` | Query -> pareto chart select -> detail sort/filter clear | Invalid/empty API payload shows fallback error banner | `/api/tmtt-defect/export` | Detail table sort/filter keeps scope continuity | Pareto/trend charts keep tooltip/legend/link state | Date range and active filter state preserved in view | Chart-table linked filtering resets correctly | N/A | TMTT chart-table parity and export remain stable in shell | `tests/test_portal_shell_wave_b_native_smoke.py::test_tmtt_defect_native_smoke_range_query_and_csv_export`; `frontend/tests/portal-shell-parity-table-chart-matrix.test.js` |
|
|
||||||
|
|
||||||
## No-Iframe Rule
|
|
||||||
|
|
||||||
- Shell content route-view must not render `<iframe>` for any Wave B route.
|
|
||||||
- Regression checks: `frontend/tests/portal-shell-no-iframe.test.js`, `tests/test_cutover_gates.py::test_g4_no_iframe_gate_blocks_if_shell_uses_iframe`.
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T17:46:00+08:00",
|
|
||||||
"scope": "wave-b-native",
|
|
||||||
"routes": [
|
|
||||||
"/job-query",
|
|
||||||
"/excel-query",
|
|
||||||
"/query-tool",
|
|
||||||
"/tmtt-defect"
|
|
||||||
],
|
|
||||||
"execution": {
|
|
||||||
"automated_runs": [
|
|
||||||
{
|
|
||||||
"command": "pytest tests/test_portal_shell_wave_b_native_smoke.py -q",
|
|
||||||
"status": "pass",
|
|
||||||
"evidence": [
|
|
||||||
"tests/test_portal_shell_wave_b_native_smoke.py"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "npm --prefix frontend test",
|
|
||||||
"status": "pass",
|
|
||||||
"evidence": [
|
|
||||||
"frontend/tests/portal-shell-wave-b-native-smoke.test.js",
|
|
||||||
"frontend/tests/portal-shell-parity-table-chart-matrix.test.js",
|
|
||||||
"frontend/tests/portal-shell-no-iframe.test.js"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"manual_replay": "waived",
|
|
||||||
"waiver_reason": "Native rewrite pages now covered by deterministic API + shell route tests"
|
|
||||||
},
|
|
||||||
"pages": {
|
|
||||||
"/job-query": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "n/a",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"status": "pass",
|
|
||||||
"critical_failures": [],
|
|
||||||
"checkpoints": {
|
|
||||||
"table": "pass",
|
|
||||||
"chart": "pass",
|
|
||||||
"filter": "pass",
|
|
||||||
"interaction": "pass",
|
|
||||||
"matrix": "n/a",
|
|
||||||
"zero_empty": "pass"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T17:47:00+08:00",
|
|
||||||
"description": "Wave B native rewrite parity audit for table/chart/filter/interaction/matrix",
|
|
||||||
"policy": {
|
|
||||||
"required_status": "pass",
|
|
||||||
"allow_na": true,
|
|
||||||
"block_on_fail": true
|
|
||||||
},
|
|
||||||
"pages": {
|
|
||||||
"/job-query": {
|
|
||||||
"table": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"job result table columns preserve API key order",
|
|
||||||
"transaction table renders empty state deterministically"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"resource_ids/start_date/end_date/search sync to URL",
|
|
||||||
"invalid date range blocks query"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interaction": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"query then txn load remains in-shell",
|
|
||||||
"export CSV uses current query scope"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"table": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"result table columns equal response columns",
|
|
||||||
"empty result keeps stable headers"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"table/search/date/query_type/return_columns sync to URL",
|
|
||||||
"missing required fields produce deterministic errors"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interaction": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"upload -> detect -> query workflow stable",
|
|
||||||
"export uses active query columns"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"table": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"resolved/history/association/equipment tables keep deterministic columns",
|
|
||||||
"empty query results keep table shell intact"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"input_type/workcenter_groups/equipment/date filters sync to URL",
|
|
||||||
"selection-required actions show deterministic errors"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interaction": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"resolve -> history -> association flow remains coherent",
|
|
||||||
"equipment-period export respects selected query type"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"table": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"detail table sort state stable after chart filter changes",
|
|
||||||
"filter clear restores full table scope"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"pareto and trend charts maintain tooltip behavior",
|
|
||||||
"legend/filter selection remains synchronized with detail table"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"date-range query semantics preserved",
|
|
||||||
"active filter badge reflects chart selection"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interaction": {
|
|
||||||
"status": "pass",
|
|
||||||
"checks": [
|
|
||||||
"chart selection narrows detail rows and supports clear",
|
|
||||||
"CSV export follows active date range"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"matrix": {
|
|
||||||
"status": "n/a",
|
|
||||||
"checks": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
{
|
|
||||||
"generated_at": "2026-02-11T16:10:00+08:00",
|
|
||||||
"description": "Wave B rewrite entry criteria gates. Native cutover is blocked unless per-page criteria are complete.",
|
|
||||||
"pages": {
|
|
||||||
"/job-query": {
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"required_smoke_checks": [
|
|
||||||
"JOB-NATIVE-SMOKE-01",
|
|
||||||
"JOB-NATIVE-SMOKE-02",
|
|
||||||
"JOB-NATIVE-SMOKE-03"
|
|
||||||
],
|
|
||||||
"required_parity_checks": [
|
|
||||||
"table-columns-and-sort",
|
|
||||||
"query-parameter-semantics",
|
|
||||||
"export-content-contract"
|
|
||||||
],
|
|
||||||
"evidence": {
|
|
||||||
"smoke": "pass",
|
|
||||||
"parity": "pass",
|
|
||||||
"telemetry": "pass"
|
|
||||||
},
|
|
||||||
"native_cutover_ready": true,
|
|
||||||
"block_reason": ""
|
|
||||||
},
|
|
||||||
"/excel-query": {
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"required_smoke_checks": [
|
|
||||||
"EXCEL-NATIVE-SMOKE-01",
|
|
||||||
"EXCEL-NATIVE-SMOKE-02",
|
|
||||||
"EXCEL-NATIVE-SMOKE-03"
|
|
||||||
],
|
|
||||||
"required_parity_checks": [
|
|
||||||
"upload-parse-contract",
|
|
||||||
"query-result-schema",
|
|
||||||
"export-content-contract"
|
|
||||||
],
|
|
||||||
"evidence": {
|
|
||||||
"smoke": "pass",
|
|
||||||
"parity": "pass",
|
|
||||||
"telemetry": "pass"
|
|
||||||
},
|
|
||||||
"native_cutover_ready": true,
|
|
||||||
"block_reason": ""
|
|
||||||
},
|
|
||||||
"/query-tool": {
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"required_smoke_checks": [
|
|
||||||
"QTOOL-NATIVE-SMOKE-01",
|
|
||||||
"QTOOL-NATIVE-SMOKE-02",
|
|
||||||
"QTOOL-NATIVE-SMOKE-03"
|
|
||||||
],
|
|
||||||
"required_parity_checks": [
|
|
||||||
"resolve-history-association-contract",
|
|
||||||
"date-range-validation",
|
|
||||||
"state-continuity"
|
|
||||||
],
|
|
||||||
"evidence": {
|
|
||||||
"smoke": "pass",
|
|
||||||
"parity": "pass",
|
|
||||||
"telemetry": "pass"
|
|
||||||
},
|
|
||||||
"native_cutover_ready": true,
|
|
||||||
"block_reason": ""
|
|
||||||
},
|
|
||||||
"/tmtt-defect": {
|
|
||||||
"owner": "frontend-mes-reporting",
|
|
||||||
"required_smoke_checks": [
|
|
||||||
"TMTT-NATIVE-SMOKE-01",
|
|
||||||
"TMTT-NATIVE-SMOKE-02",
|
|
||||||
"TMTT-NATIVE-SMOKE-03"
|
|
||||||
],
|
|
||||||
"required_parity_checks": [
|
|
||||||
"range-query-contract",
|
|
||||||
"chart-detail-linkage",
|
|
||||||
"csv-export-contract"
|
|
||||||
],
|
|
||||||
"evidence": {
|
|
||||||
"smoke": "pass",
|
|
||||||
"parity": "pass",
|
|
||||||
"telemetry": "pass"
|
|
||||||
},
|
|
||||||
"native_cutover_ready": true,
|
|
||||||
"block_reason": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Wave B Rewrite Entry Criteria and Native Cutover Gate
|
|
||||||
|
|
||||||
Last updated: 2026-02-11
|
|
||||||
|
|
||||||
Source of truth: `wave-b-rewrite-entry-criteria.json`
|
|
||||||
|
|
||||||
## Gate Rule
|
|
||||||
|
|
||||||
- If a Wave B route is switched to `render_mode=native` while `native_cutover_ready=false`, cutover validation must fail.
|
|
||||||
- `native_cutover_ready=true` requires:
|
|
||||||
- `evidence.smoke = pass`
|
|
||||||
- `evidence.parity = pass`
|
|
||||||
- `evidence.telemetry = pass` or `n/a`
|
|
||||||
|
|
||||||
## Current Status
|
|
||||||
|
|
||||||
| Route | Smoke Evidence | Parity Evidence | Telemetry Evidence | Native Cutover Ready |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| `/job-query` | pass | pass | pass | true |
|
|
||||||
| `/excel-query` | pass | pass | pass | true |
|
|
||||||
| `/query-tool` | pass | pass | pass | true |
|
|
||||||
| `/tmtt-defect` | pass | pass | pass | true |
|
|
||||||
|
|
||||||
Current policy outcome: all Wave B pages meet native cutover entry criteria.
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
# Migration Gates and Runbook
|
|
||||||
|
|
||||||
## Gate Checklist (Cutover Readiness)
|
|
||||||
|
|
||||||
A release is cutover-ready only when all gates pass:
|
|
||||||
|
|
||||||
1. Frontend build gate
|
|
||||||
- `npm --prefix frontend run build` succeeds
|
|
||||||
- expected artifacts exist in `src/mes_dashboard/static/dist/`
|
|
||||||
|
|
||||||
2. Root execution gate
|
|
||||||
- startup and deploy scripts run from repository root only
|
|
||||||
- no runtime dependency on any legacy subtree path
|
|
||||||
|
|
||||||
3. Functional parity gate
|
|
||||||
- resource-history frontend compute parity checks pass
|
|
||||||
- job-query/resource-history export headers match shared field contracts
|
|
||||||
|
|
||||||
4. Cache observability gate
|
|
||||||
- `/health` returns route cache telemetry and degraded flags
|
|
||||||
- `/health/deep` returns route cache telemetry for diagnostics
|
|
||||||
- `/health` includes `database_pool.runtime/state`, `degraded_reason`
|
|
||||||
- resource/wip derived index telemetry is visible (`resource_cache.derived_index`, `cache.derived_search_index`)
|
|
||||||
|
|
||||||
5. Runtime resilience gate
|
|
||||||
- pool exhaustion path returns `503` + `DB_POOL_EXHAUSTED` and `Retry-After`
|
|
||||||
- circuit-open path returns `503` + `CIRCUIT_BREAKER_OPEN` and fail-fast semantics
|
|
||||||
- frontend client does not aggressively retry on degraded pool exhaustion responses
|
|
||||||
- health/admin payloads expose worker policy state (`allowed`/`cooldown`/`blocked`) and alert booleans
|
|
||||||
|
|
||||||
6. Conda-systemd contract gate
|
|
||||||
- `deploy/mes-dashboard.service` and `deploy/mes-dashboard-watchdog.service` both run in the same conda runtime contract
|
|
||||||
- `WATCHDOG_RESTART_FLAG`, `WATCHDOG_PID_FILE`, `WATCHDOG_STATE_FILE` paths are consistent across app/admin/watchdog
|
|
||||||
- startup contract validation passes: `RUNTIME_CONTRACT_ENFORCE=true ./scripts/start_server.sh check`
|
|
||||||
- single-port bind (`GUNICORN_BIND`) remains stable during restart workflow
|
|
||||||
|
|
||||||
7. Regression gate
|
|
||||||
- focused unit/integration test subset passes (see validation evidence)
|
|
||||||
|
|
||||||
8. Documentation alignment gate
|
|
||||||
- `README.md` (and project-required mirror docs such as `README.mdj`) reflect current runtime architecture contract
|
|
||||||
- resilience diagnostics fields (thresholds/churn/recommendation) are documented for operators
|
|
||||||
- frontend shared-core governance updates are reflected in architecture notes
|
|
||||||
|
|
||||||
## Rollout Procedure
|
|
||||||
|
|
||||||
1. Prepare environment
|
|
||||||
- Activate conda env (`mes-dashboard`)
|
|
||||||
- install Python deps: `pip install -r requirements.txt`
|
|
||||||
- install frontend deps: `npm --prefix frontend install`
|
|
||||||
|
|
||||||
2. Build frontend artifacts
|
|
||||||
- `npm --prefix frontend run build`
|
|
||||||
|
|
||||||
3. Run migration gate tests
|
|
||||||
- execute focused pytest set covering templates/cache/contracts/health
|
|
||||||
|
|
||||||
4. Deploy with single-port mode
|
|
||||||
- start app with root `scripts/start_server.sh`
|
|
||||||
- verify portal and module pages render on same origin/port
|
|
||||||
|
|
||||||
5. Conda + systemd rehearsal (recommended before production cutover)
|
|
||||||
- `sudo cp deploy/mes-dashboard.service /etc/systemd/system/`
|
|
||||||
- `sudo cp deploy/mes-dashboard-watchdog.service /etc/systemd/system/`
|
|
||||||
- ensure deployment uses the same single env file: `/opt/mes-dashboard/.env`
|
|
||||||
- `sudo chown root:www-data /opt/mes-dashboard/.env && sudo chmod 640 /opt/mes-dashboard/.env`
|
|
||||||
- `sudo systemctl daemon-reload`
|
|
||||||
- `sudo systemctl enable --now mes-dashboard mes-dashboard-watchdog`
|
|
||||||
- call `/admin/api/worker/status` and verify runtime contract paths exist
|
|
||||||
|
|
||||||
6. Post-deploy checks
|
|
||||||
- call `/health` and `/health/deep`
|
|
||||||
- confirm route cache mode, degraded flags, and pool/runtime diagnostics align with environment (Redis on/off)
|
|
||||||
- trigger one controlled worker restart from admin API and verify single-port continuity
|
|
||||||
- verify guarded mode flow: blocked restart requires manual override payload (`manual_override`, `override_acknowledged`, `override_reason`)
|
|
||||||
- verify README architecture section matches deployed runtime contract
|
|
||||||
|
|
||||||
## Rollback Procedure
|
|
||||||
|
|
||||||
1. Trigger rollback criteria
|
|
||||||
- any critical gate failure after deployment (page unusable, export mismatch, health degradation beyond acceptable limits)
|
|
||||||
|
|
||||||
2. Operational rollback steps
|
|
||||||
- stop service: `scripts/start_server.sh stop`
|
|
||||||
- restore previously known-good build artifacts (or prior release package)
|
|
||||||
- restart service: `scripts/start_server.sh start`
|
|
||||||
- if using systemd: `sudo systemctl restart mes-dashboard mes-dashboard-watchdog`
|
|
||||||
|
|
||||||
3. Validation after rollback
|
|
||||||
- verify `/health` status is at least expected baseline
|
|
||||||
- re-run focused smoke tests for portal + key pages
|
|
||||||
- confirm CSV export downloads and headers
|
|
||||||
- verify degraded reason is cleared or matches expected dependency outage only
|
|
||||||
|
|
||||||
## Rollback Rehearsal Checklist
|
|
||||||
|
|
||||||
1. Simulate failure condition (e.g. invalid dist artifact deployment)
|
|
||||||
2. Execute stop/restore/start sequence
|
|
||||||
3. Verify health and page smoke checks
|
|
||||||
4. Capture timings and any manual intervention points
|
|
||||||
5. Update this runbook if any step was unclear or missing
|
|
||||||
|
|
||||||
## Alert Thresholds (Operational Contract)
|
|
||||||
|
|
||||||
Use these initial thresholds for alerting/escalation:
|
|
||||||
|
|
||||||
1. Sustained degraded state
|
|
||||||
- `degraded_reason` non-empty for >= 5 minutes
|
|
||||||
|
|
||||||
2. Worker restart churn
|
|
||||||
- >= 3 watchdog-triggered restarts within 10 minutes
|
|
||||||
|
|
||||||
3. Pool saturation pressure
|
|
||||||
- `database_pool.state.saturation >= 0.90` for >= 3 consecutive health probes
|
|
||||||
|
|
||||||
4. Frontend/API retry pressure
|
|
||||||
- significant increase of client retries for `DB_POOL_EXHAUSTED` or `CIRCUIT_BREAKER_OPEN` responses over baseline
|
|
||||||
|
|
||||||
5. Recovery policy blocked
|
|
||||||
- `resilience.policy_state.blocked == true` or `resilience.alerts.restart_blocked == true`
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# Migration Validation Evidence
|
|
||||||
|
|
||||||
Date: 2026-02-07
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Command:
|
|
||||||
- `npm --prefix frontend run build`
|
|
||||||
|
|
||||||
Result:
|
|
||||||
- PASS
|
|
||||||
- Generated page bundles:
|
|
||||||
- `portal.js`
|
|
||||||
- `resource-status.js`
|
|
||||||
- `resource-history.js`
|
|
||||||
- `job-query.js`
|
|
||||||
- `excel-query.js`
|
|
||||||
- `tables.js`
|
|
||||||
|
|
||||||
## Root Startup Smoke
|
|
||||||
|
|
||||||
Command:
|
|
||||||
- `PYTHONPATH=src python -c \"from mes_dashboard.app import create_app; app=create_app('testing'); print('routes', len(list(app.url_map.iter_rules())))\"`
|
|
||||||
|
|
||||||
Result:
|
|
||||||
- PASS
|
|
||||||
- `routes 71`
|
|
||||||
- Redis/Oracle warnings observed in this local environment; app factory and route registration still completed.
|
|
||||||
- Note: current tree includes additional routes (query-tool / tmtt-defect / hardening paths), so fresh smoke now reports `routes 83`.
|
|
||||||
|
|
||||||
## Focused Test Gate (root project)
|
|
||||||
|
|
||||||
Command:
|
|
||||||
- `python -m pytest -q tests/test_app_factory.py tests/test_template_integration.py tests/test_cache.py tests/test_health_routes.py tests/test_field_contracts.py tests/test_frontend_compute_parity.py tests/test_job_query_service.py tests/test_resource_history_service.py`
|
|
||||||
|
|
||||||
Result:
|
|
||||||
- PASS
|
|
||||||
- `107 passed`
|
|
||||||
|
|
||||||
## Extended Regression Spot-check
|
|
||||||
|
|
||||||
Command:
|
|
||||||
- `python -m pytest -q tests/test_job_query_routes.py tests/test_resource_history_routes.py tests/test_cache_integration.py`
|
|
||||||
|
|
||||||
Result:
|
|
||||||
- PARTIAL
|
|
||||||
- `45 passed, 2 failed`
|
|
||||||
- Failed tests:
|
|
||||||
- `tests/test_cache_integration.py::TestWipApiWithCache::test_wip_matrix_uses_cache`
|
|
||||||
- `tests/test_cache_integration.py::TestWipApiWithCache::test_packages_uses_cache`
|
|
||||||
|
|
||||||
Failure profile:
|
|
||||||
- cache-fallback path hit Oracle in local environment and returned ORA connectivity/thick-mode errors.
|
|
||||||
- categorized as environment-dependent (see `docs/environment_gaps_and_mitigation.md`).
|
|
||||||
|
|
||||||
## Health/Telemetry Coverage
|
|
||||||
|
|
||||||
Validated by tests:
|
|
||||||
- `/health` includes `route_cache` telemetry and degraded warnings
|
|
||||||
- `/health/deep` includes route-cache telemetry block
|
|
||||||
- cache telemetry includes L1/L2 mode, hit/miss counters, degraded state
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Page Architecture Map
|
|
||||||
|
|
||||||
## Portal Navigation Model
|
|
||||||
|
|
||||||
Portal (`/`) uses drawer-based navigation and keeps existing operational flow:
|
|
||||||
|
|
||||||
- 報表類
|
|
||||||
- `/wip-overview`
|
|
||||||
- `/resource`
|
|
||||||
- `/resource-history`
|
|
||||||
- 查詢類
|
|
||||||
- `/tables`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/job-query`
|
|
||||||
- 開發工具
|
|
||||||
- `/admin/pages`
|
|
||||||
- `/admin/performance`
|
|
||||||
|
|
||||||
## Independent Pages
|
|
||||||
|
|
||||||
These pages are independent views (iframe tabs in portal) and can be loaded directly:
|
|
||||||
- `/wip-overview`
|
|
||||||
- `/resource`
|
|
||||||
- `/resource-history`
|
|
||||||
- `/tables`
|
|
||||||
- `/excel-query`
|
|
||||||
- `/job-query`
|
|
||||||
|
|
||||||
## Drill-down Pages
|
|
||||||
|
|
||||||
These pages are drill-down/detail pages, linked from parent views:
|
|
||||||
- `/wip-detail` (from WIP flows)
|
|
||||||
- `/hold-detail` (from hold-related flows)
|
|
||||||
|
|
||||||
## Vite Entry Mapping
|
|
||||||
|
|
||||||
- `portal` -> `frontend/src/portal/main.js`
|
|
||||||
- `resource-status` -> `frontend/src/resource-status/main.js`
|
|
||||||
- `resource-history` -> `frontend/src/resource-history/main.js`
|
|
||||||
- `job-query` -> `frontend/src/job-query/main.js`
|
|
||||||
- `excel-query` -> `frontend/src/excel-query/main.js`
|
|
||||||
- `tables` -> `frontend/src/tables/main.js`
|
|
||||||
|
|
||||||
All pages keep inline fallback scripts in templates when module assets are unavailable.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user