The message engine outlives its substrate — harvested to Kanban/Changes/ as the change narrator
Step 2 of strategy/01-git-excision.md: CommitMessageEngine and the composer seam relocate to a neutral module renamed away from commit vocabulary (ChangeNarrator, ChangeNarrationRequest, ChangeNarrating, SemanticChangeNarration, ChangeAuthorship), GitChangedPath extracts from GitCommitOperation as ChangedPath, and the one git tie severs — authorship's foreign case carries a display name, not a GitIdentity. The spec tests transplant as ChangeNarratorTests, alive until the journal work begins. 3,009 tests green. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import Foundation
|
||||
|
||||
// MARK: - Changed paths
|
||||
|
||||
/// Harvested 2026-08-08 from `Kanban/Git/` per `strategy/01-git-excision.md`. This value type, and
|
||||
/// the narrator it feeds, are the designated core of the future activity feed / foreign-change
|
||||
/// journal — the "previous snapshot" input the narrator diffs against is supplied by the caller by
|
||||
/// contract; the journal will supply it from memory/snapshots, where the git stack supplied it from
|
||||
/// HEAD.
|
||||
|
||||
/// One path found to differ between the last-recorded snapshot and the working tree (what `git
|
||||
/// status` reports, for the git-backed supplier).
|
||||
///
|
||||
/// Board-root-relative and file-granular, which is the unit both consumers want: staging adds or
|
||||
/// removes exactly these, and attribution asks a question per *file* (06 ▸ Interaction with external
|
||||
/// writers: "classify every observed change, per file").
|
||||
public struct ChangedPath: Sendable, Equatable, Hashable {
|
||||
|
||||
/// The path, relative to the board root, in git's own spelling (`/` separators, no leading dot).
|
||||
public let path: String
|
||||
|
||||
/// Whether the file is **gone** from the working tree.
|
||||
///
|
||||
/// The `modified-by` rule turns on this bit — "any true deletion in the window falls back to
|
||||
/// `Lanework External` — a deletion leaves no file to stamp" — which is why the rename half
|
||||
/// below is a separate fact rather than folded in here.
|
||||
public let isDeletion: Bool
|
||||
|
||||
/// Whether this path is one end of a **rename** the provider paired up (libgit2's pairing, for
|
||||
/// the git-backed supplier).
|
||||
///
|
||||
/// "**A folder move is not a deletion**: items match by id across the whole board … so a moved
|
||||
/// card attributes by its stamp like any changed file" (06). A paired departure is therefore a
|
||||
/// deletion on disk that the window must not be demoted by.
|
||||
public let isRename: Bool
|
||||
|
||||
/// Whether the path is **new in this commit** — surfaced rather than inferred (git's own
|
||||
/// `GIT_DELTA_ADDED`, for the git-backed supplier, and a rename's arriving end).
|
||||
///
|
||||
/// It exists for the comment verb family (01-storage-format.md § Enhanced schema): comments are
|
||||
/// window-scoped and the board snapshot never carries them, so "Comment on 'X'" and "Edit comment
|
||||
/// on 'X'" cannot be told apart by a diff of two snapshots — the only thing that distinguishes a
|
||||
/// comment folder arriving from one being rewritten is whether HEAD already had it, which is
|
||||
/// exactly the question this diff already answered.
|
||||
public let isArrival: Bool
|
||||
|
||||
public init(path: String, isDeletion: Bool, isRename: Bool, isArrival: Bool = false) {
|
||||
self.path = path
|
||||
self.isDeletion = isDeletion
|
||||
self.isRename = isRename
|
||||
self.isArrival = isArrival
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user