coaia-visualizer 1.6.3 → 1.6.4

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 (46) hide show
  1. package/Dockerfile +61 -0
  2. package/Dockerfile.app +50 -0
  3. package/cli.ts +11 -5
  4. package/components/chart-detail-editable.tsx +2 -0
  5. package/components/chart-detail.tsx +6 -1
  6. package/components/edit-action-step.tsx +2 -0
  7. package/components/github-provenance.tsx +226 -0
  8. package/components/relation-graph.tsx +91 -27
  9. package/dist/cli.js +9 -5
  10. package/docker-build-push.sh +20 -0
  11. package/docker-entrypoint.sh +27 -0
  12. package/index.tsx +26 -0
  13. package/lib/chart-editor.ts +3 -3
  14. package/lib/github-provenance.ts +316 -0
  15. package/lib/jsonl-parser.ts +2 -2
  16. package/lib/types.ts +15 -2
  17. package/mcp/test_mcp/.gemini/settings.json +18 -0
  18. package/package.json +14 -13
  19. package/rispecs/README.md +170 -0
  20. package/rispecs/accountability-responsibility.rispec.md +110 -0
  21. package/rispecs/api-mcp-interface.spec.md +287 -0
  22. package/rispecs/app.spec.md +364 -0
  23. package/rispecs/chart-editing-workflow.spec.md +297 -0
  24. package/rispecs/chart-visualization-hierarchy.spec.md +235 -0
  25. package/rispecs/cli-mode.spec.md +224 -0
  26. package/rispecs/github-project-runtime-memory-integration.spec.md +381 -0
  27. package/rispecs/jsonl-parsing-data-types.spec.md +243 -0
  28. package/rispecs/narrative-beats-display.spec.md +189 -0
  29. package/rispecs/pde-integration.spec.md +280 -0
  30. package/rispecs/planning-integration.spec.md +329 -0
  31. package/rispecs/relation-graph-visualization.spec.md +171 -0
  32. package/rispecs/relation-to-mcp-structural-thinking.kin.md +65 -0
  33. package/rispecs/ui-component-library.spec.md +258 -0
  34. package/mcp/dist/api-client.d.ts +0 -138
  35. package/mcp/dist/api-client.d.ts.map +0 -1
  36. package/mcp/dist/api-client.js +0 -115
  37. package/mcp/dist/api-client.js.map +0 -1
  38. package/mcp/dist/index.d.ts +0 -2
  39. package/mcp/dist/index.d.ts.map +0 -1
  40. package/mcp/dist/index.js +0 -286
  41. package/mcp/dist/index.js.map +0 -1
  42. package/mcp/dist/tools/index.d.ts +0 -18
  43. package/mcp/dist/tools/index.d.ts.map +0 -1
  44. package/mcp/dist/tools/index.js +0 -322
  45. package/mcp/dist/tools/index.js.map +0 -1
  46. package/mcp/package-lock.json +0 -210
