# TASK:门禁 validate 日志过大,网页打不开 — 2026-10-04 ## 基线 - 开工时 `git fetch origin --prune` 后的最新 `origin/staging`,写本单时是 `baf4ae8b` 之后的文档提交。 - 分支 `codex/gate-log-volume-20261004`,工作树 `.worktrees/gate-log-volume-20261004`。 ## 事故实证 - 第六批 `baf4ae8b` 的门禁 validate 失败(Gitea task 6691、run 3170),staging 仍部署 `153c6e99`。 - 产品负责人打开 job 页面,「Validate backend, package, frontend, and database contracts」这一步日志太大,网页加载不出来,看不到报错。 - 这一步是 `.gitea/workflows/backend-quality-gate.yml` 里 `validate` job 的单个 `run:` 块,依次跑 ruff、py_compile、`run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime`、`commercial_privacy_artifact_scan.py --json`、`python -m build`、`npm test --prefix frontend`、`npm run lint`。 - 本机(10-04)测得的输出量: | 来源 | 行数 | 字节 | 原因 | |---|---|---|---| | `npm test --prefix frontend`(`tsx --test`,默认 TAP) | 约 26,800 | 约 1.2 MB | 4,932 条测试,每条带 `duration_ms` 等 YAML 块,通过的也打印;数据库集成测试 `tests/database-*.test.ts` 也在里面 | | `run_quality_gate.py --profile quick` | 约 14,000 | 约 0.66 MB | `run()`(`subprocess.run(cmd, …)` 不捕获输出)把子脚本整份 JSON 报告直接打到日志:`interpretation_source_inventory_gate.py` 约 11,000 行,`audit_capabilities.py --mode validate` 约 2,000 行,`character_level_inventory_manifest.py` 约 260 行;开头还 `print(json.dumps(profile, indent=2))` | | `python -m build` | 约 2,900 | 约 0.22 MB | setuptools 逐个文件打印 `copying …` / `adding …` | | 合计 | 4.4 万行以上 | 2 MB 以上 | 全部挤在同一个 step,失败点埋在中间 | ## 根因 门禁把「全部明细」当默认输出,并且把 7 个检查合成一个 step。成功的检查也刷屏;失败时既看不出是哪一项失败,网页也渲染不出来。 ## 决策记录(产品负责人 2026-10-04) 1. ①②③ 三项都做。 2. **授权修改 `.gitea/workflows/backend-quality-gate.yml`**,只限本单 T3 所述的拆分步骤和输出重定向。不改触发条件、`paths:` 过滤、job 依赖、runner、密钥、超时、发布与部署逻辑。AGENTS §2.7 规定改 workflow 只能由产品负责人触发,本条即为触发。 3. 本机验收时 `npm test` 仍输出完整 TAP(我方按测试名逐条比对失败名单,依赖它);只有门禁环境才精简。 ## 硬红线 1. **检查本身一项都不能少,也不能放宽。** 每个检查的命令、参数、退出码语义不变;任何检查失败,门禁照样失败。不得用 `|| true`、`continue-on-error`,不得吞掉退出码。用管道时必须 `set -o pipefail`,或者显式取退出码。 2. 失败时日志里**必须能看到**:失败的是哪一项;失败测试的名字和报错正文。快速门子步骤失败时,至少给出该子步骤输出的最后 200 行。 3. 完整明细不能丢:写进 runner 上的文件(如 `$RUNNER_TEMP` 或工作区内的 `gate-logs/`),并在失败时把路径打出来。不新增 artifact 上传(上传动作要新的 action 依赖,不在本单范围)。 4. 本机默认行为不变: - `npm test` 不带 CI 环境变量时输出与现在逐字节同类(TAP); - `run_quality_gate.py` 加 `--verbose` 时恢复现在的全量输出。 5. 门禁 workflow 的文本被 `frontend/tests/staging-backend-workflows.test.ts` 等合同测试断言。改 workflow 必须同步改这些测试,并按 AGENTS §7.3 写三栏。动手前先 `grep -rn` 一遍 `frontend/tests/` 和 `tests/`(AGENTS §7.8)。 6. 不用 `git stash`,不切换别人的工作树,开工前先 `df -h /`。 ## 任务分解 ### T1 快速门只打摘要(`scripts/run_quality_gate.py`) - `run()` 改为捕获子进程的 stdout / stderr: - 成功时只打一行:`✓ <步骤名> <用时 s>`; - 失败时打 `✗ <步骤名> exit=<码>`,再打该步输出的最后 200 行,并把完整输出写到 `gate-logs/<序号>-<步骤名>.log`,打出路径。 - 开头的 profile JSON 改成一行摘要。 - 新增 `--verbose`:恢复现状(不捕获,直接透传)。 - 捕获输出时要保留子进程的超时与退出码语义,现有 `test_timeout_seconds` 等参数照旧生效。 - **验收**: - 本机 `--profile quick` 输出不超过 150 行; - 人为让一个子步骤失败(测试里用假命令),能看到 ✗、最后 200 行和日志路径,退出码非 0; - `--verbose` 时的输出与改前同类; - 新增 `tests/test_run_quality_gate_output.py`。 ### T2 前端测试在门禁上精简(`frontend/`) - 新增启动脚本(例如 `frontend/scripts/run-tests.mjs`),`package.json` 的 `test` 改为调用它,测试文件范围保持 `tests/*.test.ts tests/*.test.tsx`: - 没有 `CI` / `GITEA_ACTIONS` 环境变量时:与现在完全相同(TAP 到 stdout); - 门禁环境:用 Node 内置的 `dot` reporter 输出到 stdout,同时用 `tap` reporter 写到 `gate-logs/frontend-tests.tap`。跑完后从 TAP 文件里提取并打印:`# tests / pass / fail / cancelled` 汇总,以及每条 `not ok` 的测试名和其后的报错块(每条最多 60 行)。 - 退出码等于测试进程的退出码(AGENTS §10:同时看 cancelled 和退出码,BUG-995)。 - `test:db`、`test:deployment` 不动。 - **验收**: - 本机 `npm test` 输出与改前同类,按测试名比对失败名单相同; - `CI=true npm test` 输出不超过 600 行,末尾能看到 24 条本机已知失败(DB / Docker 类)的名字和报错; - 测试总数不变。 ### T3 拆分门禁步骤(`.gitea/workflows/backend-quality-gate.yml`) 把「Validate backend, package, frontend, and database contracts」拆成按顺序执行的独立 step,每个都 `set -euo pipefail`、`export PATH="$PWD/.venv/bin:$PATH"`: 1. `Lint and compile Python`:ruff 和 py_compile; 2. `Python quality gate (quick)`:`run_quality_gate.py …`(T1 之后已精简); 3. `Privacy artifact scan`; 4. `Build Python package`:`python -m build > gate-logs/python-build.log 2>&1`,失败时打最后 200 行; 5. `Frontend and database tests`:`npm test --prefix frontend`(T2 之后已精简); 6. `Frontend lint`; 7. `Frontend production build (non-push)`:保留原有 `$GITEA_EVENT_NAME` 判断和 600 秒超时。 - 每一步一行命令对应原来的一行命令,不增不减(红线 1)。 - `gate-logs/` 目录在第一步前创建。 - **验收**: - `staging-backend-workflows.test.ts` 等合同测试按新结构更新(三栏),全部通过; - 推 staging 后,Gitea 页面每个 step 都能打开,总日志量(各 step 合计)不超过 3,000 行; - 拆分前后执行的检查集合逐项相同(在进度记录里列表对照)。 ### T4 记录 - `docs/BUG_HISTORY.md` 新增一条(门禁日志不可读,排查失败被阻塞),关联 run 3170 / task 6691。 - `CHANGELOG.md`;`docs/tasks/PROGRESS-gate-log-volume-20261004.md`(改前 / 改后行数表、检查集合对照表);`docs/tasks/README.md` 改为「已实现待验收」。 ## 让步顺序 T1 → T2 → T3 → T4。T3 碰到 workflow 合同测试改不动时,先交 T1 + T2(不改 workflow 也能把日志压到几千行),T3 写进进度记录待议。 ## 开工前置命令 ```bash cd /workspace/Jyotisha && git status -sb | head -1 git fetch origin --prune git worktree add -b codex/gate-log-volume-20261004 .worktrees/gate-log-volume-20261004 origin/staging df -h / grep -oE "BUG-1[0-9]{3}" docs/BUG_HISTORY.md | sort -u | tail -1 grep -rn "backend-quality-gate\|run_quality_gate" frontend/tests tests | cut -c1-120 | head -40 ``` ## 验收口径 - Python 快速门(默认精简模式与 `--verbose` 各跑一次);新增测试通过;英文对照、隐私测试通过。 - 前端:`tsc` 0 错;lint 0 error;本机 `npm test` 失败名单与开工基线逐条相同;`CI=true npm test` 行数与汇总正确。 - 门禁 workflow 的合同测试全部通过。 - 不 push,由 Claude 验收后推 staging;推送后第一次门禁运行时,由产品负责人确认网页能打开各 step。 ## BUG 编号起点 开工时核对最大号 +1(10-04 实测最大号 BUG-1229)。