Skip to content

Changelog

The canonical changelog lives in the repo at CHANGELOG.md. Highlights below.

3.63.0 — current · the context-budget gate now weighs the files the design will actually propose

The gate that refuses a ticket for needing too much context used to work from an estimate of which files the fix would touch; it now weighs the files the design itself proposes. In the autorun path the design is deterministic, so the gate and the build document call the same function and always agree on the verdict. The refusal line also moves from 1.5x to 2.0x the 200,000-byte budget (300,000 → 400,000 bytes); a design between 1.5x and 2.0x now proceeds with the existing “files will be excerpted” warning instead of being parked before any code is written. Measured on used tickets, one run each (counts, not a rate): of 40 fresh tickets the old estimate parked 5, four of them for files outside the fix; replaying 17 previously-parked tickets, 15 passed the new gate and the official grader resolved 9 of 17 (9 of 13 submitted), with no edit-anchor failures. Not every run was clean — one ticket took four attempts ($20.46) because a resumed run restarts the pipeline, and the two largest designs (2.5x and 2.8x the budget) still produced no patch. Not a Verified score, and no overall improvement is claimed. See the CHANGELOG.

3.62.0 — a run that fails only its own tests gets one repair turn, then ships unproven instead of failing

Where the refine loop used to give up — a stall, an exhausted refine allowance, a refine that edits nothing — if every failing test is one the run’s own test files define (and a pre-existing test ran too), the adapter now gets one repair turn on just those files first, labelled “not proof” if it goes green. If repair doesn’t clear it, the run falls through to the existing rescue: the run’s own tests are withdrawn and the suite re-run once; green ships the change labelled unproven by its own tests, with the coverage gate waived and named, and a live PR opens as a draft rather than the run failing outright. A test that fails only because it imports a library the environment lacks (not the repository) is withdrawn on the very first red run, before burning a refine on an import no code edit can fix. When repair and the automatic rescue both fail, a person can optionally be asked once — sdlc autorun --ask-minutes N (or SDLC_ASK_OWN_TESTS_MINUTES, default 0 = never ask) raises an approval and waits, answered with orchestrator sdlc runs approve <id> --choice …; sdlc runs interventions reports every answer the project has given, grouped by cause. Separately: after a test run that executes nothing, the refine prompt now names the directories the repository already uses for tests, which fixed six runs that were retrying a misplaced tests/test_*.py five or six times against modern sympy.

3.61.0 — every live PR carries a proof bundle, and its text can no longer act on someone else

A Spine PR used to carry the ticket and its criteria; what the run had established lived in log lines and a worklog. Now a “Spine proof bundle” section follows the ticket text: six checks, each checked, not checked or not applicable — the acceptance judge’s per-criterion verdict, the tests and what failed before the change, the project’s required-behaviour manifest, work the run withdrew, the risk reading (blast radius + escalation reasons), and the automated review — plus cost, models, and three SHA-256 hashes; nothing in it is blank or marked “verified”. sdlc autorun writes proof-bundle.json beside its other artifacts. Separately, the ticket’s words and the PR’s own generated text can no longer act on someone else: GitHub’s render-only markdown endpoint used to turn a decorator name (@dataclass, @Override, …) into a link to a real account and render raw HTML/images from copied text (measured at 69 images and 139 links, 21 to a real user, on one synthetic bundle) — the fix produces none, while code spans, fenced blocks and ordinary Markdown still render as before. Also: a name the repository cannot own (None, os.getcwd) no longer parks a bug ticket as a false premise, the blast-radius risk measurement now sees a file that was moved or added in a new directory, and a deterministic intake-quality score ships (no behaviour change). The 26-of-100 figure below was measured on 3.60.0 and has not been re-measured. See the CHANGELOG.

3.60.1 — intake stops treating a bug report’s own code as a requirement

The validity gate was parking bug-fix runs on acceptance criteria that were really the reporter’s own reproduction — a traceback, a REPL line, a URL — because the extraction prompt said to copy stated criteria verbatim and a bug report states none. Now the extraction prompt takes only the reporter’s own “should” sentences as criteria and routes code, commands, sessions and tracebacks to a new reproduction field that codegen still reads; the spec writer proposes Given/When/Then criteria from the report’s own words, each tagged with the sentence it rests on, and the service relabels any tag that isn’t an exact quote. Measured on a $2.43 replay of 25 of the 100 sampled P6 issues: 11 of the 17 name-parks from that sample no longer park (6 still do — real requirements naming something outside the repository), with zero junk criteria and no prose criterion lost on 8 comparison issues. The pre-registered quote check itself only passed 53% of the time, which is why the relabel step exists. The 26-of-100 figure below was measured on 3.60.0 and has not been re-measured against this change.

