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

**Source:** `.local-review/ci-test-20260605.log` (1.9 MB / 10202 lines)
**Investigated:** 2026-06-06
**Prior catalog:** `docs/CI_ISSUES_2026_06_05.md` (v5.46.2 baseline, 27 issues: 24 BLOCKING, 3 WARNING)

---

## Summary vs prior

| Status | Count | Notes |
|---|---:|---|
| Prior issues resolved (B1–B22, W1–W3) | 17 | B1, B3, B6→partial, B7, B8→mutated, B9, B10, B11, B12→masked, B13, B14, B15, B16, B21, W1–W3 tests absent or passing |
| Prior issues persisted | 9 | B2, B4, B5, B17, B18→partial, B19→partial, B20, B22, B6→re-form |
| New issues (cycle-regression) | 3 | N1 (makefile pre-setup runtime check), N2 (duckdb unique index), N3 (viz env knob) |
| New issues (new test, unimplemented) | 1 | N4 (test_v5_46_4 expects skip mark) |

## Run stats

**65 failed, 3691 passed, 37 skipped, 10 warnings, 3 errors, 136 rerun** — 1:06:09 (4 parallel workers, Python 3.14)

vs prior: 141 failed, 3548 passed, 15 skipped, 25 errors, 22 warnings, 332 rerun — 74 min 17 sec

Net improvement: **−76 failures, +143 passed, +22 skipped, −22 errors** — clear progress but 68 total failures/errors remain.

## Cycle effectiveness verdict

The v5.46.3→v5.46.6 cycle resolved ~65% of prior failures (17/27 prior issues, 76 fewer failures). However, B2 (`missing_branch`) — the single highest-leverage blocker — was **not fixed**, leaving ~20+ tests cascaded on it still failing. Three cycle-regressions introduced. The cycle was partially effective; v5.46.7 must address B2 and the three new regressions as the minimum viable pass.

---

## PERSISTED issues (from prior catalog)

### P1. `missing_branch` blocks `memorize` in CI — no git context in runner (was B2)

**Category:** env
**Severity:** BLOCKING
**Job:** test (pytest — `test_frontier_integration`, `test_memory_behavior`, `test_predictive_coding`, `test_secret_gate_plumbing`, `test_session_end_capture`, `test_subagent_stop_hook`, `test_integration`, `test_consolidate_anchor_pass`, `test_project_brief_modes`)
**Symptom:** Every `memorize` call returns `{'error': 'missing_branch', 'stored': False, …}` — CI runner has no detectable git branch from daemon perspective. ~25 tests still failing on this root cause.
**Log evidence:** Lines 190, 201, 228, 6870, 6889–6912, 6947, 8221–8239, 9909–9956
**Why it persisted:** No `YADGAR_CI_BRANCH` env var was added to the CI workflow and no `branch_hint=` added to test fixtures. `YADGAR_CI_BRANCH` is not present anywhere in the log.
**Re-fix approach:** Add `YADGAR_CI_BRANCH: master` to the pytest step env block in `.forgejo/workflows/ci.yaml`. One-line change. This is the highest-leverage fix — resolves P1 directly and unblocks P3, P4, P5 cascade tests.
**Bundle v5.46.7?** YES — top priority

---

### P2. Hook files missing: `session-start-context.py`, `stop-memory-checkpoint.py` (was B4)

