# CI Issues — v5.46.2 release.yaml + ci.yaml run (2026-06-05)

**Source:** `.local-review/ci-test-results.log` (5.9 MB / 30574 lines)
**Investigated:** 2026-06-05 evening

## Summary

| Category        | BLOCKING | WARNING | Total |
|---|---:|---:|---:|
| schema/db       | 3        | 0       | 3     |
| test            | 14       | 0       | 14    |
| env             | 5        | 0       | 5     |
| dependency      | 1        | 3       | 4     |
| build           | 1        | 0       | 1     |
| **TOTAL**       | **24**   | **3**   | **27**|

**Overall run:** 141 failed, 3548 passed, 15 skipped, 22 warnings, 25 errors, 332 rerun — 74 min 17 sec (4 parallel workers, Python 3.14.4)

Note: SBOM/cyclonedx issue mentioned in caller context does **not** appear in this log; it may be from a separate CI job or pre-run step not captured here.

---

## BLOCKING issues (fix in v5.46.3)

### B1. `directory_context` schema constraint: NOT NULL on all wiki_page inserts

**Category:** schema/db
**Job:** test (pytest, multiple test files)
**Symptom:** `SurrealDB error: Couldn't coerce value for field 'directory_context' of 'wiki_page:…': Expected 'string' but found 'NONE'` — fires on every wiki_page insert in ~30 tests
**Log:** lines 89–532, 26730–27970, 29111–29141, 30331–30332, 30534–30545
**Root cause:** A new `directory_context` NOT NULL + length constraint (migration 018) was applied to the schema, but test fixtures that create `wiki_page` records do not populate `directory_context`. The queue drainer also enforces `missing_directory` validation and DLQs all wiki_add payloads lacking the field (confirmed: lines 21063, 21119+).
**Affected tests (selected):**
- All 7 `test_wiki_cleanup_merged_branches.py` tests
- All 5 `test_wiki_read_resolution.py` tests
- `test_branch_schema_migration.py` (2 tests)
- `test_queue_drainer_validation.py` (3 tests — queue rejects wiki_add with `missing_directory`)
- `test_anchor_surfacing.py::test_empty_string_directory_context_treated_as_global` (empty string also rejected)
- Several `test_export_duckdb.py` setup errors (25 total errors — all `directory_context` or vector dim)
**Fix:** Two-pronged:
1. Update all test fixtures that create `wiki_page` records to supply a non-empty `directory_context` string.
2. Update queue drainer validation to supply a sentinel/default for test contexts, OR update test helpers to include `directory_context` in wiki_add payloads. Check `yadgar/tests/` fixtures and `conftest.py` for any shared `wiki_page` factories.
**Bundle with v5.46.3?** YES

---

### B2. `missing_branch` validation blocks `memorize` in CI — no git context in runner

**Category:** env / schema
**Job:** test (pytest, multiple test files)
**Symptom:** Every `memorize` call returns `{'error': 'missing_branch', 'stored': False, …}` — test env has no git branch detectable by the daemon. Cascades into ~20+ test failures across frontier_integration, memory_behavior, secret_gate, predictive_coding, session_context, subagent_stop, consolidate tests.
**Log:** lines 947–977, 12733, 13895, 22278, 30366–30415, 30420–30430, 30447, 30455–30465
**Root cause:** CI runner checks out code to `/data/workdir/***/yadgar/` (anonymised path). The yadgar daemon cannot detect a git branch from the test process's working directory — either the git repo is not accessible from the daemon's perspective, or the daemon's git-detection logic doesn't cover the anonymised temp path. Tests that call `memorize()` without explicit `branch_hint=` fail at the daemon's branch-context gate.
**Affected tests (selected):**
- `test_frontier_integration.py` (6 tests — KeyError 'id' from missing_branch rejection)
- `test_memory_behavior.py` (15 tests — all KeyError 'id')
- `test_integration.py::TestServerStartupShutdown::test_clean_startup_and_shutdown`
- `test_predictive_coding.py` (2 tests)
- `test_secret_gate_plumbing.py::test_memorize_allowlisted_tag_succeeds`
- `test_session_end_capture.py` (5 tests — sentinel import blocked by missing_branch)
- `test_subagent_stop_hook.py::test_endpoint_stores_findings_with_provenance` (assert 0 == 2)
**Fix:** CI workflow must either: (a) set `YADGAR_CI_BRANCH` env var (or equivalent) before running tests so the daemon can detect branch context, OR (b) all test fixtures that invoke `memorize` must pass `branch_hint="master"` or `_internal=True`. Recommend option (a) as a one-line CI env var addition; (b) requires touching every test.
**Bundle with v5.46.3?** YES

