# Revision plan — safe current-upstream dual-coach port and offline candidate

## Summary
For the nontechnical owner: PR #74072 must not be deployed as-is. Its head `0dc0f228d` is a dirty, non-mergeable, human-labelled-`invalid` PR whose three commits sit on `b88d0007c`, **6,916 commits behind** the verified current upstream base `41a07f5b8`. Its 36,770-line patch was written for the removed legacy Telegram gateway while current Hermes has a plugin-based architecture. Deploying before adapting its *behavior* to current upstream can bypass present routing/authorization/recovery conventions and accidentally include unrelated dirty-worktree material. A fresh, reviewed, offline-only replacement is therefore necessary; it is not a claim of live readiness.

Evidence retained from the reviewed artifacts: `4305027f6 → d9e03e938 → 0dc0f228d` changes 18 files; old `gateway/platforms/telegram.py` has a 61%-similarity migration to current `plugins/platforms/telegram/adapter.py`; `cron/scheduler.py`, group-gating tests, `scripts/run_tests.sh`, `pyproject.toml`, and Profile `57aaf75` surfaces were inspected. #74072 has no review and an unexplained human-applied `invalid` label. The source author is `BEENZiN0`; the prior Planner/Architect/critic artifacts and their supplied SHA-256s are immutable decision evidence.

## Intent Diff
| Concern | Unsafe legacy premise | Revision decision |
|---|---|---|
| Placement | Profile-specific code embedded in legacy/bundled gateway | Default to a separately distributed, private dual-coach plugin; upstream changes are only a generic, reusable hook when existing surfaces demonstrably cannot express a required invariant. |
| History | Rebase/cherry-pick/repair #74072 | New branch from immutable `41a07f5b8`; #74072 stays untouched until human acceptance of a replacement. |
| Product behavior | Large source snapshot | Semantic reimplementation of exact-triple isolation, append-only revisions, child reapproval, body digest pins, reservation-first/at-most-once, and no-send-on-unknown. |
| Candidate | Implied delivery readiness | Local/offline preflight plus install smoke only, with every activation/ingress/scheduler/delivery switch false. |
| Private dependency | Dynamic/profile-source coupling | Typed Profile `57aaf75` protocol with fail-closed errors; no private source, identities, paths, secrets, or canonical executable runbook enters public Hermes. |

## Decision Drivers
1. Hermes `AGENTS.md` requires capability at edges: plugins/skills before core; plugins must not special-case core, and a missing capability requires a **generic** plugin surface. It also rejects new non-secret `HERMES_*` variables and requires real-import temp-`HERMES_HOME` E2E for config/file/network boundaries.
2. Canonical safety contracts require Topic-59 reservation before every Telegram update type; configured review triple and canonical-owner triple remain distinct; all mutation revalidates owner/config/registry/consent/activation/source/artifact/epoch; unknown/receipt-present/audit-pending are no-resend.
3. Existing scheduler owns generic due/claim/execution (`tick` advances `next_run_at` before dispatch; `run_one_job` only fires a supplied job). It must not become the domain delivery ledger.
4. The invalid label is a premise warning, not a procedural obstacle. No new PR may reproduce placement or scope before its human labeler/maintainer explains or confirms the premise.

## Options
### A — Standalone/private dual-coach plugin using existing public surfaces (default)
A separately versioned distribution installed under `~/.hermes/plugins/dual-coach/` (or pip entry point) owns all Profile-aware domain code, operator console, customer/trainer flows, durable domain ledger, typed Profile adapter, and offline CLI. It uses existing generic plugin registration and Telegram/platform interfaces only.

- **AGENTS alignment:** strongest: niche/user-specific code stays outside core/in-tree plugins; no core special case.
- **Go/no-go:** proceed only if a spike demonstrates existing surfaces reserve all required ingress before generic dispatch and can carry typed callback/session and scheduler invocation without altering Hermes. Otherwise stop; do not smuggle a monkeypatch.

### B — Minimal generic upstream Telegram ingress/callback/scheduler hooks plus standalone consumer (recommended only if A fails, subject to maintainer approval)
The sole upstream change is a generic hook contract, with no Profile, dual-coach, nutrition, Topic-59, Korean, customer, or delivery policy names:

* `gateway/builtin_hooks/telegram.py`: `TelegramIngressHook.reserve_update(update, *, kind: TelegramUpdateKind) -> TelegramIngressDisposition` and `TelegramCallbackHook.handle_callback(query) -> TelegramCallbackDisposition`;
* adapter registration runs ingress hooks in strict order: `reserve_update` **before** contact, edited-message, channel-post, edited-channel-post, text/command, location/venue, media (photo/video/audio/voice/document/sticker), and before generic/model dispatch; callback hooks run before built-in generic callback namespaces; `RESERVED` consumes without downstream dispatch, `PASS` continues, `REJECTED` consumes and emits only a bounded safe notice;
* `cron/hooks.py`: `ScheduledJobHook.launch(job, *, adapters, loop) -> ScheduledLaunchResult`; it is invoked by `run_one_job` only after generic scheduler due/claim ownership, and cannot call `mark_job_run`, change `next_run_at`, or claim a generic job.

The standalone plugin consumer owns dual-coach dispatch and invokes its **separate** `DualCoachReservationLedger.reserve()` before any provider effect. Generic cron claim/execution and domain reservation are deliberately non-interchangeable: generic claim prevents duplicate scheduler execution; the domain ledger proves immutable delivery intent/consumption/receipt. A generic claim never authorizes or substitutes for a domain reservation, and a domain reservation never mutates generic cron state.

- **AGENTS alignment:** acceptable only because it has a concrete standalone consumer and is capability-generic; no plugin-specific core condition.
- **Public allowlist if approved:** exactly `gateway/builtin_hooks/telegram.py`, `cron/hooks.py`, hook registration points in `plugins/platforms/telegram/adapter.py` and `cron/scheduler.py`, their generic tests, config schema/docs for generic hook discovery, and package metadata only if inclusion is required. Any other public path is rejected.

### C — Bundled Profile-aware behavior
Place Profile/dual-coach behavior in the bundled Telegram adapter/scheduler.

- **AGENTS alignment:** conflicts with the edge/plugin rule and repeats the invalid PR's likely premise.
- **Decision:** rejected unless the **Hermes upstream maintainer of record who applied/owns the `invalid` disposition for #74072**, plus the named architecture reviewer, explicitly approve a written exception that names public files/APIs from the above allowlist, proves why A/B cannot work, and accepts long-term maintenance. Absence, ambiguity, or silence is a no-go.

### Required human gate (closed before implementation)
Record a single decision comment/evidence record addressed to **the #74072 invalid-label maintainer of record** and **the Hermes architecture maintainer**: (1) why `invalid` was applied, (2) A/B/C selection, (3) whether a generic hook is acceptable, (4) approved public allowlist, and (5) whether a replacement PR is wanted. `GO` requires both named humans and immutable link/timestamp; `NO` or no response stops implementation. This removes executor discretion and prevents a replacement PR that merely repeats an invalid premise.

## In scope / out of scope
**In scope:** an approved A or B semantic port, source-hunk ledger, typed private Profile boundary, current-base replacement branch/PR procedure, hermetic verification, offline plugin artifact/preflight, and provenance.

**Out of scope:** changing #74072; force push/delete/merge of any remote branch/PR; private Profile source/data/paths/identities; credentials, bots/topics/accounts/consent/customer registration/activation; raw registry/EventStore edits; live Telegram/provider calls; Gate-D; rollout; broad generic scheduler refactors; C without exception.

## Reviewed source-hunk behavior ownership ledger — prerequisite
This table is the approval baseline, not a request for executor design. Before creating a port branch, the reviewer signs a machine-generated hunk appendix from `git diff --find-renames b88d0007c..0dc0f228d` in which **every hunk** has this row's disposition, source line range, invariant, exact target symbol, owner, public/private location, config key/default, Profile contract/error, ingress/order (when applicable), and named test. A hunk not represented is `OMIT` and may not be copied. The appendix is private because it may reference Profile signatures, but contains no source/data.

