Guidance for AI coding agents (Claude Code reads this as CLAUDE.md; Codex reads it as AGENTS.md via symlink) working in this repository.
This is a large codebase (~1500 files). A graphify knowledge graph makes orienting much faster — but it is optional and machine-local (graphify-out/ is gitignored, so a fresh clone won't have it).
First, check availability. The graph is usable only if both are true: the /graphify skill is installed and graphify-out/graph.json exists in the project root.
-
If the graph is available — use it first, before grepping or reading files top-to-bottom:
- Natural-language question:
/graphify query "how does the SystemVerilog constructor lower always_ff blocks?" - Trace between two concepts:
/graphify path "SNLSVConstructor" "DNL" - Explain one node:
/graphify explain "SNLSVConstructor" - Browse:
graphify-out/GRAPH_REPORT.md— its "Community Hubs" section is a navigable TOC (SNLSVConstructor Core Implementation, Sequential Assignment Lowering, najaeda Instance Query API, NL Library Management, …).
Graph first for where and how it connects; then Read/Grep the candidate files for the exact lines.
- Natural-language question:
-
If
graphify-out/is absent or the skill isn't installed — just use the normal tools (Grep, Glob, Read) and the source layout below. Do NOT run a full/graphifybuild to answer an ordinary question — that's a slow, expensive 1500-file extraction and is not worth it for a lookup. Building the graph is opt-in, and only when the user explicitly asks for it.
Keeping it fresh (only if you already have a graph): after a non-trivial change, /graphify . --update re-extracts just the changed files so future queries stay accurate.
Open-source EDA framework for hardware design loading and transformation — from Verilog and SystemVerilog RTL elaboration through structural netlist analysis, optimization, and editing. Usable from C++ and Python (najaeda). See README.md for the public overview.
Two complementary C++ APIs:
- SNL (Structured Netlist) — full read/write netlist representation.
- DNL (Dissolved Netlist) — fast, read-only flattened view for parallel analysis.
src/nl/netlist/— core netlist model:snl/,pnl/,core/,decorators/,serialization/(Cap'n Proto interchange),visual/.src/nl/formats/— frontends/backends:systemverilog/(slang-based),verilog/,liberty/,lefdef/.src/nl/python/— Python bindings for thenajaedapackage.src/dnl/— Dissolved Netlist (flattened, read-only, parallel).src/najaeda/— Python package (najaeda/,examples/,benchmarks/).src/apps/naja_edit/—naja_editCLI (optimize/translate netlists).src/app_snippet/— copy-to-start template for a new C++ tool.src/{bne,core,metrics,optimization}/— supporting libraries (logic opt: DLE, constant propagation).primitives/— primitive/standard-cell libraries.test/mirrorssrc/.tutorials/— the six Colab notebooks.
NajaIF snapshots carry a small snl.mf manifest. The immediate objective is
to prevent a snapshot written by a different Naja build from being silently
deserialized into a truncated or otherwise incorrect netlist.
- The manifest writes
V <major> <minor> <revision>(the legacy format/schema revision) andP <naja-version> <git-hash>(the producer identity). SNLCapnP::load()reads this manifest before either Cap'n Proto payload. It retains the strictVcheck and, for now, also requires an exact match of both producer values withnaja::NAJA_VERSIONandnaja::NAJA_GIT_HASH. A mismatch, or a legacy manifest withoutP, throwsSNLDumpException; callers must regenerate the snapshot.naja.snapshot_manifest(path)reads onlysnl.mfand returnsschema_version,producer_version, andproducer_git_hash, without creating anNLUniverseor loading payloads.- This exact-build producer gate is deliberately temporary and conservative.
The remaining design work is to define and maintain a schema version owned
by
thirdparty/naja-if, then use that version as the durable compatibility contract so compatible Naja builds can exchange snapshots.
Relevant implementation: SNLCapnP.cpp, SNLDumpManifest.cpp, and
PyNLDB.cpp; focused tests live under test/nl/snl/serialization/capnp/ and
test/nl/python/naja_wrapping/test_nldb.py.
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$NAJA_INSTALL
make && make test && make install- Build dirs already present:
build/,build-coverage/,build-coverage-svconstructor/. Prefer building in an existing one to reuse the CMake cache. - Tests are CTest-driven; run
ctest(ormake test) from the build dir. Test sources live undertest/mirroringsrc/. - Python usage after install needs
export PYTHONPATH=$PYTHONPATH:$NAJA_INSTALL/lib/python. - Build deps (macOS):
brew install cmake capnp tbb bison flex boostand put flex/bison onPATH.
CMake is naja's primary build system — it's what CI, packaging (wheels,
Docker images), and most workflows use, and it's the one to reach for by
default. Bazel (MODULE.bazel, BUILD.bazel throughout the tree) is
kept in parallel as a validated smoke test only, via ubuntu-bazel.yml/
macos-bazel.yml (bazel build //... && bazel test //..., no
submodules — bzlmod fetches its own copies of shared dependencies). It
is not CI's primary gate and doesn't need to track every workflow's
behavior (sanitizer suppressions, coverage flags, etc.) — just prove the
Bazel side keeps compiling and passing tests.
Keep submodule pins and Bazel pins in sync. CMake pins shared
upstream dependencies via git submodules (.gitmodules, thirdparty/*);
Bazel pins its own copies of the same dependencies via
git_override()/git_repository() commits in MODULE.bazel. Nothing
forces these to move together — bumping one without the other silently
makes the two build systems test different upstream code. When you bump
a submodule commit (or vice versa), update the matching MODULE.bazel
pin in the same change:
cpptrace,slang: must be an exact commit match.naja-if,naja-verilog: these are the project's own forks, and Bazel tracks a separatebazel-supportbranch (native Bazel BUILD files added on top) rather than the branch CMake tracks — so an exact match isn't meaningful. Instead, the submodule's pinned commit must be an ancestor of (or equal to) thebazel-supportpin, i.e.bazel-supportmust never fall behind main.googletest: deliberately excluded — CMake pins an old submodule dev commit, Bazel takes a BCR release (1.17.0.bcr.2). Different dependency-sourcing mechanisms entirely; not meant to track in lockstep.
This is enforced automatically: ci/check_submodule_bazel_sync.py
(run by .github/workflows/dependency-sync-check.yml on every push/PR)
checks exactly this and fails CI if a pin has drifted. Run it locally
after bumping any of these dependencies: python3 ci/check_submodule_bazel_sync.py.
- Match the surrounding code's style, naming, and comment density — the SNL layer uses
NL*/SNL*prefixes; follow the local idiom. - The SystemVerilog frontend is built on slang; sequential lowering and always-block handling live in
SNLSVConstructorand the "Sequential Assignment Lowering" community — query the graph before touching them. - New DB0 primitives (flops, etc.) follow a canonical ID scheme resolved on capnp load; don't invent ad-hoc primitive IDs.
*.py~,*.txt~,build*/, andgraphify-out/.venv*are local artifacts — don't edit or commit them.
SNLDesignModeling.h is the canonical C++ primitive timing-model API. Timing
metadata can be populated by the Liberty frontend, by direct C++ construction
of NLDB0 primitives, and by the Python primitive libraries under
src/najaeda/najaeda/primitives/. Keep these three paths aligned: when adding
or changing timing arcs, timing parameters, term roles, active levels, or
related queries, verify that the raw najaeda.naja bindings expose the feature
needed to express the same model from Python and update the Python primitive
loaders where applicable. Add or update focused tests for both the raw bindings
and the affected Python primitive libraries so that equivalent decorations do
not silently drift apart.
When changing either Python API level exposed by the najaeda package, update the package documentation in src/najaeda/najaeda/docs/source/ in the same change. This includes both the high-level najaeda.netlist API and the raw compiled najaeda.naja / naja.so API.
This applies to:
- the high-level API in
src/najaeda/najaeda/netlist.py; - Python helper modules such as
instance_visitor.py,net_visitor.py,stats.py, andpandas_stats.py; - raw Python bindings under
src/nl/python/naja_wrapping/, including exported module functions, exception types, classes, constructors, enum-like values, and public methods; - changes in underlying SNL/NL C++ APIs when they alter what the raw Python bindings expose or how raw Python users should call them.
Documentation expectations:
- High-level API changes should update
api.rst, the relevant class guide page, or the user guide pages (concepts.rst,quickstart.rst,loading.rst,editing.rst). - Raw
najaeda.naja/naja.soAPI changes should updateraw_api.rst, including the expert reference table when public raw classes, module functions, exceptions, enum-like values, constructors, or methods change. - If a raw binding change makes a new native feature usable from Python, document when experts should use the raw API directly and whether a high-level
najaeda.netlistwrapper should also be added. - Workflow or example changes should update
examples.rst.inor the relevant guide page.
Before finishing a documentation-impacting change, run a Sphinx build when the local environment supports it:
sphinx-build -b html src/najaeda/najaeda/docs/source /tmp/najaeda-docs-checkIf sphinx_rtd_theme is not installed locally, use the built-in theme override:
sphinx-build -b html -D html_theme=alabaster src/najaeda/najaeda/docs/source /tmp/najaeda-docs-check- Default/PR base branch:
main. Don't commit or push unless asked; if onmain, branch first.