@techgoblin/gobstack 0.4.4-beta.7 → 0.5.0-beta.1
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 +28 -12
- package/VERSION +1 -1
- package/bin/goblin-audit +1 -1
- package/bin/goblin-bans +9 -1
- package/bin/goblin-init +55 -37
- package/bin/goblin-install +100 -12
- package/bin/goblin-lib.sh +59 -1
- package/bin/goblin-upgrade +1 -1
- package/bin/goblin-verify +52 -11
- package/docs/ADOPTION.md +42 -37
- package/docs/CI.md +11 -8
- package/docs/CONTRACTS.md +30 -15
- package/docs/ENFORCEMENT.md +24 -20
- package/docs/GUIDE.md +37 -33
- package/docs/LIMITS.md +20 -3
- package/manifest/classes.tsv +50 -60
- package/package.json +1 -1
- package/presets/electron-overlay.yaml +38 -0
- package/presets/{E-fleet-config.yaml → fleet.yaml} +1 -1
- package/presets/{C-game.yaml → game.yaml} +1 -1
- package/presets/{D-knowledge.yaml → research.yaml} +1 -1
- package/presets/{B-service-config.yaml → service.yaml} +1 -1
- package/presets/{A-shipped-software.yaml → software.yaml} +1 -1
- package/skills/goblin-bootstrap/SKILL.md +15 -7
- package/templates/AGENTS.md.tmpl +14 -5
- package/templates/goblin.yaml.tmpl +9 -1
- package/presets/F-electron.yaml +0 -67
package/bin/goblin-verify
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
set -uo pipefail
|
|
21
21
|
|
|
22
|
-
GOBLIN_VERIFY_VERSION="0.
|
|
22
|
+
GOBLIN_VERIFY_VERSION="0.5.0"
|
|
23
23
|
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
24
24
|
# shellcheck source=goblin-lib.sh
|
|
25
25
|
. "$SELF_DIR/goblin-lib.sh"
|
|
@@ -261,15 +261,17 @@ if [ -n "$ONLY" ]; then
|
|
|
261
261
|
fi
|
|
262
262
|
|
|
263
263
|
if [ ! -f "$CONFIG" ]; then
|
|
264
|
-
g_err "not installed: $CONFIG is absent. Run: goblin-install --target $ROOT --class <
|
|
264
|
+
g_err "not installed: $CONFIG is absent. Run: goblin-install --target $ROOT --class <software|service|game|research|fleet>"
|
|
265
265
|
exit 2
|
|
266
266
|
fi
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
# The class is read-time resolved: goblin.yaml records the canonical name on new installs, but
|
|
269
|
+
# every repo installed before the 5-class merge records a letter (A..F) or an old name
|
|
270
|
+
# (app/agent/desktop). g_class_canon maps them all to the five canonical names; the tsv carries
|
|
271
|
+
# the canonical names, so nothing else in this file has to know about letters. `desktop`/F -> software.
|
|
272
|
+
CLASS_RAW=$(g_yaml_scalar "$CONFIG" class)
|
|
273
|
+
CLASS=$(g_class_canon "$CLASS_RAW") \
|
|
274
|
+
|| { g_err "unknown class '${CLASS_RAW:-<empty>}' in $CONFIG (expected software|service|game|research|fleet, or A-E / app / agent / desktop)"; exit 2; }
|
|
273
275
|
|
|
274
276
|
# Root vs git toplevel. find_installed_root resolves the CONFIG lookup correctly, but the
|
|
275
277
|
# rows that shell out to git run in $ROOT and git resolves that to the ENCLOSING repo when
|
|
@@ -374,8 +376,12 @@ PERF_BASELINE_COMMIT=$(g_unquote "$(g_yaml_block_scalar "$CONFIG" perf baseline_
|
|
|
374
376
|
PERF_BASELINE_VALUE=$(g_yaml_block_scalar "$CONFIG" perf baseline_value)
|
|
375
377
|
PERF_MEASURED=$(g_unquote "$(g_yaml_block_scalar "$CONFIG" perf measured)")
|
|
376
378
|
PERF_HOST_GATE=$(g_yaml_block_scalar "$CONFIG" perf host_gate)
|
|
379
|
+
# The electron opt-in (software class): true means the electron ban set stays on regardless of a
|
|
380
|
+
# hand-edited bans: list, and a perf host gate must be declared (PF-01). Absent on a repo
|
|
381
|
+
# installed before the merge — read as off, so a legacy class-F repo verifies with no rewrite.
|
|
382
|
+
ELECTRON=$(g_yaml_scalar "$CONFIG" electron)
|
|
377
383
|
export SEC_GITIGNORE_FAMILY SEC_BUILD_OUTPUT SEC_AUDIT_CMD SEC_AUDIT_MAX_AGE SEC_WAIVER_MAX_AGE
|
|
378
|
-
export SEC_WRITE_ROUTES PERF_METRIC PERF_CMD PERF_BASELINE_VALUE PERF_HOST_GATE
|
|
384
|
+
export SEC_WRITE_ROUTES PERF_METRIC PERF_CMD PERF_BASELINE_VALUE PERF_HOST_GATE ELECTRON
|
|
379
385
|
|
|
380
386
|
# Whole days between an ISO date and today, or empty when the date does not parse on this host
|
|
381
387
|
# (`date -d` is GNU; the row FAILS rather than assuming, because an unreadable date is not a
|
|
@@ -536,6 +542,13 @@ check_in_02() {
|
|
|
536
542
|
[ -e "$ROOT/$p" ] || continue ;;
|
|
537
543
|
esac
|
|
538
544
|
fi
|
|
545
|
+
# W6 neutral-first: an EXPLICIT --skills no re-install removes the recorded skill files
|
|
546
|
+
# by design (the record follows the switch). Their absence on a record whose options now
|
|
547
|
+
# say skills=no is the documented opt-out, not drift - the same clause W4a wrote for the
|
|
548
|
+
# global tier, scoped to the record's own declared choice.
|
|
549
|
+
case "$p" in
|
|
550
|
+
.hermes/skills/*) [ -e "$ROOT/$p" ] || [ "$(g_installed_scalar_options "$INSTALLED" skills)" = "yes" ] || continue ;;
|
|
551
|
+
esac
|
|
539
552
|
n=$((n + 1))
|
|
540
553
|
cur=$(g_sha256_file "$ROOT/$p")
|
|
541
554
|
if [ "$cur" != "$h" ]; then
|
|
@@ -606,8 +619,14 @@ check_hp_03() {
|
|
|
606
619
|
m = split(names, dn, " ")
|
|
607
620
|
for (i = 1; i <= m; i++) if (dn[i] != "") decl[dn[i]] = 1
|
|
608
621
|
}
|
|
609
|
-
|
|
610
|
-
|
|
622
|
+
# Portability (clean-room catch, 2026-10-05): the Debian default awk (mawk) has no
|
|
623
|
+
# interval expressions ({2,3}) — the header rule matched nothing and HP-03 went red on
|
|
624
|
+
# every fresh Debian install. The hash-header variants are spelled out as two pattern
|
|
625
|
+
# statements, and the date as repeated [0-9] groups. (No apostrophes here: this
|
|
626
|
+
# program is a single-quoted shell string.)
|
|
627
|
+
tolower($0) ~ /^###[[:space:]]+[^[:alpha:]]*gates?([^[:alpha:]]|$)/ { g = 1; next }
|
|
628
|
+
tolower($0) ~ /^##[[:space:]]+[^[:alpha:]]*gates?([^[:alpha:]]|$)/ { g = 1; next }
|
|
629
|
+
g && (/^###[[:space:]]/ || /^##[[:space:]]/ || /^#[[:space:]]/) { g = 0 }
|
|
611
630
|
!g { next }
|
|
612
631
|
tolower($0) ~ /example of the required form/ { next }
|
|
613
632
|
$0 !~ /=/ { next }
|
|
@@ -617,7 +636,7 @@ check_hp_03() {
|
|
|
617
636
|
if (!hit) for (k in decl) if (hasname($0, k)) { hit = 1; break }
|
|
618
637
|
if (hit) {
|
|
619
638
|
n++
|
|
620
|
-
if ($0 !~ /measured [0-9]
|
|
639
|
+
if ($0 !~ /measured [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/) {
|
|
621
640
|
print "a gate line carries no measured date: " $0
|
|
622
641
|
bad = 1
|
|
623
642
|
}
|
|
@@ -1587,6 +1606,13 @@ check_pf_01() {
|
|
|
1587
1606
|
# to ignore it.
|
|
1588
1607
|
local rname bad=0
|
|
1589
1608
|
rname=$(g_yaml_block_scalar "$CONFIG" ratchet name)
|
|
1609
|
+
# The electron opt-in's done-definition: an Electron repo declares its host gate (the FPS /
|
|
1610
|
+
# main_thread_busy_pct number a hermetic rule cannot measure). `electron: true` with no host
|
|
1611
|
+
# gate is a repo missing half its definition — FAIL, before the baseline skip below can mask it.
|
|
1612
|
+
if [ "$ELECTRON" = "true" ] && [ -z "$PERF_HOST_GATE" ]; then
|
|
1613
|
+
printf 'electron: true requires a declared perf.host_gate — declare the host-gate probe (the number no hermetic rule can measure) or drop electron: true\n'
|
|
1614
|
+
return 1
|
|
1615
|
+
fi
|
|
1590
1616
|
[ -n "$PERF_METRIC" ] || {
|
|
1591
1617
|
printf 'this class declares no perf metric (perf.metric is empty). The ratchet still carries\n%s; declare metric/cmd here the day you pick one\n' "${rname:-nothing}"
|
|
1592
1618
|
return 3
|
|
@@ -2661,6 +2687,21 @@ while IFS=$'\t' read -r id scope rule enforced_by artifact check why; do
|
|
|
2661
2687
|
only_selected "$id" || continue
|
|
2662
2688
|
|
|
2663
2689
|
part=$(row_part "$id")
|
|
2690
|
+
# W6 neutral-first: in global engine mode AU-01's subject is the PRODUCER glob, which
|
|
2691
|
+
# global mode reads from the engine dir — the row must run there even when the repo's
|
|
2692
|
+
# skills opt-out (playbooks) is set, or the W1 control below it is skip-masked. This
|
|
2693
|
+
# clause precedes the opt-out branch on purpose. SK-01/SK-02/SK-04 and AU-02..AU-04 keep
|
|
2694
|
+
# their opt-out semantics.
|
|
2695
|
+
if [ "$id" = "AU-01" ] && [ "$ENGINE_MODE" = "global" ]; then
|
|
2696
|
+
out=$(check_au_01 2>&1); rc=$?
|
|
2697
|
+
case "$rc" in
|
|
2698
|
+
0) PASS=$((PASS + 1)); emit "$id" PASS "$out" ;;
|
|
2699
|
+
1) FAIL=$((FAIL + 1)); emit "$id" FAIL "$out" ;;
|
|
2700
|
+
2) ADV=$((ADV + 1)); emit "$id" ADV "$out" ;;
|
|
2701
|
+
3) SKIP=$((SKIP + 1)); emit "$id" SKIP "$out" ;;
|
|
2702
|
+
esac
|
|
2703
|
+
continue
|
|
2704
|
+
fi
|
|
2664
2705
|
if [ -n "$part" ] && g_part_disabled "$CONFIG" "$part"; then
|
|
2665
2706
|
SKIP=$((SKIP + 1)); emit "$id" SKIP "$rule (opt-out: $part)"; continue
|
|
2666
2707
|
fi
|
package/docs/ADOPTION.md
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
# Adoption — classes, presets, and the order
|
|
2
2
|
|
|
3
|
-
## The
|
|
3
|
+
## The five classes
|
|
4
4
|
|
|
5
5
|
A class is **not** a stringency level. It selects which parts are required, optional or off, and
|
|
6
6
|
it supplies the default gate and ratchet shape. The gate vocabulary differs by class; the
|
|
7
|
-
harness does not.
|
|
7
|
+
harness does not. The names are words; the letters `A`..`E` are read-time aliases.
|
|
8
8
|
|
|
9
9
|
| Class | What "done" means |
|
|
10
10
|
|---|---|
|
|
11
|
-
| **
|
|
12
|
-
| **B
|
|
13
|
-
| **C
|
|
14
|
-
| **
|
|
15
|
-
| **
|
|
16
|
-
| **F. Desktop shell** | the renderer is isolated from Node, the main process is not busy, and the packaged bundle ships no dev dependency |
|
|
11
|
+
| **software** (A) | a gate set reports measured numbers, a round lands, the artifact deploys or publishes |
|
|
12
|
+
| **service** (B) | a contract (schema, route, API) is unchanged, or the change is intentional and migrated |
|
|
13
|
+
| **game** (C) | a suite green in the Editor **and** a human feel verdict — the verdict is a first-class deliverable |
|
|
14
|
+
| **research** (D) | a question is answered with sources and the answer is findable |
|
|
15
|
+
| **fleet** (E) | a config change is applied, verified against the **artifact**, and versioned |
|
|
17
16
|
|
|
18
17
|
Two placements worth arguing about:
|
|
19
18
|
|
|
20
19
|
- A repo whose code is small and lives elsewhere, while the repo holds *output*, belongs in
|
|
21
|
-
**
|
|
22
|
-
not compilation.
|
|
23
|
-
- An input directory that is not a build target at all belongs in **
|
|
20
|
+
**research**, not **software** — gating it like an application gates the wrong artifact; its gate
|
|
21
|
+
is freshness, not compilation.
|
|
22
|
+
- An input directory that is not a build target at all belongs in **research** with **`--archive`**.
|
|
24
23
|
Without the flag the installer keeps producing HANDOFFs for a directory whose own design
|
|
25
24
|
folders are empty.
|
|
26
25
|
|
|
@@ -29,25 +28,28 @@ Two placements worth arguing about:
|
|
|
29
28
|
`R` = required · `O` = optional (installed, reported) · `—` = off. The same data is in
|
|
30
29
|
`manifest/classes.tsv`, and `CL-01` checks it against the repo.
|
|
31
30
|
|
|
32
|
-
| Part |
|
|
33
|
-
|
|
34
|
-
| HANDOFF | R | R | R | R | R |
|
|
35
|
-
| SPEC before change | R | R | R | — | R |
|
|
36
|
-
| Verification gate | R | R | R | O | R |
|
|
37
|
-
| Pinned-commit REPLAY | R | — | R | — | O |
|
|
38
|
-
| Ratchet | R | O | O | — | O |
|
|
39
|
-
| PR gate | O | — | O | — | O |
|
|
40
|
-
| Review panel | O | — | R | — | O |
|
|
41
|
-
| Playbooks (the skills) | R | R | R | R | R |
|
|
42
|
-
| Design tokens | O | — | — | — |
|
|
43
|
-
| CI lane | R | — | O | — | O |
|
|
44
|
-
|
|
45
|
-
**
|
|
46
|
-
(`BN-06`..`BN-09`) and
|
|
47
|
-
|
|
48
|
-
shipped rule may depend on. Its
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
| Part | software | service | game | research | fleet |
|
|
32
|
+
|---|---|---|---|---|---|
|
|
33
|
+
| HANDOFF | R | R | R | R | R |
|
|
34
|
+
| SPEC before change | R | R | R | — | R |
|
|
35
|
+
| Verification gate | R | R | R | O | R |
|
|
36
|
+
| Pinned-commit REPLAY | R | — | R | — | O |
|
|
37
|
+
| Ratchet | R | O | O | — | O |
|
|
38
|
+
| PR gate | O | — | O | — | O |
|
|
39
|
+
| Review panel | O | — | R | — | O |
|
|
40
|
+
| Playbooks (the skills) | R | R | R | R | R |
|
|
41
|
+
| Design tokens | O | — | — | — | O |
|
|
42
|
+
| CI lane | R | — | O | — | O |
|
|
43
|
+
|
|
44
|
+
**The electron opt-in, not a class**, added at W4: an Electron app is the **software** class with
|
|
45
|
+
`electron: true`, which declares the Electron failure surface as bans (`BN-06`..`BN-09`) and
|
|
46
|
+
declares its FPS number as a **host gate** rather than a ratchet, because the probe that measures
|
|
47
|
+
it needs Playwright or Electron plus a display — neither of which a shipped rule may depend on. Its
|
|
48
|
+
ratchet carries `app_bundle_bytes` instead. The old sixth class was merged into `software` (its need
|
|
49
|
+
column measured identical on all ten parts), and the old `F` letter remains an install-time alias
|
|
50
|
+
that selects `software` **with** the opt-in. `docs/CI.md` §3 argues it in full, including why frame
|
|
51
|
+
time is the wrong number (measured flat at 16.70 ms while the main thread went from 1.8 % to 54.5 %
|
|
52
|
+
busy).
|
|
51
53
|
|
|
52
54
|
**`—` is a real, enforced option.** The installer records every off part in `disabled:`, so its
|
|
53
55
|
rows report `SKIP (opt-out)`; `CL-01` fails if a forbidden part's artifact exists. A repo with
|
|
@@ -102,13 +104,16 @@ Each step is independently useful and the later ones build on the earlier:
|
|
|
102
104
|
Then, in order:
|
|
103
105
|
|
|
104
106
|
git add -A && git commit # the install is a change like any other
|
|
105
|
-
.goblin/bin/goblin-verify #
|
|
106
|
-
|
|
107
|
+
.goblin/bin/goblin-verify # 37 passed, 1 failed - HP-05, until HANDOFF names a commit
|
|
108
|
+
gob emit --platform <p> # optional, per platform: the agent skills are an opt-in
|
|
107
109
|
|
|
108
|
-
A class
|
|
110
|
+
A default software-class install (no agent skills) is **green** — `38 passed, 0 failed, 11 advisory,
|
|
111
|
+
33 skipped`, exit 0 — once
|
|
109
112
|
`HANDOFF.md` names a commit that exists; before that edit the scaffold's `0000000` placeholder is
|
|
110
|
-
the one expected red (`
|
|
111
|
-
(`docs/CONTRACTS.md`; step 2 of `docs/GUIDE.md`).
|
|
113
|
+
the one expected red (`37 passed, 1 failed`). Both numbers are measured, not assumed
|
|
114
|
+
(`docs/CONTRACTS.md`; step 2 of `docs/GUIDE.md`). Thirty-three rows skip with a reason: the
|
|
115
|
+
five skill rows (`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks` opt-out a skills-free
|
|
116
|
+
install records, plus the not-yet rows: `HS-02` (no
|
|
112
117
|
pinned pre-change commit yet), `AU-02`/`AU-03` (no report has been filed, so there is nothing to
|
|
113
118
|
dedup and no reporter run to audit), `SC-06`/`SC-07`/`SC-08` (no dependency manifest, no lockfile,
|
|
114
119
|
no audit record), `PF-01` (no measured perf baseline), `BN-01`/`BN-02`/`BN-05` (no `src/` for a ban
|
|
@@ -160,8 +165,8 @@ The remedy is a reconciliation. The project's file stays the file of record; not
|
|
|
160
165
|
git add -A && git commit
|
|
161
166
|
.goblin/bin/goblin-verify # HP-02, HP-03, HP-05 go green
|
|
162
167
|
|
|
163
|
-
Success is the class's full green path (`
|
|
164
|
-
class
|
|
168
|
+
Success is the class's full green path (`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0 for
|
|
169
|
+
the software class) with `git status --short` empty.
|
|
165
170
|
|
|
166
171
|
The edit is additive and small — measured on the model repo (§1's exemplar, 2450 lines): three
|
|
167
172
|
headings plus a `State` block, one dated gate line and a `Not verified` block, 15 lines, no line
|
package/docs/CI.md
CHANGED
|
@@ -43,8 +43,8 @@ not the model.
|
|
|
43
43
|
|
|
44
44
|
## 2. What goblin-stack places, and what it checks
|
|
45
45
|
|
|
46
|
-
The `ci-gate` part is `R` for
|
|
47
|
-
(`manifest/classes.tsv`). When it is installed, the installer renders
|
|
46
|
+
The `ci-gate` part is `R` for **software**, `O` for **game** and **fleet**, and `-` for **service**
|
|
47
|
+
and **research** (`manifest/classes.tsv`). When it is installed, the installer renders
|
|
48
48
|
`templates/ci/goblin-gate.yml.tmpl` into `.github/workflows/goblin-gate.yml` — one job, no `if:` at
|
|
49
49
|
any level, whose only step runs `.goblin/bin/goblin-verify`. It is `owned`, so a second install is a
|
|
50
50
|
no-op and a hand-edited copy is never overwritten.
|
|
@@ -70,11 +70,14 @@ comment and cannot count toward a pass.
|
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
73
|
-
## 3. The Electron
|
|
73
|
+
## 3. The Electron opt-in (software class)
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
An Electron app is the **`software`** class with `electron: true`. The opt-in exists because such an
|
|
76
|
+
app needs a part no other shape has — a **host gate**, a number measured on a machine with a display
|
|
77
|
+
— and forbids a thing the plain software class allows: a **renderer that reaches Node or the
|
|
78
|
+
filesystem directly**. The merge measured the old sixth class's part needs identical to `software` on
|
|
79
|
+
all ten parts, so none of this is a sixth column in `manifest/classes.tsv`: it is the `bans:` list and
|
|
80
|
+
the `perf.host_gate:` key, rendered over the software preset by `presets/electron-overlay.yaml`.
|
|
78
81
|
|
|
79
82
|
### 3.1 The failure surface, and the check for each
|
|
80
83
|
|
|
@@ -94,14 +97,14 @@ reaches Node or the filesystem directly**. That is a new row-set, not a flag on
|
|
|
94
97
|
|
|
95
98
|
### 3.2 The perf lane: one ratchet, and a host gate beside it
|
|
96
99
|
|
|
97
|
-
There is **one** mechanism, and
|
|
100
|
+
There is **one** mechanism, and the opt-in reuses it unchanged: `ratchet: {name, cmd, ceiling}`,
|
|
98
101
|
enforced by `GT-04`/`GT-05` and pinned to a commit by `PF-01`. A second perf mechanism is not
|
|
99
102
|
introduced.
|
|
100
103
|
|
|
101
104
|
What the ratchet measures here is `app_bundle_bytes` — the packaged bundle's byte count. It is
|
|
102
105
|
hermetic, it needs no browser, no display and no dependency, and a fat bundle is a slow cold start on
|
|
103
106
|
every machine. **The FPS number is declared as a host gate instead** (`perf_host_gate:` in
|
|
104
|
-
`presets/
|
|
107
|
+
`presets/electron-overlay.yaml`), and carried in the HANDOFF with the date it was measured.
|
|
105
108
|
|
|
106
109
|
This is a **deviation from G6 §B.3**, which put `main_thread_busy_pct` in the ratchet, and the reason
|
|
107
110
|
is measured: the instrument that produces it — CDP `Performance.getMetrics`, or
|
package/docs/CONTRACTS.md
CHANGED
|
@@ -9,12 +9,18 @@ same way the fleet's own tool reads it. Everything else is line-oriented shell.
|
|
|
9
9
|
goblin-install --target <dir> [options]
|
|
10
10
|
|
|
11
11
|
--target <dir> required; the repo root to install into
|
|
12
|
-
--class
|
|
12
|
+
--class <class> required unless --uninstall or --re-pin. One of the five domain classes:
|
|
13
|
+
software · service · game · research · fleet. The letters A-E and the
|
|
14
|
+
older names app (software), agent (fleet) and desktop/F (software + the
|
|
15
|
+
electron opt-in) are read-time aliases.
|
|
13
16
|
--models <path> model mapping file (default: $GOBLIN_MODELS -> ~/projects/fleet-model.yaml)
|
|
14
17
|
--practice <path> the referenced standard (default: $GOBLIN_PRACTICE -> ~/projects/PROJECT-PRACTICE.md)
|
|
15
18
|
--parts <list> comma list to install; default = every part the class requires
|
|
16
19
|
--archive mark the project archive: verify requires no HANDOFF and no gates
|
|
17
|
-
--skills yes|no install .hermes/skills (default
|
|
20
|
+
--skills yes|no install agent skills under .hermes/skills (default no — the harness is
|
|
21
|
+
neutral; opt in per platform with: gob emit --platform <p>). On a repo whose
|
|
22
|
+
record already has skills installed, an OMITTED flag keeps them; an explicit
|
|
23
|
+
--skills no removes them.
|
|
18
24
|
--dry-run print the plan; write nothing
|
|
19
25
|
--upgrade re-install at the current version; report created/updated/unchanged/skipped
|
|
20
26
|
--opt-out <part> record the part in disabled: so its required checks are skipped
|
|
@@ -37,7 +43,7 @@ files whose hash changed and prints `created C · updated U · unchanged N · sk
|
|
|
37
43
|
|
|
38
44
|
| kind | recorded as | overwritten? | hash-checked? | removed by `--uninstall`? |
|
|
39
45
|
|---|---|---|---|---|
|
|
40
|
-
| installed artifact (bin, manifest, roles, skills, harness scaffold) | `files` | yes, on upgrade | yes — IN-02, SK-02 | yes |
|
|
46
|
+
| installed artifact (bin, manifest, roles, opt-in skills, harness scaffold) | `files` | yes, on upgrade | yes — IN-02, SK-02 | yes |
|
|
41
47
|
| created once, then yours (`.goblin/goblin.yaml`, `HANDOFF.md`, `AGENTS.md`, `*-SPEC.md`, `reviews/.gitkeep`) | `owned` | never | no — you are meant to edit them | no, except the config |
|
|
42
48
|
| pre-existing, left alone | `refused` | never | no — IN-04 only proves it was not taken over | no |
|
|
43
49
|
|
|
@@ -112,9 +118,9 @@ Output is one line per executed row, in manifest order, plus a summary line at t
|
|
|
112
118
|
ADV MD-02 code lane and review lane both resolve to the same family
|
|
113
119
|
SKIP HS-02 no pinned pre-change commit yet - REPLAY not provable
|
|
114
120
|
|
|
115
|
-
Those four lines are one row of each marking. The summary line of a green class
|
|
121
|
+
Those four lines are one row of each marking. The summary line of a green default software-class run is:
|
|
116
122
|
|
|
117
|
-
|
|
123
|
+
38 passed, 0 failed, 11 advisory, 33 skipped
|
|
118
124
|
|
|
119
125
|
**Exit codes:** `0` every executed check passed (advisories and skips do not fail the run) ·
|
|
120
126
|
`1` at least one check FAILED · `2` verify could not run (not installed, a missing dependency,
|
|
@@ -135,8 +141,10 @@ settings that make a workflow a **gate** are written down.
|
|
|
135
141
|
|
|
136
142
|
### A fresh install verifies green
|
|
137
143
|
|
|
138
|
-
Measured on a fresh class
|
|
139
|
-
11 advisory,
|
|
144
|
+
Measured on a fresh DEFAULT software-class install (skills opt-in, W6 neutral-first), committed with no
|
|
145
|
+
hand edit: **`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0.** Thirty-three rows skip with
|
|
146
|
+
a reason — the same not-yet rows as before, plus the five skill rows (`SK-01`..`SK-04`,
|
|
147
|
+
`AU-04`) that skip on the `playbooks` opt-out a skills-free install records: `HS-02` — no pre-change commit
|
|
140
148
|
is pinned yet, so the REPLAY is not provable (`docs/LIMITS.md` #11) — `AU-02` and `AU-03`, which
|
|
141
149
|
have no report to audit in a repo where no reporter has run — `SC-06`, `SC-07` and `SC-08`, which
|
|
142
150
|
have no dependency manifest, no lockfile and no audit record to read yet — `PF-01`, which has
|
|
@@ -173,13 +181,20 @@ verifier is reporting FAILs.
|
|
|
173
181
|
- **Per part:** `--opt-out <part>` records the part in `disabled:`. `goblin-verify` then reports
|
|
174
182
|
the part's rows as `SKIP (opt-out)` in the summary, so the opt-out is **visible rather than
|
|
175
183
|
absent**. The same mechanism is what makes a class's `-` (off) real.
|
|
176
|
-
- **The opt-out numbers are pinned (V3-3).** A
|
|
177
|
-
`
|
|
178
|
-
asserts that line: a silent drift in the opt-out path is caught
|
|
179
|
-
nobody wrote down (the `--skills no` count moved from `37/0/9/11`
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
- **The opt-out numbers are pinned (V3-3).** A software-class install with an explicit `--skills no`
|
|
185
|
+
verifies `38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0, and
|
|
186
|
+
`tests/t-install-off-switch.sh` asserts that line: a silent drift in the opt-out path is caught
|
|
187
|
+
rather than left as a number nobody wrote down (the `--skills no` count moved from `37/0/9/11`
|
|
188
|
+
at v0.2 when the ban rows landed, **15 → 18 on 2026-09-25 (G1)** — the feature-map rows,
|
|
189
|
+
**18 → 24 on 2026-09-25 (W3)** — the judge/loop rows, and **to `38/0/11/33` at W6
|
|
190
|
+
(neutral-first)**, when this opt-out shape BECAME the default and the two lines converged:
|
|
191
|
+
the old default install measured `43/0/11/28`).
|
|
192
|
+
- **Skills, W6 neutral-first.** A default install ships no agent skills. A repo whose record has
|
|
193
|
+
`skills: yes` keeps them through every flag-less re-install and `--upgrade` (the installer
|
|
194
|
+
reads the record's choice and says so out loud); an explicit `--skills no` removes exactly the
|
|
195
|
+
recorded skill files; `--uninstall` removes everything recorded, as always.
|
|
196
|
+
`tests/t-install-off-switch.sh` walks that migration: install `--skills yes`, upgrade flag-less,
|
|
197
|
+
the skills survive byte-identical; uninstall, and they are all gone.
|
|
183
198
|
- **Whole harness:** `--uninstall` deletes the `files` list plus `.goblin/goblin.yaml`, removes
|
|
184
199
|
every directory that leaves empty (deepest first, after `installed.json` itself is gone — the
|
|
185
200
|
order that used to leave `.goblin/` and the sixteen `.hermes/skills/*` directories behind),
|
|
@@ -189,7 +204,7 @@ verifier is reporting FAILs.
|
|
|
189
204
|
|
|
190
205
|
## The two commands, verbatim
|
|
191
206
|
|
|
192
|
-
bash bin/goblin-install --target /path/to/repo --class
|
|
207
|
+
bash bin/goblin-install --target /path/to/repo --class software
|
|
193
208
|
.goblin/bin/goblin-verify
|
|
194
209
|
|
|
195
210
|
From a checkout, without installing anything:
|
package/docs/ENFORCEMENT.md
CHANGED
|
@@ -164,26 +164,30 @@ OR its `enforced_by` cell does.
|
|
|
164
164
|
the installer records every `-` part in `disabled:`, so its rows report `SKIP (opt-out)`
|
|
165
165
|
instead of silently passing, and `CL-01` fails if a forbidden part's artifact exists.
|
|
166
166
|
|
|
167
|
-
| part |
|
|
168
|
-
|
|
169
|
-
| handoff | R | R | R | R | R |
|
|
170
|
-
| spec | R | R | R | - | R |
|
|
171
|
-
| gate | R | R | R | O | R |
|
|
172
|
-
| replay | R | - | R | - | O |
|
|
173
|
-
| ratchet | R | O | O | - | O |
|
|
174
|
-
| pr-gate | O | - | O | - | O |
|
|
175
|
-
| review-panel | O | - | R | - | O |
|
|
176
|
-
| playbooks | R | R | R | R | R |
|
|
177
|
-
| tokens | O | - | - | - |
|
|
178
|
-
| ci-gate | R | - | O | - | O |
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
167
|
+
| part | software | service | game | research | fleet |
|
|
168
|
+
|---|---|---|---|---|---|
|
|
169
|
+
| handoff | R | R | R | R | R |
|
|
170
|
+
| spec | R | R | R | - | R |
|
|
171
|
+
| gate | R | R | R | O | R |
|
|
172
|
+
| replay | R | - | R | - | O |
|
|
173
|
+
| ratchet | R | O | O | - | O |
|
|
174
|
+
| pr-gate | O | - | O | - | O |
|
|
175
|
+
| review-panel | O | - | R | - | O |
|
|
176
|
+
| playbooks | R | R | R | R | R |
|
|
177
|
+
| tokens | O | - | - | - | O |
|
|
178
|
+
| ci-gate | R | - | O | - | O |
|
|
179
|
+
|
|
180
|
+
The five columns carry the domain names; the letters `A`..`E` and the older names `app` (software)
|
|
181
|
+
and `agent` (fleet) are read-time aliases. The old `F` class — the Electron shell — was merged into
|
|
182
|
+
`software`: the merge measured the two need columns identical on all ten parts, so what it added
|
|
183
|
+
lives in config, not in this table. The **electron opt-in** (`electron: true`) turns the electron
|
|
184
|
+
bans `BN-06`..`BN-09` on even when a hand-edited `bans:` list omits them, and requires a declared
|
|
185
|
+
**host gate** — a number measured on a machine with a display; `docs/CI.md` §3 is the contract for
|
|
186
|
+
it. `ci-gate` is the one part added at W4: when it is required or optional the installer renders
|
|
187
|
+
`templates/ci/goblin-gate.yml.tmpl` into `.github/workflows/goblin-gate.yml`, and when it is `-` the
|
|
188
|
+
artifact must be **absent** (which is why `CL-01` keys off that exact path, not the `.github/`
|
|
189
|
+
directory — a repo is still allowed CI of its own). `docs/CI.md` is the contract for what that file
|
|
190
|
+
does and does not make true.
|
|
187
191
|
|
|
188
192
|
## The ban list (G5)
|
|
189
193
|
|
package/docs/GUIDE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A step-by-step guide for your first week. **Read this before the README.** The README tells you
|
|
4
4
|
what the pieces are; this tells you what to *do*, in order, and what you should see when it works.
|
|
5
5
|
|
|
6
|
-
Version: `0.
|
|
6
|
+
Version: `0.5.0` · Last measured: 2026-10-05 · Every command and every output below was run on a
|
|
7
7
|
real repository while writing this guide.
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -97,27 +97,29 @@ see the plan first:
|
|
|
97
97
|
git config user.email "you@example.com"
|
|
98
98
|
git config user.name "you"
|
|
99
99
|
|
|
100
|
-
gob init --target . --class
|
|
100
|
+
gob init --target . --class software --branch main --email "you@example.com" \
|
|
101
101
|
--gate "bash tests/run-tests.sh" --yes
|
|
102
102
|
|
|
103
103
|
or the plain installer this wizard drives, if you prefer the one-shot shape:
|
|
104
104
|
|
|
105
|
-
gob install --target . --class
|
|
105
|
+
gob install --target . --class software
|
|
106
106
|
|
|
107
107
|
Expected output (this is a real transcript, trimmed):
|
|
108
108
|
|
|
109
|
-
created
|
|
109
|
+
created 25 · updated 0 · unchanged 0 · skipped 0
|
|
110
110
|
|
|
111
111
|
next:
|
|
112
112
|
1. cd /tmp/gs-try && git add -A && git commit # the install is a change like any other
|
|
113
113
|
2. .goblin/bin/goblin-verify # or add .goblin/bin to PATH
|
|
114
114
|
3. edit .goblin/goblin.yaml: replace the default gate with your real commands (P8 step 3)
|
|
115
|
-
4.
|
|
115
|
+
4. agent skills are opt-in: gob emit --platform <p> # hermes, claude, copilot, cursor, opencode, codex, gemini
|
|
116
116
|
|
|
117
|
-
**`created
|
|
118
|
-
the 8 it `owns`, and `.gitignore`. It writes **
|
|
119
|
-
record it keeps for itself, which it writes but does not count. It has written nothing
|
|
120
|
-
directory.
|
|
117
|
+
**`created 25`** is the installer's count of the files it **tracks** — the 16 in its `files`
|
|
118
|
+
map, the 8 it `owns`, and `.gitignore`. It writes **26**: the 26th is `.goblin/installed.json`,
|
|
119
|
+
the record it keeps for itself, which it writes but does not count. It has written nothing
|
|
120
|
+
outside this directory. The default install ships **no agent skills** — the harness is neutral,
|
|
121
|
+
and `gob emit --platform <p>` is the per-platform opt-in (the old `--skills yes` default is
|
|
122
|
+
still there for repos that want the Hermes project tier vendored).
|
|
121
123
|
|
|
122
124
|
### Why `git init -b main` matters
|
|
123
125
|
|
|
@@ -141,14 +143,14 @@ branch** (unless you want to).
|
|
|
141
143
|
You will see one line per rule. The shape:
|
|
142
144
|
|
|
143
145
|
PASS IN-01 (test -s .goblin/installed.json && grep -q '"version"' .goblin/installed.json)
|
|
144
|
-
PASS IN-02
|
|
146
|
+
PASS IN-02 16 installed files hashed | practice pin ok
|
|
145
147
|
FAIL HP-05 HANDOFF.md names no commit that exists in this repo
|
|
146
148
|
SKIP HS-02 no pinned pre-change commit yet - the REPLAY is not provable
|
|
147
149
|
ADV HP-04 A stale sentence is corrected in place... (advisory)
|
|
148
150
|
|
|
149
151
|
and a summary line at the bottom:
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
37 passed, 1 failed, 11 advisory, 33 skipped # the one FAIL is HP-05, below
|
|
152
154
|
|
|
153
155
|
### How to read that output
|
|
154
156
|
|
|
@@ -185,7 +187,7 @@ against a declared expectation. That is exactly what you want it to do.
|
|
|
185
187
|
`HEAD when this file was written: `0000000``, and `HP-05` **rejects that placeholder on purpose**.
|
|
186
188
|
A file that names a commit which does not exist is worse than one that names none — it looks like a
|
|
187
189
|
record. Commit first, then write the real short SHA in. Measured: with the placeholder left in,
|
|
188
|
-
verify reports `
|
|
190
|
+
verify reports `37 passed, 1 failed`; with the real SHA, `38 passed, 0 failed`.
|
|
189
191
|
|
|
190
192
|
---
|
|
191
193
|
|
|
@@ -197,7 +199,7 @@ Everything you configure lives in **one file**, created once and then never over
|
|
|
197
199
|
|
|
198
200
|
Open it. The keys that matter on day one:
|
|
199
201
|
|
|
200
|
-
class:
|
|
202
|
+
class: software # software|service|game|research|fleet (A-E are aliases) - what kind of project this is (step 6)
|
|
201
203
|
branch: main # DECLARED, never assumed
|
|
202
204
|
owner_email: you@example.com # the commit identity this repo expects
|
|
203
205
|
practice: /path/to/your-standard.md # optional: your own house rules, hash-pinned
|
|
@@ -243,19 +245,21 @@ supplies the default gate shape. Choose by asking *what does "done" mean here?*
|
|
|
243
245
|
|
|
244
246
|
| Class | Choose it when | "Done" means |
|
|
245
247
|
|---|---|---|
|
|
246
|
-
| **
|
|
247
|
-
| **B
|
|
248
|
-
| **C
|
|
249
|
-
| **
|
|
250
|
-
| **
|
|
251
|
-
|
|
248
|
+
| **software** (A) | an app, library, or tool users run | a gate set reports measured numbers and a round lands |
|
|
249
|
+
| **service** (B) | an API, schema, route, or deployment config | the contract is unchanged, or the change is deliberate and migrated |
|
|
250
|
+
| **game** (C) | a game | a suite is green **and** a human feel verdict exists |
|
|
251
|
+
| **research** (D) | notes, a vault, a research directory | a question is answered with sources and is findable |
|
|
252
|
+
| **fleet** (E) | your agent's own config (`~/.hermes`) | the change is applied, verified against the artifact, versioned |
|
|
253
|
+
|
|
254
|
+
An Electron app is **software** with `electron: true` — the opt-in adds the electron bans and a host
|
|
255
|
+
gate, not a sixth class. The old `F` letter still resolves there as an install alias.
|
|
252
256
|
|
|
253
257
|
**Two placements people get wrong:**
|
|
254
258
|
|
|
255
|
-
- A repo that holds *output* while the code lives elsewhere → **
|
|
256
|
-
application gates the wrong artifact.
|
|
257
|
-
- A plain input directory that is not a build target → **
|
|
258
|
-
expect no HANDOFF and no gates, and to say so.
|
|
259
|
+
- A repo that holds *output* while the code lives elsewhere → **research**, not **software**. Gating
|
|
260
|
+
it like an application gates the wrong artifact.
|
|
261
|
+
- A plain input directory that is not a build target → **research** with `--archive`, which tells
|
|
262
|
+
verify to expect no HANDOFF and no gates, and to say so.
|
|
259
263
|
|
|
260
264
|
Switch class later by editing `class:` in the config and re-running install. The parts you no longer
|
|
261
265
|
need are recorded as **disabled** and will report `SKIP (opt-out)` rather than failing.
|
|
@@ -305,9 +309,9 @@ honest entry, and the harness treats it as one.
|
|
|
305
309
|
> **Prove it was broken first.**
|
|
306
310
|
|
|
307
311
|
Before you trust a check, break the thing it checks and watch it go red — then put it back and watch
|
|
308
|
-
it go green. Break it on a row this walkthrough can actually break: `IN-02` hashes the **
|
|
312
|
+
it go green. Break it on a row this walkthrough can actually break: `IN-02` hashes the **16 files it
|
|
309
313
|
tracks** — not the 8 it `owns` (including `.goblin/goblin.yaml`, which §5 has you editing) and not
|
|
310
|
-
`.goblin/installed.json`; edit one of the
|
|
314
|
+
`.goblin/installed.json`; edit one of the 16 — the exercise below uses `.goblin/bans/README.md`.
|
|
311
315
|
|
|
312
316
|
# REPLAY-BEGIN (this exact block is run by tests/t-doc-guide.sh - keep the two copies identical)
|
|
313
317
|
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
@@ -390,16 +394,16 @@ the next session.
|
|
|
390
394
|
|
|
391
395
|
## 9. What to expect on day one (so you do not misread it)
|
|
392
396
|
|
|
393
|
-
A class
|
|
397
|
+
A software-class install lands on a specific shape. The scaffold ships one deliberate red — `HP-05`, the
|
|
394
398
|
`0000000` placeholder in `HANDOFF.md` (§4) — so a literal first run prints:
|
|
395
399
|
|
|
396
|
-
|
|
400
|
+
37 passed, 1 failed, 11 advisory, 33 skipped (the one FAIL is HP-05)
|
|
397
401
|
|
|
398
402
|
Name a real commit in `HANDOFF.md` and commit, and it is green:
|
|
399
403
|
|
|
400
|
-
|
|
404
|
+
38 passed, 0 failed, 11 advisory, 33 skipped (on a real project; your numbers will differ)
|
|
401
405
|
|
|
402
|
-
**
|
|
406
|
+
**Thirty-three rows skipping is correct**, and each skip prints its reason. In plain terms: the
|
|
403
407
|
harness is telling you which of its rules have nothing to read yet. It is a checklist, not a
|
|
404
408
|
scolding.
|
|
405
409
|
|
|
@@ -407,7 +411,7 @@ Two readings that are easy to get wrong:
|
|
|
407
411
|
|
|
408
412
|
- **Advisory rows are not passes.** Ten rules are labelled `advisory` — counted, not enforced, and
|
|
409
413
|
nine of them carry no executable check at all. The count is capped by `advisory_ceiling: 10`, and
|
|
410
|
-
a class
|
|
414
|
+
a software-class install already sits at 10 of 10: adding another unenforceable rule fails verify until
|
|
411
415
|
one is removed. That is intentional. (The summary line can print `11 advisory`: the eleventh ADV
|
|
412
416
|
line is `JG-02`, a row with a real command of its own that reports ADV here because your model
|
|
413
417
|
file declares no `judge:` lane — it prints the remedy rather than failing a repo for a fleet's
|
|
@@ -483,7 +487,7 @@ engine and silently de-migrate the record).
|
|
|
483
487
|
|
|
484
488
|
### Commands
|
|
485
489
|
|
|
486
|
-
gob install --target <dir> --class
|
|
490
|
+
gob install --target <dir> --class <software|service|game|research|fleet> [options]
|
|
487
491
|
gob install --target <dir> --uninstall
|
|
488
492
|
gob install --target <dir> --re-pin
|
|
489
493
|
gob install --target <dir> --upgrade
|
|
@@ -579,7 +583,7 @@ with *"prove it was broken first"* — it is the one practice that survives cont
|
|
|
579
583
|
# 1. try it somewhere disposable
|
|
580
584
|
mkdir -p /tmp/gs-try && cd /tmp/gs-try
|
|
581
585
|
git init -b main
|
|
582
|
-
gob install --target . --class
|
|
586
|
+
gob install --target . --class software # expect: created 25 (no skills — those are gob emit)
|
|
583
587
|
|
|
584
588
|
# 2. commit and check
|
|
585
589
|
git add -A && git commit -m "chore: install gobstack"
|
|
@@ -600,7 +604,7 @@ with *"prove it was broken first"* — it is the one practice that survives cont
|
|
|
600
604
|
|
|
601
605
|
# 5. do it for real, in a repo you care about
|
|
602
606
|
cd ~/projects/your-project
|
|
603
|
-
gob install --target . --class
|
|
607
|
+
gob install --target . --class software
|
|
604
608
|
git add -A && git commit -m "chore: adopt gobstack"
|
|
605
609
|
.goblin/bin/goblin-verify
|
|
606
610
|
$EDITOR HANDOFF.md # state / gates (dated!) / next / NOT verified
|