mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
docs: track sequential module organization cleanup (#31670)
This commit is contained in:
@@ -107,6 +107,8 @@ docs/**
|
||||
!docs/audits/
|
||||
!docs/audits/closed-editor-model-lifetime/
|
||||
!docs/audits/closed-editor-model-lifetime/**
|
||||
!docs/audits/module-organization/
|
||||
!docs/audits/module-organization/**
|
||||
!docs/assets/
|
||||
!docs/assets/**
|
||||
!docs/audits/
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
# Orca codebase map and cleanup backlog
|
||||
|
||||
Use this map to locate a responsibility before choosing a cleanup. Read the [assessment](README.md) for the recommended naming policy and migration process. [codebase-map.json](codebase-map.json) backs the tables with a complete tracked-directory inventory, repeated-prefix signals and proposed file paths.
|
||||
|
||||
Use the [task tracker](TASKS.md) and [execution-tracker.json](execution-tracker.json) for the current PR order and progress. Their activity-informed sequence supersedes the preliminary phases below. The original file paths here remain the audit baseline; execution tasks refine destinations and replace four forwarding-file moves with removal and direct-owner imports.
|
||||
|
||||
## How the product fits together
|
||||
|
||||
The desktop interface runs in a renderer. Electron's preload bridge exposes selected host operations, and IPC handlers connect those requests to backend domains. The host runtime also serves remote clients, owns sessions and workspace state, and coordinates execution. A long-lived daemon owns terminal processes. Shared modules supply contracts and pure rules to several surfaces.
|
||||
|
||||
The mobile app is a separate application. Its connection reaches the desktop host through cloud relay services; the cloud also has a separate push gateway. The relay's director and cells coordinate and carry connections. It is not the place where desktop application commands are implemented.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Desktop[Desktop interface] --> Preload[Electron preload bridge]
|
||||
Preload --> IPC[Desktop IPC handlers]
|
||||
IPC --> Backend[Backend domain services]
|
||||
IPC --> Runtime[Host runtime and RPC]
|
||||
Web[Web interface] --> Runtime
|
||||
CLI[Orca CLI] --> Runtime
|
||||
Phone[Mobile app] --> Relay[Cloud relay]
|
||||
Relay --> Runtime
|
||||
Runtime --> Backend
|
||||
Runtime --> Daemon[Terminal daemon]
|
||||
Backend --> Platform[Process and native adapters]
|
||||
Contracts[Shared contracts and pure rules] -.-> Desktop
|
||||
Contracts -.-> Phone
|
||||
Contracts -.-> Runtime
|
||||
```
|
||||
|
||||
Arrows describe principal responsibilities and communication routes; this is not an exhaustive deployment or security diagram. The machine-readable `productionAreaImportEdges` is a separate static import summary: an import does not establish the direction of a runtime request.
|
||||
|
||||
## Where to look
|
||||
|
||||
| Area | Repository path | Tracked files | JS/TS files | Responsibility |
|
||||
| ------------------ | ------------------ | ------------: | ----------: | ------------------------------------------------------------------------------------------------- |
|
||||
| desktop-shell | `src/main` | 8,728 | 8,645 | Desktop application lifecycle, integrations, operating system services and execution adapters |
|
||||
| runtime | `src/main/runtime` | 3,290 | 3,024 | Host-owned sessions, workspace state, command execution, RPC and orchestration |
|
||||
| ipc | `src/main/ipc` | 1,208 | 1,208 | Electron renderer-to-host request handlers |
|
||||
| terminal-daemon | `src/main/daemon` | 587 | 574 | Long-lived terminal processes, endpoint ownership and terminal history |
|
||||
| preload | `src/preload` | 181 | 181 | Typed Electron bridge exposed to renderer windows |
|
||||
| renderer | `src/renderer` | 12,906 | 12,842 | Desktop and web React interface, local state and runtime clients |
|
||||
| contracts | `src/shared` | 3,647 | 3,592 | Cross-surface contracts, schemas and domain rules; a physical folder, not one cohesive domain |
|
||||
| cli | `src/cli` | 428 | 428 | Orca command-line client and command presentation |
|
||||
| process-host | `src/packages` | 41 | 36 | Internal packages including the controlled subprocess API |
|
||||
| wsl-guest | `src/wsl-guest` | 94 | 94 | Windows Subsystem for Linux guest dispatcher and protocol |
|
||||
| types | `src/types` | 1 | 1 | Ambient TypeScript declarations and build constants |
|
||||
| mobile | `mobile` | 3,901 | 2,995 | Expo phone application, transport, embedded web engines and mobile tooling; independent workspace |
|
||||
| cloud | `cloud` | 662 | 578 | Relay, push services, wire packages, operations and infrastructure; independent workspace |
|
||||
| native | `native` | 74 | 4 | Platform-specific operating system integrations and native packages |
|
||||
| tooling | `config` | 1,138 | 1,035 | Build configuration, generators, audits, development scripts and CI checks |
|
||||
| integration-tests | `tests` | 1,166 | 1,106 | End-to-end, cross-version and test-tool coverage; unit tests also live beside source |
|
||||
| documentation-site | `docs/site` | 125 | 28 | Next.js documentation website; independent package |
|
||||
| documentation | `docs` | 409 | 108 | Engineering references, investigations, audits and documentation assets |
|
||||
| automation | `.github` | 142 | 14 | CI, release and cloud-operation workflows and actions |
|
||||
| repository-support | `.` | 163 | 3 | Repository root, resources, bundled skills, examples and remaining support files |
|
||||
|
||||
Counts above assign each tracked file to its most specific area. For example, `runtime`, `ipc` and `terminal-daemon` are removed from the desktop-shell count, and the documentation website is removed from the documentation count. They therefore add up without counting those children twice. The JSON additionally provides recursive directory counts when you want a whole subtree.
|
||||
|
||||
Most unit tests live beside implementation files. The top-level `tests/` directory is not the full test suite. Root, mobile, cloud and the documentation website have separate package configurations; consult the package in the affected workspace when checking a move.
|
||||
|
||||
## Follow a feature through the codebase
|
||||
|
||||
| Feature | Backend and runtime | Contracts and interface |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Jira | `src/main/jira/`; `src/main/ipc/jira*.ts`; `src/main/runtime/rpc/methods/jira*.ts` | Flat `src/shared/jira*.ts`; Jira RPC schemas under `src/shared/rpc-contract/`; `components/task-page/jira/`; Jira state under renderer `store/slices/` |
|
||||
| Linear | `src/main/linear/`; `src/main/ipc/linear*.ts`; `src/main/runtime/runtime-linear*.ts`; Linear RPC methods | `src/shared/linear/`; `components/task-page/linear/`; Linear renderer state |
|
||||
| Terminals | `src/main/runtime/terminal/`; `src/main/ipc/pty/` and flat PTY files; `src/main/daemon/`; `src/packages/process-host/` | Renderer `components/terminal-pane/`; phone transport in `mobile/src/transport/` |
|
||||
| Agent chat | Provider adapters such as `src/main/claude/` and `src/main/codex/`; `src/main/native-chat/`; host session modules under `src/main/runtime/` | Agent-session and native-chat families in `src/shared/`; renderer `components/native-chat/`; `mobile/src/session/` |
|
||||
| Workspace state | `src/main/runtime/sidebar-inputs/`; `src/main/persistence/`; `src/main/ipc/worktrees/` | `src/shared/workspace-layout/`; renderer state under `store/slices/` |
|
||||
|
||||
Renderer component paths in this table are relative to `src/renderer/src/`. Patterns identify current filenames, not proposed directories. The JSON's `featureTrails` contains the corresponding repository paths and their roles.
|
||||
|
||||
Use each trail to retain layer boundaries during cleanup. Moving a Jira IPC handler into `src/main/ipc/jira/` is appropriate; putting the authenticated Jira client into a shared contract folder would cross an execution and dependency boundary.
|
||||
|
||||
## Cleanup backlog
|
||||
|
||||
Phase 1 means small candidate pilots; start with either IPC provider family. Phase 2 means the next domain-focused work. Phase 3 needs broader ownership and compatibility review. A row is an inventory scope, which can require several PRs.
|
||||
|
||||
| Phase | Scope ID | Files P/T | Outside prod callers | Risk | Proposed destination |
|
||||
| ----: | ------------------------ | --------: | -------------------: | -------- | -------------------------------------------------- |
|
||||
| 1 | `ipc-linear` | 6/1 | 1 | lower | `src/main/ipc/linear/` |
|
||||
| 1 | `ipc-jira` | 6/4 | 1 | lower | `src/main/ipc/jira/` |
|
||||
| 1 | `linear-issue-context` | 10/7 | 10 | moderate | `src/main/linear/issue-context/` |
|
||||
| 1 | `jira-attachments` | 9/6 | 6 | moderate | `src/main/jira/attachments/` |
|
||||
| 1 | `ephemeral-vm` | 11/6 | 2 | moderate | `src/main/ephemeral-vm/` |
|
||||
| 2 | `task-page-leftovers` | 125/68 | 61 | moderate | `src/renderer/src/components/task-page/` |
|
||||
| 2 | `settings-repository` | 40/20 | 9 | moderate | `src/renderer/src/components/settings/repository/` |
|
||||
| 2 | `store-agent-status` | 31/24 | 26 | moderate | `src/renderer/src/store/slices/agent-status/` |
|
||||
| 2 | `renderer-session-rules` | 38/21 | 52 | moderate | `src/renderer/src/lib/agent-session/` |
|
||||
| 2 | `ipc-filesystem` | 68/73 | 58 | higher | `src/main/ipc/filesystem/` |
|
||||
| 3 | `ipc-pty` | 12/78 | 31 | higher | `src/main/ipc/pty/` |
|
||||
| 2 | `shared-jira` | 21/10 | 176 | higher | `src/shared/jira/` |
|
||||
| 2 | `browser-cookie` | 25/32 | 4 | higher | `src/main/browser/cookies/` |
|
||||
| 2 | `cloud-relay-assignment` | 8/20 | 11 | higher | `cloud/apps/relay/src/assignment/` |
|
||||
| 2 | `tooling-mobile-web` | 24/51 | 2 | moderate | `config/scripts/mobile-web-app/` |
|
||||
| 3 | `shared-session` | 171/86 | 1,085 | higher | `src/shared/agent-session/` |
|
||||
| 3 | `shared-native-chat` | 98/73 | 420 | higher | `src/shared/native-chat/` |
|
||||
| 3 | `shared-remote-runtime` | 52/29 | 175 | higher | `src/shared/remote-runtime/` |
|
||||
| 3 | `runtime-session` | 59/70 | 76 | higher | `src/main/runtime/agent-session/` |
|
||||
| 3 | `mobile-native-chat` | 165/121 | 27 | higher | `mobile/src/session/native-chat/` |
|
||||
| 3 | `claude-structured` | 46/73 | 33 | higher | `src/main/claude/structured/` |
|
||||
| 3 | `codex-app-server` | 14/11 | 40 | higher | `src/main/codex/app-server/` |
|
||||
| 3 | `persistence-leftovers` | 1/84 | 209 | higher | `src/main/persistence/` |
|
||||
|
||||
`P/T` separates production-classified JS/TS from test/support files using the repository's existing classifier. Production-classified includes tools and declarations and is not a count of shipped application modules. Outside callers count distinct production-classified files with a direct import into that scope, including type imports. They measure required import-review work, not runtime cost. Paths are suggestions, not moves already applied.
|
||||
|
||||
The Task Page scope routes Jira, Linear, GitHub and GitLab files into their existing child folders; cross-provider files go to `task-page/`. Other broad scopes generally retain basenames in the initial move. Inspect each proposal's `review` field before implementing it.
|
||||
|
||||
Directory-size signals also identify work beyond these selected scopes, including terminal-pane, sidebar, editor, right-sidebar and renderer runtime. The `prefixFamilies` collection supplies candidate families throughout the repository. Those signals need semantic review before becoming additional proposals.
|
||||
|
||||
## What the data proves
|
||||
|
||||
Snapshot commit: `0b82f88e9f2b6904f8ecf296b11d0581de4cfbb9`.
|
||||
|
||||
- **38,891 tracked files** across **901 directories**, including **36,496 JS/TS files** and **135 native/shell source files**. Other tracked formats remain in the directory inventory.
|
||||
- JS/TS classification: **19,666 production-classified**, **16,828 test/support**, and **2 identified generated files**.
|
||||
- **66 directories** have more than 100 direct JS/TS files; **34** have more than 100 direct production-classified JS/TS files. These are triage signals, not proposed CI limits.
|
||||
- **1,457 overlapping prefix families** and **23 reviewed candidate scopes** covering **2,008 distinct files**.
|
||||
- **197,356 distinct-per-file module specifiers** examined, of which **150,800 resolve to tracked JS/TS inputs** and **236 to other tracked inputs**. Multiple specifiers may resolve to one file; graph edges deduplicate those file pairs.
|
||||
- **0 parser failures**, **0 missing tracked paths**, and **27 unresolved local/source-package specifiers**. The unresolved list includes generated or built outputs and investigation fixture references; it is not a list of product defects. Bare external or unmodeled packages are counted separately.
|
||||
- **0 existing-target collisions**, with proposed target comparison also checking case-insensitive names.
|
||||
|
||||
The data identifies the checkout commit and a SHA-256 over JS/TS paths and current contents. `directories` inventories every existing tracked directory in scope, with direct and recursive counts. `prefixFamilies` normalizes kebab-case, PascalCase and camelCase and removes the `use-` hook prefix to find domain signals; the first one to three tokens produce overlapping families. A family appears when it has at least eight files and three production-classified files. This deliberately does not prescribe a folder for every matching prefix.
|
||||
|
||||
For each proposal, `moves` provides exact `from` and `to` paths. `dependencyBoundary` supplies all outside importer paths, a production-classified subset, outgoing dependency-directory counts and direct internal/inbound/outbound edge counts. Internal edges show relationships inside the proposed group; outbound edges show that moving a group does not make it independent. `collisionCount` checks proposed targets against existing tracked paths without case sensitivity; duplicate targets and overlapping source selections are rejected by the generator.
|
||||
|
||||
The import analyzer reuses the existing AST module-specifier collector. It recognizes relative imports, supported renderer/mobile/documentation aliases, root workspace source exports and JS-to-TS source counterparts. Bare third-party packages and other unmodeled package paths are not included in local dependency counts. Computed runtime paths, worker URLs, fixture copying, package scripts and workflow strings need a separate search during implementation. Native and shell files have inventory counts but no import graph.
|
||||
|
||||
## Working with the JSON
|
||||
|
||||
Change the human-maintained `audit-plan.json` when an architectural label or proposed scope changes. Regenerate and verify with:
|
||||
|
||||
```sh
|
||||
node docs/audits/module-organization/generate-map.mjs
|
||||
node docs/audits/module-organization/generate-map.mjs --check
|
||||
```
|
||||
|
||||
These commands read tracked paths and working-tree contents; they do not move source files. Add new source files to Git before expecting them in the map. Keep this walkthrough's tables and the assessment in sync when refreshing the snapshot.
|
||||
|
||||
Examples with `jq`:
|
||||
|
||||
```sh
|
||||
# Largest directories by production-classified sibling count
|
||||
jq '.directories | sort_by(-.directProductionJsTs) | .[:20]
|
||||
| .[] | {path, directProductionJsTs, directTestSupportJsTs}' \
|
||||
docs/audits/module-organization/codebase-map.json
|
||||
|
||||
# Exact pilot paths and every outside caller
|
||||
jq '.proposals[] | select(.id == "ipc-linear")
|
||||
| {moves, callers: .dependencyBoundary.externalImporterFiles}' \
|
||||
docs/audits/module-organization/codebase-map.json
|
||||
|
||||
# Domain discovery signals in the flat shared root
|
||||
jq '.prefixFamilies[] | select(.directory == "src/shared")' \
|
||||
docs/audits/module-organization/codebase-map.json
|
||||
```
|
||||
|
||||
| Field | How to use it |
|
||||
| --------------------------- | --------------------------------------------------------------------------------- |
|
||||
| `snapshot` | Check commit, corpus hash, coverage and missing tracked paths. |
|
||||
| `methodology` | Read count definitions and analysis limits before interpreting a metric. |
|
||||
| `areas` | Locate system responsibilities; counts are mutually exclusive. |
|
||||
| `featureTrails` | Trace one product feature across execution layers. |
|
||||
| `directories` | Find oversized sibling lists and inspect production/test breakdowns. |
|
||||
| `prefixFamilies` | Discover repeated domain names; overlapping and not additive. |
|
||||
| `productionAreaImportEdges` | See observed direct dependencies between architectural areas. |
|
||||
| `importAnalysis` | Inspect unresolved inputs and any parser failures. |
|
||||
| `proposals` | Review priority, risk, current target contents, callers and exact proposed moves. |
|
||||
@@ -0,0 +1,144 @@
|
||||
# Orca file and folder organization assessment
|
||||
|
||||
**Yes: clean up the busiest directories in small, domain-focused steps.** The repeated prefixes often describe folders that should exist, or existing folders whose organization is incomplete. Keep kebab-case for ordinary filenames. The problem is how many unrelated responsibilities sit beside each other and how hard it is to find their owners.
|
||||
|
||||
This is a navigation and maintainability investment. Moving files alone does not make the application faster or establish better architecture. It becomes valuable when a contributor can find a feature, understand its entry points, and make a change without searching several enormous directories.
|
||||
|
||||
Start with the seven Linear IPC files and ten Jira IPC files, then Jira attachments, Linear issue context, repository settings and ephemeral VM ownership. Follow the quieter platform and tooling work before the busier Task Page and filesystem folders. Schedule the shared contracts, terminal lifecycle and session runtime work with their owners; those areas have broader compatibility and integration obligations. The [execution task tracker](TASKS.md) defines the full sequence.
|
||||
|
||||
This assessment proposes moves; it applies none. The accompanying [codebase walkthrough](CODEBASE-MAP.md) explains the system, and [codebase-map.json](codebase-map.json) contains the measured directory inventory, dependency summaries and exact proposed paths. [audit-plan.json](audit-plan.json) holds the editable architectural labels and proposals.
|
||||
|
||||
The [execution task tracker](TASKS.md) now provides the authoritative PR sequence, starting with quieter areas based on recent development activity. It refines the original phase suggestions below into smaller batches and explicitly removes existing forwarding modules. [execution-tracker.json](execution-tracker.json) records file actions, checks and merge progress; no barrel exports or old-path shims are allowed.
|
||||
|
||||
## What the filenames are telling us
|
||||
|
||||
Your example is a sound instinct. A prefix can act as a folder name written repeatedly:
|
||||
|
||||
```text
|
||||
Current: src/main/ipc/
|
||||
linear.ts
|
||||
linear-issue-handlers.ts
|
||||
linear-project-handlers.ts
|
||||
linear-team-handlers.ts
|
||||
linear-custom-view-handlers.ts
|
||||
linear-ipc-args.ts
|
||||
linear.test.ts
|
||||
|
||||
Proposed: src/main/ipc/linear/
|
||||
register-handlers.ts
|
||||
issue-handlers.ts
|
||||
project-handlers.ts
|
||||
team-handlers.ts
|
||||
custom-view-handlers.ts
|
||||
ipc-args.ts
|
||||
register-handlers.test.ts
|
||||
```
|
||||
|
||||
The proposed registration file still exports the same registration function. The `linear:*` channel names stay the same. A source-path cleanup should not change the external contract.
|
||||
|
||||
However, the backend already has [`src/main/jira`](../../../src/main/jira) and [`src/main/linear`](../../../src/main/linear). There is no reason to combine everything mentioning Jira into one repository-wide folder. Jira UI, authenticated backend access, IPC handlers and pure contracts have different jobs and run in different environments. Group by domain **within the appropriate layer**:
|
||||
|
||||
```text
|
||||
src/main/jira/ backend client and domain operations
|
||||
src/main/ipc/jira/ desktop request handlers, proposed
|
||||
src/main/runtime/rpc/methods/ remote request methods
|
||||
src/shared/jira/ pure contracts and rules, proposed
|
||||
src/renderer/src/components/
|
||||
task-page/jira/ existing provider UI
|
||||
```
|
||||
|
||||
These are related locations, not necessarily duplicate implementations. The [feature trails](CODEBASE-MAP.md#follow-a-feature-through-the-codebase) connect them.
|
||||
|
||||
## Where the problem is substantial
|
||||
|
||||
The following counts are JS/TS files directly in a directory, including its tests and support files. They do not include children. A directory with many colocated tests is less concerning than the same count of production modules, so the JSON also separates those categories.
|
||||
|
||||
| Directory | Direct files | Assessment |
|
||||
| ------------------------------------------- | -----------: | ----------------------------------------------------------------------------------------------------- |
|
||||
| `src/shared` | 2,905 | Largest discovery problem; partition contracts and pure rules by domain. |
|
||||
| `src/renderer/src/lib` | 1,292 | Too many unrelated rules and adapters behind one role-based name; introduce concrete domain children. |
|
||||
| `src/main/runtime` | 1,251 | Keep runtime composition visible; group owned session, workspace and transport responsibilities. |
|
||||
| `src/renderer/src/components/terminal-pane` | 1,070 | Already a feature folder; needs smaller responsibilities inside it, not another `terminal/` wrapper. |
|
||||
| `config/scripts` | 987 | Organize tool families by purpose; grouping everything named `check-*` would mix unrelated owners. |
|
||||
| `src/renderer/src/components/settings` | 838 | Repository settings are a good first section to group. |
|
||||
| `src/renderer/src/components/native-chat` | 813 | Review composer, journal presentation and interaction boundaries separately. |
|
||||
| `src/renderer/src/components/sidebar` | 807 | Review concrete sidebar responsibilities; keep host facts separate from view state. |
|
||||
| `src/main/ipc` | 797 | Provider families and incomplete filesystem/PTY consolidation are clear candidates. |
|
||||
| `src/renderer/src/components/editor` | 776 | Extend the existing responsibility-based organization; do not move every editor file at once. |
|
||||
| `mobile/src/session` | 722 | Separate mobile chat, terminal and session-navigation responsibilities. |
|
||||
| `src/renderer/src/components` | 619 | Finish the existing Task Page subtree before inventing a new feature hierarchy. |
|
||||
|
||||
The complete directory inventory covers the whole tracked repository, including cloud, mobile, native code, tooling and documentation. The proposed work is a selected backlog, not an assertion that every other directory is already well organized.
|
||||
|
||||
## The strongest findings
|
||||
|
||||
### Complete the organization that is already there
|
||||
|
||||
Task Page already has `jira/`, `linear/`, `github/` and `gitlab/` children, yet many `task-page-*` and `use-task-page-*` files remain in the parent components directory. The [Task Page entry](../../../src/renderer/src/components/task-page/TaskPage.tsx) reaches out to those hooks through parent-relative imports. This is direct evidence of one feature spread across two discovery locations. Route provider-specific files into the existing provider children and keep cross-provider composition at the feature root.
|
||||
|
||||
Filesystem and PTY code have the same pattern: existing [`ipc/filesystem`](../../../src/main/ipc/filesystem) and [`ipc/pty`](../../../src/main/ipc/pty) trees coexist with flat `filesystem-*`, `parcel-watcher-*` and `pty-*` families. Consolidate by the responsibilities already established inside those trees. Filesystem watchers include a separately built process entry, so this is more involved than the provider IPC pilot.
|
||||
|
||||
[`src/shared/linear`](../../../src/shared/linear) already groups issue types, workspace types, links, media and agent-access contracts. There are no direct `linear-*` JS/TS files left in `src/shared` in this snapshot. Jira's equivalent flat family is a concrete place to follow that precedent.
|
||||
|
||||
### A feature folder can itself be too flat
|
||||
|
||||
`browser/`, `claude/`, `codex/`, `terminal-pane/` and mobile `session/` already have meaningful names. Wrapping each in another folder does not solve their problem. Useful next boundaries are responsibilities such as browser cookies, structured Claude sessions, the Codex app-server adapter and the mobile chat composer.
|
||||
|
||||
There is an existing local example in [`editor/combined-diff`](../../../src/renderer/src/components/editor/combined-diff): children include `browse-files`, `load-sections`, `resolve-changes`, `review-controls`, `scroll-viewport` and `remember-view`. Those names help someone find an operation. It is a useful organization model, though the folder layout alone does not prove that its implementation is otherwise ideal.
|
||||
|
||||
### Keep composition visible and scrutinize excessive fragmentation separately
|
||||
|
||||
The [root store](../../../src/renderer/src/store/index.ts) explicitly composes named Zustand slices, and [OrcaRuntime](../../../src/main/runtime/orca-runtime.ts) composes host responsibilities. These entry points should remain easy to find. Moving their related files into domain folders does not require hiding the composition behind `index.ts` re-export files.
|
||||
|
||||
The repository also enforces file-size limits: generally 300 counted lines for TS, 400 for TSX, 600 for MJS and 800 for tests, with existing exceptions tracked by a ratchet. That policy encourages splitting large files. It does not tell contributors where the resulting modules belong. Add folder guidance alongside it. The audit does not establish that this rule caused the current directory sizes.
|
||||
|
||||
The Task Page entry also chains many staged hooks. Folders will improve discoverability, but whether that composition has too many stages is a separate design review. Do not use a file-move PR to silently rewrite state ownership or behavior.
|
||||
|
||||
## Naming rules I would adopt
|
||||
|
||||
- Create a folder when files describe one feature or responsibility with an identifiable owner. Three or more production modules is a useful review prompt, not an automatic rule.
|
||||
- Prefer completing an existing folder to creating a parallel one. Keep tests and their fixtures with the code they exercise, preserving special shared fixture locations where needed.
|
||||
- Remove a prefix when the enclosing path already supplies that exact context and the shorter name stays informative: `jira/attachments/image-cache.ts` is clearer than repeating `jira-attachment-` everywhere.
|
||||
- Retain meaningful context when ambiguity would hurt search or review. `agent-session/journal-position.ts` is more useful than `agent-session/position.ts`. A descriptive kebab-case filename is healthy.
|
||||
- Use concrete entries such as `register-handlers.ts`, `read-context.ts` or a named slice file. Avoid new `utils`, `helpers`, `common`, or catch-all folders. Avoid a forest of one-file folders and generic `manager.ts` files.
|
||||
- Keep a small root for composition and entries. Add another level when distinct responsibilities emerge; do not mechanically convert every hyphen into a path separator.
|
||||
- Keep imports explicit. A new folder does not require an `index.ts` that re-exports every internal file. A folder provides navigation; an enforced boundary or a real package provides stronger isolation.
|
||||
|
||||
The first small proposals shorten redundant prefixes. Most larger proposals retain basenames initially so a reviewer can verify the relocation easily. Shortening those names can follow after the boundary is settled. No global case-style migration is needed.
|
||||
|
||||
## How to pursue the cleanup
|
||||
|
||||
Phase 1 contains independently selectable pilots. Start with **one provider IPC family**. Linear and Jira are lower-risk choices because the selected files are desktop transport handlers around existing backend domains. Lower risk does not mean no validation is needed. Issue-context, attachments and VM code have more lifecycle or permission concerns and should follow after the pilot establishes the process.
|
||||
|
||||
Phase 2 completes existing trees and groups clearer subdomains. Phase 3 handles broadly shared contracts and lifecycle code after ownership review. Priority and risk are independent: shared contracts have high navigation value but also high migration cost.
|
||||
|
||||
The dependency evidence makes that sequencing concrete. Each provider IPC proposal has one outside production-classified caller. The shared agent-session scope has 1,085, while Jira's shared scope has 176. The flat persistence scope contains only one production-classified module among 85 files, yet 209 outside production-classified files import that scope. Moving a central entry can create substantial churn even when its neighbors are mostly tests. These counts include type imports and reflect direct callers in this snapshot, not runtime coupling or transitive impact.
|
||||
|
||||
The exact scope, file counts, outside callers and destinations for all proposals are in [the backlog table](CODEBASE-MAP.md#cleanup-backlog) and `codebase-map.json.proposals`. Broad proposals must be split into smaller changes; the JSON is an inventory of candidate ownership, not an executable bulk-move plan.
|
||||
|
||||
For each implementation PR:
|
||||
|
||||
1. Select a concrete responsibility and inspect its incoming callers and outgoing dependencies. Agree its public entry and identify any generated inputs, runtime entries, fixtures or compatibility fences.
|
||||
2. Move production files with their relevant tests. Update imports, source-reading tests, aliases, package exports, build entry tables, scripts and CI references. Search for both full paths and entry basenames; the import graph cannot find every path string.
|
||||
3. Keep exported APIs, channel names, wire formats, stored formats, registration order and lifecycle behavior stable. Keep semantic redesign in a separate review.
|
||||
4. Run the affected tests and typechecks. Use desktop checks for desktop moves, mobile's independent checks for mobile/shared consumers and cloud's checks for cloud work. Run applicable boundary and ratchet checks; path moves must not add baseline exceptions to make CI pass.
|
||||
5. Verify packaged worker/process entries when affected, including Windows/WSL and macOS/Linux behavior. Shared-contract changes need mixed-version client/server coverage; workspace changes need both folder workspaces and git worktrees.
|
||||
|
||||
For the first Linear IPC PR, the concrete change is seven file moves plus caller updates. Check its registration composition, the Linear IPC tests and desktop typechecking. The [main build-entry table](../../../config/build-plugins/main-build-entry-points.ts) becomes particularly relevant for later watcher and worker-related moves. Existing [compatibility import fences](../../../config/scripts/check-compat-boundary-imports.mjs) and the [process-host import boundary](../../../config/scripts/check-process-host-imports.mjs) should remain intact.
|
||||
|
||||
Judge success by whether a contributor can locate and change one responsibility in one obvious subtree, with explicit composition and a reviewable diff. Raw filename length, folder count and a lower sibling count are supporting signals, not success on their own. There is no defensible runtime-speed or engineering-hours savings estimate from this static audit.
|
||||
|
||||
## Keeping the evidence useful
|
||||
|
||||
Run from the repository root with its existing Node and pnpm dependencies installed:
|
||||
|
||||
```sh
|
||||
node docs/audits/module-organization/generate-map.mjs
|
||||
node docs/audits/module-organization/generate-map.mjs --check
|
||||
```
|
||||
|
||||
The first command refreshes only `codebase-map.json` and records the checkout commit as snapshot provenance. The second preserves that recorded commit while recomputing and strictly comparing every measured field, including the source-content hash. Committing the audit or other changes outside the measured inventory therefore does not make an otherwise identical snapshot stale. The recorded commit must have a valid full Git object ID; checking does not require its historical object to be available in a shallow checkout. Both commands read Git-tracked paths with current working-tree contents and exclude this audit's own artifacts. Newly added source files must be tracked before they enter the inventory. The hash covers JS/TS paths and contents, not every repository asset.
|
||||
|
||||
The generator reuses the repository's tracked-file inventory, AST module-specifier parser, test classifier and root workspace source resolver. Human judgments live in `audit-plan.json`; changing those judgments requires review. After moves land, remove or update completed selectors before regeneration: an empty selector deliberately fails rather than silently retaining an obsolete proposal. Update the prose tables when refreshing the data; the generator does not rewrite the report.
|
||||
|
||||
The import graph includes type-only imports and recognized literal loaders. It counts direct file dependencies, not calls or runtime loading cost. Computed paths, `new URL`, filesystem reads, spawn/worker strings, package scripts and CI path references require separate inspection. Unresolved specifiers and parse failures are recorded explicitly. Folder size and repeated-prefix families identify places to inspect; the families overlap and must not be summed.
|
||||
@@ -0,0 +1,275 @@
|
||||
# Orca module organization task tracker
|
||||
|
||||
Start with the small, quieter provider and backend areas, merge each PR, and build the next change on the merged result. Keep the session runtime, broadly shared contracts and terminal lifecycle work later in the queue. This tracker is the execution order; it supersedes the preliminary phase ordering in the [assessment](README.md) and [codebase map](CODEBASE-MAP.md).
|
||||
|
||||
The full plan accounts for all **2,008 files in the original 23 scopes**: **2,004 proposed relocations and four forwarding-module removals**. It breaks them into **88 provisional code PR batches**, with seven checkpoints and nine discovery tasks covering all 66 directories with more than 100 direct JS/TS files. These are review-sized planning units, not a PR quota or a promise to finish everything in a few days. Caller fanout and live development may require further splits; adjacent tasks can combine when they form one responsibility and remain easy to review. Record that adjustment before implementation and preserve file coverage.
|
||||
|
||||
Implementation has not started; G00 documentation publication is in progress. Every completion checkbox below is open. [execution-tracker.json](execution-tracker.json) is the authoritative structured tracker: it includes each batch's file actions, activity evidence, validation profile, export review, workflow predecessor, status, owner, PR URL, merge SHA and check records. Keep this checklist and the JSON in sync in each PR. Stable task IDs are local planning IDs, not GitHub PR numbers; never reuse an ID for unrelated work.
|
||||
|
||||
## Start here
|
||||
|
||||
- [ ] **G00 — Save and merge the documentation PR.** Include the original report, data, generator, this tracker and the narrow `.gitignore` allowlist. This makes the multi-day plan available on main.
|
||||
- [ ] **PR00 — Linear IPC, seven files.** Move the six implementation modules and their existing test into `src/main/ipc/linear/`; import `linear/register-handlers` directly from registration composition. No directory-index resolution and no forwarding file at the old path.
|
||||
- [ ] **PR01 — Jira IPC, ten files.** Apply the same pattern to Jira, preserving cancellation, site scope, channel names and handler registration order.
|
||||
- [ ] Continue through the quiet-area queue, then complete **G01** before taking on broader changes.
|
||||
|
||||
Open one code-move PR at a time by default. Do not spend several days preparing a stack that will conflict with the rest of the team. Each merged PR should stand on its own, pass required checks and leave direct imports in their final form.
|
||||
|
||||
## How quieter work was selected
|
||||
|
||||
The bounded sample is the latest **1,000 first-parent commits at the audited checkout**, spanning **October 16–21, 2026**. It measures development edits, which predict merge conflict exposure; it does not measure product usage. It does not include unmerged work or other branches. Zero edits in this sample is a prioritization clue, not proof of an inactive feature.
|
||||
|
||||
| Scope | Commits editing scope | Commits editing outside callers |
|
||||
| ------------------------ | --------------------: | ------------------------------: |
|
||||
| `ipc-linear` | 1 | 5 |
|
||||
| `ipc-jira` | 1 | 5 |
|
||||
| `jira-attachments` | 0 | 4 |
|
||||
| `linear-issue-context` | 1 | 2 |
|
||||
| `settings-repository` | 1 | 2 |
|
||||
| `ephemeral-vm` | 3 | 1 |
|
||||
| `browser-cookie` | 0 | 3 |
|
||||
| `tooling-mobile-web` | 5 | 6 |
|
||||
| `cloud-relay-assignment` | 3 | 12 |
|
||||
| `task-page-leftovers` | 12 | 7 |
|
||||
| `ipc-filesystem` | 14 | 38 |
|
||||
| `store-agent-status` | 9 | 20 |
|
||||
| `renderer-session-rules` | 14 | 59 |
|
||||
| `codex-app-server` | 8 | 29 |
|
||||
| `claude-structured` | 19 | 36 |
|
||||
| `shared-jira` | 2 | 30 |
|
||||
| `shared-remote-runtime` | 8 | 160 |
|
||||
| `shared-native-chat` | 23 | 119 |
|
||||
| `shared-session` | 48 | 267 |
|
||||
| `mobile-native-chat` | 32 | 27 |
|
||||
| `runtime-session` | 32 | 91 |
|
||||
| `ipc-pty` | 20 | 15 |
|
||||
| `persistence-leftovers` | 31 | 118 |
|
||||
|
||||
Caller activity uses the audit's entire outside-caller set for the scope, including tests; it is deliberately broader than the files any one sub-batch will edit. Production caller counts in the JSON are also scope-wide, not per-batch estimates. Refresh actual callers and their activity before implementation.
|
||||
|
||||
IPC goes first because its scopes are small and each has one outside production-classified caller. Jira attachments have no sampled edits, but image authorization deserves a focused review. Browser cookies are also quiet, yet touch credential and platform behavior, so they follow the first pilots. High-fanout shared contracts and busy session code come later. The sequence balances edit traffic, caller fanout and operational risk; sorting by a single edit count would miss those differences.
|
||||
|
||||
Before every new PR, inspect current main and available active-work information. If another change is touching this scope or its required callers, select the next quieter eligible task and record the revised order. `sequenceAfter` encodes the default workflow, not a proven code dependency. Resolve actual technical dependencies during preflight and record them in `technicalDependencies` before resequencing.
|
||||
|
||||
## Rules for every PR
|
||||
|
||||
**No barrels, including temporary ones.** No new `index.ts` aggregation, `export *` convenience surfaces, named re-export aggregators, old-path forwarding stubs, or folder imports that rely on an index. A named entry such as `register-handlers.ts` should implement registration; other callers import the concrete module that owns the behavior or type.
|
||||
|
||||
The source export review identified **52 production-classified modules with local forwarding exports**, including exports of imported bindings. Four are pure forwarding modules. Their tasks explicitly remove them and change consumers to the real owners:
|
||||
|
||||
| Forwarding module to remove | Import the concrete owner |
|
||||
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `src/main/ephemeral-vm-recipe-runner.ts` | `src/shared/ephemeral-vm-recipe-runner.ts` |
|
||||
| `src/renderer/src/components/task-page-github-task-kind.ts` | `src/shared/task-github-search.ts` |
|
||||
| `mobile/src/session/mobile-native-chat-image-transcript-markers.ts` | `src/shared/native-chat-image-transcript-markers.ts` |
|
||||
| `src/main/persistence.ts` | The existing `persistence/loading-store/` and `persistence/applying-settings/` implementation files appropriate to each imported symbol |
|
||||
|
||||
For the other flagged modules, retain their implementation and remove convenience forwarding exports as their consumers switch to direct owners. Each task lists the relevant existing exports in `existingLocalReexports`. Review actual bindings, mock behavior and cycles; a path-only search cannot distinguish a type's owner from a forwarding API. Required external package export contracts remain explicit package boundaries, not a reason to create internal barrels.
|
||||
|
||||
Each PR follows this checklist:
|
||||
|
||||
1. **Refresh the scope.** Set owner and status; verify the source file list, existing target contents, test ownership, recent activity and current outside callers. Read applicable platform, Git and subtree instructions.
|
||||
2. **Settle the destination.** Confirm concrete responsibility folders and final descriptive basenames. The JSON gives path proposals; its larger batches need semantic review. Avoid a second broad rename pass just to shorten every name.
|
||||
3. **Bound the review.** Aim for 5–20 production modules. More than 25 production modules, 60 moved files or 100 caller/path-reference edits triggers a split review. Keep production/test pairs and platform twins together; assign cross-cutting integration tests by what they exercise rather than their prefix alone. Batch file counts do not include caller edits.
|
||||
4. **Make the atomic change.** Move code and relevant tests, remove designated forwarding modules, and update all imports, mocks, runtime path strings, fixture-copying code, build entries, scripts and CI references in the same PR. Never bridge two batches with re-export shims.
|
||||
5. **Preserve behavior.** Keep wire methods, IPC channels, stored formats, process output names, lifecycle order and lazy-loading boundaries stable. Keep API redesign, performance changes and unrelated formatting out of the move.
|
||||
6. **Verify and review.** Run the affected existing tests, typechecks, formatting, changed-code quality and applicable CI gates. Apply the [refactor-audit skill](../../../.agents/skills/refactor-audit/SKILL.md): compare against the merge base, check moved bodies, literals, callers and wire names, and obtain an independent sub-agent review before merging. Inspect changed imports/exports for accidental aggregation, cycles and stale path strings. Preserve upstream fixes when rebasing and run the skill's merge-validation gate. Record commands, review findings and results.
|
||||
7. **Merge and reconcile.** Record PR URL, merge SHA and actual file actions. Set status to `merged`, then `verified` after checking merged main. Update the map selectors, inventory, report and tracker at scope checkpoints so completed work does not remain a pending proposal. Start the next PR from updated main.
|
||||
|
||||
Do not mark a task complete when its PR opens or local tests pass. Preserve its full scope when splitting it; create child tasks with stable IDs and exact file ownership. If a file should stay in place, record a reviewed reason rather than silently dropping it. A failed move can be reverted as an atomic PR; restoring a barrel is not the recovery plan.
|
||||
|
||||
## Sequential PR checklist
|
||||
|
||||
`Files P/T` means production-classified files / test-support files in this batch. Production-classified includes tooling and declarations; the four removal tasks count their forwarding files in that column. `Edits` counts sample commits touching the batch's original paths. It excludes edits to its outside callers. PR scope also includes necessary caller and path-reference updates, which must be measured at preflight.
|
||||
|
||||
### Wave 1 Quiet provider backend and settings work
|
||||
|
||||
First merge milestone: PR00 and PR01. Continue one at a time through the remaining quiet scopes; G01 checks what the pilot taught us.
|
||||
|
||||
| Done | Task | Proposed PR or checkpoint | Files P/T | Edits |
|
||||
| ---- | -------- | --------------------------------------------------------------- | --------: | ----: |
|
||||
| [ ] | **PR00** | Group Linear IPC handlers with direct registration imports | 6/1 | 1 |
|
||||
| [ ] | **PR01** | Group Jira IPC handlers with direct registration imports | 6/4 | 1 |
|
||||
| [ ] | **PR02** | Group Jira attachment discovery, images and cache ownership | 9/6 | 0 |
|
||||
| [ ] | **PR03** | Group Linear issue-context reads and replace forwarding imports | 10/7 | 1 |
|
||||
| [ ] | **PR04** | Group repository identity and icon settings | 8/5 | 0 |
|
||||
| [ ] | **PR05** | Group repository hooks and host setup settings | 15/7 | 1 |
|
||||
| [ ] | **PR06** | Group source-control AI repository settings | 9/3 | 0 |
|
||||
| [ ] | **PR07** | Move repository settings composition and remaining fields | 8/5 | 0 |
|
||||
| [ ] | **PR08** | Group ephemeral VM provisioning and cleanup | 11/6 | 3 |
|
||||
| [ ] | **G01** | Review the first merged moves before expanding scope | — | — |
|
||||
|
||||
### Wave 2 Quiet platform and tool subsystems
|
||||
|
||||
Cookie files had no edits in the sample, but their platform and credential ownership needs review. Tooling and cloud work follow; neither entails a deployment.
|
||||
|
||||
| Done | Task | Proposed PR or checkpoint | Files P/T | Edits |
|
||||
| ---- | -------- | ---------------------------------------------------------- | --------: | ----: |
|
||||
| [ ] | **PR09** | Group Chromium cookie scanning and import steps | 5/0 | 0 |
|
||||
| [ ] | **PR10** | Group cookie source detection, parsers and decryption | 10/4 | 0 |
|
||||
| [ ] | **PR11** | Group cookie import orchestration, state and cleanup | 10/28 | 0 |
|
||||
| [ ] | **PR12** | Group mobile web input and platform seam tools | 5/3 | 0 |
|
||||
| [ ] | **PR13** | Group mobile web preview and evidence tools | 12/3 | 1 |
|
||||
| [ ] | **PR14** | Group mobile web route and bundle analysis tools | 7/45 | 5 |
|
||||
| [ ] | **PR15** | Group relay assignment modules and existing tests | 8/20 | 3 |
|
||||
| [ ] | **G02** | Reconcile quiet-area PRs and approve the busier-area queue | — | — |
|
||||
|
||||
### Wave 3 Desktop features and provider lifecycle
|
||||
|
||||
These are busier scopes. Keep provider and subsystem batches separate and refresh caller lists before every PR.
|
||||
|
||||
| Done | Task | Proposed PR or checkpoint | Files P/T | Edits |
|
||||
| ---- | -------- | --------------------------------------------------------------------- | --------: | ----: |
|
||||
| [ ] | **PR16** | Finish the existing GitLab Task Page folder | 1/2 | 2 |
|
||||
| [ ] | **PR17** | Finish the existing Linear Task Page folder | 22/5 | 3 |
|
||||
| [ ] | **PR18** | Group Jira Task Page board behavior | 6/5 | 0 |
|
||||
| [ ] | **PR19** | Group Jira Task Page creation and summary flows | 6/2 | 0 |
|
||||
| [ ] | **PR20** | Move Jira Task Page filters, issue lists and creation flows | 24/26 | 0 |
|
||||
| [ ] | **PR21** | Group GitHub Task Page mutation ownership | 19/7 | 0 |
|
||||
| [ ] | **PR22** | Move GitHub Task Page reads, refresh and composition | 19/5 | 3 |
|
||||
| [ ] | **PR23** | Move cross-provider Task Page hooks and composition | 13/1 | 4 |
|
||||
| [ ] | **PR24** | Move cross-provider Task Page composition and pure view rules | 15/15 | 5 |
|
||||
| [ ] | **PR25** | Consolidate local filesystem guards, import and search | 11/13 | 2 |
|
||||
| [ ] | **PR26** | Group Parcel watcher child acquisition and termination | 12/4 | 0 |
|
||||
| [ ] | **PR27** | Group the Parcel watcher process and supervisor | 19/16 | 2 |
|
||||
| [ ] | **PR28** | Group remote and WSL filesystem watchers | 9/5 | 5 |
|
||||
| [ ] | **PR29** | Consolidate local watcher lifecycle and events | 15/17 | 7 |
|
||||
| [ ] | **PR30** | Move filesystem request registration and integration suites | 2/18 | 5 |
|
||||
| [ ] | **PR31** | Group agent-status records, contracts and reducers | 13/2 | 5 |
|
||||
| [ ] | **PR32** | Move agent-status actions, scheduling and slice composition | 18/22 | 8 |
|
||||
| [ ] | **PR33** | Group renderer session launch records and publication | 12/4 | 2 |
|
||||
| [ ] | **PR34** | Group renderer session launch admission and requests | 24/15 | 12 |
|
||||
| [ ] | **PR35** | Group renderer session tab activation and cancellation | 2/2 | 5 |
|
||||
| [ ] | **PR36** | Group Codex app-server protocol and handshake leaves | 9/2 | 3 |
|
||||
| [ ] | **PR37** | Move Codex app-server connection, session and teardown | 5/9 | 8 |
|
||||
| [ ] | **PR38** | Group structured Claude launch options and model selection | 16/15 | 9 |
|
||||
| [ ] | **PR39** | Group structured Claude journal translation and prompts | 8/11 | 5 |
|
||||
| [ ] | **PR40** | Group structured Claude acquisition and session startup | 9/3 | 10 |
|
||||
| [ ] | **PR41** | Move structured Claude session and dispatch lifecycle | 13/44 | 15 |
|
||||
| [ ] | **G03** | Review desktop feature and process boundaries before shared contracts | — | — |
|
||||
|
||||
### Wave 4 Shared contracts and all consumers
|
||||
|
||||
Move each leaf group with all consumer updates atomically. Split further by concrete owner if caller churn exceeds the review-size prompt. Preserve mixed-version compatibility.
|
||||
|
||||
| Done | Task | Proposed PR or checkpoint | Files P/T | Edits |
|
||||
| ---- | -------- | --------------------------------------------------------------- | --------: | ----: |
|
||||
| [ ] | **PR42** | Group shared Jira URL, query and status rules | 9/5 | 0 |
|
||||
| [ ] | **PR43** | Group shared Jira attachment and stream contracts | 3/0 | 0 |
|
||||
| [ ] | **PR44** | Move shared Jira types, capabilities and saved-filter contracts | 9/5 | 2 |
|
||||
| [ ] | **PR45** | Group remote runtime requests and subscriptions | 18/12 | 4 |
|
||||
| [ ] | **PR46** | Group remote runtime shared-control lifecycle | 21/7 | 4 |
|
||||
| [ ] | **PR47** | Move remote runtime connection, handshake and socket rules | 13/10 | 5 |
|
||||
| [ ] | **PR48** | Group shared native-chat tool, edit and diff rules | 23/19 | 1 |
|
||||
| [ ] | **PR49** | Group shared native-chat prompts and session options | 19/12 | 9 |
|
||||
| [ ] | **PR50** | Group shared native-chat transcript and row rules | 24/19 | 9 |
|
||||
| [ ] | **PR51** | Group shared native-chat command, stream and provider rules | 10/7 | 3 |
|
||||
| [ ] | **PR52** | Move shared native-chat command, stream and root contracts | 22/16 | 13 |
|
||||
| [ ] | **PR53** | Group shared agent-session options and catalogs | 25/11 | 7 |
|
||||
| [ ] | **PR54** | Group shared agent-session journal contracts | 9/4 | 3 |
|
||||
| [ ] | **PR55** | Group shared agent-session requests, queue and send contracts | 16/8 | 11 |
|
||||
| [ ] | **PR56** | Group shared agent-session failure and refusal rules | 23/9 | 10 |
|
||||
| [ ] | **PR57** | Group shared agent-session records and stored forms | 4/4 | 3 |
|
||||
| [ ] | **PR58** | Group shared agent-session lifecycle and provider identity | 23/13 | 10 |
|
||||
| [ ] | **PR59** | Group shared agent-session projection and turn rules | 24/15 | 8 |
|
||||
| [ ] | **PR60** | Group shared agent-session capabilities and availability | 12/1 | 10 |
|
||||
| [ ] | **PR61** | Group shared agent-session context, approvals and questions | 9/9 | 4 |
|
||||
| [ ] | **PR62** | Group shared agent-session conversation and message identity | 7/3 | 3 |
|
||||
| [ ] | **PR63** | Move remaining agent-session capability and root contracts | 19/9 | 15 |
|
||||
| [ ] | **G04** | Verify shared consumers and mixed-version compatibility | — | — |
|
||||
|
||||
### Wave 5 Mobile runtime terminal and persistence
|
||||
|
||||
These areas have substantial lifecycle or active-work risk. Revisit the order using current main. The persistence forwarding entry is removed, not moved.
|
||||
|
||||
| Done | Task | Proposed PR or checkpoint | Files P/T | Edits |
|
||||
| ---- | -------- | ---------------------------------------------------------------- | --------: | ----: |
|
||||
| [ ] | **PR64** | Group mobile native-chat visual documents and media rendering | 11/5 | 5 |
|
||||
| [ ] | **PR65** | Group mobile native-chat attachment and image ownership | 10/5 | 2 |
|
||||
| [ ] | **PR66** | Group mobile native-chat composer, attachments and drafts | 19/12 | 9 |
|
||||
| [ ] | **PR67** | Group mobile native-chat session options and queued editing | 12/7 | 9 |
|
||||
| [ ] | **PR68** | Group mobile native-chat asks, permissions and prompts | 20/15 | 3 |
|
||||
| [ ] | **PR69** | Group mobile native-chat tool, task and subagent rows | 6/4 | 4 |
|
||||
| [ ] | **PR70** | Group mobile native-chat page and scroll ownership | 17/6 | 3 |
|
||||
| [ ] | **PR71** | Group mobile native-chat message text and prose presentation | 12/11 | 8 |
|
||||
| [ ] | **PR72** | Group mobile native-chat row and turn presentation | 17/11 | 11 |
|
||||
| [ ] | **PR73** | Group mobile native-chat send state and terminal-stream coupling | 16/15 | 4 |
|
||||
| [ ] | **PR74** | Move mobile native-chat controller, send and session composition | 25/30 | 20 |
|
||||
| [ ] | **PR75** | Group runtime session records and durable store | 17/11 | 9 |
|
||||
| [ ] | **PR76** | Group runtime session claims, leases and process identity | 15/15 | 7 |
|
||||
| [ ] | **PR77** | Group runtime session teardown and tab ownership | 8/5 | 2 |
|
||||
| [ ] | **PR78** | Move runtime session dispatch, teardown and tab lifecycle | 19/39 | 27 |
|
||||
| [ ] | **PR79** | Move PTY IPC test harness and provider fixtures | 4/10 | 9 |
|
||||
| [ ] | **PR80** | Group PTY delivery, backpressure and drain behavior | 4/15 | 1 |
|
||||
| [ ] | **PR81** | Consolidate PTY ownership, spawning and management suites | 4/53 | 18 |
|
||||
| [ ] | **PR82** | Remove the persistence forwarding entry and import real owners | 1/0 | 0 |
|
||||
| [ ] | **PR83** | Colocate persistence orcad migration tests and fixtures | 0/10 | 8 |
|
||||
| [ ] | **PR84** | Colocate persistence terminal topology and lease tests | 0/22 | 10 |
|
||||
| [ ] | **PR85** | Colocate persistence workspace and repository tests | 0/20 | 14 |
|
||||
| [ ] | **PR86** | Colocate persistence settings and automation tests | 0/15 | 7 |
|
||||
| [ ] | **PR87** | Colocate remaining persistence loading and security tests | 0/17 | 7 |
|
||||
| [ ] | **G05** | Verify lifecycle moves before the remaining-directory review | — | — |
|
||||
|
||||
### Wave 6 Remaining directory dispositions
|
||||
|
||||
These are discovery tasks, not bulk move PRs. Each assigned directory needs an explicit keep, organize or deeper-design-review decision. Add exact implementation tasks for organize decisions.
|
||||
|
||||
| Done | Task | Work | Coverage |
|
||||
| ---- | ------- | ------------------------------------------------------------------ | -------------- |
|
||||
| [ ] | **D00** | Review remaining terminal UI and daemon organization | 3 directories |
|
||||
| [ ] | **D01** | Review remaining desktop native-chat and provider organization | 10 directories |
|
||||
| [ ] | **D02** | Review sidebar, tab bar and remaining workspace UI organization | 5 directories |
|
||||
| [ ] | **D03** | Review editor and remaining settings organization | 2 directories |
|
||||
| [ ] | **D04** | Review remaining renderer libraries, runtime, state and components | 7 directories |
|
||||
| [ ] | **D05** | Review mobile transport, tasks, components and session leftovers | 7 directories |
|
||||
| [ ] | **D06** | Review remaining host services, IPC and runtime organization | 21 directories |
|
||||
| [ ] | **D07** | Review remaining shared contracts and root composition | 6 directories |
|
||||
| [ ] | **D08** | Review remaining tooling, cloud and integration-test organization | 5 directories |
|
||||
|
||||
### Final verification
|
||||
|
||||
Completion includes discovery-created work; it does not stop at the initial 23 scopes.
|
||||
|
||||
| Done | Task | Work | Coverage |
|
||||
| ---- | ------- | ------------------------------------------------------------ | -------------------------------------------- |
|
||||
| [ ] | **G06** | Close the organization program with a refreshed codebase map | All completed scopes and directory decisions |
|
||||
|
||||
The file groups above are planning partitions, not proof of architectural independence. The JSON has exact source actions and proposed targets for every original file. Later tasks require boundary and caller review before execution; the currently small source counts of a shared-contract task can still conceal hundreds of consumer edits.
|
||||
|
||||
## Validation and merge evidence
|
||||
|
||||
Set `ORCA_BACKGROUND_LAUNCH=1` for all tests and agent-launched applications. UI checks, when needed, use the repository's Electron skill and hidden Playwright CDP; platform-native checks run in an isolated environment or CI. File moves alone do not require inventing new behavioral tests.
|
||||
|
||||
| Change | Required local validation to select from current callers |
|
||||
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Desktop backend or IPC | `pnpm tc:node`; existing moved tests and affected consumer/registration suites with `pnpm test` |
|
||||
| Renderer | `pnpm tc:web`; affected existing component, hook and store tests; node typecheck if its config explicitly includes a moved path |
|
||||
| Shared contracts | `pnpm tc`; mobile typecheck and test typecheck; affected shared, desktop, CLI and mobile suites; cloud checks if actual cloud consumers are affected |
|
||||
| Mobile | `pnpm --dir mobile typecheck`; `pnpm --dir mobile typecheck:tests`; affected suites through mobile's own test command |
|
||||
| Filesystem workers or PTY | Desktop typecheck and lifecycle suites; rebuild affected entry sources and verify launch paths and stable output names; applicable Windows/WSL and macOS/Linux checks |
|
||||
| Tooling | Existing tool/fixture/CI contract tests; affected generator verification; script-relative paths and generated output checks |
|
||||
| Cloud assignment | Cloud workspace typecheck and tests; no deploy is part of this organization task |
|
||||
|
||||
Every PR also runs changed-code quality, changed-file formatting, `git diff --check` and its required CI checks. Applicable import-boundary and ratchet gates must pass without adding baseline exemptions. Preserve remote/local execution, mobile consumers, mixed-version compatibility and folder workspaces as well as git worktrees.
|
||||
|
||||
Fill in commands from the task's `validationProfile`; angle-bracket arguments in JSON are templates, not runnable literal paths. Attach check evidence like:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "verified",
|
||||
"prUrl": "actual merged PR URL",
|
||||
"mergeCommit": "actual merge SHA",
|
||||
"checks": [
|
||||
{ "command": "actual command with exact paths", "result": "passed", "commit": "checked SHA" }
|
||||
],
|
||||
"notes": "Actual moves, direct-owner import changes and any approved scope refinement"
|
||||
}
|
||||
```
|
||||
|
||||
Keep the JSON as a durable record; do not regenerate it from the original snapshot and overwrite progress. The original `generate-map.mjs` updates `codebase-map.json` only. Retire or update completed selectors in `audit-plan.json` before rerunning it, because an empty selector deliberately fails. If early path moves change later callers, update later file actions and checks against current main without changing the baseline coverage record.
|
||||
|
||||
## Completion means a reconciled codebase
|
||||
|
||||
The program closes after the original scopes and any discovery-created implementation tasks are verified, every large directory has a recorded disposition, obsolete source paths and convenience forwarding exports are removed from the changed scopes, and the refreshed map reflects merged main. A remaining design-review decision is open work, not a completed cleanup.
|
||||
|
||||
Publish the merged PR list and before/after directory counts. Evaluate whether a contributor can find a responsibility and its concrete owners; file moves alone do not establish performance gains or prove every module is well designed.
|
||||
@@ -0,0 +1,551 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"purpose": "Human-reviewed organization proposals and architecture labels; no source moves are authorized or executed by the generator.",
|
||||
"areas": [
|
||||
{
|
||||
"id": "desktop-shell",
|
||||
"path": "src/main",
|
||||
"purpose": "Desktop application lifecycle, integrations, operating system services and execution adapters"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
"path": "src/main/runtime",
|
||||
"purpose": "Host-owned sessions, workspace state, command execution, RPC and orchestration"
|
||||
},
|
||||
{
|
||||
"id": "ipc",
|
||||
"path": "src/main/ipc",
|
||||
"purpose": "Electron renderer-to-host request handlers"
|
||||
},
|
||||
{
|
||||
"id": "terminal-daemon",
|
||||
"path": "src/main/daemon",
|
||||
"purpose": "Long-lived terminal processes, endpoint ownership and terminal history"
|
||||
},
|
||||
{
|
||||
"id": "preload",
|
||||
"path": "src/preload",
|
||||
"purpose": "Typed Electron bridge exposed to renderer windows"
|
||||
},
|
||||
{
|
||||
"id": "renderer",
|
||||
"path": "src/renderer",
|
||||
"purpose": "Desktop and web React interface, local state and runtime clients"
|
||||
},
|
||||
{
|
||||
"id": "contracts",
|
||||
"path": "src/shared",
|
||||
"purpose": "Cross-surface contracts, schemas and domain rules; a physical folder, not one cohesive domain"
|
||||
},
|
||||
{
|
||||
"id": "cli",
|
||||
"path": "src/cli",
|
||||
"purpose": "Orca command-line client and command presentation"
|
||||
},
|
||||
{
|
||||
"id": "process-host",
|
||||
"path": "src/packages",
|
||||
"purpose": "Internal packages including the controlled subprocess API"
|
||||
},
|
||||
{
|
||||
"id": "wsl-guest",
|
||||
"path": "src/wsl-guest",
|
||||
"purpose": "Windows Subsystem for Linux guest dispatcher and protocol"
|
||||
},
|
||||
{
|
||||
"id": "types",
|
||||
"path": "src/types",
|
||||
"purpose": "Ambient TypeScript declarations and build constants"
|
||||
},
|
||||
{
|
||||
"id": "mobile",
|
||||
"path": "mobile",
|
||||
"purpose": "Expo phone application, transport, embedded web engines and mobile tooling; independent workspace"
|
||||
},
|
||||
{
|
||||
"id": "cloud",
|
||||
"path": "cloud",
|
||||
"purpose": "Relay, push services, wire packages, operations and infrastructure; independent workspace"
|
||||
},
|
||||
{
|
||||
"id": "native",
|
||||
"path": "native",
|
||||
"purpose": "Platform-specific operating system integrations and native packages"
|
||||
},
|
||||
{
|
||||
"id": "tooling",
|
||||
"path": "config",
|
||||
"purpose": "Build configuration, generators, audits, development scripts and CI checks"
|
||||
},
|
||||
{
|
||||
"id": "integration-tests",
|
||||
"path": "tests",
|
||||
"purpose": "End-to-end, cross-version and test-tool coverage; unit tests also live beside source"
|
||||
},
|
||||
{
|
||||
"id": "documentation-site",
|
||||
"path": "docs/site",
|
||||
"purpose": "Next.js documentation website; independent package"
|
||||
},
|
||||
{
|
||||
"id": "documentation",
|
||||
"path": "docs",
|
||||
"purpose": "Engineering references, investigations, audits and documentation assets"
|
||||
},
|
||||
{
|
||||
"id": "automation",
|
||||
"path": ".github",
|
||||
"purpose": "CI, release and cloud-operation workflows and actions"
|
||||
},
|
||||
{
|
||||
"id": "repository-support",
|
||||
"path": ".",
|
||||
"purpose": "Repository root, resources, bundled skills, examples and remaining support files"
|
||||
}
|
||||
],
|
||||
"featureTrails": [
|
||||
{
|
||||
"domain": "Jira",
|
||||
"locations": [
|
||||
{
|
||||
"path": "src/main/jira",
|
||||
"purpose": "Backend client, credentials, issues and attachments"
|
||||
},
|
||||
{
|
||||
"path": "src/main/ipc",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"purpose": "Desktop transport handlers"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime/rpc/methods",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"purpose": "Runtime RPC methods"
|
||||
},
|
||||
{
|
||||
"path": "src/shared",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"purpose": "Flat Jira contracts and domain rules"
|
||||
},
|
||||
{
|
||||
"path": "src/shared/rpc-contract",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"purpose": "RPC parameter schemas"
|
||||
},
|
||||
{
|
||||
"path": "src/renderer/src/components/task-page/jira",
|
||||
"purpose": "Provider-specific Task Page UI"
|
||||
},
|
||||
{
|
||||
"path": "src/renderer/src/store/slices",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"purpose": "Renderer state"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"domain": "Linear",
|
||||
"locations": [
|
||||
{
|
||||
"path": "src/main/linear",
|
||||
"purpose": "Backend client, issue context, mutations and workspace credentials"
|
||||
},
|
||||
{
|
||||
"path": "src/main/ipc",
|
||||
"basenamePattern": "^linear(?:[-.]|$)",
|
||||
"purpose": "Desktop transport handlers"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime",
|
||||
"basenamePattern": "^runtime-linear(?:[-.]|$)",
|
||||
"purpose": "Host command implementations"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime/rpc/methods",
|
||||
"basenamePattern": "^linear(?:[-.]|$)",
|
||||
"purpose": "Runtime RPC methods"
|
||||
},
|
||||
{
|
||||
"path": "src/shared/linear",
|
||||
"purpose": "Existing domain contract folder"
|
||||
},
|
||||
{
|
||||
"path": "src/renderer/src/components/task-page/linear",
|
||||
"purpose": "Provider-specific Task Page UI"
|
||||
},
|
||||
{
|
||||
"path": "src/renderer/src/store/slices",
|
||||
"basenamePattern": "^linear(?:[-.]|$)",
|
||||
"purpose": "Renderer state"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"domain": "Terminals",
|
||||
"locations": [
|
||||
{
|
||||
"path": "src/renderer/src/components/terminal-pane",
|
||||
"purpose": "Desktop terminal UI and input/output behavior"
|
||||
},
|
||||
{
|
||||
"path": "src/main/ipc/pty",
|
||||
"purpose": "Electron terminal request handling"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime/terminal",
|
||||
"purpose": "Host terminal ownership and lifecycle"
|
||||
},
|
||||
{
|
||||
"path": "src/main/daemon",
|
||||
"purpose": "Persistent terminal process host"
|
||||
},
|
||||
{
|
||||
"path": "src/packages/process-host",
|
||||
"purpose": "Subprocess execution and termination"
|
||||
},
|
||||
{
|
||||
"path": "mobile/src/transport",
|
||||
"purpose": "Phone transport including terminal streams"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"domain": "Agent chat",
|
||||
"locations": [
|
||||
{
|
||||
"path": "src/renderer/src/components/native-chat",
|
||||
"purpose": "Desktop chat presentation and composer"
|
||||
},
|
||||
{
|
||||
"path": "src/main/native-chat",
|
||||
"purpose": "Agent session wire, journals and transcript integration"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime",
|
||||
"basenamePattern": "^(?:structured-)?agent-session(?:[-.]|$)",
|
||||
"purpose": "Session runtime lifecycle"
|
||||
},
|
||||
{
|
||||
"path": "src/main/claude",
|
||||
"purpose": "Claude provider adapter"
|
||||
},
|
||||
{
|
||||
"path": "src/main/codex",
|
||||
"purpose": "Codex provider adapter"
|
||||
},
|
||||
{
|
||||
"path": "mobile/src/session",
|
||||
"purpose": "Mobile session and chat interface"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"domain": "Workspace state",
|
||||
"locations": [
|
||||
{
|
||||
"path": "src/shared/workspace-layout",
|
||||
"purpose": "Layout commands and invariants"
|
||||
},
|
||||
{
|
||||
"path": "src/main/runtime/sidebar-inputs",
|
||||
"purpose": "Host-owned sidebar facts"
|
||||
},
|
||||
{
|
||||
"path": "src/main/persistence",
|
||||
"purpose": "Durable profile state, migration and restoration"
|
||||
},
|
||||
{
|
||||
"path": "src/renderer/src/store/slices",
|
||||
"purpose": "Renderer state projections and interactions"
|
||||
},
|
||||
{
|
||||
"path": "src/main/ipc/worktrees",
|
||||
"purpose": "Workspace discovery and desktop requests"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"proposals": [
|
||||
{
|
||||
"id": "ipc-linear",
|
||||
"phase": 1,
|
||||
"priority": "pilot",
|
||||
"risk": "lower",
|
||||
"directory": "src/main/ipc",
|
||||
"basenamePattern": "^linear(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/ipc/linear",
|
||||
"stripPrefix": "linear-",
|
||||
"entryStem": "linear",
|
||||
"targetEntryStem": "register-handlers",
|
||||
"reason": "Seven adjacent transport files already form a named provider family. Keep them separate from the backend client.",
|
||||
"review": "Preserve exported registration symbols, IPC channel strings, request validation and registration order. Resolve any file-versus-directory entry ambiguity explicitly."
|
||||
},
|
||||
{
|
||||
"id": "ipc-jira",
|
||||
"phase": 1,
|
||||
"priority": "pilot",
|
||||
"risk": "lower",
|
||||
"directory": "src/main/ipc",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/ipc/jira",
|
||||
"stripPrefix": "jira-",
|
||||
"entryStem": "jira",
|
||||
"targetEntryStem": "register-handlers",
|
||||
"reason": "Jira registration, request validation, cancellation and metadata handling belong together within IPC.",
|
||||
"review": "Preserve channels, cancellation ownership, site scope and registration order. Keep src/main/jira as the backend domain."
|
||||
},
|
||||
{
|
||||
"id": "linear-issue-context",
|
||||
"phase": 1,
|
||||
"priority": "pilot",
|
||||
"risk": "moderate",
|
||||
"directory": "src/main/linear",
|
||||
"basenamePattern": "^issue-context(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/linear/issue-context",
|
||||
"stripPrefix": "issue-context-",
|
||||
"entryStem": "issue-context",
|
||||
"targetEntryStem": "read-context",
|
||||
"reason": "The issue-context family covers resolution, optional includes, pagination and relations around one public read operation.",
|
||||
"review": "Keep the current-context, linked-workspace, inline-media and partial-error behavior intact; choose the public read-context entry explicitly."
|
||||
},
|
||||
{
|
||||
"id": "jira-attachments",
|
||||
"phase": 1,
|
||||
"priority": "pilot",
|
||||
"risk": "moderate",
|
||||
"directory": "src/main/jira",
|
||||
"basenamePattern": "^attachment(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/jira/attachments",
|
||||
"stripPrefix": "attachment-",
|
||||
"reason": "Attachment discovery, authenticated image fetching, grants, caching and protocols are a concrete subdomain.",
|
||||
"review": "Check image protocol registration, credential scope, cancellation and URL permissions; retain authenticated-request in its existing role."
|
||||
},
|
||||
{
|
||||
"id": "ephemeral-vm",
|
||||
"phase": 1,
|
||||
"priority": "pilot",
|
||||
"risk": "moderate",
|
||||
"directory": "src/main",
|
||||
"basenamePattern": "^ephemeral-vm(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/ephemeral-vm",
|
||||
"stripPrefix": "ephemeral-vm-",
|
||||
"reason": "VM provisioning, recipe execution, resume and SSH cleanup currently occupy the application root.",
|
||||
"review": "Preserve provisioning and cleanup ownership, folder workspace support and remote environment behavior."
|
||||
},
|
||||
{
|
||||
"id": "task-page-leftovers",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "moderate",
|
||||
"directory": "src/renderer/src/components",
|
||||
"basenamePattern": "^(?:use-)?task-page(?:[-.]|$)|^TaskPage",
|
||||
"targetDirectory": "src/renderer/src/components/task-page",
|
||||
"reason": "Finish the existing feature folder instead of creating another feature root. Most provider routing needs a later review inside its existing provider folders.",
|
||||
"review": "Preserve React component identity, lazy-loading boundaries and task draft ownership. Retain basenames and route provider-specific files to existing children; split this scope into provider-sized PRs.",
|
||||
"routes": [
|
||||
{
|
||||
"basenamePattern": "^(?:use-)?task-page-jira(?:[-.]|$)|^TaskPageJira",
|
||||
"targetDirectory": "src/renderer/src/components/task-page/jira"
|
||||
},
|
||||
{
|
||||
"basenamePattern": "^(?:use-)?task-page-linear(?:[-.]|$)|^TaskPageLinear",
|
||||
"targetDirectory": "src/renderer/src/components/task-page/linear"
|
||||
},
|
||||
{
|
||||
"basenamePattern": "^(?:use-)?task-page-github(?:[-.]|$)|^TaskPageGitHub",
|
||||
"targetDirectory": "src/renderer/src/components/task-page/github"
|
||||
},
|
||||
{
|
||||
"basenamePattern": "^(?:use-)?task-page-gitlab(?:[-.]|$)|^TaskPageGitLab",
|
||||
"targetDirectory": "src/renderer/src/components/task-page/gitlab"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-repository",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "moderate",
|
||||
"directory": "src/renderer/src/components/settings",
|
||||
"basenamePattern": "^(?:Repository|repository-)",
|
||||
"targetDirectory": "src/renderer/src/components/settings/repository",
|
||||
"reason": "Repository settings have one user-facing owner and a large mixed family of components, state and tests.",
|
||||
"review": "Keep draft-state boundaries and workspace-window routing; do not introduce an all-settings barrel."
|
||||
},
|
||||
{
|
||||
"id": "store-agent-status",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "moderate",
|
||||
"directory": "src/renderer/src/store/slices",
|
||||
"basenamePattern": "^agent-status(?:[-.]|$)",
|
||||
"targetDirectory": "src/renderer/src/store/slices/agent-status",
|
||||
"reason": "Status reduction, subscriptions and tests are scattered beside unrelated Zustand slices.",
|
||||
"review": "Keep the root store as the composition point. Preserve subscription and reduction semantics; keep an explicit slice entry."
|
||||
},
|
||||
{
|
||||
"id": "renderer-session-rules",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "moderate",
|
||||
"directory": "src/renderer/src/lib",
|
||||
"basenamePattern": "^structured-agent-session(?:[-.]|$)",
|
||||
"targetDirectory": "src/renderer/src/lib/agent-session",
|
||||
"reason": "A concrete session-rule family can leave the 1,292-file renderer lib root without inventing a generic helpers directory.",
|
||||
"review": "Keep view rules in renderer and host rules in runtime. Review boundaries against native-chat before expanding the selection."
|
||||
},
|
||||
{
|
||||
"id": "ipc-filesystem",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/ipc",
|
||||
"basenamePattern": "^(?:filesystem|parcel-watcher)(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/ipc/filesystem",
|
||||
"reason": "Consolidate the flat filesystem and watcher families with the existing filesystem folder, then consider a watcher child.",
|
||||
"review": "Preserve parcel worker output names and process boundaries, file permissions, cancellation and mixed local/remote paths. Review the existing filesystem entry before moving."
|
||||
},
|
||||
{
|
||||
"id": "ipc-pty",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/ipc",
|
||||
"basenamePattern": "^pty(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/ipc/pty",
|
||||
"reason": "Consolidate flat terminal transport files into the existing pty tree after agreeing its ownership boundaries.",
|
||||
"review": "Preserve pane ownership, host identity, lifecycle ordering, Windows/WSL paths and daemon compatibility; use existing pty children when ownership is clear."
|
||||
},
|
||||
{
|
||||
"id": "shared-jira",
|
||||
"phase": 2,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/shared",
|
||||
"basenamePattern": "^jira(?:[-.]|$)",
|
||||
"targetDirectory": "src/shared/jira",
|
||||
"reason": "Give pure Jira contracts and rules a domain home, matching the existing shared Linear domain.",
|
||||
"review": "Keep RPC schemas at their contract boundary unless a separate decision changes that layout. Preserve wire fields and all desktop/mobile consumers."
|
||||
},
|
||||
{
|
||||
"id": "browser-cookie",
|
||||
"phase": 2,
|
||||
"priority": "medium",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/browser",
|
||||
"basenamePattern": "^browser-cookie(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/browser/cookies",
|
||||
"reason": "Cookie reading, importing and cleanup are a concrete subsystem inside a 611-file directory.",
|
||||
"review": "Review browser session ownership, credential handling and native platform adapters. Do not combine this with browser API changes."
|
||||
},
|
||||
{
|
||||
"id": "cloud-relay-assignment",
|
||||
"phase": 2,
|
||||
"priority": "medium",
|
||||
"risk": "higher",
|
||||
"directory": "cloud/apps/relay/src",
|
||||
"basenamePattern": "^assignment(?:[-.]|$)",
|
||||
"targetDirectory": "cloud/apps/relay/src/assignment",
|
||||
"reason": "Relay assignment is a named operational responsibility in an otherwise flat service root.",
|
||||
"review": "Use cloud's independent checks and deployment ownership. Preserve director/cell roles and rolling-version wire compatibility."
|
||||
},
|
||||
{
|
||||
"id": "tooling-mobile-web",
|
||||
"phase": 2,
|
||||
"priority": "medium",
|
||||
"risk": "moderate",
|
||||
"directory": "config/scripts",
|
||||
"basenamePattern": "^mobile-web-app(?:[-.]|$)",
|
||||
"targetDirectory": "config/scripts/mobile-web-app",
|
||||
"reason": "Group one product-specific tool family; build-, run- and check- prefixes describe verbs, not ownership.",
|
||||
"review": "Update package scripts, CI workflows, script-relative URLs and test fixture copying. Separate a move from changing the generated web engines."
|
||||
},
|
||||
{
|
||||
"id": "shared-session",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/shared",
|
||||
"basenamePattern": "^(?:structured-)?agent-session(?:[-.]|$)",
|
||||
"targetDirectory": "src/shared/agent-session",
|
||||
"reason": "Agent-session contracts are a large cross-surface domain currently encoded in filenames.",
|
||||
"review": "This is an initial ownership inventory, not a single PR. Identify public contracts and subdomains first; keep old-client fences and generated RPC files separate."
|
||||
},
|
||||
{
|
||||
"id": "shared-native-chat",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/shared",
|
||||
"basenamePattern": "^native-chat(?:[-.]|$)",
|
||||
"targetDirectory": "src/shared/native-chat",
|
||||
"reason": "Chat data and pure rules need a concrete domain home rather than hundreds of sibling names.",
|
||||
"review": "Review overlap with agent-session responsibility; retain wire-facing names and move in consumer-sized batches."
|
||||
},
|
||||
{
|
||||
"id": "shared-remote-runtime",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/shared",
|
||||
"basenamePattern": "^remote-runtime(?:[-.]|$)",
|
||||
"targetDirectory": "src/shared/remote-runtime",
|
||||
"reason": "Remote runtime handshake, capabilities and transport contracts are one discoverable protocol domain.",
|
||||
"review": "Preserve mixed-version client/server behavior. Rename source paths without renaming wire methods, protocol tags or capability fields."
|
||||
},
|
||||
{
|
||||
"id": "runtime-session",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/runtime",
|
||||
"basenamePattern": "^(?:structured-)?agent-session(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/runtime/agent-session",
|
||||
"reason": "Session lifecycle and persistence should be discoverable below the runtime composition layer.",
|
||||
"review": "Partition acquisition, dispatch and observation by ownership before implementing; keep OrcaRuntime composition files and remote RPC surfaces explicit."
|
||||
},
|
||||
{
|
||||
"id": "mobile-native-chat",
|
||||
"phase": 3,
|
||||
"priority": "high",
|
||||
"risk": "higher",
|
||||
"directory": "mobile/src/session",
|
||||
"basenamePattern": "^(?:use-)?mobile-native-chat(?:[-.]|$)|^(?:use)?MobileNativeChat",
|
||||
"targetDirectory": "mobile/src/session/native-chat",
|
||||
"reason": "The phone's chat family is mixed into a 722-file session folder, including presentation, interaction and hooks.",
|
||||
"review": "Use mobile's own workspace checks. Preserve native/web splits, hook identity, transport behavior and embedded WebView source paths."
|
||||
},
|
||||
{
|
||||
"id": "claude-structured",
|
||||
"phase": 3,
|
||||
"priority": "medium",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/claude",
|
||||
"basenamePattern": "^claude-structured(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/claude/structured",
|
||||
"reason": "Structured Claude session execution is a distinct provider mode within an already named provider folder.",
|
||||
"review": "Review acquisition, process and journal boundaries. Preserve CLI launch flags, account routing and session recovery."
|
||||
},
|
||||
{
|
||||
"id": "codex-app-server",
|
||||
"phase": 3,
|
||||
"priority": "medium",
|
||||
"risk": "higher",
|
||||
"directory": "src/main/codex",
|
||||
"basenamePattern": "^codex-app-server(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/codex/app-server",
|
||||
"reason": "The app-server protocol family is a more useful boundary than the broad codex filename prefix.",
|
||||
"review": "Preserve protocol, account routing, reconnect and recovery behavior. Keep generic Codex launch code outside this subtree."
|
||||
},
|
||||
{
|
||||
"id": "persistence-leftovers",
|
||||
"phase": 3,
|
||||
"priority": "medium",
|
||||
"risk": "higher",
|
||||
"directory": "src/main",
|
||||
"basenamePattern": "^persistence(?:[-.]|$)",
|
||||
"targetDirectory": "src/main/persistence",
|
||||
"reason": "The existing persistence subtree and flat root tests split the same discovery trail.",
|
||||
"review": "Mostly tests and support may move here. Keep the current persistence entry explicit; inspect source-reading tests and fixtures before relocating. Preserve on-disk formats."
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,419 @@
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { format } from 'oxfmt'
|
||||
import { listTrackedFiles } from '../../../config/scripts/ratchet-baseline.mjs'
|
||||
import { collectModuleSpecifiers } from '../../../config/scripts/static-module-specifiers.mjs'
|
||||
import { isTestOnlySourcePath } from '../../../config/scripts/test-only-source-path.mjs'
|
||||
import {
|
||||
createWorkspaceSourceResolver,
|
||||
workspacePackageManifests
|
||||
} from '../../../config/scripts/workspace-source-exports.mjs'
|
||||
|
||||
const auditDirectory = import.meta.dirname
|
||||
const root = path.resolve(auditDirectory, '../../..')
|
||||
const plan = JSON.parse(readFileSync(path.join(auditDirectory, 'audit-plan.json'), 'utf8'))
|
||||
const output = path.join(auditDirectory, 'codebase-map.json')
|
||||
const check = process.argv.includes('--check')
|
||||
const savedHead =
|
||||
check && existsSync(output) ? JSON.parse(readFileSync(output, 'utf8')).snapshot?.gitHead : null
|
||||
if (
|
||||
check &&
|
||||
(typeof savedHead !== 'string' || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(savedHead))
|
||||
) {
|
||||
throw new Error(
|
||||
'codebase-map.json is missing valid snapshot provenance; run this script without --check'
|
||||
)
|
||||
}
|
||||
const jsPattern = /\.[cm]?[jt]sx?$/
|
||||
const nativePattern = /\.(?:swift|kt|kts|java|rs|c|cc|cpp|h|hpp|m|mm|py|sh|ps1|cmd|bat)$/
|
||||
const generatedPattern = /(?:^|\/)generated\/|\.generated\.[^.]+$/
|
||||
const tracked = listTrackedFiles(root, []).filter(
|
||||
(file) => !file.startsWith('docs/audits/module-organization/')
|
||||
)
|
||||
const files = tracked.filter((file) => existsSync(path.join(root, file))).sort()
|
||||
const allFiles = new Set(files)
|
||||
const caseFoldedFiles = new Set(files.map((file) => file.toLowerCase()))
|
||||
const sources = files.filter((file) => jsPattern.test(file))
|
||||
const sourceSet = new Set(sources)
|
||||
const areas = [...plan.areas].sort((a, b) => b.path.length - a.path.length)
|
||||
const areaOf = (file) =>
|
||||
areas.find((area) => area.path === '.' || file.startsWith(`${area.path}/`)).id
|
||||
const isProduction = (file) => !isTestOnlySourcePath(file) && !generatedPattern.test(file)
|
||||
const directories = new Map()
|
||||
const familyMembers = new Map()
|
||||
const digest = createHash('sha256')
|
||||
const edges = new Map()
|
||||
const reverse = new Map()
|
||||
const unresolved = []
|
||||
const parseFailures = []
|
||||
const importStats = { specifiers: 0, localCode: 0, localNonCode: 0, externalOrUnmodeled: 0 }
|
||||
const workspace = createWorkspaceSourceResolver(root)
|
||||
const workspaceNames = workspacePackageManifests(root).map(
|
||||
(file) => JSON.parse(readFileSync(path.join(root, file), 'utf8')).name
|
||||
)
|
||||
|
||||
function directoryRecord(directory) {
|
||||
if (!directories.has(directory)) {
|
||||
directories.set(directory, {
|
||||
path: directory,
|
||||
area: areaOf(`${directory}/_`),
|
||||
directTrackedFiles: 0,
|
||||
recursiveTrackedFiles: 0,
|
||||
directJsTs: 0,
|
||||
directProductionJsTs: 0,
|
||||
directTestSupportJsTs: 0,
|
||||
directGeneratedJsTs: 0,
|
||||
recursiveJsTs: 0,
|
||||
recursiveNativeOrShell: 0,
|
||||
longestCodeBasename: null,
|
||||
longestCodeBasenameCharacters: 0
|
||||
})
|
||||
}
|
||||
return directories.get(directory)
|
||||
}
|
||||
|
||||
function familyTokens(file) {
|
||||
return path.posix
|
||||
.basename(file)
|
||||
.replace(jsPattern, '')
|
||||
.split('.')[0]
|
||||
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
||||
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
||||
.toLowerCase()
|
||||
.replace(/^use-/, '')
|
||||
.split('-')
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const parent = path.posix.dirname(file)
|
||||
const direct = directoryRecord(parent)
|
||||
direct.directTrackedFiles++
|
||||
const js = jsPattern.test(file)
|
||||
if (js) {
|
||||
direct.directJsTs++
|
||||
if (generatedPattern.test(file)) {
|
||||
direct.directGeneratedJsTs++
|
||||
} else if (isTestOnlySourcePath(file)) {
|
||||
direct.directTestSupportJsTs++
|
||||
} else {
|
||||
direct.directProductionJsTs++
|
||||
}
|
||||
const basename = path.posix.basename(file)
|
||||
if (basename.length > direct.longestCodeBasenameCharacters) {
|
||||
direct.longestCodeBasename = basename
|
||||
direct.longestCodeBasenameCharacters = basename.length
|
||||
}
|
||||
const tokens = familyTokens(file)
|
||||
for (let n = 1; n <= Math.min(3, tokens.length - 1); n++) {
|
||||
const prefix = tokens.slice(0, n).join('-')
|
||||
const key = `${parent}/${prefix}`
|
||||
if (!familyMembers.has(key)) {
|
||||
familyMembers.set(key, { directory: parent, prefix, files: [] })
|
||||
}
|
||||
familyMembers.get(key).files.push(file)
|
||||
}
|
||||
}
|
||||
for (let directory = parent; ; directory = path.posix.dirname(directory)) {
|
||||
const record = directoryRecord(directory)
|
||||
record.recursiveTrackedFiles++
|
||||
if (js) {
|
||||
record.recursiveJsTs++
|
||||
}
|
||||
if (nativePattern.test(file)) {
|
||||
record.recursiveNativeOrShell++
|
||||
}
|
||||
if (directory === '.') {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLocal(file, specifier) {
|
||||
let target = null
|
||||
if (specifier.startsWith('.')) {
|
||||
target = path.posix.normalize(path.posix.join(path.posix.dirname(file), specifier))
|
||||
} else if (specifier.startsWith('@renderer/')) {
|
||||
target = `src/renderer/src/${specifier.slice(10)}`
|
||||
} else if (specifier.startsWith('@/')) {
|
||||
const aliasRoot = file.startsWith('mobile/')
|
||||
? 'mobile/src'
|
||||
: file.startsWith('docs/site/')
|
||||
? 'docs/site/src'
|
||||
: 'src/renderer/src'
|
||||
target = `${aliasRoot}/${specifier.slice(2)}`
|
||||
} else if (
|
||||
workspaceNames.some((name) => specifier === name || specifier.startsWith(`${name}/`))
|
||||
) {
|
||||
try {
|
||||
target = workspace.resolve(specifier)?.file ?? null
|
||||
} catch {
|
||||
return { kind: 'unresolved', specifier }
|
||||
}
|
||||
}
|
||||
if (target === null) {
|
||||
return { kind: 'externalOrUnmodeled' }
|
||||
}
|
||||
target = target.split('?')[0]
|
||||
const stems = [target]
|
||||
if (/\.[cm]?js$/.test(target)) {
|
||||
stems.push(target.replace(/\.[cm]?js$/, ''))
|
||||
}
|
||||
for (const stem of stems) {
|
||||
for (const extension of [
|
||||
'',
|
||||
'.ts',
|
||||
'.tsx',
|
||||
'.mts',
|
||||
'.cts',
|
||||
'.js',
|
||||
'.mjs',
|
||||
'.cjs',
|
||||
'.jsx',
|
||||
'.json',
|
||||
'/index.ts',
|
||||
'/index.tsx',
|
||||
'/index.js',
|
||||
'/index.mjs',
|
||||
'/index.cjs'
|
||||
]) {
|
||||
const candidate = stem + extension
|
||||
if (allFiles.has(candidate)) {
|
||||
return { kind: sourceSet.has(candidate) ? 'localCode' : 'localNonCode', target: candidate }
|
||||
}
|
||||
}
|
||||
}
|
||||
return { kind: 'unresolved', specifier }
|
||||
}
|
||||
|
||||
for (const file of sources) {
|
||||
const contents = readFileSync(path.join(root, file), 'utf8')
|
||||
digest.update(file).update('\0').update(contents).update('\0')
|
||||
let specifiers
|
||||
try {
|
||||
specifiers = collectModuleSpecifiers(file, contents)
|
||||
} catch (error) {
|
||||
parseFailures.push({ file, error: error.message })
|
||||
continue
|
||||
}
|
||||
const dependencies = new Set()
|
||||
for (const specifier of specifiers) {
|
||||
importStats.specifiers++
|
||||
const resolution = resolveLocal(file, specifier)
|
||||
if (resolution.kind === 'unresolved') {
|
||||
unresolved.push({ file, specifier })
|
||||
} else {
|
||||
importStats[resolution.kind]++
|
||||
}
|
||||
if (resolution.kind !== 'localCode') {
|
||||
continue
|
||||
}
|
||||
dependencies.add(resolution.target)
|
||||
if (!reverse.has(resolution.target)) {
|
||||
reverse.set(resolution.target, new Set())
|
||||
}
|
||||
reverse.get(resolution.target).add(file)
|
||||
}
|
||||
edges.set(file, dependencies)
|
||||
}
|
||||
|
||||
function counted(values) {
|
||||
const counts = new Map()
|
||||
for (const value of values) {
|
||||
counts.set(value, (counts.get(value) ?? 0) + 1)
|
||||
}
|
||||
return [...counts]
|
||||
.map(([path, count]) => ({ path, count }))
|
||||
.sort((a, b) => b.count - a.count || a.path.localeCompare(b.path))
|
||||
}
|
||||
|
||||
function proposalDetails(proposal) {
|
||||
const pattern = new RegExp(proposal.basenamePattern)
|
||||
const selected = sources.filter(
|
||||
(file) =>
|
||||
path.posix.dirname(file) === proposal.directory && pattern.test(path.posix.basename(file))
|
||||
)
|
||||
if (!selected.length) {
|
||||
throw new Error(`Empty proposal: ${proposal.id}`)
|
||||
}
|
||||
const selectedSet = new Set(selected)
|
||||
const importers = new Set()
|
||||
const dependencies = new Set()
|
||||
let internalEdges = 0
|
||||
let inboundEdges = 0
|
||||
let outboundEdges = 0
|
||||
let productionInboundEdges = 0
|
||||
let productionInternalEdges = 0
|
||||
let productionOutboundEdges = 0
|
||||
const moves = selected.map((file) => {
|
||||
const basename = path.posix.basename(file)
|
||||
let renamed = basename
|
||||
if (proposal.entryStem && basename.startsWith(`${proposal.entryStem}.`)) {
|
||||
renamed = proposal.targetEntryStem + basename.slice(proposal.entryStem.length)
|
||||
} else if (proposal.stripPrefix && basename.startsWith(proposal.stripPrefix)) {
|
||||
renamed = basename.slice(proposal.stripPrefix.length)
|
||||
}
|
||||
const route = proposal.routes?.find((route) => new RegExp(route.basenamePattern).test(basename))
|
||||
const to = `${route?.targetDirectory ?? proposal.targetDirectory}/${renamed}`
|
||||
for (const importer of reverse.get(file) ?? []) {
|
||||
if (!selectedSet.has(importer)) {
|
||||
importers.add(importer)
|
||||
inboundEdges++
|
||||
if (isProduction(importer)) {
|
||||
productionInboundEdges++
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const dependency of edges.get(file) ?? []) {
|
||||
if (selectedSet.has(dependency)) {
|
||||
internalEdges++
|
||||
if (isProduction(file)) {
|
||||
productionInternalEdges++
|
||||
}
|
||||
} else {
|
||||
dependencies.add(dependency)
|
||||
outboundEdges++
|
||||
if (isProduction(file)) {
|
||||
productionOutboundEdges++
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
from: file,
|
||||
to,
|
||||
testSupport: isTestOnlySourcePath(file),
|
||||
targetExists: allFiles.has(to),
|
||||
targetCaseInsensitiveExists: caseFoldedFiles.has(to.toLowerCase())
|
||||
}
|
||||
})
|
||||
const destinations = new Set(moves.map((move) => move.to.toLowerCase()))
|
||||
if (destinations.size !== moves.length) {
|
||||
throw new Error(`Duplicate destination: ${proposal.id}`)
|
||||
}
|
||||
return {
|
||||
...proposal,
|
||||
status: 'proposed-not-applied',
|
||||
selectedFiles: selected.length,
|
||||
productionFiles: selected.filter(isProduction).length,
|
||||
testSupportFiles: selected.filter(isTestOnlySourcePath).length,
|
||||
existingTargetJsTs: directories.get(proposal.targetDirectory)?.recursiveJsTs ?? 0,
|
||||
collisionCount: moves.filter((move) => move.targetCaseInsensitiveExists).length,
|
||||
dependencyBoundary: {
|
||||
internalEdges,
|
||||
inboundEdges,
|
||||
outboundEdges,
|
||||
productionInternalEdges,
|
||||
productionInboundEdges,
|
||||
productionOutboundEdges,
|
||||
externalImporterFiles: [...importers].sort(),
|
||||
productionExternalImporterFiles: [...importers].filter(isProduction).sort(),
|
||||
outboundDependencyDirectories: counted(
|
||||
[...dependencies].map((file) => path.posix.dirname(file))
|
||||
),
|
||||
unresolvedSpecifiers: unresolved.filter(({ file }) => selectedSet.has(file))
|
||||
},
|
||||
moves
|
||||
}
|
||||
}
|
||||
|
||||
const proposals = plan.proposals.map(proposalDetails)
|
||||
const selectedBy = new Map()
|
||||
for (const proposal of proposals) {
|
||||
for (const { from } of proposal.moves) {
|
||||
if (selectedBy.has(from)) {
|
||||
throw new Error(`Overlapping proposals: ${from}`)
|
||||
}
|
||||
selectedBy.set(from, proposal.id)
|
||||
}
|
||||
}
|
||||
const areaEdges = []
|
||||
for (const [file, dependencies] of edges) {
|
||||
if (!isProduction(file)) {
|
||||
continue
|
||||
}
|
||||
for (const dependency of dependencies) {
|
||||
if (areaOf(file) !== areaOf(dependency)) {
|
||||
areaEdges.push(`${areaOf(file)} -> ${areaOf(dependency)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
const families = [...familyMembers.values()]
|
||||
.map(({ directory, prefix, files }) => ({
|
||||
directory,
|
||||
prefix,
|
||||
files: files.length,
|
||||
productionFiles: files.filter(isProduction).length,
|
||||
examples: files.filter(isProduction).slice(0, 3)
|
||||
}))
|
||||
.filter((family) => family.files >= 8 && family.productionFiles >= 3)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
b.files - a.files || `${a.directory}/${a.prefix}`.localeCompare(`${b.directory}/${b.prefix}`)
|
||||
)
|
||||
|
||||
const result = {
|
||||
schemaVersion: 1,
|
||||
snapshot: {
|
||||
gitHead:
|
||||
savedHead ??
|
||||
execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(),
|
||||
jsTsContentSha256: digest.digest('hex'),
|
||||
inventory:
|
||||
'Git-tracked paths with current working-tree contents; this audit directory excluded',
|
||||
missingTrackedPaths: tracked.filter((file) => !allFiles.has(file)),
|
||||
trackedFiles: files.length,
|
||||
jsTsFiles: sources.length,
|
||||
productionJsTsFiles: sources.filter(isProduction).length,
|
||||
testSupportJsTsFiles: sources.filter(isTestOnlySourcePath).length,
|
||||
generatedJsTsFiles: sources.filter((file) => generatedPattern.test(file)).length,
|
||||
nativeOrShellFiles: files.filter((file) => nativePattern.test(file)).length
|
||||
},
|
||||
methodology: {
|
||||
directoryCounts:
|
||||
'Direct and recursive counts include tracked test/support and generated files, with direct JS/TS breakdowns. Counts are files, not lines or runtime complexity.',
|
||||
production:
|
||||
'JS/TS not identified by the existing test-only-source-path policy or generated filename/directory convention. This includes tooling and declarations; it does not mean shipped product code.',
|
||||
families:
|
||||
'Overlapping discovery signals, not move recommendations. Kebab/Pascal/camel normalized; use- removed; first 1-3 tokens, with at least one remaining token; >=8 files and >=3 production-classified files. Do not sum families.',
|
||||
imports:
|
||||
'Existing collectModuleSpecifiers AST parser reused. Includes type imports, re-exports and recognized literal/const-string module loaders. Deduplicated directed file pairs for graph counts; inbound/outbound are direct, not transitive.',
|
||||
resolution:
|
||||
'Relative tracked paths, index modules, JS-to-TS source counterparts, @renderer/, context-aware @/ and root workspace orca-source exports. Exact non-code paths counted separately. Other bare packages/aliases not modeled.',
|
||||
limitations:
|
||||
'No call graph, runtime dependency proof or behavioral audit. Computed paths, new URL, filesystem reads, worker/spawn strings, package scripts and CI references are outside the import graph. Unresolved inputs and parser failures are explicit. Native/shell code is inventoried but imports are not analyzed.',
|
||||
proposals:
|
||||
'Human-reviewed candidate ownership scopes. Exact path suggestions and collision checks are supplied; broad proposals require smaller PRs and deeper semantic review. Basenames retained unless explicit stripPrefix/entryStem rules apply.'
|
||||
},
|
||||
areas: plan.areas.map((area) => ({
|
||||
...area,
|
||||
exclusiveTrackedFiles: files.filter((file) => areaOf(file) === area.id).length,
|
||||
exclusiveJsTsFiles: sources.filter((file) => areaOf(file) === area.id).length
|
||||
})),
|
||||
featureTrails: plan.featureTrails,
|
||||
directories: [...directories.values()].sort((a, b) => a.path.localeCompare(b.path)),
|
||||
prefixFamilies: families,
|
||||
productionAreaImportEdges: counted(areaEdges).map(({ path: edge, count }) => ({ edge, count })),
|
||||
importAnalysis: { ...importStats, unresolvedCount: unresolved.length, unresolved, parseFailures },
|
||||
proposals
|
||||
}
|
||||
const formatted = await format(
|
||||
output,
|
||||
`${JSON.stringify(result, null, 2)}\n`,
|
||||
JSON.parse(readFileSync(path.join(root, '.oxfmtrc.json'), 'utf8'))
|
||||
)
|
||||
if (formatted.errors.length > 0) {
|
||||
throw new Error(`Cannot format codebase map: ${JSON.stringify(formatted.errors)}`)
|
||||
}
|
||||
const serialized = formatted.code
|
||||
if (check) {
|
||||
if (!existsSync(output) || readFileSync(output, 'utf8') !== serialized) {
|
||||
throw new Error('codebase-map.json is stale; run this script without --check')
|
||||
}
|
||||
console.log(`Verified ${sources.length} JS/TS files and ${proposals.length} proposals`)
|
||||
} else {
|
||||
writeFileSync(output, serialized)
|
||||
console.log(
|
||||
`Wrote ${output}: ${sources.length} JS/TS files, ${directories.size} directories, ${proposals.length} proposals`
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user