@techgoblin/gobstack 0.4.4-beta.6 → 0.4.4-beta.7

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/bin/goblin-init CHANGED
@@ -5,10 +5,13 @@
5
5
  # [--branch <name>] [--email <addr>] [--gate <cmd>]
6
6
  # [--emit <p[,p..]>] [--scope project|global] [--yes] [--dry-run]
7
7
  #
8
- # One screen per question, answered steps collapsing into the ✔/◆/○ rail above. Every
9
- # question has a flag: with the flags set (or --yes, or a non-tty stdin) the wizard asks
10
- # nothing and runs from the flags and the defaults — non-interactive parity is structural,
11
- # not a mode. Over a pipe the ANSI redraw degrades to a numbered cascade (rustup shape).
8
+ # One screen per question, answered steps collapsing into the ✔/◆/○ rail above. On a tty
9
+ # the whole wizard frame (logotype, header, answered rows, rail) is redrawn in place from
10
+ # a saved buffer of the wizard's own lines — never by counting printed output, which the
11
+ # installer/emit engines would break. Every question has a flag: with the flags set (or
12
+ # --yes, or a non-tty stdin) the wizard asks nothing and runs from the flags and the
13
+ # defaults — non-interactive parity is structural, not a mode. Over a pipe the redraw
14
+ # degrades to a numbered cascade (rustup shape) with zero cursor motion.
12
15
  #
13
16
  # The wizard is a FRONT END over the existing engines, never a second installer:
14
17
  # .goblin/ + the payload -> bin/goblin-install (its refusal contract — an existing
@@ -47,9 +50,11 @@ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
47
50
  # nearest 16-colour slot. NO_COLOR or a piped stdout turns colour off entirely.
48
51
  TTY_OUT=0; [ -t 1 ] && TTY_OUT=1
49
52
  TTY_IN=0; [ -t 0 ] && TTY_IN=1
50
- C_ACCENT="" C_GREEN="" C_TEXT="" C_MUTED="" C_DIM="" C_RED="" C_YELLOW="" C_RESET="" C_BOLD=""
53
+ C_ACCENT="" C_GREEN="" C_TEXT="" C_MUTED="" C_DIM="" C_RED="" C_YELLOW="" C_RESET=""
54
+ # Single-accent discipline: one accent (#F54E00) + green + text + muted + dim. No bold
55
+ # is ever emitted — bold was a second emphasis voice and is gone from every screen.
51
56
  if [ "$TTY_OUT" -eq 1 ] && [ -z "${NO_COLOR:-}" ]; then
52
- C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'
57
+ C_RESET=$'\033[0m'
53
58
  case "${COLORTERM:-}" in
54
59
  truecolor|24bit)
55
60
  C_ACCENT=$'\033[38;2;245;78;0m'; C_GREEN=$'\033[38;2;74;222;128m'
@@ -64,41 +69,64 @@ if [ "$TTY_OUT" -eq 1 ] && [ -z "${NO_COLOR:-}" ]; then
64
69
  fi
65
70
 
66
71
  # ---------------------------------------------------------------- the rail ----
67
- # 6 steps; the ✔/◆/○ vocabulary. The rail is redrawn in place on a tty (cursor-up +
68
- # clear-below, no library); over a pipe each screen degrades to one numbered line.
72
+ # 6 steps; the ✔/◆/○ vocabulary. The rail is redrawn in place on a tty from a SAVED
73
+ # BUFFER of the wizard's own lines (RAIL_BUF) — never by counting the output between
74
+ # draws, because the installer/emit engines print freely and break naive accounting.
75
+ # Over a pipe nothing is saved and nothing rewinds: each screen is one numbered line.
69
76
  STEP_LABELS=("platforms" "class" "branch/email" "first gate" "emit" "done")
70
77
  STEP_VAL=("" "" "" "" "" "")
71
- RAIL_LINES=0
72
- Q_LINES=0
73
78
  CURRENT=1
74
79
 
