diff --git a/.codeyam/dependency-graph.json b/.codeyam/dependency-graph.json
index 8b466e8..a53c70b 100644
--- a/.codeyam/dependency-graph.json
+++ b/.codeyam/dependency-graph.json
@@ -1,526 +1,526 @@
{
"files": {
- "ios/Sources/AppCore/CodeyamIsolated/CounterListPanelIsolated.swift": {
+ ".claude/hooks/_step_metadata.py": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/PanelChrome.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterGraphChartIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/SettingsOverlays.swift": {
+ "ios/Sources/AppCore/Theme.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/FeedbackDisclosureToggleIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/GraphPageIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/CounterFeedback.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/AppSettingsPanelIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GraphPageIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/GraphPage.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/AppSettings.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/SettingsToggleRowIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterGraphView.swift": {
+ "ios/Sources/AppCore/ContentView.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/SettingsToggleRow.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/ControlButtonIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/SeedPolicy.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/Theme.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterColorPickerIsolated.swift": {
+ "android/app/build.gradle.kts": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/KeyValueStore.kt": {
+ "ios/Tests/AppCoreTests/ThemeTests.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/ControlButton.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterDot.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/HeaderAnchoredOverlay.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/OverridePickerIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/BoundedScrollIsolated.swift": {
+ "ios/App/App.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/HeaderBarIsolated.swift": {
+ "ios/Sources/AppCore/Views/CounterDot.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterListPanel.kt": {
+ "ios/Tests/AppCoreTests/CounterFeedbackOverrideTests.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterBottomBar.kt": {
+ "ios/Sources/AppCore/Views/CounterGraphChart.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/OverridePicker.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/CounterFeedback.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GraphCloseButton.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CountHeroIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/CountHeroSnapshotTest.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterScreen.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/ControlButtonIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/GraphGeometry.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/Counter.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/FeedbackDisclosureToggleIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterBottomBarIsolated.swift": {
+ "ios/Sources/AppCore/Views/AddCounterDot.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/History.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterScreenState.kt": {
"imports": [],
"importedBy": []
},
- "ios/App/App.swift": {
- "imports": [],
+ ".claude/skills/review-session/scripts/find-last-session.mjs": {
+ "imports": [
+ ".claude/skills/review-session/scripts/find-last-session-helpers.mjs"
+ ],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/SeedPolicy.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/HeaderBar.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/SettingsField.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterListPanelIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterGraphChartIsolated.swift": {
+ "android/app/src/testDebug/java/com/codeyam/android/ui/AppSettingsPanelInteractionTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterIcons.kt": {
+ "ios/Sources/AppCore/Views/GraphHistorySelector.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterGraphViewIsolated.swift": {
+ "ios/Sources/AppCore/Views/CountHero.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterDot.swift": {
+ "ios/Sources/AppCore/Views/HeaderAnchoredOverlay.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterStepStepper.swift": {
- "imports": [],
+ ".claude/skills/review-session/scripts/extract-session.mjs": {
+ "imports": [
+ ".claude/skills/review-session/scripts/extract-session-helpers.mjs"
+ ],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/SettingsToggleRowIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/SettingsOverlays.kt": {
"imports": [],
"importedBy": []
},
- "ui/src/helpers/stepLabels.generated.ts": {
+ "ios/Sources/AppCore/CodeyamIsolated/GraphHeaderIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GraphHeader.swift": {
+ "android/app/src/test/java/com/codeyam/android/ui/CountHeroSnapshotTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/build.gradle.kts": {
+ "ios/Sources/AppCore/CodeyamIsolated/BoundedScrollIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/ControlButton.swift": {
+ "ios/Sources/AppCore/Feedback.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterBottomBar.swift": {
+ "android/app/src/test/java/com/codeyam/android/ui/GraphGeometryTest.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GraphCloseButtonIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CodeyamIsolationHost.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/CounterModel.kt": {
+ "ui/src/helpers/stepLabels.generated.ts": {
"imports": [],
"importedBy": []
},
- "ios/Tests/AppCoreTests/CounterFeedbackOverrideTests.swift": {
+ "ios/Sources/AppCore/Views/CounterGraphView.swift": {
"imports": [],
"importedBy": []
},
- "ios/Tests/AppCoreTests/FeedbackTests.swift": {
+ "ios/Tests/AppCoreTests/ModelTests.swift": {
"imports": [],
"importedBy": []
},
- "ios/Tests/AppCoreTests/ModelTests.swift": {
+ "android/app/src/main/java/com/codeyam/android/MainActivity.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterScreen.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterBottomBar.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/AppSettings.swift": {
+ "android/app/src/test/java/com/codeyam/android/ui/CountFormatTest.kt": {
"imports": [],
"importedBy": []
},
- "ios/Package.swift": {
+ "android/build.gradle.kts": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterGraphChart.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterSettingsPanelIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/build.gradle.kts": {
+ ".claude/skills/review-session/scripts/find-last-session-helpers.mjs": {
"imports": [],
- "importedBy": []
+ "importedBy": [
+ ".claude/skills/review-session/scripts/find-last-session.mjs"
+ ]
},
- "ios/Tests/AppCoreTests/ThemeTests.swift": {
+ "ios/Tests/AppCoreTests/SettingsOverlaysTests.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/model/CounterThemeTest.kt": {
+ "android/app/src/test/java/com/codeyam/android/ui/CountHeroSizingTest.kt": {
"imports": [],
"importedBy": []
},
- ".claude/skills/review-session/scripts/extract-session.mjs": {
- "imports": [
- ".claude/skills/review-session/scripts/extract-session-helpers.mjs"
- ],
+ "ios/Tests/AppCoreTests/AppSettingsTests.swift": {
+ "imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GearButtonIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/AddCounterDotIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/CounterTheme.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/DeleteCounterButtonIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/IncrementFaceButtonStyle.swift": {
+ "ios/Sources/AppCore/AppSettings.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/IncrementBarIsolated.swift": {
+ "ios/Sources/AppCore/Views/GraphPage.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/DeleteCounterButtonIsolated.swift": {
+ "ios/Sources/AppCore/Views/GraphHeader.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GraphPage.swift": {
+ "ios/Sources/AppCore/Views/AppSettingsPanel.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GraphHistorySelectorIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterSwitcherCard.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GraphEventListIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/GraphCloseButtonIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/settings.gradle.kts": {
+ "android/app/src/main/java/com/codeyam/android/ui/PanelChrome.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/AddCounterDotIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterBottomBarIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/model/AppSettingsTest.kt": {
+ "android/app/src/test/java/com/codeyam/android/ui/AndroidFeedbackTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/SettingsControls.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CountFormat.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/MainViewModelTest.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterSettingsPanel.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/AppSettingsPanelIsolated.swift": {
+ "android/scripts/merge-test-results.py": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/DeleteCounterButton.swift": {
+ "android/app/src/test/java/com/codeyam/android/ui/CounterScreenStateTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/GraphPage.kt": {
+ "ios/Sources/AppCore/Views/SettingsField.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Theme.swift": {
+ ".claude/skills/review-session/scripts/extract-session-helpers.mjs": {
"imports": [],
- "importedBy": []
+ "importedBy": [
+ ".claude/skills/review-session/scripts/extract-session.mjs"
+ ]
},
- "ios/Sources/AppCore/Views/IncrementBar.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/CounterModel.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CountFormat.kt": {
+ "ios/Sources/AppCore/SettingsOverlays.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterSwitcherCard.kt": {
+ "android/app/src/test/java/com/codeyam/android/model/FeedbackTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterDot.kt": {
+ "ios/Sources/AppCore/Views/GraphEventList.swift": {
"imports": [],
"importedBy": []
},
- "ios/Tests/AppCoreTests/AppSettingsTests.swift": {
+ ".claude/hooks/editor-step-hook.py": {
"imports": [],
"importedBy": []
},
- ".claude/hooks/_step_metadata.py": {
+ "store/appstore/gen_assets.py": {
"imports": [],
"importedBy": []
},
- "android/scripts/merge-test-results.py": {
+ "ios/Sources/AppCore/SeedPolicy.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/GraphHeaderIsolated.swift": {
+ "ios/Package.swift": {
"imports": [],
"importedBy": []
},
- ".claude/skills/review-session/scripts/find-last-session.mjs": {
- "imports": [
- ".claude/skills/review-session/scripts/find-last-session-helpers.mjs"
- ],
+ "ios/Sources/AppCore/Model.swift": {
+ "imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/CounterHistory.kt": {
+ "ios/Sources/AppCore/Views/GearButton.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/AppSettingsPanel.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/BottomControlRowIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/GraphGeometryTest.kt": {
+ "ios/Sources/AppCore/Views/CounterSettingsPanel.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterColorPicker.swift": {
+ "android/app/src/test/java/com/codeyam/android/model/CounterThemeTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/model/SettingsOverlays.kt": {
+ "ios/Sources/AppCore/Views/GraphCloseButton.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/CountHeroSizingTest.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterIcons.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/Theme.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/IncrementBarIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/CountFormatTest.kt": {
+ "ios/Sources/AppCore/Views/IncrementFaceButtonStyle.swift": {
"imports": [],
"importedBy": []
},
- ".claude/skills/review-session/scripts/extract-session-helpers.mjs": {
+ "ios/Sources/AppCore/CodeyamIsolated/GraphHistorySelectorIsolated.swift": {
"imports": [],
- "importedBy": [
- ".claude/skills/review-session/scripts/extract-session.mjs"
- ]
+ "importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/BottomControlRowIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/AppSettings.kt": {
"imports": [],
"importedBy": []
},
- "store/appstore/gen_assets.py": {
+ "android/app/src/main/java/com/codeyam/android/model/KeyValueStore.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterSwitcherCardIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/GearButtonIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterDotIsolated.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterGraphViewIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/MainActivity.kt": {
+ "ios/Sources/AppCore/Views/ControlButton.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Feedback.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/GraphEventListIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Model.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/HeaderBarIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GraphEventList.swift": {
+ "ios/Sources/AppCore/Views/PanelScrollSupport.swift": {
"imports": [],
"importedBy": []
},
- ".claude/hooks/editor-step-hook.py": {
+ "android/app/src/main/java/com/codeyam/android/ui/ControlButton.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/BottomControlRow.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/CounterHistory.kt": {
"imports": [],
"importedBy": []
},
- ".claude/hooks/editor-pretool-hook.py": {
+ "ios/Tests/AppCoreTests/FeedbackTests.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/testDebug/java/com/codeyam/android/ui/AppSettingsPanelInteractionTest.kt": {
+ "android/app/src/test/java/com/codeyam/android/MainViewModelTest.kt": {
"imports": [],
"importedBy": []
},
- "ios/Tests/AppCoreTests/SettingsOverlaysTests.swift": {
+ "ios/Sources/AppCore/Views/SettingsToggleRow.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/AddCounterDot.swift": {
+ "ios/Sources/AppCore/Views/CounterBottomBar.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/HeaderBar.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/SettingsControls.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterSettingsPanel.swift": {
+ "android/app/src/test/java/com/codeyam/android/model/SettingsOverlaysTest.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CountHeroIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/AndroidFeedback.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/AndroidFeedback.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterSwitcherCardIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/GraphGeometry.kt": {
+ "android/app/src/main/java/com/codeyam/android/model/Counter.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/model/SettingsOverlaysTest.kt": {
+ "android/app/src/main/java/com/codeyam/android/model/CounterTheme.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CountHero.swift": {
+ "ios/Sources/AppCore/Views/IncrementBar.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/AppSettingsPanel.swift": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterStepStepperIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/OverridePickerIsolated.swift": {
+ "ios/Sources/AppCore/Views/OverridePicker.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/model/FeedbackTest.kt": {
+ "ios/Sources/AppCore/Views/BottomControlRow.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterListPanel.swift": {
+ "android/app/src/test/java/com/codeyam/android/model/AppSettingsTest.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CountHero.kt": {
+ "ios/Sources/AppCore/Views/CounterColorPicker.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterGraphChart.kt": {
+ "android/app/src/test/java/com/codeyam/android/model/CounterModelTest.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterStepStepperIsolated.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterGraphChart.kt": {
"imports": [],
"importedBy": []
},
- ".claude/skills/review-session/scripts/find-last-session-helpers.mjs": {
+ "android/settings.gradle.kts": {
"imports": [],
- "importedBy": [
- ".claude/skills/review-session/scripts/find-last-session.mjs"
- ]
+ "importedBy": []
},
- "ios/Sources/AppCore/Views/PanelScrollSupport.swift": {
+ "ios/Sources/AppCore/Views/HeaderBar.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GearButton.swift": {
+ "ios/Sources/AppCore/Views/CounterListPanel.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/AndroidFeedbackTest.kt": {
+ "ios/Sources/AppCore/History.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CodeyamIsolationHost.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/AppSettingsPanel.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/CodeyamIsolated/CounterSettingsPanelIsolated.swift": {
+ "ios/Sources/AppCore/Views/CounterSwitcherCard.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/GraphHistorySelector.swift": {
+ "android/app/src/main/java/com/codeyam/android/ui/CounterListPanel.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/ui/CounterScreenStateTest.kt": {
+ "ios/Sources/AppCore/Views/CounterStepStepper.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/test/java/com/codeyam/android/model/CounterModelTest.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterColorPickerIsolated.swift": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/Views/CounterSwitcherCard.swift": {
+ ".claude/hooks/editor-pretool-hook.py": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterScreenState.kt": {
+ "ios/Sources/AppCore/CodeyamIsolated/CounterDotIsolated.swift": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/CounterSettingsPanel.kt": {
+ "android/app/src/main/java/com/codeyam/android/ui/CountHero.kt": {
"imports": [],
"importedBy": []
},
- "ios/Sources/AppCore/ContentView.swift": {
+ "android/app/src/main/java/com/codeyam/android/model/SeedPolicy.kt": {
"imports": [],
"importedBy": []
},
- "android/app/src/main/java/com/codeyam/android/ui/HeaderBar.kt": {
+ "ios/Sources/AppCore/Views/DeleteCounterButton.swift": {
"imports": [],
"importedBy": []
}
@@ -544,5 +544,5 @@
"importedBy": []
}
},
- "lastUpdated": "2026-07-29T12:50:21Z"
+ "lastUpdated": "2026-07-29T16:00:44Z"
}
\ No newline at end of file
diff --git a/.codeyam/state/finalize-debt.json b/.codeyam/state/finalize-debt.json
index fd2e5fe..4bf49e1 100644
--- a/.codeyam/state/finalize-debt.json
+++ b/.codeyam/state/finalize-debt.json
@@ -1,7 +1,7 @@
{
- "lastFinalizedAt": "2026-07-29T12:50:26Z",
- "lastFinalizedSha": "c10509e9f2cec7b9177d65af78384cb5c25b5071",
- "lastFullFinalizeSha": "c10509e9f2cec7b9177d65af78384cb5c25b5071",
- "lastFullFinalizeTree": "a29f2b2e3ea8a8170d5391b7875c162cb0516baa11e99e8b710f74355a60e032",
+ "lastFinalizedAt": "2026-07-29T16:00:47Z",
+ "lastFinalizedSha": "867412a06316387dad9a41c812d9bfc12486f5c1",
+ "lastFullFinalizeSha": "867412a06316387dad9a41c812d9bfc12486f5c1",
+ "lastFullFinalizeTree": "475c75c73964da583882770e7cf16858a95257554a4ef8250666794e8c337045",
"deferred": []
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 16d5425..6ef060b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
# `--disable-swift-testing` flag used below (absent on older toolchains).
runs-on: macos-15
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Swift version
run: swift --version
@@ -30,11 +30,11 @@ jobs:
# no emulator is booted — this is compile + JVM unit tests only.
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Set up JDK 17
# Gradle 8.7 + Android Gradle Plugin require JDK 17.
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
@@ -42,7 +42,7 @@ jobs:
- name: Set up Android SDK
# Installs the SDK and accepts licenses, exposing ANDROID_SDK_ROOT so
# Gradle resolves it without the gitignored android/local.properties.
- uses: android-actions/setup-android@v3
+ uses: android-actions/setup-android@v4
- name: Build
run: android/gradlew -p android compileDebugKotlin
diff --git a/.github/workflows/codeyam-protection.yml b/.github/workflows/codeyam-protection.yml
new file mode 100644
index 0000000..07f7045
--- /dev/null
+++ b/.github/workflows/codeyam-protection.yml
@@ -0,0 +1,71 @@
+name: codeyam-protection
+
+# codeyam-managed: merge-readiness gates.
+#
+# `ci.yml` proves the code builds and its tests pass. This workflow proves the
+# codeyam state that travels with the code — scenarios, glossary, test registry
+# — is actually finalized, which a build/test run cannot see. The two are split
+# so a metadata-debt failure is legible on its own rather than reading as a
+# broken app.
+#
+# These are the authoritative backstop for the pre-push hook: a contributor
+# pushing from a fork (or with hooks bypassed) is caught here instead.
+#
+# Recommended: make `Merge-readiness gates` a required status check on `main`
+# in repository settings → Branches.
+
+on:
+ pull_request:
+ branches: [main]
+ push:
+ branches: [main]
+
+jobs:
+ gates:
+ name: Merge-readiness gates
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ # Every gate walks history: the deferred-finalize check diffs a commit
+ # range and the full-finalize check resolves a recorded SHA. A shallow
+ # clone makes both unresolvable.
+ fetch-depth: 0
+
+ - name: Fetch the base branch
+ # `fetch-depth: 0` gives full history for the checked-out ref, but on a
+ # pull_request event the `origin/main` remote-tracking ref the
+ # deferred-finalize gate defaults to may not exist locally. Fetch it
+ # explicitly so `--base origin/main` always resolves.
+ run: git fetch --no-tags origin main:refs/remotes/origin/main || true
+
+ - name: Set up Node
+ uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+
+ - name: Install codeyam-editor
+ run: npm install -g @codeyam-editor/codeyam-editor@latest
+
+ - name: Verify no deferred-finalize commits
+ # Fails when a commit carries `Codeyam-Finalize: deferred` without a
+ # later `session-finalize` covering it — i.e. Fast Commit debt that was
+ # never paid down.
+ run: |
+ codeyam-editor editor verify-no-deferred-finalize \
+ --base origin/main \
+ --head HEAD \
+ --format text
+
+ - name: Verify a full branch-wide finalize covers HEAD
+ # The deferred check above only catches *trailered* commits. This one
+ # catches a branch that simply never ran a full finalize, keeping
+ # whole-repo debt — invisible to the diff-only Fast Commit gate — off
+ # `main`.
+ run: codeyam-editor editor verify-full-finalize --format text
+
+ - name: Verify completed plans still have their implementations
+ # Guards the merge boundary against a post-finalize rebase silently
+ # dropping the code a plan was archived as "completed" for. Plans with
+ # no deliverables manifest are reported as needs-review, not failed.
+ run: codeyam-editor editor verify-completed-plans --format text
diff --git a/.github/workflows/play-release.yml b/.github/workflows/play-release.yml
index 4eb449d..3c867fa 100644
--- a/.github/workflows/play-release.yml
+++ b/.github/workflows/play-release.yml
@@ -1,11 +1,12 @@
name: Play Release
# Manual trigger — build a signed AAB and promote it to a chosen Play track,
-# including **production**. The sibling `play-internal.yml` is the zero-argument
-# "just get a build to internal testing" button; this one is the parameterized
+# including **production**. This is the only Play upload path: the parameterized
# release lever with staged rollout, release notes, and R8 deobfuscation upload.
+# It replaced the retired zero-argument `play-internal.yml`; pick the `internal`
+# track here for what that button used to do.
#
-# Secrets are the SAME five `play-internal.yml` already uses (all configured):
+# Secrets are the five the retired workflow used (all configured):
# ANDROID_KEYSTORE_BASE64 / ANDROID_KEYSTORE_PASSWORD /
# ANDROID_KEY_ALIAS / ANDROID_KEY_PASSWORD / PLAY_SERVICE_ACCOUNT_JSON
#
@@ -58,7 +59,7 @@ jobs:
- uses: actions/checkout@v5
- name: Set up JDK 17
- # Gradle 8.4 + AGP require JDK 17 (matches ci.yml and play-internal.yml).
+ # Gradle 8.4 + AGP require JDK 17 (matches ci.yml).
uses: actions/setup-java@v5
with:
distribution: temurin
diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml
index 3bdcc7b..ed7e91c 100644
--- a/.github/workflows/testflight.yml
+++ b/.github/workflows/testflight.yml
@@ -10,7 +10,7 @@ jobs:
name: Archive & upload to TestFlight
runs-on: macos-26
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Show toolchain
run: |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2bf99e7..a0e2428 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,7 +20,7 @@ git clone https://github.com/codeyam-ai/codeyam-counter && cd codeyam-counter
npm install -g @codeyam-editor/codeyam-editor@latest
# Launch the editor (split-screen terminal + live preview)
-codeyam-editor editor
+codeyam-editor start
```
codeyam-editor requires a subscription to Claude, Gemini, or Codex. Inside the
@@ -35,7 +35,7 @@ you're changing.
- **iOS** — macOS with a recent Xcode (Swift 6 toolchain) and an iOS 15+
simulator or device.
-- **Android** — JDK 17 plus the Android SDK (compile SDK 34), and an emulator
+- **Android** — JDK 17 plus the Android SDK (compile SDK 35), and an emulator
image for the preview. See [ANDROID_SETUP.md](ANDROID_SETUP.md).
## Building and testing by hand — iOS
@@ -73,9 +73,26 @@ build and test commands are:
```bash
android/gradlew -p android compileDebugKotlin
android/gradlew -p android testDebugUnitTest
+
+# Verify the committed Compose snapshots still match
+android/gradlew -p android verifyPaparazziDebug
+```
+
+[Paparazzi](https://github.com/cashapp/paparazzi) renders the Compose components
+off-device on the JVM, so the goldens under
+`android/app/src/test/snapshots/` are checked without an emulator. **CI runs
+`verifyPaparazziDebug` too**, so a change to a component's rendering fails there
+unless you re-record and commit the new goldens:
+
+```bash
+android/gradlew -p android recordPaparazziDebug
```
-CI runs these same two commands on Ubuntu with JDK 17. Put JVM unit tests under
+Review the resulting image diff before committing it — a re-record makes the
+check pass by definition, so it is only correct when the visual change is
+intended.
+
+CI runs these same commands on Ubuntu with JDK 17. Put JVM unit tests under
`android/app/src/test/` and register them with the editor
(`codeyam-editor editor reconcile-registry --auto-apply`) so they stay tracked
alongside the scenarios, exactly as on the iOS side.
@@ -87,7 +104,8 @@ alongside the scenarios, exactly as on the iOS side.
captured and registered as you go.
3. Ensure the build and tests pass for the platform(s) you touched — the iOS
`swift build`/`swift test` commands and/or the Android
- `compileDebugKotlin`/`testDebugUnitTest` commands above. CI runs both.
+ `compileDebugKotlin`/`testDebugUnitTest`/`verifyPaparazziDebug` commands
+ above. CI runs both platforms.
4. Open a PR describing what changed and why, and fill in the PR template.
## Code of conduct
diff --git a/README.md b/README.md
index b4fad8c..a0a8496 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ git clone https://github.com/codeyam-ai/codeyam-counter && cd codeyam-counter
npm install -g @codeyam-editor/codeyam-editor@latest
# Launch the editor (split-screen terminal + live preview)
-codeyam-editor editor
+codeyam-editor start
```
@@ -61,7 +61,7 @@ device. See [IOS_SETUP.md](IOS_SETUP.md) for simulator prerequisites.
### Android
A native Kotlin + Jetpack Compose port lives in [`android/`](android/). Requires
-JDK 17 and the Android SDK (compile SDK 34).
+JDK 17 and the Android SDK (compile SDK 35).
```bash
# Compile the sources and run the JVM unit tests
@@ -85,33 +85,33 @@ States captured as runnable scenarios with codeyam-editor:
-### Android Counter - Added blank slot selected
+### Counter - Active count
-
+
-### Android Counter - All but one deleted
+### Android Counter - Added blank slot selected
-
+
-### Android Counter - All counters list
+### Counter - Added blank slot selected
-
+
-### Android Counter - All counters list with blank slot
+### Android Counter - All but one deleted
-
+
-### Android Counter - App Settings open
+### Counter - All but one deleted
-
+
-### Android Counter - App Settings sound and haptic on
+### Android Counter - All counters list
-
+
-### Android Counter - Blank slot incremented
+### Counter - All counters list
-
+
## Contributing
diff --git a/store/playstore/README.md b/store/playstore/README.md
index b0c50c0..ac5acea 100644
--- a/store/playstore/README.md
+++ b/store/playstore/README.md
@@ -107,7 +107,7 @@ These are decisions, not bugs. Do not "fix" them.
- **`versionCode` must increase on every single upload**, including a re-upload
to the same track, and can never be reused. This is the most common cause of a
rejected Play upload. Local builds default to `1`; CI passes
- `-PversionCodeOverride=` (see `.github/workflows/play-internal.yml`).
+ `-PversionCodeOverride=` (see `.github/workflows/play-release.yml`).
- **Upload key** lives at `android/app/upload-keystore.jks` (git-ignored), with
credentials in `android/keystore.properties` (git-ignored) or the
`ANDROID_KEYSTORE_*` env vars. **Play App Signing** is enrolled, so this is
@@ -128,7 +128,8 @@ These are decisions, not bugs. Do not "fix" them.
- Fill the Play Console store listing from `listing.md` and upload the graphics
above.
-- Clear the App content declarations using `PLAY_CONSOLE_CHEATSHEET.md`.
+- Clear the App content declarations using
+ [PLAY_CONSOLE_CHEATSHEET.md](PLAY_CONSOLE_CHEATSHEET.md).
- **Closed testing gate:** a recently-created *personal* developer account must
run a closed test with **≥12 testers for 14 continuous days** before
production access is unlocked. This does not affect Internal testing. Confirm
@@ -136,5 +137,8 @@ These are decisions, not bugs. Do not "fix" them.
- Optionally upload the R8 deobfuscation mapping file
(`android/app/build/outputs/mapping/release/mapping.txt`) so Play can
symbolicate release crash reports.
-- The privacy policy page mentions iCloud backups; consider making that line
- OS-neutral (Android uses Google Auto Backup) for a public launch.
+
+The privacy policy at `https://codeyam.com/counter/privacy` is already
+OS-neutral — the backup clause names both Apple and Google, so it reads
+correctly for an Android reviewer. The page lives in `codeyam-ai/codeyam`
+(`dashboard/app/routes/counter.privacy.tsx`), not this repo.