**Category:** env/build
**Severity:** BLOCKING
**Job:** test (`test_v565_checkpoint_scoping.py` — 3 tests)
**Symptom:** `FileNotFoundError: /home/max/git/yadgar/yadgar/hooks/session-start-context.py` and `stop-memory-checkpoint.py`
**Log evidence:** Lines 9975–9980
**Why it persisted:** Hook files were not created in the v5.46.3→v5.46.6 cycle. The path `/home/max/git/yadgar/yadgar/hooks/` is still hardcoded in the tests, and on the CI runner this resolves to the anonymised `/data/workdir/***/yadgar/yadgar/hooks/` equivalent.
**Note:** `test_stop_hook_allows_when_interval_not_reached` (assert 2 == 0) also fails because `stop-memory-checkpoint.py` is missing — the python3 process exits with returncode 2 (file not found) rather than 0.
**Re-fix approach:** Create `yadgar/hooks/session-start-context.py` and `yadgar/hooks/stop-memory-checkpoint.py`. Cross-reference `PLAN_V5_65_FRESH_MEMORY_ACCESS.md`.
**Bundle v5.46.7?** YES

---

### P3. `/hooks/session-context` endpoint returns 404 (was B5)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_session_context_endpoint.py`, `test_v579_smart_sessionstart.py` — 3 tests)
**Symptom:** `assert 404 == 200` — GET `/hooks/session-context` not registered
**Log evidence:** Lines 9563, 9574, 9585, 9600, 9606, 9612, 9960, 9982, 9984
**Why it persisted:** Route was not registered in the HTTP server in this cycle.
**Re-fix approach:** Register `GET /hooks/session-context` route in `yadgar/server/http.py`.
**Bundle v5.46.7?** YES

---

### P4. `test_consolidate_anchor_pass` — `_audit_anchors` sentinel not written (was B20)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_consolidate_anchor_pass.py` — 2 tests)
**Symptom:** `AssertionError: _audit_anchors sentinel must be written after consolidate_now()` and `_audit_anchors must be latest-wins single row`
**Log evidence:** Lines 9890, 9892
**Why it persisted:** Still blocked by P1 (`missing_branch`) — sentinel write goes through `memorize`, which is rejected. Dependent on P1 fix.
**Re-fix approach:** Fix P1 first; re-evaluate.
**Bundle v5.46.7?** YES (after P1)

---

### P5. `test_project_brief_modes` — no global anchors (was B19/B18 partial)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_project_brief_modes.py` — 1 test: `test_restore_mode_global_anchor_has_global_scope`)
**Symptom:** `assert 0 > 0` — no global-scoped anchors returned
**Log evidence:** Line 9954
**Why it persisted:** 4 of the prior 5 `test_project_brief_modes` failures were resolved, but this one survives — anchor count stays 0 because anchor writes go through `memorize` (blocked by P1 / `missing_branch`). Dependent on P1.
**Re-fix approach:** Fix P1 first; re-evaluate.
**Bundle v5.46.7?** YES (after P1)

---

### P6. Health endpoint returns empty body — JSONDecodeError (was B17)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_transport.py::TestSessionManagement::test_session_count_reflected_in_health`)
**Symptom:** `json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)` — health endpoint returns empty response body
**Log evidence:** Line 9974
**Why it persisted:** Root cause not addressed. Possibly a startup race condition in the test fixture or health handler exception swallowing. Not dependent on P1 directly (this is a transport-level issue).
**Re-fix approach:** Add readiness wait/retry in `test_transport.py` fixture before hitting `/health`. Or investigate health handler for exception swallowing returning a 200 with empty body.
**Bundle v5.46.7?** YES

---

### P7. `test_embed_service_v530` — `os.walk` mock intercept never fires (was B22)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_embed_service_v530.py::TestDbsizeCache` — 4 tests)
**Symptom:** `AssertionError: expected 1 os.walk call, got 0` (all 4 dbsize cache tests)
**Log evidence:** Lines 9895, 9898, 9901, 9904
**Why it persisted:** The production `get_db_size` function no longer calls `os.walk` directly. The mock target was not updated in this cycle.
**Re-fix approach:** Find current dbsize implementation in `yadgar/embed_service.py`, update test to mock the actual function called.
**Bundle v5.46.7?** YES

---

### P8. Makefile targets fail — `make` present but `pre-setup` exits with runtime check error (was B6, re-form)

