@@ -285,6 +285,51 @@ made output load-dependent (#145).
285285 ` callee: null→id ` remains the single sanctioned L1→L2 refinement.
286286- Neo4j projection unchanged (` PY_CALLS ` carries ` prov ` as data).
287287
288+ ## 2026-08-27 — Neutral ` Artifact ` /` Package ` subgraph (issue #157 , Task 6)
289+
290+ Design: ` .superpowers/sdd/2026-08-27-artifacts-and-dependencies/task-6-brief.md `
291+ (gitignored working brief — not committed; this entry is the durable record).
292+
293+ Projects ` PyApplication.artifacts ` /` dependencies ` /` unresolved_imports ` (Tasks
294+ 1-5) into the graph: new labels ` Artifact ` , ` Package ` (merge key ` id ` ); new
295+ rels ` HAS_ARTIFACT ` (PyApplication→Artifact), ` DECLARES_DEPENDENCY `
296+ (Artifact→Package, props ` spec ` /` kind ` /` extras ` /` prov ` ), ` LOCKS `
297+ (Artifact→Package, prop ` version ` ), ` PY_PROVIDES ` (Package→PyExternal),
298+ ` PY_UNRESOLVED_IMPORT ` (PyApplication→PyExternal, prop ` prov ` ).
299+
300+ - ** ` Artifact ` /` Package ` deliberately break the ` Py ` -prefix convention** the
301+ Level-3 CPG section above establishes (` PySymbol ` , ` PyBodyNode ` , ` PY_CALLS ` ,
302+ …). Opposite rationale, same namespacing question: a manifest file or a
303+ PyPI package is not a Python-language concept — a TypeScript analyzer
304+ reading ` package.json ` in the same repo should MERGE onto the same
305+ ` Artifact ` /` Package ` nodes, not create ` TSArtifact ` /` TSPackage ` twins. The
306+ edges that stay this analyzer's own claim (` PY_PROVIDES ` — "this analyzer
307+ resolved this import to this package", ` PY_UNRESOLVED_IMPORT ` ) keep the
308+ ` PY_ ` prefix; the nodes they connect to do not.
309+ - ** ` PY_PROVIDES ` /` PY_UNRESOLVED_IMPORT ` target ids are minted here, not
310+ looked up.** ` app.external_symbols ` only homes call-graph endpoints
311+ (` Codeanalyzer._home_external_symbols ` walks ` app.call_graph ` alone), so a
312+ module that is imported but never called — the common case for
313+ ` PyDependency.provides_imports ` , and the * only* case for an unresolved
314+ import — has no existing ` :PyExternal ` ghost to MERGE onto. The projection
315+ builds one with the same id shape ` _call_endpoint ` /` _home_external_symbols `
316+ already use for a dot-less (no ` . ` ) call-graph signature: `<app can:// id>
317+ /@external /<name >` , ` module` absent — and the same two-label
318+ ` ["PySymbol", "PyExternal"] ` RowBuilder idiom every other ghost in this file
319+ uses (schema declares ` PyExternal ` 's merge label as ` PySymbol ` ). If a call
320+ into that same bare name is ever projected too, both rows collapse onto one
321+ node under ` RowBuilder ` 's MERGE-by-` (label, id) ` semantics — correctly,
322+ since they name the same real-world symbol.
323+ - ** ` LOCKS ` fans out to every lock artifact present** , not just the one that
324+ pinned a given dependency: ` PyDependency.locked_version ` merges all lock
325+ files' pins upstream (` build_dependency_view ` ) with no per-lock-file
326+ attribution left to project. One lock file is the overwhelmingly common
327+ case; revisit if a project with two conflicting lock files in one repo
328+ turns out to matter in practice.
329+ - Always projected regardless of ` -a ` — this section is L1 data, identical at
330+ every analysis level (mirrors ` analysis.json ` ), consistent with Neo4j's
331+ existing full-depth-always posture for ` --emit neo4j ` .
332+
288333## 2026-08-27 — Artifacts, dependencies, and the ` can://artifact/ ` namespace
289334
290335Design: ` docs/design/specs/2026-08-27-artifacts-and-dependencies-design.md ` .
@@ -305,7 +350,7 @@ at every level, like entrypoints.
305350 ` --resolve-installed ` flag.
306351- ** Neo4j** : neutral labels ` :Artifact ` / ` :Package ` (no ` Py ` prefix, shared
307352 MERGE targets across analyzers); ` :Package.id ` is a purl (` pkg:pypi/<name> ` ).
308- ` PY_PROVIDES ` joins packages to the existing ` :PyExternal ` ghost ids , wiring
353+ ` PY_PROVIDES ` joins packages to existing-or-minted module-level ` :PyExternal ` ghosts , wiring
309354 dependencies into the call graph. New edges: ` HAS_ARTIFACT ` ,
310355 ` DECLARES_DEPENDENCY ` , ` LOCKS ` , ` PY_PROVIDES ` , ` PY_UNRESOLVED_IMPORT ` .
311356- Capture broad (config files as nodes with ` roles ` ), extract narrow
0 commit comments