# AgentTeam Insight `graph` — UA parity audit (#t1480)

This audit compares upstream Understand-Anything (UA) pipeline semantics —
**everything before `knowledge-graph.json` is written** — against the current
AgentTeam Insight Go implementation in the PR #2474 convergence branch.

Original #t1480 scope: **read-only analysis and recommendations**. Later
implementation batches update this same document as the live coverage matrix
and drift ledger before reviewer gates, per Section 9.

## TL;DR

PR #2474 started as a **deterministic dashboard-loadable skeleton**, not a
full UA semantic parity implementation. After #t1481, the highest-risk Layer 2
contract drift is fixed in the Go fallback path: layers contain only file-level
nodes, every file-level node is assigned exactly once, and the Go validator
reports layer exclusivity/coverage violations plus orphan warnings. Recent
convergence batches closed the scoped scanner, batching, runner, and
lightweight extractor, and final review seams: #t1486 adds deterministic
review artifact / Layer 3 checks, tour flow enhancements, `tested_by` linker
Pass 2, and scoped locale addendum loading; #t1485 adds 10-family lightweight
import-map resolution plus high-signal multi-language/non-code extraction;
#t1484 adds the scan-result contract/narrative scaffold and a mockable
OpenCode file-analyzer runner; and #t1483 wires the batch/neighborMap analyzer
seam and batch-output contract. The overall pipeline remains **partial**
against UA: exact tree-sitter resolver/
extractor parity, richer language registry/config coverage, real LLM reviewer
quality, and architecture/tour-builder LLM parity remain follow-up gaps;
#t1491 closes the previous connected-components batching drift with a pure-Go
Louvain-style community pass while retaining a residual graphology fixture drift.

The PR description material at `docs/pr/2026-07-02-agentteam-insight-graph-builder.md` has
been narrowed to "dashboard-compatible deterministic convergence branch"
wording and must not be used to claim full UA semantic parity until the
remaining #t1480 rows are closed.

> **Revision v12 / #t1505 (OpenCode/skills production design gate).** This
> revision does not implement new runtime code. It records the remaining
> #t1494 blockers after #t1501–#t1504 naming/release-surface convergence:
> server-backed OpenCode sessions are not yet wired through a production
> `--opencode-server` path; file-analyzer tasks still expose the original
> repository root and allow external directory reads instead of copying a
> session-local `sources/<relative-path>` subset; and Graph Builder has no
> allowlisted embedded skill bundle/materialization manifest. Section 2 keeps
> the affected rows partial, Section 3 adds F17–F19, Section 5 adds I22–I24,
> and the final grep gate/allowlist is documented as a release gate rather
> than an implementation claim.

> **Revision v11 / #t1491 (Louvain/community batching).** Implements
> the next remaining-gap batch for Phase 1.5: `batching.BuildPlan` now uses a
> dependency-free pure-Go Louvain-style modularity pass for code-file community
> assignment before UA's existing size enforcement, non-code Groups A-E,
> singleton pooling, neighborMap enrichment, and changed-files output filter.
> Dense import graph coverage now verifies that a 50-file connected component
> is split into multiple semantic communities instead of one BFS batch. This
> closes the observable connected-components drift; exact byte-for-byte
> graphology implementation details remain a residual drift until compared
> against upstream fixtures.


> **Revision v10 / #t1486 (final review/tour enhancements).** Implements
> the final convergence batch for scoped gate items: `reviewer` now emits
> `.agentteam-insight/intermediate/review.json` for normal and `--review`
> paths, applies deterministic safe fixes, records Layer 3-style issues/
> warnings, and revalidates through Layers 1+2; `--review` reuses existing
> graph artifacts instead of dispatching file-analyzer batches. `assembler`
> now drops semantically broken `tested_by` pairs, synthesizes production →
> test edges from naming/path conventions, and injects a `tested` tag on
> covered production nodes. `tour` now stays file-level, supports calls-flow
> ordering, configurable `MaxSteps`, and deterministic `languageLesson`;
> OpenCode prompts load UA locale addendums for agent-capable paths while
> deterministic fallback does not claim locale parity. No new drift findings
> were discovered during #t1486; #t1491 later scopes the Louvain gap. Remaining
> gaps are exact tree-sitter/path-alias/language-registry parity, real LLM
> assemble/graph-review quality, and architecture/tour-builder LLM parity.


> **Revision v9 / #t1485 (multi-language + non-code extractors).** Implements
> the fifth convergence batch: `scanner/language.go` now recognizes Kotlin,
> C#, PHP, and Ruby explicitly; `extractor/generic` performs dependency-free
> high-signal structure/import extraction for TS/JS, Python, Rust, Java,
> Kotlin, C#, PHP, C/C++, Ruby, Markdown, SQL, YAML/OpenAPI/K8s, GraphQL,
> Protobuf, and Dockerfile inputs; `importgraph` resolves project-internal
> imports for the 10 UA resolver families without adding tree-sitter/CGO/WASM
> dependencies. `graphbuilder` now emits deterministic UA-style non-code child
> nodes (`service`, `resource`, `table`, `schema`, `endpoint`) with parent
> `contains` edges, while layer generation/validation still assign only
> top-level file artifacts. This is intentionally marked partial, not strict
> UA parity: exact tree-sitter parsing, path-alias/package-manager-specific
> resolver behavior, Swift, and richer non-code extraction remain gaps. No new
> drift findings were discovered during #t1485.

> **Revision v8 / #t1484 (file-analyzer runner + scan-result contract).** Implements
> the fourth convergence batch: `scan-result.json` now uses UA-facing fields
> (`name`, `description`, `files[].sizeLines`, `files[].fileCategory`,
> `totalFiles`, `filteredByIgnore`, `estimatedComplexity`, `languages`,
> `frameworks`, `importMap`) with lowercase never-null languages and UA's
> `small|moderate|large|very-large` complexity bands; transient
> `rawDescription` / `readmeHead` work strings are kept only in internal
> project context and are not serialized to final `scan-result.json`. `run.go`
> now writes `importMap` into scan-result and passes project narrative/context
> plus the 10 UA import-map resolver family scaffold to every batch analyzer
> request. A concrete OpenCode-backed file-analyzer runner is wired behind the
> existing agent-enhancement flags and is covered with a mock command runner
> that returns enriched graph fragments; default/no-agent runs remain
> deterministic. Real OpenCode runner dispatch is a Go-first safety scope:
> serial by default unless `--isolate-opencode-runtime` is enabled, while
> deterministic fallback / injected runners retain #t1483's 5-slot cap. The
> code now records the 10 UA resolver families in `importgraph`, with only Go
> marked implemented and the other nine kept as explicit gaps. No new drift
> findings surfaced in #t1484.

> **Revision v7 / #t1483 (batch/neighborMap analyzer seam).** Implements
> the third convergence batch: `buildGraph` now materializes `batches.json`,
> dispatches one analyzer/fallback fragment per batch with `batchIndex`,
> `batchImportData`, and `neighborMap`, caps dispatch at 5 concurrent jobs,
> writes UA-compatible `batch-<i>.json` outputs, and fails closed on missing or
> empty batch outputs. Incremental mode now uses the current full-scan/full-graph
> batch assignment plus changed-files output filter (no renumbering), writes
> `tmp/changed-files.txt`, writes pruned existing graph to `batch-existing.json`,
> and merges fresh changed batch outputs without reading prior `batches.json` or
> cached unchanged batch outputs. `batching.BuildPlan` also covers non-code
> Groups A-E and singleton pooling (MIN=3, MAX_MERGE_TARGET=25). Louvain was still
> a documented gap/new drift at #t1483 time; #t1491 later replaces the
> Go-first connected-components planner with a pure-Go Louvain-style pass.

> **Revision v6 / #t1482 (scanner/ignore + pre-flight + cleanup).** Implements
> the second convergence batch: scanner now reads UA's canonical
> `.agentteam-insightignore`, root `.agentteam-insightignore`,
> `.gitignore`, and hardcoded defaults; `git ls-files -z -co
> --exclude-standard` is the preferred enumeration path with fallback walk;
> missing canonical ignore files generate a starter and require confirmation,
> with `--no-prompt` documented as a Go-first CI/smoke extension;
> pre-flight creates intermediate/tmp dirs, purges `.trash-*` older than seven
> days, and persists `config.json.autoUpdate` / normalized
> `config.json.outputLanguage` while analysis continues; successful saves keep
> `intermediate/scan-result.json` and archive both other intermediate scratch
> files and `.agentteam-insight/tmp/` to `.trash-<timestamp>/`.
> Section 2/F3/F14/F15/I3a/I3b/I13/I14/I15 are
> updated below. No new drift findings surfaced in #t1482; remaining scanner
> gaps after #t1482 were the UA LLM project-scanner narrative, wider language
> registry, and non-Go import-map resolvers (I16/F4); #t1484 later narrows the
> narrative/contract portion.

> **Revision v5 / #t1481 (schema/layer hardening).** Implements the first
> post-audit convergence batch: `layers.Generate` now assigns only UA
> file-level nodes and each such node appears in exactly one deterministic
> fallback layer; `schema.ValidateGraph` now emits Layer 2 inline issues for
> `node-in-multiple-layers` and `file-node-not-in-any-layer`, and emits
> `orphan-node` only as a warning. PR/smoke docs were narrowed so they no
> longer imply full UA semantic parity, and Section 2 reflects Phase 4/6 as
> partial instead of blocker drift. No new drift findings surfaced in this
> batch; remaining work stays in F4–F7/F12+ and I4/I16+ follow-ups.

> **Revision v4 (post third-gate incremental batching correction).**
> Reviewer2's #t1480 third gate accepted the v3 factual corrections but
> surfaced a remaining blocker: incremental `--changed-files` batching was
> still described as "prior-run `batchIndex` / prior `batches.json` / cached
> unchanged batch outputs / only changed batches re-planned". UA source
> (`compute-batches.mjs:528-535`, with the explicit comment that `batchIndex`
> on filtered batches **retains the full-graph assignment**, and `SKILL.md`
> Phase 2 incremental) shows the real semantics: **always** run the
> full-graph plan from current `scan-result.json`, filter OUTPUT only (no
> renumbering, no prior `batches.json` load, no cached unchanged
> `batch-<i>.json` read), then prune existing graph by filePath into
> `batch-existing.json` and merge with fresh changed `batch-*.json`. v4
> rewrites four ranges to reflect this — no new F-numbers or I-numbers.
> Sections touched: Section 1 Phase 1.5, Section 2 Phase 1.5 missing item,
> Section 5.1 I17 implementation + acceptance test.
>
> **Revision v3 (post second-gate source-verified correction).** Reviewer2's
> #t1480 second gate caught six factual errors in v2 where UA strict parity
> had been conflated with Go-first extensions or with hypothetical semantics
> not present in UA source. This revision is **only a documentation
> correction** — no code touched. The corrections:
> - **Phase 0** — `--auto-update/--no-auto-update` only writes
>   `config.json.autoUpdate`, analysis continues normally either way;
>   `--language` writes `config.json.outputLanguage` (NOT `meta.json`);
>   `--review` reuses existing graph + jumps to Phase 6 Layer 3, **then
>   Phase 7 still saves** (not "skip persistence").
> - **Phase 0.5** — `--no-prompt` for `.agentteam-insightignore` confirmation auto-accept is
>   **Go-first**, not UA. UA's Phase 0.5 is interactive-only and has no
>   `meta.json` auto-accept field.
> - **Phase 1** — `filteredByIgnore` is a single **integer** counter (not
>   a `{userDriven,defaultDriven}` object); `estimatedComplexity` enum is
>   **`small|moderate|large|very-large`** (not `trivial|huge`);
>   `detectLanguage` **never returns null** (fallback is lowercased ext or
>   `"unknown"`); Phase 6 validator does NOT validate `scan-result.json`.
> - **Import-map resolver list** — UA has **10 resolvers**, NOT 12, and
>   **no Swift resolver**. Languages: TS/JS (one resolver), Python, Go,
>   Java, Kotlin, C#, PHP, Rust, C/C++, Ruby.
> - **Batch file naming** — UA accepts non-zero-padded names
>   (`batch-8.json`, `batch-8-part-1.json`); zero-pad is **Go-first style**,
>   not parity.
> - **Phase 6 Layer 3 thresholds** — empty layer = **zero** nodeIds
>   (not `<2`); tour = **5-15 steps** (not `<3`); orphan nodes are
>   **warning**, not critical.
> A new Section 6.1 enumerates the Go-first extensions in a single table
> (G1–G6) so they are not mistaken for UA strict parity in downstream
> batches.
>
> **Revision v2 (post first-gate feedback).** Reviewer2's #t1480 first gate
> returned six execution-grade gaps in the original draft. v2 incorporated
> them:
> - Phase 0 — added `--auto-update/--no-auto-update`, `--language`
>   normalization + persistence, review-only path, up-to-date interaction /
>   stop semantics (Section 1 Phase 0, Section 2 Phase 0, I15).
> - Phase 0.5 — split into "read existing" vs "generate starter +
>   confirmation gate" (I3 renamed to I3a, new I3b).
> - Phase 1 — added `project-scanner` LLM narrative responsibilities,
>   per-language import-map resolver, output field verbatim constraints
>   (Section 1 Phase 1, Section 2 Phase 1, I16).
> - Phase 1.5 / Phase 2 — added batch output naming contract,
>   `--changed-files` full-graph `batchIndex` preservation, non-code
>   Groups A-E, max 5 concurrent file-analyzer subagents, batch completion
>   / file-existence check (Section 1 Phase 1.5/2, Section 2 Phase 1.5/2,
>   I17).
> - Phase 6 — split validator into three layers (schema.ts `validateGraph`,
>   inline validator, `graph-reviewer` LLM) with explicit Go mapping and
>   graph-reviewer quality-check coverage (Section 1 Phase 6, Section 2
>   Phase 6, Section 4 validator stage parity, I18).
> - Cross-batch discipline — every subsequent batch must update the coverage
>   matrix and record new drift as next-round convergence items (new
>   Section 9).

## Source-of-truth files

- UA checkout: `/Users/lonng/devel/ai/Understand-Anything/understand-anything-plugin/`
- UA pipeline spec: `skills/understand/SKILL.md`
- UA analyzer core: `packages/core/src/analyzer/{graph-builder,llm-analyzer,layer-detector,tour-generator,normalize-graph,language-lesson}.ts`
- UA schema/validator: `packages/core/src/schema.ts`
- UA change-classifier + fingerprint: `packages/core/src/{change-classifier,fingerprint}.ts`
- UA ignore filter: `packages/core/src/ignore-filter.ts`
- UA language registry: `packages/core/src/languages/configs/*.ts` (42 configs)
- UA batch planner: `skills/understand/compute-batches.mjs`
- UA structure extractor: `skills/understand/extract-structure.mjs`
- UA merge step: `skills/understand/merge-batch-graphs.py`
- Go implementation root: `kits/agentteam-insight/internal/graphbuilder/`

Throughout this document, citations use `UA:` / `Go:` prefixes with file:line.

## Section 1 — UA pipeline phase-by-phase

What UA does, in order, before `knowledge-graph.json` lands on disk.

### Phase 0 — Pre-flight

- **Input:** CLI args, CWD, existing `.agentteam-insight/{knowledge-graph,meta,config}.json`.
- **Output:** `PROJECT_ROOT`, `OUTPUT_LANGUAGE`, `$PHASE_WARNINGS`, current git
  commit, intermediate dirs, `.trash-*` dirs >7 days purged, mode decision
  (`auto | full | incremental | review`), `auto-update` policy, "up-to-date"
  state.