**Category:** env / test
**Severity:** BLOCKING
**Job:** test (`test_v5_45_1_makefile_route.py`, `test_v5_45_makefile_extended.py`, `test_v5_45_makefile_targets.py`, `test_v5_46_2_makefile_install_runtime.py` — 8 tests)
**Symptom:** `make -n setup` fails with exit code 2, not `FileNotFoundError: make`. Makefile `pre-setup` target runs `detect_runtime.sh` which fails: `ERROR: No container runtime (podman or docker) found.` in CI.
**Log evidence:** Lines 9617, 9717, 9817, 9986–10154
**Why it persisted:** B6 was partially fixed — `make` is now installed in the CI image. However the `pre-setup` target introduced a hard container-runtime check that aborts with exit 1 when podman/docker is absent, causing `make -n setup` (dry-run) to fail before producing its target list. The tests cannot observe the setup chain because the dry-run itself fails at preflight.
**Re-fix approach:** Either: (a) add `YADGAR_TEST_SKIP_RUNTIME_CHECK=1` guard in `pre-setup` to bypass runtime detection during dry-run/CI, OR (b) add `pytest.mark.skipif(shutil.which("podman") is None and shutil.which("docker") is None, ...)` to all affected tests, OR (c) remove the runtime-check from `pre-setup` entirely and move it to `install-runtime` target only.
**Bundle v5.46.7?** YES

---

### P9. `test_session_end_capture` — `extract_last_session_findings` not in signals (was B5/B2 cascade)

**Category:** test
**Severity:** BLOCKING
**Job:** test (`test_session_end_capture.py` — 6 tests)
**Symptom:** `AssertionError: Expected extract_last_session_findings in ['bootstrap_project']`, `Sentinel file should be deleted after import`, `assert None is not None`
**Log evidence:** Lines 9962–9972
**Why it persisted:** The `extract_last_session_findings` action is not emitted in signals mode; the session sentinel import is blocked by `missing_branch` (P1). This is a compound failure: (a) `memorize` rejection cascades into sentinel import failure, (b) the `extract_last_session_findings` action is not yet implemented or not wired into the signals output.
**Re-fix approach:** Fix P1 first. If `extract_last_session_findings` still absent after P1, wire it into the signals/project_brief output path.
**Bundle v5.46.7?** YES (after P1)

---

## NEW issues (cycle-regression)

### N1. `test_export_duckdb` — unique index violation on `memory_sim_link_pair_idx` (new error, replaces B8)

**Category:** schema/db — cycle-regression
**Severity:** BLOCKING
**Job:** test (`test_export_duckdb.py` — 3 errors: setup fixtures)
**Symptom:** `RuntimeError: SurrealDB error: Database index memory_sim_link_pair_idx already contains ['memory:1', 'memory:2']` — seeded_storage fixture fails to set up, 3 test errors.
**Log evidence:** Lines 67, 86, 105, 10198–10200
**Root cause hypothesis:** B8 (vector dim 4→384) was fixed by updating the seeded_storage fixture to use 384-dim vectors. However, a new unique index `memory_sim_link_pair_idx` on `memory_similarity_link` was introduced (probably in migration 017 or 018), and the fixture tries to insert duplicate `('memory:1', 'memory:2')` pairs. Either: (a) the fixture inserts the same sim-link twice across test isolation boundaries, or (b) test DB teardown does not clean the index before re-insertion.
**Prior catalog:** B8 was "vector dimension 4 instead of 384" — now that root cause is fixed (no dim errors in log), but the fixture exposed a new DB state issue. Classified as cycle-regression.
**Fix approach:** Ensure `seeded_storage` fixture uses a fresh DB namespace per test (or cleans `memory_similarity_link` between inserts), OR use unique memory IDs per test invocation rather than fixed `memory:1`, `memory:2`.
**Bundle v5.46.7?** YES

---

### N2. `test_viz_daemon_health::test_env_override_propagates` — env knob returns wrong value (new failure)

**Category:** test — cycle-regression
**Severity:** BLOCKING
**Job:** test (`test_viz_daemon_health.py::TestVizHealthRefreshEnvKnob::test_env_override_propagates`)
**Symptom:** `AssertionError: expected [10.0], got [5.0]` — the viz health refresh interval env override produces 5.0 instead of 10.0.
**Log evidence:** Line 10186
**Root cause hypothesis:** v5.46.3→v5.46.6 cycle changed the viz health refresh logic or the env var processing for the refresh interval knob. The test expects that setting the env var to `10` propagates to the internal metric, but the actual value is `5.0` (likely the default). Either: (a) the env var name was changed without updating the test, OR (b) the env var parsing logic was broken, OR (c) the env var is read at startup before the test can set it.
**Prior catalog:** `test_viz_daemon_health` was in B7 (pytest-asyncio not installed) in the prior log. pytest-asyncio is now installed (no `async def functions are not natively supported` errors), so the asyncio issue is resolved, but this test now fails with a functional regression.
**Fix approach:** Check `yadgar/server/viz.py` or wherever the viz health refresh interval is read from env. Verify env var name matches test expectation. If read at startup, move to lazy/per-request eval.
**Bundle v5.46.7?** YES

---

### N3. `test_v5_46_4_fixture_directory_context::test_anchor_surfacing_empty_string_test_is_skipped` — guard test itself fails

**Category:** test — new test with unimplemented guard
**Severity:** BLOCKING
**Job:** test (`test_v5_46_4_fixture_directory_context.py` — 1 test)
**Symptom:** `AssertionError: test_empty_string_directory_context_treated_as_global must be skip-marked — schema now rejects empty string, behavior change deferred to v5.46.6`
**Log evidence:** Line 10184
**Root cause hypothesis:** `test_v5_46_4_fixture_directory_context.py` is a new test file (not in prior catalog) that asserts the old `test_anchor_surfacing.py::test_empty_string_directory_context_treated_as_global` test must carry a `pytest.mark.skip` decorator (since the schema now rejects empty string). The test file checks for this at import time. The old test in `test_anchor_surfacing.py` was presumably not updated with the skip mark during the v5.46.6 cycle.
**Prior catalog:** `test_anchor_surfacing` had 1 failure in B1 (`empty string directory_context also rejected`). B1 appears resolved, but the follow-up guard test was added to enforce the skip mark and now fails because the skip mark was not applied.
**Fix approach:** Add `@pytest.mark.skip(reason="schema rejects empty string; behavior change deferred to v5.46.6")` to `test_anchor_surfacing.py::test_empty_string_directory_context_treated_as_global`.
**Bundle v5.46.7?** YES — trivial one-liner

---

## RESOLVED from prior catalog (confirmed absent in new log)