3.60.0 — the first end-to-end number, and three gate fixes it exposed

On a random sample of 100 SWE-bench Verified issues never run through the pipeline during development, Spine resolved 26 of 100 (Wilson 95% interval 18.4–35.4%) end to end with claude-opus-5 — the first published resolution rate: not a Verified score, not a leaderboard figure, and not compared with any other system (BENCHMARK.md). A separate, independent check re-scored investigate on 500 Verified issues: the fixing file lands in the top ten for 0.668 against a plain BM25 text search’s 0.702 — Spine is not measurably better than text search there. Running the 100-issue sample surfaced gate defects, now fixed: the validity gate weighs only the files codegen is actually shown rather than every file in the landing set (B89), the coverage gate stops failing a run over a file that’s comment-only or genuinely untested (B91), the acceptance judge no longer fails on a transcript it can’t reproduce (B92), and the validity gate no longer parks a bug over the reporter’s own reproduction example (B93). investigate now ranks landing sites on the ticket’s title, with the body only breaking ties (top-3 on 250 fresh issues: 46% → 54%, p = 0.013). A resumed sdlc autorun now reads a prior “build anyway?” yes correctly instead of parking again on the same refusal (B12), and the refine loop stops once two edits in a row leave a failing run unchanged (B80).

3.59.0 — unpriced models cost something, and the fixing file ranks higher

The run budget (SDLC_RUN_BUDGET_USD) now charges an estimate for a model call whose price the pinned table doesn’t list, instead of recording it as free: GPT-6 models made the price lookup raise, and the Claude 5.5 family (claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-fable-5-1) returned 0.0 without raising — either way the budget never tripped. The estimate uses SDLC_UNPRICED_USD_PER_MTOK (default 5,25 dollars per million input/output tokens); SDLC_MODEL_PRICES gives a model its exact price instead. Start- node search now ranks the fixing file higher and shows ten files rather than ten symbols on a pinned five-project bug corpus (top-3 on the 38 pinned labels: 16 → 18; top-10: 23 → 27). GPT-6 models route correctly, codegen’s file list is requested as native structured output, and a localization gate runs in CI. Graph fixes: Java enums, C# static calls, Python shadowed imports, Kotlin nested classes, a cross-repo Python re-export join, and a bin/ directory is now skipped only where it’s actually .NET build output. Upgrade note: a plan approved on 3.58.0 reads as stale once — several of these fixes change what the plan’s hashed sections say for the same spec; re-plan and re-approve.

3.58.0 — what a run tried, and tests on your own terms

Every codegen model call and test run of an sdlc autorun/sdlc feature run is now recorded to an attempts/ directory when one is given — the raw model reply, which files landed or were dropped and why, and each test run’s output — so a refine pass that ends with no file changes no longer leaves nothing to inspect. Credential-shaped environment variable values are redacted before anything is written. SDLC_TEST_COMMAND lets a project run its generated tests through a command you supply (typically a docker exec into an image with the project’s own environment) instead of the host’s own python -m pytest. A new SWE-bench Verified harness (orchestrator.evals.swebench) runs Spine’s pipeline on benchmark instances and keeps an honest ledger — it is the instrument, not a published benchmark number yet.

3.57.1 — grounded OpenSpec drafts, and accurate feature-coverage review

OpenSpec intent extraction now asks for, and carries into the generated proposal, an explicitly stated problem, users, observable outcome, and non-goals — without inventing missing fields; a cached draft re-extracts its source with orchestrator openspec draft --refresh. The feature runner also ignores .benchmark-tmp test scratch when identifying application code, and the acceptance judge now receives a bounded tracked diff before source excerpts, so temporary test files no longer count as feature coverage and the judge inspects the changed lines directly.

3.57.0 — untraced calls, and no more silently hidden matches