package/index.tsx CHANGED
@@ -13,6 +13,31 @@ export type {
13
13
  // JSONL Parser
14
14
  export { parseJSONL, organizeData, getChartSummary, getChartProgress } from "./lib/jsonl-parser"
15
15
 
16
+ // GitHub provenance helpers
17
+ export {
18
+ GITHUB_BRIDGE_RELATION_TYPES,
19
+ GITHUB_PROJECT_FIELD_KEYS,
20
+ GITHUB_SYNC_STATES,
21
+ formatGithubIssueRef,
22
+ formatProjectItemRef,
23
+ getGithubBridgeRelationType,
24
+ getGithubEntityProvenance,
25
+ getGithubIssueRef,
26
+ getGithubProjectItems,
27
+ getGithubSource,
28
+ getGithubSyncState,
29
+ getProjectFieldEntries,
30
+ } from "./lib/github-provenance"
31
+ export type {
32
+ GithubBridgeRelationType,
33
+ GithubEntityProvenance,
34
+ GithubIssueRef,
35
+ GithubProjectFieldEntry,
36
+ GithubProjectItemRef,
37
+ GithubSourceProvenance,
38
+ GithubSyncState,
39
+ } from "./lib/github-provenance"
40
+
16
41
  // Utilities
17
42
  export { cn } from "./lib/utils"
18
43
 
@@ -28,6 +53,7 @@ export { ChartDetail } from "./components/chart-detail"
28
53
  export { DataStats } from "./components/data-stats"
29
54
  export { CreateChartForm } from "./components/create-chart-form"
30
55
  export { NarrativeBeats } from "./components/narrative-beats"
56
+ export { ActionStepIssueBadge, ProjectSourceBadge, SyncStatePill } from "./components/github-provenance"
31
57
  export { LiveIndicator } from "./components/live-indicator"
32
58
  export { ThemeToggle } from "./components/theme-toggle"
33
59
  export { EditDesiredOutcome } from "./components/edit-desired-outcome"
@@ -1,8 +1,8 @@
1
1
  // lib/chart-editor.ts - Chart editing logic
2
2
 
3
- import type { EntityRecord, RelationRecord, ParsedData } from "./types"
4
- import { buildMetadataWarnings, buildRecordProjections, serializeParsedData } from "./jsonl-preservation"
5
- import { buildRiseSpecs } from "./jsonl-parser"
3
+ import type { EntityRecord, RelationRecord, ParsedData } from "./types.ts"
4
+ import { buildMetadataWarnings, buildRecordProjections, serializeParsedData } from "./jsonl-preservation.ts"
5
+ import { buildRiseSpecs } from "./jsonl-parser.ts"
6
6
 
7
7
  export interface ChartUpdate {
8
8
  type:
@@ -0,0 +1,316 @@
1
+ import type { EntityRecord, RelationRecord } from "./types.ts"
2
+
3
+ export const GITHUB_SYNC_STATES = ["synced", "diverged", "conflict", "project-only", "chart-only"] as const
4
+
5
+ export type GithubSyncState = (typeof GITHUB_SYNC_STATES)[number]
6
+
7
+ export interface GithubIssueRef {
8
+ owner: string
9
+ repo: string
10
+ number: number
11
+ nodeId?: string
12
+ url?: string
13
+ }
14
+
15
+ export interface GithubProjectItemRef {
16
+ projectOwner?: string
17
+ projectNumber?: number
18
+ projectTitle?: string
19
+ itemId?: string
20
+ projectId?: string
21
+ url?: string
22
+ fields?: Record<string, unknown>
23
+ fieldValues?: Record<string, unknown>
24
+ projectFields?: Record<string, unknown>
25
+ [key: string]: unknown
26
+ }
27
+
28
+ export interface GithubSourceProvenance {
29
+ system?: string
30
+ toolName?: string
31
+ sessionId?: string
32
+ createdAt?: string
33
+ version?: string
34
+ [key: string]: unknown
35
+ }
36
+
37
+ export interface GithubEntityProvenance {
38
+ issue?: GithubIssueRef
39
+ projectItems: GithubProjectItemRef[]
40
+ syncState?: GithubSyncState
41
+ lastSyncedAt?: string
42
+ source?: GithubSourceProvenance
43
+ hasGithubMetadata: boolean
44
+ }
45
+
46
+ export interface GithubProjectFieldEntry {
47
+ key: string
48
+ label: string
49
+ value: string
50
+ }
51
+
52
+ export const GITHUB_PROJECT_FIELD_KEYS = [
53
+ "goal",
54
+ "current_reality",
55
+ "observations",
56
+ "question",
57
+ "Status",
58
+ "phase",
59
+ "session_id",
60
+ "four_dir_east",
61
+ "four_dir_south",
62
+ "four_dir_west",
63
+ "four_dir_north",
64
+ "relational_assessed",
65
+ "relational_principles",
66
+ ] as const
67
+
68
+ export const GITHUB_BRIDGE_RELATION_TYPES = ["synced_to_github", "linked_to_issue", "project_lens_of"] as const
69
+
70
+ export type GithubBridgeRelationType = (typeof GITHUB_BRIDGE_RELATION_TYPES)[number]
71
+
72
+ const PROJECT_FIELD_LABELS: Record<string, string> = {
73
+ goal: "goal",
74
+ current_reality: "current reality",
75
+ observations: "observations",
76
+ question: "question",
77
+ Status: "Status",
78
+ phase: "phase",
79
+ session_id: "session",
80
+ four_dir_east: "East",
81
+ four_dir_south: "South",
82
+ four_dir_west: "West",
83
+ four_dir_north: "North",
84
+ relational_assessed: "relational assessed",
85
+ relational_principles: "relational principles",
86
+ }
87
+
88
+ function isRecord(value: unknown): value is Record<string, unknown> {
89
+ return typeof value === "object" && value !== null && !Array.isArray(value)
90
+ }
91
+
92
+ function firstString(...values: unknown[]): string | undefined {
93
+ for (const value of values) {
94
+ if (typeof value === "string" && value.trim()) {
95
+ return value.trim()
96
+ }
97
+ }
98
+ }
99
+
100
+ function firstNumber(...values: unknown[]): number | undefined {
101
+ for (const value of values) {
102
+ if (typeof value === "number" && Number.isFinite(value)) {
103
+ return value
104
+ }
105
+ if (typeof value === "string" && value.trim()) {
106
+ const parsed = Number(value)
107
+ if (Number.isFinite(parsed)) {
108
+ return parsed
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ function normalizeIssueCandidate(candidate: unknown): GithubIssueRef | undefined {
115
+ if (!isRecord(candidate)) return undefined
116
+
117
+ const owner = firstString(candidate.owner, candidate.ownerLogin, candidate.repositoryOwner)
118
+ const repo = firstString(candidate.repo, candidate.repository, candidate.repositoryName)
119
+ const number = firstNumber(candidate.number, candidate.issue_number, candidate.issueNumber, candidate.issue)
120
+
121
+ if (!owner || !repo || !number) return undefined
122
+
123
+ return {
124
+ owner,
125
+ repo,
126
+ number,
127
+ nodeId: firstString(candidate.nodeId, candidate.node_id),
128
+ url: firstString(candidate.url, candidate.html_url) ?? `https://github.com/${owner}/${repo}/issues/${number}`,
129
+ }
130
+ }
131
+
132
+ function normalizeProjectItemCandidate(candidate: unknown, fallbackOwner?: string): GithubProjectItemRef | undefined {
133
+ if (!isRecord(candidate)) return undefined
134
+
135
+ const projectOwner = firstString(candidate.projectOwner, candidate.owner, candidate.ownerLogin, fallbackOwner)
136
+ const projectNumber = firstNumber(candidate.projectNumber, candidate.project_number, candidate.number)
137
+ const itemId = firstString(candidate.itemId, candidate.item_id, candidate.projectItemId, candidate.project_id)
138
+ const projectId = firstString(candidate.projectId, candidate.project_id)
139
+ const projectTitle = firstString(candidate.projectTitle, candidate.title, candidate.project)
140
+ const url = firstString(candidate.url)
141
+
142
+ if (!projectOwner && !projectNumber && !itemId && !projectId && !projectTitle && !url) {
143
+ return undefined
144
+ }
145
+
146
+ return {
147
+ ...candidate,
148
+ projectOwner,
149
+ projectNumber,
150
+ projectTitle,
151
+ itemId,
152
+ projectId,
153
+ url,
154
+ }
155
+ }
156
+
157
+ export function getGithubIssueRef(entity?: EntityRecord): GithubIssueRef | undefined {
158
+ if (!entity) return undefined
159
+
160
+ const metadata = entity.metadata ?? {}
161
+ const github = isRecord(metadata.github) ? metadata.github : undefined
162
+ const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
163
+ const githubRef = isRecord(metadata.github_ref) ? metadata.github_ref : undefined
164
+
165
+ return normalizeIssueCandidate(github?.issue) ?? normalizeIssueCandidate(syncTarget) ?? normalizeIssueCandidate(githubRef)
166
+ }
167
+
168
+ export function getGithubProjectItems(entity?: EntityRecord): GithubProjectItemRef[] {
169
+ if (!entity) return []
170
+
171
+ const metadata = entity.metadata ?? {}
172
+ const github = isRecord(metadata.github) ? metadata.github : undefined
173
+ const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
174
+ const fallbackOwner = getGithubIssueRef(entity)?.owner
175
+
176
+ if (Array.isArray(github?.projectItems)) {
177
+ return github.projectItems
178
+ .map((item) => normalizeProjectItemCandidate(item, fallbackOwner))
179
+ .filter((item): item is GithubProjectItemRef => Boolean(item))
180
+ }
181
+
182
+ const canonicalProjectItem = normalizeProjectItemCandidate(github?.projectItem, fallbackOwner)
183
+ if (canonicalProjectItem) {
184
+ return [canonicalProjectItem]
185
+ }
186
+
187
+ const legacyProjectItem = normalizeProjectItemCandidate(syncTarget, fallbackOwner)
188
+ return legacyProjectItem ? [legacyProjectItem] : []
189
+ }
190
+
191
+ export function getGithubSyncState(entityOrMetadata?: EntityRecord | Record<string, unknown>): GithubSyncState | undefined {
192
+ const metadata =
193
+ entityOrMetadata && "metadata" in entityOrMetadata && isRecord(entityOrMetadata.metadata)
194
+ ? entityOrMetadata.metadata
195
+ : entityOrMetadata
196
+
197
+ if (!isRecord(metadata)) return undefined
198
+
199
+ const github = isRecord(metadata.github) ? metadata.github : undefined
200
+ const syncState = github?.syncState ?? metadata.syncState
201
+
202
+ return typeof syncState === "string" && GITHUB_SYNC_STATES.includes(syncState as GithubSyncState)
203
+ ? (syncState as GithubSyncState)
204
+ : undefined
205
+ }
206
+
207
+ export function getGithubSource(entity?: EntityRecord): GithubSourceProvenance | undefined {
208
+ if (!entity) return undefined
209
+
210
+ const source = entity.metadata?.source
211
+ return isRecord(source) ? (source as GithubSourceProvenance) : undefined
212
+ }
213
+
214
+ export function getGithubEntityProvenance(entity?: EntityRecord): GithubEntityProvenance {
215
+ const metadata = entity?.metadata ?? {}
216
+ const github = isRecord(metadata.github) ? metadata.github : undefined
217
+ const issue = getGithubIssueRef(entity)
218
+ const projectItems = getGithubProjectItems(entity)
219
+ const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
220
+ const githubRef = isRecord(metadata.github_ref) ? metadata.github_ref : undefined
221
+ const source = getGithubSource(entity)
222
+
223
+ return {
224
+ issue,
225
+ projectItems,
226
+ syncState: getGithubSyncState(entity),
227
+ lastSyncedAt: firstString(github?.lastSyncedAt, metadata.lastSyncedAt),
228
+ source,
229
+ hasGithubMetadata:
230
+ Boolean(github) ||
231
+ Boolean(syncTarget) ||
232
+ Boolean(githubRef) ||
233
+ source?.system === "coaia-github" ||
234
+ Boolean(issue) ||
235
+ projectItems.length > 0,
236
+ }
237
+ }
238
+
239
+ export function formatGithubIssueRef(issue?: GithubIssueRef): string | undefined {
240
+ if (!issue) return undefined
241
+ return `${issue.owner}/${issue.repo}#${issue.number}`
242
+ }
243
+
244
+ export function formatProjectItemRef(projectItem?: GithubProjectItemRef): string | undefined {
245
+ if (!projectItem) return undefined
246
+
247
+ if (projectItem.projectOwner && projectItem.projectNumber) {
248
+ return `${projectItem.projectOwner}/${projectItem.projectNumber}`
249
+ }
250
+
251
+ return projectItem.projectTitle || projectItem.itemId || projectItem.projectId || projectItem.url
252
+ }
253
+
254
+ export function getProjectFieldEntries(
255
+ entity: EntityRecord | undefined,
256
+ projectItem?: GithubProjectItemRef,
257
+ ): GithubProjectFieldEntry[] {
258
+ const metadata = entity?.metadata ?? {}
259
+ const github = isRecord(metadata.github) ? metadata.github : undefined
260
+ const sources = [
261
+ projectItem?.fields,
262
+ projectItem?.fieldValues,
263
+ projectItem?.projectFields,
264
+ github?.fields,
265
+ github?.fieldValues,
266
+ github?.projectFields,
267
+ metadata,
268
+ github,
269
+ ].filter(isRecord)
270
+
271
+ return GITHUB_PROJECT_FIELD_KEYS.flatMap((key) => {
272
+ for (const source of sources) {
273
+ if (!(key in source)) continue
274
+ const value = stringifyFieldValue(source[key])
275
+ if (value) {
276
+ return [{ key, label: PROJECT_FIELD_LABELS[key], value }]
277
+ }
278
+ }
279
+ return []
280
+ })
281
+ }
282
+
283
+ export function getGithubBridgeRelationType(relation: RelationRecord): GithubBridgeRelationType | undefined {
284
+ const relationType = relation.relationType
285
+ if (GITHUB_BRIDGE_RELATION_TYPES.includes(relationType as GithubBridgeRelationType)) {
286
+ return relationType as GithubBridgeRelationType
287
+ }
288
+
289
+ const metadata = relation.metadata ?? {}
290
+ const github = isRecord(metadata.github) ? metadata.github : undefined
291
+ const context = firstString(
292
+ metadata.context,
293
+ metadata.relation,
294
+ metadata.relationName,
295
+ metadata.semanticType,
296
+ github?.context,
297
+ github?.relation,
298
+ )
299
+
300
+ return GITHUB_BRIDGE_RELATION_TYPES.includes(context as GithubBridgeRelationType)
301
+ ? (context as GithubBridgeRelationType)
302
+ : undefined
303
+ }
304
+
305
+ function stringifyFieldValue(value: unknown): string | undefined {
306
+ if (value === null || value === undefined) return undefined
307
+ if (typeof value === "string") return value.trim() || undefined
308
+ if (typeof value === "number" || typeof value === "boolean") return String(value)
309
+ if (Array.isArray(value)) {
310
+ const rendered = value.map(stringifyFieldValue).filter(Boolean).join(", ")
311
+ return rendered || undefined
312
+ }
313
+ if (isRecord(value)) {
314
+ return firstString(value.name, value.text, value.value, value.title, value.label) ?? JSON.stringify(value)
315
+ }
316
+ }
@@ -1,5 +1,5 @@
1
- import type { Chart, EntityRecord, JSONLRecord, ParsedData, RawJSONLRecord, RelationRecord, RiseSpecProjection } from "./types"
2
- import { buildMetadataWarnings, buildRecordProjections, normalizeJsonlRecord } from "./jsonl-preservation"
1
+ import type { Chart, EntityRecord, JSONLRecord, ParsedData, RawJSONLRecord, RelationRecord, RiseSpecProjection } from "./types.ts"
2
+ import { buildMetadataWarnings, buildRecordProjections, normalizeJsonlRecord } from "./jsonl-preservation.ts"
3
3
 
4
4
  export function parseJSONL(content: string): RawJSONLRecord[] {
5
5
  const lines = content.trim().split("\n")
package/lib/types.ts CHANGED
@@ -3,7 +3,14 @@
3
3
  export interface EntityRecord {
4
4
  type: "entity"
5
5
  name: string
6
- entityType: "structural_tension_chart" | "desired_outcome" | "current_reality" | "action_step" | "narrative_beat"
6
+ entityType:
7
+ | "structural_tension_chart"
8
+ | "desired_outcome"
9
+ | "current_reality"
10
+ | "action_step"
11
+ | "narrative_beat"
12
+ | "custom"
13
+ | (string & {})
7
14
  observations: string[]
8
15
  metadata: Record<string, any>
9
16
  [key: string]: any
@@ -13,7 +20,13 @@ export interface RelationRecord {
13
20
  type: "relation"
14
21
  from: string
15
22
  to: string
16
- relationType: "contains" | "creates_tension_with" | "advances_toward" | "documents"
23
+ relationType:
24
+ | "contains"
25
+ | "creates_tension_with"
26
+ | "advances_toward"
27
+ | "documents"
28
+ | "custom"
29
+ | (string & {})
17
30
  metadata: Record<string, any>
18
31
  [key: string]: any
19
32
  }
@@ -0,0 +1,18 @@
1
+ {
2
+ "mcpServers": {
3
+ "chart-coaia-narrative-legacy-mcp": {
4
+ "command": "npx",
5
+ "args": ["-y", "${CNCV}", "--memory-path", "${memories}/trading-chart.coaia-narrative.jsonl" ]
6
+ },
7
+ "charts-mcp": {
8
+ "command": "node",
9
+ "args": [
10
+ "/workspace/repos/jgwill/coaia-visualizer-feat-4/mcp/dist/index.js"
11
+ ],
12
+ "env": {
13
+ "COAIAN_API_TOKEN":"123",
14
+ "COAIAV_API_URL":"http://localhost:4321"
15
+ }
16
+ }
17
+ }
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coaia-visualizer",
3
- "version": "1.6.3",
3
+ "version": "1.6.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./index.tsx",
@@ -9,6 +9,7 @@
9
9
  ".": "./index.tsx",
10
10
  "./lib/types": "./lib/types.ts",
11
11
  "./lib/jsonl-parser": "./lib/jsonl-parser.ts",
12
+ "./lib/github-provenance": "./lib/github-provenance.ts",
12
13
  "./lib/utils": "./lib/utils.ts",
13
14
  "./hooks/*": "./hooks/*.ts",
14
15
  "./components/*": "./components/*.tsx",
@@ -23,25 +24,24 @@
23
24
  "components/**/*",
24
25
  "hooks/**/*",
25
26
  "lib/**/*",
26
- "styles/**/*",
27
- "public/**/*",
28
27
  "dist/**/*",
29
- "mcp/dist/**/*",
30
- "mcp/src/**/*",
31
- "mcp/package.json",
32
- "mcp/package-lock.json",
33
- "mcp/README.md",
34
- "mcp/Dockerfile",
35
- "mcp/tsconfig.json",
36
- "index.tsx",
28
+ "mcp/**/*",
29
+ "public/**/*",
30
+ "rispecs/**/*",
31
+ "styles/**/*",
37
32
  "cli.ts",
33
+ "index.tsx",
38
34
  "skill.ts",
39
- "components.json",
40
- "mcp-config.json",
41
35
  "next-env.d.ts",
42
36
  "next.config.mjs",
43
37
  "postcss.config.mjs",
44
38
  "tsconfig.json",
39
+ "Dockerfile",
40
+ "Dockerfile.app",
41
+ "docker-build-push.sh",
42
+ "docker-entrypoint.sh",
43
+ "components.json",
44
+ "mcp-config.json",
45
45
  "README.md",
46
46
  "README_TELESCOPED_NAVIGATION.md",
47
47
  "KINSHIP.md",
@@ -57,6 +57,7 @@
57
57
  "test": "playwright test",
58
58
  "test:rich-jsonl": "playwright test test-scripts/rich-jsonl-preservation.spec.ts",
59
59
  "test:global-cli": "playwright test test-scripts/test-global-cli.spec.ts",
60
+ "verify:github-provenance": "node --experimental-strip-types --experimental-specifier-resolution=node test-scripts/verify-github-provenance.mjs",
60
61
  "docker:build": "docker build -t jgwill/coaia:visualizer .",
61
62
  "docker:push": "docker push jgwill/coaia:visualizer",
62
63
  "docker:build-push": "npm run docker:build && npm run docker:push",
@@ -0,0 +1,170 @@
1
+ # COAIA Visualizer — RISE Specification Directory
2
+
3
+ > Complete RISE specifications for `coaia-visualizer` v1.5.5 — the interactive web-based visualizer and editor for Structural Tension Charts from coaia-narrative JSONL files.
4
+
5
+ **Framework**: RISE (Reverse-engineer · Intent · Specifications · Export)
6
+ **Target Audience**: LLM agents, developers, and contributors rebuilding or extending coaia-visualizer
7
+ **Canonical Types**: [`coaia-visualizer/lib/types.ts`](../lib/types.ts)
8
+ **Parent Kinship**: [`coaia-narrative`](../../coaia-narrative/) — data source and memory layer
9
+
10
+ ---
11
+
12
+ ## 📚 Main Overview
13
+
14
+ - **[app.spec.md](./app.spec.md)** — Complete application specification
15
+ - What coaia-visualizer enables users to create
16
+ - How all components interconnect
17
+ - Creative intent and structural orientation
18
+
19
+ ---
20
+
21
+ ## 🔧 Component Specifications
22
+
23
+ 1. **[jsonl-parsing-data-types.spec.md](./jsonl-parsing-data-types.spec.md)**
24
+ - JSONL parsing engine and 4-pass organization algorithm
25
+ - EntityRecord, RelationRecord, Chart, ParsedData type system
26
+ - KnowledgeGraph assembly from flat JSONL lines
27
+ - File upload, export, and live reload
28
+
29
+ 2. **[chart-visualization-hierarchy.spec.md](./chart-visualization-hierarchy.spec.md)**
30
+ - ChartList with hierarchy and flat list modes
31
+ - ChartDetail and ChartDetailEditable views
32
+ - Telescoped navigation and sub-chart drilling
33
+ - Progress bars, summary truncation, narrative beat indicators
34
+
35
+ 3. **[chart-editing-workflow.spec.md](./chart-editing-workflow.spec.md)**
36
+ - Edit desired outcomes, current reality observations, action steps
37
+ - Due date management with calendar popover
38
+ - Add/remove/toggle actions and charts
39
+ - CreateChartForm and AddMasterChart creation flows
40
+ - ChartEditor class mutation patterns
41
+
42
+ 4. **[api-mcp-interface.spec.md](./api-mcp-interface.spec.md)**
43
+ - REST API routes (charts CRUD, search, JSONL I/O, watch, audio)
44
+ - MCP server with 15 tools for AI agent access
45
+ - Bearer token authentication (COAIAN_API_TOKEN)
46
+ - CoaiaVisualizerClient API wrapper
47
+
48
+ 5. **[cli-mode.spec.md](./cli-mode.spec.md)**
49
+ - CLI entry point with `--memory-path` flag
50
+ - Docker and local Next.js execution modes
51
+ - Environment variable configuration
52
+ - Auto-open browser and graceful shutdown
53
+
54
+ 6. **[narrative-beats-display.spec.md](./narrative-beats-display.spec.md)**
55
+ - Narrative beat card rendering
56
+ - Multi-universe perspective badges
57
+ - Act sequencing, dramatic type, prose display
58
+ - Lessons learned and timestamp metadata
59
+
60
+ 7. **[relation-graph-visualization.spec.md](./relation-graph-visualization.spec.md)**
61
+ - Inter-chart relation graph rendering
62
+ - Entity connection display grouped by relation type
63
+ - contains, creates_tension_with, advances_toward, documents relations
64
+ - Entity observation preview
65
+
66
+ 8. **[ui-component-library.spec.md](./ui-component-library.spec.md)**
67
+ - Radix UI / shadcn-based component primitives
68
+ - Dark/light theme support via next-themes
69
+ - DataStats dashboard cards
70
+ - LiveIndicator polling status display
71
+ - Tailwind CSS utility patterns
72
+
73
+ 9. **[accountability-responsibility.rispec.md](./accountability-responsibility.rispec.md)** *(existing)*
74
+ - Rendering accountability/responsibility distinctions in chart UI
75
+ - Schema consumption from entity metadata
76
+
77
+ ---
78
+
79
+ ## 🔮 Future Integration Specs
80
+
81
+ 10. **[pde-integration.spec.md](./pde-integration.spec.md)** *(future/aspirational)*
82
+ - Four Directions Medicine Wheel quadrant visualization
83
+ - PDE source linking with decomposition provenance trail
84
+ - Implicit intent badges and confidence indicators
85
+ - Direction-based action grouping (EAST/SOUTH/WEST/NORTH)
86
+ - Extends: chart-visualization-hierarchy, jsonl-parsing-data-types
87
+
88
+ 11. **[planning-integration.spec.md](./planning-integration.spec.md)** *(future/aspirational)*
89
+ - Plan source indicator with parse confidence scoring
90
+ - Bidirectional sync status (synced/diverged/conflict)
91
+ - Plan section mapping for action step provenance
92
+ - Confidence-based color coding for human review focus
93
+ - Re-sync trigger to push edits back to plan markdown
94
+ - Extends: chart-visualization-hierarchy, chart-editing-workflow, api-mcp-interface
95
+
96
+ ---
97
+
98
+ ## 🎯 Core Creative Intent
99
+
100
+ coaia-visualizer enables users to **create living, navigable maps of structural tension** — transforming flat JSONL memory files into interactive hierarchies where:
101
+
102
+ - **Desired outcomes** are visible as aspirational anchors
103
+ - **Current reality** is honestly assessed and continuously updated
104
+ - **Action steps** telescope into full sub-charts, creating fractal depth
105
+ - **Narrative beats** weave story across the structural work
106
+ - **Relations** reveal the kinship between charts, actions, and outcomes
107
+
108
+ This is **not** a dashboard for monitoring problems. It is a **ceremonial instrument** for holding creative tension — the generative gap between what is and what must become.
109
+
110
+ ---
111
+
112
+ ## 🏗️ Three Core Pillars
113
+
114
+ ### 1. Creative Orientation (NOT Problem-Solving)
115
+ Every chart begins with a **Desired Outcome** — what the user wants to create. The system refuses "fix" and "solve" framing. Current Reality is an honest assessment, never a complaint.
116
+
117
+ ### 2. Structural Tension (Current Reality ↔ Desired Outcome)
118
+ The irreducible gap between reality and vision generates the *mana* that drives action. The visualizer makes this tension **visible and navigable** — not as anxiety but as creative fuel.
119
+
120
+ ### 3. Advancing Patterns (NOT Oscillating)
121
+ Action steps advance toward outcomes. Completed actions update parent reality. Telescoped charts create depth without losing context. The system structurally prevents oscillation between problem and relief.
122
+
123
+ ---
124
+
125
+ ## 📐 Data Flow Architecture
126
+
127
+ ```
128
+ JSONL File / API
129
+
130
+
131
+ parseJSONL() ─── Flat records (entities + relations)
132
+
133
+
134
+ organizeData() ─── 4-pass: separate → chart assembly → populate → hierarchy
135
+
136
+
137
+ ParsedData ─── Charts[], Entities Map, Relations[], RootCharts[]
138
+
139
+ ├──▶ page.tsx ─── Main orchestrator (state management, routing)
140
+ ├──▶ ChartList ─── Hierarchy/List navigation
141
+ ├──▶ ChartDetail ─── Read-only display (unused by page.tsx)
142
+ ├──▶ ChartDetailEdit ─── Full editing via ChartEditor
143
+ ├──▶ NarrativeBeats ─── Story layer
144
+ ├──▶ RelationGraph ─── Inter-entity connections
145
+ └──▶ DataStats ─── Summary metrics
146
+
147
+
148
+ ChartEditor.exportToJSONL() ─── Serialize back to JSONL
149
+
150
+
151
+ POST /api/jsonl ─── Persist with backup
152
+
153
+
154
+ COAIAV_MEMORY_PATH ─── Filesystem path resolved by CLI / env
155
+ ```
156
+
157
+ ---
158
+
159
+ ## ✅ Quality Test: Can Another LLM Rebuild This?
160
+
161
+ Given these specifications alone, an LLM agent should be able to:
162
+
163
+ - ✅ Parse any coaia-narrative JSONL file into navigable chart hierarchies
164
+ - ✅ Render structural tension (desired outcome vs current reality) with correct visual language
165
+ - ✅ Implement telescoped navigation between parent and sub-charts
166
+ - ✅ Build all 15 MCP tools matching the existing API surface
167
+ - ✅ Create the CLI with Docker and local execution modes
168
+ - ✅ Reproduce the edit workflow: desired outcome → current reality → action steps → completion
169
+ - ✅ Display narrative beats with multi-universe perspectives
170
+ - ✅ Maintain creative orientation framing throughout all UI text