- **Key semantics (UA strict parity — source-verified):**
  - Worktree redirect to main repo root (`SKILL.md:52-72`).
  - Plugin build check + `PLUGIN_ROOT` resolution (`SKILL.md:73-119`).
  - Stale `.trash-*` purge (>7 days) (`SKILL.md:132-135`).
  - Subdomain graph pre-merge (`SKILL.md:159-164`).
  - **`--auto-update` / `--no-auto-update`** writes
    `{"autoUpdate": <bool>}` into `config.json` (`SKILL.md:136-139`).
    **Analysis proceeds normally regardless of which flag is set.** The flag
    is purely a config-record for the dashboard's auto-update UI; it does
    NOT gate persistence or trigger a dry-run.
  - **`--language <code>`** normalizes friendly names to ISO codes, then
    merges `{"outputLanguage": "<lang>"}` into `config.json` (`SKILL.md:141-158`).
    First-run detection prompts the user once when the detected language is
    not `en`; subsequent runs read `config.json.outputLanguage` and skip the
    prompt. The chosen value is **persisted into `config.json`**, not
    `meta.json`.
  - Mode decision matrix (`SKILL.md:168-178`):
    - `--full` → full analysis.
    - No existing graph/meta → full analysis.
    - `--review` + existing graph + unchanged commit hash → **review-only
      path**: copy existing `knowledge-graph.json` to
      `intermediate/assembled-graph.json`, jump to Phase 6 step 3 (run
      graph-reviewer LLM). **Phase 7 Save still runs** — `--review` does NOT
      skip persistence; it reuses the existing graph for the LLM review pass
      and then saves normally.
    - Existing graph + unchanged commit hash (no `--review`) → interactive
      prompt: (a) `--full`, (b) `--review`, (c) do nothing. (c) exits
      without analysis or persistence.
    - Existing graph + changed files → incremental update.
- **Go-first extensions beyond UA strict parity** (none should be marked as
  UA parity — see Section 6.1):
  - `--no-prompt` for non-interactive auto-accept of the Phase 0.5
    `.agentteam-insightignore` confirmation — **Go-first**, not UA. UA has no `meta.json`
    auto-accept field.
  - `mode=review` that skips `SaveArtifacts` and exits non-zero on fatal
    issues — **Go-first** (UA's `--review` still saves).
  - `--auto-update` toggling a dry-run / "stop before save" path —
    **Go-first** (UA only uses the flag to record config).
  - "Up-to-date interaction / stop semantics" gating artifact writes on
    `--auto-update` — **Go-first**.
- **LLM:** No.
- **Refs:** `UA: skills/understand/SKILL.md:42-194`.

### Phase 0.5 — Ignore config

- **Input:** `$PROJECT_ROOT`.
- **Output:** `.agentteam-insightignore` (generated if missing);
  user confirmation before Phase 1 starts.
- **Key semantics (UA strict parity — source-verified):**
  - **Read existing** (`packages/core/src/ignore-filter.ts:86-111`): combine
    (1) hardcoded defaults, (2) `.agentteam-insightignore`,
    (3) root `.agentteam-insightignore`. Apply during Phase 1 scan.
  - **Generate starter** (`skills/understand/generate-ignore.mjs` +
    `packages/core/src/ignore-generator.ts`): if
    `.agentteam-insightignore` is absent, write a starter file
    that ignores common noise (build outputs, dependency dirs, lock files,
    IDE artifacts) using the `ignore` npm package's syntax, then **print a
    message and wait for user confirmation** (`SKILL.md:202-213`). UA does
    not have a `--no-prompt` flag for this gate.
  - **Confirmation gate:** UA's Phase 0.5 is interactive-only. The skill
    explicitly says "Wait for user confirmation before proceeding"
    (`SKILL.md:209, 212`). Non-interactive runs are not part of UA's
    strict-parity contract.
- **Go-first extensions beyond UA strict parity:**
  - `--no-prompt` flag that auto-accepts the Phase 0.5 `.agentteam-insightignore` confirmation (generated or existing) — **Go-first**,
    needed for CI/smoke pipelines but not in UA. #t1482 implements this
    without any `meta.json` auto-accept record.
- **LLM:** No.
- **Refs:** `UA: skills/understand/generate-ignore.mjs`;
  `packages/core/src/ignore-generator.ts`;
  `packages/core/src/ignore-filter.ts:86-111`.

### Phase 1 — SCAN

- **Input:** Project root, README, manifest.
- **Output:** `.agentteam-insight/intermediate/scan-result.json` with
  `name`, `description`, `frameworks`, `languages[]`, `files[]` (carrying
  `path`, `language`, `sizeLines`, `fileCategory`), pre-resolved `importMap`,
  `filteredByIgnore` accounting, `estimatedComplexity`.
- **Key semantics:**
  - Two-step split: bundled `scan-project.mjs` does deterministic enumeration +
    language/category/line-count/complexity (`scan-project.mjs:466-559` prefers
    `git ls-files -z -co --exclude-standard`, falls back to recursive walk);
    `extract-import-map.mjs` resolves project-internal imports per file using
    tree-sitter + language rules.
  - **`project-scanner` LLM narrative responsibilities**: final
    `scan-result.json` includes `name`, `description`, `frameworks[]`
    (concrete framework IDs like `nextjs`, `gin`, `django`), and
    `languages[]` (concrete language IDs like `typescript`, `go`). UA uses
    `rawDescription` and `readmeHead` as transient work strings for composing
    narrative/context, but `agents/project-scanner.md:173` requires stripping
    them from the final output. The LLM **does not** enumerate files, compute
    line counts, or produce `importMap` — those are deterministic and come from
    `scan-project.mjs` / `extract-import-map.mjs`.
  - **Per-language import-map resolver** (`extract-import-map.mjs`): UA has
    dedicated resolvers for **10 languages** — TypeScript/JavaScript (one
    resolver covering `.ts/.tsx/.js/.jsx/.mjs/.cjs/.vue`), Python, Go, Java,
    Kotlin, C#, PHP, Rust, C/C++, Ruby (`extract-import-map.mjs:1407-1455`,
    dispatcher `resolveImport`). Each resolver knows the language's import
    syntax (e.g. Python relative imports `from . import x`, Go module paths,
    Rust `use` / `mod`, Java packages, Ruby `require_relative`). **There is
    no Swift resolver.** Non-code files (Dockerfile, YAML, SQL, etc.) do not
    contribute to `importMap` (they get empty arrays).
  - **Output field verbatim constraints** (these come from the
    `scan-project.mjs` source; **Phase 6 validator does NOT validate
    `scan-result.json`** — Phase 6 only validates the final
    `knowledge-graph.json`):
    - `files[].path`: forward-slash POSIX relative to `PROJECT_ROOT`.
    - `files[].language`: lowercase language ID. **Never null** — the
      fallback for unknown extensions is the lowercased extension without
      the dot, and the fallback for no-extension unknown files is the
      string `"unknown"` (`scan-project.mjs:221-248`).
    - `files[].fileCategory`: exactly one of
      `code | config | docs | infra | data | script | markup`.
    - `files[].sizeLines`: integer ≥ 0 (newline count, `wc -l` semantics).
    - `importMap[path]`: array of resolved project-internal paths; **no
      external/stdlib imports**; unresolved imports are dropped, not kept as
      raw strings.
    - `filteredByIgnore`: **integer** count of files dropped specifically by
      user `.agentteam-insightignore` patterns (the delta beyond hardcoded
      defaults). NOT an object — it is a plain integer counter
      (`scan-project.mjs:36, 677-692`).
    - `estimatedComplexity`: enum **`small | moderate | large | very-large`**
      derived from total file count bands (1-30 / 31-150 / 151-500 / >500)
      (`scan-project.mjs:37, 423-440`). NOT `trivial|huge`.
  - Ignore filter combines (1) hardcoded defaults, (2)
    `.agentteam-insightignore`, (3) root `.agentteam-insightignore`
    (`packages/core/src/ignore-filter.ts:86-111`).
  - `fileCategory` enum: `code | config | docs | infra | data | script | markup`.
- **LLM:** Yes for narrative only (`name`, `description`, `frameworks`,
  `languages`; transient `rawDescription` / `readmeHead` are stripped from
  final `scan-result.json`).
- **Refs:** `UA: skills/understand/scan-project.mjs`,
  `extract-import-map.mjs`, `agents/project-scanner.md`.

### Phase 1.5 — BATCH (Louvain)

- **Input:** `scan-result.json` (files + `importMap`).
- **Output:** `.agentteam-insight/intermediate/batches.json` with
  `batches[]` (each carrying `batchIndex`, `files`, `batchImportData`,
  `neighborMap`) and `exportsByPath`.
- **Key semantics:**
  - Per-file tree-sitter exports extraction (`compute-batches.mjs:51-121`).
  - Undirected import graph → graphology + Louvain community detection
    (`compute-batches.mjs:234-249`). Failure fallback: count-based 12-file
    chunks.
  - Community cap `MAX_COMMUNITY_SIZE=35`; oversize split alphabetically
    (`compute-batches.mjs:402-426`).
  - **Non-code Groups A-E** (verbatim from UA `compute-batches.mjs:131-215`):
    - **Group A — Dockerfiles:** one batch per directory containing a
      `Dockerfile` (so multi-stage builds stay together).
    - **Group B — `.github/workflows/`:** all GitHub Actions workflow YAMLs
      in a single batch.
    - **Group C — `.gitlab-ci.yml` + `.circleci/`:** GitLab + CircleCI
      configs in a single batch.
    - **Group D — SQL migrations:** one batch per directory matching
      `migrations?/` regex; all `.sql` files in that directory grouped.
    - **Group E — catch-all:** remaining non-code files grouped by parent
      directory, capped at 20 files per batch.
    - Each group carries a `mergeable` flag indicating whether the LLM can
      merge nodes across files in that group.
  - `mergeSmallBatches` pools singletons (MIN=3, MAX_MERGE_TARGET=25).
  - Each batch's `neighborMap` carries 1-hop cross-batch neighbors with
    exported symbols, truncated at `MAX_NEIGHBORS=50` by degree sort
    (`compute-batches.mjs:484-527`).
  - **Batch output naming contract (UA strict parity — source-verified):**
    - `.agentteam-insight/intermediate/batches.json` — the plan itself.
    - `.agentteam-insight/intermediate/batch-<i>.json` — one file per
      batch, where `<i>` is the integer `batchIndex`. UA does NOT
      zero-pad — both `batch-8.json` and `batch-008.json` are accepted by
      the merge regex `batch-(\d+)(?:-part-(\d+))?\.json`
      (`SKILL.md:304-321`).
    - `.agentteam-insight/intermediate/batch-<i>-part-<k>.json` — when a
      batch is split into multiple LLM calls (e.g. exceeds token budget),
      `<k>` is the part index. UA does NOT zero-pad `<k>` either
      (`batch-8-part-1.json` accepted).
  - **Go-first extension beyond UA strict parity:** a AgentTeam-side convention
    MAY mandate zero-padding (e.g. `batch-008.json`, `batch-008-part-01.json`)
    for stable lexicographic ordering of batch files. UA does not require
    this; the merge regex accepts either form. If AgentTeam adopts zero-pad it is
    a Go-first style rule, not a parity requirement.
  - **Incremental `--changed-files` (current-scan + output filter):** UA
    always re-runs the full-graph plan from the **current** `scan-result.json`
    (import graph → Louvain → non-code groups → `mergeSmallBatches`), then
    filters OUTPUT to only batches containing changed files
    (`compute-batches.mjs:528-535`). The full-graph `batchIndex` of kept and
    filtered-out batches is preserved through the filter (no renumbering), so
    `neighborMap` may still reference unchanged files by their full-graph
    index. No prior `batches.json` is loaded and no unchanged `batch-<i>.json`
    is read from cache. Merge prunes existing graph nodes/edges by filePath
    into `batch-existing.json`, then merges with the fresh changed
    `batch-*.json` outputs (`SKILL.md` Phase 2 incremental).
- **LLM:** No.
- **Refs:** `UA: skills/understand/compute-batches.mjs`.

### Phase 2 — ANALYZE (per-batch file-analyzer)

- **Input:** `batches.json`, `batchImportData`, `neighborMap`, project context.
- **Output:** `.agentteam-insight/intermediate/batch-<i>.json` files with
  `nodes[]` and `edges[]` (or `batch-<i>-part-<k>.json` for split batches).
- **Key semantics:**
  - **Up to 5 subagents run concurrently** (`SKILL.md:283-323`). Concurrency
    is enforced via a semaphore; the 6th batch waits for a slot. This caps
    LLM token spend rate and avoids rate limits.
  - Each file-analyzer first runs deterministic `extract-structure.mjs`
    (tree-sitter extraction of functions/classes/imports/exports + non-code
    parsers for `service`/`endpoint`/`table`/`schema`/`resource`/`step`/
    `section`/`definition`).
  - LLM enriches: `summary`, `tags`, `complexity`, `languageNotes`, per-function
    summaries, and the full semantic-edge catalog (`imports`, `calls`,
    `contains`, `inherits`, `implements`, `related`, `reads_from`, `writes_to`,
    `transforms`, `validates`, `tested_by`, `configures`, `deploys`,
    `documents`, `triggers`, `serves`, `defines_schema`, …).
  - Cross-batch edges use `neighborMap` for confidence boost
    (`agents/file-analyzer.md:55-65`).
  - **Batch completion / file-existence check** (Phase 2 → Phase 2.5
    handoff): before merge, the orchestrator verifies every entry in
    `batches.json` has a corresponding `batch-<i>.json` (or all expected
    `batch-<i>-part-<k>.json` parts) on disk. Missing or empty files fail
    closed with a Phase 2 `incomplete-batches` warning added to
    `$PHASE_WARNINGS`. No partial-graph merge.
- **LLM:** Yes — primary LLM phase; produces every summary, tag,
  languageNote, and all non-structural edges.
- **Refs:** `UA: agents/file-analyzer.md`,
  `skills/understand/extract-structure.mjs`,
  `SKILL.md:283-323` (concurrency + completion contract).

### Phase 2.5 — MERGE batch graphs

- **Input:** All `batch-*.json`.
- **Output:** `assembled-graph.json`.
- **Key semantics:** Combine, normalize (strip double prefixes / project
  prefixes, rewrite edge refs, dedupe nodes by id, dedupe edges by
  `(source,target,type)`, drop dangling edges). Then `tested_by` linker:
  - Pass 1: canonicalize inverted `test → prod` edges to `prod → test`,
    drop semantically broken pairs (test↔test, prod↔prod, orphan endpoints).
  - Pass 2: supplement with path-convention pairings (e.g. `foo.go` ←>
    `foo_test.go`).
  - Production nodes sourcing any `tested_by` edge get a `"tested"` tag.
- **LLM:** No (Python script).
- **Refs:** `UA: skills/understand/merge-batch-graphs.py`.

### Phase 3 — ASSEMBLE REVIEW

- **Input:** `assembled-graph.json`, merge stderr, `$IMPORT_MAP`.
- **Output:** `.agentteam-insight/intermediate/assemble-review.json`.
- **Key semantics:** `assemble-reviewer` LLM subagent inspects graph for
  missing files, dangling edges, broken layer assignments. Adds findings to
  `$PHASE_WARNINGS`.
- **LLM:** Yes.
- **Refs:** `UA: agents/assemble-reviewer.md`; `SKILL.md:374-396`.

### Phase 4 — ARCHITECTURE (layer assignment)

- **Input:** File-level nodes only (code/config/document/service/pipeline/
  table/schema/resource/endpoint), all imports edges, all edges for
  cross-category, directory tree, language context, framework addendums,
  optional locale file.
- **Output:** `.agentteam-insight/intermediate/layers.json` → normalized
  into final `layers[]` shape `{id, name, description, nodeIds[]}`.
- **Key semantics:**
  - Every file-level node is assigned to **exactly one** layer.
  - Function/class nodes are NOT assigned
    (`SKILL.md:432-441` — input is "file-level nodes only").
  - Post-processing: unwrap `{layers:[...]}` envelope, rename `nodes`→`nodeIds`,
    synthesize `layer:<kebab>` IDs, convert file paths to prefixed IDs, drop
    dangling refs (`SKILL.md:447-468`).
- **LLM:** Yes (`architecture-analyzer` subagent).
- **Refs:** `UA: agents/architecture-analyzer.md`; heuristic fallback
  `packages/core/src/analyzer/layer-detector.ts:105-144`.

### Phase 5 — TOUR

- **Input:** All file-level nodes, layers, all edges, README, detected entry
  point.
- **Output:** `tour.json` → normalized into final `tour[]` shape with required
  `order`, `title`, `description`, `nodeIds`, optional `languageLesson`.
- **Key semantics:**
  - Tour-builder LLM is explicitly told "do NOT include function or class
    nodes" (`SKILL.md:510-513`).
  - Post-processing: unwrap envelope, rename `nodesToInspect`→`nodeIds` /
    `whyItMatters`→`description`, convert paths, drop dangling refs, sort by
    `order`.
