Files
aios/docs/superpowers/plans/2026-07-15-v2r1-personal-weixin-r10-release-plan-amendment.md
T

13 KiB

V2R1 R10 personal Weixin release-plan amendment

Status: reviewed design-only amendment. It adds D1, B6, and B7 to the R9 linear release plan. It does not authorize runtime, candidate, production, baseline, tag, or Fleet mutation.

Authority remains docs/superpowers/plans/2026-07-14-v2r1-runtime-release-plan.md at B2 tip 68d27879103990aac692207a8512b3725c6d0d41. R9 START is a9a0b4ae396e33c9bfc35a5f5694e5e354c3a47c; accepted B1 is 57103c11d21192a50febe846d30cee6fc5da4ac1; accepted B2 is 68d27879103990aac692207a8512b3725c6d0d41. Their trees and path sets are immutable. This amendment does not relabel, squash, reorder, or absorb any of the original five blockers.

Exact linear ledger

The only valid ancestry is:

START -> B1 -> B2 -> D1 -> B3 -> B4 -> B5 -> B6 -> B7

D1 is a non-blocker design dependency whose parent is exactly B2. B3 parent is D1; every later blocker parent is the immediately preceding row. START..B7 contains exactly eight non-merge commits: B1, B2, D1, B3, B4, B5, B6, B7. B1-B5 remain five distinct original blocker commits; B6 and B7 are distinct new blocker commits. No cherry-pick collapse, squash, fixup, merge commit, history rewrite, or force push is allowed.

B3/B4 retain every R9 evidence gate. In particular, unresolved EVIDENCE_REQUIRED for the exact provider client_id -> message_id binding stops B4 with no implementation, commit, or ledger append. B5 can land only after accepted B4. Even after B5 lands, every forward stage, verify-stage, or promote operation remains blocked until accepted B6 and B7 commits exist and all gates below pass.

D1 design-only commit

D1 changes exactly these 23 paths and no others:

docs/superpowers/plans/2026-07-15-v2r1-personal-weixin-r10-release-plan-amendment.md
contracts/personal-weixin/v1/SECURITY.md
contracts/personal-weixin/v1/golden-contract.schema.json
contracts/personal-weixin/v1/fixtures/01-tenant-account-entitlement.json
contracts/personal-weixin/v1/fixtures/02-readiness-ready.json
contracts/personal-weixin/v1/fixtures/03-readiness-field-missing.json
contracts/personal-weixin/v1/fixtures/04-readiness-stale.json
contracts/personal-weixin/v1/fixtures/05-readiness-plugin-digest-mismatch.json
contracts/personal-weixin/v1/fixtures/06-readiness-account-not-entitled.json
contracts/personal-weixin/v1/fixtures/07-readiness-account-not-running.json
contracts/personal-weixin/v1/fixtures/08-readiness-error.json
contracts/personal-weixin/v1/fixtures/09-web-login-start-request.json
contracts/personal-weixin/v1/fixtures/10-web-login-wait-request.json
contracts/personal-weixin/v1/fixtures/11-qr-pending.json
contracts/personal-weixin/v1/fixtures/12-need-verify-code.json
contracts/personal-weixin/v1/fixtures/13-invalid-verify-code.json
contracts/personal-weixin/v1/fixtures/14-expired.json
contracts/personal-weixin/v1/fixtures/15-failed.json
contracts/personal-weixin/v1/fixtures/16-already-connected.json
contracts/personal-weixin/v1/fixtures/17-confirmed.json
contracts/personal-weixin/v1/fixtures/18-unknown-upstream-fail-closed.json
contracts/personal-weixin/v1/fixtures/19-authenticated-inbound-event.json
contracts/personal-weixin/v1/fixtures/20-unbind-unsupported.json

D1 validation requires JSON parse, Draft 2020-12 schema validation of every fixture, negative mutation tests, exact 23-path comparison, git diff --check, placeholder/secret scan, and confirmation that no runtime/deploy/release-active path changed.

Preserved R9 blocker allowlists

B1 and B2 are already accepted at the immutable SHAs above. Their actual commit path sets, parent SHAs, and trees are the allowlists; compare with git diff-tree --no-commit-id --name-only -r <sha> and reject any mismatch. B1 has six actual paths under openclaw/plugins/aios-tenant-boundary/; B2 has exactly 43 actual package/import/matrix paths. No later blocker may amend them.

B3 changes exactly these 15 R9 paths:

