I read this as a read-only red-team audit - I’ll trace reconciliation-to-question generation end to end, enumerate all field routes and tests, then judge v7 against concrete invariants and adversarial cases.


The first pass isolates the mechanism to nutrition onboarding’s reconciliation policy, service/store persistence, and CLI surface. I’m now tracing those symbols bidirectionally and checking whether v7 generalizes across the full field contract rather than only the newly tested equation-basis case.


The v7 delta is confirmed as a narrow `equation_sex_basis` phrase-table/filter patch. The end-to-end trace also exposes a second, independent preview path that bypasses profile persistence entirely, so a profile-only filter cannot establish the requested global guarantee. I’m validating that conclusion against the gateway tests and v7 source delta before finalizing the red-team matrix.


I read this as a read-only red-team audit - I traced reconciliation through both production and preview publication paths, mapped all 22 fields, and evaluated the v7 patch against deterministic gating requirements.

# Verdict

**Current v7 is not sufficient.**

It fixes one symptom for `equation_sex_basis` using a static phrase table and field-specific filtering. It does not establish the required invariant:

> A customer clarification may exist only when a deterministic field or cross-field policy derives a currently unresolved issue from the digest-bound answers.

The model remains the authority deciding whether to ask questions for 21 fields, and the preview path bypasses the v7 profile filter entirely.

No files or runtime state were changed.

## Critical findings

### 1. The model still authorizes questions

`installed/gateway/platforms/nutrition_onboarding_reconciler.py:35-106`

The reconciler sends all answers to the model and accepts its `clarifications` after only structural validation. The prompt explicitly asks the model to choose `field`, `kind`, and `question_ko`.

`installed/gateway/platforms/nutrition_onboarding_reconciliation.py:236-315`

`parse_reconciliation_json()` proves only:

- canonical field name;
- allowed kind;
- unique field;
- bounded text;
- presence of a clarification when advisory ambiguity/contradiction lists are nonempty.

It does **not** prove that any ambiguity or contradiction exists in the answers.

### 2. v7 only handles one field

Changed symbols:

- `checkin_cli/nutrition_onboarding_models.py:79-119`
  - `_EQUATION_SEX_BASIS_ANSWERS`
  - `canonical_equation_sex_basis`
- `checkin_cli/nutrition_onboarding.py:56-79`
  - `_normalized_reconciliation_inputs`
- `NutritionOnboardingService.submit_answer`
- `NutritionOnboardingService.record_reconciliation`
- `NutritionOnboardingService.replace_stale_reconciliation`
- `NutritionOnboardingService.revise_reconciliation_answer`

The source delta confirms exactly four changed files and no gateway changes:

- `checkin_cli/nutrition_onboarding.py`
- `checkin_cli/nutrition_onboarding_models.py`
- `tests/test_nutrition_onboarding.py`
- `tests/test_nutrition_onboarding_models.py`

Thus preview and model parsing were intentionally untouched.

### 3. Mixed clarifications can still publish the filtered question on the first pass

In production publication:

`installed/gateway/platforms/telegram_nutrition_onboarding_runtime_publication.py:84-123`

The gateway retains the original model object in `reconciliation`, calls the service to persist a possibly filtered record, then reads:

```python
clarifications = reconciliation.clarifications
current_index = int(record["current_index"])
```

If the model returns:

1. redundant `equation_sex_basis`;
2. another clarification;

the profile service filters item 1 from the persisted record, but first publication still indexes the unfiltered model tuple and publishes item 1. A restart may load the filtered advisory, so behavior differs before and after restart.

This is a concrete model/persistence split-brain defect.

### 4. Preview bypasses profile reconciliation completely

Route:

- `telegram_nutrition_onboarding_preview_flow.py:_finish_answers`
- `telegram_nutrition_onboarding_preview_completion.py:build_preview_completion`
- `reconcile_preview_answers`
- `NutritionOnboardingReconciler.reconcile`
- `render_clarification_text`
- `_publish_clarification`

At `telegram_nutrition_onboarding_preview_completion.py:128-137`, any model clarification is published directly. There is:

- no profile service;
- no deterministic filter;
- no answers digest;
- no durable reconciliation record;
- no restart-safe clarification cursor.

Therefore v7 cannot provide a global guarantee.