| Prior ID | Title | Resolved in |
|---|---|---|
| B1 | `directory_context` NOT NULL on wiki_page inserts | v5.46.3–v5.46.6 cycle |
| B3 | `hook_db_lockdown_check` not exported | v5.46.3–v5.46.6 cycle |
| B6 | `make` not in CI (root cause) | Partially resolved — `make` now present, but see P8 |
| B7 | `pytest-asyncio` not installed | v5.46.3–v5.46.6 cycle |
| B8 | Vector dimension 4 instead of 384 | v5.46.3–v5.46.6 cycle (but see N1 mutation) |
| B9 | Token budget 1–7 tokens over | v5.46.3–v5.46.6 cycle |
| B10 | Hardcoded `/home/max/git/yadgar` path | v5.46.3–v5.46.6 cycle |
| B11 | migration 014 `is_last` assertion | v5.46.3–v5.46.6 cycle |
| B12 | `consolidate_now()` missing `sleep_cycle` key | Likely resolved (test_integration's only current failure is `KeyError: 'id'` from P1, not sleep_cycle) |
| B13 | DLQ backoff tests broken by `missing_branch` perm rejection | v5.46.3–v5.46.6 cycle |
| B14 | Circuit breaker not re-open after failed probe | v5.46.3–v5.46.6 cycle |
| B15 | NLI `detect_contradictions` not called from `curate_on_remember` | v5.46.3–v5.46.6 cycle |
| B16 | `/viz/config` returns 404 | v5.46.3–v5.46.6 cycle |
| B21 | Consolidation drainer metrics not incrementing | v5.46.3–v5.46.6 cycle (or now masked by P1 fix) |
| W1 | `starlette.testclient` deprecation | Warning still present but tests pass |
| W2 | Starlette async generator lifespan deprecation | Warning still present but tests pass |
| W3 | `websockets.legacy` deprecation | Warning still present but tests pass |

---

## BLOCKING / WARNING table + fix prioritization for v5.46.7

| ID | Category | Severity | Test count | Tests |
|---|---|---|---:|---|
| P1 | env | BLOCKING | ~20 | test_frontier_integration, test_memory_behavior (15), test_predictive_coding (2), test_secret_gate_plumbing, test_session_end_capture (6), test_subagent_stop_hook, test_integration, test_consolidate_anchor_pass (2), test_project_brief_modes |
| P8 | env/test | BLOCKING | 8 | test_v5_45_* makefile files, test_v5_46_2_makefile |
| P2 | env/build | BLOCKING | 3 | test_v565_checkpoint_scoping |
| P3 | test | BLOCKING | 3 | test_session_context_endpoint, test_v579_smart_sessionstart |
| P9 | test | BLOCKING | 6 | test_session_end_capture |
| P7 | test | BLOCKING | 4 | test_embed_service_v530 |
| P6 | test | BLOCKING | 1 | test_transport |
| N1 | schema/db | BLOCKING | 3 errors | test_export_duckdb |
| N2 | test | BLOCKING | 1 | test_viz_daemon_health |
| N3 | test | BLOCKING | 1 | test_v5_46_4_fixture_directory_context |
| P4 | test | BLOCKING | 2 | test_consolidate_anchor_pass (after P1) |
| P5 | test | BLOCKING | 1 | test_project_brief_modes (after P1) |

**Fix order for v5.46.7:**

1. **P1 (`YADGAR_CI_BRANCH`)** — add one env var to CI workflow. Unblocks ~20 tests + P4 + P5 + P9.
2. **N3 (skip mark)** — trivial one-liner in `test_anchor_surfacing.py`.
3. **P2 (hook files missing)** — create `yadgar/hooks/session-start-context.py` and `stop-memory-checkpoint.py`.
4. **P3 (session-context route)** — register `GET /hooks/session-context` in HTTP server.
5. **P8 (makefile pre-setup runtime check)** — add `YADGAR_TEST_SKIP_RUNTIME_CHECK` guard or skip decorator.
6. **N1 (unique index violation)** — fix `seeded_storage` fixture to avoid duplicate sim-link records.
7. **N2 (viz env knob)** — fix env var name or lazy-eval in viz health refresh.
8. **P7 (os.walk mock)** — update mock target to current dbsize implementation.
9. **P6 (health endpoint empty body)** — add readiness retry in `test_transport` fixture.
10. **P4 + P5** — re-evaluate after P1 fix; likely auto-resolve.

---

## Recommended next action

Fix P1 (one env-var line in CI workflow) + N3 (one `@pytest.mark.skip` line) as v5.46.7 immediately — these are sub-5-minute changes that will drop ~22 failures. Then batch P2 + P3 + P8 + N1 + N2 as a second v5.46.7 commit to close the remaining ~23 failures. If P1 resolves its cascade (P4 + P5 + P9), total expected failures after full v5.46.7 pass: ~15 (P6 + P7 + remaining edge cases).

Do **not** escalate or scope-cut — the failure set is tractable. Continue cycle as v5.46.7.
