The paper agrees with the code — guide v11, README, DESIGN re-rulings, and the adjudicated sweep
Step 7 of strategy/01-git-excision.md, the companions. The agent guide bumps to v11: the Git section teaches repo-resident etiquette alone (stage only your own paths, commit your own changes, leave app-maintained files to the app) — existing boards heal to the new text on next open. README re-anchors: the four git feature bullets out, tiers say the complete Mac experience is free, and one bullet states the format's git-friendliness promise. The changelog drops the never-shipped git entries. DESIGN re-rules: 06 retired with Undo routing migrated to 13 (now the sole substrate's doc, seam kept open), 07 retired as written pending the ops-service workstream, 14 retired as superseded record, 12 carries the second pivot note, the index reflects all of it; the charter gets a pointer note (the anchors' full re-ruling stays with the user). InertGitTests renames to GitAgnosticStorageTests — the excision restores its original claim app-wide. And the sweep: ~70 comment sites across 36 files adjudicated against the keeper list, every present-tense description of the excised machinery made past tense or repointed, keepers untouched. 2,707 tests green. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -84,7 +84,15 @@ enum AgentGuide {
|
||||
/// the zero-read minimum (`mkdir` plus one `index.md`, no `order`, no `schema`; it lands at the
|
||||
/// lane's bottom and the app stamps a real rank on its first touch) while still teaching
|
||||
/// *writing* `order` as the way to control position, which is the only way to control it.
|
||||
static let version = 10
|
||||
/// **v11 rewrites Git for the excision** (strategy/01-git-excision.md, ruled 2026-08-08):
|
||||
/// app-managed git is gone — the app never runs git, and the auto-commit the old section
|
||||
/// promised no longer exists, so shipping v10's text would document machinery to agents that
|
||||
/// isn't there. The section now teaches repo-resident etiquette alone — the format stays
|
||||
/// deliberately git-friendly, a board may live in a repository of the user's own, and there an
|
||||
/// agent stages only its own paths, commits its own changes with clear messages, and leaves the
|
||||
/// app-maintained files to the app. The stamping section drops its auto-commit clause the same
|
||||
/// way.
|
||||
static let version = 11
|
||||
|
||||
// MARK: - The version marker
|
||||
|
||||
@@ -235,9 +243,8 @@ enum AgentGuide {
|
||||
/// when the decision called for it.
|
||||
///
|
||||
/// **Called inside `BoardStore.performWrite`**, so both halves ride one watcher bracket: the
|
||||
/// rescue and the guide land as a single app-mediated reload, and (under Pro) as a
|
||||
/// single honestly-attributed commit rather than a foreign-looking rename followed by an
|
||||
/// app write (06-history-undo.md ▸ Commit messages, "Update agent guide (vN)").
|
||||
/// rescue and the guide land as a single app-mediated reload rather than as two foreign-looking
|
||||
/// events.
|
||||
///
|
||||
/// The move is `FileManager.moveItem` and nothing else: it preserves the bytes exactly — the
|
||||
/// displaced file may not even be UTF-8 — and it **fails rather than overwrite** if
|
||||
@@ -298,8 +305,8 @@ enum AgentGuide {
|
||||
}
|
||||
|
||||
try BoardWriter.atomicReplace(text: content, at: guideURL, operation: .agentGuide)
|
||||
// Heal-marked: the guide's refresh is app-initiated work, and its commit is its own
|
||||
// ("Update agent guide (vN)" already commits alone — 06-history-undo.md ▸ Commit messages).
|
||||
// Heal-marked: the guide's refresh is app-initiated work, and the ledger records it as the
|
||||
// heal it is rather than as anyone's edit.
|
||||
EchoLedger.current?.markHeal(at: guideURL)
|
||||
return displaced
|
||||
}
|
||||
@@ -420,11 +427,11 @@ enum AgentGuide {
|
||||
|
||||
Add `modified-by: <your-name>` (e.g. `modified-by: claude`) to the
|
||||
frontmatter of every `index.md` you write — it attributes the change in the
|
||||
app and, on git boards, in the auto-commit. The app clears the stamp on its
|
||||
own writes, so **re-stamp on every write, and after every move**: a bare
|
||||
folder move rewrites no file, so the moved card arrives unstamped unless you
|
||||
touch its `index.md` again. When you need exact authorship, commit your
|
||||
changes yourself instead (see Git below).
|
||||
app. The app clears the stamp on its own writes, so **re-stamp on every
|
||||
write, and after every move**: a bare folder move rewrites no file, so the
|
||||
moved card arrives unstamped unless you touch its `index.md` again. On a
|
||||
board that lives in a git repository of the user's own, committing your
|
||||
changes yourself (see Git below) records exact authorship as well.
|
||||
|
||||
## Creating a card
|
||||
|
||||
@@ -567,14 +574,20 @@ enum AgentGuide {
|
||||
|
||||
## Git
|
||||
|
||||
Some boards are git repositories — because the board lives inside a repo of
|
||||
yours, or because Lanework Pro manages its history. Two rules when one is:
|
||||
Lanework itself never runs git: the app manages no repository, makes no
|
||||
commits, and never reads `.git`. But the format is deliberately
|
||||
git-friendly — one file per card, stable UUID folder names, byte-faithful
|
||||
rewrites — and a board may live inside a repository of the user's own.
|
||||
When it does:
|
||||
|
||||
- **Stage only your own paths** — never `git add -A` or `git add .`: a
|
||||
sweep would commit the user's not-yet-committed changes under your name.
|
||||
- Committing your changes yourself is fine and gives you exact authorship;
|
||||
the app follows along. If you don't commit, Lanework Pro auto-commits
|
||||
your changes as external edits (attributed via `modified-by` when you
|
||||
stamped it).
|
||||
- **Commit your own changes, with clear messages** — nothing else will
|
||||
commit them for you, and a semantic message ("Move card 'Fix login' to
|
||||
Doing") is the history the user will actually read.
|
||||
- **Leave the app-maintained files to the app** — this guide and the
|
||||
seeded `.gitignore` are rewritten by Lanework when they need to be;
|
||||
don't edit or delete them, and don't commit changes to the user's other
|
||||
files that you didn't make.
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -1060,19 +1060,22 @@ public enum BoardLoader: Sendable {
|
||||
|
||||
// MARK: - The earlier-occurrence-wins history seam
|
||||
|
||||
/// **Where git path history plugs into the duplicate-id winner rule** (01-storage-format.md
|
||||
/// **Where git path history once plugged into the duplicate-id winner rule** (01-storage-format.md
|
||||
/// § Fractal layout ▸ Rules: "on git boards, the path history already tracks outranks the
|
||||
/// newcomer (both tracked: the path that entered history first)").
|
||||
/// newcomer (both tracked: the path that entered history first)") — kept as a seam, unfilled,
|
||||
/// since app-managed git was excised entirely (`strategy/01-git-excision.md`, 2026-08-08).
|
||||
///
|
||||
/// A seam rather than an implementation because the first rung of that ladder is unreachable
|
||||
/// without git: the free tier runs no git machinery at all (12-editions.md ▸ The inert posture),
|
||||
/// so the loader consults an injected ranker and falls through to birth date and traversal order
|
||||
/// when there is none — which is every free-tier board, and every Pro board without a repo.
|
||||
/// A seam rather than an implementation because the first rung of that ladder was unreachable
|
||||
/// without git even before the excision: the free tier ran no git machinery at all
|
||||
/// (12-editions.md ▸ The inert posture), so the loader consults an injected ranker and falls
|
||||
/// through to birth date and traversal order when there is none — which is every board today, no
|
||||
/// exceptions.
|
||||
///
|
||||
/// Deliberately one closure and no protocol: the loader asks one question — "how early did this
|
||||
/// path enter history" — and pro-m1's implementation answers it from `git log --diff-filter=A
|
||||
/// --follow`-shaped plumbing behind the provider seam. `nil` means "untracked, or no history
|
||||
/// here", which the rule reads as *outranked by anything tracked*.
|
||||
/// path enter history" — and pro-m1's implementation once answered it from `git log
|
||||
/// --diff-filter=A --follow`-shaped plumbing behind the provider seam, before that plumbing left
|
||||
/// with the excision. `nil` means "untracked, or no history here", which the rule reads as
|
||||
/// *outranked by anything tracked*; nothing installs a ranker today, so every path reads `nil`.
|
||||
///
|
||||
/// - Parameter rank: keyed by the occurrence's **board-root-relative path**, which is what a
|
||||
/// repo's path history knows; lower is earlier.
|
||||
|
||||
@@ -585,9 +585,9 @@ public enum BoardWriter: Sendable {
|
||||
///
|
||||
/// - **`.DS_Store`** — the Finder writes one into every folder a user looks at, and on a board
|
||||
/// that means one per lane and one per card, each churning as icons and window positions move.
|
||||
/// On a git board it would be committed by the whole-tree stage, forever, under the user's own
|
||||
/// name; on every board it is now also the plainest thing the loose-file heal must not treat as
|
||||
/// a card's attachment.
|
||||
/// Once committed by the whole-tree stage on a git board, forever, under the user's own name;
|
||||
/// app-managed git is gone now (`strategy/01-git-excision.md`), and on every board it is the
|
||||
/// plainest thing the loose-file heal must not treat as a card's attachment.
|
||||
/// - **`.*.lanework-*`** — this file's own temp pattern, and the one entry the app is entitled to
|
||||
/// have an opinion about: `atomicReplace` writes `.<name>.lanework-<uuid>` beside its target,
|
||||
/// so a crashed write's residue is the app's litter rather than the user's file. The two must
|
||||
@@ -1890,7 +1890,8 @@ public enum BoardWriter: Sendable {
|
||||
/// `updateIndex`'s closure cannot. Everything else is identical and deliberately so — read
|
||||
/// fresh from disk, refuse an uneditable frontmatter shape, edit, stamp `modified` and clear
|
||||
/// `modified-by`, replace atomically. A toggle is "an ordinary user edit — the standard atomic
|
||||
/// write, auto-committed and undoable on git boards" (05), not a special case of anything.
|
||||
/// write" (05) — once "auto-committed and undoable on git boards" too, before app-managed git was
|
||||
/// excised (`strategy/01-git-excision.md`, 2026-08-08) — not a special case of anything.
|
||||
///
|
||||
/// ### The offset, and why it is re-checked
|
||||
///
|
||||
@@ -2085,7 +2086,7 @@ public enum BoardWriter: Sendable {
|
||||
///
|
||||
/// Text identical to what is already on disk writes nothing and returns `false` — `writeBody`'s
|
||||
/// untouched gate, applied to the whole file instead of the body span. Apply on a buffer the user
|
||||
/// only read must not churn `mtime`, wake every watcher, and (on git boards) mint an empty commit.
|
||||
/// only read must not churn `mtime` or wake every watcher.
|
||||
///
|
||||
/// - Returns: `true` when bytes were written, `false` when the file already read exactly like
|
||||
/// `text`.
|
||||
@@ -2591,9 +2592,11 @@ public enum BoardWriter: Sendable {
|
||||
/// reminting it would change an identity for no reason at all — the vanished-duplicate race,
|
||||
/// read from the surviving side.
|
||||
///
|
||||
/// **Heal-marked**, because the app started it on its own: the receipt is what splits the remint
|
||||
/// **Heal-marked**, because the app started it on its own: once the receipt that split the remint
|
||||
/// into its own commit on git boards, named for the Repair verb (06-history-undo.md ▸ Commit
|
||||
/// messages). Undo never sees it — heals are not gestures (13-native-undo.md).
|
||||
/// messages, retired with app-managed git, `strategy/01-git-excision.md`) — the receipt still
|
||||
/// exists for whatever consumes it next. Undo never sees it — heals are not gestures
|
||||
/// (13-native-undo.md).
|
||||
///
|
||||
/// - Parameter duplicate: the withheld occurrence, `path` relative to `boardRoot` so the write
|
||||
/// lands wherever the board lives *now*.
|
||||
@@ -3020,7 +3023,8 @@ public enum WriteOperation: Sendable, Equatable, CustomStringConvertible {
|
||||
|
||||
/// The board-root `.gitignore` being seeded — at creation, at instantiation, and by the
|
||||
/// scheduled heal that gives a board born before the ruling the file it now needs
|
||||
/// (06-history-undo.md ▸ Repository hygiene, re-ruled 2026-07-31; `seedGitignoreIfAbsent`).
|
||||
/// (06-history-undo.md ▸ Repository hygiene, retired with app-managed git,
|
||||
/// `strategy/01-git-excision.md`; `seedGitignoreIfAbsent`).
|
||||
///
|
||||
/// **No payload**, for `.agentGuide`'s reason: there is one such file per board, its name is
|
||||
/// fixed, and it is not an item with a title. Its own case beside `.agentGuide` rather than a
|
||||
|
||||
@@ -4,15 +4,19 @@ import Foundation
|
||||
/// one, and asked one question: is this board-relative path ignored?
|
||||
///
|
||||
/// The file outgrew git on 2026-07-31 (01-storage-format.md § Fractal layout ▸ Rules, "`.gitignore`
|
||||
/// is the noise gate"; 06-history-undo.md ▸ Repository hygiene): it is now the one definition of
|
||||
/// is the noise gate"; 06-history-undo.md ▸ Repository hygiene, retired with app-managed git,
|
||||
/// `strategy/01-git-excision.md`): it is now the one definition of
|
||||
/// noise the **loose-file relocation heal** obeys, on every board, git or not. A file matching it
|
||||
/// keeps the ordinary stray posture — "skipped, preserved verbatim, logged, never relocated, never
|
||||
/// announced" — and "the exclusion list is exactly that file, nothing hardcoded". On a Pro board the
|
||||
/// same file governs the committer through libgit2, so ignored noise neither relocates nor commits:
|
||||
/// one definition, two consumers. This type is the app's half of that, because the load path cannot
|
||||
/// reach for libgit2 — the free tier opens a board without so much as a `fileExists` under `.git`
|
||||
/// (12-editions.md ▸ The free tier and `.git`), and the heal runs on boards that have no repository
|
||||
/// at all.
|
||||
/// announced" — and "the exclusion list is exactly that file, nothing hardcoded". (On a Pro board the
|
||||
/// same file once governed the committer through libgit2 too, so ignored noise neither relocated nor
|
||||
/// committed — one definition, two consumers; app-managed git is gone now, so the heal is the only
|
||||
/// consumer left.) This type is the app's half of that — once split from libgit2 because the load
|
||||
/// path couldn't reach for it while the committer did; libgit2 left the project entirely with the
|
||||
/// git excision (`strategy/01-git-excision.md`, step 6), so today there is no other implementation to
|
||||
/// duplicate at all. The free tier always opened a board without so much as a `fileExists` under
|
||||
/// `.git` (12-editions.md ▸ The free tier and `.git`), and the heal runs on boards that have no
|
||||
/// repository at all.
|
||||
///
|
||||
/// **Pure: no I/O, no filesystem, no `URL`.** It parses text and answers about paths. Reading the
|
||||
/// bytes is `BoardLoader.ignoreRules(atBoardRoot:)`' job, once per walk — the loader stays a pure
|
||||
|
||||
@@ -878,8 +878,10 @@ public enum IntegrityRules: Sendable {
|
||||
/// The filesystem birth date (`.creationDateKey`), `nil` when it cannot be read — the
|
||||
/// **second** rung of the earlier-occurrence-wins ladder.
|
||||
public let birth: Date?
|
||||
/// Where the git path history places this path, `nil` when the board has no history or the
|
||||
/// path is untracked — the **first** rung, injected through `BoardLoader.IdentityHistoryRanker`.
|
||||
/// Where a history ranker once placed this path — `nil` unconditionally today, since
|
||||
/// app-managed git was excised entirely (`strategy/01-git-excision.md`, 2026-08-08) and
|
||||
/// nothing installs `BoardLoader.IdentityHistoryRanker` any more. The **first** rung when
|
||||
/// populated; nil-safe by design, so the ladder falls straight through to birth date now.
|
||||
public let historyRank: Int?
|
||||
|
||||
public init(
|
||||
@@ -958,8 +960,10 @@ public enum IntegrityRules: Sendable {
|
||||
/// folder and often the tracked one, so every other rung would withhold the very card the user
|
||||
/// just restored and render its ghost instead. The heal remints the trashed occurrence. The same
|
||||
/// preference governs a trashed lane sharing a live lane's UUID.
|
||||
/// 1. **Git path history** (`historyRank`): both tracked, the path that entered history first
|
||||
/// wins; one tracked, it outranks the newcomer outright.
|
||||
/// 1. **History rank** (`historyRank`): once git path history — both tracked, the path that
|
||||
/// entered history first wins; one tracked, it outranks the newcomer outright. Always `nil`
|
||||
/// today, no ranker installed since the git excision (`strategy/01-git-excision.md`,
|
||||
/// 2026-08-08); the ladder falls through to rung 2.
|
||||
/// 2. **Filesystem birth date** (`birth`): the older folder wins. Only consulted when *both*
|
||||
/// dates are readable and they differ — one unreadable date is no comparison at all.
|
||||
/// 3. **Deterministic traversal order**, which is `occurrences`' own order and therefore the
|
||||
|
||||
Reference in New Issue
Block a user