blast_radius and explain_symbol now report unresolved_calls for Python: the count of attribute calls with the same name whose receiver the graph could not type (so no edge could be drawn), a bounded list of the call sites, and how many functions in the repository share the name — a hint, never counted in caller_count. And blast_radius, explain_symbol and docs_for no longer cut a name shared by many symbols to its first five matches with no total; each now details the first 7 and reports match_count and truncated so a hidden match never reads as a complete answer.

3.56.0 — requirements get a clarity gate

Spine now checks requirements, not only builds from them. orchestrator openspec check <change> is a deterministic clarity gate — a stated problem, named users, an observable outcome, and every open question answered or deferred to a named owner — and, with a repository, it also lists which criteria already name code that exists. orchestrator openspec answer records an answer under its question with the channel Spine actually observed. Both ship as MCP tools too (requirements_check, requirements_answer), taking the MCP tool count to 34. No new model call anywhere in this; a malformed structured response from the model now gets a bounded corrective retry instead of ending a codegen run.

3.55.0 — extensive Rust validation

Extensive Rust support broadens the Core Rust release with measured async, procedural macro, no_std, and Cargo feature boundaries on supported hosts. Three hand-labelled corpus cases cover Tokio-style async calls, procedural-macro source boundaries, and no_std with core/alloc imports and conflicting cfg branches. All 14 Rust cases retain 1.00 precision on every emitted fact kind; the three known cross-module/workspace CALLS misses remain labelled. Pinned real-repository runs cover mini-redis, async-trait, embedded-hal, and Synaptreesitter, including one non-default Cargo feature and a clean-checkout async codegen patch. The extractor now normalizes external core/std trait aliases, and the verifier follows Rust semantic module containment. Before governed codegen, the runner detects a missing cross compilation target, linker, or custom JSON target and reports an actionable refusal. Macro expansion, type-aware dispatch, full feature matrices, and embedded firmware execution remain outside this release.

3.54.0 — required-behavior gates, and Core Rust support signed off

A project opts in by committing .spine/required-behavior.yaml: checks the model does not author and cannot edit away, re-verified after every refinement in sdlc feature’s native loop — closing the ONTM-4 gap where a generated test passed without exercising a feature’s real default wiring. [rust] comprehension (Cargo topology, precision-safe local calls) and Cargo-aware codegen/build/test are both signed off for supported hosts; the installed parser’s upstream grammar error is closed by pinning tree-sitter-rust-orchard.

Upgrade notes: a legacy project with no manifest takes the exact code path it took before — opting in to required-behavior gates is additive, not a breaking change.

3.53.0 — a typed-variable call graph for Python, and a locked pyjwt

A fixes release. Python’s call graph now reaches through a typed variable: a parameter annotation, a local assigned once from a constructor, or a self.attr typed by a class annotation now gives a method a caller in the graph — blast_radius, impact_of, docs_for and codegen’s refine all see it (+2,077 CALLS edges on Spine’s own repository; precision held at 1.00 with a new decoy-backed corpus case). The locked pyjwt moves from 2.13.0 to 2.15.1, closing ten published advisories. Concurrent sdlc features running on one worker no longer switch each other’s codegen guards off. The accuracy corpus was also relabeled: two Python cases filed as true calls were really refusals, so the published Python CALLS recall moves from 0.889 to 0.85 on the enlarged, more honest set.

Upgrade notes: a plan grounded in a method that gained callers can read as changed — re-approve it; pyjwt moves to 2.15.1 (ten advisories against the old pin).

3.52.0 — docs join the blast radius, and codegen stays in scope

blast_radius and explain_symbol now list the doc sections that describe a symbol — the repository’s own docs, plus Confluence and Jira pages and any RAG system’s chunks pulled over MCP — each match carrying doc_count and related_doc_count (pages naming its callers or the code it touches); a docs_unavailable reason is returned if linking fails, and the code answer still returns. New orchestrator mcp ingest-docs command pulls a repository’s external doc sources — declared under a new docs: block in .spine/repos.yaml (confluence, jira, or a rag: source backed by any RAG system’s MCP server, with an opt-in trust_read_only for unannotated tools) — into a cache outside the checkout, bound to symbols at read time; external docs never reach understand, state or episteme/, so CI stays reproducible. ${VAR} in mcp.json headers and urls now expands from the environment, so a bearer token no longer has to sit in the file. Codegen no longer edits files a ticket isn’t about (on OpenAI models this used to touch 6-9 unrelated files per run) — the design now separates Files to edit from Files to read (reference). Refine can now fix lint and type errors in the tests it wrote without dropping their asserts.

