openapi.yaml file-level

at sha256:8 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:4 fix(security): pin patched transitive deps to clear Dependabot moderate… · aaronrene · Jun 11, 2026
1 openapi: 3.0.3
2 info:
3 title: Knowtation Hub API
4 description: REST API for the Knowtation Hub (vault read/write, proposals, capture). Same contract as CLI/MCP where applicable.
5 version: 1.0.0
6 links:
7 - description: API contract (human-readable)
8 url: ./HUB-API.md
9
10 servers:
11 - url: /api/v1
12 description: Relative to Hub base URL (e.g. https://hub.example.com)
13
14 tags:
15 - name: Health
16 - name: Auth
17 - name: Notes
18 - name: Search
19 - name: Proposals
20 - name: Capture
21
22 security:
23 - BearerAuth: []
24
25 paths:
26 /health:
27 get:
28 tags: [Health]
29 summary: Health check
30 security: []
31 responses:
32 '200':
33 description: Hub is up
34 content:
35 application/json:
36 schema: { type: object, properties: { ok: { type: boolean } }, required: [ok] }
37
38 /api/v1/auth/session:
39 get:
40 tags: [Auth]
41 summary: C7 Session introspection — current identity and scopes
42 description: |
43 Returns the verified identity and derived API scopes for the caller. Accepts only a
44 `Bearer` JWT in the `Authorization` header — no cookie required, making it safe to call
45 cross-origin from Scooling or any other consumer.
46
47 The response is derived entirely from the signed token — no database call is made.
48 Scopes are role-derived today (C4 will replace this with per-user explicit grants without
49 changing the response shape).
50 security:
51 - bearerAuth: []
52 responses:
53 '200':
54 description: Verified session
55 content:
56 application/json:
57 schema:
58 type: object
59 required: [sub, provider, id, name, role, iat, exp, scopes]
60 properties:
61 sub:
62 type: string
63 description: Canonical user ID (`provider:id`)
64 example: google:104164334692309763642
65 provider:
66 type: string
67 enum: [google, github]
68 id:
69 type: string
70 description: Provider-specific user ID
71 name:
72 type: string
73 description: Display name (empty for refresh-path tokens)
74 role:
75 type: string
76 enum: [admin, member]
77 iat:
78 type: integer
79 description: Token issued-at (Unix seconds)
80 exp:
81 type: integer
82 description: Token expires-at (Unix seconds)
83 scopes:
84 type: array
85 items: { type: string }
86 description: Derived API scopes. `admin` role → `[vault:read, vault:write, admin]`; `member` → `[vault:read, vault:write]`
87 example: [vault:read, vault:write]
88 '401':
89 description: Missing, expired, or tampered token
90 content:
91 application/json:
92 schema:
93 type: object
94 properties:
95 error: { type: string }
96 code: { type: string, enum: [UNAUTHORIZED] }
97
98 /auth/providers:
99 get:
100 tags: [Auth]
101 summary: OAuth providers configured
102 security: []
103 responses:
104 '200':
105 content:
106 application/json:
107 schema:
108 type: object
109 properties:
110 google: { type: boolean }
111 github: { type: boolean }
112
113 /notes/facets:
114 get:
115 tags: [Notes]
116 summary: Projects, tags, folders for filter dropdowns
117 responses:
118 '200':
119 content:
120 application/json:
121 schema:
122 type: object
123 properties:
124 projects: { type: array, items: { type: string } }
125 tags: { type: array, items: { type: string } }
126 folders: { type: array, items: { type: string } }
127
128 /notes:
129 get:
130 tags: [Notes]
131 summary: List notes
132 parameters:
133 - name: folder
134 in: query
135 schema: { type: string }
136 - name: project
137 in: query
138 schema: { type: string }
139 - name: tag
140 in: query
141 schema: { type: string }
142 - name: since
143 in: query
144 schema: { type: string }
145 - name: until
146 in: query
147 schema: { type: string }
148 - name: limit
149 in: query
150 schema: { type: integer, minimum: 0, maximum: 100 }
151 - name: offset
152 in: query
153 schema: { type: integer, minimum: 0 }
154 - name: order
155 in: query
156 schema: { type: string, enum: [date, date-asc] }
157 - name: fields
158 in: query
159 schema: { type: string, enum: [path, path+metadata, full] }
160 - name: count_only
161 in: query
162 schema: { type: boolean }
163 responses:
164 '200':
165 content:
166 application/json:
167 schema:
168 oneOf:
169 - type: object
170 properties:
171 notes: { type: array, items: { $ref: '#/components/schemas/NoteListItem' } }
172 total: { type: integer }
173 - type: object
174 properties:
175 total: { type: integer }
176 post:
177 tags: [Notes]
178 summary: Write or update a note
179 requestBody:
180 required: true
181 content:
182 application/json:
183 schema:
184 type: object
185 required: [path]
186 properties:
187 path: { type: string }
188 body: { type: string }
189 frontmatter: { type: object }
190 append: { type: boolean }
191 responses:
192 '200':
193 content:
194 application/json:
195 schema: { type: object, properties: { path: { type: string }, written: { type: boolean } } }
196 '400':
197 '500':
198 content:
199 application/json:
200 schema: { $ref: '#/components/schemas/Error' }
201
202 /notes/{path}:
203 get:
204 tags: [Notes]
205 summary: Get one note by path
206 parameters:
207 - name: path
208 in: path
209 required: true
210 schema: { type: string }
211 responses:
212 '200':
213 content:
214 application/json:
215 schema: { $ref: '#/components/schemas/NoteFull' }
216 '404':
217 content:
218 application/json:
219 schema: { $ref: '#/components/schemas/Error' }
220
221 /note-outline:
222 get:
223 tags: [Notes]
224 summary: Body-free NoteOutline headings for one note
225 description: >
226 Returns knowtation.note_outline/v1 metadata for one authorized vault-relative note.
227 The response excludes note body text, snippets, full frontmatter, absolute paths,
228 provider payloads, MCP resource URIs, summaries, vectors, OCR, PageIndex output,
229 persistence records, and write-back state.
230 parameters:
231 - name: path
232 in: query
233 required: true
234 schema: { type: string }
235 description: Vault-relative Markdown note path.
236 responses:
237 '200':
238 content:
239 application/json:
240 schema: { $ref: '#/components/schemas/NoteOutline' }
241 '400':
242 content:
243 application/json:
244 schema: { $ref: '#/components/schemas/Error' }
245 '401':
246 content:
247 application/json:
248 schema: { $ref: '#/components/schemas/Error' }
249 '403':
250 content:
251 application/json:
252 schema: { $ref: '#/components/schemas/Error' }
253 '404':
254 content:
255 application/json:
256 schema: { $ref: '#/components/schemas/Error' }
257 '502':
258 content:
259 application/json:
260 schema: { $ref: '#/components/schemas/Error' }
261
262 /document-tree:
263 get:
264 tags: [Notes]
265 summary: Body-free DocumentTree heading hierarchy for one note
266 description: >
267 Returns knowtation.document_tree/v0 metadata for one authorized vault-relative note.
268 The response excludes note body text, snippets, full frontmatter, absolute paths,
269 provider payloads, MCP resource URIs, summaries, vectors, OCR, PageIndex output,
270 persistence records, sidecars, LLM calls, and write-back state.
271 parameters:
272 - name: path
273 in: query
274 required: true
275 schema: { type: string }
276 description: Vault-relative Markdown note path.
277 responses:
278 '200':
279 content:
280 application/json:
281 schema: { $ref: '#/components/schemas/DocumentTree' }
282 '400':
283 content:
284 application/json:
285 schema: { $ref: '#/components/schemas/Error' }
286 '401':
287 content:
288 application/json:
289 schema: { $ref: '#/components/schemas/Error' }
290 '403':
291 content:
292 application/json:
293 schema: { $ref: '#/components/schemas/Error' }
294 '404':
295 content:
296 application/json:
297 schema: { $ref: '#/components/schemas/Error' }
298 '502':
299 content:
300 application/json:
301 schema: { $ref: '#/components/schemas/Error' }
302
303 /metadata-facets:
304 get:
305 tags: [Notes]
306 summary: Body-free MetadataFacets hints for one note
307 description: >
308 Returns knowtation.metadata_facets/v0 metadata for one authorized vault-relative note.
309 The response excludes note body text, snippets, full frontmatter, absolute paths,
310 provider payloads, MCP resource URIs, summaries, labels, vectors, OCR, PageIndex output,
311 media metadata, memory events, persistence records, sidecars, LLM calls, and write-back state.
312 parameters:
313 - name: path
314 in: query
315 required: true
316 schema: { type: string }
317 description: Vault-relative Markdown note path.
318 responses:
319 '200':
320 content:
321 application/json:
322 schema: { $ref: '#/components/schemas/MetadataFacets' }
323 '400':
324 content:
325 application/json:
326 schema: { $ref: '#/components/schemas/Error' }
327 '401':
328 content:
329 application/json:
330 schema: { $ref: '#/components/schemas/Error' }
331 '403':
332 content:
333 application/json:
334 schema: { $ref: '#/components/schemas/Error' }
335 '404':
336 content:
337 application/json:
338 schema: { $ref: '#/components/schemas/Error' }
339 '502':
340 content:
341 application/json:
342 schema: { $ref: '#/components/schemas/Error' }
343
344 /section-source:
345 get:
346 tags: [Notes]
347 summary: Body-free SectionSource metadata for one note
348 description: >
349 Returns knowtation.section_source/v0 metadata for one authorized vault-relative note.
350 The response excludes note body text, section body text, snippets, full frontmatter,
351 line ranges, byte offsets, section body lengths, absolute paths, raw canister payloads,
352 provider payloads, and MCP resource URIs.
353 parameters:
354 - name: path
355 in: query
356 required: true
357 schema: { type: string }
358 description: Vault-relative Markdown note path.
359 responses:
360 '200':
361 content:
362 application/json:
363 schema: { $ref: '#/components/schemas/SectionSource' }
364 '400':
365 content:
366 application/json:
367 schema: { $ref: '#/components/schemas/Error' }
368 '401':
369 content:
370 application/json:
371 schema: { $ref: '#/components/schemas/Error' }
372 '403':
373 content:
374 application/json:
375 schema: { $ref: '#/components/schemas/Error' }
376 '404':
377 content:
378 application/json:
379 schema: { $ref: '#/components/schemas/Error' }
380 '502':
381 content:
382 application/json:
383 schema: { $ref: '#/components/schemas/Error' }
384
385 /index:
386 post:
387 tags: [Notes]
388 summary: Re-run indexer (vault to vector store)
389 responses:
390 '200':
391 content:
392 application/json:
393 schema:
394 type: object
395 properties:
396 ok: { type: boolean }
397 notesProcessed: { type: integer }
398 chunksIndexed: { type: integer }
399 vectors_deleted: { type: integer, description: Rows removed for this vault before upsert (hosted sqlite-vec) }
400 '500':
401 content:
402 application/json:
403 schema: { $ref: '#/components/schemas/Error' }
404
405 /export:
406 post:
407 tags: [Notes]
408 summary: Export one note to content (returns body + filename for client download)
409 requestBody:
410 required: true
411 content:
412 application/json:
413 schema:
414 type: object
415 required: [path]
416 properties:
417 path: { type: string }
418 format: { type: string, enum: [md, html] }
419 responses:
420 '200':
421 content:
422 application/json:
423 schema:
424 type: object
425 properties:
426 content: { type: string }
427 filename: { type: string }
428 '400':
429 content:
430 application/json:
431 schema: { $ref: '#/components/schemas/Error' }
432 '404':
433 content:
434 application/json:
435 schema: { $ref: '#/components/schemas/Error' }
436
437 /import:
438 post:
439 tags: [Notes]
440 summary: Import from uploaded file or ZIP (multipart: source_type; file except for google-sheets; optional project, tags, spreadsheet_id for google-sheets)
441 requestBody:
442 required: true
443 content:
444 multipart/form-data:
445 schema:
446 type: object
447 required: [source_type]
448 properties:
449 source_type:
450 type: string
451 description: Importer id. For google-sheets, omit file and set spreadsheet_id; optional sheets_range (A1 notation). See lib/import-source-types.mjs.
452 file: { type: string, format: binary, description: Required for all importers except google-sheets. }
453 spreadsheet_id:
454 type: string
455 description: Required when source_type is google-sheets (id from the Google Sheets URL).
456 sheets_range:
457 type: string
458 description: Optional for google-sheets; A1 range. Omit to read the first sheet from A1.
459 project: { type: string }
460 output_dir: { type: string }
461 tags: { type: string }
462 responses:
463 '200':
464 content:
465 application/json:
466 schema:
467 type: object
468 properties:
469 imported: { type: array, items: { type: object } }
470 count: { type: integer }
471 '400':
472 content:
473 application/json:
474 schema: { $ref: '#/components/schemas/Error' }
475 '500':
476 content:
477 application/json:
478 schema: { $ref: '#/components/schemas/Error' }
479
480 /import-url:
481 post:
482 tags: [Notes]
483 summary: Import from a public https URL (JSON body; editor/admin)
484 requestBody:
485 required: true
486 content:
487 application/json:
488 schema:
489 type: object
490 required: [url]
491 properties:
492 url: { type: string, description: 'Full https URL' }
493 mode: { type: string, enum: [auto, bookmark, extract], description: 'Capture mode (default auto)' }
494 project: { type: string }
495 output_dir: { type: string }
496 tags: { oneOf: [{ type: string }, { type: array, items: { type: string } }] }
497 responses:
498 '200':
499 content:
500 application/json:
501 schema:
502 type: object
503 properties:
504 imported: { type: array, items: { type: object } }
505 count: { type: integer }
506 '400':
507 content:
508 application/json:
509 schema: { $ref: '#/components/schemas/Error' }
510 '500':
511 content:
512 application/json:
513 schema: { $ref: '#/components/schemas/Error' }
514
515 /settings:
516 get:
517 tags: [Notes]
518 summary: Config status for Settings UI (no secrets)
519 responses:
520 '200':
521 content:
522 application/json:
523 schema:
524 type: object
525 properties:
526 vault_path_display: { type: string }
527 vault_git:
528 type: object
529 properties:
530 enabled: { type: boolean }
531 has_remote: { type: boolean }
532 auto_commit: { type: boolean }
533 auto_push: { type: boolean }
534
535 /vault/sync:
536 post:
537 tags: [Notes]
538 summary: Manual vault backup (git add, commit, push)
539 description: Self-hosted runs local git. Hosted (bridge) pushes notes as Markdown plus `.knowtation/backup/v1/snapshot.json` with full proposals.
540 responses:
541 '200':
542 content:
543 application/json:
544 schema:
545 type: object
546 properties:
547 ok: { type: boolean }
548 message: { type: string }
549 notesCount: { type: integer, description: Hosted bridge only }
550 proposalsCount: { type: integer, description: Hosted bridge only }
551 '400':
552 content:
553 application/json:
554 schema: { $ref: '#/components/schemas/Error' }
555 '500':
556 content:
557 application/json:
558 schema: { $ref: '#/components/schemas/Error' }
559
560 /search:
561 post:
562 tags: [Search]
563 summary: Vault search (semantic or keyword)
564 requestBody:
565 required: true
566 content:
567 application/json:
568 schema:
569 type: object
570 required: [query]
571 properties:
572 query: { type: string }
573 mode: { type: string, enum: [semantic, keyword], description: Omitted or semantic = vector search; keyword = substring/token match on note text }
574 match: { type: string, enum: [phrase, all_terms], description: Keyword only; phrase = full query substring; all_terms = every token must appear }
575 folder: { type: string }
576 project: { type: string }
577 tag: { type: string }
578 since: { type: string }
579 until: { type: string }
580 chain: { type: string }
581 entity: { type: string }
582 episode: { type: string }
583 limit: { type: integer }
584 order: { type: string }
585 fields: { type: string }
586 content_scope: { type: string, enum: [notes, approval_logs], description: Narrow to normal notes vs approvals/ logs }
587 snippetChars: { type: integer }
588 count_only: { type: boolean }
589 countOnly: { type: boolean }
590 responses:
591 '200':
592 content:
593 application/json:
594 schema:
595 type: object
596 properties:
597 results: { type: array, items: { $ref: '#/components/schemas/SearchResult' } }
598 query: { type: string }
599 mode: { type: string, enum: [semantic, keyword] }
600 count: { type: integer, description: Present when count_only keyword search }
601 '400':
602 content:
603 application/json:
604 schema: { $ref: '#/components/schemas/Error' }
605
606 /proposals:
607 get:
608 tags: [Proposals]
609 summary: List proposals
610 parameters:
611 - name: status
612 in: query
613 schema: { type: string }
614 - name: limit
615 in: query
616 schema: { type: integer }
617 - name: offset
618 in: query
619 schema: { type: integer }
620 - name: label
621 in: query
622 description: Match if proposal labels include this string (case-insensitive)
623 schema: { type: string }
624 - name: source
625 in: query
626 schema: { type: string }
627 - name: path_prefix
628 in: query
629 schema: { type: string }
630 - name: evaluation_status
631 in: query
632 description: Filter by evaluation_status (none, pending, passed, failed, needs_changes)
633 schema: { type: string }
634 - name: review_queue
635 in: query
636 description: Exact match on proposal review_queue
637 schema: { type: string }
638 - name: review_severity
639 in: query
640 description: standard or elevated
641 schema: { type: string }
642 responses:
643 '200':
644 content:
645 application/json:
646 schema:
647 type: object
648 properties:
649 proposals: { type: array, items: { $ref: '#/components/schemas/Proposal' } }
650 total: { type: integer }
651 post:
652 tags: [Proposals]
653 summary: Create proposal
654 requestBody:
655 content:
656 application/json:
657 schema:
658 type: object
659 properties:
660 path: { type: string }
661 body: { type: string }
662 frontmatter: { type: object }
663 intent: { type: string }
664 base_state_id: { type: string }
665 external_ref: { type: string }
666 labels: { type: array, items: { type: string } }
667 source: { type: string }
668 responses:
669 '201':
670 content:
671 application/json:
672 schema:
673 type: object
674 properties:
675 proposal_id: { type: string }
676 path: { type: string }
677 status: { type: string, enum: [proposed] }
678 '400':
679
680 /proposals/{id}:
681 get:
682 tags: [Proposals]
683 summary: Get one proposal
684 parameters:
685 - name: id
686 in: path
687 required: true
688 schema: { type: string }
689 responses:
690 '200':
691 content:
692 application/json:
693 schema: { $ref: '#/components/schemas/ProposalDetail' }
694 '404':
695
696 /proposals/{id}/review-hints:
697 post:
698 tags: [Proposals]
699 summary: Store async LLM review hints (canister; not a merge gate)
700 parameters:
701 - name: id
702 in: path
703 required: true
704 schema: { type: string }
705 requestBody:
706 content:
707 application/json:
708 schema:
709 type: object
710 properties:
711 review_hints: { type: string }
712 review_hints_model: { type: string }
713 responses:
714 '200':
715 content:
716 application/json:
717 schema:
718 type: object
719 properties:
720 proposal_id: { type: string }
721 ok: { type: boolean }
722
723 /proposals/{id}/evaluation:
724 post:
725 tags: [Proposals]
726 summary: Submit human evaluation (admin or evaluator)
727 parameters:
728 - name: id
729 in: path
730 required: true
731 schema: { type: string }
732 requestBody:
733 content:
734 application/json:
735 schema:
736 type: object
737 required: [outcome]
738 properties:
739 outcome:
740 type: string
741 enum: [pass, fail, needs_changes]
742 checklist:
743 type: array
744 items:
745 type: object
746 properties:
747 id: { type: string }
748 passed: { type: boolean }
749 grade: { type: string }
750 comment: { type: string }
751 responses:
752 '200':
753 content:
754 application/json:
755 schema: { $ref: '#/components/schemas/ProposalDetail' }
756 '400':
757 '404':
758
759 /proposals/{id}/approve:
760 post:
761 tags: [Proposals]
762 summary: Apply proposal to vault
763 parameters:
764 - name: id
765 in: path
766 required: true
767 schema: { type: string }
768 requestBody:
769 content:
770 application/json:
771 schema:
772 type: object
773 properties:
774 base_state_id: { type: string }
775 waiver_reason:
776 type: string
777 description: Admin override when evaluation is not passed (min length 3 after trim)
778 external_ref:
779 type: string
780 description: Optional cross-system lineage id (e.g. Muse); server may resolve via MUSE_URL when omitted
781 responses:
782 '200':
783 content:
784 application/json:
785 schema:
786 type: object
787 properties:
788 proposal_id: { type: string }
789 status: { type: string, enum: [approved] }
790 external_ref: { type: string }
791 '403':
792 description: EVALUATION_REQUIRED — pass evaluation or provide waiver_reason
793 '409':
794 description: base_state_id mismatch (CONFLICT)
795
796 /proposals/{id}/enrich:
797 post:
798 tags: [Proposals]
799 summary: Optional LLM summary and suggested labels (KNOWTATION_HUB_PROPOSAL_ENRICH=1)
800 parameters:
801 - name: id
802 in: path
803 required: true
804 schema: { type: string }
805 responses:
806 '200':
807 content:
808 application/json:
809 schema: { $ref: '#/components/schemas/ProposalDetail' }
810 '400':
811 description: >-
812 ICP canister — suggested_labels_json or assistant_suggested_frontmatter_json is valid JSON
813 but exceeds max length (4000 / 14000 characters) after validation.
814 '404':
815
816 /proposals/{id}/discard:
817 post:
818 tags: [Proposals]
819 summary: Discard proposal
820 parameters:
821 - name: id
822 in: path
823 required: true
824 schema: { type: string }
825 responses:
826 '200':
827 content:
828 application/json:
829 schema:
830 type: object
831 properties:
832 proposal_id: { type: string }
833 status: { type: string, enum: [discarded] }
834
835 /capture:
836 post:
837 tags: [Capture]
838 summary: Ingest message into vault inbox (webhook-style)
839 description: Same contract as capture-webhook. If CAPTURE_WEBHOOK_SECRET is set, require X-Webhook-Secret header.
840 security: []
841 requestBody:
842 content:
843 application/json:
844 schema:
845 type: object
846 required: [body]
847 properties:
848 body: { type: string }
849 source_id: { type: string }
850 source: { type: string }
851 project: { type: string }
852 tags: { type: array, items: { type: string } }
853 responses:
854 '200':
855 content:
856 application/json:
857 schema: { type: object, properties: { ok: { type: boolean }, path: { type: string } } }
858 '400':
859
860 components:
861 securitySchemes:
862 BearerAuth:
863 type: http
864 scheme: bearer
865 bearerFormat: JWT
866
867 schemas:
868 Error:
869 type: object
870 properties:
871 error: { type: string }
872 code: { type: string }
873
874 NoteListItem:
875 type: object
876 properties:
877 path: { type: string }
878 title: { type: string, nullable: true }
879 project: { type: string, nullable: true }
880 tags: { type: array, items: { type: string } }
881 date: { type: string, nullable: true }
882
883 NoteFull:
884 type: object
885 properties:
886 path: { type: string }
887 frontmatter: { type: object }
888 body: { type: string }
889
890 SearchResult:
891 type: object
892 properties:
893 path: { type: string }
894 snippet: { type: string }
895 score: { type: number }
896 project: { type: string }
897 tags: { type: array, items: { type: string } }
898
899 NoteOutline:
900 type: object
901 required: [schema, path, headings, truncated]
902 properties:
903 schema:
904 type: string
905 enum: [knowtation.note_outline/v1]
906 path: { type: string }
907 title: { type: string, nullable: true }
908 headings:
909 type: array
910 maxItems: 500
911 items: { $ref: '#/components/schemas/NoteOutlineHeading' }
912 truncated: { type: boolean }
913
914 NoteOutlineHeading:
915 type: object
916 required: [level, text, id]
917 properties:
918 level: { type: integer, minimum: 1, maximum: 6 }
919 text: { type: string }
920 id: { type: string }
921
922 DocumentTree:
923 type: object
924 required: [schema, path, root, truncated]
925 properties:
926 schema:
927 type: string
928 enum: [knowtation.document_tree/v0]
929 path: { type: string }
930 title: { type: string, nullable: true }
931 root:
932 type: object
933 required: [children]
934 properties:
935 children:
936 type: array
937 maxItems: 500
938 items: { $ref: '#/components/schemas/DocumentTreeNode' }
939 truncated: { type: boolean }
940
941 DocumentTreeNode:
942 type: object
943 required: [id, level, text, children]
944 properties:
945 id: { type: string }
946 level: { type: integer, minimum: 1, maximum: 6 }
947 text: { type: string }
948 children:
949 type: array
950 items: { $ref: '#/components/schemas/DocumentTreeNode' }
951
952 MetadataFacets:
953 type: object
954 required: [schema, path, facets, inferred, truncated]
955 properties:
956 schema:
957 type: string
958 enum: [knowtation.metadata_facets/v0]
959 path: { type: string }
960 facets:
961 type: object
962 required: [project, tags, date, updated, causal_chain_id, entity, episode_id]
963 properties:
964 project: { type: string, nullable: true }
965 tags:
966 type: array
967 maxItems: 100
968 items: { type: string }
969 date: { type: string, nullable: true }
970 updated: { type: string, nullable: true }
971 causal_chain_id: { type: string, nullable: true }
972 entity:
973 type: array
974 maxItems: 100
975 items: { type: string }
976 episode_id: { type: string, nullable: true }
977 inferred:
978 type: object
979 required: [folder, source_type]
980 properties:
981 folder: { type: string, nullable: true }
982 source_type: { nullable: true, enum: [null] }
983 truncated: { type: boolean }
984
985 SectionSource:
986 type: object
987 required: [schema, path, sections, truncated]
988 properties:
989 schema:
990 type: string
991 enum: [knowtation.section_source/v0]
992 path: { type: string }
993 title: { type: string, nullable: true }
994 sections:
995 type: array
996 items: { $ref: '#/components/schemas/SectionSourceSection' }
997 truncated: { type: boolean }
998
999 SectionSourceSection:
1000 type: object
1001 required:
1002 - section_id
1003 - heading_id
1004 - level
1005 - heading_path
1006 - heading_text
1007 - child_section_ids
1008 - body_available
1009 - body_returned
1010 - snippet_returned
1011 properties:
1012 section_id: { type: string }
1013 heading_id: { type: string }
1014 level: { type: integer, minimum: 1, maximum: 6 }
1015 heading_path: { type: array, items: { type: string } }
1016 heading_text: { type: string }
1017 child_section_ids: { type: array, items: { type: string } }
1018 body_available: { type: boolean }
1019 body_returned: { type: boolean, enum: [false] }
1020 snippet_returned: { type: boolean, enum: [false] }
1021
1022 Proposal:
1023 type: object
1024 properties:
1025 proposal_id: { type: string }
1026 path: { type: string }
1027 status: { type: string }
1028 intent: { type: string }
1029 base_state_id: { type: string }
1030 external_ref: { type: string }
1031 vault_id: { type: string }
1032 proposed_by: { type: string }
1033 labels: { type: array, items: { type: string } }
1034 source: { type: string }
1035 suggested_labels: { type: array, items: { type: string } }
1036 assistant_notes: { type: string }
1037 assistant_model: { type: string }
1038 assistant_at: { type: string }
1039 created_at: { type: string }
1040 updated_at: { type: string }
1041 evaluation_status:
1042 type: string
1043 enum: [none, pending, passed, failed, needs_changes]
1044 evaluation_grade: { type: string }
1045 evaluation_comment: { type: string }
1046 evaluated_by: { type: string }
1047 evaluated_at: { type: string }
1048 evaluation_waiver:
1049 type: object
1050 nullable: true
1051 properties:
1052 by: { type: string }
1053 at: { type: string }
1054 reason: { type: string }
1055 review_queue: { type: string }
1056 review_severity: { type: string, enum: [standard, elevated] }
1057 auto_flag_reasons:
1058 type: array
1059 items: { type: string }
1060 auto_flag_reasons_json: { type: string, description: JSON array string on canister }
1061 review_hints: { type: string }
1062 review_hints_at: { type: string }
1063 review_hints_model: { type: string }
1064 assistant_suggested_frontmatter:
1065 type: object
1066 description: Normalized SPEC-aligned suggested note metadata from Enrich (object on GET); omitted or empty on older proposals
1067 additionalProperties: true
1068
1069 ProposalDetail:
1070 allOf:
1071 - { $ref: '#/components/schemas/Proposal' }
1072 - type: object
1073 properties:
1074 body: { type: string }
1075 frontmatter: { type: object }
1076 evaluation_checklist:
1077 type: array
1078 items:
1079 type: object
1080 properties:
1081 id: { type: string }
1082 label: { type: string }
1083 passed: { type: boolean }