- **LLM:** Yes (`tour-builder` subagent).
- **Refs:** `UA: agents/tour-builder.md`; heuristic fallback
  `packages/core/src/analyzer/tour-generator.ts:135-293`.

### Phase 6 — REVIEW / VALIDATE

UA has **three distinct validator layers**. Conflating them obscures what
each layer is responsible for. Go currently has the first layer only.

- **Input:** Assembled full `KnowledgeGraph` JSON.
- **Output:** `review.json` (`{issues, warnings, stats}`) on `--review`;
  in-memory validation result on default path.
- **Key semantics — three layers:**

  - **Layer 1 — Schema validator (`packages/core/src/schema.ts:499-663`
    `validateGraph`).** The 4-tier pipeline (sanitize → normalize aliases →
    auto-fix → drop-broken → fatal). Tier 4 fatal covers non-object input,
    malformed top-level collections, missing/invalid project metadata, no
    valid nodes. Tier 3 drops invalid nodes/edges with referential integrity
    (drops edges whose endpoint is itself dropped). **Does NOT enforce
    layer-exclusivity or layer-coverage** — that's Layer 2.

  - **Layer 2 — Inline validator (`skills/understand/ua-inline-validate.cjs`;
    `SKILL.md:599-672`).** Runs after Layer 1. Adds **semantic checks**:
    required-field presence on every node/edge (id/type/name/summary/tags/
    complexity on nodes; source/target/type/direction/weight on edges);
    duplicate IDs; dangling edge refs (post-Layer-1); **"file-level node not
    in any layer"**; **"Node X appears in multiple layers"**; orphan nodes
    (no in or out edges); tour ref integrity; layer ref integrity. Emits
    issues into `$PHASE_WARNINGS` and to `review.json` on `--review`.

  - **Layer 3 — `graph-reviewer` LLM agent (only on `--review` path;
    `agents/graph-reviewer.md`; `SKILL.md:673-714`).** Quality-layer checks
    that need semantic understanding (source-verified against
    `agents/graph-reviewer.md`):
    - **Empty layers** — a layer with **zero** `nodeIds` is critical
      (`graph-reviewer.md:84` "No layer should have an empty `nodeIds`
      array"). NOT "<2 nodes" — a single-node layer is acceptable.
    - **Empty tour** — tour must have **between 5 and 15 steps**
      (`graph-reviewer.md:96`). NOT "<3 steps".
    - **Orphan nodes** — a node with zero in/out edges is a **warning**,
      not a critical issue (`graph-reviewer.md:102` "log as warning, not
      critical"; inline validator at `SKILL.md:650` also pushes orphan to
      `warnings`).
    - **Summary degradation** — summaries that are empty or restate the
      filename are warnings (`graph-reviewer.md:100`).
    - **Self-referencing edges** — warning (`graph-reviewer.md:101`).
    - **Non-code nodes missing edges** — a `service`/`endpoint`/`table`/
      `schema`/`resource`/`step` node with zero `contains`/`defines_schema`/
      `serves`/`configures`/`deploys` edges is a warning
      (`graph-reviewer.md:104-115`).
    - **Layer naming coherence** — not a UA graph-reviewer check; this was
      a Go-first proposal and should NOT be marked as strict UA parity.
    On critical issues, Layer 3 applies automated fixes (drop dangling
    edges, default `tags` to `["untagged"]`, `summary` to
    `"No summary available"`, drop invalid-type nodes), then re-validates
    through Layers 1+2. If still failing, save with warnings + skip
    dashboard launch.

- **Go mapping decision:**
  - **Layer 1 → `schema.ValidateGraph`.** Already wired, stage parity at the
    boundary (Section 4).
  - **Layer 2 → fold into `schema.ValidateGraph`** (no separate inline script
    in Go). Adds layer-exclusivity, layer-coverage, orphan, summary-presence
    checks. This is I2 + an extension.
  - **Layer 3 → `graph-reviewer` LLM `Runner`.** New `agent.Runner`
    implementation invoked only on `--review`. Quality checks above become
    structured prompts; automated fixes go through `assembler.NormalizeGraph`
    or a new fix-applier.
- **LLM:** Only on Layer 3 (`--review` path).
- **Refs:** `UA: packages/core/src/schema.ts:499-663` (Layer 1);
  `skills/understand/SKILL.md:599-672` (Layer 2);
  `agents/graph-reviewer.md`, `SKILL.md:673-714` (Layer 3).

### Phase 7 — SAVE + fingerprint baseline

- **Input:** Validated graph, source file paths, current commit.
- **Output:** `knowledge-graph.json`, `fingerprints.json`, `meta.json`;
  intermediate cleanup (preserving `scan-result.json`).
- **Key semantics:** Fingerprint baseline **must succeed before `meta.json`
  is written**. Per-file SHA-256 content hash + tree-sitter structural
  fingerprint (functions/classes/imports/exports signatures). Cleanup `mv`s
  scratch to `.trash-<timestamp>/` rather than `rm -rf`.
- **LLM:** No.
- **Refs:** `UA: skills/understand/build-fingerprints.mjs`;
  `packages/core/src/fingerprint.ts:79-246`.

### Cross-cutting — change classifier (incremental)

- **Decision:** SKIP (0 structural changes) | PARTIAL_UPDATE | ARCHITECTURE_UPDATE
  (dir structure changed OR >10 structural files) | FULL_UPDATE (>30 structural
  files OR >50% of graph).
- **Refs:** `UA: packages/core/src/change-classifier.ts:21-87`.

## Section 2 — PR #2474 coverage matrix

Status legend: ✅ full | 🟡 partial | 🔴 missing | ⚠️ drift (semantically wrong
against UA contract).

| # | UA phase | Status | Go home | Drift severity |
|---|---|---|---|---|
| 0 | Pre-flight | 🟡 | `run.go`, `persistence/{paths,store}.go`, `main.go` | config/trash done; subdomain/review/context gaps |
| 0.5 | Ignore config generation | ✅ | `scanner/{ignore,ignore_generator}.go`, `run.go` | strict read/starter gate done; `--no-prompt` Go-first extension |
| 1 | SCAN (enumeration + classification + contract) | 🟡 | `scanner/scanner.go`, `scanner/language.go`, `scanner/narrative.go`, `scanner/ignore.go` | contract/narrative scaffold done; wider language registry remains |
| 1 | Import map extraction | 🟡 | `importgraph/{graph,resolvers}.go` | 10-family lightweight resolvers after #t1485; exact tree-sitter/path-alias parity remains |
| 1.5 | BATCH (Louvain + neighborMap) | 🟡 | `batching/batches.go` | pure-Go Louvain community assignment after #t1491; residual graphology fixture drift remains |
| 2 | Per-batch structure extraction | 🟡 | `run.go`, `agent/runner.go`, `extractor/{golang,generic}/*.go`, `graphbuilder/builder.go` | high-signal multi-language/non-code extraction after #t1485; exact tree-sitter/rich extraction remains |
| 2 | LLM file-analyzer enrichment | 🟡 | `run.go`, `agent/runner.go` | command-runner path wired; server-backed runner/source subset/skills materialization remain #t1505 design gaps |
| 2.5 | Merge batch graphs | ✅ | `assembler/normalize.go`, `assembler/merge.go` | `tested_by` Pass 2 + tag injection done; LLM assemble-review quality tracked in Phase 3 |
| 3 | Assemble-reviewer | 🟡 | `reviewer/reviewer.go`, `run.go` | deterministic review artifact/checks done; real LLM assemble-reviewer quality remains gap |
| 4 | Architecture / layer assignment | 🟡 | `layers/heuristic.go` | file-level fallback fixed; LLM primary still gap |
| 5 | Tour builder | 🟡 | `tour/heuristic.go` | file-level/calls-flow/MaxSteps/languageLesson fallback done; LLM tour-builder parity remains gap |
| 6 | Review / validate | 🟡 | `schema/validate.go`, `reviewer/reviewer.go`, `run.go` | Layer 1+2 + deterministic Layer3 checks/review.json done; real graph-reviewer LLM quality remains gap |
| 7 | SAVE + fingerprint baseline | 🟡 | `persistence/store.go`, `fingerprint/*.go` | cleanup parity done; fingerprint semantics close; other UA save/report gaps remain |
| – | Change classifier | 🟡 | `fingerprint/classify.go` | equivalent (close) |
| – | Fingerprint hashing | 🟡 | `scanner/scanner.go:163-166`, `fingerprint/store.go` | equivalent |

Detail per row follows.

### Phase 0 — Pre-flight (🟡 gap)

- **What's there:** Resolves input/output layout, redirects git worktree to
  main root (`paths.go:71-103`), normalizes modes (full/incremental/auto),
  reads existing config/meta/graph, computes git hash (`run.go:458-469`),
  fingerprint baseline-then-meta ordering via `SaveArtifacts` (`run.go:88-92`).
- **Missing (gate-feedback additions marked ★):**
  - No subdomain-graph merge.
  - No `--review` flag (Layer 3 validator path; see Phase 6).
  - No README/manifest/dir-tree/entry-point context collection.
  - ✅ **Implemented in #t1482:** 7-day `.trash-*` purge on Phase 0 entry via `persistence.Store.PrepareRun`.
  - ✅ **Implemented in #t1482:** `--auto-update` / `--no-auto-update` writes `config.json.autoUpdate`; analysis proceeds normally either way.
  - ✅ **Implemented in #t1482:** `--language` is normalized (friendly names such as `chinese` → `zh`) and persisted to `config.json.outputLanguage`; later runs read the stored value.
  - ★ **No `--review` mode.** UA's `--review` reuses existing graph + jumps
    to Phase 6 Layer 3 LLM reviewer, then proceeds through Phase 7 to save
    normally (`SKILL.md:174-178`). Go has no `--review` mode at all.
  - ☆ **Go-first extension candidate:** `mode=review` that *skips*
    `SaveArtifacts` and exits non-zero on fatal issues — **Go-first**, not
    UA. Optional CI-friendly addition.
  - ☆ **Go-first extension candidate:** `--auto-update`-gated persistence
    / "stop before save" / "already up to date" interaction — **Go-first**,
    not UA. UA's flag is purely a config record.

### Phase 0.5 — Ignore config (✅ covered for strict read/starter gate)

- **Implemented in #t1482:** `scanner.LoadIgnoreMatcherWithProjectRoot` applies hardcoded defaults, canonical `.agentteam-insightignore`, root `.agentteam-insightignore`, and `.gitignore`; `scanner.EnsureStarterIgnoreForRoot` generates the starter file when absent.
- **Implemented in #t1482:** default runs prompt for review/confirmation after generating the starter file before scanning; non-interactive callers without `PromptReader` fail closed until `--no-prompt` is supplied.
- **Go-first extension:** CLI `--no-prompt` auto-accepts the generated/found `.agentteam-insightignore` confirmation for CI/smoke runs; it is intentionally documented as Go-first and not UA strict parity. No `meta.json` auto-accept record was added in this batch because UA does not persist one.

### Phase 1 — SCAN (🟡 partial after #t1484)

- **What's there after #t1484:** scanner prefers `git ls-files -z -co --exclude-standard`, falls back to recursive walk, applies UA/root understandignore + `.gitignore` + defaults, skips binary files, computes content hashes for internal fingerprints, emits UA-facing `scan-result.json` fields, adds deterministic README/manifest narrative (`scanner/narrative.go`), writes `importMap`, and preserves the in-memory `Ignored[]` list for tests/debug while omitting it from the UA scan-result JSON.
- **Remaining gaps (gate-feedback additions marked ★):**
  - ✅ **Implemented in #t1482:** `git ls-files -z -co --exclude-standard` fast path with fallback walk.
  - ✅ **Improved in #t1482:** matcher now handles UA/root understandignore, `.gitignore`, defaults, negation, `**`, and nested directory patterns for scanner fixtures. It remains a Go implementation of gitignore-style semantics rather than the exact npm `ignore` package internals.
  - ✅ **Implemented in #t1482:** `.agentteam-insightignore` is loaded from the output/project root and no longer silently ignored (see F3).
  - **Language coverage ~20 extension buckets vs UA's 42 configs.** Missing:
    vue, svelte, dart, lua, swift, plaintext, restructuredtext,
    makefile/jenkinsfile as distinct, json-schema, openapi,
    kubernetes, github-actions as a language, powershell variants, batch,
    scss/sass/less distinguished from css, plist, csproj/sln, properties,
    mod/sum, prisma, csv/tsv distinguished, hcl (see Drift F4).
  - **`fileCategory` mapping differs:** Go marks `.proto`/`.graphql`/`.gql` as
    `code`; UA marks them `data` (`language.go:53-59`). Minor.
  - ✅ **Implemented in #t1482:** Go scan output includes `filteredByIgnore` as an integer counter for files dropped by user `.agentteam-insightignore` rules (canonical or root) beyond defaults.
  - ✅ **Implemented in #t1484:** `scan-result.json` now includes `name`,
    `description`, `totalFiles`, `estimatedComplexity`, and a deterministic
    README/manifest-derived narrative scaffold. The transient
    `rawDescription` / `readmeHead` work strings are kept only in in-memory
    project context for downstream analyzer requests and are stripped from the
    final scan-result JSON, matching UA's output contract. This satisfies the
    contract shape but is still a conservative Go implementation, not UA's LLM
    project-scanner prose.
  - 🟡 **Improved in #t1485:** `extractor/generic` and `importgraph` now
    provide dependency-free lightweight import capture/resolution for UA's
    **10** resolver families — TypeScript/JavaScript, Python, Go, Java,
    Kotlin, C#, PHP, Rust, C/C++, Ruby. **No Swift resolver exists in UA** and
    Swift is not claimed as strict parity. Remaining gap: exact UA
    tree-sitter parsing, `.vue` SFC semantics, tsconfig/path aliases, package
    manager metadata, and language-specific edge cases are still not strict
    parity (see Drift F4 + Phase 1 import map).
  - ✅ **Implemented in #t1484:** output field constraints now use UA-facing
    names and shapes: `files[].language` is lowercase and never null,
    `files[].fileCategory` is the 7-tuple, `files[].sizeLines` is newline
    count, `filteredByIgnore` is an integer, `estimatedComplexity` is
    `small|moderate|large|very-large`, and `importMap[path]` contains resolved
    internal paths. **Note:** Phase 6 validator still does NOT validate
    `scan-result.json` (Phase 6 only validates `knowledge-graph.json`).

### Phase 1 — Import map extraction (🟡 partial after #t1485)

- **What's there after #t1485:** `importgraph/graph.go` builds per-file resolved
  `ImportMap` from extractor `Imports`, and `importgraph/resolvers.go` records
  the 10 UA resolver families. Go stays marked `implemented`; TS/JS, Python,
  Java, Kotlin, C#, PHP, Rust, C/C++, and Ruby are now marked `partial` because
  they have real lightweight internal-project resolvers but do not claim exact
  UA tree-sitter/path-alias semantics. Swift is intentionally absent.
- **Remaining gap:**
  - No tree-sitter/CGO/WASM parser dependency was added in #t1485 by design;
    parsing is regex/lightweight and favors CI safety over strict UA parity.
  - TS/JS path aliases, `.vue` SFC semantics, Python package edge cases, Rust
    macro/module edge cases, JVM/C#/PHP autoload metadata, and C/C++ include
    search paths are still partial rather than strict resolver parity.

### Phase 1.5 — BATCH (🟡 scoped Louvain after #t1491)

- **What's there after #t1491:** `batching.BuildPlan` now materializes a UA-style
  `batches.json` plan with `schemaVersion`, `algorithm`, `totalFiles`,
  `totalBatches`, `batches[]`, `batchImportData`, `neighborMap`, and
  `exportsByPath`. Code-file batching uses a dependency-free pure-Go
  Louvain-style modularity pass over the undirected import graph before size
  enforcement, then enriches cross-batch neighbors with exported symbols. It
  also preserves the previously shipped non-code Groups A-E (Dockerfile-per-dir,
  `.github/workflows/`, `.gitlab-ci.yml` + `.circleci/`, SQL migrations per
  `migrations?/` dir, catch-all parent dir capped at 20) and
  `mergeSmallBatches` singleton pooling (MIN=3, MAX_MERGE_TARGET=25).
- **Completed in #t1483:** `run.go` writes `intermediate/batches.json`, writes
  UA-compatible non-zero-padded `batch-<i>.json` outputs, verifies expected
  batch outputs before assembling, and uses the current full-scan/full-graph
  assignment plus changed-files output filter for incremental mode. Filtered
  batches keep their full-graph `batchIndex` (no renumbering), `neighborMap`
  may still reference unchanged files by full-graph index, and incremental merge
  writes pruned existing graph to `batch-existing.json` before merging with
  fresh changed `batch-*.json`; it does **not** read prior `batches.json` or
  cached unchanged batch outputs.
- **Residual drift after #t1491:** AgentTeam no longer uses connected-components
  BFS for code communities; dense connected import graphs now split into
  multiple semantic communities. Exact graphology Louvain implementation
  details (library modularity calculations/tie-breaking) remain a residual
  fixture-comparison drift and are not claimed as byte-for-byte parity.

### Phase 2 — Per-batch structure extraction (🟡 partial after #t1485)

- **What's there:** Go extractor remains full `go/ast` for Go files. #t1485
  replaces the skeleton generic extractor with dependency-free high-signal
  extraction for TS/JS, Python, Rust, Java, Kotlin, C#, PHP, C/C++, Ruby,
  Markdown headings, SQL DDL, K8s/OpenAPI YAML/JSON, GraphQL, Protobuf, and
  Dockerfile inputs. #t1483 changed the orchestration
  seam: `buildGraph` now dispatches one graph fragment per batch (runner or
  deterministic fallback), passes `batchIndex`, `totalBatches`, batch file
  metadata, `batchImportData`, and `neighborMap` through `agent.Request`, and
  caps deterministic fallback / injected runners at 5 concurrent jobs.
  `Runner==nil` still follows the same per-batch path, so batching is no
  longer dead code.
- **Completed in #t1483:** Phase 2 now writes `batch-<i>.json` outputs and
  verifies every expected batch output exists and is non-empty before assemble;
  missing/empty outputs fail closed with `incomplete-batches`.
- **Completed in #t1484:** When agent enhancement flags are supplied,
  `run.go` now constructs an OpenCode-backed file-analyzer task per batch with
  `task.json` containing `inputGraph`, `batchIndex`, batch files,
  `batchImportData`, `neighborMap`, and project narrative/context. Tests use a
  mock command runner and canned graph response, so no real external LLM is
  required.
- **Completed in #t1485:** Non-code parsers now emit deterministic UA-style
  child nodes with stable IDs plus parent `contains` edges: Dockerfile →
  `service`, K8s YAML → `service`/`resource`, SQL DDL → `table`/`schema`,
  GraphQL → `schema`/`endpoint`, Protobuf → `schema`/`service`, OpenAPI →
  `endpoint`/`schema`. Layer generation and Go validation exclude contained
  child nodes from file-level layer assignment while preserving their UA node
  types.
- **Remaining gaps:**
  - **No tree-sitter.** Non-Go extraction is high-signal regex/lightweight, not
    strict UA tree-sitter parity; CI dependency risk (CGO/WASM grammar
    footprint) is explicitly deferred.
  - Rich non-code extraction beyond the scoped detectors (Terraform, detailed
    YAML schema, nested GraphQL/OpenAPI/Protobuf semantics) remains partial.

### Phase 2 — LLM file-analyzer enrichment (🟡 partial after #t1484)

- **What's there after #t1484:** `agent/runner.go` defines a batch-aware
  `Request`, and `run.go` now wires a concrete OpenCode-backed file-analyzer
  runner when agent-enhancement flags are present (`--opencode-agent`,
  `--model`, or `--opencode-arg`). Each task receives `inputGraph`,
  `batchIndex`, batch files, `batchImportData`, `neighborMap`, and project
  narrative/context, then returns an enriched batch graph. Unit tests use a
  mock/canned response to prove summaries/tags/languageNotes and edge
  descriptions can flow through without relying on a real external LLM. Real
  command-runner dispatch is serial by default unless
  `--isolate-opencode-runtime` is enabled; this is a Go-first safety limit for
  shared OpenCode runtime state, not a UA strict-parity claim.
- **Remaining gap:** default/no-agent runs are intentionally deterministic
  no-ops; rich summary quality is not byte-for-byte UA parity. #t1485 now
  provides deterministic non-code child nodes, but LLM-enriched summaries and
  semantic descriptions still require an OpenCode runner to be explicitly
  enabled. #t1505 records three production hardening gaps that must be closed
  before this path can be called release-complete: a server-backed
  `--opencode-server` runner with fake Starter/Client tests (F17/I22), a
  session-local source subset plus `external_directory: deny` (F18/I23), and
  allowlisted Graph Builder skill materialization (F19/I24).

### Phase 2.5 — Merge batch graphs (✅ scoped parity)

- **What's there:** `NormalizeGraph` does sanitize, normalize aliases,
  normalize node IDs, remap edge refs, `fixTestedByEdges` (flips `test→prod`
  to `prod→test`, drops test↔test/prod↔prod pairs), Pass 2
  path/name-convention synthesis for production → test `tested_by` edges,
  `tested` tag injection on covered production nodes, dedupe nodes/edges,
  filter dangling, filter layer/tour nodeIds, and sort. `MergeGraphUpdate`
  does incremental prune-and-merge.
- **Completed in #t1486:** F12/I11 observable `tested_by` semantics are now
  covered for deterministic merge outputs.
- **Remaining gap:** LLM-driven assemble-reviewer quality remains tracked in
  Phase 3; no claim of UA LLM issue wording or repair parity.

### Phase 3 — Assemble-reviewer (🟡 partial after #t1486)

- **What's there:** `reviewer.ReviewAndFix` emits
  `.agentteam-insight/intermediate/review.json` (documented equivalent to
  the review artifact) with issues/warnings/stats after deterministic assemble
  checks. It covers missing/dangling files/edges, invalid nodes, broken layer
  refs through Layer 1+2 validation, and safe fixes.
- **Remaining gap:** no real LLM `assemble-reviewer` subagent; deterministic
  review checks are a Go-first scoped substitute, not strict LLM quality
  parity.

### Phase 4 — Architecture / layer assignment (🟡 partial after #t1481)

- **What's there:** `layers/heuristic.go` deterministic fallback now assigns
  only UA file-level node types (`file`, `config`, `document`, `service`,
  `pipeline`, `table`, `schema`, `resource`, `endpoint`) and each such node
  appears in exactly one layer. The fallback uses UA-style architecture
  patterns (`API Layer`, `Data Layer`, `Configuration Layer`, `Core`, etc.)
  rather than the old dir/language/entry multi-layer grouping.
- **Completed in #t1481:** F1/F2's observable contract violations are fixed
  for the deterministic fallback: non-file-level nodes such as `function` and
  `class` are excluded, and duplicate layer assignment is not generated.
- **Remaining gap:** UA's primary LLM architecture analyzer/layer detector is
  still not implemented; the deterministic fallback is Go-first but now respects
  the strict file-level-only + exactly-one-layer contract.

### Phase 5 — Tour builder (🟡 partial after #t1486)

- **What's there:** `tour/heuristic.go` deterministic fallback now filters
  tour candidates to file-level artifacts (excluding contained child
  function/class/non-code subnodes), supports calls-edge/topological flow
  ordering, keeps default max steps at 5 while allowing `MaxSteps` overrides
  in tests, and emits deterministic `languageLesson` text when an output
  language is configured.
- **Remaining gap:** still no LLM `tour-builder` subagent and no UA
  `language-lesson.ts` quality parity; deterministic fallback is scoped but
  schema-compatible.

### Phase 6 — Review / validate (🟡 partial after #t1481)

UA's Phase 6 has three distinct validator layers (see Section 1 Phase 6).
Go now has Layer 1 plus the #t1481 Layer 2 inline checks folded into
`schema.ValidateGraph`; Layer 3 remains missing.

- **What's there (Layer 1):** `schema/validate.go` runs sanitize → normalize
  aliases → auto-fix defaults → normalize node IDs → check kind → check
  top-level collections → check project meta → drop invalid nodes → drop
  invalid edges + dangling refs → drop invalid layers/tour + filter dangling
  refs. Stage ordering matches UA Layer 1. Auto-fix defaults match (type=file,
  complexity=moderate, tags=[], summary=name, type=depends_on,
  direction=forward, weight=0.5, clamp [0,1]).
- **What's there (Layer 2, #t1481):** `schema.ValidateGraph` now emits
  `node-in-multiple-layers` when a node appears in more than one layer,
  `file-node-not-in-any-layer` when a UA file-level node appears in zero
  layers, and `orphan-node` as a warning only. Summary degradation remains
  out of Layer 2, matching the audit's split. Dangling layer/tour references
  are still filtered by the Layer 1 port.
- **What's there (deterministic Layer 3, #t1486):** `reviewer.ReviewAndFix`
  runs for default/full/incremental saves and for `--review` existing-graph
  reuse. It writes `review.json`, records empty-layer critical issues, tour
  count warnings (outside 5–15), orphan warnings, summary degradation,
  self-referencing edges, node type / ID prefix warnings, and non-code nodes
  missing semantic edges. Safe fixes are limited to dropping invalid nodes /
  dangling edges, defaulting missing tags/summaries, assembler normalization,
  `tested_by` synthesis, and revalidation through Layers 1+2.
- **Remaining gap:** no real LLM `graph-reviewer` runner or UA LLM wording /
  repair-quality parity. Layer-naming coherence remains explicitly out of UA
  strict parity.

### Phase 7 — SAVE + fingerprint baseline (🟡 gap)

- **What's there:** `persistence/store.go`, `persistence/paths.go`,
  `fingerprint/store.go`, `fingerprint/compare.go`, `fingerprint/classify.go`
  write `knowledge-graph.json`, `fingerprints.json`, `meta.json`, `config.json`
  via `SaveArtifacts` (`run.go:88-92`). Worktree redirect implemented
  (`paths.go:71-103`). Fingerprint baseline order is correct (built before
  `SaveArtifacts` writes meta).
- **Implemented in #t1482:** Phase 7 cleanup now preserves
  `intermediate/scan-result.json` and moves other `intermediate/` scratch plus
  `.agentteam-insight/tmp/` into `.trash-<timestamp>/`; Phase 0 entry purges
  `.trash-*` older than seven days.
- **Still missing:** Dashboard auto-launch hook and fuller UA summary/reporting
  details remain outside this batch.

### Cross-cutting — change classifier (🟡 equivalent)

- **What's there:** Same four tiers (`SKIP`, `PARTIAL_UPDATE`,
  `ARCHITECTURE_UPDATE`, `FULL_UPDATE`) with the same numeric thresholds
  (>30, >50%, >10) and directory-structure-changed check (`classify.go:48-115`).
- **Drift (minor):** `topLevelDirsChanged` uses a slightly different definition
  than UA's `detectDirectoryChanges`. The classifier runs `BuildStore` on
  **all** files then compares; UA computes changes only over a `changedFiles`
  list (`fingerprint.ts:297-385`). Behavior close, but base definition varies.

### Cross-cutting — fingerprint hashing (🟡 equivalent)

- **What's there:** SHA-256 hex of file bytes (`scanner/scanner.go:163-166`,
  `fingerprint/store.go:21-24`). Matches UA content hash.
- **Drift:** UA's `extractFileFingerprint` captures structural signatures
  (functions/classes/imports/exports). Go does both too via `FromAnalysis`
  (`store.go:43-81`). After #t1485, the generic extractor sets
  `HasStructuralAnalysis=true` for scoped non-Go/code and non-code formats it
  can parse, so the conservative STRUCTURAL fallback now mainly applies to
  unsupported languages or parse misses. Exact tree-sitter structural
  fingerprint parity remains a gap.

## Section 3 — Concrete drift findings

Numbered F1…F16 with file:line on both sides. Blockers marked 🔴.

### F1. ✅ `layers.Generate` put one node in multiple layers (fixed in #t1481)

- **Before #t1481:** `internal/graphbuilder/layers/heuristic.go:22-46` could add
  each node to `dir:<x>` AND `language:<lang>` AND `entry:entry-points`, with
  no exclusivity check.
- **After #t1481:** `layers.Generate` makes a single architecture-layer choice
  per file-level node and never adds the same node to multiple layers.
- **UA:** `SKILL.md:630-636` — inline validator explicitly fails with
  `"Node '${id}' appears in multiple layers"`. Also `layer-detector.ts:108-126`
  puts each file node in exactly one layer (single `matchFileToLayer` call
  returns one name).
- **Status:** Fixed for the deterministic fallback in #t1481. UA LLM architecture analysis remains a separate follow-up.

### F2. ✅ `layers.Generate` assigned non-file nodes to layers (fixed in #t1481)

- **Before #t1481:** `internal/graphbuilder/layers/heuristic.go:22-25` iterated
  `graph.Nodes` with no `node.Type` filter. Function and class nodes (which
  carry `FilePath`) were grouped into `dir:`/`language:`/`entry:` layers.
- **After #t1481:** `layers.Generate` filters to UA file-level node types;
  `function`/`class`/`module`/`concept`/`step`/`entity`/`topic`/`claim`/
  `source` nodes are excluded from layers.
- **UA:** `layer-detector.ts:108` and `:119` both filter
  `node.type !== "file"` to `continue`. `SKILL.md:432-441` and `:510-513`
  specify file-level nodes only.
- **Status:** Fixed for the deterministic fallback in #t1481.

### F3. ✅ `.agentteam-insightignore` not loaded (fixed in #t1482)

- **Before #t1482:** `internal/graphbuilder/scanner/ignore.go` read only root `.gitignore` and root `.agentteam-insightignore`; the canonical `.agentteam-insightignore` had no effect.
- **After #t1482:** `LoadIgnoreMatcherWithProjectRoot` reads hardcoded defaults → `.agentteam-insightignore` → root `.agentteam-insightignore` → `.gitignore`, with tests covering canonical ignore and root negation precedence.
- **UA:** `packages/core/src/ignore-filter.ts:86-104` reads the same canonical/root understandignore sources.
- **Status:** Fixed for the scanner read path in #t1482.

### F4. 🟡 Language registry / resolver coverage (partial after #t1485)

- **Go:** `internal/graphbuilder/scanner/language.go` plus
  `extractor/generic` now cover the #t1485 10-family resolver set and scoped
  non-code detectors, but still have no UA language config registry
  (`concepts`, `filePatterns`, tree-sitter grammar mapping) for all 42 configs.
- **UA:** `packages/core/src/languages/configs/` — 42 language configs.
- **Severity:** Partial — common non-Go repos now produce imports/functions/
  classes/subnodes, but exact language registry and tree-sitter parity remain
  gaps.

### F5. ✅ Batching did not drive analysis (fixed in #t1483)

- **Before #t1483:** `buildGraph` ignored `batches` entirely; `batching.Build`
  was only logged and the graph was built from all files in one shot.
- **After #t1483:** `buildGraph` materializes `batches.json`, dispatches one
  runner/fallback graph fragment per batch, passes `batchIndex`,
  `totalBatches`, batch files, `batchImportData`, and `neighborMap` via
  `agent.Request`, writes `batch-<i>.json`, and verifies expected outputs before
  assemble. Deterministic fallback (`Runner==nil`) uses the same batch agentteam.
- **UA:** `SKILL.md:283-323` — Phase 2 iterates `batches[]`, dispatching one
  `file-analyzer` per batch with `batchImportData` + `neighborMap` context.
- **Status:** Fixed for the analyzer-seam contract in #t1483; rich LLM
  file-analyzer content remains F7/I5.

### F6. 🟡 Louvain community detection (scoped in #t1491)

- **Go after #t1491:** `batching.BuildPlan` uses a pure-Go Louvain-style
  modularity pass for code-file community assignment, then applies the existing
  UA order: size enforcement, non-code Groups A-E, singleton pooling,
  `neighborMap` enrichment, and changed-files output filtering.
- **UA:** `skills/understand/compute-batches.mjs:234-249` — graphology + Louvain.
- **Residual drift:** exact graphology library modularity/tie-breaking is not
  byte-for-byte ported; #t1491 closes the old connected-components drift and
  adds dense-graph coverage, while upstream fixture comparison remains tracked.

