---
phase: floating-clock-plugin
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - plugins/floating-clock/plugin.json
  - plugins/floating-clock/CLAUDE.md
  - plugins/floating-clock/Sources/clock.m
  - plugins/floating-clock/Makefile
  - plugins/floating-clock/Info.plist
  - plugins/floating-clock/.gitignore
  - .claude-plugin/marketplace.json
autonomous: true
requirements: []
---

<objective>
Add `plugins/floating-clock/` — a single-file Objective-C macOS floating desktop clock plugin with minimal memory footprint (~50KB binary, ~10–12 MB RSS).

Purpose: Provide an always-on-top, borderless, draggable desktop clock using native Cocoa (NSPanel) with persistent positioning, precise timer-driven updates, and sub-0.1% idle CPU. Integrate into plugin marketplace and validate against existing test suite.

Output: Complete plugin directory with source, build system, marketplace registration, and passing validator.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@/Users/terryli/eon/cc-skills/CLAUDE.md
@/Users/terryli/eon/cc-skills/plugins/CLAUDE.md
@/Users/terryli/eon/cc-skills/.claude-plugin/marketplace.json
@/Users/terryli/eon/cc-skills/plugins/claude-tts-companion/CLAUDE.md (reference for CLAUDE.md Hub+Sibling pattern)
@/Users/terryli/eon/cc-skills/plugins/claude-tts-companion/Makefile (reference for Makefile target structure)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Scaffold plugin directory and marketplace registration</name>
  <files>
    plugins/floating-clock/plugin.json
    plugins/floating-clock/.gitignore
    .claude-plugin/marketplace.json
  </files>
  <action>
    Create `plugins/floating-clock/` directory structure. Add a minimal `plugin.json` following marketplace conventions:

    ```json
    {
      "name": "floating-clock",
      "description": "Always-on-top floating desktop clock for macOS — borderless NSPanel with persistent positioning and sub-0.1% idle CPU.",
      "version": "1.0.0"
    }
    ```

    Create `plugins/floating-clock/.gitignore` to exclude build artifacts:
    ```
    build/
    .DS_Store
    *.o
    *.dSYM
    ```

    Add plugin entry to `.claude-plugin/marketplace.json` in the `plugins` array. Insert after the last existing plugin entry (before the closing `]`):

    ```json
    {
      "name": "floating-clock",
      "description": "Always-on-top floating desktop clock for macOS — borderless NSPanel with persistent positioning, timer-driven updates, persistent position via NSUserDefaults.",
      "version": "1.0.0",
      "source": "./plugins/floating-clock",
      "category": "utilities",
      "author": {
        "name": "Terry Li",
        "url": "https://github.com/terrylica"
      },
      "keywords": [
        "macos",
        "clock",
        "floating-window",
        "nspanel",
        "objective-c",
        "minimal-footprint",
        "always-on-top",
        "draggable"
      ],
      "strict": false
    }
    ```

    Do NOT modify the root `version` or `name` in marketplace.json (only add the plugin entry).

  </action>
  <verify>
    ls -la plugins/floating-clock/ | grep -E "plugin.json|.gitignore"
    grep -c '"name": "floating-clock"' .claude-plugin/marketplace.json
  </verify>
  <done>
    Directory `plugins/floating-clock/` exists with plugin.json and .gitignore. Plugin is registered in marketplace.json with all required fields (name, description, version, source, category, author, keywords, strict).
  </done>
</task>

<task type="auto">
  <name>Task 2: Implement clock.m — main Objective-C source</name>
  <files>
    plugins/floating-clock/Sources/clock.m
  </files>
  <action>
    Create `plugins/floating-clock/Sources/` directory. Implement `clock.m` (~150 LoC) with the following constraints:

    - **NSPanel subclass**: `level = NSFloatingWindowLevel`, `styleMask = NSWindowStyleMaskBorderless | NSWindowStyleMaskNonactivatingPanel`, `isFloatingPanel = YES`, `becomesKeyOnlyIfNeeded = YES`
    - **Always-on-top behavior**: `collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorStationary`, `hasShadow = YES`
    - **Text rendering**: NSTextField with `[NSFont monospacedDigitSystemFontOfSize:24 weight:NSFontWeightRegular]` (stable-width digits, white text, semi-transparent background)
    - **Timer**: `dispatch_source_t` of type `DISPATCH_SOURCE_TYPE_TIMER` aligned to next second boundary (`dispatch_time(DISPATCH_TIME_NOW, ns_until_next_second)`), 1-second interval, updates textfield on main queue
    - **Position persistence**: Save on `NSWindowDidMoveNotification` to `NSUserDefaults` key `FloatingClockWindowFrame`; restore on launch if present, else center on main screen
    - **No Dock icon**: `LSUIElement` will be set in Info.plist (not in code)
    - **Minimal footprint**: Use `@autoreleasepool`, no memory leaks, no extra frameworks beyond Cocoa

    Template structure (fill in implementation):
    ```c
    #import <Cocoa/Cocoa.h>

    static NSTimeInterval nsUntilNextSecond(void) {
        // Calculate nanoseconds to next second boundary
        // Use NSCalendarIdentifierGregorian + currentDate
        return 0; // Fill with calculation
    }

    @interface FloatingClockPanel : NSPanel
    @end

    @implementation FloatingClockPanel
    - (instancetype)init {
        // Initialize with borderless, non-activating, floating style
        // Set window properties as listed above
        // Create NSTextField for clock display
        // Set background color, text color, font
        // Register for NSWindowDidMoveNotification
        return self;
    }

    - (void)setupTimer {
        // Create dispatch_source_t for DISPATCH_SOURCE_TYPE_TIMER
        // Align to next second boundary
        // Set event handler to update time display
    }

    - (void)updateClock:(NSTimer *)timer {
        // Get current time via [NSDate date]
        // Format as HH:mm:ss using NSDateFormatter
        // Update textfield.stringValue
    }

    - (void)savePa position {
        // On NSWindowDidMoveNotification, save frame to NSUserDefaults
    }

    - (void)restorePosition {
        // On init, check NSUserDefaults for FloatingClockWindowFrame
        // If present, restore; else center on main screen
    }
    @end

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            NSApplication *app = [NSApplication sharedApplication];
            [app setActivationPolicy:NSApplicationActivationPolicyAccessory];

            FloatingClockPanel *panel = [[FloatingClockPanel alloc] init];
            [panel makeKeyAndOrderFront:nil];
            [app run];
        }
        return 0;
    }
    ```

    Do NOT use Swift, SwiftUI, or any runtime dependencies beyond Cocoa. Keep binary footprint minimal by using `-Os` optimization (enforced in Makefile).

  </action>
  <verify>
    test -f plugins/floating-clock/Sources/clock.m
    wc -l plugins/floating-clock/Sources/clock.m | awk '{print $1}' | grep -E '^[0-9]{2,3}$'
    clang -framework Cocoa -fsyntax-only plugins/floating-clock/Sources/clock.m 2>&1 | grep -q "error" && exit 1 || exit 0
  </verify>
  <done>
    File `plugins/floating-clock/Sources/clock.m` exists, ~120–180 LoC, compiles without errors with `clang -framework Cocoa`.
  </done>
</task>

<task type="auto">
  <name>Task 3: Create Makefile and Info.plist template</name>
  <files>
    plugins/floating-clock/Makefile
    plugins/floating-clock/Info.plist
  </files>
  <action>
    Create `Makefile` with targets: `build`, `bundle`, `sign`, `install`, `run`, `clean`, `all` (default).

    **Key requirements**:
    - `build`: Compile with `clang -framework Cocoa -Os -dead_strip -fobjc-arc -o build/floating-clock Sources/clock.m`
    - `bundle`: Create `.app` bundle structure (`build/FloatingClock.app/Contents/{MacOS,Resources}`), copy binary into `MacOS/`, generate `Info.plist`
    - `sign`: Ad-hoc code sign with `codesign --force --deep --sign - build/FloatingClock.app`
    - `install`: Copy bundle to `/Applications/FloatingClock.app`
    - `run`: `make all && open build/FloatingClock.app`
    - `all`: Default target = `build bundle sign`

    Create `Info.plist` template (will be generated by `bundle` target or created manually). Use XML plist format with:
    ```xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleIdentifier</key>
        <string>com.terryli.floating-clock</string>
        <key>CFBundleExecutable</key>
        <string>floating-clock</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>1.0.0</string>
        <key>CFBundleVersion</key>
        <string>1</string>
        <key>LSUIElement</key>
        <true/>
        <key>NSRequiresIPhoneOS</key>
        <false/>
    </dict>
    </plist>
    ```

    Keep Makefile short and simple (no autoconf/cmake). Use `plutil -convert xml1 -o` to generate plist if desired, or create static `Info.plist` and copy it in `bundle` target.

  </action>
  <verify>
    test -f plugins/floating-clock/Makefile
    test -f plugins/floating-clock/Info.plist
    grep -q "clang -framework Cocoa" plugins/floating-clock/Makefile
    grep -q "CFBundleIdentifier" plugins/floating-clock/Info.plist
    grep -q "LSUIElement" plugins/floating-clock/Info.plist
  </verify>
  <done>
    Files exist: `plugins/floating-clock/Makefile` (with all targets), `plugins/floating-clock/Info.plist` (with LSUIElement=true, CFBundleIdentifier, CFBundleVersion).
  </done>
