I read this as a read-only deployment investigation - I’ll trace the sealed candidate, authoritative install/bootstrap paths, live profile and service prerequisites, and prior Task27 evidence, then return a fail-closed command runbook with rollback boundaries.


The sealed release and inactive enabled user unit are present; I’m narrowing this to the release’s own installer/verification contract and the latest Task27 clean-state receipts so the runbook does not accidentally treat generic Hermes installation as authoritative.


I read this as a read-only deployment investigation - I traced the sealed candidate, current runtime/service, and Task27 evidence and produced the fail-closed decision and commands.

## Decision: BLOCKED / NO-GO

DualCoach v1.1.0 is sealed only for local artifact use:

- `deployment_authorized: false`
- `customer_upgrade_authorized: false`
- status: `GO_LOCAL_ARTIFACT_ONLY`
- candidate: `0e383539aea1b83205771772f1e8b417840defe60f5ea6ce184f80e3af8d25f9`

Task27 authorizes a bounded pilot of the **v1.0.0 base candidate** `d1109d8f...`, not installation of v1.1.0. Therefore the exact fail-closed deployment sequence must stop before snapshot or mutation.

```bash
set -euo pipefail

BUNDLE='/home/cube/projects/richard/traning coach/.omo/evidence/releases/dualcoach-v1.1.0'

python3 -I - "$BUNDLE" <<'PY'
import json, pathlib, sys
root = pathlib.Path(sys.argv[1])
seal = json.loads((root / "tagged-seal.json").read_text())
verdict = json.loads((root / "final-verdict.json").read_text())

expected = "0e383539aea1b83205771772f1e8b417840defe60f5ea6ce184f80e3af8d25f9"
assert seal["authoritative"] is True
assert seal["candidate_digest"] == expected
assert verdict["candidate_digest"] == expected
assert seal["deployment_authorized"] is True, "BLOCKED: deployment_authorized=false"
assert verdict["deployment_authorized"] is True, "BLOCKED: deployment_authorized=false"
assert seal["customer_upgrade_authorized"] is True, "BLOCKED: customer_upgrade_authorized=false"
PY
```

Current result: exits nonzero at `deployment_authorized=false`. Do not bypass this gate. A new immutable authorization/seal or successor candidate is required.

## Authoritative commands once authorization exists

### 1. Candidate integrity

The repository verifier is authoritative for the v1.0-to-v1.1 delta:

```bash
REPO='/home/cube/projects/richard/hermes-agent-dualcoach-v1.1'
BUNDLE='/home/cube/projects/richard/traning coach/.omo/evidence/releases/dualcoach-v1.1.0'
BASE="$REPO/dualcoach/releases/v1.0.0/delivered"

python3 -I "$REPO/scripts/verify_dualcoach_v11_candidate.py" "$BASE" "$BUNDLE"
# Required:
# {"status":"DUALCOACH_V11_CANDIDATE_INPUTS_PASS",...}
```

Verify every sealed byte using the authoritative tagged inventory:

```bash
python3 -I - "$BUNDLE" <<'PY'
import hashlib, json, pathlib, sys
root = pathlib.Path(sys.argv[1])
inventory = json.loads((root / "tagged-inventory.json").read_text())
assert len(inventory["files"]) == 235
for row in inventory["files"]:
    path = root / row["path"]
    assert path.is_file(), f"missing: {row['path']}"
    assert path.stat().st_size == row["size"], f"size mismatch: {row['path']}"
    assert hashlib.sha256(path.read_bytes()).hexdigest() == row["sha256"], \
        f"hash mismatch: {row['path']}"

seal = json.loads((root / "tagged-seal.json").read_text())
assert seal["inventory_sha256"] == hashlib.sha256(
    (root / "tagged-inventory.json").read_bytes()
).hexdigest()
assert seal["hermes_wheel_sha256"] == \
    "ec160d3d0e29bc747f463923d31bfe736aefb5840a3d32fcb04842aa5139ddb0"
assert seal["profile_wheel_sha256"] == \
    "a56da2417df0912f3fe407c0b78befd7362207d1c8a35271000af46701ba79d2"
print("SEALED_CANDIDATE_BYTES_PASS")
PY
```

### 2. Clean-boundary checks before snapshot

