@prmichaelsen/acp-visualizer 0.1.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 (180) hide show
  1. package/README.md +68 -0
  2. package/agent/commands/acp.clarification-address.md +417 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +437 -0
  5. package/agent/commands/acp.clarifications-research.md +326 -0
  6. package/agent/commands/acp.command-create.md +432 -0
  7. package/agent/commands/acp.design-create.md +286 -0
  8. package/agent/commands/acp.design-reference.md +355 -0
  9. package/agent/commands/acp.handoff.md +270 -0
  10. package/agent/commands/acp.index.md +423 -0
  11. package/agent/commands/acp.init.md +546 -0
  12. package/agent/commands/acp.package-create.md +895 -0
  13. package/agent/commands/acp.package-info.md +212 -0
  14. package/agent/commands/acp.package-install.md +539 -0
  15. package/agent/commands/acp.package-list.md +280 -0
  16. package/agent/commands/acp.package-publish.md +541 -0
  17. package/agent/commands/acp.package-remove.md +293 -0
  18. package/agent/commands/acp.package-search.md +307 -0
  19. package/agent/commands/acp.package-update.md +361 -0
  20. package/agent/commands/acp.package-validate.md +540 -0
  21. package/agent/commands/acp.pattern-create.md +386 -0
  22. package/agent/commands/acp.plan.md +587 -0
  23. package/agent/commands/acp.proceed.md +882 -0
  24. package/agent/commands/acp.project-create.md +675 -0
  25. package/agent/commands/acp.project-info.md +312 -0
  26. package/agent/commands/acp.project-list.md +226 -0
  27. package/agent/commands/acp.project-remove.md +379 -0
  28. package/agent/commands/acp.project-set.md +227 -0
  29. package/agent/commands/acp.project-update.md +307 -0
  30. package/agent/commands/acp.projects-restore.md +228 -0
  31. package/agent/commands/acp.projects-sync.md +347 -0
  32. package/agent/commands/acp.report.md +407 -0
  33. package/agent/commands/acp.resume.md +239 -0
  34. package/agent/commands/acp.sessions.md +301 -0
  35. package/agent/commands/acp.status.md +293 -0
  36. package/agent/commands/acp.sync.md +364 -0
  37. package/agent/commands/acp.task-create.md +500 -0
  38. package/agent/commands/acp.update.md +302 -0
  39. package/agent/commands/acp.validate.md +466 -0
  40. package/agent/commands/acp.version-check-for-updates.md +276 -0
  41. package/agent/commands/acp.version-check.md +191 -0
  42. package/agent/commands/acp.version-update.md +289 -0
  43. package/agent/commands/command.template.md +339 -0
  44. package/agent/commands/git.commit.md +526 -0
  45. package/agent/commands/git.init.md +514 -0
  46. package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
  47. package/agent/commands/tanstack-cloudflare.tail.md +275 -0
  48. package/agent/design/.gitkeep +0 -0
  49. package/agent/design/design.template.md +154 -0
  50. package/agent/design/local.dashboard-layout-routing.md +288 -0
  51. package/agent/design/local.data-model-yaml-parsing.md +310 -0
  52. package/agent/design/local.search-filtering.md +331 -0
  53. package/agent/design/local.server-api-auto-refresh.md +235 -0
  54. package/agent/design/local.table-tree-views.md +299 -0
  55. package/agent/design/local.visualizer-requirements.md +349 -0
  56. package/agent/design/requirements.template.md +387 -0
  57. package/agent/index/.gitkeep +0 -0
  58. package/agent/index/acp.core.yaml +137 -0
  59. package/agent/index/local.main.template.yaml +37 -0
  60. package/agent/manifest.template.yaml +13 -0
  61. package/agent/manifest.yaml +302 -0
  62. package/agent/milestones/.gitkeep +0 -0
  63. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
  64. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  65. package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
  66. package/agent/package.template.yaml +86 -0
  67. package/agent/patterns/.gitkeep +0 -0
  68. package/agent/patterns/bootstrap.template.md +1237 -0
  69. package/agent/patterns/pattern.template.md +382 -0
  70. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
  71. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
  72. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
  73. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
  74. package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
  75. package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
  76. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
  77. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
  78. package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
  79. package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
  80. package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
  81. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
  82. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
  83. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
  84. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
  85. package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
  86. package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
  87. package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
  88. package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
  89. package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
  90. package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
  91. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
  92. package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
  93. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
  94. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
  95. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
  96. package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
  97. package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
  98. package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
  99. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
  100. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
  101. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
  102. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
  103. package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
  104. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
  105. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
  106. package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
  107. package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
  108. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
  109. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
  110. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
  111. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
  112. package/agent/progress.template.yaml +161 -0
  113. package/agent/progress.yaml +145 -0
  114. package/agent/schemas/package.schema.yaml +276 -0
  115. package/agent/scripts/acp.common.sh +1781 -0
  116. package/agent/scripts/acp.install.sh +333 -0
  117. package/agent/scripts/acp.package-create.sh +924 -0
  118. package/agent/scripts/acp.package-info.sh +288 -0
  119. package/agent/scripts/acp.package-install.sh +893 -0
  120. package/agent/scripts/acp.package-list.sh +311 -0
  121. package/agent/scripts/acp.package-publish.sh +420 -0
  122. package/agent/scripts/acp.package-remove.sh +348 -0
  123. package/agent/scripts/acp.package-search.sh +156 -0
  124. package/agent/scripts/acp.package-update.sh +517 -0
  125. package/agent/scripts/acp.package-validate.sh +1018 -0
  126. package/agent/scripts/acp.uninstall.sh +85 -0
  127. package/agent/scripts/acp.version-check-for-updates.sh +98 -0
  128. package/agent/scripts/acp.version-check.sh +47 -0
  129. package/agent/scripts/acp.version-update.sh +176 -0
  130. package/agent/scripts/acp.yaml-parser.sh +985 -0
  131. package/agent/scripts/acp.yaml-validate.sh +205 -0
  132. package/agent/tasks/.gitkeep +0 -0
  133. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
  134. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
  135. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
  136. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
  137. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
  138. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
  139. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
  140. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
  141. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
  142. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
  143. package/agent/tasks/task-1-{title}.template.md +244 -0
  144. package/bin/visualize.mjs +84 -0
  145. package/package.json +48 -0
  146. package/src/components/ExtraFieldsBadge.tsx +15 -0
  147. package/src/components/FilterBar.tsx +33 -0
  148. package/src/components/Header.tsx +23 -0
  149. package/src/components/MilestoneTable.tsx +167 -0
  150. package/src/components/MilestoneTree.tsx +84 -0
  151. package/src/components/ProgressBar.tsx +20 -0
  152. package/src/components/SearchInput.tsx +22 -0
  153. package/src/components/Sidebar.tsx +54 -0
  154. package/src/components/StatusBadge.tsx +23 -0
  155. package/src/components/StatusDot.tsx +12 -0
  156. package/src/components/TaskList.tsx +36 -0
  157. package/src/components/ViewToggle.tsx +31 -0
  158. package/src/lib/config.ts +8 -0
  159. package/src/lib/file-watcher.ts +43 -0
  160. package/src/lib/search.ts +48 -0
  161. package/src/lib/types.ts +73 -0
  162. package/src/lib/useAutoRefresh.ts +31 -0
  163. package/src/lib/useCollapse.ts +31 -0
  164. package/src/lib/useFilteredData.ts +55 -0
  165. package/src/lib/yaml-loader-real.spec.ts +47 -0
  166. package/src/lib/yaml-loader.spec.ts +201 -0
  167. package/src/lib/yaml-loader.ts +265 -0
  168. package/src/routeTree.gen.ts +140 -0
  169. package/src/router.tsx +10 -0
  170. package/src/routes/__root.tsx +75 -0
  171. package/src/routes/api/watch.ts +29 -0
  172. package/src/routes/index.tsx +115 -0
  173. package/src/routes/milestones.tsx +50 -0
  174. package/src/routes/search.tsx +84 -0
  175. package/src/routes/tasks.tsx +63 -0
  176. package/src/services/progress-database.service.ts +46 -0
  177. package/src/styles.css +25 -0
  178. package/tsconfig.json +24 -0
  179. package/vite.config.ts +16 -0
  180. package/vitest.config.ts +27 -0