| Source file(s), all added/modified hunks | Disposition and exact semantic target | Owner/location/config/Profile/test |
|---|---|---|
| `gateway/platforms/telegram.py`; `tests/gateway/test_telegram_group_gating.py` | **REPLACE**, not port. Plugin `dual_coach.ingress.DualCoachIngressHook.reserve_update` and `DualCoachCallbackHook.handle_callback`; if B, generic `TelegramIngressHook`/`TelegramCallbackHook` above. | Plugin owns domain routing; public only generic hooks. `plugins.dual_coach.ingress_enabled: false`. Profile errors `ProfileUnavailable`, `ProfileContractMismatch`, `AmbiguousRuntime` => `REJECTED`, no generic fallback. Tests: `test_ingress_all_update_kinds_reserved_first`, `test_callback_reserved_before_generic`, `test_wrong_triple_no_downstream`. |
| `gateway/platforms/nutrition_coaching.py` (including operator console, proposal/review, approval, delivery, risk-policy, runtime paths); `tests/gateway/test_adaptive_nutrition.py`; `tests/gateway/test_nutrition_coaching.py` | **REPLACE** in plugin: `DualCoachOperatorService`, `ProposalService`, `ApprovalService`, `DualCoachReservationLedger`, `TypedProfileAdapter`; no raw text/destination/REPL console. | Plugin/private distribution owns UI and domain schema. `operator_console_enabled`, `delivery_enabled`, `activation_enabled` all false. Typed calls only: `CustomerActionContinuity`, `CustomerActionOutcome`, `AdaptiveEventStore` append/read continuity, `build_registered_daily_customer_projection`, `CustomerWeeklyReviewSource.render_customer_body`, `build_customer_weekly_review_source`; missing/wrong/duplicate/invalid returns typed unavailable/rejected. Tests: operator lifecycle, child reapproval, body pin, policy rotation, preflight vs unknown. |
| `gateway/platforms/physique_checkin.py`, `_bindings.py`, `_config.py`, `_prompts.py`; `tests/gateway/test_telegram_physique_checkin.py` | **REPLACE** only the customer/trainer exact-triple input contract in `CustomerCheckinIngress`/`TrainerRecordIngress`; **OMIT** model prompting, dynamic imports, and unneeded UI. | Plugin owns sessions/capabilities; Profile owns canonical event schema. `customer_ingress_enabled: false`, `trainer_ingress_enabled: false`. Contract failures reject with no event/provider. Tests: text, contact, edited, channel, location, voice/photo/video/audio/document/sticker and callbacks; wrong chat/topic/user produce zero state/downstream. |
| `cron/scheduler.py`; `cron/physique_inline_card.py`; `tests/cron/test_physique_inline_card.py` | **REPLACE** with plugin `DualCoachScheduledLaunch` and `DualCoachReservationLedger`; B adds only generic `ScheduledJobHook.launch`. | Scheduler retains due/claim/`next_run_at`/execution owner; plugin owns KST-day/domain reservation/receipt ledger. `scheduler_enabled: false`. No Profile runtime/preflight passes => disabled/rejected. Tests: generic-claim/domain-reservation separation, duplicate, restart/torn/corrupt/fence-not-ready, disabled zero send. |
| `gateway/platforms/diagnostic_isolation.py`; `tests/gateway/test_diagnostic_isolation.py` | **OMIT** unless a signed hunk dependency proves a pure generic requirement; never use as a Profile escape hatch. | No state/config/Profile owner. If retained privately, deterministic `DiagnosticBoundary` only; `test_no_private_path_or_payload_logging`. |
| `gateway/platforms/korean_humanizer.py`; `tests/gateway/test_korean_humanizer.py` | **OMIT** from Hermes; private plugin presentation can retain deterministic wording only after product owner approval. | No lifecycle/authority impact; no config or Profile access. Test only if retained: output is bounded and cannot alter pinned body/digest. |
| source commit metadata/config/prompt/binding hunks not represented above | **OMIT** by default; replace only through the named target above. | No generated files, aliases, compatibility imports, profile paths, secrets, or user-facing `HERMES_*` behavior variables. Each semantic reimplementation commit retains author attribution/provenance below. |

**Ingress invariant:** for each listed update type (text/command, contact, edited message, channel post, edited channel post, location/venue, every media type, callback), hook reservation executes first; exact configured review triple authenticates ingress, canonical owner is independently refreshed for lifecycle/audit, and a wrong/unknown triple is consumed with zero generic/model/domain action. Callbacks require persisted session/capability and message binding; stale/foreign callback is consumed. Media/location must not cache/download before reservation.

**Ledger acceptance gate:** architect + invalid-label maintainer sign the completed appendix and exact public allowlist before worktree creation. This is deliberately a stop condition, not an executor choice.