```bash
PROFILE='/home/cube/.hermes/profiles/dualcoachtest'
SERVICE='hermes-gateway-dualcoachtest.service'

test "$(systemctl --user show "$SERVICE" -p ActiveState --value)" = inactive
test "$(systemctl --user show "$SERVICE" -p SubState --value)" = dead
test "$(systemctl --user show "$SERVICE" -p MainPID --value)" = 0

python3 -I - "$PROFILE" <<'PY'
import os, pathlib, sys
profile = pathlib.Path(sys.argv[1]).resolve()
matches = []
for proc in pathlib.Path("/proc").iterdir():
    if not proc.name.isdecimal() or int(proc.name) == os.getpid():
        continue
    try:
        cmd = (proc / "cmdline").read_bytes().replace(b"\0", b" ").decode(errors="replace")
        env = (proc / "environ").read_bytes().split(b"\0")
    except (OSError, PermissionError):
        continue
    if str(profile) in cmd or f"HERMES_HOME={profile}".encode() in env:
        matches.append((proc.name, cmd))
assert not matches, f"profile processes remain: {matches}"
PY

test ! -e "$PROFILE/cron/jobs.json"
test ! -e "$PROFILE/data/owner-actions/draft-deliveries.json"
test ! -e "$PROFILE/data/onboarding/telegram-customer-bootstrap-v1"
test ! -e "$PROFILE/data/onboarding/telegram-publication-outbox-v1"
test ! -e "$PROFILE/data/onboarding/telegram-staff-membership-v1"
```

`gateway.lock` is stale (`pid 1170032` no longer exists); preserve it in the rollback snapshot rather than treating it as live authority.

### 3. Rollback snapshot contents

Snapshot before any installation:

- Entire profile, including configuration, credentials, databases, archives, logs, existing strict runtimes, modes, ACLs, and xattrs.
- Existing service unit.
- Unit enabled/active state and `systemctl show`.
- Candidate and snapshot hashes.

```bash
PROFILE='/home/cube/.hermes/profiles/dualcoachtest'
UNIT='/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service'
ROLLBACK="/home/cube/dualcoach-v11-rollback-$(date -u +%Y%m%dT%H%M%SZ)"

install -d -m 0700 "$ROLLBACK"
tar --acls --xattrs --numeric-owner --sparse --one-file-system \
    -C /home/cube/.hermes/profiles \
    -cpf "$ROLLBACK/dualcoachtest.tar" dualcoachtest
install -m 0600 "$UNIT" "$ROLLBACK/hermes-gateway-dualcoachtest.service"
systemctl --user show hermes-gateway-dualcoachtest.service \
    >"$ROLLBACK/systemctl-show.txt"
systemctl --user is-enabled hermes-gateway-dualcoachtest.service \
    >"$ROLLBACK/unit-enabled.txt"
sha256sum "$ROLLBACK/dualcoachtest.tar" \
          "$ROLLBACK/hermes-gateway-dualcoachtest.service" \
    >"$ROLLBACK/SHA256SUMS"
sha256sum -c "$ROLLBACK/SHA256SUMS"
tar -tf "$ROLLBACK/dualcoachtest.tar" >/dev/null
chmod -R go-rwx "$ROLLBACK"
```

### 4. Authoritative offline installation

The release’s installed-Golden replay establishes this install contract:

- Python 3.12 venv
- all 107 base wheelhouse dependencies
- `PIP_NO_INDEX=1`
- `--no-index --no-deps --no-compile`
- exact two sealed wheels

```bash
PROFILE='/home/cube/.hermes/profiles/dualcoachtest'
RUNTIME="$PROFILE/.strict-runtime/0e383539-v11"
VENV="$RUNTIME/venv"
PYTHON='/home/cube/miniconda3/bin/python3.12'
REPO='/home/cube/projects/richard/hermes-agent-dualcoach-v1.1'
BASE="$REPO/dualcoach/releases/v1.0.0/delivered"
BUNDLE='/home/cube/projects/richard/traning coach/.omo/evidence/releases/dualcoach-v1.1.0'

test ! -e "$RUNTIME"
install -d -m 0700 "$RUNTIME"
"$PYTHON" -I -m venv "$VENV"
chmod 0700 "$VENV"

mapfile -t DEPENDENCIES < <(
    find "$BASE/wheelhouse" -maxdepth 1 -type f -name '*.whl' -print | sort
)
test "${#DEPENDENCIES[@]}" -eq 107

PIP_CONFIG_FILE=/dev/null PIP_NO_INDEX=1 PYTHONNOUSERSITE=1 \
"$VENV/bin/python" -I -m pip install \
    --no-index --no-deps --no-compile \
    "${DEPENDENCIES[@]}" \
    "$BUNDLE/artifacts/hermes_agent-0.17.0-py3-none-any.whl" \
    "$BUNDLE/artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl"
```

