Skip to content

arch: B6 refactor — split 4 large modules + 1 route group + 29 regression tests#160

Merged
AVADSA25 merged 2 commits into
mainfrom
b6-architecture-refactor
May 30, 2026
Merged

arch: B6 refactor — split 4 large modules + 1 route group + 29 regression tests#160
AVADSA25 merged 2 commits into
mainfrom
b6-architecture-refactor

Conversation

@AVADSA25
Copy link
Copy Markdown
Owner

Summary

Tackles the deferred B6 architecture refactors from CODEC-FULL-AUDIT-2026-05-30.md. Pytest: 2099 → 2128 (+29 regression tests, 0 regressions).

Phases (4 splits, all back-compat via identity-equal re-exports)

P1 — codec_hooks (1097 LOC) → runtime + trust store

  • New codec_plugin_trust.py (212 LOC) — SHA-256 allowlist data layer. Owns _read_allowlist / _write_allowlist / _file_sha256 / _is_plugin_allowed / _maybe_grandfather_existing_plugins / _emit_plugin_load_blocked / _ALLOWLIST_LOCK + constants + path helpers.
  • codec_hooks keeps the runtime — PluginRegistry, run_with_hooks, HookCtx/HookVeto, 5-event dispatcher, fire_one, run_hook_with_timeout, emit_operation, approve_plugin operator entry.

P2 — codec_dashboard chat helpers → codec_chat_pipeline.py

  • New codec_chat_pipeline.py (~170 LOC) owns _StepBudget, _step_budget_enabled / _step_budget_for_route, _is_conversational. Pure-data + side-effect-light, testable without the FastAPI app.
  • codec_dashboard re-imports for back-compat.
  • chat_completion (608 LOC) stays in codec_dashboard — threads too many implicit module-level state dependencies for a safe one-session move.

P3 — Notification endpoints → routes/notifications.py

  • 4 endpoints moved: GET /api/notifications, GET /api/notifications/count, POST /api/notifications/{id}/read, POST /api/notifications/read-all.
  • All deps already lived in routes/_shared.py — clean extraction.
  • Establishes the pattern for the remaining route groups (/api/qchat, /api/vibe, /api/heartbeat, /api/cortex, /api/audit, /api/observer, /api/prompts).

P4 — Voice filters → codec_voice_filters.py

  • New codec_voice_filters.py (96 LOC) owns NOISE_WORDS, WHISPER_HALLUCINATIONS, is_noise(), is_hallucination(), rms_int16().
  • codec_voice re-exports; _rms delegates to rms_int16.

Deferred (still valuable, sprint-sized)

  • chat_completion (608 LOC) FastAPI handler extraction
  • VoicePipeline class split into VoiceVAD / VoiceTTS / VoiceSkillDispatch
  • Remaining dashboard endpoint groups
  • codec_agents crew-factory → crews/ package

Test plan

  • pytest tests/test_b6_refactor.py29/29 new tests pass (4 phase classes)
  • pytest tests/test_plugin_registry.py — 17/17 pass (3 monkeypatch sites moved to codec_plugin_trust)
  • pytest tests/test_step_budget.py — 16/16 pass (temp_config fixture patches both modules)
  • pytest tests/test_hook_lifecycle.py tests/test_hook_veto.py tests/test_hook_audit_perf.py — 80/80 pass
  • pytest tests/test_voice_pipeline.py — all pass after _rmsrms_int16 delegation
  • pytest tests/ (full) — 2128 passed, 83 skipped, 0 failed in 1m38s
  • Import-identity: codec_hooks._read_allowlist is codec_plugin_trust._read_allowlist (True). Same for _StepBudget, NOISE_WORDS, etc.

After merge

cd ~/codec-repo && git pull
pm2 restart codec-dashboard codec-mcp-http

🤖 Generated with Claude Code

Mikarina13 and others added 2 commits May 30, 2026 10:42
Tackles the deferred B6 items from CODEC-FULL-AUDIT-2026-05-30.md.
Pytest: 2099 → 2128 (+29 regression tests, 0 regressions).

REFACTORS (4 phases, all back-compat via identity-equal re-exports)

