Salak — deterministic code knowledge graph generator. Python 3.11+, PyPI `salak`.
Parses a repo, emits one provenance-tagged JSON file (`repo-graph.json`).
It is a BUILD ARTIFACT, not a database. Built to unblock Jeruk AI/MANGGA, not to
become a product.

READ THESE TWO FILES BEFORE DOING ANYTHING:
  1. CLAUDE.md                      — working rules, the binding documents by name,
                                      and what to do next
  2. project-memory/03-progress.md  — authoritative state, newest snapshot at the top

THIS FILE DELIBERATELY CARRIES NO PROJECT STATE.
No task numbers, no phase status, no document version numbers, no "next task".
It used to carry all four, and it went roughly thirty tasks stale without anyone
noticing — it still named a superseded spec and a task finished weeks earlier.
Five documents claimed the same facts and one of them was being maintained. So
the duplication is gone rather than merely corrected, and a test enforces it
(tests/test_context_pack.py). If you are tempted to paste status here, put it in
03-progress.md and let this file keep pointing at it.

DURABLE RULES — these do not change from task to task:

- Precision over recall. A wrong edge is worse than a missing edge.
- Provenance downgrades, never upgrades. `extracted` means the relation is
  literally in source AND the target was confirmed — guess any part of the target
  and it is not `extracted`.
- Determinism is a test, not an aspiration. Two runs over an unchanged tree are
  byte-identical except `generated_at`. Never let `set` or `dict` iteration order
  reach the output.
- Core must never import an adapter. Registration goes through the
  `salak.adapters` entry-point group, and a test enforces the direction.
- Every path in the artifact is repo-relative with POSIX separators. This repo is
  developed on Windows; assume nothing.
- Degraded is not failed. No language server means structure plus import edges,
  not an error. `health_check()` never raises; `resolve()` may return empty.
- Diagnostics never abort a run. A broken file yields a diagnostic and the scan
  completes.
- Exit codes are a public contract: 0 clean · 1 fatal · 2 completed with
  diagnostics · 3 overwrite guard refused.
- The bundled JSON Schema beats the prose. If the two disagree, the schema is
  right and the prose gets fixed.
- No placeholders. No "implement later", no stub returning a fake value. If a task
  is too large to finish, say so and split it before starting.
- Any binding decision the spec does not already make: STOP and ask the owner,
  then record the approved answer in project-memory/01-decision-register.md.
- One task at a time, in the plan's order — not numeric order; the two disagree,
  and following the numbers has already caused a task to be skipped. Check
  dependencies before starting.
- Gates are hard. A `rework` verdict invalidates the downstream task list rather
  than delaying it. Never pull later-phase work forward.
- Run the real tools. Never describe output you did not see.
- Update project-memory/03-progress.md in the same turn as the work it records.

HARD BOUNDARIES — decisions, not preferences. Do not build, scaffold, or
"prepare for": a UI or graph viewer · document/PDF/image ingestion · any LLM
call · clustering or community detection · cross-repo graphs · any language
beyond TypeScript and Python · dev-kickoff integration · `salak query` · watch
mode · metrics or complexity scoring. Touching one of these is an anti-pattern,
not initiative. Name it out loud instead.

VERIFY THE REPO:
  uv sync && uv run ruff check . && uv run mypy src tests && uv run pytest

CONVENTIONS: `src/salak/` layout · ruff + mypy strict · CI on 3.11 and 3.12 ·
snake_case functions, PascalCase classes, SCREAMING_SNAKE_CASE diagnostic codes ·
full type hints · `LanguageAdapter` is a `typing.Protocol` · Conventional Commits
with the phase as scope, e.g. `feat(v0.1): emit files section`.

Talk to the owner in Bahasa Indonesia. Write every file, comment, commit message
and document in English.
