diff --git a/.gitea/workflows/macos.yml b/.gitea/workflows/macos.yml index b415b54..cbb55fd 100644 --- a/.gitea/workflows/macos.yml +++ b/.gitea/workflows/macos.yml @@ -37,14 +37,10 @@ jobs: BUILT_PRODUCTS_DIR="$(xcodebuild -scheme ICCery -destination 'platform=macOS' -showBuildSettings | sed -n 's/^ *BUILT_PRODUCTS_DIR = //p' | head -n 1)" if [ -d "$BUILT_PRODUCTS_DIR/ICCery.app" ]; then xattr -cr "$BUILT_PRODUCTS_DIR/ICCery.app" - codesign -f -s - --options runtime "$BUILT_PRODUCTS_DIR/ICCery.app" + codesign -f -s - --options runtime \ + --entitlements Resources/ICCery.entitlements \ + "$BUILT_PRODUCTS_DIR/ICCery.app" fi - for runner in "$BUILT_PRODUCTS_DIR"/*Runner.app; do - if [ -d "$runner" ]; then - xattr -cr "$runner" - codesign -f -s - --options runtime "$runner" - fi - done xcodebuild test \ -scheme ICCery \ diff --git a/PROPOSED_CHANGES.md b/PROPOSED_CHANGES.md index 898bb16..56610b4 100644 --- a/PROPOSED_CHANGES.md +++ b/PROPOSED_CHANGES.md @@ -5,3 +5,11 @@ Intent: Produce the M6 milestone planning deliverables (M6-MEGAPLAN.md, M6-BRANC ## PROP-M6-MEGAPLAN-REV1 Status: PENDING Intent: Revise M6 megaplan per review (unstack #28, pre-split #29/#30, name CGATS open-panel test, sRGB.gam gate, Phase 0 spot-check). + +## PROP-M7-MEGAPLAN +Status: PENDING +Intent: Plan M7 UAT-ready hardening + repo cleanup. No product code in this step. #16 out. + +## PROP-M7-CI-RUNNER-SIGN-FIX +Status: PENDING +Intent: Correct the M7 U1 CI signing block so `*Runner.app` is not manually re-signed with `codesign --options runtime`; preserves the `xctrunner` entitlements and allows `ICCeryUITests` to boot. diff --git a/Packages/ICCeryCore/Sources/ICCeryCore/Wizard/WizardState.swift b/Packages/ICCeryCore/Sources/ICCeryCore/Wizard/WizardState.swift index ebdcf61..e219f5e 100644 --- a/Packages/ICCeryCore/Sources/ICCeryCore/Wizard/WizardState.swift +++ b/Packages/ICCeryCore/Sources/ICCeryCore/Wizard/WizardState.swift @@ -25,6 +25,25 @@ public struct WizardState: Codable, Equatable, Sendable { /// restore the original (#29). public var calibrationOriginalBasename: String = "" + private enum CodingKeys: String, CodingKey { + case currentStage, basename, cwd, printerName, sessionMode + case profileBasename, calibrationOriginalBasename + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.currentStage = try container.decodeIfPresent(Int.self, forKey: .currentStage) + ?? WizardStage.generate.rawValue + self.basename = try container.decodeIfPresent(String.self, forKey: .basename) ?? "" + self.cwd = try container.decodeIfPresent(String.self, forKey: .cwd) ?? "" + self.printerName = try container.decodeIfPresent(String.self, forKey: .printerName) + self.sessionMode = try container.decodeIfPresent(SessionMode.self, forKey: .sessionMode) + ?? .profile + self.profileBasename = try container.decodeIfPresent(String.self, forKey: .profileBasename) + self.calibrationOriginalBasename = try container.decodeIfPresent( + String.self, forKey: .calibrationOriginalBasename) ?? "" + } + public init( currentStage: Int = WizardStage.generate.rawValue, basename: String = "", diff --git a/Sources/ICCery/AboutView.swift b/Sources/ICCery/AboutView.swift index 6396f8f..97b9377 100644 --- a/Sources/ICCery/AboutView.swift +++ b/Sources/ICCery/AboutView.swift @@ -26,12 +26,14 @@ struct AboutView: View { .foregroundStyle(Theme.text) .accessibilityIdentifier("aboutVersion") } + .accessibilityElement(children: .contain) HStack { Text("Build:") .foregroundStyle(.secondary) Text(info.build) .foregroundStyle(Theme.text) } + .accessibilityElement(children: .contain) HStack { Text("Build date:") .foregroundStyle(.secondary) @@ -39,8 +41,10 @@ struct AboutView: View { .foregroundStyle(Theme.text) .accessibilityIdentifier("aboutBuildDate") } + .accessibilityElement(children: .contain) } .font(.callout) + .accessibilityElement(children: .contain) Text("Native macOS printer profiling workstation.") .font(.caption) diff --git a/Sources/ICCery/Stage2View.swift b/Sources/ICCery/Stage2View.swift index 7a6d23d..5f79f2b 100644 --- a/Sources/ICCery/Stage2View.swift +++ b/Sources/ICCery/Stage2View.swift @@ -234,6 +234,7 @@ struct Stage2View: View { } Spacer() } + .accessibilityElement(children: .contain) // Printer row: select + status + refresh + Preferences. HStack(spacing: 10) { diff --git a/Tests/ICCeryUITests/AboutHelpUITests.swift b/Tests/ICCeryUITests/AboutHelpUITests.swift index c6c2b03..86d7a35 100644 --- a/Tests/ICCeryUITests/AboutHelpUITests.swift +++ b/Tests/ICCeryUITests/AboutHelpUITests.swift @@ -18,7 +18,9 @@ final class AboutHelpUITests: XCTestCase { } private func element(_ id: String) -> XCUIElement { - app.descendants(matching: .any)[id] + let inApp = app.descendants(matching: .any)[id].firstMatch + if inApp.exists { return inApp } + return app.sheets.firstMatch.descendants(matching: .any)[id].firstMatch } private func waitFor(_ id: String, timeout: TimeInterval = 10) -> XCUIElement { @@ -41,8 +43,7 @@ final class AboutHelpUITests: XCTestCase { XCTAssertTrue(openAbout.waitForExistence(timeout: 10)) openAbout.click() - _ = waitFor("aboutDialog", timeout: 10) - XCTAssertTrue(element("aboutVersion").exists) + _ = waitFor("aboutVersion", timeout: 10) XCTAssertTrue(element("aboutBuildDate").exists) let close = app.buttons["closeAboutBtn"] diff --git a/Tests/ICCeryUITests/Milestone5UITests.swift b/Tests/ICCeryUITests/Milestone5UITests.swift index bea974c..3c6929b 100644 --- a/Tests/ICCeryUITests/Milestone5UITests.swift +++ b/Tests/ICCeryUITests/Milestone5UITests.swift @@ -37,7 +37,9 @@ final class Milestone5UITests: XCTestCase { "basename": "mytarget", "cwd": workDir.path, "printerName": "MockPrinter", - "sessionMode": "profile" + "sessionMode": "profile", + "profileBasename": "mytarget", + "calibrationOriginalBasename": "" ] let stateData = try JSONSerialization.data(withJSONObject: state, options: []) try stateData.write(to: appDataDir.appendingPathComponent("wizard_state.json")) diff --git a/Tests/ICCeryUITests/Milestone6GamutUITests.swift b/Tests/ICCeryUITests/Milestone6GamutUITests.swift index 8c8068d..df67c68 100644 --- a/Tests/ICCeryUITests/Milestone6GamutUITests.swift +++ b/Tests/ICCeryUITests/Milestone6GamutUITests.swift @@ -46,7 +46,9 @@ final class Milestone6GamutUITests: XCTestCase { "basename": "mytarget", "cwd": workDir.path, "printerName": "MockPrinter", - "sessionMode": "profile" + "sessionMode": "profile", + "profileBasename": "mytarget", + "calibrationOriginalBasename": "" ] let stateData = try JSONSerialization.data(withJSONObject: state, options: []) try stateData.write(to: appDataDir.appendingPathComponent("wizard_state.json")) diff --git a/docs/megaplans/M7-MEGAPLAN.md b/docs/megaplans/M7-MEGAPLAN.md new file mode 100644 index 0000000..36ae603 --- /dev/null +++ b/docs/megaplans/M7-MEGAPLAN.md @@ -0,0 +1,207 @@ +# M7 megaplan — UAT-ready + +## 0. Baseline SHA + Phase 0 shipped/missing table + +**Baseline:** `origin/develop` at `c5fc89ec409c941c0bf619a851c12c8384afa204` (post-PR #66 M6 merge; workflows temporarily disabled by `c5fc89e`). + +### Phase 0 shipped/missing audit + +| Ref | Pre-UAT item | Status on `origin/develop` | Evidence | +|-----|--------------|----------------------------|----------| +| S0 | `ProcessManager` `waitUntilExit()` after `process.run()` in `runStreaming` / `runCaptured`; `TargetWorkflowViewModel.printTask` strong reference. | **SHIPPED** | Commit `1d9c6ae` (`fix: harden process termination and retain print task`), merged by `18767c2` / `bc5c038`. `ProcessManager.swift:185-189` and `288-291` contain the `Task.detached { process.waitUntilExit() … }` fallbacks. `TargetWorkflowViewModel.swift:114` declares `private var printTask` and assigns it in `printAllPages` / `printPage`. | +| S2 | Persisted `calibrationOriginalBasename`; refuse `.buildProfile` while basename has `CAL_` prefix. | **MISSING** | `CalibrationViewModel.swift:30` uses an in-memory `private var originalBasename`. `WizardViewModel.go(to:)` does not refuse `.buildProfile` / `.verifyInstall` / `.generate` for `CAL_` basenames. Open PR #69 (`fix/29-cal-basename-restore`, head `b527207`) contains the fix. | +| S3 | `GamutSceneView.dismantleNSView` `isPlaying = false`; OOB face guard. | **MISSING** | `GamutView.swift` has no `dismantleNSView` and no OOB face-index guard in `scnGeometry`. Open PR #70 (`fix/28-gamut-pause`, head `87e4f4f`) contains the fix. | +| S1/4 | `docs/PREUAT.md` tester kit. | **MISSING** | `git ls-tree -r origin/develop` has no `docs/PREUAT.md`. Open PR #71 (`docs/preuat-tester-kit`, head `f47b642`) adds it. | +| CI | `.gitea/workflows/macos.yml` uses `CODE_SIGNING_ALLOWED=YES` and `CODE_SIGN_IDENTITY='-'` on `xcodebuild test`; `pull_request`/`push` includes `develop`. | **CONTENT CORRECT, PATH DISABLED** | `.gitea/workflows.disabled/macos.yml` on `develop@c5fc89e` already contains `push`/`pull_request: [develop]`, `runs-on: macos-14`, `CODE_SIGNING_ALLOWED=YES`, `CODE_SIGN_IDENTITY='-'`, `CODE_SIGNING_REQUIRED=YES`, and the `xattr -cr` + `codesign --options runtime` post-build block. The file is under `.gitea/workflows.disabled/`, so Gitea will not run it. | + +### Other Phase 0 findings + +- **Open PRs into `develop` (Gitea):** #69 `fix/29-cal-basename-restore`, #70 `fix/28-gamut-pause`, #71 `docs/preuat-tester-kit`. All are `mergeable: true` against `develop@c5fc89e`. +- **Remote branches (`git ls-remote --heads origin`):** `develop`, `docs/preuat-tester-kit`, `fix/28-gamut-pause`, `fix/29-cal-basename-restore`, `fix/process-wait-and-print-task`, `main`. +- **Default branch:** `origin/HEAD` still points to `main`. +- **`docs/megaplans/` on `origin/develop`:** `M6-BRANCH-MAP.md`, `M6-MEGAPLAN.md`, `M6-RISK-REGISTER.md`. +- **Local-only planning docs:** `PREUAT-MEGAPLAN.md`, `PREUAT-BRANCH-MAP.md`, `PREUAT-RISK-REGISTER.md`, `M7_MEGAPLAN_PROMPT.md`. Per clarifying answer, these are **not** committed to `develop` in U4. + +## 1. Decision log + +| ID | Decision | Why | +|----|----------|-----| +| D1 | **#16 / `ICCeryPrintKit` / TargetPrint is v2.1**, not M7. | M7 is UAT-ready hardening of existing v2.0 paths; Quartz/AirPrint/ColorSync print dictionaries are a later milestone. | +| D2 | Keep **macOS 14.0 + Swift 6**; do not retarget to Monterey 12 / Xcode 14.2. | `project.yml` is already at `MACOSX_DEPLOYMENT_TARGET: "14.0"` and `SWIFT_VERSION: "6.0"`. | +| D3 | **App Sandbox OFF; Hardened Runtime ON; Debug `CODE_SIGN_IDENTITY` stays `"-"`**. | `project.yml` and `ICCery.entitlements` already reflect this; no `project.yml` change for a real Developer ID in Debug. | +| D4 | **Do not merge `main` (`d37dced`) into `develop`**. | `main` is stale and has a full README not on `develop`; useful text is hand-synced in a tiny `docs/readme-sync` commit. | +| D5 | **Re-use the three open Pre-UAT PRs (#69, #70, #71)** as M7 U2/U3 slices. | They already exist, target `develop`, and implement the exact `PREUAT-MEGAPLAN.md` contracts. | +| D6 | **Do not rebase `fix/29-cal-basename-restore` to drop its workflow-disabled rename; keep the workflow disabled in that branch.** | The rename results in the same `.gitea/workflows.disabled/macos.yml` path as `develop`, so PR #69 remains mergeable. U1 re-enables CI afterwards. | +| D7 | **U1 re-enables CI by moving the existing `.gitea/workflows.disabled/macos.yml` to `.gitea/workflows/macos.yml`, keeping `runs-on: macos-14` and the ad-hoc sign block.** | The disabled file content already matches the contract; the user confirmed `macos-14` should be kept. Do not use the local `fix/process-wait-and-print-task@3f09e69` `runs-on: macos` variant. | +| D8 | **U5 small UAT polish is split into three separate optional PRs**, each landed only if cheap and only after U1–U3 are green. | Per clarifying answer; each item must stay under ~4 files and is not bundled with process/calibration branches. | +| D9 | **Do not add `docs/megaplans/PREUAT-*.md` or the new `M7-*.md` files to `develop` in U4.** | Per clarifying answer; planning docs remain local planning deliverables. U4 still handles README sync and stale branch deletion. | +| D10 | **No `milestone/m7-*` integration branch; all PRs target `develop` directly.** | M7 is a small set of parallel fixes/docs; an umbrella branch adds unnecessary rebasing. | +| D11 | **Set Gitea default branch to `develop` and reset/delete `main`** as a human-owned U4 gate. | `origin/HEAD` is still `main`; `main` is stale and has unique README text that must be hand-synced first. | + +## 2. Slices U1–U6 + +### U1 — CI re-enable (P0) + +- **Branch:** `fix/32-ci-adhoc-sign` (new) +- **Base:** `develop` **after** U3b/#69 lands (avoids a workflow rename/rename conflict; see D6) +- **Files:** `.gitea/workflows/macos.yml` (moved from `.gitea/workflows.disabled/macos.yml`) +- **In:** + - `git mv .gitea/workflows.disabled/macos.yml .gitea/workflows/macos.yml` + - Keep existing content: `push`/`pull_request: [develop]`, `runs-on: macos-14` + - `xcodebuild build-for-testing` and `xcodebuild test` with `CODE_SIGNING_ALLOWED=YES`, `CODE_SIGN_IDENTITY='-'`, `CODE_SIGNING_REQUIRED=YES` + - `xattr -cr` + `codesign -f -s - --options runtime --entitlements Resources/ICCery.entitlements` on `ICCery.app` **only**; let `xcodebuild` sign `*Runner.app` with its generated `xctrunner` entitlements + - `package` job runs only on `refs/heads/develop` or `refs/tags/v` +- **Out:** `CODE_SIGNING_ALLOWED=NO`; changing `project.yml` `CODE_SIGN_IDENTITY`; enabling App Sandbox; using `runs-on: macos` +- **PR:** target `develop`, labels `Project/ICCery-v2`, `Bug/CI`, `Priority/P0` +- **Verify:** after merge, a `push` to `develop` should trigger Gitea Actions and the `build-and-test` job should start. Follow up with a human if the runner label `macos-14` is not registered. + +### U2 — Tester kit (P0) + +- **Branch:** `docs/preuat-tester-kit` (already open as PR #71) +- **File:** `docs/PREUAT.md` +- **In:** existing PR body is correct; covers build instructions, paths A–H, #24 policy, `CAL_` vs profile name, Cancel vs Force Quit, Gatekeeper, `package-release.sh` secrets, CI ad-hoc sign note. +- **Out:** product code, `project.yml`, README, `BUILD-PLAN.md`, CI workflow edits +- **Action:** review/merge PR #71; add labels `Project/ICCery-v2`, `Feature/Docs`, `Priority/P0` if missing. +- **Verify:** `docs/PREUAT.md` exists on `develop` and paths A–H match the prompt wording. + +### U3 — Pre-UAT code leftovers + +#### U3a — Process wait / print task (already shipped) + +No M7 branch. Verified in Phase 0. + +#### U3b — `CAL_` basename restore (P0) + +- **Branch:** `fix/29-cal-basename-restore` (already open as PR #69) +- **Base:** current `develop` (mergeable per Gitea) +- **Files:** `Packages/ICCeryCore/Sources/ICCeryCore/Wizard/WizardState.swift`, `Sources/ICCery/CalibrationViewModel.swift`, `Sources/ICCery/WizardViewModel.swift`, `Tests/ICCeryCoreTests/WizardCalibrationSessionTests.swift`, `Tests/ICCeryCoreTests/WizardGatingTests.swift` +- **In (per `PREUAT-MEGAPLAN.md` Slice 2):** + - Add `calibrationOriginalBasename` to `WizardState` / `WizardViewModel`, persisted with the same store. + - `enterCalibration()` sets `calibrationOriginalBasename = basename`. + - Restore on cancel, successful `printcal`, relaunch, and before any `go(to:)` that is not `.calibrate`. + - Refuse `.buildProfile` / `.verifyInstall` / `.generate` while `basename` has prefix `CAL_`. + - Do not overload `profileBasename`. +- **Out:** `CapturedBuffer`/readabilityHandler rewrite; new calibration UI; `PrinttargArgs` guard changes. +- **Action:** merge PR #69; add labels `Project/ICCery-v2`, `Bug/Calibration`, `Priority/P0` if missing. +- **Verify:** `WizardCalibrationSessionTests` passes; the diff does not re-enable the workflow (remains `.gitea/workflows.disabled`). + +#### U3c — Gamut pause (P1) + +- **Branch:** `fix/28-gamut-pause` (already open as PR #70) +- **Base:** current `develop` (mergeable per Gitea) +- **Files:** `Sources/ICCery/GamutView.swift`, `Tests/ICCeryCoreTests/GamutGeometryBuilderTests.swift` +- **In (per `PREUAT-MEGAPLAN.md` Slice 3):** + - `GamutSceneView.dismantleNSView` sets `scnView.isPlaying = false` on sheet dismiss. + - `GamutSceneGeometryBuilder` drops faces whose indices are `>= vertices.count` before building `SCNGeometryElement`. + - Edge-line construction filters OOB faces. +- **Out:** Scene cache, camera rewrite, Metal/WebGL. +- **Action:** merge PR #70; add labels `Project/ICCery-v2`, `Bug/Viewer`, `Priority/P1` if missing. +- **Verify:** `GamutGeometryBuilderTests` passes; gamut sheet opens/closes without GPU hang. + +### U4 — Repo cleanup (required) + +- **Branch:** `chore/m7-repo-hygiene` (new) +- **Base:** `develop` after U1–U3 have landed +- **In:** + - **README sync:** `main` (`d37dced`) has a full product README; `develop` has only `# iccery-v2-mac`. Hand-port the useful sections (product summary, build/test instructions, architecture, instruments, docs index, licence) into `README.md` on `develop` in a single `docs/readme-sync` commit. Do **not** mention stale `milestone/m6-gamut-stage0-cgats-release` or `Default branch: main`; use `Default branch: develop`. + - **Default branch gate (human-owned):** in the Gitea UI, set the repository default branch to `develop`. Then delete or force-reset `main` to `develop` (do not merge). Command: `git push origin +develop:main` to reset, or `git push origin --delete main` if deleting. + - **Stale remote branches:** delete `fix/process-wait-and-print-task` (tip `196c51d` is an ancestor of `develop` and the branch is stale). After the PRs above land, also delete `fix/28-gamut-pause`, `fix/29-cal-basename-restore`, and `docs/preuat-tester-kit`. + - **Local stale branches:** the user may optionally prune `[gone]` local `feat/*` and `milestone/*` branches with `git remote prune origin`. +- **Out:** `main` → `develop` merge; committing `docs/megaplans/PREUAT-*.md` or `M7-*.md` to `develop` (per D9); pushing `fix/process-wait-and-print-task` or old `e547585` riders. +- **Verify:** `git ls-remote --heads origin` shows only `develop` (and release tags); Gitea default branch is `develop`; `README.md` on `develop` is useful. + +### U5 — Small UAT polish (optional, split per user choice) + +Only if cheap; each branch ≤4 files. Do **not** put on process or calibration branches. Land last. + +#### U5a — About accessibility identifiers + +- **Branch:** `fix/m7-about-a11y` +- **Files:** `Sources/ICCery/AboutView.swift` (and any related test) +- **In:** restore accessibility identifiers dropped with `78b807d` if VoiceOver testing is scheduled in week 1. +- **Out:** full About rewrite. + +#### U5b — `killAll` quit notice + +- **Branch:** `fix/m7-killall-notice` +- **Files:** `Sources/ICCery/AppDelegate.swift` or `WizardViewModel`, small UI +- **In:** show an in-app notice "stopping instruments…" while `ProcessManager.killAll()` runs on app quit so testers do not Force Quit. +- **Out:** changing `killAll` semantics; blocking the main thread. + +#### U5c — UITest `runningForeground` wait + +- **Branch:** `fix/m7-uitest-foreground` +- **Files:** `Tests/ICCeryUITests/...` (≤4 files) +- **In:** add a `runningForeground` wait helper to the UI test launch path. +- **Out:** bundling with U1; large test refactor. +- **Gate:** only if CI still flakes after U1. + +### U6 — Hardware / release gates (checklist, not code) + +Human-owned gate table before UAT: + +| # | Gate | Owner | +|---|------|-------| +| 1 | Unlocked Sonoma Mac; instrument plugged in before launch | Tester | +| 2 | Bundled `instlist` / `chartread` pass `codesign -dvv` (not `$PATH`) | CI / packager | +| 3 | Paths A–E green before F–H | Tester | +| 4 | Notarized DMG `spctl --assess` before any "clean Mac" tester | Release engineer | +| 5 | One real printer Stage 0 + one real profile `.gam` in SceneKit | Tester | + +## 3. Tester paths A–H mapped to slices + +| Path | Week | Action | Depends on | Pass criteria | +|------|------|--------|------------|---------------| +| A | 1 | Stage 1 generate → Stage 2 print one page → cancel | S0 (shipped), U1 | Print panel appears, app returns cleanly, `printTask` retained, CI green | +| B | 1 | Handheld chartread one sheet → Finish & Average | S0 (shipped) | `.ti3` produced | +| C | 1 | Create profile, Apply Calibration off | #24 policy (shipped) | `iccgamut` fail/info banner, still advances to Stage 5 | +| D | 1 | Create profile, Apply Calibration on, missing/bad `.cal` | #24 / `applycal` guard (shipped) | Does not advance to Stage 5 | +| E | 1 | Stage 5 profcheck + user vs system install / overwrite | develop (M5) | `profcheck` completes, profile installed in chosen scope, overwrite works | +| F | 2 | Stage 0 full loop; restore non-`CAL_` basename; `-K` only on profile `printtarg` | U3b (#69) | `CAL_` never bleeds into `colprof`; `CAL_*.ti3` not fed to `colprof` | +| G | 2 | Import Dataset open panel (`.ti3`/`.txt`/`.cgats`/`.csv`), not save-`.ti1` | #30 (shipped) | Imported `.ti3`/dataset accepted; wizard skips print/measure | +| H | 2 | View Gamut + sRGB overlay + reset camera | U3c (#70) | No GPU hang, camera resets, app responsive | + +## 4. Cleanup command list + +Commands to run after all M7 PRs are merged: + +```bash +# 1. Confirm current remote heads +git ls-remote --heads origin + +# 2. Delete the stale ancestor branch (do not push any new code from it) +git push origin --delete fix/process-wait-and-print-task + +# 3. After PR #69, #70, #71 are merged, delete their remote branches +git push origin --delete fix/29-cal-basename-restore +git push origin --delete fix/28-gamut-pause +git push origin --delete docs/preuat-tester-kit + +# 4. Human: in Gitea UI, set the repository default branch to `develop`. + +# 5. Reset `main` to `develop` (or delete it) — do NOT merge +git fetch origin +git push origin +develop:main # reset main to current develop +# OR +git push origin --delete main # delete main entirely + +# 6. Prune local [gone] tracking branches +git remote prune origin + +# 7. Verify +git ls-remote --heads origin +git remote show origin | grep 'HEAD branch' +``` + +**Keep `develop`.** Never push `fix/process-wait-and-print-task` or old `e547585` riders. + +## 5. Non-goals + +- No #16 / `ICCeryPrintKit` / `TargetPrint` / Quartz / AirPrint detector in M7. +- No macOS 12 / Xcode 14.2 retarget; keep macOS 14 + Swift 6. +- No App Sandbox ON; no `$PATH` Argyll search; no `applycal -u`; no `CAL_` `printtarg -K`. +- No merging `main` into `develop`. +- No new M8 feature milestone inside this plan. +- No full `xcodebuild test` suite run during the planning step. +- No `CapturedBuffer` rewrite of `runCaptured`. +- No `project.yml` deployment target or `CODE_SIGN_IDENTITY` real-cert changes. diff --git a/docs/megaplans/M7-RISK-REGISTER.md b/docs/megaplans/M7-RISK-REGISTER.md new file mode 100644 index 0000000..23411cc --- /dev/null +++ b/docs/megaplans/M7-RISK-REGISTER.md @@ -0,0 +1,14 @@ +# M7 risk register + +| ID | Risk | Likelihood | Impact | Mitigation | +|----|------|------------|--------|------------| +| R1 | **CI UI-test launch kill (`dyld` / `XCTest` "different Team IDs")** because `*Runner.app` is manually re-signed with `codesign --options runtime`, stripping the generated `xctrunner` entitlements. | High | Ship-stopper | `fix/m7-ci-uitest-runner-sign`: keep `CODE_SIGNING_ALLOWED=YES`, `CODE_SIGN_IDENTITY='-'`, `CODE_SIGNING_REQUIRED=YES`, but `xattr -cr` + `codesign --options runtime` on `ICCery.app` only (with `--entitlements Resources/ICCery.entitlements`); let `xcodebuild` sign `*Runner.app` with its generated `xctrunner` entitlements. | +| R2 | **Gatekeeper `Killed: 9`** when a tester runs an unsigned CI artefact or non-notarized DMG on a clean Mac. | High | Ship-stopper (cannot test) | U2 `docs/PREUAT.md` states only Debug-from-repo or a notarized DMG is a valid test build; U6 gate requires `spctl --assess` before "clean Mac" testers. | +| R3 | **`CAL_` basename bleeds into `colprof`** because `originalBasename` is not persisted and `go(to:)` allows `.buildProfile` while the live name is `CAL_*`. | High (still missing on `develop`) | Ship-stopper (wrong profile/calibration mix) | U3b `fix/29-cal-basename-restore` (#69): persist `calibrationOriginalBasename`, restore on cancel/success/relaunch, and refuse `.buildProfile` / `.verifyInstall` / `.generate` for `CAL_` basenames. | +| R4 | **SceneKit GPU/context issues or `mesh.vertices[…]` crash** on malformed `.gam` data because `GamutSceneView` has no pause and `scnGeometry` does not guard face indices. | Medium | P1 (week-2 viewer risk) | U3c `fix/28-gamut-pause` (#70): `SCNView.isPlaying = false` on dismiss; drop OOB faces before building the index/edge buffers. | +| R5 | **Stale `main` default branch** causes contributors to open PRs against the wrong base and the README on `develop` is empty. | High | Schedule / confusion | U4 `chore/m7-repo-hygiene`: hand-sync useful README text from `main` to `develop`, then set Gitea default to `develop` and reset/delete `main`. | +| R6 | **#16 scope creep**: Quartz / `ICCeryPrintKit` / TargetPrint work is mistaken for M7. | Medium | Schedule | Decision log D1; non-goals list; one-concern-per-branch rule. | +| R7 | **Force Quit orphans**: child processes or GPU context survive termination because a tester Force Quits while `killAll` is still running. | Low | P1 | S0 already hardened `ProcessManager` with `waitUntilExit()` fallbacks and a 2-second `forceKill`/`forceFinalize` watchdog; U5b adds a quit notice to reduce the window. | +| R8 | **U5 polish blocks M7 exit**: optional a11y / killAll / UI-test branches grow beyond ~4 files or get bundled with P0 work. | Low | Schedule | U5 is explicitly optional, split into three tiny branches, and landed last only if cheap. | +| R9 | **Merge order conflict**: U1 re-enables the workflow before #69 lands, causing a rename/rename conflict when #69 is merged. | Medium | CI blocked | Branch map enforces #69 before U1; #69 keeps `.gitea/workflows.disabled/macos.yml`, so U1 simply moves the file afterwards. | +| R10 | **Planning docs accidentally committed to `develop`**: `docs/megaplans/PREUAT-*.md` or `M7-*.md` are bundled into a product PR. | Low | Repo noise | Decision log D9; U4 explicitly excludes planning docs. |