@@ -0,0 +1,193 @@
1
+ # Task 3: Build Server API & Data Loading
2
+
3
+ **Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
4
+ **Design Reference**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
5
+ **Estimated Time**: 2 hours
6
+ **Dependencies**: Task 2
7
+ **Status**: Not Started
8
+
9
+ ---
10
+
11
+ ## Objective
12
+
13
+ Create the server-side data loading pipeline using TanStack Start server functions and the library-services pattern. The pipeline reads progress.yaml from disk, parses it through the lenient YAML parser, and delivers typed ProgressData to the client via SSR preloading.
14
+
15
+ ---
16
+
17
+ ## Context
18
+
19
+ TanStack Start supports server-side data loading via `beforeLoad` in route definitions, which enables SSR preloading so the dashboard renders with data on first paint (no loading spinner). The data loading follows the library-services pattern: a static service class (`ProgressDatabaseService`) encapsulates all data access logic, and routes call service methods in their `beforeLoad` hooks. The file path is configurable via environment variable, CLI argument, or default convention.
20
+
21
+ ---
22
+
23
+ ## Steps
24
+
25
+ ### 1. Create configuration module
26
+
27
+ Create `app/lib/config.ts` to resolve the progress.yaml file path:
28
+
29
+ ```typescript
30
+ // app/lib/config.ts
31
+
32
+ export function getProgressYamlPath(): string {
33
+ // Priority: env var > CLI arg > default
34
+ if (process.env.PROGRESS_YAML_PATH) {
35
+ return process.env.PROGRESS_YAML_PATH;
36
+ }
37
+
38
+ const cliArg = process.argv.find((arg) => arg.startsWith("--progress-yaml="));
39
+ if (cliArg) {
40
+ return cliArg.split("=")[1];
41
+ }
42
+
43
+ return "./agent/progress.yaml";
44
+ }
45
+ ```
46
+
47
+ ### 2. Create the ProgressDatabaseService
48
+
49
+ Create `app/services/progress-database.service.ts` following the library-services pattern (static methods, no instantiation):
50
+
51
+ ```typescript
52
+ // app/services/progress-database.service.ts
53
+ import fs from "node:fs";
54
+ import { getProgressYamlPath } from "../lib/config";
55
+ import { parseProgressYaml } from "../lib/yaml-loader";
56
+ import type { ProgressData } from "../lib/types";
57
+
58
+ export interface ProgressResult {
59
+ success: true;
60
+ data: ProgressData;
61
+ } | {
62
+ success: false;
63
+ error: "FILE_NOT_FOUND" | "PARSE_ERROR";
64
+ message: string;
65
+ }
66
+
67
+ export class ProgressDatabaseService {
68
+ static getProgressData(): ProgressResult {
69
+ const filePath = getProgressYamlPath();
70
+
71
+ try {
72
+ const raw = fs.readFileSync(filePath, "utf-8");
73
+ const data = parseProgressYaml(raw);
74
+ return { success: true, data };
75
+ } catch (err: unknown) {
76
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
77
+ return {
78
+ success: false,
79
+ error: "FILE_NOT_FOUND",
80
+ message: `progress.yaml not found at: ${filePath}`,
81
+ };
82
+ }
83
+ return {
84
+ success: false,
85
+ error: "PARSE_ERROR",
86
+ message: `Failed to parse progress.yaml: ${(err as Error).message}`,
87
+ };
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ### 3. Implement error handling with structured errors
94
+
95
+ The service returns a discriminated union (`ProgressResult`) so the UI can render appropriate error states:
96
+
97
+ - `FILE_NOT_FOUND`: Show a helpful message with the expected file path and how to configure it
98
+ - `PARSE_ERROR`: Show the error message so the user can fix the YAML
99
+
100
+ ### 4. Update the index route with SSR data preloading
101
+
102
+ Update `app/routes/index.tsx` to load data server-side using `beforeLoad`:
103
+
104
+ ```typescript
105
+ // app/routes/index.tsx
106
+ import { createFileRoute } from "@tanstack/react-router";
107
+ import { ProgressDatabaseService } from "../services/progress-database.service";
108
+
109
+ export const Route = createFileRoute("/")({
110
+ beforeLoad: async () => {
111
+ const result = ProgressDatabaseService.getProgressData();
112
+ return { progressResult: result };
113
+ },
114
+ component: HomePage,
115
+ });
116
+
117
+ function HomePage() {
118
+ const { progressResult } = Route.useRouteContext();
119
+
120
+ if (!progressResult.success) {
121
+ return (
122
+ <div className="p-8">
123
+ <h1 className="text-xl font-bold text-red-400">
124
+ Error: {progressResult.error}
125
+ </h1>
126
+ <p className="mt-2 text-gray-400 font-mono text-sm">
127
+ {progressResult.message}
128
+ </p>
129
+ </div>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <div className="p-8">
135
+ <h1 className="text-2xl font-bold">ACP Progress Visualizer</h1>
136
+ <pre className="mt-4 p-4 bg-gray-900 rounded text-xs font-mono overflow-auto max-h-[80vh]">
137
+ {JSON.stringify(progressResult.data, null, 2)}
138
+ </pre>
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ### 5. Access data via Route.useRouteContext()
145
+
146
+ The `beforeLoad` return value is available in the component via `Route.useRouteContext()`. This pattern ensures:
147
+
148
+ - Data is loaded server-side during SSR (no loading spinner)
149
+ - The component receives typed data
150
+ - Error states are handled before rendering
151
+
152
+ ### 6. Display raw JSON for pipeline verification
153
+
154
+ The initial implementation renders raw JSON of the parsed data. This serves as a visual verification that the entire pipeline works: file reading, YAML parsing, SSR delivery, and client rendering. This will be replaced with proper dashboard components in Milestone 2.
155
+
156
+ ---
157
+
158
+ ## Verification
159
+
160
+ - [ ] `app/lib/config.ts` exists and exports `getProgressYamlPath()`
161
+ - [ ] `app/services/progress-database.service.ts` exists and exports `ProgressDatabaseService`
162
+ - [ ] Page loads with SSR data (no loading spinner, data visible on first paint)
163
+ - [ ] Setting `PROGRESS_YAML_PATH` env var changes the file path
164
+ - [ ] Missing file shows structured FILE_NOT_FOUND error with the attempted path
165
+ - [ ] Malformed YAML shows PARSE_ERROR with error message
166
+ - [ ] Valid progress.yaml renders as formatted JSON on the page
167
+ - [ ] No client-side fetch waterfall (data arrives with SSR)
168
+
169
+ ---
170
+
171
+ ## Expected Output
172
+
173
+ **Key Files Created**:
174
+ - `app/lib/config.ts`: Configuration module for resolving progress.yaml path
175
+ - `app/services/progress-database.service.ts`: Service class for reading and parsing progress data
176
+
177
+ **Key Files Modified**:
178
+ - `app/routes/index.tsx`: Updated with `beforeLoad` SSR data preloading and JSON display
179
+
180
+ ---
181
+
182
+ ## Notes
183
+
184
+ - The library-services pattern uses static methods on a class rather than module-level functions. This provides a clear namespace and makes it easy to add methods later (e.g., `getProgressDataForMilestone`)
185
+ - The `beforeLoad` hook runs on the server during SSR, which means `fs.readFileSync` is safe to use here
186
+ - The raw JSON display is temporary and will be replaced by dashboard components in Milestone 2 tasks
187
+ - Consider adding caching to `ProgressDatabaseService` if performance becomes a concern (not needed for local dev tool)
188
+
189
+ ---
190
+
191
+ **Next Task**: [Task 4: Add Auto-Refresh via SSE](./task-4-add-auto-refresh-sse.md)
192
+ **Related Design Docs**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
193
+ **Estimated Completion Date**: TBD
@@ -0,0 +1,262 @@
1
+ # Task 4: Add Auto-Refresh via SSE
2
+
3
+ **Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
4
+ **Design Reference**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
5
+ **Estimated Time**: 2 hours
6
+ **Dependencies**: Task 3
7
+ **Status**: Not Started
8
+
9
+ ---
10
+
11
+ ## Objective
12
+
13
+ Implement file watching and Server-Sent Events (SSE) so the dashboard auto-refreshes when progress.yaml changes on disk. This enables a live development experience where agents updating progress.yaml see their changes reflected in the browser without manual reload.
14
+
15
+ ---
16
+
17
+ ## Context
18
+
19
+ The progress.yaml file is updated frequently by agents during work sessions. Rather than requiring manual browser refresh, the dashboard should detect file changes and re-render automatically. The implementation uses Node.js `fs.watch` on the server to detect changes, SSE to push notifications to the browser, and TanStack Router's `router.invalidate()` to trigger data re-fetching. This is a local development tool, so the SSE approach is appropriate (no need for WebSocket complexity or production-scale polling).
20
+
21
+ ---
22
+
23
+ ## Steps
24
+
25
+ ### 1. Create the file watcher module
26
+
27
+ Create `app/lib/file-watcher.ts` that manages file watching and connected SSE clients:
28
+
29
+ ```typescript
30
+ // app/lib/file-watcher.ts
31
+ import fs from "node:fs";
32
+ import { getProgressYamlPath } from "./config";
33
+
34
+ type ClientCallback = () => void;
35
+
36
+ let clients: Set<ClientCallback> = new Set();
37
+ let watcher: fs.FSWatcher | null = null;
38
+
39
+ export function createFileWatcher() {
40
+ if (watcher) return; // Already watching
41
+
42
+ const filePath = getProgressYamlPath();
43
+
44
+ try {
45
+ watcher = fs.watch(filePath, { persistent: false }, (eventType) => {
46
+ if (eventType === "change") {
47
+ // Notify all connected clients
48
+ for (const client of clients) {
49
+ client();
50
+ }
51
+ }
52
+ });
53
+
54
+ watcher.on("error", (err) => {
55
+ console.error("[file-watcher] Watch error:", err.message);
56
+ watcher?.close();
57
+ watcher = null;
58
+ });
59
+ } catch (err) {
60
+ console.error("[file-watcher] Failed to start watching:", (err as Error).message);
61
+ }
62
+ }
63
+
64
+ export function registerClient(callback: ClientCallback): () => void {
65
+ clients.add(callback);
66
+ // Return unregister function
67
+ return () => {
68
+ clients.delete(callback);
69
+ };
70
+ }
71
+
72
+ export function getClientCount(): number {
73
+ return clients.size;
74
+ }
75
+ ```
76
+
77
+ Key design decisions:
78
+ - Uses `fs.watch` (not `fs.watchFile`) for efficiency since this is a single known file path
79
+ - `persistent: false` so the watcher does not keep the Node process alive
80
+ - Singleton pattern: only one watcher instance regardless of how many clients connect
81
+ - Returns an unregister function for clean client removal
82
+
83
+ ### 2. Create the SSE API route
84
+
85
+ Create `app/routes/api/watch.ts` that serves the SSE endpoint:
86
+
87
+ ```typescript
88
+ // app/routes/api/watch.ts
89
+ import { createAPIFileRoute } from "@tanstack/react-start/api";
90
+ import { createFileWatcher, registerClient } from "../../lib/file-watcher";
91
+
92
+ export const APIRoute = createAPIFileRoute("/api/watch")({
93
+ GET: async () => {
94
+ // Ensure watcher is running
95
+ createFileWatcher();
96
+
97
+ const stream = new ReadableStream({
98
+ start(controller) {
99
+ const encoder = new TextEncoder();
100
+
101
+ // Send initial connection confirmation
102
+ controller.enqueue(encoder.encode("data: connected\n\n"));
103
+
104
+ // Register for file change notifications
105
+ const unregister = registerClient(() => {
106
+ try {
107
+ controller.enqueue(encoder.encode("data: refresh\n\n"));
108
+ } catch {
109
+ // Client disconnected
110
+ unregister();
111
+ }
112
+ });
113
+
114
+ // Handle client disconnect (stream cancellation)
115
+ // Note: cleanup is handled when enqueue throws
116
+ },
117
+ });
118
+
119
+ return new Response(stream, {
120
+ headers: {
121
+ "Content-Type": "text/event-stream",
122
+ "Cache-Control": "no-cache",
123
+ Connection: "keep-alive",
124
+ },
125
+ });
126
+ },
127
+ });
128
+ ```
129
+
130
+ ### 3. Create the useAutoRefresh hook
131
+
132
+ Create `app/lib/useAutoRefresh.ts` that connects to the SSE endpoint and triggers router invalidation:
133
+
134
+ ```typescript
135
+ // app/lib/useAutoRefresh.ts
136
+ import { useEffect } from "react";
137
+ import { useRouter } from "@tanstack/react-router";
138
+
139
+ export function useAutoRefresh() {
140
+ const router = useRouter();
141
+
142
+ useEffect(() => {
143
+ const eventSource = new EventSource("/api/watch");
144
+
145
+ eventSource.onmessage = (event) => {
146
+ if (event.data === "refresh") {
147
+ // Invalidate all route loaders, triggering re-fetch
148
+ router.invalidate();
149
+ }
150
+ };
151
+
152
+ eventSource.onerror = () => {
153
+ // EventSource will automatically reconnect on error
154
+ // (built-in browser behavior)
155
+ console.warn("[auto-refresh] SSE connection lost, reconnecting...");
156
+ };
157
+
158
+ return () => {
159
+ eventSource.close();
160
+ };
161
+ }, [router]);
162
+ }
163
+ ```
164
+
165
+ Key behaviors:
166
+ - `EventSource` has built-in reconnection on connection loss (browser standard behavior)
167
+ - `router.invalidate()` re-runs all `beforeLoad` hooks, which re-reads and re-parses progress.yaml
168
+ - Cleanup on unmount prevents memory leaks
169
+
170
+ ### 4. Add useAutoRefresh to root layout
171
+
172
+ Update `app/routes/__root.tsx` to activate auto-refresh on all pages:
173
+
174
+ ```typescript
175
+ // app/routes/__root.tsx
176
+ import { createRootRoute, Outlet } from "@tanstack/react-router";
177
+ import { useAutoRefresh } from "../lib/useAutoRefresh";
178
+
179
+ export const Route = createRootRoute({
180
+ component: RootLayout,
181
+ });
182
+
183
+ function RootLayout() {
184
+ useAutoRefresh();
185
+
186
+ return (
187
+ <div className="dark min-h-screen bg-gray-950 text-gray-100">
188
+ <Outlet />
189
+ </div>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ### 5. Test the auto-refresh flow
195
+
196
+ End-to-end verification:
197
+
198
+ 1. Start the dev server with `npm run dev`
199
+ 2. Open the dashboard in a browser
200
+ 3. Verify the SSE connection is established (check Network tab for `/api/watch` request with `text/event-stream` type)
201
+ 4. Modify the project's `progress.yaml` file (e.g., change a task status)
202
+ 5. Verify the browser updates automatically without manual refresh
203
+ 6. Check that there are no errors in the browser console or server logs
204
+
205
+ ---
206
+
207
+ ## Verification
208
+
209
+ - [ ] `app/lib/file-watcher.ts` exists and exports `createFileWatcher`, `registerClient`, `getClientCount`
210
+ - [ ] `app/routes/api/watch.ts` exists and serves SSE responses
211
+ - [ ] `app/lib/useAutoRefresh.ts` exists and exports `useAutoRefresh` hook
212
+ - [ ] SSE endpoint responds with `Content-Type: text/event-stream`
213
+ - [ ] SSE endpoint sends `data: connected` on initial connection
214
+ - [ ] Modifying progress.yaml triggers `data: refresh` SSE event
215
+ - [ ] Browser re-renders with updated data after file change
216
+ - [ ] EventSource reconnects automatically on connection loss (verify by restarting server)
217
+ - [ ] No errors in browser console during normal operation
218
+ - [ ] Root layout includes `useAutoRefresh()` call
219
+
220
+ ---
221
+
222
+ ## Expected Output
223
+
224
+ **Key Files Created**:
225
+ - `app/lib/file-watcher.ts`: Node.js file watcher with client management
226
+ - `app/routes/api/watch.ts`: SSE API endpoint for pushing file change notifications
227
+ - `app/lib/useAutoRefresh.ts`: React hook that connects to SSE and triggers router invalidation
228
+
229
+ **Key Files Modified**:
230
+ - `app/routes/__root.tsx`: Updated to include `useAutoRefresh()` hook
231
+
232
+ ---
233
+
234
+ ## Common Issues and Solutions
235
+
236
+ ### Issue 1: fs.watch fires multiple events for a single save
237
+ **Symptom**: Dashboard refreshes multiple times when progress.yaml is saved once
238
+ **Solution**: Add a debounce (e.g., 100ms) to the file watcher notification. Ignore events that fire within the debounce window of the last event.
239
+
240
+ ### Issue 2: fs.watch not available or not working on certain filesystems
241
+ **Symptom**: File changes are not detected
242
+ **Solution**: Fall back to `fs.watchFile` (polling-based) if `fs.watch` fails. Log a warning about reduced performance.
243
+
244
+ ### Issue 3: SSE connection shows as pending indefinitely in DevTools
245
+ **Symptom**: Network tab shows `/api/watch` request stuck in "pending" state
246
+ **Solution**: This is expected behavior for SSE connections. The connection stays open to receive events. It is not an error.
247
+
248
+ ---
249
+
250
+ ## Notes
251
+
252
+ - This is a local development tool, so SSE is appropriate. For production, consider WebSocket or polling with ETags
253
+ - The file watcher is a singleton: multiple browser tabs share the same watcher but each has its own SSE connection
254
+ - `fs.watch` behavior varies by OS; test on the target platform (Linux with inotify is reliable)
255
+ - Consider adding a debounce to the file watcher if double-fires become an issue during testing
256
+ - The `useAutoRefresh` hook is placed in the root layout so it works across all routes, including future milestone and task detail pages
257
+
258
+ ---
259
+
260
+ **Next Task**: [Milestone 2 Tasks](../milestone-2-dashboard-views-interaction/)
261
+ **Related Design Docs**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
262
+ **Estimated Completion Date**: TBD
@@ -0,0 +1,156 @@
1
+ # Task 10: Polish & Integration Testing
2
+
3
+ **Milestone**: [M2 - Dashboard Views & Interaction](../../milestones/milestone-2-dashboard-views-interaction.md)
4
+ **Design Reference**: [Visualizer Requirements](../../design/local.visualizer-requirements.md)
5
+ **Estimated Time**: 2 hours
6
+ **Dependencies**: Task 9
7
+ **Status**: Not Started
8
+
9
+ ---
10
+
11
+ ## Objective
12
+
13
+ Final polish pass — ensure all views work together, error states are handled, empty states are graceful, and the full data flow works end-to-end. This task validates that all M2 features are functional and ready for use.
14
+
15
+ ---
16
+
17
+ ## Context
18
+
19
+ Tasks 5–9 build individual features (layout, overview, table, tree, search/filter) in isolation. This task validates they work together as a cohesive application. It focuses on integration testing, edge cases, error handling, and visual polish. Real-world progress.yaml files can be large (1800+ lines) and may contain unexpected data shapes, so testing with real data is critical. This is the final task in Milestone 2 and should result in a fully functional dashboard.
20
+
21
+ ---
22
+
23
+ ## Steps
24
+
25
+ ### 1. Test full data flow end-to-end
26
+
27
+ Verify the complete data pipeline works across all views:
28
+
29
+ - `progress.yaml` file is read from configured path
30
+ - YAML is parsed into typed ProgressData
31
+ - Data is served via SSR (server function → route loader)
32
+ - Overview page renders project metadata, milestones, next steps, blockers
33
+ - Milestones page renders table view with all milestones and tasks
34
+ - Milestones page renders tree view when toggled
35
+ - Search page finds and displays matching results
36
+ - Auto-refresh updates all views when progress.yaml changes
37
+
38
+ ### 2. Verify error states
39
+
40
+ Test error handling for common failure scenarios:
41
+
42
+ - **File not found**: When `PROGRESS_YAML_PATH` points to a non-existent file, display a configuration prompt explaining how to set the path
43
+ - **Parse error**: When progress.yaml contains invalid YAML, display a helpful error message showing the parse error details
44
+ - **Missing fields**: When progress.yaml is valid but missing expected fields, degrade gracefully (show available data, skip missing sections)
45
+
46
+ ### 3. Verify empty states
47
+
48
+ Test empty data scenarios:
49
+
50
+ - **No milestones**: Overview and milestones pages show "No milestones found" message
51
+ - **No tasks in milestone**: Expanded milestone row shows "No tasks" message
52
+ - **No search results**: Search page shows "No results found for '{query}'" message
53
+ - **No blockers**: BlockersDisplay renders nothing (no empty card)
54
+ - **No next steps**: NextSteps renders nothing (no empty card)
55
+
56
+ ### 4. Verify auto-refresh across all views
57
+
58
+ - File watcher detects changes to progress.yaml
59
+ - All currently visible views update with new data
60
+ - Expanded state in table/tree views is preserved after refresh
61
+ - Search/filter state is preserved after refresh
62
+ - No flicker or layout shift during refresh
63
+
64
+ ### 5. Test with real progress.yaml data
65
+
66
+ Load a real-world progress.yaml file (e.g., from ACP core project, 1800+ lines):
67
+
68
+ - Verify all milestones and tasks render without errors
69
+ - Verify performance is acceptable (no lag when scrolling, sorting, searching)
70
+ - Verify ExtraFieldsBadge shows correct counts for tasks with custom fields
71
+ - Verify long text (names, notes) truncates gracefully
72
+ - Verify large numbers of tasks don't break layout
73
+
74
+ ### 6. Fix styling inconsistencies
75
+
76
+ Review all views for visual polish:
77
+
78
+ - Consistent spacing between sections (space-y-6)
79
+ - Consistent card styling (bg-gray-900/50, border-gray-800, rounded-xl)
80
+ - Consistent text hierarchy (headings, subheadings, body, captions)
81
+ - Hover states on interactive elements (nav links, table rows, buttons)
82
+ - Focus states for keyboard navigation (search input, filter buttons)
83
+ - No orphaned borders, extra padding, or alignment issues
84
+
85
+ ### 7. Verify route navigation
86
+
87
+ Test all navigation paths:
88
+
89
+ - Sidebar links navigate to correct pages
90
+ - Browser back/forward navigation works
91
+ - Direct URL access works (e.g., navigating directly to `/milestones`)
92
+ - Active nav item in sidebar matches current route
93
+ - No 404 or blank pages for any defined route
94
+
95
+ ### 8. Test edge cases
96
+
97
+ Verify the app handles unusual data shapes:
98
+
99
+ - Single milestone with no tasks
100
+ - Single task with no notes
101
+ - Milestone with many tasks (20+)
102
+ - Task with many extra fields (10+)
103
+ - Very long milestone/task names (100+ characters)
104
+ - Special characters in names and notes (quotes, angle brackets, unicode)
105
+ - Status values beyond the standard set (should render as gray/unknown)
106
+
107
+ ---
108
+
109
+ ## Verification
110
+
111
+ - [ ] All P0 features from requirements are functional (overview, table, tree, search, filter)
112
+ - [ ] No console errors or warnings in browser developer tools
113
+ - [ ] All views render correctly with real progress.yaml data (1800+ lines)
114
+ - [ ] Error state: file-not-found shows configuration prompt
115
+ - [ ] Error state: parse error shows helpful message with details
116
+ - [ ] Empty state: no milestones shows appropriate message
117
+ - [ ] Empty state: no search results shows appropriate message
118
+ - [ ] Auto-refresh works across overview, table, tree, and search views
119
+ - [ ] Search finds milestones and tasks by name with fuzzy matching
120
+ - [ ] Status filter narrows results correctly
121
+ - [ ] All sidebar navigation links work correctly
122
+ - [ ] Browser back/forward navigation works
123
+ - [ ] No styling inconsistencies or visual bugs
124
+ - [ ] Edge cases handled: long text, special characters, missing fields, many items
125
+
126
+ ---
127
+
128
+ ## Expected Output
129
+
130
+ **Key Files Modified**:
131
+ - Bug fixes and polish across existing files created in Tasks 5–9
132
+ - No major new files expected — this is a testing and polish task
133
+
134
+ **Potential files touched**:
135
+ - `app/routes/__root.tsx` — error boundary, layout adjustments
136
+ - `app/routes/index.tsx` — empty state handling
137
+ - `app/routes/milestones.tsx` — error/empty states
138
+ - `app/routes/search.tsx` — empty state messaging
139
+ - `app/components/*.tsx` — styling fixes, edge case handling
140
+ - `app/lib/*.ts` — search/filter edge case fixes
141
+
142
+ ---
143
+
144
+ ## Notes
145
+
146
+ - This is a testing and polish task, not a feature-building task — the goal is quality, not new functionality
147
+ - Keep a list of issues found and fixed for the commit message
148
+ - If any P0 feature is broken or missing, fix it before marking this task complete
149
+ - Performance testing with large files is important — if search or rendering is slow, optimize before closing
150
+ - This task marks the end of Milestone 2; after completion, the dashboard should be fully usable for viewing progress.yaml data
151
+
152
+ ---
153
+
154
+ **Next Task**: Milestone 3 tasks (TBD)
155
+ **Related Design Docs**: [Visualizer Requirements](../../design/local.visualizer-requirements.md)
156
+ **Estimated Completion Date**: TBD