Skip to content

Commit 782bed9

Browse files
committed
docs: add drift lint checks to CI and pre-commit
Add a lightweight docs drift checker and wire it into CI as an early gate plus husky pre-commit. Also refresh README and copilot instructions to remove stale wording and strengthen source-of-truth guidance.
1 parent 7343408 commit 782bed9

6 files changed

Lines changed: 213 additions & 25 deletions

File tree

.github/copilot-instructions.md

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Open-source, cross-platform Git desktop app with a **worktree-first** workflow.
2424

2525
## Project Structure
2626

27+
This structure is a high-level orientation map. For file-level accuracy, verify against the current workspace tree before making assumptions.
28+
2729
```
2830
sproutgit/
2931
├── src/ # SvelteKit frontend
@@ -41,6 +43,8 @@ sproutgit/
4143
│ ├── +layout.svelte # Minimal: imports app.css, renders <slot />
4244
│ ├── +layout.ts # export const ssr = false
4345
│ ├── +page.svelte # Screen 1: Project picker (clone, open, recent projects)
46+
│ ├── settings/
47+
│ │ └── +page.svelte # Settings screen
4448
│ └── workspace/
4549
│ └── +page.svelte # Screen 2: Workspace (worktree mgmt + commit graph)
4650
├── src-tauri/
@@ -98,6 +102,24 @@ Key reminders:
98102
- If an interaction depends on visual state (hover-only controls, transient overlays), add or use a dedicated test ID before introducing brittle structural selectors.
99103
- **If a needed `data-testid` does not exist in the UI source, add it.** Never work around a missing test ID with fragile structural selectors — add the `data-testid` attribute to the component/template and use it in the test.
100104

105+
## E2E Failure Triage Protocol (Required)
106+
107+
When debugging a flaky or platform-specific E2E failure, follow this order exactly.
108+
109+
1. Reproduce locally with the narrowest failing scope (single spec or test name).
110+
2. Classify the failure as one of: deterministic logic bug, timing/timeout issue, state reset leak, or platform-only behavior.
111+
3. Validate reset assumptions first (workspace cleanup, config DB reset, `sg_workspace_hint` clear, verified reload).
112+
4. If Windows-only, inspect path format and shell semantics before raising timeouts.
113+
5. Apply the smallest fix that addresses root cause; avoid masking deterministic failures with broad timeout increases.
114+
6. Re-run only the affected spec first, then re-run the required E2E gate.
115+
116+
### Timeout Budget Policy (Required)
117+
118+
- Keep Playwright per-test timeout and helper timeouts aligned; do not change one without auditing the other.
119+
- For CI reliability updates, document why each timeout changed and which operation consumed the budget.
120+
- Increase timeouts only when evidence shows slow-environment variance; if failure is deterministic, fix behavior instead.
121+
- When a timeout is increased, include before/after values in the commit message or PR notes.
122+
101123
## Workspace Layout (User Projects)
102124

103125
SproutGit manages user repos in a prescribed directory layout:
@@ -114,6 +136,8 @@ SproutGit manages user repos in a prescribed directory layout:
114136

115137
## Rust Backend (`src-tauri/src/lib.rs`)
116138

139+
`src-tauri/src/lib.rs` is the source of truth for registered Tauri commands via `tauri::generate_handler![]`.
140+
117141
### Structs (all `#[serde(rename_all = "camelCase")]`)
118142

119143
- `GitInfo` — installed, version
@@ -127,17 +151,19 @@ SproutGit manages user repos in a prescribed directory layout:
127151
- `CommitGraphResult` — repo_path, commits
128152
- `CreateWorktreeResult` — worktree_path, branch, from_ref
129153

130-
### Tauri Commands
154+
### Tauri Commands (Representative, Not Exhaustive)
155+
156+
Representative command groups currently include:
157+
158+
- Git operations and worktree lifecycle (`git_info`, `list_worktrees`, `create_managed_worktree`, `delete_managed_worktree`, `checkout_worktree`, `reset_worktree_branch`)
159+
- Diff and staging (`get_diff_files`, `get_diff_content`, `get_worktree_status`, `stage_files`, `unstage_files`, `create_commit`, `get_working_diff`)
160+
- Workspace and config (`create_sproutgit_workspace`, `import_git_repo_workspace`, `inspect_sproutgit_workspace`, recent workspaces, app settings)
161+
- Hooks (`list_workspace_hooks`, create/update/delete/toggle, `run_workspace_hook`)
162+
- Editor/Git tool integration (`open_in_editor`, editor detection, git config read/write)
163+
- Terminal and watcher (`spawn_terminal`, `terminal_input`, `start_watching_worktrees`)
164+
- Optional E2E-only helpers (`set_window_size` when `e2e-testing` feature is enabled)
131165

