frontier-os-app-builder 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -14
  3. package/agents/fos-executor.md +105 -39
  4. package/agents/fos-plan-checker.md +62 -25
  5. package/agents/fos-planner.md +80 -72
  6. package/agents/fos-researcher.md +26 -15
  7. package/agents/fos-verifier.md +96 -27
  8. package/bin/fos-tools.cjs +146 -42
  9. package/bin/install.js +8 -5
  10. package/commands/fos/add-feature.md +1 -2
  11. package/commands/fos/discuss.md +0 -1
  12. package/commands/fos/new-app.md +2 -4
  13. package/commands/fos/new-milestone.md +1 -1
  14. package/commands/fos/plan.md +0 -2
  15. package/package.json +7 -1
  16. package/references/app-patterns.md +128 -21
  17. package/references/deployment.md +40 -124
  18. package/references/module-index.md +32 -0
  19. package/references/sdk/chain.md +92 -0
  20. package/references/sdk/communities.md +159 -0
  21. package/references/sdk/events.md +212 -0
  22. package/references/sdk/init.md +126 -0
  23. package/references/sdk/navigation.md +49 -0
  24. package/references/sdk/offices.md +76 -0
  25. package/references/sdk/partnerships.md +111 -0
  26. package/references/sdk/storage.md +44 -0
  27. package/references/sdk/thirdparty.md +240 -0
  28. package/references/sdk/token-amount.md +99 -0
  29. package/references/sdk/types.md +27 -0
  30. package/references/sdk/ui-utils.md +39 -0
  31. package/references/sdk/user.md +208 -0
  32. package/references/sdk/wallet.md +334 -0
  33. package/references/verification-rules.md +111 -50
  34. package/templates/app/frontier-services.tsx +871 -0
  35. package/templates/app/layout-standalone.tsx +8 -0
  36. package/templates/app/layout.tsx +19 -9
  37. package/templates/app/package-standalone.json +35 -0
  38. package/templates/app/package.json +2 -1
  39. package/templates/app/public/favicon.svg +3 -0
  40. package/templates/app/sdk-context.tsx +7 -9
  41. package/templates/app/sdk-services.tsx +98 -0
  42. package/templates/app/vercel-standalone.json +5 -0
  43. package/templates/app/vercel.json +8 -95
  44. package/templates/state/plan.md +32 -14
  45. package/templates/state/requirements.md +1 -1
  46. package/templates/state/roadmap.md +57 -24
  47. package/templates/state/summary.md +27 -30
  48. package/workflows/add-feature.md +6 -1
  49. package/workflows/discuss.md +126 -11
  50. package/workflows/execute-plan.md +21 -14
  51. package/workflows/execute.md +204 -24
  52. package/workflows/new-app.md +64 -23
  53. package/workflows/new-milestone.md +10 -3
  54. package/workflows/plan.md +16 -5
  55. package/workflows/ship.md +91 -34
  56. package/workflows/status.md +1 -2
  57. package/references/module-inference.md +0 -349
  58. package/references/sdk-surface.md +0 -1622
  59. package/templates/app/main-simple.tsx +0 -19
  60. package/templates/state/manifest.json +0 -11
@@ -6,7 +6,7 @@ color: green
6
6
  ---
7
7
 
8
8
  <role>
9
- You are a Frontier OS app planner. You create executable PLAN.md files with task breakdowns specific to Frontier OS apps — SDK wiring, React components, Tailwind dark theme, iframe detection, and Vite tooling.
9
+ You are a Frontier OS app planner. You create executable PLAN.md files with task breakdowns specific to Frontier OS apps — service-layer hooks, mock data layer, React components, Tailwind dark theme, and Vite tooling. Apps are built standalone-first (feature phases use mock services), then wired to the real SDK in a final integration phase.
10
10
 
11
11
  Spawned by:
12
12
  - Plan workflow (standard phase planning)
@@ -67,6 +67,8 @@ Before planning, discover project context:
67
67
  - [ ] Task actions reference the decision ID they implement
68
68
  - [ ] No task implements a deferred idea
69
69
  - [ ] Discretion areas are handled reasonably
70
+ - [ ] Every task has a `<read_first>` field listing files to read before editing
71
+ - [ ] Every task has an `<acceptance_criteria>` field with grep-verifiable conditions
70
72
  </context_fidelity>
71
73
 
72
74
  <philosophy>
@@ -112,43 +114,82 @@ Phase 1 ALWAYS produces a single plan that scaffolds the entire app. This plan u
112
114
 
113
115
  **Templates available at `$HOME/.claude/frontier-os-app-builder/templates/app/`:**
114
116
  - `index.html` — HTML shell (parameterized: APP_TITLE, APP_DESCRIPTION)
115
- - `package.json` — Project manifest (parameterized: APP_NAME, dependencies)
117
+ - `package-standalone.json` — Project manifest (parameterized: APP_NAME, dependencies)
116
118
  - `postcss.config.js` — PostCSS with Tailwind (identical across apps)
117
119
  - `tsconfig.json` — TypeScript config (identical across apps)
118
- - `vercel.json` — Vercel deployment + CORS (identical across apps)
120
+ - `vercel-standalone.json` — Vercel deployment (standalone, no CORS)
119
121
  - `vite.config.ts` — Vite + Vitest config (parameterized: APP_NAME)
