The v0.4.3 CLI surface added aliases (cass index, robot docs topics, current
session shorthands, html export, query, search hit source, line_number
drill-down) and the new robot-trace-ingest flag. The supporting shell
scripts get aligned with the canonical names so the e2e and CI scripts
keep matching what the binary actually accepts.
Touches:
- scripts/e2e/connector_stress.sh, query_parser_e2e.sh,
security_paths_e2e.sh, e2e_logging_acceptance_test.sh,
full_coverage_validation.sh: invoke the new alias-free canonical CLI
forms and consume the v0.4.3 robot envelope fields.
- scripts/test-all.sh, scripts/test-pages-e2e.sh, scripts/tests/run_all.sh:
pick up the new test files (tests/cli_index.rs, doctor_e2e_runner.rs,
e2e_search_index.rs, e2e_tui_smoke_flows.rs, metamorphic_introspect_schema.rs)
in their shard plans.
- scripts/validate-e2e-jsonl.sh: handles the new ingest-trace NDJSON shape.
- scripts/validate_ci.sh: minor flag-set update to match the v0.4.3 CLI.
No production behaviour change here — these are CI/test plumbing edits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This lands the long-running "doctor v2" effort (tracked in
docs/planning/DOCTOR_V2_RELEASE_CHECKLIST.md). It reshapes `cass doctor`
from a flat "check + auto-fix" command into a typed surface of bounded
operations whose recovery model is **archive-first**: cass-owned evidence
(archives, raw-session mirrors, backups, receipts, source ledgers) is
preserved before any repair runs, and every mutating action goes through a
plan-fingerprint candidate flow that the operator must inspect and approve.
==== New / changed doctor surfaces (`src/doctor.rs`) ====
DoctorCommandSurface now enumerates: Diagnose, Check, Repair, Cleanup,
**ArchiveScan**, **ArchiveNormalize**, Backups, Reconstruct, Restore,
SafeAutoFix, Init.
DoctorExecutionMode adds the corresponding `*DryRun` / `*Apply` pairs for
ArchiveNormalize and tightens the existing Cleanup/Repair modes around
plan-fingerprint matching. DoctorCommandRequest grows `archive_scan` and
`archive_normalize` flags and the plumbing to keep the typed surface
exhaustive (compile-time match coverage on every variant).
Repair, restore, archive-normalize, and cleanup all emit
`operation_outcome` envelopes and machine-readable receipts; the SafeAutoFix
path is preserved as the legacy escape hatch for low-risk derived-only
repairs (Tantivy index rebuild, stale lock removal) and now fails closed if
it detects archive- or source-affecting risk.
==== Source / indexer / storage hardening (`src/sources/*`, `src/indexer/*`, `src/storage/sqlite.rs`) ====
- `src/sources/sync.rs` — `remote_spec_for_scp`, `parse_remote_home_stdout`,
and friends so the SSH/SCP source-mirror path quotes remote operands
consistently and rejects ambiguous remote-home outputs (more than one
candidate path → refuse instead of guessing). Closes the #raw-mirror
scan-root capture-before-parse hole that 5c1dba9e / 5be8a6e9 first surfaced.
- `src/sources/config.rs`, `index.rs`, `install.rs`, `probe.rs`, `setup.rs`
— typed source-provider lifecycle: discovery, probe, install, setup, sync
now share a single error vocabulary the doctor surface understands so its
candidate plans can describe both what they will do and which source the
action is rooted at.
- `src/indexer/mod.rs`, `src/indexer/refresh_ledger.rs` — small fixes to
bind refresh ledgers to source-authority decisions.
- `src/storage/sqlite.rs` — extra integrity probes called by `doctor check`
before any cleanup or archive operation runs.
==== UI / HUD / CLI surface ====
- `src/ui/app.rs` — new `DoctorHudSummary` (parsed from the unified
doctor runtime summary) drives a HUD lane that surfaces archive coverage,
source-mirror state, fallback mode, sole-copy warnings, active repair and
index-maintenance, and the recommended next action. Operators no longer
have to leave the TUI to know whether running a repair is safe.
- `src/lib.rs` — large surface change: every CLI subcommand that touched
doctor surfaces is rewired through the bounded request/response types,
the new archive-scan / archive-normalize subcommands are exposed, the
receipts and plan-fingerprint payloads are emitted on stdout (json/jsonl)
with stderr reserved for fatal envelopes, and the `--robot` mode learns
the new doctor JSON shape (see golden updates).
- `src/update_check.rs` — version-check now feeds into the doctor HUD as
one of the recommended-action sources (an outdated cass that knows about
archive issues should still nudge the operator to upgrade first).
==== Tests + golden snapshots ====
- `tests/cli_doctor.rs` (+2086 lines) — behavioral coverage for every new
subcommand, plan-fingerprint mismatch handling, dry-run/apply parity,
archive-coverage gating of mutating ops, and the legacy `cass doctor`
alias mapping into the v2 surface.
- `tests/doctor_e2e_runner.rs`, `tests/util/doctor_e2e_runner.rs`,
`tests/util/doctor_fixture.rs`, `tests/doctor_fixture_factory.rs` —
end-to-end doctor harness updates: synthesizes archive trees, raw-session
mirrors, partial backups, and corrupt SQLite headers; asserts that
archive-normalize and repair never escape into source / archive paths.
- `tests/doctor_release_checklist.rs` (new) — release-gate test that
cross-checks the runbook against the implemented surfaces (every entry in
DOCTOR_V2_RELEASE_CHECKLIST.md must map to a known DoctorCommandSurface
variant; new variants must add a checklist entry).
- `tests/cli_robot.rs`, `tests/cli_status.rs`, `tests/cli_dispatch_coverage.rs`,
`tests/e2e_health.rs` — smaller dispatch-coverage / status / robot
surface-area follow-ups required by the new doctor shape.
- `tests/golden_robot_docs.rs`, `tests/golden_robot_json.rs` — assertion
helpers for the regenerated robot JSON / docs goldens.
- `tests/golden/robot/*.json.golden` and
`tests/golden/robot_docs/*.txt.golden` — regenerated for the new shapes;
`introspect.json.golden` and `introspect_shape.json.golden` carry the
bulk of the diff because they enumerate the full doctor surface +
argument schema.
==== Docs + scripts ====
- `docs/planning/RECOVERY_RUNBOOK.md` — operator runbook (referenced from
the README) describing the archive-first model, plan-fingerprint flow,
source-pruning warnings, sole-copy prompts, and the support-bundle
handoff.
- `docs/planning/DOCTOR_V2_RELEASE_CHECKLIST.md` (new) — release gate; the
new checklist test consumes this file.
- `README.md` — replaces the old single-doctor table with the v2 surface
table and updated examples (`cass doctor check --json`,
`cass doctor repair --plan-fingerprint`, etc.).
- `CHANGELOG.md` — Unreleased entry covering the archive-first
documentation and the doctor migration guidance for existing data dirs.
- `scripts/e2e/doctor_v2.sh`, `scripts/bakeoff/cass_validation_e2e.sh` —
scripted runners for the doctor v2 surface; consumed by the e2e harness
and the cass bakeoff campaign.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related advances on top of the doctor command surface landed in
15f6bba3:
1) Structured root-cause incidents
2) V14 → V15 migration safe re-run
src/lib.rs (+1736 LOC):
- New DoctorIncidentRootCauseKind enum with kebab-case Serialize:
derived-index-stale, derived-index-stale-after-db-promotion,
semantic-model-missing-lexical-fallback,
source-pruned-with-mirror-intact, mirror-missing-with-db-sole-copy,
archive-db-unreadable-with-valid-candidate, archive-db-unreadable,
active-lock-blocking-repair, interrupted-repair-state,
storage-pressure-derived-cleanup-available, backup-exclusion-risk,
backup-unverified, previous-repair-failed, unknown
- New DoctorRootCauseIncident struct surfaced through cass doctor
check --json: schema_version, incident_id, root_cause_kind,
severity, affected_asset_classes, archive_risk_level,
derived_risk_level, confidence, evidence_check_ids,
blocked_actions, safe_next_actions
- Replaces the prior "string-typed advisory" output so downstream
tooling (br/bv triage, ops dashboards) can pivot on the typed kind
rather than scraping wording out of free-form text
- Doctor check evaluator now classifies each evidence row into one
of the kinds above, attaches blocked_actions (e.g. cannot run
reconstruct while archive db unreadable) and safe_next_actions
(e.g. run cass doctor cleanup --derived first), and includes the
evidence_check_ids that produced the classification so the report
is auditable end-to-end
src/storage/sqlite.rs (+109 / -33):
- MIGRATION_V15 split: MIGRATION_V15_TAIL_STATE_TABLE keeps the
CREATE TABLE IF NOT EXISTS conversation_tail_state DDL idempotent.
The two ALTER TABLE conversations ADD COLUMN statements now run
conditionally via franken_table_column_names() so retrying the
migration on a partially-promoted database (where last_message_idx
was added but last_message_created_at was not, or both were added
but conversation_tail_state was not) does not error with
"duplicate column name"
- discover_historical_database_bundles comment block compressed:
the V13 / V14 / lazy-FTS reasoning is preserved but the redundant
"MIGRATION_FRESH_SCHEMA (V13) creates fts_messages eagerly..."
preamble is gone now that the gating logic itself is the
authoritative source of truth
src/doctor.rs (+12 LOC) routes the new incident report through the
typed surface so check / repair / cleanup variants all consume the
same structured payload.
src/indexer/mod.rs (+11) and src/search/asset_state.rs (+13) wire
the new incident types into the existing asset-state classifier so
"derived index stale" vs "derived index stale after db promotion"
flow into the right cleanup recommendation.
Test coverage:
- tests/cli_doctor.rs (+397/-): asserts the JSON shape, kebab-case
variant emission, and that blocked_actions/safe_next_actions are
populated for every kind
- tests/doctor_e2e_runner.rs (+154 LOC) and tests/util/doctor_e2e_runner.rs
(+378/-): drive the full check → repair plan → apply cycle
end-to-end with realistic incident states
- tests/util/doctor_fixture.rs (+117/-): adds fixtures for the
V14→V15 partial-promotion scenarios so the migration safe-rerun
is exercised in CI
- Goldens regenerated for tests/golden/robot/{doctor, doctor_quarantine,
doctor_shape, introspect, introspect_shape}.json.golden and
tests/golden/robot_docs/schemas.txt.golden — these are deterministic
outputs of the new incident schema
Cargo.toml / Cargo.lock / build.rs / README.md: bump franken-agent-
detection pinned rev to 029253c450702a... so the doctor evidence
collector can call into the new connector source-bundle inspection
helper that was added upstream.
scripts/e2e/doctor_v2.sh (+4/-): drive the new
--repair --plan-fingerprint --yes path so the existing E2E shell
script exercises the typed apply boundary, not the legacy --fix flag.
Beads update (.beads/issues.jsonl + .beads/last-touched): mark the
incident-classifier sub-bead in_progress to match the work landing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two coordinated additions to the doctor v2 e2e + CLI surface
that together make the doctor's failure paths first-class
testable instead of "should-never-happen" branches.
src/lib.rs (+552 net inserts)
`open_franken_cli_read_db_with_hard_timeout(path, reason,
timeout)`:
A hard-timeout wrapper around the existing
`open_franken_cli_read_db` helper. The naked open path
is dependent on frankensqlite's internal lock-contention
backoff, which can stall a CLI invocation indefinitely
if the DB file is corrupt OR if a runaway process holds
the lock with no SIGINT path. The wrapper:
- spawns a worker thread that runs the open
- sets up a `mpsc::channel`
- blocks on `rx.recv_timeout(timeout)`
- on timeout: returns a typed
`CliError { code: 9, kind: DbOpen, retryable: true }`
with a "open timed out after Ns" message — operator
can then run `cass doctor` to investigate the cause
- on Ok: returns the open conn
- on disconnect (worker thread panic): returns a typed
error rather than a panic-aborted CLI
Pulled out into
`receive_franken_cli_read_db_open_result_with_hard_timeout`
so the worker-side and recv-side can be tested
independently — the timeout / disconnect / Ok arms each
get their own unit test.
Wired into the doctor + status + history command paths
that read from the archive DB so a single corrupt-DB
scenario never wedges the entire CLI.
scripts/e2e/doctor_v2.sh (+10 lines)
`--include-failure-self-test` flag. When set, exports
`CASS_DOCTOR_E2E_INCLUDE_FAILURE_SELF_TEST=1` so the test
binary picks up the intentional-failure scenario.
Default behaviour (flag unset) is unchanged.
tests/util/doctor_e2e_runner.rs (~29 net inserts)
- DoctorE2eCliArgs gains `include_failure_self_test`
boolean.
- `selects(scenario)` widens to include scenarios labeled
`self-test` when the flag is on.
- `DoctorE2eScenarioSpec::expected_runner_status()` ->
`"pass"` for normal scenarios, `"fail"` for self-test
scenarios — pins the contract that the self-test
intentionally fails.
- New `failure_self_test_doctor_e2e_scenario()` returns a
DoctorE2eScenarioSpec named `intentional-failure-self-test`
with label `self-test` and required JSON pointers that
deliberately won't be satisfied.
tests/doctor_e2e_runner.rs (~42 net inserts)
`doctor_e2e_include_failure_self_test_selects_intentional_failure`
test: parses CLI args including the flag, builds the
scenario set with the flag-driven extension, asserts the
intentional-failure scenario is selected, and asserts
the runner records a `"fail"` status for it.
Pins the contract that the doctor v2 e2e harness can prove
its own failure-detection — running `--include-failure-self-test`
on a healthy build produces a deliberately-failing scenario
that the runner correctly reports as failed (not silently
masked).
`.beads/issues.jsonl` + `.beads/last-touched` advance one
paired touch as the metadata mirror.
Summary:
- Migrates cass semantic indexing/search internals from legacy CVVI/HNSW wrappers to frankensearch-owned FSVI and ANN primitives.
- Replaces remaining in-repo lexical helper implementations with frankensearch lexical APIs where applicable.
- Aligns daemon/indexer/perf suites with on-disk FSVI behavior, normalized vectors, and doc-id metadata parsing.
Details:
- search/query integration:
- switches query sanitization/boolean parsing/wildcard handling and tantivy query construction to cass_* helpers from frankensearch::lexical.
- removes legacy local bridge/index conversion paths and local parser helpers superseded by frankensearch.
- keeps cass-specific orchestration and filter mapping while delegating lexical mechanics.
- vector index facade:
- retires CVVI-specific structures in favor of frankensearch::index VectorIndex/Writer/SearchParams exports.
- standardizes index filenames to index-<embedder>.fsvi.
- preserves cass role-code/filter helper APIs around the new backend.
- ANN module:
- shrinks local ann_index module to diagnostics/stat payloads and path helpers.
- removes in-tree HNSW graph build/search logic now provided by frankensearch.
- semantic indexer/worker/model manager:
- builds FSVI directly via create_with_revision + write_record + finish.
- uses SemanticDocId serialization and VectorIndex::open for runtime loading and dedup hash recovery.
- updates rebuild checks to read embedder metadata from FSVI headers.
- indexer/connectors/source robustness:
- tantivy preflight now uses cass-compatible reader open helper.
- local root skip behavior only triggers when root is actually missing.
- franken-agent-detection integration now does targeted connector probing with UnknownConnectors fallback handling.
- OpenCode timestamp normalization now handles plausible seconds vs milliseconds safely.
- source install disk probe avoids HOME tilde expansion races under concurrent tests.
- benches/tests:
- regex cache bench now calls frankensearch lexical cache functions directly.
- search/memory/perf benches and property tests now generate on-disk FSVI fixtures with normalized vectors.
- semantic e2e checks updated from .cvvi expectations to .fsvi.
- semantic e2e script now verifies .fsvi artifacts.
Why:
- Consolidates core search behavior in frankensearch to reduce duplicated implementations and divergence risk.
- Moves cass onto a single canonical vector artifact format and runtime path.
- Keeps benchmark/test coverage aligned with production code paths to catch regressions in the new backend.
Implement sources_sync.sh from scratch using e2e_log.sh (was empty).
Add e2e_log.sh sourcing and standard event emission to cli_flow.sh,
semantic_index.sh, and cass_daemon_e2e.sh while preserving their
existing custom JSONL logging (Option A compatibility layer).
All scripts now emit structured JSONL to test-results/e2e/ in addition
to their custom output locations. Remove sources_sync.sh from .gitignore
since it is no longer a stub.
Closes br-20bz.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two bugs were preventing the daemon E2E test from working:
1. Semantic indexing needed --full flag - incremental scan found no
messages because the test data timestamps were considered "old"
2. Test data filename pattern was wrong - Codex connector expects
rollout-*.jsonl, not daemon-e2e.jsonl
Also add scripts/e2e/daemon_fallback.sh wrapper so the daemon test
is included in the orchestrated E2E runner (run_all.sh picks up
scripts/e2e/*.sh).
Part of T7.3: E2E daemon fallback + health script
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major improvements to the semantic search subsystem:
- Add fastembed-based reranker for improved result relevance
- Implement model manager for dynamic embedding model selection
- Expand indexer semantic capabilities with batch processing
- Enhance TUI with reranker-aware result presentation
- Add e2e test script for semantic indexing validation
The reranker module provides a secondary ranking pass that
significantly improves search result quality for ambiguous queries.
Co-Authored-By: Claude <noreply@anthropic.com>