</task>

<task type="auto">
  <name>Task 4: Write CLAUDE.md with Hub+Sibling navigation</name>
  <files>
    plugins/floating-clock/CLAUDE.md
  </files>
  <action>
    Create `plugins/floating-clock/CLAUDE.md` following the pattern from `plugins/claude-tts-companion/CLAUDE.md`. Include:

    **Header (Hub+Sibling links)**:
    ```markdown
    # floating-clock

    Always-on-top floating desktop clock for macOS. Single-file Objective-C implementation (~50KB binary, ~10–12 MB RSS) using NSPanel with persistent positioning and sub-0.1% idle CPU.

    **Hub:** [CLAUDE.md](../../CLAUDE.md) | **Sibling:** [plugins/CLAUDE.md](../CLAUDE.md)
    ```

    **Build section**:
    ```markdown
    ## Build

    From the plugin directory:
    ```bash
    cd plugins/floating-clock
    make all          # build + bundle + sign
    make run          # build + bundle + sign + open app
    make install      # install to /Applications
    make clean        # remove build/ artifacts
    ```

    Binary at `build/floating-clock` (~50KB), app bundle at `build/FloatingClock.app`.

    ## Design

    - **NSPanel (not NSWindow)**: Borderless, non-activating, always floating
    - **All Spaces**: `collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorStationary`
    - **Timer**: `dispatch_source_t` aligned to second boundary, 1-second interval
    - **Position persistence**: NSUserDefaults key `FloatingClockWindowFrame`, restored on launch
    - **No Dock icon**: `LSUIElement=YES` in Info.plist
    - **Monospaced digits**: `monospacedDigitSystemFontOfSize:weight:` (stable-width for clean display)

    ## Implementation

    **Single-file source**: `Sources/clock.m` (~150 LoC)
    - `@autoreleasepool` for memory hygiene
    - Self-contained: no separate header files, no external dependencies beyond Cocoa
    - No SwiftUI, no Swift runtime tax

    ## Future Enhancements

    - 12h/24h format toggle (requires simple pref UI)
    - Seconds toggle
    - Menu bar integration (future plugin: `statusline-tools` could consume this)
    - Launchd autostart option
    - Settings UI (PreferencesWindowController)
    ```

    Keep the document concise (200–300 words). DO NOT include full code listings; link to source files instead.

  </action>
  <verify>
    test -f plugins/floating-clock/CLAUDE.md
    grep -q "Hub:" plugins/floating-clock/CLAUDE.md
    grep -q "Sibling:" plugins/floating-clock/CLAUDE.md
    grep -q "NSPanel" plugins/floating-clock/CLAUDE.md
    grep -q "make all" plugins/floating-clock/CLAUDE.md
  </verify>
  <done>
    File `plugins/floating-clock/CLAUDE.md` exists with Hub+Sibling navigation, Build section with all targets, Design section explaining NSPanel/timer/persistence, Implementation section, and Future Enhancements.
  </done>
</task>