---

### B3. `hook_db_lockdown_check` not exported from `yadgar.scripts.hook_runner`

**Category:** build / dependency
**Job:** test (`test_hook_runner_pretooluse_schema.py`)
**Symptom:** `ImportError: cannot import name 'hook_db_lockdown_check' from 'yadgar.scripts.hook_runner'` — 4 tests fail
**Log:** lines 1010–1041, 30381–30385
**Root cause:** Tests in `test_hook_runner_pretooluse_schema.py` import `hook_db_lockdown_check` which does not exist in the current `hook_runner.py`. Either the function was renamed, removed, or the tests were written against a planned API not yet implemented.
**Fix:** Add `hook_db_lockdown_check` to `yadgar/scripts/hook_runner.py`, OR update the tests to import the current function name. Check git log on `hook_runner.py` to determine if it was renamed.
**Bundle with v5.46.3?** YES

---

### B4. Hook files missing from repo: `session-start-context.py`, `stop-memory-checkpoint.py`

**Category:** env / build
**Job:** test (`test_v565_checkpoint_scoping.py`)
**Symptom:** `FileNotFoundError: /home/max/git/yadgar/yadgar/hooks/session-start-context.py` and `stop-memory-checkpoint.py` — 3 tests fail
**Log:** lines 26100–26114, 30469–30474
**Root cause:** Tests reference hook scripts that are expected at `yadgar/hooks/session-start-context.py` and `yadgar/hooks/stop-memory-checkpoint.py` but these files do not exist in the repo at v5.46.2. The tests were written for a planned hook deployment not yet landed.
**Fix:** Create the missing hook files at `yadgar/hooks/session-start-context.py` and `yadgar/hooks/stop-memory-checkpoint.py`, OR update the tests to point to their actual installed paths. Cross-reference `PLAN_V5_65_FRESH_MEMORY_ACCESS.md` for planned hook locations.
**Bundle with v5.46.3?** YES

---

### B5. `/hooks/session-context` endpoint returns 404 — route not registered

**Category:** test
**Job:** test (`test_session_context_endpoint.py`, `test_v579_smart_sessionstart.py`)
**Symptom:** `assert 404 == 200` — GET `/hooks/session-context` returns 404 in all 4 affected tests
**Log:** lines 22282–22329, 26115–26141, 30451–30453, 30476–30478
**Root cause:** The HTTP server does not have a route registered for `GET /hooks/session-context`. Tests in `test_session_context_endpoint.py` and `test_v579_smart_sessionstart.py` were written against a planned endpoint not yet implemented.
**Fix:** Register the `/hooks/session-context` route in `yadgar/server/http.py` (or wherever routes are declared). The endpoint should return project-brief-style session context as a text/JSON response.
**Bundle with v5.46.3?** YES

---

### B6. `make` not available in CI runner — 37 Makefile tests fail

**Category:** env
**Job:** test (all `test_v5_45_*` and `test_v5_46_2_makefile_*` test files)
**Symptom:** `FileNotFoundError: [Errno 2] No such file or directory: 'make'` — 37 tests across 5 test files
**Log:** lines 26170–26500+, 30480–30516
**Affected test files:**
- `test_v5_45_1_makefile_route.py` (2 tests)
- `test_v5_45_install_subagents_config_sync.py` (4 tests)
- `test_v5_45_makefile_extended.py` (11 tests)
- `test_v5_45_makefile_targets.py` (10 tests)
- `test_v5_46_2_makefile_install_runtime.py` (6 tests)
**Root cause:** CI Docker/container image lacks `make`. All Makefile integration tests invoke `make` as a subprocess.
**Fix:** Add `make` to the CI container image (`Dockerfile` or CI workflow `apt-get install make`), OR add a `pytest.importorskip` / `pytest.mark.skipif(shutil.which("make") is None, ...)` guard to all Makefile tests. The latter is safer for portability.
**Bundle with v5.46.3?** YES

---

### B7. `pytest-asyncio` not installed — 3 async tests fail