### 5. Filtering clears unrelated advisory evidence

`checkin_cli/nutrition_onboarding.py:74-78`

When the only persisted clarification is filtered, v7 clears the entire:

- `ambiguities_ko`;
- `contradictions_ko`.

Those lists are unkeyed, so the code cannot prove they concern `equation_sex_basis`. An unrelated advisory ambiguity or contradiction is silently deleted.

Conversely, when another clarification remains, the equation-related advisory text may remain because individual advisory entries cannot be associated with fields.

### 6. Clarification answers are not generally proven to resolve anything

`NutritionOnboardingService.revise_reconciliation_answer`, lines 568-621, validates only `equation_sex_basis`. For every other field it:

1. writes the supplied value;
2. advances the cursor;
3. marks the reconciliation resolved after the final cursor.

It does not rerun field or cross-field policy. An unchanged, invalid, or still-contradictory answer can resolve the record.

The preview has the same issue at `telegram_nutrition_onboarding_preview_flow.py:129-153`: it advances through the original list, then suppresses further clarifications with `allow_clarifications=False`.

### 7. Parser and persistence disagree on clarification count

- Gateway parser permits up to all 22 fields:
  `nutrition_onboarding_reconciliation.py:239`
- Profile persistence permits at most 3:
  `checkin_cli/nutrition_onboarding_reconciliation.py:53,64-65`

A model returning four structurally valid questions passes gateway parsing but fails profile persistence. Production catches the error and falls back to the generic attestation card; preview can publish all four sequentially.

### 8. Privacy is unnecessarily exposed

`NutritionOnboardingReconciler.reconcile` serializes all raw answers, including:

- full date of birth;
- conditions;
- medications;
- pregnancy/breastfeeding;
- eating-disorder risk;
- allergies and restrictions.

The model may echo these values into `question_ko`, and `render_clarification_text` publishes that text verbatim. Profile persistence also accepts arbitrary `advisory: dict[str, object]` without the gateway’s size/schema constraints.

A deterministic question renderer should never interpolate customer values. Safety-sensitive fields should lead to existing safety/owner review policy, not model-authored probing.

### 9. Initial reconciliation creation is race-prone

`record_reconciliation()` performs read/check/write without `self.store.locked()`. `replace_stale_reconciliation()` uses the lock.

Concurrent publication attempts can both invoke the model and race to persist differing reconciliation records. Publication generation/outbox logic protects many delivery cases, but it does not make nondeterministic model decisions equivalent.

## Complete route and caller map

### Production route

1. All 22 answers collected through:
   - `TelegramNutritionOnboardingRuntimeCollectionMixin.handle_text`
   - `telegram_nutrition_onboarding_copy.parse_answer`
   - `NutritionOnboardingService.submit_answer`
2. On `customer_attestation`:
   - `TelegramNutritionOnboardingRuntimePublicationMixin._publish`
   - `service.reconciliation_answers`
   - `service.reconciliation_record`
   - `NutritionOnboardingReconciler.reconcile`
   - `parse_reconciliation_json`
   - `service.record_reconciliation` or `replace_stale_reconciliation`
3. Customer question:
   - `render_clarification_text`
   - `_send_publication`
4. Reply:
   - runtime collection reads publication payload’s `clarification_field` and `answers_digest`
   - `parse_answer`
   - `service.revise_reconciliation_answer`
   - `advance_reconciliation`
5. Completion:
   - `require_resolved_reconciliation`
   - `attest_baseline`
   - safety hold or owner review.

### Preview route

1. `TelegramNutritionOnboardingPreviewFlowMixin.handle_text`
2. `_finish_answers`
3. `build_preview_completion`
4. `reconcile_preview_answers`
5. `NutritionOnboardingReconciler.reconcile`
6. direct `render_clarification_text`
7. `_publish_clarification`
8. `_advance_clarification`
9. final model rerun with `allow_clarifications=False`.

### Non-customer callers

`nutrition_onboarding_cli.py` exposes status, purge, migration, and owner review only. It does not create reconciliation questions.

## All 22 fields and required deterministic treatment

