A controlled-experiment audit on a 1240-file Python project (opensre)
showed that 27.2% of resolved-internal imports never made it from
project-scanner's `importMap` into the final knowledge graph. Of the
404 source files with internal imports, 91 ended up with ZERO imports
edges in the graph despite their `file:` node being present (consistent
with main-session orchestrator dropping the entry from `batchImportData`
during batch construction), and 104 had partial coverage (consistent
with file-analyzer agent dropping rows during edge enumeration).
GitHub issue #128 reported the same failure mode at 16-21% on a Go
monorepo.
The fix has two layers:
1. `merge-batch-graphs.py` now runs a deterministic recovery pass
after merge: for every `(source, target)` in scan-result.json's
`importMap` whose source `file:` node exists in the assembled graph
and whose target `file:` node also exists, emit an `imports` edge
if the batches didn't already. Recovered edges are tagged
`recoveredFromImportMap: true` so downstream consumers can audit
which edges came from the deterministic source vs. agent emission.
The merge report logs the recovered count plus how many importMap
entries were skipped because their source/target had no graph node.
2. `file-analyzer.md` rewrites the imports edge rule to demand 1:1
emission with a self-check: "the number of `imports` edges in your
output MUST equal `sum(batchImportData[file].length)` across the
batch's code files". This drives the agent to enumerate every row
instead of summarizing — recovery should report 0 when this works.
Tests: +6 cases covering the recovery path — drops, no-double-emit,
missing source/target nodes, missing scan-result.json (incremental
update), and self-import suppression. 770 passing (was 764).
Bumps version to 2.6.3 across the five tracked manifests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A deep audit of the project-scanner → file-analyzer → merge pipeline
turned up a wide range of silent data-loss bugs. Each one alone is
small; together they were producing graphs with very few import edges,
missing sub-file nodes for non-code formats, and inconsistent metrics.
Root-cause fixes (high impact):
- project-scanner.md: extend import-pattern table to resolve absolute
imports for Python (`from a.b.c import x`), TS/JS (tsconfig.json
paths/baseUrl aliases), Java/Kotlin (`com.foo.Bar` ↔ file paths),
Ruby (`require 'foo/bar'` load-path), PHP (composer PSR-4 namespaces),
and C/C++ (`#include` headers). Was relative-only, which produced
empty importMap entries for the majority of real projects.
- project-scanner.md: add `.ps1`, `.bat`, `.cmd`, `.jsonc` to language
table; require non-null `language` field with an explicit fallback.
- file-analyzer.md: document `sections`, `definitions`, `services`,
`endpoints`, `steps`, `resources` in the extraction-output schema and
spell out the sub-file node-creation rules per category. Was missing,
so per-table / endpoint / resource nodes were never created from
SQL / OpenAPI / Terraform / K8s / Dockerfile parser output.
- file-analyzer.md: add explicit source-reading fallback rules for
PowerShell, Batch, Bash, Swift, Kotlin (no tree-sitter coverage).
- yaml-parser: declare `kubernetes`, `docker-compose`, `github-actions`,
`openapi` languages so files the language-registry tags with those
ids actually get section extraction. Recognize quoted top-level keys
(e.g. `"on":` in GitHub Actions). Emit one section per entry for
array-root YAML documents.
- json-parser: declare `json-schema`, `openapi`; add `stripJsoncSyntax`
helper that removes line / block comments and trailing commas before
parse so `.jsonc` files (wrangler, tsconfig with comments) parse cleanly.
- shell-parser: declare `jenkinsfile`. Tighten function-detection regex
to require a reachable `{` brace so `name() echo hi` and patterns
appearing inside heredocs are no longer false-positives.
- markdown-parser: track fenced-code-block state and skip headings
inside ``` / ~~~ blocks (`# install` shell comments were being
emitted as level-1 sections).
- merge-batch-graphs.py: add `article`, `entity`, `topic`, `claim`,
`source` to VALID_NODE_PREFIXES and TYPE_TO_PREFIX so knowledge-base
node types stop being flagged unknown / coerced to `file:`. Add
`direction` to the edge dedup key so `forward` and `bidirectional`
variants of the same (src, tgt, type) don't overwrite each other.
Use a placeholder in bare-id fallback when `filePath` is missing on
function/class nodes so unrelated `parse()` functions don't merge.
- typescript-extractor: actually compute `isDefault` for default
exports (was always emitted as `false` from buildResult).
- extract-structure.mjs: match `wc -l` semantics for `totalLines` so
the scanner's `sizeLines` and the extractor's `totalLines` agree on
POSIX text files. Filter the parser-imports fallback to relative-only
so `importCount` semantics stay *internal-import* whether the scanner
resolved them or not. Drop unused `isCode` local.
Tests: +19 cases covering JSONC parsing, markdown fenced-code skip,
YAML quoted-keys / array-root, shell function false-positives,
extract-structure import fallback semantics + totalLines off-by-one.
764 passing (was 745).
Bumps version to 2.6.2 across the five tracked manifests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs surfaced when analyzing Python projects that use absolute imports:
- The dispatch prompt (SKILL.md) and file-analyzer agent omitted the
per-file `language` field, so `extract-structure.mjs` received null and
passed it through to the graph.
- `extract-structure.mjs` used `if (importPaths)` to decide whether to
trust pre-resolved imports. Empty arrays are truthy, so files where the
project scanner could not resolve any imports (e.g. Python absolute
imports) clobbered the parser's import count with 0, never falling
back to tree-sitter's own analysis.
Bumps plugin version to 2.6.1 across the five tracked manifests and adds
unit tests for `buildResult` covering language pass-through and the
importCount fallback paths. To make the script testable, `buildResult` is
now exported and the CLI invocation is guarded so importing the module
no longer triggers `main()`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove bin/ from DEFAULT_IGNORE_PATTERNS (Node/Ruby CLI launchers use bin/)
- .NET users can add bin/ to .understandignore manually
- Fix project-scanner Step 2.5 to re-filter from original file list when
.understandignore exists, ensuring ! negation correctly overrides defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Python scripts to merge knowledge graphs (closes#70) and move
mechanical normalization out of LLM context into deterministic scripts
with diagnostic reporting. Convert all agent definitions from dispatch
templates to self-contained system prompts to prevent instruction loss.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous wording ("MUST have sub-nodes") implied every code file
must produce function nodes, which is wrong for files without significant
functions. Now correctly states: if significant functions exist in the
script output, you must create nodes for them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prompt templates (file-analyzer, project-scanner, architecture-analyzer,
tour-builder, graph-reviewer) were being compressed by the orchestrator
when dispatched as subagent prompts, causing function/class extraction
to be silently skipped. Moving them to agents/ ensures the framework
loads the full prompt without compression.
- Move 5 prompt templates from skills/understand/ to agents/
- Update SKILL.md to reference agent definitions instead of templates
- Set all agent models to `inherit` for cross-platform compatibility
- Update CLAUDE.md to reflect new agent model policy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLMs systematically abbreviate node types (e.g. "func" instead of
"function") and edge types (e.g. "extends" instead of "inherits"),
causing dashboard validation failures. This combines two fixes:
Option A: Rename the ambiguous `func:` ID prefix to `function:` across
all prompts, source code, tests, and example data so LLMs see consistent
naming. Also fix `relates_to` ghost edge type in django.md.
Option B: Add NODE_TYPE_ALIASES and EDGE_TYPE_ALIASES normalization maps
in schema.ts that transparently correct common abbreviations before Zod
validation, as a runtime safety net.
Closes#36
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Edge weights, node ID prefix conventions, and tag formatting were listed
in both Check 1 (Schema Validation — Critical) and Check 7 (Quality —
Warning). A deterministic script following check headings would classify
these as warnings, potentially approving invalid graphs. Remove the
duplicates from Check 7 since Check 1 already covers them as critical.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The exclusion rule conditionally kept .sh files for bash-first projects,
but the source-file whitelist and language mapping table had no .sh entry,
causing retained shell files to be dropped or untyped. Add .sh/.bash to
the whitelist and language table (mapped to 'bash'), and remove the
conditional exclusion rule.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Duplicate node IDs were listed under Check 6 (Warning) but classified as
critical in the Severity Classification section. A script following check
headings would classify duplicates as warnings, letting invalid graphs
pass review. Move duplicate-ID check to its own Check 5 (Critical) and
renumber subsequent checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redesign all 5 agent prompts with a "Phase 1 — Script" + "Phase 2 — LLM
Reasoning" structure. Agents now write and execute scripts for deterministic
work (validation, file discovery, structure extraction, graph analysis)
before applying LLM judgment for semantic tasks (summaries, tags, pedagogy).
Key changes per agent:
- graph-reviewer: script performs all 6 validation checks deterministically
- project-scanner: script handles file discovery, language detection, line counting
- file-analyzer: script extracts functions/classes/imports via regex patterns
- architecture-analyzer: script computes import adjacency, inter-group frequency
- tour-builder: script calculates fan-in, BFS traversal, cluster detection
Design safeguards:
- scriptCompleted sentinel field guards against partial script output
- Batch-indexed temp paths prevent collision in concurrent file-analyzer agents
- Explicit "trust the script" directives prevent LLM from re-reading raw data
- Up to 2 script retries on failure
- Explicit instructions to strip intermediate fields from final output
- Bash tool added to all agent tool lists
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove ChatPanel and @anthropic-ai/sdk dependency (redundant with /understand-chat)
- Replace Monaco editor with styled summary code viewer
- New graph-first layout: 75% graph + 360px right sidebar
- Dark luxury aesthetic: deep blacks, gold/amber accents, DM Serif Display typography
- Add ProjectOverview component for sidebar default state
- Learn persona now shows tour panel directly in sidebar
- Add schema validation on graph load with error banner
- Defensive null checks in store for tour methods
- Agent pipeline: write intermediate results to disk instead of context
- Agent models: sonnet for simple tasks, opus for complex (no haiku)
- Prompt-engineer all 5 agent prompts and SKILL.md
- Auto-trigger /understand-dashboard after /understand completes
- Add dashboard screenshot to README
- Bump version to 1.0.1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move packages/{core,dashboard,skill} into understand-anything-plugin/ to
conform to the Claude Code plugin format. Add .claude-plugin/marketplace.json
for plugin discovery. Update workspace config and docs accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>