diff --git a/ROADMAP.md b/ROADMAP.md index 05cdd47b..8b80385d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -759,6 +759,7 @@ Completed Milestone 23 by adding the `cloud-inference-skills` child plugin, ship - [ ] Investigate further standardization and automation for shared skill scaffolding, evidence capture, validation prompts, and generated references so common workflow knowledge is maintained once and reused with lower token load. - [ ] Redesign the Socket release flow around branch-backed worktrees. Split pre-merge feature-branch gates from post-merge `main` gates, make the release-ready failure mode more helpful when run from a worktree, preserve the rule that tags and release evidence come from reviewed `main`, and document how a release-prep branch should carry version bumps without making the base checkout dirty. - [ ] Centralize Socket validation behind one root command before adding more plugin-specific validators. The first pass should gather marketplace metadata, plugin manifests, icon assets, child `AGENTS.md`, `SKILL.md` frontmatter, `agents/openai.yaml`, shared version inventory, release-prep checks, and optional child-local tests into a clear report while keeping heavyweight behavior tests opt-in. +- [ ] Add a future Apple Developer Portal Driver for accessible, interactive portal-only provisioning tasks. Keep Apple authentication, two-factor authentication, account/team selection, and destructive operations behind explicit user-visible confirmation gates; retain official App Store Connect REST, Xcode-aware discovery, `cktool`, and CKTool JS as the primary surfaces, and do not automate unsupported portal forms until a reviewed driver design exists. ## Backlog Candidates diff --git a/plugins/apple-dev-skills/.codex-plugin/plugin.json b/plugins/apple-dev-skills/.codex-plugin/plugin.json index ef59d4f9..5832a302 100644 --- a/plugins/apple-dev-skills/.codex-plugin/plugin.json +++ b/plugins/apple-dev-skills/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "apple-dev-skills", "version": "8.7.0", - "description": "Apple development workflows for Codex, including SwiftData, SwiftUI architecture, media and audio repair, XcodeGen migration, Xcode coding intelligence, Core Animation, typography, SF Symbols, AppKit, Safari, DeviceCheck/App Attest, Swift OpenAPI clients, and DocC.", + "description": "Apple development workflows for Codex, including Apple Developer provisioning and CloudKit automation planning, SwiftData, SwiftUI architecture, media and audio repair, XcodeGen migration, Xcode coding intelligence, Core Animation, typography, SF Symbols, AppKit, Safari, DeviceCheck/App Attest, Swift OpenAPI clients, and DocC.", "author": { "name": "Gale", "email": "mail@galewilliams.com", @@ -39,6 +39,10 @@ "app-attest", "attestation", "fraud-risk", + "provisioning", + "app-store-connect", + "cloudkit", + "cktool", "openapi", "urlsession", "ios", @@ -69,6 +73,7 @@ "Help me design and preview an Apple app icon using Icon Composer, local Mac artwork tools, ictool exports, and careful Computer Use guidance for the GUI.", "Choose the right Safari extension, Safari Web Inspector, SafariServices, messaging, content blocker, or automation fallback path for this Mac app.", "Design a DeviceCheck or App Attest flow for this Apple app, keeping DCDevice, DCAppAttestService, server challenges, entitlements, rollout, and backend validation handoffs explicit.", + "Plan safe Apple Developer provisioning or CloudKit automation using official App Store Connect REST APIs, Xcode-aware discovery, cktool, or CKTool JS; keep credentials local, show a dry run first, and route portal-only configuration to the Apple Developer Portal.", "Add or diagnose a generated Swift OpenAPI client in this Apple app using OpenAPIURLSession and current Apple docs.", "Help me build, test, or debug this Swift or Xcode project using the repo's Apple workflows.", "Design, migrate, test, or integrate SwiftData persistence using the dedicated SwiftData workflow and current Apple documentation.", diff --git a/plugins/apple-dev-skills/.github/scripts/sync_shared_snippets.sh b/plugins/apple-dev-skills/.github/scripts/sync_shared_snippets.sh index f03cb690..b8e7d475 100755 --- a/plugins/apple-dev-skills/.github/scripts/sync_shared_snippets.sh +++ b/plugins/apple-dev-skills/.github/scripts/sync_shared_snippets.sh @@ -67,6 +67,7 @@ sync_one \ "$ROOT_DIR/skills/swiftui-animation-workflow/references/snippets/apple-xcode-project-core.md" \ "$ROOT_DIR/skills/safari-extension-control-workflow/references/snippets/apple-xcode-project-core.md" \ "$ROOT_DIR/skills/devicecheck-app-attest-workflow/references/snippets/apple-xcode-project-core.md" \ + "$ROOT_DIR/skills/apple-developer-provisioning-workflow/references/snippets/apple-xcode-project-core.md" \ "$ROOT_DIR/skills/swiftui-app-architecture-workflow/references/snippets/apple-xcode-project-core.md" \ "$ROOT_DIR/skills/appkit-app-architecture-workflow/references/snippets/apple-xcode-project-core.md" \ "$ROOT_DIR/skills/sync-xcode-project-guidance/references/snippets/apple-xcode-project-core.md" \ diff --git a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh index 8391804b..6e033547 100644 --- a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +++ b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh @@ -124,6 +124,7 @@ active_skill_mds=( "./skills/swiftui-animation-workflow/SKILL.md" "./skills/safari-extension-control-workflow/SKILL.md" "./skills/devicecheck-app-attest-workflow/SKILL.md" + "./skills/apple-developer-provisioning-workflow/SKILL.md" "./skills/appkit-app-architecture-workflow/SKILL.md" "./skills/swiftui-app-architecture-workflow/SKILL.md" "./skills/swiftdata-workflow/SKILL.md" @@ -138,7 +139,7 @@ active_skill_mds=( "./skills/sync-swift-package-guidance/SKILL.md" "./skills/xcode-coding-intelligence-workflow/SKILL.md" ) -[[ ${#active_skill_mds[@]} -eq 31 ]] || fail "Expected exactly 31 active skills, found ${#active_skill_mds[@]}." +[[ ${#active_skill_mds[@]} -eq 32 ]] || fail "Expected exactly 32 active skills, found ${#active_skill_mds[@]}." shared_xcode_snippet="./shared/agents-snippets/apple-xcode-project-core.md" shared_package_snippet="./shared/agents-snippets/apple-swift-package-core.md" diff --git a/plugins/apple-dev-skills/README.md b/plugins/apple-dev-skills/README.md index 64789c25..b9f154ce 100644 --- a/plugins/apple-dev-skills/README.md +++ b/plugins/apple-dev-skills/README.md @@ -1,6 +1,6 @@ # apple-dev-skills -Apple, Swift, SwiftUI animation and architecture, Core Animation, Apple typography, SF Symbols, AppKit, Icon Composer app icons, Safari, DeviceCheck, App Attest, Xcode, Swift OpenAPI client, DocC, and `Dash.app` workflows for Codex. +Apple, Swift, SwiftUI animation and architecture, Core Animation, Apple typography, SF Symbols, AppKit, Apple Developer provisioning, CloudKit, Icon Composer app icons, Safari, DeviceCheck, App Attest, Xcode, Swift OpenAPI client, DocC, and `Dash.app` workflows for Codex. ![Codex plugin directory filtered to the Socket marketplace, showing Apple Dev Skills listed alongside companion plugins below a Productivity Skills suggestion.](./docs/media/codex-plugin-directory-socket-apple-dev-skills.png) @@ -73,6 +73,7 @@ Use Apple Dev Skills when an agent is helping with: - Icon Composer app icon design, preview, and Xcode handoff - Safari extension and SafariServices integration choices - DeviceCheck per-device state and App Attest app-integrity flow planning +- Safe official App Store Connect provisioning and CloudKit automation planning, with explicit Apple Developer Portal fallbacks - Swift OpenAPI client generation and `URLSession` transport integration - Swift package bootstrap, build, and testing - Apple UI accessibility work @@ -140,6 +141,7 @@ uv run pytest - `core-animation-layer-workflow` - `coremedia-timing-samplebuffer-workflow` - `devicecheck-app-attest-workflow` +- `apple-developer-provisioning-workflow` - `explore-apple-swift-docs` - `format-swift-sources` - `icon-composer-app-icon-workflow` diff --git a/plugins/apple-dev-skills/ROADMAP.md b/plugins/apple-dev-skills/ROADMAP.md index 12191b0b..9567df36 100644 --- a/plugins/apple-dev-skills/ROADMAP.md +++ b/plugins/apple-dev-skills/ROADMAP.md @@ -30,6 +30,7 @@ Swift naming and persistence ownership are now standardized: each project explic - [Milestone 50: Swift Lang Handoff And Compatibility](#milestone-50-swift-lang-handoff-and-compatibility) - [Milestone 52: Apple Design Animation And Symbols Workflow Skills](#milestone-52-apple-design-animation-and-symbols-workflow-skills) - [Milestone 53: DeviceCheck and App Attest Workflow](#milestone-53-devicecheck-and-app-attest-workflow) +- [Milestone 54: Apple Developer Provisioning and CloudKit Workflow](#milestone-54-apple-developer-provisioning-and-cloudkit-workflow) - [Backlog Candidates](#backlog-candidates) - [History](#history) @@ -71,6 +72,7 @@ Swift naming and persistence ownership are now standardized: each project explic - Milestone 50: Swift Lang Handoff And Compatibility - In Progress - Milestone 52: Apple Design Animation And Symbols Workflow Skills - In Progress - Milestone 53: DeviceCheck and App Attest Workflow - Completed +- Milestone 54: Apple Developer Provisioning and CloudKit Workflow - Completed ## Milestone 21: Swift Cleanup Automation Exploration @@ -766,6 +768,27 @@ Completed Completed Milestone 53 by adding `devicecheck-app-attest-workflow`, focused references for DeviceCheck state, App Attest client flow, server validation, and App ID/entitlement validation, plus plugin metadata, README inventory, shared snippet coverage, customization contract files, and targeted tests for routing, docs gates, and handoffs. +## Milestone 54: Apple Developer Provisioning and CloudKit Workflow + +### Status + +Completed + +### Scope + +- [x] Add a docs-first workflow for the official App Store Connect REST provisioning surface, Xcode-aware discovery, `cktool`, and CKTool JS. +- [x] Make team-key requirements, individual-key limits, Keychain/local-secret handling, short-lived JWTs, dry runs, and explicit mutation confirmation mandatory. +- [x] Keep App Groups, CloudKit container registration/assignment, Service IDs, and other undocumented configuration visibly portal-only. +- [x] Record the future interactive Apple Developer Portal Driver separately from the shipped official-API workflow. + +### Exit Criteria + +- [x] Apple Dev Skills ships `apple-developer-provisioning-workflow` with official and portal-only paths clearly separated. +- [x] The skill documents CloudKit management-token setup and safe `cktool` / CKTool JS boundaries without committing secrets. +- [x] Plugin metadata, README inventory, validator, and focused tests agree on the added skill. + +Completed Milestone 54 by shipping `apple-developer-provisioning-workflow` with plan-first App Store Connect provisioning, local Xcode discovery handoffs, Keychain-backed CloudKit guidance, account/key prerequisites, and explicit portal-only fallbacks. + ## Backlog Candidates - [ ] Record plausible future work that is not yet committed to a milestone. diff --git a/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md b/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md index 890d6297..265950db 100644 --- a/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md +++ b/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md @@ -8,8 +8,8 @@ Record the Milestone 20 audit of the current customization system, decide whethe ## Current State Summary -- The active skill surface ships `31` separate `references/customization.template.yaml` files. -- The active skill surface ships `31` separate `scripts/customization_config.py` entrypoints. +- The active skill surface ships `32` separate `references/customization.template.yaml` files. +- The active skill surface ships `32` separate `scripts/customization_config.py` entrypoints. - Those `customization_config.py` files are functionally identical and exist only because installed skills are expected to keep runtime resources inside the skill directory. - The current templates expose `21` knobs total: - `20` are documented as `runtime-enforced` @@ -30,7 +30,7 @@ Milestone 20 audited a larger surface before the implementation pass landed. - Milestone 27 applied the approved reduction so the live surface now reflects the smaller counts in the current-state summary above. - Milestone 38 later added the narrower `author-swift-docc-docs` skill with one runtime-enforced tutorial-handling knob, which is included in the current-state counts above. - The current-state counts also include `structure-swift-sources`, which now ships runtime-enforced header-policy and split-threshold knobs for the structural-cleanup workflow. -- The current-state counts now also include the no-runtime-knob `apple-ui-accessibility-workflow`, `safari-extension-control-workflow`, `devicecheck-app-attest-workflow`, `swiftui-app-architecture-workflow`, `swiftui-animation-workflow`, `sf-symbols-workflow`, `core-animation-layer-workflow`, `apple-typography-workflow`, `appkit-app-architecture-workflow`, `xcode-coding-intelligence-workflow`, `migrate-xcode-project-to-xcodegen`, `avfaudio-session-workflow`, `avaudio-engine-workflow`, `avfoundation-media-pipeline-workflow`, `coremedia-timing-samplebuffer-workflow`, and `coreaudio-modernization-repair-workflow` surfaces, all of which keep the customization-file contract without introducing runtime knobs. +- The current-state counts now also include the no-runtime-knob `apple-ui-accessibility-workflow`, `safari-extension-control-workflow`, `devicecheck-app-attest-workflow`, `apple-developer-provisioning-workflow`, `swiftui-app-architecture-workflow`, `swiftui-animation-workflow`, `sf-symbols-workflow`, `core-animation-layer-workflow`, `apple-typography-workflow`, `appkit-app-architecture-workflow`, `xcode-coding-intelligence-workflow`, `migrate-xcode-project-to-xcodegen`, `avfaudio-session-workflow`, `avaudio-engine-workflow`, `avfoundation-media-pipeline-workflow`, `coremedia-timing-samplebuffer-workflow`, and `coreaudio-modernization-repair-workflow` surfaces, all of which keep the customization-file contract without introducing runtime knobs. ## Decision diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/SKILL.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/SKILL.md new file mode 100644 index 00000000..23b8d849 --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/SKILL.md @@ -0,0 +1,129 @@ +--- +name: apple-developer-provisioning-workflow +description: Plan and safely automate officially supported Apple Developer provisioning and CloudKit workflows through the App Store Connect REST API, Xcode-aware local discovery, cktool, and CKTool JS. Use when inspecting or changing bundle IDs, capabilities, certificates, devices, provisioning profiles, CloudKit schemas, or sandbox test data while keeping portal-only configuration, credentials, dry runs, and confirmation gates explicit. +--- + +# Apple Developer Provisioning Workflow + +## Purpose + +Guide Apple Developer provisioning and CloudKit work without treating the Developer Portal as an undocumented API. The supported automation path is the App Store Connect REST provisioning API for bundle IDs, capabilities, certificates, devices, and profiles; `xcrun cktool` or CKTool JS for existing CloudKit containers; and local Xcode or `xcrun mcpbridge` discovery when a project-aware view improves safety. + +The workflow never commits a `.p8` key, CloudKit token, JWT, profile payload, or local signing material. It plans first, reads state before changing it, uses short-lived JWTs only in the invoking process, and requires an explicit confirmation immediately before every create, update, revoke, delete, reset, or schema/data apply operation. + +## When To Use + +- Use this skill for App Store Connect API provisioning work involving registered bundle IDs, supported capabilities, certificates, devices, and provisioning profiles. +- Use this skill for a plan or dry run that compares an Xcode project’s bundle IDs, entitlements, signing settings, and installed profiles with Apple’s current state. +- Use this skill for existing CloudKit container schema export, schema apply, sandbox reset, or test-data workflows through `xcrun cktool` or the TypeScript-ready CKTool JS packages. +- Use this skill when deciding whether an action remains official REST/CLI automation or must be completed in the Apple Developer Portal. +- Recommend `xcode-build-run-workflow` for target edits, entitlements, signing settings, build, device, or profile-install follow-through. +- Recommend `xcode-coding-intelligence-workflow` for a running Xcode session or external access through `xcrun mcpbridge`. +- Recommend `explore-apple-swift-docs` when current Apple documentation is the primary need. + +## Single-Path Workflow + +1. Establish the account and credential boundary: + - confirm the selected team’s program type before choosing an API: this workflow’s App Store Connect path requires Apple Developer Program access; Apple Developer Enterprise Program accounts use Apple’s separate Enterprise Program API and must not be treated as App Store Connect team-key users; + - for App Store Connect provisioning endpoints, require a **team** API key with the least sufficient role; individual API keys cannot use provisioning endpoints. The Account Holder must request API access, and an Account Holder or Admin generates the team key; + - retain the issuer ID, key ID, and downloaded `.p8` private key only in local secret storage such as Keychain or an approved local secret manager; never place them in the repo, project settings, CI logs, shell history, or agent transcript; + - create a short-lived JWT locally for one invocation and avoid writing it to disk; + - for CloudKit, separately obtain a CloudKit management token from CloudKit Console and save it through `xcrun cktool save-token --type management` so macOS Keychain owns it. +2. Discover before mutating: + - inspect the project’s bundle identifiers, entitlements, signing configuration, and existing profiles through Xcode-local tools or `xcrun mcpbridge` when Xcode is open; + - list the matching App Store Connect bundle IDs, capabilities, certificates, devices, and profiles with read-only REST requests; + - export the current CloudKit schema and identify the team ID, container ID, and development (Sandbox) versus production environment before selecting `cktool` or CKTool JS; + - return a plan with exact proposed requests, affected IDs, expected profile relationships, and portal-only steps. +3. Classify each requested operation: + - official REST: registered bundle IDs, supported bundle-ID capabilities, certificates, devices, provisioning profiles, and their documented relationships; + - official local CloudKit: schema export/apply, sandbox reset, and test-data work on an already registered container via `cktool` or CKTool JS; + - portal-only: App Group registration or assignment, CloudKit container registration or assignment to an App ID, Service ID registration, and any identifier/capability action absent from the current REST resource set; + - unsupported or unclear: stop, preserve the plan, and link to the exact portal surface instead of guessing an endpoint. +4. Require confirmation for mutations: + - show the exact create, update, revoke, delete, reset, schema-apply, or test-data command/request and name every affected team, identifier, certificate, device, profile, container, environment, and destructive consequence; + - default to dry-run/plan output; do not use a broad "yes" captured earlier in the conversation; + - re-read server state after a mutation and report the resulting IDs without emitting secrets. +5. Choose the CloudKit adapter deliberately: + - use `xcrun cktool` for interactive local, one-off, Keychain-backed work; + - use CKTool JS in a TypeScript/pnpm project when schema or test-data operations belong in typed integration automation. Use `@apple/cktool.database` with `@apple/cktool.target.nodejs`, inject the management token from local secret storage at runtime, and never commit it; + - treat production schema deployment, schema resets, and data deletion as high-impact operations requiring a separate explicit confirmation and a backup/export plan. +6. Hand off project mutation and validation: + - make entitlement and signing changes through Xcode-aware workflows rather than hand-editing project state; + - regenerate or download a profile only after its certificate, device, and bundle-ID inputs are confirmed; + - validate with the narrowest appropriate build/signing or CloudKit sandbox test after state is updated. + +## Inputs + +- `request`: desired provisioning or CloudKit outcome. +- `team_context`: Apple Developer team ID, program type, and account role, when available. +- `project_context`: Xcode project/workspace, target, bundle ID, entitlements, and signing mode. +- `operation_mode`: `inspect`, `plan`, `apply`, `portal-only`, or `unknown`; default is `plan`. +- `cloudkit_context`: existing container ID, environment, schema/data intent, and whether `cktool` or a TypeScript integration harness is preferred. +- Defaults: + - read-only discovery and a concrete plan precede every mutation; + - secrets stay local and short-lived; + - an explicit current confirmation is required for mutation; + - portal-only operations stay portal-only until Apple documents an official API. + +## Outputs + +- `status` + - `success`: supported read-only discovery or a confirmed, verified mutation completed. + - `plan`: the supported requests and portal tasks are ready, but no mutation has occurred. + - `portal-only`: the requested configuration needs Apple Developer Portal interaction. + - `blocked`: account role, team key, CloudKit token, project evidence, docs, or confirmation is missing. +- `path_type` + - `app-store-connect-provisioning`, `xcode-discovery`, `cloudkit-cktool`, `cloudkit-js`, `portal-only`, or `handoff`. +- `output` + - documented Apple behavior relied on; + - account/key prerequisites and the local-secret boundary; + - discovered state and an ordered dry-run plan; + - exact mutation confirmation prompt when applicable; + - portal-only tasks and a safe handoff; + - validation evidence and remaining manual gaps. + +## Guards and Stop Conditions + +- Do not use an individual API key for provisioning endpoints; Apple documents that individual keys cannot use them. +- Do not commit, paste, log, or transmit `.p8` private keys, App Store Connect JWTs, CloudKit management tokens, user tokens, profile contents, certificate private keys, or Keychain exports. +- Do not create a persistent JWT, store an API key in an entitlement, or expose it to a client app. +- Do not represent App Group registration/assignment, CloudKit container registration/assignment, Service IDs, or undocumented portal configuration as supported REST automation. +- Do not create an App Store app record or upload a build through this provisioning workflow; those remain website/Xcode/Transporter operations as Apple documents. +- Do not reset a CloudKit schema, apply a production schema, delete a certificate/profile/device, revoke a key, or alter test data without an operation-specific confirmation after the plan is shown. +- Do not make Xcode project or entitlement edits without routing through `xcode-build-run-workflow`. +- Stop with `blocked` when an Enterprise Program account needs its separate API, or the chosen App Store Connect team lacks an eligible team API key, required role, CloudKit management token, a confirmed target/container/environment, or fresh mutation confirmation. + +## Fallbacks and Handoffs + +- Recommend `xcode-build-run-workflow` for entitlement, signing, target, build, profile-install, simulator, or device validation work. +- Recommend `xcode-coding-intelligence-workflow` for Xcode-hosted inspection or external access through `xcrun mcpbridge`; it owns the running-Xcode and permissions boundary. +- Recommend `explore-apple-swift-docs` for current App Store Connect, Xcode, CloudKit, or entitlement documentation lookup. +- Use the Apple Developer Portal manually for App Groups, CloudKit-container registration or App-ID assignment, Service IDs, and any capability configuration Apple does not expose through the current REST API. +- Recommend `references/snippets/apple-xcode-project-core.md` when a repository needs durable Xcode-project guidance for entitlements, signing, and project-integrity follow-through. + +## Customization + +Use `references/customization-flow.md`. + +This workflow intentionally has no runtime-enforced mutation override. Customization can record a preferred discovery mode and CloudKit adapter, but it cannot bypass plan-first behavior, local-secret handling, portal-only classification, or explicit per-operation confirmation. + +## References + +### Workflow References + +- `references/app-store-connect-provisioning.md` +- `references/cloudkit-automation.md` +- `references/portal-only-configuration.md` +- `references/customization-flow.md` + +### Support References + +- [App Store Connect API provisioning overview](https://developer.apple.com/app-store-connect/api/) documents the REST surface for bundle IDs, certificates, devices, and profiles. +- [Creating API keys](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api) documents team versus individual key limits and JWT authorization. +- [Using cktool](https://developer.apple.com/icloud/ck-tool/) documents management-token setup and Keychain-backed local use. +- [CKTool JS](https://developer.apple.com/documentation/cktooljs/) documents the official JavaScript client modules for CloudKit automation. +- Recommend `references/snippets/apple-xcode-project-core.md` for reusable Xcode project policy. + +### Script Inventory + +- `scripts/customization_config.py` diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/agents/openai.yaml b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/agents/openai.yaml new file mode 100644 index 00000000..820c71ec --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Apple Developer Provisioning Workflow" + short_description: "Plan safe Apple provisioning and CloudKit work" + default_prompt: "Use $apple-developer-provisioning-workflow to inspect or plan Apple Developer provisioning or CloudKit work. Start with current Apple docs and read-only discovery, keep App Store Connect team API keys and CloudKit management tokens local, distinguish official REST or cktool/CKTool JS operations from portal-only work, and require an explicit confirmation before mutations. Hand off project edits to $xcode-build-run-workflow and Xcode bridge setup to $xcode-coding-intelligence-workflow." diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/app-store-connect-provisioning.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/app-store-connect-provisioning.md new file mode 100644 index 00000000..45b775d8 --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/app-store-connect-provisioning.md @@ -0,0 +1,29 @@ +# App Store Connect Provisioning + +## Supported Automation Boundary + +The [App Store Connect API provisioning overview](https://developer.apple.com/app-store-connect/api/) documents automation for registered bundle IDs, supported capabilities, certificates, devices, and provisioning profiles. The workflow starts with list/read requests, filters to the selected team and project identifier, then produces an explicit request plan before any change. + +The profile resource combines a bundle ID with certificates and, where applicable, devices; Apple documents create, delete, download, and relationship endpoints in [Profiles](https://developer.apple.com/documentation/appstoreconnectapi/profiles). Do not regenerate a profile blindly: first identify the certificate, device list, profile type, and target bundle ID it would replace or affect. + +## Account Setup + +For provisioning REST endpoints, use a **team API key**, not an individual key. Apple documents that individual API keys cannot access provisioning endpoints in [Creating API Keys](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api). The Account Holder must first request App Store Connect API access; an Account Holder or Admin can generate a team key and assigns its role. Team keys apply across all apps, so choose the least privileged role that permits the planned operation. + +Do not use this App Store Connect key path for Apple Developer Enterprise Program accounts: Apple directs those accounts to the separate Enterprise Program API, and team API keys are unavailable in that program. For portal-only identifier work, Apple documents Account Holder or Admin as the required role for [App ID registration](https://developer.apple.com/help/account/identifiers/register-an-app-id/) and [App Group registration](https://developer.apple.com/help/account/identifiers/register-an-app-group/). Individual enrolments can grant App Store Connect access to additional people, but those people are not Apple Developer Program team members and do not gain Certificates, Identifiers & Profiles access. + +Store the one-time-downloaded `.p8` private key locally, along with its key ID and issuer ID. Generate the JWT in memory for the current invocation; it is authorization material, not a project configuration value. Apple advises revocation if a private key is lost or compromised. + +Individual enrollment has an additional constraint: people added in App Store Connect receive App Store Connect access but are not members of the Apple Developer Program team, so they do not receive Certificates, Identifiers & Profiles access. See Apple’s [roles reference](https://developer.apple.com/help/account/access/roles/). + +## Plan Contract + +Before a write, return: + +- selected team and expected role/key type; +- matched project bundle ID and App Store Connect resource ID; +- read-only current certificates, devices, capabilities, and profiles; +- exact resource operation and the user-visible consequence; +- validation that will run after the requested change. + +For a create, update, revoke, or delete, show the exact resource name and identifier and ask for an operation-specific confirmation. Do not silently substitute a different team or similarly named bundle ID. diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/cloudkit-automation.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/cloudkit-automation.md new file mode 100644 index 00000000..89aaeda2 --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/cloudkit-automation.md @@ -0,0 +1,32 @@ +# CloudKit Automation + +## Existing Container Boundary + +`cktool` and CKTool JS automate schema and data workflows for an existing CloudKit container. They do not make Apple Developer Portal registration or app-ID assignment automatable. Confirm the team ID, container ID, development (Sandbox) or production environment, and intended schema/data operation before invoking either official surface. + +## cktool + +Apple distributes [`cktool`](https://developer.apple.com/icloud/ck-tool/) with Xcode. It is stateless for CloudKit API calls and stores management and user tokens securely in macOS Keychain. Generate a management token in CloudKit Console’s account Settings, save it once with: + +```zsh +xcrun cktool save-token --type management +``` + +Use it for a read-first sequence: inspect help, export the current schema, review the artifact outside versioned secrets, and only then plan a schema apply or sandbox reset. `reset-schema` reverts the development schema to the production definition, so it is destructive and requires a fresh confirmation. + +## CKTool JS + +[CKTool JS](https://developer.apple.com/documentation/cktooljs/) is Apple’s official JavaScript client alternative to `cktool`, intended for local development and integration tests. In a TypeScript project, use pnpm and add the official packages: + +```zsh +pnpm add @apple/cktool.database @apple/cktool.target.nodejs +``` + +At runtime, create the Node configuration and pass a management token from local secret storage to `PromisesApi`; never put the token in source, a lockfile, fixture, committed `.env`, or browser bundle. Apple documents that CKTool JS supports sandbox schema application, test-data population, reset to production schema, and integration-test scripts. User-data access additionally needs a user token; do not treat a management token as user authorization. + +## Validation Contract + +- Export and review schema before a schema-affecting operation. +- Default integration automation to sandbox/development and isolated test records. +- Require an explicit operation-specific confirmation for production work, reset, schema apply, or data mutation. +- Report container/environment and changed schema/data identifiers, but never tokens or record content that contains sensitive user data. diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization-flow.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization-flow.md new file mode 100644 index 00000000..c6fb160d --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization-flow.md @@ -0,0 +1,5 @@ +# Customization Flow + +The template records non-security preferences only. `preferredDiscoveryMode` may be `xcode-local` or `rest-first`; `preferredCloudKitAdapter` may be `cktool` or `cktool-js`. + +Preferences do not authorize mutation, relax secret handling, convert a portal-only operation into API work, or suppress operation-specific confirmation. Store a durable customization file outside the repository through `scripts/customization_config.py`. diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization.template.yaml b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization.template.yaml new file mode 100644 index 00000000..fe64e87c --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/portal-only-configuration.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/portal-only-configuration.md new file mode 100644 index 00000000..7c85a57e --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/portal-only-configuration.md @@ -0,0 +1,20 @@ +# Portal-Only Configuration + +## Honest Boundary + +The App Store Connect API’s documented provisioning resources cover bundle IDs, capabilities, certificates, devices, and profiles. It does not make every Apple Developer Portal form programmable. A missing resource or relationship in the current official API is a portal task, not an invitation to reverse engineer the website. + +Keep these operations explicitly portal-only unless Apple publishes a supported API for the exact action: + +- registering an App Group and assigning it to an App ID; +- registering a CloudKit container and assigning it to an App ID; +- registering Service IDs and completing their related configuration; +- portal-only service configuration or capability options that have no documented REST resource. + +## Portal Handoff + +The plan should name the selected team, exact identifier/container, expected entitlement or service effect, and any follow-on profile regeneration. The user completes the portal step interactively, then the workflow re-runs read-only discovery, verifies the project configuration, and only creates or refreshes the profile after an explicit confirmation. + +## Future Portal Driver + +An interactive Apple Developer Portal Driver may eventually improve accessible navigation and evidence capture, but it must not bypass Apple authentication, two-factor authentication, account selection, or destructive-change confirmation. Until then, preserve the manual portal step and do not implement browser automation in this workflow. diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..56f3f97a --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,133 @@ +# Apple Xcode Project Core AGENTS Snippet + +Use this snippet in repository `AGENTS.md` files when you want baseline standards for an existing native Apple app project managed through Xcode. + +## General Swift Baseline + +- For any Swift, Apple-framework, Apple-platform, SwiftUI, SwiftData, Observation, AppKit, UIKit, Foundation-on-Apple, or Xcode-related task, read the relevant Apple documentation first before planning, proposing, or making changes. +- Use Xcode MCP `DocumentationSearch` or Xcode-local documentation first for Apple-owned SDK, framework, lifecycle, and Xcode behavior; use Dash MCP or Dash HTTP next when installed local package docs or multi-ecosystem docs are a better fit; use open source Swift project repositories, generated DocC, or release notes when the relevant Swift package or tool is open source and available there; use official Apple web docs only when the page content is actually readable through a capable source. Generic no-JS web search/open results, snippets, metadata shells, or bare Apple Developer URLs are not enough evidence that Apple docs were read. +- Before proposing an architecture or implementation, state the documented API behavior, lifecycle rule, or workflow requirement being relied on. +- Do not rely on memory, habit, or analogy as the primary source when Apple documentation exists. +- If Apple documentation and the current code disagree, stop and report the conflict before continuing. +- If no relevant Apple documentation can be found, say that explicitly before proceeding. +- Prefer the simplest correct Swift that is easiest to read, reason about, and maintain. +- Treat idiomatic Swift, Cocoa conventions, and modern Swift features as tools in service of readability, not as goals by themselves. +- Do not add ceremony, abstraction, or boilerplate just to make code look more architectural, more generic, or more "Swifty". +- Strongly prefer synthesized, implicit, and framework-provided behavior over custom code. +- Prefer synthesized conformances (`Codable`, `Equatable`, `Hashable`, etc.) whenever they satisfy the actual requirements. +- Prefer memberwise and otherwise synthesized initializers, default property values, and framework defaults over handwritten setup code. +- Do not add `CodingKeys`, manual `Codable` methods, custom initializers, wrappers, helper types, protocols, coordinators, or extra layers unless they are required by a concrete constraint or they make the final code clearly easier to understand. +- Prefer applicable existing framework or platform error types before inventing custom error wrappers or error hierarchies. +- Prefer direct, simple error flows and small focused error enums only when they materially improve understanding. +- Prefer stable, source-of-truth naming across layers when the data and meaning have not changed. +- Treat naming consistency as a reliability feature: if the same data still serves the same purpose, keep the same name. +- Do not rename fields just to match local style conventions when the external schema is already clear and stable. +- Do not use automatic case-conversion strategies such as `.convertFromSnakeCase` or `.convertToSnakeCase` unless the project explicitly wants that behavior and it clearly improves readability overall. +- When an API, cloud service, or wire format already provides clear names, preserve those names directly in Swift models and nearby code unless the meaning actually changes or a concrete collision must be resolved. +- Preserve raw wire and persistence shapes by default; do not add DTO, domain, or view-model conversion layers unless meaning actually changes or a concrete boundary requires it. +- Treat redundant wrappers, rename-and-copy layers, and duplicated logic as anti-patterns by default. +- This guidance is optimized for an advanced Swift reader and may prefer dense but readable modern Swift over beginner-style explicitness. +- Prefer explicit names that are consistent, unambiguous, and easy to scan at the call site. +- For public Swift APIs, treat streamlined, compact, ergonomic call sites as the only acceptable default; do not grow method families, overload sets, or loosely typed entry points when one clear typed API can express the operation. +- Prefer optional parameters with explicit default values over additional methods or overloads whenever the difference is optional behavior on the same operation. +- When a public function, initializer, or method reaches four or more arguments or parameters, strongly prefer a named typed `struct` request, options, or configuration value so call sites stay readable and future additions do not multiply overloads. +- Prefer enums, enum cases with associated values, and narrow typed values over strings, booleans, sentinel values, or parallel parameters whenever the domain has a closed or meaningful set of choices. +- Prefer compact syntax when it improves local reasoning, including shorthand syntax, ternary expressions, trailing closures, enums, `switch`, `map`, `filter`, `forEach`, async iteration, `AsyncSequence`, `AsyncStream`, and `AsyncAlgorithms`. +- Prefer explicit default values at initialization when they reduce optional-handling clutter and keep the code easier to follow. +- When lines, chains, or expressions get long, prefer chopping them down into a clean vertical, top-down structure with straight visual flow. +- Do not force value types by default, protocols at seams, actors by default, or other pattern slogans when a plainer concrete implementation is easier to reason about. +- Keep code compliant with Swift 6 language mode. +- Keep strict concurrency checking enabled. +- Prefer modern structured concurrency (`async`/`await`, task groups, actors) over legacy async patterns when it keeps the flow clearer and more direct. +- Make async code cancellation-aware and keep actor or task boundaries explicit instead of hiding them behind detached tasks or queue wrappers. +- Prefer clear `Sendable` boundaries for values that cross task or actor isolation, and keep unchecked sendability exceptional and justified locally. +- Prefer Swift Testing (`import Testing`) as the default test framework, and use XCTest only when a dependency or platform constraint requires it. +- Prefer Swift Testing for unit-style and package-style test surfaces in modern Xcode projects, including suites, tags, parameterized tests, and direct async tests. +- Use XCTest when the platform surface, dependency graph, or Apple tooling still expects it, and keep XCTest and Swift Testing responsibilities clearly separated when both coexist. +- Use XCUITest for UI automation, and prefer explicit element wait APIs such as `waitForExistence(timeout:)`, `waitForNonExistence(timeout:)`, and related state waits over fixed sleeps. +- Keep `.xctestplan` files versioned when test configurations, diagnostics, sanitizers, locale coverage, or selective plan execution matter, and inspect or run them explicitly with `xcodebuild -showTestPlans` and `xcodebuild -testPlan ...`. +- Prefer normal Xcode and XCTest parallel execution for ordinary Swift Testing, XCTest, and XCUITest runs when the project, scheme, destination, and test plan support it. Do not serialize regular tests just because they use Swift, XCTest, async tests, UI automation, or `.xctestplan` matrices. +- Treat tests that load large local AI or ML models, especially models over 500 million parameters, as heavy system-resource tests. Run those tests sequentially, one at a time, and call `unload_models` on Gale's live TTS service before the heavy run and `reload_models` after it ends, even when the run fails or is interrupted. +- Prefer first-party and top-tier Swift ecosystem packages from Apple, `swiftlang`, the Swift Server Work Group, and similarly trusted core Swift projects when they simplify the code and make it easier to reason about. +- Commonly approved examples include `swift-configuration` and `swift-async-algorithms` when they reduce bespoke code and improve readability. +- For Apple app projects, prefer Apple-native logging facilities first and allow Swift Logging where it makes the project API clearer. +- Prefer Swift OpenTelemetry for telemetry and instrumentation when telemetry is needed, and prefer existing ecosystem integrations over bespoke wrappers. +- Prefer a checked-in repo-root `.swiftformat` file as the default Swift formatting source of truth, and prefer a pre-commit hook that formats staged Swift sources and then verifies them with `swiftformat --lint` before commit. +- Treat SwiftLint as an optional complementary signal layer for clarity, safety, and maintainability after SwiftFormat owns formatting shape. +- Keep automation and CI commands deterministic, non-interactive, and explicit about toolchain, platform, and configuration assumptions. + +## SwiftUI and State Architecture + +- Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. +- Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. +- Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. +- Keep updates to view-driving state minimal and localized. +- Prefer durable identity for types that drive SwiftUI state and view updates. +- Treat `App` as the application entry and scene composition boundary, `Scene` as the container for scene-specific lifecycle and environment, and `View` as the component rendering layer. +- Every native app target must have exactly one app lifecycle entry point: one `@main` app type, one `main.swift`, or the platform-equivalent single launch entry. Do not add alternate app entry points, second `@main` types, duplicate `main.swift` files, target-specific app entry files, or parallel app structs for variants. When launch behavior must differ by platform, configuration, or feature flag, keep the single entry point and use Swift conditional compilation or ordinary runtime conditionals inside that boundary. +- Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. +- Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. +- Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. +- Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. +- Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. + +## Xcode Workspace and Project Baseline + +- Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for Apple platform app integration, schemes, build settings, destinations, and target membership. +- Prefer edits through Xcode-aware project structure and keep project file changes intentional and reviewed closely. +- Use the standard top-level Xcode app repository layout when creating or normalizing native app repos: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/`. +- `Sources/` owns the main app target implementation and app-owned resources/support files. `Tests/` owns all test targets. `Shared/` owns reusable source intended to be compiled into the app and extension targets. `Extensions/` owns extension target roots, one folder per extension. `Configurations/` owns `.xcconfig` layers. `Scripts/` owns project-local automation and build helper scripts. `Packages/` owns local Swift packages only when a real package boundary is justified. +- Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. +- Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. +- `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. +- `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. +- Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. +- Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. +- For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. +- When scripts or terminal workflows add files on disk, verify that Xcode project membership, target membership, build-phase membership, and resource-bundle inclusion all match the intended result; files appearing in the directory tree alone are not enough. +- Direct filesystem edits outside `.pbxproj` are generally safe when Xcode is closed or when the current project is not open in Xcode, but still verify that the Xcode project picks up the intended files and memberships afterward. +- Prefer Debug builds for everyday edit-build-test loops, but validate Release builds explicitly when optimization, packaging, launch behavior, watchdog timing, or deployment realism matters. +- Treat tagged releases as a signal to validate both the normal Debug path and a Release artifact path, and when shipping apps or deliverables test the Release behavior without relying on an attached debugger. +- Prefer direct filesystem edits in Xcode-managed scope only when the workflow already accounts for project-file and scheme integrity. +- Never edit `.pbxproj` files directly. If a project-file change is needed and no safe project-aware tool is available, stop and ask for an Xcode-mediated project change instead. When `.pbxproj` is tracked and Xcode, XcodeGen, or another project-aware workflow legitimately changes it, treat that diff as critical project state: review it, stage it, and commit it with the branch before any push, merge, release, or cleanup. + +## XcodeGen and Build Configuration Defaults + +- For new Xcode app, framework, and workspace repositories, prefer an XcodeGen-backed project by default unless the user explicitly asks for a hand-managed Xcode project or the repository has a concrete reason to avoid a generator dependency. +- If the repo contains `project.yml`, `project.yaml`, or clearly named included XcodeGen spec files, treat the XcodeGen spec set as the source of truth for generated project structure. +- For XcodeGen-backed repos, make target membership, resource membership, schemes, Swift package declarations, test-plan references, project references, build configurations, configuration-file wiring, generation options, and project-level settings in the XcodeGen specs instead of editing the generated `.pbxproj`. +- Before running `xcodegen generate`, inspect the current git diff for generated `.xcodeproj` or `.pbxproj` changes. Treat existing project-file diffs as intentional user or Xcode GUI changes by default, not disposable generator drift. +- When Xcode GUI changes added build settings, signing settings, capabilities, `Info.plist` build setting overrides, file membership, scheme changes, or entitlement wiring to `.pbxproj`, preserve the user intent by moving each intentional value to the owning tracked source first: XcodeGen spec for structure, `.xcconfig` for build settings, `.entitlements` for entitlement keys, `Info.plist` for plist keys, `.xcscheme` or scheme spec for scheme behavior, and `.xctestplan` for test-plan content. +- Only regenerate after that promotion is complete, then review the generated project diff to confirm XcodeGen preserved the intended behavior instead of deleting it. If the owning tracked file is ambiguous, stop and ask before regenerating. +- For new XcodeGen-backed app scaffolds, start from the maintained `apple-dev-skills/templates/xcodegen/` templates when available instead of inventing a fresh project-spec shape from memory. +- Keep `minimumXcodeGenVersion` on a recent validated release for new scaffolds. Prefer updating the template and validation together when the repo intentionally raises the baseline. +- For Xcode 16 or newer project formats, prefer XcodeGen `syncedFolder` roots at the broad top-level directory boundary so file creation, deletion, and organization stay synchronized between Xcode and the filesystem without hand-listing every source file in YAML. +- Do not fragment ordinary XcodeGen source roots by subdirectory. A standard app target gets one `Sources` source entry that includes all app source, resource, support, generated plist, entitlement, and nested feature folders, plus one `Shared` source entry when shared app/extension code exists. A standard test target gets one `Tests` source entry that includes all test subdirectories. Extension targets use one `Extensions/` source entry per extension target. If a project has another separate top-level logical root, use one top-level entry for that root, not one entry per child folder. +- Never split `Sources/App`, `Sources/Resources`, `Sources/Support`, feature folders, or `Tests/Tests` into separate XcodeGen source entries unless a specific non-ordinary file or folder truly needs custom compiler flags, build-phase routing, destination filters, or target membership that cannot be represented from the broad root. +- If `syncedFolder` behaves poorly for a repo, fall back to the same broad top-level recursive paths such as `Sources`, `Tests`, or `Resources` with explicit `includes` and `excludes`; do not fall back to subdirectory-level fragmentation or one YAML entry per ordinary source file. +- Keep XcodeGen specs readable as project structure, not as a dumping ground for every build setting. Use `configs`, `configFiles`, `targets`, `schemes`, `packages`, `projectReferences`, `targetTemplates`, and `schemeTemplates` deliberately so future edits have an obvious owner. +- Prefer explicit top-level schemes for app scaffolds once scheme behavior matters. Put build, run, test, profile, analyze, archive, environment variables, command-line arguments, and test-plan references in the scheme spec rather than relying on hidden generated defaults. +- Prefer external `.xcconfig` files as the default home for nontrivial build settings. Keep build settings in XcodeGen inline settings only when they are small, local, and clearer there. +- Use `.xcconfig` files for settings that vary by Debug, Release, CI, local development, signing, bundle identity, compiler flags, Swift settings, deployment variants, or environment-specific behavior. +- Keep configuration layering explicit. Prefer a small shared base config, target-level configs for app/test/extension identity, then per-configuration configs that include the narrower target config and override only what changes. +- In XcodeGen specs, wire build configurations to their matching `.xcconfig` files instead of duplicating the same settings across generated project objects. +- Prefer checked-in external `.entitlements` files for app, extension, and capability-bearing targets, with `CODE_SIGN_ENTITLEMENTS` declared in the owning target's `.xcconfig`. Let Xcode capabilities update the entitlement plist when possible, then review and commit the entitlement diff; keep XcodeGen responsible for wiring the file, not regenerating its contents from inline YAML. +- Do not assume Xcode's Build Settings UI writes edited values back into `.xcconfig` files. When a build setting should remain tracked in `.xcconfig`, inspect the generated project diff after GUI changes and move intentional build-setting overrides from `.pbxproj` back into the owning `.xcconfig` before regenerating. +- Keep secrets, personal team IDs, local machine paths, provisioning profiles, API tokens, and private signing material out of committed `.xcconfig` files. Use build settings only for non-secret configuration values, safe placeholders, references to externally supplied values, or local developer placeholders that are safe to commit. +- Before changing generated project structure, inspect the root spec plus any `include` entries so the edit lands in the owning spec rather than duplicating settings in the wrong file. Remember that included specs merge into the root spec, and local overrides may intentionally replace arrays or maps. +- After changing XcodeGen specs, `.xcconfig` files, or entitlement-file wiring, run `xcodegen generate` from the spec root, or `xcodegen generate --spec ` when the project uses a non-default spec path. +- If the spec uses environment variables or generation hooks, preserve and document the required environment before regenerating so CI and other contributors can reproduce the project. +- Review the spec diff, `.xcconfig` diff, and generated `.xcodeproj` diff after regeneration. Generated `.pbxproj` changes are acceptable output when they come from XcodeGen, but they should still be reviewed for unintended target, scheme, signing, package, build-setting, or file-membership churn. +- Validate regenerated projects with explicit `xcodebuild` commands for the affected scheme, destination or SDK, and configuration. +- For existing hand-managed Xcode projects, do not migrate to XcodeGen or externalize build settings into `.xcconfig` files unless the user explicitly asks for that migration. When they do, treat it as a project-structure migration with before/after validation. diff --git a/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/scripts/customization_config.py b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/scripts/customization_config.py new file mode 100644 index 00000000..75ba7a08 --- /dev/null +++ b/plugins/apple-dev-skills/skills/apple-developer-provisioning-workflow/scripts/customization_config.py @@ -0,0 +1,105 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = ["PyYAML>=6.0.2,<7"] +# /// +"""Load and persist safe per-skill customization preferences.""" + +from __future__ import annotations + +import argparse +import os +from pathlib import Path + +import yaml + +SKILL_NAME = "apple-developer-provisioning-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_SETTINGS = {"preferredDiscoveryMode", "preferredCloudKitAdapter"} + + +def fail(message: str) -> None: + raise SystemExit(f"ERROR: Apple Developer Provisioning Workflow customization: {message}") + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def durable_path() -> Path: + root = Path(os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT)).expanduser() + return root / SKILL_NAME / "customization.yaml" + + +def load(path: Path, *, required: bool) -> dict: + if not path.exists(): + if required: + fail(f"missing customization template at {path}") + return {} + try: + value = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except yaml.YAMLError as error: + fail(f"invalid YAML in {path}: {error}") + if not isinstance(value, dict): + fail(f"top-level YAML must be a mapping in {path}") + if set(value) - {"schemaVersion", "isCustomized", "settings"}: + fail(f"unknown top-level keys in {path}") + settings = value.get("settings") or {} + if not isinstance(settings, dict) or set(settings) - ALLOWED_SETTINGS: + fail(f"settings in {path} must contain only {sorted(ALLOWED_SETTINGS)}") + return value + + +def effective() -> dict: + base = load(template_path(), required=True) + overlay = load(durable_path(), required=False) + settings = dict(base.get("settings") or {}) + settings.update(overlay.get("settings") or {}) + settings.setdefault("preferredDiscoveryMode", "xcode-local") + settings.setdefault("preferredCloudKitAdapter", "cktool") + result = { + "schemaVersion": 1, + "isCustomized": bool(overlay) or bool(base.get("isCustomized", False)), + "settings": settings, + } + if result["settings"].get("preferredDiscoveryMode") not in {"xcode-local", "rest-first"}: + fail("preferredDiscoveryMode must be xcode-local or rest-first") + if result["settings"].get("preferredCloudKitAdapter") not in {"cktool", "cktool-js"}: + fail("preferredCloudKitAdapter must be cktool or cktool-js") + return result + + +def write(config: dict) -> None: + path = durable_path() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(yaml.safe_dump(config, sort_keys=False), encoding="utf-8") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Inspect or persist safe workflow preferences.") + commands = parser.add_subparsers(dest="command", required=True) + commands.add_parser("path") + commands.add_parser("effective") + set_command = commands.add_parser("set") + set_command.add_argument("--discovery-mode", choices=["xcode-local", "rest-first"]) + set_command.add_argument("--cloudkit-adapter", choices=["cktool", "cktool-js"]) + args = parser.parse_args() + + if args.command == "path": + print(durable_path()) + elif args.command == "effective": + print(yaml.safe_dump(effective(), sort_keys=False), end="") + else: + config = effective() + if args.discovery_mode: + config["settings"]["preferredDiscoveryMode"] = args.discovery_mode + if args.cloudkit_adapter: + config["settings"]["preferredCloudKitAdapter"] = args.cloudkit_adapter + config["isCustomized"] = True + write(config) + print(yaml.safe_dump(config, sort_keys=False), end="") + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py b/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py new file mode 100644 index 00000000..839dd5e6 --- /dev/null +++ b/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +class AppleDeveloperProvisioningWorkflowTests(unittest.TestCase): + def read(self, relative_path: str) -> str: + return (ROOT / relative_path).read_text(encoding="utf-8") + + def test_skill_distinguishes_supported_and_portal_only_operations(self) -> None: + skill = self.read("skills/apple-developer-provisioning-workflow/SKILL.md") + portal = self.read("skills/apple-developer-provisioning-workflow/references/portal-only-configuration.md") + + self.assertIn("registered bundle IDs, supported capabilities, certificates, devices, and provisioning profiles", skill) + self.assertIn("App Group registration or assignment", skill) + self.assertIn("CloudKit container registration or assignment", skill) + self.assertIn("Service ID registration", skill) + self.assertIn("not an invitation to reverse engineer the website", portal) + + def test_skill_requires_safe_credentials_planning_and_confirmation(self) -> None: + skill = self.read("skills/apple-developer-provisioning-workflow/SKILL.md") + provisioning = self.read("skills/apple-developer-provisioning-workflow/references/app-store-connect-provisioning.md") + + self.assertIn("individual API keys cannot use provisioning endpoints", skill) + self.assertIn("Enterprise Program accounts use Apple’s separate Enterprise Program API", skill) + self.assertIn("short-lived JWT", skill) + self.assertIn("requires an explicit confirmation immediately before every", skill) + self.assertIn("never place them in the repo", skill) + self.assertIn("team API key", provisioning) + self.assertIn("team API keys are unavailable in that program", provisioning) + + def test_cloudkit_paths_remain_local_and_token_safe(self) -> None: + skill = self.read("skills/apple-developer-provisioning-workflow/SKILL.md") + cloudkit = self.read("skills/apple-developer-provisioning-workflow/references/cloudkit-automation.md") + + self.assertIn("xcrun cktool save-token --type management", skill) + self.assertIn("@apple/cktool.database", skill) + self.assertIn("@apple/cktool.target.nodejs", cloudkit) + self.assertIn("pnpm add", cloudkit) + self.assertIn("never put the token in source", cloudkit) + + def test_inventory_metadata_and_roadmap_are_updated(self) -> None: + readme = self.read("README.md") + plugin = self.read(".codex-plugin/plugin.json") + validator = self.read(".github/scripts/validate_repo_docs.sh") + roadmap = self.read("ROADMAP.md") + + self.assertIn("apple-developer-provisioning-workflow", readme) + self.assertIn("Apple Developer provisioning", plugin) + self.assertIn("./skills/apple-developer-provisioning-workflow/SKILL.md", validator) + self.assertIn("Expected exactly 32 active skills", validator) + self.assertIn("Milestone 54: Apple Developer Provisioning and CloudKit Workflow - Completed", roadmap) + + +if __name__ == "__main__": + unittest.main() diff --git a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py b/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py index dee98132..88970aee 100644 --- a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py +++ b/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py @@ -66,8 +66,8 @@ def test_review_doc_counts_match_live_customization_surface(self) -> None: knob_count = _count_template_knobs() runtime_enforced, policy_only = _count_statuses() - self.assertEqual(template_count, 31) - self.assertEqual(script_count, 31) + self.assertEqual(template_count, 32) + self.assertEqual(script_count, 32) self.assertEqual(knob_count, 21) self.assertEqual(runtime_enforced, 20) self.assertEqual(policy_only, 1) diff --git a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py index 040d9950..4d23274a 100644 --- a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py +++ b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py @@ -71,7 +71,7 @@ def test_plugin_inventory_includes_devicecheck_workflow(self) -> None: self.assertIn("DeviceCheck", plugin) self.assertIn("App Attest", plugin) self.assertIn("./skills/devicecheck-app-attest-workflow/SKILL.md", validator) - self.assertIn("Expected exactly 31 active skills", validator) + self.assertIn("Expected exactly 32 active skills", validator) self.assertIn("Milestone 53: DeviceCheck and App Attest Workflow - Completed", roadmap)