## File-level changes
- **A:** no Hermes source change; private standalone plugin package contains the named plugin symbols/tests and its `plugin.yaml`; the candidate contains no private data.
- **B:** only the public allowlist above, generic names only, plus tests proving two synthetic consumers can reserve/decline independently. Profile-aware modules never land in Hermes.
- **C:** prohibited without the stated exception.

## Sequencing and dependencies
1. Freeze base/source/Profile/PR facts and capture the human placement/invalid-label gate. Do not implement without `GO`.
2. Produce and approve the closed hunk appendix/ledger, including test mapping and source provenance map.
3. Create isolated port/scratch worktrees under the collision procedure below; preserve original fingerprints.
4. Implement private plugin first against existing surfaces (A) or generic hooks then consumer (B); write Lore trailers with original source SHA/hunk IDs and `Co-authored-by: BEENZiN0 <224058983+BEENZiN0@users.noreply.github.com>` where substantive semantic code is reimplemented. Preserve the source author in commits whenever clean replay is possible; never falsely claim authorship.
5. Add focused tests, run required verification, build/install the offline candidate, capture redacted evidence and write-set checks.
6. Publish only after green proof and named human authorization; use rollback matrix. Stop before all live boundaries.

## Collision-safe worktree procedure
All administrative commands execute from a neutral directory using `git -C /home/cube/projects/richard/hermes-agent`; never from the dirty original worktree and never with `fetch --prune` (the pinned object is already verified). Let `ORIG=/home/cube/projects/richard/hermes-agent`, `PORT=/home/cube/projects/richard/hermes-agent-dualcoach-port-41a07f5b8`, `SCRATCH=/home/cube/projects/richard/hermes-agent-dualcoach-scratch-41a07f5b8`, and `BRANCH=port/dual-coach-41a07f5b8`.

1. Record `git -C "$ORIG" branch --show-current`, `status --porcelain=v1`, `diff --no-ext-diff --binary`, `diff --cached --no-ext-diff --binary`, and SHA-256 of those three outputs as the original fingerprint. Record `git -C "$ORIG" worktree list --porcelain` and `rev-parse 41a07f5b8^{commit}`.
2. Preflight that `PORT` and `SCRATCH` do not exist, `refs/heads/$BRANCH` does not exist, and no worktree record names either path. Any collision, shared-ref surprise, or unclean newly created worktree is a **stop**: do not delete, reset, clean, reuse, or checkout an unexpected path/ref.
3. Create only after preflight: `git -C "$ORIG" worktree add --detach "$PORT" 41a07f5b8`; then `git -C "$PORT" status --porcelain=v1` must be empty and `git -C "$PORT" switch -c "$BRANCH"`. A scratch worktree is similarly detached and only for read-only conflict discovery; never cherry-pick into the original.
4. After work, recapture the original fingerprint and require exact equality. A mismatch stops publication and is reported as user-work change, not repaired.
5. Cleanup only a recorded `PORT`/`SCRATCH` created in this run, after it is clean and its path/ref identity matches the record: `git -C "$ORIG" worktree remove "$CREATED_PATH"`. Never use `--force`; never remove the original or an unexpected worktree.

## PR rollback matrix
`BASE=41a07f5b8`; `SOURCE_HEAD=0dc0f228d`; `CANDIDATE_SHA` is captured with `git rev-parse HEAD` immediately before push and recorded immutably in the evidence manifest. No executor merges a PR, deletes a remote branch, or force-updates any remote branch.

| Phase/trigger | Immutable expected SHA | Actor | Required action |
|---|---|---|---|
| Pre-push verification fails or fingerprint changes | `BASE`, `SOURCE_HEAD`; no candidate SHA | executor | Do not push/open PR. Preserve #74072 untouched; retain local evidence only; fix in isolated port or stop. |
| Replacement open | exact `CANDIDATE_SHA` rooted at `BASE` | designated PR owner | Open a new branch/PR pinned to that SHA; link source SHAs and invalid-label disposition. Do not update #74072. |
| CI/review fails | recorded `CANDIDATE_SHA` | PR owner | Mark replacement blocked/close only replacement if owner chooses; do not force-update it. Create a new replacement branch/PR for a different immutable SHA after re-review; #74072 remains untouched. |
| Accepted but unmerged | approved `CANDIDATE_SHA` | human release/PR owner | Keep branch and #74072 intact; do not merge. Any post-acceptance change is a new SHA and new review, not an amendment. |
| Wrong head/accidental publication | published SHA ≠ `CANDIDATE_SHA` | PR owner | Stop CI/review, post correction identifying both SHAs, close/archive only the erroneous replacement if owner chooses; open a fresh correct replacement. Never force-push/delete remote branches; never touch #74072. |
| Replacement accepted for supersession | accepted immutable `CANDIDATE_SHA` plus explicit human acceptance | #74072 owner only | Human may close #74072 as superseded, linking replacement and source provenance. It is never auto-closed and does not imply merge. |

