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”.
3.49.0 — sdlc plan --refresh --follow-links can re-extract a linked-pages spec
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 : Baseno 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 toOkHttpClientinstead of the Dagger keySet<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 aNavHostController— 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
--reposruns, 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_gapsentry can no longer read as a regression. The published score is unchanged.scoreboard.jsonis version 2 — runorchestrator pkg accuracy --scoreboardonce 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 draftcan 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## Groundingsection — where the change lands, withfile:line, and each stated acceptance criterion bound against the graph;tasks.mdbecomes one checkbox per criterion. - 3.40.0 — briefs quote the code they locate.
investigateandroot-causeused 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 ownepisteme/under its key. - 3.39.0 — builds land in the project the ticket names. A field report from a Nucor
engagement:
sdlc featurescaffolded 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-nameretargets 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/Podsis no longer walked and a symlinked file keeps its own path, sonode_modulescannot return a third-party header one path at a time;--language autoweighs 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” reachesDeleteAccountScreen. - 3.37.0 — Blazor components enter the graph.
.razoris 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/WRITESparsed from@Query, Retrofit calls as cross-repo consumer evidence); Ktor and Spring MVC routes through a module shared with Java; Compose navigation asNAV-verbEndpoints;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.mdnow opens with the worked example and absorbs the former standaloneFEATURES.mdcapability 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/Fieldonto the universal vocabulary, inheritance resolved across five literal spellings),CALLS, Mojolicious/Dancer2 routes asEndpoints, and a DBIx::Class data layer asEntitys — plus codegen (layout, scaffold, observed conventions,Perl::Critic, thenprove, with optionalcpanm), proved live on greenfield and brownfield checkouts. One toolchain registry replaces per-languageifchains 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,CALLSrecall) 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.ymlis aworkflow_callworkflow another repository references in a few lines: a credential-freeplanjob, then abuildjob bound to a GitHub Environment whose required reviewers are the plan gate — safe by default,live: trueto 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 asEndpoints, and Eloquent/Doctrine models asEntitys. 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_changeandsdlc_baselinejoin the free, read-only tier;sdlc_address_review,sdlc_completeandsdlc_remediateclose 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:lineand caller counts, and nosecrets:beyond the caller’s ownGITHUB_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
.ktsGradle-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.