### 5. Exact installed-byte verification

```bash
SITE="$("$VENV/bin/python" -I -c \
    "import sysconfig; print(sysconfig.get_paths()['purelib'])")"

"$VENV/bin/python" -I - \
    "$VENV" "$SITE" "$BUNDLE" <<'PY'
import importlib.util, json, pathlib, sys

venv, site, bundle = map(pathlib.Path, sys.argv[1:])
helper_path = bundle / "installed-golden-bundle/verification-tools/installed_wheel_provenance.py"
spec = importlib.util.spec_from_file_location("sealed_provenance", helper_path)
helper = importlib.util.module_from_spec(spec)
spec.loader.exec_module(helper)

runtime = helper.collect_installed_runtime(
    venv=venv,
    site_packages=site,
    hermes_wheel=bundle / "artifacts/hermes_agent-0.17.0-py3-none-any.whl",
    profile_wheel=bundle / "artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl",
)
actual = helper.portable_installed_runtime(runtime)
golden = json.loads(
    (bundle / "installed-golden-bundle/installed-provenance.json").read_text()
)
expected = golden["binding"]["runtime_portable"]
assert actual == expected, "installed runtime differs from sealed Golden Path"
print("INSTALLED_BYTES_MATCH_SEALED_GOLDEN")
PY
```

### 6. Service bootstrap without starting it

The authoritative host command is the installed candidate CLI, not the source checkout:

```bash
HERMES_HOME="$PROFILE" VIRTUAL_ENV="$VENV" \
PATH="$VENV/bin:/home/cube/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
"$VENV/bin/python" -I -m hermes_cli.main \
    --profile dualcoachtest gateway install --force
```

Then verify, while still stopped:

```bash
systemctl --user daemon-reload
test "$(systemctl --user is-enabled hermes-gateway-dualcoachtest.service)" = enabled
test "$(systemctl --user show hermes-gateway-dualcoachtest.service -p ActiveState --value)" = inactive
test "$(systemctl --user show hermes-gateway-dualcoachtest.service -p MainPID --value)" = 0
grep -F "ExecStart=$VENV/bin/python -m hermes_cli.main --profile dualcoachtest gateway run" \
    /home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service
```

Do not start the service until separate deployment/customer authorization and provider-readiness checks pass.

## Rollback

```bash
systemctl --user stop hermes-gateway-dualcoachtest.service || true
test "$(systemctl --user show hermes-gateway-dualcoachtest.service -p MainPID --value)" = 0

rm -rf /home/cube/.hermes/profiles/dualcoachtest
tar --acls --xattrs --numeric-owner \
    -C /home/cube/.hermes/profiles \
    -xpf "$ROLLBACK/dualcoachtest.tar"
install -m 0644 "$ROLLBACK/hermes-gateway-dualcoachtest.service" \
    /home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service
systemctl --user daemon-reload
systemctl --user enable hermes-gateway-dualcoachtest.service
```

Leave it inactive because the captured pre-state was inactive/dead.

## Prerequisites and current state

- Service unit: **present**, enabled, inactive/dead, `MainPID=0`.
- Systemd linger: **enabled**.
- `.env`: present, mode `0600`, includes Telegram token and allowlist variables.
- `auth.json`: present, mode `0600`, contains an OpenAI Codex credential.
- Provider validity/readiness: **not established**; verifying it would contact a provider and was forbidden.
- Current v8 runtime is **not** v1.1.0: its Hermes wheel origin hash is `c673e7...`, versus candidate `ec160d...`.
- Missing prerequisite: immutable v1.1.0 deployment/customer-upgrade authorization.

## Primary evidence

- Sealed decision: `.../dualcoach-v1.1.0/final-verdict.json`
- Authoritative seal: `.../dualcoach-v1.1.0/tagged-seal.json`
- Byte inventory: `.../dualcoach-v1.1.0/tagged-inventory.json`
- Offline install contract: repository `scripts/replay_dualcoach_v11_installed_golden.sh`
- Delta verifier: repository `scripts/verify_dualcoach_v11_candidate.py`
- Installed-byte verifier: `.../installed-golden-bundle/verification-tools/installed_wheel_provenance.py`
- Task27 final verdict: `.omo/evidence/task27/task27-v38-bounded-pilot-release-verdict-final.md`
- Task27 cleanup receipts:
  - `.omo/evidence/task27/task27-live-cleanup-receipt.json`
  - `.omo/evidence/task27/task27-supplemental-live-cleanup-receipt.json`
  - `.omo/evidence/task27/receipt-hashes.sha256`

