@bodhi-ventures/aiocs 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -3
- package/dist/chunk-GX6CZOO7.js +10429 -0
- package/dist/cli.js +352 -2
- package/dist/mcp-server.js +707 -4
- package/docs/README.md +1 -1
- package/docs/codex-integration.md +65 -18
- package/docs/json-contract.md +446 -3
- package/package.json +23 -20
- package/skills/aiocs/SKILL.md +46 -36
- package/skills/aiocs-curation/SKILL.md +145 -0
- package/sources/nktkas-hyperliquid.yaml +30 -0
- package/dist/chunk-CZ6C4YUX.js +0 -4601
- package/docs/2026-03-26-agent-json-and-daemon-design.md +0 -157
- package/docs/2026-03-28-hybrid-search-design.md +0 -423
- package/docs/superpowers/specs/2026-03-29-tag-driven-release-pipeline-design.md +0 -135
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# AI Agent JSON CLI And Daemon Design
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
`aiocs` will add a machine-oriented JSON contract across the CLI and a first-class long-running daemon mode for scheduled refreshes. The CLI remains human-friendly by default, but every command will support a global `--json` flag so agents can consume one stable structured payload instead of parsing text. The daemon will live inside the same binary as `docs daemon`, and a Docker image will run that command in a loop with environment-configured cadence.
|
|
6
|
-
|
|
7
|
-
## Goals
|
|
8
|
-
|
|
9
|
-
- make every one-shot CLI command safe for direct agent use
|
|
10
|
-
- keep a single canonical implementation path inside the existing CLI
|
|
11
|
-
- avoid introducing a separate HTTP service or second control plane
|
|
12
|
-
- support a long-running local container that keeps the shared catalog warm
|
|
13
|
-
|
|
14
|
-
## Non-Goals
|
|
15
|
-
|
|
16
|
-
- MCP in this change
|
|
17
|
-
- remote registries or distributed scheduling
|
|
18
|
-
- a second machine API beyond the CLI
|
|
19
|
-
|
|
20
|
-
## JSON Output Contract
|
|
21
|
-
|
|
22
|
-
### Scope
|
|
23
|
-
|
|
24
|
-
`--json` is a root-level global flag that applies to every CLI command.
|
|
25
|
-
|
|
26
|
-
### One-shot commands
|
|
27
|
-
|
|
28
|
-
These commands emit exactly one JSON document to stdout:
|
|
29
|
-
|
|
30
|
-
- `source upsert`
|
|
31
|
-
- `source list`
|
|
32
|
-
- `fetch`
|
|
33
|
-
- `refresh due`
|
|
34
|
-
- `snapshot list`
|
|
35
|
-
- `project link`
|
|
36
|
-
- `project unlink`
|
|
37
|
-
- `search`
|
|
38
|
-
- `show`
|
|
39
|
-
|
|
40
|
-
### Envelope
|
|
41
|
-
|
|
42
|
-
Every command returns:
|
|
43
|
-
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"ok": true,
|
|
47
|
-
"command": "source.list",
|
|
48
|
-
"data": {}
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Failures also emit a single JSON document to stdout, with exit code `1`:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"ok": false,
|
|
57
|
-
"command": "search",
|
|
58
|
-
"error": {
|
|
59
|
-
"message": "No linked project scope found. Use --source or --all."
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Command payloads
|
|
65
|
-
|
|
66
|
-
- `source.upsert`: upserted source metadata
|
|
67
|
-
- `source.list`: array of sources with due/snapshot fields
|
|
68
|
-
- `fetch`: array of per-source fetch results, even for a single source
|
|
69
|
-
- `refresh.due`: array of per-source fetch results; empty array when nothing is due
|
|
70
|
-
- `snapshot.list`: array of snapshots
|
|
71
|
-
- `project.link`: canonical project path and linked source ids
|
|
72
|
-
- `project.unlink`: canonical project path and removed scope
|
|
73
|
-
- `search`: array of chunk results
|
|
74
|
-
- `show`: one chunk result
|
|
75
|
-
|
|
76
|
-
### Daemon exception
|
|
77
|
-
|
|
78
|
-
`docs daemon` is long-running, so a single final JSON document is the wrong shape. In JSON mode it will emit newline-delimited JSON event objects to stdout, one event per lifecycle action. This is the one intended exception to the single-document rule.
|
|
79
|
-
|
|
80
|
-
## Daemon Design
|
|
81
|
-
|
|
82
|
-
### Command
|
|
83
|
-
|
|
84
|
-
Add `docs daemon`.
|
|
85
|
-
|
|
86
|
-
### Responsibilities
|
|
87
|
-
|
|
88
|
-
- ensure config and data directories exist
|
|
89
|
-
- optionally bootstrap source specs from configured directories
|
|
90
|
-
- optionally run an immediate refresh cycle on startup
|
|
91
|
-
- loop forever:
|
|
92
|
-
- upsert any source spec files from configured directories
|
|
93
|
-
- run refresh for due sources
|
|
94
|
-
- sleep until the next cycle
|
|
95
|
-
|
|
96
|
-
### Environment variables
|
|
97
|
-
|
|
98
|
-
- `AIOCS_DAEMON_INTERVAL_MINUTES`
|
|
99
|
-
- required positive integer semantics, default `60`
|
|
100
|
-
- `AIOCS_DAEMON_FETCH_ON_START`
|
|
101
|
-
- `true` by default
|
|
102
|
-
- `AIOCS_SOURCE_SPEC_DIRS`
|
|
103
|
-
- comma-separated list of directories to scan for `.yaml`, `.yml`, and `.json` source specs
|
|
104
|
-
- default points at the bundled `sources/` directory in the image and local repo
|
|
105
|
-
|
|
106
|
-
### Logging
|
|
107
|
-
|
|
108
|
-
- human mode: concise single-line operational logs
|
|
109
|
-
- JSON mode: one JSON event per line with `event`, `timestamp`, and event-specific fields
|
|
110
|
-
|
|
111
|
-
### Failure model
|
|
112
|
-
|
|
113
|
-
- invalid env config fails fast at startup
|
|
114
|
-
- invalid source spec files fail the cycle and are logged explicitly
|
|
115
|
-
- fetch failures for one source do not kill the daemon process unless startup config is invalid
|
|
116
|
-
|
|
117
|
-
## Docker Design
|
|
118
|
-
|
|
119
|
-
### Image
|
|
120
|
-
|
|
121
|
-
Ship a Dockerfile that builds `aiocs`, includes the bundled `sources/` directory, and runs:
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
./dist/cli.js daemon
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Runtime contract
|
|
128
|
-
|
|
129
|
-
- mount persistent data to `/root/.aiocs/data` or provide `AIOCS_DATA_DIR`
|
|
130
|
-
- optional config mount for `/root/.aiocs/config`
|
|
131
|
-
- source specs available from bundled `/app/sources` by default
|
|
132
|
-
- allow overriding `AIOCS_SOURCE_SPEC_DIRS` with mounted custom directories
|
|
133
|
-
|
|
134
|
-
### Compose
|
|
135
|
-
|
|
136
|
-
Ship a compose example that:
|
|
137
|
-
|
|
138
|
-
- builds the image locally
|
|
139
|
-
- mounts a persistent volume for the data directory
|
|
140
|
-
- sets `AIOCS_DAEMON_INTERVAL_MINUTES`
|
|
141
|
-
- optionally mounts a host directory of custom source specs
|
|
142
|
-
|
|
143
|
-
## Testing Strategy
|
|
144
|
-
|
|
145
|
-
- CLI tests for `--json` across representative commands and failure paths
|
|
146
|
-
- unit tests for daemon env parsing and cycle behavior
|
|
147
|
-
- integration tests for daemon bootstrap + due refresh behavior with a short injected interval
|
|
148
|
-
- existing CLI/fetch regression suite stays green in human mode
|
|
149
|
-
|
|
150
|
-
## Risks And Mitigations
|
|
151
|
-
|
|
152
|
-
- daemon JSON logs differ from one-shot JSON
|
|
153
|
-
- mitigate by documenting daemon as the explicit streaming exception
|
|
154
|
-
- source spec drift inside long-running containers
|
|
155
|
-
- mitigate by re-upserting source specs each cycle
|
|
156
|
-
- duplicated output logic across commands
|
|
157
|
-
- mitigate by centralizing response/error emission in one CLI output path
|
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
# aiocs Hybrid Search Design
|
|
2
|
-
|
|
3
|
-
Date: 2026-03-28
|
|
4
|
-
|
|
5
|
-
## Goal
|
|
6
|
-
|
|
7
|
-
Add hybrid retrieval to `aiocs` so agents get better docs results for fuzzy and conceptual queries without weakening the current source/snapshot semantics.
|
|
8
|
-
|
|
9
|
-
This design keeps `aiocs` as the canonical docs system:
|
|
10
|
-
|
|
11
|
-
- `aiocs` owns fetching, normalization, chunking, snapshots, canaries, diffs, and ranking policy
|
|
12
|
-
- SQLite FTS5 remains the primary lexical index and source of truth
|
|
13
|
-
- a dedicated `aiocs-qdrant` container stores only derived embedding vectors for `aiocs`
|
|
14
|
-
- local Ollama generates embeddings
|
|
15
|
-
- SocratiCode remains separate and may later consume selected `aiocs` snapshots, but it is not the runtime for `aiocs` hybrid search
|
|
16
|
-
|
|
17
|
-
## Non-Goals
|
|
18
|
-
|
|
19
|
-
- No replacement of SQLite FTS5 with vector-only search
|
|
20
|
-
- No reuse of SocratiCode's Qdrant collection or deployment
|
|
21
|
-
- No new hosted service or remote dependency
|
|
22
|
-
- No cross-repo code search in this phase
|
|
23
|
-
- No backup/restore of vector state; vectors are rebuildable
|
|
24
|
-
|
|
25
|
-
## Why This Shape
|
|
26
|
-
|
|
27
|
-
The current `aiocs` search path in [catalog.ts](../src/catalog/catalog.ts) is pure FTS5 BM25 over the latest successful snapshots. That is excellent for exact docs lookups, versioned terms, and API names. It is weaker for:
|
|
28
|
-
|
|
29
|
-
- synonym-heavy prompts
|
|
30
|
-
- conceptual questions
|
|
31
|
-
- vague agent prompts
|
|
32
|
-
- recall across wording shifts in docs
|
|
33
|
-
|
|
34
|
-
Hybrid retrieval is the right improvement, but the ranking policy must remain docs-aware. That is why `aiocs` itself should own the hybrid query plan instead of delegating search semantics to a generic vector layer.
|
|
35
|
-
|
|
36
|
-
## Recommended Architecture
|
|
37
|
-
|
|
38
|
-
### 1. Canonical storage remains SQLite
|
|
39
|
-
|
|
40
|
-
SQLite remains the system of record for:
|
|
41
|
-
|
|
42
|
-
- sources
|
|
43
|
-
- snapshots
|
|
44
|
-
- pages
|
|
45
|
-
- chunks
|
|
46
|
-
- project links
|
|
47
|
-
- fetch/canary/daemon metadata
|
|
48
|
-
|
|
49
|
-
Add embedding-specific metadata tables in the same catalog:
|
|
50
|
-
|
|
51
|
-
- `embedding_models`
|
|
52
|
-
- `embedding_jobs`
|
|
53
|
-
- `embedding_state`
|
|
54
|
-
|
|
55
|
-
These track derived vector work, not source content.
|
|
56
|
-
|
|
57
|
-
### 2. Dedicated `aiocs-qdrant`
|
|
58
|
-
|
|
59
|
-
Ship a separate Qdrant container in `aiocs/docker-compose.yml`:
|
|
60
|
-
|
|
61
|
-
- service name: `aiocs-qdrant`
|
|
62
|
-
- dedicated persistent volume
|
|
63
|
-
- default local URL from `aiocs` runtime
|
|
64
|
-
|
|
65
|
-
This container is strictly for `aiocs`. It must not share collections or lifecycle with SocratiCode.
|
|
66
|
-
|
|
67
|
-
### 3. Ollama as embedding provider
|
|
68
|
-
|
|
69
|
-
Use Ollama locally for embeddings, with explicit config:
|
|
70
|
-
|
|
71
|
-
- provider: `ollama`
|
|
72
|
-
- model: configurable
|
|
73
|
-
- default model chosen from the local setup you already use for embeddings
|
|
74
|
-
|
|
75
|
-
`aiocs` should own its own embedding config even if the model matches SocratiCode.
|
|
76
|
-
|
|
77
|
-
### 4. Hybrid retrieval strategy
|
|
78
|
-
|
|
79
|
-
Search modes:
|
|
80
|
-
|
|
81
|
-
- `lexical`
|
|
82
|
-
- `hybrid`
|
|
83
|
-
- `semantic`
|
|
84
|
-
- `auto`
|
|
85
|
-
|
|
86
|
-
Default: `auto`
|
|
87
|
-
|
|
88
|
-
Behavior:
|
|
89
|
-
|
|
90
|
-
- if vector infra is healthy and the target scope has embeddings, use hybrid
|
|
91
|
-
- otherwise fall back to lexical
|
|
92
|
-
|
|
93
|
-
Hybrid query plan:
|
|
94
|
-
|
|
95
|
-
1. Run FTS5 BM25 against SQLite
|
|
96
|
-
2. Run vector similarity search in Qdrant
|
|
97
|
-
3. Fuse result sets with Reciprocal Rank Fusion
|
|
98
|
-
4. Return the existing `aiocs` chunk shape plus hybrid metadata
|
|
99
|
-
|
|
100
|
-
RRF is preferred over weighted score mixing because:
|
|
101
|
-
|
|
102
|
-
- BM25 and cosine/dot-product scores are not directly comparable
|
|
103
|
-
- RRF is robust across model swaps
|
|
104
|
-
- RRF is simple and stable for agents
|
|
105
|
-
|
|
106
|
-
## Data Model
|
|
107
|
-
|
|
108
|
-
### SQLite additions
|
|
109
|
-
|
|
110
|
-
#### `embedding_models`
|
|
111
|
-
|
|
112
|
-
Tracks the embedding configuration currently in use.
|
|
113
|
-
|
|
114
|
-
Columns:
|
|
115
|
-
|
|
116
|
-
- `id`
|
|
117
|
-
- `provider`
|
|
118
|
-
- `model`
|
|
119
|
-
- `dimension`
|
|
120
|
-
- `distance_metric`
|
|
121
|
-
- `created_at`
|
|
122
|
-
- `active`
|
|
123
|
-
|
|
124
|
-
#### `embedding_state`
|
|
125
|
-
|
|
126
|
-
Tracks per-chunk embedding lifecycle.
|
|
127
|
-
|
|
128
|
-
Columns:
|
|
129
|
-
|
|
130
|
-
- `chunk_id`
|
|
131
|
-
- `source_id`
|
|
132
|
-
- `snapshot_id`
|
|
133
|
-
- `embedding_model_id`
|
|
134
|
-
- `content_hash`
|
|
135
|
-
- `vector_id`
|
|
136
|
-
- `status` (`pending`, `embedded`, `stale`, `failed`)
|
|
137
|
-
- `last_embedded_at`
|
|
138
|
-
- `last_error`
|
|
139
|
-
|
|
140
|
-
This avoids guessing whether a vector is current.
|
|
141
|
-
|
|
142
|
-
#### `embedding_jobs`
|
|
143
|
-
|
|
144
|
-
Persistent queue for background embedding work.
|
|
145
|
-
|
|
146
|
-
Columns:
|
|
147
|
-
|
|
148
|
-
- `id`
|
|
149
|
-
- `source_id`
|
|
150
|
-
- `snapshot_id`
|
|
151
|
-
- `job_type` (`snapshot_latest`, `snapshot_remove`, `reindex_model`)
|
|
152
|
-
- `status` (`pending`, `running`, `failed`, `completed`)
|
|
153
|
-
- `attempt_count`
|
|
154
|
-
- `last_error`
|
|
155
|
-
- `created_at`
|
|
156
|
-
- `started_at`
|
|
157
|
-
- `finished_at`
|
|
158
|
-
|
|
159
|
-
This queue is important because embedding is slower and more failure-prone than lexical indexing.
|
|
160
|
-
|
|
161
|
-
### Qdrant payload
|
|
162
|
-
|
|
163
|
-
Each vector point stores:
|
|
164
|
-
|
|
165
|
-
- `chunk_id`
|
|
166
|
-
- `source_id`
|
|
167
|
-
- `snapshot_id`
|
|
168
|
-
- `page_url`
|
|
169
|
-
- `page_title`
|
|
170
|
-
- `section_title`
|
|
171
|
-
- `embedding_model_id`
|
|
172
|
-
- `content_hash`
|
|
173
|
-
- `is_latest_snapshot`
|
|
174
|
-
|
|
175
|
-
The point id should be stable and deterministic per chunk/model, for example:
|
|
176
|
-
|
|
177
|
-
- `${embedding_model_id}:${chunk_id}:${content_hash}`
|
|
178
|
-
|
|
179
|
-
That makes reindexing idempotent.
|
|
180
|
-
|
|
181
|
-
## Indexing Lifecycle
|
|
182
|
-
|
|
183
|
-
### Snapshot write path
|
|
184
|
-
|
|
185
|
-
When `recordSuccessfulSnapshot()` writes chunks:
|
|
186
|
-
|
|
187
|
-
1. normal SQLite snapshot/page/chunk write happens first
|
|
188
|
-
2. `aiocs` marks the new latest snapshot as requiring embeddings
|
|
189
|
-
3. an embedding job is enqueued
|
|
190
|
-
|
|
191
|
-
The fetch path must not block on embedding completion. Search must continue working lexically even with zero vectors.
|
|
192
|
-
|
|
193
|
-
### Latest-only vector policy
|
|
194
|
-
|
|
195
|
-
For this phase, vectors should be generated only for the latest successful snapshot per source.
|
|
196
|
-
|
|
197
|
-
Rationale:
|
|
198
|
-
|
|
199
|
-
- minimizes vector volume
|
|
200
|
-
- aligns with how current `search()` already targets latest successful snapshots by default
|
|
201
|
-
- avoids wasting GPU/CPU on historical snapshots rarely used in normal retrieval
|
|
202
|
-
|
|
203
|
-
Historical snapshot diffs remain SQLite-only.
|
|
204
|
-
|
|
205
|
-
If a source gets a new latest snapshot:
|
|
206
|
-
|
|
207
|
-
- mark prior latest vectors as stale
|
|
208
|
-
- enqueue cleanup/remove for stale vectors
|
|
209
|
-
- enqueue embedding for the new latest snapshot
|
|
210
|
-
|
|
211
|
-
### Embedding worker
|
|
212
|
-
|
|
213
|
-
Add an embedding worker loop to the daemon process:
|
|
214
|
-
|
|
215
|
-
- fetch/canary cycle remains unchanged in purpose
|
|
216
|
-
- after refresh work, process a bounded number of embedding jobs
|
|
217
|
-
- retry failed jobs with capped attempts and backoff
|
|
218
|
-
|
|
219
|
-
The daemon becomes the single operational background process for both freshness and vector health.
|
|
220
|
-
|
|
221
|
-
## Query Path
|
|
222
|
-
|
|
223
|
-
### Lexical path
|
|
224
|
-
|
|
225
|
-
Keep the existing query path as-is for:
|
|
226
|
-
|
|
227
|
-
- `searchMode=lexical`
|
|
228
|
-
- `searchMode=auto` when vectors are unavailable
|
|
229
|
-
|
|
230
|
-
### Semantic path
|
|
231
|
-
|
|
232
|
-
For `searchMode=semantic`:
|
|
233
|
-
|
|
234
|
-
1. embed the query with Ollama
|
|
235
|
-
2. query Qdrant with the same scope constraints
|
|
236
|
-
3. fetch result chunk records from SQLite by `chunk_id`
|
|
237
|
-
4. return ordered results
|
|
238
|
-
|
|
239
|
-
### Hybrid path
|
|
240
|
-
|
|
241
|
-
For `searchMode=hybrid`:
|
|
242
|
-
|
|
243
|
-
1. run lexical query for top `N`
|
|
244
|
-
2. run semantic query for top `K`
|
|
245
|
-
3. fuse with RRF
|
|
246
|
-
4. fetch canonical chunk data from SQLite
|
|
247
|
-
5. return result rows with mode metadata
|
|
248
|
-
|
|
249
|
-
Initial defaults:
|
|
250
|
-
|
|
251
|
-
- BM25 candidate window: `40`
|
|
252
|
-
- vector candidate window: `40`
|
|
253
|
-
- final page size: existing `limit`
|
|
254
|
-
- RRF `k`: `60`
|
|
255
|
-
|
|
256
|
-
These should be configurable, but not user-tuned in the first release.
|
|
257
|
-
|
|
258
|
-
## Filtering and Invariants
|
|
259
|
-
|
|
260
|
-
All source/snapshot/project scoping remains authoritative in `aiocs`, not in Qdrant.
|
|
261
|
-
|
|
262
|
-
The runtime must:
|
|
263
|
-
|
|
264
|
-
- resolve project scope in SQLite first
|
|
265
|
-
- resolve latest snapshot ids in SQLite first
|
|
266
|
-
- constrain vector retrieval to those snapshot ids
|
|
267
|
-
|
|
268
|
-
This preserves the current guarantee that source/project/snapshot filters are exact.
|
|
269
|
-
|
|
270
|
-
Qdrant is a retrieval backend, not a source of truth.
|
|
271
|
-
|
|
272
|
-
## CLI and MCP Changes
|
|
273
|
-
|
|
274
|
-
### CLI
|
|
275
|
-
|
|
276
|
-
Extend `docs search` with:
|
|
277
|
-
|
|
278
|
-
- `--mode lexical|hybrid|semantic|auto`
|
|
279
|
-
|
|
280
|
-
Add operational commands:
|
|
281
|
-
|
|
282
|
-
- `docs embeddings status`
|
|
283
|
-
- `docs embeddings backfill [source-id|all]`
|
|
284
|
-
- `docs embeddings clear [source-id|all]`
|
|
285
|
-
|
|
286
|
-
Optional later:
|
|
287
|
-
|
|
288
|
-
- `docs embeddings doctor`
|
|
289
|
-
|
|
290
|
-
### MCP
|
|
291
|
-
|
|
292
|
-
Extend `search` input with `mode`.
|
|
293
|
-
|
|
294
|
-
Add tools:
|
|
295
|
-
|
|
296
|
-
- `embeddings_status`
|
|
297
|
-
- `embeddings_backfill`
|
|
298
|
-
- `embeddings_clear`
|
|
299
|
-
|
|
300
|
-
The existing JSON envelope remains unchanged.
|
|
301
|
-
|
|
302
|
-
## Docker and Runtime
|
|
303
|
-
|
|
304
|
-
### `docker-compose.yml`
|
|
305
|
-
|
|
306
|
-
Add:
|
|
307
|
-
|
|
308
|
-
- `aiocs-qdrant`
|
|
309
|
-
- volume for Qdrant storage
|
|
310
|
-
- daemon env vars for Qdrant/Ollama config
|
|
311
|
-
|
|
312
|
-
The daemon container should depend on Qdrant health, not just startup ordering.
|
|
313
|
-
|
|
314
|
-
### Config
|
|
315
|
-
|
|
316
|
-
Add environment variables:
|
|
317
|
-
|
|
318
|
-
- `AIOCS_SEARCH_MODE_DEFAULT`
|
|
319
|
-
- `AIOCS_QDRANT_URL`
|
|
320
|
-
- `AIOCS_QDRANT_COLLECTION`
|
|
321
|
-
- `AIOCS_EMBEDDING_PROVIDER`
|
|
322
|
-
- `AIOCS_OLLAMA_BASE_URL`
|
|
323
|
-
- `AIOCS_OLLAMA_EMBEDDING_MODEL`
|
|
324
|
-
- `AIOCS_EMBEDDING_BATCH_SIZE`
|
|
325
|
-
- `AIOCS_EMBEDDING_JOB_LIMIT_PER_CYCLE`
|
|
326
|
-
|
|
327
|
-
Defaults should make local Docker + local Ollama work without extra ceremony.
|
|
328
|
-
|
|
329
|
-
## Doctor and Health
|
|
330
|
-
|
|
331
|
-
Extend `doctor` with new checks:
|
|
332
|
-
|
|
333
|
-
- `qdrant`
|
|
334
|
-
- `embedding-provider`
|
|
335
|
-
- `embedding-coverage`
|
|
336
|
-
- `embedding-backlog`
|
|
337
|
-
|
|
338
|
-
Examples:
|
|
339
|
-
|
|
340
|
-
- pass: vectors are healthy and mostly current
|
|
341
|
-
- warn: lexical search works, but vectors are unavailable or backlog is growing
|
|
342
|
-
- fail: `searchMode=hybrid` default is configured but vector infra is broken
|
|
343
|
-
|
|
344
|
-
## Backups
|
|
345
|
-
|
|
346
|
-
Backups remain SQLite/config only.
|
|
347
|
-
|
|
348
|
-
Do not export Qdrant state in `backup export`.
|
|
349
|
-
|
|
350
|
-
After `backup import`:
|
|
351
|
-
|
|
352
|
-
- mark embeddings stale
|
|
353
|
-
- enqueue re-embedding for current latest snapshots
|
|
354
|
-
|
|
355
|
-
This keeps backup semantics simple and avoids trying to synchronize two storage engines.
|
|
356
|
-
|
|
357
|
-
## Testing Strategy
|
|
358
|
-
|
|
359
|
-
### Unit
|
|
360
|
-
|
|
361
|
-
- query mode parsing
|
|
362
|
-
- embedding config validation
|
|
363
|
-
- RRF fusion
|
|
364
|
-
- deterministic vector id generation
|
|
365
|
-
- embedding-state transitions
|
|
366
|
-
|
|
367
|
-
### Integration
|
|
368
|
-
|
|
369
|
-
- snapshot creation enqueues embedding work
|
|
370
|
-
- daemon processes embedding jobs
|
|
371
|
-
- hybrid search falls back cleanly when vector infra is absent
|
|
372
|
-
- hybrid search respects source/project/snapshot filters
|
|
373
|
-
- `backup import` triggers rebuild behavior
|
|
374
|
-
|
|
375
|
-
### Docker/runtime
|
|
376
|
-
|
|
377
|
-
- compose config includes dedicated Qdrant service
|
|
378
|
-
- doctor reports degraded state when Qdrant is unreachable
|
|
379
|
-
|
|
380
|
-
## Migration Strategy
|
|
381
|
-
|
|
382
|
-
1. add schema and config surfaces
|
|
383
|
-
2. add Qdrant/Ollama client integration
|
|
384
|
-
3. add embedding queue and daemon worker
|
|
385
|
-
4. add hybrid query mode
|
|
386
|
-
5. add doctor/docs/tests
|
|
387
|
-
|
|
388
|
-
This keeps lexical search live throughout the rollout.
|
|
389
|
-
|
|
390
|
-
## Risks
|
|
391
|
-
|
|
392
|
-
### 1. Ranking regressions
|
|
393
|
-
|
|
394
|
-
Mitigation:
|
|
395
|
-
|
|
396
|
-
- lexical remains available
|
|
397
|
-
- `auto` falls back safely
|
|
398
|
-
- RRF instead of fragile score blending
|
|
399
|
-
|
|
400
|
-
### 2. Embedding backlog growth
|
|
401
|
-
|
|
402
|
-
Mitigation:
|
|
403
|
-
|
|
404
|
-
- latest-only vector policy
|
|
405
|
-
- bounded jobs per cycle
|
|
406
|
-
- explicit backlog health checks
|
|
407
|
-
|
|
408
|
-
### 3. Vector/schema drift
|
|
409
|
-
|
|
410
|
-
Mitigation:
|
|
411
|
-
|
|
412
|
-
- embedding model registry in SQLite
|
|
413
|
-
- deterministic point ids
|
|
414
|
-
- explicit stale/rebuild lifecycle
|
|
415
|
-
|
|
416
|
-
## Recommended Next Step
|
|
417
|
-
|
|
418
|
-
Turn this design into an execution plan and implement it in phases, starting with:
|
|
419
|
-
|
|
420
|
-
1. embedding config + schema
|
|
421
|
-
2. dedicated Qdrant runtime
|
|
422
|
-
3. daemon embedding worker
|
|
423
|
-
4. hybrid search mode
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Tag-Driven Release Pipeline Design
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
`aiocs` should release as the public scoped npm package `@bodhi-ventures/aiocs` through a stable, tag-driven GitHub Actions workflow. The repository remains the source of truth for versioning. Releases are created only from pushed stable tags of the form `vX.Y.Z`.
|
|
6
|
-
|
|
7
|
-
The workflow must never mutate git state. It should validate the tag against `package.json`, run the full release verification stack, publish publicly to npm with provenance, and create a GitHub release from the same tag.
|
|
8
|
-
|
|
9
|
-
## Goals
|
|
10
|
-
|
|
11
|
-
- publish `aiocs` publicly under `@bodhi-ventures/aiocs`
|
|
12
|
-
- eliminate workflow-managed version bumps, commits, and tag creation
|
|
13
|
-
- remove improvised git author configuration from the release workflow
|
|
14
|
-
- make the release contract deterministic and easy to reason about
|
|
15
|
-
- keep CI and release validation aligned with the actual shipped package surface
|
|
16
|
-
|
|
17
|
-
## Non-Goals
|
|
18
|
-
|
|
19
|
-
- prerelease publishing
|
|
20
|
-
- automatic releases from `main`
|
|
21
|
-
- workspace/multi-package publishing
|
|
22
|
-
- alternative binary distribution outside npm
|
|
23
|
-
|
|
24
|
-
## Current State
|
|
25
|
-
|
|
26
|
-
- `package.json` still uses the unscoped package name `aiocs`
|
|
27
|
-
- the current release workflow is `workflow_dispatch`-driven
|
|
28
|
-
- the workflow edits `package.json`, creates commits and tags, and configures a bot git identity
|
|
29
|
-
- release automation is more complex than necessary and mixes version mutation with publishing
|
|
30
|
-
|
|
31
|
-
## Chosen Design
|
|
32
|
-
|
|
33
|
-
### Package Identity
|
|
34
|
-
|
|
35
|
-
- rename the npm package to `@bodhi-ventures/aiocs`
|
|
36
|
-
- keep the package public
|
|
37
|
-
- keep CLI command names unchanged:
|
|
38
|
-
- `docs`
|
|
39
|
-
- `aiocs-mcp`
|
|
40
|
-
- add explicit `publishConfig`:
|
|
41
|
-
- `access: public`
|
|
42
|
-
- `provenance: true`
|
|
43
|
-
|
|
44
|
-
### Release Trigger
|
|
45
|
-
|
|
46
|
-
- release workflow triggers only on pushed stable semver tags matching `v*.*.*`
|
|
47
|
-
- the tag version must match `package.json.version` exactly
|
|
48
|
-
- only stable releases are supported; prerelease tags are rejected
|
|
49
|
-
|
|
50
|
-
### Release Workflow Behavior
|
|
51
|
-
|
|
52
|
-
- check out the tagged revision
|
|
53
|
-
- install dependencies and release prerequisites
|
|
54
|
-
- fail fast unless all of these are true:
|
|
55
|
-
- tag matches `vX.Y.Z`
|
|
56
|
-
- `package.json.version === X.Y.Z`
|
|
57
|
-
- `package.json.name === @bodhi-ventures/aiocs`
|
|
58
|
-
- run full release validation:
|
|
59
|
-
- `pnpm lint`
|
|
60
|
-
- `pnpm test`
|
|
61
|
-
- `pnpm build`
|
|
62
|
-
- `npm pack --dry-run`
|
|
63
|
-
- built CLI smoke, at minimum `node dist/cli.js --version`
|
|
64
|
-
- publish to npm with the existing `NPM_TOKEN` org secret
|
|
65
|
-
- create a GitHub release for the pushed tag
|
|
66
|
-
- if a GitHub release already exists for that tag, do not recreate it
|
|
67
|
-
|
|
68
|
-
### Rerun and Partial-Failure Policy
|
|
69
|
-
|
|
70
|
-
Tag releases must be safely rerunnable.
|
|
71
|
-
|
|
72
|
-
- `workflow_dispatch` is removed entirely; tag pushes are the only release trigger
|
|
73
|
-
- if a rerun sees that `@bodhi-ventures/aiocs@X.Y.Z` is already published on npm, it must skip `npm publish` instead of failing
|
|
74
|
-
- if a rerun sees that the GitHub release for `vX.Y.Z` already exists, it must skip release creation instead of failing
|
|
75
|
-
- validation steps always run on every attempt, even when publish/release steps are skipped
|
|
76
|
-
- if npm already contains `@bodhi-ventures/aiocs@X.Y.Z` but the checked-out tag does not match `package.json.version === X.Y.Z` or `package.json.name === @bodhi-ventures/aiocs`, the workflow must fail fast
|
|
77
|
-
- the workflow should treat npm and GitHub release publication as independently idempotent so a partial success can be completed by rerunning the same tag job
|
|
78
|
-
|
|
79
|
-
### Git Behavior
|
|
80
|
-
|
|
81
|
-
- the workflow never edits files
|
|
82
|
-
- the workflow never commits
|
|
83
|
-
- the workflow never creates tags
|
|
84
|
-
- the workflow never configures a synthetic git author
|
|
85
|
-
|
|
86
|
-
Version bumps happen in normal development flow:
|
|
87
|
-
|
|
88
|
-
1. update `package.json.version`
|
|
89
|
-
2. commit the version bump
|
|
90
|
-
3. create tag `vX.Y.Z`
|
|
91
|
-
4. push commit and tag
|
|
92
|
-
|
|
93
|
-
## CI Alignment
|
|
94
|
-
|
|
95
|
-
CI remains the pre-release gate and should stay close to the release workflow:
|
|
96
|
-
|
|
97
|
-
- install dependencies
|
|
98
|
-
- install Playwright Chromium
|
|
99
|
-
- run lint, tests, build, and `npm pack --dry-run`
|
|
100
|
-
- validate Docker image and compose config
|
|
101
|
-
- smoke test the packaged CLI surface
|
|
102
|
-
|
|
103
|
-
CI should also assert package metadata consistency where useful, especially the scoped package name.
|
|
104
|
-
|
|
105
|
-
## Documentation Changes
|
|
106
|
-
|
|
107
|
-
Update repository docs to reflect the scoped public package and release process:
|
|
108
|
-
|
|
109
|
-
- install command becomes `npm install -g @bodhi-ventures/aiocs`
|
|
110
|
-
- add a short release section to the README describing the tag-based flow
|
|
111
|
-
- keep the CLI-facing command examples unchanged
|
|
112
|
-
|
|
113
|
-
## Risks
|
|
114
|
-
|
|
115
|
-
- npm org publishing can still fail if org/package permissions are not configured correctly on npmjs
|
|
116
|
-
- the first release is the highest-risk run because the scoped package has not been proven yet
|
|
117
|
-
- GitHub Actions can only validate the workflow structure locally; final proof requires one live tag release
|
|
118
|
-
|
|
119
|
-
## Acceptance Criteria
|
|
120
|
-
|
|
121
|
-
- `package.json` is updated to `@bodhi-ventures/aiocs`
|
|
122
|
-
- release workflow triggers on stable tags only
|
|
123
|
-
- release workflow does not mutate git state
|
|
124
|
-
- release workflow validates tag/version/name consistency before publishing
|
|
125
|
-
- README/install docs reference the scoped package name
|
|
126
|
-
- local verification passes on the updated tree:
|
|
127
|
-
- lint
|
|
128
|
-
- tests
|
|
129
|
-
- build
|
|
130
|
-
- `npm pack --dry-run`
|
|
131
|
-
- the first real release can be executed by bumping version, pushing a `vX.Y.Z` tag, and observing npm + GitHub release creation
|
|
132
|
-
|
|
133
|
-
## Follow-Up
|
|
134
|
-
|
|
135
|
-
After implementation, do one real tagged stable release to prove the pipeline end to end.
|