| Field | Deterministic proof before clarification |
|---|---|
| `date_of_birth` | ISO date, not future, derived age 18-120 |
| `equation_sex_basis` | Closed enum: `male`, `female`, `decline` |
| `height_cm` | Decimal in 120-250 |
| `weight_kg` | Decimal in 35-300 |
| `activity_category` | Closed activity enum |
| `activity_rationale` | Bounded normalized text; required/nonempty if product policy requires it |
| `goal_type` | Closed `loss/maintain/gain` enum |
| `target_weight_kg` | Null for maintain; bounded and present for loss/gain |
| `target_date` | Null for maintain; valid future date and present for loss/gain |
| `allergies` | Exact structured status/items contract |
| `intolerances` | Exact structured status/items contract |
| `religious_ethical_exclusions` | Exact structured status/items contract |
| `disliked_foods` | Exact structured status/items contract |
| `dietary_preferences` | Exact structured status/items contract |
| `conditions` | Exact structured contract; provided values trigger safety review, not model questioning |
| `medications` | Exact structured contract; provided values trigger safety review, not model questioning |
| `pregnancy_breastfeeding` | Exact `true/false/null`; null or true remains safety policy |
| `eating_disorder_risk` | Exact `true/false/null`; null or true remains safety policy |
| `cooking_access` | Bounded normalized text; explicit empty policy |
| `budget_band` | Closed band or bounded explicit answer, according to product contract |
| `meal_count` | Integer 2-6 |
| `schedule_constraints` | Bounded normalized text; explicit none representation |

Cross-field proofs should include at minimum:

- maintain implies both target fields are null;
- loss/gain implies both target fields are present;
- target date is future relative to a persisted reference date;
- loss target is below current weight; gain target is above current weight, if that is product policy;
- exact normalized set intersections for restrictions versus preferences can establish a contradiction;
- safety-sensitive disclosures never require extra model interrogation before safety hold.

Free-text semantic uncertainty cannot be deterministically proven after accepting arbitrary text. The correct fix is an explicit collection grammar or treating customer text as authoritative—not asking the model to guess whether it is vague.

# Smallest robust root fix

Implement one profile-owned deterministic policy and make both gateway routes consume it.

Suggested location:

`checkin_cli/nutrition_onboarding_reconciliation.py`

Suggested symbols:

```python
class ReconciliationIssue:
    field: canonical field
    kind: ambiguity | contradiction
    reason_code: closed enum

def evaluate_reconciliation_issues(
    answers: Mapping[str, object],
    *,
    reference_date: date,
) -> tuple[ReconciliationIssue, ...]
```

Then:

1. **Ignore model clarifications for authority.**
   Model output may supply advisory summary/facts/safety observations only.

2. **Build persisted clarifications exclusively from deterministic issues.**
   Prefer persisting `field`, `kind`, and `reason_code`; render customer copy from the canonical field prompt. Do not persist or publish model `question_ko`.

3. **Render from the persisted record, never the pre-persistence model object.**
   Fix `_publish` to deserialize the returned record after `record_reconciliation()`.

4. **Use the same evaluator in preview.**
   Preview must derive issues before deciding `phase="clarifying"`.

5. **Re-evaluate after every clarification answer.**
   Replace blind `advance_reconciliation()` with rebuilding remaining issues against the new answers. A field advances only if its issue is actually gone.

6. **Validate/canonicalize at initial collection where possible.**
   Invalid enum, range, date, structured status, or cross-field answer should cause the existing deterministic field prompt to be repeated before model invocation.

7. **Preserve safety separately.**
   Conditions, medications, pregnancy, and eating-disorder signals continue through `requires_safety_hold` and clinical review. Advisory contradictions cannot clear or create safety state.

8. **Lock initial create and stale replacement.**
   The lock must cover read/check/build/write for both methods.

9. **Minimize model input.**
   Exclude DOB and raw health/medication item text where not needed for an advisory summary. Questions must contain no customer-derived interpolation.

This avoids static answer phrase tables and field-by-field model-output filtering. A closed parser table for UI enum labels is acceptable at ingress; a growing phrase table used to suppress hallucinated questions is not.

# Required invariants

