I read this as a security investigation - I will inspect the NutriCoach source and tests, trace each requested control, and report evidence without modifying anything.


I found a likely journal-integrity gap and am validating it alongside the focused security tests before finalizing the verdict.


I read this as a security review - I inspected the source and focused tests without edits or external services.

## FAIL - 1 blocker

**B1: Bootstrap journal accepts valid-tail truncation.**
`gateway/platforms/telegram_customer_bootstrap.py:978-988` treats the journal's last surviving event as authoritative and rewrites the ledger projection to match it. `:1023-1077` verifies only an internally consistent chain starting at the surviving first event; `:1080-1102` then appends from that shortened chain. There is no authenticated/independent terminal-head witness.

Reproduced without modifying repository files: removing the final valid JSONL event and reopening the store succeeded:

```text
journal-tail-truncation-accepted:
expected=AWAITING_CONSENT recovered=REGISTERING
events_before=4 events_after=3
```

This violates append-only journal integrity and permits rollback of durable bootstrap state/audit history. The store must fail closed on a missing/reduced terminal head, using an independently protected authenticated head/anchor rather than projecting backward from the remaining file.

## Controls verified

- **Invite recipient binding / wrong actor denial: PASS.** Private-DM identity is required at `gateway/platforms/telegram.py:12094-12118`; claim requires `user == chat` and exact drafted customer ID at `gateway/platforms/telegram_customer_bootstrap.py:388-415`.
- **Consent handoff durability and recovery: PASS.** The authenticated handoff is persisted before the domain transition at `gateway/platforms/telegram.py:6723-6827`; reconciliation and recovery marking follow at `:6858-6875`. Exact actor/chat/topic/card/generation/callback/provenance binding is enforced at `telegram_customer_bootstrap.py:767-820`.
- **Activation consent preflight: PASS.** Activation requires both a handoff and completed reconciliation at `gateway/platforms/dualcoach_activation_cutover.py:235-266`.
- **Capability replay resistance: PASS.** Callback serialization calls the locked handler at `telegram_nutrition_onboarding_runtime_callback.py:114-134`; publication receipt HMAC validation is at `telegram_nutrition_onboarding_publication_outbox.py:516-525`; recovery capability verifies candidate, owner, and seal at `telegram_nutrition_onboarding_operator_recovery.py:92-122`.
- **Symlink/permission guards: PASS.** Invite/profile files require owner-only directories/files and reject symlinks/hardlinks at `dualcoach_customer_invite.py:55-96`; bootstrap state-chain and lock protection is at `telegram_customer_bootstrap.py:938-972`; profile onboarding uses no-follow validation at `dualcoach/profile/checkin_cli/nutrition_onboarding_fs.py:12-118`.
- **Bounded uncertain-publication replacement: PASS.** Only one uncertain consent-card replacement is allowed; a second uncertainty terminates the session at `telegram_customer_bootstrap.py:688-730`.

## Verification

- `dualcoach/profile/tests/test_nutrition_onboarding.py`: **27 passed**.
- Focused gateway suite: **30 passed, 2 failed**. The failures are test-fixture defects, not reached production authorization behavior: `tests/gateway/test_telegram_operator_notification_recovery.py:506` and `:617` invoke the mixin on a `SimpleNamespace` that lacks `_handle_callback_locked`, required by `gateway/platforms/telegram_nutrition_onboarding_runtime_callback.py:127`.

## Residual risk

The two broken recovery tests leave owner-risk/owner-approval callback coverage incomplete until their fixture supplies the concrete locked handler.


I read this as a fix re-review - I’ll inspect the terminal-witness implementation and new tests, then rerun the callback fixtures and relevant security tests.


I read this as a fix re-review - I inspected the witness/recovery paths and reran the requested callback fixtures.

## FAIL - blocker_count: 1

**B1: Deleting `events.jsonl` bypasses the terminal witness.**

- `gateway/platforms/telegram_customer_bootstrap.py:315-317` only treats ledger/journal existence as prior state; it ignores an existing `events-head.json`.
- `:985-1012` reads and validates the terminal head only when the journal exists. A missing journal falls back to the ledger.
- `:1139-1162` then permits the next mutation to recreate a new journal/head from that ledger.

No-edit reproduction:

```text
journal-deletion: accepted=AWAITING_CONSENT
journal-and-projection-deletion: accepted=state-reset
```

Thus, deleting only the journal erases append-only history without rejection; deleting journal plus projection overwrites the surviving terminal witness and resets bootstrap state. Treat an existing head with a missing journal as a hard conflict. Legacy migration must require both journal and head to be absent, with a separately durable migration marker if deletion of both must be distinguished from legacy state.

The new valid-tail test passes, but it covers only removal of the final line while the journal file remains: `tests/gateway/test_new_customer_activation_artifacts.py:118-129`. The legacy test explicitly removes both journal and head at `:171-185`, leaving the head-survives case untested.

## Non-blocking residual

`telegram_customer_bootstrap.py:985-1012` accepts a projection matching **any** earlier journal event, not just the immediate predecessor permitted by a single interrupted write. A manually restored historic projection was silently repaired to current state. No stale state was exposed, but strict rewritten-projection rejection should limit recovery to `journal[-2]`.

## Callback fixtures

**PASS.** The wrapper now falls back to the concrete locked handler at `gateway/platforms/telegram_nutrition_onboarding_runtime_callback.py:123-137`.
`tests/gateway/test_telegram_operator_notification_recovery.py`: **14 passed**.

## Verification

- New activation-artifact, invite, consent, lock, and admin security tests: **21 passed**.
- All gateway tests directly using `RoomBootstrapStore`: **18 passed**.
- Profile onboarding tests: **20 passed**.


I read this as a final security re-review - I’ll verify the hardened witness logic and rerun the relevant security and callback tests.


FAIL
blocker_count: 1
remaining blocker: Deleting both `events.jsonl` and sibling `events-head.json` is accepted as legacy ledger-only state; deleting the projection too resets bootstrap state. The witness shares the same trust boundary and cannot distinguish deletion from legacy state.


I read this as a final security re-review - I’ll inspect the durable migration marker and rerun the affected security suite.


FAIL
blocker_count: 1
remaining blocker: The parent marker is deletable with the journal, head, and ledger under the same writable parent directory, allowing fresh-state initialization and bootstrap reset.


I read this as a final threat-model-scoped review - I’ll verify the sealed authority directory and rerun the affected security tests.


PASS
blocker_count: 0
remaining blocker: none