@hybridlabor-api/aos 4.0.2 → 4.2.0-beta.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.
Files changed (31) hide show
  1. package/.agents/agents.md +77 -0
  2. package/.agents/graph.md +43 -0
  3. package/.agents/state.schema.json +6 -0
  4. package/.claude/agents/database-reviewer.md +109 -0
  5. package/.claude/agents/go-build-resolver.md +112 -0
  6. package/.claude/agents/opensource-forker.md +216 -0
  7. package/.claude/agents/opensource-sanitizer.md +206 -0
  8. package/.claude/agents/security-reviewer.md +126 -0
  9. package/.claude/agents/silent-failure-hunter.md +68 -0
  10. package/.claude/workflows/startcycle-dispatch.mjs +126 -8
  11. package/CLAUDE.md +62 -0
  12. package/GEMINI.md +9 -1
  13. package/README.md +12 -19
  14. package/THIRD_PARTY_NOTICES.md +133 -0
  15. package/package.json +4 -2
  16. package/skills/basic/bdbmediastorm/SKILL.md +7 -1
  17. package/skills/basic/startcycle/SKILL.md +21 -0
  18. package/skills/basic/startcycle-graph/SKILL.md +27 -7
  19. package/skills/basic/startcycle-graph-user/SKILL.md +65 -11
  20. package/skills/bdbrainstorm/SKILL.md +1 -0
  21. package/skills/global_config/plan-canvas/SKILL.md +233 -0
  22. package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +59 -0
  23. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +301 -0
  24. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sdk.js +239 -0
  25. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +636 -0
  26. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sessions.js +271 -0
  27. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +630 -0
  28. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +419 -0
  29. package/docs/sessions/AUDIT-HANDOVER-2026-08-28.md +0 -169
  30. package/docs/sessions/BDB_REMOTEOS_MCP_HANDOVER.md +0 -130
  31. package/docs/sessions/SESSION-HANDOVER-v3.13.md +0 -249
