feat(extract): Go's native chan and goroutines are not modelled — the Go channel extractor only recognises WebSocket connections
Status: draft implementation-proposal open — PR #1949 (v1: send/receive/select by grammar → gochan Channel nodes + EMITS/LISTENS_ON, package-qualified identity; element types, go statements and range-receives deferred as stated below); marked ready on maintainer design ack.
What is missing
Channel is an existing label with an existing per-language extractor (internal/cbm/extract_channels.c). The Go branch (extract_channels_go → go_process_call) only classifies gorilla/nhooyr-style WebSocket send/receive (WriteMessage/ReadJSON/… on receivers named conn/ws/c/…), emitting transport:"websocket" nodes named after the enclosing function. Go's own primitives — make(chan T), ch <- v, <-ch, select, close(ch), go f() — are untouched by any pass.
Damage measured (~1150-file Go repo)
| construct |
source |
graph |
make(chan …) |
250 |
0 |
sends x <- v |
~210 |
0 |
receives <-x |
~500 |
0 |
go statements |
245 |
0 |
Channel nodes |
— |
2 (both websocket) |
The repository's event pipeline is channel-plumbed end to end (sources own an output channel; enrichment stages consume and re-emit; a consumer drains into storage). That producer/consumer topology — the most load-bearing structure in the codebase — is absent: trace_path stops at the send site and resumes nowhere.
Detection recipe
SELECT json_extract(properties,'$.transport'), count(*) FROM nodes WHERE label='Channel' GROUP BY 1;
vs grep -rho 'make(chan' --include='*.go' . | wc -l and grep -rhoE '^\s*go [a-zA-Z_(]' --include='*.go' . | wc -l.
Natural extraction points (scope, not a design)
- Identity: a Go channel's stable identity is its declaration site — struct field (
OutputChannel chan Event, strongest: shared across files by type) > package var > escaping local. Record the element type (the join key that makes producer/consumer pairs meaningful).
ch <- v → EMITS (edge type exists; count 1 in this repo); <-ch / for range ch → LISTENS_ON (exists; count 2); select cases → same two, one per comm clause.
go f(...) → the existing CROSS_ASYNC notion; even without it, EMITS/LISTENS_ON pairs alone reconstruct the pipeline. close(ch) optional.
- Transport
"gochan", distinct from "websocket" so the existing classifier is untouched. The tree-sitter Go grammar already exposes everything (channel_type, send_statement, select_statement, go_statement); work confined to extract_channels.c + the pass that materialises Channel nodes.
Related: #1114 (edges that model indirection); tracked in #1932.
feat(extract): Go's native
chanand goroutines are not modelled — the Go channel extractor only recognises WebSocket connectionsStatus: draft implementation-proposal open — PR #1949 (v1: send/receive/select by grammar →
gochanChannel nodes + EMITS/LISTENS_ON, package-qualified identity; element types,gostatements and range-receives deferred as stated below); marked ready on maintainer design ack.What is missing
Channelis an existing label with an existing per-language extractor (internal/cbm/extract_channels.c). The Go branch (extract_channels_go→go_process_call) only classifies gorilla/nhooyr-style WebSocket send/receive (WriteMessage/ReadJSON/… on receivers namedconn/ws/c/…), emittingtransport:"websocket"nodes named after the enclosing function. Go's own primitives —make(chan T),ch <- v,<-ch,select,close(ch),go f()— are untouched by any pass.Damage measured (~1150-file Go repo)
make(chan …)x <- v<-xgostatementsChannelnodeswebsocket)The repository's event pipeline is channel-plumbed end to end (sources own an output channel; enrichment stages consume and re-emit; a consumer drains into storage). That producer/consumer topology — the most load-bearing structure in the codebase — is absent:
trace_pathstops at the send site and resumes nowhere.Detection recipe
vs
grep -rho 'make(chan' --include='*.go' . | wc -landgrep -rhoE '^\s*go [a-zA-Z_(]' --include='*.go' . | wc -l.Natural extraction points (scope, not a design)
OutputChannel chan Event, strongest: shared across files by type) > package var > escaping local. Record the element type (the join key that makes producer/consumer pairs meaningful).ch <- v→EMITS(edge type exists; count 1 in this repo);<-ch/for range ch→LISTENS_ON(exists; count 2);selectcases → same two, one per comm clause.go f(...)→ the existingCROSS_ASYNCnotion; even without it,EMITS/LISTENS_ONpairs alone reconstruct the pipeline.close(ch)optional."gochan", distinct from"websocket"so the existing classifier is untouched. The tree-sitter Go grammar already exposes everything (channel_type,send_statement,select_statement,go_statement); work confined toextract_channels.c+ the pass that materialisesChannelnodes.Related: #1114 (edges that model indirection); tracked in #1932.