177 Commits

Author SHA1 Message Date
Dicklesworthstone 5a97520d36 fix(e2e): rch-wrap logging acceptance tests 2026-05-08 04:42:00 -04:00
Dicklesworthstone b8295ed17b fix(e2e): rch-wrap daemon harness build 2026-05-08 04:37:43 -04:00
Dicklesworthstone 8eb9597158 fix(e2e): rch-wrap cli flow bootstrap 2026-05-08 04:34:10 -04:00
Dicklesworthstone e9fc8139e6 fix(e2e): rch-wrap full coverage cargo gates 2026-05-08 04:34:08 -04:00
Dicklesworthstone f0736ec758 fix(scripts): rch-wrap evidence bundle tests 2026-05-08 04:32:03 -04:00
Dicklesworthstone d551ade22f fix(e2e): rch-wrap sources sync bootstrap 2026-05-08 04:31:15 -04:00
Dicklesworthstone fad576e5cb fix(e2e): rch-wrap connector stress bootstrap 2026-05-08 04:28:24 -04:00
Dicklesworthstone 7ef18f6bbd fix(e2e): rch-wrap security paths bootstrap 2026-05-08 04:25:47 -04:00
Dicklesworthstone bf308633bd fix(scripts): rch-wrap pages perf helper build 2026-05-08 04:22:46 -04:00
Dicklesworthstone 2640642805 fix(e2e): rch-wrap query parser bootstrap 2026-05-08 04:22:02 -04:00
Dicklesworthstone c59c5da9b8 fix(scripts): rch-wrap docs validation cargo 2026-05-08 04:16:06 -04:00
Dicklesworthstone 64ef4c9766 fix(scripts): rch-wrap benchmark report helper 2026-05-08 04:11:56 -04:00
Dicklesworthstone 0440cf5091 fix(ci): rch-wrap validate helper cargo gates 2026-05-08 04:09:08 -04:00
Dicklesworthstone 9ec8c41f0c fix(scripts): rch-wrap test report helper 2026-05-08 04:09:07 -04:00
Dicklesworthstone 49fd42ffec fix(bakeoff): remove validation cargo fallback 2026-05-08 04:05:59 -04:00
Dicklesworthstone 9430688164 fix(scripts): rch-wrap comprehensive test helper 2026-05-08 04:05:47 -04:00
Dicklesworthstone 0b6acc471d fix(scripts): rch-wrap pages e2e helper 2026-05-08 04:03:34 -04:00
Dicklesworthstone 17b568bbc1 fix(bakeoff): default embedder e2e verbosity 2026-05-08 04:01:42 -04:00
Dicklesworthstone d09ba2c4b6 fix(ci): fail closed when no-mock tools are missing 2026-05-08 04:00:15 -04:00
Dicklesworthstone ced9909c76 fix(scripts): rch-wrap quick test helper 2026-05-08 03:51:49 -04:00
Dicklesworthstone 5d2060a9bc fix(bakeoff): offload embedder e2e cargo checks 2026-05-08 03:49:42 -04:00
Dicklesworthstone 1166b85561 fix(bakeoff): harden rerank e2e script 2026-05-08 03:32:55 -04:00
Dicklesworthstone 2aa2cc92a7 feat(doctor): doctor v2 — archive-first, candidate-based recovery model
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>
2026-05-06 17:17:14 -04:00
Dicklesworthstone 66cee37273 chore(repo): reorganize evidence trees + harden tracked-artifact hygiene
Pulls all of the long-form evidence the project has been accumulating
into a small, named set of `docs/` subtrees and tightens `.gitignore`
plus the CI hygiene gate so freshly-generated scratch artifacts cannot
quietly become tracked again. Pure repo-hygiene change: no application
code, schema, or build behavior is touched.

New canonical locations under `docs/`:

- `docs/artifacts/` is the durable home for evidence we want to keep
  reviewable in-tree: migration baselines, refactor proof bundles, the
  no-mock CI audit, and per-pass simplification dashboards. A new
  `docs/artifacts/README.md` documents the policy: anything under here
  is curated evidence, not scratch.
- `docs/artifacts/migration-baseline/` absorbs the entire former
  `.beads/migration_baseline/` tree (BASELINE_SUMMARY, SUMMARY, all
  `baseline_*.{log,txt,json}` outputs, the `search_quality/*.json` and
  `search_queries/q*.json` fixtures, and `binary_size.txt`) plus a new
  `fs_verification.log` so the migration evidence is not buried under a
  beads daemon directory.
- `docs/artifacts/refactor-runs/` collects every dated
  `20260424T*` … `20260427T*` and `20260428T*` codex/repeated-simplify
  pass directory that previously lived under `refactor/artifacts/`.
  Every per-pass report (`pass1_*.md` … `pass10_*.md`, `DASHBOARD.md`,
  `architecture.md`, `baseline.md`, plus the Twelfth/Thirteenth
  twelve-pass dashboards) moves verbatim — only the path changes.
- `docs/artifacts/no-mock-audit.md` replaces
  `test-results/no_mock_audit.md` as the durable copy of the no-mock
  policy report; the `test-results/` directory is now treated as
  ephemeral.
- `docs/assets/` becomes the project's image / screenshot home, with a
  new `docs/assets/README.md`. `cass_illustration.webp`,
  `gh_og_share_image.jpg`, and `screenshots/screenshot_0{1,2,3}.webp`
  move out of the repo root into `docs/assets/images/` and
  `docs/assets/screenshots/`.
- `docs/planning/` gains the formerly root-level
  `cli-robot-enhancements.md` and the dated investigation
  `2026-05-05-franken-agent-detection.md` (now under
  `docs/planning/sessions/`), so all planning material is in one
  predictable subtree.
- `docs/reference/E2E_LOGGING_SCHEMA.md` is added as the durable
  schema definition for the JSONL E2E log, replacing inline references
  to `tests/e2e/exports/setup-metadata.json` paths.
- `tests/policies/` becomes the home for machine-enforced policy
  inputs. `test-results/no_mock_allowlist.json` moves to
  `tests/policies/no_mock_allowlist.json` so that CI-relevant policy
  files survive `test-results/` being ignored.
- `tests/performance/lighthouse-budget.json` moves to
  `tests/fixtures/performance/lighthouse-budget.json` to consolidate
  test inputs under `tests/fixtures/`.

Tracked-artifact hygiene (`.gitignore`):

- Search-demo data: keeps the frozen v1 index and `watch_state.json`
  visible (`!tests/fixtures/search_demo_data/index/`,
  `!.../watch_state.json`, `!.../v1/**`) while ignoring later
  scratch generations (`v*/` minus `v1/`), `index-run.lock`, the
  doctor scratch directory, and the `.lexical-publish-backups/`
  staging tree.
- Test outputs: `test-results/*` is now ignored wholesale (no
  per-file allowlist; durable inputs live under `tests/policies/`).
  `/test-results-*.txt`, `claude-upgrade-progress.json`,
  `tests/test-results/`, `tests/tests/`, `tests/e2e/exports/`,
  `tests/e2e/pages_preview/`, `tests/artifacts/`, `tests/e2e/.env.test`,
  and root-level `/artifacts/` and `tmp/` are all explicitly ignored.
- Local SQLite sidecars: `*.db-wal`, `*.db-shm`, `*.db-journal`,
  `*.sqlite-wal`, `*.sqlite-shm`, `*.sqlite-journal`, `*.sqlite3-wal`,
  `*.sqlite3-shm`, `*.sqlite3-journal` are added globally so
  process-local journal files cannot leak in next to a deliberately
  checked-in fixture DB. Root-level `/*.db`, `/*.db-wal`,
  `/*.db-shm`, `/*.sqlite`, `/*.sqlite-wal`, `/*.sqlite-shm` follow
  the same rule.