<task type="auto">
  <name>Task 5: Validate plugin and run build smoke test</name>
  <files></files>
  <action>
    From repo root (`/Users/terryli/eon/cc-skills`), run the validation script and build smoke test:

    ```bash
    cd /Users/terryli/eon/cc-skills
    bun scripts/validate-plugins.mjs
    ```

    Expected: No errors, exit 0. If validation passes, proceed to build smoke test:

    ```bash
    cd plugins/floating-clock
    make all
    ```

    Expected: Build succeeds, binary at `build/floating-clock` exists and is <200KB. App bundle at `build/FloatingClock.app/Contents/MacOS/floating-clock` exists.

    If either validation or build fails, stop and report the error. Do NOT attempt to fix — escalate to user.

  </action>
  <verify>
    <automated>
    cd /Users/terryli/eon/cc-skills && bun scripts/validate-plugins.mjs
    test -f plugins/floating-clock/build/floating-clock
    test -f plugins/floating-clock/build/FloatingClock.app/Contents/MacOS/floating-clock
    file plugins/floating-clock/build/floating-clock | grep -q "Mach-O"
    du -h plugins/floating-clock/build/floating-clock | awk '{print $1}' | grep -E "^[0-9]{2,3}K$|^[0-9]M$"
    </automated>
  </verify>
  <done>
    `bun scripts/validate-plugins.mjs` exits 0. Floating-clock plugin is discoverable in marketplace. `make all` succeeds. Binary is Mach-O arm64, <200KB. App bundle properly structured at `build/FloatingClock.app/Contents/MacOS/floating-clock`.
  </done>
</task>

</tasks>

<threat_model>

## Trust Boundaries

| Boundary                     | Description                                                                              |
| ---------------------------- | ---------------------------------------------------------------------------------------- |
| Timer dispatch → main thread | Untrusted dispatch event must not crash main runloop                                     |
| NSUserDefaults read/write    | User can tamper with plist; must validate frame before applying                          |
| Window positioning           | Malformed frame from defaults could place window off-screen; clamp to main screen bounds |

## STRIDE Threat Register

| Threat ID | Category               | Component              | Disposition | Mitigation Plan                                                                                                                    |
| --------- | ---------------------- | ---------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| T-FC-01   | Elevation of Privilege | NSPanel floating level | accept      | No privileged operations; NSFloatingWindowLevel is cosmetic only                                                                   |
| T-FC-02   | Tampering              | NSUserDefaults frame   | mitigate    | Validate frame bounds before applying; fallback to centered position if invalid (per clamping logic in code)                       |
| T-FC-03   | Information Disclosure | Timer dispatch events  | accept      | No secrets involved; clock display is public time                                                                                  |
| T-FC-04   | Denial of Service      | Timer dispatch source  | mitigate    | Use dispatch_source with stable 1-second interval; kill stale dispatch sources on shutdown; add signal handler for SIGTERM cleanup |
| T-FC-05   | Repudiation            | Position changes       | accept      | Low-value: position changes are user-initiated and non-critical                                                                    |

</threat_model>

<verification>
After all tasks complete, verify:

1. Plugin discovered: `bun scripts/validate-plugins.mjs` exits 0, no errors
2. Binary built: `plugins/floating-clock/build/floating-clock` exists, Mach-O, <200KB
3. App bundle: `build/FloatingClock.app/Contents/MacOS/floating-clock` exists, executable
4. Marketplace entry: `.claude-plugin/marketplace.json` contains `"name": "floating-clock"` with required fields
5. Documentation: `plugins/floating-clock/CLAUDE.md` exists with Hub+Sibling navigation
6. Manual smoke test (when user runs): `open build/FloatingClock.app` → clock window appears, draggable, persists across restart, updates every second, <0.1% idle CPU
   </verification>

<success_criteria>

- Floating-clock plugin fully integrated into marketplace
- All files created and committed to git
- `bun scripts/validate-plugins.mjs` passes
- `make all` succeeds with binary <200KB
- CLAUDE.md Hub+Sibling navigation pattern followed
- No build warnings or errors
- Plugin ready for `/plugin-dev:create` template reference and future feature enhancements
  </success_criteria>

<output>
After completion, create `.planning/quick/260423-n9b-add-floating-clock-plugin-single-file-ob/260423-n9b-SUMMARY.md` documenting:
- Plugin directory structure created
- Marketplace registration added and validated
- Build system (Makefile) targets verified
- CLAUDE.md follows Hub+Sibling pattern
- Smoke test results (binary size, app bundle, validation exit code)
</output>
