Repository navigation
Refactor global architecture - #108
Merged
Merged
Conversation
Applies each upstream path as its own three-way patch so one moved path no longer aborts the whole sync. Paths Vatra renamed are retargeted via .github/upstream-moves.json; split, removed and unmapped paths become pending ports under .upstream-pending/ (exit code 4). moves.mjs regenerates detected renames and --check validates map coverage in check:web and CI.
Adds the pending-port and new-upstream-file steps for paths Vatra moved, split or removed.
Biome lints without formatting. lint, check:size and check:boundaries freeze today's findings in baselines: new violations fail, fixed ones must be locked in with --update, so the baselines only shrink. Layer rules follow app -> features -> integrations -> platform -> domain -> shared; other features are reachable only through their top-level files.
Every non-component .ts/.tsx file under src/ moves to kebab-case (components stay PascalCase.tsx); imports, mocks, worker URLs, comments, docs, NOTICE and the check baselines follow. The 709 renames are recorded in .github/upstream-moves.json so upstream edits to the old paths are retargeted by the sync.
The pending-navigation cases queued a one-off invoke result, which a watcher stat call could consume before the initial read; they now model the file like the growth case. Reload waits poll outside act() so React commits the new document, replacing fixed sleeps, and waits on lazy CodeMirror chunks get a longer timeout.
Relative imports stay within a feature or layer; 2054 cross-layer specifiers in 593 files now use @/ so moving a module no longer means counting ../ segments.
--update --transfer accepts per-file regressions when no kind's repo-wide total grows, so code (and its existing findings) can move to new modules. The file-size check never accepts a new oversized file this way.
The move map gains an extractions section; the sync still applies such paths and reports the moves under extracted, so the agent ports hunks that touch moved declarations into their new modules.
…main
Session, block, turn, attachment, queue and harness types, plus pure
session helpers, move out of features/sessions/model/session.ts into
src/domain/{harness,session}; the composer card contracts they reference
(inbox, linked work items, notes, orchestration proposals, handoff) move
to src/domain as well. Session factories that read provider settings
stay in the sessions feature. The notes barrel is removed in favour of
direct imports. The move map records the split, the extractions and
the barrel removal.
Merges origin/main (fda78db, PR #39): Vatra -> MOLFAR, version 1.0.0, MonoCode-era migration code removed, sphere brand visual, legacy worktree prefixes. Resolution on the refactor side: - vatraToolCall moves to molfar-tool-call.ts (kebab-case); the Block flag rename (vatra/monocode -> molfar) lands in src/domain/session/block.ts - operatorUserPrompt is gone with the legacy command aliases - legacy storage migration modules stay deleted - main's new hooks use kebab-case file names - refactor-only files (sync tooling, ratchets, docs) use the new name; the legacy vatra/ worktree branch prefixes stay as main has them - move map: renames regenerated, removed entries for dropped migration code and replaced brand assets - ratchet baselines record main's growth of five Rust files and the SphereVisual canvas finding, matching its sibling visuals
Model types and pure id/setting helpers move to src/domain/models; the bundled catalog, the live overlay store and model resolution move to src/integrations/harness/core/models, so harness providers and the host no longer import the sessions feature. Preference persistence and picker state stay in features/sessions/model/models.ts. HARNESS_ORDER duplicated HARNESSES and is dropped. The bundled-catalog test suite moves next to the catalog, and the Pi catalog test mocks the module it actually imports. The move map records both splits.
…tions The app <-> host protocol moves to src/domain/remote/protocol.ts, and the renderer's remote session client (remoteRequest, session sync, attachment previews) moves to src/integrations/remote, so the host and its end-to-end sync test no longer reach into the connections feature. Pure pieces the host and harness providers share move to src/domain: attachment MIME checks and prompt blocks, worktree branch names, the GitHub work-item URL parser, project search results, and the file and working-tree DTOs of the Tauri commands. The IO that produces them stays where it was. Tests follow their modules; the Codex attachment test imports prompt helpers from their new home and the remote command test mocks the module remote-commands actually imports. The move map records the renames and extractions.
The automated moves in the 4.1-4.3 extractions left imports on one long line or split without trailing commas. Only import declarations that changed since then are reformatted, in Prettier's style; no other line moves. The file-size baseline records the resulting few lines in 36 files that are already over the limit and get split later.
…low features Harness providers no longer import the source-control and providers features: - git commit/PR/branch prompt helpers move to src/domain/git/git-text.ts - the runtime provider binary path registry that child.ts reads moves to src/integrations/harness/core/provider-binary-paths.ts - provider account id helpers move to src/domain/harness/provider-account.ts - rate limit types, windows and states move to src/domain/rate-limits; provider usage payload parsing moves to integrations (shared window parsing in core/usage-window.ts, Claude, Codex and OpenCode parsers next to their providers) Display formatters, poll intervals and account persistence stay in the providers feature. Rate limit tests move next to the code they test (23 before and after). The move map records the renames, the rate limit split and the account extraction.
Production code: - isFailedStatus moves to src/domain/session/tool-status.ts - planTurnKey, isProviderFailureText and isReviewablePlan move to src/domain/session/plan.ts - normalizeProjectPath moves to src/shared/lib/project-path.ts and the localStorage flag helpers to src/shared/lib/storage-flags.ts - the Claude hooks preference moves next to the Claude adapter that reads it (providers/claude/claude-hooks-setting.ts) Tests: - blankSession (domain) builds the session literal newSession used to inline; harness tests use core/test-session.ts, which is newSession without saved model preferences - the Codex approval UI and attachment tests test features, so they move under features/sessions - the plan-key restart test, which needs session persistence, moves to features/sessions/data; the transcript folding checks for background commands move to a transcript-activity test built from the blocks the Claude harness test pins The move map records the renames and extractions.
IS_MAC, IS_WIN, IS_LINUX, HAS_NATIVE_GLASS and the shortcut glyphs read navigator only, so they move from src/platform/tauri/platform.ts to src/shared/lib/platform.ts, where shared UI can use them. Tests mock the new path.
… to shared GlassBackdrop, RailAction, ClosingTab (TabWidthMotion) and IconButton (out of TitleBar) have no app dependencies and are used by features, so they move to src/shared/ui. The alert/confirm queue moves to src/shared/lib/app-dialog.ts; the AppDialog host that renders it stays in the app shell. Tests mock the new app-dialog path.
…a feature WindowControls (the Tauri window buttons) and the overlay navigation pieces split out of TitleBar (OverlayNav, TabVisitNav, DevModeLabel, DevModeSlot) move to src/features/window-chrome/ui, so surface features no longer import the app shell. Tests mock the new WindowControls path.
… feature The updater flow, update prompt and install, recheck, installed-update notice, fork updater policy and release notes model move from src/app/model to src/features/updates/model with their tests, and ReleaseNotesSurface to src/features/updates/ui, so Settings, the workspace tabs and the file pane no longer import the app. The dialogs and rail cards that present updates stay in the app shell. The bundled CHANGELOG import is re-pointed by hand (the move tooling does not rewrite ?raw imports), and app shell tests mock the new paths.
… workspace The Tab summary TitleBar renders is built by the workspace tab model, so it moves to src/features/workspace/model/title-tab.ts. Tests that render ProjectRail or TitleBar move from features to src/app/shell, and the type-only imports the move produced use import type.
- basename moves from platform/tauri/fs to shared/lib/paths - attachment size limits move to src/domain/session/attachment-limits.ts, and native clipboard paste-to-attachments moves from platform/tauri/ clipboard to features/sessions/model/clipboard-attachments.ts; the clipboard round-trip test, which builds attachments, moves with it - the Obsidian vault command client moves from platform/tauri/vault to features/knowledge/model/vault/vault-client.ts, the only caller - useColorScheme and PrivateEmail read settings, so they move into the settings feature - the HarnessIcon rendering check leaves the shared icon catalog test Tests mock the new paths; fs mocks no longer stub basename, which callers now take from shared.
The Confluence API cache, hidden spaces, kinds, mentions and prompt helpers lived under inbox/model/confluence, which made them internal to Inbox while agent-app, sessions and settings use them. They move to src/features/confluence/model with their tests, clearing the last layer boundary violations; the boundaries baseline is now empty.
Nothing rendered the real App in tests. app-harness renders it in happy-dom against Tauri's official IPC, window and event mocks, with a fake live harness the test drives. ipc-defaults answers every command with an empty value of the type its invoke call expects. The first tests pin boot, submitting a prompt and streaming the reply, persistence (user turn at once, answered transcript after the debounce, nothing outside a project), opening, closing and cycling tabs, and the terminal toggle.
The fake harness records steers, cancellations and approval replies. New Workspace characterization covers a mid-turn follow-up steering the running turn, Stop cancelling it, answering an approval from the transcript, split_right, Close Others / Close All (which starts the last tab over on a blank session), and reordering title tabs by drag through the real useAnimatedReorder gesture.
The characterization suite failed under machine load because it slept fixed intervals. The harness now waits for the composer before returning and before typing, click waits for its button, and every test waits for the state it expects (until) with a 60s budget for cold boots.
… listeners' removal on unmount
…load save into useWindowUnload
…live, running chat hides)
…into useWindowClose
…eFamiliarThreadMigration
…into useHarnessBootRefresh
…sets in useSessionStorage
…nd removal stop in useHarnessTurnFlows
…ate cards and add-to-chat deps in useInboxFlows
… ref mirrors into useWorkspaceCore
…a useWorkspaceFlows facade
…hrome hook inputs in useWorkspaceViewFlows
…renders Root cause: useWorkspaceSnapshotAutosave recorded the snapshot key before its 250 ms timer fired. A re-render inside that window (same snapshot, any listed dep changed) ran the cleanup, which cleared the timer, and then returned early because the key already matched, so the save never ran until the layout changed again. The key is now recorded when the timer fires, so an unchanged re-render restarts the debounce instead of dropping it. A failed save clears the key and logs, so the next render retries instead of treating it as saved.
…e written Root cause: the close handler fired persistLiveTranscripts and hideCurrentWindow without awaiting either, and upsertSession does async work before its IPC write, so hide_window always reached the host before session_upsert. persistLiveTranscripts also swallowed every write failure. A running chat (and a kept-alive macOS Familiar) now awaits the transcript writes before hiding, bounded by a 2 s wait so a hung write cannot keep the window up. persistLiveTranscripts returns the settled results; failed writes and a timed-out wait are logged. The close characterization tests now pin session_upsert before hide_window.
…_ready Root cause: useSessionAutosave's 650 ms timer stayed armed through the coordinated quit. handleQuitRequested writes every chat itself (running turns marked interrupted) and reports quit_ready, then the timer fired and wrote the pending, unmarked copies, racing the exit and able to overwrite the interrupted marks. A write that comes due while the app is quitting now stays pending. The quit already wrote the same latest state (sessionsRef and the autosave read one session store), so nothing is lost; if the quit is called off, the next change writes the pending chats as before.
The project filter lived in BoardView's useState, and LocalSurfaceViews unmounts the board when another surface opens, so every visit and every launch started on All projects. The choice now lives in a board store (model/board-project, the same module-store pattern as board-focus) persisted under the molfar.taskBoard.* localStorage keys the board already uses. useBoardProject reads it with useSyncExternalStore. A saved project that is no longer listed shows All projects without being erased, so a project list that is still loading cannot wipe the choice. Failed writes are logged.
Biome reports diagnostics with native separators, so on Windows every file (host\engine.ts, src\...\stage-fail.ts) missed its forward-slash baseline entry and all 845 baselined findings showed up as new.
…d Linux
Root cause: happy-dom builds navigator.platform from the host
("X11; Darwin arm64" on macOS), and IS_WIN's /Win/i matches "Darwin",
so the close suite silently ran the Windows tray branch on macOS. On
Linux CI the platform reads as Linux, which has no tray: an idle window
is destroyed and a busy one asks first, so waiting for hide_window
timed out. The app behaves correctly on Linux.
The suite now pins Win32 explicitly, and a Linux variant pins that a
stored close-to-tray choice is ignored there.
…n saves Root cause: renderApp returns 50 ms after the composer shows, while boot's first layout save is still on its 250 ms debounce. On a fast host the file opens inside that window and supersedes it; on a slow runner (Ubuntu CI) it lands after the mark, so two saves were counted. Not platform-specific: reproduced on macOS with a 400 ms pause. The test now waits for boot's save before marking; the single-save assertion is unchanged.
Root cause: the kebab-case renames (familiarMessaging, sessionStateStore, streamingPerformance) never reached tests/browser, which no tsc project includes. Vite failed to build the transcript and workspace-performance fixture pages, so every test on them timed out. Also points stale type-only imports (Block, QueuedMessage, Tab) at the modules that now export them; esbuild erased those, so they built, but they no longer type-checked.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refactor global architecture
Long-running, behaviour-preserving refactor that brings the whole codebase (including MonoCode-derived modules) to the project standard: small modules, a real workspace state layer, enforced layer boundaries, and upstream sync that survives file moves/splits. Spec, plan and task tracker:
docs/specs/global-refactor/(ADRdocs/adr/0001-own-structure-port-upstream-changes.md).Status: work in progress — not ready to merge. Stages 0–4 are done, Stage 5 is partially done (paused 2026-10-19), and Stages 6–14 have not started. This PR is open to show the full diff against
mainand run CI.Goals
file-size.exceptions.jsonwith a reason.<Sidebar>had 105 props).check:web.Scope (done)
Stage 1 — Upstream sync tooling
patch-sync.mjsapplies the upstream diff per path withgit apply --3wayinstead of one atomic apply. Paths that MOLFAR moved are rewritten through the move map. Split or missing paths go to.upstream-pending/with exit code4, so one missing path no longer aborts the whole sync..github/upstream-moves.json(renames generated from git, splits written by hand) is validated bycheck:moves.upstream/mainproduced an index identical to the old script.Stage 2 — Guard rails
noImportCycles,noBarrelFile,noReExportAll,noConsole, …), pluscheck:sizeandcheck:boundaries, all baselined and wired intocheck:weband CI.zustandand the@/path alias.Stage 3 — Naming
useFilenamingConventionenforces the convention. All renames are recorded in the move map.@/.Stage 4 — Layer boundaries
src/domain/for shared contracts: harness, session, models, remote protocol, git.shared/; window chrome and updates became their own features.main.Stage 5 — Workspace store +
App.tsx(in progress)Workspacecomponent.main(0ef02fa: task board, familiars, companion, Devin) in 91e83a4.main's behaviour into the new structure.flows/btw.App.tsx: 13 531 lines after the merge, 12 574 now.Remaining
App.tsx≤ 500 lines, composition only, full check, owner smoke test. Resume point:tasks.md§ "Resume point (2026-10-19)".app/shell(Sidebar, TitleBar, ProjectRail), per-feature splits with ad-hoc stores moved to Zustand,integrations/harness,shared/platform, CSS, Rust (fs.rs,harness.rs,session_store.rs),host/, close-out, merge.main. Do Stages 6–7 afterwards onmainas small per-feature PRs, so the branches stop drifting apart.Notes for reviewers
tasks.md, is far more useful than reading the full diff.mainmerge brought new findings: lint 2400 → 3029, boundaries 0 → 11 (3 in production code), 45 new files over 250 lines.scratch/merge2-debt/.App.tsxonmainuntil this branch merges. Every change there has to be ported by hand into the extracted flows.getState()) instead of from refs updated during render. The values are the same or fresher.pre-main-merge-20261018(e3a7ca9) marks the branch before the secondmainmerge.Testing
On
f147324c(2026-10-19):npx tsc --noEmit(web) and-p host/tsconfig.json: 0 errorsnpx vitest run: 6157 passed / 13 skippednpm run test:host: 111 passed / 5 skippednpm run check:rust(fmt + clippy-D warnings+cargo test): 714 passed / 1 ignorednpm run lint,check:size,check:boundaries,check:moves: pass against the current baselinesnpx vite build: OKmainare present in the merged tree.Outstanding: no manual UI smoke test yet. Areas to cover: