diff --git a/.agents/learnings/promoted/2026-08-30-ios-extension-version-verification.md b/.agents/learnings/promoted/2026-08-30-ios-extension-version-verification.md new file mode 100644 index 0000000..d8497c0 --- /dev/null +++ b/.agents/learnings/promoted/2026-08-30-ios-extension-version-verification.md @@ -0,0 +1,39 @@ +--- +id: 2026-08-30-ios-extension-version-verification +date: 2026-08-30 +status: promoted +scope: iOS release artifact verification +evidence: build 45 Xcode archive warning plus direct PlistBuddy inspection of the exported IPA +destination: .codex/skills/release-mobile/SKILL.md +review_after: 2027-02-28 +--- + +## Symptom + +The signed iOS build logged that the widget extension build number 44 did not +match the containing app build number 45, even though export completed. + +## Reproduction + +Build iOS version 1.0.6 with EAS auto-increment from build 44 to 45 and retain the +archive log. Extract the resulting IPA with `ditto`, then read the containing app +and every packaged extension `Info.plist` with `PlistBuddy`. + +## Cause + +The archive warning described an intermediate state and was not authoritative +for the exported package. Direct IPA inspection showed both the app and +`LiveDrawWidget.appex` at version 1.0.6 build 45. + +## Remediation + +For every iOS release, inspect version and build values in the exported app and +all packaged extensions. Reject or rebuild only when the final IPA values differ; +do not accept or reject a release from archive warnings alone. + +## Evidence + +`PlistBuddy` read `CFBundleShortVersionString` 1.0.6 and `CFBundleVersion` 45 from +both packaged plists. `codesign --verify --deep --strict` passed for the app, and +the verified IPA SHA-256 was +`9d4779bddac4647911a3a38e4ee37cf794c2b8691e0746a041b629c62b308e7f`. diff --git a/.codex/skills/release-mobile/SKILL.md b/.codex/skills/release-mobile/SKILL.md index bb40cd8..ba7e23c 100644 --- a/.codex/skills/release-mobile/SKILL.md +++ b/.codex/skills/release-mobile/SKILL.md @@ -54,5 +54,9 @@ additional TestFlight distribution requires another fresh authorization. Preserve platform signing and local EAS versioning. After a build, verify that tracked changes are limited to the expected version metadata, then independently -read the artifact version and signature. Report the artifact, version/build -number, upload result when authorized, and store read-back state. +read the artifact version and signature. For an iOS IPA, inspect every packaged +app extension as well as the containing app and require matching +`CFBundleShortVersionString` and `CFBundleVersion` values. Treat Xcode archive +warnings as diagnostic evidence, not the final artifact state: decide whether to +reject or rebuild only after reading the exported IPA itself. Report the artifact, +version/build number, upload result when authorized, and store read-back state. diff --git a/AGENTS.md b/AGENTS.md index 8355dd9..507994e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,7 @@ MCP service in one npm/Turborepo workspace. mechanical one-line edit whose coordination cost exceeds the work. 11. Use high-effort reviewers only for the risk triggers in `docs/agents/routing.md`. +12. Agents must not use em dash characters in text they add to this repository. ## Repository map diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 49b2ebc..123ada7 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -24,7 +24,7 @@ "group.com.sauci.app" ] }, - "buildNumber": "44", + "buildNumber": "45", "usesAppleSignIn": true, "googleServicesFile": "./GoogleService-Info.plist", "infoPlist": { diff --git a/apps/mobile/ios/Sauci/Info.plist b/apps/mobile/ios/Sauci/Info.plist index 875de15..78a8d59 100644 --- a/apps/mobile/ios/Sauci/Info.plist +++ b/apps/mobile/ios/Sauci/Info.plist @@ -47,7 +47,7 @@ CFBundleVersion - 44 + 45 GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED ITSAppUsesNonExemptEncryption diff --git a/apps/mobile/targets/widget/Info.plist b/apps/mobile/targets/widget/Info.plist index c7c5fea..059cefe 100644 --- a/apps/mobile/targets/widget/Info.plist +++ b/apps/mobile/targets/widget/Info.plist @@ -8,7 +8,7 @@ com.apple.widgetkit-extension CFBundleVersion - 44 + 45 CFBundleShortVersionString 1.0.6 diff --git a/scripts/lint-harness.mjs b/scripts/lint-harness.mjs index 3611d07..ed8a046 100644 --- a/scripts/lint-harness.mjs +++ b/scripts/lint-harness.mjs @@ -40,6 +40,13 @@ for (const file of allFiles) { } if (!scoped) { + const agentsFile = path.join(root, 'AGENTS.md'); + const agents = await readFile(agentsFile, 'utf8'); + const noEmDashRule = 'Agents must not use em dash characters in text they add to this repository.'; + if (!agents.includes(noEmDashRule)) { + fail(agentsFile, 'agent writing rule against em dash characters is missing', `restore: ${noEmDashRule}`); + } + const packageJson = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8')); const codexConfigFile = path.join(root, '.codex', 'config.toml'); const codexConfig = await readFile(codexConfigFile, 'utf8');