Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b5461d8
docs: amend the L3/L4 spec to the re-checked L4 design
sinha108 Oct 25, 2026
5d77600
fix: share one call/closure walker; real positions under //line (#26)
sinha108 Oct 25, 2026
7aa2b1a
fix(l2): resolve call sites inside closures (#17)
sinha108 Oct 25, 2026
02a034e
feat(l3): model closure capture writes and deferred-operand writes (#22)
sinha108 Oct 25, 2026
9d5286c
feat(l3): writes through reference fields of value structs; bucketed …
sinha108 Oct 25, 2026
e8f219b
perf: stream analysis.json and release ASTs after use (#27)
sinha108 Oct 25, 2026
d0eb60f
feat(l4): accept -a 4 and declare the L4 schema fields
sinha108 Oct 25, 2026
4b2d0af
feat(l4): build go/ssa over the project load
sinha108 Oct 25, 2026
69aa99a
fix(l4): rebuild SSA without a package whose build panicked; recover …
sinha108 Oct 25, 2026
845deab
feat(l4): join go/ssa to the L3 units
sinha108 Oct 26, 2026
6f56ed8
fix(l4): fnUnit joins only units; interface methods have their own ac…
sinha108 Oct 26, 2026
c6f2bd4
fix(l1): keep methods declared above their receiver type
sinha108 Oct 26, 2026
c91351c
feat(l4): VTA dispatch targets in call_graph
sinha108 Oct 26, 2026
cd38f26
feat(l4): global and mutation footprints over call-graph SCCs
sinha108 Oct 26, 2026
bc6da6c
fix(l4): footprints over-approximate aliases, skipped units, literals…
sinha108 Oct 26, 2026
38750c5
fix(l4): every function literal is a callee of its encloser; honour T…
sinha108 Oct 26, 2026
fb4e907
feat(l4): formal and actual vertices
sinha108 Oct 26, 2026
396e563
feat(l4): port wiring ddg edges
sinha108 Oct 26, 2026
723fe48
feat(l4): summaries and parameter edges
sinha108 Oct 26, 2026
34ecbde
feat(l4): library stand-ins and --library-models
sinha108 Oct 26, 2026
e340fdd
perf(l4): release SDG caches early, intern port names, lower GC targe…
sinha108 Oct 26, 2026
3c671f0
feat(l4): emit the SDG in JSON and Neo4j
sinha108 Oct 26, 2026
57ff5a4
feat(l4): type-based may-alias points-to edges
sinha108 Oct 27, 2026
a2be9a4
fix(l4): may-alias compares pointee types, aliases interfaces and add…
sinha108 Oct 27, 2026
c74c11b
fix(l4): may-alias keeps distinct-def entry edges, both anchors at of…
sinha108 Oct 27, 2026
7ca9c57
fix(l4): give a reference-holding capture its formal_out
sinha108 Oct 27, 2026
7c29d44
test(l4): metamorphic relations for L3/L4 on fixtures and real apps
sinha108 Oct 27, 2026
40b03ea
fix(l3): a closure's host writes through every reference-holding capture
sinha108 Oct 27, 2026
3331abe
test(l4): metamorphic harness race, CDG mismatch as violation, tighte…
sinha108 Oct 27, 2026
30d7459
test(l4): metamorphic examples module and memos-default real-app run
sinha108 Oct 27, 2026
c227081
test(l4): metamorphic script rejects unknown flags; unique statement …
sinha108 Oct 27, 2026
104d7e5
fix(neo4j): force a full push when the stored level is lower
sinha108 Oct 27, 2026
b0092f5
fix(l4): deterministic id for duplicate callable keys
sinha108 Oct 27, 2026
7b5b35d
test(l4): no-dangling gates cover the L4 projection and degraded call…
sinha108 Oct 27, 2026
c5f14ea
fix(eval): a failed -a 4 run fails the app and the script
sinha108 Oct 27, 2026
587a3df
ci: run the metamorphic script argument checks
sinha108 Oct 27, 2026
0607ef0
test(l4): a released SDG cache rebuilt before fill ends fails
sinha108 Oct 27, 2026
2112620
test(neo4j): live lazy L4 push over a lower-level graph equals an eag…
sinha108 Oct 27, 2026
0724e20
fix(neo4j): force a full push when the stored level is not an integer
sinha108 Oct 27, 2026
b3ba3dc
test(l4): count summary recomputes; assert the duplicate-key warning
sinha108 Oct 27, 2026
1fac5b0
test(l4): HTTP-to-SQL done fixture
sinha108 Oct 27, 2026
a2e7ea0
feat(eval): -a 4 in the evaluation
sinha108 Oct 27, 2026
a98f6b7
docs: L4 evaluation, spec and README
sinha108 Oct 27, 2026
69d1204
docs: L4 overlay live in the Neo4j spec; measured memory; eval keeps …
sinha108 Oct 27, 2026
c9d1c00
test(l4): done gate checks a no-flow pair inside the DB plumbing
sinha108 Oct 27, 2026
6c7a011
test(l4): done gate pins the sanitizer as a variable cut
sinha108 Oct 27, 2026
d90aadf
docs: library models change summaries, not statement-level reachability
sinha108 Oct 27, 2026
4fb14b5
fix(l4): follow VTA through synthetic wrappers
sinha108 Oct 27, 2026
3752427
fix(l4): SSA for every package variant
sinha108 Oct 27, 2026
4a2121d
fix(l4): call_graph edges only for targets VTA produced
sinha108 Oct 27, 2026
7d6faa1
refactor(l4): declKey is a free function
sinha108 Oct 27, 2026
0a3ea12
fix(l4): CHA targets for interface calls on library-originated values
sinha108 Oct 27, 2026
0767221
fix(l4): emit partial_types
sinha108 Oct 27, 2026
1be1643
fix(neo4j): clean push clears a stale degraded_json
sinha108 Oct 27, 2026
ce41831
docs: PR #16 merged, L4 live; partial_types and SSA variants in the s…
sinha108 Oct 27, 2026
ffda1c2
docs: the CHA complement's origin walk is intraprocedural
sinha108 Oct 27, 2026
7762b6e
fix(l4): library-origin is interprocedural for the CHA complement
sinha108 Oct 28, 2026
34f70bb
docs: interprocedural library origin and no_cha
sinha108 Oct 28, 2026
d6908f8
fix(l4): library-origin reads only fixed or tracked facts
sinha108 Oct 28, 2026
0e2eb20
docs: what drives the CHA complement's volume; the fixpoint's order i…
sinha108 Oct 28, 2026
e203f8c
fix: analysis.json honours the umask and is synced before the rename
sinha108 Oct 28, 2026
e950b68
fix(l4): bind receivers without a signature, classify fileless callee…
sinha108 Oct 28, 2026
ab6fb7a
test(l4): self-test the superset gate; determinism compares stand-ins
sinha108 Oct 28, 2026
98ebfef
fix: create analysis.json's temp file with the umask, not a umask rou…
sinha108 Oct 28, 2026
278b981
docs: L4 evaluation at the final head
sinha108 Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
- name: Test
run: go test ./... -count=1

- name: Metamorphic script arguments
run: scripts/metamorphic_args_test.sh

# The release binary is built with the latest Go (release.yml), so test on
# it too; the job above tests the go.mod minimum.
test-stable:
Expand Down
64 changes: 51 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,39 @@ committed spec is the full transcript: `docs/design/specs/v2-l1-emission.md`.
- **`k_limit`** (envelope, L3+ only) = `--graph-field-depth` (default 3), the access-path
bound of the syntactic `ddg` (`x.f.g.h` → `x.f.g.*`); `ddg.prov` is `["ssa"]` (parity).

### Deferred (not this train)
- L4 (SDG param edges) — reuses Group A ids/spans and the L3 vocabulary unchanged. Designed in
`docs/design/specs/v2-l3-l4-dataflow.md`. A Go-specific `select` cfg kind was not added
(`select` is a `switch` with `switch_case` edges).
### L4 SDG (`-a 4`; spec `docs/design/specs/v2-l3-l4-dataflow.md` §4.6–§4.16, §6)
Reuses Group A ids/spans and the L3 vocabulary unchanged; every L4 fact is an addition
(`-a 3` ⊆ `-a 4`, gated). Leaves:
- **Vertices** (body nodes, span-less, no `callee`): `@formal_in:<i>`, `@formal_out[:<i>]`,
`<call-key>/actual_in:<i>`, `<call-key>/actual_out[:<i>]` (Python's `IdentityMap` indexing:
`formal_in`/`actual_in` always indexed; an out is bare iff its group has one vertex). Actuals
carry `parent` = the call key (a non-call capture site: the literal's host). **`of`** (D6,
Python spelling, which the SDK's resolver reads): the param/receiver name; unnamed or `_` param
`_<i>` (declared index, receiver excluded); unnamed or `_` receiver **`_recv`**; results
`<return>` / **`<return>:<i>`** (never user result names; `:<i>` is a Go leaf);
`<capture>:<name>`; `<global>:<pkgpath>::<Name>`. The SDK reads `of`, never
`parameters[]` (`formal_in:0` is a method's receiver).
- **Edges:** application `param_in[]` / `param_out[]` `{src, dst, var}` (global ids, `var` = the
callee formal's `of`); callable `summary[] {src, dst}` (local keys, actual_in → actual_out);
port wiring in `ddg` with `prov: ["reaching-defs"]` (Python precedent; coarse host wiring:
host → every actual_in, every actual_out → host); may-alias `ddg` with `prov: ["points-to"]`.
- **Dispatch:** VTA over the project's functions (library targets in the set); `call_graph`
gains VTA-only edges `prov: ["vta"]`, weight 1 (an L2 edge is never changed). **D7:** an
interface call binds the interface method (pass-through summary) *and* every VTA target.
- **Stand-ins:** `external_symbols[].receiver {name, type}` and `.body` (formal vertices only,
ids `<external-id>@formal_in:<i>`); summary all-to-all unless `--library-models` overrides it
(keys `<import-path>/<sig>`, values `[in, out]` `of` pairs). A model changes summaries, not
statement-level reachability; sanitizer semantics are the SDK's [user, 2026-10-27;
codellm-devkit/.github#103].
- **`degraded[]`** envelope leaf `{code, detail?, packages[]?, callables[]?}`, absent when
nothing degraded; codes `no_ssa | no_vta | no_cha | partial_ssa | partial_types | unmapped_ssa |
l3_skipped | range_func` (open vocabulary). `--strict` exits non-zero on any.
- **Rulings:** global → global summary edges are omitted [user, 2026-10-26; #30]; memory is
measured and reported per app, not gated (D3) [user, 2026-10-26]; range-over-func is not
modelled, degraded `range_func` (D8); `-a 4 -t` is an error and `--emit neo4j -t` projects
at L3 with a warning (D9).
- A Go-specific `select` cfg kind was not added (`select` is a `switch` with `switch_case`
edges).

## Neo4j-projection decisions

Expand Down Expand Up @@ -185,11 +214,17 @@ shared neutral vocabulary verbatim; never rename a sibling's shared name). Spec:
It carries `call_node` (JSON `parent` verbatim — the **local** key, see § Flattened
properties) and `var` (JSON `of`; L4 — absent at L3). `GO_HAS_BODY_NODE` may originate at `:GoExternal` (L4 stand-ins).

### Reserved L4 overlay (declared now, emits nothing until L4 lands)
- `schema.go` still declares `GO_PARAM_IN`, `GO_PARAM_OUT`, `GO_SUMMARY` (`Reserved`) with
parity-correct names; the projector emits **zero rows** for them. When L4 lands they fill
with **no schema-contract change**. (`--emit neo4j` is "full-depth = max implemented" —
L3 today; the graph grows additively.)
### L4 overlay (live; `--emit neo4j` runs at L4, D4)
- `GO_PARAM_IN {var}`, `GO_PARAM_OUT {var}` and `GO_SUMMARY` are live (no longer `Reserved`;
same names as declared, no contract change). One row per JSON `param_in[]` / `param_out[]` /
`summary[]` entry; endpoints are `:GoBodyNode` global ids. Port and points-to ddg ride
`GO_DDG {var, prov}` (`_k = var|prov`).
- Vertices are `:GoBodyNode`s with `var` (= JSON `of`) and `call_node` (actuals); stand-in
vertices hang off their `:GoExternal` via `GO_HAS_BODY_NODE`. `:GoApplication.degraded_json`
carries the envelope's `degraded[]`.
- **Lazy push (D5):** every L4 row is upserted on every push (L4 facts are non-local); stale L4
rows need `--eager`. A stored `max_level` below the push's (or unreadable) forces a full
upsert; a lower-level push leaves old L4 rows. `--emit neo4j -t` projects at L3 (D9).

### SDK facade (`GoAnalysisBackend`, on the `python-sdk` fork — frontend rung)
- **Type-centric surface** (Go is the SDK's first class-less facade; no C precedent exists
Expand All @@ -202,10 +237,13 @@ shared neutral vocabulary verbatim; never rename a sibling's shared name). Spec:
getters + `get_source`. Slicing / CFG / CDG / DDG / reachability are **omitted** (they
need L3/L4 the analyzer does not produce) per the ABC contract. (2026-10-24: L3 is now
emitted at `-a 3` — per-callable `cfg`/`cdg`/`ddg` and `GO_CFG_NEXT`/`GO_CDG`/`GO_DDG` —
so L3 accessors are supportable now; interprocedural slicing and taint still need L4:
`param_in`/`param_out`/`summary` and the points-to `ddg`.)
so L3 accessors are supportable now. 2026-10-27: L4 is emitted at `-a 4` and by
`--emit neo4j` — `param_in`/`param_out`/`summary` and the points-to `ddg` — so
interprocedural slicing and taint are supportable too.)

### Deferred (not this train)
- SDK consumer slicing/taint surface and L3/L4 facade methods — arrive with the L3/L4
analyzer train, reusing this graph vocabulary unchanged.
- SDK consumer slicing/taint surface and L3/L4 facade methods — the analyzer now emits L4;
the SDK side reuses this graph vocabulary unchanged.
- Struct-tag modeling. (`defer_resume` landed with L3; see § L3 body and edges.)
- `GO_EMBEDS` / `GO_SATISFIES` resolution: `base_types[]` holds raw type strings and
`interfaces[]` is never computed (#31, pre-existing).
Loading
Loading