Deterministic code context from symbol graphs — not vector search
Project description
Stubborn
Deterministic code context from symbol graphs — for SCIP-indexed codebases.
Status: Beta (Java-first) — release
0.9.0b4· BETA.md · POSITIONING.md · CHANGELOG
Stubborn compiles a SCIP symbol index into type-safe, privacy-preserving stub text for LLMs and agents. Same index + target + options → same context: reproducible, token-bounded, and stripped of method bodies.
Primary fit: Java / Spring repos (especially legacy modernization) where teams already run or can run scip-java. Also: Cursor/MCP users with a symbols.db who want deterministic context — not a zero-config repo map.
Independent project under stubborn-ai (program map). Full positioning: docs/POSITIONING.md.
This repo is a personal showcase of architecture-led, AI-assisted engineering: the developer defines system design, boundary protocols, and acceptance criteria; AI implements most of the code; the shipped artifact is deterministic Python — reproducible, test-gated, and verifiable (same SCIP → same context).
Development model → — human vs AI roles, deterministic core, boundary protocols.
Why Stubborn?
Real pain points: type hallucinations when LLMs lack declarations, token waste from whole files, privacy from leaking implementations, non-reproducible embedding chunks.
Stubborn addresses these when you already have (or can build) a SCIP index. Compare on three axes — not “us vs vector RAG” alone:
| Stubborn | Vector chunk RAG | Low-friction symbol maps (e.g. Aider repo-map) | |
|---|---|---|---|
| Determinism | Same SCIP → same stub | Chunk / embedding drift | Varies |
| Type structure | Symbol graph + signatures | Arbitrary text spans | Repo structure / tags |
| Setup friction | SCIP index required | Index / embed pipeline | Open repo |
| Token KPI | Built-in metrics, ~80–90% vs sources (Java E2E) |
Unpredictable | Not a design goal |
| Privacy | No method bodies by design | May include full files | Full files possible |
| CI reconcile | diff, symbol guards |
Hard to baseline | Uncommon |
Stub = skeleton declarations (like header files).
Stubborn = compiles symbol graphs into bounded stub text; use --prune-mode strict when you want SCIP-proven neighbors only (ADR-003).
Use cases
Primary (Java / Spring + SCIP workflow):
- Legacy / modernization — scoped context for large estates (examples/migration-bridge, dukesbank)
- PR semantic audit —
difftwo SCIP indexes; CI symbol guards - Program runbooks — reproducible stub artifacts with compression KPIs
Secondary (agents / individuals):
- Cursor / MCP —
get_contextbefore codegen on a pre-builtsymbols.db - Large-repo onboarding — dependency skeleton for one target symbol
Start with 30-second fixture or Docker E2E; production Java path requires scip-java (see Requirements).
Requirements
- Python 3.11+ (or use Docker)
- A SCIP index for your project — Stubborn does not index source directly
| Goal | You need |
|---|---|
| Try Stubborn (no Java) | Bundled fixture only — below |
| Real Java / Spring repo | scip-java (or CI that produces index.scip) + stubborn index |
| Reproduce project E2E | Docker Desktop — docker/README.md |
Beta scope: weave quality and KPIs are Java-validated. Other SCIP languages may ingest; output quality is not guaranteed until language-specific E2E ships (BETA.md).
Docker quick start
docker compose build
docker compose run --rm e2e # demo-spring E2E
docker compose run --rm petclinic-e2e # spring-petclinic scale-up
docker compose run --rm cli --help
See docker/README.md.
Installation
PyPI: stubborn-stub · CLI: stubborn (alias: stub)
pip install stubborn-stub
From source (development):
git clone https://github.com/stubborn-ai/stubborn.git
cd stubborn
pip install -e ".[dev]"
Try in 30 seconds (no Java required)
Uses the bundled minimal SCIP fixture — no JDK, Maven, or scip-java needed:
pip install stubborn-stub
stubborn index --scip examples/fixtures/minimal.scip --out /tmp/symbols.db
stubborn info /tmp/symbols.db
stubborn context /tmp/symbols.db \
--target "semanticdb maven com/example/OrderService#" \
--out /tmp/order-service.stub.java
Quick start
Full E2E with a modern Spring Boot demo: see examples/demo-spring.
1. Index symbols
# Binary SCIP from scip-java (recommended)
stubborn index --scip index.scip --out ./metadata/symbols.db
# Or use the bundled fixture while bootstrapping
stubborn index \
--scip examples/fixtures/minimal.scip \
--out ./metadata/symbols.db
2. Inspect
stubborn info ./metadata/symbols.db
3. Get LLM context for a target symbol
stubborn context ./metadata/symbols.db \
--target "semanticdb maven com/example/OrderService#process()." \
--out ./context/order-service.stub.java
# Compact cross-language format (~fewer tokens):
stubborn context ./metadata/symbols.db \
--target "semanticdb maven com/example/OrderService#process()." \
--format stubborn-dsl \
--out ./context/order-service.stubborn-dsl
See docs/STUBBORN-DSL.md for the compact cross-language format.
Choose output format:
| Project | Suggested --format |
|---|---|
| Pure Java / Spring codegen | java-stub (default) |
| Mixed-language or token-sensitive / graph-first | stubborn-dsl |
Tune output granularity: --member-signatures off|target|neighbors|all, --javadoc off|summary|full (guide).
Choose neighbor expansion (--prune-mode):
| Mode | When |
|---|---|
smart (default) |
Richest type neighbors; CI guards assume this |
strict |
SCIP-proven edges only — no signature heuristics |
fast |
Smaller neighborhood + no heuristics — tight token budget |
Or use the short CLI alias: stub.
4. Reconcile before/after (CI-friendly)
stubborn diff ./metadata/before.db ./metadata/after.db
# exit 1 if symbols are missing
5. MCP server (Cursor / agents)
Use the separate stubborn-mcp package:
pip install stubborn-mcp
export STUBBORN_DB=./metadata/symbols.db
stubborn-mcp
Tools: get_context, list_symbols, metrics. See stubborn-mcp docs.
CLI
| Command | Description |
|---|---|
init-db |
Create empty SQLite symbol graph |
index |
Ingest SCIP (.scip, .scip.ndjson, or .json fixture) |
info |
Index run summary |
context |
Prune graph → emit LLM context (--format java-stub | stubborn-dsl; --prune-mode smart | strict | fast) |
metrics |
Compression KPI: stub vs full Java sources |
diff |
Symbol set reconcile (missing/extra) |
Architecture
Design rationale is recorded as Architecture Decision Records (docs/adr/). Pipeline overview:
[Source code] → scip-java / scip-clang / … → index.scip
↓
stubborn index → SQLite symbol graph
↓
stubborn context → stub text (java-stub or stubborn-dsl)
↓
LLM / Agent / CI
SQLite schema: src/stubborn/store/schema/v1.sql
Roadmap
| Version | Focus |
|---|---|
| 0.1 | SQLite schema, JSON fixture ingest, Java stub weaver, CLI shell |
| 0.2 | Binary .scip protobuf ingest, .scip.ndjson, scip-java compatible |
| 0.3 | Token budget enforcement, metrics KPI, weaver quality, Docker CI |
| 0.4 | MCP server (get_context, list_symbols, metrics) |
| 0.5 | Type-neighbor pruning, PR symbol-diff Action, context guard |
| 0.6 | spring-petclinic scale-up E2E (~90% savings) |
| 0.7 | Stubborn-DSL weaver (--format stubborn-dsl) |
| 0.8 | Java-first beta track — BETA.md, demo-spring cases |
| 0.9 | Method signatures, STUBBORN-DSL-GUIDE |
| 0.9.0b3 (now) | Standalone cleanup — rename debt removed, ruff CI, CLI smoke tests |
| 0.9.0b2 | Java-first Beta — classifier + weave granularity switches |
| 1.0 | Multi-language E2E, stable API |
Documentation
| Doc | Description |
|---|---|
| docs/DEVELOPMENT-MODEL.md | Architecture-led, AI-assisted development model |
| docs/adr/README.md | Architecture Decision Records (design rationale) |
| docs/POSITIONING.md | Product positioning — audience, competitors, honest scope |
| docs/BETA.md | Beta checklist, limitations, KPI baselines |
| docs/STUBBORN-DSL.md | Stubborn-DSL grammar |
| docs/STUBBORN-DSL-GUIDE.md | java-stub vs stubborn-dsl decision guide |
| docs/STUBBORN-DSL-LLM.txt | LLM system-prompt snippet |
| docs/MCP.md | Cursor / agent integration |
| docs/SCIP-INGEST.md | SCIP ingest |
| examples/README.md | E2E examples |
Optional ecosystem integrations
Stubborn is standalone. Some teams also use it alongside the anchor-migration program:
| Repo | Role |
|---|---|
| stubborn (this repo) | LLM context compiler |
| migration-hub | Optional program docs — integration guide |
| java-ast-ssot | Full Java AST SSOT (complementary) |
| db-metadata | Database schema SSOT (complementary) |
See docs/POSITIONING.md.
Development
pip install -e ".[dev]"
pytest -v
ruff check src tests
ruff format --check src tests
MCP server (stubborn-mcp):
pip install -e "../stubborn-mcp"
stubborn-mcp
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stubborn_stub-0.9.0b4.tar.gz.
File metadata
- Download URL: stubborn_stub-0.9.0b4.tar.gz
- Upload date:
- Size: 37.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98e9c1f8fcb9ab20ec2e0eb1c8cb9aa2194b95e99297dafc653e8067069d5f95
|
|
| MD5 |
2bbbf92caaed39254f955a23ab9766f5
|
|
| BLAKE2b-256 |
fafab6d30755cee0ace25d55037b8f22921340ddea002eb4a1a520083e953c5a
|
File details
Details for the file stubborn_stub-0.9.0b4-py3-none-any.whl.
File metadata
- Download URL: stubborn_stub-0.9.0b4-py3-none-any.whl
- Upload date:
- Size: 42.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7faa322a6900e0c5b052798124e2bd644951965b026eb595d8a524372646e66b
|
|
| MD5 |
142fac7cb3fdb3dc13eb7955a7fd44cc
|
|
| BLAKE2b-256 |
234dc49f2f2c6d022beccc4dd83a30649dee317e4ae6d59dde938b576961abe9
|