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
@@ -0,0 +1,8 @@
1
+ import { Outlet } from 'react-router-dom';
2
+ import { FrontierServicesProvider } from '../lib/frontier-services';
3
+
4
+ export const Layout = () => (
5
+ <FrontierServicesProvider>
6
+ <Outlet />
7
+ </FrontierServicesProvider>
8
+ );
@@ -1,21 +1,27 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useMemo, useState, type ReactNode } from 'react';
2
2
  import { Outlet } from 'react-router-dom';
3
3
  import { isInFrontierApp, createStandaloneHTML } from '@frontiertower/frontier-sdk/ui-utils';
4
- import { SdkProvider } from '../lib/sdk-context';
4
+ import { SdkProvider, useSdk } from '../lib/sdk-context';
5
+ import { FrontierServicesProvider } from '../lib/frontier-services';
6
+ import { createSdkServices } from '../lib/sdk-services';
7
+
8
+ // Bridges the live SDK (from SdkProvider) into the FrontierServices seam that
9
+ // feature code consumes via useServices(). Keeps feature code SDK-agnostic: the
10
+ // exact same components run against mocks (standalone) and the real SDK (iframe).
11
+ const SdkServicesBridge = ({ children }: { children: ReactNode }) => {
12
+ const sdk = useSdk();
13
+ const services = useMemo(() => createSdkServices(sdk), [sdk]);
14
+ return <FrontierServicesProvider services={services}>{children}</FrontierServicesProvider>;
15
+ };
5
16
 
6
17
  export const Layout = () => {
7
18
  const [loading, setLoading] = useState(true);
8
19
  const [standaloneHtml, setStandaloneHtml] = useState('');
9
20
 
10
21
  useEffect(() => {
11
- const inFrontier = isInFrontierApp();
12
-
13
- if (!inFrontier) {
22
+ if (!isInFrontierApp()) {
14
23
  setStandaloneHtml(createStandaloneHTML('{{APP_NAME}}'));
15
- setLoading(false);
16
- return;
17
24
  }
18
-
19
25
  setLoading(false);
20
26
  }, []);
21
27
 
@@ -37,9 +43,13 @@ export const Layout = () => {
37
43
  );
38
44
  }
39
45
 
46
+ // In-frame: provide both the raw SDK (useSdk) and the SDK-backed services
47
+ // seam (useServices) so every feature component works unchanged from standalone.
40
48
  return (
41
49
  <SdkProvider>
42
- <Outlet />
50
+ <SdkServicesBridge>
51
+ <Outlet />
52
+ </SdkServicesBridge>
43
53
  </SdkProvider>
44
54
  );
45
55
  };
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "{{PACKAGE_NAME}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "tsc --noEmit",
11
+ "test": "vitest run"
12
+ },
13
+ "dependencies": {
14
+ "react": "^19.2.3",
15
+ "react-dom": "^19.2.3",
16
+ "react-icons": "^5.5.0",
17
+ "react-router-dom": "^7.12.0"
18
+ },
19
+ "devDependencies": {
20
+ "@tailwindcss/postcss": "^4.1.18",
21
+ "@testing-library/jest-dom": "^6.9.1",
22
+ "@testing-library/react": "^16.3.1",
23
+ "@testing-library/user-event": "^14.6.1",
24
+ "@types/react": "^19.2.7",
25
+ "@types/react-dom": "^19.2.3",
26
+ "@vitejs/plugin-react": "^5.1.2",
27
+ "@vitest/coverage-v8": "^4.0.18",
28
+ "jsdom": "^27.4.0",
29
+ "postcss": "^8.5.6",
30
+ "tailwindcss": "^4.1.18",
31
+ "typescript": "^5.9.3",
32
+ "vite": "^7.3.0",
33
+ "vitest": "^4.0.16"
34
+ }
35
+ }
@@ -15,7 +15,8 @@
15
15
  "react": "^19.2.3",