active-runtime/app/aios_v2r1/identity.py
active-runtime/app/aios_v2r1/tenant_context.py
active-runtime/app/aios_v2r1/ingress_service.py
active-runtime/app/aios_v2r1/bridge_auth.py
active-runtime/app/aios_v2r1/weixin_projection.py
fixes/patch_openclaw_weixin_v2r1_ingress.py
tests/v2r1/test_bridge_auth.py
tests/v2r1/test_weixin_projection.py
tests/integration/test_v2r1_bridge_transport.py
tests/test_weixin_v2r1_ingress_bridge.py
tests/fixtures/weixin_v2r1/text.json
tests/fixtures/weixin_v2r1/quoted-text.json
tests/fixtures/weixin_v2r1/image-with-text.json
tests/fixtures/weixin_v2r1/unresolved-reference.json
release/v2r1c0-rc1/weixin-bridge-evidence.json

B4 changes exactly these 19 R9 paths and remains evidence-stopped until both R9 evidence files are accepted:

active-runtime/app/aios_v2r1/outbox.py
active-runtime/app/aios_v2r1/hermes_http.py
active-runtime/app/aios_v2r1/hermes_worker.py
active-runtime/app/aios_v2r1/outbound_receipt.py
active-runtime/app/aios_v2r1/ingress_service.py
active-runtime/app/aios_v2r1/message_ledger.py
tests/v2r1/test_outbox.py
tests/v2r1/test_hermes_http.py
tests/v2r1/test_outbound_receipt.py
tests/integration/test_v2r1_delivery_flow.py
fixes/patch_openclaw_weixin_outbound_quotes.py
fixes/patch_openclaw_weixin_outbound_receipts.py
openclaw/plugins/aios-tenant-boundary/dist/receipt-client.js
openclaw/plugins/aios-tenant-boundary/dist/outbound-receipt-journal.js
openclaw/plugins/aios-tenant-boundary/test/receipt-client.test.js
tests/test_weixin_outbound_quote_timestamp.py
tests/test_weixin_outbound_receipt_boundary.py
release/v2r1c0-rc1/hermes-http-contract-evidence.json
release/v2r1c0-rc1/weixin-outbound-binding-evidence.json

B5 changes exactly these 14 R9 paths:

deploy/v2r1/release_ctl.py
deploy/v2r1/capture_live_preflight.py
deploy/v2r1/install.sh
deploy/v2r1/validate.sh
deploy/v2r1/healthcheck.sh
deploy/v2r1/rollback.sh
deploy/v2r1/aios-v2r1-candidate-gateway.service
deploy/v2r1/aios-v2r1-production-gateway.service
deploy/v2r1/aios-v2r1-ingress.service
deploy/v2r1/aios-v2r1-hermes-worker.service
deploy/v2r1/openclaw-candidate.json5
release/v2r1c0-rc1/FLEET_UNSUPPORTED
tests/deploy/test_v2r1_release_contract.py
release/v2r1c0-rc1/RELEASE.md

B6: evidence-bound OpenClaw personal-Weixin candidate

B6 changes exactly these 13 paths:

openclaw/candidates/personal-weixin-r10/README.md
openclaw/candidates/personal-weixin-r10/manifest.json
openclaw/candidates/personal-weixin-r10/src/golden-contract.js
openclaw/candidates/personal-weixin-r10/src/gateway-inbound-auth.js
openclaw/candidates/personal-weixin-r10/plugin/openclaw.plugin.json
openclaw/candidates/personal-weixin-r10/plugin/index.js
fixes/patch_openclaw_personal_weixin_login.py
fixes/patch_openclaw_personal_weixin_gateway_auth.py
openclaw/candidates/personal-weixin-r10/test/golden-contract.test.js
openclaw/candidates/personal-weixin-r10/test/gateway-auth.test.js
tests/personal_weixin/test_golden_fixtures.py
tests/integration/test_personal_weixin_gateway_contract.py
release/v2r1c0-rc1/personal-weixin-b6-evidence.json

Before B6 code, evidence records exact installed upstream source commit, every patch target path/digest, plugin loader version/digest, Gateway auth owner, credential files, and candidate-only state/config. Stop on inference or drift. Tests are RED before implementation and prove: required nonempty opaque sessionKey; no account fallback; exact canonical statuses and readiness shapes; unknown status fail-closed; 4-8 digit one-shot verifyCode with no persistence/log/response; exact event body with no authentication/tenant/user/session assertions; transport signature/skew/nonce/replay checks; exact 501 zero mutation; byte-idempotent patches; build/runtime inspect in isolated candidate state. The B6 evidence JSON is produced only after these gates pass.

B7: tenant-scoped Portal adapter and durable challenge

B7 changes exactly these 13 paths:

