Skip to content

julie-extract v2.8.0

Choose a tag to compare

@github-actions github-actions released this 04 Jul 13:40

v2.8.0

This minor release finishes the backend HTTP boundary lane: server-side
route/mount fact families for six more mainstream backend stacks (NestJS,
Kotlin + Spring, Laravel, Phoenix, axum, actix), the http.client_request.v1
extension to four more languages (Kotlin, PHP, Elixir, Rust), and per-framework
prefix-registration ("mount") families — all joined on the same
normalized_route_template (:param flavor) key introduced in v2.7.0, so
Miller can bridge outbound client calls to handler definitions across these
stacks too.

The static-vs-dynamic silence decision for the four new languages is made by a
single shared AST whole-argument allowlist helper (static_arg.rs, ADR-0005),
not by extending the byte-level source mask — see Contract Changes.

New Structural-Fact Families

Handler-definition facts (all emit route_template plus the cross-family
normalized_route_template join key in :param flavor, with attested
verb/verb_source and prefix/effective keys where the framework has them):

  • TypeScript/JavaScript (NestJS): nestjs.route.v1@Get/@Post/… (and
    @All, verb omitted) method decorators, joined to the @Controller class
    prefix.
  • Kotlin (Spring): reuses the existing spring.request_mapping.v1 id
    (languages extended to [java, kotlin]) — @GetMapping/@RequestMapping
    with class-prefix join, bracket multi-path arrays, and method=[...]
    multi-verb expansion.
  • PHP (Laravel): laravel.route.v1, laravel.resource_route.v1,
    laravel.route_prefix.v1Route::get/match/any, Route::resource,
    and Route::prefix(...)->group(...) / Route::group(['prefix'=>...]) prefix
    joins.
  • Elixir (Phoenix): phoenix.route.v1, phoenix.resource_route.v1,
    phoenix.forward.v1 — router macros with nested scope prefixes,
    resources, and forward.
  • Rust (axum): axum.route.v1, axum.nest.v1Router::new().route(...)
    with per-verb method-router expansion and .nest(...) mounts;
    single-assignment receiver tracing (poison on non-literal).
  • Rust (actix): actix.attribute_route.v1, actix.scope_route.v1,
    actix.mount.v1#[get(...)]/#[route(...)] attribute macros (verb
    always), web::scope(...).route(...) same-file scope joins, and
    web::scope(...).configure/.service mounts. Mirrors the shipped ASP.NET
    attribute-vs-call routing split.

Outbound-call facts: http.client_request.v1 now also covers Kotlin (Ktor
client receiver.verb(url)), PHP (Guzzle and the Laravel Http facade),
Elixir (Req), and Rust (reqwest reqwest::get / client.get), in addition to
the v2.7.0 coverage.

Cross-Family Doctrine (unchanged from v2.7.0)

  • normalized_route_template remains the universal join key: :param flavor,
    computed per family from the raw source template.
  • M2 static-literal silence: only static string literals emit route or client
    facts. Interpolations, concatenations, format!/sprintf/macro forms,
    heredocs/sigils with interpolation, and identifier/const arguments stay
    silent; a non-literal prefix poisons the prefix chain rather than guessing.
  • Verbs are recorded only when source-attested; verbless registrations
    (@All, actix web::route(), axum any, method-less @RequestMapping)
    omit verb.
  • Prefix-registration ("mount") facts emit at the prefix's own definition site
    with mount_path/normalized_mount_path (and mount_target where a
    same-file target exists); same-file-resolvable prefixes also flow into
    route_group_prefix/effective_route_template on the route fact itself.

Static-Literal Detection (ADR-0005)

The four new languages make the static-vs-dynamic decision via
static_route_arg(node, content, lang) — an AST allowlist run on the whole
route/URL argument expression, returning the literal's text only for an
approved static string-literal node and None for every wrapper. The vendored
tree-sitter grammars differ from a naive "interpolation-child" reading, so each
arm carries a grammar-verified guard: Kotlin also rejects an unescaped $ in
string_content; Elixir also rejects #{ in quoted_content (the ~S
capital-sigil case) and requires sigil_name ∈ {s, S}; PHP runs its allowlist
on the heredoc_body/nowdoc_body child. The byte-level SourceMask in
scan.rs is deliberately not extended to these languages.

