Skip to content

feat(shift_report): end-of-day shift report (Phase 2 Step 7)#12

Merged
AVADSA25 merged 1 commit intomainfrom
phase2-step7-shift-report
May 2, 2026
Merged

feat(shift_report): end-of-day shift report (Phase 2 Step 7)#12
AVADSA25 merged 1 commit intomainfrom
phase2-step7-shift-report

Conversation

@AVADSA25
Copy link
Copy Markdown
Owner

@AVADSA25 AVADSA25 commented May 2, 2026

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

Trigger When Detected by
time Wall clock matches config.shift_report.daily_at_hour:daily_at_minute (default 18:00 local) codec-observer daemon loop
idle CGEventSourceSecondsSinceLastEventTypeconfig.shift_report.idle_minutes * 60 (default 30 min) Same
manual User invokes via skill name ("shift report", "what did i do today", "summarize my day") Chat / voice / MCP — bypasses dedup

Per-day dedup at ~/.codec/shift_report_state.json ensures 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

  1. Completed tasks — successful tool_result / crew_complete / hook_fired / trigger_fired counts + most-fired tools
  2. Blocked / stuck momentsstuck_warning / step_budget_exhausted / ask_user_question_timeout / trigger_blocked
  3. Observed work patterns — app time-share from observation_tick METADATA + persisted observer summary count
  4. Pending decisions — open type="question" notifications + unreviewed skill proposals
  5. Tomorrow's open threadscrew_start without matching crew_complete (incomplete operations)

Each section gracefully falls back to a "(no data)" placeholder for quiet days.

Commits

sha what
1601c4d Implementation: audit constants + skills/shift_report.py + codec_observer.py integration + 20 tests + AGENTS.md

(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.py20/20 passing in 0.16s
  • All filesystem paths redirected to tmp_path per test — NO real ~/.codec/* writes
  • All audit emits redirected to tmp_path — NO real audit.log writes
  • State files clean post-test: pending_questions=0, Apple Reminders=0, /tmp/codec_*.txt=0, shift_report_state.json absent
  • Per Step 4 contract: did NOT run full pytest suite

Test breakdown (20)

Area Count What
Assembly 8 Empty inputs / completed / blocked / observed_patterns / pending decisions / proposals / tomorrow / observer summaries
Notification + state 3 post type=shift_report / per-day dedup / manual bypasses dedup
Kill switch + config 3 disabled / default enabled / config overrides honored
Audit emit 3 started+completed share cid / extras carried / kill-switch silent
Observer integration 3 idle path fires / off-window doesn't / dedup honored across paths

§6 audit events (2 new)

Event Source level When
shift_report_started codec-shift-report info Assembly opens — extra.trigger_kind
shift_report_completed codec-shift-report info Assembly closes with summary stats — extra.{trigger_kind, sections_included, word_count, audit_records_scanned, notifications_scanned, observer_summaries_used} + top-level duration_ms

Both share a single correlation_id (multi-emit op per Step 1 §1.4).

What this PR does NOT do

  • No new PM2 service — runs inline in codec-observer's daemon loop
  • No Apple Reminders / Calendar / Notes
  • No _HTTP_BLOCKED change
  • No modification of codec_self_improve.py (Step 4 work, sealed)
  • No auto-fire at merge time — observer fires only when time or idle conditions match (default 18:00 OR 30 min idle)
  • No new PWA endpoint — the existing notifications endpoint surfaces the report; type="shift_report" is the rendering hint

Kill switch

SHIFT_REPORT_ENABLED=false env var blocks all three trigger paths (time / idle / manual). The skill is still discoverable but run() returns the disabled message immediately.

After merge

  1. git pull on main checkout
  2. cp ~/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)
  3. pm2 restart codec-observer — picks up the integration
  4. To test manually: in chat or via MCP, "shift report" → posts a notification (bypasses dedup so you can iterate)
  5. To verify time/idle paths: wait for 18:00 local OR be idle 30 min OR temporarily set daily_at_hour to a near-time value in ~/.codec/config.json:shift_report and pm2 restart codec-observer

Phase 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 to docs/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

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 AVADSA25 merged commit 0e40687 into main May 2, 2026
1 check passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants