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:
egg
2026-02-13 17:42:11 +08:00
parent 5b358d71c1
commit 248cbc25e0
119 changed files with 842 additions and 9085 deletions

View File

@@ -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

View File

@@ -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 |

View File

@@ -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: 永久
### MesApiHTTP 請求)
定義於 `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` |

View File

@@ -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`

View File

@@ -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`

View File

@@ -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.

View File

@@ -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`

View File

@@ -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

View File

@@ -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.

View File

@@ -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"
}
]
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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>
```

View File

@@ -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"
}
]
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"
}
]
}

View File

@@ -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.

View File

@@ -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"
]
}

View File

@@ -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.

View File

@@ -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": []
}

View File

@@ -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"
}
}

View File

@@ -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"
]
}

View File

@@ -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.

View File

@@ -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"
}
]
}

View File

@@ -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/`.

View File

@@ -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"]
}
}

View File

@@ -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
}
}

View File

@@ -1,5 +0,0 @@
{
"change": "full-modernization-architecture-blueprint",
"records": [],
"rule": "next route cutover is blocked until current route has approved manual sign-off"
}

View File

@@ -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.

View File

@@ -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`

View File

@@ -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"]
}
}

View File

@@ -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"
]
}

View File

@@ -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": []
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"]
}
}
}

View File

@@ -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"
}
]
}

View File

@@ -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": []
}

View File

@@ -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"]
}
}

View File

@@ -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.

View File

@@ -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)"
}
}
}

View File

@@ -1,4 +0,0 @@
{
"source": "data/page_status.json",
"errors": []
}

View File

@@ -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
}
]
}
]
}

View File

@@ -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"
}
}
}

View File

@@ -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`

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 檢查一致。

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
]
}
]
}

View File

@@ -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
]
}
]
}

View File

@@ -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:

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 / Dont
- Do: prefer composable utility classes and shared Vue components.
- Do: keep style changes scoped to one route family per batch.
- Dont: introduce new long inline `<style>` blocks in templates.
- Dont: mix unrelated refactors with migration styling tasks.

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"
}
}
}

View File

@@ -1,5 +0,0 @@
{
"generated_at": "2026-02-11T07:44:03+00:00",
"source": "data/page_status.json",
"errors": []
}

View File

@@ -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
}
]
}
]
}

View File

@@ -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"
]
}
}
}

View File

@@ -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"
}
}
}

View File

@@ -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"
]
}
]
}

View File

@@ -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`

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"
}
]
}

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
{
"generated_at": "2026-02-11T07:44:03+00:00",
"errors": []
}

View File

@@ -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.

View File

@@ -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"
}
]
}

View File

@@ -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.

View File

@@ -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"
}
}
}
}

View File

@@ -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`.

View File

@@ -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"
}
}
}
}

View File

@@ -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": []
}
}
}
}

View File

@@ -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": ""
}
}
}

View File

@@ -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.

View File

@@ -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`

View File

@@ -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

View File

@@ -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