diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 760f9d4e..8d271630 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,11 +9,11 @@ { "name": "agent-portability-skills", "source": "./plugins/agent-portability-skills", "description": "Cross-host agent-skill and plugin portability workflows.", "category": "developer-tools", "tags": ["skills", "portability"], "strict": false }, { "name": "android-dev-skills", "source": "./plugins/android-dev-skills", "description": "Android, Kotlin, Java, Gradle, testing, and release workflows.", "category": "developer-tools", "tags": ["android", "skills"], "strict": false }, { "name": "apple-creator-studio-skills", "source": "./plugins/apple-creator-studio-skills", "description": "Apple Creator Studio workflows for production and delivery.", "category": "productivity", "tags": ["apple", "creative"], "strict": false }, - { "name": "apple-dev-skills", "source": "./plugins/apple-dev-skills", "description": "Apple, SwiftPM extension, Swift, Xcode, and platform-development workflows.", "category": "developer-tools", "tags": ["apple", "swift", "swiftpm", "xcode"], "mcpServers": "./.mcp.json", "strict": false }, + { "name": "apple-dev-skills", "source": "./plugins/apple-dev-skills", "description": "Apple, SwiftPM, Xcode, and macOS or Linux virtualization workflows.", "category": "developer-tools", "tags": ["apple", "swift", "swiftpm", "xcode", "virtualization"], "mcpServers": "./.mcp.json", "strict": false }, { "name": "cardhop-app", "source": "./plugins/cardhop-app", "description": "Cardhop contact workflows with a Claude Code local MCP server.", "category": "productivity", "tags": ["contacts", "macos", "local-mcp"], "mcpServers": "./claude.mcp.json", "strict": false }, { "name": "cloud-deployment-skills", "source": "./plugins/cloud-deployment-skills", "description": "Cloud deployment routing and provider integration workflows.", "category": "developer-tools", "tags": ["cloud", "deployment"], "strict": false }, { "name": "cloud-inference-skills", "source": "./plugins/cloud-inference-skills", "description": "Cloud AI inference, training, conversion, and GPU workflows.", "category": "developer-tools", "tags": ["ai", "cloud", "mcp"], "mcpServers": "./.mcp.json", "strict": false }, - { "name": "cybersecurity-skills", "source": "./plugins/cybersecurity-skills", "description": "Security analysis, incident response, and defensive workflow skills.", "category": "developer-tools", "tags": ["security", "skills"], "strict": false }, + { "name": "cybersecurity-skills", "source": "./plugins/cybersecurity-skills", "description": "Security analysis, isolated lab, incident response, and defensive workflows.", "category": "developer-tools", "tags": ["security", "analysis-lab", "skills"], "strict": false }, { "name": "dotnet-skills", "source": "./plugins/dotnet-skills", "description": ".NET, F#, C#, ASP.NET Core, and tooling workflows.", "category": "developer-tools", "tags": ["dotnet", "csharp", "fsharp"], "strict": false }, { "name": "game-dev-skills", "source": "./plugins/game-dev-skills", "description": "Apple game-development workflows for Metal, SpriteKit, SceneKit, and gameplay systems.", "category": "developer-tools", "tags": ["games", "apple", "metal"], "strict": false }, { "name": "messaging-collaboration-skills", "source": "./plugins/messaging-collaboration-skills", "description": "Messaging, collaboration, communication, and meeting workflows.", "category": "productivity", "tags": ["messaging", "collaboration"], "strict": false }, @@ -24,7 +24,7 @@ { "name": "reverse-engineering-skills", "source": "./plugins/reverse-engineering-skills", "description": "Artifact triage, binary analysis, and reproducible evidence workflows.", "category": "developer-tools", "tags": ["reverse-engineering", "security"], "strict": false }, { "name": "rust-skills", "source": "./plugins/rust-skills", "description": "Rust, Cargo, crate, test, CI, and package workflows.", "category": "developer-tools", "tags": ["rust", "cargo"], "strict": false }, { "name": "server-side-jvm", "source": "./plugins/server-side-jvm", "description": "Java, Scala, JVM service, build, and testing workflows.", "category": "developer-tools", "tags": ["java", "scala", "jvm"], "strict": false }, - { "name": "server-side-swift", "source": "./plugins/server-side-swift", "description": "Vapor, Hummingbird, SwiftNIO, and server-side Swift workflows.", "category": "developer-tools", "tags": ["swift", "server"], "strict": false }, + { "name": "server-side-swift", "source": "./plugins/server-side-swift", "description": "Vapor, Hummingbird, SwiftNIO, Docker, and Apple container workflows.", "category": "developer-tools", "tags": ["swift", "server", "containers"], "strict": false }, { "name": "swift-lang", "source": "./plugins/swift-lang", "description": "Shared Swift language, syntax, compiler, semantic indexing, LSP, formatting, and modernization workflows.", "category": "developer-tools", "tags": ["swift", "language", "tooling"], "strict": false }, { "name": "swiftasb-skills", "source": "./plugins/swiftasb-skills", "description": "SwiftASB integration and application-development workflows.", "category": "developer-tools", "tags": ["swift", "swiftasb"], "strict": false }, { "name": "things-app", "source": "./plugins/things-app", "description": "Things planning and reminder workflows with a Claude Code local MCP server.", "category": "productivity", "tags": ["things", "macos", "local-mcp"], "mcpServers": "./claude.mcp.json", "strict": false }, diff --git a/README.md b/README.md index 416b845a..59377e6a 100644 --- a/README.md +++ b/README.md @@ -148,11 +148,11 @@ Current Socket catalog shape: - `agent-portability-skills`: maintainer skills plus a source-bundled guidance-sync custom-agent definition for Socket-owned agent skill portability, Codex plugin surfaces, and host adapter guidance - `android-dev-skills`: Android, Kotlin, Java, Gradle, Android Gradle Plugin, Compose/XML UI, testing, lint, emulator-aware validation handoff, and release-readiness workflow guidance - `apple-creator-studio-skills`: source-preserving Final Cut Pro editing, Motion template, Compressor delivery, Logic Pro production, MainStage concert, and GarageBand project workflows with local Help Viewer discovery, explicit Computer Use safeguards, and artifact or rehearsal verification -- `apple-dev-skills`: Apple, Swift, SwiftPM package plugins/macros/traits, Core Image and Image I/O, Vision and Core ML recognition, AVFoundation camera and depth capture, ARKit spatial sensing, VideoToolbox and Core Video codecs, PhotosUI and PhotoKit, AVFAudio, Core Media, Core Audio, SwiftUI, AppKit, Xcode, Safari, OpenAPI, and DocC workflows, plus the source-bundled `swift-steward` custom-agent definition with its own roadmap +- `apple-dev-skills`: Apple, Swift, SwiftPM, macOS-hosted boundary selection, custom Virtualization framework hosts, persistent Linux development guests, clean macOS development guests, imaging, Vision/Core ML, camera, spatial sensing, media/audio, SwiftUI, AppKit, Xcode, Safari, OpenAPI, and DocC workflows, plus the source-bundled `swift-steward` custom-agent definition with its own roadmap - `cardhop-app`: mixed skill plus bundled MCP server for Cardhop.app contact workflows - `cloud-deployment-skills`: cloud provider deployment routing, official provider plugin selection, credential and mutation boundary checks, and AWS handoff to the official AWS Agent Toolkit rather than duplicated AWS MCP, CLI, or SAM setup - `cloud-inference-skills`: cloud AI inference, training, model conversion, and GPU infrastructure routing for Runpod, Hugging Face, AWS, Vast.ai, CoreWeave, and similar providers, with bundled Runpod MCP server configuration, upstream Runpod skill mirrors, and first-party Hugging Face/AWS handoffs -- `cybersecurity-skills`: suspicious-content triage, evidence preservation, malware analysis, isolation, agentic security-tool controls, macOS investigation and defense, vulnerability validation, authorized web/API and network testing, incident response, threat hunting, detection content, and clear non-specialist advice +- `cybersecurity-skills`: suspicious-content triage, evidence preservation, isolation selection, disposable Linux and macOS analysis-lab preparation, malware analysis, agentic security-tool controls, macOS investigation and defense, vulnerability validation, authorized testing, incident response, threat hunting, detection content, and clear non-specialist advice - `messaging-collaboration-skills`: chat-app, bot, business-messaging, meeting-collaboration, iMessage collaboration, Communication Notifications, Push to Talk, VoIP/SIP, documented iOS/iPadOS default communication roles, and app-owned macOS client workflows for Discord, Telegram, Slack, Teams, WhatsApp Business, SMS/MMS/RCS, Google Meet, and Apple communication surfaces, with explicit Signal and Mac operator-automation boundaries - `model-lab-skills`: reproducible language-model experiment design, dataset preparation, fine-tuning, evaluation, checkpoint comparison, representation and steering research, refusal ablation, authorized jailbreak and tool-calling evaluation, runtime benchmarking, and current-source routing across Core AI, Core ML, MLX, ExecuTorch, and Foundation Models - `agentdeck`: local Codex runtime utilities, starting with hooks that prefix generated Codex thread titles with the project directory name @@ -163,7 +163,7 @@ Current Socket catalog shape: - `python-skills`: Python runtime and tooling workflows for Python-based projects; see the [Python skills expansion plan](./docs/maintainers/python-skills-plugin-plan.md) for maintainer details - `reverse-engineering-skills`: artifact triage, preservation, exact-build comparison, decompiler review, Apple Mach-O/runtime/signing/Apple Silicon/dyld/dynamic/kernel research, Cutter/Rizin, Malimite, Ghidra, Hopper, .NET, Unity and IL2CPP, and reproducible security evidence workflows - `server-side-jvm`: server-side JVM, Java, Scala, Gradle, Maven, SBT, and testing workflow guidance, with future Clojure support planned -- `server-side-swift`: server-side Swift bootstrap and guidance sync, Vapor, Hummingbird, hb Server/Lambda flows, persistence, OpenAPI/RPC, SwiftNIO, observability, auth, app sync, Docker, Apple Containerization, and Fly.io support plus the source-bundled `server-swift-steward` custom-agent definition +- `server-side-swift`: server-side Swift bootstrap and guidance sync, Vapor, Hummingbird, persistence, OpenAPI/RPC, SwiftNIO, observability, auth, app sync, Docker, Apple `container` 1.x, persistent `container machine` environments, exact-version Containerization APIs, and Fly.io support plus the source-bundled `server-swift-steward` custom-agent definition - `swift-lang`: shared Swift language, API style, error handling, functional pipelines, formatting, source organization, SwiftSyntax transformation, compiler inspection, SourceKit semantics and indexing, SourceKit-LSP diagnosis, Swiftly/Xcode toolchain routing, and modernization cleanup workflows - `rust-skills`: Rust, Cargo, rustup, crate, workspace, CLI, library, package, CI, test, lint, and format workflow guidance - `speak-swiftly`: Git-backed Speak Swiftly plugin from the standalone SpeakSwiftlyServer repository diff --git a/ROADMAP.md b/ROADMAP.md index 7d316f69..fb83e477 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -30,6 +30,7 @@ - [Milestone 27: Cybersecurity skills plugin](#milestone-27-cybersecurity-skills-plugin) - [Milestone 28: Swift language tooling expansion](#milestone-28-swift-language-tooling-expansion) - [Milestone 29: Model Lab skills plugin](#milestone-29-model-lab-skills-plugin) +- [Milestone 30: macOS virtualization and container skills expansion](#milestone-30-macos-virtualization-and-container-skills-expansion) - [Small Tickets](#small-tickets) - [Backlog Candidates](#backlog-candidates) - [History](#history) @@ -73,6 +74,7 @@ - Milestone 27: Cybersecurity skills plugin - Planned - Milestone 28: Swift language tooling expansion - In Progress - Milestone 29: Model Lab skills plugin - Planned +- Milestone 30: macOS virtualization and container skills expansion - Completed ## Milestone 5: SwiftASB skills plugin @@ -995,6 +997,41 @@ Implemented; release pending - [x] Refusal ablation and jailbreak workflows measure ordinary behavior, regressions, and uncertainty in addition to bypass outcomes. - [x] Root docs, marketplace wiring, Codex/Hermes/Claude compatibility, plugin metadata, and validation agree on the shipped inventory. +## Milestone 30: macOS virtualization and container skills expansion + +### Status + +Completed + +### Scope + +- [x] Record the cross-plugin architecture, ownership, source baseline, phased skills, security boundaries, validation, and forward-test plan in [`docs/maintainers/macos-virtualization-and-container-skills-plan.md`](./docs/maintainers/macos-virtualization-and-container-skills-plan.md). +- [x] Add Apple Dev selection and Virtualization framework foundations that distinguish host execution, OCI containers, persistent Linux machines, full Linux VMs, full macOS VMs, and physical Macs. +- [x] Expand the Apple Containerization workflow for the `container` 1.x CLI, persistent `container machine` environments, TOML configuration, structured-output changes, and exact-version 0.x Containerization package APIs. +- [x] Add separate Linux and macOS development-VM workflows instead of flattening their boot, identity, device, lifecycle, and fidelity contracts. +- [x] Add a Cybersecurity lab-preparation workflow that turns an isolation decision into verified mount, clipboard, credential, device, network, baseline, evidence-export, revert, and teardown controls. +- [x] Keep the expansion guidance-only: no VM images, restore images, kernels, malware samples, privileged helpers, daemons, guest agents, MCP servers, remote credentials, or automatic third-party tool installation. + +### Planned Slices + +- [x] Phase 1: add `apple-dev-skills:choose-macos-virtualization-shape` and `apple-dev-skills:virtualization-framework-workflow`, then align Cybersecurity isolation handoffs. +- [x] Phase 2: update `server-side-swift:apple-containerization-workflow` for `container` 1.x and add `apple-dev-skills:linux-development-vm-workflow`. +- [x] Phase 3: add `apple-dev-skills:macos-development-vm-workflow` with restore-image, VM-bundle, identity, clean-baseline, and reset guidance. +- [x] Phase 4: add `cybersecurity-skills:prepare-isolated-analysis-lab` and align dynamic-analysis and macOS-investigation workflows around its lab record. +- [x] Forward-test the ten planned stable guidance paths through scenario contract tests before adding any tool-specific adapter skill; treat Lima, Colima, Tart, UTM, VMware Fusion, Parallels Desktop, OrbStack, and similar products as discovered adapters until repeated tasks justify a dedicated surface. +- [x] Regenerate portable Hermes exports, update Claude and Cowork classifications, refresh root architecture metadata and user-facing inventory text, and run affected child plus root validation with each shipped phase. + +### Exit Criteria + +- [x] An agent selects a development or research boundary by required fidelity, persistence, portability, host integration, and threat model instead of choosing a familiar tool name first. +- [x] Apple `container`, `container machine`, full Linux VMs, and full macOS VMs have distinct owners and lifecycle contracts. +- [x] A custom Virtualization framework host validates guest configuration, entitlements, devices, lifecycle, and save/restore compatibility without calling saved state a full snapshot system. +- [x] macOS security-control claims use a macOS guest or physical Mac when Linux cannot reproduce the behavior, and remaining hardware or anti-VM gaps are explicit. +- [x] Security labs default ambient host authority to absent, preserve intended evidence through a narrow export, and verify revert or teardown after execution. +- [x] Socket's skill metadata, portability exports, compatibility records, documentation, and validation agree on the shipped virtualization inventory. + +Completed Milestone 30 by shipping four Apple Dev virtualization workflows, a disposable Cybersecurity lab-preparation workflow, Apple `container` 1.x and `container machine` guidance, guest-versus-host evidence rules, Hermes exports, Claude and Cowork compatibility metadata, architecture inventory updates, and ten scenario-level forward tests. The rebased `9.19.0` release candidate preserves the concurrent Model Lab inventory and passed 268 Apple Dev tests, 126 Socket tests with one intentional skip, Apple and Cybersecurity child validators, Socket marketplace validation, Hermes parity, Claude/Cowork validation, and the architecture consistency check. + ## Small Tickets - [ ] Record issue-sized fixes, TODO/FIXME imports, and cleanup work that is too small or too unplanned for a milestone. diff --git a/docs/architecture/ARCHITECTURE.md b/docs/architecture/ARCHITECTURE.md index ca9020cf..5d0b26ad 100644 --- a/docs/architecture/ARCHITECTURE.md +++ b/docs/architecture/ARCHITECTURE.md @@ -22,6 +22,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `dotnet-skills` (codex-plugin) uses targets: skills:plugins/dotnet-skills/skills. - `game-dev-skills` (codex-plugin) uses targets: skills:plugins/game-dev-skills/skills. - `messaging-collaboration-skills` (codex-plugin) uses targets: skills:plugins/messaging-collaboration-skills/skills. +- `model-lab-skills` (codex-plugin) uses targets: skills:plugins/model-lab-skills/skills. - `network-protocol-skills` (codex-plugin) uses targets: skills:plugins/network-protocol-skills/skills. - `productivity-skills` (codex-plugin) uses targets: skills:plugins/productivity-skills/skills, mcp:plugins/productivity-skills/.mcp.json. - `python-skills` (codex-plugin) uses targets: skills:plugins/python-skills/skills. @@ -34,7 +35,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `swiftasb-skills` (codex-plugin) uses targets: skills:plugins/swiftasb-skills/skills. - `things-app` (codex-plugin) uses targets: skills:plugins/things-app/skills, mcp:plugins/things-app/.mcp.json. - `web-dev-skills` (codex-plugin) uses targets: skills:plugins/web-dev-skills/skills. -- `socket` (codex-plugin-marketplace) uses targets: agent-portability-skills, android-dev-skills, apple-dev-skills, apple-creator-studio-skills, cardhop-app, cloud-deployment-skills, cloud-inference-skills, dotnet-skills, productivity-skills, python-skills, network-protocol-skills, server-side-swift, swift-lang, server-side-jvm, rust-skills, speak-swiftly, swiftasb-skills, things-app, spotify, web-dev-skills, reverse-engineering-skills, agentdeck, game-dev-skills, messaging-collaboration-skills, cybersecurity-skills. +- `socket` (codex-plugin-marketplace) uses targets: agent-portability-skills, android-dev-skills, apple-dev-skills, apple-creator-studio-skills, cardhop-app, cloud-deployment-skills, cloud-inference-skills, dotnet-skills, productivity-skills, python-skills, network-protocol-skills, server-side-swift, swift-lang, server-side-jvm, rust-skills, speak-swiftly, swiftasb-skills, things-app, spotify, web-dev-skills, reverse-engineering-skills, agentdeck, game-dev-skills, messaging-collaboration-skills, cybersecurity-skills, model-lab-skills. - `speak-swiftly` (remote-plugin-entry) uses targets: no targets recorded. @@ -75,6 +76,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:apple-dev-skills/bootstrap-swift-package` (codex-skill) at `plugins/apple-dev-skills/skills/bootstrap-swift-package/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/bootstrap-xcode-app-project` (codex-skill) at `plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/camera-capture-depth-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/camera-capture-depth-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:apple-dev-skills/choose-macos-virtualization-shape` (codex-skill) at `plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/core-animation-layer-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/core-image-processing-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/core-image-processing-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/coreaudio-modernization-repair-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/SKILL.md` depends on: no declared dependencies. @@ -86,6 +88,8 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:apple-dev-skills/format-swift-sources` (codex-skill) at `plugins/apple-dev-skills/skills/format-swift-sources/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/icon-composer-app-icon-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/ios-runtime-forensics-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/ios-runtime-forensics-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:apple-dev-skills/linux-development-vm-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:apple-dev-skills/macos-development-vm-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/macos-distribution-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/macos-distribution-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/macos-window-management-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/macos-window-management-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/mailkit-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/mailkit-workflow/SKILL.md` depends on: no declared dependencies. @@ -96,6 +100,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:apple-dev-skills/structure-swift-sources` (codex-skill) at `plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/swift-openapi-client-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/swift-package-build-run-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swift-package-build-run-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:apple-dev-skills/swift-package-extension-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/swift-package-testing-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swift-package-testing-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/swift-package-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swift-package-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/swiftdata-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/swiftdata-workflow/SKILL.md` depends on: no declared dependencies. @@ -109,6 +114,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:apple-dev-skills/tipkit-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/tipkit-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/tips-helpviewer-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/tips-helpviewer-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/video-codec-processing-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/video-codec-processing-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:apple-dev-skills/virtualization-framework-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/vision-coreml-recognition-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/vision-coreml-recognition-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/vision-image-analysis-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/vision-image-analysis-workflow/SKILL.md` depends on: no declared dependencies. - `skill:apple-dev-skills/xcode-app-project-workflow` (codex-skill) at `plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md` depends on: no declared dependencies. @@ -144,6 +150,7 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:cybersecurity-skills/operate-agentic-security-tools` (codex-skill) at `plugins/cybersecurity-skills/skills/operate-agentic-security-tools/SKILL.md` depends on: no declared dependencies. - `skill:cybersecurity-skills/perform-dynamic-malware-analysis` (codex-skill) at `plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md` depends on: no declared dependencies. - `skill:cybersecurity-skills/perform-static-malware-analysis` (codex-skill) at `plugins/cybersecurity-skills/skills/perform-static-malware-analysis/SKILL.md` depends on: no declared dependencies. +- `skill:cybersecurity-skills/prepare-isolated-analysis-lab` (codex-skill) at `plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md` depends on: no declared dependencies. - `skill:cybersecurity-skills/preserve-security-evidence` (codex-skill) at `plugins/cybersecurity-skills/skills/preserve-security-evidence/SKILL.md` depends on: no declared dependencies. - `skill:cybersecurity-skills/recover-security-incident` (codex-skill) at `plugins/cybersecurity-skills/skills/recover-security-incident/SKILL.md` depends on: no declared dependencies. - `skill:cybersecurity-skills/report-security-assessment` (codex-skill) at `plugins/cybersecurity-skills/skills/report-security-assessment/SKILL.md` depends on: no declared dependencies. @@ -197,6 +204,19 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:messaging-collaboration-skills/voip-sip-calling-workflow` (codex-skill) at `plugins/messaging-collaboration-skills/skills/voip-sip-calling-workflow/SKILL.md` depends on: no declared dependencies. - `skill:messaging-collaboration-skills/webhook-and-event-lifecycle` (codex-skill) at `plugins/messaging-collaboration-skills/skills/webhook-and-event-lifecycle/SKILL.md` depends on: no declared dependencies. - `skill:messaging-collaboration-skills/whatsapp-business-workflow` (codex-skill) at `plugins/messaging-collaboration-skills/skills/whatsapp-business-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/ablate-refusal-representations` (codex-skill) at `plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/benchmark-model-runtime` (codex-skill) at `plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/choose-apple-model-runtime` (codex-skill) at `plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/choose-model-lab-workflow` (codex-skill) at `plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/compare-model-checkpoints` (codex-skill) at `plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/design-model-experiment` (codex-skill) at `plugins/model-lab-skills/skills/design-model-experiment/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/evaluate-jailbreak-resilience` (codex-skill) at `plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/evaluate-language-model` (codex-skill) at `plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/evaluate-tool-calling-model` (codex-skill) at `plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/fine-tune-language-model` (codex-skill) at `plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/prepare-language-model-dataset` (codex-skill) at `plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/research-model-representations` (codex-skill) at `plugins/model-lab-skills/skills/research-model-representations/SKILL.md` depends on: no declared dependencies. +- `skill:model-lab-skills/steer-language-model-behavior` (codex-skill) at `plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md` depends on: no declared dependencies. - `skill:network-protocol-skills/choose-network-transport` (codex-skill) at `plugins/network-protocol-skills/skills/choose-network-transport/SKILL.md` depends on: no declared dependencies. - `skill:network-protocol-skills/http3-quic-workflow` (codex-skill) at `plugins/network-protocol-skills/skills/http3-quic-workflow/SKILL.md` depends on: no declared dependencies. - `skill:network-protocol-skills/network-protocol-diagnostics` (codex-skill) at `plugins/network-protocol-skills/skills/network-protocol-diagnostics/SKILL.md` depends on: no declared dependencies. @@ -280,12 +300,17 @@ See [SLICES.md](./SLICES.md) for provable end-to-end code paths. - `skill:server-side-swift/swiftnio-workflow` (codex-skill) at `plugins/server-side-swift/skills/swiftnio-workflow/SKILL.md` depends on: no declared dependencies. - `skill:server-side-swift/sync-hummingbird-service-guidance` (codex-skill) at `plugins/server-side-swift/skills/sync-hummingbird-service-guidance/SKILL.md` depends on: no declared dependencies. - `skill:server-side-swift/vapor-server-workflow` (codex-skill) at `plugins/server-side-swift/skills/vapor-server-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:swift-lang/choose-swift-language-tooling` (codex-skill) at `plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md` depends on: no declared dependencies. +- `skill:swift-lang/sourcekit-lsp-workflow` (codex-skill) at `plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-api-style-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-api-style-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:swift-lang/swift-compiler-inspection-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-error-handling-style-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-error-handling-style-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-format-style-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-format-style-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-functional-pipelines-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-functional-pipelines-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-modernization-cleanup-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:swift-lang/swift-semantic-indexing-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swift-lang/swift-source-organization-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md` depends on: no declared dependencies. +- `skill:swift-lang/swift-syntax-tooling-workflow` (codex-skill) at `plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md` depends on: no declared dependencies. - `skill:swiftasb-skills/build-appkit-app` (codex-skill) at `plugins/swiftasb-skills/skills/build-appkit-app/SKILL.md` depends on: no declared dependencies. - `skill:swiftasb-skills/build-swift-package` (codex-skill) at `plugins/swiftasb-skills/skills/build-swift-package/SKILL.md` depends on: no declared dependencies. - `skill:swiftasb-skills/build-swiftui-app` (codex-skill) at `plugins/swiftasb-skills/skills/build-swiftui-app/SKILL.md` depends on: no declared dependencies. @@ -347,6 +372,7 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/bootstrap-swift-package/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/camera-capture-depth-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/core-image-processing-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/SKILL.md`. @@ -358,6 +384,8 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/format-swift-sources/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/icon-composer-app-icon-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/ios-runtime-forensics-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/macos-distribution-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/macos-window-management-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/mailkit-workflow/SKILL.md`. @@ -368,6 +396,7 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swift-package-build-run-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swift-package-testing-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swift-package-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/swiftdata-workflow/SKILL.md`. @@ -381,6 +410,7 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/tipkit-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/tips-helpviewer-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/video-codec-processing-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/vision-coreml-recognition-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/vision-image-analysis-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md`. @@ -420,6 +450,7 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/operate-agentic-security-tools/SKILL.md`. - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md`. - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/perform-static-malware-analysis/SKILL.md`. +- `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md`. - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/preserve-security-evidence/SKILL.md`. - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/recover-security-incident/SKILL.md`. - `skill-manifest` evidence from `plugins/cybersecurity-skills/skills/report-security-assessment/SKILL.md`. @@ -477,6 +508,20 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/messaging-collaboration-skills/skills/webhook-and-event-lifecycle/SKILL.md`. - `skill-manifest` evidence from `plugins/messaging-collaboration-skills/skills/whatsapp-business-workflow/SKILL.md`. - `codex-plugin-manifest` evidence from `plugins/messaging-collaboration-skills/.codex-plugin/plugin.json`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/design-model-experiment/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/research-model-representations/SKILL.md`. +- `skill-manifest` evidence from `plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md`. +- `codex-plugin-manifest` evidence from `plugins/model-lab-skills/.codex-plugin/plugin.json`. - `skill-manifest` evidence from `plugins/network-protocol-skills/skills/choose-network-transport/SKILL.md`. - `skill-manifest` evidence from `plugins/network-protocol-skills/skills/http3-quic-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/network-protocol-skills/skills/network-protocol-diagnostics/SKILL.md`. @@ -568,12 +613,17 @@ The structured visual model lives in [architecture.json](./architecture.json). I - `skill-manifest` evidence from `plugins/server-side-swift/skills/vapor-server-workflow/SKILL.md`. - `codex-plugin-manifest` evidence from `plugins/server-side-swift/.codex-plugin/plugin.json`. - `codex-plugin-manifest` evidence from `plugins/spotify/.codex-plugin/plugin.json`. +- `skill-manifest` evidence from `plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md`. +- `skill-manifest` evidence from `plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-api-style-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-error-handling-style-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-format-style-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-functional-pipelines-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md`. - `skill-manifest` evidence from `plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md`. +- `skill-manifest` evidence from `plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md`. - `codex-plugin-manifest` evidence from `plugins/swift-lang/.codex-plugin/plugin.json`. - `skill-manifest` evidence from `plugins/swiftasb-skills/skills/build-appkit-app/SKILL.md`. - `skill-manifest` evidence from `plugins/swiftasb-skills/skills/build-swift-package/SKILL.md`. diff --git a/docs/architecture/architecture.json b/docs/architecture/architecture.json index b3ee9c18..cd87ba43 100644 --- a/docs/architecture/architecture.json +++ b/docs/architecture/architecture.json @@ -1,5 +1,5 @@ { - "detectedAt": "2026-07-16T22:38:25.224646+00:00", + "detectedAt": "2026-07-19T17:41:35.923846+00:00", "detectionSource": "plugin-repo", "evidence": [ { @@ -146,6 +146,10 @@ "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/camera-capture-depth-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md" @@ -190,6 +194,14 @@ "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/ios-runtime-forensics-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/macos-distribution-workflow/SKILL.md" @@ -230,6 +242,10 @@ "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/swift-package-build-run-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/swift-package-testing-workflow/SKILL.md" @@ -282,6 +298,10 @@ "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/video-codec-processing-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/apple-dev-skills/skills/vision-coreml-recognition-workflow/SKILL.md" @@ -438,6 +458,10 @@ "kind": "skill-manifest", "path": "plugins/cybersecurity-skills/skills/perform-static-malware-analysis/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/cybersecurity-skills/skills/preserve-security-evidence/SKILL.md" @@ -666,6 +690,62 @@ "kind": "codex-plugin-manifest", "path": "plugins/messaging-collaboration-skills/.codex-plugin/plugin.json" }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/design-model-experiment/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/research-model-representations/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md" + }, + { + "kind": "codex-plugin-manifest", + "path": "plugins/model-lab-skills/.codex-plugin/plugin.json" + }, { "kind": "skill-manifest", "path": "plugins/network-protocol-skills/skills/choose-network-transport/SKILL.md" @@ -1030,10 +1110,22 @@ "kind": "codex-plugin-manifest", "path": "plugins/spotify/.codex-plugin/plugin.json" }, + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md" + }, + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/swift-lang/skills/swift-api-style-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/swift-lang/skills/swift-error-handling-style-workflow/SKILL.md" @@ -1050,10 +1142,18 @@ "kind": "skill-manifest", "path": "plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md" + }, { "kind": "skill-manifest", "path": "plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md" }, + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md" + }, { "kind": "codex-plugin-manifest", "path": "plugins/swift-lang/.codex-plugin/plugin.json" @@ -1286,6 +1386,20 @@ "skills:plugins/messaging-collaboration-skills/skills" ] }, + { + "evidence": [ + { + "kind": "codex-plugin-manifest", + "path": "plugins/model-lab-skills/.codex-plugin/plugin.json" + } + ], + "kind": "codex-plugin", + "name": "model-lab-skills", + "path": "plugins/model-lab-skills", + "targets": [ + "skills:plugins/model-lab-skills/skills" + ] + }, { "evidence": [ { @@ -1489,7 +1603,8 @@ "agentdeck", "game-dev-skills", "messaging-collaboration-skills", - "cybersecurity-skills" + "cybersecurity-skills", + "model-lab-skills" ] }, { @@ -1891,6 +2006,18 @@ "label": "plugin exposes skill", "to": "target:skill:apple-dev-skills/camera-capture-depth-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md" + } + ], + "from": "product:apple-dev-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:apple-dev-skills/choose-macos-virtualization-shape" + }, { "evidence": [ { @@ -2023,6 +2150,30 @@ "label": "plugin exposes skill", "to": "target:skill:apple-dev-skills/ios-runtime-forensics-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md" + } + ], + "from": "product:apple-dev-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:apple-dev-skills/linux-development-vm-workflow" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md" + } + ], + "from": "product:apple-dev-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:apple-dev-skills/macos-development-vm-workflow" + }, { "evidence": [ { @@ -2143,6 +2294,18 @@ "label": "plugin exposes skill", "to": "target:skill:apple-dev-skills/swift-package-build-run-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md" + } + ], + "from": "product:apple-dev-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:apple-dev-skills/swift-package-extension-workflow" + }, { "evidence": [ { @@ -2299,6 +2462,18 @@ "label": "plugin exposes skill", "to": "target:skill:apple-dev-skills/video-codec-processing-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md" + } + ], + "from": "product:apple-dev-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:apple-dev-skills/virtualization-framework-workflow" + }, { "evidence": [ { @@ -2719,6 +2894,18 @@ "label": "plugin exposes skill", "to": "target:skill:cybersecurity-skills/perform-static-malware-analysis" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md" + } + ], + "from": "product:cybersecurity-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:cybersecurity-skills/prepare-isolated-analysis-lab" + }, { "evidence": [ { @@ -3355,6 +3542,162 @@ "label": "plugin exposes skill", "to": "target:skill:messaging-collaboration-skills/whatsapp-business-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/ablate-refusal-representations" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/benchmark-model-runtime" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/choose-apple-model-runtime" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/choose-model-lab-workflow" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/compare-model-checkpoints" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/design-model-experiment/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/design-model-experiment" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/evaluate-jailbreak-resilience" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/evaluate-language-model" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/evaluate-tool-calling-model" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/fine-tune-language-model" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/prepare-language-model-dataset" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/research-model-representations/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/research-model-representations" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md" + } + ], + "from": "product:model-lab-skills", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:model-lab-skills/steer-language-model-behavior" + }, { "evidence": [ { @@ -4351,6 +4694,30 @@ "label": "plugin exposes skill", "to": "target:skill:server-side-swift/vapor-server-workflow" }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md" + } + ], + "from": "product:swift-lang", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:swift-lang/choose-swift-language-tooling" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md" + } + ], + "from": "product:swift-lang", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:swift-lang/sourcekit-lsp-workflow" + }, { "evidence": [ { @@ -4361,67 +4728,103 @@ "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-api-style-workflow" + "to": "target:skill:swift-lang/swift-api-style-workflow" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md" + } + ], + "from": "product:swift-lang", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:swift-lang/swift-compiler-inspection-workflow" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/swift-lang/skills/swift-error-handling-style-workflow/SKILL.md" + } + ], + "from": "product:swift-lang", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:swift-lang/swift-error-handling-style-workflow" + }, + { + "evidence": [ + { + "kind": "skill-directory", + "path": "plugins/swift-lang/skills/swift-format-style-workflow/SKILL.md" + } + ], + "from": "product:swift-lang", + "kind": "exposes", + "label": "plugin exposes skill", + "to": "target:skill:swift-lang/swift-format-style-workflow" }, { "evidence": [ { "kind": "skill-directory", - "path": "plugins/swift-lang/skills/swift-error-handling-style-workflow/SKILL.md" + "path": "plugins/swift-lang/skills/swift-functional-pipelines-workflow/SKILL.md" } ], "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-error-handling-style-workflow" + "to": "target:skill:swift-lang/swift-functional-pipelines-workflow" }, { "evidence": [ { "kind": "skill-directory", - "path": "plugins/swift-lang/skills/swift-format-style-workflow/SKILL.md" + "path": "plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md" } ], "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-format-style-workflow" + "to": "target:skill:swift-lang/swift-modernization-cleanup-workflow" }, { "evidence": [ { "kind": "skill-directory", - "path": "plugins/swift-lang/skills/swift-functional-pipelines-workflow/SKILL.md" + "path": "plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md" } ], "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-functional-pipelines-workflow" + "to": "target:skill:swift-lang/swift-semantic-indexing-workflow" }, { "evidence": [ { "kind": "skill-directory", - "path": "plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md" + "path": "plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md" } ], "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-modernization-cleanup-workflow" + "to": "target:skill:swift-lang/swift-source-organization-workflow" }, { "evidence": [ { "kind": "skill-directory", - "path": "plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md" + "path": "plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md" } ], "from": "product:swift-lang", "kind": "exposes", "label": "plugin exposes skill", - "to": "target:skill:swift-lang/swift-source-organization-workflow" + "to": "target:skill:swift-lang/swift-syntax-tooling-workflow" }, { "evidence": [ @@ -5130,6 +5533,30 @@ "kind": "owns", "label": "marketplace entry points at local plugin root", "to": "path:./plugins/cybersecurity-skills" + }, + { + "evidence": [ + { + "kind": "plugin-marketplace", + "path": ".agents/plugins/marketplace.json" + } + ], + "from": "product:socket", + "kind": "exposes", + "label": "marketplace exposes plugin entry", + "to": "product:model-lab-skills" + }, + { + "evidence": [ + { + "kind": "plugin-marketplace", + "path": ".agents/plugins/marketplace.json" + } + ], + "from": "product:model-lab-skills", + "kind": "owns", + "label": "marketplace entry points at local plugin root", + "to": "path:./plugins/model-lab-skills" } ], "schemaVersion": 1, @@ -5519,6 +5946,18 @@ "name": "skill:apple-dev-skills/camera-capture-depth-workflow", "path": "plugins/apple-dev-skills/skills/camera-capture-depth-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:apple-dev-skills/choose-macos-virtualization-shape", + "path": "plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -5651,6 +6090,30 @@ "name": "skill:apple-dev-skills/ios-runtime-forensics-workflow", "path": "plugins/apple-dev-skills/skills/ios-runtime-forensics-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:apple-dev-skills/linux-development-vm-workflow", + "path": "plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:apple-dev-skills/macos-development-vm-workflow", + "path": "plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -5771,6 +6234,18 @@ "name": "skill:apple-dev-skills/swift-package-build-run-workflow", "path": "plugins/apple-dev-skills/skills/swift-package-build-run-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:apple-dev-skills/swift-package-extension-workflow", + "path": "plugins/apple-dev-skills/skills/swift-package-extension-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -5927,6 +6402,18 @@ "name": "skill:apple-dev-skills/video-codec-processing-workflow", "path": "plugins/apple-dev-skills/skills/video-codec-processing-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:apple-dev-skills/virtualization-framework-workflow", + "path": "plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -6347,6 +6834,18 @@ "name": "skill:cybersecurity-skills/perform-static-malware-analysis", "path": "plugins/cybersecurity-skills/skills/perform-static-malware-analysis/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:cybersecurity-skills/prepare-isolated-analysis-lab", + "path": "plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -6983,6 +7482,162 @@ "name": "skill:messaging-collaboration-skills/whatsapp-business-workflow", "path": "plugins/messaging-collaboration-skills/skills/whatsapp-business-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/ablate-refusal-representations", + "path": "plugins/model-lab-skills/skills/ablate-refusal-representations/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/benchmark-model-runtime", + "path": "plugins/model-lab-skills/skills/benchmark-model-runtime/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/choose-apple-model-runtime", + "path": "plugins/model-lab-skills/skills/choose-apple-model-runtime/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/choose-model-lab-workflow", + "path": "plugins/model-lab-skills/skills/choose-model-lab-workflow/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/compare-model-checkpoints", + "path": "plugins/model-lab-skills/skills/compare-model-checkpoints/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/design-model-experiment/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/design-model-experiment", + "path": "plugins/model-lab-skills/skills/design-model-experiment/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/evaluate-jailbreak-resilience", + "path": "plugins/model-lab-skills/skills/evaluate-jailbreak-resilience/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/evaluate-language-model", + "path": "plugins/model-lab-skills/skills/evaluate-language-model/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/evaluate-tool-calling-model", + "path": "plugins/model-lab-skills/skills/evaluate-tool-calling-model/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/fine-tune-language-model", + "path": "plugins/model-lab-skills/skills/fine-tune-language-model/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/prepare-language-model-dataset", + "path": "plugins/model-lab-skills/skills/prepare-language-model-dataset/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/research-model-representations/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/research-model-representations", + "path": "plugins/model-lab-skills/skills/research-model-representations/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:model-lab-skills/steer-language-model-behavior", + "path": "plugins/model-lab-skills/skills/steer-language-model-behavior/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -7979,6 +8634,30 @@ "name": "skill:server-side-swift/vapor-server-workflow", "path": "plugins/server-side-swift/skills/vapor-server-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:swift-lang/choose-swift-language-tooling", + "path": "plugins/swift-lang/skills/choose-swift-language-tooling/SKILL.md" + }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:swift-lang/sourcekit-lsp-workflow", + "path": "plugins/swift-lang/skills/sourcekit-lsp-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -7991,6 +8670,18 @@ "name": "skill:swift-lang/swift-api-style-workflow", "path": "plugins/swift-lang/skills/swift-api-style-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:swift-lang/swift-compiler-inspection-workflow", + "path": "plugins/swift-lang/skills/swift-compiler-inspection-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -8039,6 +8730,18 @@ "name": "skill:swift-lang/swift-modernization-cleanup-workflow", "path": "plugins/swift-lang/skills/swift-modernization-cleanup-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:swift-lang/swift-semantic-indexing-workflow", + "path": "plugins/swift-lang/skills/swift-semantic-indexing-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ @@ -8051,6 +8754,18 @@ "name": "skill:swift-lang/swift-source-organization-workflow", "path": "plugins/swift-lang/skills/swift-source-organization-workflow/SKILL.md" }, + { + "dependencies": [], + "evidence": [ + { + "kind": "skill-manifest", + "path": "plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md" + } + ], + "kind": "codex-skill", + "name": "skill:swift-lang/swift-syntax-tooling-workflow", + "path": "plugins/swift-lang/skills/swift-syntax-tooling-workflow/SKILL.md" + }, { "dependencies": [], "evidence": [ diff --git a/docs/maintainers/claude-compatibility.json b/docs/maintainers/claude-compatibility.json index d57fe04c..adc988ba 100644 --- a/docs/maintainers/claude-compatibility.json +++ b/docs/maintainers/claude-compatibility.json @@ -6,11 +6,11 @@ "agentdeck": { "claudeCode": "not_supported", "cowork": "not_supported", "note": "Codex thread-title hooks have no Claude equivalent." }, "android-dev-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "apple-creator-studio-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, - "apple-dev-skills": { "claudeCode": "local_mcp", "cowork": "skills_only", "note": "Xcode bridge requires the local Mac and Xcode." }, + "apple-dev-skills": { "claudeCode": "local_mcp", "cowork": "skills_only", "note": "Virtualization guidance is portable; the Xcode bridge and VM execution require the local Mac and Xcode." }, "cardhop-app": { "claudeCode": "local_mcp", "cowork": "skills_only", "note": "Cardhop server runs on the local Mac." }, "cloud-deployment-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "cloud-inference-skills": { "claudeCode": "remote_mcp", "cowork": "remote_mcp", "note": "Runpod endpoints are public remote MCP services; authentication remains operator-managed." }, - "cybersecurity-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, + "cybersecurity-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows; analysis-lab execution remains local and approval-gated." }, "dotnet-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "game-dev-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "messaging-collaboration-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, @@ -21,7 +21,7 @@ "reverse-engineering-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "rust-skills": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, "server-side-jvm": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, - "server-side-swift": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, + "server-side-swift": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows; Apple container execution requires a supported local Mac." }, "speak-swiftly": { "claudeCode": "not_supported", "cowork": "not_supported", "note": "The standalone payload auto-loads a Codex-only hook with a hard-coded Codex cache path; add a Claude-native payload there before exposing it here." }, "spotify": { "claudeCode": "not_supported", "cowork": "not_supported", "note": "Socket placeholder without a usable skill payload." }, "swift-lang": { "claudeCode": "supported", "cowork": "skills_only", "note": "Instruction-only workflows." }, diff --git a/docs/maintainers/macos-virtualization-and-container-skills-plan.md b/docs/maintainers/macos-virtualization-and-container-skills-plan.md new file mode 100644 index 00000000..b2e315bf --- /dev/null +++ b/docs/maintainers/macos-virtualization-and-container-skills-plan.md @@ -0,0 +1,281 @@ +# macOS Virtualization And Container Skills Plan + +## Intent + +Expand Socket's Apple-hosted compute guidance so an agent can choose and operate the right boundary for development, compatibility testing, and authorized security research: + +- an ordinary process on the host Mac +- an OCI container +- an Apple `container` lightweight per-container Linux VM +- an Apple `container machine` persistent Linux environment +- a full Linux VM +- a full macOS VM +- a spare physical Mac when VM fidelity is insufficient + +The practical result should be a clean path for building on Linux while editing on macOS, validating software on a clean macOS release, reproducing security controls such as SIP or Gatekeeper in a separate guest, and running suspicious Linux or macOS workloads without casually exposing the host's files, clipboard, credentials, devices, or network. + +This is a coordinated skill expansion across existing plugins. It does not create a new virtualization plugin, ship a VM manager, install a hypervisor, bundle guest images, or turn Socket into a lab runtime. + +## Source Baseline + +This plan was checked on 2026-07-19 against current local and upstream evidence. + +- Apple's [`container` 1.0.0 release](https://github.com/apple/container/releases/tag/1.0.0) adds persistent `container machine` environments, replaces UserDefaults-backed system properties with TOML configuration, changes structured list output, adds `container cp`, and removes compatibility with version-zero XPC APIs. +- Apple's current [`container machine` documentation](https://github.com/apple/container/blob/main/docs/container-machine.md) describes persistent OCI-image-backed Linux environments, automatic host user and home-directory integration, long-running init systems, configurable CPU and memory, optional home sharing, and nested virtualization on supported hosts with a compatible kernel. The 1.0.0 release links this current-branch document rather than a tag-contained copy, so implementation must pair it with the installed CLI's own help. +- Apple's [`container` technical overview](https://github.com/apple/container/blob/1.0.0/docs/technical-overview.md) states that ordinary `container` workloads run each Linux container in its own lightweight VM and documents the Virtualization, vmnet, XPC, Keychain, launchd, and unified-log architecture. +- The lower-level [`apple/containerization` project](https://github.com/apple/containerization) has not reached 1.0. Its current [release page](https://github.com/apple/containerization/releases) still exposes 0.x prerelease tags, so package API guidance must continue to use exact-version source and release documentation rather than treating the Swift package as source-stable. +- Apple's [Virtualization framework](https://developer.apple.com/documentation/virtualization) supports custom macOS and Linux guests, configurable devices, VM lifecycle control, and `VZVirtualMachineView` on macOS. +- Virtualization framework save and restore APIs are available on macOS 14 and later, require a compatible configuration, and are distinct from disk cloning or a complete snapshot-management product. +- Apple's public nested-virtualization control is exposed through `VZGenericPlatformConfiguration`. Treat nested virtualization for generic or Linux guests as capability-gated, and do not promise that an Apple `container` runtime can run inside a macOS guest without separate current proof. +- Automated macOS guest provisioning through `VZMacGuestProvisioningOptions` is a beta, macOS 27-or-later guest capability. Keep it version-gated and outside the stable first slice. + +Local host evidence on the planning date: + +- macOS 26.5.2 on arm64 Apple silicon +- Lima 2.1.4, Colima 0.10.3, and Docker CLI 29.6.2 are installed +- Apple's `container` CLI is not installed + +The installed-tool observation informs forward-test sequencing only. The skills must remain portable and must discover the actual host, tool, version, guest, and configuration at runtime. + +## Architecture Decision + +Extend `apple-dev-skills`, `server-side-swift`, and `cybersecurity-skills` through explicit handoffs. Do not create a fourth plugin that tries to own every VM, container, and security-lab concern. + +This is a durable building-block change. It creates a reusable selection contract and separate implementation and operating workflows while preserving the repositories that already own Apple framework implementation, Apple container tooling, and defensive isolation decisions. + +The change unlocks these near-term uses: + +- clean macOS development guests for OS-version, signing, entitlement, privacy, installer, update, and Xcode/toolchain validation +- persistent Linux development environments that can run init systems and services without being confused with application containers +- custom Virtualization framework apps and tools for macOS or Linux guests +- disposable macOS and Linux analysis labs with explicit host-integration controls +- full-OS validation of SIP-, TCC-, Gatekeeper-, XProtect-, quarantine-, persistence-, kernel-, and service-dependent behavior +- repeatable evidence export, revert, and teardown after security experiments +- explicit comparison among Docker, Apple `container`, `container machine`, Lima or Colima, a full VM, and a physical device + +The simpler extension path was to enlarge `server-side-swift:apple-containerization-workflow` into a general VM and security-lab skill. That would mix OCI image work, persistent Linux development, macOS restore images, Virtualization framework app code, and hostile-workload isolation into one oversized workflow. It would also make generic macOS and security work depend on a server-side Swift plugin. Keep that skill focused on Apple's container stack and use cross-plugin handoffs instead. + +## Ownership And Handoffs + +| Surface | Primary owner | Responsibility | +| --- | --- | --- | +| Choosing a macOS-hosted development boundary | `apple-dev-skills:choose-macos-virtualization-shape` | Compare host execution, containers, persistent Linux machines, full Linux or macOS VMs, and physical Macs by fidelity, lifecycle, integration, and risk. | +| Implementing a custom VM host app or Swift package | `apple-dev-skills:virtualization-framework-workflow` | Own `VZVirtualMachineConfiguration`, guest/platform configuration, devices, lifecycle, UI, entitlements, save/restore support, and framework diagnostics. | +| Provisioning and operating a macOS development guest | `apple-dev-skills:macos-development-vm-workflow` | Own restore-image compatibility, VM bundle identity, installation, resources, guest setup, development-tool installation handoffs, clean baselines, and reset strategy. | +| Provisioning and operating a full Linux development guest | `apple-dev-skills:linux-development-vm-workflow` | Own kernel or EFI boot choice, disks, virtio devices, Rosetta handoff, system services, host integration, distro matrix, and persistent guest lifecycle. | +| Apple `container`, `container machine`, Containerization APIs, OCI images, and Apple-native Linux container runtime behavior | `server-side-swift:apple-containerization-workflow` | Own the 1.0 CLI split between application containers and persistent Linux machines, plus exact-version lower-level package use. | +| Dockerfile, Compose, registries, and portable OCI deployment | `server-side-swift:docker-workflow` | Keep portable image and deployment behavior independent from the selected macOS runtime. | +| Selecting an isolation level for untrusted material | `cybersecurity-skills:select-analysis-isolation` | Choose the smallest environment that reproduces the target while containing its likely behavior. | +| Preparing and tearing down a disposable security lab | `cybersecurity-skills:prepare-isolated-analysis-lab` | Convert the isolation decision into verified mount, clipboard, credential, device, network, baseline, evidence-export, revert, and teardown controls. | +| Observing malicious or suspicious behavior | `cybersecurity-skills:perform-dynamic-malware-analysis` | Own the observation plan and findings while consuming the prepared lab contract. | +| Binary internals | `reverse-engineering-skills` | Consume preserved guest artifacts and own disassembly, decompilation, symbols, and binary behavior. | + +The two operating skills under Apple Dev are intentionally guest-specific. A macOS guest is installed from a compatible restore image and carries Apple identity, platform, signing, privacy, and restore constraints. A Linux guest uses a generic platform configuration, Linux or EFI boot, virtio devices, and may expose Rosetta or nested virtualization. Combining them would hide the failure modes that matter most. + +## Shared Virtualization Shape Record + +Every development or implementation workflow should produce or consume one compact record: + +- purpose: development, compatibility, CI-like validation, security analysis, framework implementation, or runtime diagnosis +- host: Mac model or chip family, architecture, macOS build, memory, storage budget, and selected Xcode or Swift toolchain when relevant +- workload: target OS, target version or distro, architecture, GUI or headless mode, privileged behavior, kernel needs, devices, and expected lifetime +- boundary: host, OCI container, Apple per-container VM, Apple container machine, full Linux VM, full macOS VM, remote environment, or physical Mac +- provenance: CLI, framework, VM manager, restore image, OCI image, kernel, init filesystem, and exact versions or digests +- resources: CPU, memory, disks, ballooning expectations, graphics, audio, USB, and performance constraints +- integration: mounts or directory shares, clipboard, sockets, port forwarding, bridged or NAT networking, SSH agent, credentials, browser profiles, developer identities, and cloud accounts +- lifecycle: create, install, start, pause, save, restore, stop, clone, reset, update, export, and remove semantics supported by the selected tool +- validation: configuration validation, guest boot, identity, network, filesystem, service, application, and teardown checks +- evidence: logs, guest artifacts, packet capture, hashes, screenshots, state files, and an explicit safe export path +- uncertainty: unsupported combinations, beta-only APIs, anti-VM or hardware fidelity gaps, and claims still requiring a physical Mac + +Use this record as a handoff shape, not as a new runtime abstraction or serialized compatibility layer. Each owning skill should retain its platform-specific types and commands. + +## Boundary Selection Rules + +### Host Process + +Use the host directly when the work needs native macOS behavior, the dependency set is trusted, and isolation or clean-state reproduction is not part of the question. + +### OCI Application Container + +Use an OCI container when the job is one application or service, disposable Linux user space is sufficient, and image portability matters. Keep Dockerfile and image design portable even when Apple's `container` CLI is the local runtime. + +### Apple Per-Container Lightweight VM + +Use Apple `container` when the job is an OCI workload on a supported Apple silicon Mac and per-container VM isolation is useful. Keep host mounts, SSH-agent forwarding, credentials, capabilities, network selection, and kernel overrides explicit. + +### Apple Container Machine + +Use `container machine` when the job is a persistent Linux development environment with an init system, services, distro-specific state, or repeated interactive shell access. Treat its automatic user and home-directory integration as a development convenience, not a safe default for hostile code or security research. + +### Full Linux VM + +Use a full Linux VM when the work needs a custom kernel, installer or boot flow, full-system observation, separate disk lifecycle, stronger control over host integration, GUI Linux, or behavior that does not fit an OCI-image-backed environment. + +### Full macOS VM + +Use a macOS VM when the work needs native macOS frameworks, installers, signing, quarantine, Gatekeeper, XProtect, TCC, SIP-enabled customer-like behavior, launch services, persistence, or clean OS-version state. Do not substitute a Linux container or Linux VM for these questions. + +### Physical Mac + +Use a spare physical Mac when the behavior depends on hardware, Secure Enclave identity, unsupported devices, VM detection, performance characteristics, recoveryOS, or another capability that the selected VM cannot reproduce faithfully. + +## Phase 1: Selection And Stable Framework Foundation + +### `apple-dev-skills:choose-macos-virtualization-shape` + +- Classify workload fidelity, persistence, portability, host integration, guest OS, threat level, and resource needs before recommending a tool. +- Distinguish an application container from a persistent container machine and both from a full VM. +- Route OCI authoring to Docker, Apple runtime behavior to Apple Containerization, full-VM implementation to Virtualization framework, and hostile-workload selection to Cybersecurity. +- Return the shared virtualization shape record and one primary path, not a menu without a decision. +- Keep third-party products behind capability discovery and official documentation rather than making any one installed tool the default. + +### `apple-dev-skills:virtualization-framework-workflow` + +- Cover macOS and Linux guest configuration without flattening their platform and boot differences. +- Require `com.apple.security.virtualization`, `validate()`, supported CPU and memory ranges, and exact availability checks before start. +- Cover storage, network, shared directories, sockets, serial or console, graphics, input, audio, clipboard, USB, Rosetta, memory balloon, and nested-virtualization decisions only when the guest and OS version support them. +- Keep VM configuration, VM bundle persistence, lifecycle state, and UI ownership separate. +- Treat save/restore state as configuration-compatible paused-machine state, not as a complete disk snapshot or cloning system. +- Preserve framework errors with the failed configuration surface, guest state, host capability, and likely cause. + +### Existing-skill alignment + +- Update `cybersecurity-skills:select-analysis-isolation` to hand custom VM implementation to `virtualization-framework-workflow` and development-shape selection to `choose-macos-virtualization-shape`. +- Update Apple Dev and Cybersecurity routing references so a macOS VM is the stable high-fidelity answer for SIP-sensitive behavior while local sandbox, TCC, or failure injection remain explicitly lower-fidelity approximations. + +Phase 1 exit criteria: an agent can choose the correct boundary and implement or diagnose a custom Virtualization framework host without confusing macOS guests, Linux guests, containers, saved state, disk snapshots, or physical-device proof. + +## Phase 2: Apple Container 1.0 And Persistent Linux Development + +### Expand `server-side-swift:apple-containerization-workflow` + +- Add a current version gate that distinguishes `container` CLI 1.x from the still-0.x Containerization Swift package. +- Add `container machine` as a first-class job alongside build, pull, run, registry, and lower-level API work. +- Cover create, run, inspect, set-default, resource changes, stop, and remove semantics for persistent machines. +- Cover the TOML configuration migration and structured-output changes from 1.0 without preserving removed `container system property` commands as compatibility shims. +- Separate ordinary container mounts from a container machine's automatic user and home sharing. +- Require `home-mount=none` or an equivalently isolated configuration before treating a container machine as a security boundary. +- Cover nested virtualization only after checking host support, compatible Apple silicon, OS version, kernel configuration, and `/dev/kvm` exposure. +- Keep `container machine` positioned as Linux development, not macOS virtualization and not a Docker Compose replacement. +- Add exact-version source checks for the lower-level Containerization package because its public API is not 1.0-stable. + +### `apple-dev-skills:linux-development-vm-workflow` + +- Compare `container machine`, Lima or Colima, and a full Virtualization framework Linux guest by required fidelity rather than brand. +- Own persistent distro environments, system services, kernel or EFI boot, disks, resource budgets, virtio integration, networking, Rosetta, nested virtualization, guest provisioning, and repeatable reset. +- Keep portable OCI image authoring in Docker and Apple CLI behavior in Apple Containerization. +- Provide a distro-matrix validation shape for toolchain, build, test, service, filesystem, architecture, and cleanup evidence. + +Phase 2 exit criteria: an agent can use the 1.0 Apple CLI for either a disposable OCI workload or a persistent Linux development machine, and can choose a full Linux VM when container-machine integration or image constraints are the wrong fit. + +## Phase 3: macOS Development Guests + +### `apple-dev-skills:macos-development-vm-workflow` + +- Verify Apple silicon, host build, restore-image support, guest build, hardware model, machine identifier, auxiliary storage, disk, CPU, memory, and graphics requirements before installation. +- Treat the restore image, VM bundle, machine identity, disk contents, saved machine state, and exported evidence as separate artifacts with separate lifecycle rules. +- Support clean baseline, named development checkpoint, update-testing checkpoint, and disposable clone strategies without promising a framework-level snapshot feature that was not verified. +- Keep directory sharing, clipboard, audio input, USB, iCloud, developer accounts, signing identities, and network access opt-in and purpose-bound. +- Hand Xcode installation, signing, project build, and test work to their existing Apple Dev owners after the guest itself is ready. +- Add stable manual or tool-specific guest provisioning first. Add macOS 27 automated guest provisioning only as an availability-gated beta reference after current Xcode documentation and runtime behavior are verified. +- Record which questions still require a physical Mac, recoveryOS, or device-attached validation. + +Phase 3 exit criteria: an agent can prepare and reset a clean macOS development guest, explain exactly which host integrations and identities cross the boundary, and produce evidence for OS-version or security-control validation without calling a VM a container. + +## Phase 4: Disposable Security Labs + +### `cybersecurity-skills:prepare-isolated-analysis-lab` + +- Consume an approved isolation decision and produce a concrete lab configuration before executing untrusted content. +- Default host folders, home sharing, clipboard, drag and drop, sockets, SSH agents, browser profiles, cloud credentials, Apple accounts, signing identities, USB, microphone, camera, and unrestricted networking to absent. +- Require a trusted base image or restore image, exact host and guest builds, clock strategy, resource limits, baseline hashes or state, and an explicit evidence-export directory. +- Provide distinct profiles for offline static tooling, monitored Linux dynamic analysis, monitored macOS dynamic analysis, network-service research, and nested-virtualization experiments. +- Keep evidence collection separate from guest control. Hand observations to dynamic malware analysis, binaries to Reverse Engineering, and VM implementation defects to Apple Dev. +- Verify teardown by stopping the workload, exporting only intended evidence, scanning the export, reverting or removing disposable state, revoking temporary credentials, and confirming that no share, forwarded port, or helper remains active. +- Stop when target-platform fidelity, isolation controls, legal authorization for active testing, or safe evidence export cannot be verified. + +### Security workflow alignment + +- Update `perform-dynamic-malware-analysis` to require the prepared-lab record for active execution. +- Update macOS threat and runtime workflows to distinguish guest-observed behavior from host-observed behavior and to record virtualization artifacts that may affect conclusions. +- Add explicit anti-VM, hardware, Secure Enclave, recoveryOS, kernel-extension, system-extension, and device-access limitations to the isolation reference. +- Keep images, kernels, malware samples, credentials, privileged helpers, and runtime services out of the plugin payload. + +Phase 4 exit criteria: an agent can create a reviewable lab plan for Linux or macOS security work, prove the intended isolation controls before execution, export evidence through a narrow path, and verify teardown afterward. + +## Tool Adapter Policy + +Do not create tool-specific skills during the first pass merely because a tool is installed or popular. + +- Keep Apple Virtualization framework and Apple `container` as first-party owner workflows. +- Treat Lima, Colima, Tart, UTM, VMware Fusion, Parallels Desktop, OrbStack, and similar products as adapters behind the guest and boundary workflows when current official documentation is available. +- Add a dedicated adapter skill only after repeated real tasks show that the tool has a distinct lifecycle, configuration model, or failure surface that cannot remain concise in the owner skill. +- Do not claim that two products provide equivalent isolation merely because both use Virtualization framework or both launch a Linux VM. +- Do not instruct an agent to launch a GUI VM product, start a VM, start Apple's container system service, or execute a guest workload without first telling Gale the exact visible or resource-intensive action. + +The first implementation pass should forward-test Apple framework and Apple container paths, then use the already installed Lima and Colima tools only for comparative read-only discovery or explicitly approved execution. Third-party installation is outside this plan unless Gale requests it separately. + +## Reusable References + +Keep the new `SKILL.md` files procedural and concise. Prefer directly linked references for details that are version-sensitive or shared across workflows: + +- `virtualization-shape-record.md`: shared inputs, outputs, and handoff vocabulary +- `macos-and-linux-guest-matrix.md`: platform, boot, identity, device, sharing, save/restore, and fidelity differences +- `virtualization-device-and-availability-matrix.md`: framework device families and OS availability +- `macos-vm-artifact-lifecycle.md`: restore images, VM bundles, auxiliary storage, disks, machine identity, saved state, clones, and evidence exports +- `apple-container-version-matrix.md`: CLI 1.x versus Containerization package versions and breaking surfaces +- `security-lab-control-profile.md`: mounts, clipboard, credentials, devices, networking, baseline, export, and teardown controls + +Do not duplicate the same matrix in multiple plugins. Put Apple framework facts under Apple Dev, Apple CLI and package facts under Server-Side Swift, and threat-driven control policy under Cybersecurity. Cross-link the owning reference from handoff sections. + +## Validation And Forward Tests + +### Static validation + +- Generate or refresh `agents/openai.yaml` from final skill content. +- Run the Apple Dev docs validator and pytest suite for Apple Dev changes. +- Run the Cybersecurity child metadata validator for security changes. +- Run root Socket metadata validation after every skill inventory or plugin metadata change. +- Export portable skills through Hermes and update Claude and Cowork classifications in the same pass. +- Keep all repository documentation links portable and all runtime paths discovered rather than machine-coded. + +### Forward-test scenarios + +1. Choose between host, Apple container, container machine, full Linux VM, macOS VM, and physical Mac for a server-side Swift service that needs Linux compatibility and a local database. +2. Build a minimal Virtualization framework Linux VM configuration, validate it, boot headlessly, and explain each exposed device. +3. Install and run a macOS guest from a supported restore image, then prove a clean SIP-enabled error path without weakening the host. +4. Save and restore a paused compatible VM, then reject an incompatible configuration instead of describing the state file as a portable snapshot. +5. Use `container` 1.x for a disposable OCI service and `container machine` for a persistent systemd-based development environment; show why their mount and lifecycle policies differ. +6. Attempt a nested-virtualization workflow on a supported and unsupported configuration, preserving the exact host, kernel, and capability evidence. +7. Prepare an offline Linux analysis lab with no host home mount, clipboard, agent socket, credentials, or network, then verify evidence export and teardown. +8. Prepare a macOS analysis guest for a benign persistence fixture and distinguish guest evidence from host evidence. +9. Reject a Linux container as proof of macOS Gatekeeper, TCC, XProtect, LaunchServices, or native persistence behavior. +10. Escalate a hardware-, recoveryOS-, Secure Enclave-, or anti-VM-dependent question to a spare physical Mac with the unresolved fidelity gap stated plainly. + +Do not run untrusted payloads as forward-test fixtures. Use locally authored benign fixtures or public redistributable test artifacts, and obtain approval before starting visible VM apps, installing Apple `container`, downloading large restore images, creating large VM disks, or launching resource-intensive guests. + +## Documentation And Release Impact + +The planning slice changes only this maintainer plan and `ROADMAP.md`. It does not change the shipped skill inventory, plugin manifests, marketplace metadata, compatibility exports, or README inventory. + +Implementation should land in coherent phases: + +1. selection record, router, and Virtualization framework foundation +2. Apple `container` 1.0 expansion and Linux development VM workflow +3. macOS development VM workflow +4. disposable security-lab workflow and security-owner alignment + +Each phase should update its owning plugin skill metadata, tests, references, Hermes export, Claude and Cowork compatibility record, root architecture metadata, README inventory text when user-visible coverage changes, and `ROADMAP.md`. A phase that adds only portable guidance needs no MCP server or native host plugin. + +## Explicit Non-Goals + +- no new aggregate virtualization plugin +- no VM, container, kernel, init filesystem, restore image, malware sample, or tool database bundled in Socket +- no automatic Apple `container`, Lima, Colima, Tart, UTM, Docker, VMware, Parallels, or OrbStack installation +- no privileged helper, daemon, launch agent, guest agent, remote lab service, or credential broker +- no claim that Apple `container` runs macOS containers +- no claim that a macOS VM faithfully reproduces every hardware, recoveryOS, Secure Enclave, anti-VM, or device behavior +- no claim that saved machine state is equivalent to a complete disk snapshot or portable VM clone +- no automatic execution of suspicious content +- no weakening of host SIP, Gatekeeper, XProtect, TCC, App Sandbox, or other platform protections to make a lab easier to use diff --git a/plugins/agent-portability-skills/.codex-plugin/plugin.json b/plugins/agent-portability-skills/.codex-plugin/plugin.json index 599b63b9..d7e1d0f9 100644 --- a/plugins/agent-portability-skills/.codex-plugin/plugin.json +++ b/plugins/agent-portability-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agent-portability-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Maintainer skills for Socket-owned agent skill portability, Codex plugin surfaces, and host adapter guidance.", "author": { "name": "Gale", diff --git a/plugins/agent-portability-skills/pyproject.toml b/plugins/agent-portability-skills/pyproject.toml index 70c7aa7e..2175dd97 100644 --- a/plugins/agent-portability-skills/pyproject.toml +++ b/plugins/agent-portability-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agent-portability-skills-maintenance" -version = "9.18.0" +version = "9.19.0" description = "Maintainer-only Python tooling baseline for Agent Portability Skills." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/agent-portability-skills/uv.lock b/plugins/agent-portability-skills/uv.lock index 0ceb3921..a1b6c7fb 100644 --- a/plugins/agent-portability-skills/uv.lock +++ b/plugins/agent-portability-skills/uv.lock @@ -8,7 +8,7 @@ resolution-markers = [ [[package]] name = "agent-portability-skills-maintenance" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/agentdeck/.codex-plugin/plugin.json b/plugins/agentdeck/.codex-plugin/plugin.json index 2ca42f00..68764dfa 100644 --- a/plugins/agentdeck/.codex-plugin/plugin.json +++ b/plugins/agentdeck/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentdeck", - "version": "9.18.0", + "version": "9.19.0", "description": "Local Codex runtime utilities for thread, hook, and app-server workflows.", "author": { "name": "Gale", diff --git a/plugins/android-dev-skills/.codex-plugin/plugin.json b/plugins/android-dev-skills/.codex-plugin/plugin.json index 1c0c0228..e92ac920 100644 --- a/plugins/android-dev-skills/.codex-plugin/plugin.json +++ b/plugins/android-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "android-dev-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Android, Kotlin, Java, Gradle, Android Gradle Plugin, testing, lint, UI implementation, and release-readiness workflow skills.", "author": { "name": "Gale", diff --git a/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json b/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json index 49b36a2c..0dc2ba2a 100644 --- a/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json +++ b/plugins/apple-creator-studio-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "apple-creator-studio-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Human-facing and Computer Use-aware Apple Creator Studio workflows for Final Cut Pro editing, Motion templates, Compressor delivery, Logic Pro production, MainStage concert preparation, and GarageBand projects.", "author": { "name": "Gale", diff --git a/plugins/apple-dev-skills/.codex-plugin/plugin.json b/plugins/apple-dev-skills/.codex-plugin/plugin.json index 3925c9f7..7fe4b337 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": "9.18.0", - "description": "Apple development workflows for Codex, including SwiftPM plugins, macros, traits, app extensions, MailKit, File Provider, Finder Sync, App Intents, Liquid Glass, PhotosUI/PhotoKit, VideoToolbox codecs, ARKit spatial/face/body sensing, camera/depth capture, Core Image, Image I/O, Vision/Core ML recognition, media/audio repair, provisioning, CloudKit, TipKit, SwiftData, SwiftUI, XcodeGen, Core Animation, AppKit, Safari, security, OpenAPI, and DocC.", + "version": "9.19.0", + "description": "Apple development workflows for Codex, including macOS and Linux virtualization, SwiftPM, Xcode, app extensions, imaging, media, Vision/Core ML, spatial sensing, camera, provisioning, SwiftData, SwiftUI, AppKit, Safari, security, OpenAPI, and DocC.", "author": { "name": "Gale", "email": "mail@galewilliams.com", @@ -98,14 +98,19 @@ "openapi", "urlsession", "ios", - "macos" + "macos", + "virtualization", + "virtual-machine", + "linux-vm", + "macos-vm", + "virtualization-framework" ], "skills": "./skills/", "mcpServers": "./.mcp.json", "interface": { "displayName": "Apple Dev Skills", - "shortDescription": "Apple extension, SwiftPM, Swift, image/media, SwiftUI, Xcode, AppKit, security, OpenAPI, and DocC workflows for Codex.", - "longDescription": "Bundle Apple-platform development skills for app extension architecture, MailKit, File Provider and Finder Sync, PhotosUI selection and PhotoKit libraries/editing, VideoToolbox codecs, Core Video buffers, compressed samples, color/HDR, ARKit spatial/face/body sensing, world tracking, scene depth, LiDAR meshes and visionOS providers, AVFoundation camera/photo/depth/computational capture, Core Image, Image I/O, Apple Vision, custom Core ML recognition, AppKit/UIKit/Core Graphics images, AVFAudio, AVAudioEngine, AVFoundation media pipelines, Core Media, Core Audio, TipKit, XcodeGen migration, Xcode coding intelligence and String Catalog localization, Swift, SwiftUI, Core Animation, Apple typography, SF Symbols, AppKit, Icon Composer, Safari, DeviceCheck and App Attest, Swift OpenAPI clients, Xcode, SwiftPM, DocC, testing, formatting, and repository guidance. Most workflows work standalone; bootstrap and guidance-sync workflows require the companion Productivity Skills plugin, or the socket marketplace that installs both.", + "shortDescription": "Apple Swift, Xcode, virtualization, UI, media, security, and documentation workflows for Codex.", + "longDescription": "Bundle Apple-platform development skills for macOS-hosted boundary selection, custom Virtualization framework hosts, persistent Linux development guests, clean macOS development guests, app extensions, Photos, video, spatial sensing, camera, imaging, Vision and Core ML, audio and media, XcodeGen migration, coding intelligence, localization, Swift, SwiftUI, Core Animation, Apple typography, SF Symbols, AppKit, Safari, security, provisioning, Swift OpenAPI, SwiftPM, DocC, testing, formatting, and repository guidance. Most workflows work standalone; bootstrap and guidance-sync workflows require the companion Productivity Skills plugin, or the Socket marketplace that installs both.", "developerName": "Gale", "category": "Developer Tools", "capabilities": [ @@ -114,6 +119,10 @@ ], "websiteURL": "https://github.com/gaelic-ghost/apple-dev-skills", "defaultPrompt": [ + "Choose whether this macOS-hosted task belongs on the host, in an OCI container, an Apple container machine, a full Linux or macOS VM, or a physical Mac.", + "Design or diagnose a custom macOS or Linux VM host with Apple's Virtualization framework, explicit devices, lifecycle, validation, and save or restore boundaries.", + "Prepare and validate a persistent Linux development guest with explicit distro, services, resources, host integrations, nested-virtualization gates, and reset strategy.", + "Prepare a clean macOS development guest from a compatible restore image with explicit identity, artifact lifecycle, checkpoints, integrations, and physical-Mac fidelity gaps.", "Repair or modernize Apple media and audio code across AVFAudio sessions, AVAudioEngine graphs, AVFoundation pipelines, Core Media timing, and legacy Core Audio surfaces.", "Build or repair Core Image processing, RAW, color, HDR, filter, custom-kernel, and rendering pipelines using current Apple documentation.", "Decode, encode, inspect, thumbnail, preserve metadata, or bridge Apple image representations across Image I/O, Core Graphics, AppKit, UIKit, Core Image, and Core Video.", 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 9af8d89b..1ad8aa3a 100644 --- a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +++ b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh @@ -160,8 +160,12 @@ active_skill_mds=( "./skills/sync-swift-package-guidance/SKILL.md" "./skills/xcode-coding-intelligence-workflow/SKILL.md" "./skills/xcode-localization-workflow/SKILL.md" + "./skills/choose-macos-virtualization-shape/SKILL.md" + "./skills/virtualization-framework-workflow/SKILL.md" + "./skills/linux-development-vm-workflow/SKILL.md" + "./skills/macos-development-vm-workflow/SKILL.md" ) -[[ ${#active_skill_mds[@]} -eq 54 ]] || fail "Expected exactly 54 active skills, found ${#active_skill_mds[@]}." +[[ ${#active_skill_mds[@]} -eq 58 ]] || fail "Expected exactly 58 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 990b4a2a..752a3788 100644 --- a/plugins/apple-dev-skills/README.md +++ b/plugins/apple-dev-skills/README.md @@ -1,6 +1,6 @@ # apple-dev-skills -Apple app extensions, MailKit, File Provider, Finder Sync, SwiftPM plugins/macros/traits, Swift, image and video processing, Vision and Core ML recognition, camera and depth capture, ARKit spatial sensing, Photos, audio and media pipelines, 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. +Apple macOS and Linux virtualization, app extensions, MailKit, File Provider, Finder Sync, SwiftPM plugins/macros/traits, Swift, image and video processing, Vision and Core ML recognition, camera and depth capture, ARKit spatial sensing, Photos, audio and media pipelines, SwiftUI, AppKit, provisioning, Xcode, OpenAPI, 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) @@ -55,6 +55,10 @@ codex plugin marketplace upgrade socket Use Apple Dev Skills when an agent is helping with: +- Choosing among host execution, OCI containers, Apple container machines, full Linux or macOS VMs, remote environments, and physical Macs +- Building and diagnosing custom macOS or Linux VM hosts with Apple's Virtualization framework +- Preparing persistent Linux development guests with explicit services, resources, integration, validation, and reset boundaries +- Preparing clean macOS development guests for OS-version, installer, signing, privacy, security-control, and update validation - Strict Apple media type and framework selection across AVFoundation, AVFAudio, Core Media, Core Audio, and Audio Toolbox work - AVFAudio session, route, interruption, permission, and app-audio policy repair - AVAudioEngine graph, format, rendering, tap, and real-time callback repair @@ -142,6 +146,10 @@ uv run pytest ## Active Skills +- `choose-macos-virtualization-shape` +- `virtualization-framework-workflow` +- `linux-development-vm-workflow` +- `macos-development-vm-workflow` - `apple-ui-accessibility-workflow` - `apple-image-representation-workflow` - `arkit-face-body-tracking-workflow` diff --git a/plugins/apple-dev-skills/ROADMAP.md b/plugins/apple-dev-skills/ROADMAP.md index 38ed1738..c536ddc9 100644 --- a/plugins/apple-dev-skills/ROADMAP.md +++ b/plugins/apple-dev-skills/ROADMAP.md @@ -43,6 +43,7 @@ Swift naming and persistence ownership are now standardized: each project explic - [Milestone 64: Xcode String Catalog Localization Workflow](#milestone-64-xcode-string-catalog-localization-workflow) - [Milestone 65: Feedback Assistant Workflow](#milestone-65-feedback-assistant-workflow) - [Milestone 66: App Extension, MailKit, and File Provider Workflows](#milestone-66-app-extension-mailkit-and-file-provider-workflows) +- [Milestone 67: macOS and Linux Virtualization Workflows](#milestone-67-macos-and-linux-virtualization-workflows) - [Backlog Candidates](#backlog-candidates) - [History](#history) @@ -1266,6 +1267,29 @@ Completed Completed Milestone 66 by shipping `app-extension-architecture-workflow`, `mailkit-workflow`, and `file-provider-and-finder-sync-workflow` with Apple-docs-first boundaries, explicit Messaging Collaboration handoffs, and a validated Hermes skill-tap export. +## Milestone 67: macOS and Linux Virtualization Workflows + +### Status + +Completed + +### Scope + +- [x] Add `choose-macos-virtualization-shape` for one evidence-backed host, container, persistent Linux machine, full VM, remote, or physical-Mac decision. +- [x] Add `virtualization-framework-workflow` for guest-specific configuration, entitlement, devices, lifecycle, validation, and honest save/restore boundaries. +- [x] Add separate `linux-development-vm-workflow` and `macos-development-vm-workflow` owners for their different boot, identity, artifact, integration, and fidelity contracts. +- [x] Keep security-lab policy in Cybersecurity and Apple `container` command ownership in Server-Side Swift through explicit handoffs. +- [x] Add metadata, tests, customization contracts, shared Xcode guidance, and Hermes exports without adding a VM runtime, image, kernel, helper, or service. +- [x] Complete full validation, review, and finding remediation for the Socket minor release candidate. + +### Exit Criteria + +- [x] The Apple workflows choose and operate virtualization boundaries by required behavior rather than installed product preference. +- [x] macOS and Linux guests retain distinct platform, boot, identity, device, lifecycle, and evidence models. +- [x] All Apple Dev, root Socket, and portability gates pass from the reviewed release candidate. + +Completed Milestone 67 with four focused Apple virtualization workflows, owner-specific references, portable exports, metadata, and scenario contracts. The final reviewed candidate keeps guest implementation, guest operation, container commands, and security-lab policy in separate owners and is prepared for the Socket `9.19.0` minor release. + ## 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 75733b66..3ae4c880 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 `55` separate `references/customization.template.yaml` files. -- The active skill surface ships `55` separate `scripts/customization_config.py` entrypoints. +- The active skill surface ships `59` separate `references/customization.template.yaml` files. +- The active skill surface ships `59` 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`, `app-extension-architecture-workflow`, `mailkit-workflow`, `file-provider-and-finder-sync-workflow`, `devicecheck-app-attest-workflow`, `apple-developer-provisioning-workflow`, `swiftui-app-architecture-workflow`, `swiftui-component-audit-workflow`, `swiftui-animation-workflow`, `sf-symbols-workflow`, `core-animation-layer-workflow`, `apple-typography-workflow`, `appkit-app-architecture-workflow`, `xcode-coding-intelligence-workflow`, `xcode-localization-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 `choose-macos-virtualization-shape`, `virtualization-framework-workflow`, `linux-development-vm-workflow`, `macos-development-vm-workflow`, `apple-ui-accessibility-workflow`, `safari-extension-control-workflow`, `app-extension-architecture-workflow`, `mailkit-workflow`, `file-provider-and-finder-sync-workflow`, `devicecheck-app-attest-workflow`, `apple-developer-provisioning-workflow`, `swiftui-app-architecture-workflow`, `swiftui-component-audit-workflow`, `swiftui-animation-workflow`, `sf-symbols-workflow`, `core-animation-layer-workflow`, `apple-typography-workflow`, `appkit-app-architecture-workflow`, `xcode-coding-intelligence-workflow`, `xcode-localization-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/pyproject.toml b/plugins/apple-dev-skills/pyproject.toml index 2a76a927..ba8b4ab9 100644 --- a/plugins/apple-dev-skills/pyproject.toml +++ b/plugins/apple-dev-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "apple-dev-skills-maintainer" -version = "9.18.0" +version = "9.19.0" description = "Maintainer tooling for the apple-dev-skills repository" requires-python = ">=3.10" dependencies = [] diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md new file mode 100644 index 00000000..a2ebe6fb --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/SKILL.md @@ -0,0 +1,75 @@ +--- +name: choose-macos-virtualization-shape +description: Choose the smallest macOS-hosted boundary for development, compatibility, or authorized security research. Use when deciding among the host, containers, container machine, full Linux or macOS VMs, remote systems, or physical Macs. +--- + +# Choose macOS Virtualization Shape + +## Purpose + +Choose one boundary from evidence about fidelity, persistence, portability, host integration, threat level, and resources. Produce the shared shape record in [virtualization-shape-record.md](references/virtualization-shape-record.md); do not return an undecided product menu. + +## When To Use + +- Use for macOS-hosted development, clean-state validation, Linux compatibility, custom VM tools, and security-lab boundary selection. +- Use when container, persistent Linux machine, full VM, and physical Mac terminology is being mixed. +- Use before `virtualization-framework-workflow`, either development-VM workflow, or `prepare-isolated-analysis-lab` when the boundary is not already approved. + +## Single-Path Workflow + +1. Record the purpose, host, target OS and architecture, GUI/headless needs, privileges, kernel/devices, expected lifetime, and evidence requirements. +2. Classify fidelity and risk: + - trusted native macOS behavior: host process + - one portable Linux application: OCI container + - Apple-native per-container VM runtime: Apple `container` + - persistent OCI-backed Linux environment with services: `container machine` + - custom kernel, boot, disk, full-system, or GUI Linux: full Linux VM + - native macOS security, installer, signing, privacy, or OS-version behavior: macOS VM + - hardware, recoveryOS, Secure Enclave, unsupported device, performance, or anti-VM behavior: physical Mac +3. Reject any option that cannot reproduce the target or safely bound the expected behavior. +4. Choose one primary boundary and one explicit fallback only when a named fidelity or availability gap requires it. +5. Complete the shape record with provenance, resources, integrations, lifecycle, validation, evidence, and uncertainty. +6. Hand off implementation or operation to the owner skill. + +## Inputs + +- Task purpose and target behavior. +- Host chip, macOS build, memory, storage, and toolchain. +- Guest OS/version/distro, architecture, devices, privilege, and lifetime. +- Portability, persistence, integration, isolation, and evidence requirements. + +## Outputs + +- `status`: `success`, `handoff`, or `blocked`. +- One selected `boundary` and the reason it is the smallest adequate choice. +- A completed virtualization shape record. +- One owner handoff and any unresolved fidelity gap. + +## Guards and Stop Conditions + +- Do not call a Linux container or Linux VM evidence for native macOS behavior such as Gatekeeper, TCC, XProtect, LaunchServices, or macOS persistence. +- Do not treat automatic home sharing, clipboard, credentials, or sockets as harmless defaults. +- Do not claim two products have equivalent isolation because they use the same framework. +- Do not promise saved VM state is a disk snapshot or portable clone. +- Stop when target fidelity, host capacity, authorization, or safe evidence handling cannot be established. +- Tell Gale before launching a GUI VM, starting a VM or container service, downloading a restore image, creating a large disk, or running a resource-intensive workload. + +## Fallbacks and Handoffs + +- Use `server-side-swift:docker-workflow` for portable OCI authoring and deployment. +- Use `server-side-swift:apple-containerization-workflow` for Apple `container`, `container machine`, or Containerization APIs. +- Use `virtualization-framework-workflow` for custom VM host implementation. +- Use `linux-development-vm-workflow` or `macos-development-vm-workflow` for guest lifecycle work. +- Use `cybersecurity-skills:select-analysis-isolation` and `prepare-isolated-analysis-lab` for untrusted material. +- Escalate to a physical Mac with the unresolved gap stated when VM fidelity is insufficient. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [Virtualization shape record](references/virtualization-shape-record.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for repository guidance when implementation enters an Xcode project. diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/agents/openai.yaml b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/agents/openai.yaml new file mode 100644 index 00000000..f2fb9585 --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Choose macOS Virtualization Shape" + short_description: "Choose the right macOS-hosted compute boundary" + default_prompt: "Use $choose-macos-virtualization-shape to choose a host, container, VM, or physical Mac boundary for this task." diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization-flow.md b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization.template.yaml b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/choose-macos-virtualization-shape/references/virtualization-shape-record.md b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/virtualization-shape-record.md new file mode 100644 index 00000000..653a050d --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/references/virtualization-shape-record.md @@ -0,0 +1,17 @@ +# Virtualization Shape Record + +Record this compact handoff before implementation. It is a reasoning shape, not a serialized compatibility layer. + +- `purpose`: development, compatibility, CI-like validation, security analysis, framework implementation, or runtime diagnosis +- `host`: Mac/chip, architecture, macOS build, memory/storage budget, and selected toolchain +- `workload`: target OS/version/distro, architecture, GUI/headless, privileges, kernel/devices, and lifetime +- `boundary`: host, OCI container, Apple per-container VM, Apple container machine, full Linux VM, full macOS VM, remote environment, or physical Mac +- `provenance`: tool/framework, restore or OCI image, kernel/init filesystem, and exact versions/digests +- `resources`: CPU, memory, disks, graphics, audio, USB, ballooning, and performance limits +- `integration`: shares, clipboard, sockets, ports, network, agents, credentials, identities, accounts, and browser profiles +- `lifecycle`: supported create/install/start/pause/save/restore/stop/clone/reset/update/export/remove operations +- `validation`: configuration, boot, identity, network, filesystem, service, application, and teardown checks +- `evidence`: logs, artifacts, capture, hashes, screenshots, state, and safe export path +- `uncertainty`: unsupported combinations, beta APIs, VM artifacts, hardware gaps, and physical-device requirements + +Use `absent`, `disabled`, or `not supported` instead of leaving a security-sensitive integration implicit. diff --git a/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/scripts/customization_config.py b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/scripts/customization_config.py new file mode 100755 index 00000000..805ecef7 --- /dev/null +++ b/plugins/apple-dev-skills/skills/choose-macos-virtualization-shape/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "choose-macos-virtualization-shape" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md new file mode 100644 index 00000000..e12c77e5 --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/SKILL.md @@ -0,0 +1,74 @@ +--- +name: linux-development-vm-workflow +description: Prepare and reset persistent Linux development guests on macOS. Use when comparing container machine, Lima or Colima, and full VMs for distros, init systems, services, custom boot, disks, Rosetta, or nested virtualization. +--- + +# Linux Development VM Workflow + +## Purpose + +Prepare one persistent Linux development environment whose lifecycle, host integrations, provenance, validation, and reset path are explicit. + +## When To Use + +- Use for distro-specific builds, services, systemd or another init system, repeated shells, full-system tests, custom kernels, EFI boot, or GUI Linux. +- Use to decide between `container machine`, Lima/Colima, and a full VM by required fidelity rather than product preference. +- Do not use for a single portable application image; use the container owner skills. + +## Single-Path Workflow + +1. Consume the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +2. Discover current official documentation and installed versions/help for every candidate tool. +3. Select the smallest adequate path: + - `container machine`: OCI-backed persistent Linux, init/services, repeated interactive development + - Lima/Colima adapter: tool-managed Linux environment when its documented lifecycle and integration match the task + - full Virtualization framework VM: custom boot/kernel/disk/devices, full-system or GUI behavior, or tighter integration control +4. Record distro/image/kernel provenance, architecture, CPU, memory, disks, network, mounts, sockets, credentials, and expected lifetime. +5. Keep host home, writeable shares, SSH agent, credentials, clipboard, and unrestricted network opt-in. A development convenience is not a security boundary. +6. Configure Linux or EFI boot, virtio devices, provisioning, services, Rosetta, and nested virtualization only when the selected path and current host/guest support them. +7. Define create, provision, start, shell/SSH, stop, update, checkpoint/reset, export, and remove semantics using the selected tool's vocabulary. +8. Validate the distro matrix: identity, architecture, toolchain, build, tests, services, filesystem semantics, network, reboot persistence, and cleanup. + +## Inputs + +- Completed virtualization shape record. +- Distro/version, architecture, system services, boot/kernel needs, toolchain, resources, integrations, and reset frequency. +- Exact selected tool version and official documentation. + +## Outputs + +- Selected Linux guest path and rejected alternatives. +- Provenance and resource/integration record. +- Exact lifecycle and provisioning path. +- Distro-matrix validation and reset/teardown evidence. + +## Guards and Stop Conditions + +- Do not call `container machine` a macOS VM, ordinary application container, or Compose replacement. +- Do not assume Docker, Apple `container`, Lima, Colima, or a custom VM share flags or lifecycle semantics. +- Do not enable home sharing for untrusted work; hand security research to `prepare-isolated-analysis-lab`. +- Do not promise Rosetta or nested virtualization without host, OS, kernel, and device proof. +- Do not commit images, kernels, disks, credentials, or machine-local runtime state. +- Stop when provenance, capacity, reset strategy, host integration, or required fidelity cannot be verified. +- Announce before starting a VM/service, downloading an image, or creating a large disk. + +## Fallbacks and Handoffs + +- Use `server-side-swift:apple-containerization-workflow` for `container machine` command semantics. +- Use `server-side-swift:docker-workflow` for Dockerfiles, Compose, registries, and portable OCI deployment. +- Use `virtualization-framework-workflow` for custom full-VM implementation. +- Use `xcode-build-run-workflow`, `swift-package-build-run-workflow`, or stack-specific skills after the guest is ready. +- Use `prepare-isolated-analysis-lab` for disposable hostile-workload controls. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [Linux development guest matrix](references/linux-development-guest-matrix.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple container machine documentation](https://github.com/apple/container/blob/main/docs/container-machine.md) +- [Lima documentation](https://lima-vm.io/docs/) +- [Colima repository](https://github.com/abiosoft/colima) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for a custom Xcode VM host. diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/agents/openai.yaml b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/agents/openai.yaml new file mode 100644 index 00000000..46a054e8 --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Linux Development VM Workflow" + short_description: "Prepare persistent Linux development guests" + default_prompt: "Use $linux-development-vm-workflow to choose, prepare, validate, and reset this Linux development guest." diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization-flow.md b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization.template.yaml b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md new file mode 100644 index 00000000..96b7d729 --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md @@ -0,0 +1,17 @@ +# Linux Development Guest Matrix + +Use exact current docs and installed help; this matrix chooses a shape, not a brand default. + +| Need | Container machine | Lima/Colima adapter | Full Virtualization framework VM | +| --- | --- | --- | --- | +| Persistent interactive Linux | primary fit | primary fit | supported with more ownership | +| OCI-image-backed root | primary model | tool-specific | custom image/disk work | +| Init and services | supported by compatible image | tool-specific | guest-owned | +| Custom kernel/boot | limited to documented machine/kernel controls | tool-specific | primary fit | +| Full disk/install lifecycle | not the primary model | tool-specific | primary fit | +| GUI Linux | not primary | tool-specific | explicit graphics/input/UI path | +| Host integration | automatic user/home conveniences require review | tool-specific mounts/sockets | explicitly selected devices/shares | +| Portable OCI deployment | hand off to Docker workflow | hand off to Docker workflow | hand off to Docker workflow | +| Hostile workload | disable home integration; still require lab review | require lab review | require lab profile and verified controls | + +For every selected distro record: image/digest, OS release, architecture, kernel, init, toolchain, build/test result, services, filesystem, network, reboot persistence, reset, and removal. diff --git a/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/linux-development-vm-workflow/scripts/customization_config.py b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/scripts/customization_config.py new file mode 100755 index 00000000..e814a986 --- /dev/null +++ b/plugins/apple-dev-skills/skills/linux-development-vm-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "linux-development-vm-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md new file mode 100644 index 00000000..7c2386dc --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/SKILL.md @@ -0,0 +1,70 @@ +--- +name: macos-development-vm-workflow +description: Prepare and reset clean macOS development guests on Apple silicon. Use for restore-image compatibility, VM identity, installation, resources, OS-version testing, signing, privacy, and disposable macOS research guests. +--- + +# macOS Development VM Workflow + +## Purpose + +Prepare a reproducible macOS guest while keeping restore images, identity, disks, saved state, clones, integrations, and exported evidence as separate lifecycle artifacts. + +## When To Use + +- Use for clean macOS releases, installers, updates, signing, entitlements, quarantine, Gatekeeper, XProtect, TCC, SIP-enabled behavior, LaunchServices, and native persistence. +- Use after custom host implementation or with an existing documented VM manager. +- Use for development guests and benign security fixtures; security controls still require `prepare-isolated-analysis-lab` for untrusted execution. + +## Single-Path Workflow + +1. Consume the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +2. Verify Apple silicon host, host build, current framework/tool docs, restore-image support, guest build, storage, memory, and installation time budget. +3. Record each artifact using [macOS VM artifact lifecycle](references/macos-vm-artifact-lifecycle.md): restore image, hardware model, machine identifier, auxiliary storage, disk, bundle metadata, saved state, clone, and evidence export. +4. Build or verify a compatible Mac platform, boot loader, disk, CPU/memory, graphics/display, network, input, and entropy configuration; validate before installation or boot. +5. Install with the documented restore-image flow and preserve exact progress/errors. Do not invent or duplicate Mac identity artifacts. +6. Configure guest purpose and integrations. Directory sharing, clipboard, audio input, USB, Apple account, iCloud, developer account, signing identities, browser profiles, and network are opt-in. +7. Establish a clean baseline, then choose named development/update checkpoints or disposable clones using only lifecycle operations the selected tool actually supports. +8. Validate guest build/architecture, SIP and relevant controls, network/shares, reboot, toolchain, target behavior, evidence export, and reset. +9. Record VM artifacts and physical-hardware gaps that may affect the conclusion. + +## Inputs + +- Completed virtualization shape record. +- Host and target guest builds, restore-image source, resources, selected VM tool/framework, integrations, and validation purpose. +- Required toolchains, identities, accounts, security controls, reset strategy, and evidence path. + +## Outputs + +- Compatible restore/image and VM identity record. +- Separate artifact lifecycle and integration decisions. +- Installation, baseline/checkpoint, validation, export, and reset evidence. +- Explicit physical-Mac or unsupported-capability gaps. + +## Guards and Stop Conditions + +- Do not treat an arbitrary restore image as compatible with the host/platform configuration. +- Do not conflate saved machine state with disk state, a clone, or a portable snapshot. +- Do not copy identity artifacts between independent VMs without documented tool support and an explicit identity decision. +- Do not add personal Apple accounts, developer identities, credentials, shares, clipboard, devices, or microphone by default. +- Treat automated macOS guest provisioning as beta and availability-gated until current SDK/runtime evidence proves the selected path. +- Stop for unsupported restore compatibility, unresolved disk ownership, insufficient capacity, unclear identity, or a hardware/recoveryOS/Secure Enclave fidelity requirement. +- Announce before downloads, large disk creation, installation, or visible/resource-intensive launch. + +## Fallbacks and Handoffs + +- Use `virtualization-framework-workflow` for custom host configuration or lifecycle defects. +- Use `apple-developer-provisioning-workflow` only after the guest boundary is approved and ready. +- Use `xcode-build-run-workflow`, `xcode-testing-workflow`, or `macos-distribution-workflow` for work inside the prepared guest. +- Use `prepare-isolated-analysis-lab` before executing untrusted content. +- Use a spare physical Mac when hardware, recoveryOS, Secure Enclave, device, performance, or anti-VM fidelity is required. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [macOS VM artifact lifecycle](references/macos-vm-artifact-lifecycle.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for a custom Xcode VM host. diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/agents/openai.yaml b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/agents/openai.yaml new file mode 100644 index 00000000..fd6b8f11 --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "macOS Development VM Workflow" + short_description: "Prepare clean macOS development guests" + default_prompt: "Use $macos-development-vm-workflow to prepare, validate, checkpoint, and reset this macOS development guest." diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization-flow.md b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization.template.yaml b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md new file mode 100644 index 00000000..a36dbec6 --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md @@ -0,0 +1,17 @@ +# macOS VM Artifact Lifecycle + +Keep these artifacts separate; no single file is “the VM snapshot.” + +| Artifact | Owns | Lifecycle rule | +| --- | --- | --- | +| Restore image | installer and supported macOS build metadata | verify compatibility and provenance; cache/remove independently | +| Hardware model | supported virtual Mac hardware description | bind to compatible configuration and guest install | +| Machine identifier | virtual Mac identity | generate/persist deliberately; do not casually duplicate | +| Auxiliary storage | platform boot/security state | persist with its VM identity and installed guest | +| Disk image | guest filesystem and installed software | coordinate shutdown/copy semantics; saved state does not replace it | +| VM bundle metadata | configuration and artifact locations | keep portable paths and explicit schema/version ownership | +| Saved machine state | paused/stopped runtime state | restore only with documented state and compatible configuration/artifacts | +| Clone/checkpoint | tool-specific copy of required artifacts | name the exact copy/revert operation; do not imply framework snapshots | +| Evidence export | intentionally selected logs/artifacts | export narrowly, hash/scan as required, and keep separate from control state | + +Record ownership, permissions, size, source/digest, creation time, compatible host/guest versions, and removal semantics for every artifact. diff --git a/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/macos-development-vm-workflow/scripts/customization_config.py b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/scripts/customization_config.py new file mode 100755 index 00000000..163086d4 --- /dev/null +++ b/plugins/apple-dev-skills/skills/macos-development-vm-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "macos-development-vm-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md new file mode 100644 index 00000000..eed2a8e8 --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/SKILL.md @@ -0,0 +1,72 @@ +--- +name: virtualization-framework-workflow +description: Build and diagnose custom macOS and Linux VM hosts with Apple's Virtualization framework. Use for platform and boot configuration, devices, VM bundles, lifecycle, save and restore, UI, entitlements, and framework errors. +--- + +# Virtualization Framework Workflow + +## Purpose + +Implement one explicit macOS or Linux Virtualization framework path without flattening their platform, boot, identity, or device differences. + +## When To Use + +- Use for `VZVirtualMachineConfiguration`, guest devices, `VZVirtualMachine`, `VZVirtualMachineView`, lifecycle, and diagnostics. +- Use when building a custom VM host app or Swift package rather than operating an existing VM manager. +- Use for save/restore capability checks, not as a general snapshot-product workflow. + +## Single-Path Workflow + +1. Read current Xcode-local Virtualization documentation for every selected API and availability gate. +2. Consume or create the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +3. Choose the guest family using [macOS and Linux guest matrix](references/macos-and-linux-guest-matrix.md): + - macOS: Mac platform identity, macOS boot loader, restore-image compatibility, auxiliary storage + - Linux/generic: generic platform, Linux or EFI boot, kernel/initrd/command line or EFI disk +4. Separate the implementation into configuration construction, bundle/artifact persistence, VM lifecycle, and optional UI ownership. Make a headless console/service path or `VZVirtualMachineView` ownership explicit rather than creating both accidentally. +5. Add only required devices after checking [device and availability matrix](references/virtualization-device-and-availability-matrix.md). +6. Require the virtualization entitlement, supported CPU/memory values, exact OS availability, and `validate()` before start. +7. Model start, pause, resume, stop, and state transitions explicitly. Save/restore only in documented states with a configuration compatible with the saved state. +8. Validate configuration, boot, console/UI, disk, network, shares, services, shutdown, and teardown at the narrowest relevant level. +9. Preserve the failed configuration surface, VM state, host/guest versions, underlying error, and likely cause. + +## Inputs + +- Completed virtualization shape record. +- Guest family, boot source, identity artifacts, disks, devices, resources, UI needs, and lifecycle requirements. +- Host macOS/Xcode version and target deployment version. + +## Outputs + +- `status`: `success`, `handoff`, or `blocked`. +- Documented configuration and availability decisions. +- Separate configuration, artifact, lifecycle, and UI ownership. +- Validation evidence and exact diagnostics. + +## Guards and Stop Conditions + +- Do not start before configuration validation succeeds. +- Do not reuse a macOS hardware model, machine identifier, or auxiliary storage as if it were a generic Linux platform. +- Do not expose shares, clipboard, sockets, devices, audio input, or USB without a stated need. +- Do not call saved machine state a disk snapshot, clone, or portable VM bundle. +- Do not promise nested virtualization, Rosetta, clipboard, USB, or save/restore without guest and OS capability proof. +- Stop when the restore image, boot artifacts, entitlement, host support, configuration compatibility, or disk ownership is unresolved. +- Announce before any visible or resource-intensive launch. + +## Fallbacks and Handoffs + +- Use `choose-macos-virtualization-shape` when the boundary is undecided. +- Use `linux-development-vm-workflow` or `macos-development-vm-workflow` for guest preparation and reset strategy. +- Use `xcode-app-project-workflow` for target membership, entitlement wiring, and app-project integration. +- Use `xcode-build-run-workflow` and `xcode-testing-workflow` for execution and tests. +- Use `prepare-isolated-analysis-lab` for hostile-workload control policy. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [macOS and Linux guest matrix](references/macos-and-linux-guest-matrix.md) +- [Device and availability matrix](references/virtualization-device-and-availability-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) when editing an Xcode project. diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/agents/openai.yaml b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/agents/openai.yaml new file mode 100644 index 00000000..a4531283 --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Virtualization Framework Workflow" + short_description: "Build and diagnose Apple virtual machines" + default_prompt: "Use $virtualization-framework-workflow to design or diagnose this Virtualization framework host." diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization-flow.md b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization.template.yaml b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md new file mode 100644 index 00000000..3d639a68 --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md @@ -0,0 +1,15 @@ +# macOS And Linux Guest Matrix + +| Concern | macOS guest | Linux or generic guest | +| --- | --- | --- | +| Platform | `VZMacPlatformConfiguration` | `VZGenericPlatformConfiguration` | +| Boot | `VZMacOSBootLoader` plus compatible restore image | `VZLinuxBootLoader` or EFI boot loader | +| Identity | hardware model, machine identifier, auxiliary storage | generic platform; no Mac identity artifacts | +| Installation | restore-image and installer flow | kernel/initrd/root filesystem or EFI installer/disk | +| Graphics/UI | Mac graphics device and display | virtio graphics when supported/needed | +| Rosetta | not an assumed nested container capability | Linux Rosetta directory share when documented and supported | +| Nested virtualization | do not infer support from generic-platform APIs | capability-gated generic platform plus guest kernel/device proof | +| Security fidelity | useful for native macOS controls, with VM artifacts | cannot prove Gatekeeper, TCC, XProtect, LaunchServices, or macOS persistence | +| Physical fidelity | limited for hardware, Secure Enclave, recoveryOS, devices, performance, and anti-VM behavior | limited for host-hardware and anti-VM behavior | + +Always check the current SDK documentation and host capabilities; this table identifies ownership differences, not universal availability. diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md new file mode 100644 index 00000000..2816c6eb --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md @@ -0,0 +1,20 @@ +# Virtualization Device And Availability Matrix + +Check current Xcode-local documentation for every concrete class before implementing it. Record host OS, guest family, minimum deployment target, required guest support, and whether the device crosses a security boundary. + +| Family | Decision to record | +| --- | --- | +| Storage | image/block device ownership, caching/synchronization, read-only state, attachment lifetime | +| Network | NAT or bridged attachment, MAC address, ports, monitoring, external reachability | +| Directory sharing | exact host directory, read/write state, tag/mount point, hostile-workload prohibition | +| Socket/console | endpoint ownership, authentication, serial console/log retention | +| Graphics/input | display dimensions, headless/UI path, keyboard and pointing devices | +| Audio | output/input need; microphone access remains opt-in | +| USB | controller/device support and explicit passthrough need | +| Memory balloon | guest support and expected pressure behavior | +| Entropy | guest random device requirement | +| Rosetta | supported Linux guest path and installation/share requirements | +| Nested virtualization | host/chip/OS support, generic-platform setting, guest kernel, `/dev/kvm` proof | +| Save/restore | host API availability, paused/stopped state rule, compatible configuration and artifacts | + +Never add a device merely because the API exists. Each device expands behavior, failure surface, or host integration. diff --git a/plugins/apple-dev-skills/skills/virtualization-framework-workflow/scripts/customization_config.py b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/scripts/customization_config.py new file mode 100755 index 00000000..0c2ec7f6 --- /dev/null +++ b/plugins/apple-dev-skills/skills/virtualization-framework-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "virtualization-framework-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/tests/test_app_extension_workflows.py b/plugins/apple-dev-skills/tests/test_app_extension_workflows.py index e8501e0b..01c6e29d 100644 --- a/plugins/apple-dev-skills/tests/test_app_extension_workflows.py +++ b/plugins/apple-dev-skills/tests/test_app_extension_workflows.py @@ -62,7 +62,7 @@ def test_inventory_and_metadata_include_the_new_skills(self) -> None: self.assertIn(f"`{skill}`", readme) self.assertIn("mailkit", manifest) self.assertIn("file-provider", manifest) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) if __name__ == "__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 index 31e00e2d..01358794 100644 --- a/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py +++ b/plugins/apple-dev-skills/tests/test_apple_developer_provisioning_workflow.py @@ -55,7 +55,7 @@ def test_inventory_metadata_and_roadmap_are_updated(self) -> None: 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 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn("Milestone 54: Apple Developer Provisioning and CloudKit Workflow - Completed", roadmap) def test_customization_cli_preserves_shared_apply_and_reset_verbs(self) -> None: diff --git a/plugins/apple-dev-skills/tests/test_arkit_spatial_face_body_workflows.py b/plugins/apple-dev-skills/tests/test_arkit_spatial_face_body_workflows.py index e2861df8..ccb925a5 100644 --- a/plugins/apple-dev-skills/tests/test_arkit_spatial_face_body_workflows.py +++ b/plugins/apple-dev-skills/tests/test_arkit_spatial_face_body_workflows.py @@ -109,7 +109,7 @@ def test_inventory_metadata_customization_and_cross_skill_handoffs_are_aligned(s ) self.assertIn("ARKit", plugin) self.assertIn("LiDAR", plugin) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn("arkit-spatial-sensing-workflow", self.read("skills/camera-capture-depth-workflow/SKILL.md")) self.assertIn("arkit-face-body-tracking-workflow", self.read("skills/vision-image-analysis-workflow/SKILL.md")) self.assertIn("arkit-spatial-sensing-workflow", self.read("skills/apple-ui-accessibility-workflow/SKILL.md")) diff --git a/plugins/apple-dev-skills/tests/test_camera_capture_depth_workflow.py b/plugins/apple-dev-skills/tests/test_camera_capture_depth_workflow.py index 3ec9d0cc..f2f6f68c 100644 --- a/plugins/apple-dev-skills/tests/test_camera_capture_depth_workflow.py +++ b/plugins/apple-dev-skills/tests/test_camera_capture_depth_workflow.py @@ -93,7 +93,7 @@ def test_inventory_metadata_customization_and_handoffs_are_aligned(self) -> None ) self.assertIn("camera", plugin.lower()) self.assertIn("depth", plugin.lower()) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn(skill, self.read("skills/avfoundation-media-pipeline-workflow/SKILL.md")) self.assertIn(skill, self.read("skills/vision-image-analysis-workflow/SKILL.md")) 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 e61f0154..3a271255 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, 55) - self.assertEqual(script_count, 55) + self.assertEqual(template_count, 59) + self.assertEqual(script_count, 59) 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 95428109..f42a86ac 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 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn("Milestone 53: DeviceCheck and App Attest Workflow - Completed", roadmap) diff --git a/plugins/apple-dev-skills/tests/test_imaging_foundation_workflows.py b/plugins/apple-dev-skills/tests/test_imaging_foundation_workflows.py index 1c526a76..f3db79a0 100644 --- a/plugins/apple-dev-skills/tests/test_imaging_foundation_workflows.py +++ b/plugins/apple-dev-skills/tests/test_imaging_foundation_workflows.py @@ -95,7 +95,7 @@ def test_inventory_metadata_and_customization_are_aligned(self) -> None: self.assertIn("Core Image", plugin) self.assertIn("Image I/O", plugin) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_macos_virtualization_workflows.py b/plugins/apple-dev-skills/tests/test_macos_virtualization_workflows.py new file mode 100644 index 00000000..b9f28f54 --- /dev/null +++ b/plugins/apple-dev-skills/tests/test_macos_virtualization_workflows.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def read(relative: str) -> str: + return (ROOT / relative).read_text(encoding="utf-8") + + +def assert_skill_contract(skill: str, *phrases: str) -> None: + contents = read(f"skills/{skill}/SKILL.md").lower() + missing = [phrase for phrase in phrases if phrase.lower() not in contents] + assert not missing, f"{skill} is missing virtualization contract phrases: {missing}" + + +def test_shape_router_selects_one_boundary_and_records_uncertainty() -> None: + assert_skill_contract( + "choose-macos-virtualization-shape", + "do not return an undecided product menu", + "apple `container`", + "persistent oci-backed linux environment", + "native macos security", + "secure enclave", + "virtualization shape record", + ) + + +def test_framework_workflow_keeps_guest_and_state_models_separate() -> None: + assert_skill_contract( + "virtualization-framework-workflow", + "macos or linux virtualization framework path", + "require the virtualization entitlement", + "`validate()` before start", + "do not call saved machine state a disk snapshot", + "announce before any visible or resource-intensive launch", + ) + + +def test_linux_workflow_separates_machine_adapters_and_full_vm() -> None: + assert_skill_contract( + "linux-development-vm-workflow", + "`container machine`", + "lima/colima adapter", + "full virtualization framework vm", + "development convenience is not a security boundary", + "nested virtualization", + ) + + +def test_macos_workflow_separates_identity_disk_state_and_evidence() -> None: + assert_skill_contract( + "macos-development-vm-workflow", + "restore images, identity, disks, saved state, clones", + "sip and relevant controls", + "do not conflate saved machine state with disk state", + "physical mac", + ) + + +def test_inventory_metadata_and_customization_contracts_include_all_four() -> None: + validator = read(".github/scripts/validate_repo_docs.sh") + readme = read("README.md") + manifest = read(".codex-plugin/plugin.json") + for skill in ( + "choose-macos-virtualization-shape", + "virtualization-framework-workflow", + "linux-development-vm-workflow", + "macos-development-vm-workflow", + ): + assert f"./skills/{skill}/SKILL.md" in validator + assert f"`{skill}`" in readme + assert (ROOT / "skills" / skill / "agents" / "openai.yaml").is_file() + assert (ROOT / "skills" / skill / "references" / "customization.template.yaml").is_file() + assert (ROOT / "skills" / skill / "scripts" / "customization_config.py").is_file() + assert "Expected exactly 58 active skills" in validator + assert "virtualization-framework" in manifest diff --git a/plugins/apple-dev-skills/tests/test_photos_library_editing_workflow.py b/plugins/apple-dev-skills/tests/test_photos_library_editing_workflow.py index 9ab2c2c9..28144c7d 100644 --- a/plugins/apple-dev-skills/tests/test_photos_library_editing_workflow.py +++ b/plugins/apple-dev-skills/tests/test_photos_library_editing_workflow.py @@ -95,7 +95,7 @@ def test_no_repository_inventory_metadata_customization_and_handoffs_are_aligned ) self.assertIn("PhotosUI", plugin) self.assertIn("PhotoKit", plugin) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn(name, self.read("skills/apple-image-representation-workflow/SKILL.md")) self.assertIn(name, self.read("skills/core-image-processing-workflow/SKILL.md")) self.assertIn(name, self.read("skills/avfoundation-media-pipeline-workflow/SKILL.md")) diff --git a/plugins/apple-dev-skills/tests/test_tipkit_workflow.py b/plugins/apple-dev-skills/tests/test_tipkit_workflow.py index 9f447844..7eb357a6 100644 --- a/plugins/apple-dev-skills/tests/test_tipkit_workflow.py +++ b/plugins/apple-dev-skills/tests/test_tipkit_workflow.py @@ -52,7 +52,7 @@ def test_inventory_and_metadata_include_tipkit(self) -> None: self.assertIn("tipkit-workflow", readme) self.assertIn("TipKit", plugin) self.assertIn("./skills/tipkit-workflow/SKILL.md", validator) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn("Milestone 55: TipKit Workflow", roadmap) diff --git a/plugins/apple-dev-skills/tests/test_video_codec_processing_workflow.py b/plugins/apple-dev-skills/tests/test_video_codec_processing_workflow.py index 03bcaf9d..e854907c 100644 --- a/plugins/apple-dev-skills/tests/test_video_codec_processing_workflow.py +++ b/plugins/apple-dev-skills/tests/test_video_codec_processing_workflow.py @@ -92,7 +92,7 @@ def test_avfoundation_preference_inventory_metadata_and_handoffs_are_aligned(sel ) self.assertIn("VideoToolbox", plugin) self.assertIn("Core Video", plugin) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) self.assertIn(name, self.read("skills/avfoundation-media-pipeline-workflow/SKILL.md")) self.assertIn(name, self.read("skills/coremedia-timing-samplebuffer-workflow/SKILL.md")) diff --git a/plugins/apple-dev-skills/tests/test_vision_recognition_workflows.py b/plugins/apple-dev-skills/tests/test_vision_recognition_workflows.py index 142c31b1..14382420 100644 --- a/plugins/apple-dev-skills/tests/test_vision_recognition_workflows.py +++ b/plugins/apple-dev-skills/tests/test_vision_recognition_workflows.py @@ -89,7 +89,7 @@ def test_inventory_metadata_customization_and_handoffs_are_aligned(self) -> None ) self.assertIn("Apple Vision", plugin) self.assertIn("Core ML", plugin) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py b/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py index 75945f44..385f0d84 100644 --- a/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py +++ b/plugins/apple-dev-skills/tests/test_xcode_localization_workflow.py @@ -53,7 +53,7 @@ def test_inventory_metadata_and_roadmap_name_the_shipped_skill(self) -> None: self.assertIn("xcode-localization-workflow", text) self.assertIn("String Catalog localization", manifest) self.assertIn('"string-catalog"', manifest) - self.assertIn("Expected exactly 54 active skills", validator) + self.assertIn("Expected exactly 58 active skills", validator) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/uv.lock b/plugins/apple-dev-skills/uv.lock index b2243a0d..21932feb 100644 --- a/plugins/apple-dev-skills/uv.lock +++ b/plugins/apple-dev-skills/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.10" [[package]] name = "apple-dev-skills-maintainer" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/cardhop-app/.codex-plugin/plugin.json b/plugins/cardhop-app/.codex-plugin/plugin.json index f400eded..222312e4 100644 --- a/plugins/cardhop-app/.codex-plugin/plugin.json +++ b/plugins/cardhop-app/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cardhop-app", - "version": "9.18.0", + "version": "9.19.0", "description": "Cardhop.app workflow guidance plus a bundled local MCP server for contact capture and updates on macOS.", "author": { "name": "Gale", diff --git a/plugins/cardhop-app/mcp/pyproject.toml b/plugins/cardhop-app/mcp/pyproject.toml index 8762b50d..520b9bb5 100644 --- a/plugins/cardhop-app/mcp/pyproject.toml +++ b/plugins/cardhop-app/mcp/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cardhop-app-mcp" -version = "9.18.0" +version = "9.19.0" requires-python = ">=3.13" dependencies = [ "fastmcp>=3.0.2", diff --git a/plugins/cardhop-app/mcp/uv.lock b/plugins/cardhop-app/mcp/uv.lock index fe769bc8..459dea90 100644 --- a/plugins/cardhop-app/mcp/uv.lock +++ b/plugins/cardhop-app/mcp/uv.lock @@ -141,7 +141,7 @@ wheels = [ [[package]] name = "cardhop-app-mcp" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } dependencies = [ { name = "fastmcp" }, diff --git a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json index 30ca3e3c..f873fba7 100644 --- a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-deployment-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for routing cloud deployment work through official provider plugins, MCP servers, CLIs, and Socket-owned deployment guidance.", "author": { "name": "Gale", diff --git a/plugins/cloud-inference-skills/.codex-plugin/plugin.json b/plugins/cloud-inference-skills/.codex-plugin/plugin.json index 6a76f7c5..133fd3b3 100644 --- a/plugins/cloud-inference-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-inference-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-inference-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Cloud AI inference workflow skills for routing model serving, training, conversion, and GPU infrastructure work across Runpod, Hugging Face, AWS, Vast.ai, CoreWeave, and similar providers.", "author": { "name": "Gale", diff --git a/plugins/cybersecurity-skills/.codex-plugin/plugin.json b/plugins/cybersecurity-skills/.codex-plugin/plugin.json index f156b22e..4062b2ac 100644 --- a/plugins/cybersecurity-skills/.codex-plugin/plugin.json +++ b/plugins/cybersecurity-skills/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "cybersecurity-skills", - "version": "9.18.0", - "description": "Defensive cybersecurity, suspicious-content, malware-analysis, macOS, vulnerability, pentest, and incident-response workflows.", + "version": "9.19.0", + "description": "Defensive cybersecurity, isolated Linux and macOS analysis labs, suspicious-content and malware analysis, macOS defense, vulnerability testing, pentesting, and incident response workflows.", "skills": "./skills/", "author": { "name": "Gale", @@ -14,6 +14,8 @@ "keywords": [ "cybersecurity", "malware-analysis", + "analysis-lab", + "virtual-machine-isolation", "macos-security", "incident-response", "vulnerability-research", @@ -28,9 +30,10 @@ "interface": { "displayName": "Cybersecurity Skills", "shortDescription": "Defensive analysis and authorized security testing workflows.", - "longDescription": "Guide agents from an ambiguous suspicious artifact, host behavior, vulnerability report, or incident to preserved evidence, appropriate isolation, validated findings, proportionate containment, and an understandable defensive explanation. Includes malware analysis, macOS defense, authorized testing, incident response, hunting, and detection content with explicit handoffs to specialist Socket plugins.", + "longDescription": "Guide agents from an ambiguous suspicious artifact, host behavior, vulnerability report, or incident to preserved evidence, an appropriate isolation decision, a verified disposable Linux or macOS analysis lab, validated findings, proportionate containment, and an understandable defensive explanation. Includes malware analysis, macOS defense, authorized testing, incident response, hunting, detection content, evidence export, teardown verification, and explicit handoffs to specialist Socket plugins.", "defaultPrompt": [ "Help me safely determine whether this suspicious artifact is dangerous.", + "Prepare and preflight a disposable Linux or macOS analysis lab with no ambient host authority, monitored networking, narrow evidence export, and verified teardown.", "Investigate this Mac without destroying evidence or weakening protections.", "Validate this vulnerability within an explicitly authorized test scope." ], diff --git a/plugins/cybersecurity-skills/scripts/validate_repo_metadata.py b/plugins/cybersecurity-skills/scripts/validate_repo_metadata.py index 04a37ab0..f631aad6 100755 --- a/plugins/cybersecurity-skills/scripts/validate_repo_metadata.py +++ b/plugins/cybersecurity-skills/scripts/validate_repo_metadata.py @@ -44,6 +44,7 @@ "perform-dynamic-malware-analysis", "perform-static-malware-analysis", "preserve-security-evidence", + "prepare-isolated-analysis-lab", "recover-security-incident", "report-security-assessment", "route-security-work", @@ -203,7 +204,7 @@ def main() -> int: details.append(f"missing: {', '.join(missing)}") if unexpected: details.append(f"unexpected: {', '.join(unexpected)}") - findings.append(Finding("skills", f"inventory differs from the expected 30-skill surface ({'; '.join(details)})")) + findings.append(Finding("skills", f"inventory differs from the expected 31-skill surface ({'; '.join(details)})")) for skill_dir in skill_dirs: findings.extend(validate_skill(skill_dir)) if findings: diff --git a/plugins/cybersecurity-skills/skills/assess-macos-threat/SKILL.md b/plugins/cybersecurity-skills/skills/assess-macos-threat/SKILL.md index d2cd046d..6be30126 100644 --- a/plugins/cybersecurity-skills/skills/assess-macos-threat/SKILL.md +++ b/plugins/cybersecurity-skills/skills/assess-macos-threat/SKILL.md @@ -15,6 +15,7 @@ Read [references/macos-security-layers.md](references/macos-security-layers.md) 1. Identify the Mac and event. - Record model/chip, exact macOS build, update state, user/session, time/timezone, managed-device context, and what the person observed. + - Record whether evidence comes from the affected physical host, a macOS guest, or a reproduction guest. For guest evidence, include the VM framework/tool, virtual hardware, restore image, integrations, baseline/reset state, and anti-VM or hardware fidelity limits. 2. Preserve the triggering evidence. - Record alert text/screenshots, file path/source/hash, quarantine metadata, process identity, prompts, downloads, and relevant logs before cleanup. 3. Inspect artifact identity. @@ -27,6 +28,7 @@ Read [references/macos-security-layers.md](references/macos-security-layers.md) - Separate a blocked attempt from successful execution and successful execution from compromise. 6. Assess and advise. - State classification/confidence, immediate isolation needs, evidence gaps, and the smallest next workflow. + - Do not generalize guest-observed behavior to a physical Mac when hardware, Secure Enclave, recoveryOS, kernel/system-extension, device, or anti-VM behavior remains unresolved. ## Output diff --git a/plugins/cybersecurity-skills/skills/inspect-macos-persistence/SKILL.md b/plugins/cybersecurity-skills/skills/inspect-macos-persistence/SKILL.md index fdca7607..2c1bc2cd 100644 --- a/plugins/cybersecurity-skills/skills/inspect-macos-persistence/SKILL.md +++ b/plugins/cybersecurity-skills/skills/inspect-macos-persistence/SKILL.md @@ -14,6 +14,7 @@ Read [references/macos-persistence-surfaces.md](references/macos-persistence-sur ## Workflow 1. Record host/build, user domains, event timeline, and the suspected executable or label. + - Record whether the system is a physical Mac or macOS guest, plus the guest's restore image/build, VM tool/framework, integrations, baseline/reset state, and known virtualization artifacts. 2. Inventory user-visible registrations. - Review Login Items and background-item state, profiles, extensions, browser add-ons, and app-managed helpers. 3. Inventory launch services. @@ -25,6 +26,7 @@ Read [references/macos-persistence-surfaces.md](references/macos-persistence-sur - Identify parent installer/app, creation/change time, signature/notarization, executable hash, running process ancestry, files, network, and logs. 6. Classify each item. - Expected, suspicious, confirmed malicious, disabled/orphaned, or unresolved; explain evidence and impact. + - Keep guest-observed persistence distinct from physical-host proof when anti-VM, hardware, recoveryOS, kernel/system-extension, or device behavior may differ. 7. Preserve before containment. - Record files and service state before using official `launchctl bootout` or app/uninstaller paths in the containment workflow. diff --git a/plugins/cybersecurity-skills/skills/inspect-macos-runtime-activity/SKILL.md b/plugins/cybersecurity-skills/skills/inspect-macos-runtime-activity/SKILL.md index 5d97ae79..16f3134c 100644 --- a/plugins/cybersecurity-skills/skills/inspect-macos-runtime-activity/SKILL.md +++ b/plugins/cybersecurity-skills/skills/inspect-macos-runtime-activity/SKILL.md @@ -14,6 +14,7 @@ Read [references/macos-runtime-evidence.md](references/macos-runtime-evidence.md ## Workflow 1. Fix host/build, user/session, time window, process/artifact identity, and reported symptom. + - Label every observation as physical-host, affected-host, or macOS-guest evidence. For a guest, record VM tool/framework, virtual hardware, restore-image/build, shares/devices/network, baseline/reset state, and virtualization artifacts that may alter behavior. 2. Capture current process context. - Record PID, executable path/hash/signature, user, parent/ancestry, arguments, environment when authorized, start time, code state, and deleted/replaced executable clues. 3. Correlate files and registrations. @@ -27,6 +28,7 @@ Read [references/macos-runtime-evidence.md](references/macos-runtime-evidence.md - Separate user action, launch, child processes, file changes, prompts, network, persistence, detection, and termination. 7. Assess behavior and gaps. - Route binary internals, dynamic reproduction, containment, or hunting as needed. + - State anti-VM, hardware, Secure Enclave, recoveryOS, kernel/system-extension, and device-access limitations before treating guest evidence as physical-Mac proof. ## Output diff --git a/plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md b/plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md index 63e3438e..7b1e6b45 100644 --- a/plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md +++ b/plugins/cybersecurity-skills/skills/perform-dynamic-malware-analysis/SKILL.md @@ -7,7 +7,7 @@ description: Observe suspicious content in a disposable, instrumented environmen ## Overview -Execute only inside an environment chosen by `select-analysis-isolation`, with an observation plan that can distinguish artifact behavior from baseline noise. Preserve the exact sample and environment identity. +Execute only inside an environment chosen by `select-analysis-isolation` and preflighted by `prepare-isolated-analysis-lab`, with an observation plan that can distinguish artifact behavior from baseline noise. Preserve the exact sample, prepared-lab record, and environment identity. Read [references/dynamic-observation-plan.md](references/dynamic-observation-plan.md) for baseline, stimulus, telemetry, and teardown fields. @@ -15,7 +15,8 @@ Read [references/dynamic-observation-plan.md](references/dynamic-observation-pla 1. Define the unresolved question and minimum stimulus. 2. Verify isolation. - - Record guest/platform build, snapshot, accounts, shares, clipboard, devices, credentials, network mode, monitoring, and export path. + - Require the prepared-lab record and verify its guest/platform build, baseline/reset state, accounts, shares, clipboard, devices, credentials, network mode, monitoring, stop controls, export path, and teardown plan are still current. + - Record virtualization artifacts or anti-VM behavior that may affect the conclusion. 3. Capture a baseline. - Record processes, files/registrations, persistence surfaces, network state, services, and relevant logs before execution. 4. Execute one controlled step. @@ -31,4 +32,4 @@ Read [references/dynamic-observation-plan.md](references/dynamic-observation-pla ## Output -Return environment/baseline identity, stimulus, observed timeline, artifacts and indicators, absent expected behavior, evasion/coverage limits, conclusion, and teardown verification. +Return the prepared-lab identity, environment/baseline identity, stimulus, observed timeline, artifacts and indicators, absent expected behavior, virtualization/evasion/coverage limits, conclusion, and teardown verification. diff --git a/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md new file mode 100644 index 00000000..ef15110c --- /dev/null +++ b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/SKILL.md @@ -0,0 +1,57 @@ +--- +name: prepare-isolated-analysis-lab +description: Prepare a verified disposable Linux or macOS analysis lab from an approved isolation decision. Use before active research to control host integration, networking, baseline, monitoring, evidence export, reset, and teardown. +--- + +# Prepare Isolated Analysis Lab + +## Overview + +Turn the boundary selected by `select-analysis-isolation` into a concrete, reviewable control profile before executing untrusted content. Evidence collection and analysis remain owned by their specialist skills. + +Read [security-lab-control-profile.md](references/security-lab-control-profile.md) before approving a lab. + +## Workflow + +1. Consume the approved isolation decision. + - Record authorization, unresolved question, target OS/architecture/privilege, expected behavior, selected boundary, host/guest builds, and VM artifacts that may affect conclusions. +2. Select one profile. + - offline static tooling + - monitored Linux dynamic analysis + - monitored macOS dynamic analysis + - network-service research + - nested-virtualization experiment +3. Verify a trusted base. + - Record image/restore provenance and digest, guest build, tool versions, clock strategy, resource limits, baseline state or hashes, reset mechanism, and virtualization artifacts that may change observed behavior. +4. Remove ambient authority. + - Default host folders/home sharing, clipboard, drag/drop, sockets, SSH agent, browser profiles, cloud credentials, Apple accounts, signing identities, USB, microphone, camera, and unrelated devices to absent. +5. Constrain and observe networking. + - Default to offline or simulated services. + - When external connectivity is authorized, record destinations, routes, DNS, monitoring/capture, ingress, egress, and forwarded ports. +6. Define a narrow evidence path. + - Name the guest staging location, allowed artifact types, host export directory, hashing and scanning steps, size limits, and owner. +7. Run a preflight without executing the target. + - Verify accounts, shares, clipboard, devices, sockets, credentials, network, monitoring, clock, baseline, stop controls, export path, and reset operation. +8. Hand the prepared-lab record to `perform-dynamic-malware-analysis` or the relevant observation skill. +9. Verify teardown. + - Stop the workload; export only intended evidence; hash/scan it; revert or remove disposable state; revoke temporary credentials; remove shares/ports/helpers; confirm no workload or integration remains active. + +## Output + +Return the approved isolation decision, selected profile, trusted-base identity, full control profile, preflight evidence, observation handoff, export manifest, teardown evidence, and remaining fidelity limits. + +## Stop Conditions + +- Stop when authorization for active testing, target-platform fidelity, trusted-base provenance, isolation controls, observation coverage, safe evidence export, or reset/teardown cannot be verified. +- Stop when the task requires host secrets, personal accounts, developer identities, or uncontrolled devices. +- Stop when anti-VM, hardware, Secure Enclave, recoveryOS, kernel/system-extension, or device behavior makes VM evidence insufficient; state the physical-device gap. +- Never weaken host SIP, Gatekeeper, XProtect, TCC, App Sandbox, or other protections to make the lab convenient. +- Never start a guest, service, network capture, or payload without announcing the exact visible or resource-intensive action first. + +## Handoffs + +- Use `perform-dynamic-malware-analysis` for controlled execution and observation. +- Use Reverse Engineering skills for exported binaries, disassembly, decompilation, and symbols. +- Use `apple-dev-skills:virtualization-framework-workflow` for custom VM implementation defects. +- Use `apple-dev-skills:macos-development-vm-workflow` or `linux-development-vm-workflow` for benign guest provisioning and lifecycle mechanics. +- Return to `select-analysis-isolation` when the chosen boundary fails fidelity or containment preflight. diff --git a/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/agents/openai.yaml b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/agents/openai.yaml new file mode 100644 index 00000000..0f97bd8b --- /dev/null +++ b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Prepare Isolated Analysis Lab" + short_description: "Prepare and verify disposable analysis labs" + default_prompt: "Use $prepare-isolated-analysis-lab to turn this approved isolation choice into a verified disposable lab and teardown contract." diff --git a/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md new file mode 100644 index 00000000..26780319 --- /dev/null +++ b/plugins/cybersecurity-skills/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md @@ -0,0 +1,60 @@ +# Security Lab Control Profile + +Every field must be explicit. Use `absent`, `disabled`, `read-only`, `allowlisted`, or another verified state instead of relying on a product default. + +## Identity And Base + +- authorization and research question +- host model/chip/build; guest OS/build/architecture +- base image or restore-image source and digest +- VM manager/framework/CLI and exact version +- accounts, privilege, clock/time zone, CPU, memory, disk, and reset operation +- baseline hashes/state and virtualization artifacts that may change behavior + +## Host Integration + +- host folders and home sharing +- clipboard and drag/drop +- host/guest sockets and SSH agent +- browser profiles, password stores, developer certificates, signing identities +- Apple/cloud accounts, tokens, registries, package-manager credentials +- USB, microphone, camera, graphics, audio, and other passthrough devices + +Default every item above to absent. Approve an exception only when it is required by the named observation and has a removal check. + +## Network + +- offline, simulated, allowlisted egress, or monitored network mode +- DNS, gateway, routes, packet/log capture, ingress, egress, and time source +- external destinations and authorization +- forwarded/listening ports and their teardown checks + +## Observation And Stop Controls + +- process, filesystem, persistence, service, security-control, DNS/network, and log telemetry +- minimum stimulus, execution identity, stop command/control, resource/time limits +- anti-VM and coverage limitations + +## Evidence Export + +- guest staging directory and host export directory +- allowed artifact types and maximum size +- hashes, archive format, metadata, malware scanning, and human review +- prohibition on exporting live credentials, sockets, whole home directories, or unrelated guest state + +## Teardown Proof + +- workload and guest stopped +- intended evidence exported and scanned +- disposable state reverted or removed +- temporary credentials revoked +- shares, clipboard, sockets, devices, routes, captures, and forwarded ports removed +- no helper, service, or workload remains active + +## Profiles + +- `offline-static`: no target execution, no network, read-only sample input, narrow report export +- `monitored-linux-dynamic`: disposable Linux guest, no home sharing, monitored or simulated network, process/filesystem/service capture +- `monitored-macos-dynamic`: disposable macOS guest, native control telemetry, no personal Apple/developer identities, VM-fidelity caveats +- `network-service`: isolated test network, explicit clients/servers, allowlisted ingress/egress, packet capture, port teardown +- `nested-virtualization`: verified host/guest/kernel capability, no home sharing, inner and outer lifecycle/evidence ownership, resource limits diff --git a/plugins/cybersecurity-skills/skills/select-analysis-isolation/SKILL.md b/plugins/cybersecurity-skills/skills/select-analysis-isolation/SKILL.md index 712d1ec3..c6eb69d2 100644 --- a/plugins/cybersecurity-skills/skills/select-analysis-isolation/SKILL.md +++ b/plugins/cybersecurity-skills/skills/select-analysis-isolation/SKILL.md @@ -37,6 +37,11 @@ Read [references/isolation-matrix.md](references/isolation-matrix.md) before sel 6. Verify teardown. - Stop the workload, export intended evidence, revert or destroy disposable state, revoke temporary credentials, and confirm no host share or forwarded port remains. +7. Hand an approved execution boundary to `prepare-isolated-analysis-lab`. + - Use `apple-dev-skills:choose-macos-virtualization-shape` when the development boundary is still undecided. + - Use `apple-dev-skills:virtualization-framework-workflow` when a custom macOS or Linux VM host must be implemented or diagnosed. + - Treat a SIP-enabled macOS VM as the stable high-fidelity path for SIP-sensitive behavior; local sandbox, TCC, and failure injection are explicitly lower-fidelity approximations. + ## Stop Conditions -Stop before execution when the environment cannot reproduce the target platform, the isolation controls cannot be verified, or the task requires host secrets or privileges beyond the approved analysis plan. +Stop before execution when the environment cannot reproduce the target platform, the isolation controls cannot be verified, or the task requires host secrets or privileges beyond the approved analysis plan. Selection alone does not authorize execution; require the prepared-lab record first. diff --git a/plugins/dotnet-skills/.codex-plugin/plugin.json b/plugins/dotnet-skills/.codex-plugin/plugin.json index 63b81557..392ab192 100644 --- a/plugins/dotnet-skills/.codex-plugin/plugin.json +++ b/plugins/dotnet-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "dotnet-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for choosing, bootstrapping, building, testing, packaging, diagnosing, and maintaining .NET projects with F# and C# as equal first-party languages.", "author": { "name": "Gale", diff --git a/plugins/game-dev-skills/.codex-plugin/plugin.json b/plugins/game-dev-skills/.codex-plugin/plugin.json index 01357683..8e96c52b 100644 --- a/plugins/game-dev-skills/.codex-plugin/plugin.json +++ b/plugins/game-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "game-dev-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Apple platform game development workflow skills for native Metal rendering, Game Porting Toolkit routing, MetalFX, GPU asset streaming, neural rendering, frameworks, input, haptics, and profiling.", "author": { "name": "Gale", diff --git a/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json b/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json index 001ecfc0..1eb823c3 100644 --- a/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json +++ b/plugins/messaging-collaboration-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "messaging-collaboration-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex workflows for chat apps, bots, collaboration, iMessage, Apple notifications and Push to Talk, VoIP, and default communication-app planning.", "author": { "name": "Gale", diff --git a/plugins/model-lab-skills/.codex-plugin/plugin.json b/plugins/model-lab-skills/.codex-plugin/plugin.json index e29b9c81..85d4c9fc 100644 --- a/plugins/model-lab-skills/.codex-plugin/plugin.json +++ b/plugins/model-lab-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "model-lab-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Reproducible model training, evaluation, intervention, and runtime research workflows.", "author": { "name": "Gale" diff --git a/plugins/network-protocol-skills/.codex-plugin/plugin.json b/plugins/network-protocol-skills/.codex-plugin/plugin.json index cdac0f0c..5cc7bf5a 100644 --- a/plugins/network-protocol-skills/.codex-plugin/plugin.json +++ b/plugins/network-protocol-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "network-protocol-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for choosing, planning, implementing, and diagnosing modern application transports and real-time networking protocols, including QUIC, HTTP/3, WebRTC, Media over QUIC, WebTransport-adjacent handoffs, protocol maturity checks, and stack-specific implementation routing.", "author": { "name": "Gale", diff --git a/plugins/productivity-skills/.codex-plugin/plugin.json b/plugins/productivity-skills/.codex-plugin/plugin.json index 05201b57..62914a8f 100644 --- a/plugins/productivity-skills/.codex-plugin/plugin.json +++ b/plugins/productivity-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "productivity-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Broadly useful productivity workflows for Codex.", "author": { "name": "Gale", diff --git a/plugins/productivity-skills/pyproject.toml b/plugins/productivity-skills/pyproject.toml index 9fd34fdc..d2c1f4c4 100644 --- a/plugins/productivity-skills/pyproject.toml +++ b/plugins/productivity-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "productivity-skills-maintenance" -version = "9.18.0" +version = "9.19.0" description = "Maintainer-only Python tooling baseline for productivity-skills." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/productivity-skills/uv.lock b/plugins/productivity-skills/uv.lock index 0076310a..47d655fe 100644 --- a/plugins/productivity-skills/uv.lock +++ b/plugins/productivity-skills/uv.lock @@ -228,7 +228,7 @@ wheels = [ [[package]] name = "productivity-skills-maintenance" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/python-skills/.codex-plugin/plugin.json b/plugins/python-skills/.codex-plugin/plugin.json index 722c6a4d..d72891e2 100644 --- a/plugins/python-skills/.codex-plugin/plugin.json +++ b/plugins/python-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "python-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Bundled Python-focused Codex skills for uv bootstrapping, project implementation, diagnostics, packaging, tooling, CI, upgrades, FastAPI, FastMCP, and pytest workflows.", "author": { "name": "Gale", diff --git a/plugins/python-skills/pyproject.toml b/plugins/python-skills/pyproject.toml index 24f61f52..6c43874a 100644 --- a/plugins/python-skills/pyproject.toml +++ b/plugins/python-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "python-skills-maintainer" -version = "9.18.0" +version = "9.19.0" description = "Maintainer tooling for the python-skills repository" requires-python = ">=3.11" dependencies = [] diff --git a/plugins/python-skills/uv.lock b/plugins/python-skills/uv.lock index c532bc4d..b368052f 100644 --- a/plugins/python-skills/uv.lock +++ b/plugins/python-skills/uv.lock @@ -251,7 +251,7 @@ wheels = [ [[package]] name = "python-skills-maintainer" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json index 22fafa9f..f310894d 100644 --- a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json +++ b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "reverse-engineering-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Workflow skills for reverse engineering, decompilation, disassembly, symbol, and artifact-analysis tasks.", "skills": "./skills/", "author": { diff --git a/plugins/rust-skills/.codex-plugin/plugin.json b/plugins/rust-skills/.codex-plugin/plugin.json index f91719ec..fb50e6d5 100644 --- a/plugins/rust-skills/.codex-plugin/plugin.json +++ b/plugins/rust-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "rust-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Rust, Cargo, rustup, crate, workspace, CLI, library, package, CI, testing, linting, and formatting workflow skills.", "skills": "./skills/", "author": { diff --git a/plugins/server-side-jvm/.codex-plugin/plugin.json b/plugins/server-side-jvm/.codex-plugin/plugin.json index 55540b4d..02c966f4 100644 --- a/plugins/server-side-jvm/.codex-plugin/plugin.json +++ b/plugins/server-side-jvm/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-jvm", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for choosing, building, testing, and maintaining server-side JVM backend projects with Java and Scala as equal first-party languages and future Clojure support planned.", "author": { "name": "Gale", diff --git a/plugins/server-side-swift/.codex-plugin/plugin.json b/plugins/server-side-swift/.codex-plugin/plugin.json index 4dae76c8..d5d333b9 100644 --- a/plugins/server-side-swift/.codex-plugin/plugin.json +++ b/plugins/server-side-swift/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-swift", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for bootstrapping, syncing, building, running, containerizing, deploying, and maintaining server-side Swift services, including Vapor, Hummingbird, hb, persistence, Swift OpenAPI, RPC-fit decisions, SwiftNIO, observability, auth, app sync, Docker, Apple Containerization, Fly.io, and SwiftPM-first workflows.", "author": { "name": "Gale", @@ -48,6 +48,7 @@ "flyctl", "containerization", "apple-container", + "container-machine", "oci", "swiftpm" ], @@ -55,7 +56,7 @@ "interface": { "displayName": "Server-Side Swift", "shortDescription": "Server-side Swift bootstrap, guidance sync, Vapor, Hummingbird, persistence, OpenAPI, RPC, SwiftNIO, observability, auth, app sync, Docker, Apple Containerization, and Fly.io workflow guidance for Codex.", - "longDescription": "Guide Codex agents through server-side Swift projects with CLI-first bootstrap and guidance-sync workflows for Vapor and Hummingbird, SwiftPM-first execution, current Vapor, Vapor 5 alpha adoption posture, Hummingbird hb CLI usage, hb Server and Lambda shape preservation, Vapor Environment, Hummingbird swift-configuration support, Fluent ORM, PostgreSQL, CLI-generated Dockerfile and Compose defaults, generated service AGENTS guidance, Swift OpenAPI, OpenAPIHummingbird, hummingbird-lambda, Docker, Apple Containerization, Fly.io, SwiftNIO, Swift Logging, Swift Metrics, Swift Distributed Tracing, authentication, authorization, and app-sync documentation, local run commands, app structure, routing, middleware, request contexts, Fluent models, database migrations, query design, OpenAPI generation and transport wiring, RPC-fit decisions, Dockerfile and Compose workflows, Apple's container CLI and Containerization APIs, fly.toml deployment configuration, Fly secrets, health checks, observability signals, auth boundaries, sync contracts, environment configuration, tests, and deployment handoffs.", + "longDescription": "Guide Codex agents through server-side Swift projects with CLI-first Vapor and Hummingbird workflows, SwiftPM, Fluent and PostgreSQL, Docker and Compose, Swift OpenAPI, RPC-fit decisions, SwiftNIO, observability, authentication, app sync, Fly.io, Apple's container 1.x CLI, persistent container machine Linux environments, and exact-version Containerization Swift APIs. Keep portable OCI authoring separate from Apple-local runtime behavior and persistent Linux-machine lifecycle.", "developerName": "gaelic-ghost", "category": "Developer Tools", "capabilities": [ @@ -86,6 +87,7 @@ "Prepare this Vapor or Hummingbird service for Fly.io deployment with Dockerfile, fly.toml, secrets, health checks, and validation.", "Diagnose why this Swift service fails to deploy or pass health checks on Fly.io.", "Plan an Apple Containerization workflow for running this Swift service locally on Apple silicon.", + "Choose and operate an Apple container 1.x application container or persistent container machine with explicit version, home sharing, resources, services, and nested-virtualization checks.", "Compare Docker and Apple's container CLI for this server-side Swift development workflow.", "Inspect this Vapor app and explain its routes, configuration, and run commands.", "Assess whether this Vapor 4 app is ready for a Vapor 5 alpha spike without treating alpha APIs as stable.", diff --git a/plugins/server-side-swift/skills/apple-containerization-workflow/SKILL.md b/plugins/server-side-swift/skills/apple-containerization-workflow/SKILL.md index 180ddf8f..32329713 100644 --- a/plugins/server-side-swift/skills/apple-containerization-workflow/SKILL.md +++ b/plugins/server-side-swift/skills/apple-containerization-workflow/SKILL.md @@ -1,6 +1,6 @@ --- name: apple-containerization-workflow -description: Plan, build, test, and diagnose Apple Containerization and apple/container CLI workflows for server-side Swift services on Apple silicon, keeping Apple's container tooling distinct from generic Docker guidance. +description: Build and diagnose Apple Containerization, apple/container 1.x, and persistent container machine workflows for server-side Swift on Apple silicon while keeping Apple tooling distinct from Docker and full VMs. license: Apache-2.0 compatibility: Designed for Codex and compatible Agent Skills clients working with Apple's Containerization package, apple/container CLI, OCI images, SwiftPM, Vapor, Hummingbird, and server-side Swift services on Apple silicon Macs. metadata: @@ -25,7 +25,8 @@ The practical decision is whether the task needs Apple's macOS-native container ## When To Use - Use this skill when a user asks for Apple Containerization, Apple's `container` CLI, Containerization Swift APIs, OCI image work on Apple silicon, or native macOS container runtime behavior. -- Use this skill when diagnosing `container system start`, `container build`, `container run`, image pull or push, registry login, kernel setup, lightweight VM startup, networking, Rosetta, or Apple silicon runtime behavior. +- Use this skill when diagnosing `container system start`, `container build`, `container run`, `container machine`, image pull or push, registry login, kernel setup, lightweight VM startup, networking, Rosetta, nested virtualization, or Apple silicon runtime behavior. +- Use this skill for a persistent OCI-image-backed Linux development machine with an init system, services, repeated shell access, or explicit CPU, memory, and home-sharing policy. - Use this skill when comparing Apple Containerization to Docker for local server-side Swift development. - Use this skill when deciding whether a server-side Swift package should add Apple-container-specific docs, tests, or local development guidance. - Do not use this skill for ordinary Dockerfile, Compose, CI image build, registry publish, or generic Linux deployment work unless the task explicitly asks how it behaves under Apple's tooling. Use `docker-workflow` for generic Docker work. @@ -40,11 +41,14 @@ Use current official Apple and project documentation before claiming behavior, b - [apple/container repository](https://github.com/apple/container) - [apple/container tutorial](https://github.com/apple/container/blob/main/docs/tutorial.md) - [apple/container technical overview](https://github.com/apple/container/blob/main/docs/technical-overview.md) +- [apple/container machine documentation](https://github.com/apple/container/blob/main/docs/container-machine.md) - [Apple Open Source Container page](https://opensource.apple.com/projects/container/) - [Apple Open Source Containerization page](https://opensource.apple.com/projects/containerization/) - [Virtualization framework documentation](https://developer.apple.com/documentation/virtualization) -Treat documentation on a repository's `main` branch as current-branch documentation. When a user needs release-stable behavior, open the matching release tag and use that version's docs instead. +Treat documentation on a repository's `main` branch as current-branch documentation. When a user needs release-stable behavior, open the matching release tag and use that version's docs instead. The `container` 1.0.0 release links current-branch machine documentation that is not present under the release tag, so pair that document with the installed 1.x CLI's `container machine --help` and subcommand help before encoding commands. + +Read [references/apple-container-version-matrix.md](references/apple-container-version-matrix.md) before changing a version-sensitive workflow. ## Planning Workflow @@ -61,6 +65,7 @@ Treat documentation on a repository's `main` branch as current-branch documentat - script a local development flow around `container` - use Containerization Swift APIs from a macOS tool - diagnose kernel, VM, image, network, or registry behavior + - create or operate a persistent `container machine` Linux environment 3. Confirm the required host: - Apple silicon Mac - supported macOS version for the selected release @@ -74,6 +79,15 @@ Treat documentation on a repository's `main` branch as current-branch documentat - Swift API integration only when the app's job is to manage containers directly 5. Validate with the narrowest useful `container`, SwiftPM, log, or HTTP check. +## Version Gate + +Keep the CLI and Swift package version lines separate: + +- `apple/container` CLI 1.x is the stable command surface covered here. Version 1.0 introduced persistent `container machine` environments, TOML-backed configuration, structured-list output changes, `container cp`, and removal of the version-zero XPC compatibility path. +- `apple/containerization` remains a 0.x Swift package. Pin an exact release or revision, read that source and generated API documentation, and treat source compatibility as unproven until the selected version builds and tests. +- Do not preserve removed `container system property` commands as compatibility shims. Migrate to the documented 1.x TOML configuration and report the required user-visible change. +- Do not infer CLI 1.x behavior from a package 0.x version number, or package API stability from the CLI major version. + ## Apple Containerization Versus Docker Use plain language when choosing between the paths: @@ -111,6 +125,20 @@ When using the `container` CLI: Do not assume Docker Compose features, Docker Desktop behavior, or Docker CLI flags map directly to Apple's `container` CLI. Check the `container` command's own help and release docs first. +## Container Machine Workflow + +Use `container machine` for a persistent Linux development environment, not for a disposable application container, macOS virtualization, or a Compose replacement. + +1. Verify CLI version 1.x, current machine documentation, and installed `container machine --help` output. +2. Record the OCI image/digest, architecture, init system, default-machine state, CPU, memory, disks, home-mount policy, kernel, and required services. +3. Inspect the exact create, run, list/inspect, set-default, resource-change, stop, and remove subcommand help before recommending syntax. +4. Treat automatic host-user and home-directory integration as a development convenience. For untrusted work require `home-mount=none` or the current documented equivalent, then hand control review to `cybersecurity-skills:prepare-isolated-analysis-lab`. +5. Stop the machine before any documented resource or configuration operation that requires it, then re-inspect and validate after restart. +6. Gate nested virtualization on supported Apple silicon, host macOS, CLI, compatible kernel configuration, guest support, and observed `/dev/kvm`; never promise it from a flag alone. +7. Validate guest identity, architecture, init/services, filesystem persistence, network, resource changes, reboot behavior, home isolation, and removal. + +Use `apple-dev-skills:linux-development-vm-workflow` when the question is whether a container machine, Lima/Colima environment, or full Linux VM provides the required fidelity. Use `apple-dev-skills:virtualization-framework-workflow` for custom full-VM implementation. + ## Swift API Workflow When using the Containerization Swift package directly: @@ -158,6 +186,7 @@ Return: 2. `Docs used`: Apple Containerization, apple/container release docs, Virtualization framework, SwiftPM, Vapor, Hummingbird, Docker, or persistence docs consulted. 3. `Command path`: exact `container`, SwiftPM, run, log, registry, or HTTP commands run or recommended. 4. `Runtime behavior`: system service state, image build or pull behavior, entry point, arguments, environment, ports, volumes, networking, Rosetta, and kernel assumptions. + - For container machines also include image/digest, init/services, default-machine state, resources, home-mount policy, persistence, and nested-virtualization evidence. 5. `Validation`: tool version, system start, build, run, logs, HTTP check, or Swift test results. 6. `Handoffs`: Docker, Vapor, Hummingbird, persistence, macOS app integration, CI, registry publish, or deployment follow-up when the task crosses this skill's boundary. @@ -165,7 +194,11 @@ Return: - Do not claim Apple Containerization behavior from memory when current official docs, GitHub release docs, or local CLI help can be checked. - Do not assume Apple's `container` CLI is a drop-in replacement for Docker or Compose. +- Do not treat `container machine` as an ordinary application container, macOS VM, or safe hostile-workload boundary while home integration is enabled. +- Do not use removed `container system property` commands as a fallback for 1.x configuration. +- Do not claim the 0.x Containerization Swift package is API-stable because the `container` CLI reached 1.x. - Do not commit registry credentials, local `.env` files, machine-local paths, kernel artifacts, private images, or local runtime state. - Do not add Containerization Swift package dependencies to a service unless the service is actually a container-management tool. - Do not hide Apple silicon, macOS, kernel, Virtualization framework, or Rosetta requirements when they affect whether the workflow can run. - Do not turn local Apple-container diagnostics into production deployment guidance unless the repository already deploys through that path. +- Do not start container services or machines without announcing the exact visible or resource-intensive action first. diff --git a/plugins/server-side-swift/skills/apple-containerization-workflow/references/apple-container-version-matrix.md b/plugins/server-side-swift/skills/apple-containerization-workflow/references/apple-container-version-matrix.md new file mode 100644 index 00000000..1a8a0a92 --- /dev/null +++ b/plugins/server-side-swift/skills/apple-containerization-workflow/references/apple-container-version-matrix.md @@ -0,0 +1,22 @@ +# Apple Container Version Matrix + +Use exact current release notes, tagged source, current docs when explicitly linked by a release, and installed CLI help. Never infer one project's stability from the other's version. + +| Surface | Version rule | Important 1.x or stability boundary | +| --- | --- | --- | +| `apple/container` CLI | discover installed version; use matching release docs and command help | 1.0 adds persistent `container machine`, TOML configuration, structured-output changes, `container cp`, and removes version-zero XPC compatibility | +| `container machine` docs | current-branch docs may be linked from the 1.0 release | pair current docs with installed `container machine --help`; do not assume unreleased flags | +| `apple/containerization` Swift package | pin exact 0.x release/revision and inspect its source/DocC | public API remains pre-1.0; compile/test the selected version and expect source changes | +| OCI image/Dockerfile | pin tags/digests according to project policy | keep portable image authoring independent from the local Apple runtime | +| Linux kernel/init filesystem | record exact provenance and compatibility | runtime, Rosetta, init, and nested-virtualization behavior may depend on these artifacts | + +## 1.x Migration Decisions + +- Replace removed `container system property` automation with the documented TOML configuration path; do not keep a duplicate compatibility command path. +- Re-check parsers that consume structured `list` output instead of assuming the pre-1.0 shape. +- Use `container cp` only after confirming installed help and source/destination semantics. +- Treat ordinary container mounts and a machine's user/home integration as different security and lifecycle surfaces. + +## Required Record + +Record CLI version, package version/revision when applicable, docs/tag/commit consulted, host macOS/architecture, image digest, kernel/init provenance, and every relied-on help surface. diff --git a/plugins/spotify/.codex-plugin/plugin.json b/plugins/spotify/.codex-plugin/plugin.json index 9b93d150..7826730c 100644 --- a/plugins/spotify/.codex-plugin/plugin.json +++ b/plugins/spotify/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "spotify", - "version": "9.18.0", + "version": "9.19.0", "description": "Placeholder plugin repository for future Spotify-focused Codex workflows.", "author": { "name": "Gale", diff --git a/plugins/swift-lang/.codex-plugin/plugin.json b/plugins/swift-lang/.codex-plugin/plugin.json index 7d0aa9bc..a0b78473 100644 --- a/plugins/swift-lang/.codex-plugin/plugin.json +++ b/plugins/swift-lang/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swift-lang", - "version": "9.18.0", + "version": "9.19.0", "description": "Shared Swift language and tooling skills for API style, errors, functional pipelines, formatting, source organization, SwiftSyntax, compiler inspection, SourceKit, indexing, SourceKit-LSP, and modernization.", "skills": "./skills/", "author": { diff --git a/plugins/swiftasb-skills/.codex-plugin/plugin.json b/plugins/swiftasb-skills/.codex-plugin/plugin.json index f2f83e00..800fc316 100644 --- a/plugins/swiftasb-skills/.codex-plugin/plugin.json +++ b/plugins/swiftasb-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swiftasb-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for explaining SwiftASB and building SwiftUI, AppKit, and Swift package integrations on top of it.", "author": { "name": "Gale", diff --git a/plugins/things-app/.codex-plugin/plugin.json b/plugins/things-app/.codex-plugin/plugin.json index af9b5c97..794181b6 100644 --- a/plugins/things-app/.codex-plugin/plugin.json +++ b/plugins/things-app/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "things-app", - "version": "9.18.0", + "version": "9.19.0", "description": "Things.app skills and a bundled local MCP server for reminders, planning digests, and structured task workflows.", "author": { "name": "Gale", diff --git a/plugins/things-app/mcp/pyproject.toml b/plugins/things-app/mcp/pyproject.toml index 5b141f5f..d5dc3dc0 100644 --- a/plugins/things-app/mcp/pyproject.toml +++ b/plugins/things-app/mcp/pyproject.toml @@ -7,7 +7,7 @@ packages = ["app"] [project] name = "things-mcp" -version = "9.18.0" +version = "9.19.0" requires-python = ">=3.13" dependencies = [ "fastmcp>=3.0.2", diff --git a/plugins/things-app/mcp/uv.lock b/plugins/things-app/mcp/uv.lock index 9ee2e3a5..22528d0d 100644 --- a/plugins/things-app/mcp/uv.lock +++ b/plugins/things-app/mcp/uv.lock @@ -1244,7 +1244,7 @@ wheels = [ [[package]] name = "things-mcp" -version = "9.18.0" +version = "9.19.0" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/plugins/things-app/pyproject.toml b/plugins/things-app/pyproject.toml index b38ae1f7..bb91522f 100644 --- a/plugins/things-app/pyproject.toml +++ b/plugins/things-app/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "things-app-maintenance" -version = "9.18.0" +version = "9.19.0" description = "Maintainer-only Python tooling baseline for things-app skills and plugin packaging." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/things-app/uv.lock b/plugins/things-app/uv.lock index 0e9a06bb..025bb4e7 100644 --- a/plugins/things-app/uv.lock +++ b/plugins/things-app/uv.lock @@ -120,7 +120,7 @@ wheels = [ [[package]] name = "things-app-maintenance" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/web-dev-skills/.codex-plugin/plugin.json b/plugins/web-dev-skills/.codex-plugin/plugin.json index 35718e48..7201ef48 100644 --- a/plugins/web-dev-skills/.codex-plugin/plugin.json +++ b/plugins/web-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "web-dev-skills", - "version": "9.18.0", + "version": "9.19.0", "description": "Codex skills for focused web and Expo native-boundary workflows.", "author": { "name": "Gale", diff --git a/pyproject.toml b/pyproject.toml index d0817202..df4d9158 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "socket-maintenance" -version = "9.18.0" +version = "9.19.0" description = "Root uv tooling baseline for the socket superproject." requires-python = ">=3.11" dependencies = [] diff --git a/scripts/export_hermes_skills.py b/scripts/export_hermes_skills.py index 3a919a81..b6805630 100644 --- a/scripts/export_hermes_skills.py +++ b/scripts/export_hermes_skills.py @@ -20,6 +20,7 @@ REVERSE_ENGINEERING_SOURCE_ROOT = ( REPO_ROOT / "plugins" / "reverse-engineering-skills" / "skills" ) +SERVER_SIDE_SWIFT_SOURCE_ROOT = REPO_ROOT / "plugins" / "server-side-swift" / "skills" SWIFT_LANG_SOURCE_ROOT = REPO_ROOT / "plugins" / "swift-lang" / "skills" MODEL_LAB_SOURCE_ROOT = REPO_ROOT / "plugins" / "model-lab-skills" / "skills" EXPORT_ROOT = REPO_ROOT / "skills" @@ -48,6 +49,10 @@ ) APPLE_SKILLS = ( "app-extension-architecture-workflow", + "choose-macos-virtualization-shape", + "virtualization-framework-workflow", + "linux-development-vm-workflow", + "macos-development-vm-workflow", "mailkit-workflow", "file-provider-and-finder-sync-workflow", "swift-package-extension-workflow", @@ -71,6 +76,7 @@ "perform-dynamic-malware-analysis", "perform-static-malware-analysis", "preserve-security-evidence", + "prepare-isolated-analysis-lab", "recover-security-incident", "report-security-assessment", "route-security-work", @@ -84,6 +90,9 @@ "use-objective-see-tools", "validate-vulnerability", ) +SERVER_SIDE_SWIFT_SKILLS = ( + "apple-containerization-workflow", +) REVERSE_ENGINEERING_SKILLS = ( "connect-hopper-mcp", "script-hopper-analysis", @@ -117,6 +126,7 @@ + MESSAGING_SKILLS + APPLE_SKILLS + CYBERSECURITY_SKILLS + + SERVER_SIDE_SWIFT_SKILLS + REVERSE_ENGINEERING_SKILLS + SWIFT_LANG_SKILLS + MODEL_LAB_SKILLS @@ -137,6 +147,10 @@ def source_paths(source_root: Path | None = None) -> dict[str, Path]: **{ skill_name: CYBERSECURITY_SOURCE_ROOT for skill_name in CYBERSECURITY_SKILLS }, + **{ + skill_name: SERVER_SIDE_SWIFT_SOURCE_ROOT + for skill_name in SERVER_SIDE_SWIFT_SKILLS + }, **{ skill_name: REVERSE_ENGINEERING_SOURCE_ROOT for skill_name in REVERSE_ENGINEERING_SKILLS diff --git a/skills.sh.json b/skills.sh.json index 06108a69..32284b14 100644 --- a/skills.sh.json +++ b/skills.sh.json @@ -34,6 +34,10 @@ "title": "Apple Dev Skills", "skills": [ "app-extension-architecture-workflow", + "choose-macos-virtualization-shape", + "virtualization-framework-workflow", + "linux-development-vm-workflow", + "macos-development-vm-workflow", "mailkit-workflow", "file-provider-and-finder-sync-workflow", "swift-package-extension-workflow" @@ -60,6 +64,7 @@ "perform-dynamic-malware-analysis", "perform-static-malware-analysis", "preserve-security-evidence", + "prepare-isolated-analysis-lab", "recover-security-incident", "report-security-assessment", "route-security-work", @@ -74,6 +79,12 @@ "validate-vulnerability" ] }, + { + "title": "Server-Side Swift Skills", + "skills": [ + "apple-containerization-workflow" + ] + }, { "title": "Reverse Engineering Skills", "skills": [ diff --git a/skills/apple-containerization-workflow/SKILL.md b/skills/apple-containerization-workflow/SKILL.md new file mode 100644 index 00000000..32329713 --- /dev/null +++ b/skills/apple-containerization-workflow/SKILL.md @@ -0,0 +1,204 @@ +--- +name: apple-containerization-workflow +description: Build and diagnose Apple Containerization, apple/container 1.x, and persistent container machine workflows for server-side Swift on Apple silicon while keeping Apple tooling distinct from Docker and full VMs. +license: Apache-2.0 +compatibility: Designed for Codex and compatible Agent Skills clients working with Apple's Containerization package, apple/container CLI, OCI images, SwiftPM, Vapor, Hummingbird, and server-side Swift services on Apple silicon Macs. +metadata: + owner: gaelic-ghost + repo: socket + category: server-side-swift-apple-containerization +allowed-tools: Read Bash(rg:*) Bash(git:*) Bash(swift:*) Bash(container:*) Bash(curl:*) +--- + +# Apple Containerization Workflow + +## SwiftData And SwiftUI Rule + +When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. + +## Purpose + +Build, run, test, or diagnose server-side Swift container work that specifically uses Apple's Containerization project or the `apple/container` command-line tool. + +The practical decision is whether the task needs Apple's macOS-native container runtime, the lower-level Containerization Swift APIs, or ordinary Docker-compatible files. This skill keeps those paths separate so Docker deployment guidance does not accidentally become Apple-only runtime guidance. + +## When To Use + +- Use this skill when a user asks for Apple Containerization, Apple's `container` CLI, Containerization Swift APIs, OCI image work on Apple silicon, or native macOS container runtime behavior. +- Use this skill when diagnosing `container system start`, `container build`, `container run`, `container machine`, image pull or push, registry login, kernel setup, lightweight VM startup, networking, Rosetta, nested virtualization, or Apple silicon runtime behavior. +- Use this skill for a persistent OCI-image-backed Linux development machine with an init system, services, repeated shell access, or explicit CPU, memory, and home-sharing policy. +- Use this skill when comparing Apple Containerization to Docker for local server-side Swift development. +- Use this skill when deciding whether a server-side Swift package should add Apple-container-specific docs, tests, or local development guidance. +- Do not use this skill for ordinary Dockerfile, Compose, CI image build, registry publish, or generic Linux deployment work unless the task explicitly asks how it behaves under Apple's tooling. Use `docker-workflow` for generic Docker work. +- Do not use this skill for Apple-platform app, simulator, signing, SwiftUI, or Xcode project work unless the container task is part of a macOS tool that embeds Containerization APIs. + +## Source Check + +Use current official Apple and project documentation before claiming behavior, because this surface is young and changes quickly: + +- [apple/containerization repository](https://github.com/apple/containerization) +- [Containerization API documentation](https://apple.github.io/containerization/documentation/containerization/) +- [apple/container repository](https://github.com/apple/container) +- [apple/container tutorial](https://github.com/apple/container/blob/main/docs/tutorial.md) +- [apple/container technical overview](https://github.com/apple/container/blob/main/docs/technical-overview.md) +- [apple/container machine documentation](https://github.com/apple/container/blob/main/docs/container-machine.md) +- [Apple Open Source Container page](https://opensource.apple.com/projects/container/) +- [Apple Open Source Containerization page](https://opensource.apple.com/projects/containerization/) +- [Virtualization framework documentation](https://developer.apple.com/documentation/virtualization) + +Treat documentation on a repository's `main` branch as current-branch documentation. When a user needs release-stable behavior, open the matching release tag and use that version's docs instead. The `container` 1.0.0 release links current-branch machine documentation that is not present under the release tag, so pair that document with the installed 1.x CLI's `container machine --help` and subcommand help before encoding commands. + +Read [references/apple-container-version-matrix.md](references/apple-container-version-matrix.md) before changing a version-sensitive workflow. + +## Planning Workflow + +1. Inspect project shape: + - `Package.swift` + - executable target and `swift run` command + - existing Dockerfile, Compose file, OCI image documentation, or Apple `container` notes + - Vapor, Hummingbird, OpenAPI, persistence, migration, or background-service entry points + - port, environment, volume, registry, and architecture assumptions +2. Identify the Apple-container job: + - run an existing OCI image locally on Apple silicon + - build an image with Apple's `container` CLI + - compare `container` behavior with Docker behavior + - script a local development flow around `container` + - use Containerization Swift APIs from a macOS tool + - diagnose kernel, VM, image, network, or registry behavior + - create or operate a persistent `container machine` Linux environment +3. Confirm the required host: + - Apple silicon Mac + - supported macOS version for the selected release + - installed `container` CLI or package source checkout + - installed Linux kernel or documented kernel setup path + - started container system services when the selected command needs them +4. Decide whether the repository needs a committed change: + - no change when the task is local runtime diagnosis only + - docs or scripts when the workflow is repeatable for contributors + - Dockerfile reuse when the image should stay OCI-compatible + - Swift API integration only when the app's job is to manage containers directly +5. Validate with the narrowest useful `container`, SwiftPM, log, or HTTP check. + +## Version Gate + +Keep the CLI and Swift package version lines separate: + +- `apple/container` CLI 1.x is the stable command surface covered here. Version 1.0 introduced persistent `container machine` environments, TOML-backed configuration, structured-list output changes, `container cp`, and removal of the version-zero XPC compatibility path. +- `apple/containerization` remains a 0.x Swift package. Pin an exact release or revision, read that source and generated API documentation, and treat source compatibility as unproven until the selected version builds and tests. +- Do not preserve removed `container system property` commands as compatibility shims. Migrate to the documented 1.x TOML configuration and report the required user-visible change. +- Do not infer CLI 1.x behavior from a package 0.x version number, or package API stability from the CLI major version. + +## Apple Containerization Versus Docker + +Use plain language when choosing between the paths: + +- Docker workflow means Dockerfile, Compose, Docker-compatible CI, registries, and generic Linux deployment expectations. +- Apple `container` workflow means Apple's macOS CLI and services for building and running OCI images locally on Apple silicon. +- Containerization Swift API work means writing Swift code against Apple's packages to manage images, registries, filesystems, VMs, or containerized processes. + +Prefer Docker guidance when the service needs portable deployment artifacts for Linux hosts, CI, or common container platforms. Prefer Apple Containerization guidance when the user specifically wants Apple's local macOS runtime, lower-level Swift APIs, or a comparison on Apple silicon. + +## Capability Probe + +Before recommending concrete Apple `container` commands, scripts, or flags, run or request the smallest probe that proves the local tool and docs match the intended workflow: + +- identify the installed `container` version or the checked source/release tag +- open the matching release documentation when release-stable behavior matters +- verify the host is an Apple silicon Mac on a supported macOS version +- verify whether the container system service is started, and start it only through the documented command path when needed +- verify kernel setup status through documented prompts, docs, or CLI diagnostics +- inspect `container --help` and the relevant subcommand help before using Docker-like flags +- record whether the task depends on Rosetta, amd64 images, registry credentials, networking, volumes, or published ports + +If any probe fails, report the missing capability and stop before adding repo scripts or docs that would encode a workflow the machine cannot run. + +## CLI Workflow + +When using the `container` CLI: + +- verify the installed CLI version and the docs version before changing behavior +- start required services with the documented `container system start` path +- follow documented kernel installation prompts or release-specific kernel setup +- inspect available commands with `container --help` and `container --help` +- keep image names, tags, platform assumptions, and registry credentials explicit +- validate the service with logs, process status, published ports, and HTTP checks when applicable + +Do not assume Docker Compose features, Docker Desktop behavior, or Docker CLI flags map directly to Apple's `container` CLI. Check the `container` command's own help and release docs first. + +## Container Machine Workflow + +Use `container machine` for a persistent Linux development environment, not for a disposable application container, macOS virtualization, or a Compose replacement. + +1. Verify CLI version 1.x, current machine documentation, and installed `container machine --help` output. +2. Record the OCI image/digest, architecture, init system, default-machine state, CPU, memory, disks, home-mount policy, kernel, and required services. +3. Inspect the exact create, run, list/inspect, set-default, resource-change, stop, and remove subcommand help before recommending syntax. +4. Treat automatic host-user and home-directory integration as a development convenience. For untrusted work require `home-mount=none` or the current documented equivalent, then hand control review to `cybersecurity-skills:prepare-isolated-analysis-lab`. +5. Stop the machine before any documented resource or configuration operation that requires it, then re-inspect and validate after restart. +6. Gate nested virtualization on supported Apple silicon, host macOS, CLI, compatible kernel configuration, guest support, and observed `/dev/kvm`; never promise it from a flag alone. +7. Validate guest identity, architecture, init/services, filesystem persistence, network, resource changes, reboot behavior, home isolation, and removal. + +Use `apple-dev-skills:linux-development-vm-workflow` when the question is whether a container machine, Lima/Colima environment, or full Linux VM provides the required fidelity. Use `apple-dev-skills:virtualization-framework-workflow` for custom full-VM implementation. + +## Swift API Workflow + +When using the Containerization Swift package directly: + +- verify the package requirements from the current repository or release tag +- inspect the sample or `cctl` executable before writing new API code +- keep API use scoped to the actual job: image management, registry interaction, ext4 filesystem creation, VM runtime, process launch, networking, or Rosetta behavior +- keep Virtualization framework and Apple silicon requirements visible in docs or diagnostics +- add tests around pure Swift decision logic where possible, and keep host-runtime tests explicit because they require supported macOS and Apple silicon + +Do not add Containerization package dependencies to an ordinary server-side Swift service just to run the service in a container. That is local runtime tooling, not service business logic. + +## Vapor And Hummingbird Handoffs + +For Vapor services: + +- use `vapor-server-workflow` for app commands, routes, middleware, migrations, and Vapor environment behavior +- confirm whether `container run` should execute the server, a migration command, or a custom Vapor command + +For Hummingbird services: + +- use `hummingbird-server-workflow` for router, middleware, request context, application lifecycle, and service configuration +- confirm executable arguments, host, port, and logging behavior before encoding a run command + +Use `docker-workflow` when the work is mostly Dockerfile, Compose, or portable OCI image authoring. Use `persistence-workflow` when local container work adds a database service, volume, migration timing, or seed data. + +## Testing And Validation + +Prefer this order: + +1. Verify host and tool support with the current `container` CLI or checked release docs. +2. Validate SwiftPM behavior outside the container runtime when the issue is not runtime-specific. +3. Start Apple's container services through the documented command path. +4. Build, pull, or run the image with explicit tags, ports, and environment. +5. Inspect logs or process status from the `container` CLI. +6. Use `curl` against the running service only when HTTP behavior matters. + +When a `container` command fails, report the exact command, image, runtime service, kernel state, port, registry, architecture, or API symbol involved. Include the likely cause, such as unsupported macOS, missing kernel setup, service not started, registry auth failure, port mismatch, amd64 emulation mismatch, image entry-point mismatch, or a CLI flag that belongs to Docker rather than Apple's tool. + +## Output Shape + +Return: + +1. `Apple container shape`: CLI or Swift API path, host requirements, image source, executable target, ports, environment, and runtime services. +2. `Docs used`: Apple Containerization, apple/container release docs, Virtualization framework, SwiftPM, Vapor, Hummingbird, Docker, or persistence docs consulted. +3. `Command path`: exact `container`, SwiftPM, run, log, registry, or HTTP commands run or recommended. +4. `Runtime behavior`: system service state, image build or pull behavior, entry point, arguments, environment, ports, volumes, networking, Rosetta, and kernel assumptions. + - For container machines also include image/digest, init/services, default-machine state, resources, home-mount policy, persistence, and nested-virtualization evidence. +5. `Validation`: tool version, system start, build, run, logs, HTTP check, or Swift test results. +6. `Handoffs`: Docker, Vapor, Hummingbird, persistence, macOS app integration, CI, registry publish, or deployment follow-up when the task crosses this skill's boundary. + +## Guardrails + +- Do not claim Apple Containerization behavior from memory when current official docs, GitHub release docs, or local CLI help can be checked. +- Do not assume Apple's `container` CLI is a drop-in replacement for Docker or Compose. +- Do not treat `container machine` as an ordinary application container, macOS VM, or safe hostile-workload boundary while home integration is enabled. +- Do not use removed `container system property` commands as a fallback for 1.x configuration. +- Do not claim the 0.x Containerization Swift package is API-stable because the `container` CLI reached 1.x. +- Do not commit registry credentials, local `.env` files, machine-local paths, kernel artifacts, private images, or local runtime state. +- Do not add Containerization Swift package dependencies to a service unless the service is actually a container-management tool. +- Do not hide Apple silicon, macOS, kernel, Virtualization framework, or Rosetta requirements when they affect whether the workflow can run. +- Do not turn local Apple-container diagnostics into production deployment guidance unless the repository already deploys through that path. +- Do not start container services or machines without announcing the exact visible or resource-intensive action first. diff --git a/skills/apple-containerization-workflow/references/apple-container-version-matrix.md b/skills/apple-containerization-workflow/references/apple-container-version-matrix.md new file mode 100644 index 00000000..1a8a0a92 --- /dev/null +++ b/skills/apple-containerization-workflow/references/apple-container-version-matrix.md @@ -0,0 +1,22 @@ +# Apple Container Version Matrix + +Use exact current release notes, tagged source, current docs when explicitly linked by a release, and installed CLI help. Never infer one project's stability from the other's version. + +| Surface | Version rule | Important 1.x or stability boundary | +| --- | --- | --- | +| `apple/container` CLI | discover installed version; use matching release docs and command help | 1.0 adds persistent `container machine`, TOML configuration, structured-output changes, `container cp`, and removes version-zero XPC compatibility | +| `container machine` docs | current-branch docs may be linked from the 1.0 release | pair current docs with installed `container machine --help`; do not assume unreleased flags | +| `apple/containerization` Swift package | pin exact 0.x release/revision and inspect its source/DocC | public API remains pre-1.0; compile/test the selected version and expect source changes | +| OCI image/Dockerfile | pin tags/digests according to project policy | keep portable image authoring independent from the local Apple runtime | +| Linux kernel/init filesystem | record exact provenance and compatibility | runtime, Rosetta, init, and nested-virtualization behavior may depend on these artifacts | + +## 1.x Migration Decisions + +- Replace removed `container system property` automation with the documented TOML configuration path; do not keep a duplicate compatibility command path. +- Re-check parsers that consume structured `list` output instead of assuming the pre-1.0 shape. +- Use `container cp` only after confirming installed help and source/destination semantics. +- Treat ordinary container mounts and a machine's user/home integration as different security and lifecycle surfaces. + +## Required Record + +Record CLI version, package version/revision when applicable, docs/tag/commit consulted, host macOS/architecture, image digest, kernel/init provenance, and every relied-on help surface. diff --git a/skills/assess-macos-threat/SKILL.md b/skills/assess-macos-threat/SKILL.md index d2cd046d..6be30126 100644 --- a/skills/assess-macos-threat/SKILL.md +++ b/skills/assess-macos-threat/SKILL.md @@ -15,6 +15,7 @@ Read [references/macos-security-layers.md](references/macos-security-layers.md) 1. Identify the Mac and event. - Record model/chip, exact macOS build, update state, user/session, time/timezone, managed-device context, and what the person observed. + - Record whether evidence comes from the affected physical host, a macOS guest, or a reproduction guest. For guest evidence, include the VM framework/tool, virtual hardware, restore image, integrations, baseline/reset state, and anti-VM or hardware fidelity limits. 2. Preserve the triggering evidence. - Record alert text/screenshots, file path/source/hash, quarantine metadata, process identity, prompts, downloads, and relevant logs before cleanup. 3. Inspect artifact identity. @@ -27,6 +28,7 @@ Read [references/macos-security-layers.md](references/macos-security-layers.md) - Separate a blocked attempt from successful execution and successful execution from compromise. 6. Assess and advise. - State classification/confidence, immediate isolation needs, evidence gaps, and the smallest next workflow. + - Do not generalize guest-observed behavior to a physical Mac when hardware, Secure Enclave, recoveryOS, kernel/system-extension, device, or anti-VM behavior remains unresolved. ## Output diff --git a/skills/choose-macos-virtualization-shape/SKILL.md b/skills/choose-macos-virtualization-shape/SKILL.md new file mode 100644 index 00000000..a2ebe6fb --- /dev/null +++ b/skills/choose-macos-virtualization-shape/SKILL.md @@ -0,0 +1,75 @@ +--- +name: choose-macos-virtualization-shape +description: Choose the smallest macOS-hosted boundary for development, compatibility, or authorized security research. Use when deciding among the host, containers, container machine, full Linux or macOS VMs, remote systems, or physical Macs. +--- + +# Choose macOS Virtualization Shape + +## Purpose + +Choose one boundary from evidence about fidelity, persistence, portability, host integration, threat level, and resources. Produce the shared shape record in [virtualization-shape-record.md](references/virtualization-shape-record.md); do not return an undecided product menu. + +## When To Use + +- Use for macOS-hosted development, clean-state validation, Linux compatibility, custom VM tools, and security-lab boundary selection. +- Use when container, persistent Linux machine, full VM, and physical Mac terminology is being mixed. +- Use before `virtualization-framework-workflow`, either development-VM workflow, or `prepare-isolated-analysis-lab` when the boundary is not already approved. + +## Single-Path Workflow + +1. Record the purpose, host, target OS and architecture, GUI/headless needs, privileges, kernel/devices, expected lifetime, and evidence requirements. +2. Classify fidelity and risk: + - trusted native macOS behavior: host process + - one portable Linux application: OCI container + - Apple-native per-container VM runtime: Apple `container` + - persistent OCI-backed Linux environment with services: `container machine` + - custom kernel, boot, disk, full-system, or GUI Linux: full Linux VM + - native macOS security, installer, signing, privacy, or OS-version behavior: macOS VM + - hardware, recoveryOS, Secure Enclave, unsupported device, performance, or anti-VM behavior: physical Mac +3. Reject any option that cannot reproduce the target or safely bound the expected behavior. +4. Choose one primary boundary and one explicit fallback only when a named fidelity or availability gap requires it. +5. Complete the shape record with provenance, resources, integrations, lifecycle, validation, evidence, and uncertainty. +6. Hand off implementation or operation to the owner skill. + +## Inputs + +- Task purpose and target behavior. +- Host chip, macOS build, memory, storage, and toolchain. +- Guest OS/version/distro, architecture, devices, privilege, and lifetime. +- Portability, persistence, integration, isolation, and evidence requirements. + +## Outputs + +- `status`: `success`, `handoff`, or `blocked`. +- One selected `boundary` and the reason it is the smallest adequate choice. +- A completed virtualization shape record. +- One owner handoff and any unresolved fidelity gap. + +## Guards and Stop Conditions + +- Do not call a Linux container or Linux VM evidence for native macOS behavior such as Gatekeeper, TCC, XProtect, LaunchServices, or macOS persistence. +- Do not treat automatic home sharing, clipboard, credentials, or sockets as harmless defaults. +- Do not claim two products have equivalent isolation because they use the same framework. +- Do not promise saved VM state is a disk snapshot or portable clone. +- Stop when target fidelity, host capacity, authorization, or safe evidence handling cannot be established. +- Tell Gale before launching a GUI VM, starting a VM or container service, downloading a restore image, creating a large disk, or running a resource-intensive workload. + +## Fallbacks and Handoffs + +- Use `server-side-swift:docker-workflow` for portable OCI authoring and deployment. +- Use `server-side-swift:apple-containerization-workflow` for Apple `container`, `container machine`, or Containerization APIs. +- Use `virtualization-framework-workflow` for custom VM host implementation. +- Use `linux-development-vm-workflow` or `macos-development-vm-workflow` for guest lifecycle work. +- Use `cybersecurity-skills:select-analysis-isolation` and `prepare-isolated-analysis-lab` for untrusted material. +- Escalate to a physical Mac with the unresolved gap stated when VM fidelity is insufficient. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [Virtualization shape record](references/virtualization-shape-record.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for repository guidance when implementation enters an Xcode project. diff --git a/skills/choose-macos-virtualization-shape/agents/openai.yaml b/skills/choose-macos-virtualization-shape/agents/openai.yaml new file mode 100644 index 00000000..f2fb9585 --- /dev/null +++ b/skills/choose-macos-virtualization-shape/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Choose macOS Virtualization Shape" + short_description: "Choose the right macOS-hosted compute boundary" + default_prompt: "Use $choose-macos-virtualization-shape to choose a host, container, VM, or physical Mac boundary for this task." diff --git a/skills/choose-macos-virtualization-shape/references/customization-flow.md b/skills/choose-macos-virtualization-shape/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/skills/choose-macos-virtualization-shape/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/skills/choose-macos-virtualization-shape/references/customization.template.yaml b/skills/choose-macos-virtualization-shape/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/skills/choose-macos-virtualization-shape/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md b/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/skills/choose-macos-virtualization-shape/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/skills/choose-macos-virtualization-shape/references/virtualization-shape-record.md b/skills/choose-macos-virtualization-shape/references/virtualization-shape-record.md new file mode 100644 index 00000000..653a050d --- /dev/null +++ b/skills/choose-macos-virtualization-shape/references/virtualization-shape-record.md @@ -0,0 +1,17 @@ +# Virtualization Shape Record + +Record this compact handoff before implementation. It is a reasoning shape, not a serialized compatibility layer. + +- `purpose`: development, compatibility, CI-like validation, security analysis, framework implementation, or runtime diagnosis +- `host`: Mac/chip, architecture, macOS build, memory/storage budget, and selected toolchain +- `workload`: target OS/version/distro, architecture, GUI/headless, privileges, kernel/devices, and lifetime +- `boundary`: host, OCI container, Apple per-container VM, Apple container machine, full Linux VM, full macOS VM, remote environment, or physical Mac +- `provenance`: tool/framework, restore or OCI image, kernel/init filesystem, and exact versions/digests +- `resources`: CPU, memory, disks, graphics, audio, USB, ballooning, and performance limits +- `integration`: shares, clipboard, sockets, ports, network, agents, credentials, identities, accounts, and browser profiles +- `lifecycle`: supported create/install/start/pause/save/restore/stop/clone/reset/update/export/remove operations +- `validation`: configuration, boot, identity, network, filesystem, service, application, and teardown checks +- `evidence`: logs, artifacts, capture, hashes, screenshots, state, and safe export path +- `uncertainty`: unsupported combinations, beta APIs, VM artifacts, hardware gaps, and physical-device requirements + +Use `absent`, `disabled`, or `not supported` instead of leaving a security-sensitive integration implicit. diff --git a/skills/choose-macos-virtualization-shape/scripts/customization_config.py b/skills/choose-macos-virtualization-shape/scripts/customization_config.py new file mode 100755 index 00000000..805ecef7 --- /dev/null +++ b/skills/choose-macos-virtualization-shape/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "choose-macos-virtualization-shape" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/skills/inspect-macos-persistence/SKILL.md b/skills/inspect-macos-persistence/SKILL.md index fdca7607..2c1bc2cd 100644 --- a/skills/inspect-macos-persistence/SKILL.md +++ b/skills/inspect-macos-persistence/SKILL.md @@ -14,6 +14,7 @@ Read [references/macos-persistence-surfaces.md](references/macos-persistence-sur ## Workflow 1. Record host/build, user domains, event timeline, and the suspected executable or label. + - Record whether the system is a physical Mac or macOS guest, plus the guest's restore image/build, VM tool/framework, integrations, baseline/reset state, and known virtualization artifacts. 2. Inventory user-visible registrations. - Review Login Items and background-item state, profiles, extensions, browser add-ons, and app-managed helpers. 3. Inventory launch services. @@ -25,6 +26,7 @@ Read [references/macos-persistence-surfaces.md](references/macos-persistence-sur - Identify parent installer/app, creation/change time, signature/notarization, executable hash, running process ancestry, files, network, and logs. 6. Classify each item. - Expected, suspicious, confirmed malicious, disabled/orphaned, or unresolved; explain evidence and impact. + - Keep guest-observed persistence distinct from physical-host proof when anti-VM, hardware, recoveryOS, kernel/system-extension, or device behavior may differ. 7. Preserve before containment. - Record files and service state before using official `launchctl bootout` or app/uninstaller paths in the containment workflow. diff --git a/skills/inspect-macos-runtime-activity/SKILL.md b/skills/inspect-macos-runtime-activity/SKILL.md index 5d97ae79..16f3134c 100644 --- a/skills/inspect-macos-runtime-activity/SKILL.md +++ b/skills/inspect-macos-runtime-activity/SKILL.md @@ -14,6 +14,7 @@ Read [references/macos-runtime-evidence.md](references/macos-runtime-evidence.md ## Workflow 1. Fix host/build, user/session, time window, process/artifact identity, and reported symptom. + - Label every observation as physical-host, affected-host, or macOS-guest evidence. For a guest, record VM tool/framework, virtual hardware, restore-image/build, shares/devices/network, baseline/reset state, and virtualization artifacts that may alter behavior. 2. Capture current process context. - Record PID, executable path/hash/signature, user, parent/ancestry, arguments, environment when authorized, start time, code state, and deleted/replaced executable clues. 3. Correlate files and registrations. @@ -27,6 +28,7 @@ Read [references/macos-runtime-evidence.md](references/macos-runtime-evidence.md - Separate user action, launch, child processes, file changes, prompts, network, persistence, detection, and termination. 7. Assess behavior and gaps. - Route binary internals, dynamic reproduction, containment, or hunting as needed. + - State anti-VM, hardware, Secure Enclave, recoveryOS, kernel/system-extension, and device-access limitations before treating guest evidence as physical-Mac proof. ## Output diff --git a/skills/linux-development-vm-workflow/SKILL.md b/skills/linux-development-vm-workflow/SKILL.md new file mode 100644 index 00000000..e12c77e5 --- /dev/null +++ b/skills/linux-development-vm-workflow/SKILL.md @@ -0,0 +1,74 @@ +--- +name: linux-development-vm-workflow +description: Prepare and reset persistent Linux development guests on macOS. Use when comparing container machine, Lima or Colima, and full VMs for distros, init systems, services, custom boot, disks, Rosetta, or nested virtualization. +--- + +# Linux Development VM Workflow + +## Purpose + +Prepare one persistent Linux development environment whose lifecycle, host integrations, provenance, validation, and reset path are explicit. + +## When To Use + +- Use for distro-specific builds, services, systemd or another init system, repeated shells, full-system tests, custom kernels, EFI boot, or GUI Linux. +- Use to decide between `container machine`, Lima/Colima, and a full VM by required fidelity rather than product preference. +- Do not use for a single portable application image; use the container owner skills. + +## Single-Path Workflow + +1. Consume the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +2. Discover current official documentation and installed versions/help for every candidate tool. +3. Select the smallest adequate path: + - `container machine`: OCI-backed persistent Linux, init/services, repeated interactive development + - Lima/Colima adapter: tool-managed Linux environment when its documented lifecycle and integration match the task + - full Virtualization framework VM: custom boot/kernel/disk/devices, full-system or GUI behavior, or tighter integration control +4. Record distro/image/kernel provenance, architecture, CPU, memory, disks, network, mounts, sockets, credentials, and expected lifetime. +5. Keep host home, writeable shares, SSH agent, credentials, clipboard, and unrestricted network opt-in. A development convenience is not a security boundary. +6. Configure Linux or EFI boot, virtio devices, provisioning, services, Rosetta, and nested virtualization only when the selected path and current host/guest support them. +7. Define create, provision, start, shell/SSH, stop, update, checkpoint/reset, export, and remove semantics using the selected tool's vocabulary. +8. Validate the distro matrix: identity, architecture, toolchain, build, tests, services, filesystem semantics, network, reboot persistence, and cleanup. + +## Inputs + +- Completed virtualization shape record. +- Distro/version, architecture, system services, boot/kernel needs, toolchain, resources, integrations, and reset frequency. +- Exact selected tool version and official documentation. + +## Outputs + +- Selected Linux guest path and rejected alternatives. +- Provenance and resource/integration record. +- Exact lifecycle and provisioning path. +- Distro-matrix validation and reset/teardown evidence. + +## Guards and Stop Conditions + +- Do not call `container machine` a macOS VM, ordinary application container, or Compose replacement. +- Do not assume Docker, Apple `container`, Lima, Colima, or a custom VM share flags or lifecycle semantics. +- Do not enable home sharing for untrusted work; hand security research to `prepare-isolated-analysis-lab`. +- Do not promise Rosetta or nested virtualization without host, OS, kernel, and device proof. +- Do not commit images, kernels, disks, credentials, or machine-local runtime state. +- Stop when provenance, capacity, reset strategy, host integration, or required fidelity cannot be verified. +- Announce before starting a VM/service, downloading an image, or creating a large disk. + +## Fallbacks and Handoffs + +- Use `server-side-swift:apple-containerization-workflow` for `container machine` command semantics. +- Use `server-side-swift:docker-workflow` for Dockerfiles, Compose, registries, and portable OCI deployment. +- Use `virtualization-framework-workflow` for custom full-VM implementation. +- Use `xcode-build-run-workflow`, `swift-package-build-run-workflow`, or stack-specific skills after the guest is ready. +- Use `prepare-isolated-analysis-lab` for disposable hostile-workload controls. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [Linux development guest matrix](references/linux-development-guest-matrix.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple container machine documentation](https://github.com/apple/container/blob/main/docs/container-machine.md) +- [Lima documentation](https://lima-vm.io/docs/) +- [Colima repository](https://github.com/abiosoft/colima) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for a custom Xcode VM host. diff --git a/skills/linux-development-vm-workflow/agents/openai.yaml b/skills/linux-development-vm-workflow/agents/openai.yaml new file mode 100644 index 00000000..46a054e8 --- /dev/null +++ b/skills/linux-development-vm-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Linux Development VM Workflow" + short_description: "Prepare persistent Linux development guests" + default_prompt: "Use $linux-development-vm-workflow to choose, prepare, validate, and reset this Linux development guest." diff --git a/skills/linux-development-vm-workflow/references/customization-flow.md b/skills/linux-development-vm-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/skills/linux-development-vm-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/skills/linux-development-vm-workflow/references/customization.template.yaml b/skills/linux-development-vm-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/skills/linux-development-vm-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md b/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md new file mode 100644 index 00000000..96b7d729 --- /dev/null +++ b/skills/linux-development-vm-workflow/references/linux-development-guest-matrix.md @@ -0,0 +1,17 @@ +# Linux Development Guest Matrix + +Use exact current docs and installed help; this matrix chooses a shape, not a brand default. + +| Need | Container machine | Lima/Colima adapter | Full Virtualization framework VM | +| --- | --- | --- | --- | +| Persistent interactive Linux | primary fit | primary fit | supported with more ownership | +| OCI-image-backed root | primary model | tool-specific | custom image/disk work | +| Init and services | supported by compatible image | tool-specific | guest-owned | +| Custom kernel/boot | limited to documented machine/kernel controls | tool-specific | primary fit | +| Full disk/install lifecycle | not the primary model | tool-specific | primary fit | +| GUI Linux | not primary | tool-specific | explicit graphics/input/UI path | +| Host integration | automatic user/home conveniences require review | tool-specific mounts/sockets | explicitly selected devices/shares | +| Portable OCI deployment | hand off to Docker workflow | hand off to Docker workflow | hand off to Docker workflow | +| Hostile workload | disable home integration; still require lab review | require lab review | require lab profile and verified controls | + +For every selected distro record: image/digest, OS release, architecture, kernel, init, toolchain, build/test result, services, filesystem, network, reboot persistence, reset, and removal. diff --git a/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md b/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/skills/linux-development-vm-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/skills/linux-development-vm-workflow/scripts/customization_config.py b/skills/linux-development-vm-workflow/scripts/customization_config.py new file mode 100755 index 00000000..e814a986 --- /dev/null +++ b/skills/linux-development-vm-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "linux-development-vm-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/skills/macos-development-vm-workflow/SKILL.md b/skills/macos-development-vm-workflow/SKILL.md new file mode 100644 index 00000000..7c2386dc --- /dev/null +++ b/skills/macos-development-vm-workflow/SKILL.md @@ -0,0 +1,70 @@ +--- +name: macos-development-vm-workflow +description: Prepare and reset clean macOS development guests on Apple silicon. Use for restore-image compatibility, VM identity, installation, resources, OS-version testing, signing, privacy, and disposable macOS research guests. +--- + +# macOS Development VM Workflow + +## Purpose + +Prepare a reproducible macOS guest while keeping restore images, identity, disks, saved state, clones, integrations, and exported evidence as separate lifecycle artifacts. + +## When To Use + +- Use for clean macOS releases, installers, updates, signing, entitlements, quarantine, Gatekeeper, XProtect, TCC, SIP-enabled behavior, LaunchServices, and native persistence. +- Use after custom host implementation or with an existing documented VM manager. +- Use for development guests and benign security fixtures; security controls still require `prepare-isolated-analysis-lab` for untrusted execution. + +## Single-Path Workflow + +1. Consume the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +2. Verify Apple silicon host, host build, current framework/tool docs, restore-image support, guest build, storage, memory, and installation time budget. +3. Record each artifact using [macOS VM artifact lifecycle](references/macos-vm-artifact-lifecycle.md): restore image, hardware model, machine identifier, auxiliary storage, disk, bundle metadata, saved state, clone, and evidence export. +4. Build or verify a compatible Mac platform, boot loader, disk, CPU/memory, graphics/display, network, input, and entropy configuration; validate before installation or boot. +5. Install with the documented restore-image flow and preserve exact progress/errors. Do not invent or duplicate Mac identity artifacts. +6. Configure guest purpose and integrations. Directory sharing, clipboard, audio input, USB, Apple account, iCloud, developer account, signing identities, browser profiles, and network are opt-in. +7. Establish a clean baseline, then choose named development/update checkpoints or disposable clones using only lifecycle operations the selected tool actually supports. +8. Validate guest build/architecture, SIP and relevant controls, network/shares, reboot, toolchain, target behavior, evidence export, and reset. +9. Record VM artifacts and physical-hardware gaps that may affect the conclusion. + +## Inputs + +- Completed virtualization shape record. +- Host and target guest builds, restore-image source, resources, selected VM tool/framework, integrations, and validation purpose. +- Required toolchains, identities, accounts, security controls, reset strategy, and evidence path. + +## Outputs + +- Compatible restore/image and VM identity record. +- Separate artifact lifecycle and integration decisions. +- Installation, baseline/checkpoint, validation, export, and reset evidence. +- Explicit physical-Mac or unsupported-capability gaps. + +## Guards and Stop Conditions + +- Do not treat an arbitrary restore image as compatible with the host/platform configuration. +- Do not conflate saved machine state with disk state, a clone, or a portable snapshot. +- Do not copy identity artifacts between independent VMs without documented tool support and an explicit identity decision. +- Do not add personal Apple accounts, developer identities, credentials, shares, clipboard, devices, or microphone by default. +- Treat automated macOS guest provisioning as beta and availability-gated until current SDK/runtime evidence proves the selected path. +- Stop for unsupported restore compatibility, unresolved disk ownership, insufficient capacity, unclear identity, or a hardware/recoveryOS/Secure Enclave fidelity requirement. +- Announce before downloads, large disk creation, installation, or visible/resource-intensive launch. + +## Fallbacks and Handoffs + +- Use `virtualization-framework-workflow` for custom host configuration or lifecycle defects. +- Use `apple-developer-provisioning-workflow` only after the guest boundary is approved and ready. +- Use `xcode-build-run-workflow`, `xcode-testing-workflow`, or `macos-distribution-workflow` for work inside the prepared guest. +- Use `prepare-isolated-analysis-lab` before executing untrusted content. +- Use a spare physical Mac when hardware, recoveryOS, Secure Enclave, device, performance, or anti-VM fidelity is required. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [macOS VM artifact lifecycle](references/macos-vm-artifact-lifecycle.md) +- [macOS and Linux guest matrix](../virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) for a custom Xcode VM host. diff --git a/skills/macos-development-vm-workflow/agents/openai.yaml b/skills/macos-development-vm-workflow/agents/openai.yaml new file mode 100644 index 00000000..fd6b8f11 --- /dev/null +++ b/skills/macos-development-vm-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "macOS Development VM Workflow" + short_description: "Prepare clean macOS development guests" + default_prompt: "Use $macos-development-vm-workflow to prepare, validate, checkpoint, and reset this macOS development guest." diff --git a/skills/macos-development-vm-workflow/references/customization-flow.md b/skills/macos-development-vm-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/skills/macos-development-vm-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/skills/macos-development-vm-workflow/references/customization.template.yaml b/skills/macos-development-vm-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/skills/macos-development-vm-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md b/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md new file mode 100644 index 00000000..a36dbec6 --- /dev/null +++ b/skills/macos-development-vm-workflow/references/macos-vm-artifact-lifecycle.md @@ -0,0 +1,17 @@ +# macOS VM Artifact Lifecycle + +Keep these artifacts separate; no single file is “the VM snapshot.” + +| Artifact | Owns | Lifecycle rule | +| --- | --- | --- | +| Restore image | installer and supported macOS build metadata | verify compatibility and provenance; cache/remove independently | +| Hardware model | supported virtual Mac hardware description | bind to compatible configuration and guest install | +| Machine identifier | virtual Mac identity | generate/persist deliberately; do not casually duplicate | +| Auxiliary storage | platform boot/security state | persist with its VM identity and installed guest | +| Disk image | guest filesystem and installed software | coordinate shutdown/copy semantics; saved state does not replace it | +| VM bundle metadata | configuration and artifact locations | keep portable paths and explicit schema/version ownership | +| Saved machine state | paused/stopped runtime state | restore only with documented state and compatible configuration/artifacts | +| Clone/checkpoint | tool-specific copy of required artifacts | name the exact copy/revert operation; do not imply framework snapshots | +| Evidence export | intentionally selected logs/artifacts | export narrowly, hash/scan as required, and keep separate from control state | + +Record ownership, permissions, size, source/digest, creation time, compatible host/guest versions, and removal semantics for every artifact. diff --git a/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md b/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/skills/macos-development-vm-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/skills/macos-development-vm-workflow/scripts/customization_config.py b/skills/macos-development-vm-workflow/scripts/customization_config.py new file mode 100755 index 00000000..163086d4 --- /dev/null +++ b/skills/macos-development-vm-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "macos-development-vm-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/skills/perform-dynamic-malware-analysis/SKILL.md b/skills/perform-dynamic-malware-analysis/SKILL.md index 63e3438e..7b1e6b45 100644 --- a/skills/perform-dynamic-malware-analysis/SKILL.md +++ b/skills/perform-dynamic-malware-analysis/SKILL.md @@ -7,7 +7,7 @@ description: Observe suspicious content in a disposable, instrumented environmen ## Overview -Execute only inside an environment chosen by `select-analysis-isolation`, with an observation plan that can distinguish artifact behavior from baseline noise. Preserve the exact sample and environment identity. +Execute only inside an environment chosen by `select-analysis-isolation` and preflighted by `prepare-isolated-analysis-lab`, with an observation plan that can distinguish artifact behavior from baseline noise. Preserve the exact sample, prepared-lab record, and environment identity. Read [references/dynamic-observation-plan.md](references/dynamic-observation-plan.md) for baseline, stimulus, telemetry, and teardown fields. @@ -15,7 +15,8 @@ Read [references/dynamic-observation-plan.md](references/dynamic-observation-pla 1. Define the unresolved question and minimum stimulus. 2. Verify isolation. - - Record guest/platform build, snapshot, accounts, shares, clipboard, devices, credentials, network mode, monitoring, and export path. + - Require the prepared-lab record and verify its guest/platform build, baseline/reset state, accounts, shares, clipboard, devices, credentials, network mode, monitoring, stop controls, export path, and teardown plan are still current. + - Record virtualization artifacts or anti-VM behavior that may affect the conclusion. 3. Capture a baseline. - Record processes, files/registrations, persistence surfaces, network state, services, and relevant logs before execution. 4. Execute one controlled step. @@ -31,4 +32,4 @@ Read [references/dynamic-observation-plan.md](references/dynamic-observation-pla ## Output -Return environment/baseline identity, stimulus, observed timeline, artifacts and indicators, absent expected behavior, evasion/coverage limits, conclusion, and teardown verification. +Return the prepared-lab identity, environment/baseline identity, stimulus, observed timeline, artifacts and indicators, absent expected behavior, virtualization/evasion/coverage limits, conclusion, and teardown verification. diff --git a/skills/prepare-isolated-analysis-lab/SKILL.md b/skills/prepare-isolated-analysis-lab/SKILL.md new file mode 100644 index 00000000..ef15110c --- /dev/null +++ b/skills/prepare-isolated-analysis-lab/SKILL.md @@ -0,0 +1,57 @@ +--- +name: prepare-isolated-analysis-lab +description: Prepare a verified disposable Linux or macOS analysis lab from an approved isolation decision. Use before active research to control host integration, networking, baseline, monitoring, evidence export, reset, and teardown. +--- + +# Prepare Isolated Analysis Lab + +## Overview + +Turn the boundary selected by `select-analysis-isolation` into a concrete, reviewable control profile before executing untrusted content. Evidence collection and analysis remain owned by their specialist skills. + +Read [security-lab-control-profile.md](references/security-lab-control-profile.md) before approving a lab. + +## Workflow + +1. Consume the approved isolation decision. + - Record authorization, unresolved question, target OS/architecture/privilege, expected behavior, selected boundary, host/guest builds, and VM artifacts that may affect conclusions. +2. Select one profile. + - offline static tooling + - monitored Linux dynamic analysis + - monitored macOS dynamic analysis + - network-service research + - nested-virtualization experiment +3. Verify a trusted base. + - Record image/restore provenance and digest, guest build, tool versions, clock strategy, resource limits, baseline state or hashes, reset mechanism, and virtualization artifacts that may change observed behavior. +4. Remove ambient authority. + - Default host folders/home sharing, clipboard, drag/drop, sockets, SSH agent, browser profiles, cloud credentials, Apple accounts, signing identities, USB, microphone, camera, and unrelated devices to absent. +5. Constrain and observe networking. + - Default to offline or simulated services. + - When external connectivity is authorized, record destinations, routes, DNS, monitoring/capture, ingress, egress, and forwarded ports. +6. Define a narrow evidence path. + - Name the guest staging location, allowed artifact types, host export directory, hashing and scanning steps, size limits, and owner. +7. Run a preflight without executing the target. + - Verify accounts, shares, clipboard, devices, sockets, credentials, network, monitoring, clock, baseline, stop controls, export path, and reset operation. +8. Hand the prepared-lab record to `perform-dynamic-malware-analysis` or the relevant observation skill. +9. Verify teardown. + - Stop the workload; export only intended evidence; hash/scan it; revert or remove disposable state; revoke temporary credentials; remove shares/ports/helpers; confirm no workload or integration remains active. + +## Output + +Return the approved isolation decision, selected profile, trusted-base identity, full control profile, preflight evidence, observation handoff, export manifest, teardown evidence, and remaining fidelity limits. + +## Stop Conditions + +- Stop when authorization for active testing, target-platform fidelity, trusted-base provenance, isolation controls, observation coverage, safe evidence export, or reset/teardown cannot be verified. +- Stop when the task requires host secrets, personal accounts, developer identities, or uncontrolled devices. +- Stop when anti-VM, hardware, Secure Enclave, recoveryOS, kernel/system-extension, or device behavior makes VM evidence insufficient; state the physical-device gap. +- Never weaken host SIP, Gatekeeper, XProtect, TCC, App Sandbox, or other protections to make the lab convenient. +- Never start a guest, service, network capture, or payload without announcing the exact visible or resource-intensive action first. + +## Handoffs + +- Use `perform-dynamic-malware-analysis` for controlled execution and observation. +- Use Reverse Engineering skills for exported binaries, disassembly, decompilation, and symbols. +- Use `apple-dev-skills:virtualization-framework-workflow` for custom VM implementation defects. +- Use `apple-dev-skills:macos-development-vm-workflow` or `linux-development-vm-workflow` for benign guest provisioning and lifecycle mechanics. +- Return to `select-analysis-isolation` when the chosen boundary fails fidelity or containment preflight. diff --git a/skills/prepare-isolated-analysis-lab/agents/openai.yaml b/skills/prepare-isolated-analysis-lab/agents/openai.yaml new file mode 100644 index 00000000..0f97bd8b --- /dev/null +++ b/skills/prepare-isolated-analysis-lab/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Prepare Isolated Analysis Lab" + short_description: "Prepare and verify disposable analysis labs" + default_prompt: "Use $prepare-isolated-analysis-lab to turn this approved isolation choice into a verified disposable lab and teardown contract." diff --git a/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md b/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md new file mode 100644 index 00000000..26780319 --- /dev/null +++ b/skills/prepare-isolated-analysis-lab/references/security-lab-control-profile.md @@ -0,0 +1,60 @@ +# Security Lab Control Profile + +Every field must be explicit. Use `absent`, `disabled`, `read-only`, `allowlisted`, or another verified state instead of relying on a product default. + +## Identity And Base + +- authorization and research question +- host model/chip/build; guest OS/build/architecture +- base image or restore-image source and digest +- VM manager/framework/CLI and exact version +- accounts, privilege, clock/time zone, CPU, memory, disk, and reset operation +- baseline hashes/state and virtualization artifacts that may change behavior + +## Host Integration + +- host folders and home sharing +- clipboard and drag/drop +- host/guest sockets and SSH agent +- browser profiles, password stores, developer certificates, signing identities +- Apple/cloud accounts, tokens, registries, package-manager credentials +- USB, microphone, camera, graphics, audio, and other passthrough devices + +Default every item above to absent. Approve an exception only when it is required by the named observation and has a removal check. + +## Network + +- offline, simulated, allowlisted egress, or monitored network mode +- DNS, gateway, routes, packet/log capture, ingress, egress, and time source +- external destinations and authorization +- forwarded/listening ports and their teardown checks + +## Observation And Stop Controls + +- process, filesystem, persistence, service, security-control, DNS/network, and log telemetry +- minimum stimulus, execution identity, stop command/control, resource/time limits +- anti-VM and coverage limitations + +## Evidence Export + +- guest staging directory and host export directory +- allowed artifact types and maximum size +- hashes, archive format, metadata, malware scanning, and human review +- prohibition on exporting live credentials, sockets, whole home directories, or unrelated guest state + +## Teardown Proof + +- workload and guest stopped +- intended evidence exported and scanned +- disposable state reverted or removed +- temporary credentials revoked +- shares, clipboard, sockets, devices, routes, captures, and forwarded ports removed +- no helper, service, or workload remains active + +## Profiles + +- `offline-static`: no target execution, no network, read-only sample input, narrow report export +- `monitored-linux-dynamic`: disposable Linux guest, no home sharing, monitored or simulated network, process/filesystem/service capture +- `monitored-macos-dynamic`: disposable macOS guest, native control telemetry, no personal Apple/developer identities, VM-fidelity caveats +- `network-service`: isolated test network, explicit clients/servers, allowlisted ingress/egress, packet capture, port teardown +- `nested-virtualization`: verified host/guest/kernel capability, no home sharing, inner and outer lifecycle/evidence ownership, resource limits diff --git a/skills/select-analysis-isolation/SKILL.md b/skills/select-analysis-isolation/SKILL.md index 712d1ec3..c6eb69d2 100644 --- a/skills/select-analysis-isolation/SKILL.md +++ b/skills/select-analysis-isolation/SKILL.md @@ -37,6 +37,11 @@ Read [references/isolation-matrix.md](references/isolation-matrix.md) before sel 6. Verify teardown. - Stop the workload, export intended evidence, revert or destroy disposable state, revoke temporary credentials, and confirm no host share or forwarded port remains. +7. Hand an approved execution boundary to `prepare-isolated-analysis-lab`. + - Use `apple-dev-skills:choose-macos-virtualization-shape` when the development boundary is still undecided. + - Use `apple-dev-skills:virtualization-framework-workflow` when a custom macOS or Linux VM host must be implemented or diagnosed. + - Treat a SIP-enabled macOS VM as the stable high-fidelity path for SIP-sensitive behavior; local sandbox, TCC, and failure injection are explicitly lower-fidelity approximations. + ## Stop Conditions -Stop before execution when the environment cannot reproduce the target platform, the isolation controls cannot be verified, or the task requires host secrets or privileges beyond the approved analysis plan. +Stop before execution when the environment cannot reproduce the target platform, the isolation controls cannot be verified, or the task requires host secrets or privileges beyond the approved analysis plan. Selection alone does not authorize execution; require the prepared-lab record first. diff --git a/skills/virtualization-framework-workflow/SKILL.md b/skills/virtualization-framework-workflow/SKILL.md new file mode 100644 index 00000000..eed2a8e8 --- /dev/null +++ b/skills/virtualization-framework-workflow/SKILL.md @@ -0,0 +1,72 @@ +--- +name: virtualization-framework-workflow +description: Build and diagnose custom macOS and Linux VM hosts with Apple's Virtualization framework. Use for platform and boot configuration, devices, VM bundles, lifecycle, save and restore, UI, entitlements, and framework errors. +--- + +# Virtualization Framework Workflow + +## Purpose + +Implement one explicit macOS or Linux Virtualization framework path without flattening their platform, boot, identity, or device differences. + +## When To Use + +- Use for `VZVirtualMachineConfiguration`, guest devices, `VZVirtualMachine`, `VZVirtualMachineView`, lifecycle, and diagnostics. +- Use when building a custom VM host app or Swift package rather than operating an existing VM manager. +- Use for save/restore capability checks, not as a general snapshot-product workflow. + +## Single-Path Workflow + +1. Read current Xcode-local Virtualization documentation for every selected API and availability gate. +2. Consume or create the [virtualization shape record](../choose-macos-virtualization-shape/references/virtualization-shape-record.md). +3. Choose the guest family using [macOS and Linux guest matrix](references/macos-and-linux-guest-matrix.md): + - macOS: Mac platform identity, macOS boot loader, restore-image compatibility, auxiliary storage + - Linux/generic: generic platform, Linux or EFI boot, kernel/initrd/command line or EFI disk +4. Separate the implementation into configuration construction, bundle/artifact persistence, VM lifecycle, and optional UI ownership. Make a headless console/service path or `VZVirtualMachineView` ownership explicit rather than creating both accidentally. +5. Add only required devices after checking [device and availability matrix](references/virtualization-device-and-availability-matrix.md). +6. Require the virtualization entitlement, supported CPU/memory values, exact OS availability, and `validate()` before start. +7. Model start, pause, resume, stop, and state transitions explicitly. Save/restore only in documented states with a configuration compatible with the saved state. +8. Validate configuration, boot, console/UI, disk, network, shares, services, shutdown, and teardown at the narrowest relevant level. +9. Preserve the failed configuration surface, VM state, host/guest versions, underlying error, and likely cause. + +## Inputs + +- Completed virtualization shape record. +- Guest family, boot source, identity artifacts, disks, devices, resources, UI needs, and lifecycle requirements. +- Host macOS/Xcode version and target deployment version. + +## Outputs + +- `status`: `success`, `handoff`, or `blocked`. +- Documented configuration and availability decisions. +- Separate configuration, artifact, lifecycle, and UI ownership. +- Validation evidence and exact diagnostics. + +## Guards and Stop Conditions + +- Do not start before configuration validation succeeds. +- Do not reuse a macOS hardware model, machine identifier, or auxiliary storage as if it were a generic Linux platform. +- Do not expose shares, clipboard, sockets, devices, audio input, or USB without a stated need. +- Do not call saved machine state a disk snapshot, clone, or portable VM bundle. +- Do not promise nested virtualization, Rosetta, clipboard, USB, or save/restore without guest and OS capability proof. +- Stop when the restore image, boot artifacts, entitlement, host support, configuration compatibility, or disk ownership is unresolved. +- Announce before any visible or resource-intensive launch. + +## Fallbacks and Handoffs + +- Use `choose-macos-virtualization-shape` when the boundary is undecided. +- Use `linux-development-vm-workflow` or `macos-development-vm-workflow` for guest preparation and reset strategy. +- Use `xcode-app-project-workflow` for target membership, entitlement wiring, and app-project integration. +- Use `xcode-build-run-workflow` and `xcode-testing-workflow` for execution and tests. +- Use `prepare-isolated-analysis-lab` for hostile-workload control policy. + +## Customization + +Use [customization-flow.md](references/customization-flow.md). The first release has no runtime-enforced knobs. + +## References + +- [macOS and Linux guest matrix](references/macos-and-linux-guest-matrix.md) +- [Device and availability matrix](references/virtualization-device-and-availability-matrix.md) +- [Apple Virtualization framework](https://developer.apple.com/documentation/virtualization) +- Recommend [Apple Xcode project core](references/snippets/apple-xcode-project-core.md) when editing an Xcode project. diff --git a/skills/virtualization-framework-workflow/agents/openai.yaml b/skills/virtualization-framework-workflow/agents/openai.yaml new file mode 100644 index 00000000..a4531283 --- /dev/null +++ b/skills/virtualization-framework-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Virtualization Framework Workflow" + short_description: "Build and diagnose Apple virtual machines" + default_prompt: "Use $virtualization-framework-workflow to design or diagnose this Virtualization framework host." diff --git a/skills/virtualization-framework-workflow/references/customization-flow.md b/skills/virtualization-framework-workflow/references/customization-flow.md new file mode 100644 index 00000000..54484e3e --- /dev/null +++ b/skills/virtualization-framework-workflow/references/customization-flow.md @@ -0,0 +1,21 @@ +# Customization Flow + +Preserve the repo-wide customization-file contract without pretending this +workflow already has runtime-tunable behavior. + +## Current Behavior + +- `references/customization.template.yaml` is the default persisted shape. +- `scripts/customization_config.py` can show, apply, and reset customization + state for consistency with the rest of Apple Dev Skills. +- The workflow currently ignores persisted settings at runtime because no + runtime-enforced knobs are documented yet. + +## Future Knobs + +Only add runtime behavior after documenting: + +- the exact setting key +- the allowed values +- which recommendation changes when the setting is present +- how tests prove the change is applied diff --git a/skills/virtualization-framework-workflow/references/customization.template.yaml b/skills/virtualization-framework-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/skills/virtualization-framework-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md b/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md new file mode 100644 index 00000000..3d639a68 --- /dev/null +++ b/skills/virtualization-framework-workflow/references/macos-and-linux-guest-matrix.md @@ -0,0 +1,15 @@ +# macOS And Linux Guest Matrix + +| Concern | macOS guest | Linux or generic guest | +| --- | --- | --- | +| Platform | `VZMacPlatformConfiguration` | `VZGenericPlatformConfiguration` | +| Boot | `VZMacOSBootLoader` plus compatible restore image | `VZLinuxBootLoader` or EFI boot loader | +| Identity | hardware model, machine identifier, auxiliary storage | generic platform; no Mac identity artifacts | +| Installation | restore-image and installer flow | kernel/initrd/root filesystem or EFI installer/disk | +| Graphics/UI | Mac graphics device and display | virtio graphics when supported/needed | +| Rosetta | not an assumed nested container capability | Linux Rosetta directory share when documented and supported | +| Nested virtualization | do not infer support from generic-platform APIs | capability-gated generic platform plus guest kernel/device proof | +| Security fidelity | useful for native macOS controls, with VM artifacts | cannot prove Gatekeeper, TCC, XProtect, LaunchServices, or macOS persistence | +| Physical fidelity | limited for hardware, Secure Enclave, recoveryOS, devices, performance, and anti-VM behavior | limited for host-hardware and anti-VM behavior | + +Always check the current SDK documentation and host capabilities; this table identifies ownership differences, not universal availability. diff --git a/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md b/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..f161db8e --- /dev/null +++ b/skills/virtualization-framework-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,142 @@ +# 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. +- For Apple, Swift, and Xcode documentation, use Xcode MCP `DocumentationSearch` first. Then use the Dash.app MCP when its installed docsets cover the question. Use Dash localhost HTTP only when the Dash.app MCP is unavailable or incomplete; use checked-out source, generated DocC, GitHub/source repositories, release notes, and readable online documentation only after those local MCP paths. 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. +- 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 as declarative component UI, closer to React, F# Fabulous, and Elm than to imperative AppKit or UIKit code. Keep views self-contained, reactive, flexible, reusable, and easy to scan from top to bottom. +- Give each independently reusable view a declarative interface of plain values, narrow bindings, and action closures. Do not inject external ViewModels, stores, coordinators, managers, services, or other collaborating objects from one reusable view into another. +- 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` and extracted modifiers `GEAWhateverViewModifier.swift`. Do not introduce ViewModel files as a SwiftUI default. +- 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. +- Prefer `@State`, derived values, bindings, and small private helpers for component-local presentation state. When a component genuinely needs an observable state type, create and own it locally with `@State`; do not pass it to a separately reusable view. +- 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 existing SwiftUI environment values and actions before inventing an equivalent router or service. Use environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Model app capabilities as direct, concrete feature services. A service provides one capability or a cohesive group of related operations directly to the app; it talks directly to the framework, persistence, network, or system boundary that capability needs instead of forwarding through an app-service wrapper, repository stack, or manager chain. +- Create a feature service at the narrowest app or scene boundary that owns its lifecycle. Put a service into the SwiftUI environment only when independent descendants need to invoke it or observe its state directly. Keep a service private to its feature root when that is the only consumer. +- A service may be `@Observable` when the UI must observe its feature state. Otherwise prefer direct values, async operations, explicit errors, and narrow action closures. Reusable leaf views still receive only values, bindings, and action closures; never pass a service, repository, coordinator, manager, ViewModel, store, or other collaborator into their public interface. +- Keep services concrete by default. Introduce a protocol only for a demonstrated alternate implementation or boundary that cannot otherwise be tested; do not create protocol, adapter, or wrapper layers merely because a service exists. +- Add custom environment values or actions when a capability is dynamic across the hierarchy or shared by many independent components. Keep actions local to the owning component when only that component and its private child views use them. +- Use preference keys only to publish descendant-derived information upward to an ancestor, never as a general state bus. +- Prefer Swift's synthesized memberwise initializer for view properties. Do not write an explicit initializer unless it has real behavior beyond assigning those properties. +- 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 direct concrete feature and boundary services. Use `Consumed/` for external capabilities the app calls, `Internal/` for app-owned feature services, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. These directories describe ownership and direction; they do not justify wrapper layers or an app-wide service container. +- Name a service for its capability, such as `GEADownloadService.swift` or `GEAImportService.swift`. Do not create `GEAAppService.swift` as an umbrella service by default; `GEAApp.swift` remains 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/skills/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md b/skills/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md new file mode 100644 index 00000000..2816c6eb --- /dev/null +++ b/skills/virtualization-framework-workflow/references/virtualization-device-and-availability-matrix.md @@ -0,0 +1,20 @@ +# Virtualization Device And Availability Matrix + +Check current Xcode-local documentation for every concrete class before implementing it. Record host OS, guest family, minimum deployment target, required guest support, and whether the device crosses a security boundary. + +| Family | Decision to record | +| --- | --- | +| Storage | image/block device ownership, caching/synchronization, read-only state, attachment lifetime | +| Network | NAT or bridged attachment, MAC address, ports, monitoring, external reachability | +| Directory sharing | exact host directory, read/write state, tag/mount point, hostile-workload prohibition | +| Socket/console | endpoint ownership, authentication, serial console/log retention | +| Graphics/input | display dimensions, headless/UI path, keyboard and pointing devices | +| Audio | output/input need; microphone access remains opt-in | +| USB | controller/device support and explicit passthrough need | +| Memory balloon | guest support and expected pressure behavior | +| Entropy | guest random device requirement | +| Rosetta | supported Linux guest path and installation/share requirements | +| Nested virtualization | host/chip/OS support, generic-platform setting, guest kernel, `/dev/kvm` proof | +| Save/restore | host API availability, paused/stopped state rule, compatible configuration and artifacts | + +Never add a device merely because the API exists. Each device expands behavior, failure surface, or host integration. diff --git a/skills/virtualization-framework-workflow/scripts/customization_config.py b/skills/virtualization-framework-workflow/scripts/customization_config.py new file mode 100755 index 00000000..0c2ec7f6 --- /dev/null +++ b/skills/virtualization-framework-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "virtualization-framework-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/tests/test_cybersecurity_skill_contracts.py b/tests/test_cybersecurity_skill_contracts.py index a7d43bf2..461aba33 100644 --- a/tests/test_cybersecurity_skill_contracts.py +++ b/tests/test_cybersecurity_skill_contracts.py @@ -44,6 +44,25 @@ def test_isolation_rejects_linux_container_for_macos_payload() -> None: ) +def test_prepared_lab_removes_ambient_authority_and_verifies_teardown() -> None: + assert_contract( + "prepare-isolated-analysis-lab", + "default host folders/home sharing, clipboard, drag/drop, sockets, ssh agent", + "narrow evidence path", + "run a preflight without executing the target", + "confirm no workload or integration remains active", + ) + + +def test_dynamic_analysis_requires_prepared_lab_and_virtualization_limits() -> None: + assert_contract( + "perform-dynamic-malware-analysis", + "preflighted by `prepare-isolated-analysis-lab`", + "require the prepared-lab record", + "virtualization artifacts or anti-vm behavior", + ) + + def test_authorized_testing_has_scope_and_stop_conditions() -> None: assert_contract( "scope-authorized-security-test", @@ -74,6 +93,21 @@ def test_macos_assessment_separates_platform_controls() -> None: ) +def test_macos_guest_evidence_retains_virtualization_limits() -> None: + assert_contract( + "assess-macos-threat", + "physical host, a macos guest, or a reproduction guest", + "secure enclave", + "anti-vm", + ) + assert_contract( + "inspect-macos-runtime-activity", + "physical-host, affected-host, or macos-guest evidence", + "virtualization artifacts", + "physical-mac proof", + ) + + def test_macos_recovery_preserves_evidence_and_verifies_outcome() -> None: assert_contract( "contain-and-recover-macos", diff --git a/tests/test_macos_virtualization_forward_scenarios.py b/tests/test_macos_virtualization_forward_scenarios.py new file mode 100644 index 00000000..90de71ab --- /dev/null +++ b/tests/test_macos_virtualization_forward_scenarios.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).resolve().parent.parent + + +def skill(plugin: str, name: str) -> str: + return (ROOT / "plugins" / plugin / "skills" / name / "SKILL.md").read_text(encoding="utf-8").lower() + + +@pytest.mark.parametrize( + ("plugin", "name", "phrases"), + [ + ( + "apple-dev-skills", + "choose-macos-virtualization-shape", + ("one portable linux application", "persistent oci-backed linux environment", "native macos security"), + ), + ( + "apple-dev-skills", + "virtualization-framework-workflow", + ("configuration construction", "headless", "add only required devices"), + ), + ( + "apple-dev-skills", + "macos-development-vm-workflow", + ("sip and relevant controls", "clean baseline", "restore-image support"), + ), + ( + "apple-dev-skills", + "virtualization-framework-workflow", + ("save/restore only in documented states", "configuration compatible", "not call saved machine state a disk snapshot"), + ), + ( + "server-side-swift", + "apple-containerization-workflow", + ("disposable application container", "persistent linux development environment", "home-mount=none"), + ), + ( + "server-side-swift", + "apple-containerization-workflow", + ("supported apple silicon", "compatible kernel configuration", "observed `/dev/kvm`"), + ), + ( + "cybersecurity-skills", + "prepare-isolated-analysis-lab", + ("offline static tooling", "default host folders/home sharing", "verify teardown"), + ), + ( + "cybersecurity-skills", + "prepare-isolated-analysis-lab", + ("monitored macos dynamic analysis", "baseline state or hashes", "virtualization artifacts"), + ), + ( + "apple-dev-skills", + "choose-macos-virtualization-shape", + ("do not call a linux container or linux vm evidence for native macos behavior", "gatekeeper", "tcc"), + ), + ( + "apple-dev-skills", + "choose-macos-virtualization-shape", + ("secure enclave", "recoveryos", "physical mac"), + ), + ], +) +def test_planned_forward_scenario_has_an_explicit_decision_path( + plugin: str, name: str, phrases: tuple[str, ...] +) -> None: + contents = skill(plugin, name) + missing = [phrase for phrase in phrases if phrase not in contents] + assert not missing, f"{plugin}:{name} is missing forward-test decisions: {missing}" diff --git a/tests/test_macos_virtualization_skill_contracts.py b/tests/test_macos_virtualization_skill_contracts.py new file mode 100644 index 00000000..a62ddec1 --- /dev/null +++ b/tests/test_macos_virtualization_skill_contracts.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def text(relative: str) -> str: + return (ROOT / relative).read_text(encoding="utf-8").lower() + + +def test_apple_container_1x_and_package_versions_are_separate() -> None: + contents = text("plugins/server-side-swift/skills/apple-containerization-workflow/SKILL.md") + for phrase in ( + "cli 1.x is the stable command surface", + "remains a 0.x swift package", + "do not preserve removed `container system property` commands", + "container machine workflow", + "`home-mount=none`", + "observed `/dev/kvm`", + ): + assert phrase in contents + + +def test_portability_export_names_every_virtualization_owner() -> None: + export = text("scripts/export_hermes_skills.py") + grouping = text("skills.sh.json") + for skill in ( + "choose-macos-virtualization-shape", + "virtualization-framework-workflow", + "linux-development-vm-workflow", + "macos-development-vm-workflow", + "prepare-isolated-analysis-lab", + "apple-containerization-workflow", + ): + assert skill in export + assert skill in grouping diff --git a/tests/test_validate_hermes_compatibility.py b/tests/test_validate_hermes_compatibility.py index 0b50c0ed..5d2c3965 100644 --- a/tests/test_validate_hermes_compatibility.py +++ b/tests/test_validate_hermes_compatibility.py @@ -73,6 +73,7 @@ def configure_paths(repo_root: Path, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(export_hermes_skills, "MESSAGING_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") monkeypatch.setattr(export_hermes_skills, "APPLE_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") monkeypatch.setattr(export_hermes_skills, "CYBERSECURITY_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") + monkeypatch.setattr(export_hermes_skills, "SERVER_SIDE_SWIFT_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") monkeypatch.setattr(export_hermes_skills, "REVERSE_ENGINEERING_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") monkeypatch.setattr(export_hermes_skills, "SWIFT_LANG_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") monkeypatch.setattr(export_hermes_skills, "MODEL_LAB_SOURCE_ROOT", repo_root / "plugins" / "agent-portability-skills" / "skills") diff --git a/uv.lock b/uv.lock index 24b79c37..6a38dba7 100644 --- a/uv.lock +++ b/uv.lock @@ -286,7 +286,7 @@ wheels = [ [[package]] name = "socket-maintenance" -version = "9.18.0" +version = "9.19.0" source = { virtual = "." } [package.dev-dependencies]