- Aider state: `.aider.chat.history.md` and friends remain ignored
  globally, with an explicit
  `!tests/fixtures/aider/.aider.chat.history.md` exception so the
  intentional fixture survives.
- Root noise: `/cass_illustration.webp`, `/gh_og_share_image.*`,
  `/screenshots/`, `/UPGRADE_LOG.md`, `/refactor/`, `/perf/`,
  `/*.log`, `/*.mcp.json`, `cline.mcp.json`, `opencode.json`,
  `*.mcp.json.*.bak`, and `__pycache__/` plus `.ruff_cache/` are
  explicitly ignored at root so future regenerations of these
  artifacts do not become tracked.
- Preserved-by-name carve-out: an explicit
  `!docs/planning/MODES_OF_REASONING_REPORT_AND_ANALYSIS_OF_PROJECT.md`
  re-include line guards against developer-level globals that still
  match the pre-reorganization scratch filename.

Ephemeral artifact removals (now ignored):

- `.ntm/logs/am-coding_agent_session_search.log`,
  `.ntm/logs/bd-coding_agent_session_search.log`,
  `.ntm/logs/cm-coding_agent_session_search.log`, and
  `.ntm/summaries/coding_agent_session_search-20260407-115237.json`
  (~230 lines, all NTM session bookkeeping).
- `tests/e2e/.env.test` (regenerated per run).
- `tests/e2e/exports/setup-metadata.json`, `test-basic.html`,
  `test-encrypted.html`, `test-large.html`, `test-no-cdn.html`,
  `test-tool-calls.html`, `test-unicode.html` (~41,400 lines of
  generated Playwright export HTML).
- `tests/e2e/pages_preview/bundle/{private,site}/*` — the entire
  generated preview bundle (HTML, JS, CSS, payload chunk, master-key
  + integrity-fingerprint scratch). All produced by the preview build
  step.
- `tests/e2e/pages_preview/db/agent_search.db{,-shm,-wal}`,
  `tests/e2e/pages_preview/encrypt/{config.json,payload/chunk-00000.bin}`,
  `tests/e2e/pages_preview/export/export.db`,
  `tests/e2e/pages_preview/preview-server.log` (binary test DBs and
  encrypt/export scratch produced by the preview server).
- `tests/tests/test-results/e2e/placeholder` (zero-byte placeholder).

Total: ~52,900 deletion lines removed, all generated test artifacts
that the new `.gitignore` patterns now block from re-entering the tree.

CI gate (`.github/workflows/ci.yml` + `scripts/validate_ci.sh`):

- A new "Run repository artifact hygiene check" step runs
  `./scripts/validate_ci.sh --artifact-hygiene-only` first, before the
  no-mock policy check, so the build fails fast if any of the patterns
  above ever become tracked again.
- The no-mock workflow step is repointed at the new policy location
  (`tests/policies/no_mock_allowlist.json`); the in-workflow
  remediation guidance string in `AUDIT_REPORT` is updated to match.
- `scripts/migration_e2e_validate.sh` and `scripts/lib/e2e_log.sh`
  are repointed to the new schema doc and policy paths.
- `tests/e2e/reporters/jsonl-reporter.ts` and `tests/util/e2e_log.rs`
  reference the moved schema doc.

Companion docs (no behavioral change):

- `README.md` updates `<img>` `src` attributes for the project
  illustration and the three TUI screenshots to point at the new
  `docs/assets/...` paths, and adds a Remote Archive Safety subsection
  documenting that `cass doctor`/`health`/`status` JSON treats
  `remote_source_*` gaps as preservation signals (no behavior change,
  doc only).