## Offline candidate contract
**Entry point:** standalone plugin CLI `hermes dual-coach preflight --config <path-to-config.yaml> --artifact <path> --json`. Its call surface is `run_preflight(request: DualCoachPreflightRequest) -> DualCoachPreflightReport`; validation errors are `DualCoachPreflightError(code: Literal['CONFIG_INVALID','PROFILE_UNAVAILABLE','PROFILE_CONTRACT_MISMATCH','WRITE_ATTEMPT','NETWORK_ATTEMPT'], detail: str)` and return nonzero without partial artifact.

`DualCoachPreflightRequest` has only `config_path`, `artifact_path`, `profile_adapter`; it does not accept raw destinations, bodies, IDs, tokens, or activation flags. `DualCoachPreflightReport` is bounded booleans/counts/digests/epoch only: `{schema:'dual-coach-offline-candidate/v1', hermes_sha, plugin_sha, profile_api_pin:'57aaf75', config_digest, hook_available, profile_contract_ok, review_triple_configured, flags, ledger_health, write_set_digest, network_calls, provider_calls, generated_at}`. Artifact JSON schema is exactly `dual-coach-offline-candidate/v1`; the package checksum is SHA-256 of the wheel and is included. It must explicitly state `manual Gate-D pending / rollout approval pending`.

Config ownership is the plugin's `config.yaml` section only, with schema-owned default false values:
```yaml
plugins:
  dual_coach:
    enabled: false
    ingress_enabled: false
    customer_ingress_enabled: false
    trainer_ingress_enabled: false
    operator_console_enabled: false
    scheduler_enabled: false
    activation_enabled: false
    delivery_enabled: false
```
No new user-facing `HERMES_*` behavioral variable exists. Secrets remain secret-provider configuration and are neither required nor read by preflight. Preflight is available while `enabled: false` because it is read-only; it must reject any true behavioral flag.

**Candidate procedure:** build `python -m build`; record `sha256sum dist/*.whl`; create a fresh venv; `pip install --no-deps dist/<wheel>`; run with a temporary `HERMES_HOME` and synthetic typed Profile fixture using the above CLI. Private compatibility runs separately in private CI against the real pinned `57aaf75` interface, never in the public artifact. Before/after snapshots of temp `HERMES_HOME` and artifact parent (path list, file digest map, mtime-free write set) must be identical except the explicitly named artifact file; network, Telegram adapter, scheduler transport, and provider recording counters must all be zero. Any write, socket/network attempt, provider call, private path, token, ID, body, payload, or log leak fails.

## Verification Plan
All commands run only in the clean port worktree. Pass is exit 0, no unexplained warning, no xfail/skip added to mask behavior, and all required CI checks green; any flake is a failure until reproduced/fixed or explicitly waived by maintainer with evidence.

1. Required focused suites through the repository wrapper:
```text
scripts/run_tests.sh tests/gateway/test_adaptive_nutrition.py
scripts/run_tests.sh tests/gateway/test_nutrition_coaching.py
scripts/run_tests.sh tests/gateway/test_telegram_physique_checkin.py
scripts/run_tests.sh tests/gateway/test_telegram_group_gating.py
scripts/run_tests.sh tests/cron/test_physique_inline_card.py
scripts/run_tests.sh tests/gateway/test_diagnostic_isolation.py  # only if retained
scripts/run_tests.sh tests/gateway/test_korean_humanizer.py      # only if retained
scripts/run_tests.sh tests/gateway/ tests/cron/
scripts/run_tests.sh
```
2. Run `ruff check <every changed public/private-plugin Python path>`; this is blocking. Run `ty check <every changed typed path>`; `ty`'s configured `unknown-argument=warn` diagnostics are captured and triaged, and any newly introduced warning/error is blocking unless the named maintainer records a narrow waiver. Do not suppress `ty` with ignores.
3. Run `python -m build`, isolated wheel install/smoke, then the real-import temporary-`HERMES_HOME` preflight E2E above. Package success requires plugin manifest discovery and no source-tree import.
4. Recording fakes wrap every network/provider/Telegram adapter method. Assert zero calls for disabled/preflight paths; exactly one only for the explicitly local fake approved delivery simulation; duplicates, timeout, restart, receipt/audit reconciliation add zero calls.
5. Capture logs and metrics for success and injected exception paths. Assert serialized output contains no preview/body bytes, customer/chat/topic/user IDs, Profile paths, credentials, or provider payloads; it may contain only bounded hash/digest, status, counter, and correlation token. Fail on a raw forbidden string and retain raw captures privately; publish only redacted summaries.
6. Run public secret/privacy scan and manual changed-file allowlist review. Record before/after write-set manifests, `git diff --check`, base ancestry, candidate SHA, config digest, test results, ruff/ty/build output, and all recording counters.