### F7. 🟡 LLM-enriched nodes require explicit runner (partial in #t1484)

- **Before #t1484:** `Runner==nil` meant all runs stayed deterministic and no
  production command runner path consumed the batch context.
- **After #t1484:** OpenCode file-analyzer tasks are wired behind the existing
  agent-enhancement flags and can return enriched `summary`, `tags`,
  `languageNotes`, function summaries, and edge descriptions; the mock test
  proves those fields merge into the final graph.
- **UA:** Every normal Phase 2 run uses the LLM file-analyzer
  (`agents/file-analyzer.md:14-23`).
- **Severity:** Partial — default AgentTeam smoke remains deterministic/no-agent;
  deterministic non-code child nodes now exist after #t1485, but rich LLM
  quality is still not byte-for-byte UA parity.

### F8. Change-classifier tier names match; threshold semantics close (equivalent)

- **Go:** `internal/graphbuilder/fingerprint/classify.go:13-18` — `SKIP`,
  `PARTIAL_UPDATE`, `ARCHITECTURE_UPDATE`, `FULL_UPDATE` — identical strings
  to UA `change-classifier.ts:6`.
- **UA:** `packages/core/src/change-classifier.ts:21-87` — same thresholds
  (>30, >50%, >10) and directory-structure rule.
- **Severity:** Equivalent.

### F9. Fingerprint hashing matches at content-hash level (equivalent)

- **Go:** SHA-256 hex of bytes. UA: SHA-256 hex of string. Boundaries match.
- **Severity:** Equivalent.

### F10. Agent seam production coverage is partial (narrowed in #t1484)

- **Go:** #t1484 adds a concrete OpenCode-backed file-analyzer runner for Phase
  2 batch enrichment. #t1483 had already added per-batch request context
  (`batchIndex`, `totalBatches`, files, `batchImportData`, `neighborMap`) and
  dispatching.
- **UA:** Subagent dispatch is intrinsic to every phase, including project
  scanner narrative, assemble-reviewer, architecture/tour builders, and graph
  reviewer.
- **Severity:** Partial — Phase 2 file-analyzer runner path exists; later LLM
  stages still remain I18+ follow-ups.

### F11. ✅ Validator missing layer-exclusivity / coverage checks (fixed in #t1481)

- **Before #t1481:** `internal/graphbuilder/schema/validate.go:278-296` layer
  and tour validators checked shape only.
- **After #t1481:** `schema.ValidateGraph` adds Layer 2 inline checks for
  `node-in-multiple-layers`, `file-node-not-in-any-layer`, and `orphan-node`
  as a warning.
- **UA:** `SKILL.md:611-644` — inline validator checks duplicate layer
  assignment and uncovered file-level nodes.
- **Status:** Fixed for Layer 2 in #t1481; graph-reviewer Layer 3 remains a follow-up.

### F12. ✅ `tested_by` linker missing Pass 2 + `"tested"` tag (fixed in #t1486)

- **Before #t1486:** `fixTestedByEdges` only flipped `test→prod` to
  `prod→test`, set `Direction="forward"`, and dropped nothing.
- **After #t1486:** `assembler.NormalizeGraph` drops semantically broken
  test↔test/prod↔prod `tested_by` pairs, synthesizes path/name convention
  production → test edges, and tags production nodes that source a `tested_by`
  edge with `"tested"`.
- **UA:** `SKILL.md:339` — Pass 1 flips inverted edges and drops
  semantically broken ones; Pass 2 supplements with path-convention pairings;
  production nodes sourcing any `tested_by` edge get a `"tested"` tag.
- **Status:** Fixed for deterministic linker semantics in #t1486; no LLM
  assemble-reviewer quality claim.

### F13. Graph-builder emits file-category-based node types (minor drift)

- **Go:** `internal/graphbuilder/graphbuilder/builder.go:97-112` — `config`
  files → `config:`; `docs` → `document:`; `infra` with `.github/workflows/`
  → `pipeline:`, else `resource:`; default → `file:`.
- **UA:** `packages/core/src/analyzer/graph-builder.ts:84-103` — code files
  become `file:`; non-code becomes `${meta.nodeType}` driven by LLM/detector
  (UA's non-code detectors choose among `service`, `endpoint`, `table`,
  `schema`, `resource`, `pipeline`, `config`, `document`). Go's mapping is
  plausible but more rigid; e.g., a Dockerfile becomes `resource:` in Go but
  `service:` in UA (`graph-builder.ts:39-58` `KIND_TO_NODE_TYPE`).
- **Severity:** Minor drift.

### F14. ✅ No `filteredByIgnore` accounting (fixed in #t1482)

- **Before #t1482:** `scanner/scanner.go` populated `Ignored[]` with reasons but did not emit the integer counter UA emits.
- **After #t1482:** `scanner.Result` has `filteredByIgnore` and increments it for files dropped by user `.agentteam-insightignore` rules beyond defaults.
- **UA:** `scan-project.mjs:677-692` emits the same plain integer field on `scan-result.json`.
- **Status:** Fixed for scanner output in #t1482.

### F15. ✅ No git-ls-files fast path (fixed in #t1482)

- **Before #t1482:** `scanner.scanner.go` used `filepath.WalkDir` and a text-parsed `.gitignore` matcher.
- **After #t1482:** `scanner.Scan` prefers `git ls-files -z -co --exclude-standard` and falls back to recursive walk only when git enumeration is unavailable.
- **UA:** `scan-project.mjs:466-488` prefers the same git command.
- **Status:** Fixed in #t1482; fallback matcher coverage also improved for `**` and nested negation fixtures.

### F16. Drifts reviewer2 may have missed

- **`buildGraph` validates inside the build agentteam**
  (`graphbuilder/builder.go:76-80`) — runs `schema.ValidateGraph` once and
  discards the result if successful; the *intermediate* graph is validated,
  but `layers.Generate` + `tour.Generate` run afterward and the merged graph
  is re-validated separately (`run.go:252-256`). The first validation is
  wasted work and the second uses the same loose validator that fails to
  catch F1/F2.
- **`buildGraph` is called for incremental even when `FilesToReanalyze==[]`
  is non-empty but with the FULL file list** (`run.go:148-149` passes
  `scanResult.Files`, not filtered). With the current fake runner this is
  moot, but it is a latent scaling bug — UA's incremental path only feeds
  changed batches.

### F17. 🔴 No production server-backed OpenCode runner lifecycle (#t1505 design gap)

- **Go now:** `run.go` builds file-analyzer tasks by invoking the configured
  OpenCode binary with `opencode run --title ... --dir <task-workdir>`.
  Tests cover mock command-runner behavior, but there is no
  `--opencode-server` production entry, no server Starter/Client lifecycle, no
  "one server per graph run" invariant, and no fake integration test proving
  per-batch/per-intent session isolation plus stop/cleanup on success/failure.
- **Required design:** a later code batch must add a server-backed TaskRunner
  path selected by `--opencode-server <addr|auto>` when no fake runner is
  injected. `auto` starts one server for the graph run; every file-analyzer
  batch or later LLM intent gets an independent session; all sessions are
  closed and the server is stopped once on both normal and error paths.
- **Severity:** Blocker for claiming production OpenCode support. #t1505 only
  records the design and test contract; it does not close the implementation.

### F18. 🔴 File-analyzer task reads are not confined to a session-local source subset (#t1505 design gap)

- **Go now:** `fileAnalyzerTaskManifest.ProjectRoot` exposes the original
  input repository absolute path, `buildFileAnalyzerPrompt` tells the agent the
  manifest includes `projectRoot: absolute input repository path`, and
  `writeOpenCodeTaskConfig` sets `"external_directory": "allow"`.
- **Required design:** before dispatch, copy only `request.files` into
  `workDir/sources/<relative-path>` after rejecting absolute paths, `..`,
  symlink escapes, and non-regular files. The manifest must include
  `sourcesRoot` and a relative `sourceMap`, prompts must reference only
  `sources/<relative-path>`, and generated OpenCode config must set
  `"external_directory": "deny"`. Tests must prove a fake runner/client can
  complete from copied sources and does not need or receive the original repo
  path as a source-reading instruction.
- **Severity:** Blocker for least-privilege agent execution and for claiming
  the current file-analyzer contract is production-hardened.

### F19. 🔴 Graph Builder runtime skills are not materialized from an allowlist (#t1505 design gap)

- **Go now:** the file-analyzer task writes `opencode.json`, `task.json`, and
  prompt text. There is no Graph Builder `.agents/skills/.../SKILL.md`
  bundle, no allowlist, no skills manifest with hashes, and no materialization
  tests. Legacy `codeanalysis` embedded skills are not part of the current
  Graph Builder contract.
- **Required design:** embed a AgentTeam Insight Graph Builder skill bundle and
  materialize only allowlisted runtime files such as
  `.agents/skills/agentteam-insight/file-analyzer/SKILL.md`,
  `assemble-reviewer/SKILL.md`, `graph-reviewer/SKILL.md`,
  `architecture-analyzer/SKILL.md`, `tour-builder/SKILL.md`, and explicitly
  listed `references/` files. Write a skills manifest with bundle version and
  SHA-256 hashes, make repeated materialization idempotent, clean stale files,
  and isolate concurrent task workdirs. UA validator/smoke scripts remain
  compatibility gates only and must not be required at runtime.
- **Severity:** Blocker for claiming embedded skills materialization or
  self-contained production OpenCode execution.

## Section 4 — Schema/type parity

Comparing `UA: packages/core/src/schema.ts` against
`Go: internal/graphbuilder/schema/{graph.go,aliases.go,metadata.go}`.

### Node types — ✅ full parity

- UA enum (`schema.ts:370-376`): 21 types (`file`, `function`, `class`,
  `module`, `concept`, `config`, `document`, `service`, `table`, `endpoint`,
  `pipeline`, `schema`, `resource`, `domain`, `flow`, `step`, `article`,
  `entity`, `topic`, `claim`, `source`).
- Go `NodeTypes` (`aliases.go:3-9`): identical 21 types.

### Edge types — ✅ full parity

- UA enum (`schema.ts:4-14`): 35 values across 8 categories.
- Go `EdgeTypes` (`aliases.go:11-21`): identical 35 values.

### Required node fields — ✅ structural parity (validator side gaps)

- UA `GraphNodeSchema` required: `id`, `type`, `name`, `summary`, `tags`,
  `complexity`. Optional: `filePath`, `lineRange`, `languageNotes`,
  `domainMeta`, `knowledgeMeta`. `.passthrough()` preserves unknown keys.
- Go `GraphNode` (`graph.go:35-49`): all fields represented; `Extra
  map[string]JSON` preserves unknown keys with custom Marshal/Unmarshal
  (`graph.go:68-118`). Tags default to `[]` on marshal (`graph.go:71-73`).
- **Parity:** Full structural parity. Validator-side gaps live in F11.

### Required edge fields — ✅ full parity

- UA: required `source`, `target`, `type`, `direction`, `weight` (number,
  [0,1]). Optional: `description`. NOT passthrough.
- Go: matches; `weight` parsed from number-or-string with `weightInvalid`
  flag for non-numeric.

### Layer / Tour / Project / KnowledgeGraph shapes — ✅ full parity

- `Kind` field: UA optional `z.enum(["codebase","knowledge"])`; Go optional
  string validated in `ValidateGraph` (`validate.go:59-61`). Match.
- `domainMeta.entryType`: UA enum `["http","cli","event","cron","manual"]`;
  Go `validEntryTypes` (`aliases.go:79`). Match.

### Validator stage parity — 🟡 Layer 1+2 partial after #t1481

UA `validateGraph` (`schema.ts:499-663`) runs in order:
1. Tier 4 fatal: not an object.
2. Tier 1 sanitize (`sanitizeGraph`).
3. Normalize aliases (`normalizeGraph`).
4. Tier 2 auto-fix (`autoFixGraph`).
5. Tier 4 fatal: malformed top-level collections; missing/invalid project
   metadata.
6. Tier 3 drop broken: nodes, then edges (with referential integrity).
7. Validate layers/tour — drop broken, filter dangling nodeIds.
8. Tier 4 fatal: no valid nodes.

Go `ValidateGraph` now runs:
1. (caller path) `ValidateGraphJSON` checks JSON + object shape.
2. `SanitizeGraph`.
3. `NormalizeAliases`.
4. `autoFixGraph`.
5. `normalizeGraphNodeIDs` — **Go-only extra stage**. UA does ID
   normalization inside `merge-batch-graphs.py` / `normalize-graph.ts`, not
   in `validateGraph`.
6. Kind check, top-level collection check, project meta check.
7. Drop invalid nodes.
8. Drop invalid edges.
9. Drop invalid layers/tour (shape), filter dangling nodeIds.
10. **#t1481 Layer 2 mapping:** report `node-in-multiple-layers`,
    `file-node-not-in-any-layer`, and warning-only `orphan-node`; summary
    degradation is intentionally left to Layer 3.

**Stage parity:** Stages 1–4 and 6–9 match UA Layer 1 closely. **Stage 5
(ID normalization) is Go-only** (a behavior plus, not a gap). #t1481 adds
UA's inline Layer 2 semantics to the Go validator, even though UA's
TypeScript `validateGraph` itself does not enforce layer exclusivity. Layer 3
`graph-reviewer` remains missing.

## Section 5 — Implementation items + acceptance fixtures/tests

For each, list what to implement, where it goes, and how to verify.

### I1. ✅ Fix `layers.Generate` exclusivity and file-only filter (completed in #t1481)

- **Implemented in #t1481:** `layers.Generate` skips non-file-level nodes and
  picks exactly one architecture layer per UA file-level node.
- **Decision needed:** Match UA's `LAYER_PATTERNS` heuristic (`layer-detector.ts:16-67`)
  OR re-introduce LLM-driven `architecture-analyzer`. Given 龙恒's
  "完整版本" mandate, the LLM path is required; the heuristic still needs to
  satisfy exclusivity/file-only as a fallback.
- **Acceptance:**
  - New test: `layers/heuristic_test.go` asserts (1) no node appears in >1
    layer, (2) no `function`/`class`/`module`/`concept`/`step`/`entity`/
    `topic`/`claim`/`source` node ends up in any layer.
  - Existing smoke fixture (`testdata/smoke/fixture-repo`) — fixture has 2
    files (`cmd/app/main.go`, `internal/hello/hello.go`); each must appear in
    exactly one layer.
  - UA compat test: add `TestLayerExclusivity` that runs UA `validateGraph`
    on Go output and asserts no `"appears in multiple layers"` issue.

### I2. ✅ Add layer-exclusivity / coverage to Go validator (completed in #t1481)

