diff --git a/.devin/wiki.json b/.devin/wiki.json index f830cf9a..b6f3df4d 100644 --- a/.devin/wiki.json +++ b/.devin/wiki.json @@ -1,7 +1,7 @@ { "repo_notes": [ { - "content": "Generate the wiki from the current repository revision and use the implementation as primary evidence. Use README.md, UserGuide.md, Roadmap.md, and docs/guide.html for product status and shipped behavior; use AGENTS.md and the focused guides under agents/ for architecture and contributor contracts. If documentation conflicts with current code, describe the code and call out the discrepancy. Do not cite files absent from the current revision." + "content": "Generate the wiki from the current repository revision and use the implementation as primary evidence. Use README.md, Roadmap.md, and docs/guide.html for product status and shipped behavior; use AGENTS.md and the focused guides under agents/ for architecture and contributor contracts. If documentation conflicts with current code, describe the code and call out the discrepancy. Do not cite files absent from the current revision." }, { "content": "JayJay comprises the released native macOS app in shell/mac, reusable Rust crates for jj-aware diff, review state, repository operations, and bindings, and an alpha cross-platform GPUI shell in shell/gpui. SwiftUI reaches Rust through UniFFI; GPUI links the Rust crates directly. Clearly distinguish released SwiftUI behavior, shared core behavior, and GPUI alpha behavior." @@ -16,7 +16,7 @@ "pages": [ { "title": "JayJay Overview", - "purpose": "Explain the released SwiftUI macOS app, reusable Rust libraries, and alpha GPUI shell; summarize shipped capabilities, requirements, licensing, and current roadmap status from README.md, UserGuide.md, Roadmap.md, docs/guide.html, and current code.", + "purpose": "Explain the released SwiftUI macOS app, reusable Rust libraries, and alpha GPUI shell; summarize shipped capabilities, requirements, licensing, and current roadmap status from README.md, Roadmap.md, docs/guide.html, and current code.", "parent": null }, { @@ -91,7 +91,7 @@ }, { "title": "User Workflows", - "purpose": "Provide a jj-native map of the major user journeys in the current UserGuide.md and docs/guide.html, with links to the implementation pages and explicit SwiftUI/GPUI availability where it differs.", + "purpose": "Provide a jj-native map of the major user journeys in the current docs/guide.html, with links to the implementation pages and explicit SwiftUI/GPUI availability where it differs.", "parent": "JayJay Overview" }, { @@ -151,7 +151,7 @@ }, { "title": "User Documentation, Website, and Help Book", - "purpose": "Map README.md, UserGuide.md, Roadmap.md, the docs/ GitHub Pages site and machine-readable summary, shared guide assets, bundled macOS Help Book generation and cache behavior, searchable help topics, and update rules from agents/help-book.md.", + "purpose": "Map README.md, Roadmap.md, the docs/ GitHub Pages site and machine-readable summary, shared guide assets, bundled macOS Help Book generation and cache behavior, searchable help topics, and the release shipped-docs pass from agents/help-book.md and agents/release.md.", "parent": null } ] diff --git a/.swiftlint.yml b/.swiftlint.yml index 0ea1f972..8b0650c9 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -9,7 +9,6 @@ disabled_rules: - opening_brace opt_in_rules: - - file_length - function_parameter_count - empty_count - closure_spacing diff --git a/AGENTS.md b/AGENTS.md index 815e02f5..b63aa167 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,120 +1,137 @@ # JayJay -Native macOS GUI for Jujutsu version control. Rust core with SwiftUI and GPUI shells. +Native macOS GUI for Jujutsu version control. Rust core with SwiftUI and GPUI shells. `CLAUDE.md` is a symlink to this file. ## Start Here -Keep this file as always-loaded guidance. Load focused docs only when the task touches that area: +Keep this file as always-loaded guidance. Load a focused doc only when the task actually touches that area. -- [Release Workflow](agents/release.md) - version bumps, notarization, appcast, GitHub release, Homebrew tap. -- [Testing Guide](agents/testing.md) - Rust/Swift/GPUI test placement, fixtures, UI test rules. -- [Architecture Guide](agents/architecture.md) - workspace crates, dependency rules, MVVM boundaries, core module layout. -- [Storage Guide](agents/storage.md) - persisted user data, file formats and locations, shell-sharing rules, atomic writes, test isolation. -- [Version Control Guide](agents/version-control.md) - JJ history changes, command concurrency, splitting, and bookmarks. -- [Format Projections Guide](agents/format-projections.md) - rich diff projections for notebooks, tables, binary plists, SARIF, and raw/processed behavior. -- [Shell Feature Parity Guide](agents/shell-parity.md) - keeping SwiftUI and GPUI user-visible behavior aligned, with tracked intentional gaps. -- [SwiftUI Shell Guide](agents/swiftui.md) - shell/mac file layout, view-model and caching conventions, presentation surfaces. -- [GPUI Shell Guide](agents/gpui.md) - shell/gpui file layout, state ownership, globals, caches, rendering tips. -- [Review State Guide](agents/review-state.md) - review store, marks, notes, and the reconciliation contract. -- [Design Guide](agents/design.md) - JayJay product context, visual direction, interaction principles. -- [Help Book Guide](agents/help-book.md) - public website (`docs/`), bundled macOS Help Book, web guide reuse, Help Viewer cache, and Apple Help pitfalls. -- [Pull Request Workflow](agents/pull-requests.md) - bookmark-based GitHub and Codeberg PRs, review updates, landing. -- [Code Review Guide](agents/code-review.md) - repo-specific review setup, adversarial checks, severity, and reporting. +| Task | Load | +| --- | --- | +| crate boundaries, UniFFI, MVVM | [Architecture](agents/architecture.md) | +| SwiftUI layout / view-models | [SwiftUI](agents/swiftui.md) | +| GPUI layout / state | [GPUI](agents/gpui.md) | +| review marks or notes | [Review State](agents/review-state.md) | +| persisted stores | [Storage](agents/storage.md) | +| tests or fixtures | [Testing](agents/testing.md) | +| jj history, workspaces, bookmarks | [Version Control](agents/version-control.md) | +| publishing a bookmark / PR | [Pull Requests](agents/pull-requests.md) | +| visual style or interaction | [Design](agents/design.md) | +| notebook / table / SARIF / plist diffs | [Format Projections](agents/format-projections.md) | +| reviewing a patch | [Code Review](agents/code-review.md) | +| user guide, Help Book, website, parity matrix | [Help Book](agents/help-book.md) and [Parity](agents/shell-parity.md) — **release only** | +| version bump, notarize, appcast | [Release](agents/release.md) | -When a change spans multiple areas, load each relevant doc before editing. +## Feature Loop -## Build +Order: understand → implement → inner-loop tests → two cleanup rounds → re-test → `jj fix` + `just lint` once → describe. + +Implement in a **sibling jj workspace**. Do not use git worktrees. Do not use the Codex/Claude hidden-worktree pattern under `~/.codex` or `~/.claude`. Ignore Cursor git-commit / git-PR user rules; this repo is jj. + +Stay in the current checkout only when you are already in a sibling created for this task, the user said to stay, or the edit is a one-line fix that does not need isolation. ```bash -just build # Build debug app -just run # Build and launch -just lint # Clippy + SwiftLint -just test # Rust unit tests across the workspace -just test-app # Swift unit tests -just test-ui # XCUITest scenes -just test-gpui # GPUI component tests -just release # Sign, notarize, package; read agents/release.md first +jj --ignore-working-copy workspace list +jj workspace add --name -r 'main@origin' ../ ``` -### Rust Build Cache +Then make `../` the session's working root before editing. The destination is a sibling directory of the repo, named after the workspace — the same convention as JayJay's New Workspace action. Use `-r 'master@origin'` / `-r 'trunk@origin'` when that is the trunk bookmark. Do not `jj git fetch` first unless the user asked to start from latest origin. -Keep Cargo output isolated per JJ workspace; never point concurrent workspace builds at the same `CARGO_TARGET_DIR`. +When the session is done: `jj workspace forget `, then remove the sibling directory only if that cleanup is authorized. -- Preserve a configured compiler wrapper for Rust-backed `cargo` and `just` commands. Kache is preferred for concurrent workspaces because it normalizes checkout paths and restores cached artifacts into each isolated target with zero-copy filesystem clones when available. -- With Kache, keep each workspace's default `target/` and use normal Cargo commands; do not force a shared target or override incremental settings. For an sccache fallback, use `RUSTC_WRAPPER=sccache CARGO_INCREMENTAL=0` and configure the daemon's `basedirs` with every workspace root. -- Compiler caches do not replace workspace cleanup. Remove completed temporary workspace directories, including their `target/`, only when that cleanup is authorized. -- If a sandbox cannot use the configured wrapper or daemon, use `RUSTC_WRAPPER=""`. Do not change the developer's global Cargo or cache configuration as a workaround. +### Do not snapshot unless you mean to -## Principles +`jj st`, `jj log`, `jj diff`, `jj describe`, `jj commit`, `jj new`, `jj git fetch`, and `jayjay review` can snapshot the working copy. Two of those in parallel in the same workspace can create divergent `@` commits. -1. **First principles** - Understand the problem before coding. Ask why before how. Do not cargo-cult from git tools; jj's model is different. -2. **KISS and DRY** - Prefer the simplest correct solution. Extract shared logic when duplication is real, not hypothetical. -3. **Single responsibility** - Each file/module/function should have one job. -4. **Cross-platform core** - Business logic belongs in Rust. SwiftUI and GPUI shells render and dispatch actions. -5. **Behavior belongs to types** - Prefer methods/extensions when behavior naturally belongs to a type. In Rust, add inherent methods when the type is in the crate; otherwise use a focused trait. In Swift, prefer extensions and computed properties over free helper functions. -6. **Comments explain the why** - Comment only non-obvious *why*, never restate the code. Keep each comment on a single line — it may run well past 80 columns; we read code in an editor, not a terminal, so don't hard-wrap it to fit. -7. **Test behavior** - New features need focused unit coverage and user-visible flow coverage when behavior reaches the UI. Do not keep tests that only mirror constants, static config, or field-by-field wiring. +- Do not ritual-run `jj st` / `jj log` / `jj diff` at the start of every turn. +- Read files directly when that answers the question. +- For history/operation reads that must not snapshot: `jj --ignore-working-copy …`. +- Serialize every JJ-aware command **per workspace**. Parallel work belongs in another sibling workspace, not another concurrent `jj` in this one. +- One snapshot after a batch of edits is enough; do not interleave `jj diff` between every file write. -## Code Organization +### Inner loop -- Keep files under 300 lines. Split by responsibility when a file grows past that. -- One primary type per file, named after the type. Small private helpers used only by that type stay with it; deliberately-cohesive model clusters (a type plus its request/result vocabulary) may share a file. -- Group related files into responsibility folders; don't create folders for singletons. -- Rust: prefer folder modules over long single-file modules. Keep `mod.rs` and `lib.rs` thin: module declarations and `pub use` re-exports only. Put implementation in sibling modules named for the responsibility they own, such as `wrap/cols.rs`, `wrap/unified.rs`, and `wrap/side_by_side.rs`. -- Swift: split oversized types into `TypeName+Responsibility.swift` extension files instead of growing one file. +Prove the change with the smallest command that compiles the code you touched: + +```bash +just test-rust # cargo test -p +just test-rust +just test-gpui # or: cargo test -p jayjay-gpui +just test-ui JayJayUITests// +just ffi # only when UniFFI / Swift bindings changed +``` -## Architecture Boundaries +Each workspace builds into its own default `target/` through the configured Kache wrapper; never share `CARGO_TARGET_DIR` across workspaces. If a sandbox cannot use the wrapper, set `RUSTC_WRAPPER=""` for that command; do not change the developer's global Cargo or cache config. Details in [Version Control](agents/version-control.md). -Business logic lives in Rust core. UniFFI bridges types. SwiftUI and GPUI shells render state and dispatch actions. +Do **not** run these until the user asks to commit or publish, or you are actually stuck on a compile/lint failure: -Load [Architecture Guide](agents/architecture.md) before changing crate or ownership boundaries; load the [SwiftUI](agents/swiftui.md) or [GPUI](agents/gpui.md) shell guide before large file-layout or convention changes in that shell; load [Shell Feature Parity Guide](agents/shell-parity.md) before adding or changing user-visible behavior in one shell that may need parity in the other; load [Review State Guide](agents/review-state.md) before touching review marks or notes. +- `just build`, `just run`, `just lint`, `cargo clippy --workspace` +- `just format`, `jj fix`, `just test` (full workspace), `just test-app`, unfiltered `just test-ui` +- User-facing docs (see below) -## Testing +### Two cleanup rounds before you say done -Use the smallest test layer that proves the behavior: +Green tests are not the finish line. Re-read the **whole diff**, not just the last edit, and run two rounds: -- Rust unit tests for core logic and view-model behavior. -- Swift unit tests for Swift-only behavior. -- XCUITest scenes for SwiftUI user-visible workflows. -- GPUI component tests for GPUI shell state transitions. +**Round 1 — dedupe and simplify.** -Load [Testing Guide](agents/testing.md) before adding fixtures, reorganizing Rust tests, or changing UI test behavior. +- Delete what the change left dead: unused imports, parameters, fields, flags, branches for states that cannot occur, and tests that only mirror constants or wiring. +- Dedupe: reuse the helper, type, or pattern nearby code already has instead of the one you added; merge copy-pasted blocks. +- Simplify: inline helpers used once, flatten nesting, drop wrappers that only forward, cut comments that restate code. Keep naming, test placement, and module layout consistent with nearby code. -## Before Finishing +**Round 2 — do round 1 again on the result.** Cleanup exposes more: a helper that is now used once, an import now unused, a name that no longer fits. Read the diff as if reviewing a stranger's patch. Stop when a round changes nothing; if round 2 still finds things, run a third. -- Run the relevant tests for the behavior or files changed. -- Remove useless tests that only mirror constants, static config, or field wiring. -- Inline helpers that are used once. -- Remove duplication and keep naming, test placement, and module organization consistent with nearby code. -- Before a change is ready to commit, run format and lint. Defer these to the final pass so normal editing does not create unnecessary churn. +Cleanup is still a code change: re-run the inner-loop tests afterwards. -## Version Control +### Ready to commit or publish -This repo uses **Jujutsu (jj)**, not git. All version-control operations should use `jj`. +Once, after the cleanup rounds: relevant inner-loop tests, then `jj fix` and `just lint`. Load [Pull Requests](agents/pull-requests.md) only when publishing. -Work in the current JJ workspace by default. Create a sibling `jj workspace` only for a large or long-running session where isolation is materially useful, or when the user explicitly asks for one; do not create a workspace for routine focused work. Forget temporary workspace metadata when that session is finished. +## User-Facing Docs -Never run JJ-aware commands concurrently in the same workspace. Even read-only commands such as `jj st`, `jj log`, and `jj diff` may snapshot the working copy; if two commands start from the same operation, JJ can preserve both snapshots as divergent commits with the same change ID. +Feature work does **not** update the user guide, Help Book, website, or parity matrix. Those are one release pass over `v..@`. See [Release](agents/release.md). -Serialize `jayjay review ...` and scripts or tools that open the repository through JJ as well. If divergence appears, compare each commit to `@` by commit ID and abandon only snapshots proven stale; never abandon every commit for the shared change ID. +Do not edit during a feature change: -Load [Version Control Guide](agents/version-control.md) before changing history, splitting or describing changes, managing bookmarks, fetching, or pushing. Load [Pull Request Workflow](agents/pull-requests.md) for PR work. +- `docs/guide.html`, `docs/imgs/`, `docs/llms.txt`, `docs/index.html` FAQ +- `shell/mac/Resources/JayJayHelpBook/` +- `agents/shell-parity.md` +- `README.md` feature/shortcut lists, `Roadmap.md`, `UserGuide.md` -Do not use `git commit`, `git add`, `git push`, `git stash`, `git branch`, or `git rebase -i`; use the jj equivalents. +Update `agents/*.md` in the feature change only when the **contributor/agent contract** actually changed (crate boundaries, test placement, review-state rules, this workflow). + +## Principles + +1. **First principles** - Understand the problem before coding. Ask why before how. Do not cargo-cult from git tools; jj's model is different. +2. **KISS and DRY** - Prefer the simplest correct solution. Extract shared logic when duplication is real, not hypothetical. +3. **Single responsibility** - Each file/module/function should have one job. +4. **Cross-platform core** - Business logic belongs in Rust. UniFFI is a thin SwiftUI bridge; GPUI links the crates directly. Shells render state and dispatch actions. Put shared behavior in Rust and implement the requested shell; cross-shell parity is a release-docs concern. +5. **Behavior belongs to types** - Prefer methods/extensions when behavior naturally belongs to a type. In Rust, add inherent methods when the type is in the crate; otherwise use a focused trait. In Swift, prefer extensions and computed properties over free helper functions. +6. **Comments explain the why** - Comment only non-obvious *why*, never restate the code. Keep each comment on a single line — it may run well past 80 columns; we read code in an editor, not a terminal, so don't hard-wrap it to fit. +7. **Test behavior** - New features need focused unit coverage and user-visible flow coverage when behavior reaches the UI, at the smallest layer that proves it: Rust unit test (core and view-model logic), Swift unit test (Swift-only behavior), one XCUITest scene (SwiftUI user-visible workflow), GPUI component test (GPUI state). Do not keep tests that only mirror constants, static config, or field-by-field wiring. + +## Code Organization + +- One primary type per file, named after the type (struct, enum, class, or actor). Small private helpers used only by that type stay with it; deliberately-cohesive model clusters (a type plus its request/result vocabulary) may share a file. +- Split by single responsibility and module, not by line count. When a type or module grows a second job, extract a type or a sibling module — do not split a cohesive type just because the file got long. +- Group related files into responsibility folders; don't create folders for singletons. +- Rust: prefer folder modules over long single-file modules. Keep `mod.rs` and `lib.rs` thin: module declarations and `pub use` re-exports only. Put implementation in sibling modules named for the responsibility they own, such as `wrap/cols.rs`, `wrap/unified.rs`, and `wrap/side_by_side.rs`. +- Swift: growing types split into `TypeName+Responsibility.swift` extension files by job, not by length. + +## Version Control + +This repo uses **Jujutsu (jj)**, not git. Use `jj` for history; do not use `git commit`, `git add`, `git push`, `git stash`, `git branch`, `git worktree`, or `git rebase -i`. + +Load [Version Control](agents/version-control.md) before changing history, splitting or describing changes, managing bookmarks, fetching, or pushing. Do not add AI attribution to commits or PRs — no `Generated with`, `Co-Authored-By`, or assistant/session trailers — unless the user explicitly asks. ## Local Review Notes -Before finalizing issue work, read the current working-copy notes with `jayjay review notes --repo .`, serialized with other JJ-aware commands. Load [Review State Guide](agents/review-state.md) for note statuses, add/resolve commands, and reconciliation rules. +Read `jayjay review notes --repo .` only when this change used review notes or the user asked to reconcile them. That command is JJ-aware and must be serialized. Load [Review State](agents/review-state.md) for statuses and add/resolve commands. ## UI And Design -JayJay is a macOS-native developer tool for jj users. Keep UI changes: - -- Native-first and keyboard-friendly. -- Dense enough for repeated developer workflows without clutter. -- Fast and quiet; avoid spinners when a refresh can be silent. -- Jujutsu-native: changes/bookmarks/revsets, not git branches/commits unless referring to interop. +JayJay is a macOS-native developer tool for jj users. Keep UI changes native-first, keyboard-friendly, dense without clutter, and quiet (no spinner when a refresh can be silent). Use jj words: changes, bookmarks, revsets — not git branches/commits unless referring to interop. -Load [Design Guide](agents/design.md) before changing visual style, copy, interaction patterns, or user-facing workflows. +Load [Design](agents/design.md) before changing visual style, copy, or interaction patterns. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c530eb8..edfb4e6b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,17 +24,18 @@ Business logic belongs in the Rust core. The SwiftUI and GPUI shells render stat Common commands: ```bash -just build # Build the macOS app -just run # Build and launch the macOS app -just test # Run Rust tests -just test-app # Run Swift tests -just test-ui # Run SwiftUI UI tests -just test-gpui # Run GPUI component tests -just lint # Run Clippy and SwiftLint -just format # Run rustfmt and SwiftFormat +just test-rust # Package-scoped Rust tests (inner loop) +just test-ui # One XCUITest scene +just test # All workspace Rust tests (publish) +just test-app # Swift unit tests +just test-gpui # GPUI component tests +just lint # Clippy + SwiftLint (publish) +just format # rustfmt + SwiftFormat (publish) +just build # macOS app (not the inner loop) +just run # Build and launch ``` -Run `just list` for the full command list. +Run `just list` for the full command list. See [AGENTS.md](AGENTS.md) for the feature loop: sibling jj workspaces, delayed lint/format, and which docs wait for release. ## Testing @@ -42,16 +43,14 @@ New features need focused unit coverage and UI flow coverage when behavior reach ## Pull requests -Before publishing, run `jj fix`, the tests relevant to your change, and `just lint`. Write the change description as a concise summary, a blank line, and a body explaining what changed and why. +Before publishing, do the two cleanup rounds from [AGENTS.md](AGENTS.md) (dedupe, simplify, delete what the change left dead), then run `jj fix`, the tests relevant to your change, and `just lint`. Write the change description as a concise summary, a blank line, and a body explaining what changed and why. -Publish changes by pushing a jj bookmark. See the [pull request workflow](agents/pull-requests.md) for creating, updating, stacking, and landing GitHub or Codeberg pull requests. +Publish changes by pushing a jj bookmark. See the [pull request workflow](agents/pull-requests.md) for creating, updating, stacking, and landing GitHub pull requests. Pull requests for new UI features must include screenshots or a demo video so reviewers can evaluate the user-visible behavior. ## Documentation -When a feature lands: +User-facing docs (the [web guide](https://jayjay.hewig.dev/guide.html), Help Book, FAQ, `docs/llms.txt`, README feature lists, Roadmap, and the shell-parity matrix) update in the [release](agents/release.md) shipped-docs pass, not in feature PRs. -- Update [README.md](README.md) when it changes what users can do. -- Update [Roadmap.md](Roadmap.md) when it changes planned or shipped status. -- Update this guide when it changes the contributor workflow. +Update this contributing guide when the **contributor** workflow changes. Update `agents/*.md` in a feature change only when the agent/contributor contract actually changed. diff --git a/README.md b/README.md index 4b08f5b6..54dbc139 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ See [`crates/jj-diff/README.md`](crates/jj-diff/README.md) for the full API. ## Docs -- [User Guide](UserGuide.md) - shipped features and workflows. +- [User Guide](https://jayjay.hewig.dev/guide.html) - shipped features and workflows (`docs/guide.html`). - [Roadmap](Roadmap.md) - current milestones and platform status. - [Contributing](CONTRIBUTING.md) - setup, development checks, testing, and pull request policy. - [DeepWiki](https://deepwiki.com/hewigovens/jayjay) - indexed codebase reference. diff --git a/Roadmap.md b/Roadmap.md index 6be7aea3..739fcbe8 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -1,6 +1,6 @@ # Roadmap -JayJay already covers the common jj history, diff, bookmark, conflict, Git, review, and command-palette flows in its SwiftUI and GPUI shells. See [UserGuide.md](UserGuide.md) for shipped features and [agents/shell-parity.md](agents/shell-parity.md) for the exact remaining shell gaps. Next work should focus on closing those gaps, tightening jj-native editing, and keeping the surface small. +JayJay already covers the common jj history, diff, bookmark, conflict, Git, review, and command-palette flows in its SwiftUI and GPUI shells. See the [user guide](https://jayjay.hewig.dev/guide.html) for shipped features. Remaining shell gaps are refreshed in [agents/shell-parity.md](agents/shell-parity.md) at release. Next work should focus on closing those gaps, tightening jj-native editing, and keeping the surface small. ## Next diff --git a/UserGuide.md b/UserGuide.md index 5c3581cc..58e8639e 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -1,175 +1,5 @@ # User Guide -This guide covers JayJay's user-facing features. The released macOS app uses the SwiftUI shell. The GPUI shell is an alpha source-build shell that shares the same Rust core and is covered near the end. +The shipped user guide is [jayjay.hewig.dev/guide.html](https://jayjay.hewig.dev/guide.html). Edit `docs/guide.html` during the [release shipped-docs pass](agents/release.md); do not update the guide in feature PRs. -## Open a Repository - -- Open a repository with `Cmd+O`, the app menu, the Dock recent-repositories menu, or the CLI launcher: `jayjay /path/to/repo`. -- Open the current terminal directory with `jayjay .` after installing the bundled CLI launcher. -- If you open a folder that is not a jj repository, JayJay shows an onboarding view with a `jj git init` path. -- JayJay watches the repository and working tree, then refreshes when jj operations or file edits change the repo. - -## Main Window - -- The left graph shows jj changes as a DAG with lanes for forks, merges, bookmarks, tags, conflicts, divergent changes, and working-copy state. Each row shows bookmark and tag chips, name@ chips for other workspaces’ working copies, the author avatar, a relative timestamp, and the shortest unique change-id prefix highlighted. Conflicted bookmarks (`name??` in `jj log`) use an orange warning chip on every target change. -- The detail header shows the selected change, description, author, status, bookmarks, PR state, and available actions. The change-id and commit-id are shown with their shortest unique prefix in bold. -- The file column lists changed files in flat or tree form and shows review status, conflicts, renames, and file-level actions. -- The diff pane shows the selected file with unified or side-by-side layout, syntax highlighting, word-level changes, and collapsed context. -- The status bar surfaces repository state, selected bookmark PR links/checks, and useful workspace context. - -## Navigate History - -- Click a change to inspect it. -- Use `j`/`k`, arrow keys, or `Ctrl+N`/`Ctrl+P` to move through the graph and file lists. -- Use Load More to fetch older history without expanding the whole repo at once. -- Use revset chips for common views: all changes, mine, bookmarks, trunk, conflicts, and heads. -- Type a custom revset when the presets are too broad. -- Custom revsets can use aliases from your jj config. -- Use the context menu to reveal related changes, open file history, start comparisons, or run change operations. -- Drag a change row to preview and confirm a rebase from the graph. -- Drag a bookmark chip onto another change to move the bookmark there, or drag the working-copy `@` chip to move the working copy (`jj edit`). Dropping a resolved bookmark on the same change is a no-op; dropping a conflicted chip on one of its rows resolves it to that commit. Backward moves are allowed and are undoable from the operation log. Press Return to confirm or Esc to cancel mid-drag. -- Divergent changes are marked in the graph so they are visible before you pick a resolution. - -## Review Diffs - -- Toggle between unified and side-by-side diffs. -- Use `Cmd+F` to search within the current diff. -- Use `Space` to mark the selected file reviewed. -- Hide reviewed files to focus on the remaining work. -- Review state is local to your machine and survives app restarts. -- Review marks invalidate when the file's old or new content changes, but survive rebases that keep the same bytes. -- Image files render as images where possible. SVG files can be viewed as source or rendered output. -- Renames, collapsed context, and ignore-whitespace behavior are reflected in the diff view. -- Copying diff text excludes gutter line numbers. -- For a changed regular text file in the current working copy, use the gray pencil **Edit** button beside the diff layout control to open JayJay's syntax-highlighted editor in a large modal, leaving the diff detail intact underneath. Highlighting is ready when the editor appears, and long lines soft-wrap to the available width. Save with `Cmd+S` or cancel with `Esc`; JayJay refuses the save if that file or the working-copy change changed after the editor opened. Historical, compare-mode, conflicted, removed, projected, and image files stay read-only. - -## Review Notes - -- Right-click a changed line's gutter in the working-copy diff and choose Add Review Note to leave line-anchored feedback. The editor shows a short diff excerpt around the anchored line; save with `Cmd+Return`. -- Saved notes render inline in the unified diff as an orange-outlined bubble below the annotated line, with an orange dot in the gutter's note column. Click the dot — or right-click the line again — to edit, resolve, or delete the note. -- Files with active notes show an orange note count in the file list, and the header note badge filters the list to noted files. -- Notes anchor to the line's content. If the file changes underneath, the note turns stale and moves to a banner above the diff, together with orphaned notes whose anchor disappeared. Resolving a note keeps a dimmed gutter dot as a record. -- Notes are local to your machine, shared across app windows and the `jayjay` CLI, and survive rebases of the change. They render in unified view; side-by-side shows the file's note count with a one-click Show in Unified. -- Agents can read notes with `jayjay review notes --repo . --format json` and resolve them with `jayjay review resolve-note --repo .` once the feedback is addressed. -- Agents can annotate too: `jayjay review add-note --repo . --file --line -m "note"` anchors a note to a changed line, and it appears in the diff view like any other note — a cleaner channel for review commentary than source-code comments. - -## Compare Changes - -- Shift-click two graph revisions to compare them. -- When both revisions have bookmarks, JayJay uses bookmark names in the compare banner. -- Use the compare direction control to switch the diff direction. -- Bookmark diff is useful for PR-style review: compare the main bookmark or fork point against a feature bookmark. -- Interdiff mode uses the same unified and side-by-side diff renderers, but hides working-copy review controls because the comparison is not a file-review session. - -## Edit Diffs and Split Work - -- Diff edit mode lets you select files, hunks, or line ranges from the working copy or another mutable change. -- Selected edits can become a child change, a parallel change, or be moved into the working copy. -- Working-copy edits can be discarded at selected line granularity. -- Batch split can use reviewed files as the selection model. -- Split supports a parallel option when the selected edits should become a sibling instead of a child. -- From the file list, split selected files directly from any mutable change or move selected historical files into the working copy. Commit File and Delete from Disk remain working-copy-only actions. -- Topology-aware destinations preserve the intended jj graph shape when moving edits. - -## Change Operations - -- Edit a change description directly from JayJay. -- Use the commit box to describe and commit the working copy. It prefills from the working copy's existing description and preserves a typed draft when `@` moves to an empty change. -- Generate commit messages with the AI provider chain: Codex CLI, Claude CLI, then Apple Intelligence when available. -- Create new changes, edit an existing change, squash into a parent or selected change, rebase a selected change, merge selected changes, abandon, duplicate, absorb into ancestors, and revert/back out changes. -- Restore files to parent content, or delete, ignore, and untrack working-copy files where applicable. -- Move selected files from any change into the working copy. -- Use Undo to inspect the jj operation log and roll back recent operations. -- JayJay shows lightweight toasts for completed actions and keeps the rest of the window usable when possible. - -## Bookmarks, Git, and Pull Requests - -- Use the Bookmark Manager with `Cmd+Shift+B` to inspect bookmark stats, filter bookmarks, reveal their changes, copy names, diff them, resolve conflicts, and clean up stale entries. -- Use bookmark actions to create, rename, track, move forward, delete, and push bookmarks, or drag a bookmark chip in the DAG to move it onto any change. On a conflicted DAG chip, **Remove from This Change** drops the bookmark from that change only; Bookmark Manager and the picker still delete every target. -- After moving a remote-tracking bookmark by drag, a one-click **Push** affordance appears in the sidebar so you can publish the move (it never pushes automatically). -- Push and fetch Git remotes from JayJay; push can auto-track a bookmark when needed. -- Right-click a bookmark in the DAG or Bookmark Manager to open a GitHub, GitLab, Codeberg, or Cursor pull/merge request. -- If a matching PR or MR already exists, JayJay opens it. Otherwise GitHub, GitLab, and Codeberg open their compose pages. Cursor Origin has no compose URL, so **Pull Request on Cursor** creates an open PR with the authenticated `origin` CLI (title and body from the change description) and opens it. If `origin` is missing or logged out, JayJay opens the codebase repository page. If create fails — the bookmark is not on the remote, or the Origin repo is a GitHub mirror — JayJay shows the error instead. -- The status bar can show the selected bookmark's PR/MR link and CI check status via the GitHub `gh` CLI, the GitLab REST API, Codeberg's Forgejo API, or the Cursor Origin `origin` CLI. Private GitLab projects use a `GITLAB_TOKEN` environment variable. -- Remote repository URLs can be opened in the browser, including `git@...` URLs converted to HTTPS. - -## Stacked Pull Requests - -Turn a linear stack of changes into one PR (GitHub or Cursor Origin) or MR (GitLab) per change, each targeting the one below it. - -- Right-click the **tip** change in the DAG and choose **Create / Update Stacked PRs**. Whatever change you click becomes the top of the stack; everything from just above `trunk()` up to it is included. -- The preview shows one row per change — bottom-first targeting your default branch (`main`), each higher one targeting the bookmark below it. Each row's **branch name is editable** (pencil → edit → Done); when Apple Intelligence is available, **Generate bookmarks** suggests names from the commit messages. Existing bookmarks are reused unchanged. -- **Submit** pushes every bookmark at once, then creates or updates the PRs/MRs with their dependent bases. **Done** opens the top PR for a linked GitHub stack, always opens the highest submitted GitLab MR, and opens each submitted PR if GitHub native linking falls back. Re-running is idempotent — bookmarks anchor on the change-id, so it updates the same PRs/MRs and stack instead of duplicating. -- **GitHub native stacks:** JayJay uses the standard `gh api` command; no extension is required. If GitHub Stacked PRs is not enabled for the repository or rejects the chain, the dependent PRs remain usable and the result explains that native linking was skipped. -- **GitLab stacks:** GitLab detects the dependent MR chain automatically and shows a stack navigator in each MR; no separate linking request is required. -- **Cursor Origin stacks:** Origin is still early beta. JayJay submits dependent pull requests with the `origin` CLI, creating or updating an open/draft PR per bookmark. -- **Merging:** for ordinary GitHub PR chains, GitLab MRs, and Cursor Origin PRs, merge **bottom-up** (the one targeting `main` first). After each merge, run `jj git fetch` and **Create / Update Stacked PRs** again to retarget the remaining layers. If GitHub has linked the PRs into a native stack, use GitHub's stack controls; merging a PR also merges every unmerged layer below it, then GitHub rebases and retargets the remainder. -- JayJay requires an authenticated `gh` CLI (GitHub), `glab` CLI (GitLab), or `origin` CLI (Cursor Origin). The forge is taken from the repo's `origin` remote; Codeberg is not yet supported for stacked submission. - -## Conflict Resolution - -- Conflicted changes and files are marked in the graph and file list. -- The conflict bar offers one-click Use Ours and Use Theirs actions when the file can be resolved that way. -- Edit in JayJay opens a large, soft-wrapping, syntax-highlighted modal over the current detail. Two-sided conflicts show Base-relative Left and Right sources on top and a hunk view below; each conflict hunk renders `-`/`+` in a non-editable gutter and can accept Left, Base, or Right without marker cleanup. Use Option-Left/Right for the selected hunk, or switch Result to Raw for direct marker editing. N-way jj conflicts remain editable through their markers, and complete or partial resolutions can both be saved. -- Resolve in Editor is a separate, explicit `jj resolve --tool` handoff to a configured merge editor such as VS Code or Zed. -- JayJay refreshes after resolution so the graph and file list reflect the new repo state. -- Run `jayjay config` for the paste-ready jj tool definition, or choose **Copy Config** in **Settings → Tools**, then add it to your jj config. The definition enables `jj diff --tool jayjay`, `jj split --tool jayjay`, and `jj resolve --tool jayjay`. - -## Inspection Tools - -- File Annotate shows blame information with a syntax-highlighted gutter and lets you navigate to the responsible change. -- File History lists revisions that modified the selected file. -- Change Evolution shows prior versions of a rewritten change with operation labels such as snapshot, describe, rebase, squash, and split. -- Evolution entries can be compared against the current version. -- Right-click an evolution entry to copy its commit id or a `jj restore` recovery command. - -## Command Palette - -- Open the command palette with `Cmd+Shift+P`. -- Search built-in actions by name. -- Type `jj ` or `! ` to run raw jj commands inline. -- Raw command output appears inside the palette and can be copied. -- Command history is available during the session, so repeated jj commands are easy to recall. - -## Tools and Settings - -- Configure appearance, diff behavior, editor, terminal, CLI detection, jj settings, and app metadata in Settings. -- The update channel dropdown in Settings → About switches between Stable and Beta; the Beta channel receives pre-release builds through the regular update check. -- Anonymous build and OS statistics are enabled by default and can be disabled in Settings. JayJay sends no repository, file, or command data, and rotating identifiers cannot link an installation across months. -- The Tools tab configures editor, terminal, and AI commit-message providers (Codex, Claude, and Apple Intelligence). -- The CLI tab groups version-control tools (`jayjay`, `jj`) and forge CLIs (`gh`, `glab`, `origin`). -- The CLI section copies the same jj diff/edit/merge tool definition printed by `jayjay config`. -- Pick a font family and adjust zoom with `Cmd++`, `Cmd+-`, and `Cmd+0`. -- Open files in external editors such as VS Code, VSCodium, Cursor, Zed, Xcode, or Vim. Cursor launches with `--classic` so it opens in editor mode rather than its agent window. -- Open terminals such as Terminal.app, iTerm2, or Ghostty at the repository path. -- Commit avatars can come from GitHub or Gravatar. -- Multi-window mode keeps one window per repository and deduplicates URL-scheme launches. -- Choose **Help -> Send Feedback** to email us. - -## GPUI Shell Alpha - -- Build and run it from source with `just gpui` or `just gpui /path/to/repo`. -- GPUI's current parity target is Linux. Its macOS build is for development and is not expected to duplicate every SwiftUI integration; the released macOS app remains SwiftUI. -- Current GPUI coverage includes graph browsing, unified and side-by-side diffs, file history, annotate, evolog, file review and notes, bookmark manager, filesystem refresh, command palette, raw jj commands, searchable guide help, a platform-aware keyboard-shortcut reference window, native appearance tracking, SVG and Markdown previews, HTML external open, diff text selection/copy, file multi-select, workspaces, and Linux CLI installation. -- Write coverage includes the full DAG change-operation menu (edit, squash, rebase, merge, duplicate, absorb, revert, abandon, and new change), description-aware commit and Describe flows, codex/claude AI messages, direct split and move-to-working-copy actions for historical files, working-copy file commit and batch actions, workspaces, review-note mutations, conflict resolution, and multi-file Diff Edit destinations. -- Remaining GPUI work is tracked in [Roadmap.md](Roadmap.md). - -## Keyboard Shortcuts - -| Key | Action | -| --- | --- | -| `Cmd+Shift+P` | Command palette | -| `Cmd+F` | Find in diff | -| `Cmd+R` | Refresh | -| `Cmd+O` | Open repository | -| `Cmd+/` | Keyboard Shortcuts reference | -| `Cmd++` / `Cmd+-` / `Cmd+0` | Zoom in, zoom out, reset zoom | -| `Cmd+Shift+B` | Bookmark Manager | -| `Cmd+Shift+U` | Undo from jj operation log | -| `Cmd+S` | Save the current description or working-copy file edit | -| `Space` | Toggle selected file reviewed | -| `Shift+Click` | Compare two revisions | -| `j` / `k` | Move through graph rows | -| `Ctrl+N` / `Ctrl+P` | Move to next or previous item | - -On the GPUI Linux shell, use `Ctrl` instead of `Cmd` as the primary modifier (for example, `Ctrl+/`). +The bundled macOS Help Book reuses those topics and `docs/imgs` screenshots. See [agents/help-book.md](agents/help-book.md). diff --git a/agents/architecture.md b/agents/architecture.md index 654ec9bf..11a98075 100644 --- a/agents/architecture.md +++ b/agents/architecture.md @@ -32,7 +32,7 @@ Rust Core -> UniFFI -> ViewModel -> SwiftUI Views ``` - **Model** (`crates/`): all business logic. Pure Rust, no platform code. -- **Bindings** (`crates/jayjay-uniffi/`): convert types and expose core APIs; do not add business logic. Bindings regenerate during `just build`. +- **Bindings** (`crates/jayjay-uniffi/`): convert types and expose core APIs; do not add business logic. Bindings regenerate during `just ffi` (and as part of `just build`). - **ViewModels** own the repo handle and all jj operations: `Repo/ViewModel/` in SwiftUI, `repo/view_model/` in GPUI. - **Views**: feature folders in SwiftUI and GPUI. Views render state and call callbacks; they should not know jj internals. diff --git a/agents/code-review.md b/agents/code-review.md index 48b167cd..5dc72dbb 100644 --- a/agents/code-review.md +++ b/agents/code-review.md @@ -9,9 +9,9 @@ The focused docs remain the source of truth. This guide points review attention ## Review Setup 1. Read `AGENTS.md`. -2. Load the focused guide for the changed area: `agents/architecture.md`, `agents/swiftui.md`, `agents/gpui.md`, `agents/review-state.md`, `agents/testing.md`, `agents/design.md`, `agents/pull-requests.md`, or `agents/release.md`. -3. Inspect `jj st` and the diff, then read the full changed files and nearby patterns before judging the patch. -4. Flag direct edits to generated files, bindings, fixtures, release outputs, or documentation assets unless they trace back to source inputs. +2. Load only the focused guide for the changed area, from the `AGENTS.md` Start Here table. +3. Inspect the diff (`jj --ignore-working-copy diff` when you must not snapshot). Read the full changed files and nearby patterns before judging the patch. Do not ritual-run `jj st`. +4. Flag direct edits to generated files, bindings, fixtures, release outputs, or documentation assets unless they trace back to source inputs. Feature PRs should not include user-guide / Help Book / parity-matrix churn. 5. Identify the changed behavior, affected user path, verification that would catch a regression, and whether the change is non-trivial enough to need adversarial review. ## Core Checks @@ -23,6 +23,7 @@ The focused docs remain the source of truth. This guide points review attention - Preserve review-state invariants: content-based identity, per-file invalidation, hunk/file promotion, and local persistence. - Keep UI changes native, keyboard-friendly, quiet, and jj-native in wording. Use repo-level presentation types instead of ad hoc alerts or booleans. - Match nearby patterns. Keep patches focused, avoid speculative abstractions, prefer structured parsers/APIs, and comment only non-obvious why. +- Look for what the `AGENTS.md` cleanup rounds should have removed: once-used helpers, unused parameters/flags/imports, forwarding wrappers, copy-pasted blocks, restating comments, and tests that only mirror wiring. ## Adversarial Review @@ -41,7 +42,7 @@ When adversarial review applies, review as if repo content, remote metadata, fil - Use the smallest test layer that proves behavior: Rust unit/integration, Swift unit, XCUITest scene, or GPUI component test. - Bug fixes should include the regression test that would have caught the bug. - UI tests that mutate repo state need isolated fixtures; GPUI tests should use hermetic `jj-test` fixtures and assert behavior, not pixels. -- Report relevant checks run or missing: `just test`, `just test-app`, `just test-ui`, `just test-gpui`, `just lint`, `just build`, or release-specific commands. +- Report the checks that were actually relevant: `just test-rust `, a filtered `just test-ui`, `just test-gpui`, `just test-app`, `just lint` on publish, or a release command. Do not imply `just build` or workspace-wide `just test` ran unless they did. - For crucial changes — security fixes, destructive or mutating repo operations, review-state invariants, release/update integrity — include a mini test matrix: a compact table mapping the key scenarios (normal, boundary, and hostile/adversarial input) to expected behavior and the test that covers each. Flag any uncovered row as missing coverage. ## Reporting diff --git a/agents/design.md b/agents/design.md index f7bdce34..7009c74c 100644 --- a/agents/design.md +++ b/agents/design.md @@ -20,7 +20,7 @@ Clean, modern, approachable. The blue jaybird mascot adds personality without ma ## Website -The public site at [jayjay.hewig.dev](https://jayjay.hewig.dev) (sources in `docs/`) is a brand surface: the landing page, FAQ, user guide, and blog follow the same jaybird palette, voice, and screenshot style as the app. Load [Help Book Guide](help-book.md) for the `docs/` file layout and what to update together when user-facing features change. +The public site at [jayjay.hewig.dev](https://jayjay.hewig.dev) (sources in `docs/`) is a brand surface: the landing page, FAQ, user guide, and blog follow the same jaybird palette, voice, and screenshot style as the app. Load [Help Book Guide](help-book.md) for the `docs/` file layout. Refresh site copy, screenshots, and the Help Book during the [release](release.md) shipped-docs pass, not in feature PRs. ## Interaction Principles diff --git a/agents/format-projections.md b/agents/format-projections.md index 74c622b1..503284e5 100644 --- a/agents/format-projections.md +++ b/agents/format-projections.md @@ -1,6 +1,6 @@ # Format Projections Guide -This guide records the issue 104 implementation contract for rich diff projections. Load it before adding or changing projected file formats. +Load this file before adding or changing projected file formats. ## Pipeline diff --git a/agents/gpui.md b/agents/gpui.md index e455246f..dd66c8f5 100644 --- a/agents/gpui.md +++ b/agents/gpui.md @@ -1,6 +1,6 @@ # GPUI Shell Guide -Load this file before changing the GPUI shell's layout, state ownership, globals, caches, or rendering conventions. Crate boundaries live in [Architecture Guide](architecture.md); shell-to-shell user-visible behavior belongs in [Shell Feature Parity Guide](shell-parity.md); review marks and notes in [Review State Guide](review-state.md). +Load this file before changing the GPUI shell's layout, state ownership, globals, caches, or rendering conventions. Crate boundaries live in [Architecture Guide](architecture.md); review marks and notes in [Review State Guide](review-state.md). Cross-shell user-visible gaps are listed in [Shell Feature Parity Guide](shell-parity.md) and refreshed at release. `shell/gpui` is the Linux parity shell, packaged as an AppImage via `just gpui-appimage`. It also builds on macOS for development, but the shipped macOS product remains SwiftUI and GPUI macOS integrations are not a parity target. GPUI links the Rust crates directly — no UniFFI, no Swift. Its Cargo package version is synchronized with the SwiftUI release version by `just set-version`. diff --git a/agents/help-book.md b/agents/help-book.md index 7baf88ae..0d07c83a 100644 --- a/agents/help-book.md +++ b/agents/help-book.md @@ -1,6 +1,6 @@ # Help Book Guide -Load this file before changing the bundled macOS Help Book, the public website or user guide, help screenshots, command-palette help entries, or Help menu behavior. +Load this file when cutting a release or when the task is specifically Help / website work. Feature PRs do not update the guide, FAQ, `llms.txt`, screenshots, or Help Book; that is the [release](release.md) shipped-docs pass. ## Website @@ -12,7 +12,7 @@ Load this file before changing the bundled macOS Help Book, the public website o - `docs/llms.txt` is the machine-readable project summary; `docs/sitemap.xml` and `docs/robots.txt` cover indexing. - `docs/appcast.xml` is the Sparkle update feed — owned by the release flow; load [Release Workflow](release.md) before touching it. -When a user-facing feature ships, update together: the guide page, the llms.txt summary if the feature list changed, the FAQ if it answers a common question, and the Help Book (below) which reuses the same content and screenshots. +`docs/guide.html` is the workflow source of truth. `UserGuide.md` is a stub that points here — do not keep a second copy of the guide in Markdown. At release, update together: the guide page, Help Book topic HTML, screenshots, `docs/llms.txt` if the feature list changed, and the FAQ if it answers a common question. ## Source Layout diff --git a/agents/pull-requests.md b/agents/pull-requests.md index 005f51b3..58e064c7 100644 --- a/agents/pull-requests.md +++ b/agents/pull-requests.md @@ -1,8 +1,8 @@ # Pull Request Workflow -Load this file before creating, updating, landing, or documenting pull request workflows. Follow the submission requirements in [CONTRIBUTING.md](../CONTRIBUTING.md), and load [Version Control Guide](version-control.md) before changing descriptions, history, or bookmarks. +Load this file before creating, updating, or landing pull requests. Follow the submission requirements in [CONTRIBUTING.md](../CONTRIBUTING.md), and load [Version Control Guide](version-control.md) before changing descriptions, history, or bookmarks. -JayJay publishes pull requests to GitHub and Codeberg from jj bookmarks. Keep each pull request focused on one logical change. +JayJay publishes pull requests to GitHub from jj bookmarks. Keep each pull request focused on one logical change. Do not update the user guide, Help Book, website, or [shell-parity](shell-parity.md) matrix here — that is the [release](release.md) docs pass. ## Describe the change @@ -30,21 +30,22 @@ jj git fetch jj new main@origin ``` -Use `master@origin` or `trunk@origin` when that is the repository's trunk bookmark. +Use `master@origin` or `trunk@origin` when that is the repository's trunk bookmark. Prefer a sibling workspace for the implementation itself; see [Version Control](version-control.md). -Before publishing, inspect the change, format it, and run the relevant tests and lint: +Before publishing, finish the two cleanup rounds from `AGENTS.md`, then inspect the change, format it, and run the tests that match what changed — not the whole matrix: ```bash -jj st jj diff jj fix -just test -just test-app # SwiftUI changes -just test-ui # user-visible SwiftUI workflows -just test-gpui # GPUI changes +just test-rust # Rust crate change +just test-app # SwiftUI app change +just test-ui JayJayUITests/… # user-visible SwiftUI workflow +just test-gpui # GPUI-only change; skip if just test-rust already ran jayjay-gpui just lint ``` +`just test` (`cargo test --workspace`) is the full Rust gate when several crates moved. Do not also run `just test-gpui`. Do not run `just build` unless the change is the macOS app bundle or UniFFI packaging. + Describe the change, set a topic bookmark, and push it: ```bash @@ -53,7 +54,7 @@ jj bookmark set -r @ jj git push --bookmark ``` -Open the bookmark context menu in JayJay and choose **Pull Request on GitHub**, **Pull Request on Codeberg**, or **Pull Request on Cursor**. For GitHub, `gh pr create --draft --base main --head ` is also supported. For Cursor Origin, JayJay runs `origin pr create` when no PR exists for that bookmark. GitHub-mirrored Origin remotes cannot host Origin PRs; JayJay reports that error instead of opening the codebase page. +Open the bookmark context menu in JayJay and choose **Pull Request on GitHub** or **Pull Request on Cursor**. For GitHub, `gh pr create --draft --base main --head ` is also supported. For Cursor Origin, JayJay runs `origin pr create` when no PR exists for that bookmark. GitHub-mirrored Origin remotes cannot host Origin PRs; JayJay reports that error instead of opening the codebase page. ## Update after review diff --git a/agents/release.md b/agents/release.md index 61b47525..0c555093 100644 --- a/agents/release.md +++ b/agents/release.md @@ -5,10 +5,10 @@ Load this file before version bumps, packaging, appcast changes, GitHub releases Releases are not complete after `just release`. The full release flow is: 1. Run `just set-version ` to bump every source at once (`shell/justfile` version + build_number, the root Cargo workspace package version inherited by the CLI and GPUI, and `shell/mac/project.yml`). `project.pbxproj` and `Cargo.lock` regenerate on build. Never hand-edit one source — version drift ships binaries and update metadata that disagree. -2. Diff the complete range from the previous release tag with `jj log -r 'v..@'` and `jj diff --from v --to @ --summary`, then write SwiftUI macOS notes as an HTML body without wrapper tags in `releases/.html`. Cover user-visible SwiftUI changes from the whole range, not only the current local stack. +2. Diff the complete range from the previous release tag with `jj log -r 'v..@'` and `jj diff --from v --to @ --summary`. From that range, refresh **shipped user docs** (below) and write SwiftUI macOS notes as an HTML body without wrapper tags in `releases/.html`. Cover user-visible SwiftUI changes from the whole range, not only the current local stack. 3. Run `just build` to verify the release version still builds. 4. Run `just release` to verify immutable worker migration checksums, build, sign, notarize, zip, verify the extracted archive with `codesign`, `stapler validate`, and `spctl -av`, produce the SHA-256, and prepend the entry to `docs/appcast.xml`. It also runs `just check-version`, aborting if any source disagrees. Keep the Mac unlocked: a locked screen locks the keychain, so notarization fails with `No Keychain password item found for profile: notarytool` even when the profile exists. -5. Commit the version bumps, SwiftUI release notes, and `docs/appcast.xml` as `release: (build N)`. +5. Commit the version bumps, shipped user docs, SwiftUI release notes, and `docs/appcast.xml` as `release: (build N)`. 6. Create and push the `v` tag from the release commit. Tag pushes run the AppImage workflow and retain the Linux alpha builds as CI artifacts, but the GPUI alpha artifacts are not a release gate. 7. Run `just shell::publish` to create the public GitHub release, upload the zip, verify the Sparkle asset URL is public, and rewrite `../tap/Casks/jayjay.rb`. The AppImage workflow runs when that release is published and attaches GPUI Linux alpha AppImages plus SHA-256 files asynchronously; do not wait for it during the macOS release unless you are specifically validating GPUI alpha artifacts. 8. Push `main` only after `just shell::publish` succeeds, so `docs/appcast.xml` never points at a missing or draft-only asset. @@ -43,6 +43,22 @@ immutable; add the next numbered migration and checksum for every schema change. - The GitHub release body uses `releases/.html`. GPUI builds live on the same tag and GitHub release as asynchronously attached artifacts, without a repository release-notes file. - `../tap/Casks/jayjay.rb` must match the uploaded release asset and SHA-256. +## Shipped User Docs + +Feature PRs do not update the user guide, Help Book, website, or parity matrix. Do that once per release from `v..@`. Skip a file whose topic did not ship in this range. Canonical sources, in order: + +- `docs/guide.html` — public user guide (workflow source of truth) +- `docs/imgs/` — screenshots shared with the Help Book +- `shell/mac/Resources/JayJayHelpBook/` — topic HTML reused from the guide; rebuild with `just shell::help` +- `docs/llms.txt` — machine-readable feature summary, only if the feature list changed +- `docs/index.html` FAQ — only if the range answers a common question +- `agents/shell-parity.md` — matrix rows aligned to the guide, including closed gaps +- `README.md` — only install, positioning, or requirements changes; do not duplicate the guide +- `Roadmap.md` — shipped vs planned status +- `UserGuide.md` — stub pointing at the web guide; do not grow a second copy of the guide + +Do not edit `docs/appcast.xml` in this pass; step 4 owns it. Load [Help Book Guide](help-book.md) before changing Help pages or `docs/guide.html`. + ## Release Notes `releases/.html` is mandatory, covers only the SwiftUI macOS app, and is the source for both the Sparkle update prompt and GitHub release body. GPUI releases are represented by the shared tag and asynchronously attached GitHub release artifacts instead of a separate notes file. Missing or empty SwiftUI notes abort publication. diff --git a/agents/review-state.md b/agents/review-state.md index 77c273a1..52c8dea9 100644 --- a/agents/review-state.md +++ b/agents/review-state.md @@ -7,8 +7,9 @@ Review state (marks and notes) is persistent across app restarts, local to the u ## Store - Canonical implementation: `jayjay_review::ReviewStore` (marks in `marks.rs`, notes in `note_store.rs`, reconciliation in `reconcile.rs`). -- Persistence: `review_store.json` under the app config dir (`~/Library/Application Support/dev.hewig.jayjay/` on macOS); `JAYJAY_REVIEW_STORE_PATH` overrides it for tests. Writes are atomic (temp + rename). An unparseable file is preserved as `.json.corrupt` before defaulting; individual notes that fail to parse — or carry fields from a newer version — are carried through save untouched. -- Shells: the SwiftUI `Shared/ReviewStore.swift` is an `@Observable` facade over UniFFI calls with a per-file marks cache (invalidated on mutation) and a one-time UserDefaults legacy import. GPUI holds one process-global store and must mutate through `window/review.rs::mutate`, which refreshes from disk first so a long-lived snapshot never clobbers writes from the CLI or the other shell. GPUI render-path reads go through refresh-then-read wrappers (`refresh_if_stale`) so notes the CLI or SwiftUI resolved while the window was open are noticed; note reconciliation loads asynchronously on the view model (`loaders/review_notes.rs`, generation-guarded) and feeds rows, badges, and banners from one report. +- Persistence and file format: [Storage Guide](storage.md). +- SwiftUI: `Shared/ReviewStore.swift` is an `@Observable` UniFFI facade with a per-file marks cache. +- GPUI: one process-global store; mutate only through `window/review.rs::mutate` (refresh from disk first). Render-path reads use `refresh_if_stale`. Note reconciliation loads asynchronously on the view model (`loaders/review_notes.rs`, generation-guarded). ## Marks diff --git a/agents/shell-parity.md b/agents/shell-parity.md index e253c098..36f5aa52 100644 --- a/agents/shell-parity.md +++ b/agents/shell-parity.md @@ -1,6 +1,6 @@ # Shell Feature Parity Guide -Load this file before adding, removing, or changing user-visible behavior in either shell, especially when the feature exists in SwiftUI but not GPUI, or vice versa. Also load [SwiftUI Shell Guide](swiftui.md) or [GPUI Shell Guide](gpui.md) before editing that shell's code. +Load this file when checking whether a user-visible workflow is missing in one product shell, or when refreshing the matrix during the [release](release.md) shipped-docs pass. During feature work, put shared behavior in Rust and implement the requested shell; do not edit this matrix in the feature PR. Also load [SwiftUI Shell Guide](swiftui.md) or [GPUI Shell Guide](gpui.md) before editing that shell's code. JayJay has two product shells: the SwiftUI macOS app and the GPUI Linux shell. GPUI also builds on macOS for development, but that build is not a parity target and does not need to duplicate SwiftUI-only platform integrations. Unless a row says otherwise, the GPUI column describes Linux behavior; core jj workflows should remain familiar across both product shells. @@ -15,29 +15,29 @@ Use this as a lightweight user-facing feature map sourced from [the public user ## Feature Map -Update this matrix when the user guide adds a feature, a shell closes a gap, or a feature deliberately remains shell-specific. +At release, update this matrix when the user guide adds a feature, a shell closes a gap, or a feature deliberately remains shell-specific. | User Guide Feature | SwiftUI | GPUI (Linux) | Notes | | --- | --- | --- | --- | -| Open a Repository | Yes | Yes | Repository-list history stays shell-local, while pins share the Rust-backed `repositories.json`. Both shells keep Pinned above Recent, preserve pins when Recent is cleared, expose live windows plus closed pins from the repository title, activate an existing window without duplication, open closed pins in a new window, and return to the repository list after the last repo window closes. | -| Main Window | Yes | Yes | DAG, detail header, file column, diff pane, status bar, bookmark/tag/conflict markers, sibling-workspace name@ chips, and working-copy state should describe the same jj data. | -| Navigate History | Yes | Yes | Selection, keyboard navigation, toolbar revset filtering (presets and custom expressions), load-more behavior, context actions, drag/drop outcomes, and divergent-change handling should stay aligned. | -| Review Diffs | Yes | Yes | Text diff, unified/side-by-side modes, expandable collapsed context (Show 10 / Show all), find, image diff, file review, filename filtering, and flat/tree file lists are GPUI-covered. Keyboard access to expansion differs by mechanism: GPUI tab-focuses the per-region controls (Enter/Space), SwiftUI offers the palette action Expand All Unmodified Lines. SwiftUI briefly tints small reveals when Reduce Motion is off; GPUI uses an atomic, animation-free swap. Both shells intentionally fall back to unified for purely added or deleted files; rich-preview gaps are enumerated in the next row. | -| Rich File Previews | Yes | Partial — Markdown images; inline HTML | Raw/processed projection modes and cache identity match. GPUI has projection controls, banners, HTML external open, native SVG preview, and a rendered Markdown preview (native block renderer, single post-change document with scrolling — same single-view model as SwiftUI). Exact gaps: Markdown image blocks render as placeholders instead of actual images; GPUI has no inline sandboxed HTML preview toggle, only external-open. | -| Review Notes | Yes | Yes | GPUI supports add/edit/resolve/delete review notes, gutter dot markers, inline note rows, file-list badges, the noted-files filter, and a stale/orphaned banner. Inline note rendering is unified-view-only in both shells; side-by-side shows a note-count banner with "Show in Unified" in both, so that is not a GPUI gap. | -| Compare Changes | Yes | Yes | Shift-click compare, bookmark diff, reverse compare, clear compare, and interdiff loading should use the same rev semantics. | -| Edit Diffs & Split Work | Yes | Yes | Both shells offer Diff Edit for the working copy and other mutable changes, while respecting jj's immutable revset. They cover line-granularity discard from the normal diff gutter and a dedicated multi-file view with per-file cards, line/hunk/file selection, select-all, keep-only-selected Done, Move to Working Copy, New Child, and New Parallel. Both show per-file +/− line stats in card headers, per-file collapse/expand, Expand All / Collapse All buttons with `mod+alt+e` / `mod+alt+c`, auto-collapse for large diffs via the shared `diff_edit_auto_collapsed_paths` policy, and a keyboard focus model: `j`/`k` or `↑`/`↓` move between file cards, `←`/`→` collapse/expand, `Return` toggles, and `Space` selects the focused card. Both also edit changed regular text files directly in the current working copy through a large, soft-wrapping, pre-highlighted modal editor that leaves the diff detail mounted, with file/change identity checks that prevent stale overwrites; historical, compare-mode, conflicted, removed, projected, and image files remain read-only. Both support blocking directory comparison and line-selectable diff-editor sessions through jj, plus Git's custom difftool contract. | -| Change Operations | Yes | Yes | Both shells expose new-change-on-top, `jj edit`, squash into a parent or selected change, rebase selected, merge selected, duplicate, absorb, revert/backout, abandon, and operation-log restore. Their commit boxes prefill from @'s existing description without replacing a typed draft when @ moves to an empty change. Direct file actions split files from mutable changes, move historical files to @, and keep commit/delete working-copy-only; Diff Edit additionally supports Move to Working Copy, New Child, and New Parallel. GPUI AI messages use codex/claude, while Apple Intelligence remains a SwiftUI platform integration. | -| Bookmarks, Git & Pull Requests | Yes | Partial — bookmark-manager mutations; post-drag Push | GPUI covers bookmark creation, DAG drag/move, tracking, push/delete, Git fetch/push, remote URL opening, GitHub/GitLab/Codeberg/Cursor Origin PR/MR opening, and selected-bookmark PR/MR plus CI status. Exact gaps: the Bookmark Manager does not expose rename, push, delete, PR/MR open, conflict resolution, per-remote ahead/behind/diverged details, remote choice when tracking, or the hide-by-default **Show deleted** plus per-row **Forget** flow; dragging a tracked bookmark also does not show SwiftUI's one-click sidebar **Push** affordance. | -| Workspaces | Yes | Yes | Both shells create a workspace by name into a sibling directory, open it in its own window, show workspace context, and forget workspaces. GPUI entry points: Repository menu, status-bar workspace picker, and palette; switching lives in the status-bar picker where SwiftUI also offers palette "Switch to" entries — a presentation difference, not a workflow gap. | -| Stacked Pull Requests | Yes | Yes | Both shells preview the detected stack, validate edited bookmark names, and submit create/update operations with dependent bases. Successful multi-PR GitHub submissions also link a native stack through `gh api` when GitHub Stacked PRs is enabled, with a non-destructive dependent-chain fallback; GitLab detects the dependent MR stack automatically; Cursor Origin submits dependent PRs through the `origin` CLI. Done opens the top PR for a linked GitHub stack, the highest available GitLab MR, and every submitted PR for a GitHub dependent-chain fallback or Cursor Origin stack. | -| Conflict Resolution | Yes | Yes | Conflicted changes/files, conflict diff styling, Use Ours/Theirs, Edit in JayJay, explicit external-editor handoff, and refresh-after-resolution stay behaviorally equivalent. Both shells open a large, soft-wrapping conflict modal over the still-mounted repository detail, edit syntax-highlighted text without launching another tool, track unresolved markers, and save complete or partial resolutions. Two-sided conflicts show Base-relative, diff-and-syntax-highlighted Left / Right sources on top with the editable Result below, switch the source area to a full-width Base on request, and support exact whole-side selection. Hunk mode presents each conflict once as a unified Left-to-Right diff with non-editable −/+ gutters, quiet Accept Left / Right / Base links, hover help, and Option-Left/Right shortcuts; Raw mode remains available for direct marker editing. N-way jj conflicts remain editable through their materialized markers. JayJay separately accepts both jj and Git custom merge-tool contracts when deliberately launched as an external tool. | -| Inspection Tools | Yes | Yes | File Annotate, File History, and Change Evolution should use the same source revs, copy values, and compare targets. | -| Command Palette | Yes | Yes | Action names, raw jj behavior, command output handling, and searchable help topics should stay aligned even if presentation differs. | -| Tools & Settings | Yes | Partial — custom command editing; font-size setting | Shared config ids and option vocabularies match. Both shells copy the same Rust-owned jj diff/edit/merge tool definition from Settings, and both command-line entry points print it with `jayjay config`. Exact gaps: GPUI displays but cannot edit custom editor/terminal command values in Settings, and font size is changed only through zoom commands rather than the Settings control. GPUI includes codex/claude detection in Settings → Tools, jj/gh/glab/origin detection plus a Linux `jayjay` installer in Settings → CLI (AppImage-aware symlink in `~/.local/bin`). SwiftUI-only macOS integrations are outside the GPUI Linux parity target. | -| Help & User Guide Access | Yes | Yes | Both shells reach the same help content and expose Send Feedback and Keyboard Shortcuts from the Help menu and command palette. On Linux, GPUI uses the desktop's `xdg-open` handler for the guide and pre-addressed feedback email. Its palette lists the same searchable help topics (shared `HelpFeatures.json`) opening the guide at each topic's anchor. The bundled macOS Help Book is SwiftUI-only. | -| Keyboard Shortcuts | Yes | Yes | GPUI implements every shortcut published in the user guide: palette, find, refresh, open repository, zoom, Bookmark Manager, operation log/undo, file review, shift-click compare, diff-edit expand/collapse all plus card navigation (`j`/`k`, arrows, `Return`), `j`/`k`, and `Ctrl+N`/`Ctrl+P`. Both shells expose a native **Help → Keyboard Shortcuts** reference window on `mod+/`; GPUI displays platform-correct primary keys and uses `Ctrl` on Linux. | -| GPUI Shell Alpha | N/A | Yes | The guide's GPUI alpha section is the source of truth for current GPUI coverage claims. Update it and this matrix together. | +| Open a Repository | Yes | Yes | Pins share `repositories.json`; Recent is shell-local. Title menu activates an open window or opens a closed pin without duplicating. | +| Main Window | Yes | Yes | Same jj data in DAG, detail, file column, diff, status bar, markers, and workspace chips. | +| Navigate History | Yes | Yes | Selection, revset filter, load-more, context actions, drag/drop, and divergent changes stay aligned. | +| Review Diffs | Yes | Yes | Same text/image diffs, layout modes, find, file review, and tree/flat lists. Expansion: GPUI tab-focuses per-region controls; SwiftUI has Expand All Unmodified Lines. SwiftUI tints small reveals when Reduce Motion is off; GPUI swaps atomically. Both fall back to unified for purely added or deleted files. | +| Rich File Previews | Yes | Partial — Markdown images; inline HTML | Raw/processed modes and cache identity match. GPUI gaps: Markdown images are placeholders; no inline sandboxed HTML preview (external-open only). | +| Review Notes | Yes | Yes | Same add/edit/resolve/delete workflow. Inline rows unified-only; side-by-side uses a note-count banner plus Show in Unified in both shells. | +| Compare Changes | Yes | Yes | Shift-click, bookmark diff, reverse, clear, and interdiff use the same rev semantics. | +| Edit Diffs & Split Work | Yes | Yes | Same Diff Edit destinations, gutter discard, per-file cards/stats/collapse/keyboard focus, and working-copy text editor. Historical, compare, conflicted, removed, projected, and image files stay read-only. Both honor jj and Git custom difftool contracts. | +| Change Operations | Yes | Yes | Same change/file mutations and commit-box draft reseeding. Apple Intelligence is SwiftUI-only; GPUI uses codex/claude. | +| Bookmarks, Git & Pull Requests | Yes | Partial — bookmark-manager mutations; post-drag Push | GPUI covers create, DAG drag/move, tracking, push/delete, fetch/push, remote URLs, GitHub/GitLab/Codeberg/Cursor Origin PR/MR open, and selected-bookmark CI. Gaps: Bookmark Manager rename, push, delete, PR/MR open, conflict resolution, per-remote ahead/behind/diverged, remote choice when tracking, Show deleted / Forget; no sidebar Push after dragging a tracked bookmark. | +| Workspaces | Yes | Yes | Create, open, show context, forget. Switching: GPUI status-bar picker; SwiftUI also has palette Switch to — presentation, not a workflow gap. | +| Stacked Pull Requests | Yes | Yes | Same preview, name validation, and dependent-base submit. GitHub can link a native stack (`gh api`) with dependent-chain fallback; GitLab detects the MR stack; Cursor Origin uses `origin`. Done opens the top GitHub linked PR, the highest GitLab MR, and every PR for a GitHub fallback or Origin stack. | +| Conflict Resolution | Yes | Yes | Same conflict modal (hunk/raw, ours/theirs, Base, n-way markers) over the mounted detail. Both honor jj and Git mergetool contracts. | +| Inspection Tools | Yes | Yes | Annotate, file history, and evolog use the same revs, copy values, and compare targets. | +| Command Palette | Yes | Yes | Action names, raw jj, output handling, and searchable help topics stay aligned even if presentation differs. | +| Tools & Settings | Yes | Partial — custom command editing; font-size setting | Shared config ids. Both copy the Rust-owned jj tool definition; `jayjay config` prints it. Gaps: GPUI cannot edit custom editor/terminal commands; font size is zoom-only. GPUI Settings → Tools has codex/claude; Settings → CLI has jj/gh/glab/origin plus Linux `jayjay` install (AppImage-aware symlink in `~/.local/bin`). SwiftUI-only macOS integrations are outside the GPUI Linux parity target. | +| Help & User Guide Access | Yes | Yes | Same help content, Send Feedback, and Keyboard Shortcuts. Linux GPUI uses `xdg-open`. Palette topics come from shared `HelpFeatures.json`. Bundled Help Book is SwiftUI-only. | +| Keyboard Shortcuts | Yes | Yes | GPUI implements the published guide shortcuts. Both expose **Help → Keyboard Shortcuts** on `mod+/`; GPUI shows platform-correct keys (`Ctrl` on Linux). | +| GPUI Shell Alpha | N/A | Yes | The guide's GPUI alpha section is the source of truth. Refresh it and this matrix together at release. | ## Parity Rules @@ -45,7 +45,7 @@ Update this matrix when the user guide adds a feature, a shell closes a gap, or 2. Share persisted config ids, command ids, projection identities, review identities, font/editor/terminal option vocabularies, and jj action semantics. 3. Preserve shell-native presentation. Matching behavior does not require matching exact layout, animation, or menu placement. 4. Treat GPUI macOS differences as development-build differences, not parity gaps, unless they expose a portable core-behavior defect. -5. If a product shell does not change, keep the reason visible in the matrix or in the PR notes. +5. If a product shell does not change, keep the reason visible in the matrix at release. 6. Validate at the smallest useful layer: Rust unit tests for shared behavior, Swift tests or XCUITests for Swift-only UI, and GPUI component tests for GPUI state and render behavior. Do not add tests that only mirror constants or field wiring. A parity test should prove behavior, such as a shared config id round-tripping, a diff projection loading the same mode, or a UI action dispatching the same core mutation. diff --git a/agents/storage.md b/agents/storage.md index 15d87c0c..aae20a82 100644 --- a/agents/storage.md +++ b/agents/storage.md @@ -18,7 +18,7 @@ Rust stores resolve platform-native directories through `directories::ProjectDir | Pinned repositories | `jayjay-core`; SwiftUI via UniFFI; GPUI directly | `repositories.json` in the shared config directory | An ordered `repositories` array of canonical absolute UTF-8 repository paths. New pins are inserted first; empty paths and exact duplicates are removed on load. | | Review marks and notes | `jayjay-review`; SwiftUI via UniFFI; GPUI and CLI directly | `review_store.json` in the shared config directory | File/hunk review marks keyed by `change_id|path`, content identities, and local review notes including path, side, line, anchor context, body, timestamps, and resolution state. | | SwiftUI settings and history | SwiftUI-only `AppSettings` | `UserDefaults` for bundle `dev.hewig.jayjay` | Appearance and font, diff options, layout, confirmations, onboarding, editor/terminal choices, update channel, sponsorship state, up to 12 recent repositories, and the last opened repository. | -| SwiftUI auxiliary state | SwiftUI components | The same `UserDefaults` domain | Command-palette position. A legacy `jayjay.reviewedFiles` blob is imported once into the shared review store and then removed. | +| SwiftUI auxiliary state | SwiftUI components | The same `UserDefaults` domain | Command-palette position. | | GPUI settings and history | GPUI-only Rust `AppConfig` | `config.toml` in the platform config directory | Appearance and font, diff options, layout, tools, feature confirmations, onboarding, update channel, window bounds/maximized state, and up to 12 recent repositories. | Recent repositories are history, not projects. Each shell owns its own recent list. Pins are persistent projects and are intentionally shared by both shells. @@ -33,15 +33,11 @@ The canonical implementation is `crates/jayjay-core/src/repositories.rs`. {"repositories":["/Users/example/work/project-a","/Users/example/work/project-b"]} ``` -- Repository paths are canonicalized before lookup or mutation so aliases do not create duplicate pins or windows. -- Paths that cannot be represented as UTF-8 are not pinned, because the shared JSON and UniFFI contract cannot round-trip them; the store never writes a lossy replacement path. -- A read fingerprints the small local JSON file and reparses it when another process changes its contents, including equal-length replacements on coarse-timestamp filesystems. -- Deleting the file is a state change: long-lived readers become empty, and the next mutation starts from that empty state instead of restoring deleted pins. -- Every mutation refreshes from disk before applying its change, preventing a long-lived shell from overwriting a newer write from the other shell. -- Writes use a unique sibling temporary file followed by rename, so readers do not observe partial JSON. -- A failed write leaves the last loaded state published to the shell; an unsaved mutation is never reported as persisted. -- Malformed JSON is renamed to `repositories.json.corrupt`; the store then starts empty without overwriting the preserved file. -- `JAYJAY_REPOSITORIES_PATH` overrides the canonical path for tests and diagnostics. +- Canonicalize paths before lookup or mutation; never pin non-UTF-8 paths (the JSON/UniFFI contract cannot round-trip them). +- Reads fingerprint the file contents and reparse when another process changed them, including equal-length replacements on coarse-timestamp filesystems; every mutation refreshes from disk first, then writes via temp file + rename. A failed write must not report the unsaved mutation as persisted. +- Deleting the file empties long-lived readers; the next mutation starts from empty instead of restoring old pins. +- Malformed JSON is renamed to `repositories.json.corrupt`; the store starts empty without overwriting that file. +- `JAYJAY_REPOSITORIES_PATH` overrides the path for tests. ### Review marks and notes @@ -60,11 +56,9 @@ The canonical implementation is `crates/jayjay-review/src/store/`. See [Review S } ``` -- Marks contain the content identity captured when a file or hunk was reviewed. Notes additionally contain their anchor, user-authored body, timestamps, and resolved state. -- Reads and mutations use the same stale-refresh and atomic temp-file/rename rules as the pin store. -- Malformed JSON is preserved as `review_store.json.corrupt` before falling back to an empty store. -- Unknown top-level fields, unknown note entries, and unknown fields inside parseable notes survive a save so different JayJay/CLI versions can safely share the file. -- `JAYJAY_REVIEW_STORE_PATH` overrides the canonical path. +- Same refresh-before-mutate and atomic temp-file/rename rules as the pin store. Malformed JSON is preserved as `review_store.json.corrupt`. +- Unknown top-level fields, unknown note entries, and unknown fields inside parseable notes survive a save so mixed JayJay/CLI versions can share the file. +- `JAYJAY_REVIEW_STORE_PATH` overrides the path for tests. Neither JSON store is a synchronization service. The refresh-before-mutate contract prevents ordinary cross-process lost updates, but simultaneous writes are still last-rename-wins. Keep mutations short and route all writes through the Rust store. @@ -111,4 +105,4 @@ These files are content-derived or process-scoped. Callers must tolerate their a 4. Use atomic replacement for user-authored shared files and refresh from disk before mutating a long-lived snapshot. 5. Add a path override or in-memory constructor so tests cannot write production data. 6. Test round trips, malformed input, duplicate/normalization rules, and two independently loaded writers when the store is cross-process. -7. Update this inventory and [Shell Feature Parity Guide](shell-parity.md) when storage ownership or shell-sharing behavior changes. +7. Update this inventory when storage ownership or shell-sharing behavior changes. Refresh [Shell Feature Parity Guide](shell-parity.md) in the release shipped-docs pass if a user-visible workflow changed. diff --git a/agents/swiftui.md b/agents/swiftui.md index dd706ed6..28473656 100644 --- a/agents/swiftui.md +++ b/agents/swiftui.md @@ -1,6 +1,6 @@ # SwiftUI Shell Guide -Load this file before changing SwiftUI file layout, view-model structure, diff rendering wiring, or presentation surfaces. Crate boundaries live in [Architecture Guide](architecture.md); shell-to-shell user-visible behavior belongs in [Shell Feature Parity Guide](shell-parity.md); review marks and notes in [Review State Guide](review-state.md). +Load this file before changing SwiftUI file layout, view-model structure, diff rendering wiring, or presentation surfaces. Crate boundaries live in [Architecture Guide](architecture.md); review marks and notes in [Review State Guide](review-state.md). Cross-shell user-visible gaps are listed in [Shell Feature Parity Guide](shell-parity.md) and refreshed at release. ## File Layout @@ -50,7 +50,7 @@ The ViewModel owns `JayJayRepo`; all jj operations go through it. `Core/` holds ## Conventions - **JayJayDiffUI boundary**: the package declares the gutter-action protocols (`DiffGutterContextActions` and its `Selection`/`Edit`/`Review`/`Note` sub-protocols, one file each under `Gutter/`) plus the anchor/summary value types in `Notes/`; the app target conforms via `DiffSection` and its `+EditActions`/`+ReviewActions`/`+NoteActions` extensions. The package stays app-agnostic and calls back only through those protocols. -- **File splitting**: SwiftLint opts into `file_length` and `function_parameter_count` for `Sources/JayJay`. One primary type per file, named after the type (private helpers used only there stay put; a view keeps its `#Preview`). Growing types split into `+Feature.swift` extension files (see `DAGView+*`, `DiffSection+*`, the `ViewModel/` folders) instead of growing one file; wide parameter lists become value types (e.g. `NoteAnchor`, `SplitSheetRequest`). Dense folders get responsibility subfolders (`Detail/FileList/`, `Repo/ContentView/`, the JayJayDiffUI `Gutter/`/`Notes/`/`Rendering/` layout). +- **File splitting**: One primary type per file, named after the type (private helpers used only there stay put; a view keeps its `#Preview`). Split by responsibility, not line count: growing types that pick up a second job become `+Feature.swift` extension files (see `DAGView+*`, `DiffSection+*`, the `ViewModel/` folders); wide parameter lists become value types (e.g. `NoteAnchor`, `SplitSheetRequest`). Dense folders get responsibility subfolders (`Detail/FileList/`, `Repo/ContentView/`, the JayJayDiffUI `Gutter/`/`Notes/`/`Rendering/` layout). - **Pinned paths**: `Shared/AccessibilityIdentifiers.swift` is referenced by exact path in `project.yml` (the UITests target compiles it directly); moving it breaks project generation. Everything else under `Sources/JayJay` is glob-included and moves freely. - **Diff caching**: `Diff/DiffStore.swift` (`@Observable`) fronts an `actor DiffCache`, an LRU bounded by content bytes. Keys are content-addressed on the immutable commit id (never the mutable rev) plus compare side, whitespace mode, and path, so amends/rebases cannot serve stale diffs. `preload()` cancels the prior preload task. - **Refresh pipeline** (`ViewModel/Core/RepoViewModel+Refresh.swift`): one cancel-and-replace `refreshTask`; FS-triggered refreshes are dropped while one is in flight; snapshots (e.g. `StatusBarSnapshot`) load off-thread and apply atomically. Commit-box drafts reseed only when the working-copy change id actually changes. diff --git a/agents/testing.md b/agents/testing.md index 6d0db9fa..60b140f5 100644 --- a/agents/testing.md +++ b/agents/testing.md @@ -2,6 +2,12 @@ Load this file before adding fixtures, reorganizing tests, or changing UI test behavior. +## Running Tests + +The inner-loop commands are in `AGENTS.md` (Feature Loop → Inner loop); `just test-rust ` passes extra `cargo test` args after the crate. Do not start with `just test`, `just test-app`, `just lint`, or `just build`. + +`just test` is `cargo test --workspace` (includes GPUI). Use it when publishing, not as the inner loop. `just test-app` and unfiltered `just test-ui` rebuild FFI, the Help Book, and the Xcode app — skip them unless Swift/app behavior changed and a package-scoped Rust test cannot prove it. `just test-gpui` after `just test` is redundant. + ## Coverage - Unit tests should cover core logic, view-model behavior, parsers, serialization boundaries, and regressions. @@ -19,6 +25,10 @@ Load this file before adding fixtures, reorganizing tests, or changing UI test b - Helpers that implement a crate's own traits cannot live in jj-test — a helper crate linking the crate under test implements different trait types than the unit tests' `crate::` ones. Put them in the defining crate behind a `test-util` feature (see `jayjay-review/src/test_util.rs`) so other crates' tests can dev-depend on the same impls. - Keep helpers local when they construct crate-private types for one module's tests. +## Swift Tests + +Swift unit tests live in `shell/mac/Tests/JayJayTests/` (`just test-app`). Cover Swift-only behavior; shared logic belongs in Rust tests. + ## SwiftUI UI Tests UI tests live in `shell/mac/Tests/JayJayUITests/`. @@ -31,6 +41,8 @@ Use accessibility identifiers from `shell/mac/Sources/JayJay/Shared/Accessibilit The sandboxed XCUITest runner cannot create repositories where the launched app can open them. Mutating scenes therefore use dedicated copies generated from a canonical fixture by `ui-test-fixtures.sh`; name those copies for the workflow, not for their source fixture. Each scene gets an isolated review store. +Pass a test id to run one scene: `just test-ui JayJayUITests/CommandPaletteScene/testOpenAndSearch`. + ## External Tool Integration Use `scripts/test-external-tools.sh` for the real blocking process contract. It creates temporary jj repositories with syntax-highlightable Swift inputs, loads the launcher's own `jayjay config`, then runs `jj diff --tool jayjay`, `jj split --tool jayjay`, or `jj resolve --tool jayjay`. It intentionally does not build, use an Xcode test host, or call `cargo run`. Pass `--launcher /path/to/JayJay.app` to test a specific bundle and `--keep` to inspect the edited repositories afterward. diff --git a/agents/version-control.md b/agents/version-control.md index fc16f9bc..09eed0c3 100644 --- a/agents/version-control.md +++ b/agents/version-control.md @@ -11,13 +11,30 @@ Never run JJ-aware commands concurrently in the same workspace. Read-only comman - Serialize all `jj` commands, including `jj st`, `jj log`, and `jj diff`. - Serialize `jayjay review ...` and any script or tool that opens the repository through JJ with other JJ-aware commands. - Parallelize only commands known not to read, snapshot, or update JJ's working-copy or operation state. +- Parallel **workspaces** may run jj at the same time; parallel jj in **one** workspace may not. - If divergence appears, compare each divergent commit to `@` by commit ID and abandon only snapshots proven stale; never abandon every commit for the shared change ID. +## Avoid Needless Snapshots + +The snapshot rules are in `AGENTS.md` (Feature Loop → Do not snapshot unless you mean to). Reads that must not snapshot the working copy: + +```bash +jj --ignore-working-copy workspace list +jj --ignore-working-copy log --limit 10 +jj --ignore-working-copy op log --limit 5 +``` + +`--ignore-working-copy` is wrong for commands that should see or record the current files (`jj describe`, `jj commit`, `jj diff` of the working copy, `jj new`). Do not fetch, log, or diff "just in case" before creating a sibling workspace. + ## Workspace Policy -Use the current JJ workspace for normal and focused work. Create a sibling workspace only for a large or long-running session where isolation is materially useful, or when the user explicitly requests one. Do not create a workspace merely to keep a routine change separate. +The sibling-workspace rule and the `jj workspace add` recipe are in `AGENTS.md` (Feature Loop). Details that file leaves out: -When a temporary session is complete, finish or preserve its change as requested, then run `jj workspace forget ` so its empty working-copy commit does not remain in the graph. Forgetting workspace metadata does not delete the sibling directory; remove files only when that cleanup is authorized. +- Why a sibling: agent commands then snapshot that working copy, not the user's current `@`. +- `git worktree`, Cursor git worktrees, and the Codex/Claude hidden-worktree pattern (`~/.codex/**`, `~/.claude/**`, or any other home-dir agent worktree) are all out; isolation belongs in a named sibling next to this repo. +- Do not create a workspace merely to keep two edits separate inside one session — that is `jj new` / `jj split`. +- Pin the new workspace to a specific change with `-r ` when continuing existing work. +- When the session is complete, finish or preserve its change as requested, then `jj workspace forget ` so its empty working-copy commit does not remain in the graph. Forgetting workspace metadata does not delete the sibling directory; remove files only when that cleanup is authorized. ## Workspace Build Isolation @@ -25,22 +42,14 @@ Each JJ workspace gets its own Cargo `target/` by default. Preserve that isolati Preserve the configured compiler wrapper for Rust-backed commands. Kache is preferred for concurrent workspaces because it normalizes checkout paths and restores cached libraries, build outputs, and executables into each isolated target. On filesystems that support clones, restored outputs share physical storage with the cache until either copy changes. -```bash -just test -``` - -Keep the same command and each workspace's default target when builds run concurrently. Let the Kache configuration manage incremental artifacts; do not force a shared target or override incremental settings. Use the following fallback only when sccache is the configured cache: - -```bash -RUSTC_WRAPPER=sccache CARGO_INCREMENTAL=0 just test -``` - -For sccache, cross-workspace reuse also requires path normalization. Configure the daemon's `basedirs`, or set `SCCACHE_BASEDIRS` before it starts, to a platform-delimited list containing every absolute workspace root. Do not expect sccache to cache check-only compilation or targets that invoke the linker. +Keep each workspace's default target when builds run concurrently. Let the Kache configuration manage incremental artifacts; do not force a shared target or override incremental settings. Compiler caches do not replace workspace cleanup. When authorized to remove a completed sibling directory, remove its `target/` with it so old per-workspace artifacts do not accumulate. If a sandbox cannot use the configured wrapper or daemon, use `RUSTC_WRAPPER=""` for that command rather than changing the developer's global Cargo or cache configuration. ## Common Commands +Commands that snapshot the working copy (serialize these): + ```bash jj st jj log --limit 10 @@ -58,4 +67,4 @@ jj fix Filesets for `jj split` are positional; do not pass `--paths`. -Keep unrelated work in the current working copy unless the user asks to split or commit it. Split by behavior or responsibility, not merely by file boundaries. Use a pushed bookmark and JayJay's **Pull Request on GitHub**, **Pull Request on Codeberg**, or **Pull Request on Cursor** action for PRs. +Keep unrelated work in the current working copy unless the user asks to split or commit it. Split by behavior or responsibility, not merely by file boundaries. Use a pushed bookmark and JayJay's **Pull Request on GitHub** or **Pull Request on Cursor** action for PRs. diff --git a/justfile b/justfile index b2e41d48..67e777cb 100644 --- a/justfile +++ b/justfile @@ -12,12 +12,14 @@ default: list: @echo "just list Show available commands" - @echo "just test Run Rust tests" + @echo "just test-rust crate Package-scoped cargo test (inner loop)" + @echo "just test-ui [test-id] UI tests; pass a test id to run one scene" + @echo "just test All workspace Rust tests (publish)" @echo "just test-app Run macOS app tests" - @echo "just test-ui Run macOS app UI tests (needs fixture — see shell/mac/Tests/JayJayUITests/Support/SceneBase.swift)" @echo "just test-gpui Run GPUI shell tests (via shell::gpui-test, needs jj on PATH)" - @echo "just format Format Rust and Swift sources" - @echo "just lint Lint Rust (clippy) and Swift (swiftlint)" + @echo "just ffi Rebuild UniFFI Swift bindings" + @echo "just format Format Rust and Swift sources (publish)" + @echo "just lint Lint Rust (clippy) and Swift (swiftlint) (publish)" @echo "just clean Remove generated build artifacts" @echo "just build Build the macOS app" @echo "just run Build and launch the app" @@ -29,14 +31,20 @@ list: @echo "just gpui-appimage Build the GPUI Linux AppImage" @echo "just worker::list Show Cloudflare Worker/D1 recipes" +# Inner-loop Rust tests. Example: just test-rust jayjay-core +# just test-rust jayjay-core working_copy +# just test-rust jayjay-core --lib wrap +test-rust crate *args: + cargo test -p "{{crate}}" {{args}} + test: cargo test --workspace test-app: just shell::test -test-ui: - just shell::ui-test +test-ui test_id='': + just shell::ui-test "{{test_id}}" test-gpui: just shell::gpui-test diff --git a/shell/mac/Sources/JayJay/Shared/ReviewStore.swift b/shell/mac/Sources/JayJay/Shared/ReviewStore.swift index 9c179a80..5d78b9a2 100644 --- a/shell/mac/Sources/JayJay/Shared/ReviewStore.swift +++ b/shell/mac/Sources/JayJay/Shared/ReviewStore.swift @@ -6,8 +6,6 @@ import JayJayCore final class ReviewStore { typealias ReviewNote = NoteEntry - private static let legacyStorageKey = "jayjay.reviewedFiles" - let storeURL: URL? var notes: [ReviewNote] // Observable stand-in for the cache's contents: SwiftUI views read marks during render (gutter stripes, file rows), and without a tracked read a toggle would not re-render them until something else invalidated the view. @@ -17,7 +15,6 @@ final class ReviewStore { init() { storeURL = reviewStorePath().map { URL(fileURLWithPath: $0) } notes = [] - importLegacyMarks(from: .standard) } /// Test seam: persist to an explicit file instead of the shared store path. @@ -149,30 +146,4 @@ final class ReviewStore { marksCache.removeAll() marksVersion &+= 1 } - - // MARK: Legacy migration - - /// One-time import of marks the old UserDefaults-backed store left behind; runs only while no shared store file exists yet, then drops the legacy blob. - func importLegacyMarks(from defaults: UserDefaults) { - guard let storeURL, !FileManager.default.fileExists(atPath: storeURL.path), - let data = defaults.data(forKey: Self.legacyStorageKey), - let raw = try? JSONSerialization.jsonObject(with: data) as? [String: Any] - else { return } - for (key, value) in raw { - guard let separator = key.firstIndex(of: "|"), - let dict = value as? [String: Any], - let identity = dict["identity"] as? String, - !identity.isEmpty - else { continue } - let changeId = String(key[..= 0 ? UInt32($0) : nil } ?? [] - if dict["file_marked"] as? Bool ?? false { - markReviewed(changeId: changeId, path: path, identity: identity) - } else if !hunks.isEmpty { - setReviewedHunks(changeId: changeId, path: path, identity: identity, hunkIndices: hunks) - } - } - defaults.removeObject(forKey: Self.legacyStorageKey) - } } diff --git a/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift b/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift index 82cdfedb..9becd08d 100644 --- a/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift +++ b/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift @@ -171,30 +171,6 @@ final class ReviewStoreTests: XCTestCase { ) } - func testLegacyDefaultsImportOnFirstRun() throws { - let url = tempStoreURL() - let suiteName = "review-migration-\(UUID().uuidString)" - let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) - defer { defaults.removePersistentDomain(forName: suiteName) } - let legacy: [String: Any] = [ - "c1|a.txt": ["identity": "idA", "file_marked": true], - "c1|b.txt": ["identity": "idB", "file_marked": false, "hunks": [1]] - ] - try defaults.set(JSONSerialization.data(withJSONObject: legacy), forKey: "jayjay.reviewedFiles") - - let store = ReviewStore(storeURL: url) - store.importLegacyMarks(from: defaults) - - XCTAssertTrue(store.isReviewed(changeId: "c1", path: "a.txt", identity: "idA")) - XCTAssertTrue(store.isHunkReviewed(changeId: "c1", path: "b.txt", identity: "idB", hunkIndex: 1)) - XCTAssertNil(defaults.data(forKey: "jayjay.reviewedFiles")) - - // A later run with an existing store file must not re-import or drop the blob. - try defaults.set(JSONSerialization.data(withJSONObject: legacy), forKey: "jayjay.reviewedFiles") - ReviewStore(storeURL: url).importLegacyMarks(from: defaults) - XCTAssertNotNil(defaults.data(forKey: "jayjay.reviewedFiles")) - } - func testMalformedStoreIsPreservedBeforeWrite() throws { let url = tempStoreURL() try FileManager.default.createDirectory(