修正(08-08-production-deployment-execution): 先验生产公网身份再冻结目录
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-08 19:36:32 +08:00
父节点 eb76de7c98
当前提交 bf2631c0d4
共修改 8 个文件,包含 151 行新增和 48 行删除
+12 -9
查看文件
@@ -29,30 +29,32 @@ operations require explicit approval and evidence.
### 2. Signatures
- Bootstrap entry: `node deploy/trialrun/production-identity-bootstrap.mjs`.
- Identity-only stop: `PRODUCTION_IDENTITY_ONLY=1 node deploy/trialrun/production-identity-bootstrap.mjs`.
- Read-only stop: `PRODUCTION_READ_ONLY_PREFLIGHT=1 node deploy/trialrun/production-identity-bootstrap.mjs`.
- Exact local inputs passed to the existing wrapper: `EXPECTED_DEPLOY_SERVER`, `EXPECTED_REMOTE_DIR`, `EXPECTED_COMPOSE_PROJECT_NAME`, `EXPECTED_PRODUCTION_ORIGIN` and `EXPECTED_PRODUCTION_DB_NAME_SHA256`.
### 3. Contracts
- Recover deployment target, repository root and Compose project from reviewed Git history; when values come from different commits, require their shared identity fields to agree.
- Recover deployment target and Compose project from reviewed Git history. A shell `${REMOTE_DIR:-...}` default that operations routinely override is a fallback, not proof of the production repository root.
- Select the refresh identity only when exactly one concrete SSH candidate returns the exact forced-command probe token. Do not print candidate aliases.
- Compare target, directory and project independently. Missing or empty expected values are errors, not reasons to skip a comparison.
- Compare target and project against their independent history. Use a structurally validated systemd contract only to locate the remote env/runtime, verify both origin samples, then freeze its directory as the exact expected value for later preflight. Missing or empty expected values are errors, not reasons to skip a comparison.
- A legacy token such as `trialrun` is neutral. Continue to reject loopback/zero addresses, local regression, staging, shadow and placeholder identities.
- Read `POSTGRES_DB` only after history, systemd contract and public origin agree. Keep the database name remote; pass only a hash comparison baseline to the preflight.
- Identity-only mode must stop after env/runtime origin agreement and contract-directory freeze; it must not read `POSTGRES_DB`, run aggregate checks or enter Prisma status.
- Read `POSTGRES_DB` only after history target/project, systemd contract structure and both public-origin samples agree. Keep the database name remote; pass only a hash comparison baseline to the preflight.
- Read-only mode must exit after target/pre-status and rollback-window checks and before `deploy.sh`, rsync, migration or application switching.
### 4. Validation & Error Matrix
- Zero refresh probe matches -> `REFRESH_IDENTITY_NOT_FOUND`; stop before target gate.
- Multiple exact probe matches -> `REFRESH_IDENTITY_NOT_UNIQUE`; do not select the first candidate.
- Historical target/directory/project disagreement -> an explicit `HISTORICAL_*` or `SYSTEMD_*_MISMATCH`; stop before database identity read.
- Historical target/project or contract-relative-path disagreement -> an explicit `HISTORICAL_*` or `SYSTEMD_*_MISMATCH`; stop before database identity read.
- Any expected exact value missing/empty -> `EXPECTED_*_MISSING`; no SSH preflight fallback.
- Env origin or running-container origin differs from the approved origin -> `PROD_TARGET_*_ORIGIN_MISMATCH`; do not read migration status.
- Read-only mode reaches `deploy.sh` -> contract failure; tests and env validation must reject the ordering.
### 5. Good/Base/Bad Cases
- Good: a legacy-named deployment passes because all independent identity values match exactly, then read-only mode records status/window and exits before writes.
- Good: a legacy-named deployment passes target/project checks, both origin samples match, the contract directory becomes the exact in-process baseline, then identity-only mode exits before DB/status reads.
- Base: an explicitly supplied, unambiguous production identity passes the same exact comparisons.
- Bad: reject or accept solely because the alias/project contains or omits `trialrun`.
- Bad: database name lookup fails and the wrapper continues without comparing database identity.
@@ -60,10 +62,10 @@ operations require explicit approval and evidence.
### 6. Tests Required
- Positive fixture for the approved legacy naming pattern.
- Separate negative assertions and stable error codes for deploy target, remote directory and Compose project mismatches.
- Separate negative assertions and stable error codes for deploy target, Compose project, contract-relative env path and later preflight directory mismatches.
- Missing-value tests proving exact comparison cannot be skipped.
- Zero/multiple refresh-probe fixtures.
- Ordering assertion: identity/DB bootstrap -> target/pre-status/window -> read-only exit -> `deploy.sh` only when read-only mode is off.
- Ordering assertion: contract structure -> env/runtime origin -> contract-directory freeze -> identity-only exit; only a later approved run may read DB -> target/pre-status/window -> read-only exit -> `deploy.sh` when read-only mode is off.
- Evidence assertions proving target/path/database values are absent from stdout and committed reports.
### 7. Wrong vs Correct
@@ -77,8 +79,9 @@ name does not contain staging -> assume production -> run migrate deploy
#### Correct
```text
reviewed history + unique refresh probe + exact systemd contract + exact origin
-> remote-only DB identity hash -> target/pre-status/window
reviewed target/project + unique refresh probe + contract structure
-> env/runtime origin agreement -> freeze contract directory -> identity-only stop
-> separately approved remote-only DB identity hash -> target/pre-status/window
-> explicit read-only stop or separately approved deploy
```