feat(shift_report): end-of-day shift report (Phase 2 Step 7)#12
Merged
feat(shift_report): end-of-day shift report (Phase 2 Step 7)#12
Conversation
End-of-day summary of everything CODEC observed and accomplished. Single
notification with type="shift_report" and a 5-section markdown body the
PWA renders inline. Three trigger paths (time / idle / manual) with
per-day dedup. 20 tests, all mocking filesystem (NO real ~/.codec writes).
Components:
codec_audit.py (+25 LOC)
- 2 new event constants: SHIFT_REPORT_STARTED, SHIFT_REPORT_COMPLETED
- PHASE2_STEP7_EVENTS frozenset
- SHIFT_REPORT_EXTRA_FIELDS doc tuple
skills/shift_report.py NEW (~470 LOC)
- SKILL_NAME="shift_report", 8 trigger phrases, MCP-exposed
- Manual entry: run(task) — also handles "[trigger=time]" /
"[trigger=idle]" markers from observer
- Internal entry: run_with_trigger_kind(kind) — used by observer
- 5 section renderers (completed / blocked / observed / pending /
tomorrow), each with a "no data" fallback for empty days
- Inputs: audit.log + rotated logs (24h lookback configurable),
notifications.json, observer_summaries/, skill_proposals/<today>/
- Per-day dedup via ~/.codec/shift_report_state.json (atomic write)
- Optional auto-save to ~/Documents/CODEC Shift Reports/YYYY-MM-DD.md
(config.shift_report.auto_save_path; default null = notification only)
- Kill switch: SHIFT_REPORT_ENABLED env var (default true)
codec_observer.py (+50 LOC)
- importlib.util added at top
- run_daemon() loop calls _maybe_fire_shift_report(idle) after each
poll's long-idle reset check; try/except wraps so shift_report
failures NEVER break observer polling
- _maybe_fire_shift_report() lazy-imports shift_report module, checks
kill switch + per-day dedup + config flag, then matches:
time path: now.hour == daily_at_hour AND now.minute == daily_at_minute
idle path: idle_seconds >= idle_minutes * 60
Whichever matches first fires; per-day dedup suppresses the other.
tests/test_shift_report.py NEW (~440 LOC, 20 tests)
Assembly (8): empty inputs / completed / blocked / observed_patterns /
pending_decisions / proposals / tomorrow / observer_summaries
Notification + state (3): post type=shift_report / dedup / manual bypass
Kill switch + config (3): disabled / default / config overrides
Audit emit (3): started+completed share cid / extras / kill-switch silent
Observer integration (3): idle fires / off-window doesn't / dedup honored
Test isolation: every storage path (audit_log, notifications.json,
observer_summaries/, skill_proposals/, shift_report_state.json,
config.json) redirected to tmp_path. NO real ~/.codec/* writes.
NO Apple Reminders / Calendar / Notes. Audit emits redirected.
AGENTS.md (+45 LOC)
- §3 new "Shift Report (Phase 2 Step 7)" sub-section
- §6 new audit events table
- §10 new don't-touch entries (shift_report_state.json,
SHIFT_REPORT_ENABLED env var, config.shift_report.{...} tunables)
Test result: 20 passed in 0.17s. Side-effects clean:
pending_questions: 0
Apple Reminders: 0
/tmp/codec_*.txt: 0
shift_report_state.json: absent (not written by tests)
Diff inventory: ~+1,030 LOC functional + tests + docs.
Phase 2 status after merge: 3/3 (Steps 5 + 6 + 7 all done). Phase 2
COMPLETE pending this PR + sign-off.
Safety summary (what this PR explicitly does NOT do):
- Does NOT auto-fire at merge time (observer's _maybe_fire_shift_report
only fires when conditions match; with default config that's 18:00
local OR 30 min idle)
- Does NOT touch _HTTP_BLOCKED
- Does NOT create Apple Reminders / Notes / Calendar entries
- Does NOT add a new PM2 service
- Does NOT modify codec_self_improve.py (Step 4 work, sealed)
AVADSA25
pushed a commit
that referenced
this pull request
May 2, 2026
…(Steps 5/6/7) Phase 2 (Observer + Triggers + Shift Report) merged and production-stable: - Step 5 (PR #9 824a52f) + hotfix PR #10 (26e6add) — `observer.ocr_enabled` flag - Step 6 (PR #11 2d2ff3f) — Trigger System (matcher + cooldown + consent) - Step 7 (PR #12 0e40687) — end-of-day shift report Net: +91 passing tests (823/20/73), 0 new failures, 0 new skips. Live audit proof captured: shift_report_started+_completed paired emits at 2026-05-02T18:49:40Z with shared cid=5f188e5485e5.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 Step 7 — final Phase 2 step. End-of-day summary of everything CODEC observed and accomplished. Single PWA notification with
type="shift_report"and a 5-section markdown body the dashboard renders inline. Phase 2 is complete after this lands.Three trigger paths
config.shift_report.daily_at_hour:daily_at_minute(default 18:00 local)codec-observerdaemon loopCGEventSourceSecondsSinceLastEventType≥config.shift_report.idle_minutes * 60(default 30 min)"shift report","what did i do today","summarize my day")Per-day dedup at
~/.codec/shift_report_state.jsonensures time + idle fire at most once per local date; whichever wins suppresses the other. Manual invocations always work (so you can re-summarize any time).5 sections, ~500-1500 words
tool_result/crew_complete/hook_fired/trigger_firedcounts + most-fired toolsstuck_warning/step_budget_exhausted/ask_user_question_timeout/trigger_blockedobservation_tickMETADATA + persisted observer summary counttype="question"notifications + unreviewed skill proposalscrew_startwithout matchingcrew_complete(incomplete operations)Each section gracefully falls back to a "(no data)" placeholder for quiet days.
Commits
1601c4d(Single commit — Step 7 is contained enough that a separate design doc + impl split would be overhead. The blueprint already covered §0-§10 design; this PR description serves as the design summary.)
Test plan
pytest tests/test_shift_report.py→ 20/20 passing in 0.16stmp_pathper test — NO real~/.codec/*writestmp_path— NO real audit.log writespending_questions=0,Apple Reminders=0,/tmp/codec_*.txt=0,shift_report_state.json absentTest breakdown (20)
§6 audit events (2 new)
shift_report_startedcodec-shift-reportextra.trigger_kindshift_report_completedcodec-shift-reportextra.{trigger_kind, sections_included, word_count, audit_records_scanned, notifications_scanned, observer_summaries_used}+ top-levelduration_msBoth share a single
correlation_id(multi-emit op per Step 1 §1.4).What this PR does NOT do
codec-observer's daemon loop_HTTP_BLOCKEDchangecodec_self_improve.py(Step 4 work, sealed)type="shift_report"is the rendering hintKill switch
SHIFT_REPORT_ENABLED=falseenv var blocks all three trigger paths (time / idle / manual). The skill is still discoverable butrun()returns the disabled message immediately.After merge
git pullon main checkoutcp ~/codec-repo/skills/shift_report.py ~/.codec/skills/shift_report.py— copies the skill to the user runtime install (matches Phase 1 Step 4 pattern)pm2 restart codec-observer— picks up the integration"shift report"→ posts a notification (bypasses dedup so you can iterate)daily_at_hourto a near-time value in~/.codec/config.json:shift_reportandpm2 restart codec-observerPhase 2 status after this PR
After merge: Phase 2 COMPLETE. The blueprint's three-step plan delivered. Final write-up will live in
docs/PHASE2-COMPLETE.md(similar todocs/PHASE1-COMPLETE.md) — I'll do that as a follow-up commit on main once you've burned in the shift report.🤖 Generated with Claude Code