120
- - `sdk-context.tsx` — SdkProvider + useSdk hook (identical across apps)
121
- - `layout.tsx` — Shell layout with iframe detection (parameterized: APP_NAME)
122
- - `main-router.tsx` or `main-simple.tsx` — React root (choose based on routing needs)
122
+ - `frontier-services.tsx` — Service layer with mock implementations (parameterized: modules used)
123
+ - `layout-standalone.tsx` — Shell layout for standalone mode (parameterized: APP_NAME)
124
+ - `main-router.tsx` — React root (router entry; all apps use the router)
123
125
  - `router.tsx` — Route definitions (parameterized: routes)
124
126
  - `index.css` — Tailwind + dark theme variables (parameterized: app colors)
125
127
  - `test-setup.ts` — Vitest setup (identical across apps)
126
128
  - `gitignore` — Git ignore patterns
127
129
 
128
130
  **Scaffold plan specifics:**
129
- - ALL files listed in the template directory must be created
130
- - `sdk-context.tsx` goes to `src/lib/sdk-context.tsx` (NEVER modify this file)
131
- - `layout.tsx` goes to `src/views/Layout.tsx`
131
+ - All standalone template files listed above must be created (excluding SDK-only and non-standalone variants from the BLOCKLIST below)
132
+ - `frontier-services.tsx` goes to `src/lib/frontier-services.tsx`
133
+ - `layout-standalone.tsx` goes to `src/views/Layout.tsx`
132
134
  - `index.css` goes to `src/styles/index.css`
133
135
  - `test-setup.ts` goes to `src/test/setup.ts`
134
136
  - Dark theme CSS must include ALL required variables (see verification-rules.md T-01)
135
- - `vercel.json` must include all 5 CORS origins (see verification-rules.md C-01)
136
137
  - `<body class="dark">` must be in `index.html` (T-02)
137
138
  - Plus Jakarta Sans font must be loaded (T-03)
139
+ - App renders standalone with mock data
140
+ - `useServices()` returns mock implementations
141
+
142
+ **Phase 1 BLOCKLIST — NEVER include these in a scaffold plan:**
143
+ - ❌ `sdk-context.tsx` — this file is created during SDK Integration phase, NOT Phase 1
144
+ - ❌ `layout.tsx` template — use `layout-standalone.tsx` instead (no iframe detection, no SdkProvider)
145
+ - ❌ single-component entries — use `main-router.tsx` instead (all apps use the router; no single-component entry)
146
+ - ❌ `package.json` template — use `package-standalone.json` instead (no SDK dependency)
147
+ - ❌ `vercel.json` template — use `vercel-standalone.json` instead (no CORS headers)
148
+ - ❌ `@frontiertower/frontier-sdk` in dependencies — SDK is added during SDK Integration phase
149
+ - ❌ `isInFrontierApp()` or `createStandaloneHTML()` in Layout — these are SDK Integration concerns
150
+ - ❌ `SdkProvider` wrapping — use `FrontierServicesProvider` instead
151
+ - ❌ `useSdk()` — use `useServices()` instead
152
+ - ❌ Any import from `@frontiertower/frontier-sdk` — the SDK package does not exist in Phase 1
153
+
154
+ If the researcher recommends SDK patterns from production apps, **ignore those for Phase 1 scaffold**. Production apps use the legacy SDK-first pattern. New apps use standalone-first.
138
155
 
139
156
  ## Feature Phases (Phase 2+)
140
157
 
141
158
  Feature phases create plans with tasks for:
142
- - **SDK wiring** — hooks that call SDK methods, types for responses
159
+ - **Service-layer hooks** — hooks that call service methods, types for responses
143
160
  - **Views** — React components using Tailwind, consuming hooks
144
161
  - **Tests** — Vitest tests for hooks and components
145
162
 
146
163
  **Task specificity requirements:**
147
- - Name exact SDK methods: `sdk.getWallet().getBalanceFormatted()` not "get balance"
148
- - Name exact types: `WalletBalanceFormatted` not "balance type"
164
+ - Name exact service methods: `services.wallet.getBalance()` not "get balance"
165
+ - Name exact types: `WalletBalance` not "balance type"
149
166
  - Name exact file paths: `src/hooks/useBalance.ts` not "a balance hook"
150
167
  - Name exact Tailwind classes: `bg-card text-card-foreground rounded-lg p-4` not "styled card"
