Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ jobs:
# for scripts/ on the S2083 reasoning above — the engine does not model
# the check — and kept on for src/.
#
# tests/fixtures/ is excluded outright: it holds input corpora, not
# tests/fixtures/ is excluded from test analysis
# (`sonar.test.exclusions`; it sits under `sonar.tests`, so
# `sonar.exclusions` would not reach it): it holds input corpora, not
# code. Its package.json files exist to be parsed by cgis, and a lock
# file next to them (text:S8564) would be a fixture nothing reads.
#
Expand Down Expand Up @@ -230,7 +232,6 @@ jobs:
-Dsonar.newCode.referenceBranch=main
-Dsonar.sources=src,scripts,.github
-Dsonar.tests=tests
-Dsonar.exclusions=tests/fixtures/**
-Dsonar.test.exclusions=tests/fixtures/**
-Dsonar.python.version=3.12
-Dsonar.python.coverage.reportPaths=coverage.xml
Expand Down
25 changes: 16 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ The pipeline runs in three phases: **Extract → Resolve → Store**.

```
IngestionPipeline (pipeline.py)
├── BaseExtractor subclasses (extractors/) ← language-specific AST → Nodes+Edges
│ └── PythonExtractor ← only language implemented so far
├── cgis.languages (registry) ← lazy: a pack is imported when its first file is met
│ ├── languages/python/ ← PythonExtractor + PACK
│ └── languages/typescript/ ← TypeScriptExtractor (.ts/.tsx) + PACK
├── ResolverEngine (resolver/engine.py) ← raw_call: targets → resolved FQN edges
└── SQLiteStore (storage/sqlite_store.py) ← persists graph; WAL mode
└── QueryEngine (query/engine.py) ← BFS traversals (impact / flow)
```

**Node FQN format**: `module.ClassName.method_name` — fully dot-separated, derived from the file path (e.g., `src/cgis/pipeline.py` → `src.cgis.pipeline.IngestionPipeline.run`). Use `file_path_to_module_fqn(path)` from `extractors/python_extractor.py` to convert paths. `__init__.py` strips the `/__init__` suffix.
**Node FQN format**: `module.ClassName.method_name` — fully dot-separated, derived from the file path (e.g., `src/cgis/pipeline.py` → `src.cgis.pipeline.IngestionPipeline.run`). Use `cgis.languages.scope_of(path, source_root)` to convert paths (it picks the file's own pack; the Python rule lives in `languages/python/extractor.py`). `__init__.py` strips the `/__init__` suffix.

**Raw call convention**: Extractors emit edges with `target = "raw_call:<name>"`. The `ResolverEngine` then resolves these to actual FQNs or leaves them unresolved (keeps the `raw_call:` prefix in output).

Expand All @@ -68,12 +69,18 @@ IngestionPipeline (pipeline.py)
- `NodeType` — structural (`FILE`, `MODULE`, `CLASS`, `FUNCTION`, `METHOD`…), runtime (`API_ENDPOINT`, `DB_TABLE`…), semantic (`DOMAIN_CONCEPT`).
- `EdgeType` — structural (`CONTAINS`, `DECLARES`, `IMPORTS`), behavioral (`CALLS`, `REFERENCES`), semantic (`HANDLES`, `PERSISTS`, `AUTHORIZES`).

## Adding a New Language Extractor

1. Subclass `BaseExtractor` in `src/cgis/extractors/`.
2. Implement `parse(code: str, file_path: str) -> tuple[list[Node], list[Edge]]`.
3. Emit `CALLS` edges with `target="raw_call:<name>"` — the resolver handles the rest.
4. Register the extractor in `cli.py`'s `extractors` dict with the file extension key.
## Adding a Language Pack

1. Create `src/cgis/languages/<name>/` with an extractor subclassing `cgis.core.extractor.BaseExtractor`
(`parse(code, file_path) -> tuple[list[Node], list[Edge]]`; emit `CALLS` edges with
`target="raw_call:<name>"`, set `Node.language = "<name>"`).
2. In its `__init__.py`, define `PACK`, subclassing `cgis.core.extractor.LanguagePack` (mark methods `@override`)
(`name`, `extensions`, `code_fence`, `is_test`, `scope_of`, `extractor`).
3. Add one `Manifest` to `MANIFESTS` in `src/cgis/languages/__init__.py`, with a `load` function that
imports the pack statically inside it (lazy, yet visible to the review-fingerprint walk). Nothing
else names the pack: CLI, MCP server, guardian and scripts all go through the registry, and the
`surface-packs` forbidden-edge gate fails if a surface imports a pack directly.
4. A pack never imports another pack or `cgis.storage` (`cross-pack`, `pack-isolation` gates).

## Type Discipline

Expand Down
4 changes: 2 additions & 2 deletions docs/ontology/domains.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ domains:
description: "Source code parsing and AST extraction"
heuristics:
file_path_patterns:
- "*extractors/*"
- "*languages/*"
fqn_patterns:
- "*.Extractor*"
- "*extractors.*"
- "*languages.*"

StorageLayer:
description: "Database storage, schemas, and persistence"
Expand Down
4 changes: 3 additions & 1 deletion docs/ontology/patterns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ patterns:
# measurement (see plan: docs/specs/plans/2026-06-10-motif-basis-part-b.md).
project_domains:
# Python graph (ingest root: src/ → FQNs carry the cgis. prefix)
# Re-pointed in language-packs step 1: the extractors moved into cgis.languages.<pack>;
# the contract (BaseExtractor) moved to cgis.core.
- name: "extraction"
fqn_prefix: "cgis.extractors"
fqn_prefix: "cgis.languages"
expected_pattern: layered_dag
profile: python
# Re-negotiated in #116: PythonExtractor was decomposed from a God Object into a
Expand Down
Loading
Loading