- `docs/planning/TESTING.md` adds an "Artifact hygiene check" intro
  paragraph and a Doctor-V2 filesystem-portability section that names
  the `CASS_TEST_DOCTOR_RENAME_FAILURE=cross-device` simulation and
  the `fallback_kind: cross_device_copy_replace` evidence contract.
- `docs/planning/UPGRADE_LOG.md`, `docs/ftui_visual_parity_*` are
  re-pointed at the new artifact paths.
- `tests/fixtures/README.md` and `tests/fixtures/sources/probe/README.md`
  pick up the new path references.
2026-05-06 12:33:33 -04:00
Dicklesworthstone 9b061f12be feat(doctor): emit structured root-cause incidents and harden V15 migration
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>
2026-05-05 20:14:39 -04:00
Dicklesworthstone fe299bb09b feat(doctor): hard-timeout DB-open helper + intentional-failure self-test scenario
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.
2026-05-05 01:05:23 -04:00
Dicklesworthstone 423b67ea95 test(doctor): add scripted e2e artifact runner 2026-05-05 00:50:27 -04:00
Dicklesworthstone 20a9b3a0fe feat(scripts): add batch timeout, OOM detection, and state file error handling to watch-once driver
Batch timeout support:
- Add --batch-timeout-seconds flag (default: 1800s / 30 minutes) that
  kills a cass subprocess if it runs longer than the specified duration.
  The driver first sends SIGTERM with a 5-second grace period, then
  escalates to SIGKILL if the process does not exit. Timed-out batches
  exit with code 124 (matching the GNU timeout convention) and are
  logged with a "subprocess_timeout" failure reason. This prevents
  stuck batches from blocking the entire recovery pipeline indefinitely.

OOM detection:
- New is_out_of_memory_failure() function consolidates OOM heuristics:
  it checks for "out of memory" in stderr, exit codes -9/137 (SIGKILL)
  when peak memory reached 90% of the hard budget, and "killed" in
  output with similar memory pressure. The batch-size halving logic
  now uses this function instead of a simple string match, making it
  more reliable at detecting kernel OOM kills that don't include the
  "out of memory" string.
- The OOM check is skipped for timed-out batches to avoid conflating
  timeout kills with memory kills.

State file error handling:
- load_state() now uses explicit file handle management with encoding
  parameter and raises SystemExit with a diagnostic message on OSError
  or JSONDecodeError, instead of crashing with an opaque traceback.
- save_state() uses a context manager for atomic writes.

Logging improvements:
- Batch log entries now include timed_out (bool), batch_timeout_seconds,
  and out_of_memory_failure fields for post-hoc analysis.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-30 22:50:27 -04:00
Dicklesworthstone 62b2870994 fix: use let-chains in watch callback, check snippet for tool noise, and update batch driver docs
indexer/mod.rs:
- Collapse nested if/if-let in watch_sources Full rebuild handler
  into a single let-chain: if !pending.is_empty() && let Err(error).
  Cleaner control flow, no behavior change.

search/query.rs:
- deduplicate_hits() now checks hit.snippet when hit.content is empty
  before calling is_tool_invocation_noise(). Previously, empty content
  caused tool-invocation messages to slip through deduplication when
  the noise text was only in the snippet field.

scripts/historical_recovery:
- README.md: add note about --paths-file accepting pre-generated
  path lists alongside glob-based discovery
- run_watch_once_batches.py: improve batch completion log message
  with elapsed time formatting

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-29 22:22:23 -04:00
Dicklesworthstone 1fb026bcc2 feat(scripts): add byte-budget batch caps, replace sqlite3 db_counts with cass stats --json, and extract build_state_snapshot helper
Byte-budget batch caps:
- New --max-batch-bytes-mib CLI flag that limits the estimated raw input
  bytes per batch (measured via os.stat() before handoff to cass). When
  combined with --batch-size, the more restrictive limit wins. A single
  oversized file always passes through even if it exceeds the cap (at
  least 1 file per batch guaranteed).