**Category:** dependency
**Job:** test (`test_loop_heartbeats.py`, `test_viz_daemon_health.py`)
**Symptom:** `Failed: async def functions are not natively supported` + `PytestUnknownMarkWarning: Unknown pytest.mark.asyncio` — 3 tests fail
**Log:** lines 1079–1082, 26692–26703, 30388, 30517, 30524; warnings at lines 30300–30312
**Root cause:** `pytest-asyncio` is not installed in the CI test environment. The package is listed as a suggestion (`anyio`, `pytest-asyncio` shown in warning message) but not in the active test dependencies.
**Fix:** Add `pytest-asyncio` (and `anyio` if needed) to the test extras in `pyproject.toml` under `[project.optional-dependencies]` test group, or to `requirements-test.txt`. Also add `asyncio_mode = "auto"` to `pytest.ini` / `pyproject.toml` `[tool.pytest.ini_options]` to avoid per-decorator mark noise.
**Bundle with v5.46.3?** YES

---

### B8. Incorrect vector dimension error — test fixtures use dim=4 instead of 384

**Category:** schema/db
**Job:** test (`test_export_duckdb.py` setup, 25 errors)
**Symptom:** `SurrealDB error: Incorrect vector dimension (4). Expected a vector of 384 dimension.` — fires in test setup fixtures for the duckdb export tests (25 setup errors total)
**Log:** lines 70–531 (alternating with B1 errors in setup blocks)
**Root cause:** Some test fixtures seed memory records with a placeholder 4-dimensional embedding vector instead of the required 384-dim sentence-transformer output. This was presumably acceptable before a schema migration enforced the exact vector dimension.
**Fix:** Update `seeded_storage` fixture in `yadgar/tests/test_export_duckdb.py` (and any shared conftest) to use correctly-sized 384-dim zero vectors or mock embeddings. E.g. `[0.0] * 384`.
**Bundle with v5.46.3?** YES

---

### B9. Token budget regression — signals payload 1-7 tokens over budget

**Category:** test
**Job:** test (`test_active_work_warn_tier.py`, `test_anchor_hygiene_signals.py`)
**Symptom:** `signals mode empty dir too large: 102 tokens (budget: 100)`, `signals too large (empty): 101 tokens`, `signals too large with K=3 cap: 167 tokens (budget: 160)`
**Log:** lines 30322–30328
**Affected tests:** 4 tests (1 + 3)
**Root cause:** A recent addition to the signals payload (likely a new field or slightly longer label) pushed the serialized output 1-7 tokens over the test budget thresholds. The budgets were not updated to reflect the new baseline.
**Fix:** Either reduce the payload (trim field names or values in signals output) to fit within existing budgets, OR raise the budget constants in `test_active_work_warn_tier.py` and `test_anchor_hygiene_signals.py` if the increase is intentional. Determine which new field caused the growth first.
**Bundle with v5.46.3?** YES

---

### B10. `test_harness_hardening` uses hardcoded `/home/max/git/yadgar` path

**Category:** env
**Job:** test (`test_harness_hardening.py::TestGlobalTimeout`)
**Symptom:** `FileNotFoundError: [Errno 2] No such file or directory: '/home/max/git/yadgar'` — 2 tests fail because test code opens `pyproject.toml` from a hardcoded local path
**Log:** lines 992–1025, 30380, 30383
**Root cause:** Tests at `test_harness_hardening.py:468` and `:494` hardcode `/home/max/git/yadgar` instead of deriving the project root dynamically (e.g. `Path(__file__).parents[2]`).
**Fix:** Replace the hardcoded path with a dynamic repo-root calculation. E.g. `REPO_ROOT = Path(__file__).resolve().parents[2]`.
**Bundle with v5.46.3?** YES

---

### B11. Migration test `test_migration_014_is_last` — asserts 014 is last migration but 018 now exists

