Skip to main content

ARCLUX Progress — Core (pipeline, parser, indexer, graph, impact, incremental)

See PROGRES.md for the index. Split by topic from the original PROGRES-status.md.

2026-08-03 — ✅ DONE — pipeline & core

Single entry point: packages/engine/pipeline.ts → analyzeRepository({ repoUrl }). Don’t call individual steps from outside engine/.
  • packages/git/cloneRepository.ts, cleanupRepository.ts, readGitignore.ts
  • packages/parser/core/* (ParserInterface, ParserRegistry, scanFiles, LanguageDetector)
  • packages/parser/typescript/parseTs.ts (194 lines) — note: parseTsx.ts and parseTsConfig.ts are still separate empty stubs, TSX is likely already handled inside parseTs.ts itself — check before assuming TSX can’t be parsed at all.
  • packages/parser/python/* — parsePython.ts, highlightPython.ts, pythonHighlightQuery.ts (see gotcha details below)
  • packages/indexer/buildIndex.ts, resolveAliases.ts
  • packages/graph/buildDependencyGraph.ts, buildFolderGraph.ts, resolvePath.ts, serializeGraph.ts
  • packages/repository/* (Repository, Module, File, Folder, Node, Edge, Dependency, Graph)
  • packages/engine/detectRepositoryMeta.ts
  • packages/rules/RuleEngine.ts + rules/nextjs/requirePage.ts
  • packages/shared/* (types.ts, errors.ts, hash.ts, paths.ts, constants.ts, logger.ts, utils.ts)
  • packages/search/fuzzyScore.ts — adapted from cmdk (see NOTICE)

2026-08-03 — ⚠️ PARTIAL / NEEDS VERIFICATION

Python parsing & syntax highlighting — works (parsePython.ts 203 lines, highlightPython.ts 142 lines, pythonHighlightQuery.ts 151 lines copied verbatim from tree-sitter-python, MIT — attribution in NOTICE), but:
  • Never tested end-to-end through the real ParserRegistry (only via a separate experimental script)
  • Not yet visually verified in the browser (the syntax-highlight colors have never actually been confirmed to attach to the right characters)
  • FileDetails.tsx, which uses this, is not yet wired into any page
web-tree-sitter gotcha (MUST read before adding another tree-sitter-based language parser):
  • Must be exactly 0.25.0, not 0.26.x — newer versions fail to load the WASM grammar (getDylinkMetadata ABI mismatch)
  • Must be called via require() (through createRequire(import.meta.url)), not a pure import — otherwise “Dynamic require of fs/promises is not supported” error
  • No .d.ts, can’t be augmented via declare module (TS2665 error) — solved with a custom type (TSNode interface), required as any
  • Per-language grammar .wasm files live in node_modules/tree-sitter-wasms/out/, NOT from the ~/research/tree-sitter clone (that’s just a reference for concepts)
  • Parser instance MUST be a singleton (getPythonRuntime() promise-cache pattern) — reloading the WASM per parse() call would be very slow
  • Query constructor differs between 2 API versions (old language.query() vs new new Query()) — highlightPython.ts already handles the fallback
  • Python has no export — every top-level function_definition/ class_definition is treated as an “export” (heuristic, doesn’t yet read __all__)
packages/detectors/detectUnusedExports.ts — see limitations in the detectors section above.

External references already used

Full attribution is in NOTICE (root). Summary: Repos in ~/research used only to read patterns/architecture, not copied from: git, language-server-protocol, llvm-project, sqlite, tree-sitter, nx, clack, shadcn-table, drizzle-orm, forever, pm2 (check which ones are actually cloned before assuming).

2026-08-03 — Update — packages/incremental (new foundation, not wired in yet)

packages/incremental/ — Cell (input), Query (memoized function with dependency tracking + early cutoff), Database (revision coordination). Principle adapted from salsa-rs/salsa (dual MIT/Apache-2.0) — NOT a port (Rust proc-macro vs runtime tracking in TS), re-implemented from scratch. Full attribution in the Database.ts comment. Verified via a runnable demo (packages/incremental/demo.ts, run with npx tsx packages/incremental/demo.ts), not just tsc --noEmit:
  • Memoization: repeated calls with no change = 0 recomputation
  • Dependency tracking: only Cells that are actually read trigger invalidation
  • Early cutoff: Cell.set() with an identical value (Object.is) is a no-op, doesn’t bump the revision
  • Cycle detection: a query re-entering the same key while still computing → throws, instead of an infinite loop
Known limitations (documented in the Query.ts comment):
  • Early cutoff only works for reference equality (Object.is) — a new object with identical contents is still considered “changed”. Deep- equality cutoff would need a custom comparator, not yet implemented.
  • Dependency tracking during cache-hit revalidation is over-approximate (query C calling A which calls B ends up depending on both A AND B directly, rather than just A with B implied transitively) — safe (no missed invalidations) but not maximally minimal.
  • Cycles throw, there’s no fixed-point resolution for genuinely recursive queries — that’s treated as a caller bug, not a supported pattern.
NOT wired into any pipeline yet — buildIndex.ts, pipeline.ts, the detectors, everything still runs the old way (full re-scan). This is a standalone foundation that needs separate integration as a bigger next step, not something that’s automatically used just because this file exists.

2026-08-04 — CORRECTION — packages/impact/* turned out to be ALREADY DONE (8/8)

Previously recorded as priority #1 at 0% total. It turned out to be fully implemented in commit 8b69831a (before this session even started), just never cross-checked against PROGRES.md. Verified via direct cat (not just wc -l):
  • traceImports.ts (33 lines), traceExports.ts (46 lines) — identifier- level tracing, consistent with the same pattern as detectUnusedExports.ts (namespace/default/named import handling)
  • calculateAffectedFiles.ts (66 lines) — base function
  • calculateAffectedModules.ts, calculateAffectedComponents.ts, calculateAffectedRoutes.ts — all compose on top of calculateAffectedFiles, not duplicated logic. Routes even correctly converts a file path to a Next.js route path (stripping route groups (...))
  • buildImpactTree.ts (38 lines) — has a cycle guard (ancestors: Set) + maxDepth, important because a repo can have a real circular import (see playground/python-demo/cyclic_a.py ↔ cyclic_b.py)
  • traceDependencies.ts, traceConsumers.ts — not manually cat’d yet, assumed done based on the consistent pattern of the other 6 files, but re-verify before actually relying on them
Additional lesson: this incident is EXACTLY the same as the earlier components/layout/* incident — actual progress was further ahead than recorded because parallel work sessions weren’t synced to PROGRES.md. Redundant verification (cat, not assumption from file name/old PROGRES.md) remains mandatory before starting work in any area. Action item: apps/cli/impact.ts is currently WRONG — it says “not yet implemented” even though the functionality already exists. Needs to be fixed so it actually calls buildImpactTree/calculateAffectedFiles etc.

2026-08-04 — Update — JavaScript parser: parseJs/parseJsx/parseCommonJs written and registered

packages/parser/javascript/extractJs.ts (shared) + parseJs.ts + parseJsx.ts
  • parseCommonJs.ts implemented and registered in packages/engine/pipeline.ts. Reuses TypeScript Compiler API with ScriptKind.JS/JSX (NOT reusing parseTs.ts’s extractors directly — those detect TS-only syntax like “import type” that plain JS can never have). Detects ES import/export, dynamic import(), require(), and per-property CommonJS (module.exports.x = / exports.x =).
KNOWN GAP, NOT YET FIXED: whole-object exports (module.exports = { a, b }) are NOT detected — only per-property assignment is. This under-reports exports for a lot of real-world CommonJS. Confirmed common via nodejs/cjs-module-lexer’s own test fixtures (~/research/cjs-module-lexer/test/_unit.js) during research for this work. Follow-up PR needed for extractWholeObjectExports() (shorthand props, renamed props, string-literal keys, getter exports — spread props and computed keys can stay unsupported/silently skipped for now). parseCommonJs.ts is currently behavior-identical to parseJs.ts (kept separate on purpose — see its own file comment — because the whole-object-exports follow-up is scoped to CommonJS specifically, not plain JS). NOT YET DONE: no playground fixture, no end-to-end verification via scripts/testPlayground.ts or CLI doctor — only tsc —noEmit passed so far. Next session should build playground/commonjs-demo/ using patterns from cjs-module-lexer’s test file before trusting this beyond typecheck.

2026-08-05 — Update — Go & Java parsers written and verified (parseGo.ts, parseJava.ts)

packages/parser/go/parseGo.ts and packages/parser/java/parseJava.ts implemented (regex/line-based, not tree-sitter — no grammar wired up for either language yet, unlike Python). Registered in both packages/engine/pipeline.ts AND scripts/testPlayground.ts (the latter had been silently only registering parseTs+parsePython — same class of bug as files getting skipped in buildIndex.ts’s “no parser registered, skip silently” path, worth remembering next time a new parser is added anywhere). Verified via scripts/testPlayground.ts against playground/go-demo and playground/java-demo (not just tsc —noEmit):
  • 6/6 modules indexed in both fixtures (previously 0, confirming parsers are now actually registered and running)
  • Export extraction confirmed correct: Go’s uppercase-first-letter convention (HelperA, User, Product, Slugify, UnusedHelper) and Java’s public modifier convention (class/method/field level) both extract the expected names, matching what each fixture file was written to contain — including the deliberately-unused UnusedHelper/unusedHelper in each
EXPECTED LIMITATION, confirmed empirically, NOT a bug: both fixtures show 0 graph edges despite real cross-file calls existing (cyclic_a.go calls HelperB in cyclic_b.go, Main.java calls Service/Models/Utils) — this is because Go and Java don’t require any import statement between files in the same package/directory, so there’s nothing for resolvePath.ts to resolve. This makes every file/export in both fixtures show up as a false positive in detectOrphanFiles/detectUnusedExports/ detectUnusedFiles, same root cause class as the already-documented Python/main.py false positive from resolveRoutes.ts being empty. A “same-package implicit dependency” resolution pass would be needed to fix this for Go/Java specifically — not yet built, not scoped. NOT yet tested: real-world Go/Java repos beyond the playground fixture (multi-package Go with actual cross-package imports, Java with actual import demo.other.Thing; statements) — only the same-package-only fixture has been verified so far.

2026-08-05 — Update — Manifest parsers built (parseGoMod, parseCargoToml, parsePackageJson, parseComposer, parseGemfile, parseGradle/parsePom, parseCsproj) + ManifestParser interface

New packages/parser/core/ManifestParserInterface.ts (ManifestDependency: name/versionRange/kind runtime|dev; ManifestParser: filename + sync parse()). Distinct from LanguageParser/ParserRegistry — manifests are single well-known files read directly, not scanned. Generic format primitives added: config/parseJson.ts, config/parseToml.ts (parseTomlSections, NOT spec-complete TOML), config/parseYaml.ts (parseFlatYaml, unused so far). Verified against REAL manifests from public repos (gin, tokio, laravel, rails, spring-petclinic — downloaded to ~/manifest-samples, outside the repo) via scripts/testManifests.ts, not hand-written fixtures:
  • parseGoMod: 35/35 deps correct (gin’s go.mod)
  • parseCargoToml: found and FIXED a real bug — first version only handled flat [dependencies]/[dev-dependencies], missed tokio’s many [target.‘cfg(…)‘.dependencies] conditional sections and single-dep-per-section form ([target.‘cfg(windows)‘.dependencies.windows-sys]). Fixed via classifySection() suffix matching + SINGLE_DEP_SECTION_PATTERN. 13 -> 36 deps after fix, matches tokio’s Cargo.toml.
  • parseComposer: 8/8 correct, php/ext-/lib- platform entries correctly filtered
  • parseGemfile: 82 deps found, all reported “runtime” — KNOWN LIMITATION, doesn’t read group :test do...end blocks, so gems only ever declared inside a group (rubocop, mdl, sdoc group etc in rails’ real Gemfile) are mis-classified as runtime. Not fixed, documented in file comment.
  • parsePom (Maven): 30 found but 2 are WRONG — regex matches every <dependency> in the file including ones nested inside <plugin><dependencies> (checkstyle plugin’s own deps), which aren’t real project dependencies. NOT FIXED — would need real XML tree parsing to scope to the top-level <dependencies> block only, not a regex. Known false-positive, documented in file comment.
  • parseGradle (Groovy DSL) — NOT yet tested against a real build.gradle, only pom.xml side was verified.
Also fixed while building this: packages/graph/buildFolderGraph.ts was failing tsc with “Cannot find module ‘d3-hierarchy’” — dependency was used but never installed. Fixed with pnpm add d3-hierarchy -w + pnpm add -D @types/d3-hierarchy -w (-w needed because packages/* isn’t a pnpm workspace member, only apps/* is — deps go to root package.json). STILL NOT DONE: nothing calls these manifest parsers from detectRepositoryMeta.ts or anywhere else in the pipeline yet — they exist and are verified standalone (via testManifests.ts) but aren’t wired into analyzeRepository()‘s framework/dependency detection. detectRepositoryMeta.ts’s own readDependencyNames() (package.json only, flat Set<string>) still runs separately and hasn’t been merged with this new ManifestDependency-based system — that’s a follow-up, not done here. Gotcha hit while building this: a cat > file << EOF heredoc run while cd’d into ~/manifest-samples instead of ~/arclux silently created packages/parser/rust/parseCargoToml.ts under ~/manifest-samples/packages/… instead of overwriting the real file — the real ~/arclux file kept its old (buggy, pre-fix) content even though the terminal showed no error. Caught only because testManifests.ts’s output (13 deps) didn’t match the expected fix. Lesson: always pwd before a cat > path << EOF if you’ve cd’d anywhere else in the same session — a wrong-directory heredoc fails silently, it doesn’t error.

2026-08-05 — Update - same-scope implicit dependencies implemented (Go/Java)

Followed up on the decision recorded earlier (same-package resolution: one generic pass, not per-language fixes). Implemented in full this session:
  • packages/shared/types.ts: added ParsedFile.scopeId (optional) and ModuleInfo.implicitDependencies (required, kept SEPARATE from imports[] since it’s a regex heuristic that can false-positive on comments/strings, not a certain import statement).
  • parseGo.ts and parseJava.ts: both now set scopeId = the file’s directory (posix.dirname). Confirmed via javaparser’s JavaParserTypeSolver.java javadoc that Java package MUST match directory by language convention, so Java can reuse the exact same directory-based scopeId logic as Go - no need to parse the package statement separately.
  • packages/indexer/resolveSameScopeDependencies.ts (new): groups files by scopeId, does a regex whole-word scan of each file’s content against sibling files’ exported names. Deliberately NOT AST-aware - documented limitation, not a bug to silently fix later without redesigning the approach.
  • buildIndex.ts: wired in as a new pass 2 (content is now kept around in contentByPath specifically to feed this pass).
STILL NOT DONE: implicitDependencies is populated but NOT yet consumed by buildDependencyGraph.ts, buildImportGraph.ts, or buildExportGraph.ts. This means the root cause of the near-empty Kubernetes/Spring Boot graphs is now understood and the data exists, but the graph VIEW won’t actually show the extra edges until a follow-up wires this in. That’s a good next task for whoever picks this up. Tested against the real playground/go-demo fixtures (main.go -> models.go + service.go, service.go -> models.go + utils.go), 6 tests, including edge cases (no self-dependency despite service.go containing its own function name in the signature, lone file with no scope siblings). All merged via PR #67. Gotcha hit and fixed this session: /tmp doesn’t exist in Termux (already noted elsewhere in gotchas.md, but re-confirmed here) - a Python patch script heredoc failed with “No such file or directory” when targeting /tmp/patch_types2.py. Fix: write the script into the repo directory itself (~/arclux/patch_types2.py) and delete it after running, instead of using /tmp.

2026-08-06 — Update - documented 3 previously-mysterious empty stub files

Investigated 3 files that were 0-line-except-license-header stubs with no obvious purpose from surrounding code, and documented each with an in-file comment explaining its actual status, so future sessions don’t re-investigate the same files from scratch:
  • packages/parser/typescript/parseTsConfig.ts — INTENTIONALLY EMPTY permanently. Superseded by resolveAliases.ts, which already handles tsconfig.json/jsconfig.json parsing for path alias resolution. Do not implement a config parser here.
  • packages/parser/core/parseImports.ts — intentionally empty for now, not confirmed dead. Every language parser (parseGo.ts, parseJava.ts, extractJs.ts, parsePython.ts, parseTs.ts) implements its own extractImports() independently since each language’s import syntax differs too much for an obvious shared generic version. Could be revisited if a genuinely shared pattern emerges later.
  • packages/parser/php/parsePhp.ts — deliberately DEFERRED (not abandoned, not intentionally-empty-forever). Waiting on issue #53’s parsePhpRoutes.ts (assigned to Alitindrawan24) to land first, so this general-purpose PHP parser follows whatever regex/extraction convention emerges from that PR instead of establishing a second, inconsistent PHP-parsing style in parallel. Merged via PR #70.
Also confirmed: not every empty-looking file in this repo is unfinished work. Before investigating an empty stub, cat the whole file first (not just wc -l) — some are marked intentionally empty in a comment, which immediately answers the question.
[STATUS UPDATE, 2026-08-14]: the resolver family IS now consumed — getEntryModuleIds() is used by detectUnusedExports.ts / detectOrphanFiles.ts (issue #4, entry-point filtering). The resolver results are still not attached to ModuleInfo/Repository by buildIndex.ts (pass 5 never landed) — detectors call them directly. See “2026-08-14 — Issues #4/#7/#9/#50” below.

2026-08-06 — Update — route/export/component/hook/provider resolvers + Explorer panel

packages/indexer/resolveRoutes.ts: detects Next.js App Router entry files (page/layout/route/loading/error/not-found/template/default/ global-error) under app/. Returns moduleId + derived routePath (route groups in parentheses stripped). getEntryModuleIds() helper exposed for detectors to check entryModuleIds.has(mod.id). Not yet wired into detectUnusedExports.ts/detectOrphanFiles.ts — those still have the entry-file false-positive caveat noted earlier in this doc until someone adds the 2-line skip check. packages/indexer/resolveExports.ts: resolves re-export chains beyond the single hop ModuleInfo.resolvedReExports already covers (documented limitation in types.ts’s own doc comment). Walks the chain per export name until it hits a non-re-export origin, with cycle detection (stops at last resolvable hop if a cycle is found). Inherits the same aliased-re-export limitation as resolvedReExports itself (export &#123; foo as bar &#125; — name tracking breaks across hops since RawExport only stores the final name) — documented in-file, not fixed here, needs parser layer changes. packages/indexer/resolveComponents.ts, resolveHooks.ts, resolveProviders.ts: naming-convention heuristics, NOT AST-based — no parser currently extracts “is this a component/hook/provider” as structured data. Components = PascalCase export name in a .tsx/.jsx file. Hooks = export name matching ^use[A-Z0-9], any extension. Providers = export name ending in Provider. Each file documents this limitation explicitly and says to switch to AST-derived data if/when a parser adds that signal — do not assume these are ground truth. apps/web/components/explorer/Explorer.tsx (new) + DependencyList.tsx (new): tabbed panel (File / Dependencies / Impact) wrapping the existing FileDetails.tsx and ImpactSummary.tsx plus the new DependencyList.tsx. Confirmed via grep before writing: zero existing consumers of Explorer, so the prop shape (repoUrl, moduleId, branch?, onClose?) is a new design, not an established contract — reconsider if wiring it into SplitPane.tsx/the graph route reveals a different shape is needed. DependencyList.tsx fetches /api/graph and filters edges client-side by moduleId — same pattern as GraphProvider.tsx’s importCounts memo. Query param shape for /api/graph (?repoUrl=&branch=) was assumed from /api/file//api/impact’s pattern, not confirmed against the actual route handler — check app/api/graph/route.ts before trusting this without testing. vendor-ui/shadcn/scroll-area.tsx (new): was missing, blocking file-tree.tsx’s typecheck alongside a missing @radix-ui/react-accordion dependency. Standard shadcn Radix wrapper, no ARCLUX-specific logic. Incidental fix: npm install was failing repo-wide with Cannot read properties of null (reading 'matches') — root cause not fully diagnosed, but rm -rf node_modules package-lock.json && npm cache clean --force && npm install resolved it (66 packages, 0 vulnerabilities). package-lock.json regenerated — check whether it’s gitignored before committing it, it showed as untracked rather than modified, which usually means it wasn’t tracked before. Verification: cd apps/web && npx tsc --noEmit clean across all of the above. NOT visually verified in browser — same standard gap noted elsewhere in this doc (typecheck alone isn’t sufficient evidence). Still not done:
  • Resolver family (routes/exports/components/hooks/providers) not called from buildIndex.ts — no pass 5 yet, results aren’t attached to ModuleInfo or Repository anywhere
  • Explorer.tsx not mounted on any page/route
  • detectUnusedExports.ts/detectOrphanFiles.ts not updated to consult getEntryModuleIds() yet, so the entry-file false-positive caveat still applies

2026-08-08 — ManifestRegistry wired into pipeline.ts

Status: Done 8 manifest parsers (packageJson, goMod, cargoToml, gemfile, composer, csproj, gradle, pom, requirements) now registered and consumed via manifestRegistry.detectDependencies() in analyzeRepository(). New ‘dependencies’ field on AnalyzeRepositoryResult. Fixes bug logged earlier today: manifest parsing was implemented but 100% dead code, never called by anything.

2026-08-08 — parseRequirements.ts implemented

Status: Done Python requirements.txt manifest parser added, following parseGemfile.ts’s line-based regex pattern. Handles name, one version specifier, comments, environment markers, option flags.

2026-08-08 — packages/cache: fileCache, repositoryCache, graphCache built but NOT wired

Status: Done 3 of 5 cache files implemented (fileCache.ts content-hash based per-file, repositoryCache.ts + graphCache.ts sharing a repo-level fingerprint). None are called from engine/pipeline.ts yet — still standalone, unlike the manifest registry which is now wired in. Next session: wire these into buildIndex.ts (fileCache) and pipeline.ts (repositoryCache/graphCache), or decide this isn’t worth doing yet.

2026-08-08 — ManifestRegistry wired into pipeline.ts

Status: Done 8 manifest parsers (packageJson, goMod, cargoToml, gemfile, composer, csproj, gradle, pom, requirements) now registered and consumed via manifestRegistry.detectDependencies() in analyzeRepository(). New ‘dependencies’ field on AnalyzeRepositoryResult. Fixes bug logged earlier today: manifest parsing was implemented but 100% dead code, never called by anything.

2026-08-08 — parseRequirements.ts implemented

Status: Done Python requirements.txt manifest parser added, following parseGemfile.ts’s line-based regex pattern. Handles name, one version specifier, comments, environment markers, option flags.

2026-08-08 — packages/cache: fileCache, repositoryCache, graphCache built but NOT wired

Status: In Progress 3 of 5 cache files implemented (fileCache.ts content-hash based per-file, repositoryCache.ts + graphCache.ts sharing a repo-level fingerprint). None are called from engine/pipeline.ts yet — still standalone, unlike the manifest registry which is now wired in. Next session: wire these into buildIndex.ts (fileCache) and pipeline.ts (repositoryCache/graphCache), or decide this isn’t worth doing yet. docs/correct-cache-status

2026-08-09 — packages/cache status correction: 3/5 done and wired, 2/5 still empty

Status: In Progress Correction: a draft progress entry from another session claimed ‘no code written for packages/cache yet’ — that’s outdated. Actual status: fileCache.ts, repositoryCache.ts, graphCache.ts are all implemented AND wired into buildIndex.ts/pipeline.ts (confirmed via grep — getCachedRepository/getCachedGraph are actively called). CacheProvider.ts and memoryCache.ts are still 8-line stubs (confirmed via cat), unclear if still needed given the 3 content-hash caches already cover the main use cases.

2026-08-09 — resolveRoutes, resolveExports, resolveComponents/Hooks/Providers implemented

Status: Not Started Five new files in packages/indexer/. resolveRoutes.ts detects Next.js App Router entry files (page/layout/route/etc under app/), exposes getEntryModuleIds() for detectors to skip false positives. resolveExports.ts walks re-export chains beyond the single hop ModuleInfo.resolvedReExports covers, with cycle detection. resolveComponents.ts, resolveHooks.ts, resolveProviders.ts are naming-convention heuristics only (PascalCase/use*/*Provider), explicitly documented as such since no parser extracts this from AST yet. None of the five are wired into buildIndex.ts pipeline yet — results are not attached to ModuleInfo or Repository anywhere. tsc clean, not otherwise tested. main