16
16
  "react-dom": "^19.2.3",
17
17
  "react-icons": "^5.5.0",
18
- "react-router-dom": "^7.12.0"
18
+ "react-router-dom": "^7.12.0",
19
+ "viem": "^2.44.0"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@tailwindcss/postcss": "^4.1.18",
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" role="img" aria-label="Frontier">
2
+ <circle cx="16" cy="16" r="14" fill="#764AE2" />
3
+ </svg>
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useEffect, useRef, useState, type ReactNode } from 'react';
1
+ import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
2
2
  import { FrontierSDK } from '@frontiertower/frontier-sdk';
3
3
 
4
4
  const SdkContext = createContext<FrontierSDK | null>(null);
@@ -10,23 +10,21 @@ export const useSdk = (): FrontierSDK => {
10
10
  };
11
11
 
12
12
  export const SdkProvider = ({ children }: { children: ReactNode }) => {
13
- const sdkRef = useRef<FrontierSDK | null>(null);
14
- const [ready, setReady] = useState(false);
13
+ const [sdk, setSdk] = useState<FrontierSDK | null>(null);
15
14
 
16
15
  useEffect(() => {
17
- const sdk = new FrontierSDK();
18
- sdkRef.current = sdk;
19
- setReady(true);
16
+ const instance = new FrontierSDK();
17
+ setSdk(instance);
20
18
 
21
19
  return () => {
22
- sdk.destroy();
20
+ instance.destroy();
23
21
  };
24
22
  }, []);
25
23
 
26
- if (!ready) return null;
24
+ if (!sdk) return null;
27
25
 
28
26
  return (
29
- <SdkContext.Provider value={sdkRef.current}>
27
+ <SdkContext.Provider value={sdk}>
30
28
  {children}
31
29
  </SdkContext.Provider>
32
30
  );
@@ -0,0 +1,98 @@
1
+ /**
2
+ * SDK Services Adapter
3
+ *
4
+ * Maps the FrontierServices interface to real SDK module calls.
5
+ * Used when the app runs inside the Frontier OS iframe.
6
+ * Only modules declared in manifest.json are wired; unused modules throw.
7
+ *
8
+ * During SDK Integration the executor should:
9
+ * 1. Wire modules the app actually uses (replace Proxy stubs with real SDK calls)
10
+ * 2. Keep Proxy stubs for unused modules
11
+ */
12
+
13
+ import type { FrontierSDK } from '@frontiertower/frontier-sdk';
14
+ import type {
15
+ FrontierServices,
16
+ WalletService,
17
+ StorageService,
18
+ ChainService,
19
+ UserService,
20
+ PartnershipsService,
21
+ ThirdPartyService,
22
+ CommunitiesService,
23
+ EventsService,
24
+ OfficesService,
25
+ NavigationService,
26
+ } from './frontier-services';
27
+
28
+ function notAvailable(module: string): never {
29
+ throw new Error(`[SDK] ${module} module is not available in this app`);
30
+ }
31
+
32
+ export function createSdkServices(sdk: FrontierSDK): FrontierServices {
33
+ void sdk; // marks `sdk` used while modules are still stubs; remove once you wire one below (e.g. sdk.getWallet())
34
+ // ── Wire modules your app uses ──────────────────────────────
35
+ // Replace the Proxy stubs below with real SDK adapter objects
36
+ // for each module declared in manifest.json.
37
+ //
38
+ // Example (wallet):
39
+ // const walletAccess = sdk.getWallet();
40
+ // const wallet: WalletService = {
41
+ // getBalance: () => walletAccess.getBalance(),
42
+ // ...
43
+ // };
44
+
45
+ // ── Stub modules not used by this app ───────────────────────
46
+ const wallet = new Proxy({} as WalletService, {
47
+ get: () => () => notAvailable('Wallet'),
48
+ });
49
+
50
+ const storage = new Proxy({} as StorageService, {
51
+ get: () => () => notAvailable('Storage'),
52
+ });
53
+
54
+ const chain = new Proxy({} as ChainService, {
55
+ get: () => () => notAvailable('Chain'),
56
+ });
57
+
58
+ const user = new Proxy({} as UserService, {
59
+ get: () => () => notAvailable('User'),
60
+ });
61
+
62
+ const partnerships = new Proxy({} as PartnershipsService, {
63
+ get: () => () => notAvailable('Partnerships'),
64
+ });
65
+
66
+ const thirdParty = new Proxy({} as ThirdPartyService, {
67
+ get: () => () => notAvailable('ThirdParty'),
68
+ });
69
+
70
+ const communities = new Proxy({} as CommunitiesService, {
71
+ get: () => () => notAvailable('Communities'),
72
+ });
73
+
74
+ const events = new Proxy({} as EventsService, {
75
+ get: () => () => notAvailable('Events'),
76
+ });
77
+
78
+ const offices = new Proxy({} as OfficesService, {
79
+ get: () => () => notAvailable('Offices'),
80
+ });
81
+
82
+ const navigation = new Proxy({} as NavigationService, {
83
+ get: () => () => notAvailable('Navigation'),
84
+ });
85
+
86
+ return {
87
+ wallet,
88
+ storage,
89
+ chain,
90
+ user,
91
+ partnerships,
92
+ thirdParty,
93
+ communities,
94
+ events,
95
+ offices,
96
+ navigation,
97
+ };
98
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "rewrites": [
3
+ { "source": "/(.*)", "destination": "/index.html" }
4
+ ]
5
+ }
@@ -5,13 +5,6 @@
5
5
  "headers": [
6
6
  {
7
7
  "source": "/(.*)",
8
- "has": [
9
- {
10
- "type": "header",
11
- "key": "Origin",
12
- "value": "https://os.frontiertower.io"
13
- }
14
- ],
15
8
  "headers": [
16
9
  {
17
10
  "key": "Access-Control-Allow-Origin",
@@ -24,102 +17,22 @@
24
17
  {
25
18
  "key": "Access-Control-Allow-Headers",
26
19
  "value": "Content-Type"
27
- }
28
- ]
29
- },
30
- {
31
- "source": "/(.*)",
32
- "has": [
33
- {
34
- "type": "header",
35
- "key": "Origin",
36
- "value": "https://alpha.os.frontiertower.io"
37
- }
38
- ],
39
- "headers": [
40
- {
41
- "key": "Access-Control-Allow-Origin",
42
- "value": "https://alpha.os.frontiertower.io"
43
20
  },
44
21
  {
45
- "key": "Access-Control-Allow-Methods",
46
- "value": "GET, OPTIONS"
22
+ "key": "Content-Security-Policy",
23
+ "value": "frame-ancestors https://os.frontiertower.io https://sandbox.os.frontiertower.io http://localhost:5173;"
47
24
  },
48
25
  {
49
- "key": "Access-Control-Allow-Headers",
50
- "value": "Content-Type"
51
- }
52
- ]
53
- },
54
- {
55
- "source": "/(.*)",
56
- "has": [
57
- {
58
- "type": "header",
59
- "key": "Origin",
60
- "value": "https://beta.os.frontiertower.io"
61
- }
62
- ],
63
- "headers": [
64
- {
65
- "key": "Access-Control-Allow-Origin",
66
- "value": "https://beta.os.frontiertower.io"
26
+ "key": "X-Content-Type-Options",
27
+ "value": "nosniff"
67
28
  },
68
29
  {
69
- "key": "Access-Control-Allow-Methods",
70
- "value": "GET, OPTIONS"
30
+ "key": "Referrer-Policy",
31
+ "value": "strict-origin-when-cross-origin"
71
32
  },
72
33
  {
73
- "key": "Access-Control-Allow-Headers",
74
- "value": "Content-Type"
75
- }
76
- ]
77
- },
78
- {
79
- "source": "/(.*)",
80
- "has": [
81
- {
82
- "type": "header",
83
- "key": "Origin",
84
- "value": "https://sandbox.os.frontiertower.io"
85
- }
86
- ],
87
- "headers": [
88
- {
89
- "key": "Access-Control-Allow-Origin",
90
- "value": "https://sandbox.os.frontiertower.io"
91
- },
92
- {
93
- "key": "Access-Control-Allow-Methods",
94
- "value": "GET, OPTIONS"
95
- },
96
- {
97
- "key": "Access-Control-Allow-Headers",
98
- "value": "Content-Type"
99
- }
100
- ]
101
- },
102
- {
103
- "source": "/(.*)",
104
- "has": [
105
- {
106
- "type": "header",
107
- "key": "Origin",
108
- "value": "http://localhost:5173"
109
- }
110
- ],
111
- "headers": [
112
- {
113
- "key": "Access-Control-Allow-Origin",
114
- "value": "http://localhost:5173"
115
- },
116
- {
117
- "key": "Access-Control-Allow-Methods",
118
- "value": "GET, OPTIONS"
119
- },
120
- {
121
- "key": "Access-Control-Allow-Headers",
122
- "value": "Content-Type"
34
+ "key": "Permissions-Policy",
35
+ "value": "camera=(), microphone=(), geolocation=()"
123
36
  }
124
37
  ]
125
38
  }
@@ -42,8 +42,9 @@ SDK Modules: [Which SDK modules are used in this plan, if any]
42
42
  @.frontier-app/ROADMAP.md
43
43
  @.frontier-app/STATE.md
44
44
 
45
- # SDK reference for modules used in this plan:
46
- @frontier-sdk/docs/[module].md
45
+ # Feature phases consume the mock service layer (useServices) — no SDK docs in <context>.
46
+ # SDK module docs are referenced ONLY in the final SDK Integration plan, via:
47
+ # @frontier-os-app-builder/references/sdk/[module].md
47
48
 
48
49
  # Only reference prior plan SUMMARYs if genuinely needed:
49
50
  # - This plan uses types/exports from prior plan
@@ -88,7 +89,7 @@ SDK Modules: [Which SDK modules are used in this plan, if any]
88
89
  <how-to-verify>Visit http://localhost:{{DEV_PORT}} and verify:
89
90
  - [Visual check 1]
90
91
  - [Visual check 2]
91
- Open Frontier OS at localhost:3000 and verify app loads in iframe.</how-to-verify>
92
+ (SDK Integration phase only) Open Frontier OS at localhost:3000 and verify the app loads in the iframe.</how-to-verify>
92
93
  <resume-signal>Type "approved" or describe issues</resume-signal>
93
94
  </task>
94
95
 
@@ -136,26 +137,43 @@ After completion, create `.frontier-app/phases/XX-name/{phase}-{plan}-SUMMARY.md
136
137
 
137
138
  ## Frontier OS Specifics
138
139
 
139
- **Phase 1 plans always include:**
140
+ **Phase 1 (scaffold) plans always include — standalone-first:**
140
141
  - Vite scaffold from `templates/app/vite.config.ts`
141
- - SdkProvider from `templates/app/sdk-context.tsx`
142
- - Layout with iframe detection from `templates/app/layout.tsx`
143
- - Dark theme setup via Tailwind
144
- - Standalone fallback UI
142
+ - `FrontierServicesProvider` + mock services from `templates/app/frontier-services.tsx` → `src/lib/frontier-services.tsx` (feature code calls `useServices()`)
143
+ - Layout from `templates/app/layout-standalone.tsx` → `src/views/Layout.tsx` (NO iframe detection, NO SdkProvider)
144
+ - Entry from `templates/app/main-router.tsx` (router entry — all apps use the router)
145
+ - Dark theme via Tailwind 4 `@theme` in `src/styles/index.css` (CSS-only — no `tailwind.config`)
146
+ - Standalone UI rendering with mock data
145
147
  - Dev server on assigned port
146
148
 
147
- **SDK usage in tasks:**
148
- - Always specify the exact import: `import { FrontierSDK } from '@frontiertower/frontier-sdk'`
149
- - Always use `useSdk()` hook, never instantiate SDK directly in components
150
- - Always wrap SDK calls in try/catch SDK may not be available in standalone mode
151
- - Reference the specific SDK module docs in `<context>` section
149
+ **Phase 1 BLOCKLIST — NEVER include these in a scaffold plan:**
150
+ - `sdk-context.tsx` this file is created during SDK Integration phase, NOT Phase 1
151
+ - `layout.tsx` template — use `layout-standalone.tsx` instead (no iframe detection, no SdkProvider)
152
+ - single-component entries use `main-router.tsx` instead (all apps use the router; no single-component entry)
153
+ - `package.json` template use `package-standalone.json` instead (no SDK dependency)
154
+ - ❌ `vercel.json` template — use `vercel-standalone.json` instead (no CORS headers)
155
+ - ❌ `@frontiertower/frontier-sdk` in dependencies — SDK is added during SDK Integration phase
156
+ - ❌ `isInFrontierApp()` or `createStandaloneHTML()` in Layout — these are SDK Integration concerns
157
+ - ❌ `SdkProvider` wrapping — use `FrontierServicesProvider` instead
158
+ - ❌ `useSdk()` — use `useServices()` instead
159
+ - ❌ Any import from `@frontiertower/frontier-sdk` — the SDK package does not exist in Phase 1
160
+
161
+ **Service usage in feature phases (Phase 2+):**
162
+ - Call service methods through the mock seam: `services.<module>.<method>()` (e.g. `services.wallet.getBalance()`)
163
+ - Always obtain services via `useServices()` — `import { useServices } from '../lib/frontier-services'`
164
+ - Wrap service calls in try/catch and handle loading/error states
165
+ - Never import `@frontiertower/frontier-sdk` or call `useSdk()` in feature code
166
+ - The `FrontierSDK` import + `useSdk()` pattern belongs ONLY to the final SDK Integration phase
152
167
 
153
168
  **Verification always includes:**
154
169
  - Build succeeds (`npm run build`)
155
170
  - Dev server starts on correct port
156
171
  - No TypeScript errors
157
172
  - Dark theme renders correctly (no white backgrounds)
158
- - App works in both iframe and standalone modes
173
+ - Standalone renders with mock services (`useServices()` resolves)
174
+
175
+ **SDK Integration tier (final phase) additionally verifies:**
176
+ - App works in both iframe and standalone modes (in-frame `services.*` resolve via the SDK bridge)
159
177
 
160
178
  ---
161
179
 
@@ -87,7 +87,7 @@ Which phases cover which requirements. Updated during roadmap creation.
87
87
 
88
88
  **Standard Platform Requirements:**
89
89
  - PLAT-01 through PLAT-05 are ALWAYS included for every Frontier OS app
90
- - They always map to Phase 1 (Scaffold + SDK Core)
90
+ - They always map to Phase 1 (Scaffold + Standalone Shell)
91
91
  - They are non-negotiable — an app without these cannot run in Frontier OS
92
92
 
93
93
  **Requirement Format:**
@@ -14,27 +14,28 @@ What makes this app worth building and what does the finished product look like?
14
14
 
15
15
  ## {{MILESTONE_VERSION}} Phases
16
16
 
17
- - [ ] **Phase 1: Scaffold + SDK Core** — Project setup, SdkProvider, iframe detection, dark theme, standalone fallback
17
+ - [ ] **Phase 1: Scaffold + Standalone Shell** — Project setup, services layer, mock data, dark theme
18
18
  - [ ] **Phase 2: [Feature Name]** — [One-line description]
19
19
  - [ ] **Phase 3: [Feature Name]** — [One-line description]
20
- - [ ] **Phase N: [Feature Name]** — [One-line description]
20
+ - [ ] **Phase N-1: [Feature Name]** — [One-line description]
21
+ - [ ] **Phase N: SDK Integration** — Wire SDK, create adapter, upgrade Layout for iframe
21
22
 
22
23
  ## Phase Details
23
24
 
24
- ### Phase 1: Scaffold + SDK Core
25
- **Goal**: Working app shell with SDK connected, running in iframe and standalone
25
+ ### Phase 1: Scaffold + Standalone Shell
26
+ **Goal**: Working app shell running standalone in browser with mock data
26
27
  **Depends on**: Nothing (always first)
27
28
  **Requirements**: PLAT-01, PLAT-02, PLAT-03, PLAT-04, PLAT-05
28
29
  **Success Criteria** (what must be TRUE):
29
- 1. App renders inside Frontier OS iframe without errors
30
- 2. App detects standalone mode and shows appropriate fallback UI
31
- 3. Dark theme applied no white backgrounds, no light-mode artifacts
32
- 4. SdkProvider initializes and useSdk() returns a valid SDK instance
33
- 5. Dev server runs on assigned port with HMR working
30
+ 1. App renders standalone in browser with mock data
31
+ 2. Dark theme applied no white backgrounds, no light-mode artifacts
32
+ 3. useServices() returns mock wallet balance, user data, storage
33
+ 4. Dev server runs on assigned port with HMR working
34
+ 5. npm run build succeeds
34
35
  **Plans**: 1 plan
35
36
 
36
37
  Plans:
37
- - [ ] 01-01: Vite + React scaffold, SdkProvider, iframe detection, dark theme, dev config
38
+ - [ ] 01-01: Vite + React scaffold, services layer, mock data, dark theme, dev config
38
39
 
39
40
  ### Phase 2: [Feature Name]
40
41
  **Goal**: [What this phase delivers — one sentence]
@@ -50,9 +51,9 @@ Plans:
50
51
  - [ ] 02-01: [Brief description of first plan]
51
52
  - [ ] 02-02: [Brief description of second plan]
52
53
 
53
- ### Phase N: [Feature Name]
54
+ ### Phase N-1: [Feature Name]
54
55
  **Goal**: [What this phase delivers]
55
- **Depends on**: Phase [N-1]
56
+ **Depends on**: Phase [N-2]
56
57
  **Requirements**: [REQ-XX, REQ-YY]
57
58
  **Success Criteria** (what must be TRUE):
58
59
  1. [Observable behavior from user perspective]
@@ -62,13 +63,30 @@ Plans:
62
63
  Plans:
63
64
  - [ ] NN-01: [Brief description]
64
65
 
66
+ ### Phase N: SDK Integration
67
+ **Goal**: Wire real Frontier SDK into the standalone app shell
68
+ **Depends on**: All feature phases
69
+ **Requirements**: PLAT-SDK-01
70
+ **Success Criteria** (what must be TRUE):
71
+ 1. sdk-context.tsx exists and exports useSdk + SdkProvider
72
+ 2. sdk-services.tsx maps all service methods to real SDK calls
73
+ 3. Layout.tsx has isInFrontierApp() detection and SdkProvider wrapping
74
+ 4. vercel.json has CORS + CSP frame-ancestors (3 origins) + security headers
75
+ 5. App works both standalone (mocks) and in iframe (real SDK)
76
+ 6. npm run build succeeds
77
+ **Plans**: 1 plan (mechanical)
78
+
79
+ Plans:
80
+ - [ ] NN-01: SDK dependency, adapter, Layout upgrade, CORS
81
+
65
82
  ## Progress
66
83
 
67
84
  | Phase | Plans Complete | Status | Completed |
68
85
  |-------|----------------|--------|-----------|
69
- | 1. Scaffold + SDK Core | 0/1 | Not started | - |
86
+ | 1. Scaffold + Standalone Shell | 0/1 | Not started | - |
70
87
  | 2. [Name] | 0/N | Not started | - |
71
- | N. [Name] | 0/N | Not started | - |
88
+ | N-1. [Name] | 0/N | Not started | - |
89
+ | N. SDK Integration | 0/1 | Not started | - |
72
90
 
73
91
  ---
74
92
  *Roadmap created: {{DATE}}*
@@ -80,15 +98,22 @@ Plans:
80
98
  <guidelines>
81
99
 
82
100
  **Phase 1 is always the same:**
83
- - "Scaffold + SDK Core" — never skip, never rename
101
+ - "Scaffold + Standalone Shell" — never skip, never rename
84
102
  - Covers all PLAT-* requirements
85
103
  - Always 1 plan (the scaffold is well-defined)
86
104
  - Success criteria are standardized (see template)
87
- - Uses templates from `templates/app/` directory
105
+ - Uses standalone templates from `templates/app/` directory (frontier-services.tsx, layout-standalone.tsx, package-standalone.json, vercel-standalone.json)
106
+
107
+ **Final phase is always SDK Integration:**
108
+ - Auto-added as the last phase — never skip, never rename
109
+ - Always 1 plan (mechanical, no user decisions)
110
+ - Wires real SDK: adds dependency, creates adapter, upgrades Layout, adds CORS
111
+ - Fixed success criteria (see template)
88
112
 
89
113
  **Phase structure:**
90
- - Phase 1: Scaffold (always)
91
- - Phases 2-N: Feature phases (from requirements)
114
+ - Phase 1: Scaffold + Standalone Shell (always)
115
+ - Phases 2 to N-1: Feature phases (from requirements)
116
+ - Phase N: SDK Integration (always last)
92
117
  - Keep to 3-6 total phases for v1 — ship fast
93
118
  - Each phase delivers something coherent and testable
94
119
 
@@ -113,17 +138,25 @@ Plans:
113
138
 
114
139
  <frontier_specifics>
115
140
 
116
- **Phase 1 always generates from templates:**
141
+ **Phase 1 always generates from standalone templates:**
117
142
  - `templates/app/vite.config.ts` → configured with app's dev port
118
- - `templates/app/sdk-context.tsx` → SdkProvider + useSdk hook
119
- - `templates/app/layout.tsx` → dark theme shell with iframe detection
120
- - `templates/app/main-simple.tsx` or `main-router.tsx` → entry point
143
+ - `templates/app/frontier-services.tsx` → useServices() provider + mock services
144
+ - `templates/app/layout-standalone.tsx` → dark theme shell with FrontierServicesProvider
145
+ - `templates/app/main-router.tsx` → entry point
146
+ - `templates/app/package-standalone.json` → dependencies without SDK
147
+ - `templates/app/vercel-standalone.json` → SPA rewrite only (no CORS)
121
148
  - `templates/app/tsconfig.json` → TypeScript config
122
149
  - `templates/app/postcss.config.js` → Tailwind setup
123
150
 
124
- **Feature phases should reference SDK modules:**
151
+ **Feature phases should reference service modules:**
125
152
  - If a phase uses Events module, note it in the goal
126
153
  - If a phase uses Wallet module, note it in the goal
127
- - This helps the planner know which SDK APIs to use
154
+ - This helps the planner know which service methods to use via useServices()
155
+
156
+ **SDK Integration phase generates from SDK templates:**
157
+ - `templates/app/sdk-context.tsx` → SdkProvider + useSdk hook
158
+ - `templates/app/sdk-services.tsx` → adapter mapping services to real SDK
159
+ - `templates/app/layout.tsx` → Layout upgrade with iframe detection
160
+ - `templates/app/vercel.json` → full CORS origins
128
161
 
129
162
  </frontier_specifics>