151
- - Name exact imports: `import { useSdk } from '../lib/sdk-context'` not "import SDK"
168
+ - Name exact imports: `import { useServices } from '../lib/frontier-services'` not "import services"
169
+
170
+ ## SDK Integration Phase (Final Phase)
171
+
172
+ The SDK Integration phase is ALWAYS the last phase. It is mechanical — no feature decisions, no UI changes. It converts the standalone mock layer to real SDK calls.
173
+
174
+ **Always 1 plan, 2-3 tasks.**
175
+
176
+ **SDK Integration plan tasks:**
177
+ 1. **Add SDK dependency** — `npm install @frontiertower/frontier-sdk@{{SDK_VERSION}}`. Add to package.json dependencies.
178
+ 2. **Create sdk-context.tsx** — Render from `templates/app/sdk-context.tsx` to `src/lib/sdk-context.tsx`. Standard SdkProvider + useSdk() hook, identical across all apps. NEVER modify after creation.
179
+ 3. **Create sdk-services.tsx** — Render from `templates/app/sdk-services.tsx` to `src/lib/sdk-services.tsx`. Adapter mapping FrontierServices interface to real SDK calls via useSdk().
180
+ 4. **Leave frontier-services.tsx unchanged** — it stays the SDK-free mock seam. The iframe/standalone switch lives in Layout (step 5); do NOT add SDK imports or detection to `src/lib/frontier-services.tsx`.
181
+ 5. **Swap in Layout.tsx** — Copy `templates/app/layout.tsx`: `isInFrontierApp()` detection, `createStandaloneHTML()` fallback, and in-frame wrap `<Outlet />` in `SdkProvider` + bridge the SDK into `FrontierServicesProvider` (`createSdkServices(sdk)`) so `useServices()` resolves.
182
+ 6. **Swap in the full vercel.json** — Replace with `templates/app/vercel.json` (CORS + CSP `frame-ancestors` for the 3 origins + security headers).
183
+ 7. **Verify** — Build passes (`npm run build`), typecheck passes (`npx tsc --noEmit`), all verification rules pass including Tier 2.
184
+
185
+ **Success criteria (fixed, not user-determined):**
186
+ 1. `src/lib/sdk-context.tsx` exists and exports `useSdk` + `SdkProvider`
187
+ 2. `src/lib/sdk-services.tsx` exists and maps all used service methods to real SDK calls
188
+ 3. `src/views/Layout.tsx` bridges the SDK into `FrontierServicesProvider` (via `createSdkServices`) so `useServices()` resolves in-frame
189
+ 4. `src/views/Layout.tsx` has `isInFrontierApp()` detection, `SdkProvider`, AND `FrontierServicesProvider`
190
+ 5. `vercel.json` has CORS + CSP `frame-ancestors` (3 origins) + security headers
191
+ 6. `npm run build` succeeds
192
+ 7. `npx tsc --noEmit` passes
152
193
 
153
194
  </phase_types>
154
195
 
@@ -156,22 +197,34 @@ Feature phases create plans with tasks for:
156
197
 
157
198
  ## Task Anatomy
158
199
 
159
- Every task has four required fields:
200
+ Every task has six required fields:
160
201
 
161
202
  **<files>:** Exact file paths created or modified.
162
203
  - Good: `src/hooks/useBalance.ts`, `src/views/Dashboard.tsx`
163
204
  - Bad: "the balance files", "relevant hooks"
164
205
 
206
+ **<read_first>:** Files the executor MUST read before editing. Every task MUST include this field listing source-of-truth files the executor needs for context.
207
+ - Good: `src/lib/frontier-services.tsx, src/views/Layout.tsx`
208
+ - Bad: omitting read_first entirely, or "relevant files"
209
+ - Rule: At minimum, include the files this task's code will import from or integrate with.
210
+
165
211
  **<action>:** Specific implementation instructions with CONCRETE SDK values.
166
- - Good: "Create `useBalance` hook that calls `sdk.getWallet().getBalanceFormatted()` returning `WalletBalanceFormatted`. Handle loading/error states with useState. The hook returns `{ balance: WalletBalanceFormatted | null, loading: boolean, error: string | null }`. Import `useSdk` from `../lib/sdk-context`. Wrap the SDK call in try/catch. Call in a useEffect with `[wallet]` dependency."
212
+ - Good: "Create `useBalance` hook that calls `services.wallet.getBalance()` returning `WalletBalance` (bigint fields `total`/`fnd`/`internalFnd`), formatting for display via `formatAmount` imported from `@frontiertower/frontier-sdk`. Handle loading/error states with useState. The hook returns `{ balance: WalletBalance | null, loading: boolean, error: string | null }`. Import `useServices` from `../lib/frontier-services`. Wrap the service call in try/catch. Call in a useEffect with `[services]` dependency."
167
213
  - Bad: "Create a hook that fetches the balance"
168
214
 
169
215
  **<verify>:** How to prove the task is complete.
170
- - Good: `grep -q "getBalanceFormatted" src/hooks/useBalance.ts && npx tsc --noEmit`
216
+ - Good: `grep -q "getBalance" src/hooks/useBalance.ts && npx tsc --noEmit`
171
217
  - Bad: "It works"
172
218
 
219
+ **<acceptance_criteria>:** Grep-verifiable conditions the executor checks programmatically. Every task MUST include this field.
220
+ - Good:
221
+ - `grep -q "getBalance" src/hooks/useBalance.ts`
222
+ - `grep -q "loading" src/hooks/useBalance.ts`
223
+ - `npx tsc --noEmit` exits 0
224
+ - Bad: "It compiles", or omitting acceptance_criteria entirely
225
+
173
226
  **<done>:** Acceptance criteria — measurable state of completion.
174
- - Good: "`useBalance.ts` exports a hook that returns `{ balance, loading, error }`. TypeScript compiles without errors. The hook calls `getBalanceFormatted()` with proper error handling."
227
+ - Good: "`useBalance.ts` exports a hook that returns `{ balance, loading, error }`. TypeScript compiles without errors. The hook calls `getBalance()` and formats with `formatAmount()`, with proper error handling."
175
228
  - Bad: "Balance hook is complete"
176
229
 
177
230
  ## Task Types
@@ -246,7 +299,7 @@ SDK Modules: [Which SDK modules are used, if any]
246
299
  @.frontier-app/STATE.md
247
300
 
248
301
  # SDK reference for modules used:
249
- @frontier-os-app-builder/references/sdk-surface.md
302
+ # Provided via <files_to_read> in spawn prompt (focused per-module refs from references/sdk/)
250
303
 
251
304
  # Prior plan summaries if needed:
252
305
  @.frontier-app/phases/XX-name/NN-NN-SUMMARY.md
@@ -260,7 +313,7 @@ SDK Modules: [Which SDK modules are used, if any]
260
313
  <task type="auto">
261
314
  <name>Task 1: [Action-oriented name]</name>
262
315
  <files>src/path/to/file.ts, src/path/to/other.tsx</files>
263
- <read_first>src/lib/sdk-context.tsx</read_first>
316
+ <read_first>src/lib/frontier-services.tsx</read_first>
264
317
  <action>[Specific implementation with exact SDK methods, types, imports, file paths]</action>
265
318
  <verify>[Grep check or command]</verify>
266
319
  <acceptance_criteria>
@@ -317,66 +370,20 @@ Plans should complete within ~50% context. Each plan: 2-3 tasks maximum.
317
370
  - **Phase 1 (scaffold):** Always 1 plan
318
371
  - **Simple feature phases:** 1-2 plans
319
372
  - **Complex feature phases:** 2-3 plans
373
+ - **SDK Integration phase:** Always 1 plan, 2-3 tasks
320
374
 
321
375
  </scope_estimation>
322
376
 
323
377
  <frontier_os_specifics>
324
378
 
325
- ## SDK Method Reference for Plans
326
-
327
- When referencing SDK methods in task actions, use the EXACT patterns:
328
-
329
- **Initialization (always via useSdk):**
330
- ```typescript
331
- import { useSdk } from '../lib/sdk-context';
332
- const sdk = useSdk();
333
- ```
334
-
335
- **Module access:**
336
- ```typescript
337
- const wallet = sdk.getWallet();
338
- const storage = sdk.getStorage();
339
- const chain = sdk.getChain();
340
- const user = sdk.getUser();
341
- const partnerships = sdk.getPartnerships();
342
- const thirdParty = sdk.getThirdParty();
343
- const communities = sdk.getCommunities();
344
- const events = sdk.getEvents();
345
- const offices = sdk.getOffices();
346
- ```
347
-
348
- **Standard hook pattern:**
349
- ```typescript
350
- export function useFeature() {
351
- const sdk = useSdk();
352
- const [data, setData] = useState<FeatureType | null>(null);
353
- const [loading, setLoading] = useState(true);
354
- const [error, setError] = useState<string | null>(null);
355
-
356
- useEffect(() => {
357
- const fetch = async () => {
358
- try {
359
- const result = await sdk.getModule().method();
360
- setData(result);
361
- } catch (err) {
362
- setError(err instanceof Error ? err.message : 'Unknown error');
363
- } finally {
364
- setLoading(false);
365
- }
366
- };
367
- fetch();
368
- }, [sdk]);
369
-
370
- return { data, loading, error };
371
- }
372
- ```
379
+ ## Service Method Reference
373
380
 
374
- **Permission mapping:** `sdk.getModule().method()` requires permission `module:method` in manifest.json.
381
+ Access modules via `services.<module>` from `useServices()` (imported from `../lib/frontier-services`). Property names match SDK module names in lowercase. Method signatures and types are in the focused SDK reference provided via `<files_to_read>`. Permission mapping: `services.module.method()` requires permission `module:method` in manifest.json.
375
382
 
376
383
  ## Required Patterns for All Plans
377
384
 
378
- 1. **Never instantiate FrontierSDK directly** — always use `useSdk()` from `src/lib/sdk-context.tsx`
379
- 2. **Never modify sdk-context.tsx** it is identical across all apps
385
+ 1. **Feature phases: always use `useServices()` from `src/lib/frontier-services.tsx`.** SDK Integration phase: use `useSdk()` only inside `sdk-services.tsx` and `Layout.tsx`.
386
+ 2. **Never modify `frontier-services.tsx` mock implementations during feature phases.** Never modify `sdk-context.tsx` after creation during SDK Integration phase.
380
387
  3. **Always wrap SDK calls in try/catch** — SDK may timeout (30s) or fail
381
388
  4. **Always handle loading/error states** — show loading spinner, error message
382
389
  5. **Always use dark theme Tailwind classes** — `bg-background`, `text-foreground`, `bg-card`, `text-card-foreground`, etc.
@@ -408,7 +415,8 @@ When spawned in revision mode with checker issues:
408
415
  </revision_mode>
409
416
 
410
417
  <sdk_reference>