- New select_batch_paths() replaces simple list slicing: iterates from
  start_index up to target_batch_size, accumulating estimated_file_size_bytes()
  until either the count or byte budget is exhausted, returning
  (batch_paths, batch_bytes)
- max_batch_bytes persisted in state and normalized across resume

Replace sqlite3 with cass stats --json:
- db_counts() no longer opens agent_search.db directly via the sqlite3
  module. Instead it shells out to `cass --color=never stats --json
  --data-dir <path>` and parses the JSON output. This eliminates the
  hard coupling to the internal DB schema and avoids WAL locking
  conflicts with a running cass index process.
- Graceful error handling: timeout (30s), non-zero exit, empty stdout,
  and invalid JSON all return {conversations: None, messages: None,
  error: "reason"} instead of crashing

build_state_snapshot() helper:
- Extracts the ~25-field state dict construction into a single function
  with named kwargs, reducing duplication across the 4 call sites that
  previously assembled the dict inline (initial save, batch success,
  batch failure, run completion)
- All fields are typed in the signature for self-documentation

Additional improvements:
- read_explicit_paths() returns (paths, path_stats) tuple with
  listed_explicit_paths and missing_explicit_paths counts for diagnostics
- collect_paths() returns (paths, path_stats) with collected_paths count
- paths_files included in legacy state migration equality check
- normalize_state_metadata() accepts fallback_max_batch_size and
  fallback_max_batch_bytes for consistent defaults on resume
- State gains successful_batches_this_run counter
- import sqlite3 removed, import math added

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-29 18:25:24 -04:00
Dicklesworthstone 0f4550c90b feat(scripts): add --paths-file for pre-generated path lists and TailBuffer for streaming subprocess output
--paths-file support:
- New CLI flag (repeatable) accepts newline-delimited files containing
  explicit paths to process, bypassing glob-based discovery entirely.
  Lines starting with # and blank lines are skipped. Relative paths are
  resolved against the paths-file's parent directory.
- Validation relaxed: either --paths-file OR both --root and --pattern
  must be provided (previously --root and --pattern were required)
- config_signature() now includes paths_files in the SHA-256 hash, so
  explicit-path runs get their own isolated resume state
- collect_paths() merges explicit paths with glob-discovered paths via
  the existing seen-set deduplication

TailBuffer for streaming subprocess output:
- Replace proc.communicate() (which buffers entire stdout/stderr in
  memory) with a TailBuffer that retains only the last 20KB of each
  stream. This prevents OOM when cass produces large debug output on
  oversized session files.
- Two daemon threads drain stdout and stderr concurrently using 4KB
  reads, feeding into independent TailBuffer instances
- The CompletedProcess returned by run_batch() now contains the tail
  text (str) instead of the full bytes, which is what all callers
  already expected (the truncated tail is sufficient for error
  diagnosis and logging)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-28 22:53:54 -04:00
Dicklesworthstone 822aed5516 feat(watch): add watch-once fast path with segment merge and improve batch recovery scripts
- Add dedicated watch-once reindex path that calls optimize_if_idle()
  after each incremental reindex for better segment hygiene
- Make watch callback return Result to propagate errors properly
- Update historical recovery scripts with improved batch processing

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-28 21:58:10 -04:00
Dicklesworthstone 65c3fadcab feat(scripts): add memory-aware autotuning and per-root state isolation to watch-once batch driver
Major upgrade to run_watch_once_batches.py for production-grade
historical recovery across heterogeneous session trees.

Per-root/per-pattern state isolation:
- State files are now keyed by a SHA-256 signature of (sorted roots,
  sorted patterns), so each unique combination (Claude sessions, Gemini
  sessions, backup mirrors, etc.) resumes independently without
  overwriting each other's progress
- Legacy state migration: if the new signature-keyed file doesn't exist
  but the legacy watch_once_batches_state.json does AND its roots/patterns
  match the current invocation, the state is transparently migrated