132-
| Command | Purpose |
133-
|---------|---------|
134-
| `git_info` | Check Git installation and version |
135-
| `create_sproutgit_workspace` | Create managed workspace, optionally clone a repo URL |
136-
| `inspect_sproutgit_workspace` | Validate an existing SproutGit project directory |
137-
| `list_worktrees` | Enumerate Git worktrees with branch/HEAD info |
138-
| `list_refs` | Fetch branches & tags sorted by commit date |
139-
| `get_commit_graph` | Structured commit log with parents, refs (limit 20-400) |
140-
| `create_managed_worktree` | Create a new worktree under `worktrees/` dir |
166+
When command surfaces change, update this section in the same change.
141167

142168
### Helper Functions
143169

@@ -157,12 +183,21 @@ SproutGit manages user repos in a prescribed directory layout:
157183

158184
## Frontend API (`src/lib/sproutgit.ts`)
159185

160-
Typed wrappers around `invoke()` from `@tauri-apps/api/core`. Every Rust struct has a matching TypeScript type. Key exports:
186+
Typed wrappers around `invoke()` from `@tauri-apps/api/core`. Every Rust struct has a matching TypeScript type.
187+
188+
`src/lib/sproutgit.ts` is the source of truth for frontend-callable API wrappers.
189+
190+
Representative export groups include:
161191

162192
- `getGitInfo()`, `createWorkspace()`, `inspectWorkspace()`
163-
- `listWorktrees()`, `listRefs()`, `getCommitGraph()`
164-
- `createManagedWorktree()`
165-
- `onCloneProgress(callback)` — Event listener helper using `@tauri-apps/api/event`
193+
- Workspace import and recents/settings
194+
- Worktree lifecycle (`createManagedWorktree`, delete, checkout, reset)
195+
- Diff and staging helpers
196+
- Hook CRUD + progress listeners (`onHookProgress`)
197+
- File watcher helpers
198+
- Terminal lifecycle helpers
199+
- GitHub auth/repo helpers
200+
- Event listeners (`onCloneProgress`, `onImportProgress`, `onWorktreeChanged`, terminal events)
166201

167202
## Theme System
168203