Upgrade notes: a plan whose ticket names a module can now read as changed and need approving again; a literal ${NAME} in an mcp.json header or url now needs NAME set in the environment, or escaped as $${NAME}.

3.51.1 — a sandbox timeout, reported reliably

A patch release: one sandbox fix. The local sandbox enforces run_python_analysis’s time limit twice — as wall-clock time and as RLIMIT_CPU — and a busy loop can reach both at once. When the CPU limit won, about one run in six, the tool raised ProcessLookupError or returned exit -24 with an empty stderr instead of a timeout. It now reports exit 124 and timeout after Ns either way, adding of CPU time when the CPU limit stopped it; a SIGKILL from the OOM killer before the limit is still reported as itself.

3.51.0 — --layout auto stops in every language, not only Python

3.50.0 stopped a Python sdlc feature run from scaffolding a new package beside .py files it couldn’t place; C#, TypeScript, Java, Kotlin, Go, C and C++ still scaffolded a .sln, package.json, pom.xml, build.gradle.kts, go.mod or CMakeLists.txt beside loose source that build would never compile. sdlc feature now stops with exit 2 and names the build file it looked for, across every language but SQL (whose scaffold is just a migrations/README.md and a .gitignore). sdlc plan reports a layout_would_stop finding before anyone approves the Build, so the plan doesn’t need to guess which --layout the Build will get.

A TypeScript, Go, C or C++ project living below the repo root (frontend/package.json, svc/go.mod, native/CMakeLists.txt) is now followed rather than refused — Spine picks the project holding the files the design names, else the one with the most source, as C#, Java and Kotlin already did, and dependencies install and tests run in that directory.

Upgrade notes: a Build that used to scaffold beside existing code now exits 2 — pass --layout existing (with --package-name) to build into the code that’s there, or --layout new to scaffold anyway. Plans for such repositories render a new finding, so an approval made before the upgrade reads as stale; approve the plan again.

3.50.0 — a damaged workspace is rebuilt, not reused; C# writes its real namespace

A fixes release driven by field reports from a .NET/Blazor and a Python/AWS SAM pilot. sdlc feature runs failed with git worktree add … fatal: not a git repository whenever the shared workspace base under /tmp/sdlc-workspaces lost .git/HEAD to macOS’s age-based /tmp sweep — a base is now reused only when git can read its HEAD commit, there’s one base per repository and branch under <root>/_bases/, and workspaces default to ~/.cache/orchestrator/sdlc-workspaces (SDLC_WORKSPACE_ROOT still wins). C# code now reads the project’s real namespace from <RootNamespace> — else what the project’s files declare, else <AssemblyName> — instead of assuming the .csproj file stem was the namespace, and an illegal namespace/using line is refused before it’s written rather than costing a dotnet test. dotnet test failures are no longer truncated to their last 4,000 characters, so refine sees the compiler’s first error instead of a warning cascade.

Also fixed: a Python run now baselines the suite once before codegen, so a later run whose every failure was already failing is reported, not counted (SDLC_TEST_BASELINE=0 skips this); --layout auto on Python now stops (exit 2) rather than scaffold a new package beside .py files it can’t place; an AWS SAM repository is recognized from template.yaml; and the plan writer is told the repo’s languages and the symbols a ticket’s words match, so it stops rating an invented spec “high”.

Before this release, ingest --refresh only ever rewrote the ticket-only intake entry — a ticket planned with --follow-links was cached separately, and nothing could re-extract it, so a Confluence page linked to the ticket after extraction never reached the spec even though the build document’s header named it. sdlc plan --refresh --follow-links now re-extracts that linked-pages entry (sdlc plan --refresh alone still re-extracts the ticket-only one, never both); it’s refused together with --spec (exit 2). A refresh that changes an approved spec prints a WARNING: naming who approved it, and says whether the approval still holds by comparing the re-rendered plan to the approved one — a mismatch parks sdlc autorun (exit 6) until sdlc approve runs again.

Also fixed: sdlc complete now renders the BACKLOG.md ledger that actually holds the merged intent when a --follow-links plan’s intent is named differently by the ticket-only plan (it used to render the ticket-only ledger and show 0 / 1 done). And the --follow-links intake extractor’s bounded view of a ticket’s attachments now keeps reading past its first 20 files whenever the full read still has room, matching 3.44.0’s byte-for-byte output — a gap that, on tickets with 21-30 readable attachments, dropped about 1 in 100 attachments from the spec.