1. `persisted_clarifications == evaluate_reconciliation_issues(digest_bound_answers)`.
2. No model field, kind, list entry, or prose can add or remove an issue.
3. Every published clarification is present in the current persisted issue set.
4. Published question text is deterministic from `field + reason_code` and contains no answer values.
5. Updating an answer recomputes all affected field and cross-field issues.
6. A reconciliation is resolved iff the recomputed issue set is empty.
7. Safety state is derived independently from model output.
8. Advisory ambiguities/contradictions are never used as authorization.
9. Initial create, stale replacement, and clarification revision are serialized and digest-bound.
10. Runtime and preview produce the same issue set for identical answers.
11. Restart does not change question order, wording, cursor, or answers digest.
12. Duplicate updates never invoke the model twice or publish a second question.
13. Policy ordering and any maximum-question behavior are deterministic. Exceeding a cap must not silently drop unresolved issues.

# Adversarial test matrix

| Case | Expected result |
|---|---|
| Model asks about every valid field | Zero clarifications |
| Model asks about valid sex plus a real goal/target contradiction | Only deterministic goal/target issue; never sex |
| Model advisory lists contradiction but emits no question | Advisory retained or redacted; no gate unless policy derives issue |
| Model emits question but advisory lists are empty | Rejected from authority |
| Valid answer phrased outside sex phrase table | Ingress parser decides validity; model cannot decide |
| Sex text contains both male and female | Deterministic enum issue remains |
| Maintain plus target weight/date | Deterministic contradiction |
| Loss/gain missing one target field | Deterministic ambiguity |
| Loss target above current weight | Deterministic contradiction if direction policy is enabled |
| Structured `none` with items | Deterministic validation failure |
| Structured `provided` with no items | Deterministic validation failure |
| Preference exactly intersects allergy | Deterministic cross-field contradiction |
| Conditions or medication disclosed | Safety review; no model-generated probing |
| Pregnancy/eating-risk unknown | Preserve safety ambiguity; do not auto-resolve |
| Clarification reply repeats original invalid value | Same issue remains |
| Clarification reply fixes one of two cross-field inputs | Recompute both; clear only resolved issue |
| Four real issues | Stable deterministic ordering; no gateway/profile 22-vs-3 mismatch |
| Filtered plus retained issue on first publication | First card equals persisted first issue |
| Restart during clarification | Same digest, issue, cursor, and copy |
| Stale reconciliation after answer mutation | Atomic replacement from current answers only |
| Concurrent first publications | One persisted winner; no divergent customer cards |
| Duplicate update after durable mutation | No second model call or question |
| Model echoes DOB/medication in `question_ko` | Text never reaches publication or persistence |
| Preview with identical answers | Same deterministic issue set as production |
| Model unavailable | Deterministic issues still work; advisory summary alone may fail |
| Tampered advisory lists | Cannot affect resolved/clarifying state |
| Tampered issue/cursor/digest | Fail closed before publication or attestation |

## Existing test coverage

Relevant profile tests:

- `tests/test_nutrition_onboarding.py`
  - authorization/copy-only reconciliation answers;
  - digest-bound revision;
  - three canonical sex normalizations;
  - ambiguous sex with both `ambiguity` and `contradiction`;
  - final reconciliation digest binding.
- `tests/test_nutrition_onboarding_models.py`
  - supported sex phrase table;
  - substring rejection.
- `tests/test_owner_v1_contract.py`
  - iterates all fields, but does not exercise reconciliation questions.

Relevant gateway tests found in the installed test set:

- production enforces persisted reconciliation;
- resolved summary controls;
- stale record regeneration/replacement;
- real-service stale digest behavior;
- publication restart/outbox/deduplication.

Missing coverage:

- no all-22 deterministic clarification policy;
- no hallucinated-question rejection;
- no mixed filtered/retained first-publication case;
- no preview policy parity;
- no resolution re-evaluation;
- no advisory-list preservation;
- no privacy-echo test;
- no initial-create race test;
- no 22-versus-3 limit contract test.

Verification limitation: the isolated source virtual environment does not contain `pytest` (`No module named pytest`), so I could not execute the focused suite. Static tracing and source-delta comparison were completed; no writes occurred.


I read this as an expanded read-only red-team audit - I’ll map one uninterrupted 22-field production journey through every human handoff, compare it with preview and the current v7 observer/launcher assertions, and return the missing adversarial cases plus required evidence before any live invite.


