Skip to content

Deterministic insights and review-only suggestions#48

Open
tony wants to merge 14 commits into
streamline-04from
streamline-05
Open

Deterministic insights and review-only suggestions#48
tony wants to merge 14 commits into
streamline-04from
streamline-05

Conversation

@tony

@tony tony commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Status. Stacked on #47 — retarget to master after it lands. The insight/suggestion schema, CLI groups, and MCP tools are written, and the sketch columns (simhash_hex, minhash_json, token_count, quality_flags) are computed and persisted. Two honest gaps to close: (1) it inherits #47's unlanded cache-consult keystone (both build on the same DbStore); and (2) InsightEngine.run_similarity today clusters only exact normalized-hash duplicates — the SimHash/MinHash → LSH-bucket → Jaccard-verify → union-find near-duplicate cascade this PR headlines is not yet consuming the persisted sketches. The design below is that remaining cascade, stdlib-first.

Motivation — why deterministic insights + review-only suggestions

Once history is a durable local read model (#47), the higher-value questions are interpretive, not retrieval: which prompts are duplicates or the same intent across projects (variant edges), which recurring instruction is absent from a given AGENTS.md / skill surface (omission findings), and which of those should become a reviewable instruction-change suggestion. The need is to surface recurring asks, forgotten-but-similar past conversations, and instruction-change candidates — without an LLM and without unbounded pairwise comparison.

LLM-free-by-default is a trust requirement: results must be reproducible and evidence-backed, and normal search must stay a read-only local evidence surface. An LLM reaches this surface only by explicitly invoking a CLI/MCP tool, never implicitly (ADR 0017/0018). Semantic/vector backends stay an optional later extra, never a required dependency.

The problem being solved

  1. Non-quadratic, deterministic candidate generation. Global similarity over thousands of prompts can't be O(n²) — and agentgrep already has the anti-pattern in-tree twice: ranking.py:collapse_near_duplicates is a nested WRatio loop, and RapidFuzz's process.cdist materializes a full len(a)×len(b) score matrix (process_py.py). Both are correct for a small already-retrieved result page and wrong for global clustering. The discipline that makes it cheap is cpython's difflib.get_close_matches: a strict cheap→expensive admissible cascade (difflib.py).
  2. Bounded by construction. Variant edges grow superlinearly on duplicate-heavy histories, so every listing pages with a default limit and reports totals rather than materializing full result sets — mirrored into the MCP payloads and the graph render.
  3. A hard review boundary. Suggestions turn evidence into proposed instruction changes for AGENTS.md / skills. They render for human review as a diff and never auto-edit files or call an LLM (ADR 0004's query/plan/execute/sink separation, applied to a mutation-shaped surface).

Design inspiration

The deterministic similarity cascade — map difflib's discipline onto the persisted sketches. get_close_matches runs real_quick_ratio (O(1) length bound) → quick_ratio (multiset overlap) → ratio (full), admitting only survivors to each stage. Port that shape at scale: LSH-band collision (cheapest gate over the stored minhash_json) → SimHash-Hamming / MinHash-overlap estimate (medium) → exact token-set Jaccard verify (authoritative) → dict-based union-find clustering (~15 lines, path compression). Add difflib's free prunes: a length-ratio gate min(|A|,|B|)/max(|A|,|B|) ≥ threshold before the Jaccard verify (bounds), and an autojunk-style corpus-document-frequency drop of ubiquitous shingles (__chain_b). semhash validates the clustering shape — greedy keep-first-representative over the candidate set (semhash.py). All sketches are hashlib-only (never the process-randomized hash()), with the algorithm + params recorded as provenance so a run is re-derivable.

The optional semantic tier — always an extra over the same SQLite file. Gate it behind agentgrep[semantic]: a pure-numpy static embedding (model2vec: tokenize → gather → mean-pool, no torch — model.py) behind an Encoder Protocol whose default is the substring/no-op path; vicinity's default backend is exact brute-force numpy cosine (basic.py), so no ANN dependency for the common case. When installed, store vectors in the same DB via sqlite-vec's vec0 virtual table (README) and back the FTS candidate generator with external-content FTS5, exactly as sqlite-rag keeps everything in one SQLite file (database.py).

Review-only suggestions — propose-don't-apply, borrowed from git and jj. Render each suggestion as a difflib.unified_diff against the live target and persist a pre-image fingerprint on the artifact — the way a unified diff's extended header records blob identity (diff-generate-patch.adoc). A future opt-in suggestions apply should mirror git apply: a --check dry-run that reports applies-cleanly / drift without touching the file (git-apply.adoc), atomic write on a clean pre-image match, and refuse-on-drift by default (atomicity). Make it undoable like jj — append a content-hashed apply-op so suggestions apply --undo restores the pre-image and history is never destroyed (op_store.rs, operation/restore.rs). Because rendered evidence is prompt-derived, route it through a span-based redactor (presidio's analyze→anonymize split; a small built-in recognizer set for emails, home-dir → ~, tokens — replace.py).

Rendering + MCP DX, pi-minimal, one emitter → three surfaces. Keep to_mermaid(run), rank_rows(run), to_unified_diff(suggestion) as pure functions returning str/rows. Emit the insight graph as a fenced mermaid flowchart LR (clusters as subgraph, variant edges as A -->|0.82| B, one classDef per cluster), bounded like Mermaid's own maxEdges cap with a terse %% N more omitted footer (grammar). Human CLI mode stays calm — Rich Table(box=None, show_edge=False) (box.py); the TUI feeds the same rows into the existing OptionList. insights_list / suggestions_list return typed Pydantic models (auto output_schema + structured_content, readOnlyHint) with per-run artifacts as templated resources agentgrep://insights/{run_id} (ToolResult, templates); an eventual interactive suggestions apply maps naturally to Context.elicit for the human-approve step (context.py).


Stacked on #47 — retarget to master after it merges; until then the diff below is insights/suggestions only.

Summary

  • Add deterministic feature and artifact storage: the DB index gains a record_features table (simhash/minhash/quality flags) plus insight-run, cluster, variant-edge, omission-finding, and suggestion schema. db sync --features defer|inline defers expensive feature extraction by default, with a batched missing-feature refresh API for insight runs.
  • Add a deterministic insights engine (ADR 0017): agentgrep insights analyze|list|explain computes similarity variant edges (simhash/minhash candidates confirmed by token-set Jaccard) and omission findings against a --target file, persisting runs, clusters, and evidence rows with provenance — no LLM involved.
  • Add live analyze progress: multi-line stderr progress with feature-refresh and artifact-write counters, Enter-to-exit, and clean JSON stdout.
  • Add bounded persisted listings: insights list pages with a default limit backed by a confidence-order index, explain is count-only, and listing payloads render as human summaries by default with explicit --json/--ndjson.
  • Add review-only suggestion artifacts (ADR 0018): agentgrep suggestions list|show|render turns insight evidence into persisted instruction-change suggestions for AGENTS.md/skill surfaces. Suggestions render for human review; agentgrep never edits instruction files or calls an LLM automatically.
  • Add read-only MCP tools insights_list and suggestions_list with bounded listing payloads and result totals.

Changes by area

Storage

src/agentgrep/db.py: feature builders, the feature/artifact schema, --features sync modes with deferred counters, refresh_missing_features with progress reporting, and similarity-row iteration.

Engines

src/agentgrep/insights.py: InsightEngine with similarity and omission analysis, bounded listings, and count APIs. src/agentgrep/suggestions.py: SuggestionEngine producing persisted, review-only SuggestionArtifact rows tied to insight evidence.

CLI

src/agentgrep/cli/parser.py, src/agentgrep/cli/render.py: the insights and suggestions command groups, analyze progress rendering, bounded listing and suggestion formatters, parse-time --target requirement for omission runs.

MCP

src/agentgrep/mcp/tools/insight_tools.py, src/agentgrep/mcp/models.py: two read-only tools with pydantic response models and capability registration.

Docs

ADRs 0006–0007; per-command CLI pages for insights and suggestions; the /insights/ feature section; MCP tool documentation.

Design decisions

  • Deterministic before semantic: similarity uses simhash/minhash candidate generation confirmed by Jaccard overlap, so insight runs are reproducible and evidence-backed; semantic backends such as LanceDB remain an optional later addition, not a required dependency (ADR 0017).
  • Hard review boundary for suggestions: suggestion workflows never edit AGENTS.md/skills and never invoke an LLM on their own — an LLM may only reach this surface by explicitly calling the CLI/MCP tools (ADR 0018).
  • Features are deferred secondary state: sync writes the ledger, records, and FTS index fast by default and lets insight runs batch-refresh missing features.
  • Listings are bounded by construction: variant edges grow superlinearly on duplicate-heavy histories, so list surfaces page with a default limit and report totals instead of materializing full result sets.

Test plan

  • tests/test_db_index.py — feature modes, batched feature refresh with progress, deferred counters
  • tests/test_cache_cli.pyinsights/suggestions CLI contracts: bounded listings, human summaries, analyze progress, early exit, --target requirement, JSON/NDJSON stdout
  • tests/test_agentgrep_mcp.py — bounded MCP listing payloads with totals
  • tests/test_widgets.py — rendered command-page docs for both groups
  • Full gate per commit: ruff check / ruff format, ty check, uv run pytest (incl. doctests), just build-docs

@tony tony force-pushed the streamline-05 branch from 1553fd6 to 63b7302 Compare June 6, 2026 22:14
@tony tony force-pushed the streamline-05 branch from 4ab05ce to d7376df Compare June 6, 2026 22:38
@tony tony force-pushed the streamline-05 branch from d7376df to d695b0d Compare June 6, 2026 23:31
@tony tony force-pushed the streamline-04 branch from d1d9c97 to 456ea4a Compare June 7, 2026 00:15
@tony tony force-pushed the streamline-05 branch from 9df8d3a to 22ee543 Compare June 7, 2026 00:16
@tony tony force-pushed the streamline-05 branch from 22ee543 to 0fdd15c Compare June 7, 2026 01:13
@tony tony force-pushed the streamline-04 branch from 8f15333 to c4b8fdc Compare June 7, 2026 01:38
@tony tony force-pushed the streamline-05 branch from 0fdd15c to d26b600 Compare June 7, 2026 01:43
@tony tony force-pushed the streamline-05 branch from d26b600 to 9628372 Compare June 7, 2026 11:04
@tony tony force-pushed the streamline-05 branch from 9628372 to 9aa4c3c Compare June 7, 2026 11:07
@tony tony force-pushed the streamline-04 branch from 560486b to feed723 Compare June 7, 2026 11:25
@tony tony force-pushed the streamline-05 branch from 9aa4c3c to 2d48f82 Compare June 7, 2026 11:25
@tony tony force-pushed the streamline-05 branch from 2d48f82 to 74b4121 Compare June 7, 2026 12:59
@tony tony force-pushed the streamline-04 branch from c8249f8 to efb6245 Compare July 4, 2026 14:27
@tony tony force-pushed the streamline-05 branch from 94d27a9 to c77cb5d Compare July 4, 2026 14:29
tony added a commit that referenced this pull request Jul 4, 2026
…decoupled architecture

why: PR #48 (stacked on #47) was authored on the monolith; rebasing onto
the ADR-0010 layered master dropped its facade-era hunks (descriptions,
MCP models, the insights/suggestions CLI render layer, facade re-exports
and dispatch), leaving the branch red.

what:
- Home INSIGHTS/SUGGESTIONS_DESCRIPTION in _text (+ CLI example groups).
- Home the insight/suggestion MCP models and export them from agentgrep.mcp.
- Re-land the dropped insights/suggestions CLI render layer, de-coupled
  from the facade; wire InsightsArgs/SuggestionsArgs dispatch in main().
- Facade re-exports; de-couple insights.py; restore cli/index cards.
- Doc-console fixes + extend the doc harness data-dependent-empty set to
  the insights/suggestions read-only query commands.
run_similarity remains exact-only (near-dup cascade follows).
@tony tony force-pushed the streamline-05 branch from c77cb5d to 7a13f2b Compare July 4, 2026 17:47
@tony tony force-pushed the streamline-04 branch from 4aefbf6 to 76e9e61 Compare July 5, 2026 12:59
tony added a commit that referenced this pull request Jul 5, 2026
…decoupled architecture

why: PR #48 (stacked on #47) was authored on the monolith; rebasing onto
the ADR-0010 layered master dropped its facade-era hunks (descriptions,
MCP models, the insights/suggestions CLI render layer, facade re-exports
and dispatch), leaving the branch red.

what:
- Home INSIGHTS/SUGGESTIONS_DESCRIPTION in _text (+ CLI example groups).
- Home the insight/suggestion MCP models and export them from agentgrep.mcp.
- Re-land the dropped insights/suggestions CLI render layer, de-coupled
  from the facade; wire InsightsArgs/SuggestionsArgs dispatch in main().
- Facade re-exports; de-couple insights.py; restore cli/index cards.
- Doc-console fixes + extend the doc harness data-dependent-empty set to
  the insights/suggestions read-only query commands.
run_similarity remains exact-only (near-dup cascade follows).
@tony tony force-pushed the streamline-05 branch from 9a51979 to 5699d8e Compare July 5, 2026 13:02
@tony tony force-pushed the streamline-04 branch from 76e9e61 to c6429ce Compare July 6, 2026 02:25
tony added a commit that referenced this pull request Jul 6, 2026
…decoupled architecture

why: PR #48 (stacked on #47) was authored on the monolith; rebasing onto
the ADR-0010 layered master dropped its facade-era hunks (descriptions,
MCP models, the insights/suggestions CLI render layer, facade re-exports
and dispatch), leaving the branch red.

what:
- Home INSIGHTS/SUGGESTIONS_DESCRIPTION in _text (+ CLI example groups).
- Home the insight/suggestion MCP models and export them from agentgrep.mcp.
- Re-land the dropped insights/suggestions CLI render layer, de-coupled
  from the facade; wire InsightsArgs/SuggestionsArgs dispatch in main().
- Facade re-exports; de-couple insights.py; restore cli/index cards.
- Doc-console fixes + extend the doc harness data-dependent-empty set to
  the insights/suggestions read-only query commands.
run_similarity remains exact-only (near-dup cascade follows).
@tony tony force-pushed the streamline-05 branch from 5699d8e to c183af8 Compare July 6, 2026 02:30
tony added 14 commits July 10, 2026 19:01
why: The insights analysis and suggestion workflows have different
ownership boundaries and failure modes from the DB cache. Recording
them as separate ADRs keeps the architecture reviewable while
preserving the deterministic-first insight decision and the review-only
suggestion boundary.

what:
- Add ADR 0006 for deterministic insights, variants, omissions, and
  optional semantic backends.
- Add ADR 0007 for reviewable suggestion skills and AGENTS.md/skill
  change semantics.
- Register both ADRs in the development architecture decision index.
why: ADR 0006 needs the DB index to carry deterministic similarity
features and persisted artifact tables before the insight engines can
land. Deferring feature extraction by default keeps db sync responsive
while inline mode populates the feature cache during sync itself.

what:
- Add the record_features table plus insight-run, cluster, variant-edge,
  omission-finding, and suggestion artifact schema to DbStore.
- Add simhash/minhash/quality-flag feature builders with a batched
  missing-feature refresh API and feature-refresh progress reporting.
- Add db sync --features defer|inline, features-deferred counters in
  sync results, progress lines, and human summaries.
- Extend db status and the db_status MCP payload with feature and
  artifact counts, and cover feature modes and refresh with tests.
why: ADRs 0006 and 0007 call for deterministic insight outputs and
review-only instruction suggestions over the DB index. The engines
generate persisted, evidence-backed artifacts without calling an LLM
and without editing instruction files.

what:
- Add InsightEngine with similarity variant edges, omission findings,
  bounded persisted listings, count APIs, and live analyze progress
  with feature-refresh and artifact-write counters.
- Add the review-only SuggestionEngine with persisted suggestion
  artifacts rendered for human review.
- Add agentgrep insights analyze|list|explain and suggestions
  list|show|render with human summaries by default and explicit JSON
  and NDJSON modes.
- Add the read-only insights_list and suggestions_list MCP tools with
  bounded payloads and totals.
- Cover engines, CLI contracts, progress rendering, and MCP payloads
  with regression tests.
why: The insights and suggestions command groups should follow the
existing CLI documentation contract, with feature explanations under
/insights/ and the MCP tool surface documented alongside the other
read-only tools.

what:
- Add argparse-backed CLI pages for insights analyze|list|explain and
  suggestions list|show|render with grid cards and toctree entries.
- Add the /insights/ feature guide and suggestions workflow page.
- Register insights_list and suggestions_list across the MCP docs,
  reference pages, and docs-build shim.
- Extend rendered-doc regression coverage to the new command pages.
why: The insight and suggestion listing tools opened the cache through
the migration path without closing it, leaking one SQLite connection
per MCP call and writing schema metadata from surfaces documented as
read-only. The insights and suggestions CLI commands leaked their
per-call connections the same way.

what:
- Open the insights_list and suggestions_list helpers read-only and
  close them via the runtime context manager.
- Close the per-call runtime in run_insights_command and
  run_suggestions_command on every exit path.
- Assert both tool helpers and the CLI path leave their connections
  closed.
why: The schema-version-mismatch rebuild dropped only the base tables,
so feature rows and insight artifacts survived a rebuild with stale
contents keyed to regenerated record ids — breaking ADR 0008's promise
that a mismatch recreates the schema empty.

what:
- Extend the rebuild drop list with the feature and artifact tables,
  children before parents.
- Seed features and variant edges in the rebuild test, assert every
  status count is zero afterward, and assert sqlite_master matches a
  fresh create so future drop/create drift fails mechanically.
why: insights list and explain, suggestions show and render, and
suggestions list without a target only read persisted artifacts, yet
they opened the cache through the migration path, which writes schema
metadata on every call and creates a missing cache as a side effect.
The db status surfaces and the MCP listing tools already follow the
read-only contract.

what:
- Route the read-only insights and suggestions actions through a
  shared read-only open with the db-status semantics: empty payloads
  for missing caches without creating the file, and clean errors for
  foreign files.
- Keep writable opens for insights analyze and suggestions list with a
  target.
- Cover missing-cache list behavior for both command groups and pin
  the read actions to read-only captured opens.
why: Suggestion listings fetched every persisted row while the sibling
insight listings were explicitly bounded with totals and truncation
flags. Suggestion rows accumulate across targets and runs, so the list
surfaces need the same page discipline.

what:
- Add count_suggestions and a limit parameter to list_suggestions,
  backed by a confidence-order index.
- Add --limit to suggestions list with parse-time validation, and emit
  a bounded payload with total, returned, and truncated fields plus a
  matching human summary.
- Extend the suggestions_list MCP tool and response model with limit,
  total, and truncation fields.
- Cover parser cases, the bounded CLI payload, and the bounded MCP
  payload, and document the new flag and response shape.
why: The lazy-import rule reserves function-local imports for heavy
modules that hurt CLI cold-start. json is a cheap stdlib module, and
insights.py is itself only imported by insight commands.

what:
- Move the json import from InsightEngine._record_run to module level.
why: simhash_hex, minhash_signature, and format_db_deferred_count are
pure, offline-runnable helpers whose siblings all carry Examples
blocks; the doctest guidance covers them the same way.

what:
- Add Examples sections to the two feature-signature helpers and the
  deferred-count formatter.
…files

why: The insight and suggestion listing tools returned an empty
payload for a missing cache file but surfaced a raw tool error for a
file that exists and is not a SQLite database — inconsistent with
db_status and the CLI read surfaces, which treat both the same way.

what:
- Catch sqlite3.DatabaseError around the read-only opens in
  _insights_list_sync and _suggestions_list_sync and return the same
  empty payload the missing-file branch uses, extracted into shared
  builders.
- Cover both tools against a plain-text cache file with named cases.
why: master already defines ADR 0006 (public-cli-mcp-surface-contract)
and ADR 0007 (query-language-comparison). Rebasing this branch onto
master left two same-numbered ADR files on disk. Move the insights and
suggestion-skill ADRs to the next free numbers after the stacked DB
ADRs (0015/0016) so ADR numbers stay unique.

what:
- Rename agentic-insights-engine 0006 -> 0017.
- Rename suggestion-skills-and-agent-instruction-changes 0007 -> 0018.
- Update the ADR toctree order.
…decoupled architecture

why: PR #48 (stacked on #47) was authored on the monolith; rebasing onto
the ADR-0010 layered master dropped its facade-era hunks (descriptions,
MCP models, the insights/suggestions CLI render layer, facade re-exports
and dispatch), leaving the branch red.

what:
- Home INSIGHTS/SUGGESTIONS_DESCRIPTION in _text (+ CLI example groups).
- Home the insight/suggestion MCP models and export them from agentgrep.mcp.
- Re-land the dropped insights/suggestions CLI render layer, de-coupled
  from the facade; wire InsightsArgs/SuggestionsArgs dispatch in main().
- Facade re-exports; de-couple insights.py; restore cli/index cards.
- Doc-console fixes + extend the doc harness data-dependent-empty set to
  the insights/suggestions read-only query commands.
run_similarity remains exact-only (near-dup cascade follows).
why: run_similarity only clustered exact normalized-hash duplicates; the
persisted MinHash/SimHash sketches went unused. This consumes them to
surface near-duplicate prompt variants deterministically, without an LLM
or new dependencies.

what:
- db.py: iter_feature_rows() + DbFeatureRow load the persisted MinHash
  signature and record text per record (INNER JOIN record_features).
- insights.py: a near-dup cascade after the exact pass — LSH-band the
  16-hash MinHash signature to generate candidates, verify with exact
  token-set Jaccard (>= 0.6), cluster with dict-based union-find, and
  write near_duplicate_prompt clusters + near_duplicate variant edges
  (idempotent edge/cluster ids; deterministic ordering).
- Tests cover detection, exact/near separation, threshold, transitive
  clustering, and run determinism.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant