Add document converter, seeder data structure, and project wiki
- ai-service/convert.py: converts Office/PDF files to markdown with frontmatter - database/seeders/data/: folder structure for themas, projects, documents, etc. - database/seeders/data/raw/: drop zone for Office/PDF files to convert - wiki/: project architecture, concepts, and knowledge graph documentation - Remove unused Laravel example tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
89
wiki/SCHEMA.md
Normal file
89
wiki/SCHEMA.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Project Wiki Schema
|
||||
|
||||
## Purpose
|
||||
LLM-maintained knowledge base for this project. The LLM writes and maintains everything. You read it (ideally in Obsidian). Knowledge compounds across sessions instead of being lost in chat history.
|
||||
|
||||
## Directory Structure
|
||||
```
|
||||
wiki/
|
||||
SCHEMA.md — this file (how to maintain the wiki)
|
||||
index.md — catalog of all pages with one-line summaries
|
||||
log.md — chronological record of updates
|
||||
overview.md — project overview and current status
|
||||
metrics.md — all numbers with provenance
|
||||
knowledge-graph.yaml — structured data, machine-queryable
|
||||
tools/ — search, lint, query scripts
|
||||
concepts/ — core ideas and mechanisms
|
||||
architecture/ — design decisions, system internals
|
||||
findings/ — honest results (what worked AND what didn't)
|
||||
sessions/ — per-session summaries
|
||||
```
|
||||
|
||||
## Page Conventions
|
||||
|
||||
### Frontmatter
|
||||
Every page starts with YAML frontmatter:
|
||||
```yaml
|
||||
---
|
||||
title: Page Title
|
||||
created: YYYY-MM-DD
|
||||
updated: YYYY-MM-DD
|
||||
status: proven | disproven | evolving | speculative
|
||||
tags: [tag1, tag2]
|
||||
sources: [path/to/file.py, commit abc1234]
|
||||
---
|
||||
```
|
||||
|
||||
### Status values
|
||||
- **proven**: tested and verified with evidence
|
||||
- **disproven**: tested and honestly shown NOT to work (document WHY)
|
||||
- **evolving**: partially working, boundary not fully mapped
|
||||
- **speculative**: proposed but not yet tested
|
||||
|
||||
### Cross-references
|
||||
Use `[[Page Name]]` Obsidian-style wikilinks.
|
||||
|
||||
### Contradictions
|
||||
When new evidence contradicts a prior claim, DON'T delete the old claim. Add:
|
||||
```
|
||||
> [!warning] Superseded
|
||||
> This was shown to be incorrect on YYYY-MM-DD. See [[New Finding]].
|
||||
```
|
||||
|
||||
### Honesty rule
|
||||
If something doesn't work, say so. If a result was a false positive, document how it was discovered. The wiki must be trustworthy.
|
||||
|
||||
## Operations
|
||||
|
||||
### Ingest (after a session or new source)
|
||||
1. Read outputs, commits, findings
|
||||
2. Update relevant pages
|
||||
3. Create new pages for new concepts
|
||||
4. Update `index.md`, `log.md`, `knowledge-graph.yaml`
|
||||
5. Check for contradictions with existing pages
|
||||
|
||||
### Query
|
||||
1. Use `python3 wiki/tools/query.py` for structured lookup
|
||||
2. Use `wiki/tools/search.sh` for full-text
|
||||
3. Read `index.md` to find relevant pages
|
||||
4. File valuable answers back into the wiki
|
||||
|
||||
### Lint (periodically)
|
||||
```bash
|
||||
bash wiki/tools/lint.sh
|
||||
```
|
||||
Checks: orphan pages, broken wikilinks, missing frontmatter, index completeness.
|
||||
|
||||
## Data Layer
|
||||
|
||||
- `knowledge-graph.yaml` — structured YAML with every metric and data point
|
||||
- `metrics.md` — human-readable dashboard
|
||||
- When adding new results, update BOTH the wiki page AND the knowledge graph
|
||||
- The knowledge graph is the single source of truth for numbers
|
||||
|
||||
## Source of Truth Hierarchy
|
||||
1. **Test results** (actual outputs) — highest authority
|
||||
2. **Code** (current state) — second authority
|
||||
3. **Knowledge graph** (knowledge-graph.yaml) — structured metrics
|
||||
4. **Wiki pages** — synthesis, may lag
|
||||
5. **Chat/memory** — ephemeral, may be stale
|
||||
112
wiki/architecture/domain-model.md
Normal file
112
wiki/architecture/domain-model.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: Domain Model
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [domain, models, relationships, lifecycle]
|
||||
sources: [app/Models/, app/Enums/, database/migrations/]
|
||||
---
|
||||
|
||||
# Domain Model
|
||||
|
||||
21 Eloquent models organized in 8 layers, with 14 enums for type safety.
|
||||
|
||||
## Entity Relationship Overview
|
||||
|
||||
```
|
||||
Thema (strategic theme)
|
||||
└── Speerpunt (focus area)
|
||||
└── Project (innovation project)
|
||||
├── Fase (lifecycle phase)
|
||||
├── Risico (risk)
|
||||
├── Commitment → Actie (action)
|
||||
├── Besluit (decision) → Commitment
|
||||
├── Budget → Besteding (expenditure)
|
||||
├── Document ← Tag (M:N)
|
||||
├── LessonLearned
|
||||
├── Afhankelijkheid (dependency, Project ↔ Project)
|
||||
├── Overdrachtsplan (handover plan)
|
||||
│ ├── Criterium
|
||||
│ └── Acceptatie
|
||||
└── ProjectUser (team membership, pivot)
|
||||
|
||||
User ──── Role (M:N)
|
||||
AuditLog (append-only, all mutations)
|
||||
KennisArtikel ← Tag (M:N)
|
||||
```
|
||||
|
||||
## Layer Details
|
||||
|
||||
### Strategic Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Thema** | naam, beschrijving, prioriteit, periode | has many Speerpunten |
|
||||
| **Speerpunt** | naam, beschrijving, eigenaar, status | belongs to Thema, has many Projects |
|
||||
| **RoadmapItem** | titel, start, eind, type, status | belongs to Thema |
|
||||
|
||||
### Project Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Project** | naam, beschrijving, eigenaar_id, status, prioriteit, startdatum, streef_einddatum | SoftDeletes, belongs to Speerpunt + User (eigenaar), has many of everything |
|
||||
| **Fase** | type (enum), status (enum), startdatum, einddatum, opmerkingen | belongs to Project |
|
||||
| **Risico** | beschrijving, impact, kans, mitigatie, eigenaar | belongs to Project |
|
||||
| **Afhankelijkheid** | type, beschrijving, status | self-referential N:M between Projects |
|
||||
|
||||
### Commitment Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Commitment** | beschrijving, eigenaar_id, deadline, status, bron | belongs to Project + Besluit, has many Acties |
|
||||
| **Actie** | beschrijving, eigenaar_id, deadline, status, prioriteit | belongs to Commitment |
|
||||
|
||||
### Governance Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Besluit** | titel, beschrijving, datum, type, status, onderbouwing | belongs to Project, has many Commitments |
|
||||
| **Budget** | bedrag, type, periode, status | belongs to Project, has many Bestedingen |
|
||||
| **Besteding** | bedrag, beschrijving, datum, categorie | belongs to Budget |
|
||||
|
||||
### Knowledge Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Document** | titel, type, inhoud, versie, auteur, datum, **embedding** (vector) | belongs to Project + Fase, M:N Tags |
|
||||
| **KennisArtikel** | titel, inhoud, tags, auteur, datum, **embedding** (vector) | M:N Tags |
|
||||
| **LessonLearned** | titel, inhoud, project, fase, tags | belongs to Project + Fase |
|
||||
| **Tag** | naam, categorie | M:N with Documents, KennisArtikels |
|
||||
|
||||
### Handover Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **Overdrachtsplan** | type, status, eigenaar_rnd, eigenaar_ontvanger | belongs to Project, has many Criteria + Acceptaties |
|
||||
| **Criterium** | beschrijving, status, verificatie | belongs to Overdrachtsplan |
|
||||
| **Acceptatie** | datum, door, opmerkingen, status | belongs to Overdrachtsplan |
|
||||
|
||||
### Auth Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **User** | name, email, password, phone, afdeling, functie, 2FA fields | has many ProjectUsers, can own Projects/Commitments/Risicos |
|
||||
| **Role** | naam, beschrijving, permissies | M:N with Users |
|
||||
| **ProjectUser** | project_id, user_id, rol (enum) | pivot table |
|
||||
|
||||
### System Layer
|
||||
| Model | Key Fields | Relationships |
|
||||
|---|---|---|
|
||||
| **AuditLog** | user_id, action, entity_type, entity_id, payload (JSON) | append-only |
|
||||
|
||||
## Innovation Lifecycle Phases (FaseType enum)
|
||||
|
||||
```
|
||||
signaal → verkenning → concept → experiment → pilot → besluitvorming → overdracht_bouwen → overdracht_beheer → evaluatie
|
||||
```
|
||||
|
||||
Special statuses (ProjectStatus only, not FaseType):
|
||||
- `geparkeerd` — temporarily halted
|
||||
- `gestopt` — permanently stopped
|
||||
- `afgerond` — completed
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
1. **Dutch naming** — all models, fields, enums use Dutch names to match domain language
|
||||
2. **Soft deletes on Project only** — projects are never hard-deleted
|
||||
3. **Embedding vectors on Document + KennisArtikel** — pgvector columns for semantic search
|
||||
4. **ProjectUser pivot with role** — team membership is role-typed (Eigenaar, Lid, Reviewer, Stakeholder)
|
||||
5. **FaseType maps 1:1 to first 9 ProjectStatus values** — `FaseType::tryFrom($projectStatus->value)` is used for phase transitions
|
||||
121
wiki/architecture/metro-map-ui.md
Normal file
121
wiki/architecture/metro-map-ui.md
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Metro Map UI Architecture
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [architecture, ui, d3, metro-map, retro]
|
||||
sources: [resources/js/Components/MetroMap/MetroCanvas.vue, STYLE_GUIDE.md, resources/js/Components/Cli/CliBar.vue]
|
||||
---
|
||||
|
||||
# Metro Map UI Architecture
|
||||
|
||||
The entire platform navigates via a **zoomable metro/transit map**. No sidebar, no traditional dashboard. Users explore the innovation landscape visually.
|
||||
|
||||
## Map Levels
|
||||
|
||||
| Level | What You See | Lines = | Stations = | Zoom Target |
|
||||
|---|---|---|---|---|
|
||||
| 1. Strategy | Full innovation landscape | Strategic themes (Thema) | Projects | Click station → Level 2 |
|
||||
| 2. Project | Single project lifecycle | Lifecycle + Commitments + Documents | Phases, items | Click station → Level 3 |
|
||||
| 3. Detail | Individual item | (not yet implemented) | — | Click to open |
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### MetroCanvas.vue (356 LOC)
|
||||
|
||||
- **Rendering**: D3.js 7.9 on SVG
|
||||
- **Zoom**: `d3.zoom()` with `scaleExtent([0.3, 5])`, pan + zoom
|
||||
- **Lines**: `d3.curveMonotoneX` paths connecting stations
|
||||
- **Stations**: Two concentric circles (outer ring = line color, inner dot = status color)
|
||||
- **Labels**: VT323 monospace font, positioned below stations
|
||||
- **Interactions**: hover → glow effect + tooltip, click → emit node-click
|
||||
- **Dependencies**: Dashed lines between connected stations across lines
|
||||
|
||||
### Data Contract (MapDataService → MetroCanvas)
|
||||
|
||||
```json
|
||||
{
|
||||
"lines": [
|
||||
{ "id": "thema-1", "name": "Waterkwaliteit", "color": "#00d2ff" }
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "project-1",
|
||||
"entityId": 1,
|
||||
"entityType": "project",
|
||||
"name": "Sensor Netwerk",
|
||||
"lineId": "thema-1",
|
||||
"x": -200, "y": 0,
|
||||
"order": 1,
|
||||
"status": "verkenning",
|
||||
"description": "...",
|
||||
"owner": "Jan",
|
||||
"badge": "Verkenning",
|
||||
"children": 5
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{ "from": "project-1", "to": "project-3" }
|
||||
],
|
||||
"level": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Station Status Colors
|
||||
|
||||
| Status | Color | Hex |
|
||||
|---|---|---|
|
||||
| afgerond / completed | Neon green | `#00ff88` |
|
||||
| actief / active | Vivid cyan | `#00d2ff` |
|
||||
| geparkeerd | Warning yellow | `#ffd93d` |
|
||||
| gestopt | Signal red | `#e94560` |
|
||||
| default (pending) | Dark fill | `#16213e` |
|
||||
|
||||
### Visual Effects
|
||||
|
||||
- **Glow filter**: SVG `feGaussianBlur` with `stdDeviation=4`, applied on hover
|
||||
- **Scanline pattern**: 4px repeating lines at 8% opacity (subtle CRT effect)
|
||||
- **Hover transition**: 200ms radius 8→12 + glow filter
|
||||
- **Zoom animation**: 500ms d3 zoom transition for `zoomTo()` method
|
||||
|
||||
## C64 CLI Bar (CliBar.vue)
|
||||
|
||||
Fixed at bottom of screen. Commodore 64 aesthetic:
|
||||
- Monospace font (Press Start 2P / VT323)
|
||||
- Blinking block cursor
|
||||
- Dark background, cyan/green text
|
||||
- Natural language input
|
||||
- Responses slide up above bar with `[AI]` prefix
|
||||
|
||||
## Design System
|
||||
|
||||
### Color Palette
|
||||
|
||||
| Role | Hex | Usage |
|
||||
|---|---|---|
|
||||
| Background | `#1a1a2e` | Canvas, page background |
|
||||
| Surface | `#16213e` | Cards, tooltips, panels |
|
||||
| Primary | `#0f3460` | Metro lines, primary actions |
|
||||
| Accent (cyan) | `#00d2ff` | Active states, highlights, CLI cursor |
|
||||
| Accent (red) | `#e94560` | Warnings, deadlines |
|
||||
| Accent (green) | `#00ff88` | Success, completed |
|
||||
| Accent (purple) | `#7b68ee` | Knowledge, documentation |
|
||||
| Text primary | `#e8e8e8` | Main text |
|
||||
| Text secondary | `#8892b0` | Labels, secondary text |
|
||||
|
||||
### Fonts
|
||||
|
||||
| Use | Font | Fallback |
|
||||
|---|---|---|
|
||||
| Map labels | VT323 | monospace |
|
||||
| CLI bar | Press Start 2P | VT323, monospace |
|
||||
| Body text | IBM Plex Sans | Inter, sans-serif |
|
||||
| Code | IBM Plex Mono | monospace |
|
||||
|
||||
## Planned Enhancements
|
||||
|
||||
- **Zoom-to-dimension**: scroll-zoom near a station gradually cross-fades into child dimension (no click needed)
|
||||
- **Recursive dimensions**: every node can contain children forming a sub-metro-map, infinitely nestable
|
||||
- **Right-click context menu**: canvas = "New node here", station = "Edit/Delete/Add child"
|
||||
- **[+] FAB**: creates at current depth
|
||||
- **Mobile**: list fallback with metro line colors preserved
|
||||
109
wiki/architecture/system-architecture.md
Normal file
109
wiki/architecture/system-architecture.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: System Architecture
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [architecture, stack, docker, layers]
|
||||
sources: [docker-compose.yml, composer.json, package.json, ai-service/app/main.py]
|
||||
---
|
||||
|
||||
# System Architecture
|
||||
|
||||
## Stack
|
||||
|
||||
| Layer | Technology | Version |
|
||||
|---|---|---|
|
||||
| Backend | Laravel (PHP) | 13.0 / PHP 8.3+ |
|
||||
| Frontend | Vue 3 + Inertia.js | Vue 3.5, Inertia 3 |
|
||||
| Build | Vite | 8.0 |
|
||||
| Styling | Tailwind CSS | 4.2 |
|
||||
| Visualization | D3.js | 7.9 |
|
||||
| Database | PostgreSQL + pgvector | 16 |
|
||||
| Cache / Queue | Redis | alpine |
|
||||
| AI Service | Python FastAPI | 0.1.0 |
|
||||
| Auth | Laravel Fortify + Sanctum | Fortify 1.36, Sanctum 4.0 |
|
||||
| Fonts | VT323, Press Start 2P, IBM Plex Mono | — |
|
||||
|
||||
## Architecture Principles
|
||||
|
||||
1. **Service-oriented** — domain logic lives in service classes (`app/Services/`), not controllers
|
||||
2. **Event-driven** — status transitions go through transactional methods with audit logging
|
||||
3. **API-first** — all functionality reachable via REST endpoints
|
||||
4. **Audit trail** — all mutations logged to `audit_logs` table (append-only)
|
||||
5. **AI content labeled** — AI-generated content marked and requires human confirmation
|
||||
6. **Inertia SPA** — server-side routing (Laravel) with client-side rendering (Vue 3), no separate API layer needed for pages
|
||||
|
||||
## Docker Topology
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ nginx:alpine ─────────────────────────────→ :80 │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ laravel-app (PHP 8.4-FPM) │
|
||||
│ │ │
|
||||
│ ├── laravel-worker (queue:work) │
|
||||
│ ├── laravel-scheduler (cron) │
|
||||
│ │ │
|
||||
│ ┌───▼────────────┐ ┌─────────────────┐ │
|
||||
│ │ postgresql:16 │ │ redis:alpine │ │
|
||||
│ │ + pgvector │ │ cache/queue │ │
|
||||
│ │ :5432 │ │ :6379 │ │
|
||||
│ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
│ ai-service (Python FastAPI) ──────────────→ :8000 │
|
||||
│ └── connects to postgresql for embeddings │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
7 services total. All on `innovatieplatform` bridge network.
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Page Rendering (Inertia)
|
||||
|
||||
```
|
||||
Browser → nginx → PHP-FPM → Laravel Router → Controller
|
||||
→ Service (business logic)
|
||||
→ Inertia::render('Page', $data)
|
||||
→ Vue component receives props
|
||||
→ D3.js renders metro map canvas
|
||||
```
|
||||
|
||||
### API Calls (Map data)
|
||||
|
||||
```
|
||||
Vue component → axios GET /api/map/strategy
|
||||
→ MapController::apiStrategy()
|
||||
→ MapDataService::getStrategyMap()
|
||||
→ Eloquent queries (Thema → Speerpunten → Projects)
|
||||
→ JSON response {lines, nodes, connections, level}
|
||||
→ D3 re-renders canvas
|
||||
```
|
||||
|
||||
### AI Integration (planned)
|
||||
|
||||
```
|
||||
CliBar.vue → POST /api/chat
|
||||
→ Laravel proxy → ai-service:8000/api/chat
|
||||
→ LangGraph agent → Anthropic Claude
|
||||
→ RAG: pgvector similarity search on documents
|
||||
→ Response with source attribution
|
||||
→ CliBar displays with [AI] prefix
|
||||
```
|
||||
|
||||
## Key Service Classes
|
||||
|
||||
| Service | LOC | Responsibility |
|
||||
|---|---|---|
|
||||
| `ProjectService` | 186 | Project CRUD, lifecycle transitions, park/stop, audit logging |
|
||||
| `MapDataService` | 165 | Build metro map data structures (Level 1: strategy, Level 2: project) |
|
||||
| `ThemaService` | 60 | Theme CRUD operations |
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Session/Cache/Queue**: All Redis-backed (`config/session.php`, `config/cache.php`, `config/queue.php`)
|
||||
- **Database**: PostgreSQL with pgvector extension for embedding vectors
|
||||
- **Auth**: Fortify handles registration/login/password flows, Sanctum for API tokens
|
||||
- **OPcache**: Production-optimized (`docker/php/opcache.ini`)
|
||||
- **Gzip**: Enabled in nginx config
|
||||
80
wiki/concepts/ai-integration.md
Normal file
80
wiki/concepts/ai-integration.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: AI Integration
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: speculative
|
||||
tags: [concept, ai, rag, langgraph, embeddings]
|
||||
sources: [ai-service/app/main.py, ai-service/requirements.txt, docker-compose.yml]
|
||||
---
|
||||
|
||||
# AI Integration
|
||||
|
||||
## Current State
|
||||
|
||||
The AI service is a **Python FastAPI stub** with placeholder endpoints. No actual AI processing is wired up yet.
|
||||
|
||||
### Implemented (stub only)
|
||||
|
||||
| Endpoint | Method | Status |
|
||||
|---|---|---|
|
||||
| `GET /health` | Health check | Working |
|
||||
| `POST /api/chat` | Chat with context | Stub — returns placeholder text |
|
||||
| `POST /api/summarize` | Generate summaries | Stub — returns placeholder text |
|
||||
| `POST /api/search` | Semantic search | Stub — returns empty results |
|
||||
|
||||
### Request/Response Models (Pydantic)
|
||||
|
||||
```
|
||||
ChatRequest: message, project_id?, conversation_history[]
|
||||
ChatResponse: reply, project_id?
|
||||
SummarizeRequest: content, project_id?, summary_type?
|
||||
SummarizeResponse: summary, project_id?
|
||||
SearchRequest: query, project_id?, limit?
|
||||
SearchResponse: results[{id, content, score, metadata}], query
|
||||
```
|
||||
|
||||
## Planned Architecture
|
||||
|
||||
```
|
||||
Laravel App ↔ HTTP ↔ Python AI-Service (FastAPI)
|
||||
├── LangGraph Orchestrator
|
||||
│ ├── Router / Classifier
|
||||
│ └── Agent graph (state machine)
|
||||
├── Anthropic Claude (LLM)
|
||||
├── pgvector (embeddings / similarity search)
|
||||
└── Tools:
|
||||
├── DB query (project data, commitments, phases)
|
||||
├── Document retrieval (semantic search)
|
||||
└── Embedding generation
|
||||
```
|
||||
|
||||
## RAG Pipeline (planned)
|
||||
|
||||
### Sources
|
||||
- Project descriptions and phase notes
|
||||
- Documents (uploaded files, meeting notes)
|
||||
- Lessons learned
|
||||
- Decisions and their rationale
|
||||
- Knowledge articles
|
||||
|
||||
### Embedding Strategy
|
||||
- **Storage**: pgvector extension on PostgreSQL 16
|
||||
- **Models**: Document and KennisArtikel already have `embedding` vector columns
|
||||
- **Update triggers**: On document create/update, on project phase change
|
||||
- **Chunking**: Per document type and size
|
||||
|
||||
### Agent Skills (from CLAUDE.md)
|
||||
| Agent | Autonomy | Purpose |
|
||||
|---|---|---|
|
||||
| Project Assistant | Low | Answer questions about specific projects |
|
||||
| Knowledge Assistant | Low | Search and surface knowledge articles |
|
||||
| Document Assistant | Medium | Summarize, compare, extract from documents |
|
||||
| System Tasks | High | Background indexing, embedding updates |
|
||||
|
||||
## Content Governance Rules
|
||||
|
||||
1. AI-generated content always labeled ("AI-suggestie", "Concept")
|
||||
2. Human confirmation required before AI content gains system status
|
||||
3. All AI interactions logged (request, response, tools used, sources cited)
|
||||
4. Source attribution mandatory in AI responses
|
||||
5. Confidence indicators when certainty is low
|
||||
76
wiki/concepts/innovation-lifecycle.md
Normal file
76
wiki/concepts/innovation-lifecycle.md
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Innovation Lifecycle
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [concept, lifecycle, phases, governance]
|
||||
sources: [app/Enums/FaseType.php, app/Enums/ProjectStatus.php, app/Services/ProjectService.php]
|
||||
---
|
||||
|
||||
# Innovation Lifecycle
|
||||
|
||||
Every innovation project at the R&D lab follows a 9-phase lifecycle, from initial signal to final evaluation. The platform enforces this via `FaseType` and `ProjectStatus` enums with transactional phase transitions.
|
||||
|
||||
## Phases
|
||||
|
||||
```
|
||||
signaal → verkenning → concept → experiment → pilot → besluitvorming → overdracht_bouwen → overdracht_beheer → evaluatie
|
||||
```
|
||||
|
||||
| # | Phase | Dutch | Purpose |
|
||||
|---|---|---|---|
|
||||
| 1 | Signal | Signaal | Initial idea or observation captured |
|
||||
| 2 | Exploration | Verkenning | Research and feasibility assessment |
|
||||
| 3 | Concept | Concept | Design a proposed solution |
|
||||
| 4 | Experiment | Experiment | Small-scale test of the concept |
|
||||
| 5 | Pilot | Pilot | Larger-scale test in production environment |
|
||||
| 6 | Decision | Besluitvorming | Go/no-go decision by governance |
|
||||
| 7 | Handover to Build | Overdracht bouwen | Transfer to development/implementation team |
|
||||
| 8 | Handover to Operations | Overdracht beheer | Transfer to operational team for maintenance |
|
||||
| 9 | Evaluation | Evaluatie | Post-handover review and lessons learned |
|
||||
|
||||
## Special Statuses
|
||||
|
||||
These are Project-only statuses (not lifecycle phases):
|
||||
|
||||
| Status | Purpose |
|
||||
|---|---|
|
||||
| Geparkeerd | Temporarily halted — can resume later |
|
||||
| Gestopt | Permanently stopped — documented why |
|
||||
| Afgerond | Successfully completed the full lifecycle |
|
||||
|
||||
## Phase Transition Logic
|
||||
|
||||
Phase transitions go through `ProjectService::transitionPhase()` in a database transaction:
|
||||
|
||||
1. Close current active phase (set status → `afgerond`, set `einddatum`)
|
||||
2. Create new phase record (via `FaseType::tryFrom()` mapping)
|
||||
3. Update project status
|
||||
4. Write audit log entry with `from` and `to` states
|
||||
|
||||
Key constraint: `FaseType::tryFrom($newStatus->value)` — the first 9 ProjectStatus values map 1:1 to FaseType. Special statuses (geparkeerd, gestopt, afgerond) do not create new Fase records.
|
||||
|
||||
## Park and Stop
|
||||
|
||||
- **Park** (`ProjectService::park()`): closes active phase with reason in `opmerkingen`, sets status to `geparkeerd`
|
||||
- **Stop** (`ProjectService::stop()`): same mechanics, sets status to `gestopt`
|
||||
- Both log the transition reason in the audit trail
|
||||
|
||||
## Audit Trail
|
||||
|
||||
Every phase transition, park, or stop creates an `AuditLog` record:
|
||||
|
||||
```php
|
||||
AuditLog::create([
|
||||
'user_id' => Auth::id(),
|
||||
'action' => "project.phase_transition",
|
||||
'entity_type' => 'project',
|
||||
'entity_id' => $project->id,
|
||||
'payload' => ['from' => 'signaal', 'to' => 'verkenning'],
|
||||
]);
|
||||
```
|
||||
|
||||
## Metro Map Representation
|
||||
|
||||
- **Level 1**: Project status shown as station dot color (green=completed, cyan=active, yellow=parked, red=stopped)
|
||||
- **Level 2**: Each lifecycle phase is a station on the project's "lifecycle" line, with active phase highlighted
|
||||
34
wiki/index.md
Normal file
34
wiki/index.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Wiki Index
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
---
|
||||
|
||||
# Innovatieplatform Wiki Index
|
||||
|
||||
## Overview
|
||||
- [Project Overview](overview.md) — current status, what works, what doesn't
|
||||
- [Metrics Dashboard](metrics.md) — all numbers with provenance
|
||||
- [Knowledge Graph](knowledge-graph.yaml) — structured data, machine-queryable
|
||||
|
||||
## Architecture
|
||||
- [System Architecture](architecture/system-architecture.md) — layers, services, data flow
|
||||
- [Domain Model](architecture/domain-model.md) — entities, relationships, lifecycle states
|
||||
- [Metro Map UI](architecture/metro-map-ui.md) — D3 canvas, zoom dimensions, interaction model
|
||||
|
||||
## Core Concepts
|
||||
- [Innovation Lifecycle](concepts/innovation-lifecycle.md) — phases from signaal to evaluatie
|
||||
- [AI Integration](concepts/ai-integration.md) — RAG pipeline, LangGraph, agent skills
|
||||
|
||||
## Findings
|
||||
<!-- Add findings pages — both proven AND disproven -->
|
||||
|
||||
## Sessions
|
||||
- [2026-04-08 Wiki Setup](sessions/2026-04-08-wiki-setup.md) — initial wiki from template, codebase scan
|
||||
|
||||
## Not Yet Documented
|
||||
- RBAC model and permission matrix
|
||||
- Handover process (overdracht) details
|
||||
- Budget/finance governance
|
||||
- Student projects (Phase 2)
|
||||
- Deployment pipeline / CI/CD
|
||||
141
wiki/knowledge-graph.yaml
Normal file
141
wiki/knowledge-graph.yaml
Normal file
@@ -0,0 +1,141 @@
|
||||
# Innovatieplatform — Structured Knowledge Graph
|
||||
# Single source of truth for numbers, metrics, and verifiable claims.
|
||||
# Last updated: 2026-04-08
|
||||
|
||||
# ── TESTS ──
|
||||
tests:
|
||||
phpunit_feature:
|
||||
count: 0
|
||||
passing: 0
|
||||
file: tests/Feature/
|
||||
date: 2026-04-08
|
||||
phpunit_unit:
|
||||
count: 0
|
||||
passing: 0
|
||||
file: tests/Unit/
|
||||
date: 2026-04-08
|
||||
|
||||
# ── METRICS ──
|
||||
metrics:
|
||||
backend_loc:
|
||||
value: "~13,500"
|
||||
source: "wc -l on PHP files excl. vendor"
|
||||
date: 2026-04-08
|
||||
frontend_loc:
|
||||
value: "~3,900"
|
||||
source: "wc -l on Vue/JS files excl. node_modules"
|
||||
date: 2026-04-08
|
||||
eloquent_models:
|
||||
value: 21
|
||||
source: "ls app/Models/"
|
||||
date: 2026-04-08
|
||||
enums:
|
||||
value: 14
|
||||
source: "ls app/Enums/"
|
||||
date: 2026-04-08
|
||||
migrations:
|
||||
value: 27
|
||||
source: "ls database/migrations/"
|
||||
date: 2026-04-08
|
||||
vue_components:
|
||||
value: 15
|
||||
source: "find resources/js -name '*.vue'"
|
||||
date: 2026-04-08
|
||||
docker_services:
|
||||
value: 7
|
||||
source: docker-compose.yml
|
||||
date: 2026-04-08
|
||||
git_commits:
|
||||
value: 7
|
||||
source: "git log --oneline"
|
||||
date: 2026-04-08
|
||||
|
||||
# ── DISPROVEN CLAIMS ──
|
||||
disproven: {}
|
||||
|
||||
# ── PERFORMANCE ──
|
||||
performance: {}
|
||||
|
||||
# ── ARCHITECTURE ──
|
||||
architecture:
|
||||
backend: "Laravel 13.0 (PHP 8.3+)"
|
||||
frontend: "Vue 3.5 + Inertia.js 3 + Vite 8"
|
||||
database: "PostgreSQL 16 + pgvector"
|
||||
ai_service: "Python FastAPI + LangGraph"
|
||||
cache_queue: "Redis (cache, session, queue)"
|
||||
styling: "Tailwind CSS 4.2"
|
||||
visualization: "D3.js 7.9"
|
||||
auth: "Laravel Fortify + Sanctum"
|
||||
infrastructure: "Docker Compose (7 services)"
|
||||
|
||||
# ── DOMAIN MODEL ──
|
||||
domain_model:
|
||||
strategic_layer:
|
||||
- Thema
|
||||
- Speerpunt
|
||||
- RoadmapItem
|
||||
project_layer:
|
||||
- Project
|
||||
- Fase
|
||||
- Risico
|
||||
- Afhankelijkheid
|
||||
commitment_layer:
|
||||
- Commitment
|
||||
- Actie
|
||||
governance_layer:
|
||||
- Besluit
|
||||
- Budget
|
||||
- Besteding
|
||||
knowledge_layer:
|
||||
- Document
|
||||
- KennisArtikel
|
||||
- LessonLearned
|
||||
- Tag
|
||||
handover_layer:
|
||||
- Overdrachtsplan
|
||||
- Criterium
|
||||
- Acceptatie
|
||||
auth_layer:
|
||||
- User
|
||||
- Role
|
||||
- ProjectUser
|
||||
system_layer:
|
||||
- AuditLog
|
||||
|
||||
# ── LIFECYCLE PHASES ──
|
||||
lifecycle_phases:
|
||||
- signaal
|
||||
- verkenning
|
||||
- concept
|
||||
- experiment
|
||||
- pilot
|
||||
- besluitvorming
|
||||
- overdracht_bouwen
|
||||
- overdracht_beheer
|
||||
- evaluatie
|
||||
|
||||
# ── SPRINT STATUS ──
|
||||
sprint_status:
|
||||
sprint_0_scaffold: completed
|
||||
sprint_1_foundation: completed
|
||||
sprint_2_detail: not_started
|
||||
|
||||
# ── DATA SOURCES ──
|
||||
data_sources: {}
|
||||
|
||||
# ── TIMELINE ──
|
||||
timeline:
|
||||
- date: 2026-04-01
|
||||
desc: "Initial scaffold: Laravel 13, Vue 3, Docker, domain model"
|
||||
- date: 2026-04-01
|
||||
desc: "Style guide: metro map + retro-futurism aesthetic"
|
||||
- date: 2026-04-02
|
||||
desc: "Sprint 1: Auth, metro map canvas, services, retro UI"
|
||||
- date: 2026-04-02
|
||||
desc: "Fix Dutch model table names and enum values"
|
||||
- date: 2026-04-02
|
||||
desc: "Performance: OPcache, gzip, font subsetting, lazy loading"
|
||||
- date: 2026-04-02
|
||||
desc: "Logout button, metro label positioning fixes"
|
||||
- date: 2026-04-08
|
||||
desc: "Wiki initialized from wiki-template"
|
||||
11
wiki/log.md
Normal file
11
wiki/log.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Wiki Log
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [log, history]
|
||||
---
|
||||
|
||||
# Wiki Log
|
||||
|
||||
## [2026-04-08] init | Wiki initialized from wiki-template, populated with codebase scan data
|
||||
55
wiki/metrics.md
Normal file
55
wiki/metrics.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Metrics Dashboard
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [metrics, tests, performance]
|
||||
sources: [phpunit.xml, composer.json, package.json]
|
||||
---
|
||||
|
||||
# Metrics Dashboard
|
||||
|
||||
All numbers with provenance. Source of truth: `knowledge-graph.yaml`.
|
||||
|
||||
## Test Results
|
||||
|
||||
| Suite | Score | File | Date |
|
||||
|---|---|---|---|
|
||||
| PHPUnit Feature | 0/0 | tests/Feature/ | 2026-04-08 |
|
||||
| PHPUnit Unit | 0/0 | tests/Unit/ | 2026-04-08 |
|
||||
|
||||
> No tests written yet. PHPUnit 12.5 is configured and ready.
|
||||
|
||||
## Codebase Size
|
||||
|
||||
| Metric | Value | Source |
|
||||
|---|---|---|
|
||||
| Backend LOC | ~13,500 | PHP files excl. vendor |
|
||||
| Frontend LOC | ~3,900 | Vue/JS files excl. node_modules |
|
||||
| Models | 21 | app/Models/ |
|
||||
| Enums | 14 | app/Enums/ |
|
||||
| Migrations | 27 | database/migrations/ |
|
||||
| Vue components | 15 | resources/js/ |
|
||||
| Docker services | 7 | docker-compose.yml |
|
||||
|
||||
## Build Dependencies
|
||||
|
||||
| Layer | Package Manager | Production | Dev |
|
||||
|---|---|---|---|
|
||||
| Backend | Composer | 5 (Laravel, Fortify, Sanctum, Inertia, Tinker) | 6 (Faker, Pail, Pint, Mockery, Collision, PHPUnit) |
|
||||
| Frontend | npm | 8 (Vue, Inertia, D3, Pinia, VueUse, fonts) | 6 (Tailwind, Vite, Axios, Concurrently, Laravel-Vite) |
|
||||
| AI Service | pip | FastAPI, LangGraph, LangChain, Anthropic, pgvector | — |
|
||||
|
||||
## Sprint Progress
|
||||
|
||||
| Sprint | Status | Key Deliverables |
|
||||
|---|---|---|
|
||||
| Sprint 0 — Scaffold | completed | Domain model, Docker, migrations, seeders, agent definitions |
|
||||
| Sprint 1 — Foundation | completed | Auth (Fortify), metro map canvas (D3), services, retro UI |
|
||||
| Sprint 2 — Detail & Interaction | not started | Project detail view, zoom-to-dimension, commitment UI |
|
||||
|
||||
## Disproven Claims
|
||||
|
||||
| Claim | Evidence For | Evidence Against | Date |
|
||||
|---|---|---|---|
|
||||
| (none yet) | — | — | — |
|
||||
69
wiki/overview.md
Normal file
69
wiki/overview.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Project Overview
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [overview, status, roadmap]
|
||||
sources: [CLAUDE.md, composer.json, package.json, docker-compose.yml]
|
||||
---
|
||||
|
||||
# Innovatieplatform — R&D Lab Waterschap Brabantse Delta
|
||||
|
||||
Innovation governance platform supporting the full lifecycle of innovation trajectories — from signal to handover — with built-in AI support. The primary UI is a **zoomable metro map** with retro-futurism aesthetic (Commodore 64-inspired CLI bar, pixel accents, monospace typography).
|
||||
|
||||
## What Works
|
||||
|
||||
| Capability | Status | Evidence |
|
||||
|---|---|---|
|
||||
| Authentication (Fortify) | proven | Login, register, password reset, email verification all functional |
|
||||
| Metro map canvas (D3.js) | proven | Zoomable SVG canvas with stations, lines, hover preview, breadcrumb |
|
||||
| Project CRUD API | proven | REST endpoints for create, update, transition, park, stop, delete |
|
||||
| Thema CRUD API | proven | REST endpoints for list, create, update |
|
||||
| Domain model (21 models) | proven | All Eloquent models with relationships, 27 migrations |
|
||||
| Service-oriented architecture | proven | ProjectService, MapDataService, ThemaService (411 LOC) |
|
||||
| Docker Compose infrastructure | proven | 7 services: nginx, php-fpm, worker, scheduler, postgresql, redis, ai-service |
|
||||
| Retro-futurism UI | proven | C64 CLI bar, VT323/Press Start 2P fonts, dark palette, glow effects |
|
||||
| Demo data seeder | proven | 4 themes x 3 projects with lifecycle phases |
|
||||
| Performance optimizations | proven | OPcache, gzip, font subsetting, lazy-loaded pages |
|
||||
|
||||
## What Doesn't Work (honestly)
|
||||
|
||||
| Capability | Status | Notes |
|
||||
|---|---|---|
|
||||
| Project detail pages | not started | Routes exist, page component missing |
|
||||
| Commitment/action tracking UI | not started | Models + enums exist, no frontend |
|
||||
| Document management | not started | Document model with embedding vector, no upload/display |
|
||||
| AI chat integration | not started | Python FastAPI stub only, no LangGraph/RAG pipeline |
|
||||
| Semantic search | not started | pgvector enabled, no embedding generation |
|
||||
| RBAC UI | not started | Role/CheckRole middleware exist, no admin UI |
|
||||
| Test suite | not started | PHPUnit 12.5 configured, 0 tests written |
|
||||
| Roadmap visualization | not started | RoadmapItem model exists, no UI |
|
||||
| Zoom-to-dimension transitions | not started | Canvas supports zoom, no cross-fade to child dimension |
|
||||
| Right-click create/edit | not started | Planned interaction pattern, not implemented |
|
||||
| Bilingual NL/EN | not started | All code in Dutch, no i18n framework |
|
||||
|
||||
## Current Scale
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| Backend LOC (excl. vendor) | ~13,500 |
|
||||
| Frontend LOC (excl. node_modules) | ~3,900 |
|
||||
| Eloquent models | 21 |
|
||||
| Enums | 14 |
|
||||
| Services | 3 |
|
||||
| Controllers | 4 |
|
||||
| Vue components/pages | 15 |
|
||||
| Database migrations | 27 |
|
||||
| Docker services | 7 |
|
||||
| Git commits | 7 |
|
||||
| Test coverage | 0% |
|
||||
|
||||
## Next Steps (Priority Order)
|
||||
|
||||
1. **Project detail view** — click station -> full project page with phases, commitments, documents
|
||||
2. **Zoom-to-dimension** — scroll-zoom near station cross-fades into child metro map
|
||||
3. **Commitment/action tracking** — UI for managing commitments with deadlines and owners
|
||||
4. **Test suite** — PHPUnit feature tests for API + unit tests for services
|
||||
5. **AI chat integration** — Wire Python FastAPI service with LangGraph orchestrator
|
||||
6. **Document management** — Upload, link, embed, search documents
|
||||
7. **RBAC admin** — Role assignment UI, permission management
|
||||
41
wiki/sessions/2026-04-08-wiki-setup.md
Normal file
41
wiki/sessions/2026-04-08-wiki-setup.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "Session: Wiki Setup"
|
||||
created: 2026-04-08
|
||||
updated: 2026-04-08
|
||||
status: evolving
|
||||
tags: [session, setup, wiki]
|
||||
---
|
||||
|
||||
# 2026-04-08 — Wiki Setup
|
||||
|
||||
## Scope
|
||||
Initialize wiki from wiki-template and populate with real codebase data.
|
||||
|
||||
## What Was Done
|
||||
|
||||
1. **Copied wiki-template** from `/home/znetsixe/wiki-template/` to `innovatieplatform/wiki/`
|
||||
2. **Populated overview.md** with what works / what doesn't based on actual code scan
|
||||
3. **Populated metrics.md** with codebase size, dependencies, sprint progress
|
||||
4. **Populated knowledge-graph.yaml** with structured data: tests, metrics, architecture, domain model, lifecycle phases, timeline
|
||||
5. **Created architecture pages**:
|
||||
- `architecture/system-architecture.md` — stack, Docker topology, data flow, services
|
||||
- `architecture/domain-model.md` — 21 models in 8 layers, relationships, enums, lifecycle
|
||||
- `architecture/metro-map-ui.md` — D3 canvas, data contract, design system, planned enhancements
|
||||
6. **Created concept pages**:
|
||||
- `concepts/innovation-lifecycle.md` — 9 phases, transition logic, audit trail
|
||||
- `concepts/ai-integration.md` — current stub state, planned RAG architecture
|
||||
7. **Updated index.md** with all new pages
|
||||
8. **Wired wiki into CLAUDE.md** for agent startup
|
||||
|
||||
## Key Numbers (from scan)
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| Backend LOC | ~13,500 |
|
||||
| Frontend LOC | ~3,900 |
|
||||
| Models | 21 |
|
||||
| Enums | 14 |
|
||||
| Migrations | 27 |
|
||||
| Vue components | 15 |
|
||||
| Tests | 0 |
|
||||
| Git commits | 7 |
|
||||
46
wiki/tools/lint.sh
Normal file
46
wiki/tools/lint.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Wiki health check — find issues
|
||||
# Usage: ./wiki/tools/lint.sh
|
||||
|
||||
WIKI_DIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
|
||||
|
||||
echo "=== Wiki Health Check ==="
|
||||
echo ""
|
||||
|
||||
echo "-- Page count --"
|
||||
find "$WIKI_DIR" -name "*.md" -not -path "*/tools/*" | wc -l
|
||||
echo " total pages"
|
||||
echo ""
|
||||
|
||||
echo "-- Orphans (not linked from other pages) --"
|
||||
for f in $(find "$WIKI_DIR" -name "*.md" -not -name "index.md" -not -name "log.md" -not -name "SCHEMA.md" -not -path "*/tools/*"); do
|
||||
basename=$(basename "$f" .md)
|
||||
refs=$(grep -rl --include="*.md" "$basename" "$WIKI_DIR" 2>/dev/null | grep -v "$f" | wc -l)
|
||||
if [ "$refs" -eq 0 ]; then
|
||||
echo " ORPHAN: $f"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "-- Status distribution --"
|
||||
for status in proven disproven evolving speculative; do
|
||||
count=$(grep -rl "status: $status" "$WIKI_DIR" --include="*.md" 2>/dev/null | wc -l)
|
||||
echo " $status: $count"
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "-- Pages missing frontmatter --"
|
||||
for f in $(find "$WIKI_DIR" -name "*.md" -not -name "SCHEMA.md" -not -path "*/tools/*"); do
|
||||
if ! head -1 "$f" | grep -q "^---"; then
|
||||
echo " NO FRONTMATTER: $f"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "-- Index completeness --"
|
||||
indexed=$(grep -c '\[.*\](.*\.md)' "$WIKI_DIR/index.md" 2>/dev/null)
|
||||
total=$(find "$WIKI_DIR" -name "*.md" -not -name "index.md" -not -name "log.md" -not -name "SCHEMA.md" -not -path "*/tools/*" | wc -l)
|
||||
echo " Indexed: $indexed / Total: $total"
|
||||
echo ""
|
||||
|
||||
echo "=== Done ==="
|
||||
249
wiki/tools/query.py
Normal file
249
wiki/tools/query.py
Normal file
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wiki Knowledge Graph query tool.
|
||||
|
||||
Queryable interface over knowledge-graph.yaml + wiki pages.
|
||||
Usable by both humans (CLI) and LLM agents (imported).
|
||||
|
||||
Usage:
|
||||
python wiki/tools/query.py health # project health
|
||||
python wiki/tools/query.py entity "search term" # everything about an entity
|
||||
python wiki/tools/query.py metric "search term" # find metrics
|
||||
python wiki/tools/query.py status "proven" # all pages with status
|
||||
python wiki/tools/query.py test "test name" # test results
|
||||
python wiki/tools/query.py search "keyword" # full-text search
|
||||
python wiki/tools/query.py related "page-name" # pages linking to/from
|
||||
python wiki/tools/query.py timeline # commit timeline
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
WIKI_DIR = Path(__file__).parent.parent
|
||||
GRAPH_PATH = WIKI_DIR / 'knowledge-graph.yaml'
|
||||
|
||||
|
||||
def load_graph():
|
||||
if not GRAPH_PATH.exists():
|
||||
return {}
|
||||
with open(GRAPH_PATH) as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
|
||||
|
||||
def load_all_pages():
|
||||
pages = {}
|
||||
for md_path in WIKI_DIR.rglob('*.md'):
|
||||
if 'tools' in str(md_path):
|
||||
continue
|
||||
rel = md_path.relative_to(WIKI_DIR)
|
||||
content = md_path.read_text()
|
||||
meta = {}
|
||||
if content.startswith('---'):
|
||||
parts = content.split('---', 2)
|
||||
if len(parts) >= 3:
|
||||
try:
|
||||
meta = yaml.safe_load(parts[1]) or {}
|
||||
except yaml.YAMLError:
|
||||
pass
|
||||
content = parts[2]
|
||||
links = re.findall(r'\[\[([^\]]+)\]\]', content)
|
||||
pages[str(rel)] = {
|
||||
'path': str(rel), 'meta': meta, 'content': content,
|
||||
'links': links, 'title': meta.get('title', str(rel)),
|
||||
'status': meta.get('status', 'unknown'),
|
||||
'tags': meta.get('tags', []),
|
||||
}
|
||||
return pages
|
||||
|
||||
|
||||
def flatten_graph(graph, prefix=''):
|
||||
items = []
|
||||
if isinstance(graph, dict):
|
||||
for k, v in graph.items():
|
||||
path = f"{prefix}.{k}" if prefix else k
|
||||
if isinstance(v, (dict, list)):
|
||||
items.extend(flatten_graph(v, path))
|
||||
else:
|
||||
items.append((path, str(v)))
|
||||
elif isinstance(graph, list):
|
||||
for i, v in enumerate(graph):
|
||||
path = f"{prefix}[{i}]"
|
||||
if isinstance(v, (dict, list)):
|
||||
items.extend(flatten_graph(v, path))
|
||||
else:
|
||||
items.append((path, str(v)))
|
||||
return items
|
||||
|
||||
|
||||
def cmd_health():
|
||||
graph = load_graph()
|
||||
pages = load_all_pages()
|
||||
statuses = {}
|
||||
for p in pages.values():
|
||||
s = p['status']
|
||||
statuses[s] = statuses.get(s, 0) + 1
|
||||
|
||||
tests = graph.get('tests', {})
|
||||
total_pass = sum(t.get('passing', 0) for t in tests.values() if isinstance(t, dict))
|
||||
total_count = sum(t.get('count', t.get('total', 0)) for t in tests.values() if isinstance(t, dict))
|
||||
disproven = len(graph.get('disproven', {}))
|
||||
timeline = len(graph.get('timeline', []))
|
||||
|
||||
# Count broken links
|
||||
all_titles = set()
|
||||
for p in pages.values():
|
||||
all_titles.add(p['title'].lower())
|
||||
all_titles.add(p['path'].lower().replace('.md', '').split('/')[-1])
|
||||
broken = sum(1 for p in pages.values() for link in p['links']
|
||||
if not any(link.lower().replace('-', ' ') in t or t in link.lower().replace('-', ' ')
|
||||
for t in all_titles))
|
||||
|
||||
print(f"Wiki Health:\n")
|
||||
print(f" Pages: {len(pages)}")
|
||||
print(f" Statuses: {statuses}")
|
||||
if total_count:
|
||||
print(f" Tests: {total_pass}/{total_count} passing")
|
||||
print(f" Disproven: {disproven} claims tracked")
|
||||
print(f" Timeline: {timeline} commits")
|
||||
print(f" Broken links: {broken}")
|
||||
|
||||
|
||||
def cmd_entity(query):
|
||||
graph = load_graph()
|
||||
pages = load_all_pages()
|
||||
q = query.lower()
|
||||
print(f"Entity: '{query}'\n")
|
||||
|
||||
flat = flatten_graph(graph)
|
||||
hits = [(p, v) for p, v in flat if q in p.lower() or q in v.lower()]
|
||||
if hits:
|
||||
print(" -- Knowledge Graph --")
|
||||
for path, value in hits[:20]:
|
||||
print(f" {path}: {value}")
|
||||
|
||||
print("\n -- Wiki Pages --")
|
||||
for rel, page in sorted(pages.items()):
|
||||
if q in page['content'].lower() or q in page['title'].lower():
|
||||
lines = [l.strip() for l in page['content'].split('\n')
|
||||
if q in l.lower() and l.strip()]
|
||||
print(f" {rel} ({page['status']})")
|
||||
for line in lines[:3]:
|
||||
print(f" {line[:100]}")
|
||||
|
||||
|
||||
def cmd_metric(query):
|
||||
flat = flatten_graph(load_graph())
|
||||
q = query.lower()
|
||||
print(f"Metrics matching '{query}':\n")
|
||||
found = 0
|
||||
for path, value in flat:
|
||||
if q in path.lower() or q in value.lower():
|
||||
print(f" {path}: {value}")
|
||||
found += 1
|
||||
if not found:
|
||||
print(" (no matches)")
|
||||
|
||||
|
||||
def cmd_status(status):
|
||||
pages = load_all_pages()
|
||||
graph = load_graph()
|
||||
print(f"Status: '{status}'\n")
|
||||
for rel, page in sorted(pages.items()):
|
||||
if page['status'] == status:
|
||||
print(f" {page['title']} ({rel})")
|
||||
if page['tags']:
|
||||
print(f" tags: {page['tags']}")
|
||||
if status == 'disproven' and 'disproven' in graph:
|
||||
print("\n -- Disproven Claims --")
|
||||
for name, claim in graph['disproven'].items():
|
||||
print(f" {name}:")
|
||||
for k, v in claim.items():
|
||||
print(f" {k}: {v}")
|
||||
|
||||
|
||||
def cmd_test(query):
|
||||
tests = load_graph().get('tests', {})
|
||||
q = query.lower()
|
||||
print(f"Test results for '{query}':\n")
|
||||
for name, suite in tests.items():
|
||||
if q in name.lower() or q in str(suite).lower():
|
||||
print(f" -- {name} --")
|
||||
if isinstance(suite, dict):
|
||||
for k, v in suite.items():
|
||||
if isinstance(v, dict):
|
||||
print(f" {k}: {v.get('passing', '?')}/{v.get('total', '?')}")
|
||||
elif k in ('count', 'passing', 'accuracy', 'file', 'date'):
|
||||
print(f" {k}: {v}")
|
||||
elif k == 'results' and isinstance(v, list):
|
||||
for r in v:
|
||||
mark = '✓' if r.get('result') == 'pass' else '✗'
|
||||
print(f" {mark} {r.get('test', '?')}")
|
||||
|
||||
|
||||
def cmd_search(query):
|
||||
flat = flatten_graph(load_graph())
|
||||
pages = load_all_pages()
|
||||
q = query.lower()
|
||||
print(f"Search: '{query}'\n")
|
||||
|
||||
graph_hits = [(p, v) for p, v in flat if q in v.lower()]
|
||||
if graph_hits:
|
||||
print(f" -- Knowledge Graph ({len(graph_hits)} hits) --")
|
||||
for p, v in graph_hits[:10]:
|
||||
print(f" {p}: {v[:80]}")
|
||||
|
||||
page_hits = sorted(
|
||||
[(page['content'].lower().count(q), rel, page['title'])
|
||||
for rel, page in pages.items() if q in page['content'].lower()],
|
||||
reverse=True)
|
||||
if page_hits:
|
||||
print(f"\n -- Wiki Pages ({len(page_hits)} pages) --")
|
||||
for count, rel, title in page_hits:
|
||||
print(f" {count:3d}x {title} ({rel})")
|
||||
|
||||
|
||||
def cmd_related(page_name):
|
||||
pages = load_all_pages()
|
||||
q = page_name.lower().replace('-', ' ').replace('_', ' ')
|
||||
print(f"Related to: '{page_name}'\n")
|
||||
|
||||
print(" -- Links TO --")
|
||||
for rel, page in sorted(pages.items()):
|
||||
for link in page['links']:
|
||||
if q in link.lower().replace('-', ' '):
|
||||
print(f" <- {page['title']} ({rel})")
|
||||
break
|
||||
|
||||
print("\n -- Links FROM --")
|
||||
for rel, page in pages.items():
|
||||
if q in page['title'].lower().replace('-', ' '):
|
||||
for link in page['links']:
|
||||
print(f" -> [[{link}]]")
|
||||
break
|
||||
|
||||
|
||||
def cmd_timeline():
|
||||
for entry in load_graph().get('timeline', []):
|
||||
print(f" [{entry.get('date')}] {entry.get('commit', '?')}: {entry.get('desc', '?')}")
|
||||
|
||||
|
||||
COMMANDS = {
|
||||
'health': cmd_health, 'entity': cmd_entity, 'metric': cmd_metric,
|
||||
'status': cmd_status, 'test': cmd_test, 'search': cmd_search,
|
||||
'related': cmd_related, 'timeline': cmd_timeline,
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
|
||||
print(f"Usage: query.py <{'|'.join(COMMANDS)}> [args]")
|
||||
sys.exit(1)
|
||||
cmd = sys.argv[1]
|
||||
args = sys.argv[2:]
|
||||
if cmd in ('timeline', 'health'):
|
||||
COMMANDS[cmd]()
|
||||
elif args:
|
||||
COMMANDS[cmd](' '.join(args))
|
||||
else:
|
||||
print(f"Usage: query.py {cmd} <query>")
|
||||
18
wiki/tools/search.sh
Normal file
18
wiki/tools/search.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Search the wiki — usable by both humans and LLM agents
|
||||
# Usage: ./wiki/tools/search.sh "query" [--files-only]
|
||||
|
||||
WIKI_DIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
|
||||
QUERY="$1"
|
||||
MODE="${2:---content}"
|
||||
|
||||
if [ -z "$QUERY" ]; then
|
||||
echo "Usage: $0 <query> [--files-only]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "--files-only" ]; then
|
||||
grep -rl --include="*.md" --include="*.yaml" "$QUERY" "$WIKI_DIR" 2>/dev/null | sort
|
||||
else
|
||||
grep -rn --include="*.md" --include="*.yaml" --color=auto -i "$QUERY" "$WIKI_DIR" 2>/dev/null
|
||||
fi
|
||||
Reference in New Issue
Block a user