P1 / SR-32: codec_hooks (1097 LOC → 879) split into runtime + trust
- New codec_plugin_trust.py (212 LOC) — SHA-256 allowlist data layer.
  Owns: _read_allowlist, _write_allowlist, _file_sha256,
  _is_plugin_allowed, _maybe_grandfather_existing_plugins,
  _emit_plugin_load_blocked, _ALLOWLIST_LOCK, _PLUGINS_DIR_DEFAULT,
  _PLUGINS_ALLOWLIST_DEFAULT, _PLUGIN_FILE_SUFFIX, path helpers.
- codec_hooks keeps the runtime — PluginRegistry, run_with_hooks,
  HookCtx/HookVeto, 5-event dispatcher, _fire_one_* helpers,
  _run_hook_with_timeout, _emit_hook_*, emit_operation_*,
  approve_plugin operator entry. Trust-store names re-imported at
  the top of the file so any external `codec_hooks._read_allowlist`
  etc. import keeps working.
- Test update: 3 of 4 plugin_load_blocked tests now monkeypatch
  codec_plugin_trust._log_event (the new emit home); the 4th
  (plugin_hook_timeout) stays on codec_hooks.

P2 / SR-33: codec_dashboard chat helpers → codec_chat_pipeline
- New codec_chat_pipeline.py (~170 LOC) owns _StepBudget, the 2
  config-reading helpers (_step_budget_enabled, _step_budget_for_route),
  and _is_conversational. Pure-data + side-effect-light — testable
  without standing up the FastAPI app.
- codec_dashboard re-imports for back-compat.
- chat_completion (608 LOC) STAYS in codec_dashboard — it threads too
  many implicit module-level state dependencies for a safe one-session
  move. This phase extracts the testable building blocks; the handler
  itself is a separate sprint.
- Test update: test_step_budget temp_config fixture now monkeypatches
  both codec_dashboard.CONFIG_PATH AND codec_chat_pipeline.CONFIG_PATH.

P3 / SR-34: notification endpoints → routes/notifications.py
- 4 endpoints moved: GET /api/notifications, GET /api/notifications/count,
  POST /api/notifications/{id}/read, POST /api/notifications/read-all.
- All deps already lived in routes/_shared (_notif_lock,
  _load_notifications, _write_notifications) — clean extraction.
