The M7 U1 workflow re-enabled CI, but manually re-signing *Runner.app with codesign --options runtime stripped the xctrunner entitlements that xcodebuild generates for the UI test runner. The hardened ad-hoc runner then rejected XCTest at dyld load time. - Remove the *Runner.app loop from .gitea/workflows/macos.yml. - Keep xattr -cr and codesign --options runtime on ICCery.app only, explicitly passing Resources/ICCery.entitlements. - Update M7-MEGAPLAN.md and M7-RISK-REGISTER.md to match. - Append PROP-M7-CI-RUNNER-SIGN-FIX to PROPOSED_CHANGES.md. Generated with Devin
15 KiB
15 KiB
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): #69fix/29-cal-basename-restore, #70fix/28-gamut-pause, #71docs/preuat-tester-kit. All aremergeable: trueagainstdevelop@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/HEADstill points tomain. docs/megaplans/onorigin/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 todevelopin 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:
developafter 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-testingandxcodebuild testwithCODE_SIGNING_ALLOWED=YES,CODE_SIGN_IDENTITY='-',CODE_SIGNING_REQUIRED=YESxattr -cr+codesign -f -s - --options runtime --entitlements Resources/ICCery.entitlementsonICCery.apponly; letxcodebuildsign*Runner.appwith its generatedxctrunnerentitlementspackagejob runs only onrefs/heads/developorrefs/tags/v
- Out:
CODE_SIGNING_ALLOWED=NO; changingproject.ymlCODE_SIGN_IDENTITY; enabling App Sandbox; usingruns-on: macos - PR: target
develop, labelsProject/ICCery-v2,Bug/CI,Priority/P0 - Verify: after merge, a
pushtodevelopshould trigger Gitea Actions and thebuild-and-testjob should start. Follow up with a human if the runner labelmacos-14is 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.shsecrets, 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/P0if missing. - Verify:
docs/PREUAT.mdexists ondevelopand 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.mdSlice 2):- Add
calibrationOriginalBasenametoWizardState/WizardViewModel, persisted with the same store. enterCalibration()setscalibrationOriginalBasename = basename.- Restore on cancel, successful
printcal, relaunch, and before anygo(to:)that is not.calibrate. - Refuse
.buildProfile/.verifyInstall/.generatewhilebasenamehas prefixCAL_. - Do not overload
profileBasename.
- Add
- Out:
CapturedBuffer/readabilityHandler rewrite; new calibration UI;PrinttargArgsguard changes. - Action: merge PR #69; add labels
Project/ICCery-v2,Bug/Calibration,Priority/P0if missing. - Verify:
WizardCalibrationSessionTestspasses; 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.mdSlice 3):GamutSceneView.dismantleNSViewsetsscnView.isPlaying = falseon sheet dismiss.GamutSceneGeometryBuilderdrops faces whose indices are>= vertices.countbefore buildingSCNGeometryElement.- 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/P1if missing. - Verify:
GamutGeometryBuilderTestspasses; gamut sheet opens/closes without GPU hang.
U4 — Repo cleanup (required)
- Branch:
chore/m7-repo-hygiene(new) - Base:
developafter U1–U3 have landed - In:
- README sync:
main(d37dced) has a full product README;develophas only# iccery-v2-mac. Hand-port the useful sections (product summary, build/test instructions, architecture, instruments, docs index, licence) intoREADME.mdondevelopin a singledocs/readme-synccommit. Do not mention stalemilestone/m6-gamut-stage0-cgats-releaseorDefault branch: main; useDefault branch: develop. - Default branch gate (human-owned): in the Gitea UI, set the repository default branch to
develop. Then delete or force-resetmaintodevelop(do not merge). Command:git push origin +develop:mainto reset, orgit push origin --delete mainif deleting. - Stale remote branches: delete
fix/process-wait-and-print-task(tip196c51dis an ancestor ofdevelopand the branch is stale). After the PRs above land, also deletefix/28-gamut-pause,fix/29-cal-basename-restore, anddocs/preuat-tester-kit. - Local stale branches: the user may optionally prune
[gone]localfeat/*andmilestone/*branches withgit remote prune origin.
- README sync:
- Out:
main→developmerge; committingdocs/megaplans/PREUAT-*.mdorM7-*.mdtodevelop(per D9); pushingfix/process-wait-and-print-taskor olde547585riders. - Verify:
git ls-remote --heads originshows onlydevelop(and release tags); Gitea default branch isdevelop;README.mdondevelopis 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
78b807dif VoiceOver testing is scheduled in week 1. - Out: full About rewrite.
U5b — killAll quit notice
- Branch:
fix/m7-killall-notice - Files:
Sources/ICCery/AppDelegate.swiftorWizardViewModel, 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
killAllsemantics; blocking the main thread.
U5c — UITest runningForeground wait
- Branch:
fix/m7-uitest-foreground - Files:
Tests/ICCeryUITests/...(≤4 files) - In: add a
runningForegroundwait 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:
# 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
$PATHArgyll search; noapplycal -u; noCAL_printtarg -K. - No merging
mainintodevelop. - No new M8 feature milestone inside this plan.
- No full
xcodebuild testsuite run during the planning step. - No
CapturedBufferrewrite ofrunCaptured. - No
project.ymldeployment target orCODE_SIGN_IDENTITYreal-cert changes.