diff --git a/.agents/skills/tldrgraph-init/SKILL.md b/.agents/skills/tldrgraph-init/SKILL.md new file mode 100644 index 0000000..7ddfa53 --- /dev/null +++ b/.agents/skills/tldrgraph-init/SKILL.md @@ -0,0 +1,128 @@ +--- +name: tldrgraph-init +description: Build or continue this repository's TLDRGraph architecture graph (layers, extraction, enrichment) +--- + +# TLDRGraph: build this repository's architecture graph + +One command, run repeatedly until it says DONE. `tldrgraph init` never guesses: +it stops and tells you exactly what it needs. + +```bash +tldrgraph init +``` + +Read the `NEXT ACTION` block it prints, do what it says, then run `tldrgraph init` +again. Repeat until the output says `status: done`. There are only three things +it can ask for. + +## 1. `status: needs_layers` + +TLDRGraph ships **no layer templates** and will not invent an architecture. +Design one from this repository. + +1. Read `.tldrgraph/propose_layers_request.json`. It carries the symbols and + files extraction already found -- a starting point, not a substitute for + opening the code. +2. **Open real source files**: entry points first, then a representative file + from each cluster in the evidence. Work out what this codebase actually does + and where responsibility changes hands. +3. Write `.tldrgraph/propose_layers_response.json`: + +```json +{ + "utility_id": "", + "layers": [ + { + "id": "short_machine_id", + "name": "Layer 1: Human Friendly Name", + "order": 1, + "description": "One sentence on what lives here", + "rules": [ + {"file_contains": ["substring"], "exclude_file": ["optional"]}, + {"label_contains": ["SymbolNamePart"]} + ] + } + ] +} +``` + +4. Run `tldrgraph init` again. + +### What a layer set looks like + +Sketches from other codebases, to show the *shape* of an answer. They are not a +menu and none of them will fit this repository -- read the code and name what you +actually find. + +- A web app might split presentation from request handling from domain logic + from persistence, with background jobs and deployment config as their own tiers. +- A CLI tool might split the command surface from the processing engine from + local state, with adapters to outside systems separate again. +- A library might split its public API from the core implementation from its + data types, with backend adapters separate. +- A data pipeline might split ingestion from transformation from model training + from serving. + +The useful question is not "which of these is it?" but "where does responsibility +change hands in *this* code, and what would a new engineer need named?" + +### Rules that hold for any answer + +- 3 to 6 layers, plus exactly one catch-all whose `id` equals `utility_id` and + whose `rules` are `[]`. +- Unique `id` and `name` per layer; sequential integer `order` from 1. +- Rule keys: `file_contains`, `exclude_file`, `path_regex`, `label_contains`, + `exclude_label`, `label_ends_with`, `type_in`, `id_prefix`. Values are lists of + strings. Rules are evaluated in `order` and the first match wins. +- Derive rules from paths and symbol names you actually saw. A rule matching + nothing is worse than no rule; a rule matching everything collapses the map. + +## 2. `status: needs_confirmation` + +The output shows how many nodes need enrichment and how many agent round-trips +that implies. **Ask the user whether to proceed, and show them that estimate.** +Do not decide for them. + +- They agree: `tldrgraph init --yes` +- Smaller first pass: `tldrgraph init --yes --limit 100` +- They decline: stop. The graph is already built and queryable. + +## 3. `status: needs_enrichment` + +1. Read `.tldrgraph/enrichment_request.yaml`. +2. **Open the source file of every node in it.** This is the entire point: an + intent paraphrased from a symbol name poisons semantic search with + confident-sounding noise. +3. Write `.tldrgraph/enrichment_response.yaml` -- a *different* file from the + request, which is regenerated on every run: + +```yaml +- id: "" + intent: | + What this symbol does, why it exists, and its execution logic. + input_fields: [caseId, remarks] + output_fields: [status, disposition] + calls: [ApplicationsService, pension_cases] +``` + +4. Run `tldrgraph init --yes` again. It applies the response and hands you the + next batch, until there is nothing left. + +**Copy every `id` verbatim.** A constructed id matches nothing, is dropped, and +gets reported back to you -- but the work is wasted. + +**Never invent `fields` or `calls`.** Omit what you cannot verify in the code: an +empty list is a correct answer, a wrong `calls` entry becomes a real wrong edge. + +## Once it says DONE + +```bash +tldrgraph query "" +tldrgraph trace "" "" +tldrgraph layers +tldrgraph ui --serve +``` + +Read-only, and they never trigger enrichment. Full schema: +`.tldrgraph/AGENT_CONTRACT.md`. diff --git a/.claude/commands/tldrgraph-init.md b/.claude/commands/tldrgraph-init.md new file mode 100644 index 0000000..7ddfa53 --- /dev/null +++ b/.claude/commands/tldrgraph-init.md @@ -0,0 +1,128 @@ +--- +name: tldrgraph-init +description: Build or continue this repository's TLDRGraph architecture graph (layers, extraction, enrichment) +--- + +# TLDRGraph: build this repository's architecture graph + +One command, run repeatedly until it says DONE. `tldrgraph init` never guesses: +it stops and tells you exactly what it needs. + +```bash +tldrgraph init +``` + +Read the `NEXT ACTION` block it prints, do what it says, then run `tldrgraph init` +again. Repeat until the output says `status: done`. There are only three things +it can ask for. + +## 1. `status: needs_layers` + +TLDRGraph ships **no layer templates** and will not invent an architecture. +Design one from this repository. + +1. Read `.tldrgraph/propose_layers_request.json`. It carries the symbols and + files extraction already found -- a starting point, not a substitute for + opening the code. +2. **Open real source files**: entry points first, then a representative file + from each cluster in the evidence. Work out what this codebase actually does + and where responsibility changes hands. +3. Write `.tldrgraph/propose_layers_response.json`: + +```json +{ + "utility_id": "", + "layers": [ + { + "id": "short_machine_id", + "name": "Layer 1: Human Friendly Name", + "order": 1, + "description": "One sentence on what lives here", + "rules": [ + {"file_contains": ["substring"], "exclude_file": ["optional"]}, + {"label_contains": ["SymbolNamePart"]} + ] + } + ] +} +``` + +4. Run `tldrgraph init` again. + +### What a layer set looks like + +Sketches from other codebases, to show the *shape* of an answer. They are not a +menu and none of them will fit this repository -- read the code and name what you +actually find. + +- A web app might split presentation from request handling from domain logic + from persistence, with background jobs and deployment config as their own tiers. +- A CLI tool might split the command surface from the processing engine from + local state, with adapters to outside systems separate again. +- A library might split its public API from the core implementation from its + data types, with backend adapters separate. +- A data pipeline might split ingestion from transformation from model training + from serving. + +The useful question is not "which of these is it?" but "where does responsibility +change hands in *this* code, and what would a new engineer need named?" + +### Rules that hold for any answer + +- 3 to 6 layers, plus exactly one catch-all whose `id` equals `utility_id` and + whose `rules` are `[]`. +- Unique `id` and `name` per layer; sequential integer `order` from 1. +- Rule keys: `file_contains`, `exclude_file`, `path_regex`, `label_contains`, + `exclude_label`, `label_ends_with`, `type_in`, `id_prefix`. Values are lists of + strings. Rules are evaluated in `order` and the first match wins. +- Derive rules from paths and symbol names you actually saw. A rule matching + nothing is worse than no rule; a rule matching everything collapses the map. + +## 2. `status: needs_confirmation` + +The output shows how many nodes need enrichment and how many agent round-trips +that implies. **Ask the user whether to proceed, and show them that estimate.** +Do not decide for them. + +- They agree: `tldrgraph init --yes` +- Smaller first pass: `tldrgraph init --yes --limit 100` +- They decline: stop. The graph is already built and queryable. + +## 3. `status: needs_enrichment` + +1. Read `.tldrgraph/enrichment_request.yaml`. +2. **Open the source file of every node in it.** This is the entire point: an + intent paraphrased from a symbol name poisons semantic search with + confident-sounding noise. +3. Write `.tldrgraph/enrichment_response.yaml` -- a *different* file from the + request, which is regenerated on every run: + +```yaml +- id: "" + intent: | + What this symbol does, why it exists, and its execution logic. + input_fields: [caseId, remarks] + output_fields: [status, disposition] + calls: [ApplicationsService, pension_cases] +``` + +4. Run `tldrgraph init --yes` again. It applies the response and hands you the + next batch, until there is nothing left. + +**Copy every `id` verbatim.** A constructed id matches nothing, is dropped, and +gets reported back to you -- but the work is wasted. + +**Never invent `fields` or `calls`.** Omit what you cannot verify in the code: an +empty list is a correct answer, a wrong `calls` entry becomes a real wrong edge. + +## Once it says DONE + +```bash +tldrgraph query "" +tldrgraph trace "" "" +tldrgraph layers +tldrgraph ui --serve +``` + +Read-only, and they never trigger enrichment. Full schema: +`.tldrgraph/AGENT_CONTRACT.md`. diff --git a/.cursor/commands/tldrgraph-init.md b/.cursor/commands/tldrgraph-init.md new file mode 100644 index 0000000..7ddfa53 --- /dev/null +++ b/.cursor/commands/tldrgraph-init.md @@ -0,0 +1,128 @@ +--- +name: tldrgraph-init +description: Build or continue this repository's TLDRGraph architecture graph (layers, extraction, enrichment) +--- + +# TLDRGraph: build this repository's architecture graph + +One command, run repeatedly until it says DONE. `tldrgraph init` never guesses: +it stops and tells you exactly what it needs. + +```bash +tldrgraph init +``` + +Read the `NEXT ACTION` block it prints, do what it says, then run `tldrgraph init` +again. Repeat until the output says `status: done`. There are only three things +it can ask for. + +## 1. `status: needs_layers` + +TLDRGraph ships **no layer templates** and will not invent an architecture. +Design one from this repository. + +1. Read `.tldrgraph/propose_layers_request.json`. It carries the symbols and + files extraction already found -- a starting point, not a substitute for + opening the code. +2. **Open real source files**: entry points first, then a representative file + from each cluster in the evidence. Work out what this codebase actually does + and where responsibility changes hands. +3. Write `.tldrgraph/propose_layers_response.json`: + +```json +{ + "utility_id": "", + "layers": [ + { + "id": "short_machine_id", + "name": "Layer 1: Human Friendly Name", + "order": 1, + "description": "One sentence on what lives here", + "rules": [ + {"file_contains": ["substring"], "exclude_file": ["optional"]}, + {"label_contains": ["SymbolNamePart"]} + ] + } + ] +} +``` + +4. Run `tldrgraph init` again. + +### What a layer set looks like + +Sketches from other codebases, to show the *shape* of an answer. They are not a +menu and none of them will fit this repository -- read the code and name what you +actually find. + +- A web app might split presentation from request handling from domain logic + from persistence, with background jobs and deployment config as their own tiers. +- A CLI tool might split the command surface from the processing engine from + local state, with adapters to outside systems separate again. +- A library might split its public API from the core implementation from its + data types, with backend adapters separate. +- A data pipeline might split ingestion from transformation from model training + from serving. + +The useful question is not "which of these is it?" but "where does responsibility +change hands in *this* code, and what would a new engineer need named?" + +### Rules that hold for any answer + +- 3 to 6 layers, plus exactly one catch-all whose `id` equals `utility_id` and + whose `rules` are `[]`. +- Unique `id` and `name` per layer; sequential integer `order` from 1. +- Rule keys: `file_contains`, `exclude_file`, `path_regex`, `label_contains`, + `exclude_label`, `label_ends_with`, `type_in`, `id_prefix`. Values are lists of + strings. Rules are evaluated in `order` and the first match wins. +- Derive rules from paths and symbol names you actually saw. A rule matching + nothing is worse than no rule; a rule matching everything collapses the map. + +## 2. `status: needs_confirmation` + +The output shows how many nodes need enrichment and how many agent round-trips +that implies. **Ask the user whether to proceed, and show them that estimate.** +Do not decide for them. + +- They agree: `tldrgraph init --yes` +- Smaller first pass: `tldrgraph init --yes --limit 100` +- They decline: stop. The graph is already built and queryable. + +## 3. `status: needs_enrichment` + +1. Read `.tldrgraph/enrichment_request.yaml`. +2. **Open the source file of every node in it.** This is the entire point: an + intent paraphrased from a symbol name poisons semantic search with + confident-sounding noise. +3. Write `.tldrgraph/enrichment_response.yaml` -- a *different* file from the + request, which is regenerated on every run: + +```yaml +- id: "" + intent: | + What this symbol does, why it exists, and its execution logic. + input_fields: [caseId, remarks] + output_fields: [status, disposition] + calls: [ApplicationsService, pension_cases] +``` + +4. Run `tldrgraph init --yes` again. It applies the response and hands you the + next batch, until there is nothing left. + +**Copy every `id` verbatim.** A constructed id matches nothing, is dropped, and +gets reported back to you -- but the work is wasted. + +**Never invent `fields` or `calls`.** Omit what you cannot verify in the code: an +empty list is a correct answer, a wrong `calls` entry becomes a real wrong edge. + +## Once it says DONE + +```bash +tldrgraph query "" +tldrgraph trace "" "" +tldrgraph layers +tldrgraph ui --serve +``` + +Read-only, and they never trigger enrichment. Full schema: +`.tldrgraph/AGENT_CONTRACT.md`. diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..45c8b4f --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Git pre-commit hook to verify code health rules and test integrity. + +set -e + +echo "🔍 Running TLDRGraph pre-commit code health check..." + +# Run code health linter +python3 scripts/check_code_health.py --target-dir tldrgraph + +if [ $? -ne 0 ]; then + echo "❌ Pre-commit check failed: Code quality limits exceeded." + echo "Please ensure all files are < 400 lines and functions have complexity <= 15." + exit 1 +fi + +echo "✅ Code health check passed." diff --git a/.gitignore b/.gitignore index 5823d21..3a041ee 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ htmlcov/ # Graphify / TLDRGraph analysis outputs & state graphify-out/ -.tldrgraph/ +# .tldrgraph/ # superseded by the TLDRGraph block below .codechakra/ scratch/ @@ -82,3 +82,11 @@ Thumbs.db !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json + +# BEGIN TLDRGRAPH +# TLDRGraph analysis state. Generated artifacts are ignored; the agent +# contract and layer map are committed so the whole team shares them. +.tldrgraph/* +!.tldrgraph/AGENT_CONTRACT.md +!.tldrgraph/layers.config.yaml +# END TLDRGRAPH diff --git a/.tldrgraph/AGENT_CONTRACT.md b/.tldrgraph/AGENT_CONTRACT.md new file mode 100644 index 0000000..d9c48ba --- /dev/null +++ b/.tldrgraph/AGENT_CONTRACT.md @@ -0,0 +1,268 @@ +# TLDRGraph Agent Contract + +**Audience: the coding agent with this repository open** (Claude Code, Cursor, Antigravity). + +TLDRGraph builds an architectural graph from the graphify AST export. The layer set +itself is designed by you, reading this repository. TLDRGraph ships no layer templates. +Structure *within* a layer, and the high-volume deterministic seams between layers, are +extracted automatically. What cannot be extracted automatically is: + +- indirect dispatch, queue / event hops, dynamically-built routes; +- the natural-language **intent** that makes semantic search work at all. + +That is your job. You are not a fallback for a hosted model — you are the primary +enrichment path, because **you can open the files**. The API path only ever sees a label +and a path (`snippet` is never populated), so it guesses. You do not have to guess. + +--- + +## Start here: `tldrgraph init` + +One command does everything, and it is resumable: + +```bash +tldrgraph init +``` + +It runs every deterministic step — extraction, classification, indexing, applying whatever +you last wrote — then stops with a `NEXT ACTION` block the moment it needs judgement only +you can supply. Do what the block says and run it again. Repeat until `status: done`. + +| status | what it wants | +| --- | --- | +| `needs_layers` | Read the code and design this repository's architecture. **TLDRGraph ships no layer templates**; nothing will be applied for you. The request carries sketches of how other kinds of codebase divide — for shape only, never to copy. | +| `needs_confirmation` | Enrichment costs the user tokens. Show them the estimate and ask. Then `tldrgraph init --yes`. | +| `needs_enrichment` | A batch to open, read and describe, per the schema below. | +| `done` | Nothing left. Use `query` / `trace` / `layers`. | + +`--json` gives you the same thing machine-readably. The sections below document the file +formats `init` reads and writes; the underlying `queue-enrichment` / `apply-enrichment` +commands remain available for scripting. + +**Copy every `id` verbatim from the request.** A constructed id matches nothing, is +dropped, and will be reported back to you — but the work is wasted. + +--- + +## The loop + +```bash +tldrgraph queue-enrichment --limit 50 # 1. writes .tldrgraph/enrichment_request.yaml +# 2. you read it, read the SOURCE, and write +# .tldrgraph/enrichment_response.yaml +tldrgraph apply-enrichment # 3. merges into the graph, cache and index +tldrgraph queue-enrichment --limit 50 # 4. repeat -- the queue advances automatically +``` + +Request and response are **separate files**. Never write your answer back into +`enrichment_request.yaml`; it is regenerated on every run and your work would be lost. + +| File | Written by | Read by | +| --- | --- | --- | +| `.tldrgraph/enrichment_request.yaml` (or `enrichment_request.json`) | `queue-enrichment` | you | +| `.tldrgraph/enrichment_response.yaml` (or `enrichment_response.json`) | **you** | `apply-enrichment` | +| `.tldrgraph/enrichment_cursor.json` | both commands | both commands | +| `.tldrgraph/pending_enrichment.json` | *(legacy)* | `apply-enrichment`, only if no response file exists | + +--- + +## Request schema (`enrichment_request.yaml`) + +```yaml +schema: codechakra/enrichment-request@1 +generated_at: "2026-08-19T00:00:00+00:00" +response_file: .tldrgraph/enrichment_response.yaml +contract: .tldrgraph/AGENT_CONTRACT.md +progress: + total_candidates: 1873 # un-enriched, non-utility nodes + already_enriched: 12 # nodes that already carry an intent + queued_now: 50 # entries in "nodes" below + remaining_after: 1823 # still waiting after this batch is applied +nodes: + - id: backend_src_applications_applications_controller_applicationscontroller + label: ApplicationsController + layer_id: api + layer: "Layer 2: API Gateway" + file: backend/src/applications/applications.controller.ts + source_location: L31 + degree: 41 # in + out edges in the AST graph + cross_layer_degree: 17 # of those, how many cross a layer boundary + rank: 1 # 1 = highest priority in this batch + existing_intent_source: heuristic # "" when the node has no intent at all +``` + +`file` is repo-relative. `source_location` is graphify's line hint and may be `null`. +`layer_id` is the stable machine key (e.g. `cli`, `engine`, `storage`, `api`, `ui`). + +`existing_intent_source` is `"heuristic"` when the node already carries an intent written +by the offline template enricher. That text was generated from the label and layer alone +— it has not read a line of source — so the node is still a candidate and your answer +should overwrite it. Applied answers are stamped `"agent"` and are never re-queued. + +--- + +## Response schema (`enrichment_response.yaml` or `enrichment_response.json`) + +A **YAML list** (preferred) or **JSON array** of objects: + +```yaml +- id: backend_src_applications_applications_controller_applicationscontroller + intent: | + ### Pension Application Lifecycle Gateway + REST gateway for the pension application lifecycle. Authorizes DEO/AAO/AO/DAG roles, + dispatches cases to ApplicationsService and records status transitions. + input_fields: + - caseId + - transitionPayload + - remarks + - sanctionOrderNo + output_fields: + - applicationStatus + - disposition + calls: + - ApplicationsService + - JwtAuthGuard + - RolesGuard + - pension_cases +``` + +Equivalent JSON format (also accepted from `.tldrgraph/enrichment_response.json` or `.tldrgraph/pending_enrichment.json`): +```json +[ + { + "id": "backend_src_applications_applications_controller_applicationscontroller", + "intent": "### Pension Application Lifecycle Gateway\nREST gateway for the pension application lifecycle.", + "input_fields": ["caseId", "transitionPayload", "remarks", "sanctionOrderNo"], + "output_fields": ["applicationStatus", "disposition"], + "calls": ["ApplicationsService", "JwtAuthGuard", "RolesGuard", "pension_cases"] + } +] +``` + +| Key | Type | Meaning | +| --- | --- | --- | +| `id` | string, **required** | The node id, copied **verbatim** from the request. An id that is not in the graph is skipped silently. | +| `intent` | string (Markdown) | Markdown formatted explanation: what this symbol does, its role, and why it exists. AI decides how much depth is needed. This is the text semantic search matches against. | +| `input_fields` | array of strings | Input parameters, arguments, request body payload attributes, query filters. | +| `output_fields` | array of strings | Return types, response models, emitted event names, or mutated state attributes. | +| `fields` | array of strings (legacy) | Supported for backwards compatibility (maps to input fields). | +| `calls` | array of strings or objects | Downstream symbols, files (`file:symbol`), or node IDs this symbol calls. Cross-layer bridges are created with 100% confidence. | +| `layer_id` | string (optional) | Explicitly reassign the architectural layer ID if the AST classification miscategorized it. | + +`input_fields`, `output_fields`, and `calls` may be omitted or empty. An object with only `id` and `intent` is +valid and useful. + +--- + +## Hard rules + +1. **Open and read the actual source file before writing an intent.** You have the repo + checked out; that is the entire reason this path exists. Read `file` (use + `source_location` to find the symbol), and read enough of its imports and callees to + describe what it really does. An intent paraphrased from the label is worse than no + intent, because it poisons search with confident-sounding noise. + +2. **Do not invent fields or calls. Omit what you cannot verify in the code.** If you + read the file and it handles three params, list three. Do not pad the list with what a + symbol of that name "usually" has. `"fields": []` is a correct, honest answer. + A wrong `calls` entry creates a real, wrong edge in the graph that later queries will + follow. + +3. **`calls` entries are resolved with 2-tier high precision.** + - **Tier 1 (Exact Match, 100% confidence):** Exact symbol names (`ApplicationsService`), + function names, node IDs, file paths (`calc.ts`), or database table names (`pension_cases`). + - **Tier 2 (Vector Fallback):** Semantic search with a calibrated 0.35 score floor. + + | Good | Bad | + | --- | --- | + | `ApplicationsService` | `the application service` | + | `calc.ts` | `some calculation helper` | + | `pension_cases` | `the database` | + | `JwtAuthGuard` | `auth stuff` | + + Prefer the exact symbol name, file name, or table/model name as it appears in the source. + +4. **Copy `id` verbatim.** Do not normalize, shorten or re-case it. + +5. **Answer only the nodes in the request.** Extra ids are ignored; missing ids just come + back in a later batch. + +--- + +## Priority order in the queue + +The queue is not arbitrary — a node that many things depend on is worth more of your +attention than a leaf. A node is a **candidate** when it sits outside `General / Utility` +and either has no intent at all, or has one that came from the offline template heuristic +(`enrichment_source: "heuristic"`, i.e. nobody read the source). Candidates are sorted by: + +1. **`cross_layer_degree` descending** — neighbours that sit in a *different* layer. + These are the seams TLDRGraph exists to describe, and they are exactly where the AST + alone is weakest. +2. **`degree` descending** — total in + out edges. Hub nodes first. +3. **node id ascending** — only to make the ordering deterministic. + +Both degrees are computed from the live graph. (The `degree` key that graphify emits is +absent, so anything reading `node["degree"]` from the raw export sees `0`; TLDRGraph +recomputes it and stamps it back into `.tldrgraph/graph.json`.) + +--- + +## Paging and progress + +`queue-enrichment` remembers what it has handed out in `.tldrgraph/enrichment_cursor.json`: + +- `applied` — ids successfully merged by `apply-enrichment`. Never re-queued. +- `queued` — ids handed out but not yet applied ("in flight"). Skipped by default. + +So running `queue-enrichment` twice in a row **advances** to the next batch instead of +repeating. Two escape hatches: + +- `--requeue` — also hand out in-flight ids again (use when a batch was abandoned). +- `--reset` — clear all progress and start again from the highest-priority node. +- `--limit 0` — no cap; queue every remaining candidate at once. + +--- + +## What `apply-enrichment` does with your answer + +For each object it can match to a node: + +1. sets `intent`, rewrites `summary` to `":