I read this as an expanded read-only red-team audit - I mapped the one-pass production journey and identified what must be proven before another live invite.

# Stop verdict

**BLOCK another live invite.**

The current v7 harness does not observe or prove the requested golden path. It stops at invite delivery. Its “lifecycle observer” does not observe lifecycle transitions at all.

## Existing evidence proves only invite preparation

`deployment/v7/receipt.jsonl` contains:

1. `DRY_RUN`
2. `ONE_USE`
3. `PROVIDER_READY`
4. `PREPARED`
5. `RUNTIME_READY`
6. `MEMBERSHIP_READY`
7. `READY`
8. `INVITE_INTENT`
9. `INVITE_RESULT`
10. `READY_CUSTOMER_CLAIM`
11. `CLEAN_ROLLBACK`

It contains no evidence for:

- customer claiming the invite;
- any of the 22 answers;
- reconciliation;
- customer attestation;
- owner review;
- finalization;
- activation;
- check-in;
- generation;
- approval;
- capability issuance;
- customer send;
- screenshot;
- successful retained cleanup.

The final rollback reports:

```text
reason = "transient gateway/observer already active"
status = BLOCKED_CLEAN_ROLLBACK
```

That means even cleanup was not established by a clean terminal proof.

# Observer defect

`lifecycle_observer_v7.py` declares states:

```text
membership_hash_chain
polling_receipts
onboarding
checkin
generation
approval_card
delivery_capability
delivery
cleanup
```

But in `observe` mode it only:

1. creates an events file;
2. writes one `SUBSCRIBED` event;
3. writes `observer-ready.json`;
4. sleeps for six hours;
5. exits blocked on timeout.

It never reads:

- Telegram polling receipts;
- onboarding workflow;
- reconciliation records;
- registry or activation journal;
- check-in events;
- generation ledger;
- approved-card projection;
- capability ledger;
- transport receipts;
- screenshots;
- cleanup state.

The state list is therefore descriptive metadata, not evidence.

# Required one-pass golden path

The next rehearsal must use a synthetic customer and the exact installed candidate, not preview. It must subscribe before the invite and produce one append-only, digest-linked evidence chain.

## 1. Invite and claim

Required proof:

- exact candidate, wheel, interpreter, config, registry and membership digests;
- one `INVITE_INTENT` followed by one terminal `INVITE_RESULT`;
- invite message ID, destination digest and bootstrap session/generation;
- customer presses Start from the bound private route;
- bootstrap state changes exactly once;
- stale, duplicated and wrong-actor Start actions mutate nothing.

The current launcher proves invite send only, not claim.

## 2. All 22 production answers

Exercise the actual chain:

```text
Telegram message
 -> runtime collection
 -> parse_answer
 -> NutritionOnboardingService.submit_answer
 -> durable workflow
 -> next production publication
```

Use exactly one accepted response for each field:

1. `date_of_birth`
2. `equation_sex_basis`
3. `height_cm`
4. `weight_kg`
5. `activity_category`
6. `activity_rationale`
7. `goal_type`
8. `target_weight_kg`
9. `target_date`
10. `allergies`
11. `intolerances`
12. `religious_ethical_exclusions`
13. `disliked_foods`
14. `dietary_preferences`
15. `conditions`
16. `medications`
17. `pregnancy_breastfeeding`
18. `eating_disorder_risk`
19. `cooking_access`
20. `budget_band`
21. `meal_count`
22. `schedule_constraints`

To exercise v7 specifically, the equation response should be a supported natural form such as `넵 남성입니다`, followed by proof that the stored answer is canonical `male`.

Required evidence per field:

- field index and canonical field name;
- inbound Telegram update/message ID and route digest;
- pre/post cursor;
- consumed-update proof;
- canonical answer-set digest after mutation;
- publication generation and message ID for the next field;
- no raw answer in observer output;
- previous question retired;
- no duplicate publication for replayed updates.

After field 22:

- cursor exactly 22;
- state exactly `customer_attestation`;
- all canonical fields present once;
- no unknown or extra field;
- answer digest independently reproduced.

## 3. Reconciliation

Required proof:

- model invocation count exactly one for the current answer digest;
- persisted reconciliation digest bound to that digest;
- deterministic policy issue set independently recomputed;
- no clarification unless that policy set is nonempty;
- published content derived from the persisted record, not the transient model object;
- production output contains no `[PREVIEW]` marker;
- preview session and preview in-memory state are absent.

