一键导入
release-prepare
Prepare a new VivaDicta release — version bump, What's New screen, App Store metadata, code sweep, and pre-submission checklist
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Prepare a new VivaDicta release — version bump, What's New screen, App Store metadata, code sweep, and pre-submission checklist
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Run an ASO audit on canonical App Store metadata under `./metadata`, then a krankie-based competitor keyword-gap analysis (no Astro subscription). Krankie replacement for asc-aso-audit. Use after `asc metadata pull`.
Pull VivaDicta App Store keyword rankings via krankie (free, self-hosted), compare against the previous check, and write a dated rankings report to the Obsidian vault. Krankie-based replacement for asc-aso-rankings (no Astro subscription required).
Measure VivaDicta test code coverage from the terminal. Use when asked what the test coverage is, a module's or the whole-app coverage %, which lines/files are covered, to get coverage without opening Xcode, or to see coverage after writing tests. Covers both the fast per-SPM-module path (swift test + llvm-cov) and the canonical whole-app path (xcodebuild + xccov), and which first-party targets count.
Count Swift lines of code across the VivaDicta codebase and refresh the production-vs-test history chart. Use when asked how many LOC / lines of code the project has, for a codebase-size or LOC breakdown by SPM module / main app / extension, how big a module or extension is, the prod-vs-test ratio, or to update / regenerate the LOC growth chart embedded in documentation/Module-Architecture.md.
Writes, reviews, and improves Swift Testing code using modern APIs and best practices. Use when reading, writing, or reviewing projects that use Swift Testing.
Comprehensively reviews SwiftUI code for best practices on modern APIs, maintainability, and performance. Use when reading, writing, or reviewing SwiftUI projects.
| name | release-prepare |
| description | Prepare a new VivaDicta release — version bump, What's New screen, App Store metadata, code sweep, and pre-submission checklist |
Use this skill when preparing a new VivaDicta release for App Store submission.
Use the full X.Y.Z form in every user-facing surface — never the short X.Y form like "3.3":
headline field (e.g. "What's New in VivaDicta 3.3.0")"VivaDicta 3.3.0 adds...")<h2> already shows 3.3.0, but the prose must too)whats-new-X.Y.Z.md, description-X.Y.Z.md, linkedin-X.Y.Z-<slug>.mdThe only place short X.Y form survives is the internal WhatsNewCatalog.releases dictionary key, because the version-match logic is intentionally major.minor (so 3.3.0 / 3.3.1 / 3.3.2 all hit the same entry). That key is not user-visible.
asc-release-flow — drive the App Store Connect submission flowasc-whats-new-writer — generate App Store release notesasc-metadata-sync — sync and validate App Store metadataasc-localize-metadata — sync metadata across localizations (used for ASO, not actual translation)asc-aso-audit — run ASO audit on App Store metadata and surface keyword gapsgit checkout -b release/X.Y.Z
Update MARKETING_VERSION and CURRENT_PROJECT_VERSION in project.pbxproj across ALL 10 targets × 3 configs (Debug, QA, Release) = 30 entries each:
Fastest approach - global sed replace catches all 30 entries in one shot:
grep -cE "MARKETING_VERSION = {OLD}" VivaDicta.xcodeproj/project.pbxproj # should print 30
sed -i '' 's/MARKETING_VERSION = {OLD};/MARKETING_VERSION = {NEW};/g' VivaDicta.xcodeproj/project.pbxproj
sed -i '' 's/CURRENT_PROJECT_VERSION = {OLD_BUILD};/CURRENT_PROJECT_VERSION = {NEW_BUILD};/g' VivaDicta.xcodeproj/project.pbxproj
# verify
grep -E "MARKETING_VERSION|CURRENT_PROJECT_VERSION" VivaDicta.xcodeproj/project.pbxproj | sort -u # should show only 2 unique lines
Convention: CURRENT_PROJECT_VERSION is a monotonic counter, independent of MARKETING_VERSION. Bump it by +1 for every new build uploaded to TestFlight/App Store Connect, regardless of whether the marketing version changed. Apple only requires the build number to be strictly greater than any previously uploaded build for the same marketing version, so a plain incrementing integer is the simplest correct approach.
Before editing, check the current value:
grep -E "CURRENT_PROJECT_VERSION" VivaDicta.xcodeproj/project.pbxproj | head -1
Then use current + 1 as the new value across all targets.
Legacy note: earlier releases up through
3.0.0used a packedXYZNscheme (e.g.1.1.0 → 1101,3.0.0 → 3001), which is why the counter currently sits at a value like3001-ish. That scheme is abandoned because it caps each segment at 9. Going forward, just+1from the last build number - do NOT try to re-pack based on the marketing version.
Run a pre-release code sweep checking for:
print() statements that should use LoggerUse references/whats-new-screen.md for the full guide on adding What's New content to the in-app screen.
Write App Store release notes and save to Obsidian vault at:
Projects/VivaDicta/what's new/whats-new-X.Y.Z.md
Read the guide first: Projects/VivaDicta/description/description-guide.md - covers structure, writing rules, and common pitfalls for both What's New and description text.
Format: No frontmatter, no markdown header — plain text only, ready to copy-paste into App Store Connect.
IMPORTANT: App Store What's New (release notes) limit is 4,000 characters. Always verify the character count.
ASO strategy (important, easy to misread):
description + whatsNew → identical English text in all 10 locales (no translation)keywords → unique per locale (this is the ASO hack - different keyword sets target different search markets)marketingUrl + supportUrl → same across localesPrimary source - the running draft: Projects/VivaDicta/what's new/whats-new-running.md is a running accumulator of user-facing What's New items for the upcoming release. Add to it as features land between releases, then use it as the main source for both the in-app What's New (Step 4) and these App Store release notes.
Source features from:
Projects/VivaDicta/what's new/whats-new-running.mdProjects/VivaDicta/feature-changelog.mdhttps://vivadicta.com/ios/changelogAfter the release ships, empty whats-new-running.md - clear all items, leaving only the Running What's New (next release) header line. It tracks only the next upcoming release, so its items must be cleared once they have shipped in whats-new-X.Y.Z.md. (Also in the Step 10 checklist.)
Framing note (Apple Foundation Model): when a release adds on-device / local AI, only recommend Apple Foundation Model on Apple-owned surfaces - the App Store description and these release notes - so App Review sees we are not positioning against Apple's own model. On our own channels (in-app What's New, website changelog, LinkedIn), present the on-device feature directly, without recommending Apple FM. The "works even on devices without Apple Intelligence, including iOS 18" value angle is fine everywhere - it is a capability statement, not a recommendation.
Check if the current App Store description needs updating for new features.
Read the guide first: Projects/VivaDicta/description/description-guide.md - covers section ordering, core identity rules, jargon avoidance, and Apple Foundation Model placement.
Previous descriptions are stored at: Projects/VivaDicta/description/
Format: No frontmatter, no markdown header — plain text only, ready to copy-paste into App Store Connect.
IMPORTANT: App Store description limit is 4,000 characters. Always verify the character count before finalizing.
If updating, save the new version as description-X.Y.Z.md in the same directory.
Create metadata/version/X.Y.Z/*.json for all 10 locales, ready to push to App Store Connect via asc. The directory is gitignored - ASC is source of truth, this is just the staging payload.
Seed from the previous version, overwrite description and whatsNew with the new English text, keep per-locale keywords/marketingUrl/supportUrl untouched:
mkdir -p metadata/version/{NEW}
python3 << 'PYEOF'
import json
locales = ['ar-SA','en-US','es-MX','fr-FR','ko','pt-BR','ru','vi','zh-Hans','zh-Hant']
with open("/Users/antonnovoselov/Documents/Vault/Projects/VivaDicta/description/description-{NEW}.md") as f:
new_desc = f.read().rstrip('\n') + '\n'
with open("/Users/antonnovoselov/Documents/Vault/Projects/VivaDicta/what's new/whats-new-{NEW}.md") as f:
new_wn = f.read().rstrip('\n') + '\n'
for loc in locales:
with open(f'metadata/version/{PREV}/{loc}.json') as f:
d = json.load(f)
d['description'] = new_desc
d['whatsNew'] = new_wn
with open(f'metadata/version/{NEW}/{loc}.json', 'w') as f:
json.dump(d, f, indent=2, ensure_ascii=False)
PYEOF
Push happens during submission (see Step 10):
asc metadata push --app 6758147238 --version X.Y.Z --platform IOS --dir ./metadata
asc validate --app 6758147238 --version X.Y.Z --platform IOS # expect 0 errors / 0 blocking; one info-level App Privacy advisory is normal
Move shipped features from "Unreleased" to "Released" section in:
Projects/VivaDicta/feature-changelog.md (Obsidian vault)
Also add a new ### vX.Y.Z (YYYY-MM-DD) section with feature groupings and PR numbers (see earlier entries for format).
Add a new version section to the website's public iOS changelog at:
/Users/antonnovoselov/Desktop/_Projects/iOS/VivaDictaMeta/vivadicta_website/app/ios/changelog/page.tsx
Insert the new section above the previous version (the changelog shows newest first) and add an <hr className="my-12 border-border" /> separator between them. Pattern-match the existing entries:
<h2> with version string + <span> with absolute date<p> with group title + <ul> of 1-4 bullet pointsKeep copy aligned with the in-app What's New and App Store release notes so messaging stays consistent across surfaces. Commit and push in the website repo (separate from the iOS app repo).
Prepare a launch post for the VivaDicta company page on LinkedIn (not Anton's personal feed - the voice is announcement-style, not first-person story).
Save two files to /Users/antonnovoselov/Documents/Vault/Projects/VivaDicta/linkedin-posts/:
linkedin-X.Y.Z-<slug>.md - the working draft (all variants + posting notes below). Normal blank lines so it reads cleanly in Obsidian.linkedin-X.Y.Z-<slug>-copy.md - the paste-ready primary draft only, formatted for LinkedIn's composer (see "LinkedIn composer formatting" below). No headings, no frontmatter, no variants - just the exact text that goes into the composer.The working draft (.md) should include:
Style rules (also in ~/.claude/skills/linkedin-post-style/SKILL.md):
➡️ as the bullet marker (one per item, followed by a space), never -, *, or •. Numbered lists still use 1., 2., etc.https://vivadicta.com/ios/changelog/X.Y.Z, e.g. https://vivadicta.com/ios/changelog/3.5.0) - the per-release deep page, not the generic /ios/changelog list and not the App Store URL. Pin the App Store URL as the first comment instead - this gives commenters two routes (deep-context page in body, one-click install in comment).LinkedIn composer formatting (the -copy.md whitespace hack): LinkedIn's web composer collapses true blank lines when you paste plain text, destroying the paragraph spacing. So in the paste-ready -copy.md, every otherwise-blank line between paragraphs must contain a single Braille pattern blank character ⠀ (U+2800), not be empty. LinkedIn treats a line containing ⠀ as having content and preserves it as visible spacing, and the character renders invisibly to readers. Apply this only to -copy.md - leave the working .md draft with normal blank lines so it stays readable in Obsidian. (Same convention as the user-level linkedin-post-style skill, which is the source of truth for Anton's LinkedIn formatting.)
Publishing happens after the App Store build is approved and live, so users hitting the post can install immediately. The draft is prepared during release prep so it's ready to go when approval lands.
Update the AI-discoverability files in the website repo so they track the current feature set:
/Users/antonnovoselov/Desktop/_Projects/iOS/VivaDictaMeta/vivadicta_website/public/llms.txt (short summary)/Users/antonnovoselov/Desktop/_Projects/iOS/VivaDictaMeta/vivadicta_website/public/llms-full.txt (full doc)These are easy to forget - nothing auto-generates them, and the major chatbots do not auto-fetch /llms.txt (it's a voluntary convention; mainly agents/dev tools that deliberately fetch it benefit). Still, on each release re-sync them against this release's changes so anything that does read them sees current data:
AIProvider.swift defaultModel / availableModels and TranscriptionKit).## AI Enhancement Providers table, the provider count ("N+ AI providers"), and the one-line provider list in llms.txt (source: AIProvider.generalProviders). Add any provider shipped this release.Commit and push in the website repo (can be folded into the same commit as Step 8.5, since both touch that repo).
First, detect whether any SwiftData @Model classes changed since the last release tag:
git diff v{PREV}..HEAD -- 'VivaDicta/Models/*.swift' | grep -E "^\+.*(@Model|^\+\s+(var|let) )" | head -20
If new fields / relationships / models appear, deploy the CloudKit schema to Production before submitting the build:
iCloud.com.antonnovoselov.VivaDictaIf no SwiftData models changed, skip this step.
See Projects/VivaDicta/CloudKit Schema Deployment.md in the Obsidian vault for full details.
Known-answer ASC questions (don't re-ask these):
Before handing off to asc-release-flow:
whats-new-X.Y.Z.md)metadata/version/X.Y.Z/*.json generated for all 10 localesvivadicta_website/app/ios/changelog/page.tsx + push)llms.txt + llms-full.txt refreshed if providers/presets/pricing changed (vivadicta_website/public/ + push).md + paste-ready -copy.md (with ⠀ U+2800 blank lines and ➡️ bullets) in Projects/VivaDicta/linkedin-posts/ - publish after App Store approval landsasc metadata push + asc validate returns 0 errors / 0 blocking (one info-level App Privacy advisory is expected)whats-new-running.md (clear items, keep the Running What's New (next release) header) so it only tracks the next upcoming release