Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ Visually identical pads can contain different controllers or pinouts. Never
flash a device based on appearance alone. Check the factory VID/PID and follow
the firmware repository's preflight procedure.

### Other boards are not supported

Support means one board. Treat every other device as incompatible until someone
proves otherwise on that specific hardware.

**Guaranteed not to work.** Any pad with a different number of controls — more
or fewer keys, no encoder, a second encoder — cannot run this firmware. The
control count, the pin map, and the six-LED chain are compile-time constants, so
a mismatch is not a degraded experience, it is a non-functional device.

**Not expected to work.** A pad with the same six-key, one-encoder layout may
still differ in controller, pin assignment, LED order, or SW2 wiring. If such a
board happens to be wired identically, that is luck, not compatibility. Nothing
about the enclosure, product name, or listing predicts what is on the PCB.

Other hardware would need the firmware **ported** to it: the pin and LED maps in
the firmware repository's `HARDWARE_NOTES.md` describe what has to be
re-measured and changed. That is development work, not a configuration step, and
this project cannot verify the result for you.

| USB identity | Support |
| --- | --- |
| `1189:8890` | Verified factory CH57x configuration path |
Expand Down
31 changes: 23 additions & 8 deletions Tests/AgentMicroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@ import Carbon.HIToolbox
import XCTest
@testable import AgentMicro

/// Removes a temporary `UserDefaults` suite completely.
///
/// `removePersistentDomain(forName:)` only empties the domain; `cfprefsd` still
/// leaves an empty `~/Library/Preferences/<suite>.plist` behind. Every test run
/// creates a fresh UUID-named suite, so without this the developer's Preferences
/// directory accumulates one stray file per suite per run, forever.
func removeDefaultsSuite(_ suiteName: String, defaults: UserDefaults) {
defaults.removePersistentDomain(forName: suiteName)
defaults.removeSuite(named: suiteName)
let plist = FileManager.default
.homeDirectoryForCurrentUser
.appendingPathComponent("Library/Preferences/\(suiteName).plist")
try? FileManager.default.removeItem(at: plist)
}

final class AgentMicroTests: XCTestCase {
func testPrivacySettingsDeepLinksTargetTheExpectedTCCPanes() {
XCTAssertEqual(
Expand Down Expand Up @@ -103,7 +118,7 @@ final class AgentMicroTests: XCTestCase {
func testAgentMicroMigratesLegacyPreferencesAndApplicationSupport() throws {
let suiteName = "AgentMicroTests.migration.\(UUID().uuidString)"
let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }
defaults.set("de", forKey: "CodexPad.appLanguage")

let root = FileManager.default.temporaryDirectory
Expand Down Expand Up @@ -2083,7 +2098,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryPersistsOfferedTriggersAcrossProfiles() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

let codex = ProfileFactory.codex(catalog: CodexActionCatalog())
let claude = ProfileFactory.claude(catalog: CodexActionCatalog(resourceName: "ClaudeActions", app: .claude))
Expand All @@ -2101,7 +2116,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryAdvancesPastEveryPreviouslyOfferedCandidate() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

let profiles = [ProfileFactory.codex(catalog: CodexActionCatalog())]
CodexTriggerRegistry.reserve("cmd-ctrl-opt-shift-a", defaults: defaults)
Expand All @@ -2120,7 +2135,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryRemembersAnActionAcrossPadControls() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

CodexTriggerRegistry.remember(
"cmd-ctrl-opt-shift-f7",
Expand All @@ -2140,7 +2155,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryConfirmsOnlyAfterSuccessfulSetup() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

CodexTriggerRegistry.remember(
"cmd-ctrl-opt-shift-f8",
Expand All @@ -2165,7 +2180,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryRestoresConfirmedActionAfterRestart() throws {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

let definition = try XCTUnwrap(CodexActionCatalog().action(id: "toggle-pet"))
CodexTriggerRegistry.markConfirmed(
Expand All @@ -2184,7 +2199,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistryImportsExistingBindingsFromInactiveLayers() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

var profile = ProfileFactory.codex(catalog: CodexActionCatalog())
var inactiveLayer = profile.layers[0]
Expand All @@ -2208,7 +2223,7 @@ final class AgentMicroTests: XCTestCase {
func testTriggerRegistrySkipsTriggersUsedInAnotherProfile() {
let suiteName = "CodexPadTests.triggerRegistry.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
defer { defaults.removePersistentDomain(forName: suiteName) }
defer { removeDefaultsSuite(suiteName, defaults: defaults) }

let codex = ProfileFactory.codex(catalog: CodexActionCatalog())
var claude = ProfileFactory.claude(catalog: CodexActionCatalog(resourceName: "ClaudeActions", app: .claude))
Expand Down