Source of truth: docs/issues/19-app-scaffold-xcodegen.md in this repository (see PR #1 until merged). If this issue and the draft diverge, the draft wins.
Summary
Create ScanMD.app's project structure: committed App/project.yml (XcodeGen), a
SwiftUI MenuBarExtra shell with the full menu (actions stubbed), Info.plist with usage
strings, entitlements, and the CI job that builds the app.
Context
ADR-005 fixes the packaging approach (XcodeGen; Developer ID + hardened runtime; no
sandbox in v1). This issue makes the app build and show its menu; behavior arrives in
Issues 20–23.
Scope
App/project.yml, App/ScanMD/ sources, Info.plist, ScanMD.entitlements,
app icon placeholder, CI job addition, .gitignore already covers *.xcodeproj.
Detailed Requirements
App/project.yml (XcodeGen):
- project name
ScanMD; one app target ScanMD (platform macOS, deployment 14.0).
PRODUCT_BUNDLE_IDENTIFIER = dev.saber5656.scanmd (KU-6: owner confirms or
replaces at signing time; keep it a single build setting).
- Local package dependency on the repo root package → product
ScanMDKit.
- Remote package
KeyboardShortcuts (sindresorhus) pinned exact — added HERE so
project.yml churn stays in one issue, used in Issue 22.
- Build settings:
ENABLE_HARDENED_RUNTIME = YES, CODE_SIGN_STYLE = Automatic
with empty team (local dev signs ad-hoc; release signing is Issue 26),
SWIFT_STRICT_CONCURRENCY = complete, MARKETING_VERSION = 0.1.0,
CURRENT_PROJECT_VERSION = 1.
Info.plist: LSUIElement = true; NSCameraUsageDescription (same string as CLI,
Issue 16); NO other usage strings (screen recording needs none — TCC prompts via
preflight API); CFBundleDisplayName = ScanMD.
ScanMD.entitlements: hardened-runtime compatible, empty of exceptions (no
com.apple.security.cs.*, no sandbox key). A comment header explains ADR-005.
- App code (
App/ScanMD/):
ScanMDApp.swift: @main, MenuBarExtra("ScanMD", systemImage: "text.viewfinder") with .menuBarExtraStyle(.menu).
AppMenu.swift: the exact DESIGN §9.2 menu; every capture item calls
CaptureCoordinator.shared.request(.…) — coordinator in this issue is a stub enum
NSLog-free no-op that shows a "Not implemented yet" alert; Settings item opens
the (empty) Settings scene; Quit works; About shows version from bundle.
CaptureCoordinator.swift: skeleton @MainActor final class with the §9.4 state
enum (idle/acquiring/recognizing/delivering) and a state published property —
transitions implemented, effects stubbed (Issue 20 fills them). Menu icon switches
to text.viewfinder.fill when not idle (SwiftUI binding).
- CI (extends Issue 03's workflow): job
build-app — brew install xcodegen (pinned
formula version comment), xcodegen generate --spec App/project.yml,
xcodebuild -project App/ScanMD.xcodeproj -scheme ScanMD -configuration Release CODE_SIGN_IDENTITY=- build. The no-network guard (Issue 03) now also scans
App/ScanMD/.
- Add
Scripts/dev-app.sh: xcodegen + xcodebuild + open the built app for local
testing.
Acceptance Criteria
Validation
CI link + screenshot of the open menu + codesign -d --entitlements - <app> output in
the PR.
Dependencies
Issue 05 (Kit builds). Soft: 03 (workflow file exists to extend).
Non-goals
Real capture actions (20), camera window (21), hotkey/settings content (22),
notifications/delivery (23), release signing (26).
Design References
DESIGN §9.1, §9.2, §9.4; ADR-005; ISSUE_PLAN KU-6.
Summary
Create
ScanMD.app's project structure: committedApp/project.yml(XcodeGen), aSwiftUI
MenuBarExtrashell with the full menu (actions stubbed), Info.plist with usagestrings, entitlements, and the CI job that builds the app.
Context
ADR-005 fixes the packaging approach (XcodeGen; Developer ID + hardened runtime; no
sandbox in v1). This issue makes the app build and show its menu; behavior arrives in
Issues 20–23.
Scope
App/project.yml,App/ScanMD/sources,Info.plist,ScanMD.entitlements,app icon placeholder, CI job addition,
.gitignorealready covers*.xcodeproj.Detailed Requirements
App/project.yml(XcodeGen):ScanMD; one app targetScanMD(platform macOS, deployment 14.0).PRODUCT_BUNDLE_IDENTIFIER = dev.saber5656.scanmd(KU-6: owner confirms orreplaces at signing time; keep it a single build setting).
ScanMDKit.KeyboardShortcuts(sindresorhus) pinnedexact— added HERE soproject.yml churn stays in one issue, used in Issue 22.
ENABLE_HARDENED_RUNTIME = YES,CODE_SIGN_STYLE = Automaticwith empty team (local dev signs ad-hoc; release signing is Issue 26),
SWIFT_STRICT_CONCURRENCY = complete,MARKETING_VERSION = 0.1.0,CURRENT_PROJECT_VERSION = 1.Info.plist:LSUIElement = true;NSCameraUsageDescription(same string as CLI,Issue 16); NO other usage strings (screen recording needs none — TCC prompts via
preflight API);
CFBundleDisplayName = ScanMD.ScanMD.entitlements: hardened-runtime compatible, empty of exceptions (nocom.apple.security.cs.*, no sandbox key). A comment header explains ADR-005.App/ScanMD/):ScanMDApp.swift:@main,MenuBarExtra("ScanMD", systemImage: "text.viewfinder")with.menuBarExtraStyle(.menu).AppMenu.swift: the exact DESIGN §9.2 menu; every capture item callsCaptureCoordinator.shared.request(.…)— coordinator in this issue is a stub enumNSLog-free no-op that shows a "Not implemented yet" alert; Settings item opensthe (empty)
Settingsscene; Quit works; About shows version from bundle.CaptureCoordinator.swift: skeleton@MainActor final classwith the §9.4 stateenum (
idle/acquiring/recognizing/delivering) and astatepublished property —transitions implemented, effects stubbed (Issue 20 fills them). Menu icon switches
to
text.viewfinder.fillwhen not idle (SwiftUI binding).build-app—brew install xcodegen(pinnedformula version comment),
xcodegen generate --spec App/project.yml,xcodebuild -project App/ScanMD.xcodeproj -scheme ScanMD -configuration Release CODE_SIGN_IDENTITY=- build. The no-network guard (Issue 03) now also scansApp/ScanMD/.Scripts/dev-app.sh: xcodegen + xcodebuild +openthe built app for localtesting.
Acceptance Criteria
Scripts/dev-app.shproduces a running menu bar app showing the full §9.2 menu;stub alert appears for capture items; Quit/About work.
xcodegen generateis deterministic (running twice yields identical project) and.xcodeprojstays untracked.build-appjob green; guard scans app sources.LSUIElementtrue (no Dock iconat runtime — manual check).
Validation
CI link + screenshot of the open menu +
codesign -d --entitlements - <app>output inthe PR.
Dependencies
Issue 05 (Kit builds). Soft: 03 (workflow file exists to extend).
Non-goals
Real capture actions (20), camera window (21), hotkey/settings content (22),
notifications/delivery (23), release signing (26).
Design References
DESIGN §9.1, §9.2, §9.4; ADR-005; ISSUE_PLAN KU-6.