Project setup
Goal of this chapter: an empty folder turns into a project that builds, runs, and tests from toolbar buttons — and that an agent can work on without asking where anything is.
1. Generate the project, don’t click it
Section titled “1. Generate the project, don’t click it”The Xcode project is generated from a declarative project.yml
with xcodegen. That keeps
the project definition diffable, mergeable, and maintainable by the
agent — nobody resolves .pbxproj conflicts by hand. 4Notice’s
definition starts like this (abridged):
name: FourNoticeoptions: bundleIdPrefix: com.better-apps developmentLanguage: en deploymentTarget: macOS: "26.0" iOS: "26.0"settings: base: CODE_SIGN_STYLE: Automatic SWIFT_STRICT_CONCURRENCY: completetargets: FourNotice: type: application platform: macOS sources: [Sources/Shared, Sources/macOS, Resources] FourNotice-iOS: type: application platform: iOS sources: [Sources/Shared, Sources/iOS, Resources]Two platforms, one Sources/Shared — the SwiftUI app lives there,
each platform adds a thin layer. Then:
xcodegen generateThe generated FourNotice.xcodeproj is not committed — it is a
build artifact of project.yml. Every build script regenerates it
first, so it can never be stale.
2. Git from minute one
Section titled “2. Git from minute one”git init -b maingit add -A && git commit -m "Generate FourNotice project from project.yml"gh repo create 4Notice --private --source . --pushA private repo on day one costs nothing and gives every later step — atomic commits, skill provenance, CI — something to hang on to.
3. Open it in iKanban AI
Section titled “3. Open it in iKanban AI”Open the folder as a project in iKanban AI. The app writes the
agent setup: .agent/AGENT.md (the workflow contract), pointer files
CLAUDE.md and AGENTS.md, and the empty plans/ and board/
folders. From this moment, any terminal agent you start in this
directory knows the rules of the house — and the app shows whatever
the agent does.
4. Actions: build, run, test as buttons
Section titled “4. Actions: build, run, test as buttons”Recurring commands become actions in .agent/actions.json. The
real 4Notice entries (abridged):
[ { "name": "Build (macOS)", "command": "sh scripts/build.sh", "toolbar": true, "shortcut": "cmd-b" }, { "name": "Run (macOS)", "command": "sh scripts/run.sh", "toolbar": true, "shortcut": "cmd-r" }, { "name": "Test (macOS)", "command": "sh scripts/test.sh", "toolbar": true, "shortcut": "cmd-u" }]They execute through the local exec server
(allowlisted, project-rooted). Note that every command is
sh scripts/….sh — that’s a lesson, not a style choice: the exec
server runs argv without a shell, so &&, pipes, and $(…)
in an action command would arrive as literal arguments. Compound
logic lives in scripts:
#!/bin/sh# scripts/build.sh — Build the macOS app (Debug). DerivedData stays in# ~/Library: a build folder inside this iCloud-synced directory gets# Finder xattrs and breaks codesign.set -eucd "$(dirname "$0")/.."xcodegen generate --quietxcodebuild -project FourNotice.xcodeproj -scheme FourNotice \ -configuration Debug -destination 'platform=macOS' \ -allowProvisioningUpdates buildThat comment about DerivedData is the second real lesson: 4Notice
lives on an iCloud-synced Desktop, and an in-project build/ folder
picked up Finder extended attributes that broke codesign. Build
products stay in ~/Library/Developer/Xcode/DerivedData; run.sh
asks xcodebuild -showBuildSettings for BUILT_PRODUCTS_DIR and
launches the app from there.
Where we are
Section titled “Where we are”A click on ▶ in the iKanban AI toolbar builds and launches the app;
⌘U runs the tests; everything the setup consists of — project.yml,
scripts/, .agent/ — is committed and readable. Time to decide
what to build: a plan and a board.