For a valid golden fixture, expected result is:

```text
state = resolved
clarifications = []
current_index = 0
```

The current v7 patch cannot guarantee this across all fields.

## 4. Customer attestation

Required proof:

- card generation, body digest, message ID and callback token bind to the resolved reconciliation;
- exact customer identity/chat/topic membership;
- one `attest` callback;
- callback update consumed once;
- `require_resolved_reconciliation` succeeds against the current answer digest;
- baseline candidate includes the same reconciliation digest;
- customer receives confirmation;
- state becomes `owner_review` or a deterministically justified `safety_hold`;
- no activation occurs yet.

Negative controls:

- stale attestation card;
- wrong actor;
- duplicate callback;
- changed answers after reconciliation;
- unresolved reconciliation;
- provider/model unavailable.

All must fail without owner publication or activation.

## 5. Owner review and finalization

Required proof:

- owner card is sent only to the canonical owner route;
- its generation, message ID, body digest and callback data are persisted;
- customer answer values are minimized appropriately;
- exact owner presses `owner_ok`;
- owner callback receipt is persisted before the irreversible handoff;
- finalization consumes the same baseline and reconciliation digests;
- readiness documents and pointer agree;
- customer remains disabled until activation;
- final ready card is owner-only.

Crash seams to exercise:

1. after owner callback receipt, before `review_as_owner`;
2. after `review_as_owner`, before finalization;
3. after finalization artifacts, before publication;
4. after publication provider receipt, before profile commit.

A restart must converge without a second owner action or duplicate card.

## 6. Activation

Required proof:

- readiness audit passes immediately before activation;
- registry authority, consent and membership evidence are current;
- activation journal transitions once;
- disabled registry entry becomes enabled once;
- bootstrap session becomes activated once;
- activation receipt binds readiness, registry and membership digests;
- stale or duplicate activation is idempotent;
- no delivery capability exists yet;
- delivery flags remain non-authorizing.

The current v7 launcher does not invoke or observe activation.

## 7. First production check-in

Required proof:

- message belongs to the newly activated customer route;
- canonical check-in event is appended once;
- finalized event ID and check-in revision are independently derived;
- safety policy runs before generation;
- duplicate/replayed check-in cannot create a second finalized event;
- generated draft binds the exact check-in event ID and revision.

Include one stale-answer edit or duplicate update as a negative control without changing the golden event.

## 8. Draft generation and owner approval

Required proof:

- one generation intent;
- one model attempt;
- generation lineage from pending to draft-created;
- exact check-in revision pin;
- owner card displays the same proposed targets and customer text as the canonical draft;
- owner card message ID, route, nonce and payload digest are captured;
- approval creates one immutable approved event/revision;
- customer has received nothing.

Important seam:

`telegram.py` catches failures from `persist_approved_delivery_card()` after editing the Telegram card. The owner can see an approved/send-capable card even when the durable approved-card projection failed. The rehearsal must fault this write and prove send/capability issuance remains impossible.

## 9. One-use capability

Required proof:

- exactly one `awaiting_capability` row exists;
- capability issuance is a separate operator action;
- all launch, customer route, owner route, draft, generation, check-in, revision, approved-event, payload and card pins match;
- expiry is exactly bounded;
- provider and Telegram actions remain zero during issuance;
- wrong, stale, expired or second capability issuance is rejected.

The controller globally requires exactly one awaiting row. Test a stray second row: issuance must block rather than target the wrong customer.

## 10. Customer send

Required proof sequence:

1. owner presses Send on the exact persisted card;
2. capability is validated;
3. capability is durably marked consumed;
4. delivery state becomes `unknown_provider_outcome`;
5. only then is Telegram transport attempted;
6. provider message ID is persisted;
7. delivery becomes delivered and then `sent_audited`;
8. customer receives one message whose body digest matches the approved payload.

Adversarial cases:

- duplicate Send callback;
- stale card message ID or nonce;
- changed owner/customer route;
- expired capability;
- changed draft/check-in revision;
- crash after capability consumption but before Telegram call;
- Telegram timeout after possible send;
- receipt-write failure after confirmed send;
- process restart in every intermediate state.