## Acceptance criteria
- A named maintainer/architect GO explicitly resolves #74072's `invalid` premise and selects A/B/C before implementation; C needs exception + allowlist.
- The signed hunk ledger covers all 18 changed source/test files and every hunk with the required ownership, config, typed Profile exception, ingress/order, scheduler/domain-reservation, and test fields.
- The original dirty worktree fingerprint is unchanged; collision-safe worktrees only are created/removed; no `fetch --prune`, reset, clean, reuse, force-update, delete, or merge occurs.
- Plugin routing reserves all enumerated Telegram ingress/callback types first; all non-exact identities are consumed with zero downstream/domain/network effect.
- Generic scheduler claim/execution and domain reservation are independently represented and tested; no ambiguity is retryable after claim/consume.
- Offline candidate schema/config defaults/CLI/install smoke meet the contract, with zero writes except artifact and zero network/provider calls.
- #74072 remains untouched until an explicit human accepts a correct immutable replacement; no live/Gate-D/customer delivery claim is made.

## Escalation/Risk Gate
Stop and return to the named maintainers—not an executor—on: invalid-label rejection/no response; missing generic hook seam; a required public exception beyond allowlist; inability to produce the hunk appendix; Profile mismatch/raw-storage pressure; any ingress ordering ambiguity; worktree collision/fingerprint drift; redaction/write-set/network failure; or PR head mismatch. An architect is required for B hook contracts and scheduler ownership; critic re-review is required before PR open; an executor implements only after both gates. No team/ultragoal/deployment lane has authority past the offline boundary.

## Pre-mortem and mitigations
| Failure | Early signal / impact | Mitigation / owner |
|---|---|---|
| Maintainer rejects placement | `invalid` remains unexplained or rejects A/B; replacement repeats it | No branch/PR before named GO; choose external plugin or stop. Owner: invalid-label maintainer + architect. |
| Invalid-label premise misunderstood | Team treats label as stale metadata | Require written reason/selection/allowlist; link it in PR. Owner: PR owner. |
| Worktree/ref collision | Existing path/ref/worktree record or original fingerprint differs | Fail closed; do not delete/reset/reuse; preserve evidence. Owner: executor. |
| Published wrong replacement cannot be unwound | remote SHA differs from manifest or CI targets wrong head | Freeze/close only erroneous replacement by PR owner, new immutable branch/PR; never force/delete/#74072 mutation. Owner: PR owner. |
| Generic claim conflated with domain reservation | provider attempt occurs without domain reservation or scheduler state is mutated by plugin | Separate APIs and tests; architect reviews B. Owner: architect. |
| Semantic reimplementation loses provenance | New code lacks source hunk/author attribution | private hunk appendix + source SHAs + Lore `Related:` and accurate co-author/provenance trailers. Owner: executor/PR owner. |
| Offline test silently performs I/O or leaks data | write/network counter nonzero or captured log contains forbidden data | recording fakes, before/after manifests, strict redaction assertions; block candidate. Owner: executor/reviewer. |

## Risks and mitigations
The 6,916-commit divergence, 36K-line source patch, and dirty original worktree are contained through an immutable base, semantic ledger, collision-safe isolated worktrees, and independent PR. Private/public leakage is contained by external plugin distribution, typed contract tests, no user-facing behavior env vars, and private evidence. Live-risk is contained by all-false configuration, no real credentials/accounts/providers, and the explicit human-only Gate-D/rollout stop.