3.48.0 — the review’s fixes ship in the PR, and a budget cap that actually applies

sdlc autorun --live used to open the pull request before the review that follows fixed anything it found — so the PR never carried those fixes. Now the build commits locally, the review runs, its fixes are committed as <KEY>: review fixes, and only then does the PR open. A review that doesn’t finish clean (unresolved findings, a fix that broke the tests, a crash) opens a draft PR saying why and exits 1 instead of ending done. SDLC_RUN_BUDGET_USD now caps sdlc feature and sdlc autorun too — previously only the Temporal worker applied it — at $25 by default (0 disables); a resumed run continues from what it already spent. Upgrade note: a run that spends more than the cap without --max-cost now stops instead of continuing uncapped, and a script that treated autorun’s exit 0 as success will see exit 1 for a run whose review left findings unresolved.

The Java/C# type lookup that resolves a typed receiver, static call, creation or DI binding now sees every binding closer than the one it used to find — a local class hiding a same-named package type, an anonymous class body’s own member types first, a qualified name’s head resolved before its tail, and a member type an external base class declares. On guava this moves 16 edges, and all 16 are now right; four other field repositories move none. CALLS recall on the corpus: Java 0.92, C# 0.89.

3.47.0 — calls through a typed receiver, and callers reached through an interface

Java and C# calls through a field, parameter, typed local, or var x = new T() (and, in C#, static calls on an in-repo type) now land on the member the declared type means — both front-ends used to drop them: on a .NET service 2,427 such calls went from 0 resolved to all 2,427 (CALLS 775 → 5,456 in the whole graph); on mysql-connector-j, 334 of 14,984 went to 12,962. Constructor calls (new Order(7), object initializers, anonymous classes) now land as CALLS to the created type — the corpus rule Python and Kotlin already followed. ASP.NET Core DI registrations (services.AddScoped<IMailer, SmtpMailer>()) become PROVIDES edges, the same binding Kotlin’s Hilt/Dagger wiring introduced. blast_radius now also reports interface_callers: the callers of each member an implementation fulfils, reached through IMPLEMENTS/PROVIDES — because dependency injection means nothing calls the implementation by name, so its blast radius used to read empty.

Fixed: C# base types reached through a sibling-namespace using (46 of 77 “external” bases on a .NET service were in-repo interfaces); a Java interface X extends Y now emits its IMPLEMENTS edge; and Java static calls now settle only once every file in the repository is known, so a capitalized field access is no longer misread as a static call. Upgrade note: Java and C# graphs gain many CALLS, IMPLEMENTS and (C#) PROVIDES edges — regenerate episteme/ with orchestrator understand ..

3.46.0 — graphs that say who really calls what

A Python call through a re-export (from app import Store; Store()) now lands on the symbol that defines it instead of an external placeholder — on Spine’s own graph that moved 1,271 CALLS (5.5%) onto their real targets, across 141 previously-phantom twins; pkg verify now warns on any phantom-symbol left (an external node carrying calls while a first-party symbol of the same name lives under the same path). Upgrade note: Python graphs change wherever a repository re-exports — regenerate episteme/ with orchestrator understand ..

Three Kotlin fixes: a one-line body (interface Iface { fun f() }) no longer drops every declaration after it in the same file (a tree-sitter-kotlin parse ambiguity); two Room/DI recall gaps from wildcard-imported entities and cross-file same-package extensions are closed; and a bare call inside with(x) { } / x.apply { } / x.run { } no longer lands on the enclosing class instead of the receiver (17 invented self-loops removed from the Android validation app) — trading false edges for an honest gap moved Kotlin CALLS recall 0.93 → 0.85 on the corpus, as the new case labels the true targets the rule gives up as known gaps.

3.45.0 — a plan’s criteria are checked against the whole ticket