**Category:** test
**Job:** test (`test_migration_014_wiki_embedding_backfill.py`)
**Symptom:** `assert '018_directory_context_backfill_repair' == '014_wiki_page_embedding_backfill'`
**Log:** lines 12721–12728, 30416–30419
**Root cause:** The test hard-codes that migration 014 must be the last migration in `_MIGRATIONS`. Migrations 015–018 have since been added. The test assertion is stale.
**Fix:** Update `test_migration_014_wiki_embedding_backfill.py` to assert migration 014 exists in `_MIGRATIONS` (not that it's last), or remove the `is_last` assertion entirely. Add a separate test that verifies the *current* last migration is the expected one.
**Bundle with v5.46.3?** YES

---

### B12. `consolidate_now()` response missing `sleep_cycle` key

**Category:** test
**Job:** test (`test_integration.py::TestConsolidateNowWithSleepCycle`)
**Symptom:** `assert 'sleep_cycle' in {'action_memories_created': 0, 'actions_processed': 0, …}` — key absent from consolidation response
**Log:** lines 1060–1064, 30387
**Root cause:** `consolidate_now()` endpoint response schema was changed or the sleep-cycle reporting was removed/renamed. The test expects a `sleep_cycle` key in the consolidation result dict that is no longer emitted.
**Fix:** Either restore the `sleep_cycle` key to the consolidation response (if the feature should still be reported), or update the test to assert on the new response shape. Check `yadgar/server/` consolidation handler for current response schema.
**Bundle with v5.46.3?** YES

---

### B13. DLQ backoff tests broken by `missing_branch` permanent rejection

**Category:** test
**Job:** test (`test_file_queue_dlq.py`)
**Symptom:** `IndexError: list index out of range` (pending queue empty — item already DLQ'd), `assert 0 == 1` (transient not surviving), `KeyError: filename` (item not in attempts dict) — 4 tests fail
**Log:** lines 856–930, 30358–30365
**Root cause:** DLQ backoff tests inject `memorize`/`wiki_add` payloads and expect them to stay in the pending queue for multiple retry cycles. The `missing_branch` and `missing_directory` permanent rejections cause items to skip the retry queue entirely and go straight to DLQ, breaking assumptions about pending-queue state. The tests do not set up valid branch/directory context.
**Fix:** DLQ test fixtures must supply a valid `branch_hint` and `directory_context` in their test payloads, OR the tests must mock the validation layer to inject a pre-specified transient error rather than relying on a real rejection reason.
**Bundle with v5.46.3?** YES

---

### B14. Circuit breaker: breaker does not re-open after failed probe in backoff window

**Category:** test
**Job:** test (`test_circuit_breaker.py::TestBreakerReopensOnProbeFailure`)
**Symptom:** `AssertionError: breaker should be OPEN again after failed probe` — breaker is CLOSED when expected OPEN
**Log:** lines 688–714, 30333
**Root cause:** After a failed probe in the backoff window, the circuit breaker logs `probe failed (backoff)` and increases cooldown to 120s, but the `is_open()` predicate returns `False`. The breaker's probe-failure re-open logic may have a bug: it emits the log and updates cooldown but does not set its internal state back to OPEN before the test assertion runs.
**Fix:** Investigate `yadgar/ml_client.py` `_CircuitBreaker.probe()` or equivalent — ensure that after a failed probe in backoff, `self._open = True` (or equivalent state flag) is set before returning. The log line confirms the event fires but the state is not updated.
**Bundle with v5.46.3?** YES

---

### B15. `test_write_default_on_fires_detector` — `detect_contradictions` not called from `curate_on_remember`

**Category:** test
**Job:** test (`test_write_time_contradiction.py`)
**Symptom:** `AssertionError: detect_contradictions was not called from curate_on_remember`
**Log:** lines 30273–30275, 30546
**Root cause:** The write-time contradiction detector (NLI) was toggled OFF or the `curate_on_remember` call path was changed such that it no longer invokes `detect_contradictions` by default. The plan `PLAN_V5_57_58_D2_D3_AB_RESCOPE.md` notes "correct NLI default state (flipped to OFF without benchmark)" — this is the confirmed cause.
**Fix:** Either: (a) restore the NLI default to ON (if benchmark justifies it), OR (b) update `test_write_time_contradiction.py::test_default_on_fires_detector` to match the new default-OFF behavior by setting the relevant env var/config to ON in the test setup. The test name `test_default_on_fires_detector` implies it specifically tests the default-ON case, so option (b) requires renaming or restructuring.
**Bundle with v5.46.3?** YES

---

### B16. `test_viz_config_defaults_search_and_colors` — `/viz/config` returns 404

**Category:** test
**Job:** test (`test_viz_config_endpoint.py`)
**Symptom:** `AssertionError: Expected 200, got 404: Not Found` — GET `/viz/config` not found
**Log:** lines 26708–26713, 30531
**Root cause:** The `/viz/config` HTTP route is not registered or was removed/renamed. The test was written against a planned endpoint.
**Fix:** Register `GET /viz/config` route in `yadgar/server/http.py` returning viz configuration defaults, OR if the route was renamed, update the test URL to match.
**Bundle with v5.46.3?** YES

---

### B17. `test_health_*` — health endpoint returns empty body (JSONDecodeError)

**Category:** test
**Job:** test (`test_transport.py::TestHealthEndpoint`)
**Symptom:** `json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)` — health endpoint returns empty response body
**Log:** lines 26049–26088, 30467–30468
**Root cause:** The test server's `/health` endpoint returns an empty body in this test context. Likely a startup race condition (server not fully initialized when health is polled), or the health handler raises an exception that is swallowed and returns a 200 with empty body.
**Fix:** Add a readiness wait/retry in the test fixture before hitting `/health`, or investigate the health handler for exception swallowing. Check whether the `missing_branch` validation issue (B2) is causing the server startup to fail mid-way, resulting in an empty response.
**Bundle with v5.46.3?** YES

---

### B18. `test_anchor_scope_split_global_list` — no global-scoped anchors returned

**Category:** test
**Job:** test (`test_project_brief.py`)
**Symptom:** `assert False` — no anchor has global scope in the split result
**Log:** lines 15032–15037, 30431
**Root cause:** `project_brief` anchor scope splitting (global vs project) returns no global anchors in this test. Likely caused by the `missing_branch` issue (B2) blocking memory writes — if the test anchors cannot be written, the global list will be empty.
**Fix:** Dependent on B2 fix. If B2 is resolved and this still fails, investigate the anchor scope-split logic in `project_brief` for a regression in global anchor detection.
**Bundle with v5.46.3?** YES (fix after B2)

---

### B19. `test_project_brief_modes` — stale `active_work` / `checkpoint` age logic + missing anchors

**Category:** test
**Job:** test (`test_project_brief_modes.py`)
**Symptom:** `assert 'refresh_active_work' in ['bootstrap_project']` (5 tests), `assert None is not None` (2 tests — age_hours), `assert 0 > 0` (2 tests — anchor count)
**Log:** lines 30433–30439
**Root cause:** Mixed causes: (a) project_brief in signals mode returns only `bootstrap_project` action instead of the expected `refresh_active_work` / `refresh_checkpoint` — likely because active_work and checkpoint records are absent (due to B2 write failures). (b) `active_work_age_hours` and `checkpoint_age_hours` are `None` because no records exist. (c) anchor count is 0 for same reason.
**Fix:** Dependent on B2 fix. Once `memorize` works in CI, these tests should self-correct if the test fixtures seed the required records. If not, the `project_brief_modes` tests need explicit fixture setup for active_work and checkpoint records.
**Bundle with v5.46.3?** YES (after B2)

---

### B20. `test_consolidate_anchor_pass` — `_audit_anchors` sentinel not written after consolidation

**Category:** test
**Job:** test (`test_consolidate_anchor_pass.py`)
**Symptom:** `AssertionError: _audit_anchors sentinel must be written after consolidate_now() when ANCHOR_AUDIT_CONSOLIDATION_ENABLED=true` — 2 tests
**Log:** lines 724, 30337–30339
**Root cause:** Consolidation sentinel write goes through `memorize`, which is blocked by `missing_branch` (B2). The anchor audit pass runs but cannot persist its sentinel.
**Fix:** Dependent on B2 fix. After B2, verify `consolidate_now()` correctly writes the `_audit_anchors` sentinel.
**Bundle with v5.46.3?** YES (after B2)

---

### B21. `test_consolidation_drainer_metrics` — drain stage metrics not incrementing

**Category:** test
**Job:** test (`test_consolidation_drainer_metrics.py`)
**Symptom:** `yadgar_drain_stage_ms{stage=archive} sum did not increase` and `{stage=insert} sum did not increase` — Prometheus drain stage metrics unchanged after drain
**Log:** lines 757–785, 30342–30344
**Root cause:** The drain itself may be failing silently (no items processed due to `missing_branch`/`missing_directory` DLQ rejections) so the stage timers never fire, leaving Prometheus counters at baseline.
**Fix:** Dependent on B1+B2 fix. After those, recheck. If still failing, investigate whether the stage timer instrumentation (histogram observation) is correctly wired to the drainer's actual execution path.
**Bundle with v5.46.3?** YES (after B1+B2)

---

### B22. `TestDbsizeCache` — `os.walk` call counter always 0

**Category:** test
**Job:** test (`test_embed_service_v530.py`)
**Symptom:** `expected 1 os.walk call, got 0` (all 4 dbsize cache tests)
**Log:** lines 805–830, 30346–30355
**Root cause:** The test patches `os.walk` but the production code path under test no longer calls `os.walk` directly — it may have been replaced by a different filesystem API or the `get_db_size` function was refactored to not use `os.walk`. The mock interception never fires.
**Fix:** Investigate `yadgar/embed_service.py` (or wherever dbsize is computed) to find the current implementation. Update the test to mock the actual function called, OR restore the `os.walk`-based implementation if it was accidentally removed.
**Bundle with v5.46.3?** YES

---

## WARNING issues (defer or fold opportunistically)

### W1. `starlette.testclient` deprecation — use `httpx2`

**Category:** dependency
**Severity:** WARNING
**Job:** test (`test_admin_config.py`)
**Symptom:** `StarletteDeprecationWarning: Using 'httpx' with 'starlette.testclient' is deprecated; install 'httpx2' instead.` — 4 occurrences
**Log:** line 30288
**Root cause:** Starlette has deprecated `httpx` in favour of `httpx2` for `TestClient`.
**Fix:** Add `httpx2` to test dependencies in `pyproject.toml` and update imports in `test_admin_config.py`.
**Bundle with v5.46.3?** DEFER

---

### W2. Starlette async generator lifespan deprecation

**Category:** dependency
**Severity:** WARNING
**Job:** test (starlette routing)
**Symptom:** `DeprecationWarning: async generator function lifespans are deprecated, use an @contextlib.asynccontextmanager function instead`
**Log:** line 30294
**Root cause:** The ASGI lifespan handler in `yadgar/server/` uses the deprecated async-generator pattern.
**Fix:** Convert lifespan handler to use `@contextlib.asynccontextmanager` pattern per Starlette docs.
**Bundle with v5.46.3?** DEFER

---

### W3. `websockets.legacy` and `WebSocketServerProtocol` deprecation

**Category:** dependency
**Severity:** WARNING
**Job:** test (`test_graceful_shutdown.py`)
**Symptom:** `DeprecationWarning: websockets.legacy is deprecated` and `WebSocketServerProtocol is deprecated`
**Log:** lines 30315–30318
**Root cause:** `uvicorn` still depends on `websockets.legacy`. Will resolve when uvicorn upgrades its websocket implementation.
**Fix:** Pin newer uvicorn version that uses `websockets` v14+ native protocol, or wait for upstream fix. No code change required in yadgar itself.
**Bundle with v5.46.3?** DEFER

---

## Pytest failure tabulation

| Test file | Count | Primary error |
|---|---:|---|
| `test_memory_behavior.py` | 15 | KeyError: 'id' (missing_branch blocks memorize) |
| `test_v5_45_makefile_targets.py` | 10 | FileNotFoundError: make |
| `test_v5_45_makefile_extended.py` | 11 | FileNotFoundError: make |
| `test_wiki_cleanup_merged_branches.py` | 7 | SurrealDB directory_context NONE |
| `test_frontier_integration.py` | 6 | KeyError: 'id' / missing_branch |
| `test_v5_46_2_makefile_install_runtime.py` | 6 | FileNotFoundError: make |
| `test_wiki_read_resolution.py` | 5 | SurrealDB directory_context NONE |
| `test_session_end_capture.py` | 5 | missing_branch / 404 sentinel import |
| `test_embed_service_v530.py` | 4 | os.walk call count 0 |
| `test_file_queue_dlq.py` | 4 | IndexError / DLQ backoff broken |
| `test_hook_runner_pretooluse_schema.py` | 4 | ImportError: hook_db_lockdown_check |
| `test_anchor_hygiene_signals.py` | 3 | Token budget exceeded |
| `test_v5_45_install_subagents_config_sync.py` | 4 | FileNotFoundError: make |
| `test_v565_checkpoint_scoping.py` | 3 | Hook file missing |
| `test_queue_drainer_validation.py` | 3 | missing_directory DLQ |
| `test_branch_schema_migration.py` | 2 | SurrealDB directory_context NONE |
| `test_predictive_coding.py` | 3 | missing_branch |
| `test_project_brief_modes.py` | 5 | bootstrap_project only / None age |
| `test_v579_smart_sessionstart.py` | 2 | 404 session-context endpoint |
| `test_session_context_endpoint.py` | 2 | 404 session-context endpoint |
| `test_v5_45_1_makefile_route.py` | 2 | FileNotFoundError: make |
| `test_viz_daemon_health.py` | 2 | pytest-asyncio not installed |
| `test_consolidation_drainer_metrics.py` | 2 | Prometheus counters unchanged |
| `test_consolidate_anchor_pass.py` | 2 | _audit_anchors sentinel missing |
| `test_transport.py` | 2 | JSONDecodeError: empty body |
| `test_integration.py` | 2 | KeyError 'id' + sleep_cycle missing |
| `test_harness_hardening.py` | 2 | Hardcoded /home/max path |
| `test_viz_config_endpoint.py` | 1 | 404 /viz/config |
| `test_circuit_breaker.py` | 1 | Breaker not OPEN after failed probe |
| `test_active_work_warn_tier.py` | 1 | Token budget 102>100 |
| `test_anchor_surfacing.py` | 1 | Empty string directory_context rejected |
| `test_migration_014_wiki_embedding_backfill.py` | 1 | 014 asserted as last migration |
| `test_project_brief.py` | 1 | No global anchors |
| `test_write_time_contradiction.py` | 1 | detect_contradictions not called |
| `test_loop_heartbeats.py` | 1 | pytest-asyncio not installed |
| `test_subagent_stop_hook.py` | 1 | assert 0 == 2 (missing_branch) |
| **TOTAL** | **141** | |

---

## Recommended v5.46.3 fix scope

Priority order — fix the root causes first to unblock dependent tests:

1. **B2 (missing_branch in CI)** — highest leverage. Fixing this unblocks ~50+ tests. Add `YADGAR_CI_BRANCH=master` (or equivalent) to CI workflow env before pytest invocation.

2. **B1 (directory_context NOT NULL)** — second highest. Update all test fixtures that create `wiki_page` records to supply `directory_context`. Also update queue drainer test payloads to include `directory_context`. Unblocks ~30 tests and all 25 setup errors.

3. **B8 (vector dimension 4→384)** — fix `seeded_storage` fixture to use 384-dim zero vectors. Unblocks 25 setup errors (overlaps with B1).

4. **B6 (make not in CI)** — add `apt-get install -y make` to CI Dockerfile or workflow, OR add `pytest.mark.skipif(shutil.which("make") is None)` to all Makefile tests. Unblocks 37 tests.

5. **B3 (hook_db_lockdown_check missing)** — add function to `yadgar/scripts/hook_runner.py` or fix test imports. 4 tests.

6. **B4 (hook files missing)** — create `yadgar/hooks/session-start-context.py` and `yadgar/hooks/stop-memory-checkpoint.py`. 3 tests.

7. **B5 + B16 (unregistered endpoints)** — register `/hooks/session-context` and `/viz/config` routes. 6 tests.

8. **B7 (pytest-asyncio missing)** — add to test deps + configure `asyncio_mode`. 3 tests.

9. **B10 (hardcoded path)** — replace `/home/max/git/yadgar` with `Path(__file__).parents[2]`. 2 tests.

10. **B11 (migration 014 is-last assertion)** — update test to not assert 014 is last. 1 test.

11. **B9 (token budget)** — identify new payload field and either trim or raise budget. 4 tests.

12. **B13 (DLQ backoff tests)** — after B1+B2, add branch/directory context to DLQ test fixtures. 4 tests.

13. **B14 (circuit breaker state)** — fix probe-failure state update in `ml_client.py`. 1 test.

14. **B15 (contradiction detector)** — align test with NLI default-OFF or add explicit setup. 1 test.

15. **B12 (sleep_cycle missing)** — restore `sleep_cycle` key to consolidation response or update test. 1 test.

16. **B17 (health endpoint empty body)** — fix server startup race or health handler exception swallowing. 2 tests.

17. **B22 (os.walk count 0)** — find current dbsize implementation, update mock target. 4 tests.

18. **B18–B21** — dependent on B1+B2. Re-evaluate after those land.
