mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
feat(health): integrate gbrain as D6 composite dimension
Adds a GBrain row to the /health dashboard rubric with weight 10%. Three sub-signals rolled into one 0-10 score: doctor status (0.5), sync queue depth (0.3), last-push age (0.2). Redistributes when gbrain_sync_mode is off so the dimension stays fair. Weights rebalance: typecheck 25→22, lint 20→18, test 30→28, deadcode 15→13, shell 10→9, gbrain +10 — sums to 100. gbrain doctor --json wrapped in timeout 5s so a hung gbrain never stalls the /health dashboard. Dimension is omitted (not red) when gbrain is not installed — running /health on a non-gbrain machine shouldn't penalize that choice. History-JSONL adds a `gbrain` field. Pre-D6 entries read as null for trend comparison; new tracking starts from first post-D6 run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+46
-17
@@ -952,6 +952,12 @@ command -v knip >/dev/null 2>&1 && echo "DEADCODE: knip"
|
||||
|
||||
# Shell linting
|
||||
command -v shellcheck >/dev/null 2>&1 && ls *.sh scripts/*.sh bin/*.sh 2>/dev/null | head -1 | xargs -I{} echo "SHELL: shellcheck"
|
||||
|
||||
# GBrain presence (D6) — only report as a dimension if gbrain is actually
|
||||
# set up; otherwise skip so machines without gbrain aren't penalized.
|
||||
if command -v gbrain >/dev/null 2>&1 && [ -f "$HOME/.gbrain/config.json" ]; then
|
||||
echo "GBRAIN: gbrain doctor --json (wrapped in timeout 5s)"
|
||||
fi
|
||||
```
|
||||
|
||||
Use Glob to search for shell scripts:
|
||||
@@ -1016,11 +1022,12 @@ Score each category on a 0-10 scale using this rubric:
|
||||
|
||||
| Category | Weight | 10 | 7 | 4 | 0 |
|
||||
|-----------|--------|------|-----------|------------|-----------|
|
||||
| Type check | 25% | Clean (exit 0) | <10 errors | <50 errors | >=50 errors |
|
||||
| Lint | 20% | Clean (exit 0) | <5 warnings | <20 warnings | >=20 warnings |
|
||||
| Tests | 30% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
|
||||
| Dead code | 15% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
|
||||
| Shell lint | 10% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
|
||||
| Type check | 22% | Clean (exit 0) | <10 errors | <50 errors | >=50 errors |
|
||||
| Lint | 18% | Clean (exit 0) | <5 warnings | <20 warnings | >=20 warnings |
|
||||
| Tests | 28% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
|
||||
| Dead code | 13% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
|
||||
| Shell lint | 9% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
|
||||
| GBrain (D6) | 10% | doctor=ok, queue<10, pushed <24h | doctor=warnings OR queue<100 OR pushed <72h | doctor broken OR queue>=100 OR pushed >=72h | N/A (gbrain not installed) |
|
||||
|
||||
**Parsing tool output for counts:**
|
||||
- **tsc:** Count lines matching `error TS` in output.
|
||||
@@ -1031,11 +1038,30 @@ Score each category on a 0-10 scale using this rubric:
|
||||
|
||||
**Composite score:**
|
||||
```
|
||||
composite = (typecheck_score * 0.25) + (lint_score * 0.20) + (test_score * 0.30) + (deadcode_score * 0.15) + (shell_score * 0.10)
|
||||
composite = (typecheck_score * 0.22) + (lint_score * 0.18) + (test_score * 0.28) + (deadcode_score * 0.13) + (shell_score * 0.09) + (gbrain_score * 0.10)
|
||||
```
|
||||
|
||||
If a category is skipped (tool not available), redistribute its weight proportionally
|
||||
among the remaining categories.
|
||||
If a category is skipped (tool not available — includes GBrain when gbrain
|
||||
is not installed), redistribute its weight proportionally among the
|
||||
remaining categories.
|
||||
|
||||
**GBrain sub-score computation (D6):**
|
||||
|
||||
```
|
||||
doctor_component: 10 if `gbrain doctor --json | jq -r .status` == "ok";
|
||||
7 if "warnings"; 0 otherwise (or command times out after 5s).
|
||||
queue_component: 10 if ~/.gstack/.brain-queue.jsonl has <10 lines;
|
||||
7 if 10-100; 0 if >=100 (suggests secret-scan rejections
|
||||
piling up). N/A if gbrain_sync_mode == off.
|
||||
push_component: 10 if (now - mtime of ~/.gstack/.brain-last-push) < 24h;
|
||||
7 if <72h; 0 if >=72h. N/A if gbrain_sync_mode == off.
|
||||
gbrain_score = 0.5 * doctor_component + 0.3 * queue_component + 0.2 * push_component
|
||||
(redistribute 0.3 + 0.2 into doctor when sync_mode is off:
|
||||
gbrain_score = doctor_component in that case)
|
||||
```
|
||||
|
||||
The `gbrain doctor --json` call MUST be wrapped in `timeout 5s` so a hung
|
||||
or misconfigured gbrain doesn't stall the entire /health dashboard.
|
||||
|
||||
---
|
||||
|
||||
@@ -1058,6 +1084,7 @@ Lint biome check . 8/10 WARNING 2s 3 warnings
|
||||
Tests bun test 10/10 CLEAN 12s 47/47 passed
|
||||
Dead code knip 7/10 WARNING 5s 4 unused exports
|
||||
Shell lint shellcheck 10/10 CLEAN 1s 0 issues
|
||||
GBrain gbrain doctor 10/10 CLEAN <1s doctor=ok, queue=3, pushed 2h ago
|
||||
|
||||
COMPOSITE SCORE: 9.1 / 10
|
||||
|
||||
@@ -1091,17 +1118,19 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gst
|
||||
Append one JSONL line to `~/.gstack/projects/$SLUG/health-history.jsonl`:
|
||||
|
||||
```json
|
||||
{"ts":"2026-03-31T14:30:00Z","branch":"main","score":9.1,"typecheck":10,"lint":8,"test":10,"deadcode":7,"shell":10,"duration_s":23}
|
||||
{"ts":"2026-03-31T14:30:00Z","branch":"main","score":9.1,"typecheck":10,"lint":8,"test":10,"deadcode":7,"shell":10,"gbrain":10,"duration_s":23}
|
||||
```
|
||||
|
||||
Fields:
|
||||
- `ts` -- ISO 8601 timestamp
|
||||
- `branch` -- current git branch
|
||||
- `score` -- composite score (one decimal)
|
||||
- `typecheck`, `lint`, `test`, `deadcode`, `shell` -- individual category scores (integer 0-10)
|
||||
- `typecheck`, `lint`, `test`, `deadcode`, `shell`, `gbrain` -- individual category scores (integer 0-10)
|
||||
- `duration_s` -- total time for all tools in seconds
|
||||
|
||||
If a category was skipped, set its value to `null`.
|
||||
If a category was skipped, set its value to `null`. Pre-D6 history entries
|
||||
won't have a `gbrain` field — treat them as `null` for trend comparison
|
||||
and start new tracking from the first post-D6 run.
|
||||
|
||||
---
|
||||
|
||||
@@ -1120,12 +1149,12 @@ tail -10 ~/.gstack/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_H
|
||||
```
|
||||
HEALTH TREND (last 5 runs)
|
||||
==========================
|
||||
Date Branch Score TC Lint Test Dead Shell
|
||||
---------- ----------- ----- -- ---- ---- ---- -----
|
||||
2026-03-28 main 9.4 10 9 10 8 10
|
||||
2026-03-29 feat/auth 8.8 10 7 10 7 10
|
||||
2026-03-30 feat/auth 8.2 10 6 9 7 10
|
||||
2026-03-31 feat/auth 9.1 10 8 10 7 10
|
||||
Date Branch Score TC Lint Test Dead Shell GBrain
|
||||
---------- ----------- ----- -- ---- ---- ---- ----- ------
|
||||
2026-03-28 main 9.4 10 9 10 8 10 10
|
||||
2026-03-29 feat/auth 8.8 10 7 10 7 10 10
|
||||
2026-03-30 feat/auth 8.2 10 6 9 7 10 7
|
||||
2026-03-31 feat/auth 9.1 10 8 10 7 10 10
|
||||
|
||||
Trend: IMPROVING (+0.9 since last run)
|
||||
```
|
||||
|
||||
+46
-17
@@ -69,6 +69,12 @@ command -v knip >/dev/null 2>&1 && echo "DEADCODE: knip"
|
||||
|
||||
# Shell linting
|
||||
command -v shellcheck >/dev/null 2>&1 && ls *.sh scripts/*.sh bin/*.sh 2>/dev/null | head -1 | xargs -I{} echo "SHELL: shellcheck"
|
||||
|
||||
# GBrain presence (D6) — only report as a dimension if gbrain is actually
|
||||
# set up; otherwise skip so machines without gbrain aren't penalized.
|
||||
if command -v gbrain >/dev/null 2>&1 && [ -f "$HOME/.gbrain/config.json" ]; then
|
||||
echo "GBRAIN: gbrain doctor --json (wrapped in timeout 5s)"
|
||||
fi
|
||||
```
|
||||
|
||||
Use Glob to search for shell scripts:
|
||||
@@ -133,11 +139,12 @@ Score each category on a 0-10 scale using this rubric:
|
||||
|
||||
| Category | Weight | 10 | 7 | 4 | 0 |
|
||||
|-----------|--------|------|-----------|------------|-----------|
|
||||
| Type check | 25% | Clean (exit 0) | <10 errors | <50 errors | >=50 errors |
|
||||
| Lint | 20% | Clean (exit 0) | <5 warnings | <20 warnings | >=20 warnings |
|
||||
| Tests | 30% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
|
||||
| Dead code | 15% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
|
||||
| Shell lint | 10% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
|
||||
| Type check | 22% | Clean (exit 0) | <10 errors | <50 errors | >=50 errors |
|
||||
| Lint | 18% | Clean (exit 0) | <5 warnings | <20 warnings | >=20 warnings |
|
||||
| Tests | 28% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
|
||||
| Dead code | 13% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
|
||||
| Shell lint | 9% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
|
||||
| GBrain (D6) | 10% | doctor=ok, queue<10, pushed <24h | doctor=warnings OR queue<100 OR pushed <72h | doctor broken OR queue>=100 OR pushed >=72h | N/A (gbrain not installed) |
|
||||
|
||||
**Parsing tool output for counts:**
|
||||
- **tsc:** Count lines matching `error TS` in output.
|
||||
@@ -148,11 +155,30 @@ Score each category on a 0-10 scale using this rubric:
|
||||
|
||||
**Composite score:**
|
||||
```
|
||||
composite = (typecheck_score * 0.25) + (lint_score * 0.20) + (test_score * 0.30) + (deadcode_score * 0.15) + (shell_score * 0.10)
|
||||
composite = (typecheck_score * 0.22) + (lint_score * 0.18) + (test_score * 0.28) + (deadcode_score * 0.13) + (shell_score * 0.09) + (gbrain_score * 0.10)
|
||||
```
|
||||
|
||||
If a category is skipped (tool not available), redistribute its weight proportionally
|
||||
among the remaining categories.
|
||||
If a category is skipped (tool not available — includes GBrain when gbrain
|
||||
is not installed), redistribute its weight proportionally among the
|
||||
remaining categories.
|
||||
|
||||
**GBrain sub-score computation (D6):**
|
||||
|
||||
```
|
||||
doctor_component: 10 if `gbrain doctor --json | jq -r .status` == "ok";
|
||||
7 if "warnings"; 0 otherwise (or command times out after 5s).
|
||||
queue_component: 10 if ~/.gstack/.brain-queue.jsonl has <10 lines;
|
||||
7 if 10-100; 0 if >=100 (suggests secret-scan rejections
|
||||
piling up). N/A if gbrain_sync_mode == off.
|
||||
push_component: 10 if (now - mtime of ~/.gstack/.brain-last-push) < 24h;
|
||||
7 if <72h; 0 if >=72h. N/A if gbrain_sync_mode == off.
|
||||
gbrain_score = 0.5 * doctor_component + 0.3 * queue_component + 0.2 * push_component
|
||||
(redistribute 0.3 + 0.2 into doctor when sync_mode is off:
|
||||
gbrain_score = doctor_component in that case)
|
||||
```
|
||||
|
||||
The `gbrain doctor --json` call MUST be wrapped in `timeout 5s` so a hung
|
||||
or misconfigured gbrain doesn't stall the entire /health dashboard.
|
||||
|
||||
---
|
||||
|
||||
@@ -175,6 +201,7 @@ Lint biome check . 8/10 WARNING 2s 3 warnings
|
||||
Tests bun test 10/10 CLEAN 12s 47/47 passed
|
||||
Dead code knip 7/10 WARNING 5s 4 unused exports
|
||||
Shell lint shellcheck 10/10 CLEAN 1s 0 issues
|
||||
GBrain gbrain doctor 10/10 CLEAN <1s doctor=ok, queue=3, pushed 2h ago
|
||||
|
||||
COMPOSITE SCORE: 9.1 / 10
|
||||
|
||||
@@ -208,17 +235,19 @@ DETAILS: Lint (3 warnings)
|
||||
Append one JSONL line to `~/.gstack/projects/$SLUG/health-history.jsonl`:
|
||||
|
||||
```json
|
||||
{"ts":"2026-03-31T14:30:00Z","branch":"main","score":9.1,"typecheck":10,"lint":8,"test":10,"deadcode":7,"shell":10,"duration_s":23}
|
||||
{"ts":"2026-03-31T14:30:00Z","branch":"main","score":9.1,"typecheck":10,"lint":8,"test":10,"deadcode":7,"shell":10,"gbrain":10,"duration_s":23}
|
||||
```
|
||||
|
||||
Fields:
|
||||
- `ts` -- ISO 8601 timestamp
|
||||
- `branch` -- current git branch
|
||||
- `score` -- composite score (one decimal)
|
||||
- `typecheck`, `lint`, `test`, `deadcode`, `shell` -- individual category scores (integer 0-10)
|
||||
- `typecheck`, `lint`, `test`, `deadcode`, `shell`, `gbrain` -- individual category scores (integer 0-10)
|
||||
- `duration_s` -- total time for all tools in seconds
|
||||
|
||||
If a category was skipped, set its value to `null`.
|
||||
If a category was skipped, set its value to `null`. Pre-D6 history entries
|
||||
won't have a `gbrain` field — treat them as `null` for trend comparison
|
||||
and start new tracking from the first post-D6 run.
|
||||
|
||||
---
|
||||
|
||||
@@ -237,12 +266,12 @@ tail -10 ~/.gstack/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_H
|
||||
```
|
||||
HEALTH TREND (last 5 runs)
|
||||
==========================
|
||||
Date Branch Score TC Lint Test Dead Shell
|
||||
---------- ----------- ----- -- ---- ---- ---- -----
|
||||
2026-03-28 main 9.4 10 9 10 8 10
|
||||
2026-03-29 feat/auth 8.8 10 7 10 7 10
|
||||
2026-03-30 feat/auth 8.2 10 6 9 7 10
|
||||
2026-03-31 feat/auth 9.1 10 8 10 7 10
|
||||
Date Branch Score TC Lint Test Dead Shell GBrain
|
||||
---------- ----------- ----- -- ---- ---- ---- ----- ------
|
||||
2026-03-28 main 9.4 10 9 10 8 10 10
|
||||
2026-03-29 feat/auth 8.8 10 7 10 7 10 10
|
||||
2026-03-30 feat/auth 8.2 10 6 9 7 10 7
|
||||
2026-03-31 feat/auth 9.1 10 8 10 7 10 10
|
||||
|
||||
Trend: IMPROVING (+0.9 since last run)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user