feat: 完成P10生产硬化与矩阵收口(P10E1-P10G1)

这个提交包含在:
2026-02-10 11:25:18 +08:00
父节点 1b6e801b12
当前提交 bec21e1b38
共修改 15 个文件,包含 1718 行新增和 793 行删除
+36 -3
查看文件
@@ -3,40 +3,73 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
echo "=== [1/3] Server Prisma Validate ==="
echo "=== [1/4] Server Prisma Validate ==="
(
cd "$ROOT_DIR/server"
npm run prisma:validate
)
echo ""
echo "=== [2/3] Server Static Checks ==="
echo "=== [2/4] Server Static Checks ==="
(
cd "$ROOT_DIR/server"
node --check src/routes/reimbursements.js
node --check src/routes/workflow.js
node --check src/routes/delegations.js
node --check src/routes/invoicePool.js
node --check src/routes/invoicePool/verification.js
node --check src/routes/invoicePool/read.js
node --check src/routes/invoicePool/shared/selects.js
node --check src/routes/invoicePool/shared/mappers.js
node --check src/routes/invoicePool/shared/schemas.js
node --check src/routes/invoicePool/shared/helpers.js
node --check src/routes/audit.js
node --check src/routes/users.js
node --check src/routes/dashboard.js
node --check src/routes/budgets.js
node --check src/routes/integrations.js
node --check src/routes/travelOrders.js
node --check src/routes/notifications.js
node --check src/jobs/delegationRevokeJob.js
node --check src/jobs/ocrJob.js
node --check src/jobs/pgBossJobs.js
node --check src/jobs/verificationJob.js
node --check src/jobs/integrationOutboxJob.js
node --check src/jobs/integrationOutboxWorker.js
node --check src/services/ocr/index.js
node --check src/services/verification/index.js
node --check src/services/integrations/outbox.js
node --check src/services/integrations/erp.js
node --check src/services/integrations/dispatcher.js
bash -n scripts/regression-workflow-actions.sh
bash -n scripts/regression-audit.sh
bash -n scripts/regression-ocr.sh
bash -n scripts/regression-org-settings.sh
bash -n scripts/regression-auth-security.sh
bash -n scripts/regression-verification.sh
bash -n scripts/regression-dashboard.sh
bash -n scripts/regression-budget.sh
bash -n scripts/regression-integrations.sh
bash -n scripts/regression-all.sh
)
echo ""
echo "=== [3/3] Client Typecheck + Build ==="
echo "=== [3/4] Client Typecheck + Build ==="
(
cd "$ROOT_DIR/client"
npm run typecheck
npm run build
)
echo ""
echo "=== [4/4] Feature Matrix Consistency ==="
(
cd "$ROOT_DIR"
python3 scripts/validate-feature-matrix.py
)
echo ""
echo "[OK] Minimal checks passed"
+149
查看文件
@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""Validate docs/feature-matrix.md against execution-contract CSV sources.
Checks:
- feature-matrix contains exactly one row per ID in CSV fact sources
- every ID from CSVs appears in matrix
- matrix does not contain unknown IDs
- every matrix row status equals DONE
- every fact-source CSV row is fully DONE on Dev/Review1/Regression axes
"""
from __future__ import annotations
import csv
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MATRIX_PATH = ROOT / "docs" / "feature-matrix.md"
CSV_FACT_SOURCES = [
ROOT / "issues" / "2026-02-09_13-39-02-p5-blueprint-v3.csv",
ROOT / "issues" / "2026-02-09_23-30-00-p6-p8-remaining-work-master.csv",
ROOT / "issues" / "2026-02-10_09-42-31-p9-maintenance-performance-matrix.csv",
ROOT / "issues" / "2026-02-10_10-35-30-p10-production-hardening.csv",
]
ID_PATTERN = re.compile(r"^[A-Z][0-9][0-9A-Za-z]*$")
STATUS_INDEX = 6
def load_csv_rows(path: Path) -> list[dict[str, str]]:
with path.open(encoding="utf-8", newline="") as fp:
return list(csv.DictReader(fp))
def collect_expected_ids_and_source_status(paths: list[Path]) -> tuple[list[str], list[str]]:
ids: list[str] = []
source_errors: list[str] = []
for path in paths:
rows = load_csv_rows(path)
for row in rows:
row_id = (row.get("ID") or "").strip()
if not row_id:
continue
ids.append(row_id)
dev_status = (row.get("Dev_Status") or "").strip().upper()
review_status = (row.get("Review1_Status") or "").strip().upper()
regression_status = (row.get("Regression_Status") or "").strip().upper()
if not (dev_status == review_status == regression_status == "DONE"):
source_errors.append(
f"{path.name}:{row_id} source status not all DONE "
f"(Dev={dev_status}, Review1={review_status}, Regression={regression_status})"
)
return ids, source_errors
def load_matrix_rows(path: Path) -> list[list[str]]:
rows: list[list[str]] = []
for line in path.read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if not stripped.startswith("|"):
continue
if stripped.startswith("| ---"):
continue
parts = [part.strip() for part in stripped.strip("|").split("|")]
if not parts:
continue
row_id = parts[0]
if not ID_PATTERN.match(row_id):
continue
rows.append(parts)
return rows
def main() -> int:
errors: list[str] = []
if not MATRIX_PATH.exists():
errors.append(f"Missing matrix file: {MATRIX_PATH}")
for source in CSV_FACT_SOURCES:
if not source.exists():
errors.append(f"Missing csv file: {source}")
if errors:
for err in errors:
print(f"[ERR] {err}")
return 1
expected_ids, source_status_errors = collect_expected_ids_and_source_status(CSV_FACT_SOURCES)
matrix_rows = load_matrix_rows(MATRIX_PATH)
matrix_ids = [row[0] for row in matrix_rows]
expected_id_set = set(expected_ids)
matrix_id_set = set(matrix_ids)
missing_in_matrix = sorted(expected_id_set - matrix_id_set)
extra_in_matrix = sorted(matrix_id_set - expected_id_set)
if missing_in_matrix:
errors.append(f"IDs missing in matrix: {missing_in_matrix}")
if extra_in_matrix:
errors.append(f"Extra IDs in matrix: {extra_in_matrix}")
duplicate_matrix_ids = sorted({item for item in matrix_ids if matrix_ids.count(item) > 1})
if duplicate_matrix_ids:
errors.append(f"Duplicate IDs in matrix: {duplicate_matrix_ids}")
if len(matrix_ids) != len(expected_ids):
errors.append(f"Row count mismatch: matrix={len(matrix_ids)} expected={len(expected_ids)}")
duplicate_expected_ids = sorted({item for item in expected_ids if expected_ids.count(item) > 1})
if duplicate_expected_ids:
errors.append(f"Duplicate IDs in fact-source CSVs: {duplicate_expected_ids}")
for row in matrix_rows:
if len(row) <= STATUS_INDEX:
errors.append(f"Malformed matrix row (too few columns): {row}")
continue
status = row[STATUS_INDEX].upper()
if status != "DONE":
errors.append(f"Non-DONE status in matrix for {row[0]}: {row[STATUS_INDEX]}")
errors.extend(source_status_errors)
if errors:
print("[ERR] feature-matrix validation failed")
for err in errors:
print(f" - {err}")
return 1
source_names = ", ".join(path.name for path in CSV_FACT_SOURCES)
print(
f"[OK] feature-matrix matches fact sources ({source_names}) "
f"(IDs/status/count)"
)
return 0
if __name__ == "__main__":
sys.exit(main())