2026-08-13 — Kernel diperbaiki + RuntimeManager wiring ke CLI

Status: In Progress Kernel.ts diperbaiki: import ProcessStatus yang salah diganti ke ProcessStatusValue. RuntimeManager.ts baru dibuat sebagai titik integrasi tunggal Kernel + ProcessManager. apps/cli/commands/run.ts sekarang memanggil RuntimeManager.startService() untuk service web sebagai proof-of-concept integrasi pertama. Belum ditest jalan end-to-end, baru lolos tsc —noEmit.

2026-08-13 — Kernel & ProcessManager diimplementasi pakai referensi PM2

Status: Done packages/kernel/ (ProcessTable, SignalBus, ServiceRegistry, Kernel) dan packages/runtime/ProcessManager.ts sekarang berisi logic asli, bukan stub. Dibangun dengan clone referensi PM2 (github.com/Unitech/pm2, lib/God.js dan lib/God/ForkMode.js) untuk pattern proses: spawn via child_process.spawn, tracking pid/status, capture stdout/stderr, IPC message forwarding, exit handling dengan auto-restart. Status naming (launching/online/stopping/stopped/errored) mengikuti konvensi PM2. Event bus pakai Node EventEmitter bawaan, bukan EventEmitter2 seperti PM2. Sengaja TIDAK diporting: cluster mode (lib/God/ClusterMode.js, belum dibutuhkan), log file persistence ke disk, PID file writing, uid/gid options. Scoped untuk service internal ARCLUX (web server, watcher, indexer).