Memory-aware autotuning:
- /proc/meminfo sampling at batch start for MemTotal and MemAvailable
- /proc/$pid/status polling during batch execution (configurable
  --sample-interval-ms, default 100ms) for VmRSS and VmHWM
- Dual budget computation: soft budget = min(MemAvailable * soft_fraction,
  MemTotal * 10%, soft_cap_gb) and hard budget = min(MemAvailable *
  hard_fraction, MemTotal * 18%, hard_cap_gb), both floored at 256MB
- Autotuner decisions after each successful batch:
  - decrease_high_rss: halve batch size when peak RSS exceeds hard budget
  - return_to_best_throughput: revert to best-known batch size when
    throughput regresses >15% at a larger size
  - increase_safe_headroom: grow by max(1, batch_size/4) when RSS stays
    below 60% of soft budget
  - hold_steady: maintain current size in the neutral zone
- OOM shrink path also records memory telemetry in tuning state

New CLI arguments:
- --max-batch-size (default 256): upper bound for autotuner growth
- --sample-interval-ms (default 100): /proc polling frequency
- --memory-soft-fraction (0.20), --memory-hard-fraction (0.35)
- --memory-soft-cap-gb (8.0), --memory-hard-cap-gb (12.0)
- --growth-factor (1.5, reserved for future multiplicative mode)

Telemetry in state + log:
- Each batch log entry now includes peak_rss_kb, peak_hwm_kb,
  mem_total_kb, mem_available_start_kb, mem_available_end_kb,
  memory_soft_budget_kb, memory_hard_budget_kb, sample_count
- State tracks tuning dict with best_batch_size,
  best_throughput_paths_per_sec, and last autotune reason

README.md updated with per-root learning and multi-connector resume docs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-28 21:06:59 -04:00
Dicklesworthstone ca94cd238b feat(scripts): add resumable watch-once batch driver for large session tree reconciliation
New script: scripts/historical_recovery/run_watch_once_batches.py
- Drives native cass index --watch-once over large raw session trees
  (e.g., Gemini ~/.gemini/tmp with thousands of session-*.json files)
  in resumable batches to avoid whole-root OOM failures
- Discovers files via glob patterns, chunks them into configurable
  batch sizes (default 32), and invokes cass with --watch-once for
  each batch
- Tracks progress in a SQLite checkpoint database so interrupted runs
  resume from where they left off
- Automatically shrinks batch size on OOM-like failures and retries
- All parsing/insertion logic stays inside cass; the Python only
  handles path discovery, chunking, and progress bookkeeping

Updated: scripts/historical_recovery/README.md
- Document the new script with usage example for Gemini session tree
  reconciliation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-28 10:19:17 -04:00
Dicklesworthstone 548d50b9f9 feat(recovery): add historical session recovery toolkit
Add a suite of scripts for discovering, recovering, and reconciling
historical CASS session databases from scattered SQLite bundles across
the filesystem. This enables salvaging sessions from damaged or
orphaned database files that were created by various coding agents
(Claude Code, Codex, Gemini) over time.

Scripts included:
- inventory_sqlite_sources.py: Walk filesystem roots and classify
  SQLite bundles by schema version, row counts, and damage level
- recover_historical_bundle.py: Run SQLite .recover on damaged
  bundles to extract salvageable data
- merge_historical_bundle.py: Reconcile a recovered historical
  bundle into the canonical CASS database with dedup and
  conflict resolution
- import_codex_rollouts.py: Scan and reconcile Codex rollout
  sessions that use a different storage format
- screen_bundle_delta.py: Screen bundle deltas to preview what
  a merge would add before committing
- clone_core_tables_via_dump.sh: Shell helper for cloning core
  tables via SQLite .dump for cross-database migration
