great-cto 3.70.1 → 3.72.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 +9 -1
- package/board/.claude-plugin/plugin.json +93 -1
- package/board/agents/architect.md +1 -1
- package/board/agents/db-migration-reviewer.md +3 -2
- package/board/agents/design-advisor.md +1 -1
- package/board/agents/devops.md +1 -1
- package/board/agents/growth-engineer.md +1 -1
- package/board/agents/knowledge-extractor.md +1 -1
- package/board/agents/l3-support.md +1 -1
- package/board/agents/performance-engineer.md +1 -1
- package/board/agents/pm.md +5 -1
- package/board/agents/qa-engineer.md +4 -4
- package/board/agents/security-officer.md +2 -2
- package/board/agents/smart-contract-auditor.md +1 -1
- package/board/packages/board/lib/routes.mjs +14 -1
- package/board/packages/board/lib/work.mjs +34 -3
- package/board/packages/board/public/assets/work-view.js +146 -18
- package/board/scripts/codex-pipeline.mjs +2 -0
- package/board/scripts/lib/approval-level.mjs +4 -2
- package/board/scripts/lib/claude-exec.mjs +49 -6
- package/board/scripts/lib/codex-exec.mjs +42 -18
- package/board/scripts/lib/codex-host-state.mjs +18 -2
- package/board/scripts/lib/codex-pipeline.mjs +44 -7
- package/board/scripts/lib/host-graph-bindings.mjs +135 -0
- package/board/scripts/lib/host-graph-events.mjs +157 -0
- package/board/scripts/lib/host-graph-journal.mjs +210 -0
- package/board/scripts/lib/host-graph-observer.mjs +61 -0
- package/board/scripts/lib/host-graph-runtime.mjs +38 -0
- package/board/scripts/work-task.mjs +17 -4
- package/board/shared/pipeline.toml +7 -1
- package/dist/adapt.js +10 -21
- package/dist/codex.js +4 -15
- package/dist/companion.js +6 -122
- package/dist/installer.js +4 -72
- package/dist/main.js +44 -117
- package/dist/release-activation.mjs +561 -0
- package/dist/release-archive.mjs +165 -0
- package/dist/release-trust.mjs +514 -0
- package/dist/release-update.mjs +303 -0
- package/dist/self-upgrade.js +10 -39
- package/dist/update-check.js +1 -0
- package/dist/upgrade.js +5 -120
- package/package.json +1 -1
- package/board/packages/board/fixtures/beads-read-owner.mjs +0 -3
- package/board/packages/board/fixtures/fake-bd-controlled.mjs +0 -55
- package/board/packages/board/fixtures/fake-bd-fail.sh +0 -9
- package/board/packages/board/fixtures/fake-bd-slow.sh +0 -12
- package/board/packages/board/fixtures/held-receipt-git.mjs +0 -21
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# great-cto
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> CLI for the [great_cto](https://github.com/avelikiy/great_cto) engineering workspace, controlled runs and verified release updates.
|
|
4
|
+
|
|
5
|
+
**Verified distribution boundary:** ADR-045 replaces mutable installation and update
|
|
6
|
+
paths with signed artifact admission. This implementation refuses `init`, native
|
|
7
|
+
plugin refresh and automatic companion installation before mutation. The plain
|
|
8
|
+
npm self-update adapter requires an independently configured verifier and exact
|
|
9
|
+
signed release assets. See [verified updates](../../docs/reference/verified-updates.md).
|
|
10
|
+
The installation walkthrough below describes the legacy 3.70.1 flow; it is not
|
|
11
|
+
an activation route for this implementation.
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
14
|
npx great-cto init
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 72 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.72.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -88,6 +88,16 @@
|
|
|
88
88
|
"timeout": 10
|
|
89
89
|
}
|
|
90
90
|
]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"matcher": "",
|
|
94
|
+
"hooks": [
|
|
95
|
+
{
|
|
96
|
+
"type": "command",
|
|
97
|
+
"timeout": 5,
|
|
98
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
99
|
+
}
|
|
100
|
+
]
|
|
91
101
|
}
|
|
92
102
|
],
|
|
93
103
|
"PreCompact": [
|
|
@@ -120,6 +130,16 @@
|
|
|
120
130
|
"statusMessage": "Loading orchestrator contract..."
|
|
121
131
|
}
|
|
122
132
|
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"matcher": "",
|
|
136
|
+
"hooks": [
|
|
137
|
+
{
|
|
138
|
+
"type": "command",
|
|
139
|
+
"timeout": 5,
|
|
140
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
141
|
+
}
|
|
142
|
+
]
|
|
123
143
|
}
|
|
124
144
|
],
|
|
125
145
|
"SubagentStop": [
|
|
@@ -138,6 +158,16 @@
|
|
|
138
158
|
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/turn-snapshot.mjs\" 2>/dev/null || true"
|
|
139
159
|
}
|
|
140
160
|
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"matcher": "",
|
|
164
|
+
"hooks": [
|
|
165
|
+
{
|
|
166
|
+
"type": "command",
|
|
167
|
+
"timeout": 5,
|
|
168
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
169
|
+
}
|
|
170
|
+
]
|
|
141
171
|
}
|
|
142
172
|
],
|
|
143
173
|
"PreToolUse": [
|
|
@@ -208,6 +238,16 @@
|
|
|
208
238
|
"statusMessage": "Checking recorded lessons..."
|
|
209
239
|
}
|
|
210
240
|
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"matcher": "",
|
|
244
|
+
"hooks": [
|
|
245
|
+
{
|
|
246
|
+
"type": "command",
|
|
247
|
+
"timeout": 5,
|
|
248
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
249
|
+
}
|
|
250
|
+
]
|
|
211
251
|
}
|
|
212
252
|
],
|
|
213
253
|
"PostToolUse": [
|
|
@@ -293,6 +333,16 @@
|
|
|
293
333
|
"statusMessage": "Computing next pipeline stage..."
|
|
294
334
|
}
|
|
295
335
|
]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"matcher": "",
|
|
339
|
+
"hooks": [
|
|
340
|
+
{
|
|
341
|
+
"type": "command",
|
|
342
|
+
"timeout": 5,
|
|
343
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
344
|
+
}
|
|
345
|
+
]
|
|
296
346
|
}
|
|
297
347
|
],
|
|
298
348
|
"SessionEnd": [
|
|
@@ -311,6 +361,16 @@
|
|
|
311
361
|
"timeout": 5
|
|
312
362
|
}
|
|
313
363
|
]
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"matcher": "",
|
|
367
|
+
"hooks": [
|
|
368
|
+
{
|
|
369
|
+
"type": "command",
|
|
370
|
+
"timeout": 5,
|
|
371
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
372
|
+
}
|
|
373
|
+
]
|
|
314
374
|
}
|
|
315
375
|
],
|
|
316
376
|
"UserPromptSubmit": [
|
|
@@ -338,6 +398,16 @@
|
|
|
338
398
|
"timeout": 5
|
|
339
399
|
}
|
|
340
400
|
]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"matcher": "",
|
|
404
|
+
"hooks": [
|
|
405
|
+
{
|
|
406
|
+
"type": "command",
|
|
407
|
+
"timeout": 5,
|
|
408
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
409
|
+
}
|
|
410
|
+
]
|
|
341
411
|
}
|
|
342
412
|
],
|
|
343
413
|
"PermissionDenied": [
|
|
@@ -384,6 +454,16 @@
|
|
|
384
454
|
"timeout": 5
|
|
385
455
|
}
|
|
386
456
|
]
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"matcher": "",
|
|
460
|
+
"hooks": [
|
|
461
|
+
{
|
|
462
|
+
"type": "command",
|
|
463
|
+
"timeout": 5,
|
|
464
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
465
|
+
}
|
|
466
|
+
]
|
|
387
467
|
}
|
|
388
468
|
],
|
|
389
469
|
"Notification": [
|
|
@@ -397,6 +477,18 @@
|
|
|
397
477
|
}
|
|
398
478
|
]
|
|
399
479
|
}
|
|
480
|
+
],
|
|
481
|
+
"PostToolUseFailure": [
|
|
482
|
+
{
|
|
483
|
+
"matcher": "",
|
|
484
|
+
"hooks": [
|
|
485
|
+
{
|
|
486
|
+
"type": "command",
|
|
487
|
+
"timeout": 5,
|
|
488
|
+
"command": "_gd=\"$PWD\"; while [ -n \"$_gd\" ] && { [ \"$_gd\" = \"$HOME\" ] || [ ! -f \"$_gd/.great_cto/PROJECT.md\" ]; }; do _gd=\"${_gd%/*}\"; done; [ -n \"$_gd\" ] && cd \"$_gd\"; [ -d \"${GREAT_CTO_GRAPH_BINDINGS_DIR:-$HOME/.great_cto/graph-bindings}\" ] || exit 0; _pd=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -n \"$_pd\" ] || exit 0; GREAT_CTO_HOST=claude-code node \"$_pd/scripts/hooks/host-graph-observer.mjs\""
|
|
489
|
+
}
|
|
490
|
+
]
|
|
491
|
+
}
|
|
400
492
|
]
|
|
401
493
|
},
|
|
402
494
|
"homepage": "https://greatcto.systems",
|
|
@@ -376,7 +376,7 @@ constraint in the new ARCH doc — document why this design choice was not taken
|
|
|
376
376
|
ARCHETYPE=$(grep "^archetype:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); ARCHETYPE=${ARCHETYPE:-web-service}
|
|
377
377
|
PROJECT_SIZE=$(grep "^project_size:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); PROJECT_SIZE=${PROJECT_SIZE:-medium}
|
|
378
378
|
SECURITY_GATE=$(grep "^security-gate:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); SECURITY_GATE=${SECURITY_GATE:-conditional}
|
|
379
|
-
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'
|
|
379
|
+
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'); COMPLIANCE=${COMPLIANCE:-[]}
|
|
380
380
|
```
|
|
381
381
|
Read ARCHETYPES.md for QA strategy, deploy method, and thresholds matching `$ARCHETYPE`. Use these as constraints for the architecture design.
|
|
382
382
|
- **Greenfield check** — determines how deep to read existing code:
|
|
@@ -48,8 +48,9 @@ fi
|
|
|
48
48
|
echo "Migrations to review:"
|
|
49
49
|
echo "$MIGRATIONS"
|
|
50
50
|
|
|
51
|
-
DB_ENGINE=$(grep "^db:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'
|
|
52
|
-
|
|
51
|
+
DB_ENGINE=$(grep "^db:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}')
|
|
52
|
+
[ -n "$DB_ENGINE" ] || DB_ENGINE=$(grep -oE "postgresql|mysql|sqlite|aurora|cockroach|planetscale" .great_cto/PROJECT.md 2>/dev/null | head -1)
|
|
53
|
+
DB_ENGINE=${DB_ENGINE:-unknown}
|
|
53
54
|
|
|
54
55
|
# Slug from latest ARCH doc; date fallback must be an explicit branch —
|
|
55
56
|
# `|| echo` after a pipeline never fires (basename "" exits 0 with empty output)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design-advisor
|
|
3
|
-
description: Use after architect
|
|
3
|
+
description: Use after architect and before pm, for any UI-bearing feature (landing pages, dashboards, admin panels, web apps, React Native apps). Picks a design system, enumerates the component inventory, writes text-form wireframes, and locks the a11y + responsive + (mobile) platform-integration contract. Outputs docs/design/DESIGN-{slug}.md. Plan altitude only — never writes implementation code.
|
|
4
4
|
model: claude-opus-5
|
|
5
5
|
authority: autonomous
|
|
6
6
|
tools: Read, Write, Glob, Grep, WebFetch, Bash(git:*), Bash(bd:*), Bash(ls:*), Bash(cat:*), Bash(find:*), Bash(touch:*), Bash(head:*), Bash(tail:*), Bash(grep:*), Bash(wc:*), Bash(date:*), Bash(printf:*), Bash(echo:*), Bash(mkdir:*), memory_20250929, advisor_20260301
|
package/board/agents/devops.md
CHANGED
|
@@ -572,7 +572,7 @@ step for that condition before proceeding to Step 1 (gate:ship check).
|
|
|
572
572
|
ESTIMATED=${ESTIMATED_RAW:-0}
|
|
573
573
|
CONSOLE_URL=$(grep "console-url:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}')
|
|
574
574
|
SERVICE=$(grep "^service:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}' || basename "$PWD")
|
|
575
|
-
FEATURE=$(basename "$ARCH_DOC" .md 2>/dev/null | sed 's/^ARCH-//'
|
|
575
|
+
FEATURE=$(basename "$ARCH_DOC" .md 2>/dev/null | sed 's/^ARCH-//'); FEATURE=${FEATURE:-unknown}
|
|
576
576
|
|
|
577
577
|
COST_LOG=.great_cto/cost-history.log
|
|
578
578
|
[ ! -f "$COST_LOG" ] && printf '# Cost history — append only. Format: ISO8601 | service | estimated_usd_month | actual_usd_month | source | feature\n' > "$COST_LOG"
|
|
@@ -91,7 +91,7 @@ ARCH_FILE=$(ls -t docs/architecture/ARCH-*.md 2>/dev/null | head -1)
|
|
|
91
91
|
SLUG=$(basename "$ARCH_FILE" .md | sed 's/^ARCH-//')
|
|
92
92
|
|
|
93
93
|
ARCHETYPE=$(grep "^archetype:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}')
|
|
94
|
-
GROWTH_GOAL=$(grep "^growth-goal:" .great_cto/PROJECT.md 2>/dev/null | sed 's/growth-goal: //'
|
|
94
|
+
GROWTH_GOAL=$(grep "^growth-goal:" .great_cto/PROJECT.md 2>/dev/null | sed 's/growth-goal: //'); GROWTH_GOAL=${GROWTH_GOAL:-not specified}
|
|
95
95
|
# Is there an analytics/connector layer we can read from?
|
|
96
96
|
CONNECT=$(ls docs/connectors/CONNECT-*.md 2>/dev/null | head -1)
|
|
97
97
|
echo "slug=$SLUG archetype=$ARCHETYPE goal='$GROWTH_GOAL' connector=${CONNECT:-none}"
|
|
@@ -28,7 +28,7 @@ Run in parallel:
|
|
|
28
28
|
cat .great_cto/lessons.md 2>/dev/null || echo "(no lessons yet)"
|
|
29
29
|
|
|
30
30
|
# Cross-project decisions (supplement)
|
|
31
|
-
|
|
31
|
+
D=$(head -300 ~/.great_cto/decisions.md 2>/dev/null); [ -n "$D" ] && printf '%s\n' "$D" || echo "(none)"
|
|
32
32
|
|
|
33
33
|
# Session log pattern lines across all sessions
|
|
34
34
|
grep -h "^## pattern:" .great_cto/logs/session-*-end.md 2>/dev/null \
|
|
@@ -327,7 +327,7 @@ node "$PLUGIN_DIR/scripts/lib/pattern-lookup.mjs" --role incident
|
|
|
327
327
|
|
|
328
328
|
# Priority 4: systemd / journalctl
|
|
329
329
|
if [ -z "$LOG_PATH" ] && [ -z "$APP_CONTAINER" ]; then
|
|
330
|
-
journalctl --since "30 min ago" -p err 2>/dev/null | tail -50 || \
|
|
330
|
+
{ J=$(journalctl --since "30 min ago" -p err 2>/dev/null | tail -50); [ -n "$J" ] && printf '%s\n' "$J"; } || \
|
|
331
331
|
echo "No log source found. Add 'error-log: /path/to/log' to PROJECT.md L3 section."
|
|
332
332
|
fi
|
|
333
333
|
|
|
@@ -76,7 +76,7 @@ ARCH_FILE=$(ls -t docs/architecture/ARCH-*.md 2>/dev/null | head -1)
|
|
|
76
76
|
[ -z "$ARCH_FILE" ] && { echo "BLOCKED: no ARCH doc" >&2; exit 1; }
|
|
77
77
|
SLUG=$(basename "$ARCH_FILE" .md | sed 's/^ARCH-//')
|
|
78
78
|
|
|
79
|
-
PERF_SLA=$(grep "^performance-sla:" .great_cto/PROJECT.md 2>/dev/null | sed 's/performance-sla: //'
|
|
79
|
+
PERF_SLA=$(grep "^performance-sla:" .great_cto/PROJECT.md 2>/dev/null | sed 's/performance-sla: //'); PERF_SLA=${PERF_SLA:-not specified}
|
|
80
80
|
MONTHLY_RPS=$(grep "^expected-rps:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); MONTHLY_RPS=${MONTHLY_RPS:-unknown}
|
|
81
81
|
|
|
82
82
|
# Check for existing baseline
|
package/board/agents/pm.md
CHANGED
|
@@ -115,6 +115,10 @@ PHASE=$(grep "^phase:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); PH
|
|
|
115
115
|
TEAM_SIZE=$(grep "^team-size:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); TEAM_SIZE=${TEAM_SIZE:-1}
|
|
116
116
|
MONTHLY_BUDGET=$(grep "^monthly-budget-llm-usd:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}')
|
|
117
117
|
|
|
118
|
+
# Design contract — design-advisor runs before pm on UI features; plan UI tasks against it
|
|
119
|
+
DESIGN_DOC=$(ls -t docs/design/DESIGN-*.md 2>/dev/null | head -1)
|
|
120
|
+
[ -n "$DESIGN_DOC" ] && echo "=== DESIGN CONTRACT: $DESIGN_DOC (components, a11y, responsive — one task per screen/component group) ==="
|
|
121
|
+
|
|
118
122
|
# Past lessons — calibrate cost/time estimates against actuals
|
|
119
123
|
if [ -f .great_cto/lessons.md ]; then
|
|
120
124
|
COST_LESSONS=$(grep -B1 -A4 "shape: B" .great_cto/lessons.md 2>/dev/null | head -20)
|
|
@@ -138,7 +142,7 @@ FEATURE_SLUG=$(basename "$ARCH_FILE" .md | sed 's/^ARCH-//' | tr '[:upper:]' '[:
|
|
|
138
142
|
|
|
139
143
|
# Goal ancestry (Paperclip pattern) — injected into every bd create call.
|
|
140
144
|
# Built AFTER FEATURE_SLUG is known (it interpolates the slug).
|
|
141
|
-
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'
|
|
145
|
+
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //')
|
|
142
146
|
_COMP_CLEAN=$(echo "$COMPLIANCE" | sed 's/^\[none\]$//;s/^\[none, *\]//;s/, *\[none\]//')
|
|
143
147
|
GOAL_ANCESTRY="[archetype:${ARCHETYPE}]$([ -n "$_COMP_CLEAN" ] && echo " [compliance:${_COMP_CLEAN}]") [feature:${FEATURE_SLUG}] [phase:${PHASE}] | Why: see docs/plans/PLAN-${FEATURE_SLUG}.md"
|
|
144
148
|
|
|
@@ -286,9 +286,9 @@ PROJECT_SIZE=$(grep "^project_size:" .great_cto/PROJECT.md 2>/dev/null | awk '{p
|
|
|
286
286
|
|
|
287
287
|
```bash
|
|
288
288
|
ARCHETYPE=$(grep "^archetype:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); ARCHETYPE=${ARCHETYPE:-web-service}
|
|
289
|
-
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'
|
|
290
|
-
QA_EXTRAS=$(grep "^qa-extras:" .great_cto/PROJECT.md 2>/dev/null | sed 's/qa-extras: //'
|
|
291
|
-
PERF_SLA=$(grep "^performance-sla:" .great_cto/PROJECT.md 2>/dev/null | sed 's/performance-sla: //'
|
|
289
|
+
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'); COMPLIANCE=${COMPLIANCE:-[]}
|
|
290
|
+
QA_EXTRAS=$(grep "^qa-extras:" .great_cto/PROJECT.md 2>/dev/null | sed 's/qa-extras: //'); QA_EXTRAS=${QA_EXTRAS:-[]}
|
|
291
|
+
PERF_SLA=$(grep "^performance-sla:" .great_cto/PROJECT.md 2>/dev/null | sed 's/performance-sla: //')
|
|
292
292
|
```
|
|
293
293
|
|
|
294
294
|
Read ARCHETYPES.md → find QA strategy row for `$ARCHETYPE`. This gives base QA plan.
|
|
@@ -362,7 +362,7 @@ PII column additions) that functional tests miss by design — the
|
|
|
362
362
|
specialist agent owns this.
|
|
363
363
|
|
|
364
364
|
```bash
|
|
365
|
-
BASE=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null | sed 's|.*/||'
|
|
365
|
+
BASE=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null | sed 's|.*/||'); BASE=${BASE:-main}
|
|
366
366
|
MIGRATION_HIT=$(git diff "$BASE"...HEAD --name-only 2>/dev/null | \
|
|
367
367
|
grep -E '(^|/)(migrations|alembic/versions|db/migrate|prisma/migrations|flyway|knex/migrations)/.*\.(sql|py|ts|js|rb)$' | head -5)
|
|
368
368
|
|
|
@@ -424,7 +424,7 @@ Continues from the original Workflow below — produces `CSO-{slug}-{date}.md`,
|
|
|
424
424
|
1b. **Read** `.great_cto/PROJECT.md` → get `archetype`, `type`, `stack`, and `compliance` params:
|
|
425
425
|
```bash
|
|
426
426
|
ARCHETYPE=$(grep "^archetype:" .great_cto/PROJECT.md 2>/dev/null | awk '{print $2}'); ARCHETYPE=${ARCHETYPE:-web-service}
|
|
427
|
-
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'
|
|
427
|
+
COMPLIANCE=$(grep "^compliance:" .great_cto/PROJECT.md 2>/dev/null | sed 's/compliance: //'); COMPLIANCE=${COMPLIANCE:-[]}
|
|
428
428
|
```
|
|
429
429
|
Read ARCHETYPES.md → find security rules for `$ARCHETYPE`.
|
|
430
430
|
The `compliance:` list in PROJECT.md determines which checklists to run (see Step 5).
|
|
@@ -670,7 +670,7 @@ Observations (signal < 2 or no direct evidence): record in a separate `## Observ
|
|
|
670
670
|
the pipeline dispatcher and the board parse this line):
|
|
671
671
|
```bash
|
|
672
672
|
bash scripts/log-verdict.sh security-officer <APPROVED|BLOCKED> auto \
|
|
673
|
-
findings=P0:<n>,P1:<n>,P2:<n> triaged=<n> valid=<n> invalid=<n> feature=<slug> need=<implementer|decision> finding=<id>
|
|
673
|
+
findings=P0:<n>,P1:<n>,P2:<n> triaged=<n> valid=<n> invalid=<n> feature=<slug> report=docs/security/CSO-<YYYY-MM-DD>.md need=<implementer|decision> finding=<id>
|
|
674
674
|
```
|
|
675
675
|
On BLOCKED, `need` mirrors the report's `need` line: `implementer` for
|
|
676
676
|
"senior-dev fix <finding>", `decision` for "CTO waive risk on <finding>".
|
|
@@ -318,7 +318,7 @@ REPORT=docs/security/AUDIT-{slug}.md
|
|
|
318
318
|
[ -s "$REPORT" ] || { echo "STOP: no audit report at $REPORT — write it first." >&2; exit 1; }
|
|
319
319
|
|
|
320
320
|
bash scripts/log-verdict.sh smart-contract-auditor <PASS|FAIL|BLOCKED> auto \
|
|
321
|
-
feature=<slug> "
|
|
321
|
+
feature=<slug> "report=$REPORT" findings=C:<n>,H:<n>,M:<n> not_checked=<n> need=<implementer|decision> finding=<id>
|
|
322
322
|
```
|
|
323
323
|
|
|
324
324
|
In prepare mode add `package=docs/security/audit-package-{slug}` and
|
|
@@ -45,7 +45,7 @@ import { readVerdicts } from './verdicts.mjs';
|
|
|
45
45
|
import { parseAgentBudgets, upsertAgentBudget, removeAgentBudget } from '../../../scripts/lib/agent-budget.mjs';
|
|
46
46
|
import { resolveSecondOpinion, SECOND_OPINION_PROVIDERS } from '../../../scripts/lib/second-opinion.mjs';
|
|
47
47
|
import { detectCodex } from '../../../scripts/lib/codex-exec.mjs';
|
|
48
|
-
import { getWork } from './work.mjs';
|
|
48
|
+
import { getWork, getHostGraphPage } from './work.mjs';
|
|
49
49
|
let publicationPreviewBusy = false;
|
|
50
50
|
let publicationWriteBusy = false;
|
|
51
51
|
import { publicationTickets, localPublicationOrigin } from './publication-tickets.mjs';
|
|
@@ -237,6 +237,19 @@ async function dispatch(req, res, url, cwd) {
|
|
|
237
237
|
return true;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
if (pathname === '/api/host-graph') {
|
|
241
|
+
if (requestedProject && resolveProjectInfo(requestedProject).resolved === 'fallback') {
|
|
242
|
+
res.writeHead(404, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); res.end(JSON.stringify({ error: 'Unknown project' })); return true;
|
|
243
|
+
}
|
|
244
|
+
if (req.method !== 'GET') { res.writeHead(405, { Allow: 'GET' }); res.end(); return true; }
|
|
245
|
+
try {
|
|
246
|
+
const graph = getHostGraphPage(cwd, { host: url.searchParams.get('host'), sessionId: url.searchParams.get('session'),
|
|
247
|
+
runId: url.searchParams.get('run'), cursor: url.searchParams.get('cursor') });
|
|
248
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); res.end(JSON.stringify(graph));
|
|
249
|
+
} catch { res.writeHead(404, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); res.end(JSON.stringify({ error: 'Bound graph page is unavailable' })); }
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
240
253
|
// New read model refuses project fallback rather than relabeling another project.
|
|
241
254
|
if (pathname === '/api/work') {
|
|
242
255
|
if (requestedProject && resolveProjectInfo(requestedProject).resolved === 'fallback') {
|
|
@@ -10,6 +10,8 @@ import { measureWork, compareWork } from '../../../scripts/lib/work-metrics.mjs'
|
|
|
10
10
|
import { readSessionStatus } from '../../../scripts/lib/session-status.mjs';
|
|
11
11
|
import { agentActivity } from './agent-activity.mjs';
|
|
12
12
|
import { taskInspector } from './task-inspector.mjs';
|
|
13
|
+
import { graphBinding, projectHostObservations } from '../../../scripts/lib/host-graph-observer.mjs';
|
|
14
|
+
import { nativeGraphObservations, readNativeGraphPage } from '../../../scripts/lib/host-graph-bindings.mjs';
|
|
13
15
|
|
|
14
16
|
const text = v => typeof v === 'string' ? v : null;
|
|
15
17
|
const date = v => typeof v === 'string' && Number.isFinite(Date.parse(v)) ? v : null;
|
|
@@ -30,7 +32,7 @@ function executionAttempts(run) {
|
|
|
30
32
|
}));
|
|
31
33
|
const truncated = attempts.length > 200;
|
|
32
34
|
const graph = run?.executionGraph || { state: 'unavailable', complete: false, nodes: [], edges: [], snapshots: [], reason: 'No linked controller-recorded graph is available.' };
|
|
33
|
-
return { state: nodes.length ? 'recorded' : 'unknown', nodes, topology: graph.state === 'recorded' ? 'recorded' : 'unknown', graph, truncated,
|
|
35
|
+
return { state: nodes.length ? 'recorded' : 'unknown', nodes, observations: run?.hostObservations || [], observationHealth: run?.hostObservationHealth || null, omittedObservationAttempts: run?.hostObservationOmittedAttempts || 0, topology: graph.state === 'recorded' ? 'recorded' : 'unknown', graph, truncated,
|
|
34
36
|
partial: !run || truncated || nodes.length !== window.length,
|
|
35
37
|
reason: !run ? 'Stable attempt observations are unavailable without a single explicitly linked controlled run.'
|
|
36
38
|
: !nodes.length ? 'No usable attempt observations are recorded.' : null };
|
|
@@ -63,7 +65,7 @@ function evidenceSections(entry) {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
export function projectWork({ projectId, issues = [], codex = { state: 'absent', runs: [] },
|
|
66
|
-
tasks = [], sessions = [], sources = [], activity = { state: 'none', events: [] }, observedAt = new Date().toISOString() }) {
|
|
68
|
+
tasks = [], sessions = [], nativeGraphs = [], sources = [], activity = { state: 'none', events: [] }, observedAt = new Date().toISOString() }) {
|
|
67
69
|
const entries = [];
|
|
68
70
|
for (const r of codex.runs || []) {
|
|
69
71
|
// Completion of a controller run is not proof of the user's acceptance criteria.
|
|
@@ -139,6 +141,20 @@ export function projectWork({ projectId, issues = [], codex = { state: 'absent',
|
|
|
139
141
|
command: approveCommand || (enabled ? `great-cto resume --task ${t.taskId} --host ${t.host} --revision ${t.revision}` : null),
|
|
140
142
|
});
|
|
141
143
|
}
|
|
144
|
+
for (const observed of nativeGraphs) {
|
|
145
|
+
const linked = entries.filter(e => e.taskId && tasks.some(t => t.taskId === e.taskId && t.links.sessions.includes(observed.sessionId) && t.host === observed.host));
|
|
146
|
+
if (linked.length === 1) {
|
|
147
|
+
const entry = linked[0]; entry.execution ||= executionAttempts(null);
|
|
148
|
+
if (entry.execution.graph?.source !== 'host-observation' && entry.execution.topology !== 'recorded') {
|
|
149
|
+
entry.execution.graph = observed.graph; entry.execution.topology = 'observed';
|
|
150
|
+
} else (entry.execution.observations ||= []).push(observed.graph);
|
|
151
|
+
} else if (!linked.length) entries.push({ key: `native:${observed.host}:${observed.sessionId}`, kind: 'native', taskId: null,
|
|
152
|
+
runId: null, issueIds: [], title: `Observed ${observed.host} session`, goal: null, acceptance: [],
|
|
153
|
+
host: observed.host, phase: 'unknown', nativeState: 'observed', terminal: false, updatedAt: null,
|
|
154
|
+
outcome: null, reason: 'Enrolled native observations; task acceptance and execution authority are not established.',
|
|
155
|
+
decisions: [], capabilities: [], command: null, evidence: [], release: null,
|
|
156
|
+
execution: { state: observed.graph.state, nodes: [], topology: 'observed', graph: observed.graph, partial: true, observations: [] } });
|
|
157
|
+
}
|
|
142
158
|
for (const entry of entries) entry.evidenceSections = evidenceSections(entry);
|
|
143
159
|
entries.sort((a, b) => (a.terminal - b.terminal) || (b.decisions.length - a.decisions.length)
|
|
144
160
|
|| (Date.parse(b.updatedAt || '') || 0) - (Date.parse(a.updatedAt || '') || 0) || a.key.localeCompare(b.key));
|
|
@@ -193,8 +209,23 @@ export function getWork(cwd) {
|
|
|
193
209
|
sessions = readSessionStatus(cwd);
|
|
194
210
|
source('claude_sessions', unreadable ? 'degraded' : 'current', unreadable ? `${unreadable} session state file(s) could not be read` : null);
|
|
195
211
|
} catch { source('claude_sessions', 'unavailable', 'Cannot read Claude session status'); }
|
|
212
|
+
let nativeGraphs = [];
|
|
213
|
+
try { nativeGraphs = nativeGraphObservations(cwd); source('native_graphs', nativeGraphs.some(r => r.graph.health === 'degraded') ? 'degraded' : 'current', null); }
|
|
214
|
+
catch { source('native_graphs', 'unavailable', 'Native graph enrollment is unreadable'); }
|
|
196
215
|
let canonical = cwd;
|
|
197
216
|
try { canonical = realpathSync(cwd); } catch { /* source health already reports inaccessible data */ }
|
|
198
217
|
const projectId = 'project:' + createHash('sha256').update(canonical).digest('hex');
|
|
199
|
-
return { ...projectWork({ projectId, issues, codex, tasks, sessions, sources, activity, observedAt }), projectName: basename(cwd) };
|
|
218
|
+
return { ...projectWork({ projectId, issues, codex, tasks, sessions, nativeGraphs, sources, activity, observedAt }), projectName: basename(cwd) };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Read-only pagination, with exact enrollment or controlled attempt membership. */
|
|
222
|
+
export function getHostGraphPage(root, { host, sessionId, runId = null, cursor = null }) {
|
|
223
|
+
if (!['codex', 'claude-code'].includes(host)) throw Error('invalid graph host');
|
|
224
|
+
if (!runId || runId === sessionId) return readNativeGraphPage({ root, host, sessionId, cursor });
|
|
225
|
+
const listing = listCodexRuns({ root });
|
|
226
|
+
if (listing.state === 'degraded') throw Error('controlled membership is unreadable');
|
|
227
|
+
const run = listing.runs.find(r => r.id === runId);
|
|
228
|
+
const binding = run?.hostObservationBindings.find(b => b.sessionId === sessionId && b.host === host);
|
|
229
|
+
if (!binding) throw Error('controlled observation does not belong to this project');
|
|
230
|
+
return projectHostObservations(graphBinding({ root, runId, sessionId, attemptId: binding.attemptId, host }), { cursor });
|
|
200
231
|
}
|