@techgoblin/gobstack 0.4.4-beta.7 → 0.4.4-beta.8
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 +16 -6
- package/bin/goblin-init +21 -9
- package/bin/goblin-install +63 -5
- package/bin/goblin-lib.sh +31 -0
- package/bin/goblin-verify +22 -0
- package/docs/ADOPTION.md +9 -6
- package/docs/CONTRACTS.md +25 -13
- package/docs/GUIDE.md +17 -15
- package/docs/LIMITS.md +1 -1
- package/package.json +1 -1
- package/skills/goblin-bootstrap/SKILL.md +10 -5
- package/templates/AGENTS.md.tmpl +14 -5
package/README.md
CHANGED
|
@@ -50,14 +50,17 @@ After installing, in this order:
|
|
|
50
50
|
|
|
51
51
|
cd <target> && git add -A && git commit # the install is a change like any other
|
|
52
52
|
gob verify # or .goblin/bin/goblin-verify, inside the target
|
|
53
|
-
|
|
53
|
+
gob emit --platform <p> # optional, per platform: the agent skills are an opt-in
|
|
54
54
|
gob audit # once, deliberately: the ONLY network step (SC-07)
|
|
55
55
|
|
|
56
|
-
**A class-A install
|
|
56
|
+
**A default class-A install (no agent skills — those are `gob emit`'s job) verifies green —
|
|
57
|
+
`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0 — once
|
|
57
58
|
`HANDOFF.md` names a commit that exists. Before that edit the scaffold's `0000000` placeholder is
|
|
58
|
-
the one expected red: `
|
|
59
|
-
the fix are step 2 of `docs/GUIDE.md`.**
|
|
60
|
-
|
|
59
|
+
the one expected red: `37 passed, 1 failed`, `HP-05`. Both numbers measured at W6 (neutral-first);
|
|
60
|
+
the run and the fix are step 2 of `docs/GUIDE.md`.**
|
|
61
|
+
Thirty-three rows skip — the five skill rows (`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks`
|
|
62
|
+
opt-out a skills-free install records, then the not-yet rows (`HS-02` has no pinned pre-change
|
|
63
|
+
commit yet, so the REPLAY is not provable;
|
|
61
64
|
`AU-02` and `AU-03` have no report to audit; `SC-06`, `SC-07` and `SC-08` have no dependency
|
|
62
65
|
manifest, no lockfile and no audit record to read; `PF-01` has no measured perf baseline;
|
|
63
66
|
`BN-01`/`BN-02`/`BN-05` have no `src/` for a ban to read, and `BN-03` plus the four electron bans
|
|
@@ -83,7 +86,7 @@ The measurement and the vacuous-pass reading are in `docs/CONTRACTS.md`.
|
|
|
83
86
|
| `gob verify` | run the rule matrix against the current repo — `PASS`/`FAIL`/`SKIP` per row, exit 0 pass · 1 a check failed · 2 could not run · 3 the manifest is broken |
|
|
84
87
|
| `gob bans` | run the ban list (per-pattern red lines over the source tree) |
|
|
85
88
|
| `gob audit` | check recorded dependency claims against live advisory feeds — the only command that touches the network |
|
|
86
|
-
| `gob install` | install the manifest, skills
|
|
89
|
+
| `gob install` | install the harness into a target repo: manifest, verifier, gates, HANDOFF — no agent skills (those are an opt-in: `gob emit --platform <p>`, or `--skills yes`) |
|
|
87
90
|
| `gob uninstall` | remove everything an install wrote, byte-exactly (`gob install --target <dir> --uninstall` is the same job) |
|
|
88
91
|
| `gob upgrade` | migrate a repo to the shared global engine at `~/.goblin/engine` — 8 steps, two commits, one report |
|
|
89
92
|
| `gob doctor` | one run across the platforms below: DETECTED / NOT-DETECTED / DRIFT per platform |
|
|
@@ -111,6 +114,13 @@ One run of `gob emit --platform <p> --scope project` writes the skills and the c
|
|
|
111
114
|
session of that platform reads; `--scope global` writes to the machine-level anchor. `--dry-run`
|
|
112
115
|
prints the full write plan first.
|
|
113
116
|
|
|
117
|
+
**Agent skills are opt-in.** A `gob install` writes the neutral harness only — `.goblin/`,
|
|
118
|
+
`HANDOFF.md`, `AGENTS.md`, the checks and the `.gitignore` block; no skills directory, and no
|
|
119
|
+
files belonging to any coding agent. The guided path is `gob init`'s emit screen; the one-shot
|
|
120
|
+
path is `gob emit --platform <p>` after installing. Repos whose install predates the opt-in
|
|
121
|
+
default keep their skills through `gob upgrade` (the install record names them; only an explicit
|
|
122
|
+
`--skills no`, or `--uninstall`, removes them).
|
|
123
|
+
|
|
114
124
|
## What it is not
|
|
115
125
|
|
|
116
126
|
Not a rules document (every rule carries a runnable check or is explicitly counted as
|
package/bin/goblin-init
CHANGED
|
@@ -375,29 +375,37 @@ if [ -n "$EMIT" ]; then
|
|
|
375
375
|
[ -n "$ok" ] || { g_err "--emit: unknown platform '$w' — I ship $PLA, hermes, copilot, cursor, opencode, codex, $GEM"; exit 2; }
|
|
376
376
|
done
|
|
377
377
|
else
|
|
378
|
+
# neutral-first: a flags-only run with no --emit emits NOTHING. The detected rows are
|
|
379
|
+
# shown on the emit screen as pre-ticked SUGGESTIONS for the interactive path; in
|
|
380
|
+
# non-interactive mode silence means the neutral harness only.
|
|
378
381
|
EMIT_SELECTED=""
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
if [ "$YES" -eq 0 ] && [ "$TTY_IN" -eq 1 ]; then
|
|
383
|
+
while IFS=$'\t' read -r p _proj _glob _hit; do
|
|
384
|
+
[ -n "$p" ] || continue
|
|
385
|
+
EMIT_SELECTED="$EMIT_SELECTED $p"
|
|
386
|
+
done <<< "$DET_ROWS"
|
|
387
|
+
fi
|
|
383
388
|
fi
|
|
384
389
|
EMIT_N=$(printf '%s' "$EMIT_SELECTED" | awk 'NF' | wc -l | tr -d '[:space:]')
|
|
385
390
|
|
|
386
391
|
if [ "$TTY_IN" -eq 1 ] && [ -z "$EMIT" ] && [ "$YES" -eq 0 ]; then
|
|
387
392
|
rail_say ""
|
|
388
|
-
rail_say " ${C_MUTED}
|
|
393
|
+
rail_say " ${C_MUTED}agent skills are opt-in, per platform — emit them into:$C_RESET"
|
|
389
394
|
for p in $PLATFORMS; do
|
|
390
395
|
hit=""
|
|
391
396
|
while IFS=$' ' read -r dp _a _b _c; do [ "$dp" = "$p" ] && hit=1; done <<< "$DET_ROWS"
|
|
392
397
|
[ -n "$hit" ] || continue
|
|
393
398
|
rail_say " ${C_GREEN}✔$C_RESET $C_TEXT$p$C_RESET"
|
|
394
399
|
done
|
|
395
|
-
rail_say " ${C_MUTED}
|
|
400
|
+
rail_say " ${C_MUTED}the install itself writes none — the harness stays neutral$C_RESET"
|
|
401
|
+
rail_say " ${C_MUTED}Enter = these; or type platform names to override (none: type x)$C_RESET"
|
|
396
402
|
printf ' '
|
|
397
403
|
read -r REPLY_EMIT
|
|
398
404
|
ASKED_ANY=1
|
|
399
405
|
rail_say ""
|
|
400
|
-
if [
|
|
406
|
+
if [ "$REPLY_EMIT" = "x" ] || [ "$REPLY_EMIT" = "none" ]; then
|
|
407
|
+
EMIT_SELECTED=""
|
|
408
|
+
elif [ -n "$REPLY_EMIT" ]; then
|
|
401
409
|
EMIT_SELECTED=""
|
|
402
410
|
for w in $REPLY_EMIT; do
|
|
403
411
|
ok=""
|
|
@@ -456,7 +464,7 @@ if [ "$TTY_OUT" -eq 1 ]; then
|
|
|
456
464
|
else
|
|
457
465
|
printf 'gob init [run] goblin-install --target %s --class %s\n' "$TARGET" "$CLASS"
|
|
458
466
|
fi
|
|
459
|
-
bash "$SRC/bin/goblin-install" --target "$TARGET" --class "$CLASS" --yes
|
|
467
|
+
bash "$SRC/bin/goblin-install" --target "$TARGET" --class "$CLASS" --skills no --yes
|
|
460
468
|
EXIT_INSTALL=$?
|
|
461
469
|
if [ "$EXIT_INSTALL" -ne 0 ]; then
|
|
462
470
|
g_err "install failed (exit $EXIT_INSTALL) — the refusals above name the path and the fix"
|
|
@@ -546,7 +554,11 @@ if [ "$DRYRUN" -eq 0 ]; then
|
|
|
546
554
|
printf ' 1. %sthe %s skipped checks ARE your checklist — gob verify names each one%s\n' "$C_TEXT" "$SKIPPED" "$C_RESET"
|
|
547
555
|
printf ' 2. %sreplace the placeholder gate in .goblin/goblin.yaml with your real commands (P8 step 3)%s\n' "$C_TEXT" "$C_RESET"
|
|
548
556
|
printf ' 3. %snot emitted here: gob emit --platform <p> --scope global%s\n' "$C_TEXT" "$C_RESET"
|
|
549
|
-
|
|
557
|
+
if [ "$EMIT_N" -gt 0 ]; then
|
|
558
|
+
printf ' 4. %shermes skills trust %s # one-time, so the project-tier skills load%s\n' "$C_TEXT" "$TARGET" "$C_RESET"
|
|
559
|
+
else
|
|
560
|
+
printf ' 4. %sagent skills are opt-in per platform: gob emit --platform <p>%s\n' "$C_TEXT" "$C_RESET"
|
|
561
|
+
fi
|
|
550
562
|
printf '\n'
|
|
551
563
|
if [ "$VERIFY_RC" -eq 0 ]; then
|
|
552
564
|
printf ' %s▙ the goblin sees you. keep the gate green.%s\n' "$C_ACCENT" "$C_RESET"
|
package/bin/goblin-install
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
# --practice <path> the referenced standard (default: $GOBLIN_PRACTICE -> ~/projects/PROJECT-PRACTICE.md); a named path that is absent is reported, never silently dropped
|
|
9
9
|
# --parts <list> comma list to install; default = every part the class requires
|
|
10
10
|
# --archive mark the project archive: verify requires no HANDOFF and no gates
|
|
11
|
-
# --skills yes|no install .hermes/skills (default
|
|
11
|
+
# --skills yes|no install agent skills under .hermes/skills (default no; opt in per
|
|
12
|
+
# platform with: gob emit --platform <p>)
|
|
12
13
|
# --dry-run print the plan; write nothing
|
|
13
14
|
# --upgrade re-install at the current version; report created/updated/unchanged/skipped
|
|
14
15
|
# --opt-out <part> record the part in disabled: so its required checks are skipped
|
|
@@ -40,7 +41,16 @@ PRACTICE="${GOBLIN_PRACTICE:-$HOME/projects/PROJECT-PRACTICE.md}"
|
|
|
40
41
|
PRACTICE_ARG=""
|
|
41
42
|
PARTS=""
|
|
42
43
|
ARCHIVE="false"
|
|
43
|
-
|
|
44
|
+
# W6 neutral-first: a default install is a NEUTRAL harness (manifest + verify + HANDOFF +
|
|
45
|
+
# .goblin) with NO agent skills. Agent skills are an explicit opt-in (--skills yes, or the
|
|
46
|
+
# per-platform gob emit path the wizard's emit screen drives). The old default (yes) was the
|
|
47
|
+
# house's own setup, not a product default: a non-agent user got a dead .hermes/ tree, and a
|
|
48
|
+
# user of another agent harness got nothing at all.
|
|
49
|
+
SKILLS="no"
|
|
50
|
+
# Whether --skills was named on the command line: an OMITTED flag on a re-install means
|
|
51
|
+
# "whatever the record says" (the migration contract below); a NAMED --skills no is an
|
|
52
|
+
# explicit opt-out and is honoured verbatim.
|
|
53
|
+
SKILLS_NAMED=0
|
|
44
54
|
DRY_RUN=0
|
|
45
55
|
UPGRADE=0
|
|
46
56
|
UNINSTALL=0
|
|
@@ -63,7 +73,7 @@ while [ $# -gt 0 ]; do
|
|
|
63
73
|
--practice) PRACTICE="${2:-}"; PRACTICE_ARG="$PRACTICE"; shift 2 ;;
|
|
64
74
|
--parts) PARTS="${2:-}"; shift 2 ;;
|
|
65
75
|
--archive) ARCHIVE="true"; shift ;;
|
|
66
|
-
--skills) SKILLS="${2:-}"; shift 2 ;;
|
|
76
|
+
--skills) SKILLS="${2:-}"; SKILLS_NAMED=1; shift 2 ;;
|
|
67
77
|
--dry-run) DRY_RUN=1; shift ;;
|
|
68
78
|
--upgrade) UPGRADE=1; shift ;;
|
|
69
79
|
--uninstall) UNINSTALL=1; shift ;;
|
|
@@ -233,6 +243,20 @@ fi
|
|
|
233
243
|
case "$CLASS" in A|B|C|D|E|F) ;; *) g_err "--class must be one of A B C D E F, got '$CLASS'"; exit 2 ;; esac
|
|
234
244
|
case "$SKILLS" in yes|no) ;; *) g_err "--skills must be yes or no"; exit 2 ;; esac
|
|
235
245
|
|
|
246
|
+
# W6 migration safety: on a --upgrade (or any re-install) of a repo whose record shows skills
|
|
247
|
+
# were installed, an OMITTED --skills flag must READ that state, not silently strip it. The
|
|
248
|
+
# idempotence contract (created/updated/unchanged) holds only if the effective skills choice
|
|
249
|
+
# for a flag-less re-install is `the record's choice`, so previously-installed skills survive
|
|
250
|
+
# every upgrade unchanged. An EXPLICIT --skills no is honoured verbatim (the switch stays
|
|
251
|
+
# real), and --uninstall still removes exactly what the record lists.
|
|
252
|
+
if [ -f "$INSTALLED" ] && [ "$SKILLS_NAMED" -eq 0 ]; then
|
|
253
|
+
PREV_SKILLS=$(g_installed_scalar_options "$INSTALLED" skills)
|
|
254
|
+
if [ "$PREV_SKILLS" = "yes" ]; then
|
|
255
|
+
SKILLS="yes"
|
|
256
|
+
g_info "skills: this repo's install record has agent skills installed - they are kept (remove them with an explicit --skills no, or --uninstall)"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
236
260
|
PRESET=$(ls "$SRC"/presets/"$CLASS"-*.yaml 2>/dev/null | head -n 1)
|
|
237
261
|
[ -n "$PRESET" ] || { g_err "no preset for class $CLASS"; exit 2; }
|
|
238
262
|
|
|
@@ -572,6 +596,30 @@ if [ "$SKILLS" = "yes" ] && part_wanted playbooks; then
|
|
|
572
596
|
done
|
|
573
597
|
fi
|
|
574
598
|
|
|
599
|
+
# W6 neutral-first: an EXPLICIT --skills no re-install of a repo that HAS recorded skills
|
|
600
|
+
# removes them — the switch stays real in both directions. The paths come from the previous
|
|
601
|
+
# record, so exactly what was installed is what goes; a skill file the project edited by hand
|
|
602
|
+
# is refused by put's own contract (never clobbered silently), reported in the summary.
|
|
603
|
+
if [ "$SKILLS_NAMED" -eq 1 ] && [ "$SKILLS" = "no" ] && [ -n "$PREV_FILES" ]; then
|
|
604
|
+
while IFS= read -r rel; do
|
|
605
|
+
case "$rel" in .hermes/skills/*) ;; *) continue ;; esac
|
|
606
|
+
dst="$TARGET/$rel"
|
|
607
|
+
[ -f "$dst" ] || { rm -f "$TARGET/.goblin/$(basename "$rel")" 2>/dev/null; continue; }
|
|
608
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
609
|
+
g_info "plan: remove $rel (explicit --skills no)"
|
|
610
|
+
else
|
|
611
|
+
rm -f "$dst"
|
|
612
|
+
g_info "removed $rel (explicit --skills no)"
|
|
613
|
+
fi
|
|
614
|
+
done <<EOF
|
|
615
|
+
$PREV_FILES
|
|
616
|
+
EOF
|
|
617
|
+
# the emptied .hermes/skills/*/<name> and .hermes/ directories go too (deepest first)
|
|
618
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
619
|
+
find "$TARGET/.hermes" -depth -type d -exec rmdir {} + 2>/dev/null
|
|
620
|
+
fi
|
|
621
|
+
fi
|
|
622
|
+
|
|
575
623
|
# The automation producers. They land in the target so the target is self-contained and
|
|
576
624
|
# IN-02/SK-02 hash them like every other installed file; the cron copy step then reads from
|
|
577
625
|
# here (see the "next:" block and automations/README.md).
|
|
@@ -667,12 +715,20 @@ fi
|
|
|
667
715
|
} > "$TMP/installed.json"
|
|
668
716
|
|
|
669
717
|
PREV_VERSION=""
|
|
670
|
-
|
|
718
|
+
RECORD_SKILLS_EFFECTIVE=""
|
|
719
|
+
if [ -f "$INSTALLED" ]; then
|
|
720
|
+
PREV_VERSION=$(g_installed_scalar "$INSTALLED" version)
|
|
721
|
+
# The record's own skills choice (after the migration read above): a flip of the effective
|
|
722
|
+
# choice IS a change even when no file hash moved — the explicit --skills no re-install must
|
|
723
|
+
# rewrite the record (and drop the files), never report no-op over a state change.
|
|
724
|
+
RECORD_SKILLS_EFFECTIVE=$(g_installed_scalar_options "$INSTALLED" skills)
|
|
725
|
+
fi
|
|
671
726
|
|
|
672
727
|
|
|
673
728
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
674
729
|
mkdir -p "$TARGET/.goblin"
|
|
675
|
-
if [ "$CREATED" -eq 0 ] && [ "$UPDATED" -eq 0 ] && [ "$PREV_VERSION" = "$VERSION" ]
|
|
730
|
+
if [ "$CREATED" -eq 0 ] && [ "$UPDATED" -eq 0 ] && [ "$PREV_VERSION" = "$VERSION" ] \
|
|
731
|
+
&& [ "$(printf '%s' "$RECORD_SKILLS_EFFECTIVE" )" = "$(printf '%s' "$SKILLS")" ]; then
|
|
676
732
|
g_info "no-op: $UNCHANGED files unchanged (v$VERSION already installed)"
|
|
677
733
|
else
|
|
678
734
|
cp "$TMP/installed.json" "$INSTALLED"
|
|
@@ -699,6 +755,8 @@ if [ "$DRY_RUN" -eq 0 ]; then
|
|
|
699
755
|
g_info " 3. edit .goblin/goblin.yaml: replace the default gate with your real commands (P8 step 3)"
|
|
700
756
|
if [ "$SKILLS" = "yes" ]; then
|
|
701
757
|
g_info " 4. hermes skills trust $TARGET # one-time, so the project-tier skills load"
|
|
758
|
+
else
|
|
759
|
+
g_info " 4. agent skills are opt-in: gob emit --platform <p> # run gob doctor for the platform list"
|
|
702
760
|
fi
|
|
703
761
|
g_info ""
|
|
704
762
|
g_info "automations (optional; neither writes outside this repo, and A-02 has no agent in it):"
|
package/bin/goblin-lib.sh
CHANGED
|
@@ -216,6 +216,37 @@ g_installed_scalar() {
|
|
|
216
216
|
sed -n "s/^[[:space:]]*\"$2\"[[:space:]]*:[[:space:]]*\"\{0,1\}\([^\",]*\)\"\{0,1\},\{0,1\}$/\1/p" "$1" | head -n 1
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
# g_installed_scalar_options <file> <key> — the value of one key inside the "options" object
|
|
220
|
+
# of an install record (e.g. skills). The installer writes options on ONE line
|
|
221
|
+
# (`"options": {"skills": "yes", ...}`), so the awk reads that line's shape directly; a
|
|
222
|
+
# multi-line variant is read the same way the other installed_* readers read their block.
|
|
223
|
+
g_installed_scalar_options() {
|
|
224
|
+
awk -v k="$2" '
|
|
225
|
+
/^[[:space:]]*"options"[[:space:]]*:[[:space:]]*\{/ && index($0, "\"" k "\"") {
|
|
226
|
+
# single-line form: pull the value out between the key and the next , or }
|
|
227
|
+
line = $0
|
|
228
|
+
sub(/^[^{]*\{/, "", line) # everything up to and including the opening brace
|
|
229
|
+
n = split(line, pair, ",")
|
|
230
|
+
for (i = 1; i <= n; i++) {
|
|
231
|
+
p = pair[i]
|
|
232
|
+
sub(/^[[:space:]]*/, "", p); sub(/[[:space:]]*$/, "", p)
|
|
233
|
+
key = p; sub(/[[:space:]]*:.*/, "", key); gsub(/"/, "", key)
|
|
234
|
+
if (key == k) {
|
|
235
|
+
v = p; sub(/^[^:]*:[[:space:]]*/, "", v); gsub(/"/, "", v); sub(/\}[[:space:]]*$/, "", v)
|
|
236
|
+
print v; exit
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exit
|
|
240
|
+
}
|
|
241
|
+
/^[[:space:]]*"options"[[:space:]]*:[[:space:]]*\{/ { inf = 1; next }
|
|
242
|
+
inf && /^[[:space:]]*\}/ { inf = 0 }
|
|
243
|
+
inf && index($0, "\"" k "\"") {
|
|
244
|
+
v = $0; sub(/^[^:]*:[[:space:]]*/, "", v); sub(/,?[[:space:]]*$/, "", v); gsub(/"/, "", v)
|
|
245
|
+
print v; exit
|
|
246
|
+
}
|
|
247
|
+
' "$1"
|
|
248
|
+
}
|
|
249
|
+
|
|
219
250
|
# ------------------------------------------------------------- self-test -----
|
|
220
251
|
# Proves the parser actually parses. Every assertion is a real comparison against a
|
|
221
252
|
# value written to a temp file in this function — blank the awk in g_yaml_scalar and
|
package/bin/goblin-verify
CHANGED
|
@@ -536,6 +536,13 @@ check_in_02() {
|
|
|
536
536
|
[ -e "$ROOT/$p" ] || continue ;;
|
|
537
537
|
esac
|
|
538
538
|
fi
|
|
539
|
+
# W6 neutral-first: an EXPLICIT --skills no re-install removes the recorded skill files
|
|
540
|
+
# by design (the record follows the switch). Their absence on a record whose options now
|
|
541
|
+
# say skills=no is the documented opt-out, not drift - the same clause W4a wrote for the
|
|
542
|
+
# global tier, scoped to the record's own declared choice.
|
|
543
|
+
case "$p" in
|
|
544
|
+
.hermes/skills/*) [ -e "$ROOT/$p" ] || [ "$(g_installed_scalar_options "$INSTALLED" skills)" = "yes" ] || continue ;;
|
|
545
|
+
esac
|
|
539
546
|
n=$((n + 1))
|
|
540
547
|
cur=$(g_sha256_file "$ROOT/$p")
|
|
541
548
|
if [ "$cur" != "$h" ]; then
|
|
@@ -2661,6 +2668,21 @@ while IFS=$'\t' read -r id scope rule enforced_by artifact check why; do
|
|
|
2661
2668
|
only_selected "$id" || continue
|
|
2662
2669
|
|
|
2663
2670
|
part=$(row_part "$id")
|
|
2671
|
+
# W6 neutral-first: in global engine mode AU-01's subject is the PRODUCER glob, which
|
|
2672
|
+
# global mode reads from the engine dir — the row must run there even when the repo's
|
|
2673
|
+
# skills opt-out (playbooks) is set, or the W1 control below it is skip-masked. This
|
|
2674
|
+
# clause precedes the opt-out branch on purpose. SK-01/SK-02/SK-04 and AU-02..AU-04 keep
|
|
2675
|
+
# their opt-out semantics.
|
|
2676
|
+
if [ "$id" = "AU-01" ] && [ "$ENGINE_MODE" = "global" ]; then
|
|
2677
|
+
out=$(check_au_01 2>&1); rc=$?
|
|
2678
|
+
case "$rc" in
|
|
2679
|
+
0) PASS=$((PASS + 1)); emit "$id" PASS "$out" ;;
|
|
2680
|
+
1) FAIL=$((FAIL + 1)); emit "$id" FAIL "$out" ;;
|
|
2681
|
+
2) ADV=$((ADV + 1)); emit "$id" ADV "$out" ;;
|
|
2682
|
+
3) SKIP=$((SKIP + 1)); emit "$id" SKIP "$out" ;;
|
|
2683
|
+
esac
|
|
2684
|
+
continue
|
|
2685
|
+
fi
|
|
2664
2686
|
if [ -n "$part" ] && g_part_disabled "$CONFIG" "$part"; then
|
|
2665
2687
|
SKIP=$((SKIP + 1)); emit "$id" SKIP "$rule (opt-out: $part)"; continue
|
|
2666
2688
|
fi
|
package/docs/ADOPTION.md
CHANGED
|
@@ -102,13 +102,16 @@ Each step is independently useful and the later ones build on the earlier:
|
|
|
102
102
|
Then, in order:
|
|
103
103
|
|
|
104
104
|
git add -A && git commit # the install is a change like any other
|
|
105
|
-
.goblin/bin/goblin-verify #
|
|
106
|
-
|
|
105
|
+
.goblin/bin/goblin-verify # 37 passed, 1 failed - HP-05, until HANDOFF names a commit
|
|
106
|
+
gob emit --platform <p> # optional, per platform: the agent skills are an opt-in
|
|
107
107
|
|
|
108
|
-
A class-A install is **green** — `
|
|
108
|
+
A default class-A install (no agent skills) is **green** — `38 passed, 0 failed, 11 advisory,
|
|
109
|
+
33 skipped`, exit 0 — once
|
|
109
110
|
`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`).
|
|
111
|
+
the one expected red (`37 passed, 1 failed`). Both numbers are measured, not assumed
|
|
112
|
+
(`docs/CONTRACTS.md`; step 2 of `docs/GUIDE.md`). Thirty-three rows skip with a reason: the
|
|
113
|
+
five skill rows (`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks` opt-out a skills-free
|
|
114
|
+
install records, plus the not-yet rows: `HS-02` (no
|
|
112
115
|
pinned pre-change commit yet), `AU-02`/`AU-03` (no report has been filed, so there is nothing to
|
|
113
116
|
dedup and no reporter run to audit), `SC-06`/`SC-07`/`SC-08` (no dependency manifest, no lockfile,
|
|
114
117
|
no audit record), `PF-01` (no measured perf baseline), `BN-01`/`BN-02`/`BN-05` (no `src/` for a ban
|
|
@@ -160,7 +163,7 @@ The remedy is a reconciliation. The project's file stays the file of record; not
|
|
|
160
163
|
git add -A && git commit
|
|
161
164
|
.goblin/bin/goblin-verify # HP-02, HP-03, HP-05 go green
|
|
162
165
|
|
|
163
|
-
Success is the class's full green path (`
|
|
166
|
+
Success is the class's full green path (`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0 for
|
|
164
167
|
class A) with `git status --short` empty.
|
|
165
168
|
|
|
166
169
|
The edit is additive and small — measured on the model repo (§1's exemplar, 2450 lines): three
|
package/docs/CONTRACTS.md
CHANGED
|
@@ -14,7 +14,10 @@ same way the fleet's own tool reads it. Everything else is line-oriented shell.
|
|
|
14
14
|
--practice <path> the referenced standard (default: $GOBLIN_PRACTICE -> ~/projects/PROJECT-PRACTICE.md)
|
|
15
15
|
--parts <list> comma list to install; default = every part the class requires
|
|
16
16
|
--archive mark the project archive: verify requires no HANDOFF and no gates
|
|
17
|
-
--skills yes|no install .hermes/skills (default
|
|
17
|
+
--skills yes|no install agent skills under .hermes/skills (default no — the harness is
|
|
18
|
+
neutral; opt in per platform with: gob emit --platform <p>). On a repo whose
|
|
19
|
+
record already has skills installed, an OMITTED flag keeps them; an explicit
|
|
20
|
+
--skills no removes them.
|
|
18
21
|
--dry-run print the plan; write nothing
|
|
19
22
|
--upgrade re-install at the current version; report created/updated/unchanged/skipped
|
|
20
23
|
--opt-out <part> record the part in disabled: so its required checks are skipped
|
|
@@ -37,7 +40,7 @@ files whose hash changed and prints `created C · updated U · unchanged N · sk
|
|
|
37
40
|
|
|
38
41
|
| kind | recorded as | overwritten? | hash-checked? | removed by `--uninstall`? |
|
|
39
42
|
|---|---|---|---|---|
|
|
40
|
-
| installed artifact (bin, manifest, roles, skills, harness scaffold) | `files` | yes, on upgrade | yes — IN-02, SK-02 | yes |
|
|
43
|
+
| installed artifact (bin, manifest, roles, opt-in skills, harness scaffold) | `files` | yes, on upgrade | yes — IN-02, SK-02 | yes |
|
|
41
44
|
| 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
45
|
| pre-existing, left alone | `refused` | never | no — IN-04 only proves it was not taken over | no |
|
|
43
46
|
|
|
@@ -112,9 +115,9 @@ Output is one line per executed row, in manifest order, plus a summary line at t
|
|
|
112
115
|
ADV MD-02 code lane and review lane both resolve to the same family
|
|
113
116
|
SKIP HS-02 no pinned pre-change commit yet - REPLAY not provable
|
|
114
117
|
|
|
115
|
-
Those four lines are one row of each marking. The summary line of a green class-A run is:
|
|
118
|
+
Those four lines are one row of each marking. The summary line of a green default class-A run is:
|
|
116
119
|
|
|
117
|
-
|
|
120
|
+
38 passed, 0 failed, 11 advisory, 33 skipped
|
|
118
121
|
|
|
119
122
|
**Exit codes:** `0` every executed check passed (advisories and skips do not fail the run) ·
|
|
120
123
|
`1` at least one check FAILED · `2` verify could not run (not installed, a missing dependency,
|
|
@@ -135,8 +138,10 @@ settings that make a workflow a **gate** are written down.
|
|
|
135
138
|
|
|
136
139
|
### A fresh install verifies green
|
|
137
140
|
|
|
138
|
-
Measured on a fresh class-A install, committed with no
|
|
139
|
-
11 advisory,
|
|
141
|
+
Measured on a fresh DEFAULT class-A install (skills opt-in, W6 neutral-first), committed with no
|
|
142
|
+
hand edit: **`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0.** Thirty-three rows skip with
|
|
143
|
+
a reason — the same not-yet rows as before, plus the five skill rows (`SK-01`..`SK-04`,
|
|
144
|
+
`AU-04`) that skip on the `playbooks` opt-out a skills-free install records: `HS-02` — no pre-change commit
|
|
140
145
|
is pinned yet, so the REPLAY is not provable (`docs/LIMITS.md` #11) — `AU-02` and `AU-03`, which
|
|
141
146
|
have no report to audit in a repo where no reporter has run — `SC-06`, `SC-07` and `SC-08`, which
|
|
142
147
|
have no dependency manifest, no lockfile and no audit record to read yet — `PF-01`, which has
|
|
@@ -173,13 +178,20 @@ verifier is reporting FAILs.
|
|
|
173
178
|
- **Per part:** `--opt-out <part>` records the part in `disabled:`. `goblin-verify` then reports
|
|
174
179
|
the part's rows as `SKIP (opt-out)` in the summary, so the opt-out is **visible rather than
|
|
175
180
|
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
|
-
|
|
181
|
+
- **The opt-out numbers are pinned (V3-3).** A class-A install with an explicit `--skills no`
|
|
182
|
+
verifies `38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0, and
|
|
183
|
+
`tests/t-install-off-switch.sh` asserts that line: a silent drift in the opt-out path is caught
|
|
184
|
+
rather than left as a number nobody wrote down (the `--skills no` count moved from `37/0/9/11`
|
|
185
|
+
at v0.2 when the ban rows landed, **15 → 18 on 2026-09-25 (G1)** — the feature-map rows,
|
|
186
|
+
**18 → 24 on 2026-09-25 (W3)** — the judge/loop rows, and **to `38/0/11/33` at W6
|
|
187
|
+
(neutral-first)**, when this opt-out shape BECAME the default and the two lines converged:
|
|
188
|
+
the old default install measured `43/0/11/28`).
|
|
189
|
+
- **Skills, W6 neutral-first.** A default install ships no agent skills. A repo whose record has
|
|
190
|
+
`skills: yes` keeps them through every flag-less re-install and `--upgrade` (the installer
|
|
191
|
+
reads the record's choice and says so out loud); an explicit `--skills no` removes exactly the
|
|
192
|
+
recorded skill files; `--uninstall` removes everything recorded, as always.
|
|
193
|
+
`tests/t-install-off-switch.sh` walks that migration: install `--skills yes`, upgrade flag-less,
|
|
194
|
+
the skills survive byte-identical; uninstall, and they are all gone.
|
|
183
195
|
- **Whole harness:** `--uninstall` deletes the `files` list plus `.goblin/goblin.yaml`, removes
|
|
184
196
|
every directory that leaves empty (deepest first, after `installed.json` itself is gone — the
|
|
185
197
|
order that used to leave `.goblin/` and the sixteen `.hermes/skills/*` directories behind),
|
package/docs/GUIDE.md
CHANGED
|
@@ -106,18 +106,20 @@ or the plain installer this wizard drives, if you prefer the one-shot shape:
|
|
|
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
|
|
|
@@ -305,9 +307,9 @@ honest entry, and the harness treats it as one.
|
|
|
305
307
|
> **Prove it was broken first.**
|
|
306
308
|
|
|
307
309
|
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 **
|
|
310
|
+
it go green. Break it on a row this walkthrough can actually break: `IN-02` hashes the **16 files it
|
|
309
311
|
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
|
|
312
|
+
`.goblin/installed.json`; edit one of the 16 — the exercise below uses `.goblin/bans/README.md`.
|
|
311
313
|
|
|
312
314
|
# REPLAY-BEGIN (this exact block is run by tests/t-doc-guide.sh - keep the two copies identical)
|
|
313
315
|
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
@@ -393,13 +395,13 @@ the next session.
|
|
|
393
395
|
A class-A install lands on a specific shape. The scaffold ships one deliberate red — `HP-05`, the
|
|
394
396
|
`0000000` placeholder in `HANDOFF.md` (§4) — so a literal first run prints:
|
|
395
397
|
|
|
396
|
-
|
|
398
|
+
37 passed, 1 failed, 11 advisory, 33 skipped (the one FAIL is HP-05)
|
|
397
399
|
|
|
398
400
|
Name a real commit in `HANDOFF.md` and commit, and it is green:
|
|
399
401
|
|
|
400
|
-
|
|
402
|
+
38 passed, 0 failed, 11 advisory, 33 skipped (on a real project; your numbers will differ)
|
|
401
403
|
|
|
402
|
-
**
|
|
404
|
+
**Thirty-three rows skipping is correct**, and each skip prints its reason. In plain terms: the
|
|
403
405
|
harness is telling you which of its rules have nothing to read yet. It is a checklist, not a
|
|
404
406
|
scolding.
|
|
405
407
|
|
|
@@ -579,7 +581,7 @@ with *"prove it was broken first"* — it is the one practice that survives cont
|
|
|
579
581
|
# 1. try it somewhere disposable
|
|
580
582
|
mkdir -p /tmp/gs-try && cd /tmp/gs-try
|
|
581
583
|
git init -b main
|
|
582
|
-
gob install --target . --class A # expect: created
|
|
584
|
+
gob install --target . --class A # expect: created 25 (no skills — those are gob emit)
|
|
583
585
|
|
|
584
586
|
# 2. commit and check
|
|
585
587
|
git add -A && git commit -m "chore: install gobstack"
|
package/docs/LIMITS.md
CHANGED
|
@@ -111,7 +111,7 @@ deliberate trade or an unfilled gap.
|
|
|
111
111
|
drift check — `IN-02`, `SK-02`, and `HS-01`'s hash of the harness dir — reads its expected
|
|
112
112
|
hash out of that one file, and that file is the one file no check protects. Measured: append a
|
|
113
113
|
byte to `.goblin/bin/goblin-verify`, rewrite its recorded hash in `installed.json`, commit, and
|
|
114
|
-
the run is **fully GREEN** (`
|
|
114
|
+
the run is **fully GREEN** (`38 passed, 0 failed`, exit 0). One edit defeats three rows at
|
|
115
115
|
once, and it is the cheapest way to fake a green run. Doing better needs an anchor the target
|
|
116
116
|
cannot edit — a signature, or a hash held outside the repo — and goblin-stack has no such
|
|
117
117
|
trust root: the source checkout is not guaranteed to exist at verify time, and any value
|
package/package.json
CHANGED
|
@@ -9,11 +9,16 @@ Use when adopting goblin-stack in a repo, or starting one.
|
|
|
9
9
|
|
|
10
10
|
1. **Classify the project A-F.** The class selects which parts are required, optional or off;
|
|
11
11
|
it is not a stringency level. F is a desktop shell: it adds the electron bans and a host gate.
|
|
12
|
-
2. **`goblin-install --target <dir> --class <x>`**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
2. **`goblin-install --target <dir> --class <x>`** — the default install is a NEUTRAL harness:
|
|
13
|
+
no agent skills. Opt in per platform afterwards with `gob emit --platform <p>` (or vendor the
|
|
14
|
+
Hermes project tier with `--skills yes`).
|
|
15
|
+
3. **`goblin-verify`** — a default class-A install (no agent skills) verifies green:
|
|
16
|
+
`38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0, once `HANDOFF.md` names a commit that
|
|
17
|
+
exists; before that edit the
|
|
18
|
+
scaffold's `0000000` placeholder is `HP-05`'s one expected day-one red (`37 passed, 1 failed`).
|
|
19
|
+
Thirty-three rows skip with a reason, and the reason matters: the five skill rows
|
|
20
|
+
(`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks` opt-out a skills-free install records,
|
|
21
|
+
then `HS-02`
|
|
17
22
|
(no pinned pre-change commit yet, so the REPLAY is not provable), `AU-02`/`AU-03` (no report
|
|
18
23
|
has been filed in this repo), `SC-06`/`SC-07`/`SC-08` (no dependency manifest, no lockfile, no
|
|
19
24
|
audit record), `PF-01` (no perf baseline measured yet), `BN-01`/`BN-02`/`BN-05` (the ban table is
|
package/templates/AGENTS.md.tmpl
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
This repository uses **
|
|
3
|
+
This repository uses **gobstack** (class `{{CLASS}}`, installed {{DATE}}). It is a pointer,
|
|
4
4
|
not a rule dump — the rules live in one executable place, and facts beat requirements.
|
|
5
5
|
|
|
6
|
+
- **Harness entry point:** `.goblin/bin/goblin-verify` — exit 0 pass, 1 a check failed,
|
|
7
|
+
2 could not run, 3 the manifest is broken. Run it before you commit; the gate is the
|
|
8
|
+
same run.
|
|
6
9
|
- **Rules and their checks:** `.goblin/manifest/enforcement.tsv` (one row per rule; every row
|
|
7
10
|
carries a runnable check or the literal `advisory`).
|
|
8
11
|
- **Forbidden code (the ban list):** `.goblin/bin/goblin-bans` runs the bans named by `bans:` in
|
|
9
12
|
`.goblin/goblin.yaml`; each ban's mechanism lives in `.goblin/manifest/bans.tsv`. Run it before
|
|
10
13
|
you write the line, not after — a ban is a gate, and `goblin-verify` only reports it once the
|
|
11
14
|
code exists.
|
|
12
|
-
- **Verify:** `.goblin/bin/goblin-verify` — exit 0 pass, 1 a check failed, 2 could not run,
|
|
13
|
-
3 the manifest is broken.
|
|
14
|
-
- **Procedures:** the skills installed at `.hermes/skills/` (Hermes project tier — highest
|
|
15
|
-
precedence, repo-owned). Start with `goblin-mode`, which routes a request to a playbook.
|
|
16
15
|
- **Session state:** `HANDOFF.md` at the repo root. Read it before touching anything.
|
|
17
16
|
- **Project config:** `.goblin/goblin.yaml` (class, branch, gates, ratchet, runtime data,
|
|
18
17
|
replay, opt-outs). Edit it in place; the installer never overwrites it.
|
|
19
18
|
- **The referenced standard**, if configured, is named by `practice:` in `.goblin/goblin.yaml`.
|
|
20
19
|
Read it before starting work; this repo carries no copy of it.
|
|
21
20
|
|
|
21
|
+
**Agent skills are opt-in, per platform.** A default install ships none — this is a neutral
|
|
22
|
+
harness on purpose, so nothing here assumes which coding agent you use. To install the
|
|
23
|
+
procedure skills for your tool:
|
|
24
|
+
|
|
25
|
+
gob emit --platform <p>
|
|
26
|
+
|
|
27
|
+
(`--scope project` writes them inside this repo; `--scope global` writes them for your user.)
|
|
28
|
+
Run `gob emit` with no platform to see the list. If skills were installed here, they live
|
|
29
|
+
under the platform's own directory, and `gob verify` hashes them.
|
|
30
|
+
|
|
22
31
|
Two things this repo does not do: it does not choose models (a role resolves through the
|
|
23
32
|
mapping file named by `models_file:`), and it does not write outside its own tree.
|