- README.md: Usage documentation for the recovery workflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:21:49 -04:00
Dicklesworthstone 78db3c4d78 feat(scripts): add cross-repo migration e2e validation script
New script scripts/migration_e2e_validate.sh validates the assembled
three-repo ecosystem (cass + frankensearch + franken_agent_detection)
after both the frankensearch and FAD migrations.

Features:
- Verifies repo existence and build health across all three repos
- Supports --local (no rch) and --quick (skip benchmarks) modes
- Colored terminal output with structured pass/fail/warn tracking
- Logs results to test-logs/ for CI artifact collection

This is the final piece needed to gate the migration on automated
validation rather than manual spot-checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:01:24 -05:00
Dicklesworthstone 9ece89c3d6 search/index: complete FSVI + frankensearch migration for semantic pipeline and perf tests
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.
2026-02-18 20:50:18 -05:00
Dicklesworthstone d476a9c8cb chore: remove unused import and update beads timestamp
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-11 12:29:08 -05:00
Dicklesworthstone 5329c602c5 feat: add verification evidence bundle generator for release gate (2dccg.11.8)
Shell script that runs all TUI test categories (stress, e2e_scenario,
cross_theme_degradation, density, rendering_invariants + P1 categories),
produces evidence-bundle.json manifest and evidence-summary.md. P0
categories gate the release — script exits non-zero if any P0 fails.
Supports --quick mode for fast local validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 19:59:40 -06:00
Dicklesworthstone d2d1c51c9f feat(bakeoff): add EMBEDDER env var for semantic index (bd-39th)
scripts/bakeoff/cass_validation_e2e.sh:
- Add EMBEDDER env var (default: fastembed, options: hash, fastembed)
- Pass --embedder flag to cass index when in semantic/hybrid mode

Hash embedder results vs lexical baseline:
- Lexical: NDCG@10 = 0.0412, p50=224ms, p95=270ms
- Semantic (hash): NDCG@10 = 0.1177, p50=255ms, p95=454ms
  (2.9x quality improvement with similar latency)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:27:41 -05:00
Dicklesworthstone 3d1e40e9c0 fix(bakeoff): query extraction and CASS_DATA_DIR passthrough (bd-39th)
scripts/bakeoff/cass_validation_e2e.sh:
- Extract topic keyword (first word) from synthetic queries like
  "metrics search query 0" → "metrics" to match corpus doc format
- Pass CASS_DATA_DIR env to subprocess.run to ensure cass finds the
  correct isolated index

This fixes zero-hits issue where full query strings didn't match
the synthetic corpus text patterns.

Before: NDCG@10 = 0.0000, 0 hits across all queries
After: NDCG@10 = 0.0412, 50/50 queries with hits, p50=224ms, p95=270ms

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:22:39 -05:00
Dicklesworthstone 7228ab15b8 feat(fixtures): add message grouping validation and edge cases
New fixtures and tooling:
- tests/fixtures/message_grouping/README.md: Documentation for fixtures
- tests/fixtures/message_grouping/edge_cases.jsonl: Edge case test data
- scripts/validate_fixtures.sh: Fixture validation script

Code updates:
- lib.rs, ui/tui.rs: TUI and library refinements

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-28 17:09:04 -06:00
Dicklesworthstone 6837bdee91 docs(testing): Add model fixtures and SSH test documentation
Expand TESTING.md with comprehensive documentation for test fixtures:

Model Fixtures for Semantic Search:
- Document embedder fixture (xenova-paraphrase-minilm-l3-v2-int8, ~17 MB)
- Document reranker fixture (xenova-ms-marco-minilm-l6-v2, ~22 MB)
- Add usage examples with fixture_helpers module
- Document checksum verification workflow
- Include model source attribution (Apache-2.0 licensed)

SSH Test Fixtures:
- Document Docker-based SSH test infrastructure
- Describe probe fixtures for various SSH scenarios:
  - indexed_host.json (847 sessions)
  - not_indexed_host.json (cass not indexed)
  - no_cass_host.json (cass not installed)
  - unreachable_host.json (connection failures)
