Skip to content

JSAT architecture

This document explains how JSAT 0.4.14 is assembled and how requests move through it. It is based on the code and a refreshed self-index on 2026-09-01, not only on the public feature description.

Verified snapshot

Measured at 0.4.17.

Measure Current value
Python modules under jsat/ 90
Python source lines under jsat/ 27,613
Top-level CLI commands 41
MCP tools 69
Shipped slash-command documents 47
Self-index graph 2,530 nodes / 12,008 edges
Graph nodes 2,087 functions, 220 classes, 158 files, 65 knowledge nodes
CI-safe pytest run 804 passed / 11 skipped
No-mocking self-test ~250 checks across 12 suites

The current repository is a layered modular monolith. The CLI, SDK, shell, and MCP server are adapters around one JSAT facade. The facade lazily selects graph and AI adapters, while feature logic lives in jsat/tools/. The persistent graph is the product's shared source of codebase context.

1. System context

flowchart LR
    Developer([Developer])
    Automation([CI / Python automation])
    Client[AI coding client<br/>Claude, Codex, Cursor, OpenCode, Bob, Gemini]

    subgraph JSAT[JSAT process]
        CLI[Typer CLI / shell]
        SDK[Python SDK]
        MCP[MCP JSON-RPC server]
        Core[JSAT facade + tools]
    end

    Repo[(Source repository)]
    Local[(Per-repo JSAT data<br/>graph, manifest, index, history)]
    Shared[(Optional team services<br/>Neo4j, Qdrant, Redis)]
    AI[(AI providers<br/>local CLIs, Ollama, hosted APIs)]
    Improve[(Global improve store<br/>sanitized JSAT-only signals)]

    Developer --> CLI
    Automation --> SDK
    Client <-->|MCP over stdio| MCP
    CLI --> Core
    SDK --> Core
    MCP --> Core
    Repo -->|tree-sitter parse + Git metadata| Core
    Core <--> Local
    Core -. optional .-> Shared
    Core -. AI-assisted tools .-> AI
    Core -. privacy-filtered failures .-> Improve

The AI coding client and the AI provider are separate roles. A client may call JSAT through MCP, while an AI-assisted JSAT tool can independently use a configured provider. Launch routing in _ai/routing.py prevents one client's model selection from leaking into another provider.

2. Runtime layers and ownership

flowchart TB
    subgraph Entry[Entry and integration adapters]
        cli[cli.py + _cli_*.py]
        shell[tools/shell.py]
        sdk[jsat.JSAT public SDK]
        mcp[mcp/server.py registry + JSON-RPC]
        slash[commands/jsat-*.md + _JSAT_SKILLS]
    end

    subgraph App[Application facade]
        core[_core.py / JSAT]
        models[_models.py]
        config[_config.py]
        context[_call_context.py]
    end

    subgraph Features[Feature tools]
        indexing[indexer / query / blast radius]
        assurance[security / contract / migration / review / tests]
        reasoning[prompt optimizer / IThinking / crack / orchestrator]
        memory[knowledge / sessions / improve]
        utility[export / tokens / feature planning]
    end

    subgraph Ports[Backend contracts and adapters]
        graphPort[_graph: SQLite / Neo4j / LightGraph]
        ai[_ai: CLI / Ollama / Anthropic / OpenAI / compat / none]
        embed[_embed: local / OpenAI / none]
        cache[_cache: memory / disk / Redis]
        parsers[_parsers: Python / JS / Go / Java / Ruby / Rust]
    end

    Entry --> App
    App --> Features
    Features --> Ports
    config --> Ports
    models --> App
    context --> Features

The intended dependency direction is inward-to-outward through the facade: adapters parse or serialize requests, JSAT selects dependencies, and tools contain the use cases. Heavy imports are deliberately inside functions so jsat --help and JSAT() remain fast and optional extras fail only when invoked.

Module responsibilities

