Denial is not absence — detection learns the unverifiable answer 06 ruled for it

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-06 17:52:20 -04:00
parent 503ec4872c
commit 4ac012a55e
10 changed files with 484 additions and 58 deletions
+27 -5
View File
@@ -114,6 +114,19 @@ public enum AppPreferences {
/// the key is declared here with its neighbours for `WindowID`'s reason.
public static let quickStyleBackgroundsKey = "quickStyleBackgrounds"
/// **The board's zoom level** "app-wide and persisted across restarts" (11-command-nexus.md
/// View Actual Size; 03-board-ui.md Layout zoom). A rung on `BoardZoom.levels`, stored as
/// the multiplier itself. Read and written by `BoardZoomStore`, which owns the ladder's rules; the
/// key is declared here with its neighbours for `WindowID`'s reason.
///
/// **Every read goes through `BoardZoom.normalize`**, and this one cannot use the
/// `object(forKey:) as? Double ?? default` idiom its neighbours use to tell "off" from "never set"
/// the trap here is worse than an ambiguous default. `double(forKey:)` answers 0 for an unset
/// key, and 0 is not merely a wrong level: it drives every `BoardMetrics.em` multiple to its 1pt
/// floor and draws a board of hairlines. Normalising is what makes an unset, hand-edited or
/// stale-from-a-future-build value indistinguishable from a legal one downstream.
public static let boardZoomLevelKey = "boardZoomLevel"
/// The cached subscription facts behind the tier decision (12-editions.md The entitlement)
/// JSON-encoded `SubscriptionFacts`, read and written by `ProEntitlement`.
///
@@ -252,6 +265,12 @@ public final class AppModel {
/// board-scoped and this list deliberately is not.
public let styleRecents: StyleRecents
/// The board's app-wide zoom level (03-board-ui.md Layout zoom). Owned here for
/// `styleRecents`' reason exactly: app-scoped, persisted beside it, and reached by every board
/// window through the environment while the menu rows and the toolbar buttons, which live
/// outside every scene's environment, reach it through this object.
public let zoom: BoardZoomStore
/// The app's one drag session (DRAG-REORDER.md; 04-interactions.md Drag and drop).
///
/// App-wide for the reason cross-board drags exist at all: **a drag crosses windows**, so the
@@ -326,10 +345,12 @@ public final class AppModel {
/// native undo runs". The absent git state *is* the tier test; nothing here reads a flag.
/// - **Mode `git`** (Pro only no other tier composes a git state) the git provider: undo as
/// forward restore commits over HEAD's first-parent ancestry (06).
/// - **Mode `none` and mode `repoNested` alike** the **native stack**, exactly as in the free
/// tier. "Boards without app-managed git repo-nested included bind 13-native-undo.md's
/// native stack in **every** tier" (03-board-ui.md Toolbar Catalog, re-ruled 2026-07-31
/// twice; 12 The provider seam; 13's header).
/// - **Mode `none`, `repoNested`, and `unverifiable` alike** the **native stack**, exactly as
/// in the free tier. "Boards without app-managed git repo-nested included bind
/// 13-native-undo.md's native stack in **every** tier" (03-board-ui.md Toolbar Catalog,
/// re-ruled 2026-07-31 twice; 12 The provider seam; 13's header). `unverifiable` joins the
/// same branch structurally a denial can never be told apart from a repository actually
/// being there, so it takes `repoNested`'s posture, undo included (06 Rules Detection).
///
/// **The repo-nested no-undo case is gone** (re-ruled 2026-07-31): 06's leave-strictly-alone
/// stance "concerns *git*, and this stack never touches git memory-only, journal-free,
@@ -357,7 +378,7 @@ public final class AppModel {
guard let git else { return NativeHistoryProvider() }
switch git.mode {
case .git: return GitHistoryProvider(boardRoot: store.rootURL)
case .none, .repoNested: return NativeHistoryProvider()
case .none, .repoNested, .unverifiable: return NativeHistoryProvider()
}
}
@@ -666,6 +687,7 @@ public final class AppModel {
) {
boardRegistry = BoardRegistry(storageURL: registryStorageURL)
styleRecents = StyleRecents(defaults: preferences)
zoom = BoardZoomStore(defaults: preferences)
clipboard = ClipboardStore(stagingRoot: clipboardStagingRoot)
// Reads the cached facts and nothing else no StoreKit API is touched until
// `ProEntitlement.start()`, which the app's launch calls and a test host never does.