PrompTugal for iOS

Contracts: three apps

The fixed interfaces for the split: the package graph, what moves where, apps.json, bundle IDs, CI behaviour and who owns which files. Change this page only through the conductor.

1. Package graph

There are no cycles. An arrow means "depends on".

PTCore      → (nothing)
SGitVault   → (nothing)
PTWeb       → PTCore
PTAgenda    → PTCore, PTWeb
PTNews      → PTCore, PTWeb
PTVaultUI   → PTCore, SGitVault
PTAsk       → PTCore, PTAgenda, PTNews, PTVaultUI
PTLanes     → PTCore, PTVaultUI

Each package is a local Swift package: swift-tools-version 6.0, platform .iOS(.v18), one library product named after the package, and a Tests/<Name>Tests target. If a move shows that a package needs something above it, move that piece down (usually into PTCore), or inject it with a closure or protocol. Never add an upward edge. If neither works, write it in your status page and the conductor decides.

2. What moves where

From PrompTugal/ToOwner
Design/PTCoreplatform (phase 2)
App/HTTPClient.swift, the log types, Lisbon time, Content.swift shared constants, BuildInfoPTCoreplatform (phase 2)
Debug/: the log stores and the panel building blocksPTCoreplatform (phase 2)
Debug/: screens that show team or vault datastay in the Team appdesign
Bridge/, Shell/WebPage.swift, App/Web/ (Websites.json, WebsiteView)PTWebteam (WebPage and WebsiteView: design reviews)
Schedule/, Notifications/, Shared/NowNext.swiftPTAgendateam
Store/stays in the Team app (Reading credit is hidden in Release; Dinis has not decided)team
News/, the graph (Dashboards/Graph*)PTNewsdesign
AI/PTAskcollab
Lanes/PTLanescollab
Coordination/stays in the Team appcollab
Vault/PTVaultUIvault
Team/, Dashboards/ (except the graph), Shell/ (except WebPage)the Team app (Apps/Team/)design and team
App/ (the remaining parts)the Team appplatform

Tests move with their code into the package's Tests/ when they need no app host. Tests that need the app host stay in the Team app's test target.

3. Bundle IDs and records

AppBundleExtensionsApp GroupUpload
PrompTugalnews.promptugal.reader (existing record "PrompTugal.news").widgets, .intentsgroup.news.promptugal.readeryes
Teamnews.promptugal.team (new record, Dinis creates it)none tonightgroup.news.promptugal.team if neededyes, once the record exists
RiskMandateai.riskmandate.appnonenoneno (blocked on the publishing account)

Each app has its own MARKETING_VERSION. PrompTugal continues from 0.3.1, Team starts at 0.1.0 and RiskMandate at 0.0.1. CI sets build numbers per app: run number plus that app's offset, so PrompTugal stays above the manual build 6.

Each app has its own entitlements, Info settings and PrivacyInfo.xcprivacy, and asks only for what it uses. The PrompTugal app ships no debug panels in Release, no team screens and no lanes.

4. apps.json

{
  "packages": {
    "PTCore": {"path": "Packages/PTCore", "deps": []},
    "SGitVault": {"path": "Packages/SGitVault", "deps": []},
    "PTWeb": {"path": "Packages/PTWeb", "deps": ["PTCore"]}
  },
  "apps": {
    "team": {"project": "Apps/Team/Team.xcodeproj", "scheme": "Team", "bundle": "news.promptugal.team",
             "paths": ["Apps/Team/"], "packages": ["PTLanes", "PTAsk"], "testflight": true},
    "promptugal": {"project": "Apps/PrompTugal/PrompTugal.xcodeproj", "scheme": "PrompTugal", "bundle": "news.promptugal.reader",
                   "paths": ["Apps/PrompTugal/"], "packages": ["PTAsk", "PTAgenda"], "testflight": true},
    "riskmandate": {"project": "Apps/RiskMandate/RiskMandate.xcodeproj", "scheme": "RiskMandate", "bundle": "ai.riskmandate.app",
                    "paths": ["Apps/RiskMandate/"], "packages": ["PTCore"], "testflight": false}
  },
  "all": [".github/workflows/ios.yml", ".github/workflows/ios-release.yml", "apps.json", "scripts/release/", "Config/Shared/"]
}

Rules:

  • packages on an app lists the direct packages. Tools compute the closure through deps.
  • A changed file triggers an app when it is under one of the app's paths, or under the path of any package in its closure. A changed file under all triggers every app.
  • A test (scripts/apps/check_apps.py, run in CI on Linux) fails if:
  • a project's XCLocalSwiftPackageReference list differs from its app's closure;
  • a package's Package.swift dependencies differ from its deps;
  • the graph has a cycle.
  • scripts/apps/affected.py <base> <head> prints the affected apps as JSON, for the CI matrix.

5. CI

  • ios.yml: 1. A changes job on ubuntu runs affected.py. 2. A matrix over the affected apps builds Debug and Release and runs the unit tests on iPhone and iPad. 3. A matrix over the affected packages runs swift test / xcodebuild test for the packages.
  • ios-release.yml:
  • It uses the same changes job.
  • For each affected app with testflight: true and a green build, it packages, signs and uploads to that app's record.
  • Each app has its own variable to pause pushes: TESTFLIGHT_ON_PUSH_<APP>. The existing TESTFLIGHT_ON_PUSH=false still pauses all of them.
  • Tags team-v*, promptugal-v* and riskmandate-v* release one app.
  • Manual runs take an app input (all or one app).
  • screens.yml photographs the PrompTugal app and the Team app separately.
  • website.yml is unchanged: site and docs only.

6. Ownership tonight

RoleOwnsBranch
platformthe workspace, every .xcodeproj, Config/, apps.json, scripts/apps/, PTCore, Apps/*/App/ (entry points)split/platform
teamPTWeb, PTAgenda, Store/, Team/split/team
designPTNews, Shell/ and Dashboards/ in the Team app, the PrompTugal app's screens and Destinations, Debug/ screenssplit/design
collabPTAsk, PTLanes, Coordination/split/collab
vaultPTVaultUI, SGitVaultsplit/vault
release.github/workflows/ios*.yml, .github/workflows/screens.yml, scripts/release/split/release
editorwebsite/, briefs/ (except other sessions' status pages)split/editor

The conductor merges into dev once a branch is green and its phase is due.

7. Rulings during the night

These replace the matching rows above.

  • PTCore also gets:
  • from platform (phase 2): Event, Story, FeedClient (with site), RemoteFeed, LoadPhase, FeedCache, and enum Wire {} with its loose decoders;
  • from collab: one file, CollabSeams.swift, with protocols and value types only (Data, String, URL; no vault types). The conformance, VaultCollabAccess, lives in PTVaultUI;
  • from design (phase 4): the shared shell UI parts, in its own subfolder Sources/PTCore/Shell/.
  • PTAgenda also gets News/AgendaView.swift and App/ReaderStore.swift, moved by team. ReaderStore has the schedule injected; it no longer uses TeamFeatures.shared.
  • PTNews:
  • There is no Dashboards/Graph* file; the graph is already in News/.
  • PTNews defines a NewsSource protocol and NewsFeeds (newsletter, board, graph).
  • Each app adds extension ReaderStore: NewsSource {}.
  • DashboardData forwards to NewsFeeds.
  • The bridge works at JSON level. ScheduleDocument, ScheduleMerge and ScheduleID stay in PTAgenda.
  • Seams between packages use small protocols and registries, set by each app at launch:
  • in PTWeb: WebBridgeHub, BridgeServices;
  • in PTAgenda: AgendaTeamSource, AgendaIdentitySource, VaultPicksAccess, AgendaHub.

The public app supplies team sources that return nothing.

  • PTAsk takes an AskContextProvider. PublicAskProvider, in PTAsk, offers the agenda, picks, news and the current page. TeamAskProvider, in the Team app (Coordination/), keeps every current team context and tool.
  • App Intents, widgets and the intents extension go with the PrompTugal app in phase 4. The Team app has no extensions tonight.
  • URL schemes: PrompTugal uses promptugal://, Team uses ptteam://. Reminder links take the scheme from the app.
  • Background refresh: VaultBackground.register(taskIdentifier:) takes each app's BGTask id from its entry point.
  • Keychain after the split: vault keys saved by today's app (news.promptugal.reader) stay with the PrompTugal app. In the Team app (news.promptugal.team), teammates import their key again.
  • One version for every app (Dinis, 01:10 UTC):
  • The app version of all three apps equals the repo release version that CI tags for that commit: v0.4.n → 0.4.n. This replaces the per-app versions in §3.
  • The series jumps to v0.4.0, which is above the 0.3.1 already in App Store Connect.
  • iOS release builds the release commit, so the build and the tag match.
  • Build numbers still go up with every upload.
  • Runner load: Screens runs by hand only during the split.
  • RiskMandate publishes from the same Apple account (Dinis, 01:25 UTC). It uses bundle ai.riskmandate.app and gets testflight: true once it builds. Uploads skip cleanly until Dinis creates its App Store Connect record, as for Team.
  • Internal testers only during the summit (Dinis, 01:30 UTC). Every build goes to each app's internal TestFlight group, and internal testers get it as soon as Apple finishes processing. There are no external groups and no Beta App Review submissions, so the per-push version numbers cost nothing. External testing and App Review come after the summit.

8. Next round: app-first, three workstreams (conductor.B, 8 Nov, 10:20 UTC)

Dinis chose three parallel workstreams from the app-first brief. GitHub's standard macOS runners only.

WorkstreamOwnerWherePackage graph
PTSync: manifest delta sync of promptugal.news/api/v1/manifest.json, packs, SHA-256 check, atomic switch, build seed, offline outboxcollabPackages/PTSyncPTSync → PTCore only
Decks and presenter mode in the PrompTugal app (investor decks first, four languages, offline, external display)deck (new session)Packages/PTDeck, screens wired in Apps/PrompTugal/Shell/ through designPTDeck → PTCore only. Deck data comes through an injected loader, so PTDeck does not wait for PTSync.
Native stand-ups, command centre, reports and pulse in the Team appteamApps/Team/Sources/Team/uses existing packages only

Rules for this round:

  • New package entries: the owner of a new package adds it to apps.json (packages, and the app's packages list) in the same commit that creates the package. check_apps.py must pass. Platform reviews; nobody else edits another app's project.
  • No upward edges. A package that needs PTSync data takes a closure or protocol; the app wires PTSync in at launch.
  • CI: quick runs (quick=true) for fix rounds, one full run for READY, runner=macos-26. Cancel your own red or superseded runs at once.
  • Shell entries: new screens in the PrompTugal app are registered by design in Apps/PrompTugal/Shell/; ask design with the screen id and view.
  • Merge order: whichever is green first. The conductor merges the exact green commit, as before.