@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,205 @@
1
+ #!/bin/bash
2
+ # ACP YAML Schema Validator
3
+ # Pure bash YAML validation against schema definitions
4
+ # Zero external dependencies
5
+
6
+ # Source YAML parser (using new generic AST-based parser)
7
+ SCRIPT_DIR="$(dirname "$0")"
8
+ . "${SCRIPT_DIR}/acp.yaml-parser.sh"
9
+ . "${SCRIPT_DIR}/acp.common.sh"
10
+
11
+ # Initialize colors
12
+ init_colors
13
+
14
+ # Validation error tracking
15
+ VALIDATION_ERRORS=0
16
+ VALIDATION_WARNINGS=0
17
+
18
+ # Add validation error
19
+ # Usage: add_error "Error message"
20
+ add_error() {
21
+ echo "${RED}❌ $1${NC}" >&2
22
+ VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
23
+ }
24
+
25
+ # Add validation warning
26
+ # Usage: add_warning "Warning message"
27
+ add_warning() {
28
+ echo "${YELLOW}⚠️ $1${NC}" >&2
29
+ VALIDATION_WARNINGS=$((VALIDATION_WARNINGS + 1))
30
+ }
31
+
32
+ # Validate field exists
33
+ # Usage: validate_field_exists "file.yaml" "field.path"
34
+ validate_field_exists() {
35
+ local yaml_file="$1"
36
+ local field_path="$2"
37
+
38
+ if ! yaml_has_key "$yaml_file" "$field_path"; then
39
+ return 1
40
+ fi
41
+ return 0
42
+ }
43
+
44
+ # Validate string pattern (regex)
45
+ # Usage: validate_pattern "value" "pattern" "field_name"
46
+ validate_pattern() {
47
+ local value="$1"
48
+ local pattern="$2"
49
+ local field_name="$3"
50
+
51
+ if ! echo "$value" | grep -qE "$pattern"; then
52
+ return 1
53
+ fi
54
+ return 0
55
+ }
56
+
57
+ # Validate string length
58
+ # Usage: validate_length "value" min max "field_name"
59
+ validate_length() {
60
+ local value="$1"
61
+ local min="$2"
62
+ local max="$3"
63
+ local field_name="$4"
64
+
65
+ local length=${#value}
66
+
67
+ if [ -n "$min" ] && [ "$length" -lt "$min" ]; then
68
+ add_error "Field '$field_name': Too short (minimum $min characters, got $length)"
69
+ return 1
70
+ fi
71
+
72
+ if [ -n "$max" ] && [ "$length" -gt "$max" ]; then
73
+ add_error "Field '$field_name': Too long (maximum $max characters, got $length)"
74
+ return 1
75
+ fi
76
+
77
+ return 0
78
+ }
79
+
80
+ # Validate package.yaml file
81
+ # Usage: validate_package_yaml "package.yaml"
82
+ # Returns: 0 if valid, 1 if invalid
83
+ validate_package_yaml() {
84
+ local yaml_file="$1"
85
+
86
+ if [ ! -f "$yaml_file" ]; then
87
+ add_error "File not found: $yaml_file"
88
+ return 1
89
+ fi
90
+
91
+ echo "${BLUE}Validating $yaml_file...${NC}"
92
+ echo ""
93
+
94
+ # Check YAML syntax (try to parse)
95
+ if ! yaml_get "$yaml_file" "name" >/dev/null 2>&1; then
96
+ add_error "Invalid YAML syntax in $yaml_file"
97
+ return 1
98
+ fi
99
+
100
+ # Validate required fields
101
+ local required_fields="name version description author license repository"
102
+ for field in $required_fields; do
103
+ if ! validate_field_exists "$yaml_file" "$field"; then
104
+ add_error "Required field missing: '$field'"
105
+ fi
106
+ done
107
+
108
+ # Validate name field
109
+ if validate_field_exists "$yaml_file" "name"; then
110
+ local name=$(yaml_get "$yaml_file" "name")
111
+ if ! validate_pattern "$name" "^[a-z0-9-]+$" "name"; then
112
+ add_error "Field 'name': Must be lowercase letters, numbers, and hyphens only (got: '$name')"
113
+ fi
114
+
115
+ # Check reserved names
116
+ case "$name" in
117
+ acp|local|core|system|global)
118
+ add_error "Field 'name': '$name' is a reserved package name"
119
+ ;;
120
+ esac
121
+ fi
122
+
123
+ # Validate version field
124
+ if validate_field_exists "$yaml_file" "version"; then
125
+ local version=$(yaml_get "$yaml_file" "version")
126
+ if ! validate_pattern "$version" "^[0-9]+\\.[0-9]+\\.[0-9]+$" "version"; then
127
+ add_error "Field 'version': Must be semantic version format X.Y.Z (got: '$version')"
128
+ fi
129
+ fi
130
+
131
+ # Validate description field
132
+ if validate_field_exists "$yaml_file" "description"; then
133
+ local description=$(yaml_get "$yaml_file" "description")
134
+ validate_length "$description" 10 200 "description"
135
+ fi
136
+
137
+ # Validate author field
138
+ if validate_field_exists "$yaml_file" "author"; then
139
+ local author=$(yaml_get "$yaml_file" "author")
140
+ validate_length "$author" 2 "" "author"
141
+ fi
142
+
143
+ # Validate repository field
144
+ if validate_field_exists "$yaml_file" "repository"; then
145
+ local repository=$(yaml_get "$yaml_file" "repository")
146
+ if ! validate_pattern "$repository" "^https?://.*\\.git$" "repository"; then
147
+ add_error "Field 'repository': Must be a git URL ending with .git (got: '$repository')"
148
+ fi
149
+ fi
150
+
151
+ # Validate homepage field (optional)
152
+ if validate_field_exists "$yaml_file" "homepage"; then
153
+ local homepage=$(yaml_get "$yaml_file" "homepage")
154
+ if ! validate_pattern "$homepage" "^https?://.*" "homepage"; then
155
+ add_error "Field 'homepage': Must be a valid HTTP/HTTPS URL (got: '$homepage')"
156
+ fi
157
+ fi
158
+
159
+ # Validate contents field (required) - use grep since yaml_has_key may not work for nested objects
160
+ if ! grep -q "^contents:" "$yaml_file"; then
161
+ add_error "Required field missing: 'contents'"
162
+ fi
163
+
164
+ # Validate requires.acp field (optional)
165
+ if validate_field_exists "$yaml_file" "requires.acp"; then
166
+ local acp_version=$(yaml_get "$yaml_file" "requires.acp")
167
+ if ! validate_pattern "$acp_version" "^>=?[0-9]+\\.[0-9]+\\.[0-9]+$" "requires.acp"; then
168
+ add_error "Field 'requires.acp': Must be version constraint like '>=2.0.0' (got: '$acp_version')"
169
+ fi
170
+ fi
171
+
172
+ # Report results
173
+ echo ""
174
+ if [ "$VALIDATION_ERRORS" -eq 0 ]; then
175
+ echo "${GREEN}✅ Validation passed${NC}"
176
+ if [ "$VALIDATION_WARNINGS" -gt 0 ]; then
177
+ echo "${YELLOW}⚠️ $VALIDATION_WARNINGS warning(s)${NC}"
178
+ fi
179
+ return 0
180
+ else
181
+ echo "${RED}❌ Validation failed${NC}"
182
+ echo "${RED} $VALIDATION_ERRORS error(s)${NC}"
183
+ if [ "$VALIDATION_WARNINGS" -gt 0 ]; then
184
+ echo "${YELLOW} $VALIDATION_WARNINGS warning(s)${NC}"
185
+ fi
186
+ return 1
187
+ fi
188
+ }
189
+
190
+ # Main function for standalone execution
191
+ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
192
+ # Script is being executed directly
193
+ if [ $# -eq 0 ]; then
194
+ echo "Usage: $0 <yaml-file>"
195
+ echo ""
196
+ echo "Example:"
197
+ echo " $0 package.yaml"
198
+ exit 1
199
+ fi
200
+
201
+ validate_package_yaml "$1"
202
+ exit $?
203
+ fi
204
+
205
+ # Script is being sourced, functions are available
File without changes
@@ -0,0 +1,210 @@
1
+ # Task 1: Initialize TanStack Start Project
2
+
3
+ **Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
4
+ **Design Reference**: [Dashboard Layout & Routing](../../design/local.dashboard-layout-routing.md)
5
+ **Estimated Time**: 2 hours
6
+ **Dependencies**: None
7
+ **Status**: Not Started
8
+
9
+ ---
10
+
11
+ ## Objective
12
+
13
+ Scaffold a TanStack Start project with Vite, React, TanStack Router, and Tailwind CSS. Configure design tokens for the admin dashboard theme. This establishes the foundational build toolchain and styling system that all subsequent tasks build upon.
14
+
15
+ ---
16
+
17
+ ## Context
18
+
19
+ The ACP Progress Visualizer is a TanStack Start application that renders progress.yaml data as an interactive admin dashboard. This first task creates the project skeleton with all P0 dependencies installed and Tailwind configured with the project's design token palette (status colors, typography, dark theme). Without this foundation, no UI or data pipeline work can proceed.
20
+
21
+ ---
22
+
23
+ ## Steps
24
+
25
+ ### 1. Scaffold the TanStack Start project
26
+
27
+ Run the TanStack Start scaffolding tool or manually initialize the project structure:
28
+
29
+ ```bash
30
+ npm create @tanstack/start@latest
31
+ ```
32
+
33
+ If scaffolding manually, create a `package.json` with the project name and set `"type": "module"`.
34
+
35
+ ### 2. Install dependencies
36
+
37
+ Install all P0 dependencies required for the visualizer:
38
+
39
+ ```bash
40
+ npm install @tanstack/react-router @tanstack/react-start @tanstack/react-table react react-dom
41
+ npm install tailwindcss postcss autoprefixer lucide-react js-yaml fuse.js
42
+ npm install -D @types/react @types/react-dom @types/js-yaml typescript vite
43
+ ```
44
+
45
+ ### 3. Configure Tailwind with custom design tokens
46
+
47
+ Initialize Tailwind and configure the theme in `tailwind.config.ts`:
48
+
49
+ ```bash
50
+ npx tailwindcss init -p --ts
51
+ ```
52
+
53
+ Add custom design tokens to the Tailwind config:
54
+
55
+ ```typescript
56
+ // tailwind.config.ts
57
+ import type { Config } from "tailwindcss";
58
+
59
+ export default {
60
+ content: ["./app/**/*.{ts,tsx}"],
61
+ darkMode: "class",
62
+ theme: {
63
+ extend: {
64
+ colors: {
65
+ status: {
66
+ completed: "#22c55e", // green-500
67
+ in_progress: "#3b82f6", // blue-500
68
+ not_started: "#6b7280", // gray-500
69
+ blocked: "#ef4444", // red-500
70
+ skipped: "#a855f7", // purple-500
71
+ },
72
+ },
73
+ fontFamily: {
74
+ sans: ["Inter", "system-ui", "sans-serif"],
75
+ mono: ["JetBrains Mono", "monospace"],
76
+ },
77
+ },
78
+ },
79
+ plugins: [],
80
+ } satisfies Config;
81
+ ```
82
+
83
+ ### 4. Create the root layout
84
+
85
+ Create `app/routes/__root.tsx` with a minimal root layout that includes the dark theme class and global styles:
86
+
87
+ ```typescript
88
+ // app/routes/__root.tsx
89
+ import { createRootRoute, Outlet } from "@tanstack/react-router";
90
+
91
+ export const Route = createRootRoute({
92
+ component: RootLayout,
93
+ });
94
+
95
+ function RootLayout() {
96
+ return (
97
+ <div className="dark min-h-screen bg-gray-950 text-gray-100">
98
+ <Outlet />
99
+ </div>
100
+ );
101
+ }
102
+ ```
103
+
104
+ ### 5. Create the index route
105
+
106
+ Create `app/routes/index.tsx` with placeholder content to verify the app renders:
107
+
108
+ ```typescript
109
+ // app/routes/index.tsx
110
+ import { createFileRoute } from "@tanstack/react-router";
111
+
112
+ export const Route = createFileRoute("/")({
113
+ component: HomePage,
114
+ });
115
+
116
+ function HomePage() {
117
+ return (
118
+ <div className="p-8">
119
+ <h1 className="text-2xl font-bold font-sans">
120
+ ACP Progress Visualizer
121
+ </h1>
122
+ <p className="mt-2 text-gray-400 font-mono text-sm">
123
+ Dashboard loading...
124
+ </p>
125
+ </div>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ### 6. Create TanStack Start configuration
131
+
132
+ Create `app.config.ts` for TanStack Start:
133
+
134
+ ```typescript
135
+ // app.config.ts
136
+ import { defineConfig } from "@tanstack/react-start/config";
137
+ import viteTsConfigPaths from "vite-tsconfig-paths";
138
+
139
+ export default defineConfig({
140
+ vite: {
141
+ plugins: [viteTsConfigPaths()],
142
+ },
143
+ });
144
+ ```
145
+
146
+ ### 7. Verify dev server starts
147
+
148
+ Run the development server and confirm the app loads without errors:
149
+
150
+ ```bash
151
+ npm run dev
152
+ ```
153
+
154
+ Open the browser to the local dev URL and verify the placeholder content renders with correct fonts and dark theme styling.
155
+
156
+ ---
157
+
158
+ ## Verification
159
+
160
+ - [ ] `package.json` exists with all P0 dependencies listed
161
+ - [ ] `tailwind.config.ts` exists and contains status color tokens (completed, in_progress, not_started, blocked, skipped)
162
+ - [ ] `tailwind.config.ts` specifies Inter for sans and JetBrains Mono for mono font families
163
+ - [ ] `app/routes/__root.tsx` exists with dark theme wrapper and `<Outlet />`
164
+ - [ ] `app/routes/index.tsx` exists with placeholder content
165
+ - [ ] `app.config.ts` exists with TanStack Start configuration
166
+ - [ ] `npm run dev` starts without errors
167
+ - [ ] Root route renders in the browser with dark background and light text
168
+
169
+ ---
170
+
171
+ ## Expected Output
172
+
173
+ **File Structure**:
174
+ ```
175
+ project-root/
176
+ ├── app/
177
+ │ ├── routes/
178
+ │ │ ├── __root.tsx
179
+ │ │ └── index.tsx
180
+ │ └── styles/
181
+ │ └── globals.css
182
+ ├── app.config.ts
183
+ ├── tailwind.config.ts
184
+ ├── postcss.config.js
185
+ ├── tsconfig.json
186
+ ├── package.json
187
+ └── package-lock.json
188
+ ```
189
+
190
+ **Key Files Created**:
191
+ - `package.json`: Project manifest with all P0 dependencies
192
+ - `tailwind.config.ts`: Tailwind configuration with status colors and typography tokens
193
+ - `app/routes/__root.tsx`: Root layout with dark theme wrapper
194
+ - `app/routes/index.tsx`: Index route with placeholder content
195
+ - `app.config.ts`: TanStack Start build configuration
196
+
197
+ ---
198
+
199
+ ## Notes
200
+
201
+ - Dark theme is the default and only theme for this project; no light mode toggle is planned
202
+ - The `lucide-react` icon library is installed now but used in later tasks for sidebar and status icons
203
+ - `fuse.js` and `@tanstack/react-table` are installed now to avoid dependency churn in later milestones
204
+ - If `npm create @tanstack/start@latest` output differs from expected, adjust file locations to match the scaffolding output
205
+
206
+ ---
207
+
208
+ **Next Task**: [Task 2: Implement Data Model & YAML Parser](./task-2-implement-data-model-yaml-parser.md)
209
+ **Related Design Docs**: [Dashboard Layout & Routing](../../design/local.dashboard-layout-routing.md)
210
+ **Estimated Completion Date**: TBD
@@ -0,0 +1,294 @@
1
+ # Task 2: Implement Data Model & YAML Parser
2
+
3
+ **Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
4
+ **Design Reference**: [Data Model & YAML Parsing](../../design/local.data-model-yaml-parsing.md)
5
+ **Estimated Time**: 3 hours
6
+ **Dependencies**: Task 1
7
+ **Status**: Not Started
8
+
9
+ ---
10
+
11
+ ## Objective
12
+
13
+ Create TypeScript interfaces for progress.yaml data and a lenient YAML parser that handles agent-maintained YAML with drift tolerance. The parser must gracefully handle missing fields, unknown keys, status aliases, and type coercion so the dashboard never crashes on imperfect YAML.
14
+
15
+ ---
16
+
17
+ ## Context
18
+
19
+ The progress.yaml file is maintained by AI agents and tends to drift from a strict schema over time. Fields get renamed, statuses use informal terms ("done" instead of "completed"), single strings appear where arrays are expected, and unknown fields accumulate. The parser must be maximally tolerant: extract what it can, preserve unknown fields in an `extra` bag, normalize statuses and field names, and return safe defaults for anything missing. This approach ensures the dashboard always renders something useful rather than crashing on schema violations.
20
+
21
+ ---
22
+
23
+ ## Steps
24
+
25
+ ### 1. Create TypeScript interfaces
26
+
27
+ Create `app/lib/types.ts` with all data model interfaces:
28
+
29
+ ```typescript
30
+ // app/lib/types.ts
31
+
32
+ export type Status = "completed" | "in_progress" | "not_started" | "blocked" | "skipped";
33
+
34
+ export interface ExtraFields {
35
+ [key: string]: unknown;
36
+ }
37
+
38
+ export interface ProjectMetadata {
39
+ name: string;
40
+ description: string;
41
+ version: string;
42
+ repository: string;
43
+ extra: ExtraFields;
44
+ }
45
+
46
+ export interface WorkEntry {
47
+ date: string;
48
+ task: string;
49
+ hours: number;
50
+ notes: string;
51
+ extra: ExtraFields;
52
+ }
53
+
54
+ export interface Task {
55
+ id: string;
56
+ title: string;
57
+ status: Status;
58
+ estimated_hours: number;
59
+ actual_hours: number;
60
+ dependencies: string[];
61
+ tags: string[];
62
+ notes: string;
63
+ extra: ExtraFields;
64
+ }
65
+
66
+ export interface Milestone {
67
+ id: string;
68
+ title: string;
69
+ status: Status;
70
+ target_date: string;
71
+ tasks: Task[];
72
+ extra: ExtraFields;
73
+ }
74
+
75
+ export interface DocumentationStats {
76
+ design_docs: number;
77
+ task_docs: number;
78
+ patterns: number;
79
+ clarifications: number;
80
+ extra: ExtraFields;
81
+ }
82
+
83
+ export interface ProgressSummary {
84
+ total_tasks: number;
85
+ completed_tasks: number;
86
+ completion_percentage: number;
87
+ total_hours_estimated: number;
88
+ total_hours_actual: number;
89
+ extra: ExtraFields;
90
+ }
91
+
92
+ export interface ProgressData {
93
+ project: ProjectMetadata;
94
+ milestones: Milestone[];
95
+ work_entries: WorkEntry[];
96
+ documentation: DocumentationStats;
97
+ progress: ProgressSummary;
98
+ extra: ExtraFields;
99
+ }
100
+ ```
101
+
102
+ ### 2. Create the YAML loader module
103
+
104
+ Create `app/lib/yaml-loader.ts` with the main `parseProgressYaml` function:
105
+
106
+ ```typescript
107
+ // app/lib/yaml-loader.ts
108
+ import yaml from "js-yaml";
109
+ import type { ProgressData } from "./types";
110
+
111
+ export function parseProgressYaml(raw: string): ProgressData {
112
+ // Implementation in subsequent steps
113
+ }
114
+ ```
115
+
116
+ ### 3. Implement the `extractKnown` helper
117
+
118
+ This utility separates known fields from extras, which is the foundation of drift tolerance:
119
+
120
+ ```typescript
121
+ function extractKnown<T extends Record<string, unknown>>(
122
+ obj: Record<string, unknown>,
123
+ knownKeys: string[]
124
+ ): { known: Partial<T>; extra: Record<string, unknown> } {
125
+ const known: Record<string, unknown> = {};
126
+ const extra: Record<string, unknown> = {};
127
+ for (const [key, value] of Object.entries(obj)) {
128
+ if (knownKeys.includes(key)) {
129
+ known[key] = value;
130
+ } else {
131
+ extra[key] = value;
132
+ }
133
+ }
134
+ return { known: known as Partial<T>, extra };
135
+ }
136
+ ```
137
+
138
+ ### 4. Implement `normalizeStatus`
139
+
140
+ Map informal status strings to canonical Status values using fuzzy matching:
141
+
142
+ ```typescript
143
+ function normalizeStatus(raw: unknown): Status {
144
+ if (typeof raw !== "string") return "not_started";
145
+ const s = raw.toLowerCase().trim().replace(/[\s-]/g, "_");
146
+ const aliases: Record<string, Status> = {
147
+ done: "completed",
148
+ complete: "completed",
149
+ completed: "completed",
150
+ finished: "completed",
151
+ active: "in_progress",
152
+ wip: "in_progress",
153
+ in_progress: "in_progress",
154
+ working: "in_progress",
155
+ started: "in_progress",
156
+ pending: "not_started",
157
+ not_started: "not_started",
158
+ todo: "not_started",
159
+ queued: "not_started",
160
+ blocked: "blocked",
161
+ stuck: "blocked",
162
+ waiting: "blocked",
163
+ skipped: "skipped",
164
+ dropped: "skipped",
165
+ deferred: "skipped",
166
+ };
167
+ return aliases[s] ?? "not_started";
168
+ }
169
+ ```
170
+
171
+ ### 5. Implement key alias maps
172
+
173
+ Create alias maps so renamed fields still parse correctly:
174
+
175
+ ```typescript
176
+ const TASK_KEY_ALIASES: Record<string, string> = {
177
+ est_hours: "estimated_hours",
178
+ estimated: "estimated_hours",
179
+ actual: "actual_hours",
180
+ act_hours: "actual_hours",
181
+ deps: "dependencies",
182
+ depends_on: "dependencies",
183
+ name: "title",
184
+ };
185
+
186
+ const MILESTONE_KEY_ALIASES: Record<string, string> = {
187
+ name: "title",
188
+ target: "target_date",
189
+ due_date: "target_date",
190
+ deadline: "target_date",
191
+ };
192
+ ```
193
+
194
+ ### 6. Implement `normalizeStringArray`
195
+
196
+ Handle cases where a single string appears instead of an array:
197
+
198
+ ```typescript
199
+ function normalizeStringArray(raw: unknown): string[] {
200
+ if (Array.isArray(raw)) return raw.map(String);
201
+ if (typeof raw === "string") return [raw];
202
+ return [];
203
+ }
204
+ ```
205
+
206
+ ### 7. Implement entity normalizers
207
+
208
+ Create normalizer functions for each entity type:
209
+
210
+ - `normalizeProject(raw: unknown): ProjectMetadata` — extracts name, description, version, repository with string defaults
211
+ - `normalizeMilestones(raw: unknown): Milestone[]` — iterates array, applies key aliases, normalizes status and tasks
212
+ - `normalizeTasks(raw: unknown): Task[]` — iterates array, applies key aliases, normalizes status, coerces string arrays for deps/tags
213
+ - `normalizeWorkEntries(raw: unknown): WorkEntry[]` — iterates array, coerces hours to number, defaults date/task/notes
214
+ - `normalizeDocStats(raw: unknown): DocumentationStats` — extracts numeric counts with 0 defaults
215
+ - `normalizeProgress(raw: unknown): ProgressSummary` — extracts numeric summary fields with 0 defaults
216
+
217
+ Each normalizer should use `extractKnown` to separate known fields from extras.
218
+
219
+ ### 8. Implement top-level parser with fallback
220
+
221
+ Wire up `parseProgressYaml`:
222
+
223
+ ```typescript
224
+ export function parseProgressYaml(raw: string): ProgressData {
225
+ try {
226
+ const doc = yaml.load(raw) as Record<string, unknown> | null;
227
+ if (!doc || typeof doc !== "object") {
228
+ return emptyProgressData();
229
+ }
230
+ return {
231
+ project: normalizeProject(doc.project),
232
+ milestones: normalizeMilestones(doc.milestones),
233
+ work_entries: normalizeWorkEntries(doc.work_entries ?? doc.work_log),
234
+ documentation: normalizeDocStats(doc.documentation ?? doc.docs),
235
+ progress: normalizeProgress(doc.progress ?? doc.summary),
236
+ extra: extractTopLevelExtra(doc),
237
+ };
238
+ } catch {
239
+ return emptyProgressData();
240
+ }
241
+ }
242
+ ```
243
+
244
+ ### 9. Write unit tests
245
+
246
+ Create `app/lib/__tests__/yaml-loader.test.ts` with tests covering:
247
+
248
+ - Complete valid YAML parses correctly
249
+ - Missing sections default gracefully (empty arrays, zero counts)
250
+ - Unknown fields preserved in `extra`
251
+ - Status aliases resolve correctly (`"done"` becomes `"completed"`, `"wip"` becomes `"in_progress"`)
252
+ - Single string coerced to array for dependencies and tags
253
+ - Key aliases work (`est_hours` becomes `estimated_hours`)
254
+ - Completely invalid YAML returns empty ProgressData (no crash)
255
+ - Empty string input returns empty ProgressData
256
+
257
+ ---
258
+
259
+ ## Verification
260
+
261
+ - [ ] `app/lib/types.ts` exports all interfaces: Status, ExtraFields, ProgressData, ProjectMetadata, Milestone, Task, WorkEntry, DocumentationStats, ProgressSummary
262
+ - [ ] `app/lib/yaml-loader.ts` exports `parseProgressYaml` function
263
+ - [ ] Parser handles complete YAML and returns fully populated ProgressData
264
+ - [ ] Parser handles incomplete YAML with missing sections (defaults to empty arrays/zero counts)
265
+ - [ ] Unknown fields are preserved in `extra` objects at every level
266
+ - [ ] Status aliases resolve correctly (done, wip, active, pending, stuck, etc.)
267
+ - [ ] Single string values coerced to arrays for dependencies and tags
268
+ - [ ] Key aliases resolve correctly (est_hours, deps, name, etc.)
269
+ - [ ] Completely invalid YAML returns empty ProgressData without throwing
270
+ - [ ] All unit tests pass
271
+
272
+ ---
273
+
274
+ ## Expected Output
275
+
276
+ **Key Files Created**:
277
+ - `app/lib/types.ts`: TypeScript interfaces for the entire progress.yaml data model
278
+ - `app/lib/yaml-loader.ts`: Lenient YAML parser with drift tolerance
279
+ - `app/lib/__tests__/yaml-loader.test.ts`: Unit tests for the parser
280
+
281
+ ---
282
+
283
+ ## Notes
284
+
285
+ - The parser is intentionally lenient; it should never throw. Any error results in a fallback empty ProgressData
286
+ - The `extra` field pattern is critical: it allows the dashboard to display fields the schema does not yet know about, which is essential for agent-maintained YAML that evolves over time
287
+ - Status normalization covers common agent shorthand; new aliases can be added as they are discovered
288
+ - Key alias maps should be extended if agents consistently use non-standard field names
289
+
290
+ ---
291
+
292
+ **Next Task**: [Task 3: Build Server API & Data Loading](./task-3-build-server-api-data-loading.md)
293
+ **Related Design Docs**: [Data Model & YAML Parsing](../../design/local.data-model-yaml-parsing.md)
294
+ **Estimated Completion Date**: TBD