Contract Changes

  • Rust package versions change from 2.7.0 to 2.8.0.
  • EXTRACTION_CONTRACT_VERSION gains the trailing backend-http-boundary-v2
    marker (new pattern ids and new http.client_request.v1 languages change the
    canonical extraction shape).
  • docs/contracts/structural-fact-patterns.json adds the new NestJS/Laravel/
    Phoenix/axum/actix route and mount specs and the Kotlin extension of
    spring.request_mapping.v1; the http.client_request.v1 spec gains
    kotlin/php/elixir/rust.
  • SQLite schema version remains 3. Artifact integer
    extract_contract_version remains 3. JSONL schema version remains 3.
    JSON report schema version remains 3.
  • Parser dependency versions are unchanged.

Documented Exclusions

Per-language deferrals are recorded as open_gaps (reason + closure task) on
the capability rows, not silent gaps: Ktor server routing and
RouterFunction/coRouter (Kotlin); Symfony #[Route] attributes and
cross-file RouteServiceProvider prefixes (PHP); LiveView/socket/channel and
cross-file scope/forward prefixes (Elixir); actix cross-file
configure/service scopes, web::resource guard forms, axum 0.7 :id
recording, cross-file .nest joins, and deferred clients
(OkHttp/Retrofit/WebClient, Symfony HttpClient/curl, Tesla/HTTPoison/Finch,
hyper/ureq). nestjs.global_prefix is out by design (no safe Miller consumer).

Miller Consumer Follow-Up (cross-repo)

docs/plans/2026-07-03-miller-http-boundary-consumer-followup.md tracks the
Miller-side work. Verified against the current consumer: Kotlin Spring routes
(reused spring.request_mapping.v1) and all four new client languages (reused,
non-language-gated http.client_request.v1) join today with zero Miller
changes
. The eight new server-route ids and four new mount families need
whitelist + route-family wiring on Miller's side to join; they are correct and
evidence-bearing regardless, and same-file prefixes degrade gracefully via
effective_route_template.

Verification

Local and release gates passed for commit
2dbdcad327f8025fe0f6c3aedba6ece73e8323a1:

  • cargo test --workspace --quiet
  • cargo clippy --workspace --all-targets --all-features --no-deps -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check HEAD
  • cargo test -p julie-extractors --features test-golden structural_fact_registry
  • cargo test -p julie-extractors --features test-golden golden_fixtures_match_canonical_extraction
  • cargo test -p julie-extractors --features test-capability-matrix capability_matrix
  • node scripts/language-data-quality-report.mjs --strict (silent_cells 0, quality_bar_debts 0)
  • scripts/check-agent-doc-sync.sh (AGENTS.md/CLAUDE.md synced)
  • cargo xtask release preflight --version 2.8.0 (4 targets, 19 inputs)
  • GitHub CI run 28707779894: Fast Gates, passed
  • Release workflow run 28707784242: all four binary builds and the GitHub Release job passed
  • Downloaded published Apple Silicon binary dogfood passed:
    julie-extract 2.8.0

Published Assets

Target Asset Size GitHub asset digest
Linux x86_64 julie-extract-v2.8.0-x86_64-unknown-linux-gnu.tar.gz 10,758,539 bytes sha256:185bd0cdd2b0ea45ab588fbf01fbc0d83953453c4423115cf4febcadf034ac8f
macOS Apple Silicon julie-extract-v2.8.0-aarch64-apple-darwin.tar.gz 10,523,676 bytes sha256:75424faf62b98afd02f5268919dd5269db6146b1e410ea250a20a10e9afae8af
macOS Intel julie-extract-v2.8.0-x86_64-apple-darwin.tar.gz 10,458,063 bytes sha256:12c3e6271cb73226bad1dcee6a8e508c04a3e2d7b312866352cb6b19b9b1c6d2
Windows x86_64 julie-extract-v2.8.0-x86_64-pc-windows-msvc.zip 9,759,538 bytes sha256:fb2d2df16ddb92eea4d47bee384bdf225a63b7a054087cf56f462a73a3128e0b

Release metadata verified that v2.8.0 is not a draft and not a prerelease.
Downloaded assets matched the GitHub asset digests, and each archive stores the
binary under dist/<target>/julie-extract or dist/<target>/julie-extract.exe
with the checksum file beside it.