Skip to content

APPLE-002 — Apple Modernization Rules

Level 2 — Modernization Rules & Migration Governance

document:
  id: APPLE-002
  title: Apple Modernization Rules
  version: "1.0"
  track: platforms/apple
  updated: 2026-07
  next_review: 2027-WWDC

Separation of Duties

Owns Must NOT Own
Ratchet rules, migration records, acceptable technical debt definitions, modernization lifecycle, Net-Negative Ownership Delta formula Apple capability catalog (APPLE-001). CI implementation details (APPLE-003). Verification receipts (APPLE-003/004).

Dependencies: - Ratchet rules reference capability IDs from APPLE-001 §2. - Migration record IDs (OBS-001 etc.) are referenced by APPLE-003 §5 receipts and APPLE-004 §4 receipts.


1. Two Review Tracks

All Apple platform MRs are evaluated against two independent tracks.

Track 1 — Apple Modernization

Did this MR introduce deprecated or superseded Apple patterns?

Governed by ratchet rules in §2. CI enforcement via APPLE-003 §4.

Track 2 — Bluefly Architecture

Did this MR introduce unnecessary local ownership or architecture violations?

Governed by ratchet rules in §2. LOS impact evaluated in APPLE-004.


2. Ratchet Rules

Ratchet rules prevent regression. They apply to diff-added lines only — not the full codebase. Pre-existing violations are debt (§3), not gate failures.

Track 1 — Apple Modernization Ratchet

Rule ID Pattern Action Rationale
R-OBS New ObservableObject, @StateObject, @ObservedObject, @Published FAIL Superseded by @Observable (Swift 5.9+)
R-TEST New import XCTest, XCTestCase, XCTAssert* FAIL Superseded by Swift Testing (Swift 5.10+)
R-CONC New @unchecked Sendable FAIL Superseded by actor (Swift 5.7+)
R-DEP New deprecated Apple API (from xcodebuild warnings) FAIL Use current API
R-NAV New NavigationView FAIL Superseded by NavigationSplitView / NavigationStack

Track 2 — Bluefly Architecture Ratchet

Rule ID Pattern Action Rationale
R-SVC New singleton manager (.shared pattern on new types) FAIL Violates dependency clarity
R-ENV New .environmentObject() injection FAIL Superseded by @Environment with @Observable
R-DUP New capability duplicated from APPLE-001 §5 FAIL Provider owns the capability
R-UNC New unclassified type (no product/pack/adapter classification) WARN Every type needs a declared owner

3. Migration Records (Pre-existing Debt)

These violations existed before the ratchet was introduced. They are tracked debts, not gate failures. MRs that introduce new instances of these patterns fail the gate (§2). MRs that reduce them improve LOS.

OBS-001 — State Observation: ObservableObject Classes

id: OBS-001
rule: R-OBS
description: Seven ViewModel classes use ObservableObject instead of @Observable
authority_capability: apple.observation  # APPLE-001 §2
files:
  - AgentViewModel.swift
  - ChatViewModel.swift
  - SessionViewModel.swift
  - ProfileViewModel.swift
  - CapabilityViewModel.swift
  - SettingsViewModel.swift
  - SidebarViewModel.swift
status: open
priority: high
admission_criteria: All seven classes migrated to @Observable; @StateObject replaced with @State
completion_criteria: Zero ObservableObject in codebase; Track 1 R-OBS passes on full scan

OBS-002 — State Observation: @StateObject/@ObservedObject Chain

id: OBS-002
rule: R-OBS
description: @StateObject and @ObservedObject usage throughout views, dependent on OBS-001
authority_capability: apple.observation
dependency: OBS-001
status: open
priority: high
admission_criteria: OBS-001 complete
completion_criteria: Zero @StateObject and @ObservedObject in codebase

CONC-001 — Concurrency: @unchecked Sendable Suppressions

id: CONC-001
rule: R-CONC
description: Two types suppress Sendable conformance instead of using actor isolation
authority_capability: apple.concurrency
files:
  - CacheManager.swift
  - MetricsStreamManager.swift
status: open
priority: medium
admission_criteria: Types analysed for shared mutable state
completion_criteria: Both types refactored to actor or provably Sendable without suppression

TEST-001 — Test Authorship: XCTest Throughout

id: TEST-001
rule: R-TEST
description: All tests use XCTestCase; no Swift Testing adoption
authority_capability: apple.testing
status: open
priority: medium
admission_criteria: New test target or test file created
completion_criteria: All new tests use Swift Testing; migration plan for existing tests documented
id: NAV-001
rule: R-NAV
description: Nested NavigationStack instances creating redundant navigation hierarchy
authority_capability: apple.navigation
status: open
priority: low
admission_criteria: Navigation audit complete
completion_criteria: Single navigation root; no nested NavigationStack

4. Net-Negative Ownership Delta Formula

Compute for every MR. Included in APPLE-003 MR Verification Template.

Delta = (Deletions + Adoptions) − (New_Custom + New_Violations)

Where:
  Deletions      = lines of custom code removed (replaced by Apple API or deleted)
  Adoptions      = new Apple API calls replacing prior local code
  New_Custom     = new custom abstractions introduced
  New_Violations = new ratchet rule violations (R-OBS, R-TEST, etc.)

Target: Delta ≥ 0 on every MR.
         Delta > 0 means the MR reduced total ownership.
         Delta = 0 means neutral (acceptable for functional MRs).
         Delta < 0 means the MR increased ownership — requires justification.

5. Modernization Lifecycle

Admission

A migration record (§3) is created when: - A Track 1 ratchet violation is found in the existing codebase during an audit, or - A new Apple API supersedes a pattern currently in use.

The record is created in APPLE-002, not the MR. The MR gate references it by ID.

Active Migration

During migration, the record's status field is in_progress. The MR introducing the migration work references the record ID in its description.

Completion

A migration is complete when: 1. The completion_criteria in the record are satisfied. 2. The APPLE-003 and APPLE-004 scores are recomputed. 3. The record status is updated to complete. 4. A migration receipt is appended to APPLE-004 §4.

Suspension

A migration may be suspended with documented justification if: - A platform requirement prevents migration (e.g., minimum deployment target). - The provider has deprecated the replacement API before the migration is complete. - Evidence is produced and recorded in the migration record.

Suspension does not close the record. It records the blocker.