- codec_dashboard now `app.include_router(notifications_router)`.
- Establishes the pattern: future groups (/api/qchat/*, /api/vibe/*,
  /api/heartbeat/*, /api/cortex/*, /api/audit/*, /api/observer/*)
  follow the same recipe one at a time.

P4 / SR-35: voice filters → codec_voice_filters.py
- New codec_voice_filters.py (96 LOC) owns NOISE_WORDS,
  WHISPER_HALLUCINATIONS, is_noise(), is_hallucination(), rms_int16().
  Stateless data + pure functions — tunable + testable without the
  WebSocket pipeline.
- codec_voice re-exports the constants for back-compat; _rms now
  delegates to rms_int16.
- VoicePipeline class slim down deferred — feed_audio reaches into
  too much per-pipeline state for a safe one-session extraction.

DEFERRED (still architecturally valuable, sprint-sized)
- chat_completion (608 LOC) FastAPI handler extraction
- VoicePipeline class split into VoiceVAD / VoiceTTS / VoiceSkillDispatch
- Remaining dashboard endpoint groups (/api/qchat, /api/vibe,
  /api/heartbeat, /api/schedules, /api/cortex, /api/audit, /api/observer,
  /api/prompts, /api/notifications got done in this PR)
- codec_agents crew-factory split into crews/ package

NEW TESTS

- tests/test_b6_refactor.py (29 tests, 4 classes):
  * TestB6P1HooksTrustSplit (6): identity-equal re-exports +
    end-to-end grandfather migration round-trip
  * TestB6P2ChatPipelineSplit (8): identity-equal re-exports +
    _is_conversational behavior matrix + _StepBudget consume/warn
  * TestB6P3NotificationsRoute (1): all 4 endpoints registered in app.routes
  * TestB6P4VoiceFiltersSplit (14): identity-equal re-exports +
    is_noise / is_hallucination / rms_int16 behavior matrix

Full pytest: 2128 passed, 83 skipped (env-dependent), 0 failed (1m38s).
Audit report: ~/codec-audit-reports/CODEC-FULL-AUDIT-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #160 CI smoke ruff gate failed:
- codec_chat_pipeline.py: drop unused `pathlib.Path`
- codec_dashboard.py: drop unused `typing.Optional` + `STEP_BUDGET_EXHAUSTED`
  (moved to codec_chat_pipeline along with _StepBudget)
- codec_hooks.py: add `noqa: F401` to the codec_plugin_trust import — these
  are intentional back-compat re-exports for tests/external callers
- codec_voice.py: add `noqa: F401` to `numpy as np` — still imported for
  back-compat indirection through filters helpers

No behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@AVADSA25 AVADSA25 merged commit d7826e1 into main May 30, 2026
1 check passed
AVADSA25 added a commit that referenced this pull request May 30, 2026
…c_agents 1808→1128 (#161)

* arch: 5 route groups + crews/ split — codec_dashboard 3912→3618, codec_agents 1808→1128

Continuation of B6 architecture refactor (PR #160 deferrals).
Pytest: 2128 → 2148 (+20 regression tests, 0 regressions).

DASHBOARD ROUTE GROUPS (C1..C5)

Followed the routes/notifications.py pattern from PR #160 P3. Each
group is a self-contained APIRouter included in codec_dashboard.

C1 / SR-36: /api/approvals/* → routes/approvals.py
  4 endpoints (list, count, allow, deny). All state already lived in
  routes/_shared (_pending_approvals, _approval_lock,
  _evict_expired_approvals). test_bounded_dicts source-scan updated
  to look at routes/approvals.py.

C2 / SR-37: /api/heartbeat/* → routes/heartbeat.py
  4 endpoints (config GET/PUT, alerts GET/PUT). Pure CONFIG_PATH JSON
  reads, no shared mutex needed.

C3 / SR-38: /api/cortex/* → routes/cortex.py
  4 endpoints (health, skills, logs/{svc}, restart/{svc}). The skills
  endpoint reads from codec_dispatch.registry (A-4 invariant preserved).
  test_skill_loader_unification source-scan updated.

C4 / SR-39: /api/audit, /api/audit/stream, /api/audit/stats → routes/audit.py
  3 read-only endpoints. AUDIT_LOG read from routes/_shared.

C5 / SR-40: /api/observer/buffer → routes/observer.py
  1 debug-only privileged endpoint. Emits OBSERVER_BUFFER_INSPECTED on
  every read (audit trail preserved).

CREWS SPLIT (C6)

C6 / SR-41: 12 crew builders → codec_crews.py
  ~700 LOC of declarative wiring moved out of codec_agents.py.
  codec_agents now imports and re-exports the 12 builders so
  CREW_REGISTRY (which references them by name) and any external
  `from codec_agents import deep_research_crew` import keep working.
  Single-file extraction this time; future per-crew files at
  crews/{name}.py is a separate refactor.

LOC SHIFT

  codec_dashboard.py: 3,912 → 3,618 LOC (-294, -7.5%)
  codec_agents.py:    1,808 → 1,128 LOC (-680, -37.6%)

NEW TESTS

  tests/test_route_extractions.py — 20 tests across 6 phases.
  Each phase pins (a) endpoint registered on the FastAPI app via the
  router include, (b) source-side invariants (e.g. cortex_skills uses
  codec_dispatch.registry; observer emits OBSERVER_BUFFER_INSPECTED).
  Plus a global LOC floor (codec_dashboard.py < 3,700) that tightens
  as future route extractions land.

UPDATED SOURCE-SCAN TESTS

- tests/test_bounded_dicts.py: scans routes/approvals.py for
  `_evict_expired_approvals(` (was: codec_dashboard.py)
- tests/test_skill_loader_unification.py: scans routes/cortex.py for
  `from codec_dispatch import registry` (was: codec_dashboard.py)
- tests/test_security.py: scans codec_crews.py for the 12 crew
  builders + their allowed_tools= invariant (was: codec_agents.py)

Full pytest: 2148 passed, 83 skipped (env-dependent), 0 failed (1m34s).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(lint): ruff F401 / F821 — drop unused imports + add codec_crews logger

PR #161 CI smoke ruff gate failed on 7 unused imports:
- codec_crews.py: typing.List/Optional unused (carried over from copy);
  added missing `log = logging.getLogger("codec_crews")` for the
  function-local log.warning at meeting_summarizer_crew
- codec_dashboard.py: subprocess imported at top-level was shadowed by
  function-local re-imports throughout (screenshot/pbpaste/pbcopy/
  pdftotext/cmd_runner); dropped the unused top-level import
- codec_dashboard.py: AUDIT_LOG no longer needed at module level (audit
  endpoints moved to routes/audit.py in C4)
- codec_dashboard.py: _pending_approvals / _approval_lock /
  _evict_expired_approvals no longer needed at module level (approval
  endpoints moved to routes/approvals.py in C1)

No behavior change. Full pytest still 2148 passed, 0 failed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(codec_crews): add missing logging import + log binding (CI smoke F821)

* fix(tests): test_route_extractions — use REPO-relative paths, not hardcoded /Users/

---------

Co-authored-by: Mickael Farina <farina.mickael@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
AVADSA25 added a commit that referenced this pull request May 30, 2026
* arch: D-series — 5 more route groups extracted, codec_dashboard 3618→2921

Continuation of route extraction (PR #161 deferrals).
Pytest: 2148 → 2172 (+24 regression tests, 0 regressions).

ROUTES EXTRACTED

D1 / SR-42: /api/qchat/*       → routes/qchat.py
  5 endpoints (sessions/{sid}/save/{sid}/search) + QCHAT_DB + qchat_db()
  helper. SQLite WAL + busy_timeout + auto-migration of user_id column.

D2 / SR-43: /api/vibe/*        → routes/vibe.py
  4 endpoints (sessions/session/save/delete) + VIBE_DB + vibe_db() helper.
  Same lifecycle pattern as qchat.

D3 / SR-44: /api/schedules/*   → routes/schedules.py
  6 endpoints including the /run endpoint with its background-thread
  _execute_task() (LLM report → Google Doc → notification flow).
  PUT endpoint uses codec_jsonstore.atomic_write_json (re-audit medium
  fix preserved).

D4 / SR-45: /api/prompts/*     → routes/prompts.py
  3 endpoints + the 3 helper functions (_load_prompt_overrides,
  _save_prompt_overrides, _get_all_prompts). codec_dashboard's
  chat_completion handler now lazy-imports _load_prompt_overrides
  from routes.prompts to avoid a module-load-time cycle.

D5 / SR-46: media endpoints    → routes/media.py
  6 endpoints (webcam x3, screenshot, clipboard x2). Vision-proxy POST
  in webcam_capture added to test_a12_invariant._ALLOWLIST (legitimate
  vision site pending A-11 cleanup).

NOT INCLUDED IN D5
- /api/upload and /api/upload_image (250 LOC with B1 fence markers +
  size cap + path safety) — kept in codec_dashboard.py to keep the
  security-hardening review surface tight. Future PR.

LOC SHIFT

  codec_dashboard.py: 3,618 → 2,921 LOC (-697, -19.3%)
                     (started at 3,912 before PR #160)

NEW TESTS

  tests/test_route_extractions_d.py — 24 tests across 5 phases.
  Each phase parametrizes endpoint-registered checks + source-side
  invariants (db helpers in module, atomic-write in schedules, lazy
  prompt import in dashboard). Global LOC floor tightened to < 3,000.

UPDATED SOURCE-SCAN TESTS

- tests/test_a12_invariant.py: routes/media.py added to allowlist
  (vision POST in webcam_capture, A-11 pending)
- tests/test_dashboard_llm.py: /chat/completions count assertion
  bumped 5 → 4 (vision POST moved out with media)

Full pytest: 2172 passed, 83 skipped (env-dependent), 0 failed (1m24s).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(lint): ruff F401/F821 — drop unused imports + lazy-import qchat_db/vibe_db

PR #162 CI smoke ruff gate failed:
- F401: sqlite3, uuid, pathlib.Path, _notif_lock, _load_notifications,
  _write_notifications, _append_schedule_run_log — all callers moved to
  routes/qchat, routes/vibe, routes/schedules
- F821: 4 call sites in codec_dashboard.py still referenced qchat_db()
  and vibe_db() — added lazy `from routes.qchat import qchat_db` (etc.)
  inside each function body that uses them

No behavior change. Full pytest still 2172 passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Mickael Farina <farina.mickael@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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