@@ -0,0 +1,419 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Plan Canvas CLI — open plan artifacts in a browser review canvas and block
6
+ * on human feedback.
7
+ *
8
+ * node scripts/plan-canvas.js open .claude/plans/feature.plan.md
9
+ * node scripts/plan-canvas.js await .claude/plans/feature.plan.md
10
+ * node scripts/plan-canvas.js await <file> --reply "Updated section 3."
11
+ * node scripts/plan-canvas.js end <file>
12
+ * node scripts/plan-canvas.js stop
13
+ *
14
+ * Agents: `open` returns immediately (the server is a detached process);
15
+ * `await` long-polls until the human sends feedback, a verdict, or ends the
16
+ * session, then prints a JSON payload to stdout. Progress notes go to stderr
17
+ * so stdout stays parseable.
18
+ *
19
+ * Source: affaan-m/ECC — MIT, see THIRD_PARTY_NOTICES.md
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const http = require('http');
24
+ const path = require('path');
25
+ const { spawn } = require('child_process');
26
+
27
+ const {
28
+ canonicalizeArtifactPath,
29
+ createSessionStore,
30
+ resolveStateDir,
31
+ sessionKeyFor
32
+ } = require('./lib/plan-canvas/sessions');
33
+ const {
34
+ DEFAULT_HOST,
35
+ createPlanCanvasServer,
36
+ resolveIdleTimeoutMs,
37
+ resolvePort
38
+ } = require('./lib/plan-canvas/server');
39
+
40
+ const VERSION = '1.0.0'; // vendored Plan Canvas protocol version; matches SKILL.md metadata.version.
41
+ // Bump when the vendored JS changes, to force a stale detached server to restart.
42
+
43
+ const SAFE_REQUEST_PATHS = new Set([
44
+ '/',
45
+ '/health',
46
+ '/shutdown',
47
+ '/api/await',
48
+ '/api/sessions',
49
+ '/api/end'
50
+ ]);
51
+ const SESSION_REPLY_PATH = /^\/api\/session\/[a-f0-9]{12}\/(reply|typing)$/;
52
+
53
+ function usage() {
54
+ return [
55
+ 'Plan Canvas - review plans and HTML artifacts in the browser',
56
+ '',
57
+ 'Usage:',
58
+ ' aos-plan-canvas Show server status and sessions',
59
+ ' aos-plan-canvas open <file> Open (or resume) a review session',
60
+ ' aos-plan-canvas await <file> Block until the human sends feedback',
61
+ ' aos-plan-canvas pending Show feedback queued for no listener',
62
+ ' aos-plan-canvas typing <file> Show a thinking/typing indicator in chat',
63
+ ' aos-plan-canvas end <file> End a session as the agent',
64
+ ' aos-plan-canvas stop Shut down the canvas server',
65
+ ' aos-plan-canvas server Run the server in the foreground',
66
+ '',
67
+ 'Options:',
68
+ ' open: --no-open Do not launch a browser window',
69
+ ' --reopen Reopen a session the user ended from the browser',
70
+ ' await: --reply <msg> Show an agent reply in the canvas chat before waiting',
71
+ ' --timeout-ms <n> Return {status:"waiting"} after n ms (tests/debug only)',
72
+ ' typing: --state <thinking|typing|idle> Defaults to typing',
73
+ ' server: --port <n> --host <h>',
74
+ '',
75
+ 'Environment: AOS_PLAN_CANVAS_PORT, AOS_PLAN_CANVAS_STATE_DIR, AOS_PLAN_CANVAS_IDLE_MS'
76
+ ].join('\n');
77
+ }
78
+
79
+ function valueAfter(args, name) {
80
+ const index = args.indexOf(name);
81
+ return index >= 0 && index + 1 < args.length ? args[index + 1] : null;
82
+ }
83
+
84
+ function serverInfoPath(stateDir) {
85
+ return path.join(stateDir, 'server.json');
86
+ }
87
+
88
+ function readServerInfo(stateDir) {
89
+ try {
90
+ return JSON.parse(fs.readFileSync(serverInfoPath(stateDir), 'utf8'));
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ function validatePort(port) {
97
+ const value = Number(port);
98
+ if (!Number.isInteger(value) || value < 0 || value > 65535) {
99
+ throw new Error(`invalid plan-canvas server port: ${port}`);
100
+ }
101
+ return value;
102
+ }
103
+
104
+ function validateRequestPath(requestPath) {
105
+ if (typeof requestPath !== 'string' || !requestPath.startsWith('/')) {
106
+ throw new Error('plan-canvas request path must be root-relative');
107
+ }
108
+ const url = new URL(requestPath, `http://${DEFAULT_HOST}`);
109
+ if (url.hostname !== DEFAULT_HOST) {
110
+ throw new Error('plan-canvas request path must stay on the loopback server');
111
+ }
112
+ if (!SAFE_REQUEST_PATHS.has(url.pathname) && !SESSION_REPLY_PATH.test(url.pathname)) {
113
+ throw new Error(`unsupported plan-canvas request path: ${url.pathname}`);
114
+ }
115
+ return `${url.pathname}${url.search}`;
116
+ }
117
+
118
+ function requestOptions(port, method, requestPath, headers) {
119
+ return {
120
+ host: DEFAULT_HOST,
121
+ port: validatePort(port),
122
+ method,
123
+ path: validateRequestPath(requestPath),
124
+ agent: false,
125
+ headers
126
+ };
127
+ }
128
+
129
+ function request(port, method, requestPath, body = null) {
130
+ return new Promise((resolve, reject) => {
131
+ const payload = body === null ? null : JSON.stringify(body);
132
+ const req = http.request(
133
+ requestOptions(
134
+ port,
135
+ method,
136
+ requestPath,
137
+ payload
138
+ ? { 'content-type': 'application/json', 'content-length': Buffer.byteLength(payload) }
139
+ : {}
140
+ ),
141
+ res => {
142
+ let data = '';
143
+ res.on('data', chunk => {
144
+ data += chunk;
145
+ });
146
+ res.on('end', () => {
147
+ try {
148
+ resolve({ statusCode: res.statusCode, body: JSON.parse(data.trim() || '{}') });
149
+ } catch {
150
+ resolve({ statusCode: res.statusCode, body: {} });
151
+ }
152
+ });
153
+ }
154
+ );
155
+ req.on('error', reject);
156
+ if (payload) req.write(payload);
157
+ req.end();
158
+ });
159
+ }
160
+
161
+ async function healthCheck(port) {
162
+ try {
163
+ const res = await request(port, 'GET', '/health');
164
+ return res.body && res.body.app === 'aos-plan-canvas' ? res.body : null;
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+
170
+ function sleep(ms) {
171
+ return new Promise(resolve => setTimeout(resolve, ms));
172
+ }
173
+
174
+ // Start (or reuse) the detached canvas server and return its port. A version
175
+ // mismatch after this script is updated restarts the server so browser and CLI never
176
+ // disagree about the protocol.
177
+ async function ensureServer({ stateDir, port }) {
178
+ const health = await healthCheck(port);
179
+ if (health && health.version === VERSION) return port;
180
+ if (health) {
181
+ await request(port, 'POST', '/shutdown').catch(() => {});
182
+ for (let i = 0; i < 20 && (await healthCheck(port)); i++) await sleep(100);
183
+ }
184
+ fs.mkdirSync(stateDir, { recursive: true });
185
+ const logFd = fs.openSync(path.join(stateDir, 'server.log'), 'a');
186
+ const child = spawn(process.execPath, [__filename, 'server', '--port', String(port)], {
187
+ detached: true,
188
+ stdio: ['ignore', logFd, logFd],
189
+ env: { ...process.env, AOS_PLAN_CANVAS_STATE_DIR: stateDir }
190
+ });
191
+ child.unref();
192
+ fs.closeSync(logFd);
193
+ for (let i = 0; i < 50; i++) {
194
+ await sleep(100);
195
+ if (await healthCheck(port)) return port;
196
+ }
197
+ throw new Error(`plan-canvas server did not become healthy on port ${port}; check ${path.join(stateDir, 'server.log')}`);
198
+ }
199
+
200
+ function openBrowser(url) {
201
+ const platform = process.platform;
202
+ const [cmd, args] =
203
+ platform === 'darwin' ? ['open', [url]]
204
+ : platform === 'win32' ? ['cmd', ['/c', 'start', '', url]]
205
+ : ['xdg-open', [url]];
206
+ try {
207
+ spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();
208
+ return true;
209
+ } catch {
210
+ return false;
211
+ }
212
+ }
213
+
214
+ function output(payload) {
215
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
216
+ }
217
+
218
+ async function cmdStatus({ stateDir, port }) {
219
+ const health = await healthCheck(port);
220
+ if (!health) {
221
+ return { server: 'not running', hint: 'open an artifact to start one', stateDir };
222
+ }
223
+ const sessions = await request(port, 'GET', '/api/sessions');
224
+ return { server: `http://${DEFAULT_HOST}:${port}`, version: health.version, sessions: sessions.body.sessions };
225
+ }
226
+
227
+ async function cmdOpen(file, args, { stateDir, port }) {
228
+ if (!file) throw new Error('open requires a file path');
229
+ if (!fs.existsSync(path.resolve(file))) throw new Error(`artifact not found: ${file}`);
230
+ await ensureServer({ stateDir, port });
231
+ const res = await request(port, 'POST', '/api/sessions', {
232
+ file: path.resolve(file),
233
+ reopen: args.includes('--reopen')
234
+ });
235
+ if (res.statusCode === 409) return res.body;
236
+ if (res.statusCode !== 200) throw new Error(res.body.error || `open failed (HTTP ${res.statusCode})`);
237
+ const url = `http://${DEFAULT_HOST}:${port}${res.body.url}`;
238
+ const launched = args.includes('--no-open') ? false : openBrowser(url);
239
+ return {
240
+ status: 'open',
241
+ url,
242
+ browser: launched ? 'opened' : 'not opened',
243
+ next_step:
244
+ 'Run `aos-plan-canvas await <file>` and leave it running; it returns when the human sends feedback, a verdict, or ends the session.'
245
+ };
246
+ }
247
+
248
+ function awaitRequest(port, key, timeoutMs) {
249
+ if (!/^[a-f0-9]{12}$/.test(key)) throw new Error('invalid plan-canvas session key');
250
+ const params = new URLSearchParams({ key });
251
+ if (timeoutMs !== null) params.set('timeoutMs', String(timeoutMs));
252
+ return new Promise((resolve, reject) => {
253
+ const req = http.request(
254
+ requestOptions(port, 'GET', `/api/await?${params}`, {}),
255
+ res => {
256
+ let data = '';
257
+ res.on('data', chunk => {
258
+ data += chunk;
259
+ });
260
+ res.on('end', () => {
261
+ try {
262
+ resolve(JSON.parse(data.trim()));
263
+ } catch {
264
+ reject(new Error('await response was not JSON (server restarted?) - re-run await; feedback is never lost'));
265
+ }
266
+ });
267
+ }
268
+ );
269
+ req.setTimeout(0);
270
+ req.on('error', reject);
271
+ req.end();
272
+ });
273
+ }
274
+
275
+ async function cmdAwait(file, args, { stateDir, port }) {
276
+ if (!file) throw new Error('await requires a file path');
277
+ if (!(await healthCheck(port))) {
278
+ return { status: 'no-server', hint: 'no canvas server is running; use `open` first', stateDir };
279
+ }
280
+ const reply = valueAfter(args, '--reply');
281
+ if (reply) {
282
+ const key = sessionKeyFor(canonicalizeArtifactPath(file));
283
+ await request(port, 'POST', `/api/session/${key}/reply`, { text: reply });
284
+ }
285
+ const timeoutRaw = valueAfter(args, '--timeout-ms');
286
+ const timeoutMs = timeoutRaw === null ? null : Number.parseInt(timeoutRaw, 10) || 0;
287
+ process.stderr.write('[plan-canvas] waiting for human feedback... leave this running (re-run if interrupted; queued feedback is never lost)\n');
288
+ const result = await awaitRequest(port, sessionKeyFor(canonicalizeArtifactPath(file)), timeoutMs);
289
+ if (result.status === 'feedback') {
290
+ result.next_step = result.sessionEnded
291
+ ? 'The user sent this feedback and ended the session. Address it and report in chat; do not reopen the canvas uninvited.'
292
+ : 'Address the feedback, then run `aos-plan-canvas await <file> --reply "<what you changed>"` to answer in the canvas and keep listening.';
293
+ } else if (result.status === 'ended') {
294
+ result.next_step =
295
+ result.endedBy === 'user'
296
+ ? 'The user ended this review. Stop polling and deliver any remaining updates in chat; do not reopen uninvited.'
297
+ : 'Session ended. Stop polling.';
298
+ }
299
+ return result;
300
+ }
301
+
302
+ // Show the human an activity indicator in the canvas chat. Cheap and
303
+ // fire-and-forget: a failed signal must never derail the actual work.
304
+ async function cmdTyping(file, args, { port }) {
305
+ if (!file) throw new Error('typing requires a file path');
306
+ const state = valueAfter(args, '--state') || 'typing';
307
+ if (!(await healthCheck(port))) return { status: 'no-server' };
308
+ const key = sessionKeyFor(canonicalizeArtifactPath(file));
309
+ const res = await request(port, 'POST', `/api/session/${key}/typing`, { state });
310
+ if (res.statusCode !== 200) throw new Error(res.body.error || `typing failed (HTTP ${res.statusCode})`);
311
+ return { status: 'ok', state, presence: res.body.presence };
312
+ }
313
+
314
+ // Report feedback the human sent that no agent has picked up yet. Reads state
315
+ // directly so it answers even when the server has idled out.
316
+ function cmdPending({ stateDir }) {
317
+ const store = createSessionStore({ stateDir });
318
+ const waiting = store
319
+ .list()
320
+ .filter(session => session.status !== 'ended' && session.pending > 0)
321
+ .map(session => ({ file: session.file, pending: session.pending, updatedAt: session.updatedAt }));
322
+ return {
323
+ status: waiting.length ? 'pending' : 'clear',
324
+ sessions: waiting,
325
+ next_step: waiting.length
326
+ ? 'Run `aos-plan-canvas await <file>` for each file above to receive the messages.'
327
+ : 'No canvas feedback is waiting.'
328
+ };
329
+ }
330
+
331
+ async function cmdEnd(file, { port }) {
332
+ if (!file) throw new Error('end requires a file path');
333
+ if (!(await healthCheck(port))) return { status: 'no-server' };
334
+ const res = await request(port, 'POST', '/api/end', { file: path.resolve(file) });
335
+ return res.body;
336
+ }
337
+
338
+ async function cmdStop({ stateDir, port }) {
339
+ if (!(await healthCheck(port))) return { status: 'not running' };
340
+ await request(port, 'POST', '/shutdown').catch(() => {});
341
+ fs.rmSync(serverInfoPath(stateDir), { force: true });
342
+ return { status: 'stopping' };
343
+ }
344
+
345
+ async function cmdServer(args, { stateDir, port }) {
346
+ const portArg = valueAfter(args, '--port');
347
+ const hostArg = valueAfter(args, '--host');
348
+ const listenPort = portArg !== null ? Number.parseInt(portArg, 10) : port;
349
+ const store = createSessionStore({ stateDir });
350
+ let shuttingDown = false;
351
+ const shutdown = async code => {
352
+ if (shuttingDown) return;
353
+ shuttingDown = true;
354
+ fs.rmSync(serverInfoPath(stateDir), { force: true });
355
+ await canvas.close().catch(() => {});
356
+ process.exit(code);
357
+ };
358
+ const canvas = createPlanCanvasServer({
359
+ store,
360
+ host: hostArg || DEFAULT_HOST,
361
+ version: VERSION,
362
+ idleTimeoutMs: resolveIdleTimeoutMs(),
363
+ onIdleShutdown: () => shutdown(0),
364
+ log: line => process.stderr.write(`${line}\n`)
365
+ });
366
+ const bound = await canvas.listen(listenPort);
367
+ fs.mkdirSync(stateDir, { recursive: true });
368
+ fs.writeFileSync(
369
+ serverInfoPath(stateDir),
370
+ JSON.stringify({ pid: process.pid, port: bound.port, version: VERSION, startedAt: new Date().toISOString() }, null, 2)
371
+ );
372
+ // Sessions restored from disk resume their file watchers.
373
+ for (const session of store.list()) {
374
+ if (session.status !== 'ended') canvas.watchSession(store.get(session.key));
375
+ }
376
+ process.on('SIGINT', () => shutdown(0));
377
+ process.on('SIGTERM', () => shutdown(0));
378
+ process.stderr.write(`[plan-canvas] serving on http://${bound.host}:${bound.port}\n`);
379
+ return new Promise(() => {}); // run until a signal or idle shutdown
380
+ }
381
+
382
+ async function main(argv = process.argv.slice(2)) {
383
+ const args = argv.slice();
384
+ if (args.includes('--help') || args.includes('-h')) {
385
+ process.stdout.write(`${usage()}\n`);
386
+ return 0;
387
+ }
388
+ const command = args[0] && !args[0].startsWith('--') ? args.shift() : null;
389
+ const stateDir = resolveStateDir();
390
+ // A running server may sit on a non-default port; trust its recorded info.
391
+ const recorded = readServerInfo(stateDir);
392
+ const context = { stateDir, port: (recorded && recorded.port) || resolvePort() };
393
+ try {
394
+ if (command === null) output(await cmdStatus(context));
395
+ else if (command === 'open') output(await cmdOpen(args[0], args, context));
396
+ else if (command === 'await') output(await cmdAwait(args[0], args, context));
397
+ else if (command === 'pending') output(cmdPending(context));
398
+ else if (command === 'typing') output(await cmdTyping(args[0], args, context));
399
+ else if (command === 'end') output(await cmdEnd(args[0], context));
400
+ else if (command === 'stop') output(await cmdStop(context));
401
+ else if (command === 'server') await cmdServer(args, context);
402
+ else {
403
+ process.stderr.write(`Unknown command: ${command}\n\n${usage()}\n`);
404
+ return 1;
405
+ }
406
+ return 0;
407
+ } catch (error) {
408
+ output({ error: error.message });
409
+ return 1;
410
+ }
411
+ }
412
+
413
+ if (require.main === module) {
414
+ main().then(code => {
415
+ process.exitCode = code;
416
+ });
417
+ }
418
+
419
+ module.exports = { main, ensureServer, healthCheck };
@@ -1,169 +0,0 @@
1
- # Audit-Handover — Beta Aftercare v3.13.0-nodex.4 (2026-08-28)
2
-
3
- Nachfolge-Dokument zu `SESSION-HANDOVER-v3.13.md`. Erstellt durch den
4
- Aftercare-Audit-Run (Verifikation + Cleanup-Audit, **keine Änderungen**):
5
- nichts gemergt, geschlossen, gelöscht, gepusht oder published. Alles unten
6
- ist geprüft und wartet auf Entscheidung/GO.
7
-
8
- Repo-Zustand bei Audit: `main` @ `ba49a22` (3.13.0-nodex.4), Working Tree
9
- clean. Diese Datei ist neu und **untracked** (Commit braucht GO).
10
-
11
- ---
12
-
13
- ## 1. Verifiziert korrekt
14
-
15
- Alle SHAs aus dem Brief existieren exakt in `git log main`:
16
-
17
- | Fix | Urteil |
18
- |---|---|
19
- | `5c8b634` setup-saas.mjs: Cert-Cleanup + 0600 | korrekt; regex-Replace des Marker-Blocks entfernt sogar alt-gesteckte stale Lines (self-heiling). Restrisiken siehe F9/F10 |
20
- | `5b2444a` agent-pipeline SKILL.md Rewrite | korrekt (die Datei selbst); aber fiktive Pipeline lebt woanders weiter → F3 |
21
- | `bb4053d` isNewerVersion im Status-Check | korrekt für den gemeldeten Bug; **unvollständig bei Prereleases** → F1 (wichtigster Befund) |
22
- | `6425b97` --platforms Flag | korrekt; Kleinigkeiten: Leerzeichen-Form (`--platforms 2`) nicht unterstützt, Duplikate nicht deduped, `0,2` verschweigt das `2` |
23
- | `f283b7c` MCP-Merge Claude Code/Desktop | korrekt: Read-Modify-Write, fremde Top-Level-Keys (`projects` u. a.) und User-Server bleiben in BEIDEN Dateien erhalten; Namenskollision → BDB gewinnt. Aber: F2 (chmod-Lücke), F8 (env carry-over nur im Mirror) |
24
- | `e3bea52` excludeList-Rekursion | korrekt; Bug-Klasse existiert genau einmal im Codebase |
25
- | startcycle-Naming-Fix `2138bd9` | korrekt; ein stale Pointer bleibt → F4 |
26
- | `.agents/graph.md` Rewrite | korrekt: alle 7 Nodes existieren real (agents.md + .claude/agents + .opencode/agents), alle referenzierten Dateien existieren |
27
- | go-gate.mjs Transcript-Fix (`51ae49c`) | korrekt: `type`/`message.content`, String- wie Block-Content, Sidechain-Skip, fail-closed. Nuance: `isMeta`-User-Entries werden nicht excluded (konservativ, ok) |
28
- | Hook-Drift | `~/.claude/hooks/go-gate.mjs` UND `graph-gate.mjs` sind byte-identisch mit den Repo-Kopien (diff geprüft, 2026-08-28) |
29
- | npm dist-tags | `latest=3.12.0`, `beta=3.13.0-nodex.4` — wie beabsichtigt |
30
-
31
- ## 2. Befunde (priorisiert)
32
-
33
- **P1 — solltest du fixen vor dem nächsten Release:**
34
- - **F1 `isNewerVersion` versteht keine Prereleases** (installer.js:238-248,
35
- Call-Site :1487, `npm view` ohne Tag :1476). Numerischer Dot-Compare:
36
- `3.13.0-nodex.4` gilt als NEUER als `3.13.0` (semver: älter). Sobald 3.13.0
37
- auf `latest` landet, bekommen nodex.4-User „Up to date" angezeigt, obwohl
38
- sie älter sind — dieselbe Fehlerklasse wie der gefixte Bug, nur invertiert.
39
- Außerdem sieht die Call-Site nur den `latest`-Dist-Tag: Beta-Bumps unter
40
- `beta` sind unsichtbar.
41
- - **F2 chmod-Lücke beim primären MCP-Merge** (installer.js:1884): der Write
42
- für `claude_desktop_config.json` setzt keinen Mode — eine bestehende-0644
43
- Datei behält 0644, während API-Keys injiziert werden. Der Mirror-Pfad nach
44
- `~/.claude.json` macht 0600+chmod (:1617) — inkonsistent zur eigenen Regel
45
- aus `5c8b634`. Zusätzlich: write→chmod-Fenster auch bei
46
- `writeMcpConfigSecure` (:223-226) und `catch {}` verschluckt
47
- chmod-Fehler still.
48
-
49
- **P2 — Konsistenz/Regressionsvektoren:**
50
- - **F3 Fiktive Pipeline lebt weiter:** `skills/basic/godmode-shipping/SKILL.md:14`
51
- empfiehlt `/spec` als echten Befehl; `GEMINI.md:13` und
52
- `.codex-plugin/system.md:13` listen `/build`, `/test`; „run `/ship`" steht
53
- in 5 aktiven Dateien (`.agents/workflows/startcycle.md:81`,
54
- `.codex-plugin/system.md:119`, `.claude/workflows/startcycle-dispatch.mjs:411`,
55
- `.agents/state.schema.json:20`, `.agents/graph.md:92+115`), obwohl es
56
- `/ship` nirgends als Command gibt (GO-Check passiert stattdessen im
57
- go-gate.mjs am Push-Punkt — funktional ok, Benennung inkonsistent).
58
- - **F4 Stale Path:** `.agents/state.schema.json:76` verweist noch auf
59
- `.claude/workflows/startcycle.mjs` (heißt jetzt `startcycle-dispatch.mjs`).
60
- - **F5 `github-repo` doppelt mit divergiertem Inhalt:**
61
- `skills/github-repo/SKILL.md` (neu, BDB-Standard) vs.
62
- `skills/global_config/github-repo/SKILL.md` (alt, generisch).
63
- Install-Reihenfolge (alphabetisch) überschreibt bei **jedem** Install die
64
- neuere Root-Version still mit der älteren.
65
-
66
- **P3 — klein:**
67
- - **F6** `skills/global_config/github-actions-templates/SKILL.md:83,340`
68
- referenziert `assets/test-workflow.yml` — existiert nicht.
69
- - **F7** `/grill-me` wird benutzt von bdbrainstorm, startcycle,
70
- bdbmediastorm — ist aber in keinem Repo shipped (undeclared dependency).
71
- - **F8** Mirror-Path (`~/.claude.json`) hat kein env carry-over bei
72
- Namenskollision (primärer Pfad hat es) — User-Env wird ersetzt.
73
- - **F9** setup-saas: Cert-Existenz-Check ≠ Frische-Check
74
- (bin/setup-saas.mjs:139-148): vorhandenes, aber abgelaufenes Cert bleibt
75
- verdrahtet, wenn heutiges Bootstrap failt.
76
- - **F10** Lock-freies Read-Modify-Write auf `~/.claude.json` kann
77
- konkurrierende Claude-Code-Updates verlieren (inherent, dokumentieren statt
78
- fixen).
79
-
80
- ## 3. Cleanup-Liste — wartet auf GO / Entscheidung
81
-
82
- | # | Aktion | Grund (1 Zeile) |
83
- |---|---|---|
84
- | C1 | PR #36 schließen (`gh pr close 36`) | Inhalt seit `54caa5c` in main; einziger Delta = überholter Version-Bump `8eead12` (3.9.5→3.12.0) |
85
- | C2 | Remote-Branch `feat/agent-skills-v3.13` löschen | vollständig in main gemergt (`merge-base --is-ancestor` bestanden, tip `adc7f47`) |
86
- | C3 | Lokale Branches `fix/build-profile-hardening` + `fix/v3.13-installer-blockers` löschen | beide 0 unique commits, vollständig gemergt |
87
- | C4 | `installer_old.js` entfernen (Commit) | 0 Referenzen im ganzen Repo (package.json, installer.js, scripts/, grep) |
88
- | C5 | `SESSION-HANDOVER-v3.13.md` archivieren/löschen (Commit) | von diesem Handover abgelöst; **`audit-agents.md` BEHALTEN** — wird von `.agents/graph.md:5` als Spec-Referenz zitiert, ist also produktiv |
89
- | C6 | Tag `v3.13.0-nodex.0`: pushen ODER lokal löschen | npm hat nodex.0, GitHub hat den Tag nicht — Inkonsistenz; entscheide eine Richtung |
90
- | C7 | 4 `snapshot-*`-Tags: behalten/pushen/löschen | lokale Historie-Anker; nur lokal vorhanden, keine Releases — bewusst entscheiden |
91
- | C8 | (Beobachtung) `BDB_REMOTEOS_MCP_HANDOVER.md`, `Project-overview.html` im Root | weitere Root-Kandidaten, nicht im Brief — nur angeschaut, nicht bewertet |
92
-
93
- **Explizit NICHT tun:** `mcp_config.json` anfassen (live MCP-Template-Quelle);
94
- `pr-15`/`copilot/worktree-*` suchen — existieren auf dieser Maschine nicht
95
- mehr (Brief veraltet; `.worktrees/`-Verzeichnis enthält keine Git-Repos).
96
-
97
- **Nicht-Cleanup, aber offen:** PR #7 + #8 auf `bdb-saashost-engine`
98
- (LDAP-Injection, Admin-Group-Authz) sind weiterhin OPEN — brauchen
99
- Review/Merge-Entscheidung, sitzen seit 2026-08-27.
100
-
101
- ## 4. Tag-/Release-Anomalien (Inventur, 2026-08-28)
102
-
103
- - 45 lokale Tags; 5 lokal-only: 4× `snapshot-*`, `v3.13.0-nodex.0` (→ C6/C7).
104
- - Kein GitHub-Release für: `v2.4.1`, `v3.9.5` (Tags + npm existieren).
105
- - `3.0.6`: Tag + Release existieren, aber **nie auf npm publiziert** (E404).
106
- - `3.3.1`: auf npm, aber kein Tag, kein Release.
107
- - Betas (3.13.0-beta.nodex, nodex.0–.4): auf npm, keine Git-Tags (außer
108
- lokalem nodex.0), keine Releases — konsistent zur Beta-Strategie.
109
-
110
- ## 5. Design-Abgleich (manuell, teilweise — Subagenten gequotet)
111
-
112
- Der unabhängige Screenshot-Vergleich wurde 2× abgebrochen (concurrency limit,
113
- dann quota limit) und **nicht** durchgeführt. Manuell abgeglichen:
114
- `~/Downloads/GRAOG/bdb_graph_layer_guide.html` (das distillierte Design-Doku,
115
- Aug 27, „BDB OS v3.13") gegen `.agents/graph.md` + Implementierung:
116
-
117
- | Design (Guide) | Status | Anmerkung |
118
- |---|---|---|
119
- | 7-Node State Machine (Architect→TechLead→paralleler Build→Reviewer→Shipping) | MATCH | 1:1 in graph.md + Simulator-Diagramm |
120
- | „Nodes never call each other", Dispatcher-only Edges | MATCH | Kernregel von graph.md:8-17 |
121
- | Repair-Loops (TechLead-Reject→Architect, Reviewer→owning node) | MATCH | Edge-Table identisch |
122
- | No-Progress Guard (gleiche Blocker-ID 2× → needs_human) | MATCH | Implementierung = „same finding IDs"-Eskalation (graph.md:64-75, dokumentierte Adaptation von B3) |
123
- | Phase 4: „Reply GO → `/ship`" | PARTIAL | `/ship` existiert als Command nicht; GO-Check läuft im go-gate.mjs am Push — F3 |
124
- | Phase 1: `/bdbrainstorm` & `/grill-me` | PARTIAL | `/grill-me` fehlt (F7) |
125
- | memB-Dual-Memory (Seed vor Planung, Regression-Match, Ingest nach Ship) | UNKLAR | memb-ingest Skill existiert; ob Architect/Reviewer memB wirklich abfragen, steht nicht im Graph-Contract (ggf. in .agents/agents.md prüfen) |
126
- | Multi-Harness (Claude dispatcher, Antigravity, OpenCode/Codex, Cursor/Roo-Fallback) | MATCH | Artefakte existieren (.opencode/agents, .roomodes); Guide nennt noch alten Namen `startcycle.mjs` (Guide:889, Design-Doku, nicht Repo) |
127
-
128
- **Offen:** die 10 Screenshots (IMG_6186–IMG_6300) wurden einzeln nicht
129
- ausgewertet. Der Guide referenziert IMG_6186 („4 Primitives") und IMG_6194
130
- („Multi-Agent Topology") als die beiden Blueprints — vermutlich deckt der
131
- Guide deren Inhalt already ab; die übrigen 8 sind wahrscheinlich frühere
132
- Iterationen. Ein Nachfolge-Agent kann das mit Read auf die Bilddateien
133
- verifizieren.
134
-
135
- ## 6. Best-Practices-Check (code.claude.com/docs/en/best-practices, manuell)
136
-
137
- - **Verification loops: FOLLOWS** — Stop-Hook-Gates (graph-gate, go-gate),
138
- Dispatcher gegen 9+ Szenarien verifiziert (graph.md:117-124),
139
- `max_iterations=3` bewusst unter Claude's 8-Block-Override
140
- (graph.md:137-141 zitiert die Best-Practices-Doku explizit).
141
- - **CLAUDE.md-Hygiene: FOLLOWS** — 1.6KB, schlank. **Aber:** 5+ parallele
142
- Instruction-Files (CLAUDE/AGENTS/GEMINI/CODEX/.roomodes/.codex-plugin) —
143
- der Drift ist schon sichtbar (F3: GEMINI.md + .codex-plugin stale).
144
- Empfehlung: single-source + Generierung statt manuell synchronisierter
145
- Kopien.
146
- - **Skills vs CLAUDE.md Split: FOLLOWS.** **Reviewer-Isolation (= adversarial
147
- fresh-context review): FOLLOWS** modellhaft (Reviewer sieht nie die Claims
148
- der Build-Nodes).
149
- - 161 Skills: kein Verstoß, aber Redundanz-Cluster existieren (seo×3,
150
- prompt×3, tdd×2, debugger×2, postgres×4) — Aufräum-Kandidaten, low
151
- priority.
152
-
153
- ## 7. Sonstiges
154
-
155
- - token-saver v2.6.3 aktiv, Lifetime 44.9% Ersparnis (48.3K tokens/162
156
- cmds). Auffällig: git-Processor „Mismatches" 28× (~17%) — Config-Stellschraube,
157
- unangetastet.
158
- - `token-saver update` wurde bewusst NICHT ausgeführt (Zustandsänderung).
159
- - GO-Gate gilt weiter: push/publish/npm version/rekursives rm nur nach
160
- wörtlichem „GO" direkt davor.
161
-
162
- ## 8. Empfohlene Reihenfolge für den Folgende-Agent
163
-
164
- 1. F1 + F2 fixen (vor dem 3.13.0-latest-Release!) → Beta-Gate.
165
- 2. F3/F4 in einem „stale-references"-Commit.
166
- 3. F5 entscheiden: welche github-repo-Version ist kanonisch, andere löschen
167
- + Install-Reihenfolge fixen.
168
- 4. Cleanup C1–C7 einzeln zur Freigabe vorlegen.
169
- 5. PR #7/#8 (bdb-saashost-engine) reviewen.