# Debug Journal — Physique Coach concurrency guarantees

Started: 2026-07-17T00:00:00+09:00
Goal: Ensure one check-in event and one scheduled intent are recorded per idempotency key under concurrent execution.

## Environment snapshot (Phase 0)

- Runtime: Python >=3.12 projects, Bash activation gates.
- Entry: `pytest` from each workspace; paused activation scripts are executed directly.
- Ports / sockets: no physique-coach gateway, cron, or delivery process is active.
- Git HEAD: workspace is an artifact directory; implementation is isolated under `/home/cube/.hermes/profiles/physique-coach`.
- References read: `runtimes/python.md`, `methodology/00-setup.md`, `methodology/02-investigate.md`.

## Hypotheses

1. [CONFIRMED] `EventStore.record()` performs deduplication before an unlocked append; concurrent identical calls can both observe no duplicate. Distinguishing evidence: 32-process reproduction created two identical lines. If true, fix is: advisory lock.
2. [CONFIRMED] Both activation gates use a check-then-create claim pattern; concurrent invocations can pass the check together. Distinguishing evidence: `if [[ -e "$claim" ]]; then ...; printf > "$claim"` in each script. If true, fix is: atomic claim.
3. [OPEN] The declared check-in CLI dependency is absent from the validation runtime, so a clean-environment run may fail before testing the code. Distinguishing evidence: importing `typer` raises `ModuleNotFoundError` in the Hermes venv. If true, fix is: isolated test install.

## Failed hypothesis round counter

- Round 1: partial. `record()` and gates are fixed, but independent review found that `import_history()` still bypasses the transaction lock and dry-run retained a TOCTOU simulation claim.
- Round 2: pending.

## Artifacts to revert

- [ ] No debugger, port, or temporary runtime artifact is planned. Regression tests and lock helpers are intended product changes.

## Findings

- 2026-07-17: Final QA reported `event_line_count: 2, unique_lines: 1` from 32 concurrent identical check-ins.
- 2026-07-17: Both gate scripts have an unprotected exists-then-write claim sequence.
- 2026-07-17: The declared `typer` dependency is not installed in the Hermes validation environment.
- 2026-07-17: Root cause confirmed. The record path read and deduplicated before it appended, so two processes could both decide an event was absent. The gate scripts used the same check-then-create pattern. Adding an interprocess advisory lock around record dedupe+append and atomic no-clobber file claims removes the competing window; new 32-process regression tests cover both paths.
- 2026-07-17: A fresh temporary project-local virtual environment with declared test extras completed the check-in suite (`13 passed`) and collector suite (`10 passed`), avoiding reliance on the Hermes validation environment.
- 2026-07-17: Independent code review reproduced two distinct `history_imported` events from 32 concurrent identical imports, confirming the same unlocked-transaction mechanism remains in the historical path.
- 2026-07-17: First live profile gateway launch reported `No user allowlists configured` despite profile YAML Telegram allowlist fields. The process is fail-closed (unauthorized users denied). Investigation hypotheses: (H1) this Hermes runtime reads gateway allowlists from profile `.env` variables rather than YAML; (H2) the profile config loader does not consume the documented platform fields at gateway startup; (H3) sanitized launch omitted a required profile configuration selector.
- 2026-07-17: Independent wizard verification reproduced two finalized `morning_checkin` events for the same owner/topic/KST day. H1: the open-session lookup intentionally excludes finalized sessions and start therefore creates a new draft; H2: no finalized-day uniqueness gate exists at final save; H3: calendar and text bounds are deferred to a late Pydantic write rather than parsed at the wizard boundary.

## Final fix

- `workspace/checkin_cli/checkin_cli/store.py`: flock-protected dedupe and append with durable write.
- `scripts/physique-checkin-gate.sh`, `scripts/physique-source-refresh-gate.sh`: atomic no-clobber claims.
- Regression coverage: concurrent check-in test and `activation/tests/test_atomic_gates.sh`.
- Historical imports and baseline imports now use the same transaction lock; dry-run claims are atomic.
- Independent final-quality, scope, goal, context, code, plan, and manual-QA reviews passed. Fresh suites: 21 check-in tests and 15 source collector/runner tests.