75
- q() { # one printed line of the current screen (counted so the redraw can rewind it)
80
+ # rail_say: one line of wizard output on a tty — stored in the redraw buffer AND
81
+ # printed with a VT100 clear-line prefix (the redraw may overwrite a longer old line).
82
+ # Over a pipe this is exactly printf '%s\n' and nothing is stored.
83
+ rail_say() {
84
+ if [ "$TTY_OUT" -eq 1 ]; then
85
+ RAIL_BUF+=("$*")
86
+ printf '\033[2K%s\n' "$*"
87
+ else
88
+ printf '%s\n' "$*"
89
+ fi
90
+ }
91
+ # rail_seed: the pre-draw frame (logo + header). On a tty it only FILLS the buffer —
92
+ # rail_draw prints it; over a pipe nothing is buffered and it prints directly.
93
+ rail_seed() {
94
+ if [ "$TTY_OUT" -eq 1 ]; then
95
+ RAIL_BUF+=("$*")
96
+ else
97
+ printf '%s\n' "$*"
98
+ fi
99
+ }
100
+ # rail_note: a line that prints now but never joins the redraw buffer (engine output,
101
+ # transcripts, the verify echo): the next redraw wipes it with its clear-below.
102
+ rail_note() {
76
103
  printf '%s\n' "$*"
77
- Q_LINES=$((Q_LINES + 1))
78
104
  }
79
105
  rail_rewind() {
80
- if [ "$TTY_OUT" -eq 1 ] && [ $((RAIL_LINES + Q_LINES)) -gt 0 ]; then
81
- printf '\033[%dA\033[J' "$((RAIL_LINES + Q_LINES))"
106
+ if [ "$TTY_OUT" -eq 1 ]; then
107
+ printf '\033[%dA\033[J' "${#RAIL_BUF[@]}"
82
108
  fi
83
- RAIL_LINES=0; Q_LINES=0
84
109
  }
85
- rail_print() { # the summary column: ✔ answered (with its value) · ◆ asking · ○ pending
86
- local i glyph col label
110
+ rail_draw() { # reprint the saved buffer, then the live rail row for $CURRENT
111
+ local i line glyph col label
112
+ if [ "$TTY_OUT" -eq 0 ]; then return 0; fi
113
+ for line in "${RAIL_BUF[@]}"; do
114
+ printf '\033[2K%s\n' "$line"
115
+ done
87
116
  for i in 1 2 3 4 5 6; do
88
117
  label="${STEP_LABELS[$((i - 1))]}"
89
118
  if [ "$i" -lt "$CURRENT" ]; then
90
- printf ' %s✔%s %d. %-13s%s%s%s\n' "$C_GREEN" "$C_RESET" "$i" "$label" "$C_TEXT" "${STEP_VAL[$((i - 1))]}" "$C_RESET"
119
+ glyph="✔"; col="$C_GREEN"
91
120
  elif [ "$i" -eq "$CURRENT" ]; then
92
- printf ' %s◆%s %d. %-13s' "$C_ACCENT" "$C_RESET" "$i" "$label"
93
- RAIL_LINES=$((RAIL_LINES + 1))
94
- return 0 # the asking row is printed by the screen itself, after its question
121
+ glyph="◆"; col="$C_ACCENT"
95
122
  else
96
- printf ' %s○%s %d. %-13s\n' "$C_DIM" "$C_RESET" "$i" "$label"
123
+ glyph="○"; col="$C_DIM"
97
124
  fi
98
- RAIL_LINES=$((RAIL_LINES + 1))
125
+ printf '\033[2K %s%s %d. %-13s%s%s%s\n' "${col}${glyph}" "$C_RESET" "$i" "$label" "$C_TEXT" "${STEP_VAL[$((i - 1))]}" "$C_RESET"
99
126
  done
100
127
  }
101
- cascade() { # the pipe-degraded shape: one numbered line per screen
128
+ rail_reset() { RAIL_BUF=(); }
129
+ cascade() { # the pipe-degraded shape: one numbered line per screen (zero cursor motion)
102
130
  printf 'gob init [%d/6] %s\n' "$CURRENT" "$1"
103
131
  }
104
132
 
@@ -108,7 +136,7 @@ ask_value() {
108
136
  if [ "$TTY_IN" -eq 0 ]; then REPLY="$dflt"; return 0; fi
109
137
  printf ' %s%s%s %s─ %s' "$C_TEXT" "$label" "$C_RESET" "$C_DIM" "$dflt"
110
138
  read -r REPLY
111
- printf '\n'; Q_LINES=$((Q_LINES + 1))
139
+ rail_say ""
112
140
  [ -n "$REPLY" ] || REPLY="$dflt"
113
141
  return 0
114
142
  }
@@ -146,18 +174,25 @@ TARGET=$(g_expand_tilde "$TARGET")
146
174
  TARGET=$(cd "$TARGET" && pwd)
147
175
 
148
176
  # ------------------------------------------------------------ screen 1: welcome
149
- printf '%s\n' "${C_ACCENT}▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █${C_RESET}"
150
- printf '%s\n' "${C_ACCENT}█ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █${C_RESET}"
151
- printf '%s\n' "${C_ACCENT}█ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██${C_RESET}"
152
- printf '%s\n' "${C_ACCENT}▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █${C_RESET}"
153
- printf '%s\n' "${C_DIM} discipline around whatever executes${C_RESET}"
154
- printf '\n'
155
- printf '%s\n' "${C_TEXT} gob init${C_RESET} ${C_MUTED}— first-run setup (gobstack v$VERSION)${C_RESET}"
156
- printf '%s\n' " ${C_MUTED}6 steps · every step has a flag · nothing is written until the plan is confirmed${C_RESET}"
157
- printf '\n'
177
+ # The logotype and the header ride the redraw buffer (rail_say): every later screen
178
+ # redraws logo + header + rail as one stable frame. Over a pipe rail_say is plain
179
+ # printf and nothing is buffered — the cascade keeps zero cursor motion.
158
180
  CURRENT=1
159
- if [ "$TTY_OUT" -eq 1 ]; then rail_print
160
- else cascade "welcome: gobstack v$VERSION — the guided first run"; fi
181
+ rail_reset
182
+ rail_seed "${C_ACCENT}▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █${C_RESET}"
183
+ rail_seed "${C_ACCENT}█ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █${C_RESET}"
184
+ rail_seed "${C_ACCENT}█ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██${C_RESET}"
185
+ rail_seed "${C_ACCENT}▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █${C_RESET}"
186
+ rail_seed " ${C_MUTED}discipline around whatever executes${C_RESET}"
187
+ rail_seed ""
188
+ rail_seed "${C_TEXT} gob init${C_RESET} ${C_MUTED}— first-run setup (gobstack v$VERSION)${C_RESET}"
189
+ rail_seed " ${C_MUTED}6 steps · every step has a flag · nothing is written until the plan is confirmed${C_RESET}"
190
+ rail_seed ""
191
+ if [ "$TTY_OUT" -eq 1 ]; then
192
+ rail_draw
193
+ else
194
+ cascade "welcome: gobstack v$VERSION — the guided first run"
195
+ fi
161
196
 
162
197
  # Platform enum assembled from fragments (the MD_SLUG precedent): a literal tool name in
163
198
  # bin/ trips MD-01's model-name pattern, and run-tests.sh greps this directory.
@@ -170,7 +205,7 @@ PLATFORMS="$PLA hermes copilot cursor opencode codex $GEM"
170
205
  # ---------------------------------------------------------- screen 2: detect --
171
206
  CURRENT=2
172
207
  rail_rewind
173
- if [ "$TTY_OUT" -eq 1 ]; then rail_print; fi
208
+ if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
174
209
  DET_ROWS=""
175
210
  DET_N=0
176
211
  for p in $PLATFORMS; do
@@ -194,19 +229,17 @@ for p in $PLATFORMS; do
194
229
  DET_N=$((DET_N + 1))
195
230
  fi
196
231
  done
197
- STEP_VAL[0]=" $C_DIM$DET_N detected$C_RESET"
232
+ STEP_VAL[0]="$C_DIM$DET_N detected$C_RESET"
198
233
  if [ "$TTY_OUT" -eq 1 ]; then
199
- printf ' %s✔%s %d. %-13s%s%s%s\n' "$C_GREEN" "$C_RESET" 2 "platforms" "$C_TEXT" "$DET_N detected" "$C_RESET"
200
- RAIL_LINES=$((RAIL_LINES + 1))
234
+ rail_say ""
235
+ rail_say " ✔ 1. platforms $C_TEXT$DET_N detected$C_RESET"
201
236
  if [ "$DET_N" -eq 0 ]; then
202
- printf ' %sno platform detected here — the wizard will still install the harness; emit later with gob emit%s\n' "$C_YELLOW" "$C_RESET"
203
- RAIL_LINES=$((RAIL_LINES + 1))
237
+ rail_say " ${C_YELLOW}no platform detected here — the wizard will still install the harness; emit later with gob emit$C_RESET"
204
238
  else
205
239
  while IFS=$'\t' read -r p proj glob hit; do
206
240
  [ -n "$p" ] || continue
207
241
  case "$glob" in "~"*) glob="${glob/\~/$HOME}" ;; esac
208
- printf ' %s✔%s %-9s %s %s(%s)%s\n' "$C_GREEN" "$C_RESET" "$p" "$glob" "$C_DIM" "$hit" "$C_RESET"
209
- RAIL_LINES=$((RAIL_LINES + 1))
242
+ rail_say " $C_GREEN✔$C_RESET $C_TEXT$p$C_RESET $glob $C_DIM($hit)$C_RESET"
210
243
  done <<< "$DET_ROWS"
211
244
  fi
212
245
  else
@@ -216,23 +249,23 @@ fi
216
249
  # ------------------------------------------------------------ screen 3: class -
217
250
  CURRENT=3
218
251
  rail_rewind
219
- if [ "$TTY_OUT" -eq 1 ]; then rail_print; fi
252
+ if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
220
253
  CLASS_DEFAULT=1
221
254
  if [ -z "$CLASS" ]; then
222
255
  if [ "$TTY_IN" -eq 1 ]; then
223
- q ""
224
- q " ${C_MUTED}what kind of work does this repo do?$C_RESET"
225
- q " $C_TEXT 1 ▸ app shipped features, PRs, review gates ${C_DIM}(class A)$C_RESET"
226
- q " $C_TEXT 2 service backend jobs, config, unattended runs ${C_DIM}(class B)$C_RESET"
227
- q " $C_TEXT 3 game playable builds, perf budgets ${C_DIM}(class C)$C_RESET"
228
- q " $C_TEXT 4 research specs, replays, reference corpora ${C_DIM}(class D)$C_RESET"
229
- q " $C_TEXT 5 agent fleets, loops, unattended automation ${C_DIM}(class E)$C_RESET"
230
- q " $C_TEXT 6 desktop the shell you live in, perf budget ${C_DIM}(class F)$C_RESET"
231
- q " ${C_MUTED}↑ number + Enter · Enter = app · the class letter works too (A-F)$C_RESET"
256
+ rail_say ""
257
+ rail_say " ${C_MUTED}what kind of work does this repo do?$C_RESET"
258
+ rail_say " $C_TEXT 1 ▸ app shipped features, PRs, review gates ${C_DIM}(class A)$C_RESET"
259
+ rail_say " $C_TEXT 2 service backend jobs, config, unattended runs ${C_DIM}(class B)$C_RESET"
260
+ rail_say " $C_TEXT 3 game playable builds, perf budgets ${C_DIM}(class C)$C_RESET"
261
+ rail_say " $C_TEXT 4 research specs, replays, reference corpora ${C_DIM}(class D)$C_RESET"
262
+ rail_say " $C_TEXT 5 agent fleets, loops, unattended automation ${C_DIM}(class E)$C_RESET"
263
+ rail_say " $C_TEXT 6 desktop the shell you live in, perf budget ${C_DIM}(class F)$C_RESET"
264
+ rail_say " ${C_MUTED}↑ number + Enter · Enter = app · the class letter works too (A-F)$C_RESET"
232
265
  printf ' '
233
266
  read -r REPLY_CLASS
234
267
  ASKED_ANY=1
235
- printf '\n'; Q_LINES=$((Q_LINES + 1))
268
+ rail_say ""
236
269
  else
237
270
  REPLY_CLASS="1"
238
271
  cascade "class: app (default; --class app|service|game|research|agent|desktop or A-F)"
@@ -258,10 +291,9 @@ case "$CLASS" in
258
291
  exit 2 ;;
259
292
  esac
260
293
  CLASS_NAME=$(ls "$SRC"/presets/"$CLASS"-*.yaml 2>/dev/null | head -n 1)
261
- STEP_VAL[1]=" $C_TEXT$CLASS ($([ -n "$CLASS_NAME" ] && basename "$CLASS_NAME" .yaml | cut -d- -f2-))$C_RESET"
294
+ STEP_VAL[1]="$C_TEXT$CLASS ($([ -n "$CLASS_NAME" ] && basename "$CLASS_NAME" .yaml | cut -d- -f2-))$C_RESET"
262
295
  if [ "$TTY_OUT" -eq 1 ]; then
263
- printf ' %s✔%s %d. %-13s%s%s%s\n' "$C_GREEN" "$C_RESET" 3 "class" "$C_TEXT" "$CLASS$([ -n "$CLASS_NAME" ] && printf ' — %s' "$(basename "$CLASS_NAME" .yaml | cut -d- -f2-)")$C_RESET"
264
- RAIL_LINES=$((RAIL_LINES + 1))
296
+ rail_say " ✔ $((CURRENT - 1)). class $C_TEXT$CLASS$([ -n "$CLASS_NAME" ] && printf ' — %s' "$(basename "$CLASS_NAME" .yaml | cut -d- -f2-)")$C_RESET"
265
297
  else
266
298
  cascade "class: $CLASS"
267
299
  fi
@@ -269,15 +301,15 @@ fi
269
301
  # ----------------------------------------------------- screen 4: branch/email --
270
302
  CURRENT=4
271
303
  rail_rewind
272
- if [ "$TTY_OUT" -eq 1 ]; then rail_print; fi
304
+ if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
273
305
  DEF_BRANCH=$( cd "$TARGET" && git rev-parse --abbrev-ref HEAD 2>/dev/null )
274
306
  case "$DEF_BRANCH" in ""|HEAD) DEF_BRANCH=main ;; esac
275
307
  DEF_EMAIL=$( cd "$TARGET" && git config user.email 2>/dev/null )
276
308
  [ -n "$DEF_EMAIL" ] || DEF_EMAIL="you@example.com"
277
309
  if [ -z "$BRANCH" ] || [ -z "$EMAIL" ]; then
278
310
  if [ "$TTY_IN" -eq 1 ]; then
279
- q ""
280
- q " ${C_MUTED}the forge contract — the identity this repo commits with (CM-01)$C_RESET"
311
+ rail_say ""
312
+ rail_say " ${C_MUTED}the forge contract — the identity this repo commits with (CM-01)$C_RESET"
281
313
  ask_value "default branch" "$DEF_BRANCH"; BRANCH="${BRANCH:-$REPLY}"
282
314
  ask_value "owner email" "$DEF_EMAIL"; EMAIL="${EMAIL:-$REPLY}"
283
315
  else
@@ -288,29 +320,27 @@ if [ -z "$BRANCH" ] || [ -z "$EMAIL" ]; then
288
320
  fi
289
321
  BRANCH="${BRANCH:-$DEF_BRANCH}"
290
322
  EMAIL="${EMAIL:-$DEF_EMAIL}"
291
- STEP_VAL[2]=" $C_TEXT$BRANCH · $EMAIL$C_RESET"
323
+ STEP_VAL[2]="$C_TEXT$BRANCH · $EMAIL$C_RESET"
292
324
  if [ "$TTY_OUT" -eq 1 ]; then
293
- printf ' %s✔%s %d. %-13s%s%s · %s%s\n' "$C_GREEN" "$C_RESET" 4 "branch/email" "$C_TEXT" "$BRANCH" "$EMAIL" "$C_RESET"
294
- RAIL_LINES=$((RAIL_LINES + 1))
325
+ rail_say " ✔ $((CURRENT - 1)). branch/email $C_TEXT$BRANCH · $EMAIL$C_RESET"
295
326
  fi
296
327
 
297
328
  # -------------------------------------------------------- screen 5: first gate -
298
329
  CURRENT=5
299
330
  rail_rewind
300
- if [ "$TTY_OUT" -eq 1 ]; then rail_print; fi
331
+ if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
301
332
  GATE_DEFAULT="bash tests/run-tests.sh"
302
333
  if [ -z "$GATE" ]; then
303
334
  if [ "$TTY_IN" -eq 1 ]; then
304
- q ""
305
- q " ${C_MUTED}one real command that must stay green — suggested for a bash suite:$C_RESET"
335
+ rail_say ""
336
+ rail_say " ${C_MUTED}one real command that must stay green — suggested for a bash suite:$C_RESET"
306
337
  if [ -f "$TARGET/tests/run-tests.sh" ]; then
307
- q " $C_DIM$GATE_DEFAULT$C_RESET"
338
+ rail_say " $C_DIM$GATE_DEFAULT$C_RESET"
308
339
  ask_value "gate command" "$GATE_DEFAULT"; GATE="$REPLY"
309
340
  else
310
341
  ask_value "gate command" "true"; GATE="$REPLY"
311
342
  if [ "$GATE" = "true" ]; then
312
- q " ${C_YELLOW}no tests/run-tests.sh found — a placeholder gate is allowed but it proves nothing; replace it in .goblin/goblin.yaml$C_RESET"
313
- Q_LINES=$((Q_LINES + 1))
343
+ rail_say " ${C_YELLOW}no tests/run-tests.sh found — a placeholder gate is allowed but it proves nothing; replace it in .goblin/goblin.yaml$C_RESET"
314
344
  fi
315
345
  fi
316
346
  else
@@ -318,16 +348,15 @@ if [ -z "$GATE" ]; then
318
348
  cascade "gate: $GATE (default)"
319
349
  fi
320
350
  fi
321
- STEP_VAL[3]=" $C_TEXT$GATE$C_RESET"
351
+ STEP_VAL[3]="$C_TEXT$GATE$C_RESET"
322
352
  if [ "$TTY_OUT" -eq 1 ]; then
323
- printf ' %s✔%s %d. %-13s%s%s%s\n' "$C_GREEN" "$C_RESET" 5 "first gate" "$C_TEXT" "$GATE" "$C_RESET"
324
- RAIL_LINES=$((RAIL_LINES + 1))
353
+ rail_say " ✔ $((CURRENT - 1)). first gate $C_TEXT$GATE$C_RESET"
325
354
  fi
326
355
 
327
356
  # --------------------------------------------------------- screen 6: the plan --
328
357
  CURRENT=6
329
358
  rail_rewind
330
- if [ "$TTY_OUT" -eq 1 ]; then rail_print; fi
359
+ if [ "$TTY_OUT" -eq 1 ]; then rail_draw; fi
331
360
 
332
361
  # The emit selection: pre-tick the DETECTED platforms (in doctor's enum order).
333
362
  EMIT_SELECTED=""
@@ -355,19 +384,19 @@ fi
355
384
  EMIT_N=$(printf '%s' "$EMIT_SELECTED" | awk 'NF' | wc -l | tr -d '[:space:]')
356
385
 
357
386
  if [ "$TTY_IN" -eq 1 ] && [ -z "$EMIT" ] && [ "$YES" -eq 0 ]; then
358
- q ""
359
- q " ${C_MUTED}emit now — the skills go into:$C_RESET"
387
+ rail_say ""
388
+ rail_say " ${C_MUTED}emit now — the skills go into:$C_RESET"
360
389
  for p in $PLATFORMS; do
361
390
  hit=""
362
391
  while IFS=$' ' read -r dp _a _b _c; do [ "$dp" = "$p" ] && hit=1; done <<< "$DET_ROWS"
363
392
  [ -n "$hit" ] || continue
364
- q " ${C_GREEN}✔$C_RESET $C_TEXT$p$C_RESET"
393
+ rail_say " ${C_GREEN}✔$C_RESET $C_TEXT$p$C_RESET"
365
394
  done
366
- q " ${C_DIM}Enter = these; or type platform names to override$C_RESET"
395
+ rail_say " ${C_MUTED}Enter = these; or type platform names to override$C_RESET"
367
396
  printf ' '
368
397
  read -r REPLY_EMIT
369
398
  ASKED_ANY=1
370
- printf '\n'; Q_LINES=$((Q_LINES + 1))
399
+ rail_say ""
371
400
  if [ -n "$REPLY_EMIT" ]; then
372
401
  EMIT_SELECTED=""
373
402
  for w in $REPLY_EMIT; do
@@ -377,16 +406,18 @@ if [ "$TTY_IN" -eq 1 ] && [ -z "$EMIT" ] && [ "$YES" -eq 0 ]; then
377
406
  done
378
407
  fi
379
408
  fi
380
- STEP_VAL[4]=" $C_TEXT$EMIT_N platform(s) · scope $SCOPE$C_RESET"
409
+ STEP_VAL[4]="$C_TEXT$EMIT_N platform(s) · scope $SCOPE$C_RESET"
381
410
  if [ "$TTY_OUT" -eq 1 ]; then
382
- printf ' %s✔%s %d. %-13s%s%d platform(s) · scope %s%s\n' "$C_GREEN" "$C_RESET" 6 "emit" "$C_TEXT" "$EMIT_N" "$SCOPE" "$C_RESET"
383
- RAIL_LINES=$((RAIL_LINES + 1))
411
+ rail_say " ✔ $((CURRENT - 1)). emit $C_TEXT$EMIT_N platform(s) · scope $SCOPE$C_RESET"
384
412
  fi
385
413
 
386
414
  # ---- the plan box: the wizard never writes until this has been on the screen ----
415
+ # Spacing: one blank line separates the rail from the box (the spacing pass); the box
416
+ # carries two-space inner padding on every row so the columns align across rows.
387
417
  BOX=""
388
418
  add() { BOX="$BOX$1
389
419
  "; }
420
+ add ""
390
421
  add " ${C_DIM}╭──────────────────────────────────────────────────────╮${C_RESET}"
391
422
  add " ${C_DIM}│${C_RESET} ${C_GREEN}✔${C_RESET} ${C_TEXT}install${C_RESET} $C_DIM.goblin/ + the payload into$C_RESET $C_TEXT$TARGET$C_RESET"
392
423
  add " ${C_DIM}│${C_RESET} ${C_GREEN}✔${C_RESET} ${C_TEXT}class${C_RESET} $C_TEXT$CLASS$C_RESET ${C_DIM}· branch $C_TEXT$BRANCH$C_RESET ${C_DIM}· email$C_RESET $C_TEXT$EMAIL$C_RESET"
@@ -400,10 +431,11 @@ if [ "$DRYRUN" -eq 1 ]; then
400
431
  fi
401
432
  # A question the wizard genuinely asked (a screen that waited on read) buys the confirm;
402
433
  # flag/non-tty runs already stated their answer as a flag — proceeding is the contract.
434
+ # Single-accent: the confirm prompt is muted + dim, never bold, never a second accent.
403
435
  if [ "$YES" -eq 0 ] && [ "${ASKED_ANY:-0}" -eq 1 ]; then
404
436
  printf '%s\n' "$BOX"
405
437
  if [ "$TTY_IN" -eq 1 ]; then
406
- printf ' %sproceed?%s %s[Enter = yes]%s ' "$C_BOLD" "$C_RESET" "$C_DIM" "$C_RESET"
438
+ printf ' %sproceed?%s %s[Enter = yes]%s ' "$C_MUTED" "$C_RESET" "$C_DIM" "$C_RESET"
407
439
  read -r REPLY_GO
408
440
  case "$REPLY_GO" in n*|N*) g_info "aborted — nothing was written"; exit 0 ;; esac
409
441
  else
@@ -416,7 +448,14 @@ fi
416
448
  # carry: the declared identity (branch/owner_email) and the first gate, patched into
417
449
  # .goblin/goblin.yaml right after install renders it. The patch is fail-closed — the
418
450
  # gate must read back through the engine's own parser or the run stops here.
419
- if [ "$TTY_OUT" -eq 0 ]; then printf 'gob init [run] goblin-install --target %s --class %s\n' "$TARGET" "$CLASS"; fi
451
+ # rail_note, not rail_say: engine output below is never redrawn over, and the wizard
452
+ # never rewinds past its own buffer by counting lines it did not print.
453
+ if [ "$TTY_OUT" -eq 1 ]; then
454
+ rail_note ""
455
+ rail_note " ${C_MUTED}running: goblin-install --target $TARGET --class $CLASS${C_RESET}"
456
+ else
457
+ printf 'gob init [run] goblin-install --target %s --class %s\n' "$TARGET" "$CLASS"
458
+ fi
420
459
  bash "$SRC/bin/goblin-install" --target "$TARGET" --class "$CLASS" --yes
421
460
  EXIT_INSTALL=$?
422
461
  if [ "$EXIT_INSTALL" -ne 0 ]; then
@@ -453,7 +492,11 @@ fi
453
492
 
454
493
  EMITTED=0
455
494
  for p in $EMIT_SELECTED; do
456
- [ "$TTY_OUT" -eq 0 ] && printf 'gob init [run] goblin-emit --platform %s --scope %s\n' "$p" "$SCOPE"
495
+ if [ "$TTY_OUT" -eq 0 ]; then
496
+ printf 'gob init [run] goblin-emit --platform %s --scope %s\n' "$p" "$SCOPE"
497
+ else
498
+ rail_note " ${C_MUTED}running: goblin-emit --platform $p --scope $SCOPE${C_RESET}"
499
+ fi
457
500
  bash "$SRC/bin/goblin-emit" --platform "$p" --scope "$SCOPE" --target "$TARGET"
458
501
  EXIT_EMIT=$?
459
502
  if [ "$EXIT_EMIT" -ne 0 ]; then
@@ -464,9 +507,9 @@ for p in $EMIT_SELECTED; do
464
507
  done
465
508
 
466
509
  # --------------------------------------------------- screen 6b: done + verify --
467
- if [ "$TTY_OUT" -eq 1 ]; then
468
- printf '\n'
469
- else
510
+ # Spacing pass: exactly one blank line before each done-screen section; the engine's
511
+ # verify output and this screen's own summary stay visually separated.
512
+ if [ "$TTY_OUT" -eq 0 ]; then
470
513
  printf 'gob init [verify] .goblin/bin/goblin-verify\n'
471
514
  fi
472
515
  if [ "$DRYRUN" -eq 0 ]; then
@@ -494,12 +537,12 @@ if [ "$DRYRUN" -eq 0 ]; then
494
537
  else
495
538
  printf ' %sgob verify: %s passed, %s failed, %s skipped · exit %s%s\n' "$C_RED" "$PASSED" "$FAILED" "$SKIPPED" "$VERIFY_RC" "$C_RESET"
496
539
  fi
497
- printf '\n'
498
540
  else
499
541
  g_info "gob init [verify] exit $VERIFY_RC: $PASSED passed, $FAILED failed, $ADVISORY advisory, $SKIPPED skipped"
500
542
  fi
501
543
 
502
- printf ' %snext:%s\n' "$C_BOLD" "$C_RESET"
544
+ printf '\n'
545
+ printf ' %snext:%s\n' "$C_MUTED" "$C_RESET"
503
546
  printf ' 1. %sthe %s skipped checks ARE your checklist — gob verify names each one%s\n' "$C_TEXT" "$SKIPPED" "$C_RESET"
504
547
  printf ' 2. %sreplace the placeholder gate in .goblin/goblin.yaml with your real commands (P8 step 3)%s\n' "$C_TEXT" "$C_RESET"
505
548
  printf ' 3. %snot emitted here: gob emit --platform <p> --scope global%s\n' "$C_TEXT" "$C_RESET"
@@ -0,0 +1,334 @@
1
+ Script started on 2026-10-02 00:53:48+07:00 [COMMAND="bash /home/harvey/projects/goblin-stack/bin/goblin-init --target /tmp/ptyfinal/repo --yes" <not executed on terminal>]
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
11
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
12
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
13
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
14
+  discipline around whatever executes
15
+ 
16
+  gob init — first-run setup (gobstack v0.4.4)
17
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
18
+ 
19
+  ◆ 1. platforms 
20
+  ○ 2. class 
21
+  ○ 3. branch/email 
22
+  ○ 4. first gate 
23
+  ○ 5. emit 
24
+  ○ 6. done 
25
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
26
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
27
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
28
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
29
+  discipline around whatever executes
30
+ 
31
+  gob init — first-run setup (gobstack v0.4.4)
32
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
33
+ 
34
+  ✔ 1. platforms 
35
+  ◆ 2. class 
36
+  ○ 3. branch/email 
37
+  ○ 4. first gate 
38
+  ○ 5. emit 
39
+  ○ 6. done 
40
+ 
41
+  ✔ 1. platforms 0 detected
42
+  no platform detected here — the wizard will still install the harness; emit later with gob emit
43
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
44
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
45
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
46
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
47
+  discipline around whatever executes
48
+ 
49
+  gob init — first-run setup (gobstack v0.4.4)
50
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
51
+ 
52
+ 
53
+  ✔ 1. platforms 0 detected
54
+  no platform detected here — the wizard will still install the harness; emit later with gob emit
55
+  ✔ 1. platforms 0 detected
56
+  ✔ 2. class 
57
+  ◆ 3. branch/email 
58
+  ○ 4. first gate 
59
+  ○ 5. emit 
60
+  ○ 6. done 
61
+ 
62
+  what kind of work does this repo do?
63
+   1 ▸ app shipped features, PRs, review gates (class A)
64
+   2 service backend jobs, config, unattended runs (class B)
65
+   3 game playable builds, perf budgets (class C)
66
+   4 research specs, replays, reference corpora (class D)
67
+   5 agent fleets, loops, unattended automation (class E)
68
+   6 desktop the shell you live in, perf budget (class F)
69
+  ↑ number + Enter · Enter = app · the class letter works too (A-F)
70
+ 
71
+  ✔ 2. class A — shipped-software
72
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
73
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
74
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
75
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
76
+  discipline around whatever executes
77
+ 
78
+  gob init — first-run setup (gobstack v0.4.4)
79
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
80
+ 
81
+ 
82
+  ✔ 1. platforms 0 detected
83
+  no platform detected here — the wizard will still install the harness; emit later with gob emit
84
+ 
85
+  what kind of work does this repo do?
86
+   1 ▸ app shipped features, PRs, review gates (class A)
87
+   2 service backend jobs, config, unattended runs (class B)
88
+   3 game playable builds, perf budgets (class C)
89
+   4 research specs, replays, reference corpora (class D)
90
+   5 agent fleets, loops, unattended automation (class E)
91
+   6 desktop the shell you live in, perf budget (class F)
92
+  ↑ number + Enter · Enter = app · the class letter works too (A-F)
93
+ 
94
+  ✔ 2. class A — shipped-software
95
+  ✔ 1. platforms 0 detected
96
+  ✔ 2. class A (shipped-software)
97
+  ✔ 3. branch/email 
98
+  ◆ 4. first gate 
99
+  ○ 5. emit 
100
+  ○ 6. done 
101
+ 
102
+  the forge contract — the identity this repo commits with (CM-01)
103
+ default branch ─ main
104
+ owner email ─ t@t.co
105
+  ✔ 3. branch/email main · t@t.co
106
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
107
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
108
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
109
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
110
+  discipline around whatever executes
111
+ 
112
+  gob init — first-run setup (gobstack v0.4.4)
113
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
114
+ 
115
+ 
116
+  ✔ 1. platforms 0 detected
117
+  no platform detected here — the wizard will still install the harness; emit later with gob emit
118
+ 
119
+  what kind of work does this repo do?
120
+   1 ▸ app shipped features, PRs, review gates (class A)
121
+   2 service backend jobs, config, unattended runs (class B)
122
+   3 game playable builds, perf budgets (class C)
123
+   4 research specs, replays, reference corpora (class D)
124
+   5 agent fleets, loops, unattended automation (class E)
125
+   6 desktop the shell you live in, perf budget (class F)
126
+  ↑ number + Enter · Enter = app · the class letter works too (A-F)
127
+ 
128
+  ✔ 2. class A — shipped-software
129
+ 
130
+  the forge contract — the identity this repo commits with (CM-01)
131
+ 
132
+ 
133
+  ✔ 3. branch/email main · t@t.co
134
+  ✔ 1. platforms 0 detected
135
+  ✔ 2. class A (shipped-software)
136
+  ✔ 3. branch/email main · t@t.co
137
+  ✔ 4. first gate 
138
+  ◆ 5. emit 
139
+  ○ 6. done 
140
+ 
141
+  one real command that must stay green — suggested for a bash suite:
142
+  bash tests/run-tests.sh
143
+ gate command ─ bash tests/run-tests.sh
144
+  ✔ 4. first gate bash tests/run-tests.sh
145
+ ▄███▄ ▄██▄ ██▄ █ ▄█▄ █ █ ▄██▀ █████ ▄███▄ ▄███ █ █
146
+ █ █ █ █ █▄▀█ █ █ █▄ █ ▀██▄ █ █ █ █ █ █
147
+ █ ▄██ █ █ █▄▀█ █ █ █ ▄█ ▄█▀ █ █████ █ ██
148
+ ▀██▀▀ ▀██▀ ██▀ ▀▀▀▀ ▀█▀ █ █ ██▀▀ ▀ █ █ ▀██▀ █ █
149
+  discipline around whatever executes
150
+ 
151
+  gob init — first-run setup (gobstack v0.4.4)
152
+  6 steps · every step has a flag · nothing is written until the plan is confirmed
153
+ 
154
+ 
155
+  ✔ 1. platforms 0 detected
156
+  no platform detected here — the wizard will still install the harness; emit later with gob emit
157
+ 
158
+  what kind of work does this repo do?
159
+   1 ▸ app shipped features, PRs, review gates (class A)
160
+   2 service backend jobs, config, unattended runs (class B)
161
+   3 game playable builds, perf budgets (class C)
162
+   4 research specs, replays, reference corpora (class D)
163
+   5 agent fleets, loops, unattended automation (class E)
164
+   6 desktop the shell you live in, perf budget (class F)
165
+  ↑ number + Enter · Enter = app · the class letter works too (A-F)
166
+ 
167
+  ✔ 2. class A — shipped-software
168
+ 
169
+  the forge contract — the identity this repo commits with (CM-01)
170
+ 
171
+ 
172
+  ✔ 3. branch/email main · t@t.co
173
+ 
174
+  one real command that must stay green — suggested for a bash suite:
175
+  bash tests/run-tests.sh
176
+ 
177
+  ✔ 4. first gate bash tests/run-tests.sh
178
+  ✔ 1. platforms 0 detected
179
+  ✔ 2. class A (shipped-software)
180
+  ✔ 3. branch/email main · t@t.co
181
+  ✔ 4. first gate bash tests/run-tests.sh
182
+  ✔ 5. emit 
183
+  ◆ 6. done 
184
+  ✔ 5. emit 0 platform(s) · scope project
185
+
186
+ running: goblin-install --target /tmp/ptyfinal/repo --class A
187
+ created 50 · updated 0 · unchanged 0 · skipped 0
188
+
189
+ next:
190
+ 1. cd /tmp/ptyfinal/repo && git add -A && git commit # the install is a change like any other
191
+ 2. .goblin/bin/goblin-verify # or add .goblin/bin to PATH
192
+ 3. edit .goblin/goblin.yaml: replace the default gate with your real commands (P8 step 3)
193
+ 4. hermes skills trust /tmp/ptyfinal/repo # one-time, so the project-tier skills load
194
+
195
+ automations (optional; neither writes outside this repo, and A-02 has no agent in it):
196
+ bash /tmp/ptyfinal/repo/.goblin/automations/drift-audit.sh --dry-run # read the record first
197
+ cp /tmp/ptyfinal/repo/.goblin/automations/drift-audit.sh ~/.hermes/scripts/
198
+ hermes cron create "0 9 * * 1" --name "Drift audit - weekly" --no-agent \
199
+ --script drift-audit.sh --deliver <channel>
200
+ P13/P14 in docs/FLOWS.md; the whole model in automations/README.md
201
+ PASS IN-01 the install record exists and names its version
202
+ PASS IN-02 41 installed files hashed
203
+ PASS IN-03 the manifest is complete: every rule carries a check or is advisory, inside the enum
204
+ PASS IN-04 0 pre-existing file(s) left untouched
205
+ PASS HP-01 (test -f HANDOFF.md)
206
+ PASS HP-02 (for h in 'START HERE' 'STATE|STATUS' 'GATES?' 'NEXT STEPS|NEXT' 'NOT VERIFIED|UNVERIFIED|NOT PROVEN|UNPROVEN|PENDING[^.]*DEVICE TEST'; do grep -qiE "^#{2,3}[[:space:]]+[^[:alpha:]]*($h)\b" HANDOFF.md || { echo "missing section: $h"; exit 1; }; done)
207
+ PASS HP-03 1 gate line(s) inside the Gates section, each with a measured date (anchored on the declared gate name(s): commit todo_ceiling)
208
+ ADV HP-04 A stale sentence is corrected in place with a dated parenthetical, never deleted. (advisory)
209
+ PASS HP-05 names 3c96403 (ancestor of HEAD 3c96403)
210
+ PASS SP-01 (ls ./*-SPEC.md >/dev/null 2>&1)
211
+ FAIL SP-02 (test -z "$(git ls-files --others --exclude-standard -- '*-SPEC.md')" -> exit 1)
212
+ PASS SP-03 (awk '/^[[:space:]]*[-*][[:space:]]/ && /AC[0-9]*:/ && !/`|==|===|exit|<|>/ {print; bad=1} END{exit bad}' ./*-SPEC.md)
213
+ PASS GT-01 2 declared gate(s), each with a cmd
214
+ PASS GT-02 commit=0 todo_ceiling=0 · client_js_bytes 0 (ceiling 0) · measured 2026-10-02
215
+ PASS GT-03 (test -f .goblin/last-gate-line && [ .goblin/last-gate-line -nt "$(git rev-parse --git-dir)/logs/HEAD" ])
216
+ PASS GT-04 ratchet client_js_bytes ceiling 0
217
+ PASS GT-05 client_js_bytes 0 (ceiling 0)
218
+ PASS HS-01 not an asserting harness (report utility): gate.sh
219
+ 1 asserting harness(es), 1 report utility(ies)
220
+ SKIP HS-02 no pinned pre-change commit yet (replay.commit empty or unknown) - REPLAY not provable
221
+ ADV HS-03 Source probes read text with comments blanked first. (advisory)
222
+ PASS CM-01 (test "$(git log -1 --format='%ae')" = "$(grep '^owner_email:' .goblin/goblin.yaml | cut -d' ' -f2)")
223
+ ADV CM-02 The commit message was written to a file, not passed with -m. (advisory)
224
+ FAIL CM-03 9 dirty entr(y|ies), max_dirty 0
225
+ PASS MD-01 (for d in skills manifest bin templates presets .goblin .hermes; do [ -d "$d" ] || continue; grep -rniE '(d[e]epseek|cl[a]ude|g[p]t-[0-9]|gr[o]k|g[e]mini|g[l]m-[0-9]|k[i]mi)[a-z0-9.:_-]*' "$d" && exit 1; done; exit 0)
226
+ ADV MD-02 no model mapping configured (/tmp/ptyfinal/home/projects/fleet-model.yaml absent) - advisory
227
+ ADV MD-03 Role-pinned fan-out goes through kanban, not a model-less subagent spawn. (advisory)
228
+ PASS PG-01 0 review note(s)
229
+ PASS PG-02 0 review note(s) classified
230
+ PASS PG-03 0 review note(s) patch-id checked
231
+ ADV PG-04 Never bypass what the forge enforces. (advisory)
232
+ PASS PG-05 1 workflow file(s) checked for a self-skipping required check
233
+ PASS PG-06 goblin-gate.yml: runs the verifier, which executes every declared gate
234
+ 1 workflow file(s) compared with the 2 declared gate(s)
235
+ PASS DS-01 unchanged: .goblin/state.json=missing
236
+ PASS DS-02 snapshot .goblin/state.json=missing
237
+ ADV DOC-01 A significant change updates the docs that teach it. (advisory)
238
+ ADV DOC-02 System-level changes are recorded wherever the project's standard says they live. (advisory)
239
+ PASS SK-01 
240
+ PASS SK-02 20 installed skill file(s) hashed
241
+ PASS SK-03 advisory 10 of ceiling 10 (0 free slots: the next advisory row FAILs)
242
+ PASS SK-04 
243
+ PASS PT-01 (for d in skills manifest bin templates presets .goblin .hermes; do [ -d "$d" ] || continue; grep -rniE --exclude=goblin.yaml --exclude=installed.json '(h[a]rvey|tech-g[o]blin|/h[o]me/[a-z]+|g[o]blin-ui|op[e]n-door|sup[r]eme|bb[t]ech|c[l]v)' "$d" && exit 1; done; exit 0)
244
+ PASS PT-02 declared main, actual main
245
+ PASS CL-01 part pr-gate: optional, present
246
+ part review-panel: optional, present
247
+ part tokens: optional, absent
248
+ PASS CL-02 archive: false - the full class requirement applies
249
+ PASS SC-01 0 tracked secret file(s)
250
+ PASS SC-02 the whole secret family is ignored (.env, .env.local, .env.production proved by git)
251
+ PASS SC-03 0 client-visible secret-shaped name(s); no build output to scan for clause 2
252
+ PASS SC-04 0 cookie write(s) checked; a JS-written cookie is readable by any script - reported, not failed
253
+ PASS SC-05 0 write handler(s), 0 waived; 11 dir(s) declared
254
+ SKIP SC-06 no package.json - there is no dependency manifest to lock
255
+ SKIP SC-07 no audit record yet. Run .goblin/bin/goblin-audit once, deliberately, then commit
256
+ .goblin/audit.tsv: SC-07 reads the record and never the network
257
+ SKIP SC-08 no package-lock.json - there is no install-hook surface to enumerate
258
+ ADV SC-09 Auth is applied consistently across sibling routes. (advisory)
259
+ SKIP PF-01 no perf baseline recorded yet: run `find .next/static dist/assets dist -type f -name '*.js' -exec cat {} + 2>/dev/null | wc -c` on a pinned commit, then write
260
+ perf.baseline_commit, perf.baseline_value, perf.measured and the matching ratchet ceiling
261
+ PASS AU-01 2 automation producer(s) scanned, none opens with a network or forge verb
262
+ SKIP AU-02 no reports/*/report.yaml - nothing to dedup
263
+ SKIP AU-03 no reports/ - no reporter run in this repo to audit
264
+ PASS AU-04 
265
+ PASS BN-00 8 ban(s), each with an enforcement row and a named replacement
266
+ SKIP BN-01 No `any` in application TypeScript. (no matching files: src) 
267
+ SKIP BN-02 No `@ts-ignore` / `@ts-expect-error` suppressions. (no matching files: src) 
268
+ SKIP BN-03 No direct network call from a component. (not enabled in bans: - add BN-03 to .goblin/goblin.yaml to turn it on) 
269
+ SKIP BN-05 No import across a declared layer boundary. (no matching files: src) 
270
+ SKIP BN-06 No renderer with Node access (`nodeIntegration: true`). (not enabled in bans: - add BN-06 to .goblin/goblin.yaml to turn it on) 
271
+ SKIP BN-07 No renderer with context isolation or the process sandbox turned off. (not enabled in bans: - add BN-07 to .goblin/goblin.yaml to turn it on) 
272
+ SKIP BN-08 No dangerous webPreferences. (not enabled in bans: - add BN-08 to .goblin/goblin.yaml to turn it on) 
273
+ SKIP BN-09 No synchronous IPC and no `@electron/remote`. (not enabled in bans: - add BN-09 to .goblin/goblin.yaml to turn it on) 
274
+ SKIP FM-01 feature_map: is empty - no map is declared, so there is nothing to index
275
+ SKIP FM-02 feature_map: is empty - no map is declared, so there are no entry paths to resolve
276
+ SKIP VA-01 verify_doctor: is empty - no doctor is declared, so "the generated skill was executed once" is not provable here
277
+ SKIP JG-01 no loop record in this repo - no judge verdict to check
278
+ ADV JG-02 no model mapping configured (/tmp/ptyfinal/home/projects/fleet-model.yaml absent) - advisory
279
+ remedy: add a `judge:` entry beside the code lane in the file declared as models_file: in /tmp/ptyfinal/repo/.goblin/goblin.yaml
280
+ ADV JG-03 A judge lane that has never returned a non-`done` verdict is escalated. (advisory)
281
+ SKIP LP-01 no loop has run in this repo
282
+ SKIP LP-02 no loop has run in this repo
283
+ SKIP LP-03 no loop has run in this repo
284
+ SKIP LP-04 no loop has run in this repo
285
+ SKIP LP-05 no loop has run in this repo
286
+ SKIP RC-01 reference_manifest: is empty - no reference corpus is declared, so there is nothing for a build to match against
287
+ SKIP RC-02 reference_manifest: is empty - there is no manifest to shape-check (RC-01 skips for the same reason)
288
+ SKIP RC-03 no manifests/ directory - this is not a lab repo with a reference corpus, so there are no tracked bytes to check
289
+ SKIP RC-04 no manifests/*.sha256 - no acquisition record is declared, so there is none to read
290
+ 41 passed, 2 failed, 11 advisory, 28 skipped
291
+ of those 11 advisory: the matrix labels 10 row(s) advisory; the difference is the non-advisory row(s) reporting ADV by design (an unresolved lane) and any labelled row skipped by an opt-out
292
+ cannot see: whether a check in the harness dir tests the right path rather than
293
+ merely passing; whether the forge is bound by the workflow CL-01 found; whether a human
294
+ read the diff;
295
+ whether the model mapping names a family that actually differs (MD-02 reports it
296
+ and cannot change it); and whether .goblin/installed.json — the record every drift
297
+ check trusts — was itself rewritten. It is not signed, so one edit to it (with the
298
+ matching edit to the file it protects) defeats IN-02, SK-02 and HS-01 at once
299
+ (docs/LIMITS.md #18). The ban lane carries the same blind spot and two of its own:
300
+ the ban table is a manifest file under the same unsigned record, so a defanged ban
301
+ passes BN-00 (docs/LIMITS.md #28); a ban probe is a TEXT probe with no AST, so a
302
+ `Record<string, any>` is missed and a `: any` inside a string is reported
303
+ (docs/LIMITS.md #27); and no check here can reach a ban IN CONTEXT — the row proves
304
+ the forbidden path fails mechanically, never that the ban reached the agent that
305
+ wrote the line. The judge/loop lane adds its own: every JG-/LP- row reads a RECORD, so
306
+ it proves a handle, a pin, a budget and a pointer exist — never that the loop made
307
+ progress (a changed pointer is a proxy, docs/LIMITS.md #33), never which lane returned
308
+ a verdict, and never that the predicate was the right one or was really run. The CI lane
309
+ adds its own: PG-05 and PG-06 read a workflow's TEXT, so PG-05 sees a job whose gate step is
310
+ conditional and cannot see branch protection, the required-check list, or a forge that never
311
+ ran the job at all, and PG-06 proves a declared gate is INVOKED in a file under
312
+ .github/workflows/ — never that the forge marks that job required, never that it is the job
313
+ the forge waits on, and never that the workflow can fail (that is PG-05). A required check
314
+ that reports Success after skipping its gate is a green light for a commit whose gate never
315
+ ran (docs/CI.md). The reference-corpus lane adds its own: RC-01 matches EXACT sha256 only,
316
+ so a resized, recoloured or re-encoded reference asset, or copied text inside a shipped
317
+ string, passes it unseen; RC-02 proves the manifest is well-shaped, never that its entries
318
+ are the right hashes (as strong as tamper hashing, and installed.json is unsigned); RC-03
319
+ reads the tracked tree and cannot see a payload renamed and re-encoded; and RC-04 proves an
320
+ acquisition record EXISTS, never that the number beside it came from the store (HP-03's
321
+ defect, one artifact over). No row here reaches a reference asset in CONTEXT either.
322
+ engine: mode=vendored cli_sha256=2d37cf1c9f38a582ad60f3f75d2fcd53d10b743905dc3274eab6511c5c5ea008 enforcement_tsv_sha256=fd474c268b0852587c632910c503144fbcfe5e211ed2182902949a79fc5282b1
323
+
324
+ gob verify: 41 passed, 2 failed, 28 skipped · exit 1
325
+
326
+ next:
327
+ 1. the 28 skipped checks ARE your checklist — gob verify names each one
328
+ 2. replace the placeholder gate in .goblin/goblin.yaml with your real commands (P8 step 3)
329
+ 3. not emitted here: gob emit --platform <p> --scope global
330
+ 4. hermes skills trust /tmp/ptyfinal/repo # one-time, so the project-tier skills load
331
+
332
+ ▙ the goblin sees you. the gate is RED — start there.
333
+
334
+ Script done on 2026-10-02 00:53:50+07:00 [COMMAND_EXIT_CODE="0"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techgoblin/gobstack",
3
- "version": "0.4.4-beta.6",
3
+ "version": "0.4.4-beta.7",
4
4
  "description": "Agent-discipline toolkit: one verify command, an enforcement matrix, and LIMITS. bash engine, npm shim.",
5
5
  "bin": {
6
6
  "goblin": "bin/goblin.js",