Rifqi Hafizuddin Claude Opus 4.8 commited on
Commit
ce7be17
Β·
1 Parent(s): 0b2d678

[NOTICKET] docs: pr/5 sprint tracker + endpoint-restructure contract + REPO_STATUS banner

Browse files

DEV_PLAN $0 = pr/5 sprint table (P0 contract done; Phase 1 unwire done via KM-686 0b2d678; v2/regroup/observability pending). Header bumped to pr/5 / 2026-06-30.

Add API_ENDPOINTS_RESTRUCTURE.md: contract sent to Harry β€” chat -> /api/v2/chat/stream (analysis_id), skills regroup under /api/v1/tools/ (list/help/report), new /api/v1/observability (tentative).

REPO_STATUS: pr/5 direction banner (Phase 1 marked done) + 2026-06-29 cross-repo Go verification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files changed (3) hide show
  1. API_ENDPOINTS_RESTRUCTURE.md +391 -0
  2. DEV_PLAN.md +41 -4
  3. REPO_STATUS.md +69 -19
API_ENDPOINTS_RESTRUCTURE.md ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Backend Agentic Service β€” API Endpoint Docs (endpoint restructure)
2
+
3
+ **Status:** contract draft for FE/Go integration (2026-06-30). Covers the AI-only surface after the
4
+ restructure. Sections marked **TENTATIVE** (observability) may still change β€” send feedback before we
5
+ lock them.
6
+
7
+ **What changed**
8
+ - **Only the chat pilot moves to `/api/v2`.** Everything else stays on `/api/v1`, regrouped under `/tools`.
9
+ - **Chat pilot (`/api/v2/chat/stream`) uses `analysis_id`, not `room_id`.**
10
+ - **Skills are grouped under `/api/v1/tools`:** `list` / `help` / `report`.
11
+ - **New:** `GET /api/v1/observability` β€” Responsible-AI provenance per chat answer.
12
+ - Python is **generative-AI only.** It never creates/updates an analysis, room, document, DB
13
+ client, or catalog β€” Go owns those. Python just receives `analysis_id`. Those v1 routers are
14
+ unwired from `main` + Swagger (not deleted).
15
+
16
+ **Open coordination questions (need a decision with Harry) β€” flagged inline as ⚠️:**
17
+ 1. **`message_id` origin** β€” who mints the assistant turn id used to correlate stream ↔ observability? (Recommend: Go mints it, passes it in the chat request, Python echoes on `done`.)
18
+ 2. **Deterministic `/help` dispatch** β€” dedicated endpoint (recommended below) vs router classification.
19
+ 3. **Observability storage** β€” single JSONB row per message (recommended) vs 3 normalized tables.
20
+
21
+ ---
22
+
23
+ ## 1. call_agent β€” `POST /api/v2/chat/stream`
24
+
25
+ The only FE→Python call in normal operation. Same as v1 except **`room_id` → `analysis_id`**, and
26
+ the `done` event now carries the assistant `message_id` for observability correlation.
27
+
28
+ **Request body** (`application/json`) β€” `ChatRequest`:
29
+
30
+ ```json
31
+ {
32
+ "user_id": "u_1a2b3c",
33
+ "analysis_id": "an_42",
34
+ "message_id": "msg_88f1",
35
+ "message": "What were total sales by region last quarter?"
36
+ }
37
+ ```
38
+
39
+ - `analysis_id` is the analysis-session id (replaces `room_id`). No auth header (handled by Go).
40
+ - ⚠️ `message_id` (optional): the assistant turn id. **Recommended: Go mints it** alongside the
41
+ `analyses_messages` row and passes it here, so the FE can call `/api/v1/observability?message_id=...`
42
+ in parallel. If omitted, Python mints one and returns it on `done`.
43
+
44
+ **Response:** `text/event-stream`. Events arrive in this order:
45
+
46
+ | event | data | notes |
47
+ |---|---|---|
48
+ | `sources` | JSON array of `{document_id, filename, page_label}` | structured: one per executed table; unstructured: deduped doc/page; chat/help/error: `[]`. |
49
+ | `status` | text | slow-path only β€” progress pings ("Planning…", "Running N steps…"). Safe to surface or ignore. |
50
+ | `chunk` | text fragment | concatenate in order to form the answer. |
51
+ | `done` | `{"message_id": "..."}` | **v2 change:** was empty; now returns the turn id for the observability lookup. |
52
+ | `error` | text | terminal error; stream stops after this. |
53
+
54
+ The internal `intent` event is consumed inside Python (gates caching) and **not** forwarded.
55
+
56
+ **Stream carries the answer text ONLY.** Planning / tool calls / sources detail are **not** in the
57
+ stream (it would slow it down) β€” fetch them from `/observability` (Β§7), called in parallel.
58
+
59
+ **Example β€” `structured_flow` answer** (raw SSE; blank line separates events):
60
+
61
+ ```
62
+ event: sources
63
+ data: [{"document_id":"u_1a2b3c_orders","filename":"orders","page_label":null}]
64
+
65
+ event: status
66
+ data: Planning analysis…
67
+
68
+ event: status
69
+ data: Running 3 steps…
70
+
71
+ event: chunk
72
+ data: Total sales by region last quarter:
73
+
74
+ event: chunk
75
+ data: Central led at $1.21M (38%), East $0.74M, West $0.55M (down 12% QoQ).
76
+
77
+ event: done
78
+ data: {"message_id":"msg_88f1"}
79
+ ```
80
+
81
+ **Example β€” simple chat reply** (no status pings, empty sources):
82
+
83
+ ```
84
+ event: sources
85
+ data: []
86
+
87
+ event: chunk
88
+ data: I'm your AI data analyst β€” connect a source or ask a question to get started.
89
+
90
+ event: done
91
+ data: {"message_id":"msg_12"}
92
+ ```
93
+
94
+ Behavior unchanged from v1: 1h Redis response-cache on the stateless `chat` intent only;
95
+ greeting/farewell fast-path (canned, no LLM); LLM router classifies every message into one of 5
96
+ intents (`chat Β· help Β· check Β· unstructured_flow Β· structured_flow`); messages persist on `done`.
97
+
98
+ ---
99
+
100
+ ## 2. list_skills β€” `GET /api/v1/tools/list`
101
+
102
+ Static, deterministic, safe for Go to cache. (Was `GET /api/v1/tools`.)
103
+
104
+ **Request:** none.
105
+
106
+ **Response 200** (`ListToolsResponse`):
107
+
108
+ ```json
109
+ {
110
+ "count": 2,
111
+ "tools": [
112
+ { "command": "/help", "name": "help", "type": "skill",
113
+ "description": "Show what the assistant can do and guide your next step." },
114
+ { "command": "/report", "name": "report", "type": "skill",
115
+ "description": "Generate a versioned analysis report (background, EDA, key findings, insights)." }
116
+ ]
117
+ }
118
+ ```
119
+
120
+ `CommandResponse = { command, name, type, description }`, `type ∈ {skill, analytics, data_access}`.
121
+ Catalog is `/help` + `/report` only; the `analyze_*` / `check_*` / `retrieve_*` and retired
122
+ `/problem-statement` entries are commented out (kept for restorability), not deleted.
123
+
124
+ **FE behavior:** the `/` slash menu surfaces **`/help` only**. **Report is a right-side button, not
125
+ a slash command** (it fires only when an analysis is finished β€” saves tokens).
126
+
127
+ ---
128
+
129
+ ## 3. skill: help β€” `POST /api/v1/tools/help`
130
+
131
+ ⚠️ **Proposed dedicated endpoint** (new in v2). In v1 there was no `/help` endpoint β€” help was reached
132
+ only by letting the LLM router classify a chat message. A dedicated endpoint makes `/help` dispatch
133
+ **deterministic** (no risk the router mis-classifies the slash command) and gives it a clean home in
134
+ the tools group. State-aware: reads analysis state + history to guide the next step.
135
+
136
+ > Alternative if we *don't* add this endpoint: FE keeps calling `POST /chat/stream` and trusts the
137
+ > router to classify the help intent. We recommend the dedicated endpoint β€” decision pending (open
138
+ > question #2).
139
+
140
+ **Request body** (`application/json`):
141
+
142
+ ```json
143
+ {
144
+ "user_id": "u_1a2b3c",
145
+ "analysis_id": "an_42"
146
+ }
147
+ ```
148
+
149
+ **Response:** `text/event-stream` β€” same SSE shape as chat, with `sources: []` and no `status`
150
+ pings (help never references documents). Streams a next-step guidance reply.
151
+
152
+ ```
153
+ event: sources
154
+ data: []
155
+
156
+ event: chunk
157
+ data: Your goal is set β€” you can start exploring now. Try a question like "average order value by month", then I can generate a report.
158
+
159
+ event: done
160
+ data: {"message_id":"msg_h7"}
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 4. skill: report β€” `POST /api/v1/tools/report`
166
+
167
+ The "Generate Report" button. Same as v1, moved under `/tools`. Generate, persist, and return a new
168
+ report version. Currently renders **Markdown** (FE preview); PPT/PDF/infographic export is future work
169
+ (triggered on a download button, not here).
170
+
171
+ **Query params:** `analysis_id` (required), `user_id` (required). No request body.
172
+
173
+ ```
174
+ POST /api/v1/tools/report?analysis_id=an_42&user_id=u_1a2b3c
175
+ ```
176
+
177
+ | status | meaning |
178
+ |---|---|
179
+ | 201 | new version generated β†’ `AnalysisReport` body. |
180
+ | 409 | floor not met β€” no recorded analyses yet for this session, nothing to report. |
181
+ | 500 | generation or persistence failed. |
182
+
183
+ **201 response** (`AnalysisReport`):
184
+
185
+ ```json
186
+ {
187
+ "report_id": "8f3a2b1c9d4e4f6a8b0c1d2e3f4a5b6c",
188
+ "analysis_id": "an_42",
189
+ "user_id": "u_1a2b3c",
190
+ "version": 2,
191
+ "generated_at": "2026-06-30T09:14:33.512Z",
192
+ "problem_statement": {
193
+ "objective": "Understand which regions drive revenue and why Q1 dipped.",
194
+ "business_questions": [
195
+ "Which regions contribute most to total revenue?",
196
+ "Did any region decline quarter-over-quarter?"
197
+ ]
198
+ },
199
+ "record_ids": ["rec_a1", "rec_b2"],
200
+ "executive_summary": "Revenue is concentrated in the Central region (38% of total). The West was the only region to contract, down 12% QoQ β€” the main driver of the Q1 dip.",
201
+ "findings": [
202
+ { "text": "Central region contributed 38% of total revenue, the largest share.",
203
+ "record_ids": ["rec_a1"], "supporting_data": null },
204
+ { "text": "West region revenue fell 12% quarter-over-quarter.",
205
+ "record_ids": ["rec_b2"], "supporting_data": null }
206
+ ],
207
+ "caveats": [
208
+ { "text": "March data for the East region was partially missing (~6% of rows).",
209
+ "record_ids": ["rec_b2"] }
210
+ ],
211
+ "open_questions": [
212
+ { "text": "What drove the West region's QoQ decline?", "record_ids": ["rec_b2"] }
213
+ ],
214
+ "data_sources": [
215
+ { "source_id": "src_sales_db", "name": "orders", "source_type": "postgres",
216
+ "detail": { "tables": ["orders"], "row_count": 48213,
217
+ "columns": ["region", "amount", "ordered_at"] } }
218
+ ],
219
+ "method_steps": [
220
+ { "task_id": "t1", "stage": "data_understanding", "objective": "Inventory the sales source",
221
+ "status": "success", "tools_used": ["check_data"] },
222
+ { "task_id": "t2", "stage": "modeling", "objective": "Aggregate revenue by region",
223
+ "status": "success", "tools_used": ["analyze_aggregate"] }
224
+ ],
225
+ "rendered_markdown": "# Analysis Report\n\n*Generated 2026-06-30 by u_1a2b3c Β· 2 analyses Β· 1 source(s)*\n\n## Objective\nUnderstand which regions drive revenue…\n\n## Key Findings\n1. Central region contributed 38%…"
226
+ }
227
+ ```
228
+
229
+ **409 response** (floor not met β€” the demo's most common error):
230
+
231
+ ```json
232
+ { "detail": "Not ready to generate a report β€” still needs at least one completed analysis." }
233
+ ```
234
+
235
+ ⚠️ **Precondition:** `AnalysisRecord`s persist only on the slow path, so reports require
236
+ `ENABLE_SLOW_PATH=true` on the Python deployment and β‰₯1 prior `structured_flow` question in the
237
+ session. With slow path off, `POST` 409s by design.
238
+
239
+ ---
240
+
241
+ ## 5. report versions β€” `GET /api/v1/tools/report/{analysis_id}` and `/{analysis_id}/{version}`
242
+
243
+ List a session's report versions (oldest-first). Returns `[ReportVersionEntry]`; `[]` if none.
244
+
245
+ ```json
246
+ [
247
+ { "report_id": "1b2c3d4e…", "version": 1, "generated_at": "2026-06-24T15:02:11Z", "record_count": 1 },
248
+ { "report_id": "8f3a2b1c…", "version": 2, "generated_at": "2026-06-25T09:14:33Z", "record_count": 2 }
249
+ ]
250
+ ```
251
+
252
+ `GET /api/v1/tools/report/{analysis_id}/{version}` β†’ one `AnalysisReport` (same shape as the POST
253
+ 201 body); 404 if that version doesn't exist:
254
+
255
+ ```json
256
+ { "detail": "No report v3 for analysis 'an_42'." }
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 6. Unwired in v2 (mounted in v1, OFF in v2)
262
+
263
+ Commented out of `main` + Swagger, **files kept**. Go owns these; Python is generative-only:
264
+ `POST /analysis/create` + analysis CRUD Β· `room` Β· `db_client` Β· `document` Β· `data_catalog` Β·
265
+ `users`/login. Re-mounting is a one-line `include_router` if ever needed.
266
+
267
+ ---
268
+
269
+ ## 7. observability β€” `GET /api/v1/observability` **(NEW Β· TENTATIVE)**
270
+
271
+ Responsible-AI provenance for **one chat answer**. Separate endpoint, called **in parallel with the
272
+ stream** β€” never embedded in it. The FE renders it as a collapsed dropdown the user can expand
273
+ (planning / tool calls / sources), Claude/Codex-style.
274
+
275
+ **Design (recommended):** one endpoint returns one merged object, backed by **one JSONB row per
276
+ message** written by an accumulating "scratchpad" decorator inside the chat agent and flushed on
277
+ `done`. The 3 facets (`planning` / `tool_calls` / `sources`) are **logical sections of the JSON**,
278
+ not separate tables β€” so the shape can evolve without a dedorch migration each time. (Storage is open
279
+ question #3.)
280
+
281
+ **Query params:** `analysis_id` (required), `message_id` (required).
282
+
283
+ ```
284
+ GET /api/v1/observability?analysis_id=an_42&message_id=msg_88f1
285
+ ```
286
+
287
+ **Timing:** the row is written when the turn finishes, so call this **after** the stream's `done`
288
+ event (or poll until 200). "Parallel" = a separate call the FE fires alongside the stream, not data
289
+ embedded in the stream.
290
+
291
+ **Field rules (by intent):**
292
+ - `planning` β€” present **only when the planner ran** (slow path); `null` otherwise.
293
+ - `tool_calls` β€” every tool invoked, with input + output. `[]` for pure chat / greeting / help.
294
+ - `sources` β€” **required for retrieve flows** (`structured_flow`, `unstructured_flow`). **Empty for
295
+ greeting / `chat` / `help`** (they don't reference documents).
296
+ - `thinking` β€” optional reasoning text; `null` if none.
297
+
298
+ **200 response β€” full `structured_flow` turn** (planner ran β†’ all sections present):
299
+
300
+ ```json
301
+ {
302
+ "analysis_id": "an_42",
303
+ "message_id": "msg_88f1",
304
+ "intent": "structured_flow",
305
+ "generated_at": "2026-06-30T03:21:09.114Z",
306
+ "planning": {
307
+ "goal_restated": "Find which regions drive revenue and why Q1 dipped.",
308
+ "assumptions": ["'last quarter' = Q1 2026"],
309
+ "steps": [
310
+ { "step": 1, "stage": "data_understanding", "objective": "Inventory the sales source" },
311
+ { "step": 2, "stage": "modeling", "objective": "Aggregate revenue by region" }
312
+ ]
313
+ },
314
+ "thinking": "The question needs a per-region breakdown plus a cause, so I inventory the source, aggregate revenue by region, then compare quarters.",
315
+ "tool_calls": [
316
+ {
317
+ "order": 1,
318
+ "name": "check_data",
319
+ "input": { "source_hint": "structured" },
320
+ "output": { "kind": "table", "summary": "1 source Β· 1 table (orders) Β· 48,213 rows" },
321
+ "status": "success"
322
+ },
323
+ {
324
+ "order": 2,
325
+ "name": "retrieve_data",
326
+ "input": { "source_id": "src_sales_db", "table_id": "orders",
327
+ "select": ["region", "amount"], "group_by": ["region"] },
328
+ "output": { "kind": "table", "columns": ["region", "total"], "row_count": 4,
329
+ "preview": [["Central", 1210000], ["East", 740000]] },
330
+ "status": "success"
331
+ }
332
+ ],
333
+ "sources": [
334
+ {
335
+ "type": "database",
336
+ "source_id": "src_sales_db",
337
+ "name": "orders",
338
+ "query": "SELECT region, SUM(amount) AS total FROM orders GROUP BY region",
339
+ "detail": { "tables": ["orders"], "row_count": 48213 }
340
+ }
341
+ ]
342
+ }
343
+ ```
344
+
345
+ **200 response β€” `unstructured_flow` turn** (no planner; source = document, with the retrieval query):
346
+
347
+ ```json
348
+ {
349
+ "analysis_id": "an_42",
350
+ "message_id": "msg_55",
351
+ "intent": "unstructured_flow",
352
+ "generated_at": "2026-06-30T03:40:02.001Z",
353
+ "planning": null,
354
+ "thinking": null,
355
+ "tool_calls": [
356
+ { "order": 1, "name": "retrieve_knowledge",
357
+ "input": { "query": "technology stack used in this project", "top_k": 4 },
358
+ "output": { "kind": "documents", "row_count": 4 }, "status": "success" }
359
+ ],
360
+ "sources": [
361
+ { "type": "document", "document_id": "doc_7", "filename": "tech_handbook.pdf",
362
+ "page_label": "12", "query": "technology stack used in this project",
363
+ "snippet": "The backend is built on FastAPI with async SQLAlchemy…", "score": 0.83 }
364
+ ]
365
+ }
366
+ ```
367
+
368
+ **200 response β€” simple `chat` / greeting turn** (nothing to trace):
369
+
370
+ ```json
371
+ {
372
+ "analysis_id": "an_42",
373
+ "message_id": "msg_12",
374
+ "intent": "chat",
375
+ "generated_at": "2026-06-30T03:05:00.000Z",
376
+ "planning": null,
377
+ "thinking": null,
378
+ "tool_calls": [],
379
+ "sources": []
380
+ }
381
+ ```
382
+
383
+ **404** β€” no provenance for that message yet (turn still running or unknown id):
384
+
385
+ ```json
386
+ { "detail": "No observability for message 'msg_88f1' yet." }
387
+ ```
388
+
389
+ > ⚠️ **Richness is path-dependent.** Full `planning` + tool I/O exist only when
390
+ > `ENABLE_SLOW_PATH=true`. Fast chat / single-query / help still record `sources` + the single
391
+ > tool call but have `planning: null`. This matches the rule "planning only when the planner runs."
DEV_PLAN.md CHANGED
@@ -1,10 +1,47 @@
1
- # Data Eyond β€” Current Development Plan (post 2026-06-24 meeting + 2026-06-25 checkpoint)
2
 
3
  **Purpose:** context file for Claude Code sessions working on the current sprint.
4
- **Branch:** `pr/4` Β· **Snapshot:** 2026-06-25.
5
  **Companion:** [REPO_STATUS.md](REPO_STATUS.md) describes the repo's *current built state*; this file
6
- describes the *in-flight plan* that changes it. New decisions from the 2026-06-25 checkpoint are in
7
- [Β§1.5](#15-2026-06-25-checkpoint-deltas).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  ---
10
 
 
1
+ # Data Eyond β€” Current Development Plan (post 2026-06-24 β†’ 2026-06-30 checkpoints)
2
 
3
  **Purpose:** context file for Claude Code sessions working on the current sprint.
4
+ **Branch:** `pr/5` Β· **Snapshot:** 2026-06-30.
5
  **Companion:** [REPO_STATUS.md](REPO_STATUS.md) describes the repo's *current built state*; this file
6
+ describes the *in-flight plan* that changes it. The **active sprint is pr/5** ([Β§0](#0-current-sprint--pr5-observability--endpoint-restructure)); sections Β§1–§6 are the prior
7
+ 2026-06-24/25 pivot (now largely βœ…), kept for context.
8
+
9
+ ---
10
+
11
+ ## 0. Current sprint β€” pr/5: Observability + Endpoint Restructure
12
+
13
+ From the **2026-06-30 checkpoint**. Direction: **Python β†’ generation/AI-only**; Go owns the analysis
14
+ lifecycle + data plane. Endpoint contract sent to Harry on 2026-06-30:
15
+ [API_ENDPOINTS_RESTRUCTURE.md](API_ENDPOINTS_RESTRUCTURE.md) (chat→v2, tools regroup, observability —
16
+ observability marked tentative). REPO_STATUS carries a matching `pr/5` direction banner.
17
+
18
+ Mentor's task order: **unwire β†’ regroup endpoints β†’ add tools (retrieve-data + observability)**; share
19
+ the endpoint contract *before* coding the tools. Status legend: ⬜ not started Β· πŸ”„ in progress Β· βœ… done Β·
20
+ β›” blocked Β· πŸ”Ž verify Β· ⏸️ deferred.
21
+
22
+ | Phase | Task | Owner | Status | Notes |
23
+ |---|---|---|---|---|
24
+ | **P0 β€” contract** | Draft + send endpoint contract to Harry (chat v2 Β· tools group Β· observability) | Rifqi + Sofhia | βœ… | `API_ENDPOINTS_RESTRUCTURE.md` sent 2026-06-30 (before-noon deadline met). Observability section flagged tentative. |
25
+ | **1 β€” unwire** | Unwire `users`(login)/`document`/`room`/`db_client`/`data_catalog`/`analysis` from `main` + Swagger | Sofhia | βœ… | **KM-686**, commit `0b2d678`. Commented, not deleted; `chat`/`report`/`tools` kept mounted. Resolves the analysis-CRUD scope Q β€” whole `analysis` router unwired (Go owns it). |
26
+ | **2 β€” v2 + regroup** | Create `src/api/v2/` and move the chat pilot there | Rifqi/Sofhia | ⬜ | Only chat moves to v2; mirror v1 structure. |
27
+ | **2 β€” v2 + regroup** | Chat: `room_id` β†’ **`analysis_id`** (request field + handler + history) | Rifqi | ⬜ | `ChatRequest` [chat.py:51](src/api/v1/chat.py:51); `done` event returns `message_id`. Ties to #25 (`analyses_messages`). |
28
+ | **2 β€” v2 + regroup** | Move report under tools β†’ `/api/v1/tools/report` (+ version routes) | Rifqi | ⬜ | Today standalone `/api/v1/report` (still mounted). Same functionality, new path. |
29
+ | **2 β€” v2 + regroup** | Move help under tools β†’ `POST /api/v1/tools/help` (dedicated endpoint) | Sofhia | ⬜ | Today help is a chat **intent** only. Dedicated endpoint = deterministic dispatch (open Q for Harry). |
30
+ | **2 β€” v2 + regroup** | Tools list β†’ `/api/v1/tools/list` | Sofhia | ⬜ | Today `GET /api/v1/tools` ([tools.py:133](src/api/v1/tools.py:133)). |
31
+ | **2 β€” v2 + regroup** | FE: slash menu = `/help` only; report = right-side button | Mentor (FE) | ⬜ | Coordination note, not Python work. |
32
+ | **3 β€” tools + obs** | Finish `help` so it actually **calls** (not just lists) + test | Sofhia | ⬜ | Mentor: help currently only lists tools. Core #2 after chat. |
33
+ | **3 β€” tools + obs** | Observability **scratchpad** (decorator) accumulating in the chat agent | Rifqi + Sofhia | ⬜ | Capture planning / tool I/O / sources during the run; flush one record on `done`. |
34
+ | **3 β€” tools + obs** | Audit `report_inputs` β€” covers planning + tool I/O + source? add cols / new store | Rifqi | ⬜ | **Rec:** dedicated provenance store = 1 JSONB row per message (logical 3 sections); keep Langfuse for engineering. |
35
+ | **3 β€” tools + obs** | Build `GET /api/v1/observability` (one merged response) | Rifqi | ⬜ | Intent-based source rules (greeting/help = none; retrieve = required). Richness path-dependent (full planning only on slow path). |
36
+ | **3 β€” tools + obs** | Keep stream **text-only**; observability is a separate parallel call | Rifqi | ⬜ | Per mentor β€” don't slow the stream. |
37
+ | **3 β€” tools + obs** | Resolve `message_id` correlation (stream ↔ observability) with Harry | Rifqi ↔ Harry | ⬜ | **Rec:** Go mints `message_id`, passes in chat request, Python echoes on `done`. |
38
+ | **4 β€” biz questions** | Get Go folder; confirm `business_questions` in create-analysis (max 5); sync Python | Harry/Mentor β†’ Rifqi | ⬜ | Go currently missing the field ("lagi difixing"). Python already models objective + business_questions. |
39
+ | **deferred** | Report formats: PPT (preferred) / PDF / infographic on download | β€” | ⏸️ | MD is fine for the FE preview stage now. |
40
+ | **deferred** | Charts (Plotlyβ†’JSON) + images tables | β€” | ⏸️ | Carried from Β§4 #26/#27. |
41
+
42
+ **Next up:** Phase 2 β€” stand up `src/api/v2/` for the chat pilot (`analysis_id`) and regroup
43
+ `help`/`report`/`list` under `/api/v1/tools/`. The observability scratchpad (Phase 3) can start in
44
+ parallel since its shape is already speced in the contract.
45
 
46
  ---
47
 
REPO_STATUS.md CHANGED
@@ -2,13 +2,27 @@
2
 
3
  **Audience:** teammates onboarding onto the Python repo (`Agentic-Service-Data-Eyond-Catalog`).
4
  **Scope:** what the code does **right now** (branch `pr/4`, ticket KM-652). Describes current state only β€” no roadmap or to-dos.
5
- **Snapshot date:** 2026-06-25.
 
 
 
 
6
 
7
  > This file is grounded in the source, not the older design docs. Where the two
8
  > disagree, the code wins β€” see [Β§11 Doc-vs-code](#11-where-the-older-docs-are-stale).
9
  > `REPO_CONTEXT.md` / `ARCHITECTURE.md` are the original Phase-2 design docs and are
10
  > stale on the router, joins, and the analysis/report stack.
11
 
 
 
 
 
 
 
 
 
 
 
12
  ---
13
 
14
  ## 1. The product in one paragraph
@@ -31,9 +45,13 @@ streaming.
31
  | Repo | Role | We edit? |
32
  |---|---|---|
33
  | **Python** β€” `Agentic-Service-Data-Eyond-Catalog` (this repo) | The agentic LLM service: router, gate, skills, slow analytical path, structured query engine, unstructured RAG, report generation, analysis-session state. FastAPI + async SQLAlchemy + LangChain + Azure GPT-4o. | **Yes β€” the only repo we edit.** |
34
- | **Go** — `Orchestrator-Agent-Service` | Gateway / data plane: interview agent, auth/JWT, rooms, documents (Azure Blob + CSV/XLSX→Parquet + embeddings), database_clients (Fernet creds), catalog ingestion, **all DB migrations**. | Reference only. |
35
  | **FE** β€” `E2E-Frontend-Data-Eyond` | React/Vite SPA. Talks to Go for everything and to Python only for chat streaming. | Reference only. |
36
 
 
 
 
 
37
  Shared infra: **Postgres** (app tables + `data_catalog` jsonb + PGVector `langchain_pg_embedding`), **Azure Blob**, and (Python-only) **Redis**.
38
 
39
  ---
@@ -59,6 +77,10 @@ Tests live locally and are gitignored. Run with `./.venv/Scripts/python.exe -m p
59
  Entry: `POST /api/v1/chat/stream` (`src/api/v1/chat.py`) β†’ `ChatHandler.handle(...)`
60
  (`src/agents/chat_handler.py`). One shared `ChatHandler` per process keeps the Azure clients warm.
61
 
 
 
 
 
62
  ```
63
  POST /chat/stream { user_id, room_id, message }
64
  β”‚ (analysis_id == room_id β€” one session = one analysis = one chat room)
@@ -131,6 +153,11 @@ Two facts to internalise:
131
 
132
  ## 7. API surface (this repo, all under `/api/v1`)
133
 
 
 
 
 
 
134
  | Endpoint | Purpose | Caller |
135
  |---|---|---|
136
  | `POST /chat/stream` | Main chat SSE (router β†’ dispatch) | FE β†’ Go β†’ Python (the only FEβ†’Python call today) |
@@ -153,10 +180,18 @@ unless `SKIP_INIT_DB=true`.
153
  | `documents`, `databases` | uploads + DB creds (Fernet-encrypted) | Go ingestion | executor cred resolution |
154
  | `data_catalog` | per-user jsonb `Catalog` (Source β†’ Table β†’ Column) | Go ingestion / Python pipeline | CatalogReader, planner, tools |
155
  | `langchain_pg_embedding` | PGVector document chunks | Go ingestion | DocumentRetriever |
156
- | `analysis_records` | jsonb `AnalysisRecord`, one per slow-path run | slow path | ReportGenerator, report readiness |
157
- | `analysis` *(dedorch)* | uuid id, `owner_id`, `problem_statement`, `problem_validated`, `report_id` | `/analysis/create`, state store | gate, Help, report |
158
- | `reports` *(dedorch)* | uuid, `title` + markdown `content` + `version` | ReportStore | report API |
159
- | `data_sources` *(dedorch)* | per-analysis binding; `reference_id` = catalog source_id | `/analysis/create` | structured-flow scoping, report appendix |
 
 
 
 
 
 
 
 
160
 
161
  **Catalog shape** (the jsonb in `data_catalog`):
162
  `Catalog β†’ Source[ {source_id, source_type ∈ schema|tabular|unstructured, name, location_ref} β†’ Table[ {table_id, name, row_count, foreign_keys[]} β†’ Column[ {column_id, name, data_type, nullable, pii_flag, sample_values|null, stats} ] ] ]`. PII columns have `sample_values: null` so real values never enter prompts.
@@ -268,25 +303,40 @@ copies disagree with the current code on:
268
  | Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
269
  | `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
270
  | Report source | (newer invariant) "from records, never chat history" | confirmed: generator reads `AnalysisRecord`s |
 
271
 
272
  ---
273
 
274
  ## 12. dedorch migration β€” current state
275
 
276
  The Python DB is moving from `dataeyond` β†’ **dedorch** (Go owns dedorch migrations; Python is
277
- consumer-only). Current state:
278
-
279
- - Base tables already match dedorch.
280
- - The analysis-family models have been **renamed to dedorch** on `pr/3`: `analysis` (was
281
- `analysis_states`, uuid ids), `data_sources` (was `analysis_data_sources`), `reports` (was
282
- `analysis_reports`, flattened to title + markdown content + version).
283
- - `analysis_records` (the slow-path structured output) has **no dedorch home** β€” it remains a
284
- Python-owned jsonb table.
285
- - The connection-string cutover (paired with `SKIP_INIT_DB`) is a coordinated step that has not
286
- happened yet; Python still creates tables on startup until then.
287
-
288
- The dedorch migrations themselves live outside the three checked-out repos (Harry owns them), so the
289
- dedorch table shapes are asserted by the Python model docstrings, not visible in the Go repo here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
  ---
292
 
 
2
 
3
  **Audience:** teammates onboarding onto the Python repo (`Agentic-Service-Data-Eyond-Catalog`).
4
  **Scope:** what the code does **right now** (branch `pr/4`, ticket KM-652). Describes current state only β€” no roadmap or to-dos.
5
+ **Snapshot date:** 2026-06-25. **Cross-repo update 2026-06-29:** Β§2/Β§8/Β§11/Β§12 re-verified against
6
+ the **Go source** (`Orchestrator-Agent-Service`), not its docs. The Go service has moved well past its
7
+ own (uncommitted, stale) design docs: it now hosts the **dedorch SQL migrations** in-repo and a full
8
+ **`/api/v1/analyses` + `/api/v1/skills`** REST surface. Go does **not** call Python yet β€” those skills
9
+ are placeholders (see Β§12).
10
 
11
  > This file is grounded in the source, not the older design docs. Where the two
12
  > disagree, the code wins β€” see [Β§11 Doc-vs-code](#11-where-the-older-docs-are-stale).
13
  > `REPO_CONTEXT.md` / `ARCHITECTURE.md` are the original Phase-2 design docs and are
14
  > stale on the router, joins, and the analysis/report stack.
15
 
16
+ > 🚧 **Direction update 2026-06-30 (pr/5 β€” DECIDED Β· IN PROGRESS).** The 30 June checkpoint locked a
17
+ > restructure (contract: [API_ENDPOINTS_RESTRUCTURE.md](API_ENDPOINTS_RESTRUCTURE.md); live tracker:
18
+ > [DEV_PLAN Β§0](DEV_PLAN.md)). **Python is becoming a generation/AI-only service** β€” Go owns the full
19
+ > analysis lifecycle *and* the data-plane endpoints. Scope:
20
+ > - **Unwired from `main` + Swagger** (router files kept, *not* deleted): `analysis` CRUD, `room`, `db_client`, `document`, `data_catalog`, `users`/login. **βœ… DONE β€” KM-686, commit `0b2d678`** (so the Β§7 rows for these are now commented out of `main.py`).
21
+ > - **AI surface that stays live:** `chat` β†’ **`POST /api/v2/chat/stream`** (explicit **`analysis_id`**, not `room_id`); the skills regroup under **`/api/v1/tools/`** (`list` Β· `help` Β· `report`); plus a **new `GET /api/v1/observability`** (Responsible-AI provenance per answer, backed by a provenance store β€” shape TBD). **⬜ pending.**
22
+ > - **Only `chat/stream` moves to `/api/v2`;** everything else stays `/api/v1`.
23
+ >
24
+ > Β§2/Β§4/Β§7 below still describe the **pre-restructure wiring** except the unwire above, which has landed.
25
+
26
  ---
27
 
28
  ## 1. The product in one paragraph
 
45
  | Repo | Role | We edit? |
46
  |---|---|---|
47
  | **Python** β€” `Agentic-Service-Data-Eyond-Catalog` (this repo) | The agentic LLM service: router, gate, skills, slow analytical path, structured query engine, unstructured RAG, report generation, analysis-session state. FastAPI + async SQLAlchemy + LangChain + Azure GPT-4o. | **Yes β€” the only repo we edit.** |
48
+ | **Go** β€” `Orchestrator-Agent-Service` | Gateway / data plane: auth/JWT, documents (Azure Blob + CSV/XLSXβ†’Parquet + embeddings), database_clients (Fernet creds), **catalog ingestion** (moved into Go, KM-578/590), **all dedorch SQL migrations** (now embedded in the Go repo: `internal/repository/postgres/migrations/0001–0004`), and the **full analysis-lifecycle REST surface** (`/api/v1/analyses` CRUD + messages + reports, `/api/v1/skills`). The **interview agent and chat-rooms are deprecated β†’ HTTP 410** (`internal/api/deprecation.go`). | Reference only. |
49
  | **FE** β€” `E2E-Frontend-Data-Eyond` | React/Vite SPA. Talks to Go for everything and to Python only for chat streaming. | Reference only. |
50
 
51
+ > **Β» pr/5 (decided, not yet in code):** Python's non-AI endpoints (analysis CRUD, `room`, `document`,
52
+ > `db_client`, `data_catalog`, `users`/login) are being **unwired** β€” Python keeps only the
53
+ > generation/AI surface (chat, tools: `help`/`report`/`list`, observability). See the Direction-update banner.
54
+
55
  Shared infra: **Postgres** (app tables + `data_catalog` jsonb + PGVector `langchain_pg_embedding`), **Azure Blob**, and (Python-only) **Redis**.
56
 
57
  ---
 
77
  Entry: `POST /api/v1/chat/stream` (`src/api/v1/chat.py`) β†’ `ChatHandler.handle(...)`
78
  (`src/agents/chat_handler.py`). One shared `ChatHandler` per process keeps the Azure clients warm.
79
 
80
+ > **Β» pr/5:** this endpoint moves to **`POST /api/v2/chat/stream`** with an explicit **`analysis_id`**
81
+ > field (replacing `room_id`), and the observability detail (planning / tool I/O / sources) moves out of
82
+ > the stream to a separate `GET /api/v1/observability` call. See the Direction-update banner.
83
+
84
  ```
85
  POST /chat/stream { user_id, room_id, message }
86
  β”‚ (analysis_id == room_id β€” one session = one analysis = one chat room)
 
153
 
154
  ## 7. API surface (this repo, all under `/api/v1`)
155
 
156
+ > **Β» pr/5 (decided, not yet in code):** chat β†’ `/api/v2/chat/stream` (`analysis_id`); `/tools` splits
157
+ > into `/tools/list` + `/tools/help` + `/tools/report`; new `/api/v1/observability`; and the
158
+ > analysis-CRUD / `room` / `users` / `document` / `db_client` / `data_catalog` rows are unwired from
159
+ > `main` + Swagger. See the Direction-update banner.
160
+
161
  | Endpoint | Purpose | Caller |
162
  |---|---|---|
163
  | `POST /chat/stream` | Main chat SSE (router β†’ dispatch) | FE β†’ Go β†’ Python (the only FEβ†’Python call today) |
 
180
  | `documents`, `databases` | uploads + DB creds (Fernet-encrypted) | Go ingestion | executor cred resolution |
181
  | `data_catalog` | per-user jsonb `Catalog` (Source β†’ Table β†’ Column) | Go ingestion / Python pipeline | CatalogReader, planner, tools |
182
  | `langchain_pg_embedding` | PGVector document chunks | Go ingestion | DocumentRetriever |
183
+ | `report_inputs` *(was `analysis_records`)* | jsonb `AnalysisRecord`, one per slow-path run; **Python-owned** | slow path | ReportGenerator, report readiness |
184
+ | `analyses` *(dedorch, plural)* | uuid `id`, `user_id`, `analysis_title`, `objective`, `business_questions` jsonb, `status` (active\|inactive), `data_bind`(+`data_bind_version`), `report_id`, `report_collection` β€” **defined by Go migrations**; `problem_statement`/`problem_validated`/`owner_id` already **dropped** there (`0003`/`0004`) | Go `/api/v1/analyses`; Python state store | gate (no-op), Help, report |
185
+ | `reports` *(dedorch)* | uuid, `analysis_id`, `user_id`, `title` + markdown `content` + `version` (UNIQUE per analysis) | Go + Python ReportStore | report API |
186
+ | `data_sources` *(dedorch)* | per-analysis binding; `reference_id` = catalog source_id; `type ∈ document\|database` | Go `/analyses/{id}/data-bind` (+ Python `/analysis/create`) | structured-flow scoping, report appendix |
187
+ | `analyses_messages` *(dedorch)* | the analysis chat room (`role ∈ user\|ai`); replaces deprecated `rooms`/`chat_messages` | Go `/analyses/{id}/messages` | Python chat path **not yet migrated here** (§12) |
188
+
189
+ > ⚠️ **Python ORM ↔ dedorch drift (verified 2026-06-29).** Python's `AnalysisStateRow` + `state_store.py`
190
+ > still model **`problem_statement` / `problem_validated`** and do **not** carry `objective` /
191
+ > `business_questions`, but the Go migrations have already dropped the former and added the latter.
192
+ > Pre-cutover this is harmless (Python runs `create_all` on its own copy); **post-`SKIP_INIT_DB`**, when
193
+ > Python reads dedorch directly, ORM column selection on the dropped columns will break. Reconcile the
194
+ > Python model before the connection-string cutover.
195
 
196
  **Catalog shape** (the jsonb in `data_catalog`):
197
  `Catalog β†’ Source[ {source_id, source_type ∈ schema|tabular|unstructured, name, location_ref} β†’ Table[ {table_id, name, row_count, foreign_keys[]} β†’ Column[ {column_id, name, data_type, nullable, pii_flag, sample_values|null, stats} ] ] ]`. PII columns have `sample_values: null` so real values never enter prompts.
 
303
  | Analysis / report / gate / slow path | "Phase 2 spine only" | All built and present |
304
  | `analysis_id` | open question | resolved: **`analysis_id == room_id`** |
305
  | Report source | (newer invariant) "from records, never chat history" | confirmed: generator reads `AnalysisRecord`s |
306
+ | Go service scope | "interview agent + ingestion; dedorch migrations live outside the repos" | Go now hosts the **dedorch migrations in-repo** + a full **`/api/v1/analyses` + `/api/v1/skills`** REST surface; interview/rooms **deprecated (410)**. (Go's own `PROJECT_SUMMARY.md`/`REPO_CONTEXT.md` are uncommitted + stale.) |
307
 
308
  ---
309
 
310
  ## 12. dedorch migration β€” current state
311
 
312
  The Python DB is moving from `dataeyond` β†’ **dedorch** (Go owns dedorch migrations; Python is
313
+ consumer-only). State **re-verified against the Go source 2026-06-29**:
314
+
315
+ - **The dedorch migrations now live IN the Go repo** β€” embedded SQL at
316
+ `internal/repository/postgres/migrations/0001_create_core_schema.sql … 0004_replace_chat_with_analysis_scope.sql`,
317
+ run on startup by `RunMigrations`. (This corrects the earlier note that the migrations were
318
+ invisible / asserted only by Python docstrings.) The full schema is now readable there.
319
+ - **Go owns the analysis family end-to-end.** `analyses` / `analyses_messages` / `reports` /
320
+ `data_sources` / `message_sources` / `data_catalog` are created by Go migrations and served by a
321
+ full REST surface: `internal/api/analysis.go` (CRUD + `data-bind` w/ optimistic `expected_version`
322
+ + messages + reports) and `internal/api/skills.go`. `analyses` already has the **pivot shape**
323
+ (`objective` + `business_questions`, `status`, `data_bind`/`_version`, `report_collection`) and has
324
+ **dropped** `problem_statement`/`problem_validated`/`owner_id`. Migration `0004` renames the legacy
325
+ `rooms`/`chat_messages`/`interview_*` tables to `zdeprecated_*`.
326
+ - **`report_inputs`** (the slow-path structured output, formerly `analysis_records`) stays
327
+ **Python-owned**; its finalized schema goes to Harry so the dedorch migration creates it post-cutover.
328
+ - The connection-string cutover (paired with `SKIP_INIT_DB`) **has not happened yet**; Python still
329
+ runs `create_all` on its own models until then.
330
+
331
+ **⚠️ Integration gap (verified β€” the big one).** Go's `/api/v1/analyses` and `/api/v1/skills`
332
+ (`help` / `report`) are **placeholders that return dummy data** β€” the `SendMessage` / `GenerateReport`
333
+ handlers and the skills handler explicitly note *"placeholder integrasi backend agentic … will be
334
+ replaced by the external skills service."* **Go currently never calls Python's `/chat/stream`,
335
+ `/report`, or any skill** (no outbound HTTP to the agentic service exists in the Go source). So today
336
+ there are **two parallel, unconnected analysis stacks**: Go's self-contained placeholder lifecycle
337
+ (gate: β‰₯3 user messages; AI replies are canned) and Python's real agentic spine (router β†’ slow path β†’
338
+ records-based report; floor: β‰₯1 `analyze_*` success). Wiring Go β†’ Python is the open integration work
339
+ (DEV_PLAN #7/#18/#25), plus reconciling the two different report gates.
340
 
341
  ---
342