The paper trail lands — competitive analysis 08, and the git-excision plan ruled: execute as written
research/08-competitive-analysis.md profiles the direct set (features, pricing, traction, matrices, ranked threats); strategy/01-git-excision.md carries the 2026-08-08 ruling to excise app-managed git, with keepers, harvest, delete list, and sequencing. Both indexed. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Git Excision — Plan
|
||||
|
||||
This document is the **ruled plan** for removing app-managed git from Lanework entirely. It is the second step beyond the 2026-08-07 pivot recorded in DESIGN/12-editions.md (git left the paywall and went tier-independent); this step retires the git stack itself. The safety-net successor — the foreign-change journal that would reclaim tracking of agent-borne mutations — is **explicitly deferred**, a later decision and not part of this milestone; the harvest step below stays in scope precisely because it keeps that option cheap.
|
||||
|
||||
Researched: 2026-08-07 · Ruled: 2026-08-08 — excise; execute as written
|
||||
|
||||
## Why (the case, compressed)
|
||||
|
||||
The strategic reasoning lives in the 2026-08-07 tier-strategy discussion; the plan only summarizes it.
|
||||
|
||||
- The dividing line for tiers became "your board beyond your Mac" (iPhone + ops-based sync service as Pro); git carries no revenue in that structure.
|
||||
- Sync no longer needs git rails: the phone was ruled a worktree client speaking semantic ops, and once an ops protocol exists the Mac and server can speak it too.
|
||||
- The most natural developer usage — a board inside a project repo — was always excluded from app-managed git (nearest-`.git`-wins leaves it alone); those users get history from their own workflow, forever, because the *format* is git-friendly by construction.
|
||||
- The safety-net value (history of externally delivered changes, e.g. agents) survives via a designated successor: a foreign-change journal built on the write-provenance ledger plus close-time text snapshots — protection by default on every board, no opt-in, no git states.
|
||||
- What ends is the permanent design tax: every future feature answering "what does this do mid-merge, index-locked, mid-branch-switch?"
|
||||
- Market evidence (research/08): nobody anywhere successfully charges for git features; the closest competitor thrives with zero git integration; the wedge is files, not git.
|
||||
|
||||
## Reversibility
|
||||
|
||||
- Tag the pre-excision commit `pre-git-excision`. The entire stack — ~8,150 lines in `Kanban/Git/`, ~8,300 lines of tests — remains in history.
|
||||
- Dependency pins recorded here for resurrection: SwiftGitX `0.4.0`, libgit2 `1.9.2` (both `github.com/ibrahimcetin/…`, declared in `project.yml`).
|
||||
- The seams the git stack plugged into (`HistoryProviding`, `commitSeam`, `IdentityHistoryRanker`) are all nil-safe/default-native and are **kept**, so a future provider — journal, ops service, or even git again — re-binds without re-plumbing.
|
||||
|
||||
## What is kept (do not over-cut)
|
||||
|
||||
- **`.gitignore` seeding and parsing** — `BoardWriter.gitignoreSeed` / `seedGitignoreIfAbsent`, `Storage/GitignoreRules.swift`. Since 2026-07-31 this is the noise-gate definition for the loose-file heal on *every* board, git or not; the format's git-friendliness promise also keeps it. Tests: `GitignoreRulesTests`, the noise-gate suite in `LooseFileRelocationTests` — untouched.
|
||||
- **FSEvents watcher `.git` filtering** — `FolderWatcher`'s `.git` exclusion. Boards living inside user repos remain first-class; the watcher must keep ignoring their churn. Tests in `FolderWatcherTests` — untouched.
|
||||
- **The write-provenance ledger** — `EchoLedger` runs identically with the announcer as its only consumer (02-architecture.md already states this posture for no-git boards); it is the foundation of the successor journal. The macOS-gated harvest surface (`HarvestedReceipt`) stays; only its git consumer (`CommitAttribution.swift`) goes.
|
||||
- **Native undo, whole** — `Kanban/History/` becomes the only substrate: `HistoryProviding`, `NativeHistoryProvider`, `BoardUndoManager`, `CardWindowUndo`, staleness re-checks, phrase vocabulary. `HistoryProviderTests` (968 lines) untouched. The protocol seam is retained deliberately (see Reversibility).
|
||||
- **Duplicate-id repair** — the ladder's `historyRank` rung is nil-safe by design; with the ranker gone, the birth-date rung is the top rung, exactly today's no-git behavior. Only the two git-rung tests in `DuplicateIdentityTests` go.
|
||||
- **Tier/StoreKit machinery** — already dormant and structurally separate since the pivot (`Tier`, `ProEntitlement`, `ProSettingsSection` unrendered, `TierTests` git-free). Zero changes; it waits for the phone/sync split. The one git-referencing marketing string in `ProSettingsSection` gets rewritten whenever Pro re-surfaces, not now.
|
||||
- **The Sync tab placeholder** — `BoardSyncTabView` keeps its claimed position in the popover; its annotation changes from "the remote half of the git story" to the future ops-based sync service.
|
||||
- **The network-client entitlement** — stays dormant; the future sync service needs it regardless.
|
||||
- **`UndoProbe`** — provider-agnostic diagnostic, keeps working over the native provider alone.
|
||||
- **The agent guide mechanism** — `AgentGuide.swift` stays; only its `## Git` prose changes (step 6).
|
||||
|
||||
## What is harvested before deletion
|
||||
|
||||
**The semantic message composer becomes the change narrator.** `CommitMessageEngine.swift` (1,419 lines, pure Foundation, a `BoardModel` × `BoardModel` × changed-paths → prose function) and its spec (`CommitMessageTests.swift`, 998 lines) are the designated core of the future activity feed / foreign-change journal. Harvest in place, before any deletion:
|
||||
|
||||
- Relocate engine + `CommitMessageComposer.swift`'s request/protocol types out of `Kanban/Git/` into a neutral home (working name: `Kanban/Changes/`), renamed away from commit vocabulary (e.g. `ChangeNarrator`).
|
||||
- Sever the two git ties the inventory found: move `GitChangedPath` (a plain path+kind value type currently defined inside `GitCommitOperation.swift`) into the neutral module; replace `CommitAuthorship.foreign(GitIdentity)` with a plain string identity.
|
||||
- Transplant the spec tests alongside; they keep the engine alive and correct until the journal work begins.
|
||||
- The "previous snapshot" input becomes the caller's problem by contract (the journal will supply it from memory/snapshot; git supplied it from HEAD).
|
||||
|
||||
## What is deleted
|
||||
|
||||
- **`Kanban/Git/` wholesale** (minus the two harvested files): mode detection, repository wrapper, auto-committer, commit/branch/restore operations, HEAD snapshot, history walk/provider, history store, housekeeping, identity, attribution, operation stamp, commit seam type. ~6,600 lines after harvest.
|
||||
- **UI surfaces** (~1,160 lines): `BoardGitControls`, `BoardGitSetup`, `BoardGitTabView`, `CardHistorySection`; the `.git` case of `BoardInfoTab` and its switch arm; the card window's `historySlot` wiring.
|
||||
- **Menu placeholders**: `RemoteCommands` (Board ▸ Pull/Push, `m7-git`-tagged) removed; the `View ▸ History` future row re-tagged to the journal successor rather than removed (the reserved slot survives the substrate).
|
||||
- **`AppModel` wiring** (the largest single edit, ~130 mentions): `makeHistoryProvider` collapses to always-native; `wireGitUndo`, `wireBranchSwitching`, `bindHistoryProvider`'s add-git path, and the card-session staging threading (`setCardSession` / `releaseCardSessionStaging` / `resumeCardSessionStaging`) all go; `HistoryStore` composition leaves `beginSession`.
|
||||
- **`BoardStore`**: the `commitSeam` property and its three call sites (`willWrite`, `writeBracketDidClose`, `reloadDidLand`); the `makeIdentityHistoryRanker` property.
|
||||
- **`BoardRegistry`**: the persisted `gitOperationStamp` field and accessors. Safe removal: decoders ignore unknown keys, so existing registries with the field simply drop it on next write.
|
||||
- **`BannerCenter`**: `GitFailureBanner`, the `GitOperation` enum, `.gitFailure` / `.repositoryUnreadable` cases and their rows.
|
||||
- **`SessionSettleGate`**: the `GitRestoreOperation.plan` integration goes with the git restore path; the path utility it borrows from `GitHistoryWalk` moves to a neutral home; the gate itself (settle editors before wholesale operations) stays — it serves any future wholesale operation.
|
||||
- **Dependencies**: SwiftGitX and libgit2 leave `project.yml` (with their rationale comment blocks, including the now-stale "never runs in the free app" note); `xcodegen generate`; `Package.resolved` regenerates.
|
||||
- **Tests**: eleven wholesale suites (~8,285 lines minus the transplanted `CommitMessageTests`): `AutoCommitTests`, `GitUndoTests`, `BranchSwitchTests`, `HistoryStoreTests`, `RepositoryHygieneTests`, `GitIdentityTests`, `CardSessionCommitTests`, `BoardGitModeTests`, `BoardGitSetupTests`, `BoardInfoPopoverTests` (git-only despite the generic name). Mixed-file edits: `AppModelTests` (helper + 3 branch tests), `CardSessionUndoTests` (2 lines), `DuplicateIdentityTests` (2 git-rung tests), `BoardDecisionSurfaceTests` (the Pro-repair-commit suite), `BannerCenterTests` (git banner rows), `AccessibilityAuditTests` (git-tab audit mentions).
|
||||
- **Naming footguns, called out so the sweep doesn't misfire**: `BoardInfoPopoverTests.swift` is entirely git (delete); `InertGitTests.swift` is a Storage-layer keeper despite the name (keep, rename to say what it pins — the writer/loader's byte-level git-agnosticism); `RepositoryHygieneTests.swift` is fully git despite two ".gitignore"-titled suites (delete — the kept gitignore behavior is covered by `GitignoreRulesTests` and `LooseFileRelocationTests`).
|
||||
|
||||
## Companion changes (same milestone, not deletions)
|
||||
|
||||
- **Agent guide rewrite + version bump.** The `## Git` section currently documents auto-commit ("If you don't commit, Lanework Pro auto-commits your changes"). Rewrite to the new posture: boards may live in git repos; agents working a repo-resident board should commit their own changes with clear messages, stage only their own paths, never touch app state. The guide is version-gated, so the marker bumps and existing boards heal to the new text on next open.
|
||||
- **README key features.** Remove the four git bullets (git integration, auto-commit, undo-as-forward-commits/branches, card History) and the Pro-unlocks-git framing in the tiers bullet; add one line stating the format's git-friendliness promise (per-card files, stable UUIDs, byte-fidelity, seeded `.gitignore` — version boards with any tools you like).
|
||||
- **DESIGN re-rulings** (each as a dated note in the doc it amends, per the corpus convention):
|
||||
- `12-editions.md` — second pivot note: git excised entirely; tier machinery stays dormant pending the phone/sync split.
|
||||
- `06-history-undo.md` — retired, with one migration first: its tier-independent **Undo routing** section (both substrates dispatch through it) moves into `13-native-undo.md` before the retirement note lands, so the routing rules keep a live home.
|
||||
- `13-native-undo.md` — amended: the only substrate; add-git swap language removed.
|
||||
- `07-sync-collab.md` — retired as written; a successor sync doc arrives with the ops-service workstream.
|
||||
- `14-git-operations.md` — retired (superseded record).
|
||||
- `DESIGN/README.md` — index updated: retired docs marked, the stale "— Pro tier" labels on 06/07/14 fixed.
|
||||
- Source-file doc comments citing 06/07/14 by number get repointed during the sweep (mostly they go with the deleted files; the survivors repoint to 13).
|
||||
- **`strategy/00-charter.md`** — the tier anchors re-ruled to the 2026-08-07 structure (free = complete Mac experience; Pro = iPhone + ops-based sync) once the user rules; the charter edit is the ruling's paper trail, not part of the mechanical excision.
|
||||
|
||||
## Successors (deferred — explicitly outside this ruling)
|
||||
|
||||
- **Foreign-change journal** — deferred by the 2026-08-08 ruling ("we'll worry about reclaiming the value of tracking agent-borne mutation another time"); recorded here so the shape isn't lost: pre-images of foreign writes journaled at reconciliation (the store holds prior content in memory when the ledger flags a foreign batch), a close-time text snapshot covering the app-closed blind window, `modified-by` attribution, bounded retention, a plain "previous versions / what changed" surface. Text only; attachment binaries deliberately out of scope locally. Interim safety net until then: the trash for deletes, user-run `git init` on boards that warrant it, and the agent guide's commit etiquette on repo-resident boards.
|
||||
- **Ops-based sync service** — per the tier-strategy discussion: semantic ops, server-side worktree API, phone as worktree client; the harvested change narrator supplies the activity feed. Own workstream, own docs.
|
||||
|
||||
## Sequencing
|
||||
|
||||
Each step leaves the build green and the unit suite passing; steps are separately committable.
|
||||
|
||||
1. **Preconditions.** Settle or land the in-flight working-tree changes (the pivot's implementation is mid-flight in `BoardStoreHistory`/`HistoryStaleness`/`BoardStore`); tag `pre-git-excision`.
|
||||
2. **Harvest.** Relocate the message engine + composer types and `GitChangedPath` to `Kanban/Changes/`; sever the `GitIdentity` tie; transplant the spec tests; rename to narrator vocabulary. Green.
|
||||
3. **UI peel.** Remove the Git tab, `CardHistorySection`, git banners, `RemoteCommands`; re-annotate the Sync placeholder. Green — the git engine still compiles underneath, unreferenced by UI.
|
||||
4. **Seam unbind.** `AppModel` wiring out (provider always native), `BoardStore` seam + ranker out, `SessionSettleGate` cleaned, `BoardRegistry` stamp field out. Green — `Kanban/Git/` now compiles dead.
|
||||
5. **Delete.** `Kanban/Git/` (minus harvested), the eleven test suites, the mixed-file edits. Green.
|
||||
6. **Dependency drop.** SwiftGitX/libgit2 out of `project.yml`; regenerate; `Package.resolved` refreshes. Green — this is the step that proves nothing living referenced libgit2.
|
||||
7. **Companions.** Agent-guide rewrite + version bump; README features; keeper renames (`InertGitTests`); DESIGN re-rulings and index; grep sweep for orphaned references (`git` case-insensitive over `Kanban/` and `KanbanTests/`, adjudicated against the keeper list above — filename-based sweeps misfire on the three footguns).
|
||||
8. **Verify.** Full unit suite; UI suites compile (`build-for-testing`) and the accessibility audit passes without the Git tab; golden fixture suite untouched (no git fixtures exist — repos were built at runtime); a fixture board inside a real user repo still opens, watches, and heals correctly (the keeper behaviors).
|
||||
|
||||
## Size and risk
|
||||
|
||||
- Net deletion: roughly 9,000 lines of source and 8,000 of tests; edits in ~15 shared files. With the seams as clean as the inventory found, this is a one-to-two-session milestone.
|
||||
- Biggest single risk: `AppModel`'s ~130-mention entanglement — step 4 is the one to do slowly, against a green step-3 baseline.
|
||||
- Second risk: over-cutting a keeper — the keeper list and the three naming footguns above exist to prevent exactly that; the sweep adjudicates against this document.
|
||||
- Registry compatibility: dropping `gitOperationStamp` is decode-safe (unknown keys ignored); no migration needed.
|
||||
- The agent-guide bump is mandatory, not optional — shipping the old text would document machinery that no longer exists, to agents.
|
||||
|
||||
## What this plan does not decide
|
||||
|
||||
The journal's design and timing, the ops-service architecture, the Pro split's final contents, and Pro pricing are all downstream workstreams with their own documents. This plan only removes the git stack, preserves its seams and keepers, harvests its one reusable engine, and keeps the paper trail honest.
|
||||
|
||||
## Handoff
|
||||
|
||||
Excise app-managed git per `strategy/01-git-excision.md` (ruled 2026-08-08): tag `pre-git-excision`, then run Sequencing steps 1–8 in order, each committed green — the keeper list and the three test-file naming footguns are binding, the message engine is harvested to `Kanban/Changes/` before anything is deleted, and the agent guide's Git section is rewritten with a version bump; the foreign-change journal (reclaiming tracking of agent-borne mutations) is deferred and out of scope.
|
||||
Reference in New Issue
Block a user