@@ -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.
| 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`.
- **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`)
- **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
- **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 |
| 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)
| 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. |
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.