sdlc plan --source now reads the ticket fresh at every plan, with every attachment read in full (up to 20 files) — the acceptance-criteria check used to run against whatever survived the intake extractor’s caps (5 attachments, 8,000 characters each). The AI that derives the spec still reads exactly the same bounded summary as before, so no approved run re-parks. --follow-links also reads the Confluence pages a ticket links to (at most 5, and refuses rather than planning from less than was asked for without Confluence access). A Jira ticket read through an MCP server now carries its comments, links and attachments like one read over REST, and an unreadable ticket is a clean error, not a traceback. Removed: sdlc plan --out and sdlc approve --out, deprecated in 3.44.0.

3.44.0 — an approved plan is the plan that builds

sdlc plan → sdlc approve → sdlc autorun could refuse a plan nobody had changed: writing the plan into .spine/plans/ made the repo look modified, so the gate re-derived a -dirty document and refused the approval it had just been given — the exact sequence the spine-sdlc.yml build job runs. A Bug that matched no code lost its approval because the gate forgot the issue type, and --spec with --source crashed. All three are fixed, and a Jira ticket read through an MCP server now says it is the description only. sdlc plan --out and sdlc approve --out are deprecated — a plan written outside .spine/plans is one autorun can never build.

3.43.1 — a Python src/-layout fix

A repository whose src/ is a real package — it has an __init__.py, and the code imports through it (from src.services.x import …) — got a graph with no in-repo imports: module ids dropped the leading src. while the imports kept it, so nothing joined and pkg verify reported nearly every module as imported by nothing. src/ is now stripped only when it’s a plain source root (no __init__.py); a package src/ keeps its name — the one Python actually resolves. The standard src layout is unchanged.

3.43.0 — JavaScript, the 13th front-end

.js, .jsx, .mjs and .cjs used to reach the walker with no front-end and produce nothing — express and react-boilerplate extracted to zero nodes. JavaScript now rides the existing typescript extra (no new install) and extracts CommonJS require/exports, JSX, Express routes (including member handlers), and a Sequelize data layer. Twenty hand-labelled corpus cases: precision 1.00 on every node and edge kind, CALLS recall 0.97, zero dangling edges on four real repositories. Comprehension only — there is no JavaScript codegen toolchain.

Also fixed, in TypeScript as well: import './mod.js' and from '..' no longer mint modules no file declares, and sdlc plan now prices from the shipped LiteLLM map, so the same commit always writes the same build document.

3.42.0 — Kotlin precision, and an accuracy gate that stopped punishing honesty