No files, services, credentials, or profile state were mutated.


I read this as a read-only authorization and controller re-audit - I’ll verify the owner binding, sealed-candidate scope, one-use limits, hashes/modes, preflight boundary, and controller behavior, then return a strict PASS/FAIL without executing code.


The authorization hash and stopped service boundary match, but the authorization file is not itself the controller’s consumed permission format; I’m now checking that handoff and whether the three-module controller can enforce the claimed one-operation scope without an unsealed or reusable gap.


I read this as a final read-only authorization audit - the result is FAIL because the operational authorization is not enforceably connected to the controller.

# FAIL

Positive findings:

- Authorization SHA matches `4e8bfef2...373de`.
- Candidate, wheels, tagged seal, approval event, preflight, draft, controller modules, and tests all match their declared hashes.
- Sensitive JSON files are owner-owned `0600`.
- Current profile hashes match the preflight baseline.
- Service remains enabled but `inactive/dead`, `MainPID=0`.
- Target runtime is absent; no install has occurred.
- Forbidden onboarding roots remain absent.
- Candidate bootstrap source matches the sealed wheel at `777e8ed7...ab23`.

Blocking findings:

1. **No executable permission artifact exists.**
   The controller requires schema `dualcoach-first-customer-launch-permission-v1`, but only `launch-authorization.json` with schema `dualcoach-first-customer-launch-authorization-v1` exists. It lacks required fields such as `service`, `expected_python`, `unit_path`, `unit_sha256`, and `allowed_modes`.

2. **The controller never consumes or verifies `launch-authorization.json`.**
   Its SHA, `maximum_invites`, allowed/prohibited operations, target runtime, rollback runtime, and preflight hash are unused by all three modules.

3. **Owner approval is not enforced.**
   `approval_event_sha256` is parsed into `Permission` but never compared in `verify_permission()`. Tests use the arbitrary value `"1" * 64` and still pass.

4. **The controller does not control the authorized deployment operation.**
   It implements only `preflight`, `prepare`, and `verify`. It does not install the runtime, verify installed bytes, replace the unit, run provider readiness, start the gateway, snapshot, or roll back. Those authorized mutations could therefore occur outside its one-use enforcement.

5. **The operational code is writable.**
   The controller modules are mode `0664` inside a `0775` directory. Hash binding could protect them only through the missing permission artifact; the launch authorization’s hashes are not checked by runtime code.

6. **Test scope is insufficient for the claimed authorization conversion.**
   The four tests cover clean invite preparation, verification, and two rejection cases. They do not test launch-authorization binding, approval binding, one-use authorization consumption, installation, unit replacement, provider probe, service start, or rollback.

The authorization records the owner’s narrow intent, but it does **not yet truthfully convert the local-only verdict into an enforceable one-use deployment/link operation**. Snapshot/install must remain blocked.

Evidence reviewed:

- `.omo/evidence/first-customer-v11/launch-authorization.json`
- `approval-event.json`
- `preflight-baseline.json`
- `pilot-customer-draft.private.json`
- `first_customer_invite_contract.py`
- `first_customer_invite_operations.py`
- `first_customer_invite_controller.py`
- `test_first_customer_invite_controller.py`

No files or live state were mutated.


I read this as a final go/no-go audit for the install step only - I’ll verify the new authorization chain, executable permissions, sealed deployment controller, production preflight, rollback ownership, and current stopped boundary, then return PASS or FAIL without execution.


The new bundle satisfies the claimed filesystem protections and has distinct deployment/invite permissions; I’m tracing the install transaction and permission checks now, especially whether preflight is mandatory and fresh at install time and whether every failure after snapshot restores both runtime and unit.


I read this as a final install-only authorization audit - all required gates now pass.

# PASS — authorized to execute install next

Verified read-only:

- Launch authorization SHA: `1a67abc3...7e938`
- Production deployment preflight SHA: `17b42b1c...b346`, status `PASS_READY`
- Owner approval, authorization, candidate, wheel, controller, permission, and expected-unit bindings all match.
- Both executable permission artifacts exist and are `0600`.
- Seven operational modules are `0400` under a `0700` directory; all declared hashes match.
- Wheelhouse is immutable, contains exactly 107 wheels, and matches inventory digest `554176ae...6b2b2`.
- Sealed product wheels match:
  - Hermes: `ec160d3d...9ddb0`
  - Profile: `a56da241...79d2`
