Status · Team features
Done and on dev, with unit tests green on iPhone and iPad: one schedule on the website's format, synced with the web view and the vault; reminders before picks and stand-ups; team data and identity; the day planner and "next 3 hours"; the meter with a native StoreKit top-up. The bridge is checked against the live website.
Branch: night/team (merges origin/dev, never rebased) · Last update: 09:25 UTC (final)
Done
- One schedule (
Schedule/):ScheduleStoreonnr.schedule.v1, kept on the device (Application Support/PrompTugal/schedule.json). Every old Saved session (saved.json) is imported with its offline copy; saved.json stays as a backup. - Bridge (
Bridge/):AppBridge. Handlerptin its ownWKContentWorld, main frame only, https only, allow-listpromptugal.newsandweb-summit.sgit.ai.window.ptApp = { version, ops, call }. Opsschedule.get,schedule.set,identity.get,notify.plan,meter.topup. Two-way schedule sync with promptugal.news throughlocalStorage['nr.schedule.v1']and the page's ownnr:scheduleevent, with no website change.BridgeLogandBridgeLogViewfor Debug.SiteDataStoresgives each site its ownWKWebsiteDataStore. - Vault (
ScheduleVaultLink): when a reader vault opens, picks are read fromapps/agenda/picks.json(loadPicks) and merged; no Face ID. Saving (savePicks) is a button in My day, because it asks for Face ID and needs the vault key and an access token. - Reminders (
Notifications/): before each pick and each stand-up, default 10 minutes (choices 0, 5, 10, 15, 30), Lisbon time, at most 60 pending (iOS allows 64). - Team data (
Team/): models and offline cache forme.json, the command centredata.json, stand-ups, wakes, reports, pulse and log. "Who are you?" identity, and the app icon badge (only you + decide + overdue). - Planner: a proposed day from the agenda, my picks and the stand-ups, with clashes (overlap, or too little time to walk) and gaps. Sessions the team picked for me rank first, then sessions teammates have. Accept → picks. "Where is everyone" lists teammates' sessions per day.
- iPhone and iPad: every screen is a list on iPhone. On iPad, My day and the planner add the day as a time line beside the list.
- Meter and native top-up (
Store/):MeterStoreshows the website's balance;CreditStoresells the two consumables with StoreKit 2 and credits the page's meter through the bridge, once per transaction. The Stripe top-up is hidden in the app and replaced by "Top up in the app". - Backlog done: Add to Calendar (write-only EventKit, only when asked); App Intents "Save a session to my day" and "What's next in my day"; the schedule feeds platform's widget (
NowNextPublisher). - Tests (
PrompTugalTests/ScheduleTests.swift,TeamFeaturesTests.swift): wire format and an exact round trip of a website-shaped document, merge rule with negative controls, Lisbon time, migration, vault sync, markdown and JSON parsing, reminder plan, planner. The injected JavaScript is syntax-checked with node before each push.
CI
- Green on dev, everything of mine included (b5955d2 and e97868a are the last code commits):
- iPhone unit tests: run 150 (dev 991cbfa), passed.
- iPad unit tests: run 154 (dev d33acfe), passed.
- App build and SGitVault tests: run 104 and run 128, passed; the test jobs above also compile the app.
- Full runs green on dev since then (build, SGitVault, iPhone and iPad tests): run 160 (3fff23e), run 173 (8a060e8), run 180 (fadf27e, night/team's last merge) and run 188 (bf704b5).
- Earlier failures, fixed: one test expression the type checker timed out on (396968c); the schedule's JSON key order, not stable across devices (500b5ad, now written by hand as the website's
JSON.stringifywrites it). - Bridge against the live website (
Tools/bridge-e2e/, Chromium, rerun 09:25 UTC): the app's schedule write repaints the website's own "Add to my day" button and appears on /my-day/; a toggle on the website is read back; the meter credit goes £4.99 → £9.99 once and the same reference is refused; the Stripe top-up is hidden and "Top up in the app" shows;window.ptApp.callround-trips and refusals reject. Not covered there: WebKit's content worlds and the native origin check (read in code, not run).
What I learned from the websites
- Pick id is the bare slug (
ai-doom-or-boom), notevents/<slug>. atis an ISO string (toISOString()). The app writes exactly that and reads numbers too.- The website's merge is union only, and so is the vault's
savePicks. The app adds, on this device: a removal later than the pick wins (remembered 30 days), and a pick the page dropped since the last sync counts as removed. A removal does not reach other devices or the website; that is in the conductor's list for Dinis. - Meter:
localStorage['web-summit.newsroom.meter.v1'], sg-meter state v3, amounts in pence. App credit uses the site's owncredit(amount, 'link', 'appstore-<transaction id>'), which refuses a reference twice. - Team site: only
me.jsonandpeople/<id>/app/data.jsonare JSON. Wakes, reports, pulse, log and the meetings list are read from their markdown twins (index.md). Stand-up folder names do not always match the time (2026-11-05-1300was at 12:00), so the list's label is used. - Places:
/api/v1/places.json(83, no coordinates). Walking times are estimates: 5 minutes between rooms at the venue, 25 to anywhere else.
Entry views (for the shell)
| Feature | Entry view | Destination |
|---|---|---|
| Schedule | MyDayView() | replaces team.schedule |
| Planner | PlannerView() | new, Today: "Plan my day" |
| Where is everyone | WhereIsEveryoneView() | new, Team |
| Team home and identity | TeamHomeView() | optional |
| Reading credit | ReadingCreditView() | replaces team.store |
| Reminders | ReminderSettingsView() | Settings |
| Bridge panel | BridgeLogView() | Debug → Bridge |
All need .teamFeatures() once on the root view: it injects every store, starts StoreKit, reminders and the vault link, and presents the native top-up when a page asks.
Models (all Sendable, decoded leniently): TeamMe, TeamPerson, CommandCentre (Issue, Decision, Agent, Activity, Session, Workstream, Member), TeamStandup, AgentWakes, TeamReport, PulseEntry, SessionLogEntry, held by TeamStore. Schedule: ScheduleStore, SchedulePick, ScheduleDocument. URLs: TeamEndpoints.
Next (after the push)
- A test on a real iPhone at the summit: pick on promptugal.news in the app, see it in My day, get the 10-minute reminder.
- Removals across devices and the website (a "removed" list in
agenda/picks): needs the website to agree; on the conductor's list for Dinis. - Live Activity for the next item (platform owns the target;
NextHoursCardhas the data shape). - Verify a team identity from the team vault (
TeamIdentityStore.verify(by:person:)is ready; the vault session calls it when a team vault names its person).
Recently
- All my entry views are registered by design on dev (planner, reminders in Settings, Bridge in Debug, reading credit, team home). "Where is everyone" has one shell entry, collab's.
NextHoursCard()for Today ("my next 3 hours", or three sessions starting soon); design places it at the top of My day and on the Wall.- Planner: the plan, the teammate index and title keywords are cached, so redraws do not rescan the 195 agenda items.
- Reminders: permission asked once at the right moment; topped up on every foreground (iOS keeps 64).
Blocked
Nothing.
Needs from others
Nothing open. Done by others tonight, for the record:
- Platform: the bridge on
WebsiteBrowser(configure, attach,pageDidFinish,handlesNavigation);ReaderStore→ the schedule andagendaDidLoad; the widget from the schedule; thenews.promptugal.reader.refreshbackground task callingTeamFeatures.shared.refreshTeam();NSCalendarsWriteOnlyAccessUsageDescription; the Time Sensitive Notifications entitlement; App-Bound Domains. - Design:
.teamFeatures()at the root, my entry views inDestinations, the bridge onWebPage,NextHoursCardon My day and the Wall. - Vault:
loadPicks()/savePicks(_:), used byScheduleVaultLink.
Apple setup for the native top-up
- App Store Connect: two consumables,
news.promptugal.reader.credit.5(£5 of credit) andnews.promptugal.reader.credit.20(£20), each with a price, a display name and a review screenshot. - The Paid Apps agreement signed, with banking and tax details.
- The In-App Purchase capability on the app id.
- Local testing already works with
PrompTugal/Store/PrompTugal.storekiton the scheme. - Review note: the credit is for the reading meter on promptugal.news; the website's Stripe link is hidden in the app (rule 3.1.1).
Trade-offs
- Data stores: promptugal.news stays on the default store, where earlier builds kept its schedule, meter and vault session. web-summit.sgit.ai moves to its own store, so its earlier
ws.mechoice andws-keyringkeys stay behind. The app writesws.mefrom the native identity; each person opens their#key=link once more. - Bridge in its own world: page scripts cannot see a handler in another content world, so
window.ptApp.call()relays through a DOM event (and a DOM attribute) to the app's bridge-world script. The origin check is native, on the real frame.