Six reported Kotlin defects are closed — four of them measured on the Android validation app, every gate green throughout, because each fabricated edge had replaced a true one rather than adding to the count.

  • An inherited member overridden partway up the hierarchy now resolves (#391). The walk matches level by level instead of collecting every hit across the whole hierarchy, so Impl : Mid : Base no longer refuses Kotlin’s commonest inheritance shape as ambiguous.
  • A destructured lambda parameter now shadows a sibling member (#392) — fixed in both the extractor and the invention oracle that shared the same blind spot.
  • A nested generic wrapper no longer unwraps past its real key (#393): Provider<Set<OkHttpClient>> reduced to OkHttpClient instead of the Dagger key Set<OkHttpClient>. Wrapper types are now recognised by resolved id rather than bare name.
  • An extension call on a subtype receiver no longer gets redirected to an invented id. fun NavController.navigateToSearch() called on a NavHostController — the standard Compose pattern — kept its real edge instead of losing it to a fabricated placeholder; closes #390.
  • A Kotlin extension-table cache that leaked between repositories on --repos runs, and a Ktor mount / Compose route that failed to resolve in the default package, are both fixed.
  • The accuracy gate no longer fails a build for writing down a known limitation. Corpus recall is now gated on unexplained misses rather than the recall ratio, so labelling a known_gaps entry can no longer read as a regression. The published score is unchanged. scoreboard.json is version 2 — run orchestrator pkg accuracy --scoreboard once after upgrading.

3.41.1 — 2026-09-21 · bug-fix release

No new user-facing capability. An internal Grounding type-name collision (intake.pkg_evidence’s type is now DraftGrounding — found via blast_radius, which returned two matches where grep returned one undifferentiated pile) and three previously-hidden-red integration tests — a stale “no auth needed” test comment, an Alembic logger-config bug (fileConfig’s disable_existing_loggers default silencing a session-scoped fixture’s logs), and a docker-compose dependency gap — are fixed.

Earlier highlights

  • 3.41.0 — openspec draft can read the repository. Its signature was --source/--out/--refresh/--overwrite, so a drafted change could only restate the ticket more formally. It now takes a repo path (or --repos), and the proposal carries a ## Grounding section — where the change lands, with file:line, and each stated acceptance criterion bound against the graph; tasks.md becomes one checkbox per criterion.
  • 3.40.0 — briefs quote the code they locate. investigate and root-cause used to render only the graph’s index — symbol names, file:line, caller counts. Landings now show the source at the line, weak landings are never quoted, coverage is three-valued, and a merged multi-repo brief reads each repository’s own episteme/ under its key.
  • 3.39.0 — builds land in the project the ticket names. A field report from a Nucor engagement: sdlc feature scaffolded a WebApp ticket into an unrelated API client and failed after six test runs, because the build target was whichever project sorted first. It now picks the project holding the files the design names, else the one with the most source in the language being resolved; --package-name retargets rather than only renaming. Java multi-module repositories resolve at any depth, and a Kotlin scope-function check now looks at shape as well as name.
  • 3.38.0 — two field reports from a React Native engagement: a vendored ios/Pods is no longer walked and a symlinked file keeps its own path, so node_modules cannot return a third-party header one path at a time; --language auto weighs what most of the source is; the coverage probe withdraws a test it wrote itself rather than chasing it until the budget dies; retrieval reads a ticket’s inflections, so “account deletion” reaches DeleteAccountScreen.
  • 3.37.0 — Blazor components enter the graph. .razor is read as line-aligned C# through the existing C# front-end (no new grammar or extra), at precision 1.00 / recall 1.00. Jira attachments are read on intake, not only named. Retrieval carries its evidence and floors weak hits, so an all-weak ticket says locate the change before building instead of proposing confident wrong paths. A file in a shared namespace is no longer reported absent from the knowledge graph.
  • 3.36.0 — Kotlin: the 11th language, 12th front-end. Comprehension and a typed-receiver call graph; the Android data layer (Room entities, DAO READS/WRITES parsed from @Query, Retrofit calls as cross-repo consumer evidence); Ktor and Spring MVC routes through a module shared with Java; Compose navigation as NAV-verb Endpoints; PROVIDES — a twelfth edge kind — for Hilt/Dagger wiring; Kotlin Multiplatform source sets; and Kotlin/JVM + Android codegen via a new Gradle-aware test runner. The measured corpus grew to 64 hand-labelled fixture cases across all 12 front-ends; precision stays 1.00 on every node and edge kind, in all 11 languages.
  • 3.35.0 — graph export to Neo4j/Memgraph (orchestrator pkg export --format cypher) and an optional C/C++ semantic pass (pip install 'synaptixs-spine[clang]', also in [all]) that resolves member calls the CST cannot, adding edges only between symbols already in the graph.
  • 3.34.2 — maintainer tooling: a generic plan skeleton every development plan starts from, and a roadmap-currency gate that can check a plan kept outside the checkout. No engine changes.
  • 3.34.1 — Documentation has one home per task. AGENT_GUIDE.md replaces the separate Claude Code and Codex guides, which now redirect to it; its MCP tool inventory is generated and checked from tool metadata, not hand-maintained. SETUP.md owns installation, credentials and troubleshooting; USER_GUIDE.md covers the everyday build; OPERATIONS.md holds the pipeline, dashboard and MCP walkthrough. README.md now opens with the worked example and absorbs the former standalone FEATURES.md capability catalog. No engine changes — the wheel is identical to 3.34.0.
  • 3.34.0 — Perl ships end to end, and one registry owns every language. Perl becomes the 10th front-end — comprehension (Module/Type/Function/Field onto the universal vocabulary, inheritance resolved across five literal spellings), CALLS, Mojolicious/Dancer2 routes as Endpoints, and a DBIx::Class data layer as Entitys — plus codegen (layout, scaffold, observed conventions, Perl::Critic, then prove, with optional cpanm), proved live on greenfield and brownfield checkouts. One toolchain registry replaces per-language if chains across codegen, layout, scaffolding and the test environment, measured by a mutation set that went from catching 4 of 8 dispatch behaviours to 8 of 8; claims (language list, corpus count, CALLS recall) are now derived from that registry and the scoreboard instead of hand-written.
  • 3.33.2 — the SDLC runs as a pipeline, and PHP builds. .github/workflows/spine-sdlc.yml is a workflow_call workflow another repository references in a few lines: a credential-free plan job, then a build job bound to a GitHub Environment whose required reviewers are the plan gate — safe by default, live: true to open the PR. PHP code generation ships (Composer or a checksum-pinned PHPUnit PHAR), making it the 8th language with codegen. A nested git checkout is now a boundary — a superproject’s graph stops at its submodules instead of double-counting every symbol.
  • 3.33.1 — the documents say only what is true, and the help screen shows only what you need. The first full run of a documentation-drift audit fixed fifteen stale claims — including ten places that still said “8 front-ends” after PHP became the ninth, and the accuracy corpus count, which had stood at “19” while the committed corpus had grown to 38 fixture cases (34 single-language, 4 multi-repo). Thirteen platform-substrate and maintainer gold-set commands are hidden from --help — still registered, documented and invocable; the total command count is unchanged at 55.
  • 3.33.0 — a PHP codebase gets the whole graph. PHP is the 9th PKG front-end: comprehension (namespaces, classes, interfaces, traits, enums onto the universal vocabulary), a precision-first call graph ($this->, self::, parent::, new, static calls, typed receivers — never a guessed global function or a method named by a variable), Laravel/Slim/Symfony routes as Endpoints, and Eloquent/Doctrine models as Entitys. Codegen isn’t shipped for PHP yet. pip install 'synaptixs-spine[php]'. A maintainer review of the new front-end found, and this release fixed, five ways it asserted a fact the source didn’t contain, and a persistence-cache key that couldn’t tell whether the PHP extra was installed.
  • 3.32 — the plugin speaks the whole protocol: five MCP prompts and five spine:// resources, every comprehension tool answers across repositories, long-running tools report progress per stage, every tool advertises what it returns as a schema, and per-principal audit lands on the HTTP transport.
  • 3.31 — the plugin grows from twenty tools to thirty-two: operator tools (registry_runs, registry_approvals, registry_trace, registry_decide) replace the removed terminal UI; understand_repo, profile_repo, design_change and sdlc_baseline join the free, read-only tier; sdlc_address_review, sdlc_complete and sdlc_remediate close the loop after a PR. Every tool carries an MCP tier (read-only, destructive, idempotent, open-world) and, on HTTP, a checked scope.
  • 3.30 — a reusable comprehension GitHub Action: three lines in another repository’s workflow get a pull-request comment saying where its own change lands, with file:line and caller counts, and no secrets: beyond the caller’s own GITHUB_TOKEN. Also: a pluggable secrets seam (get_secret), and RBAC lands as opt-in.
  • 3.29 / 3.28 / 3.27 — cross-repo comprehension hardens: a Node or Go service can be the thing you call, cross-repo edges stop pointing at nodes that don’t exist, and TypeScript stops skipping calls it couldn’t type.
  • 3.26 — Spine measures whether it finds the right file, and publishes the number (orchestrator pkg accuracy).
  • 3.24 / 3.23 — multi-repo reaches the plugin surface; one graph across several repositories.

The standing facts

  • Comprehension across 14 front-ends — 13 languages (Python, Java, TypeScript, JavaScript, C#, C, C++, Go, Rust, PHP, Perl, Kotlin, SQL) plus a .kts Gradle-script reader — and 7 document formats; media (image OCR, audio/video ASR) is opt-in and off by default.
  • 55 CLI commands across 7 areas; 34 MCP tools (23 read-only, including the 8 comprehension tools; 11 write, each gated behind a human approval).
  • The knowledge graph carries 8 node kinds and 12 edge kinds.
  • Measured graph accuracy (precision/recall per kind) covers 13 front-ends in the accuracy corpus — 108 hand-labelled fixture cases; precision is 1.00 on every node and edge kind, in all 12 languages.
  • On a pinned five-project corpus, bug localization puts the fixing file in the top 10 for 27 of 38 tickets (top-1: 12/38) — see BENCHMARK.md.
  • On an independent, pre-registered check against 500 SWE-bench Verified issues, the fixing file lands in the top ten for 0.668 — a plain BM25 text search scores 0.702, so Spine is not measurably better than text search at that task.
  • First published end-to-end number: on 100 fresh SWE-bench Verified issues, Spine resolved 26 of 100 (95% interval 18.4–35.4%) — not a Verified score, not a leaderboard figure.
  • Deterministic, no-LLM comprehension — byte-identical reports for a given commit.