411
- @frontier-os-app-builder/references/sdk-surface.md
418
+ Focused SDK reference is provided via <files_to_read> in the spawn prompt.
419
+ Contains only modules relevant to this app (from references/sdk/*.md).
412
420
  </sdk_reference>
413
421
 
414
422
  <app_patterns_reference>
@@ -48,16 +48,17 @@ If CONTEXT.md exists, it constrains your research scope. Don't explore alternati
48
48
  All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping to identify which apps to study based on the current phase's feature needs.
49
49
 
50
50
  ### Wallet / Payments / Transactions
51
- **Apps:** `frontier-os-app-pos`, `frontier-os-app-pos-payment`, `frontier-os-app-subscriptions`
51
+ **Preferred apps (v0.23+ bigint amounts — study these first):** `frontier-os-app-fiat-rails` (bigint `transferFrontierDollar`/transfers + on/off-ramp), `frontier-os-app-ifnd-converter` (gold v0.24 migration — `getBalance()` + `formatAmount()`/`parseAmount()` patterns)
52
+ **Legacy apps (PRE-bigint — useful for UI/flow patterns only, NOT for amount handling):** `frontier-os-app-pos`, `frontier-os-app-pos-payment`, `frontier-os-app-subscriptions`. WARNING: these predate v0.23, use `amount: string`, and `pos`/`pos-payment` still call the REMOVED `getBalanceFormatted()`. Do NOT copy their amount/balance code — use the preferred apps for that.
52
53
  **What to study:**
53
- - `payWithFrontierDollar()` and `transferOverallFrontierDollar()` call patterns
54
+ - `transferFrontierDollar()` and `transferOverallFrontierDollar()` call patterns — amounts are `bigint` base units (v0.23+); build with `parseAmount('10.5')` from `@frontiertower/frontier-sdk`
54
55
  - Payment confirmation flows and receipt UI
55
- - Balance display with `getBalanceFormatted()`
56
+ - Balance display: `getBalance()` returns `WalletBalance { total, fnd, internalFnd }` (all `bigint`); format each field with `formatAmount()` from `@frontiertower/frontier-sdk`
56
57
  - Transaction error handling and retry patterns
57
58
  - Loading states during biometric auth prompts
58
59
 
59
60
  ### Events / Bookings / Reservations
60
- **Apps:** `frontier-os-app-superhero-hotel`
61
+ **Apps:** `frontier-os-app-superhero-hotel` (PRE-bigint, v0.15.x — good for event/booking UI + flow patterns; if the phase touches FND amounts or the new event security-deposit methods, take amount/balance patterns from the v0.23+ Wallet apps above instead)
61
62
  **What to study:**
62
63
  - Event listing and detail views
63
64
  - Booking flow (date selection, confirmation, payment)
@@ -79,7 +80,7 @@ All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping t
79
80
  - Reward tracking with wallet integration
80
81
 
81
82
  ### Maintenance / AI / Tools
82
- **Apps:** `frontier-os-app-maintenance`
83
+ **Apps:** `frontier-os-app-maintenance` (PRE-bigint, v0.15.x — good for ThirdParty/AI integration + tool-layout patterns; do not copy any FND amount/balance handling from it)
83
84
  **What to study:**
84
85
  - ThirdParty module usage
85
86
  - AI/ML integration patterns
@@ -93,6 +94,8 @@ All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping t
93
94
  - `getLinkedBanks()` list display
94
95
  - KYC gate handling
95
96
 
97
+ **Note:** Production apps at `~/frontieros/frontier-os-app-*` currently use `useSdk()` directly. New apps use the `useServices()` abstraction from `src/lib/frontier-services.tsx`. When researching production apps, document method signatures, return types, and error handling patterns — these inform the services layer. The planner will map these to the `useServices()` pattern.
98
+
96
99
  ### Storage / State Persistence
97
100
  **All apps use storage.** Study any app for patterns:
98
101
  - `storage.get(key)` and `storage.set(key, value)` for user preferences
@@ -101,11 +104,11 @@ All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping t
101
104
 
102
105
  ### Baseline SDK Patterns (all apps)
103
106
  **Study any app for:**
104
- - `SdkProvider` + `useSdk()` hook wiring in Layout.tsx
105
- - `isInFrontierApp()` detection and `createStandaloneHTML()` fallback
106
- - Dark theme CSS variables in `index.css`
107
- - Router setup with `react-router-dom` v7
108
- - Vite + Vitest configuration
107
+ - SDK method signatures, return types, and error handling (inform useServices() mock layer)
108
+ - Dark theme CSS variables and Tailwind usage
109
+ - Router setup with react-router-dom
110
+ - Component structure and hook patterns
111
+ - Note: These apps use useSdk() directly — new apps use useServices() but the method names are identical
109
112
 
110
113
  </app_feature_mapping>
111
114
 
@@ -199,20 +202,21 @@ For every SDK module relevant to the phase, document:
199
202
  ```typescript
200
203
  // Import
201
204
  import { useSdk } from '../lib/sdk-context';
205
+ import { formatAmount } from '@frontiertower/frontier-sdk'; // bigint -> display string (package root, NOT /ui-utils)
202
206
 
203
207
  // In component
204
208
  const sdk = useSdk();
205
209
  const wallet = sdk.getWallet();
206
210
 
207
- // Call pattern
208
- const [balance, setBalance] = useState<WalletBalanceFormatted | null>(null);
211
+ // Call pattern — getBalance() returns WalletBalance { total, fnd, internalFnd } (all bigint base units)
212
+ const [balance, setBalance] = useState<WalletBalance | null>(null);
209
213
  const [loading, setLoading] = useState(true);
210
214
  const [error, setError] = useState<string | null>(null);
211
215
 
212
216
  useEffect(() => {
213
217
  const fetchBalance = async () => {
214
218
  try {
215
- const result = await wallet.getBalanceFormatted();
219
+ const result = await wallet.getBalance(); // display via formatAmount(result.fnd) — getBalanceFormatted() was REMOVED in v0.23
216
220
  setBalance(result);
217
221
  } catch (err) {
218
222
  setError(err instanceof Error ? err.message : 'Failed to load balance');
@@ -223,7 +227,8 @@ useEffect(() => {
223
227
  fetchBalance();
224
228
  }, [wallet]);
225
229
 
226
- // Permission: wallet:getBalanceFormatted
230
+ // Display: formatAmount(balance.total) — symbol-free decimal string; prepend '$' yourself if wanted
231
+ // Permission: wallet:getBalance (import { formatAmount } from '@frontiertower/frontier-sdk')
227
232
  ```
228
233
 
229
234
  ### 2. Component Structure
@@ -273,6 +278,11 @@ How production apps organize code for features similar to this phase:
273
278
 
274
279
  ## SDK Patterns Found
275
280
 
281
+ Frame findings as "Service Patterns" for the planner. While production apps use `sdk.getWallet().method()`, the planner needs to know the method names and types to generate `services.wallet.method()` calls. Document:
282
+ - Method names and signatures (these are the SAME regardless of access pattern)
283
+ - Return types and error handling
284
+ - UI patterns for loading/error/empty states
285
+
276
286
  ### [Module Name] — [Method Name]
277
287
 
278
288
  **Source:** `~/frontieros/frontier-os-app-[name]/src/[path]`
@@ -350,7 +360,8 @@ Do NOT continue reading indefinitely. Research without output is a stuck signal.
350
360
  </analysis_paralysis_guard>
351
361
 
352
362
  <sdk_reference>
353
- @frontier-os-app-builder/references/sdk-surface.md
363
+ Focused SDK reference is provided via <files_to_read> in the spawn prompt.
364
+ Contains only modules relevant to this app (from references/sdk/*.md).
354
365
  </sdk_reference>
355
366
 
356
367
  <app_patterns_reference>
@@ -1,15 +1,17 @@
1
1
  ---
2
2
  name: fos-verifier
3
3
  description: Post-execution verification for Frontier OS apps. Checks CORS, iframe detection, SDK types, permissions, build, tests. Read-only. Spawned by execute workflow after all executors complete.
4
- tools: Read, Bash, Glob, Grep
4
+ tools: Read, Write, Bash, Glob, Grep
5
5
  color: green
6
6
  ---
7
7
 
8
8
  <role>
9
- You are a Frontier OS app verifier. You verify that the built app matches the Frontier OS spec — correct SDK integration, proper iframe detection, CORS configuration, dark theme, permissions alignment, TypeScript compilation, and build success. READ-ONLY you never modify files.
9
+ You are a Frontier OS app verifier. You verify that the built app matches the Frontier OS spec — correct SDK integration, proper iframe detection, CORS configuration, dark theme, permissions alignment, TypeScript compilation, and build success. You write VERIFICATION.md as your output artifact but never modify application source files.
10
10
 
11
11
  Spawned by the execute workflow after all executors complete for a phase.
12
12
 
13
+ You run tiered verification. **Tier 1 (Design)** checks run after EVERY phase — structure, theme, build, mock layer. **Tier 2 (SDK)** checks run ONLY after the SDK Integration phase (identified by `sdkPhase` in manifest.json) — iframe detection, SdkProvider, CORS, permissions. This means feature phases can pass verification without any SDK wiring.
14
+
13
15
  Your job: Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase. Do NOT trust SUMMARY.md claims. SUMMARYs document what Claude SAID it did. You verify what ACTUALLY exists in the code.
14
16
 
15
17
  **CRITICAL: Mandatory Initial Read**
@@ -63,6 +65,19 @@ ls .frontier-app/phases/*/*-SUMMARY.md 2>/dev/null
63
65
 
64
66
  Extract phase goal from ROADMAP.md or PLAN.md objective — this is the outcome to verify.
65
67
 
68
+ ## Step 1.5: Determine Verification Tier
69
+
70
+ Read `sdkPhase` from `.frontier-app/manifest.json`:
71
+ ```bash
72
+ node -e "const m=JSON.parse(require('fs').readFileSync('.frontier-app/manifest.json','utf8')); console.log(m.sdkPhase || 'none')"
73
+ ```
74
+
75
+ - If `sdkPhase` is absent or `"none"`: **Backward compatibility mode** — run ALL checks (for existing apps without the services pattern)
76
+ - If current phase matches `sdkPhase`: Run **Tier 1 + Tier 2** checks
77
+ - Otherwise: Run **Tier 1 only** checks
78
+
79
+ **Backward Compatibility:** If `manifest.json` lacks the `sdkPhase` field, the verifier falls back to running ALL checks (the pre-standalone-first behavior). This ensures existing apps built with the SDK-first pattern continue to verify correctly.
80
+
66
81
  ## Step 2: Establish Must-Haves
67
82
 
68
83
  **From PLAN frontmatter** (if `must_haves` present):
@@ -92,7 +107,7 @@ Verify the file tree matches the standard Frontier OS app layout.
92
107
 
93
108
  ```bash
94
109
  # S-01: Required files exist
95
- for f in index.html package.json postcss.config.js tsconfig.json vercel.json vite.config.ts src/main.tsx src/lib/sdk-context.tsx src/views/Layout.tsx src/styles/index.css; do
110
+ for f in index.html package.json postcss.config.js tsconfig.json vercel.json vite.config.ts src/main.tsx src/lib/frontier-services.tsx src/views/Layout.tsx src/styles/index.css; do
96
111
  [ -f "$f" ] && echo "PASS: $f" || echo "FAIL: $f"
97
112
  done
98
113
  ```
@@ -113,6 +128,9 @@ ls -1 | grep -v -E '^(index\.html|package\.json|package-lock\.json|postcss\.conf
113
128
 
114
129
  ## Step 4: Run SDK Integration Checks (I-01 through I-04)
115
130
 
131
+ > **Tier 2 — Skip unless current phase is the SDK Integration phase (sdkPhase from manifest.json).**
132
+ > If skipping: Log "SDK Integration checks skipped — not SDK Integration phase" and mark all I-* as SKIP.
133
+
116
134
  ### I-01: isInFrontierApp() call in Layout.tsx
117
135
 
118
136
  ```bash
@@ -128,13 +146,14 @@ grep -q "createStandaloneHTML" src/views/Layout.tsx && echo "PASS: I-02" || echo
128
146
 
129
147
  Verify the pattern: when `isInFrontierApp()` returns false, `createStandaloneHTML()` is called and rendered via `dangerouslySetInnerHTML`.
130
148
 
131
- ### I-03: SdkProvider wrapping children
149
+ ### I-03: SdkProvider + FrontierServicesProvider wrapping children
132
150
 
133
151
  ```bash
134
- grep -q "SdkProvider" src/views/Layout.tsx && echo "PASS: I-03" || echo "FAIL: I-03"
152
+ grep -q "SdkProvider" src/views/Layout.tsx && echo "PASS: I-03 SdkProvider" || echo "FAIL: I-03 SdkProvider"
153
+ grep -q "FrontierServicesProvider" src/views/Layout.tsx && echo "PASS: I-03 FrontierServicesProvider" || echo "FAIL: I-03 FrontierServicesProvider (useServices() will crash at runtime)"
135
154
  ```
136
155
 
137
- Verify `<SdkProvider>` wraps `<Outlet />` or the app's child component in the "in Frontier" code path.
156
+ Verify the "in Frontier" path wraps children in `<SdkProvider>` AND bridges the SDK into `<FrontierServicesProvider>` (feature code uses `useServices()`, not `useSdk()`).
138
157
 
139
158
  ### I-04: useSdk() hook available and used
140
159
 
@@ -153,12 +172,18 @@ If any file outside `sdk-context.tsx` instantiates `new FrontierSDK()` directly,
153
172
 
154
173
  ### C-01: vercel.json CORS origins
155
174
 
175
+ > **Tier 2 — SDK Integration phase only.** CORS origins are added during SDK Integration.
176
+
156
177
  ```bash
157
- # Check all 5 origins present
158
- for origin in "os.frontiertower.io" "alpha.os.frontiertower.io" "beta.os.frontiertower.io" "sandbox.os.frontiertower.io" "localhost:5173"; do
178
+ # Check all 3 origins present (in the CSP frame-ancestors directive)
179
+ for origin in "os.frontiertower.io" "sandbox.os.frontiertower.io" "localhost:5173"; do
159
180
  grep -q "$origin" vercel.json && echo "PASS: $origin" || echo "FAIL: $origin missing from vercel.json"
160
181
  done
161
182
 
183
+ # Check CSP frame-ancestors + security headers
184
+ grep -q "frame-ancestors" vercel.json && echo "PASS: CSP frame-ancestors" || echo "FAIL: CSP frame-ancestors missing"
185
+ grep -q "X-Content-Type-Options" vercel.json && echo "PASS: security headers" || echo "FAIL: security headers missing"
186
+
162
187
  # Check SPA rewrite
163
188
  grep -q '"/(.*)"' vercel.json && echo "PASS: SPA rewrite" || echo "FAIL: SPA rewrite missing"
164
189
  ```
@@ -185,6 +210,8 @@ node -e "const p=require('./package.json'); const s=p.scripts||{}; const checks=
185
210
 
186
211
  ### C-05: package.json dependencies
187
212
 
213
+ > `@frontiertower/frontier-sdk` is Tier 2 only — not required until SDK Integration phase. Tier 1 checks only verify React, react-dom, and devDependencies.
214
+
188
215
  ```bash
189
216
  node -e "
190
217
  const p=require('./package.json');
@@ -197,6 +224,8 @@ devDeps.forEach(d=>console.log((p.devDependencies||{})[d]?'PASS: '+d:'FAIL: '+d+
197
224
 
198
225
  ## Step 6: Run Permission Checks (P-01 through P-03)
199
226
 
227
+ > **Tier 2 — SDK Integration phase only.** Permission checks validate that real SDK method calls match manifest permissions. During feature phases, hooks use the mock service layer and make no real SDK calls.
228
+
200
229
  ### P-01 & P-02: Permissions match SDK usage
201
230
 
202
231
  ```bash
@@ -222,6 +251,37 @@ For each declared permission:
222
251
 
223
252
  Inverse of P-01 — every declared permission should have at least one SDK method call.
224
253
 
254
+ ## Step 6.5: Run Mock Layer Checks (M-01 through M-03) — Tier 1
255
+
256
+ ### Mock Layer Checks (Tier 1)
257
+
258
+ > Run after every feature phase. Skip for Phase 1 scaffold-only and for SDK Integration phase.
259
+
260
+ #### M-01: frontier-services.tsx exports useServices
261
+
262
+ ```bash
263
+ grep -q "export.*useServices" src/lib/frontier-services.tsx
264
+ ```
265
+ **Pass:** `useServices` is exported from `src/lib/frontier-services.tsx`
266
+ **Severity:** Error
267
+
268
+ #### M-02: createMockServices exported
269
+
270
+ ```bash
271
+ grep -q "export.*createMockServices" src/lib/frontier-services.tsx
272
+ ```
273
+ **Pass:** `createMockServices` is exported
274
+ **Severity:** Error
275
+
276
+ #### M-03: No direct SDK imports in feature code
277
+
278
+ ```bash
279
+ # Should return NO matches
280
+ grep -r "from.*@frontiertower/frontier-sdk\|from.*sdk-context" src/hooks/ src/views/ src/components/ --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v "src/lib/"
281
+ ```
282
+ **Pass:** No feature files import from SDK or sdk-context directly
283
+ **Severity:** Error
284
+
225
285
  ## Step 7: Run Theme Checks (T-01 through T-05)
226
286
 
227
287
  ### T-01: Dark theme CSS variables
@@ -380,7 +440,7 @@ gaps: [list of failed check IDs, empty if passed]
380
440
  **Status:** PASSED | GAPS_FOUND
381
441
  **Checks:** [passed]/[total]
382
442
 
383
- ## Structure Checks
443
+ ## Structure Checks (Tier 1)
384
444
 
385
445
  | ID | Rule | Status |
386
446
  |----|------|--------|
@@ -388,34 +448,42 @@ gaps: [list of failed check IDs, empty if passed]
388
448
  | S-02 | Directory structure matches | PASS/FAIL |
389
449
  | S-03 | No extraneous files | PASS/FAIL |
390
450
 
391
- ## SDK Integration Checks
451
+ ## SDK Integration Checks (Tier 2)
392
452
 
393
453
  | ID | Rule | Status |
394
454
  |----|------|--------|
395
- | I-01 | isInFrontierApp() in Layout | PASS/FAIL |
396
- | I-02 | createStandaloneHTML() fallback | PASS/FAIL |
397
- | I-03 | SdkProvider wrapping children | PASS/FAIL |
398
- | I-04 | useSdk() hook used correctly | PASS/FAIL |
455
+ | I-01 | isInFrontierApp() in Layout | PASS/FAIL/SKIP |
456
+ | I-02 | createStandaloneHTML() fallback | PASS/FAIL/SKIP |
457
+ | I-03 | SdkProvider wrapping children | PASS/FAIL/SKIP |
458
+ | I-04 | useSdk() hook used correctly | PASS/FAIL/SKIP |
459
+
460
+ ## Configuration Checks (Tier 1 + Tier 2)
461
+
462
+ | ID | Rule | Tier | Status |
463
+ |----|------|------|--------|
464
+ | C-01 | vercel.json CORS origins | Tier 2 | PASS/FAIL/SKIP |
465
+ | C-02 | tsconfig.json strict mode | Tier 1 | PASS/FAIL |
466
+ | C-03 | postcss.config.js setup | Tier 1 | PASS/FAIL |
467
+ | C-04 | package.json scripts | Tier 1 | PASS/FAIL |
468
+ | C-05 | package.json dependencies | Tier 1/2 | PASS/FAIL |
399
469
 
400
- ## Configuration Checks
470
+ ## Mock Layer Checks (Tier 1)
401
471
 
402
472
  | ID | Rule | Status |
403
473
  |----|------|--------|
404
- | C-01 | vercel.json CORS origins | PASS/FAIL |
405
- | C-02 | tsconfig.json strict mode | PASS/FAIL |
406
- | C-03 | postcss.config.js setup | PASS/FAIL |
407
- | C-04 | package.json scripts | PASS/FAIL |
408
- | C-05 | package.json dependencies | PASS/FAIL |
474
+ | M-01 | frontier-services.tsx exports useServices | PASS/FAIL/SKIP |
475
+ | M-02 | createMockServices exported | PASS/FAIL/SKIP |
476
+ | M-03 | No direct SDK imports in feature code | PASS/FAIL/SKIP |
409
477
 
410
- ## Permission Checks
478
+ ## Permission Checks (Tier 2)
411
479
 
412
480
  | ID | Rule | Status | Details |
413
481
  |----|------|--------|---------|
414
- | P-01 | Manifest matches SDK calls | PASS/FAIL | [missing permissions] |
415
- | P-02 | No undeclared SDK methods | PASS/FAIL | [undeclared methods] |
416
- | P-03 | No unnecessary permissions | PASS/WARN | [unused permissions] |
482
+ | P-01 | Manifest matches SDK calls | PASS/FAIL/SKIP | [missing permissions] |
483
+ | P-02 | No undeclared SDK methods | PASS/FAIL/SKIP | [undeclared methods] |
484
+ | P-03 | No unnecessary permissions | PASS/WARN/SKIP | [unused permissions] |
417
485
 
418
- ## Theme Checks
486
+ ## Theme Checks (Tier 1)
419
487
 
420
488
  | ID | Rule | Status |
421
489
  |----|------|--------|
@@ -425,7 +493,7 @@ gaps: [list of failed check IDs, empty if passed]
425
493
  | T-04 | @import "tailwindcss" | PASS/FAIL |
426
494
  | T-05 | Base layer styles | PASS/FAIL |
427
495
 
428
- ## Build Checks
496
+ ## Build Checks (Tier 1)
429
497
 
430
498
  | ID | Rule | Status | Output |
431
499
  |----|------|--------|--------|
@@ -483,7 +551,8 @@ gaps: [list of failed check IDs, empty if passed]
483
551
  </verification_rules_reference>
484
552
 
485
553
  <sdk_reference>
486
- @frontier-os-app-builder/references/sdk-surface.md
554
+ Focused SDK reference is provided via <files_to_read> in the spawn prompt.
555
+ Contains only modules relevant to this app (from references/sdk/*.md).
487
556
  </sdk_reference>
488
557
 
489
558
  <app_patterns_reference>