@@ -381,7 +416,10 @@ When adding or changing **any** git or system interaction, follow all rules belo
381416
- **Option-boundary safety**: For commands that accept untrusted values, use argument boundaries (for example `--` when supported) to prevent option smuggling.
382417
- **System command registry**: Route all git/system process execution through registered helpers (`GitAction` / `SystemAction`) so behavior is auditable and testable.
383418
- **Cross-platform compatibility**: Assume macOS, Linux, and Windows on every change. Avoid OS-specific shell utilities unless a platform-specific fallback exists.
419+
- **Cross-platform subprocess checklist**: For any subprocess-env, hook, or shell-adjacent change, verify quoting, path semantics, and command behavior for bash/zsh and PowerShell (`pwsh` + Windows PowerShell compatibility where relevant).
384420
- **Path handling**: Use `Path`/`PathBuf` and platform-aware path/env separators. Do not hardcode `:` as PATH separator.
421+
- **Path canonicalization on Windows**: `std::fs::canonicalize()` returns `\\?\`-prefixed extended-length paths on Windows (e.g. `\\?\D:\...`). Always chain `strip_win_prefix()` from `crate::git::helpers` immediately after any `canonicalize()` call. This applies to paths stored in SQLite, set as subprocess env vars, or passed to shell scripts — not just paths serialised to the frontend. Git for Windows tolerates extended-length paths silently, but PowerShell cmdlets (e.g. `Join-Path`) reject them with cryptic errors.
422+
- **Single-source normalization rule**: Path normalization logic must live in shared helpers, not duplicated at call sites. When introducing a new canonicalization/normalization path, reuse existing helper functions or add one shared helper first.
385423
- **Path serialization — frontend-bound values**: Any `Path`/`PathBuf` value serialised in a Tauri command response (struct fields, `Ok(...)` return values) **must** be converted with `path_to_frontend()` from `crate::git::helpers`, not with `to_string_lossy()`. Git always outputs forward slashes on every OS; using the same convention prevents frontend path-comparison mismatches on Windows. Paths passed as arguments to git/system commands should stay as native OS paths via `to_string_lossy()` directly.
386424
- **Least privilege and clear errors**: Fail closed on invalid input and return clear, user-safe error messages.
387425

@@ -419,6 +457,29 @@ cargo test --lib # Run unit tests
419457
-`cargo test --lib` passes (all tests)
420458
- ✅ Code is formatted (`cargo fmt` and `pnpm run format`)
421459

460+
## Prompt Improvement Loop (Required)
461+
462+
After resolving a CI failure or production bug, update these instructions in the same PR when a reusable lesson exists.
463+
464+
- Add one durable guardrail: an invariant, checklist item, or triage step.
465+
- Prefer operational rules over narrative explanation.
466+
- If no user-facing behavior changed, still capture the prevention rule.
467+
468+
## Documentation Drift Prevention (Required)
469+
470+
When editing or relying on documentation in this repository:
471+
472+
- Treat `README.md`, `.github/copilot-instructions.md`, `src-tauri/src/lib.rs`, `src/lib/sproutgit.ts`, and `package.json` as a consistency set.
473+
- If a command, script, route, or API wrapper changes in code, update the corresponding docs in the same change.
474+
- Prefer representative command/API summaries plus explicit source-of-truth links over exhaustive static lists that quickly drift.
475+
- Remove placeholders and stale claims (for example, outdated URLs or deleted files) immediately when discovered.
476+
477+
Quick verification command before commit:
478+
479+
```bash
480+
rg -n "project\.json|YOUR_USERNAME|once CI is set up" README.md .github/copilot-instructions.md
481+
```
482+
422483
## Composability & Platform Extensibility
423484

424485
### Tier 1: GitTransaction Builder (Implemented ✓)
@@ -693,3 +754,4 @@ The `Spinner.svelte` component supports:
693754
- SQLite + SeaORM (`sqlx-sqlite`) integer decode: avoid `u64` in `FromQueryResult` structs; use `i64` for `INTEGER` columns (timestamps included)
694755
- Svelte 5 `class:` directive with Tailwind arbitrary values (e.g., `class:bg-[var(--x)]/10={cond}`) works but looks odd
695756
- Window overflow: parent containers must be `flex flex-col overflow-hidden` for child `flex-1 overflow-auto` to scroll properly
757+
- **Windows `\\?\` paths in PowerShell**: If a path produced by `canonicalize()` is set as a subprocess env var and consumed by PowerShell, `Join-Path` will fail with "Cannot process argument because the value of argument 'drive' is null". The env var is non-null so a `if (-not $var)` guard won't catch it — the crash happens on the next line that uses the path. Always apply `strip_win_prefix` before passing any path into a hook or child process environment.

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,22 @@ env:
2121
BASE_VERSION: "0.1"
2222

2323
jobs:
24+
docs-lint:
25+
name: "00 · Docs · Drift Lint"
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v6
29+
30+
- uses: actions/setup-node@v6
31+
with:
32+
node-version: 20
33+
34+
- name: Run documentation drift checks
35+
run: node scripts/check-doc-drift.mjs
36+
2437
check-frontend:
2538
name: "01 · Frontend · Check + Build"
39+
needs: docs-lint
2640
runs-on: ubuntu-latest
2741
steps:
2842
- uses: actions/checkout@v6
@@ -44,6 +58,7 @@ jobs:
4458

4559
check-rust:
4660
name: "02 · Rust · Validate (${{ matrix.os }})"
61+
needs: docs-lint
4762
runs-on: ${{ matrix.os }}
4863
permissions:
4964
contents: read

.husky/pre-commit

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55

66
set -e
77

8-
echo "--- [pre-commit] 1/6 Rust target cleanup"
8+
echo "--- [pre-commit] 1/7 Rust target cleanup"
99
pnpm run cleanup:rust-targets:delete
1010

11-
echo "--- [pre-commit] 2/6 Rust unit tests"
11+
echo "--- [pre-commit] 2/7 Rust unit tests"
1212
(cd src-tauri && cargo test --lib)
1313

14-
echo "--- [pre-commit] 3/6 pnpm tests"
14+
echo "--- [pre-commit] 3/7 pnpm tests"
1515
pnpm run test
1616

17-
echo "--- [pre-commit] 4/6 oxlint"
17+
echo "--- [pre-commit] 4/7 docs drift lint"
18+
pnpm run lint:docs
19+
20+
echo "--- [pre-commit] 5/7 oxlint"
1821
pnpm run lint
1922

20-
echo "--- [pre-commit] 5/6 TypeScript/Svelte check"
23+
echo "--- [pre-commit] 6/7 TypeScript/Svelte check"
2124
pnpm run check
2225

23-
echo "--- [pre-commit] 6/6 Playwright E2E tests"
26+
echo "--- [pre-commit] 7/7 Playwright E2E tests"
2427
pnpm run test:e2e:full

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<p align="center">
1414
<a href="#features">Features</a> •
1515
<a href="#workflow-policy">Workflow Policy</a> •
16+
<a href="#documentation">Documentation</a> •
1617
<a href="#screenshots">Screenshots</a> •
1718
<a href="#installation">Installation</a> •
1819
<a href="#development">Development</a> •
@@ -82,6 +83,14 @@ No conflicts, no stash juggling, no waiting. Each agent works independently on i
8283
- Branch/worktree binding and lifecycle rules: [docs/branch-worktree-policy.md](docs/branch-worktree-policy.md)
8384
- Workspace hook model and trigger behavior: [docs/worktree-hooks.md](docs/worktree-hooks.md)
8485

86+
## Documentation
87+
88+
- Start at [docs/index.md](docs/index.md) for the maintained documentation entry point
89+
- Product scope and priorities: [docs/requirements.md](docs/requirements.md)
90+
- Architecture and backend command patterns: [docs/architecture.md](docs/architecture.md)
91+
- Security posture and hardening decisions: [docs/security-audit.md](docs/security-audit.md)
92+
- E2E process and adapter behavior: [docs/e2e-test-process.md](docs/e2e-test-process.md), [docs/tauri-playwright-adapter-cheatsheet.md](docs/tauri-playwright-adapter-cheatsheet.md)
93+
8594
## Workspace Hooks
8695

8796
SproutGit supports workspace-scoped lifecycle hooks stored in `.sproutgit/state.db`.
@@ -157,7 +166,7 @@ SproutGit builds this orchestration layer on top of native Git primitives so wor
157166

158167
### Download
159168

160-
Pre-built binaries will be available on the [Releases](../../releases) page once CI is set up.
169+
Pre-built binaries are published on the [Releases](../../releases) page when a release is cut.
161170

162171
### Build from source
163172

@@ -172,7 +181,7 @@ Pre-built binaries will be available on the [Releases](../../releases) page once
172181
#### Steps
173182

174183
```bash
175-
git clone https://github.com/YOUR_USERNAME/sproutgit.git
184+
git clone https://github.com/InterestingSoftware/SproutGit.git
176185
cd sproutgit
177186
pnpm install
178187
pnpm tauri build
@@ -244,9 +253,12 @@ These commands only touch worktree-local `src-tauri/target` directories discover
244253
## Testing & Coverage
245254

246255
```bash
247-
# Run all backend unit tests
256+
# Run backend Rust tests (all targets)
248257
pnpm run test:security
249258

259+
# Run frontend unit tests
260+
pnpm run test:unit
261+
250262
# Run tests with verbose output
251263
cd src-tauri && cargo test --all-targets -- --nocapture
252264

@@ -314,15 +326,17 @@ sproutgit/
314326
│ │ └── components/ # Reusable UI components
315327
│ └── routes/
316328
│ ├── +page.svelte # Project picker (clone, open, recent)
329+
│ ├── settings/ # Settings screen
317330
│ └── workspace/
318331
│ └── +page.svelte # Main workspace (worktrees + graph + diff)
319332
├── src-tauri/
320333
│ ├── src/lib.rs # Rust backend: Tauri commands, Git ops, DB
321334
│ ├── tauri.conf.json # App configuration
322335
│ └── Cargo.toml # Rust dependencies
336+
├── e2e/ # Playwright E2E tests and fixtures
323337
├── docs/ # Design docs and requirements
324338
├── logos/ # App icons (Apple Liquid Glass)
325-
└── tests/ # Tauri driver smoke tests
339+
└── tests/ # Frontend/unit test files
326340
```
327341

328342
## Workspace Layout
@@ -336,10 +350,11 @@ SproutGit manages repos in a prescribed directory structure:
336350
│ ├── feature-foo/
337351
│ └── bugfix-bar/
338352
└── .sproutgit/
339-
├── project.json
340353
└── state.db # Local state (SQLite)
341354
```
342355

356+
A SproutGit workspace is identified by `.sproutgit/state.db`.
357+
343358
## Tech Stack
344359

345360
| Layer | Technology |

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
1111
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
1212
"lint": "oxlint",
13+
"lint:docs": "node scripts/check-doc-drift.mjs",
1314
"lint:fix": "oxlint --fix",
1415
"format": "prettier --write .",
1516
"format:check": "prettier --check .",

0 commit comments

Comments
 (0)