- **Implemented in #t1481:** `schema.ValidateGraph` now includes:
  - Each file-level node (`file`, `config`, `document`, `service`, `pipeline`,
    `table`, `schema`, `resource`, `endpoint`) appears in exactly one layer.
  - Existing node IDs are tracked across layers so duplicate assignment is
    reported; dangling layer refs are still filtered by the existing Layer 1
    path.
  - Emit Layer 2 issue categories with specific codes:
    - `node-in-multiple-layers`
    - `file-node-not-in-any-layer` (UA's terminology).
- **Acceptance:**
  - Test fixture with duplicate layer assignment reports
    `node-in-multiple-layers`.
  - Test fixture with uncovered file-level node reports
    `file-node-not-in-any-layer`.
  - Test fixture with orphan node reports `orphan-node` as warning only.

### I3. 🔴 Load `.agentteam-insightignore` (Blocker)

> **Renamed to I3a + I3b after reviewer2's gate feedback.** See Section 5.1.

### I4. ✅ Wire batching into analysis (completed in #t1483)

- **Implemented:** `buildGraph` now iterates `batches[]`, writes
  `intermediate/batches.json`, calls the runner/fallback path per batch with
  `batchIndex`, batch files, `batchImportData`, and `neighborMap`, writes
  `batch-<i>.json`, and validates the assembled graph after all fragments are
  merged. `Runner==nil` follows the same deterministic per-batch path.
- **Acceptance covered:** `run_test.go` records batch requests from a mock
  runner, asserts every batch is visited, every file appears exactly once in
  the request set, and the concurrency cap is respected. Smoke still exercises
  the deterministic fallback path.
- **Later follow-up:** #t1491 closes the connected-components batching drift with
  scoped pure-Go Louvain-style community assignment; exact graphology fixture
  equivalence remains tracked under I8.

### I5. 🟡 LLM file-analyzer runner (runner path completed in #t1484)

- **Completed in #t1484:** concrete OpenCode-backed file-analyzer runner path
  uses the existing `CommandRunner` and agent-enhancement flags to launch one
  task per batch. It writes a task manifest with `inputGraph`, `batchIndex`,
  batch files, `batchImportData`, `neighborMap`, and project context; the task
  returns an enriched `KnowledgeGraph` fragment. Default no-agent runs remain
  deterministic.
- **Decision resolved:** AgentTeam Insight uses OpenCode agent flags (`--opencode-agent`,
  `--model`, `--opencode-arg`) rather than introducing a second LLM provider.
- **Acceptance:**
  - Fixture: enriched fixture repo (`testdata/smoke/fixture-repo-llm`) where
    `cmd/app/main.go` gets summary like "Application entry point; calls
    `hello.Greet` to print a greeting."
  - ✅ #t1484 test: mock command runner returns a canned enriched batch graph;
    final graph contains the returned `summary`, `tags`, `languageNotes`, and
    semantic edge descriptions.
  - UA parity: Go output run through UA `validateGraph` returns
    `success:true`.

### I6. 🟡 Multi-language extractor (partial after #t1485; tree-sitter gap remains)

- **Implemented in #t1485:** dependency-free high-signal extraction for
  TypeScript/JavaScript, Python, Rust, Java, Kotlin, C#, PHP, C/C++, Ruby,
  Markdown, SQL, YAML/JSON OpenAPI/K8s, GraphQL, Protobuf, and Dockerfile.
- **Decision recorded:** #t1485 deliberately avoids tree-sitter/CGO/WASM
  dependency expansion for CI safety. Existing Go `go/ast` stays the precise Go
  path; non-Go is partial regex/lightweight extraction, not strict UA parity.
- **Acceptance:**
  - Per-language fixture under `testdata/smoke/fixture-repo-<lang>/` with a
    tiny canonical sample.
  - Per-language extractor test asserting expected functions/classes/imports.
  - Cross-language smoke: a polyglot fixture (Go + TS + Python) produces a
    graph where cross-language `imports` edges are correctly resolved
    (within-language) or dropped (cross-language, no resolver yet).

### I7. 🟡 Non-code sub-node extraction (partial after #t1485)

- **Implemented in #t1485:** scoped non-code parsers matching the observable UA child-node contract:
  - Dockerfile → `service` nodes (one per `FROM`/`CMD` pair).
  - K8s YAML → `service` (Deployment/StatefulSet/DaemonSet) + `resource`
    (ConfigMap/Secret/PVC) nodes.
  - SQL DDL → `table` + `schema` nodes.
  - GraphQL schema → `schema` + `endpoint` nodes.
  - Protobuf → `schema` + `service` (if `service` block) nodes.
  - OpenAPI → `endpoint` + `schema` nodes.
- **Acceptance:**
  - ✅ #t1485 tests cover Dockerfile, K8s YAML, SQL DDL, GraphQL, Protobuf,
    OpenAPI, plus graphbuilder stable child IDs and parent `contains` edges.
  - Remaining gap: rich nested detector semantics and additional non-code
    formats remain partial until a later tree-sitter/UA-core-backed pass.

### I8. 🟡 Louvain community detection (scoped in #t1491)

- **Implemented in #t1491:** `batching.BuildPlan` now uses a dependency-free
  pure-Go Louvain-style modularity pass for code-file community assignment. The
  previous non-code Groups A-E, singleton pooling, changed-files current
  full-scan/output filter, full-graph `batchIndex` retention, batch naming,
  5-slot deterministic/mock dispatch, and completion check remain unchanged.
- **Acceptance covered:** a dense 50-file code import graph that would be one
  connected component is now split into multiple 25-file communities, with
  every community under `MAX_COMMUNITY_SIZE=35`.
- **Residual drift:** exact graphology implementation behavior is not
  byte-for-byte ported; compare against upstream fixtures before claiming full
  graphology strict parity.

### I9. Assemble-reviewer + graph-reviewer LLM phases (Partial in #t1486)

- **Implemented in #t1486:** deterministic `reviewer.ReviewAndFix` runs after
  assembly and on `--review` existing-graph reuse, writes `review.json`,
  records missing/dangling/broken-layer and Layer3-style findings, applies
  only safe fixes, and revalidates.
- **Remaining gap:** no real LLM `assemble-reviewer` / `graph-reviewer`
  `Runner`; strict UA LLM quality and issue wording remain out of scope.
- **Acceptance:**
  - Tests cover `--review` reuse, `review.json`, dangling/invalid/defaulting
    fixes, empty layer, summary degradation, self-ref, type-prefix, orphan,
    and non-code missing-edge findings.

### I10. Tour builder enhancements (Partial in #t1486)

- **Implemented in #t1486:** deterministic tour fallback now filters to
  file-level candidates, follows calls-edge dependency flow when available,
  accepts configurable `MaxSteps` (default 5), and emits deterministic
  `languageLesson` for configured output languages.
- **Remaining gap:** no real LLM `tour-builder` / UA `language-lesson.ts`
  quality parity.
- **Acceptance:** tests cover file-level-only tour candidates, calls-flow
  ordering, `MaxSteps=10`, and `languageLesson` population.

### I11. ✅ `tested_by` linker Pass 2 + `"tested"` tag (fixed in #t1486)

- **Implemented in #t1486:** `fixTestedByEdges` drops semantically broken
  pairs and normalizes direction; `synthesizeTestedByEdges` adds convention
  production → test links; `tagTestedProductionNodes` injects `"tested"` on
  covered production nodes.
- **Acceptance:** tests cover `foo.go` + `foo_test.go`, function `Run` +
  `TestRun`, no test↔test/prod↔prod synthesis, and `"tested"` tag injection.

### I12. ✅ PR body / docs scope tightening (completed in #t1481)

- **Implemented in #t1481:** `docs/pr/2026-07-02-agentteam-insight-graph-builder.md` and
  `docs/research/2026-07-02-agentteam-insight-graph-builder-smoke.md` now describe the
  branch as dashboard-compatible deterministic convergence work, with full UA
  semantic parity still tracked by #t1480 follow-up batches.
- **Acceptance:** reviewer2 sign-off.

### I13. ✅ Git-ls-files fast path (completed in #t1482)

- **Implement:** In `scanner/scanner.go`, prefer `git ls-files -z -co
  --exclude-standard` when in a git repo; fall back to recursive walk.
- **Acceptance:**
  - Test fixture: repo with `.gitignore` using `**` and nested negation.
    Go scan output matches `git ls-files` output.

### I14. ✅ Intermediate cleanup (completed in #t1482)

- **Implement:** After Phase 7 success, `mv` intermediate scratch and
  `.agentteam-insight/tmp/` to `.trash-<timestamp>/` (preserving
  `intermediate/scan-result.json`). 7-day purge sweep on Phase 0 entry.
- **Acceptance:**
  - Test: after a successful run, `.agentteam-insight/intermediate/`
    contains only `scan-result.json`; `.agentteam-insight/tmp/` is moved
    under a sibling `.trash-<ts>/tmp/`; repeated saves in the same second use a suffixed trash dir to avoid collisions.

## Section 5.1 — Gate-feedback implementation items (v2 supplement)

Each item below addresses one of reviewer2's six gate blockers. Numbered to
extend Section 5 (I1–I14) without renumbering.

### I3a. ✅ Load `.agentteam-insightignore` (completed in #t1482)

- **Implement:** In `scanner.LoadIgnoreMatcher`, add a third source:
  `<root>/.agentteam-insightignore`. Order: hardcoded defaults →
  `.agentteam-insightignore` → root `.agentteam-insightignore` →
  `.gitignore`. (Splits the old I3 into read + generate.)
- **Acceptance:**
  - Test fixture: repo with `.agentteam-insightignore`
    containing `ignored-dir/` and a file `ignored-dir/x.go`. Scanner skips
    it.
  - Test fixture: root `.agentteam-insightignore` containing
    `!ignored-dir/keep.go` — must respect negation precedence.
  - UA parity test: run Go scan on a fixture with both ignore files; assert
    file set matches UA's `scan-project.mjs` output byte-for-byte.

### I3b. ✅ Generate starter `.agentteam-insightignore` + confirmation gate (completed in #t1482)

- **Implement:** New `scanner.IgnoreGenerator` (or `internal/graphbuilder/ignoregen`)
  that writes a starter ignore file covering:
  - Build outputs: `dist/`, `build/`, `target/`, `out/`, `.next/`, `__pycache__/`,
    `node_modules/`.
  - Dependency dirs: `vendor/`, `.venv/`, `venv/`.
  - Lock files: `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `go.sum`,
    `Cargo.lock`, `poetry.lock`, `composer.lock`.
  - IDE artifacts: `.idea/`, `.vscode/`, `.DS_Store`.
  - VCS: `.git/`, `.hg/`, `.svn/`.
  The output uses `ignore` npm syntax (negation `!`, anchored `/*`, dir
  trailing `/`, `**` glob).
- **Confirmation gate:** when starter is generated, the CLI asks the user to
  review/confirm before scanning, matching UA's pause-before-Phase-1
  semantics. Non-interactive callers without a prompt reader fail closed;
  CI/smoke may accept with `--no-prompt`. This flag is a Go-first extension
  and does **not** imply any UA `meta.json` field.
- **Implemented in #t1482:** starter generation, stop-before-scan gate, and
  `--no-prompt` auto-accept extension for generated or found ignore files. No auto-accept record is written to
  `meta.json` because UA has no such field.
- **Acceptance:**
  - Test: starter content includes UA-style header, `.gitignore` suggestions,
    detected directories, and grouped test-file suggestions.
  - Test: default non-interactive run generates the starter and returns a
    confirmation error before writing `knowledge-graph.json`; with an
    interactive confirmation reader it proceeds.
  - Test: `--no-prompt` continues after generation and writes graph artifacts,
    with help/progress text labeling it as Go-first.
  - Test: when `.agentteam-insightignore` already exists, no
    starter is generated but the same review/confirmation gate still runs,
    matching UA Phase 0.5.

### I15. 🟡 Pre-flight: `--auto-update` config write, `--language` config persistence, `--review` reusing existing graph (partial in #t1482)

> **v3 source-verified correction.** v2 of this item conflated UA strict
> parity with several Go-first extensions. The two are now split.
> **#t1482 status:** `--auto-update` / `--no-auto-update` and `--language` persistence are implemented; `--review` Layer 3 reuse remains a later review/Layer 3 batch item.

- **Implement (UA strict parity — source-verified):** in `options.go`,
  `run.go`:
  - `--auto-update` / `--no-auto-update` writes
    `{"autoUpdate": <bool>}` into `config.json` (`SKILL.md:136-139`).
    Analysis proceeds normally either way — the flag is purely a config
    record for the dashboard's auto-update UI. It does NOT gate persistence.
  - `--language <code>` normalizes friendly names to ISO codes (e.g.
    `chinese → zh`), then merges `{"outputLanguage": "<lang>"}` into
    `config.json` (`SKILL.md:141-158`). Persisted into **`config.json`**,
    not `meta.json`. Subsequent runs read the stored value and skip the
    detection prompt.
  - `--review` reuses the existing `knowledge-graph.json` (copied to
    `intermediate/assembled-graph.json`) and jumps to Phase 6 Layer 3 LLM
    reviewer (`SKILL.md:174-178`). **Phase 7 Save still runs** — `--review`
    does NOT skip persistence. It is the "force a fresh LLM review pass on
    the existing graph" mode.
- **Implement (Go-first extensions beyond UA strict parity — optional,
  must be labeled as such in code & docs):**
  - `mode=review` that **skips** `SaveArtifacts` and exits non-zero on fatal
    issues — Go-first CI-friendly variant. Different from UA's `--review`.
    If adopted, expose under a different flag (e.g. `--ci-review`) to avoid
    collision with UA semantics.
  - `--auto-update`-gated "stop before save" / dry-run path — Go-first.
  - "Already up to date" stop semantics gating artifact writes on
    `--auto-update` — Go-first.
- **Acceptance:**
  - Test (UA strict): `--auto-update` writes `config.json.autoUpdate=true`
    and analysis completes normally (artifacts written).
  - Test (UA strict): `--language zh` on first run persists
    `config.json.outputLanguage="zh"`; a subsequent run without `--language`
    reads the stored value and skips the prompt.
  - Test (UA strict): `--review` on an existing graph jumps to Phase 6
    Layer 3, runs the LLM reviewer, and Phase 7 saves normally
    (`knowledge-graph.json` is rewritten).
  - Test (Go-first, if adopted): `--ci_review` / equivalent skips
    `SaveArtifacts` and produces `review.json` only — clearly labeled as a
    Go-first extension in the help text.

### I16. 🟡 SCAN: `project-scanner` narrative + 10-language import-map resolver + verbatim output (partial after #t1485)

> **v3 source-verified correction.** v2 of this item listed "12 languages
> including Swift"; the actual UA count is **10** with no Swift resolver
> (`extract-import-map.mjs:1407-1455`). The `filteredByIgnore` and
> `estimatedComplexity` field types were also wrong in v2 and are corrected
> below.

- **Completed in #t1484:**
  - **Narrative/contract scaffold.** `scanner/narrative.go` reads README plus
    `go.mod`/`package.json` and emits final `scan-result.json` fields `name`,
    `description`, `frameworks[]`, and `languages[]`. It keeps
    `rawDescription` / `readmeHead` only as in-memory transient project
    context for downstream analyzer requests, matching UA's final-output
    stripping requirement. This is deterministic Go scaffolding, not UA's full
    LLM project-scanner prose.
  - **Output field verbatim constraints** are now implemented for lowercase
    never-null `language`, `fileCategory`, `sizeLines`, `filteredByIgnore`,
    `estimatedComplexity`, and `importMap`.
  - **10-family import-map resolver contract scaffold.** `importgraph/resolvers.go`
    records the UA resolver families.
- **Completed in #t1485:**
  - **Per-language lightweight import-map resolvers** for **10 languages**: TypeScript/JavaScript, Python, Go, Rust, Java, Kotlin, C#, PHP, C/C++, Ruby. Each returns current-project internal paths where the lightweight resolver can prove a match. **No Swift resolver exists in UA** — Swift is out of scope for parity.
- **Remaining gap:**
  - Exact `extract-import-map.mjs` parity still requires tree-sitter and language-specific alias/search-path handling; #t1485 marks the nine non-Go contracts `partial`, not `implemented`.
  - Full LLM project-scanner narrative remains a quality gap if strict prose
    parity is required; #t1484 only provides deterministic README/manifest-
    derived narrative fields.
  - **Note:** Phase 6 validator does NOT validate `scan-result.json` (Phase 6
    only validates `knowledge-graph.json`).
- **Acceptance:**
  - ✅ #t1484 test: deterministic narrative/contract fields are present in
    `scan-result.json`, including `name`, `description`, `totalFiles`,
    `estimatedComplexity`, integer `filteredByIgnore`, and `importMap`; the
    same test asserts transient `rawDescription` / `readmeHead` are absent from
    final JSON.
  - ✅ #t1485 tests cover the 10-family resolver set with internal path matches; non-Go contracts remain `partial` because strict tree-sitter/alias parity is deferred.
  - ✅ #t1484 test: `files[].language` for an unknown extension `.xyz` returns
    `"xyz"` (not null, not `"unknown"`); for a no-extension path returns
    `"unknown"`.
  - Deferred: standalone scan-result validator warning for capitalized
    `files[].language`; Phase 6 still does not validate scan-result.
  - ✅ #t1484 test: `filteredByIgnore` is emitted as an integer (not an object).
  - ✅ #t1484 test: `estimatedComplexity` is one of
    `small|moderate|large|very-large` based on the canonical file-count
    bands.
  - UA parity test: Go `scan-result.json` shape matches UA's for the same
    fixture.

### I17. ✅/🟡 BATCH + ANALYZE: naming contract, Groups A-E, 5-concurrent, completion check (mostly completed in #t1483)

- **Completed in #t1483:**
  - **Non-code Groups A-E** in `batching.BuildPlan`: Dockerfile-per-dir,
    `.github/workflows/`, `.gitlab-ci.yml` + `.circleci/`, SQL migrations per
    `migrations?/` dir, and catch-all parent-dir grouping capped at 20.
  - **Singleton pooling** (`mergeSmallBatches`): MIN_BATCH_SIZE=3,
    MAX_MERGE_TARGET=25.
  - **Batch output naming contract:** AgentTeam writes UA-compatible
    `intermediate/batch-<i>.json` names. No zero-padding was introduced, so no
    Go-first naming style needs to be tracked for this batch.
  - **`--changed-files` full-graph `batchIndex` retention via output filter:**
    incremental mode builds the plan from the current full scan, filters output
    batches to changed files without renumbering, keeps `neighborMap`
    full-graph indices, writes `tmp/changed-files.txt`, writes pruned existing
    graph to `batch-existing.json`, and merges fresh changed `batch-*.json`
    outputs without reading prior `batches.json` or cached unchanged outputs.
  - **Concurrency scope:** deterministic fallback and injected mock runners keep
    the #t1483 5-slot dispatch cap; the real OpenCode runner defaults to serial
    dispatch unless `--isolate-opencode-runtime` is enabled. This Go-first
    safety limit is covered by a unit test and documented as scoped behavior,
    not UA strict parity.
  - **Batch completion / file-existence check** before assemble via
    `batching.VerifyOutputs`; missing or empty expected outputs fail closed with
    `incomplete-batches`.
- **Louvain status after #t1491:** code-file community assignment now uses a
  pure-Go Louvain-style modularity pass rather than connected-components BFS.
  LLM file-analyzer content remains I5.
- **Acceptance status:** Louvain dense-graph split, Groups A-E, changed-files
  filter/no-renumbering, output naming, completion check, `batch-existing.json`,
  and 5-concurrent dispatch are covered by Go tests. UA fixture-level exact
  graphology equivalence remains blocked on upstream fixture comparison and
  wider extractor/import-map work.

### I18. ★ Phase 6 three-layer validator split (Blocker, gate-feedback)

- **Implement:**
  - **Layer 2 — completed in #t1481 by folding checks into
    `schema.ValidateGraph` after current stages (source-verified against
    `SKILL.md:599-672` inline validator script):
    - `node-in-multiple-layers`: file-level node ID appearing in >1 layer
      → emit issue (critical in UA's inline validator,
      `SKILL.md:633-636`).
    - `file-node-not-in-any-layer`: file-level node ID appearing in 0 layers
      → emit issue (critical in UA's inline validator, `SKILL.md:637-639`).
    - `orphan-node`: node with no in/out edges → emit **warning**, not
      issue (UA's inline validator pushes to `warnings[]`, `SKILL.md:650`).
    - `tour-ref-integrity`: tour step with non-existent `nodeIds` → emit
      issue.
    - `layer-ref-integrity`: layer with non-existent `nodeIds` → emit issue
      (already enforced by Go's `filterNodeIDs`).
    - `summary-degradation`: NOT in UA's inline validator — this is a
      Layer 3 (graph-reviewer LLM) check only. Keep in Layer 3, not
      Layer 2.
  - **Layer 3 — deterministic reviewer in #t1486, LLM still gap.** New
    `internal/graphbuilder/reviewer` package runs for saves and `--review`
    existing-graph reuse. It performs (source-verified against
    `agents/graph-reviewer.md`):
    - Empty-layer detection (layer with **zero** `nodeIds` → critical;
      `graph-reviewer.md:84`). NOT "<2".
    - Tour-step count outside **5-15** range → warning
      (`graph-reviewer.md:96`).
    - Orphan nodes (no in/out edges) → **warning**, not critical
      (`graph-reviewer.md:102`).
    - Summary degradation, self-referencing edges, node type / ID prefix
      mismatches → warning (`graph-reviewer.md:100-122`).
    - Non-code nodes missing edges (a `service`/`endpoint`/`table`/`schema`/
      `resource`/`step` node with zero `contains`/`defines_schema`/`serves`/
      `configures`/`deploys` edges) → warning
      (`graph-reviewer.md:104-115`).
    - **Layer-naming coherence is NOT a UA graph-reviewer check** — it was
      a Go-first proposal. Drop from strict-parity scope; if retained,
      clearly label as Go-first.
    - Automated-fix applier: drop dangling edges, default `tags` to
      `["untagged"]`, `summary` to `"No summary available"`, drop
      invalid-type nodes. Re-validate through Layers 1+2.
  - **`review.json` writer — completed in #t1486.** Emits
    `{schema, mode, review, generatedAt, issues, warnings, stats, strictParityGaps}`
    to `.agentteam-insight/intermediate/review.json` for both default and
    `--review` paths.
  - **Remaining gap:** real graph-reviewer LLM runner / quality repair is not
    implemented; deterministic checks are the scoped final gate.
- **Acceptance:**
  - Layer 2 (#t1481): a graph with a node in 2 layers reports
    `node-in-multiple-layers`; a graph with an uncovered file-level node
    reports `file-node-not-in-any-layer`; orphan nodes are warning-only.
  - Layer 3 (#t1486 deterministic): placeholder summaries trigger
    `summary-degradation`; `--review` reuses an existing graph and runs the
    reviewer without file-analyzer batch dispatch; an under-connected
    `service` node warns; `review.json` is written for both default and
    `--review` paths.
  - UA parity test: Go Layer 1+2 issue set matches UA inline validator for
    the canonical fixture.

### I19. Cross-batch coverage matrix discipline (Process, gate-feedback)

See Section 9 for the rule. Tracked here so the implementation items list
includes the process acceptance.

- **Implement:** in every batch branch, before sending for review, the
  assigned engineer updates the coverage matrix in
  `docs/research/2026-07-02-agentteam-insight-graph-builder-compat-parity-audit.md`
  (Section 2): mark the just-completed phase rows ✅, add any new drift rows
  discovered during implementation, and bump the v2 revision note at the top
  of the doc.
- **Acceptance:** reviewer2 verifies the matrix was updated before gating.

### I20. Subdomain graph pre-merge (Gap, deferred)

- **Implement:** Phase 0 reads `.agentteam-insight/subdomain-*/knowledge-graph.json`
  and pre-merges into the main graph before Phase 1 starts. Driven by
  `--subdomain <name>` flag.
- **Acceptance:** deferred — confirm whether AgentTeam needs subdomain support
  before scheduling (open question Q6).

### I21. `--language` locale file loading (Partial in #t1486)

- **Implemented in #t1486:** when `--language` / `--output-language` resolves
  to a non-English code and a UA locale file exists, `run.go` loads
  `skills/understand/locales/<code>.md` from `UNDERSTAND_ANYTHING_ROOT` (or
  common local UA checkout) and appends it under `## Output Language
  Guidelines` for OpenCode file-analyzer prompt paths. The request manifest
  also records `localeAddendum` so mock/injected runners can assert the seam.
- **Remaining gap:** architecture-analyzer/tour-builder LLM prompt integration
  is still absent because those LLM runners are still gaps. Deterministic
  fallback emits only schema-compatible `languageLesson` guidance and does not
  claim locale-output parity.
- **Acceptance:** tests assert the OpenCode prompt and manifest receive a zh
  locale addendum; deterministic fallback does not pretend full localized
  prose parity.

### I22. Server-backed OpenCode runner production path (Design in #t1505; implementation blocker)

- **Design added in #t1505:** add `--opencode-server <addr|auto>` as the
  production file-analyzer path. When agent enhancement is enabled and no fake
  `agent.Runner`/`CommandRunner` is injected, `Run` must create a
  server-backed TaskRunner instead of shelling out to one-off
  `opencode run --dir <tmp>`.
- **Implementation contract:**
  - introduce an `OpenCodeServerStarter` that starts one server per graph run
    for `auto` and returns an `OpenCodeClient` plus a stop function;
  - introduce an `OpenCodeClient` that creates independent sessions, runs one
    task in each session, and closes sessions;
  - create separate sessions for each file-analyzer batch and for future
    intents (`assemble-reviewer`, `graph-reviewer`, `architecture-analyzer`,
    `tour-builder`);
  - always close sessions and stop the run-owned server on success,
    cancellation, and task failure.
- **Acceptance:** fake Starter/Client integration tests prove one server per
  graph run, distinct sessions for two batch tasks, each session receives its
  own task workdir/manifest, `CloseSession` runs for every session, and `stop`
  runs once on success and once on injected failure.

### I23. File-analyzer source subset and permission boundary (Design in #t1505; implementation blocker)

- **Design added in #t1505:** file-analyzer tasks must read only copied,
  session-local source evidence. For every batch, copy `request.files` to
  `workDir/sources/<relative-path>` after path-cleaning rejects absolute
  paths, `..`, symlink escapes, and non-regular files.
- **Manifest contract:** replace source-reading reliance on
  `ProjectRoot=<absolute input repo>` with:

  ```json
  {
    "sourcesRoot": "sources",
    "sourceMap": {
      "cmd/app/main.go": "sources/cmd/app/main.go"
    }
  }
  ```

  Original relative paths may remain for graph IDs/provenance, but prompts
  must tell agents to read only the `sources/<relative-path>` copies.
- **Permission contract:** generated file-analyzer `opencode.json` must set
  `"external_directory": "deny"` and keep question/task/todowrite denied.
- **Acceptance:** tests create a sentinel file in the original repo, run the
  fake runner/client with only `workDir/sources`, and assert manifests/prompts
  do not require the absolute repo path, copied files preserve relative
  layout, malicious paths are rejected, and the fake runner can complete
  without external directory access.

### I24. Graph Builder skills materialization allowlist (Design in #t1505; implementation blocker)

- **Design added in #t1505:** Graph Builder agent tasks must materialize
  runtime skills from a bounded AgentTeam Insight bundle, not from a caller's
  external UA checkout or the legacy `codeanalysis` package.
- **Allowlist:** materialize only:
  - `.agents/skills/agentteam-insight/file-analyzer/SKILL.md`
  - `.agents/skills/agentteam-insight/assemble-reviewer/SKILL.md`
  - `.agents/skills/agentteam-insight/graph-reviewer/SKILL.md`
  - `.agents/skills/agentteam-insight/architecture-analyzer/SKILL.md`
  - `.agents/skills/agentteam-insight/tour-builder/SKILL.md`
  - explicitly referenced files under
    `.agents/skills/agentteam-insight/references/`
- **Manifest contract:** write
  `.agentteam-insight/intermediate/skills-manifest.json` and a per-task copy with
  `{schema, bundleVersion, files:[{path, sha256}], generatedAt}`.
- **Acceptance:** tests assert stable SHA-256 hashes for fixture skill files,
  no non-allowlisted file is copied, repeated materialization is idempotent,
  stale previously materialized files are removed, and two concurrent task
  workdirs do not share mutable skill files. UA validator/smoke scripts remain
  compatibility gates only and are not runtime dependencies.

### I25. Final grep gate and compatibility allowlist (Process, #t1505)

- **Gate added in #t1505:** final grep must distinguish compatibility history
  from public release surface. Allowed:
  - `docs/research/*compat*`, `docs/research/*smoke*`, and compatibility
    validator references may mention UA/Understand to document source parity.
  - Smoke/validator script names may retain compatibility labels when they are
    not public CLI commands.
- **Disallowed in public release surface:** old `agentteam-code`/`agentteam_code`
  generated workflow commands, public `understand` command/artifact names,
  `.understand*` artifact paths, old package/schema/title names, and
  user-facing PR title/body claims that imply server-backed sessions, source
  subset permission hardening, embedded skills materialization, or real LLM
  quality parity are already implemented.
- **Acceptance:** before review, report `git diff --check` plus grep output
  for old public names. Any remaining match must be assigned to the allowlist
  above or fixed.

## Section 6 — Parity tier classification

Two-axis classification: **strict parity** (must match UA byte-shape and
semantics) vs. **Go-first semantic-equivalent** (different implementation,
same observable behavior).

### Section 6.1 — Go-first extensions beyond UA strict parity (v3 gate-feedback)

> **Process rule (reviewer2's #t1480 second-gate directive).** UA strict
> parity items and Go-first extensions must NOT be conflated. Strict parity
> items cite UA source; Go-first items are explicitly labeled as
> "Go-first extension beyond UA strict parity" and tracked separately so
> downstream implementers do not mistake them for UA requirements.

The following items were previously described as if they were UA strict
parity in v1/v2 of this audit. They are **Go-first extensions** — useful
for AgentTeam's CI ergonomics but NOT in UA. All downstream implementation
batches must respect this distinction.

| # | Item | UA strict parity (source-verified) | Go-first extension (AgentTeam-only) |
|---|---|---|---|
| G1 | `--no-prompt` for Phase 0.5 `.agentteam-insightignore` confirmation auto-accept | UA Phase 0.5 is interactive-only (`SKILL.md:202-213`); no `--no-prompt` flag exists and no `meta.json` auto-accept field exists | Added in #t1482 for CI/smoke ergonomics; no `meta.json` auto-accept record is written |
| G2 | `mode=review` that skips `SaveArtifacts` and exits non-zero on fatal | UA's `--review` reuses existing graph + jumps to Phase 6 Layer 3, then Phase 7 saves normally (`SKILL.md:174-178`) | Add as `--ci_review` or similar; do NOT reuse `--review` |
| G3 | `--auto-update`-gated persistence / dry-run / stop-before-save | UA's `--auto-update` only writes `config.json.autoUpdate`; analysis proceeds normally either way (`SKILL.md:136-139`) | Add as separate flag if needed; do NOT overload `--auto-update` |
| G4 | "Already up to date" stop semantics gating artifact writes | UA's "Existing graph + unchanged commit hash" path is an interactive (a/b/c) prompt, not an auto-stop (`SKILL.md:175`) | Add for non-interactive CI pipelines |
| G5 | Zero-padded batch file names (`batch-008.json`, `batch-008-part-01.json`) | UA accepts non-zero-padded names via regex `batch-(\d+)(?:-part-(\d+))?\.json` (`SKILL.md:304-321`); zero-pad is not required | Adopt as a AgentTeam-side style rule for stable lexicographic ordering; do NOT mark as UA strict |
| G6 | Layer-naming coherence check (flag `dir:`, `language:` names for LLM re-naming) | NOT a UA `graph-reviewer.md` check | Drop from strict-parity Layer 3 scope, or keep as Go-first quality signal |

Each Go-first item above is optional — AgentTeam may adopt, defer, or drop it.
The only requirement is **labeling**: code, help text, and docs must say
"Go-first extension beyond UA strict parity" so no one conflates them with
UA parity.

### Strict parity (must match UA exactly)

- **Schema — node/edge type enums, required fields, weight clamping.** Already
  parity (Section 4).
- **Validator stages — sanitize → normalize aliases → auto-fix → drop-broken
  → fatal.** Already parity at the stage boundary.
- **`.agentteam-insight/` file layout — `knowledge-graph.json`,
  `meta.json`, `fingerprints.json`, `config.json`, optional `diff-overlay.json`,
  absent `domain-graph.json` (this batch).** Already parity.
- **Layer contract — every file-level node in exactly one layer; no
  function/class nodes in layers.** Currently violated (F1, F2, F11).
- **`tested_by` edge semantics — direction `prod → test`, tag `"tested"` on
  production node.** Fixed for deterministic merge outputs in #t1486 (F12).
- **Fingerprint content hash — SHA-256 hex of bytes.** Already parity (F9).
- **Change classifier tiers + thresholds.** Already parity (F8).

### Go-first semantic-equivalent (Go implementation, UA-observable behavior)

- **Scanner enumeration.** Go uses `filepath.WalkDir` + custom ignore; UA
  prefers `git ls-files`. As long as the resulting file set matches for
  typical repos and `.gitignore` semantics are honored, Go-first is fine.
  Current matcher has known edge-case gaps (F15).
- **Batch planner.** After #t1491 Go uses a pure-Go Louvain-style community
  assignment plus UA-style non-code Groups A-E and singleton pooling; UA uses
  graphology Louvain for code files. Both produce `batches[]` consumed by the
  same Phase 2 contract. Exact graphology fixture drift remains for token-spend
  and dense-graph batch-shape parity.
- **Structure extractor — Go.** Go uses `go/ast`; UA uses tree-sitter-go.
  Output contract is the same (functions/classes/imports/exports/calls).
  Go-first OK.
- **Structure extractor — non-Go.** #t1485 uses dependency-free lightweight
  extraction for the scoped languages and non-code formats. The observable is
  closer (functions/classes/imports/exports/calls + non-code sub-nodes), but
  exact UA tree-sitter grammar parity remains a follow-up decision.
- **Tour generator heuristic fallback.** #t1486 now keeps deterministic tours
  file-level, flow-aware, configurable, and schema-compatible. Go-first OK as
  long as LLM tour-builder/language-lesson quality remains documented as a gap.
- **Layer generator heuristic fallback.** Both produce `layers[]` matching
  the schema + exclusivity + file-only. Go-first OK as long as the
  LLM-driven `architecture-analyzer` exists for the `--review` path.
- **ID normalization.** Go does it in `validateGraph`; UA does it in
  `merge-batch-graphs`. Both produce canonical IDs in the persisted graph.
  Go-first OK.
- **Worktree redirect.** Both redirect to main repo root. Go requires
  `.git` basename; UA does not. Worth aligning (open question Q10).

## Section 7 — Open questions

1. **Agent runner choice.** OpenCode agent (already wired via
   `--opencode-agent`) vs. Claude API direct vs. pluggable. This decision
   gates I5 and I9.
2. **LLM primary vs. deterministic primary.** UA's primary path is LLM;
   heuristic is fallback. Does AgentTeam Insight want the same default, or
   deterministic default with `--review` opt-in for LLM?
3. **Layer-exclusivity check location.** Should Go's `ValidateGraph` enforce
   it (currently does not), or should a separate inline validator (like UA's
   `ua-inline-validate.cjs`) be added? Recommendation: put it in
   `ValidateGraph` (Section 4 shows UA's `validateGraph` itself does not;
   the inline script does — but Go has no separate inline script, so
   `ValidateGraph` is the right home).
4. **Tree-sitter dependency footprint.** #t1485 deliberately avoided new
   tree-sitter/CGO/WASM grammars for CI safety and documented the parity gap.
   Confirm the build/CI impact before replacing the lightweight extractors with
   strict tree-sitter parity.
5. **Louvain implementation.** #t1491 adds a dependency-free pure-Go
   Louvain-style modularity pass and removes the connected-components-only
   batching drift. Exact graphology library fixture equivalence remains a
   residual drift before claiming byte-for-byte UA parity.
6. **Subdomain graph merge.** UA pre-merges subdomain graphs in Phase 0
   (`SKILL.md:159-164`). AgentTeam Insight has no subdomain concept yet. Confirm
   whether this is needed for the AgentTeam use case.
7. **Worktree redirect parity.** Go's `RedirectWorktreeRoot`
   (`paths.go:71-103`) requires `filepath.Base(absCommonDir) == ".git"`; UA
   does not. Bare or detached worktree layouts may diverge.
8. **Intermediate cleanup.** Closed in #t1482: successful saves archive scratch
   into `.trash-<timestamp>/` and Phase 0 purges stale trash.
9. **`git ls-files` mode.** Closed in #t1482 for the strict path: scanner
   prefers `git ls-files -z -co --exclude-standard` and falls back to the
   custom matcher walk.
10. **`MergeGraphUpdate` cascading sub-nodes.** Closed for #t1485 deterministic
    sub-nodes because they carry the parent `FilePath`; `assembler/merge.go`
    prunes all nodes for changed file paths, including child service/table/
    schema/resource/endpoint nodes.

## Section 8 — Recommended batch mapping to #t1481–#t1486

(non-binding — for planner2 to refine; updated for v2 gate-feedback)

- **#t1481 — Schema/validator hardening (Layers 1+2).** I1 + I2 + I12 + I18
  (Layer 2 portion only) + I19. Smallest, highest-leverage. Releases the
  validator drift that blesses broken layer output. Tightens PR body to match
  actual coverage. Establishes the cross-batch discipline (Section 9).
- **#t1482 — Scanner alignment (completed for scoped items).** I3a + I3b + I13 + I14 + the config portion of I15. UA ignore-file read + starter generation + confirmation gate + git fast path + intermediate cleanup + pre-flight config semantics (`--auto-update` / `--language` persistence) are implemented. `--review` Layer 3 reuse remains with later graph-reviewer work.
- **#t1483 — Batching wire-up (completed; Louvain followed in #t1491).** I4 + most of I17
  are implemented: batches now drive analyzer/fallback dispatch, non-code
  Groups A-E, singleton pooling, naming contract, changed-files filter,
  `batch-existing.json`, concurrency cap, and completion check are covered.
  I8 Louvain is scoped in #t1491; exact graphology fixture drift remains. Real OpenCode runner
  dispatch added in #t1484 is serial by default unless runtime isolation is
  enabled; mock/injected runners and deterministic fallback retain the 5-slot
  cap.
- **#t1484 — LLM file-analyzer runner + project-scanner narrative (completed
  for scoped items).** I5 runner path + I16 scan-result field contract,
  deterministic narrative scaffold, and 10-family resolver contract scaffold.
  Final `scan-result.json` strips transient `rawDescription` / `readmeHead`;
  real OpenCode runner dispatch is serial by default unless runtime isolation
  is enabled. Real non-Go resolvers were narrowed by #t1485; exact
  tree-sitter/path-alias parity remains a follow-up gap.
- **#t1485 — Multi-language + non-code extractors (completed for scoped
  items).** I6 + I7 partial: lightweight 10-family import resolvers,
  high-signal structure extraction, and scoped non-code child nodes are covered
  without new tree-sitter/CGO/WASM dependencies. Exact tree-sitter/language
  registry parity remains an explicit gap.
- **#t1486 — Review/tour/merge enhancements (completed for scoped items).**
  I9/I18 deterministic Layer 3 review artifact + `--review` reuse, I10
  file-level/calls-flow tour fallback, I11 `tested_by` linker, and I21
  locale addendum prompt loading are covered. Real graph-reviewer LLM,
  tour-builder LLM, and architecture-analyzer LLM quality remain explicit
  gaps rather than release claims.
- **#t1505 — OpenCode/skills production design gate (design only).** I22–I25
  record the remaining #t1494 blockers and required tests: server-backed
  `--opencode-server`, source-subset copying with `external_directory: deny`,
  allowlisted skills materialization with hashes, and final grep/allowlist
  discipline. This batch updates docs/design/PR material only; it does not
  implement the runtime code and must not be cited as closing F17–F19.

Each batch should ship with: (1) UA-side fixture comparison test, (2) fresh
`go test ./...` + UA compat gate + smoke + `git diff --check`, (3) reviewer2
gate, (4) updated coverage matrix per Section 9.

## Section 9 — Cross-batch coverage matrix discipline (v2 gate-feedback)

> **Process gate.** Every subsequent batch (#t1481–#t1486) MUST update this
> audit's Section 2 coverage matrix before sending for review. Reviewer2's
> gate checks the matrix was updated; stale matrix blocks gate.

### Why this rule exists

The original I1–I14 list was a static snapshot of drift as of PR #2474.
Reviewer2's gate observed that subsequent batches will inevitably surface
**new drift** (e.g. implementing I6 reveals per-language tree-sitter quirks
UA handles that Go doesn't; implementing I17 reveals UA's batch split logic
has edge cases the Go side trips on). A static list goes stale and the team
loses track of remaining drift. Continuous convergence requires continuous
recording.

### What each batch must do

1. **Before implementation:** re-read Section 1 (UA spec) and Section 2
   (current coverage) for the phase(s) the batch touches. Note any UA
   behavior the batch will rely on.
2. **During implementation:** when a new UA-vs-Go drift is discovered (a
   subtle UA behavior the audit didn't catch), append it to Section 3
   (Drift Findings) with the next F-number and file:line on both sides.
3. **Before review:** update Section 2 coverage matrix — flip status from
   🟡/🔴/⚠️ to ✅ for completed rows, add new rows for any newly-surfaced
   drift. Update the TL;DR counts ("X blockers / Y gaps / Z drifts") to
   match.
4. **In commit message:** reference the new F-numbers added (e.g.
   `docs(agentteam-insight): #t1483 batch wire-up + F17/F18 drift surfacing`).

### Acceptance test

Reviewer2's per-batch gate now includes:

- ✅ Coverage matrix Section 2 reflects current state — every status cell
  matches what the code does.
- ✅ Drift findings Section 3 includes new entries for any drift surfaced
  during the batch.
- ✅ TL;DR counts match the matrix (blocker / gap / drift totals).
- ✅ Implementation items Section 5/5.1 reference the latest drift numbers
  where applicable.

Failure to update the matrix is a "需补充" gate verdict, same as missing
tests.

### Revision log

- **v1** (`ef8d88f97`): 16 drift findings (F1–F16), 14 implementation items
  (I1–I14), 8 phases mapped.
- **v2** (`8b25702e0`): adds Section 5.1 (I3a, I3b, I15–I21), Section 9
  (cross-batch discipline), expands Section 1 Phase 0/0.5/1/1.5/2/6, expands
  Section 2 Phase 0/0.5/1/1.5/2 detail rows, splits Section 2 Phase 6 into
  Layer 1 vs Layer 2 vs Layer 3 missing items, updates Section 8 batch
  mapping. No new F-numbers added (v2 is incorporation of reviewer2's
  first-gate feedback, not new code-reading discoveries).
- **v3** (this revision): source-verified correction of six factual errors
  reviewer2 caught in v2 where UA strict parity had been conflated with
  Go-first extensions. New Section 6.1 enumerates the Go-first extensions
  (G1–G6) in a single table. No new F-numbers; no new I-numbers. Sections
  touched: TL;DR v3 note, Section 1 Phase 0/0.5/1/1.5/6, Section 2
  Phase 0/0.5/1/1.5/6, Section 5.1 I15/I16/I17/I18, Section 6.1 (new).
- **v4** (this revision): corrects the incremental `--changed-files`
  batching semantics reviewer2 flagged as the v3 third-gate blocker. v3
  still described "prior-run `batchIndex` / prior `batches.json` / cached
  unchanged batch outputs / only-changed-batches-re-planned" — but UA
  source (`compute-batches.mjs:528-535`, with the explicit comment that
  `batchIndex` on filtered batches **retains the full-graph assignment**,
  and `SKILL.md` Phase 2 incremental) shows the real semantics: always
  run the full-graph plan from current `scan-result.json`, filter OUTPUT
  only (no renumbering, no prior `batches.json` load, no cached unchanged
  `batch-<i>.json` read), then prune existing graph by filePath into
  `batch-existing.json` and merge with fresh changed `batch-*.json`. Four
  ranges rewritten, no new F-numbers or I-numbers. Sections touched:
  Section 1 Phase 1.5, Section 2 Phase 1.5 missing item, Section 5.1 I17
  implementation + acceptance test.
- **v5 / #t1481** (this revision): implements schema/layer hardening. Section
  2 Phase 4 moves from blocker drift to partial fallback parity, and Section
  2 Phase 6 moves from Layer 1-only to Layer 1+2 partial. F1/F2/F11 and
  I1/I2/I12 plus the Layer 2 portion of I18 are marked completed. PR/smoke
  docs were narrowed to avoid full-parity claims. No new drift findings were
  discovered during #t1481.

- **v6 / #t1482** (this revision): implements scanner/ignore, pre-flight config, and intermediate cleanup alignment. Section 2 Phase 0/0.5/1/7 rows updated; F3/F14/F15 marked fixed; I3a/I3b/I13/I14 marked completed; I15 marked partial with `--auto-update`/`--language` done and `--review` still deferred to Layer 3 work. Smoke/PR docs updated for `--no-prompt` and scanner coverage. Reviewer2 gate follow-up corrected Phase 7 to archive `.agentteam-insight/tmp/` as well as intermediate scratch, and removed stale Section 2 cleanup-gap text. No new drift findings were discovered during #t1482.
- **v7 / #t1483** (this revision): implements batch/neighborMap analyzer seam and batch-output contract. Section 2 Phase 1.5/2 rows updated; F5 marked fixed; F10 narrowed to “no production runner”; I4 marked complete; I17 marked mostly complete with Louvain and LLM content explicitly deferred. Incremental semantics now record current full-scan/full-graph assignment + changed-files output filter, `tmp/changed-files.txt`, `batch-existing.json`, and fresh changed `batch-*.json` outputs, with no prior `batches.json`/cached unchanged batch reads. New drift recorded then: Louvain/community detection remained unimplemented and observable because batches drive analyzer dispatch; #t1491 later scopes this drift.
- **v8 / #t1484** (this revision): implements file-analyzer runner path and scan-result contract convergence. Section 2 Phase 1/2 rows updated; F7/F10 narrowed to partial instead of missing; I5 marked runner-path complete; I16 marked partial with scan-result contract and 10-family resolver scaffold done. Reviewer2 follow-up corrected final `scan-result.json` to strip transient `rawDescription` / `readmeHead`, and documented real OpenCode runner dispatch as serial by default unless `--isolate-opencode-runtime` is enabled (deterministic fallback / injected runners keep #t1483's 5-slot cap). No new drift findings were discovered during #t1484; at that point the remaining gaps were Louvain, real non-Go import-map resolvers, tree-sitter/non-code extraction, assemble-reviewer, tour enhancement, and graph-reviewer Layer 3.
- **v9 / #t1485** (this revision): implements scoped multi-language and non-code extraction. Section 2 Phase 1/2 rows updated; F4 narrowed to partial; I6/I7 and I16 marked partial with 10-family lightweight resolvers, high-signal structure extraction, and scoped non-code child nodes done. No tree-sitter/CGO/WASM dependency was introduced; exact UA tree-sitter/path-alias/language-registry parity remains a documented gap. No new drift findings were discovered during #t1485.
- **v10 / #t1486** (this revision): implements final review/tour enhancements. Section 2 Phase 2.5/3/5/6 rows updated; F12 and I11 fixed; I9/I10/I18 marked partial with deterministic review artifact/Layer3 checks, `--review` existing-graph reuse, file-level calls-flow tours, configurable `MaxSteps`, deterministic `languageLesson`, and locale addendum loading for OpenCode prompt paths. No new drift findings were discovered during #t1486; at that point remaining gaps were Louvain, exact tree-sitter/path-alias/language-registry parity, real LLM assemble/graph-review quality, and architecture/tour-builder LLM parity.
- **v11 / #t1491** (this revision): implements scoped Louvain/community batching. Section 2 Phase 1.5, F6, I8, and I17 updated; `batching.BuildPlan` now uses pure-Go Louvain-style modularity assignment before size enforcement/non-code Groups A-E/singleton pooling, and dense 50-file import graph coverage verifies multiple communities. Exact graphology fixture equivalence remains a residual drift; tree-sitter/path-alias/language-registry and real LLM quality gaps remain unchanged.
- **v12 / #t1505** (this revision): design-only closeout for the #t1494
  OpenCode/skills blockers. Section 2 keeps Phase 2 LLM file-analyzer partial
  and names the remaining production gaps; Section 3 adds F17–F19 for
  server-backed runner lifecycle, source-subset/permission boundary, and
  skills materialization allowlist; Section 5 adds I22–I25 with fake
  Starter/Client, source-copy, skills-manifest, and final grep/allowlist
  acceptance. No runtime implementation is claimed in this revision.

## References

- PR #2474: https://github.com/tidbcloud/AgentTeam/pulls/2474
- Implementation plan: `docs/research/2026-07-02-agentteam-insight-graph-builder-implementation-plan.md`
- UA semantics report: `docs/research/2026-07-02-graph-builder-compat-semantics.md`
- Smoke verification: `docs/research/2026-07-02-agentteam-insight-graph-builder-smoke.md`
- CLI scan: `docs/research/2026-07-02-agentteam-insight-graph-builder-cli-scan.md`
- Implementation PR body material: `docs/pr/2026-07-02-agentteam-insight-graph-builder.md`
- UA checkout root: `/Users/lonng/devel/ai/Understand-Anything/understand-anything-plugin/`
- AgentTeam Insight graph source: `kits/agentteam-insight/internal/graphbuilder/`