active-runtime/app/aios_v2r1/personal_weixin/official_login.py
active-runtime/app/aios_v2r1/personal_weixin/adapter.py
portal/personal_weixin/views.py
portal/personal_weixin/urls.py
portal/personal_weixin/services.py
portal/personal_weixin/templates/personal_weixin/bind.html
tests/personal_weixin/test_adapter.py
tests/personal_weixin/test_portal_views.py
tests/integration/test_personal_weixin_golden_flow.py
release/v2r1c0-rc1/personal-weixin-source-baseline.json
release/v2r1c0-rc1/personal-weixin-source-baseline.sha256
release/v2r1c0-rc1/personal-weixin-canary-contract.json
release/v2r1c0-rc1/personal-weixin-rollback-contract.json

Tests are RED before B7 implementation and prove: Django authentication/CSRF; server-session tenant resolution; browser accountId treated only as a preallocated-slot reference and current entitlement rechecked on every list/readiness/start/wait/status; exact sessionKey forwarding without fallback; live canonical readiness; authenticated Gateway transport context separate from the six-field event body; durable challenge record owning tenant/user/account/session digest; hash-only secret storage; transactional/CAS single consumption; exact replay idempotence; altered replay/concurrency/expiry/cross-tenant denial with zero mutation; no provider token/raw iLink/verify code/challenge code persistence or leakage; size/rate/no-store controls; restart; and unbind 501 before all mutations/calls/jobs.

Canonical contract gates

sessionKey is nonempty and exact. Canonical results are:

qr_pending         connected=false alreadyConnected=false terminal=false needsVerifyCode=false
need_verifycode    connected=false alreadyConnected=false terminal=false needsVerifyCode=true
invalid_verifycode connected=false alreadyConnected=false terminal=false needsVerifyCode=true
expired            connected=false alreadyConnected=false terminal=true  needsVerifyCode=false
failed             connected=false alreadyConnected=false terminal=true  needsVerifyCode=false
already_connected  connected=false alreadyConnected=true  terminal=true  needsVerifyCode=false
confirmed           connected=true  alreadyConnected=false terminal=true  needsVerifyCode=false

binded_redirect maps to already_connected. An unknown upstream status maps to canonical failed/errorCode=unknown_upstream_status. Readiness always uses the same complete shape: missing fields, stale evidence, plugin digest mismatch, absent entitlement, not-running account, and Gateway error all produce ready=false; only a live authenticated all-green snapshot produces ready=true/code=ready.

The inbound event body is exactly eventId/accountId/senderId/chatId/challengeCode/observedAt; transport authentication and server-resolved tenant/user/session context are not body fields. Fixture secrets are test inputs only. Raw verifyCode and challengeCode never enter persistence, logs, responses, URLs, analytics, traces, metrics labels, or job arguments. Raw sessionKey appears only where the golden login request/result contract requires it; it is never persisted or logged and is forwarded byte-exact without fallback.

Merge, canary, promotion, and rollback gates

For each D1/B3-B7 row: require the exact parent row SHA, one non-merge commit, exact sorted path allowlist, clean diff --check, focused and carried tests, reviewer acceptance, then append label/SHA/parent/tree to the worktree-local ledger. No row may start from an unaccepted predecessor.

After B7, rebuild once from the accepted B7 tree. Stage one isolated candidate using B5 controls and no active configuration. Canary exactly one explicitly entitled (tenant-canary, account-canary) pair. Before traffic, require live Gateway/plugin/account readiness with expected digests and freshness. Exercise all golden outcomes, invalid and unknown fail-closed paths, restart, concurrent waits, atomic challenge consumption, exact replay, altered replay, expiry, unauthenticated event, tenant/account mismatch, cross-tenant IDOR, secret redaction, and exact end-to-end unbind 501 with zero durable/runtime/upstream/job mutation. Any missing evidence, extra account, digest drift, stale snapshot, noncanonical status, secret observation, or mutation fails the canary and blocks promotion.

Promotion accepts only the exact artifact/config/plugin/source digests verified by canary; it never rebuilds, repatches, switches branches, edits VERSION/baseline/current.json, creates a tag, invokes Fleet, or changes unrelated services. Candidate and production artifacts must be byte-identical. B5 live preflight is recaptured and reconciled immediately before verify-stage and promote.

Rollback is armed before canary with the immediately preceding production artifact/config/plugin digests, units, credentials metadata, and health contract. On any gate failure, STOP blocks forward actions; invoke only B5's recorded rollback-only path. Restore byte-identical prior artifacts/config, never the candidate rebuild; do not delete STOP automatically; do not roll back the database absent a separately approved incompatibility plan. Verify previous digests, listeners, units, secret paths/modes, canonical readiness, and tenant/account isolation. If verification fails, record ROLLBACK_FAILED, keep STOP, and escalate without forward mutation.