Area Responsibility Important boundary
cli.py, _cli_*.py Typer commands, argument validation, Rich presentation, launch/connect workflows Presentation only; imports tool logic lazily
_core.py Stable SDK facade, config/path pinning, lazy graph and AI creation Shared route used by CLI and SDK
mcp/server.py 69-tool registry, JSON-RPC, RBAC, budgets, progress, dashboard and metrics MCP schemas and handlers; not mcp/tools.py
tools/ Indexing and every analysis/reasoning use case Real feature implementation
_parsers/ Tree-sitter extraction into normalized nodes and edges One parser instance per worker/file
_graph/ GraphClient contract and storage adapters SQLite is the solo default; Neo4j is optional team storage
_ai/ Provider contract, aliases, launch routing and adapters NoOpProvider is the degradation boundary
_config.py, _models.py Detection, presets, merge/defaults and validation All config fields default for backward compatibility
_improve/ Capture, sanitization, clustering and safe persistence JSAT-internal data only; drop rather than redact
commands/, _cli_skills_data.py Agent instructions for supported clients Parallel registries that must remain synchronized

3. Indexing pipeline

sequenceDiagram
    autonumber
    actor Caller
    participant Surface as CLI / SDK / MCP
    participant Core as JSAT.index()
    participant Indexer as IndexerTool
    participant Manifest as IndexManifest
    participant Pool as ThreadPoolExecutor (max 8)
    participant Parser as Language parser
    participant Graph as GraphClient

    Caller->>Surface: index repo
    Surface->>Core: index(path, branch, force, languages)
    Core->>Graph: lazy-open configured graph
    Core->>Indexer: run(...)
    Indexer->>Manifest: load previous mtime + SHA-256 state
    Indexer->>Indexer: collect supported, non-excluded, size-safe files
    Indexer->>Manifest: compute added / modified / deleted / unchanged
    Indexer->>Graph: remove stale nodes and outgoing edges
    loop changed file, in parallel
        Indexer->>Pool: submit parse job
        Pool->>Parser: tree-sitter parse(file)
        Parser-->>Pool: File / Class / Function nodes + edges
    end
    Indexer->>Graph: bulk upsert batches (2,000 nodes)
    Indexer->>Graph: resolve unique CALLS / IMPORTS names to node IDs
    Indexer->>Manifest: save new manifest + Git commit
    Indexer->>Indexer: calculate complexity hotspots and write INDEX.md
    Indexer-->>Caller: IndexResult

Indexing is incremental unless forced. Files are filtered using configured languages, exclude patterns, maximum size, and symlink policy. Parsing failures are isolated per file and return an empty result for that file rather than aborting the full index.

Parser output model

erDiagram
    SOURCE_FILE ||--o{ CODE_FUNCTION : contains
    SOURCE_FILE ||--o{ CODE_CLASS : contains
    CODE_CLASS ||--o{ CODE_FUNCTION : defines
    CODE_FUNCTION }o--o{ CODE_FUNCTION : CALLS
    SOURCE_FILE }o--o{ SOURCE_FILE : IMPORTS
    CODE_CLASS }o--o{ CODE_CLASS : INHERITS
    CODE_FUNCTION }o--o{ ERROR_TYPE : RAISES
    APP_SERVICE ||--o{ API_ENDPOINT : exposes
    APP_SERVICE }o--o{ DB_TABLE : READS_FROM_or_WRITES_TO
    APP_SERVICE }o--o{ EVENT_TOPIC : PRODUCES_or_CONSUMES
    KNOWLEDGE_ENTRY ||--o{ KNOWLEDGE_ENTITY : HAS_ENTITY

The storage schema is deliberately generic: nodes(id, label, properties JSON) and edges(id, type, source_id, target_id, properties JSON). The richer diagram above is a logical schema imposed by labels, edge types, and JSON properties, not separate SQL tables.

The refreshed self-index currently contains mostly code topology:

  • 7,831 CALLS, 936 IMPORTS, 116 RAISES, and 110 INHERITS edges.
  • Eight knowledge nodes add HAS_ENTITY, USES, DEPENDS_ON, and DOCUMENTS relationships.
  • No Service, Endpoint, Table, or Topic nodes exist in this Python-only repository, but the query and MCP surfaces understand those labels for indexed application repositories.

4. Request execution paths

Direct CLI or SDK call

sequenceDiagram
    actor Caller
    participant Adapter as CLI or Python caller
    participant Core as JSAT facade
    participant Tool as tools/feature.py
    participant Graph as GraphClient
    participant AI as AIProvider

    Caller->>Adapter: request
    Adapter->>Core: typed method call
    Core->>Graph: _get_graph() if needed
    opt AI-assisted feature
        Core->>AI: _get_ai() if needed
        AI-->>Core: provider or NoOpProvider
    end
    Core->>Tool: construct with graph/config/AI
    Tool->>Graph: graph queries / traversal
    opt synthesis or review
        Tool->>AI: complete / stream
        AI-->>Tool: response or error
    end
    Tool-->>Adapter: Pydantic model / dataclass / dict
    Adapter-->>Caller: Rich, JSON, or Python result

MCP tool call

sequenceDiagram
    autonumber
    actor Client as MCP client
    participant Server as MCPServer
    participant Guard as Auth + RBAC
    participant Worker as single-call worker
    participant Handler as registry handler
    participant Tool as JSAT tool
    participant Obs as progress / dashboard / metrics

    Client->>Server: tools/call(name, arguments, progressToken)
    Server->>Guard: validate token and role
    Guard-->>Server: allowed / JSON-RPC error
    Server->>Server: strip _budget and dashboard control args
    Server->>Worker: submit call with hard limit = 5 x soft budget
    par monitor
        Worker->>Handler: dispatch registry entry
        Handler->>Tool: execute use case
        Tool-->>Obs: checkpoint events
    and budget watcher
        Server->>Obs: soft-budget notification if late
    end
    alt completed
        Worker-->>Server: serialized result
    else hard timeout
        Server->>Server: structured retry/scope guidance
    else exception
        Server->>Server: JSON-RPC error + sanitized improve signal
    end
    Server->>Obs: record duration/error metric and finish dashboard call
    Server-->>Client: text content result

MCP has three authorization levels. viewer can use 17 read-only tools; developer can use 43 listed tools including impact, assurance, knowledge writes, and IThinking; admin is unrestricted. With no auth environment variables, local stdio access is intentionally open and emits a warning unless explicitly acknowledged.

Nested calls have depth-sensitive sub-budgets (120, 30, 15, 8, 4, 2, 1 seconds) and are rejected at depth seven. A soft timeout reports progress but does not cancel work; the worker future gets a hard timeout at five times the selected soft budget.

5. How the major feature families work

Family Main algorithm AI required?
Index Incremental manifest, parallel tree-sitter parse, bulk upsert, symbol resolution No
Query Extract question keywords, select graph context, build grounded prompt, synthesize Yes for answer; graph context still builds without it
Blast radius Resolve a file/symbol, breadth-first graph traversal, classify by final edge type No
Security Semgrep when installed, secret entropy/pattern checks, graph-based auth/data-flow checks No for core scan
Incident Read recent Git commits, score recency/relevance/scope, optionally synthesize hypotheses Optional
Contract/migration Compare specs or parse SQL operations and classify breaking/locking risk No for structural result
Review Run configured providers, parse findings, deduplicate and rank multi-model agreement Yes
Test gaps Find functions/endpoints lacking graph-linked tests and rank risk No
Prompt optimizer Offline classify/context/constraints/examples/format/compress pipeline; optional LLM rewrites Optional
IThinking Seven-phase planning/execution/reflection workflow through a callback Optional by phase
Crack/orchestrator Role-based parallel AI discussion or specialized task decomposition, then synthesis Optional; offline fallback exists in Crack
Knowledge Store entries/entities in the graph; Graphiti when available, regex + AI fallback otherwise Optional
Improve Capture JSAT-only friction, sanitize twice, cluster, diagnose in temp, create review bundle Optional for diagnosis
Tokens Offline token estimation, section accounting, and deterministic compression passes No

Query grounding pipeline

flowchart LR
    Q[Question] --> KW[Lexical keyword extraction]
    KW --> GQ[Graph queries by labels]
    GQ --> REL[Filter functions/classes by matching properties]
    REL --> BUD[Character cap = token budget x 4]
    BUD --> P[Grounded prompt]
    P --> AI{AI available?}
    AI -->|yes| ANS[Answer + cited context lines]
    AI -->|no| DEG[Explicit AI-unavailable result]

The main QueryTool uses lexical matching over graph properties. The embedding and vector-store adapter packages exist and are unit-tested, but nothing in the indexing or query path instantiates them β€” they are architectural extension points, not active dependencies. Since 0.4.17 the config schema and docs/configuration.md state this explicitly, so those settings no longer read as live semantic retrieval. Cache lookups key on an exact (query, context_hash) pair; the inert cache.similarity_threshold, which the docs had described as cosine matching, was removed.

The /jsat skill dispatcher: input correction, learning, and the internet exception

Every shipped commands/jsat-*.md skill is routed through the single /jsat <name> [flags] [args] dispatcher installed by jsat connect <client>. Three behaviors apply universally, ahead of and after the routed subcommand itself:

  • Default input correction. Before routing, the dispatcher treats free-form ARGS as likely rushed input and calls jsat__prompt_rewrite to fix spelling/grammar and tighten phrasing, skipping literal payloads (paths, diffs, URLs, code blocks) that must not be rewritten. A materially changed ARGS is routed but surfaced back to the user as πŸ“ Interpreted as: <rewritten> so a bad guess stays visible and correctable. raw=true opts out for that one call.
  • Universal learning module. After a routed subcommand completes, the dispatcher runs one more pass asking whether anything durable surfaced. Concrete, project-specific facts (a gotcha, a false-positive pattern, a convention discovered by exploration) are written to the project's own knowledge base via jsat__knowledge_add(category="project-learning"); facts about JSAT's own tools misbehaving are written instead to a jsat-improvement backlog that /jsat improve reads later. Nothing is written when the invocation was routine β€” this module is best-effort and silent in the common case.
  • jsat-internet β€” the one sanctioned exception. No jsat__* MCP tool reaches the live internet, so every other skill is restricted to jsat__* tools only. jsat-internet.md is the deliberate carve-out: it calls native WebSearch/WebFetch directly for up-to-date facts (docs, released versions, CVEs), optionally grounding the external answer against this codebase via jsat__query. jsat-magic.md wires it in as an opt-in Layer W β€” never auto-selected for a pure-codebase task, added only when a task's own wording names external or current information, and dropped first if the composed skill sequence is over budget.

Auto-generated help

commands/jsat-help.md used to be a hand-maintained document and had drifted stale β€” listing commands that had since been deleted and omitting newly added ones. It is now generated by _generate_help_body() in _cli_skills_data.py from the same live commands/jsat-*.md file list that every other dispatcher artifact already reads, removing that one registry from the "parallel hand-maintained registries" risk in Β§14 β€” the help body specifically can no longer drift out of sync with the shipped command set, even though _cli_skills_data.py and the command Markdown remain separate files overall.

6. Configuration, paths, and deployment profiles

flowchart TB
    Init[JSAT construction] --> Load[Load first matching YAML config]
    Load --> Detect[Detect RAM, CPU, GPU, CI and local services]
    Detect --> Preset{Auto-selected profile}
    Preset -->|solo| Solo[SQLite + local embeddings + memory cache]
    Preset -->|team| Team[Neo4j + OpenAI embeddings/Qdrant + Redis]
    Preset -->|CI| CI[SQLite + no embeddings + no AI + memory cache]
    Preset -->|Raspberry Pi| Pi[SQLite + reduced local embedding batch + disk cache]
    Solo --> Override[Constructor/provider overrides win]
    Team --> Override
    CI --> Override
    Pi --> Override
    Override --> Pin[Pin .jsat paths to the repository data directory]

Config search starts with an explicit path and JSAT_CONFIG, then repository-local, global user, and system paths. Per-repository data resolves in this order:

  1. JSAT_DATA_DIR override.
  2. Existing substantial <repo>/.jsat/ data for backward compatibility.
  3. ~/.jsat/<first-12-chars-of-SHA1(resolved-repo-path)>/.

The graph database, vector path, disk cache, audit log, and prompt history are pinned under that location. Session files are global under ~/.jsat/sessions/. Self-improvement data is separately global under ~/.jsat/improve/ so it can never be confused with user repository data.

7. AI provider selection and degradation

flowchart LR
    Need[AI-assisted tool] --> Cached{Provider cached?}
    Cached -->|yes| Use[Use provider]
    Cached -->|no| Factory[get_ai_provider]
    Factory --> CLI[Claude / Codex / OpenCode / Bob CLI]
    Factory --> Local[Ollama]
    Factory --> Hosted[Anthropic / OpenAI / OpenAI-compatible]
    Factory --> None[NoOpProvider]
    CLI --> Check{is_available}
    Local --> Check
    Hosted --> Check
    Check -->|yes| Use
    Check -->|no or factory error| None
    None --> Explicit[completion call raises AIError]
    Explicit --> ToolFallback[Tool catches error and returns partial/offline result]

JSAT._get_ai() never propagates provider construction or reachability failures; it substitutes NoOpProvider. That provider reports unavailable and raises on actual completion, so feature tools must explicitly degrade. Query, review, Crack, and other multi-agent tools isolate failures at the smallest practical unit.

8. Self-improvement privacy boundary

flowchart LR
    Fail[JSAT crash / gap / timeout / friction] --> Build[Build JSAT-owned record]
    Build --> Frames[Keep package-relative frames and exception type]
    Frames --> Context[Keep allowlisted finite context; other values become type-only]
    Context --> Verify{Adversarial verify_clean}
    Verify -->|machine path, user marker, env value, secret, oversized| Drop[Drop entire record]
    Verify -->|clean| Buffer[Throttle + in-memory buffer]
    Buffer --> Store[(signals.jsonl + clusters.json)]
    Store --> Diagnose[Optional AI diagnosis in temporary copy]
    Diagnose --> Verify2{Verify every bundle file}
    Verify2 -->|unsafe| Drop2[Reject bundle]
    Verify2 -->|clean| Bundle[(analysis.md + patch.diff + issue.md)]
    Bundle --> Human{Human consent}
    Human -->|review / submit| External[GitHub or patch workflow]

The design uses drop, never redact. Exception messages, user paths, identifiers, source code, environment values, and secrets are not persisted. Writes go through a path guard that permits only the improve store or an explicit temporary root. JSAT does not patch its installed source, and outward actions remain human-controlled.

9. Integration and process lifecycle

flowchart LR
    Connect[jsat connect CLIENT] --> Config[Write the client's native MCP config]
    Connect --> Guidance[Install client-specific command/instruction files]
    Start[jsat start CLIENT --via native/ollama] --> Resolve[Resolve route and model ownership]
    Resolve --> Ensure[Ensure MCP wiring]
    Ensure --> Spawn[Spawn client process]
    Spawn --> Registry[(Global process registry)]
    Registry --> PS[jsat ps]
    Registry --> Stop[jsat stop / restart / resume]
    Spawn --> MCP[jsat mcp-server --repo ...]

Connection code is client-specific because Claude, Codex, Cursor, Continue, OpenCode, Gemini, Bob, Windsurf, and Zed use different configuration formats. The generated entry launches the same local stdio MCP server. Slash-command files and MCP registration are separate surfaces: the presence of one does not prove the other is active.

10. Extensibility map

flowchart TB
    Feature[New feature] --> Logic[tools/&lt;feature&gt;.py]
    Logic --> SDK[_core.py public method if SDK-facing]
    Logic --> CLI[_cli_*.py command if human-facing]
    Logic --> MCP[mcp/server.py registry entry + handler]
    MCP --> RBAC[_ROLE_PERMISSIONS + optional budget]
    Logic --> Slash[commands/jsat-NAME.md if agent workflow]
    Slash --> Help[commands/jsat-help.md]
    Slash --> Skills[_JSAT_SKILLS registry]
    Feature --> Models[_models.py result/config types]
    Feature --> Tests[CI-marked isolated tests]
    Feature --> Docs[README / docs / changelog as applicable]

A capability is not fully integrated merely because its tool module exists. The public surfaces, RBAC, agent command registries, documentation, and tests are independent integration points.

11. Resilience and observability

  • checkpoint() records progress events without coupling feature modules to the MCP server.
  • MCP can mirror checkpoints to standard progress notifications and the zero-dependency SSE dashboard.
  • In-memory per-tool metrics track calls, total time, and errors; an optional Prometheus sidecar exports process metrics.
  • Indexing catches failures per file; review and multi-agent features catch failures per provider or agent; telemetry is always best-effort and cannot fail the caller.
  • SQLite and LightGraph use WAL mode and bulk writes. Neo4j implements the same GraphClient contract, although its bulk methods currently loop over individual operations.
  • Export/import moves the graph through a portable archive. The default SQLite graph is local; the team profile moves the graph, vectors, and cache to shared services.

12. Testing architecture and current health

The test suite is organized by feature rather than by architectural layer. Tests use temporary state directories, MagicMock for the SDK in MCP tests, and LightGraph or temporary SQLite for graph-dependent behavior. CI selects tests marked ci; integration and slow tests are excluded from the normal local/CI run.

The verified direct CI-safe pytest run produced 611 passed, 9 skipped, 34 deselected and 35 deprecation warnings from datetime.utcnow() in the generated INDEX.md path. The combined local_test.sh runner currently stops at an upstream 102-character line in tests/test_ollama_launch.py:236, so its Ruff stage is not green at this snapshot.

13. Architectural strengths

  1. One graph, multiple adapters. CLI, SDK, shell, and MCP do not maintain separate analysis engines.
  2. Useful offline core. Indexing, traversal, token work, migration inspection, and much of security analysis do not require an LLM.
  3. Lazy optional dependencies. Core startup stays light and deployment profiles can omit expensive integrations.
  4. Explicit degradation. Missing providers and per-file/per-model failures normally yield partial results instead of process-wide crashes.
  5. Strong privacy boundary. Self-improvement is structurally separated from indexed project data and uses a second adversarial verification pass.
  6. Operational MCP controls. RBAC, nesting limits, progress, budgets, hard timeouts, dashboards, and metrics live at the common server boundary.

14. Risks and known debt

Priority Finding Why it matters
High mcp/server.py is 2,881 lines and _handle has measured complexity 52 Registry, transport, auth, budgets, dashboards, metrics, and serialization change together
High The MCP β€œhard timeout” is Future.result(timeout=...) over a worker thread; shutdown(wait=False) cannot terminate code already running The client receives a timeout response, but the underlying operation may continue using resources or mutating state
High Service / Endpoint / Table / Topic nodes are never persisted by the indexer; mcp/server.py infers services and endpoints heuristically per request The MCP surface reports services while the SDK and jsat query see none on the same repo β€” the three surfaces disagree about what the graph contains
Medium mypy is declared strict = true in pyproject.toml but not run by CI; ~373 errors remain The declaration is not enforced. The self-test ratchets the count so it cannot grow and reports the gap as its own finding
Medium _MinimalJSAT in _cli_setup.py is a second, hand-maintained JSAT-shaped object Any method an MCP tool calls must be mirrored there; import_archive was missed exactly this way
Medium Embedding and vector-store backends are implemented but have no call sites Retrieval is entirely lexical. Now marked NOT YET WIRED IN at the schema and in the docs, so config no longer implies otherwise
Medium graph.backend: neo4j is partial by design β€” every tool that issues a graph query emits SQLite SQL, which the Neo4j backend rejects Indexing, traversal and blast radius work; query/feature/get_test_gaps do not. It now warns at construction naming what degrades
Medium Neo4j bulk_add_nodes / bulk_add_edges loop item-by-item; symbol resolution is skipped for non-SQLite backends Team indexing is slower and may preserve unresolved call/import targets
Medium crack.py and orchestrator.py are near-duplicate multi-agent engines with large inline prompts and no shared code Two places to change for one behaviour
Low The YAML skill registry (skills/) executes only source.type == "script", and no manifests ship Clusters are correct as documented orderings of real commands, not an execution engine
Low Token accounting is a chars-per-token heuristic (Β±12% claimed), chosen over a tiktoken dependency Every budget decision is approximate by design
Low A moved or renamed checkout silently orphans its index The global store is keyed by a hash of the absolute repo path
Low Generated index timestamps call deprecated datetime.utcnow() Deprecation warnings add noise and will eventually require a change

Resolved in 0.4.17: the parallel slash-command registries (_JSAT_SKILLS is now derived from jsat/commands/*.md), the dangling skill clusters, the dead second tool catalogue (mcp/tools.py, deleted), and the unenforced graph capacity caps. JSAT.index_status returns a real commit and computes freshness against git HEAD; GraphConfig's lightgraph value does select LightGraph through the facade β€” both earlier entries were inaccurate.

The largest cohesion hotspots after mcp/server.py are _cli_connect.py (~1,290 lines), tools/shell.py (~1,190), and tools/prompt_optimizer.py (~900). _cli_skills_data.py dropped from 1,694 to ~690 lines in 0.4.17 when the duplicated skill registry became a derivation. Measured function hotspots include cmd_disconnect (complexity 61), MCPServer._handle (52), and cmd_ai_use (36).

  1. Split MCP transport/auth/execution/observability from the declarative tool catalogue and feature handlers; generate tool schemas and RBAC checks from one typed definition.
  2. Generate client command assets and help tables from one command manifest, eliminating the two manually synchronized registries.
  3. Make index freshness compare the manifest commit and working tree against current Git state, and expose separate graph_available and index_fresh signals.
  4. Either route lightgraph correctly from _get_graph() or remove it from user configuration and document it as a test-only constructor.
  5. Introduce explicit retriever and cache ports into QueryTool, then wire the existing embedder and cache packages or simplify the unused configuration surface.
  6. Batch Neo4j writes with UNWIND and define a backend-neutral symbol-resolution strategy.
  7. Keep architectural snapshot counts generated by tests or documentation checks so version, command, and tool counts cannot drift silently.

16. Reading order for contributors

For the fastest accurate mental model, read in this order:

  1. jsat/_core.py β€” public facade and lazy dependency selection.
  2. jsat/tools/indexer.py and jsat/_parsers/ β€” how source becomes graph data.
  3. jsat/_graph/__init__.py and jsat/_graph/sqlite.py β€” graph contract and default storage.
  4. jsat/tools/query.py and jsat/tools/blast_radius.py β€” representative read workflows.
  5. jsat/mcp/server.py lines around _handle, _call, and _build_registry β€” shared AI-client execution boundary.
  6. jsat/_config.py, _models.py, and _ai/ β€” deployment and provider behavior.
  7. jsat/_improve/ β€” the privacy and safe-write invariants.
  8. Feature-specific modules under jsat/tools/, followed by their tests.