- Add SshTestServer usage examples

scripts/lib/e2e_log.sh:
- Add E2E logging utilities for shell test scripts
- Structured JSON log output for CI integration

tests/util/e2e_log.rs:
- Add Rust E2E logging utilities
- Match shell script format for consistency

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-27 17:22:13 -06:00
Dicklesworthstone bc2bd0aaa3 chore: refresh e2e assets + verbose logging 2026-01-27 17:11:26 -06:00
Dicklesworthstone f3d29dde68 feat: add verbose shell e2e logging + export specs 2026-01-27 16:56:58 -06:00
Dicklesworthstone b10009f0f7 coding_agent_session_search-3koo: e2e logging + fixture updates 2026-01-27 16:37:28 -06:00
Dicklesworthstone ab81c218e7 feat(ci): Add E2E logging compliance check to validate_ci.sh
- Checks all tests/e2e_*.rs files for e2e_log/PhaseTracker usage
- Checks all scripts/e2e/*.sh files source e2e_log.sh
- Reports warnings (non-blocking) for non-compliant files
- Closes coding_agent_session_search-1wnh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 16:25:41 -06:00
Dicklesworthstone 351430b49d feat(e2e): Add full coverage validation script and expand connector tests
New Master Coverage Script (scripts/e2e/full_coverage_validation.sh):
- Orchestrates unit tests, E2E tests, JSONL validation, and coverage artifacts
- Sources e2e_log.sh library for structured logging
- Tracks pass/fail/skip counters across all test phases
- Generates comprehensive summary.md report
- Creates organized output in test-results/ directory

Connector Test Expansions:
- tests/connector_claude.rs: Expanded Claude connector test coverage
- tests/connector_cline.rs: Expanded Cline connector test coverage
- tests/connector_pi_agent.rs: Expanded Pi Agent connector test coverage

Infrastructure Tests:
- tests/cli_robot.rs: CLI robot mode test improvements
- tests/storage.rs: Storage layer test updates

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-27 15:30:13 -06:00
Dicklesworthstone 52b396a382 feat(verify): Expand Unicode path attack detection with RTL/zero-width/confusable chars
Strengthen path integrity verification against Unicode-based path traversal attacks:

Unicode Attack Detection Enhancements (verify.rs):
- Add combining solidus overlays (U+0337, U+0338) that could visually disguise chars
- Block zero-width characters (U+200B, U+200C, U+200D, U+FEFF) that invisibly split tokens
- Block bidirectional override/embedding/isolate characters (U+202A-U+202E, U+2066-U+2069)
  that can visually reverse or scramble path display
- Add confusable slash characters (U+2044, U+2215, U+29F8, U+1735) that look like /
- Add confusable dot characters (U+2E2E, U+0701, U+0702, U+A60E, U+10A50) that look like .

Comprehensive Test Coverage:
- Add tests for each new blocked Unicode character category
- Test combining overlay characters
- Test zero-width joiner/non-joiner/space/BOM
- Test RTL override, LTR override, RTL embedding, RTL isolate
- Test fraction slash, division slash, big solidus
- Test Vai full stop, Syriac full stops
- Add NFD/NFC normalization test with legitimate accented filenames
- Add unit tests for detect_encoded_path_violation with new attack vectors

E2E Test Infrastructure Improvements:
- Update run_all.sh to include per-test cass.log files in combined aggregation
- Exclude trace.jsonl and combined.jsonl from aggregation to avoid duplication
- Update output documentation in script headers
- Fix JSONL aggregation to recursively find test logs using find command

- Update validate-e2e-jsonl.sh default file discovery to include cass.log files
- Support recursive search in test-results/e2e for complete validation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-27 14:56:18 -06:00
Dicklesworthstone 422647177c coding_agent_session_search-ai4a: harden encoded path checks 2026-01-27 14:34:17 -06:00