CLI Telemetry Review Fixes Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Close the five validated PR #36 review findings without weakening consent, privacy, run-ledger honesty, or existing CLI behavior.
Architecture: The run ledger will own durable failure classification and expose whether a terminal transition actually changed state. Telemetry will react only to that first transition. Exception events will contain structural data only, and the PostHog adapter will rebuild outbound properties from the exact validated event keys instead of subtracting known SDK fields.
Tech Stack: Python 3.12+, Pydantic models, SQLite run ledger, PostHog Python SDK, pytest, Ruff, uv.
Accepted shape#
# arch: workflows classify · ledger persists · transition reports change
result = runs.store.finish(..., failure_category=category)
if result.changed:
telemetry.capture_lifecycle(result.record, spec)
return result.record
# arch: exception telemetry is structural, never free-form
shape = exception_type + fingerprint + codealmanac_frames
# arch: validated service keys are the entire outbound property surface
allowed = frozenset(event.properties)
posthog.before_send = keep_only(message.properties, allowed)The existing classification sites remain authoritative. Telemetry does not inspect local error prose to guess a category.
Task 1: Integrate current main cleanly#
Files:
- Modify:
pyproject.toml - Regenerate:
uv.lock
- Merge
origin/mainintocodex/cli-telemetry. - Preserve both the main-branch
filelockdependency and the telemetry branchposthog>=7,<8dependency. - Regenerate
uv.lockwithuv lock; do not hand-edit generated package entries. - Run
uv sync --lockedand the focused setup/update tests changed by main.
Task 2: Make exception capture structural and non-throwing#
Files:
- Modify:
src/codealmanac/services/telemetry/service.py - Modify:
src/codealmanac/cli/execution.py - Modify:
src/codealmanac/workflows/run_queue/worker.py - Test:
tests/test_telemetry_service.py - Test:
tests/test_cli_telemetry.py - Test:
tests/test_run_telemetry.py
- Add failing tests proving arbitrary relative names, prompts, provider output, and repository text never enter
$exception_messageor$exception_list. - Add a failing CLI-boundary test whose exception
__str__raises; the original exception must still be re-raised and a structural event must be captured. - Replace free-form exception values with the exception type. Keep the stable fingerprint and bounded CodeAlmanac-only frames.
- Wrap all exception shaping inside the telemetry best-effort boundary so it cannot replace the product exception.
- Remove now-unused path/value redaction inputs and helpers from callers.
- Run the focused telemetry and CLI telemetry tests.
Task 3: Enforce an outbound property allowlist after the SDK#
Files:
- Modify:
src/codealmanac/integrations/telemetry/sender.py - Test:
tests/test_telemetry_sender.py
- Add a failing test that injects an unknown future SDK property in
before_sendand expects it to be removed. - Build the hook from
frozenset(event.properties)afterTelemetryEventvalidation. - Replace the denylist with a function that reconstructs
message["properties"]from only those allowed keys. - Verify command and exception event properties survive unchanged while arbitrary SDK context does not.
Task 4: Persist failure category and expose terminal transition change#
Files:
- Modify:
src/codealmanac/services/runs/models.py - Modify:
src/codealmanac/services/runs/requests.py - Modify:
src/codealmanac/services/runs/transitions.py - Modify:
src/codealmanac/services/runs/store.py - Modify:
src/codealmanac/services/runs/service.py - Modify:
src/codealmanac/services/telemetry/service.py - Test:
tests/test_runs_service.py - Test:
tests/test_run_telemetry.py
- Add failing tests that a failed run persists its controlled category and a second finish cannot rewrite a terminal record or append another terminal status event.
- Add
failure_categorytoRunRecord; preserve compatibility with older failed records that lack it. - Require new failed
FinishRunRequestvalues to carry a category and reject categories for non-failed statuses. - Add
RunFinishResult(record, changed)beside the existing cancellation results. - Make
finish_runreturnchanged=Falsefor every already-terminal record. - Have
RunStore.finishpersist the category and return the neutral result. - Have
RunsServicecapture lifecycle telemetry only when finish/cancellation actually creates the terminal transition. - Make telemetry read
record.failure_category; keepinternal_erroronly as a legacy-record fallback.
Task 5: Prove opt-out cannot be replayed after re-enable#
Files:
- Test:
tests/test_run_telemetry.py
- Add a failing finish test: opt out, finish, re-enable, retry finish, and assert no event.
- Add the same sequence for queued cancellation.
- Add the same sequence for confirmed running cancellation.
- Run the focused run and telemetry tests after transition gating is implemented.
Task 6: Classify returned exit code 130 as interrupted#
Files:
- Modify:
src/codealmanac/cli/execution.py - Test:
tests/test_cli_telemetry.py
- Add a failing test through the real
jobs attachdispatch path where the handler catches Ctrl-C and returns130. - Map returned
130to telemetry outcomeinterrupted; keep other nonzero codes asfailed. - Assert the event keeps command
jobs, actionattach, and exit code130.
Task 7: Update architecture knowledge and verify the PR#
Files:
- Modify:
almanac/architecture/telemetry.md - Modify:
almanac/concepts/run-ledger.md - Modify:
almanac/reference/runs/run-states-and-events.md - Modify:
docs/plans/2026-07-15-cli-telemetry-live-agreement.md
- Document structural exception events, exact outbound allowlisting, persisted failure categories, and first-transition-only capture.
- Run focused red/green tests for each finding.
- Run
uv run pytest,uv run ruff check .,uv run codealmanac validate,git diff --check, anduv build. - Build and install the final wheel in an isolated environment and smoke import/version/help.
- Review the full
origin/main...HEADdiff for privacy and unrelated-file leakage. - Commit the review fixes, push PR #36, and wait for GitHub CI.