diff --git a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md index 7c56f881..7b7f9b84 100644 --- a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md +++ b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md @@ -86,3 +86,11 @@ rg -n "IntrospectTokenResponse|tenant_id|project_id|operator_id|metadata|IssueTo 1. 已创建 `tests/contract/README.md` 与 `tests/contract/gateway_token_runtime_supply_chain.md`,明确当前 CI 覆盖缺口与四个最小 contract 场景。 2. 已创建 `docs/plans/2026-04-21-phase1-contract-gate-checklist.md`,把 Phase 1 关闭条件绑定到 contract gate。 3. 已在 `scripts/ci/backend-verify.sh` 和 `scripts/ci/repo_integrity_check.sh` 写明 contract gate 执行位、产物路径和失败语义。 + +## P2-A release manifest 合同设计完成 + +执行结果: + +1. 已创建 `docs/plans/2026-04-21-release-manifest-contract.md`,记录 `latest_file_or_empty` 依赖入口、`run_id` 规则、目录结构与 `manifest.json` 必填字段。 +2. 已创建 `reports/releases/.gitkeep`,为后续 `` 工件目录预留稳定路径。 +3. 已在四个脚本中补入 manifest 迁移设计说明,明确后续必须从 `decision_inputs` / `artifact_paths` 读取本次 run 的证据。 diff --git a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md index 7e2cff04..7819639f 100644 --- a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md +++ b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md @@ -303,21 +303,21 @@ - Create: `reports/releases/.gitkeep` - Create: `docs/plans/2026-04-21-release-manifest-contract.md` -- [ ] `P2-A-01` 盘点所有依赖 `latest_file_or_empty` 的脚本入口。 +- [x] `P2-A-01` 盘点所有依赖 `latest_file_or_empty` 的脚本入口。 完成标准:清单覆盖全部脚本和调用行。 -- [ ] `P2-A-02` 设计 `run_id` 生成规则。 +- [x] `P2-A-02` 设计 `run_id` 生成规则。 完成标准:规则包含日期、提交号或流水号。 -- [ ] `P2-A-03` 设计发布目录结构 `reports/releases//...`。 +- [x] `P2-A-03` 设计发布目录结构 `reports/releases//...`。 完成标准:目录草图写入 manifest 合同文档。 -- [ ] `P2-A-04` 设计 `manifest.json` 必填字段。 +- [x] `P2-A-04` 设计 `manifest.json` 必填字段。 完成标准:至少包含 `run_id`、`commit_sha`、`env`、`artifact_paths`、`decision_inputs`。 -- [ ] `P2-A-05` 把 `staging_release_pipeline.sh` 的输入改成 manifest 读取方案草稿。 +- [x] `P2-A-05` 把 `staging_release_pipeline.sh` 的输入改成 manifest 读取方案草稿。 完成标准:不再依赖“最新文件”。 -- [ ] `P2-A-06` 把 `staging_evidence_autofill.sh` 的输入改成 manifest 读取方案草稿。 +- [x] `P2-A-06` 把 `staging_evidence_autofill.sh` 的输入改成 manifest 读取方案草稿。 完成标准:只消费本次 run 的工件。 -- [ ] `P2-A-07` 把 `tok007_release_recheck.sh` 的输入改成 manifest 读取方案草稿。 +- [x] `P2-A-07` 把 `tok007_release_recheck.sh` 的输入改成 manifest 读取方案草稿。 完成标准:复检脚本不再扫历史目录。 -- [ ] `P2-A-08` 把 `final_decision_consistency_check.sh` 的输入改成 manifest 读取方案草稿。 +- [x] `P2-A-08` 把 `final_decision_consistency_check.sh` 的输入改成 manifest 读取方案草稿。 完成标准:最终一致性检查绑定单次 run。 ### Task P2-B: 把真实 staging 设为唯一发布硬门禁 diff --git a/docs/plans/2026-04-21-release-manifest-contract.md b/docs/plans/2026-04-21-release-manifest-contract.md new file mode 100644 index 00000000..51953f3a --- /dev/null +++ b/docs/plans/2026-04-21-release-manifest-contract.md @@ -0,0 +1,126 @@ +# 2026-04-21 Release Manifest Contract + +## P2-A-01 依赖 `latest_file_or_empty` 的脚本入口 + +### `scripts/ci/staging_release_pipeline.sh` + +1. `LATEST_STAGING_RUN_LOG` +2. `LATEST_STAGE_REPORT` +3. `LATEST_TOKEN_READINESS` +4. `LATEST_TOK007_REPORT` +5. `LATEST_PIPELINE_REPORT` + +### `scripts/ci/staging_evidence_autofill.sh` + +1. `STAGING_RUN_LOG` +2. `SP_REPORT` +3. `TOK021_REPORT` +4. `TOK007_REPORT` +5. `PIPELINE_REPORT` + +### `scripts/ci/tok007_release_recheck.sh` + +1. `TOK006_REPORT` +2. `SP_REPORT` +3. `TOK_RUNTIME_READINESS_REPORT` + +### `scripts/ci/final_decision_consistency_check.sh` + +1. `TOK007_FILE` +2. `SP_FILE` + +结论: + +1. 当前四个脚本都通过“扫历史目录里最新文件”的方式串联证据。 +2. 这种方式无法证明输入属于同一次发布运行,必须被 `run_id + manifest` 方案替换。 + +## P2-A-02 `run_id` 生成规则 + +格式: + +`YYYYMMDD_HHMMSS__[-rNN]` + +规则: + +1. `YYYYMMDD_HHMMSS` 使用执行开始时间。 +2. `` 使用 8 位提交号。 +3. `` 使用本次发布环境标识,如 `staging`、`prod`、`localmock`。 +4. 若同秒内重复生成,追加 `-rNN` 顺序号,避免目录冲突。 + +## P2-A-03 发布目录结构 + +```text +reports/releases// + manifest.json + logs/ + gate_verification/ + review_outputs/ + evidence/ +``` + +约束: + +1. 同一次运行的产物只能写入自己的 `` 目录。 +2. 历史目录只读,不允许被当前运行覆盖。 + +## P2-A-04 `manifest.json` 必填字段 + +最小字段: + +- `run_id` +- `commit_sha` +- `env` +- `created_at` +- `source_env_file` +- `artifact_paths` +- `decision_inputs` + +`artifact_paths` 最少应覆盖: + +- `staging_release_pipeline_report` +- `superpowers_release_pipeline_report` +- `staging_evidence_autofill_report` +- `tok007_recheck_report` +- `final_decision_consistency_report` + +`decision_inputs` 最少应覆盖: + +- `staging_run_log` +- `superpowers_stage_validation_report` +- `token_runtime_readiness_report` +- `tok006_gate_bundle_report` +- `supply_gate_review_report` +- `final_decision_report` + +## P2-A-05 `staging_release_pipeline.sh` 输入改造草稿 + +方案: + +1. 该脚本成为 manifest 的创建者。 +2. 启动时生成 `run_id` 和 `reports/releases//manifest.json`。 +3. 下游脚本统一接收 `--manifest `。 +4. 不再从历史目录回扫 `LATEST_*` 文件,而是把本次步骤产物写回 manifest。 + +## P2-A-06 `staging_evidence_autofill.sh` 输入改造草稿 + +方案: + +1. 增加 `--manifest` 作为首选输入。 +2. 只读取 manifest 中的 `decision_inputs` 和 `artifact_paths`。 +3. 当 manifest 缺少必填路径时直接失败,不再回退到“最新文件”。 + +## P2-A-07 `tok007_release_recheck.sh` 输入改造草稿 + +方案: + +1. 增加 `--manifest` 输入。 +2. 复审所需的 TOK006 / stage validation / token readiness / SUP review / final decision 路径全部从 manifest 读取。 +3. 复检脚本只审当前 `run_id` 的证据,不扫描历史目录。 + +## P2-A-08 `final_decision_consistency_check.sh` 输入改造草稿 + +方案: + +1. 增加 `--manifest` 输入。 +2. final decision、tok007 recheck、stage validation 三个来源全部绑定到同一个 `run_id`。 +3. 若 manifest 中任一路径缺失或跨 run_id,直接判定 `FAIL`。 diff --git a/reports/releases/.gitkeep b/reports/releases/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/reports/releases/.gitkeep @@ -0,0 +1 @@ + diff --git a/scripts/ci/final_decision_consistency_check.sh b/scripts/ci/final_decision_consistency_check.sh index 3332f032..9351eb41 100755 --- a/scripts/ci/final_decision_consistency_check.sh +++ b/scripts/ci/final_decision_consistency_check.sh @@ -4,11 +4,17 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" TS="$(date +%F_%H%M%S)" OUT_DIR="${ROOT_DIR}/reports/archive/gate_verification" +RELEASES_DIR="${ROOT_DIR}/reports/releases" mkdir -p "${OUT_DIR}" REPORT_FILE="${OUT_DIR}/final_decision_consistency_${TS}.md" LOG_FILE="${OUT_DIR}/final_decision_consistency_${TS}.log" +# Manifest migration design: +# - preferred entry: --manifest /manifest.json> +# - this script should bind final_decision, tok007_recheck and stage validation inputs +# to one run_id and stop reading historical "latest" outputs + latest_file_or_empty() { local pattern="$1" local latest @@ -74,6 +80,10 @@ parse_machine_decision() { } FINAL_DECISION_FILE="${ROOT_DIR}/review/final_decision_2026-03-31.md" +# Planned manifest keys: +# - decision_inputs.final_decision_report +# - decision_inputs.tok007_recheck_report +# - decision_inputs.superpowers_stage_validation_report TOK007_FILE="$(latest_file_or_empty "${ROOT_DIR}/review/outputs/tok007_release_recheck_*.md")" SP_FILE="$(latest_file_or_empty "${OUT_DIR}/superpowers_stage_validation_*.md")" diff --git a/scripts/ci/staging_evidence_autofill.sh b/scripts/ci/staging_evidence_autofill.sh index 75002318..8a37e456 100755 --- a/scripts/ci/staging_evidence_autofill.sh +++ b/scripts/ci/staging_evidence_autofill.sh @@ -3,12 +3,18 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" OUT_DIR="${ROOT_DIR}/reports/archive/gate_verification" +RELEASES_DIR="${ROOT_DIR}/reports/releases" TS="$(date +%F_%H%M%S)" OUT_FILE="${OUT_DIR}/staging_token_go_evidence_autofill_${TS}.md" LOG_FILE="${OUT_DIR}/staging_token_go_evidence_autofill_${TS}.log" mkdir -p "${OUT_DIR}" +# Manifest migration design: +# - preferred entry: --manifest /manifest.json> +# - this script should read only decision_inputs/artifact_paths from the supplied manifest +# - no future release evidence should be discovered by latest_file_or_empty() + usage() { cat <<'EOF' Usage: @@ -211,6 +217,12 @@ while [[ $# -gt 0 ]]; do esac done +# Planned manifest keys: +# - decision_inputs.staging_run_log +# - decision_inputs.stage_report +# - decision_inputs.token_runtime_readiness_report +# - decision_inputs.tok007_recheck_report +# - artifact_paths.superpowers_release_pipeline_report if [[ -z "${STAGING_RUN_LOG}" ]]; then STAGING_RUN_LOG="$(latest_file_or_empty "${OUT_DIR}/staging_run_*.log")" fi diff --git a/scripts/ci/staging_release_pipeline.sh b/scripts/ci/staging_release_pipeline.sh index 62350e4d..4f4d4670 100755 --- a/scripts/ci/staging_release_pipeline.sh +++ b/scripts/ci/staging_release_pipeline.sh @@ -10,12 +10,20 @@ else fi TS="$(date +%F_%H%M%S)" OUT_DIR="${ROOT_DIR}/reports/archive/gate_verification" +RELEASES_DIR="${ROOT_DIR}/reports/releases" mkdir -p "${OUT_DIR}" REPORT_FILE="${OUT_DIR}/staging_release_pipeline_${TS}.md" LOG_FILE="${OUT_DIR}/staging_release_pipeline_${TS}.log" ALLOW_LOCAL_MOCK_STAGING="${ALLOW_LOCAL_MOCK_STAGING:-0}" +# Manifest migration design: +# - run_id format: YYYYMMDD_HHMMSS__[-rNN] +# - release root: ${RELEASES_DIR}// +# - manifest path: ${RELEASES_DIR}//manifest.json +# - this script becomes the manifest seed writer and must pass the resolved manifest path +# to downstream scripts instead of relying on latest_file_or_empty(). + log() { echo "$1" | tee -a "${LOG_FILE}" } @@ -124,6 +132,12 @@ run_step \ "Superpowers release pipeline with staging env" \ "cd \"${ROOT_DIR}\" && STAGING_ENV_FILE=\"${ENV_FILE_REL}\" bash \"scripts/ci/superpowers_release_pipeline.sh\"" +# Planned manifest inputs for staging_evidence_autofill.sh: +# - decision_inputs.staging_run_log +# - decision_inputs.stage_report +# - decision_inputs.token_runtime_readiness_report +# - decision_inputs.tok007_recheck_report +# - artifact_paths.superpowers_release_pipeline_report LATEST_STAGING_RUN_LOG="$(latest_file_or_empty "${OUT_DIR}/staging_run_*.log")" LATEST_STAGE_REPORT="$(latest_file_or_empty "${OUT_DIR}/superpowers_stage_validation_*.md")" LATEST_TOKEN_READINESS="$(latest_file_or_empty "${OUT_DIR}/token_runtime_readiness_*.md")" diff --git a/scripts/ci/tok007_release_recheck.sh b/scripts/ci/tok007_release_recheck.sh index 0bfced6e..debd7d12 100755 --- a/scripts/ci/tok007_release_recheck.sh +++ b/scripts/ci/tok007_release_recheck.sh @@ -6,12 +6,18 @@ TS="$(date +%F_%H%M%S)" OUT_DIR="${ROOT_DIR}/review/outputs" mkdir -p "${OUT_DIR}" GATE_OUT_DIR="${ROOT_DIR}/reports/archive/gate_verification" +RELEASES_DIR="${ROOT_DIR}/reports/releases" mkdir -p "${GATE_OUT_DIR}" MARK_SCRIPT="${ROOT_DIR}/scripts/ci/mark_historical_snapshots.sh" CURRENT_POINTER_FILE="review/outputs/current_machine_review_sources.md" OUT_FILE="${OUT_DIR}/tok007_release_recheck_${TS}.md" LOG_FILE="${GATE_OUT_DIR}/tok007_release_recheck_${TS}.log" +# Manifest migration design: +# - preferred entry: --manifest /manifest.json> +# - this script should read run-scoped TOK006 / stage validation / token readiness inputs +# from manifest.decision_inputs instead of scanning latest files across history + log() { echo "$1" | tee -a "${LOG_FILE}" } @@ -100,6 +106,12 @@ extract_pass_fail_result() { echo "UNKNOWN" } +# Planned manifest keys: +# - decision_inputs.tok006_gate_bundle_report +# - decision_inputs.superpowers_stage_validation_report +# - decision_inputs.token_runtime_readiness_report +# - decision_inputs.supply_gate_review_report +# - decision_inputs.final_decision_report TOK006_REPORT="$(latest_file_or_empty "${GATE_OUT_DIR}/tok006_gate_bundle_*.md")" SP_REPORT="$(latest_file_or_empty "${GATE_OUT_DIR}/superpowers_stage_validation_*.md")" TOK_RUNTIME_READINESS_REPORT="$(latest_file_or_empty "${GATE_OUT_DIR}/token_runtime_readiness_*.md")"