Skip to main content

ARCLUX Progress — Design Decisions

Why things were built the way they were. See PROGRES.md for the index.

2026-08-03 — Update — GitHub infra + features/graph decision

Repo infrastructure added: branch ruleset on main (PR required, no direct push — verified by testing it against ourselves), PR + issue templates (.github/), 10 GitHub Issues created from open items in this file, release tag v0.1.0-alpha published, CONTRIBUTING.md rewritten (was stale: said pnpm/turbo, said detectors don’t exist — now says npm, 18/18 detectors, references playground/ testing pattern). Also removed turbo.json (0 bytes, unused leftover — project uses npm directly, turbo command never actually run against this repo). apps/web/features/graph/ decision*: useGraph.ts implemented as a thin re-export of GraphProvider.tsx’s useGraphContext(). The other 4 files (graphStore.ts, graphEvents.ts, useGraphLayout.ts, useGraphSelection.ts) are DELIBERATELY left as documentation-only stubs — GraphProvider.tsx already owns all graph state (transform, positions, dimensions, selection) via React Context. Do NOT implement a separate store/hooks layer here; it would create two sources of truth for the same state. Same class of risk as the packages/ui/graphColor.ts / theme/graphColors.ts naming collision noted earlier.

2026-08-03 — Update — parseTsx.ts and parseTsConfig.ts confirmed intentionally empty

Verified, not just assumed: packages/parser/typescript/parseTsx.ts and parseTsConfig.ts will stay empty stubs permanently, not because they’re “not done yet” but because their functionality already lives elsewhere:
  • .tsx parsing: handled inside parseTs.ts itself via ts.ScriptKind.TSX (checked its extensions field and ScriptKind selection logic directly).
  • tsconfig.json parsing: handled inside packages/graph/resolveAliases.ts (moved from packages/indexer/ on 2026-08-13, fix for issue #2 circular dependency indexer<->graph), which reads tsconfig.json / jsconfig.json directly (with comment/trailing-comma stripping) for path-alias resolution.
Both files now have a comment explaining this, so a future session doesn’t attempt to implement duplicate logic in either of them — same class of risk previously flagged for packages/ui/graphColor.ts vs theme/graphColors.ts (that one is still an open risk; these two are now resolved/documented).

2026-08-04 — Update — PLANNED (not yet built): graph node visual impact indicator

[STATUS UPDATE, 2026-08-07]: this plan is now implemented. See the “UPDATE: Graph impact halo — implemented” entry near the bottom of this file for what actually got built and what’s still open (tier thresholds not yet tuned, not yet visually verified). The plan below is kept as-is for historical context — don’t re-implement it.
Goal: in the dependency graph view, high-impact nodes (files with many consumers, e.g. logService.ts in a VS Code-scale repo with 430 importers) should be visually distinguishable WITHOUT clicking each node first. Motivated by dogfooding: currently every file node is the same blue regardless of impact, so finding “the important files” requires clicking through hundreds of nodes one at a time. Explicit non-goal, confirmed with user: do NOT change node color by type. graphNodeColors (theme/graphColors.ts) currently colors nodes by GraphNodeType (file=blue, external-package=amber, route=purple, component=teal, hook=red) — that must stay as-is, it’s how users tell node kinds apart. Impact must be a SEPARATE visual signal layered on top (halo ring and/or radius size), not a replacement for type color. Data availability, already confirmed:
  • packages/shared/types.ts’s GraphNode interface has NO importedBy/fan-in field built in.
  • BUT DependencyGraph.edges (GraphEdge[], with source/target as GraphNode ids) is already sent to the browser in full via GET /api/graph (packages/graph/serializeGraph.ts passes graph.edges through unmodified, no changes needed there).
  • Therefore: fan-in count per node can be computed CLIENT-SIDE by counting how many times each node id appears as an edge’s target across graph.edges. No backend/API changes needed at all — this is a frontend-only feature.
Implementation plan, in order:
  1. In apps/web/components/graph/GraphProvider.tsx: add a useMemo that, whenever graph changes, builds Map<nodeId, number> by iterating graph.edges and counting occurrences of each edge.target. Expose this as a new importCounts: Map<string, number> field on GraphContextValue (add to both the interface and the value object near the bottom of the file, same pattern as the existing positions field).
  2. In apps/web/components/graph/GraphCanvas.tsx: at the <GraphNode> render call around line 308-315 (confirmed exact location — inside {graph.nodes.map((node) => { ... return <GraphNode ... /> })}, sibling props to isSelected/isHovered), pull importCounts from useGraphContext() (already imported/used elsewhere in this file presumably — verify) and pass importCount={importCounts.get(node.id) ?? 0} as a new prop.
  3. In apps/web/components/graph/GraphNode.tsx: add importCount: number to GraphNodeProps. Define tier thresholds (user’s suggested starting point: High >100, Medium 20-100, Low/normal <20 — these are arbitrary and should be tuned after seeing it rendered against a few real repos of different sizes, e.g. python-demo vs vscode vs next.js, since “100 importers” means very different things in a 50-file repo vs a 15,000-file repo). For High/Medium tiers, render an additional <circle> halo BEHIND the existing node circle (larger radius, no fill, a neutral stroke color like white or amber at low opacity — NOT reusing graphNodeColors, since that would collide with the type-color meaning). Consider also scaling BASE_RADIUS slightly for High-tier nodes. Existing isSelected halo logic ({isSelected && <circle r={radius + 5} ... />}) is a useful reference for the halo-circle pattern already used in this file — don’t duplicate logic, structure the new halo consistently with it.
  4. Test with enableMouseInteraction-style verification: run against playground/python-demo first (small, fast iteration) to confirm no crash/visual regression, THEN test against a large real repo (the user has already tested vscode, react, vercel/next.js, microsoft/vscode via the /new flow against localhost — reuse one of those) to confirm the tiering actually looks meaningful at scale, not just correct in theory. Screenshot verification in-browser required before considering this done — typecheck alone is not sufficient evidence per this project’s established verification standard.
  5. Consider whether label text position (x={radius + 6} in GraphNode.tsx) needs to account for the halo radius too, or if it’s fine referencing only the inner circle’s radius — check visually.
  6. Consider whether d3-force’s collision detection (GraphCanvas.tsx, look for wherever simulation nodes get a radius/collision force) needs updating so bigger high-impact nodes don’t visually overlap neighboring nodes now that some nodes are bigger than others — this wasn’t investigated yet, flagged as a real risk worth checking, not confirmed either way.
Status: planning/investigation only, ZERO code written yet. All file line numbers and current-state details above were confirmed by directly reading the files in this session — safe to trust and start straight from step 1 above without re-investigating from scratch.

2026-08-05 — Decision — same-package/same-namespace resolution: ONE generic pass, not per-language fixes

Context: Go graph (Kubernetes test) and Java graph (java-demo fixture) both showed near-zero edges despite files clearly being related. Root cause confirmed identical in both parseGo.ts and parseJava.ts’s own comments (already documented by whoever wrote them, not discovered fresh here): both Go and Java let files in the same package/directory reference each other with ZERO import statements. playground/go-demo’s cyclic_a.go/cyclic_b.go and playground/java-demo’s Main.java calling Service/Models/Utils are both confirmed real examples of this. The parser only extracts what’s literally written, so these relationships never reach resolvePath.ts as anything to resolve — there’s no import statement token to feed it in the first place. Decision: do NOT write a Java-specific fix and a separate Go-specific fix. This is one general problem — “files that share an implicit scope need a same-scope dependency pass independent of import statements” — that will likely also apply to C# (namespace) and Rust (mod) once those parsers go further than manifest-only (parseCsproj.ts, parseCargoToml.ts exist; parseCSharp.ts/parseRust.ts are still empty). Build ONE resolution pass parameterized by “what counts as a shared scope” per language (directory for Go, package declaration for Java), not four copies of similar logic. Not yet built — this is a design decision recorded for whoever picks this up next, not an implementation. Same class of gap as resolveRoutes.ts being empty (noted in parseGo.ts’s own comment as a parallel case). Referenced but not portable: cloned javaparser/javaparser to ~/research/javaparser for its SymbolSolver/TypeSolver concepts — it’s a JVM library, not directly adaptable to TypeScript, but worth reading for how a mature tool structures scope resolution before designing ARCLUX’s own pass.

2026-08-06 — Decision — issues assigned to a collaborator must also be marked in-file

Context: packages/parser/php/parsePhp.ts and packages/parser/php/parsePhpRoutes.ts sit right next to each other. parsePhpRoutes.ts is assigned to Alitindrawan24 via issue #53. Someone Browse-ing packages/parser/php/ without first checking the GitHub issues list has no way to know parsePhpRoutes.ts is spoken for
  • it just looks like another empty file waiting to be filled in, identical in appearance to a genuinely unclaimed stub.
Decision: whenever an issue is filed that assigns a SPECIFIC file or narrowly-scoped task to a collaborator, also add a short comment in that file (or, if the file doesn’t exist yet, in the most relevant existing sibling file) stating the issue number and assignee. Do not rely on the GitHub issue tracker alone to communicate this - anyone working directly in the codebase (a session reading files, not Browse-ing issues first) needs the signal to be visible at the file level too. Minimum content for the marker comment: issue number, assignee username, one line on what’s being built there. See packages/parser/php/parsePhp.ts’s comment (referencing issue #53 / Alitindrawan24 / parsePhpRoutes.ts) as the template to follow. This does not replace filing the GitHub issue - both are required, the file comment is an addition for discoverability, not a substitute.

Still empty, priority order for next session

  1. packages/indexer/resolveRoutes.ts — unblocks entry-file-awareness for detectUnusedExports/detectOrphanFiles false positives
  2. packages/indexer/resolveExports.ts, resolveComponents.ts, resolveHooks.ts, resolveProviders.ts — same family as resolveRoutes
  3. apps/web/components/explorer/Explorer.tsx, DependencyList.tsx — FileDetails.tsx already exists but isn’t wired to anything, this is why
  4. apps/web/lib/api.ts, graph.ts — client fetch helpers, currently pages call fetch() inline
  5. packages/db/* — persistence layer, 0%, needed before any “history over time” feature
  6. packages/indexer/updateIndex.ts, watchIndex.ts, indexSchema.ts — incremental indexing, depends on packages/incremental being wired in first (not yet done)

2026-08-07 — DependencyList.tsx type confirmed against real API

DependencyList.tsx previously had a local GraphResponse/GraphNodeResponse/GraphEdgeResponse type, written before app/api/graph/route.ts’s actual response shape was checked (its own comment admitted this). Verified: DependencyGraph.nodes/edges from packages/shared/types match exactly. Replaced the local guessed type with the real shared type. Going forward: don’t guess API response shapes in component files — check the actual route.ts handler first, even if it means a short delay before writing the component.

2026-08-07 — Next steps priority for future sessions

[STATUS UPDATE, 2026-08-07 later same day]: item (1) below is now implemented — see “UPDATE: Graph impact halo — implemented” near the end of this file. Item (2) is still open.
Two concrete next steps identified this session, in suggested order: (1) Graph node visual impact indicator (halo ring for high-fan-in nodes) — full implementation plan already documented in an earlier entry in this file (client-side importCounts via useMemo in GraphProvider.tsx, passed as importCount prop through GraphCanvas.tsx to GraphNode.tsx, rendered as an extra halo circle). Zero code written yet, ready to start from step 1. (2) Remaining inline fetch() calls that duplicate the pattern lib/api.ts’s fetchJson() now centralizes — ImpactSummary.tsx and GlobalSearch.tsx were the two examples that motivated building fetchJson() in the first place but were NOT themselves refactored to use it. Worth a follow-up pass to actually consume the helper there, plus check FileDetails.tsx and app/api/file/route.ts for the same duplicated pattern.

2026-08-07 — Graph impact halo: zoom-gated to avoid clutter

Resolved an open question from the original halo-ring plan: halos only render when zoom level is past a threshold (not always-on), avoiding visual clutter/overlap when zoomed out on dense graphs. Rejected alternatives: always-on halo (overlaps neighboring nodes in dense graphs), thicker border/stroke instead of halo (loses the ‘grows with importance’ visual cue that a halo radius gives). Implementation-wise this means GraphNode.tsx’s halo render needs access to the current transform.scale (already available via useGraphContext()) and a MIN_ZOOM_FOR_HALO constant to gate on.

2026-08-07 — UPDATE: Graph impact halo — implemented

The halo-ring plan described in the 2026-08-0X entry above (and listed as a next step in ‘Next steps priority for future sessions’) is now implemented: GraphNode.tsx renders an impact halo circle gated by zoomScale >= MIN_ZOOM_FOR_HALO, importCount computed via useMemo in GraphProvider.tsx from graph.edges, passed through GraphCanvas.tsx. Tier thresholds (High >100, Medium 20-100) are still unverified against real repos — that part of the original plan remains open. Not yet visually verified in-browser as of this entry. If you’re reading the older halo entries above, they’re outdated — this is the current status.

2026-08-07 — Simulated stage progress instead of real backend streaming

apps/web/components/graph/AnalyzingProgress.tsx (new) replaces the static “Analyzing repository…” text in GraphCanvas.tsx’s isLoading branch. Cycles through 5 stage labels (Cloning/Scanning/Parsing/ Resolving/Building) on a fixed timer + shows an indeterminate progress bar, with a “taking longer than usual” message after ~17s. Chose client-only simulated progress over backend SSE streaming: the correct fix (backend emits real stage events, e.g. via Server-Sent Events on /api/graph) would need analyzeRepository()‘s pipeline to become event-emitting instead of a single synchronous return — a much larger change. This is a stopgap: the labels/timing are tuned by feel, NOT derived from real pipeline telemetry. Large repos will sit on the last stage indefinitely since there’s no real signal to advance further. Documented as such in the component’s own comment so nobody later mistakes this for actual progress reporting. Addresses the UX gap noted in status-backlog.md’s large-repo dogfooding entry (microsoft/TypeScript’s “Indexing failed” was indistinguishable from vercel/next.js’s “just slow” until it errored, because there was no feedback at all). Verified: tsc --noEmit -p apps/web/tsconfig.json clean. NOT yet visually verified in-browser — pushed near a chat context limit. Coordination note: git pull before this session started fast-forwarded in unrelated changes to GraphCanvas.tsx/GraphNode.tsx/GraphProvider.tsx from another session (fan-in halo indicator feature, matches an earlier decisions.md plan) — merged cleanly, no conflict with this change since they touch different parts of the same files.

2026-08-07 — QUICKSTART.md language kept English

QUICKSTART.md initially drafted in Indonesian during a mobile terminal session, per user preference for chat interaction. Decided to keep it English-only to match PROGRES.md and TOOLING.md conventions (all repo docs are English, Indonesian is only used in Claude chat sessions). No translation needed yet since the file is still short (3 sections: workflow, progress logging, pre-check for empty files).

2026-08-07 — README Contributors section intentionally removed

The Contributors section (contrib.rocks avatar grid) was intentionally removed from README.md by the user via a direct GitHub browser edit. A later session mistook this for accidental damage (based on the generic ‘Update README.md’ commit message, which matched the pattern of other stray browser-edit branches like GSF-001-patch-1/-2) and restored it — this was wrong and got reverted. If README.md is missing a Contributors section in the future, that’s the current intended state, not a bug to fix.

2026-08-07 — Next up: graph LOD (level-of-detail) rendering

[STATUS UPDATE, 2026-08-08]: this plan is now implemented. See “UPDATE: Graph LOD rendering — implemented” below.
Status: In Progress Discussed but not yet started: extend the zoomScale-gating pattern already used for the impact halo (GraphNode.tsx, MIN_ZOOM_FOR_HALO) to also gate label/icon visibility at low zoom, reducing DOM/render load when zoomed out on large graphs. Rough plan discussed: very low zoom (<0.5) hides icon+label entirely (node becomes a plain dot), mid zoom (0.5-1) keeps current behavior (label only on select/hover), high zoom (>=1) optionally always shows labels for high-importance nodes. Checked reactflow-ref’s Stress example for a reference pattern first — it’s a performance benchmarking harness, not an LOD implementation (React Flow handles this internally, not via example code), so no direct pattern to borrow. This is a natural extension of existing code (GraphNode.tsx’s opacity={isSelected || isHovered ? …} pattern at the label render, ~line 104), not a new subsystem. Prioritized as step 1 of 3 general performance directions discussed (graph viewer LOD/canvas rendering, pipeline parallelization+caching, new analysis features) — graph viewer was picked first since it’s the most immediately felt by users and doesn’t overlap with collaborator-assigned work (call graph is assigned to xcontcom via issue #50).

2026-08-07 — Cache package design research (packages/cache)

Status: Not Started Not Started

2026-08-07 — Cache package design research (packages/cache)

Status: In Progress Researched before implementing packages/cache (CacheProvider.ts, fileCache.ts, graphCache.ts, memoryCache.ts, repositoryCache.ts — all still 8-line stubs, zero consumers currently call any of them, no prior plan existed in progres/ for this). Checked packages/README.md and pipeline.ts/indexer — confirmed nothing references cache yet, this is greenfield design work. Studied ~/dependency-cruiser/src/cache/ as reference (NOT for copying, for architecture ideas — user explicitly wants ARCLUX’s cache to be MORE capable than this reference, not simplified). Key findings:
  • Two invalidation strategies: MetadataStrategy (git-diff based via watskeburt/getSHA — fast, no file reads, just asks git what changed since last SHA) vs ContentStrategy (per-file checksum comparison — works without git but has to hash every file). MetadataStrategy is the better fit for ARCLUX since packages/git and packages/watcher already do git-diff based watching — reuse that instead of hashing.
  • Cache format has an explicit CACHE_FORMAT_VERSION constant (numeric, bumped on breaking changes) so an old cache from a previous ARCLUX version gets safely invalidated instead of returning corrupt/incompatible results.
  • Cache dirtiness is scoped precisely: they maintain an explicit list of which CLI options/config changes should 100% invalidate cache vs which only invalidate a subset. ARCLUX equivalent: analyzeRepository({repoUrl, branch, …}) options that affect output (branch, exclude patterns, etc) should be part of the cache key/invalidation check.
  • brotli compression at min quality (faster than gzip, still better ratio) used for on-disk cache storage, sync not async (sync is faster in this context and avoids promisifying zlib).
NEXT STEPS for actually implementing packages/cache (not done yet):
  1. Design CacheProvider.ts as the orchestrator/interface (equivalent to their Cache class), with a pluggable invalidation strategy (metadata/git-diff-based as primary, matching packages/git’s existing capabilities).
  2. fileCache.ts, graphCache.ts, repositoryCache.ts likely map to caching ParsedFile results, DependencyGraph results, and Repository (indexed) results respectively at different pipeline stages — needs confirming against packages/engine/pipeline.ts’s actual stage boundaries before finalizing shapes.
  3. memoryCache.ts — in-memory layer (fast, non-persistent), likely sits in front of an optional on-disk persisted cache, need to decide if disk persistence is in scope for v1 or a later phase.
  4. Add a CACHE_FORMAT_VERSION-style constant so future cache shape changes don’t silently return corrupt results to old cache format.
  5. Zero code written yet — this entry is the research/design summary only.

2026-08-08 — UPDATE: Graph LOD rendering — implemented

Status: Done Both steps of the LOD plan are done and visually verified in-browser by the user: step 1 (icon gating below zoomScale 0.5) and step 2 (label gating, same threshold, plus always-show labels for high-importance nodes above zoomScale 1.5). Node radius scaling at low zoom (the optional 3rd idea mentioned in the original plan) was not implemented — current LOD (icon+label gating) was sufficient. Considered done for now; revisit radius scaling later only if a real repo shows it’s still needed.

2026-08-08 — Cache design: git-diff strategy needs getCommitHistory.ts first

Status: In Progress Follow-up to the earlier cache research entry: confirmed packages/git/getCommitHistory.ts is still an 8-line stub, and there’s no existing function anywhere that gets the current commit SHA or lists changed files via git diff. simple-git (the library cloneRepository.ts already uses) can do this easily, but the capability itself doesn’t exist yet in ARCLUX. This means the MetadataStrategy-style (git-diff based) cache invalidation approach isn’t a ‘just plug into packages/git’ thing as initially assumed — getCommitHistory.ts (or a new small function) needs to expose at least: current HEAD SHA, and a way to list files changed since a given SHA. Revised plan: implement that git capability first (or as part of the same PR), before or alongside CacheProvider.ts, rather than assuming it’s ready to consume.

2026-08-08 — Cache design: shallow clone conflicts with git-diff strategy

Status: In Progress Critical finding: cloneRepository.ts defaults to depth=1 (shallow clone, only the latest commit, no history). This directly conflicts with a MetadataStrategy-style (git-diff based) cache invalidation approach, which needs to diff against a PREVIOUS commit — with only 1 commit present locally, there’s nothing to diff against on a fresh clone. This doesn’t kill the git-diff approach, but changes what it can be used for: it would only work for INCREMENTAL re-analysis of a repo ARCLUX already has a deeper local copy of (e.g. via packages/watcher’s ongoing filesystem watch, which presumably keeps a persistent local clone across multiple analysis runs) — not for a fresh one-shot analyzeRepository() call, which is the common case today (dependency-cruiser assumes it’s running against the user’s own full local repo, a different situation from ARCLUX cloning someone else’s repo fresh each time). Revised understanding: ARCLUX’s cache is more likely to help in two different ways than dependency-cruiser’s single git-diff-driven design:
  1. Content-hash based caching of PARSE results (ContentStrategy-style, not MetadataStrategy) for repeat analysis of the same repo/branch within a short window — doesn’t need git history at all, just file content hashes, works fine with shallow clones.
  2. A git-diff strategy only makes sense later, once packages/watcher is doing persistent incremental watching (a repo checked out once, kept around, re-diffed on change) — not for the current one-shot clone-analyze-cleanup flow.
Practical next step: prioritize a content-hash based cache (ContentStrategy-style) for packages/cache’s first implementation, since it fits ARCLUX’s actual current clone behavior. Revisit git-diff based caching once/if packages/watcher’s persistent-checkout behavior is implemented — check packages/watcher’s actual design before assuming it keeps repos around long-term.

2026-08-08 — packages/cache: 3 of 5 files done

Status: In Progress fileCache.ts (content-hash based, per-ParsedFile), repositoryCache.ts (fingerprint-based, per-Repository), graphCache.ts (same fingerprint, per-DependencyGraph) all implemented and typechecked. Both repositoryCache.ts and graphCache.ts share the same fingerprint scheme (computeRepositoryFingerprint in repositoryCache.ts, derived from sorted FileInfo.hash) so they invalidate together. None wired into engine/pipeline.ts yet — that’s a separate step. Remaining: CacheProvider.ts (orchestrator) and memoryCache.ts (unclear if it’s a distinct generic layer or redundant with the three content-hash caches already built — needs more thought).

2026-08-09 — ARCHITECTURE_MAP.md added: explicit core/extension boundaries

Status: Done Added ARCHITECTURE_MAP.md defining which packages are CORE (engine, repository, shared — changes need a decisions.md entry first) vs EXTENSION POINTS (parser, detectors, rules, cache — safe to add to without discussion) vs FOUNDATION-NOT-WIRED (watcher, incremental). Also defines where AI/intelligence-layer work (semantic search, RAG, embeddings, agent tooling) should live: a new top-level package consuming ARCLUX’s structural outputs, not woven into core packages. Motivated by onboarding ManSio (5th collaborator), whose own project (mscodebase-intelligence) is a much more elaborate codebase-intelligence system — graph RAG, agentic search, embeddings, LSP bridge, 1000+ tests. Real risk identified: a skilled collaborator coming from an overengineered project has a natural tendency to keep adding intelligence layers to whatever codebase they touch, which could slowly pull ARCLUX away from its current strength (disciplined scope, verified against real repos) toward matching that complexity. This isn’t a judgment on ManSio — his first PR (detectAmbiguousSymbolResolution.ts) was excellent, well-scoped, and followed existing patterns exactly. The boundary is proactive, not reactive to any actual problem yet.

2026-08-10 — Vision: TUI (terminal UI) as a third consumer of engine/

Status: Not Started Idea discussed, not started: ARCLUX’s engine/ (analyzeRepository()) already returns pure data (DependencyGraph, Repository) decoupled from any UI — CLI and web dashboard are both just consumers of that same output. A TUI (terminal UI, think lazygit/htop-style keyboard-driven interface) would be a natural third consumer: same engine/, same data, different rendering layer. Candidate library: ink (React for terminal) or blessed, not researched yet. This is long-term vision, not scoped work — no research done, no library chosen, no timeline. Revisit once core detector/parser coverage feels solid enough to justify a new surface.

2026-08-10 — External review from ManSio (MSCodeBase) — 4 findings to verify

Status: Not Started Collaborator ManSio reviewed parsePython.ts + scanFiles.ts, found 4 potential issues, NONE verified yet by us: (1) relative imports with leading dots ‘from ..utils import X’ may not resolve correctly if tree-sitter strips the dots before resolvePath.ts sees it — highest priority, likely to produce wrong edges on real repos. (2) parsePython.ts has zero test files, unlike Go/Rust parsers. (3) scanFiles.ts has catch{} blocks that silently drop unreadable files with no warning — same class as the wasm silent-failure gotcha. (4) wasmPath in parsePython.ts is hardcoded to a pnpm-specific node_modules path (.pnpm/tree-sitter-wasms@version/…) — will silently fail under npm/yarn installs. Next session: verify each against parsePython.ts/scanFiles.ts directly before fixing anything, per usual verification standard (cat/grep the actual code, don’t just trust the review).

2026-08-11 — Cytoscape.js researched as UI/UX reference for graph rendering

Status: Done Cloned cytoscape.js (open source, unlike Obsidian which was considered but is closed-source) to research graph visualization patterns. Found src/extensions/renderer/canvas/layered-texture-cache.mjs — a sophisticated multi-layer texture caching system with zoom-tier caching, per-frame render budgets (deqCost/deqAvgCost), and priority-queue-based texture refresh. Too complex/different (Canvas-based) to adopt wholesale into ARCLUX’s SVG renderer, but extracted one directly-applicable principle: don’t re-render elements whose own state hasn’t changed. Applied as GraphNode.tsx’s React.memo wrap. Full layered-caching-style system remains a possible future direction if SVG proves insufficient at larger scale (see the earlier canvas-vs-SVG discussion in this file’s history), not pursued now.

2026-08-11 — ARCLUX stays structural-truth engine; MSCodeBase-style intelligence becomes an optional consumer layer, not a merge

Status: Not Started Source: GPT roadmap discussion after reviewing ARCLUX architecture map + file sizes, refined further after ManSio suggested keeping the two projects separate. Core decision: do not try to turn ARCLUX into MSCodeBase. ARCLUX stays parse -> index -> graph -> impact -> detect, verifiable and AI-free. Any semantic/RAG/embedding layer becomes packages/intelligence/, sitting ON TOP of Repository + DependencyGraph as a consumer, never inside core. Flow: Repository and DependencyGraph feed packages/intelligence, which then branches into semantic search, context builder, and optional RAG — core stays the single source of truth underneath all three. Phased roadmap agreed: Phase 0 reliability (python edges — already fixed this session, wasmPath pnpm-only portability — still open, silent scan failures in scanFiles.ts — still open, parser test coverage), Phase 1 structural search (graph-aware search: fuzzyScore.ts + buildImportGraph/ExportGraph/CallGraph already exist, extend GlobalSearch to return structural context — imports/exports/consumers/routes — not just filename matches), Phase 2 architecture intelligence (turn the 18 existing detectors into a scored health view — structural integrity / dependency hygiene / layer consistency percentages derived from real detector output, not AI-generated), Phase 3 optional intelligence (packages/intelligence/ — embeddings, semantic search, RAG, agent-facing tools — explicitly deprioritized, not started until Phase 0-2 are solid), Phase 4 external integrations (LSP bridge, MCP/agent interface — deferred, risk of blurring “who is the source of truth” if added too early).

2026-08-11 — LAB 1/2/3 — diff, verify, and analyzeLocal/pipeline merge (all on feat/diff-lab1-mvp, NOT pushed yet)

Context: after the ARIES→ARCLUX rename, the temptation was to treat the original ARIES blueprint (11 parsers, 9-layer event engine, full web UI) as a from-scratch spec — a “1 year project” framing. Decision made instead: ARCLUX already has ~80% of the needed machinery built. Cut scope into 3 small LABs that prove the existing pipeline works, rather than building new abstractions speculatively. See roadmap.md’s “Core Principle” — this is that principle applied to process, not just architecture.

LAB 1 — arclux diff <refA> <refB> [repoPath]

New files: packages/diff/types.ts, gitDiff.ts, architecturalDiff.ts, apps/cli/diff.ts. Registered in apps/cli/index.ts. Honest scope limit, documented in architecturalDiff.ts’s own header: this does NOT build two separate dependency graphs (one at refA, one at refB) and diff them — that would require checking out each ref into a clean state and running the full pipeline twice, not built. What it DOES do: get changed-files list from git diff --name-status (cheap), then run existing traceConsumers against the CURRENT working tree for each changed file that still exists. Answers “what’s affected by files that changed” using today’s graph, not “did the graph itself differ.” Upgrading to true dual-graph comparison is a separate, larger task. Verified working: tested against ARCLUX’s own repo, HEAD~5 HEAD correctly traced GraphFocusView.tsx/GraphProvider.tsx changes to 9 consumer files (GraphCanvas, GraphViewport, etc).

LAB 2 — arclux verify [path]

New file: apps/cli/verify.ts. Registered in apps/cli/index.ts. Combines the same 10 detectors doctor.ts runs with packages/rules/ RuleEngine.ts’s runRules(), into one PASS/FAIL verdict (exit code 0/1). Important finding, confirmed by direct inspection (not assumed): of 13 rule files across nextjs/react/nestjs/express/vite/electron, only packages/rules/nextjs/requirePage.ts (60 lines) is actually implemented. The other 12 are copyright-header-only stubs — 8 lines, zero export statement. verify.ts’s header comment documents this explicitly so a future session doesn’t assume they’re wired in. Rule/detectedFrameworks string matching confirmed working correctly (tested: frameworks checked: nextjs, react matched appliesToFramework: "nextjs" on requirePage without any mismatch). Still not built: severity policy is minimal — any detector finding OR any rule error fails the build; rule warnings are shown but don’t fail it. Not yet tuned against real usage.

LAB 3 — merge analyzeLocal.ts into pipeline.ts

This one wasn’t a new feature — it closed a duplication gap that analyzeLocal.ts’s own header comment had already flagged (“once [pipeline.ts] refactor lands, THIS FILE should be deleted”). Confirmed via direct read that no parallel-session refactor had actually landed before doing this (git log showed no such merge to main). Real bug found and fixed as a side effect of the merge: analyzeLocal.ts’s ensureParsersRegistered() only registered parseTs and parsePython — 2 of pipeline.ts’s 7 language parsers. Every CLI command (diff, verify, doctor, impact, graph, analyze, config) was silently never parsing JS/JSX/CommonJS/Go/Java files, only TS and Python, since analyzeLocal.ts was CLI’s only entry point. Post-merge, all CLI commands go through the same ensureParsersRegistered() as the web/API remote flow (all 7 parsers + 9 manifest parsers). Also removed: packages/engine/analyzeRepository.ts — was a copyright-header-only empty stub, confusingly named almost identically to the real analyzeRepository() function that lives in pipeline.ts. API change: analyzeRepository() in pipeline.ts now takes { repoUrl } OR { localPath } (throws if both or neither given). Remote-URL code path (analyzeRemoteRepository) is a direct extraction of the pre-existing clone→index→graph→cache→cleanup logic — unchanged behavior, just moved into its own function so the public analyzeRepository() can route to it or to the new analyzeLocalPath(). Web app / /api/analyze callers unaffected (still call with { repoUrl, branch }, same shape as before). No caching for local-path analysis — deliberate, not an oversight. repositoryCache.ts/graphCache.ts are keyed by repoUrl+branch, which doesn’t map to a bare local directory a developer is actively editing. Documented as a possible future gap if arclux commands feel slow on large local repos — not ruled out, just not built. Verified: tsc --noEmit clean (zero errors) after the merge touched 9 files (pipeline.ts + 7 CLI commands + 2 deletions). verify/diff/ doctor all re-tested working post-merge; detector counts shifted slightly (428→430 in apps/web) as expected from the additional files now in scope, not a regression.

Current state

All 3 LABs committed to feat/diff-lab1-mvp, 3 commits (43013a75, ccd1d5b8, 3926d61f). Not pushed to origin. Branch is local-only, awaiting review before any push/PR.

Open follow-ups, not done yet (don’t assume these are handled)

  • True dual-graph diff (LAB 1’s documented scope limit)
  • 12/13 empty rule stubs (LAB 2) — only requirePage is realDONE 2026-08-13: all 10 stubs implemented, 13 rules wired into verify.ts/contract.ts; only react/requirePropsTyping remains a documented deferral
  • Verify’s severity policy is untuned (LAB 2)
  • No cache for local-path analysis (LAB 3) — fine for now, revisit if slow on large repos

2026-08-11 — PLANNED (not yet built) — LAB 4/5/6: Stable Core Contract, Engine/API boundary, External consumers

Status: planning only, ZERO code written. This session (that built LAB 1/2/3 — see the entry above) ran near its context limit right as this was being scoped out, so it’s recorded here as a plan for the next session to execute with full context, rather than started and left half-finished. Same discipline as the earlier “graph node visual impact indicator” plan in this file — plan fully, execute in a dedicated pass. Do not skip straight to writing code from this plan. The instruction that came with it, verbatim in spirit: map the current system before adding anything new.
i.e. whoever picks this up should re-read pipeline.ts, graph/, impact/, detectors/, and the current CLI commands (including diff.ts and verify.ts from LAB 1/2) FIRST, confirm what’s really there the same way LAB 1-3 did (direct file reads, not assumptions from old docs), THEN design the boundary — not the reverse.

LAB 4 — Stable Core Contract

Goal: a single stable shape for AnalysisResult, Graph, Impact, Issue, Rule that CLI/API/future consumers all depend on, instead of each command reaching into packages/* internals directly (current state: e.g. verify.ts imports 10 individual detector functions + RuleEngine directly — works, but every consumer re-does this wiring).

LAB 5 — Engine/API boundary

Formalize that CLI (and eventually API, IDE, etc — see LAB 6) only talks to an Engine layer, never to packages/parser, packages/graph, packages/detectors etc directly. pipeline.ts’s analyzeRepository() is already close to this for the analyze step (post-LAB-3 merge) — LAB 5 is likely about extending that same pattern to diff/verify/detect, not introducing a new mechanism.

LAB 6 — External consumers

CLI, IDE, CI, SDK — multiple consumers of the same Engine boundary from LAB 5. Not started, no design yet beyond the name.

Ground rule carried over from LAB 1-3, still applies

  • Experiment on a branch (feat/* or similar), never touch main directly.
  • main stays exactly at origin/main until a reviewed PR merges — confirmed true as of this entry: LAB 1/2/3 sat on feat/diff-lab1-mvp, local-only, for the entire time they were being built, and were only pushed (as a branch, not to main) once the user explicitly decided to.
  • The workflow that worked for LAB 1-3 and should repeat for LAB 4-6: code → test → (optionally) benchmark → if it breaks, reset the branch and try again. Cheap to experiment because main is never at risk.
  • Confirm real file state before writing code (grep/cat, not memory of old docs) — this caught the rule-stub gap (LAB 2) and the 2-vs-7 parser bug (LAB 3). Assume more gaps like that exist elsewhere.

2026-08-12 — LAB 4 MVP built: runAllChecks() stable contract

Status: In Progress packages/engine/contract.ts added on feat/diff-lab1-mvp (still local/branch-only, not merged to main). Wraps the 10 detectors + requirePage rule verify.ts already runs into one runAllChecks(repository) call returning normalized Issue[] (source/checkId/severity/message) instead of consumers importing 10 detector functions individually. Hit one signature mismatch during build: runRules() needs a 3rd detectedFrameworks arg, pulled from repository.meta.detectedFrameworks. tsc clean after fix. Not yet wired into verify.ts itself (that’s LAB 5’s job, formalizing CLI -> Engine -> Core boundary) — this session only built the contract, not the migration.

2026-08-12 — LAB 4 — External repository validation + Python parser WASM fix

Status: Verified — pushed to feat/diff-lab1-mvp LAB 4 Stable Core Contract was validated against an external repository rather than only ARCLUX itself. The test target was Django, cloned locally at ~/django.

External repository test

Initial ARCLUX analysis of Django:
  • 3,039 modules indexed
  • 3,039 graph nodes
  • Initial graph contained only 3 edges
This exposed a real Python analysis problem: Django is predominantly Python, so a 3-edge graph for a 3,039-module repository was clearly suspicious.

Root cause investigation

Direct inspection confirmed:
  • parsePython exists and is registered by engine/pipeline.ts.
  • LanguageParser correctly exposes Python through .py.
  • resolvePath.ts already contains Python-specific resolution for:
    • __init__.py
    • sibling imports
    • explicit relative imports
    • dotted Python module paths
  • Direct invocation of parsePython initially returned zero imports with:
ENOENT: ... tree-sitter-wasms/.../tree-sitter-python.wasm The WASM file itself was confirmed to exist inside the pnpm store. require.resolve("tree-sitter-wasms/out/tree-sitter-python.wasm") also confirmed the correct runtime path.

Fix

packages/parser/python/parsePython.ts contained an incorrect WASM path calculation. The path was corrected to resolve the pnpm-installed tree-sitter-wasms@0.1.13 Python grammar from the actual ARCLUX working directory. No parser architecture was redesigned. The fix was isolated to the runtime WASM path.

Parser verification

Direct Python parser execution against: django/db/models/base.py successfully returned Python imports with no warnings. Examples observed included:
  • collections
  • functools
  • itertools
  • asgiref.sync
  • django
  • django.apps
  • django.conf
  • django.core
  • django.core.exceptions
  • django.db
  • django.db.models
  • django.db.models.constants
  • django.db.models.deletion
  • django.db.models.expressions
  • django.db.models.fetch_modes
  • django.db.models.fields.composite
  • django.db.models.fields.related

Full Django graph verification

After the fix:
  • 3,039 modules indexed
  • 3,039 graph nodes
  • 7,734 dependency edges
This is a major validation change from the previous 3-edge result and demonstrates that the Python parser and Python path-resolution machinery are actually participating in full-repository graph construction.

LAB 4 contract verification

runAllChecks(repository) was also executed against Django. Observed result:
  • modules: 3,039
  • issues: 11,452
  • errors: 11,452
  • warnings: 0
  • passed: false
Issue distribution observed:
  • unusedExports: 7,788
  • orphanFiles: 2,453
  • ambiguousSymbolResolution: 522
  • largeModules: 327
  • sharedModules: 225
  • circularDependency: 104
  • deadCode: 24
  • duplicateModules: 9
These results are recorded as detector output, not automatically interpreted as proof that Django itself is defective. The purpose of this test was to validate that ARCLUX can process a large external Python repository and produce normalized analysis/check output.

Important engineering finding

The first failed Django analysis was not treated as a successful test. The unusually low graph edge count triggered investigation. The workflow was:
  1. Analyze external repository.
  2. Detect suspicious result.
  3. Inspect parser registration.
  4. Inspect Python parser.
  5. Test parser directly on a real Django file.
  6. Trace failure to missing WASM path.
  7. Confirm WASM package actually exists.
  8. Fix only the path resolution.
  9. Re-run the full repository analysis.
  10. Confirm graph increased from 3 edges to 7,734 edges.
  11. Run the stable check contract against the external repository.
This confirms the LAB process is capable of finding real integration bugs that would not necessarily appear from TypeScript compilation alone.

Commit

5e3748a7 fix: resolve Python tree-sitter wasm path Branch: feat/diff-lab1-mvp Remote branch was confirmed synchronized with the local HEAD after push.

Current conclusion

LAB 4 is not merely a type/interface exercise. The stable Engine contract was exercised against a real, large, external Python repository and the test uncovered and fixed an actual runtime integration bug in the existing Python parser.

2026-08-13 — Platform layer scaffold added

docs/log-today-progress-v2
[STATUS UPDATE, 2026-08-13]: this plan is now implemented. See “UPDATE: Platform layer scaffold added — implemented — implemented” below.
[STATUS UPDATE, 2026-08-13]: this plan is now implemented. See “UPDATE: Platform layer scaffold added — implemented — implemented” below.
[STATUS UPDATE, 2026-08-13]: this plan is now implemented. See “UPDATE: Platform layer scaffold added — implemented” below.
ARCLUX.main Status: Not Started Added scaffold-only folder structure for a new additive platform layer (runtime, services, scheduler, environment, workspace, terminal, storage, networking, security, system, diagnostics, editor, language, orchestration) plus matching apps/cli commands and apps/web API routes. Files are Apache-header-only stubs, no logic yet. Per ARCHITECTURE_MAP.md, this consumes existing core (engine/graph/impact/parser) rather than duplicating it — implementation to follow incrementally. docs/log-today-progress-v2

2026-08-13 — UPDATE: Platform layer scaffold added — implemented — implemented

Status: Done Rencana awal baru sebagian ke-scaffold: package runtime, services, scheduler, environment, workspace, terminal, storage, networking, security, system, diagnostics, editor, language, orchestration sudah ada, tapi ketinggalan packages/kernel, packages/semantic-diff, packages/notifications, packages/package-manager, dan CLI command health.ts + package.ts. Semua sudah ditambahkan (stub-only). Juga ditambahkan docs-site/map/map-packages-platform.mdx section Blueprint Integration yang memetakan alur editor dan semantic-diff pipeline ke file platform layer beserta dependency ke engine yang sudah ada. Masih open: belum ada logic diisi, murni struktur file + dokumentasi peta dependency.

2026-08-13 — UPDATE: Platform layer scaffold added — implemented — implemented

Status: Done Rencana awal baru sebagian ke-scaffold: package runtime, services, scheduler, environment, workspace, terminal, storage, networking, security, system, diagnostics, editor, language, orchestration sudah ada, tapi ketinggalan packages/kernel, packages/semantic-diff, packages/notifications, packages/package-manager, dan CLI command health.ts + package.ts. Semua sudah ditambahkan (stub-only). Juga ditambahkan docs-site/map/map-packages-platform.mdx section Blueprint Integration yang memetakan alur editor dan semantic-diff pipeline ke file platform layer beserta dependency ke engine yang sudah ada. Masih open: belum ada logic diisi, murni struktur file + dokumentasi peta dependency.

2026-08-13 — UPDATE: Platform layer scaffold added — implemented

Rencana awal (lihat entry “Platform layer scaffold added” di atas) baru sebagian ke-scaffold saat pertama kali dibuat: package runtime, services, scheduler, environment, workspace, terminal, storage, networking, security, system, diagnostics, editor, language, orchestration sudah ada, tapi ketinggalan: packages/kernel/ (Kernel, ProcessTable, SignalBus, ServiceRegistry, introspection), packages/semantic-diff/ (SemanticDiff, SymbolDiff, AstDiff, DependencyDiff, DiffRenderer), packages/notifications/ (NotificationManager, Notification, NotificationChannel), packages/package-manager/ (PackageManager, PackageManifest, PackageResolver, PackageState), dan CLI command health.ts + package.ts. Semua sudah ditambahkan (stub-only, sama seperti scaffold awal — Apache header + comment placeholder, belum ada logic). Juga ditambahkan docs-site/map/map-packages-platform.mdx section “Blueprint Integration” yang memetakan setiap tahap alur editor (developer ketik → incremental analysis → diagnostics → impact → notification) dan semantic-diff pipeline (text→symbol→AST→dependency→impact→ architectural) ke file platform layer yang sesuai, plus dependency-nya ke packages/engine/, packages/parser/, packages/diff/, packages/impact/ yang sudah ada. Masih open: belum ada satupun logic diisi, ini murni struktur file + dokumentasi peta dependency. ARCLUX.main