No ambiguous provider outcome may be retried automatically.

## 11. Screenshot

A screenshot is human-facing corroboration, not delivery authority.

Required evidence:

- screenshot captured from the bound customer DM after the audited provider message ID exists;
- image SHA-256;
- capture timestamp;
- expected Telegram message ID and approved body digest;
- visual assertion that exactly one delivered card is shown;
- no preview banner;
- no other customer data or credentials;
- private retention path and mode.

The observer must not treat screenshot presence as proof of send. The durable transport receipt remains authoritative.

## 12. Cleanup

Required terminal proof:

- retention interval completed or explicitly waived by a recorded authority;
- screenshot archived or deleted according to policy;
- transient gateway and observer stopped;
- no active invite, onboarding workflow, preview session or capability;
- no unreceipted publication;
- no `unknown_provider_outcome`;
- no duplicate customer delivery;
- registry and activation artifacts retained or removed according to the declared test-customer policy;
- temporary customer data removed through the supported cleanup path;
- config flags restored unchanged;
- baseline authority inventory compared before/after;
- service state `inactive/dead`;
- a final append-only `CLEANUP_COMPLETE`, not `CLEAN_ROLLBACK`.

# Preview-versus-production seams

The golden path must explicitly prove preview was not involved.

Preview differs materially:

- consent is hardcoded `True`;
- answers and clarification cursor are only in memory;
- no answers digest;
- no durable reconciliation;
- model clarifications are published directly;
- restart loses the session;
- final rerun suppresses further clarifications;
- publications are marked `[PREVIEW][NUTRITION ONBOARDING]`.

Required negative test:

Run the same 22 synthetic answers through preview and production with a malicious reconciler that requests a question for every field. Production must publish only deterministically proven issues; preview must either use the same policy or be disabled. Current code fails this case.

# Other seams existing tests miss

1. **Transient versus persisted clarification mismatch**
   Filtering a clarification in the profile service does not replace the gateway’s in-memory model object before first publication.

2. **Static sex phrase coverage**
   Tests prove selected phrases, not the actual full Telegram parser/service/persistence route.

3. **Blind reconciliation advancement**
   Except for sex, clarification replies are not shown to resolve their issue.

4. **Owner Revise always selects field 1**
   `op_rev` hardcodes `QUESTION_FIELDS[0]`, regardless of what the owner intended to revise.

5. **Safety handoff**
   A golden fixture with no safety signal does not prove conditions, medication, pregnancy or eating-risk disclosures cannot be cleared by advisory output.

6. **Attestation fallback card**
   Model/reconciliation errors can leave the generic attestation UI visible even though enforced attestation will reject.

7. **Approved-card projection failure**
   Telegram edit succeeds while persistence failure is only logged.

8. **Capability issuance races**
   Approval state can change between card persistence and external controller issuance.

9. **Cross-suite mocks hide installed-package seams**
   Existing tests independently mock reconciliation, owner cards, activation and delivery. They do not prove the same installed candidate carries one digest chain across all phases.

10. **Observer is non-observing**
    Declared states and handoffs are never validated.

11. **Cleanup self-conflict**
    The current rollback calls `baseline()` before transient services are stopped or recognized as stopped, producing `transient gateway/observer already active`.

# Minimum evidence acceptance gate

Before another invite, require one machine-verifiable bundle containing:

- exact candidate and installed-origin proof;
- observer subscription receipt;
- 22 ordered redacted ingress receipts;
- answer digest;
- deterministic reconciliation issue proof;
- reconciliation digest;
- customer attestation receipt;
- baseline/readiness digests;
- owner review receipt;
- activation receipt and registry digest;
- finalized check-in event/revision;
- generation lineage;
- approved-card receipt;
- capability issuance and consumption receipts;
- Telegram transport receipt;
- `sent_audited` terminal row;
- screenshot hash and message-ID binding;
- cleanup inventory and `CLEANUP_COMPLETE`;
- adversarial results for stale, duplicate, wrong-route, crash and preview cases.

Until that bundle exists, the current `8191 passed`, `705 passed`, `51 passed`, and `55 passed` receipts demonstrate broad unit/integration health but do not demonstrate the requested one-pass live-safe journey.