- Bound `uv` hash matches `9a4299a0...02b81`.
- Service is enabled but `inactive/dead`, `MainPID=0`; no DualCoach runtime process exists.
- Live unit remains at baseline hash `e21f9905...5f37`.
- Profile hashes still match the stopped-boundary baseline.
- Target runtime, rollback snapshot, install receipt, and all later receipts are absent.
- Operational onboarding roots remain absent.
- Install is one-use and requires the consumed preflight receipt before snapshot.
- Failure handling removes the parallel runtime and restores the prior unit after a switch.
- Provider check, service start, and invite creation remain separately receipt-gated.

The authorized next transition is **deployment controller `install` mode only**. This PASS does not pre-authorize skipping the later provider-check, start, or invite gates.

No files, services, or live state were mutated.


I read this as a read-only r2 delta gate - I’ll verify the disclosed failed attempt, new authorization/preflight chain, runtime path isolation, copy-mode and link-count enforcement, rollback baseline, and current stopped boundary, then return PASS/FAIL for r2 install only.


I read this as an r2 install-only delta audit - one claimed provenance gate remains unenforced, so the result is FAIL.

# FAIL

Most r2 gates pass:

- Authorization and preflight hashes match.
- Service remains `inactive/dead`, `MainPID=0`.
- Baseline unit is restored exactly.
- r2 runtime, snapshot, and receipt paths are absent.
- Attempt 1 is preserved and disclosed.
- `UV_LINK_MODE=copy`, venv `0700`, and all-site-packages link scanning are implemented.
- r2 permissions and controller hashes match; files have correct modes.

Blocking discrepancy:

- The install receipt records `installed_regular_file_count`, but no downstream permission validates that field.
- `first_customer_deployment_permission.py` validates only:
  - `venv_mode == "0700"`
  - `link_mode == "copy"`
- `first_customer_invite_permission.py` does not validate `installed_regular_file_count` either.
- Tests assert `link_mode` and selected file link counts, but do not test rejection of a missing, zero, or malformed counter.

Therefore the stated remediation—“downstream permission requires those fields”—is incomplete. r2 install should not execute yet.

No files, services, or live state were mutated.


I read this as a focused read-only recheck of the counter gate - I’ll verify both downstream permissions enforce a positive integer, confirm the two rejection tests and rebound hashes, then return PASS/FAIL for r2 install.


I read this as a focused counter-gate verification - the blocker is fully resolved.

# PASS — r2 install may proceed

Verified:

- Both permissions require `installed_regular_file_count` with:
  - `type(value) is int`
  - `value > 0`
- Existing checks for `venv_mode == "0700"` and `link_mode == "copy"` remain.
- New tests cover zero-counter rejection before provider output and invite ledger mutation.
- Code/test hashes match the rebound launch authorization.
- Final authorization SHA matches `f2e365dd...244e3`.
- r2 preflight SHA remains `dda1939e...aea7d8`.
- Service remains stopped; baseline unit is exact.
- r2 runtime, snapshot, and receipts remain absent.

No mutation occurred.


I read this as a focused r3 provenance audit - I’ll verify the pip-based isolated install contract, interpreter binding, frozen Golden comparison, downstream installer gate, rebound hashes, and stopped one-use boundary, then return PASS/FAIL without mutation.


I read this as a focused r3 provenance verification - the installer metadata blocker is resolved.

# PASS — r3 install may proceed

Verified:

- Uses bound Python 3.12.9 with SHA `d9bc96d1...2a9051`.
- Creates runtime via `python -I -m venv`.
- Installs via isolated pip with `--no-index --no-deps --no-compile --no-cache-dir`.
- Receipt records `installer: pip`.
- Deployment and invite permissions require `installer == "pip"`.
- Venv `0700`, copied files, positive counter, and `nlink == 1` gates remain.
- Integration test invokes sealed `installed_wheel_provenance.py` and compares `runtime_portable` to frozen Golden provenance.
- All rebound code/test/permission hashes match authorization `6ad0014c...cefee8`.
- r3 preflight matches `0eaf948d...f22dd4`.
- Service remains stopped, baseline unit is exact, and all r3 one-use paths are absent.
- r1/r2 failures are preserved and disclosed.

No mutation occurred.