2026-08-13 — Kernel & RuntimeManager selesai, integrasi ke CLI

Status: In Progress Kernel.ts, ProcessTable.ts, SignalBus.ts, ServiceRegistry.ts, ProcessManager.ts, ProcessSpec.ts, RuntimeManager.ts semua berisi logic asli berdasarkan referensi PM2 (God.js, ForkMode.js). apps/cli/commands/run.ts terhubung ke RuntimeManager sebagai integrasi pertama. Sempat ada insiden git reset —hard yang menghapus kerjaan tanpa sengaja, sudah dipulihkan penuh dan diverifikasi lolos tsc —noEmit. Belum ditest end-to-end (arclux run web belum pernah dijalankan beneran).

2026-08-13 — Add process persistence for ps command

Status: Done Kernel now persists ProcessEntry records to ~/.arclux/pids/*.json on register/update (packages/storage/SnapshotManager.ts, new). readLiveProcessRecords() live-checks each PID via process.kill(pid, 0) and self-deletes stale records instead of trusting on-disk state blindly — pattern read from forever’s getAllProcesses/getSockets (not copied, re-implemented for file-based instead of socket-based IPC). Kernel.shutdown() now also cleans up its own records. Wired apps/cli/commands/ps.ts to read persisted records directly (CLI runs as a separate process from whatever registered the process, so it can’t share Kernel memory) and registered it in apps/cli/index.ts. Added ProcSnapshot.snapshotFromEntries() so both live in-memory ProcessTable and cross-process disk records can produce the same ProcSnapshot shape.

2026-08-13 — Cross-process process visibility via file-based persistence

Status: Done Added packages/storage/SnapshotManager.ts: writes one JSON record per process to ~/.arclux/pids/<id>.json on register/update, reads live-check each PID via process.kill(pid, 0) and self-deletes stale records. Pattern read from forever’s getAllProcesses/getSockets (see NOTICE), re-implemented with plain files instead of socket IPC. Wired into Kernel.ts (registerProcess/updateProcessStatus/removeProcess/shutdown all persist), packages/kernel/introspection/ProcSnapshot.ts (added snapshotFromEntries for cross-process use), and apps/cli/commands/ps.ts (reads disk records directly, registered in apps/cli/index.ts).

2026-08-14 — Issue #50 follow-up done: TS-family call extraction (issue #316)

[STATUS UPDATE, 2026-08-14]: the “TS-parser call extraction is a follow-up” decision below is now IMPLEMENTED — see this entry.
Status: Done parseTs.ts now runs extractCalls() (bare-identifier call sites, same rule as extractCallsJs, kept as a separate function per the JS/TS convention) and populates ParsedFile.calls — buildIndex/buildCallGraph were already language-agnostic. Verified on the arclux repo itself: 0 → 220 call edges (e.g. apps/cli/analyze.ts → detectCircularDependency). TSX handled correctly: JSX elements and type-only imports produce no call sites. 5 new tests (parser/typescript.test.ts +3, graph-callgraph.test.ts +2). Test count: 196.

2026-08-14 — Issues #4/#7/#9/#50 — entry-point-aware detectors, call graph, search engine

Status: Done
  • #7 → #4 (entry-point awareness): resolveRoutes.ts was already implemented; its getEntryModuleIds() (plus detectEntryPoints) is now wired into detectUnusedExports.ts and detectOrphanFiles.ts — App Router files and the CLI entry are no longer false-flagged as unused/orphan. 8 new tests in tests/core-detectors.test.ts. doctor.ts wording updated.
  • #50 (call graph): extractCallsJs (bare-identifier calls, excludes obj.foo()/this.foo()/require()) wired into the 3 JS parsers; buildIndex resolves calls via named imports and backfills calledBy; buildCallGraph.ts emits weighted type: "call" edges mirroring buildImportGraph. RawCall/ResolvedCall/calls/calledBy added to shared/types.ts. 15 tests (tests/graph-callgraph.test.ts). TS-family call extraction landed same-day in the #316 entry above (parseTs populates calls; arclux self-repo: 0 → 220 call edges).
  • #9 (search engine): packages/search/* implemented (SearchIndex, SearchEngine, SearchFilters, plus plain-TS SearchProvider/SearchResults/ SearchKeyboard — packages stay framework-agnostic); /api/search rewritten to use the engine, response shape unchanged. 19 tests (tests/search.test.ts).

2026-08-14 — arclux work command: proves Change Pipeline end-to-end

Status: Done apps/cli/commands/work.ts added: arclux work <file> <newContentFile> reads current + replacement content, builds PatchSet -> ChangePlan -> executes via ChangeExecutor (writeTransactional, transactional/recoverable write). This is the first real caller of packages/change/, proving the pipeline works end-to-end without needing intent parsing (ContextResolver), which doesn’t exist yet. Registered in apps/cli/index.ts. Verified with tsc -p apps/cli/tsconfig.json (root tsc —noEmit picks up the wrong/Next.js-oriented tsconfig.json for CLI code — use -p apps/cli/tsconfig.json when checking apps/cli specifically).

2026-08-14 — packages/git history helpers implemented (last git stubs)

Status: Done checkoutBranch/getCommitHistory/getContributors implemented (all were 8-line stubs; the remaining backlog git items, alongside getBranches + detectDefaultBranch which landed with the branch switcher):
  • checkoutBranch(localPath, branch) — no-op if already on it; otherwise fetch origin + checkout -b branch origin/branch, falling back to plain checkout branch for local-only branches.
  • getCommitHistory(localPath, { maxCount?, branch?, path? }) — simple-git log → { hash, date, message, authorName, authorEmail }. NOTE: requires a full (non-shallow) clone — the pipeline defaults to depth 1, so history callers must clone with depth: undefined.
  • getContributors(localPath) — git shortlog -sne --no-merges → [{ name, email, commits }] sorted by count. 6 tests (tests/git-history.test.ts) against a real temp git repo (git init + commits); tsc 0, vitest 208/208 (202 + 6).

2026-08-14 — OWP-lens hardening: guard inventory, scanSummary, crash isolation

Status: Done Three fixes from an OWP-class review (eligible_seen / population rot / structural death framing — full decision in progres/decisions.md):
  • Guard inventory: tests/guard-inventory.test.ts — positive + clean fixtures for the 7 detectors that had only prose-documented manual verifications (deadCode, duplicateModules, entryPoints, indexFiles, largeModules, layerViolation, sharedModules). All 19 detectors now have committed negative controls; coverage matrix in tests/README.md.
  • eligible_seen: ScanSummary (filesScanned/filesParsed/ filesSkippedNoParser/skippedByExtension) populated in buildIndex pass 1, carried on Repository, surfaced in AnalyzeRepositoryResult and the analyze CLI. Verified: ARCLUX itself 546 scanned = 546 parsed (0 skipped); a mixed .ts/.go/.php/.rb fixture counts the 2 skipped languages explicitly.
  • Structural-death guard: safeRun in runDoctor wraps every detector (crash → DETECTOR CRASHED error finding, suite continues); runAllChecks in contract.ts isolated the same way. Tests: 224/224 (208 + 14 guard-inventory + 1 safeRun + 1 scanSummary). tsc 0.

2026-08-14 — notifications/ implemented + wired to daemon (issue #353)

Status: Done packages/notifications/ was 3 stub files; now real:
  • Notification.ts — channel-neutral shape (id, severity, message, at, source, filePath, line), normalized from DiagnosticFinding.
  • NotificationChannel.ts — NotificationChannel interface + reference ConsoleNotificationChannel implementation.
  • NotificationManager.ts — subscribes to kernel.signalBus daemon:diagnostics:updated, normalizes findings, fans out to registered channels; idempotent start/stop, broken channel can’t starve the rest (try/catch per channel).
Wired into ArcluxDaemon: new notificationChannels option registered onto daemon.notifications, subscribed in start() before the first analysis can emit, unsubscribed first in stop(). Existing subscribers (apps/cli/daemon.ts, LocalBridgeServer) untouched — additive only. +11 tests (tests/notifications.test.ts), incl. end-to-end event flow through a real Kernel SignalBus. Tests: 236/236, tsc 0.

2026-08-14 — packages/terminal/ implemented (issue #351)

Status: Done packages/terminal/ was 4 stub files; now a managed shell execution layer (issue #351): runs commands through Sandbox capability checks instead of calling child_process directly.
  • ShellEnvironment.ts — buildShellEnvironment() (process.env + overrides, drops undefined values).
  • CommandSession.ts — session shape (status running/exited/error, exitCode, stdout/stderr, timestamps).
  • CommandExecutor.ts — spawns a ProcessSpec with sandbox.enforce() BEFORE the process starts (denied commands never launch); spawn error → reject (session marked “error”, not fake clean exit); optional timeoutMs → SIGKILL; settled-guard against error+close double-fire.
  • TerminalManager.ts — owns PermissionManager + Sandbox (same wiring as ProcessManager), default-grants EXEC+ENV_WRITE on first run, rejects duplicate session ids, records every run as a session.
+15 tests (tests/terminal.test.ts): env merge, stdout/stderr capture, non-zero exit, timeout kill, sandbox deny (no capability / wrong set), spawn-error, session lifecycle, duplicate id, list, env override. Bonus: fixed pre-existing type error in Sandbox.ts:35 (required array needed CapabilityValue[] annotation — was in KNOWN_ISSUES as owner error). Tests: 251/251, tsc 0.

2026-08-14 — packages/orchestration/ implemented (issue #352)

Status: Done packages/orchestration/ was 4 stub files; now the glue layer that generalizes the wiring ArcluxDaemon used to do inline (issue #352):
  • EventRouter.ts — declarative routes (from → to + optional transform) on top of Kernel’s SignalBus; routeMany + single unsubscribe for teardown.
  • PlatformOrchestrator.ts — turns a push-based analysis source into the platform pipeline on the signal bus: analysis:updated → daemon:analysis:updated (moduleCount) + runDiagnostics → daemon:diagnostics:updated, analysis:error → daemon:analysis:error. Emitted names/shapes identical to the old daemon wiring, so existing subscribers are unaffected.
  • TaskOrchestrator.ts — runs multi-step tasks in order, per-step state, stops at first failure, emits task:started/step:done/completed/failed.
  • RecoveryOrchestrator.ts — subscribes to a failure signal, runs a recovery task via TaskOrchestrator, caps attempts (reset on success), guards against overlapping recoveries.
ArcluxDaemon now delegates its watcher→analysis→diagnostics wiring to PlatformOrchestrator (behavior unchanged — same event names/payloads). +12 tests (tests/orchestration.test.ts): routing, transforms, single unsubscribe, orchestrator wiring + idempotent start/stop, task ordering
  • failure stop, recovery cap/reset/overlap-guard. Tests: 263/263, tsc 0.

2026-08-14 — packages/workspace/ implemented (issue #349)

Status: Done packages/workspace/ was 4 stub files; now the session concept tying project + environment + services + processes together (issue #349):
  • WorkspaceState.ts — per-workspace state: rootPath, repositoryRoot, wasStartPath, status (active/closed/error), timestamps.
  • WorkspaceSnapshot.ts — immutable point-in-time capture (state + shallow-copied processes + services) for restore/UI reads, mirroring ProcSnapshot’s philosophy.
  • WorkspaceSession.ts — owns a Kernel scoped to a detected root; registerProcess/registerService/snapshot/close/setError.
  • WorkspaceManager.ts — open() walks up to the repo root via EnvironmentDetector (idempotent per root, fallback to startPath when no .git), list/get/close/closeAll/snapshots.
Wired into the CLI: arclux workspace <path> opens a session and prints its snapshot (formatted or --json), verified running. Sessions are in-process only (no cross-process persistence yet) — the command prints in the same invocation; no misleading list/snapshot subcommands. +9 tests (tests/workspace.test.ts): walk-up detection, idempotent open, no-git fallback, close/shutdown, snapshot immutability, error state, kernel injection, duplicate service rejection. Tests: 272/272, tsc 0.

2026-08-14 — packages/system/ implemented (issue #350)

Status: Done packages/system/ was 4 stub files; now the central state aggregator (issue #350):
  • ConfigurationStore.ts — in-memory key/value with typed defaults (get falls back to default, set overrides, delete restores, listKeys/ entries only cover explicit values).
  • HealthMonitor.ts — component health checks (register/unregister/ check); per-check try/catch so a throwing check marks that component “down” without taking the whole report down; overall ok/degraded/down.
  • SystemState.ts — one immutable snapshot aggregating workspaces + processes + services + jobs + capabilities + configuration + health.
  • SystemManager.ts — composes the already-existing pieces (Kernel, WorkspaceManager, JobScheduler, PermissionManager, ConfigurationStore) into SystemState.snapshot(); wires default health checks for kernel/workspaces/jobs (failed jobs → degraded).
Additive methods added so aggregation can read them: JobScheduler.list() and PermissionManager.list() (no behavior change). Wired into the CLI: arclux system status (formatted or --json), verified running — aggregates a fresh Kernel + health in a one-shot CLI process. +11 tests (tests/system.test.ts): config defaults/override/delete/list, health ok/degraded/down (incl. throwing check), full aggregation, workspace inclusion, config inclusion, failed-job degradation, custom checks. Tests: 283/283, tsc 0.

2026-08-14 — packages/language/ implemented (issue #348)

Status: Done packages/language/ was 6 stub files; now the language-intelligence layer wrapping packages/parser/* + the indexed Repository (issue #348):
  • SyntaxEngine.ts — per-file parsing via the shared ParserRegistry (parseFile/analyze), language detection, UNSUPPORTED_LANGUAGE error. ensureParsersRegistered is now exported from engine/pipeline.ts (additive) so this layer can guarantee parsers are registered.
  • SymbolEngine.ts — symbol → declaration/importers/callers over a Repository (getSymbols/findByName/forModule/consumers).
  • LanguageService.ts — entry facade: parseFile/language/getSymbols/ findSymbol/symbolsForModule/consumers (repository optional).
  • CompletionEngine.ts — prefix auto-completion over repository exports, dedupe preferring symbols from already-imported modules.
  • FormattingEngine.ts — honest minimal normalization (CRLF→LF, trailing whitespace, final newline, tab↔space by tabWidth) — no prettier dependency exists, a full formatter is out of scope and documented.
  • DiffEngine.ts — symbol-level diff of two ParsedFile versions (exports/imports added/removed/kept, calls kept, changeCount).
IMPORTANT finding: blueprint (docs-site map-packages-platform.mdx) cites packages/engine/analyzeFile.ts / analyzeModule.ts / analyzeDependency.ts as “existing” — they are all EMPTY 8-line stubs. Rule 4 (check real shapes) applied: language/ wraps the real parserRegistry/Repository instead. Wired into the CLI: arclux language <file> (formatted or --json), verified running on real files. +12 tests (tests/language.test.ts): parsing, language detection, unsupported-language error, symbol resolution (importers/callers), findSymbol multi-module, completion + imported-boost, formatting normalization (CRLF/tabs/trailing), symbol diff add/remove/no-change. Tests: 295/295, tsc 0.

2026-08-14 — Daemon verified end-to-end (issue #347) + 2 runtime bugs fixed

Status: Done Issue #347 asked to actually RUN the daemon (it had only passed tsc). Full runtime verification on the arclux repo itself:
  • arclux daemon <path> --detach → bridge up (endpoint file + port), GET /analysis → 552 modules, GET /events SSE connects.
  • SSE push works: touching a file re-analyzes and emits event: analysis + event: diagnostics (real findings — playground cycles, ambiguous symbols).
  • --status/--stop round-trip correct; process verified dead after stop.
Two real bugs found and fixed:
  1. Detached spawn crashed on startup — ERR_MODULE_NOT_FOUND on the first extensionless .ts import: spawnDetached ran bare node apps/cli/index.ts, but dev-mode ESM imports need the tsx loader. Fix: --import tsx when cliEntry ends with .ts (DaemonProcess.ts).
  2. GET /diagnostics 404’d — documented in LocalBridgeServer’s docstring but never implemented in the handler. Fix: bridge caches the last daemon:diagnostics:updated payload and serves it; explicit { findings: [], ran: false, at: null } before any run. +1 regression test (tests/daemon-bridge.test.ts). Tests: 225/225, tsc 0. Side observation (not fixed here): the diagnostics themselves flag a type-only import cycle daemon ArcluxDaemon ↔ LocalBridgeServer — worth a follow-up.

2026-08-15 — packages/db/ implemented as JSON-record store

Status: Done Was 0%. No database dependency installed (confirmed no sqlite/lowdb/knex/prisma/drizzle) and no network access to add one this session. Implemented as JSON-file-per-record store through RecoveryManager.writeTransactional instead of raw fs writes, for crash safety. schema.ts (RepoRecord/AnalysisRecord/IssueRecord + SCHEMA_VERSION for future real-DB migration), client.ts (generic put/get/list/delete), RepoStore/AnalysisStore/IssueStore wrapping existing shapes. Wired into daemon.ts, verified end-to-end at runtime (not just typecheck) — ran daemon, triggered a re-analysis, confirmed real files written to ~/.arclux/db/. See PR #380.

2026-08-15 — packages/cache/ 5/5 files complete

Status: Done CacheProvider.ts (facade/stats over fileCache/repositoryCache/graphCache — confirmed all 3 already implemented and wired into buildIndex.ts + engine/pipeline.ts) and memoryCache.ts (generic TTL Map cache utility, resolves the ‘unclear purpose’ from the 2026-08-08 decisions entry) implemented. See PR #382.

2026-08-15 — packages/networking/ 4/4 files complete

Status: Done NetworkRegistry.ts (lists all registered daemon endpoints from ~/.arclux/endpoints/) and ConnectionManager.ts (liveness check via existing GET /analysis route) implemented, completing packages/networking/ alongside PortManager.ts and ServiceEndpoint.ts from the daemon session.

2026-08-16 — Security & Remote analysis packages implemented (phases 1-5)

Status: Done New extension packages per approved plan (docs/SECURITY_ANALYSIS_PLAN.md): packages/provenance (SourceOrigin/EvidenceOrigin/ProvenanceRecord), packages/remote (RemoteSource/RemoteRepository/RemoteSnapshot), packages/security-analysis (source/ detectors: secrets gitleaks-model, unsafe patterns Semgrep-model, sensitive-data-flow CodeQL-inspired; architecture/: trust boundary, cross-boundary, impact; reporting/: SecurityReport + SARIF 2.1.0 export + remediation templates), packages/correlation (AttackSurfaceMapper BFS validated by experiment, Finding/Evidence/ImpactCorrelator, ImpactSnapshot). Core engine untouched; consumes Repository/DependencyGraph via analyzeRepository. suite 551/551.

2026-08-18 — Daemon re-analysis routed through JobScheduler (workqueue max_active pattern)

Status: Done DaemonRepositoryWatcher now routes every analysis trigger through packages/scheduler’s JobScheduler (maxActive=1, ordered): change bursts coalesce (already-pending work is not requeued, mirroring kernel/workqueue.c queue_work), analyses never overlap, and direct getAnalysis() polls serialize behind an in-flight re-index. Also fixed a latent JobScheduler gap found while wiring: a delayed job (notBefore/delayMs) sat in the queue forever because nothing re-triggered drain() after its delay expired — drain() now arms a timer for the earliest notBefore. First production consumer for packages/scheduler. Tests: tests/daemon-scheduler.test.ts (7 cases), full suite 580/580 green, typecheck clean.

2026-08-18 — arclux exec: TerminalManager gets its first consumer

Status: Done New CLI command ‘arclux exec <cmd> [args…]’ (apps/cli/commands/exec.ts) runs commands through packages/terminal’s TerminalManager — sandbox capability checks + session recording (issue #351 layer). TerminalManager was built and tested (tests/terminal.test.ts, 15 cases) but had zero production consumers; this wires it to the CLI. Also confirmed packages/acquisition IS wired (remote-analysis/analyzeRemoteSource.ts imports createRepositoryAcquirer — wired via relative path so earlier consumer greps missed it); the remaining stub packages (boundaries/adapters/observation/web-intake/package-manager) are intentionally kept — they are planned work, not dead code. Full suite 580/580, typecheck clean.

2026-08-26 — Per-file incremental cache (PR #562)

Status: Done New packages/incremental with Cell/Database/Query classes for per-file cache invalidation. File content hashing via xxhash, disk persistence, TTL expiry. Built and verified standalone but buildIndex still does full rebuild — watchRepository wraps pipeline API coarsely. Per-file incremental is deferred (decision #6).

2026-08-26 — MCP server + npm publish milestone

Status: In Progress MCP server (32 tools) and npm publish package are both built and ready. MCP server: 32 registry-driven tools, all auto-evolving. npm package: arclux@0.2.0, single 10.2MB bundle via esbuild, ships dist/ + wasms/ tree-sitter grammars. One command: npx arclux analyze . or npx arclux mcp. Blocked on npm login — machine needs npm adduser or NPM_TOKEN.