@techgoblin/gobstack 0.0.0-stage → 0.4.4-beta.2
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/CHANGELOG.md +351 -0
- package/LICENSE +21 -0
- package/README.md +217 -2
- package/VERSION +1 -0
- package/adapters/_template/adapter.tsv +16 -0
- package/adapters/_template/detect.sh +10 -0
- package/adapters/_template/emit.sh +5 -0
- package/adapters/_template/verify.sh +4 -0
- package/adapters/claude/adapter.tsv +8 -0
- package/adapters/claude/detect.sh +8 -0
- package/adapters/claude/verify.sh +47 -0
- package/adapters/codex/adapter.tsv +12 -0
- package/adapters/codex/detect.sh +9 -0
- package/adapters/codex/verify.sh +45 -0
- package/adapters/copilot/adapter.tsv +10 -0
- package/adapters/copilot/detect.sh +8 -0
- package/adapters/copilot/verify.sh +45 -0
- package/adapters/cursor/adapter.tsv +11 -0
- package/adapters/cursor/detect.sh +10 -0
- package/adapters/cursor/verify.sh +45 -0
- package/adapters/gemini/adapter.tsv +15 -0
- package/adapters/gemini/detect.sh +11 -0
- package/adapters/gemini/verify.sh +49 -0
- package/adapters/hermes/adapter.tsv +9 -0
- package/adapters/hermes/detect.sh +8 -0
- package/adapters/hermes/verify.sh +27 -0
- package/adapters/opencode/adapter.tsv +14 -0
- package/adapters/opencode/detect.sh +9 -0
- package/adapters/opencode/verify.sh +45 -0
- package/automations/README.md +53 -0
- package/automations/bugreporter-intake.sh +145 -0
- package/automations/drift-audit.sh +139 -0
- package/automations/report.schema.tsv +10 -0
- package/bans/README.md +82 -0
- package/bans/grep-ban.sh +84 -0
- package/bans/layer-check.sh +57 -0
- package/bin/goblin +119 -0
- package/bin/goblin-audit +145 -0
- package/bin/goblin-bans +178 -0
- package/bin/goblin-doctor +233 -0
- package/bin/goblin-emit +484 -0
- package/bin/goblin-init +519 -0
- package/bin/goblin-install +720 -0
- package/bin/goblin-lib.sh +289 -0
- package/bin/goblin-model +105 -0
- package/bin/goblin-upgrade +572 -0
- package/bin/goblin-verify +2798 -0
- package/bin/goblin.js +103 -0
- package/docs/ADOPTION.md +168 -0
- package/docs/CI.md +187 -0
- package/docs/CONTRACTS.md +197 -0
- package/docs/DESIGN.md +92 -0
- package/docs/ENFORCEMENT.md +225 -0
- package/docs/FLOWS.md +164 -0
- package/docs/GUARDRAILS.md +126 -0
- package/docs/GUIDE.md +610 -0
- package/docs/INTEGRATION.md +92 -0
- package/docs/LIMITS.md +591 -0
- package/docs/LOOP.md +165 -0
- package/docs/RE-PLAYBOOK.md +183 -0
- package/docs/RISKS.md +70 -0
- package/docs/ROLES.md +105 -0
- package/manifest/bans.tsv +9 -0
- package/manifest/classes.tsv +61 -0
- package/manifest/enforcement.tsv +88 -0
- package/manifest/glossary.tsv +25 -0
- package/manifest/playbooks.tsv +16 -0
- package/package.json +37 -4
- package/presets/A-shipped-software.yaml +48 -0
- package/presets/B-service-config.yaml +40 -0
- package/presets/C-game.yaml +38 -0
- package/presets/D-knowledge.yaml +41 -0
- package/presets/E-fleet-config.yaml +42 -0
- package/presets/F-electron.yaml +67 -0
- package/roles.yaml +54 -0
- package/skills/goblin-bootstrap/SKILL.md +51 -0
- package/skills/goblin-bugfix/SKILL.md +26 -0
- package/skills/goblin-bugreporter/SKILL.md +52 -0
- package/skills/goblin-drift-audit/SKILL.md +43 -0
- package/skills/goblin-eval/SKILL.md +68 -0
- package/skills/goblin-feature/SKILL.md +26 -0
- package/skills/goblin-feature-map/SKILL.md +140 -0
- package/skills/goblin-handoff/SKILL.md +28 -0
- package/skills/goblin-investigation/SKILL.md +26 -0
- package/skills/goblin-judge/SKILL.md +74 -0
- package/skills/goblin-loop/SKILL.md +88 -0
- package/skills/goblin-mode/SKILL.md +70 -0
- package/skills/goblin-overnight/SKILL.md +42 -0
- package/skills/goblin-pr-gate/SKILL.md +42 -0
- package/skills/goblin-re-mobile/SKILL.md +51 -0
- package/skills/goblin-refactor/SKILL.md +23 -0
- package/skills/goblin-sweep/SKILL.md +23 -0
- package/skills/goblin-tdd-repro/SKILL.md +27 -0
- package/skills/goblin-verify-author/SKILL.md +50 -0
- package/skills/practice/SKILL.md +37 -0
- package/templates/AGENTS.md.tmpl +23 -0
- package/templates/HANDOFF.md.tmpl +43 -0
- package/templates/SPEC.md.tmpl +34 -0
- package/templates/audit-waiver.tsv.tmpl +10 -0
- package/templates/boundary-waivers.tmpl +8 -0
- package/templates/checks/assert.mjs.tmpl +60 -0
- package/templates/checks/gate.sh.tmpl +29 -0
- package/templates/ci/goblin-gate.yml.tmpl +46 -0
- package/templates/goblin.yaml.tmpl +138 -0
- package/templates/install-hooks.allowlist.tmpl +9 -0
- package/templates/loop/decisions.tsv.tmpl +1 -0
- package/templates/loop/predicate.tmpl +16 -0
- package/templates/report.yaml.tmpl +16 -0
package/docs/GUIDE.md
ADDED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
# Getting started with gobstack
|
|
2
|
+
|
|
3
|
+
A step-by-step guide for your first week. **Read this before the README.** The README tells you
|
|
4
|
+
what the pieces are; this tells you what to *do*, in order, and what you should see when it works.
|
|
5
|
+
|
|
6
|
+
Version: `0.4.4` · Last measured: 2026-09-25 · Every command and every output below was run on a
|
|
7
|
+
real repository while writing this guide.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 0. Who this is for, and what you will have at the end
|
|
12
|
+
|
|
13
|
+
This guide assumes you are a developer who uses an AI coding agent and has noticed the same three
|
|
14
|
+
problems everyone notices:
|
|
15
|
+
|
|
16
|
+
1. **A new session re-improvises.** The agent forgets how you work, what you decided last week,
|
|
17
|
+
which commands matter. Every session starts from zero.
|
|
18
|
+
2. **"It works" is a claim, not a measurement.** The agent says it fixed something. You have no
|
|
19
|
+
cheap way to know whether that is true.
|
|
20
|
+
3. **Rules live in prose.** You write them down, the agent reads them, and nothing enforces them —
|
|
21
|
+
so they rot silently, and you find out months later.
|
|
22
|
+
|
|
23
|
+
By the end of this guide you will have a repository that fixes those three things *mechanically*:
|
|
24
|
+
one file a new session reads first, a checker that proves a change is a change, and a rule table
|
|
25
|
+
where every rule either runs a command or is explicitly counted as unenforceable.
|
|
26
|
+
|
|
27
|
+
**Time budget:** about 45 minutes to work through it once. You do not need to understand the whole
|
|
28
|
+
design on day one.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. What this actually is, in plain language
|
|
33
|
+
|
|
34
|
+
goblin-stack (published as the npm package **`@techgoblin/gobstack`**, product name **gobstack**)
|
|
35
|
+
is **a folder of files you install into a project** from npm. Once
|
|
36
|
+
installed, three things change:
|
|
37
|
+
|
|
38
|
+
- A file called `HANDOFF.md` sits at the root. It is the note from the last session to the next one.
|
|
39
|
+
Any agent — or you, a month later — reads it first.
|
|
40
|
+
- A command called `goblin-verify` exists in that project. Run it and it checks the project against
|
|
41
|
+
a table of rules and prints `PASS` / `FAIL` / `SKIP` for each one.
|
|
42
|
+
- The rules table is a real file (`.goblin/manifest/enforcement.tsv`). Every row either names a
|
|
43
|
+
command that can fail, or is labelled `advisory`. **Nothing in between.** That is what stops the
|
|
44
|
+
rules turning into decoration.
|
|
45
|
+
|
|
46
|
+
It is not a framework, not a service, and not a runtime. It has no server and no dependencies
|
|
47
|
+
beyond `bash`, `git`, `awk`, `sed`, `grep` and `python3`. It makes **no network call at verify
|
|
48
|
+
time** — the one command in the toolbox that reaches the network is `goblin-audit`, which you run
|
|
49
|
+
deliberately, and §8 and §11 say why.
|
|
50
|
+
|
|
51
|
+
### The one idea worth holding onto
|
|
52
|
+
|
|
53
|
+
> **A rule that cannot fail is worse than no rule**, because it takes credit for verification it
|
|
54
|
+
> does not perform.
|
|
55
|
+
|
|
56
|
+
Everything else in goblin-stack follows from that sentence. If you remember one thing from this
|
|
57
|
+
guide, remember that one — it is also the standard the harness holds itself to, and the reason it
|
|
58
|
+
ships a file of things it *cannot* check (`docs/LIMITS.md`).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 2. Before you begin
|
|
63
|
+
|
|
64
|
+
**You need:**
|
|
65
|
+
|
|
66
|
+
| | |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `bash`, `git`, `awk`, `sed`, `grep`, `python3` | already on any Linux/macOS box |
|
|
69
|
+
| a project that is a **git repository** | `git status` must work; the harness reads commit identity |
|
|
70
|
+
| a branch named the same as the one you declare | see step 3 — a `master`/`main` mismatch is the most common first failure |
|
|
71
|
+
|
|
72
|
+
You need node ≥ 18 (for the npm shim only), beyond the row above.
|
|
73
|
+
|
|
74
|
+
**You do *not* need:** network access at verify time, or an agent running.
|
|
75
|
+
|
|
76
|
+
**Get gobstack:**
|
|
77
|
+
|
|
78
|
+
npm i -g @techgoblin/gobstack
|
|
79
|
+
|
|
80
|
+
This puts a single command, `goblin`, on your PATH — the node shim over the bash engine, and the
|
|
81
|
+
one way this guide installs it.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 3. Step 1 — Try it on a throwaway repo first (5 minutes)
|
|
86
|
+
|
|
87
|
+
**Do not install into a real project yet.** You want to see what it does before it touches
|
|
88
|
+
something you care about.
|
|
89
|
+
|
|
90
|
+
The guided path is `goblin init` — one screen per question (class, branch/email, first
|
|
91
|
+
gate, which platforms to emit), every question also answerable by flag, `--dry-run` to
|
|
92
|
+
see the plan first:
|
|
93
|
+
|
|
94
|
+
mkdir -p /tmp/gs-try && cd /tmp/gs-try
|
|
95
|
+
git init -b main
|
|
96
|
+
git config user.email "you@example.com"
|
|
97
|
+
git config user.name "you"
|
|
98
|
+
|
|
99
|
+
goblin init --target . --class app --branch main --email "you@example.com" \
|
|
100
|
+
--gate "bash tests/run-tests.sh" --yes
|
|
101
|
+
|
|
102
|
+
or the plain installer this wizard drives, if you prefer the one-shot shape:
|
|
103
|
+
|
|
104
|
+
goblin install --target . --class A
|
|
105
|
+
|
|
106
|
+
Expected output (this is a real transcript, trimmed):
|
|
107
|
+
|
|
108
|
+
created 50 · updated 0 · unchanged 0 · skipped 0
|
|
109
|
+
|
|
110
|
+
next:
|
|
111
|
+
1. cd /tmp/gs-try && git add -A && git commit # the install is a change like any other
|
|
112
|
+
2. .goblin/bin/goblin-verify # or add .goblin/bin to PATH
|
|
113
|
+
3. edit .goblin/goblin.yaml: replace the default gate with your real commands (P8 step 3)
|
|
114
|
+
4. hermes skills trust /tmp/gs-try # one-time, so the project-tier skills load
|
|
115
|
+
|
|
116
|
+
**`created 50`** is the installer's count of the files it **tracks** — the 41 in its `files` map,
|
|
117
|
+
the 8 it `owns`, and `.gitignore`. It writes **51**: the 51st is `.goblin/installed.json`, the
|
|
118
|
+
record it keeps for itself, which it writes but does not count. It has written nothing outside this
|
|
119
|
+
directory.
|
|
120
|
+
|
|
121
|
+
### Why `git init -b main` matters
|
|
122
|
+
|
|
123
|
+
The harness **declares** your default branch rather than assuming it (rule `PT-02`). If your repo's
|
|
124
|
+
branch is `master` and the config says `main`, verify fails on the very first run:
|
|
125
|
+
|
|
126
|
+
FAIL PT-02 declared main, actual master
|
|
127
|
+
|
|
128
|
+
That is not a bug — it is the harness refusing to guess, which is the same reason it fails instead
|
|
129
|
+
of silently skipping a repo whose branch it got wrong. **Fix it in step 3, not by renaming your
|
|
130
|
+
branch** (unless you want to).
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 4. Step 2 — Commit, then verify (the moment it earns its keep)
|
|
135
|
+
|
|
136
|
+
cd /tmp/gs-try
|
|
137
|
+
git add -A && git commit -m "chore: install goblin-stack"
|
|
138
|
+
.goblin/bin/goblin-verify
|
|
139
|
+
|
|
140
|
+
You will see one line per rule. The shape:
|
|
141
|
+
|
|
142
|
+
PASS IN-01 (test -s .goblin/installed.json && grep -q '"version"' .goblin/installed.json)
|
|
143
|
+
PASS IN-02 40 installed files hashed | practice pin ok
|
|
144
|
+
FAIL HP-05 HANDOFF.md names no commit that exists in this repo
|
|
145
|
+
SKIP HS-02 no pinned pre-change commit yet - the REPLAY is not provable
|
|
146
|
+
ADV HP-04 A stale sentence is corrected in place... (advisory)
|
|
147
|
+
|
|
148
|
+
and a summary line at the bottom:
|
|
149
|
+
|
|
150
|
+
42 passed, 1 failed, 11 advisory, 28 skipped # the one FAIL is HP-05, below
|
|
151
|
+
|
|
152
|
+
### How to read that output
|
|
153
|
+
|
|
154
|
+
| Marking | Meaning | What you do |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `PASS` | the rule's command succeeded | nothing |
|
|
157
|
+
| `FAIL` | the rule's command failed, and the message says why | fix it — this is the whole point |
|
|
158
|
+
| `SKIP` | the rule cannot run **yet**, and it says why | usually expected on day one |
|
|
159
|
+
| `ADV` | advisory — a rule with no runnable check, **counted** | nothing, but know it is not enforced |
|
|
160
|
+
|
|
161
|
+
**`SKIP` is not success and not failure.** It is the harness telling you the truth: "this rule has
|
|
162
|
+
nothing to read yet." On a brand-new install, two dozen rows skip — because there is no `src/` for a
|
|
163
|
+
ban to scan, no feature map, no loop record, no pinned pre-change commit. That is correct on day
|
|
164
|
+
one. The list of what is still skipping *is* your onboarding checklist.
|
|
165
|
+
|
|
166
|
+
**Read the failure messages.** They are written to be actionable, not decorative. `HP-05` above is
|
|
167
|
+
telling you the HANDOFF does not yet name a commit — fix it by naming your HEAD in the `State`
|
|
168
|
+
section.
|
|
169
|
+
|
|
170
|
+
### The three-day-one failures, and why they are not a broken harness
|
|
171
|
+
|
|
172
|
+
If you ran step 1 without `-b main`, or with the wrong git identity, you will see:
|
|
173
|
+
|
|
174
|
+
| FAIL | Cause | Fix |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `PT-02 declared main, actual master` | branch name mismatch | set `branch:` in `.goblin/goblin.yaml` |
|
|
177
|
+
| `CM-01` (commit identity) | the repo's commit email ≠ the declared `owner_email:` | set `owner_email:` in the config |
|
|
178
|
+
| `HP-05` | `HANDOFF.md` still names the scaffold placeholder `` `0000000` `` | replace it with your real short HEAD |
|
|
179
|
+
|
|
180
|
+
**All three are configuration, not defects.** The harness is reporting your repo's actual state
|
|
181
|
+
against a declared expectation. That is exactly what you want it to do.
|
|
182
|
+
|
|
183
|
+
`HP-05` deserves one sentence more, because it surprises people: the scaffold ships
|
|
184
|
+
`HEAD when this file was written: `0000000``, and `HP-05` **rejects that placeholder on purpose**.
|
|
185
|
+
A file that names a commit which does not exist is worse than one that names none — it looks like a
|
|
186
|
+
record. Commit first, then write the real short SHA in. Measured: with the placeholder left in,
|
|
187
|
+
verify reports `42 passed, 1 failed`; with the real SHA, `43 passed, 0 failed`.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 5. Step 3 — Make it yours: the one config file
|
|
192
|
+
|
|
193
|
+
Everything you configure lives in **one file**, created once and then never overwritten:
|
|
194
|
+
|
|
195
|
+
.goblin/goblin.yaml
|
|
196
|
+
|
|
197
|
+
Open it. The keys that matter on day one:
|
|
198
|
+
|
|
199
|
+
class: A # A|B|C|D|E|F - what kind of project this is (step 6)
|
|
200
|
+
branch: main # DECLARED, never assumed
|
|
201
|
+
owner_email: you@example.com # the commit identity this repo expects
|
|
202
|
+
practice: /path/to/your-standard.md # optional: your own house rules, hash-pinned
|
|
203
|
+
models_file: /path/to/fleet-model.yaml # the ONE machine-specific input
|
|
204
|
+
|
|
205
|
+
gates: # <- replace these with YOUR real commands
|
|
206
|
+
- name: commit
|
|
207
|
+
cmd: git rev-parse --verify --quiet HEAD
|
|
208
|
+
- name: todo_ceiling
|
|
209
|
+
cmd: test "$(grep -rniE '\b(TODO|FIXME)\b' --include='*.ts' . | wc -l)" -le 160
|
|
210
|
+
|
|
211
|
+
**The single most valuable edit you will make:** replace the default `gates:` with the commands you
|
|
212
|
+
actually run to know your project is healthy. `tsc --noEmit`, `npm run build`, your test command —
|
|
213
|
+
whichever three or four you would run before saying "this is fine."
|
|
214
|
+
|
|
215
|
+
Why it matters: from then on, `goblin-verify` runs *your* definition of healthy, every time,
|
|
216
|
+
without you remembering to. And the gate numbers are recorded with a date, so a number in
|
|
217
|
+
`HANDOFF.md` can never quietly go stale.
|
|
218
|
+
|
|
219
|
+
### The `practice:` key — the part that makes it yours
|
|
220
|
+
|
|
221
|
+
If you already have a house standard — a `CONTRIBUTING.md`, a `PROJECT-PRACTICE.md`, anything
|
|
222
|
+
written down — point `practice:` at it. goblin-stack does **not** copy its text. It records a
|
|
223
|
+
**hash** of the file and re-checks that hash on every verify.
|
|
224
|
+
|
|
225
|
+
That buys you one specific, valuable thing: **if someone edits your standard, every project that
|
|
226
|
+
pins it goes red.** You find out immediately instead of discovering six months later that half your
|
|
227
|
+
repos follow an old version.
|
|
228
|
+
|
|
229
|
+
When *you* legitimately edit your own standard:
|
|
230
|
+
|
|
231
|
+
goblin install --target . --re-pin
|
|
232
|
+
|
|
233
|
+
It re-records the hash and prints the old and new value. Nothing re-pins automatically — an
|
|
234
|
+
edited standard is never a silent no-op.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 6. Step 4 — Pick the right class (this decides what you get)
|
|
239
|
+
|
|
240
|
+
A class is **not** a strictness level. It selects which parts are required, optional, or off, and it
|
|
241
|
+
supplies the default gate shape. Choose by asking *what does "done" mean here?*
|
|
242
|
+
|
|
243
|
+
| Class | Choose it when | "Done" means |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| **A · Shipped software** | an app, library, or tool users run | a gate set reports measured numbers and a round lands |
|
|
246
|
+
| **B · Service / config** | an API, schema, route, or deployment config | the contract is unchanged, or the change is deliberate and migrated |
|
|
247
|
+
| **C · Game** | a game | a suite is green **and** a human feel verdict exists |
|
|
248
|
+
| **D · Knowledge / research** | notes, a vault, a research directory | a question is answered with sources and is findable |
|
|
249
|
+
| **E · Agent-fleet config** | your agent's own config (`~/.hermes`) | the change is applied, verified against the artifact, versioned |
|
|
250
|
+
| **F · Desktop shell** | an Electron / desktop app | renderer isolated from Node, main process not busy, no dev dependency shipped |
|
|
251
|
+
|
|
252
|
+
**Two placements people get wrong:**
|
|
253
|
+
|
|
254
|
+
- A repo that holds *output* while the code lives elsewhere → **D**, not A. Gating it like an
|
|
255
|
+
application gates the wrong artifact.
|
|
256
|
+
- A plain input directory that is not a build target → **D** with `--archive`, which tells verify to
|
|
257
|
+
expect no HANDOFF and no gates, and to say so.
|
|
258
|
+
|
|
259
|
+
Switch class later by editing `class:` in the config and re-running install. The parts you no longer
|
|
260
|
+
need are recorded as **disabled** and will report `SKIP (opt-out)` rather than failing.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 7. Step 5 — Your first real change, end to end
|
|
265
|
+
|
|
266
|
+
Now do the thing the harness exists for. Pick a small real task in a real repo.
|
|
267
|
+
|
|
268
|
+
**The loop you are going to follow:**
|
|
269
|
+
|
|
270
|
+
1. Write the intent down -> ROUND-001-SPEC.md
|
|
271
|
+
2. Make the change
|
|
272
|
+
3. Run the gate -> goblin-verify
|
|
273
|
+
4. Record what you proved -> HANDOFF.md
|
|
274
|
+
5. Commit as you go
|
|
275
|
+
|
|
276
|
+
Step by step:
|
|
277
|
+
|
|
278
|
+
# 1. Say what you are about to do, and how you will know it worked
|
|
279
|
+
cp ROUND-000-SPEC.md ROUND-001-SPEC.md
|
|
280
|
+
# edit it: state the measured problem, then a list of AC: items
|
|
281
|
+
# each AC: must be checkable by a machine - see below
|
|
282
|
+
|
|
283
|
+
# 2. make your change, committing in small steps
|
|
284
|
+
|
|
285
|
+
# 3. run the gate
|
|
286
|
+
.goblin/bin/goblin-verify
|
|
287
|
+
|
|
288
|
+
# 4. write the handoff: state / gates / next steps / NOT verified
|
|
289
|
+
|
|
290
|
+
### Writing an `AC:` item that is worth writing
|
|
291
|
+
|
|
292
|
+
A spec item is only useful if a script could evaluate it. Compare:
|
|
293
|
+
|
|
294
|
+
- AC1: the export button feels responsive after the fix <- worthless, no machine can check it
|
|
295
|
+
- AC2: export completes in < 200ms for 1000 rows <- checkable
|
|
296
|
+
- AC3: `goblin-verify --only SP-03` exits 0 <- checkable, today
|
|
297
|
+
|
|
298
|
+
Rule `SP-03` actually fails a spec line that tries to pass prose off as a criterion. If the only
|
|
299
|
+
test is your own judgement, say so in the spec, in an explicit *device-test* item — that is an
|
|
300
|
+
honest entry, and the harness treats it as one.
|
|
301
|
+
|
|
302
|
+
### The habit that makes the whole thing work
|
|
303
|
+
|
|
304
|
+
> **Prove it was broken first.**
|
|
305
|
+
|
|
306
|
+
Before you trust a check, break the thing it checks and watch it go red — then put it back and watch
|
|
307
|
+
it go green. Break it on a row this walkthrough can actually break: `IN-02` hashes the **41 files it
|
|
308
|
+
tracks** — not the 8 it `owns` (including `.goblin/goblin.yaml`, which §5 has you editing) and not
|
|
309
|
+
`.goblin/installed.json`; edit one of the 41 — the exercise below uses `.goblin/bans/README.md`.
|
|
310
|
+
|
|
311
|
+
# REPLAY-BEGIN (this exact block is run by tests/t-doc-guide.sh - keep the two copies identical)
|
|
312
|
+
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
313
|
+
printf '\n<!-- a deliberate edit -->\n' >> .goblin/bans/README.md
|
|
314
|
+
.goblin/bin/goblin-verify --only IN-02 # expect FAIL
|
|
315
|
+
git stash push -- .goblin/bans/README.md # path-limited: your own edits stay put
|
|
316
|
+
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
317
|
+
git stash drop # the break was deliberate: discard it
|
|
318
|
+
# REPLAY-END
|
|
319
|
+
|
|
320
|
+
Read the direction: the edit makes the check go **red**, and putting the file back makes it green.
|
|
321
|
+
That is the whole habit — the change you *undo* is a deliberate break, not a fix, because `IN-02`
|
|
322
|
+
measures the shipped files rather than your work.
|
|
323
|
+
|
|
324
|
+
`GT-02` is the row most readers reach for first, and it will **not** work as a REPLAY demo on the
|
|
325
|
+
shipped configuration: it runs the gates you declared (`commit`, `todo_ceiling`), and stashing a
|
|
326
|
+
local change does not change either command's exit status — so it prints `PASS` before and after,
|
|
327
|
+
which is exactly the "green on both trees" result this rule exists to kill. REPLAY a gate of your
|
|
328
|
+
own the same way, once that gate is real: declare it in `.goblin/goblin.yaml` and stash a change it
|
|
329
|
+
can see.
|
|
330
|
+
|
|
331
|
+
A check that is green on **both** the broken and the fixed tree proves nothing — it would have been
|
|
332
|
+
green anyway. goblin-stack calls this the **REPLAY** rule, and it is the single practice that has
|
|
333
|
+
caught every real regression in this repository's own development history.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 8. Step 6 — The daily loop, once you are settled
|
|
338
|
+
|
|
339
|
+
Day to day, the harness should fade into four habits:
|
|
340
|
+
|
|
341
|
+
**Starting work** — read `HANDOFF.md` first. It tells you the state, the gates, what is next, and —
|
|
342
|
+
most importantly — **what is *not* verified**. Never trust a claim in it without running the
|
|
343
|
+
command it names.
|
|
344
|
+
|
|
345
|
+
**While working** — commit small. `CM-03` fails verify when the tree carries a dead run's work, so
|
|
346
|
+
the harness nudges you to land things as they work rather than in one heroic commit at the end.
|
|
347
|
+
|
|
348
|
+
**Finishing** — update `HANDOFF.md`, then:
|
|
349
|
+
|
|
350
|
+
.goblin/bin/goblin-verify && git add -A && git commit -m "..."
|
|
351
|
+
|
|
352
|
+
**Every so often** — audit your own claims against the artifact:
|
|
353
|
+
|
|
354
|
+
.goblin/bin/goblin-audit
|
|
355
|
+
|
|
356
|
+
This is the *only* step that touches the network (rule `SC-07`), and it is deliberate: it is how a
|
|
357
|
+
recorded claim ("this dependency is fine") gets checked against reality ("this dependency has a
|
|
358
|
+
known advisory").
|
|
359
|
+
|
|
360
|
+
### Keeping `HANDOFF.md` honest
|
|
361
|
+
|
|
362
|
+
`HANDOFF.md` needs five sections, and rule `HP-02` checks the headings exist:
|
|
363
|
+
|
|
364
|
+
| Section | First word of the heading |
|
|
365
|
+
|---|---|
|
|
366
|
+
| orientation | `START HERE` |
|
|
367
|
+
| current state | `State` or `Status` |
|
|
368
|
+
| gates | `Gate` or `Gates` |
|
|
369
|
+
| next steps | `Next steps` or `Next` |
|
|
370
|
+
| what is not verified | `Not verified` / `Unverified` / `Not proven` |
|
|
371
|
+
|
|
372
|
+
Inside `Gates`, every number must carry a date:
|
|
373
|
+
|
|
374
|
+
- `tsc`=0 · `build`=0 · hex **144** (ceiling 160) · 38/38 harnesses green · measured 2026-09-24
|
|
375
|
+
|
|
376
|
+
**A number without a date is a rumour.** `HP-03` will fail it, and the reason is that a
|
|
377
|
+
measurement copied from last round is worse than no measurement — it *looks* verified.
|
|
378
|
+
|
|
379
|
+
**When a sentence in `HANDOFF.md` goes stale:** correct it in place with the date, and keep the
|
|
380
|
+
original:
|
|
381
|
+
|
|
382
|
+
> it used to say X. X was paid on 2026-09-20 (commit abc1234). The stale sentence is kept,
|
|
383
|
+
> dated, rather than deleted.
|
|
384
|
+
|
|
385
|
+
Deleting it erases the fact that it was once believed true; leaving it undated re-arms the trap for
|
|
386
|
+
the next session.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 9. What to expect on day one (so you do not misread it)
|
|
391
|
+
|
|
392
|
+
A class-A install lands on a specific shape. The scaffold ships one deliberate red — `HP-05`, the
|
|
393
|
+
`0000000` placeholder in `HANDOFF.md` (§4) — so a literal first run prints:
|
|
394
|
+
|
|
395
|
+
42 passed, 1 failed, 11 advisory, 28 skipped (the one FAIL is HP-05)
|
|
396
|
+
|
|
397
|
+
Name a real commit in `HANDOFF.md` and commit, and it is green:
|
|
398
|
+
|
|
399
|
+
43 passed, 0 failed, 11 advisory, 28 skipped (on a real project; your numbers will differ)
|
|
400
|
+
|
|
401
|
+
**Twenty-eight rows skipping is correct**, and each skip prints its reason. In plain terms: the
|
|
402
|
+
harness is telling you which of its rules have nothing to read yet. It is a checklist, not a
|
|
403
|
+
scolding.
|
|
404
|
+
|
|
405
|
+
Two readings that are easy to get wrong:
|
|
406
|
+
|
|
407
|
+
- **Advisory rows are not passes.** Ten rules are labelled `advisory` — counted, not enforced, and
|
|
408
|
+
nine of them carry no executable check at all. The count is capped by `advisory_ceiling: 10`, and
|
|
409
|
+
a class-A install already sits at 10 of 10: adding another unenforceable rule fails verify until
|
|
410
|
+
one is removed. That is intentional. (The summary line can print `11 advisory`: the eleventh ADV
|
|
411
|
+
line is `JG-02`, a row with a real command of its own that reports ADV here because your model
|
|
412
|
+
file declares no `judge:` lane — it prints the remedy rather than failing a repo for a fleet's
|
|
413
|
+
routing.)
|
|
414
|
+
- **Vacuously-passing rows are not proven.** A rule about "the first review note" passes when there
|
|
415
|
+
is no review note yet. It is not lying — it is passing on an empty set. `docs/CONTRACTS.md`
|
|
416
|
+
names which rows do this.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## 10. When something goes wrong
|
|
421
|
+
|
|
422
|
+
| Symptom | What it means | What to do |
|
|
423
|
+
|---|---|---|
|
|
424
|
+
| `refused to overwrite: HANDOFF.md`, exit 1 | your repo already had a HANDOFF | **do not `--force`** — reconcile it (below) |
|
|
425
|
+
| `PT-02 declared main, actual master` | branch mismatch | set `branch:` in the config |
|
|
426
|
+
| `IN-02 ... practice EDITED` | someone changed the pinned standard | re-pin deliberately: `--re-pin` |
|
|
427
|
+
| `goblin install: unknown subcommand` (exit 2) | you ran a bare `goblin install` without the npm package installed | install the npm package first: `npm i -g @techgoblin/gobstack`, then `goblin install` |
|
|
428
|
+
| `IN-03` fails, "manifest is broken" | a row has a broken check column | fix the row; this is a source defect, not yours |
|
|
429
|
+
| a `FAIL` you believe is wrong | the check may be weak, or your belief may be | run `--only <id>` and read the command it prints |
|
|
430
|
+
|
|
431
|
+
**The `HANDOFF.md` refusal is the most common one, and `--force` is never the answer.** `--force`
|
|
432
|
+
replaces your project's own record with a blank scaffold — the exact act the refusal exists to
|
|
433
|
+
prevent. Reconcile instead: keep your file, and add the five sections it is missing. The measured
|
|
434
|
+
cost of that edit, on a real 2450-line handoff, was **15 lines added, none removed**.
|
|
435
|
+
|
|
436
|
+
**One engine, many repos (W1):** the rule table does not have to live in every repo. A repo can
|
|
437
|
+
point at a shared engine with one line in `.goblin/goblin.yaml`:
|
|
438
|
+
|
|
439
|
+
engine_dir: ~/.goblin/engine # absolute or ~/-prefixed; absent = per-repo engine
|
|
440
|
+
|
|
441
|
+
Declared but unusable (relative path, missing directory, no manifest inside) is verify **exit 2
|
|
442
|
+
with no fallback** — a repo is never judged by an engine it did not declare. A repo whose record
|
|
443
|
+
says `mode=global` keeps hashing whatever files it still holds; the engine's own identity prints in
|
|
444
|
+
every run's footer (`engine: mode=… cli_sha256=… enforcement_tsv_sha256=…`). The same commands are
|
|
445
|
+
available outside any repo through the npm CLI: `goblin verify` / `goblin bans` / `goblin audit` /
|
|
446
|
+
`goblin doctor` / `goblin emit` / `goblin upgrade` / `goblin --version`.
|
|
447
|
+
|
|
448
|
+
**Migrating a repo to the global engine (W3):**
|
|
449
|
+
|
|
450
|
+
goblin upgrade # 8 steps, two commits, one report
|
|
451
|
+
|
|
452
|
+
It refuses on a dirty tree, a detached HEAD, a red repo, or a global engine holding different
|
|
453
|
+
bytes — each refusal names the fix. What it does: verifies every recorded hash, lands the engine
|
|
454
|
+
at `~/.goblin/engine` (or `--engine-dir <dir>`) from this repo's own verified bytes, commits the
|
|
455
|
+
declaration + record rewrite + `checks/gate.sh` + CI re-point (commit A), proves the repo green
|
|
456
|
+
with both engines present, then `git rm`s exactly the 18 engine files (commit B) and proves green
|
|
457
|
+
again. Nothing is deleted before the engine is safely landed and the tree is green mid-sequence.
|
|
458
|
+
|
|
459
|
+
**Rolling back a migration** — the two commits are pure git operations:
|
|
460
|
+
|
|
461
|
+
git revert <commit-A-sha> <commit-B-sha>
|
|
462
|
+
|
|
463
|
+
reverses byte-for-byte: the vendored payload returns, the record drops its `engine:` block, and
|
|
464
|
+
`goblin verify` is the 43-green it was before. A second `goblin upgrade` on a migrated repo is a
|
|
465
|
+
no-op; `goblin-install` onto one refuses with the revert remedy (re-installing would re-shadow the
|
|
466
|
+
engine and silently de-migrate the record).
|
|
467
|
+
|
|
468
|
+
**Two exit-code contracts worth knowing:**
|
|
469
|
+
|
|
470
|
+
| Command | Exit codes |
|
|
471
|
+
|---|---|
|
|
472
|
+
| `goblin-install` | `0` ok · `1` a refusal (with the path and the fix) · `2` bad input |
|
|
473
|
+
| `goblin-verify` | `0` all checks passed · `1` a check failed · `2` could not run · `3` the manifest itself is broken |
|
|
474
|
+
| `goblin` (npm CLI) | propagates the subcommand's codes verbatim — `verify`/`bans`/`audit`/`--version`; `install`/`uninstall`/`re-pin`/`upgrade` route into `goblin-install` (`upgrade` migrates to the global engine: `0` ok · `1` refusal · `2` bad input); `doctor`/`emit` carry the same contract: `doctor` exits `0` every probed platform DETECTED and clean · `1` any DRIFT · `2` nothing to probe, and `emit` exits `0` ok or no-op · `1` refusal (with the path and the fix) · `2` bad input or unknown platform |
|
|
475
|
+
| platforms (W4b) | `emit`/`doctor` cover seven: `claude`, `hermes`, `copilot`, `cursor`, `opencode`, `codex`, `gemini` — each detected via its own anchor (`~/.claude`, `~/.hermes`, `~/.copilot`, `~/.cursor`, `~/.config/opencode`, `~/.codex`, `~/.gemini`); codex and gemini carry `partial` command-blocking (see LIMITS #47) |
|
|
476
|
+
|
|
477
|
+
`3` is the one to notice: it means goblin-stack's own rule table is malformed, not your project.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## 11. Reference
|
|
482
|
+
|
|
483
|
+
### Commands
|
|
484
|
+
|
|
485
|
+
goblin install --target <dir> --class A|B|C|D|E|F [options]
|
|
486
|
+
goblin install --target <dir> --uninstall
|
|
487
|
+
goblin install --target <dir> --re-pin
|
|
488
|
+
goblin install --target <dir> --upgrade
|
|
489
|
+
|
|
490
|
+
.goblin/bin/goblin-verify [--only <id[,id...]>] [--json] [--list]
|
|
491
|
+
.goblin/bin/goblin-audit # the only network step
|
|
492
|
+
.goblin/bin/goblin-bans # run the ban list
|
|
493
|
+
bin/goblin-model <role> # checkout-only; resolve a role to a profile (docs/ROLES.md)
|
|
494
|
+
|
|
495
|
+
### The 15 playbooks
|
|
496
|
+
|
|
497
|
+
Named procedures, installed as project-local skills. Each has a measurable verification step.
|
|
498
|
+
|
|
499
|
+
| | Playbook | Use it when |
|
|
500
|
+
|---|---|---|
|
|
501
|
+
| P1 | `goblin-investigation` | a read-only question, or "why is this happening" |
|
|
502
|
+
| P2 | `goblin-bugfix` | a reported defect |
|
|
503
|
+
| P3 | `goblin-feature` | new behaviour |
|
|
504
|
+
| P4 | `goblin-refactor` | a behaviour-preserving reshape |
|
|
505
|
+
| P5 | `goblin-tdd-repro` | a defect where a regression test is cheap |
|
|
506
|
+
| P6 | `goblin-verify-author` | a project has no live check lane, or its gates drift |
|
|
507
|
+
| P7 | `goblin-pr-gate` | anything that should be reviewed before landing |
|
|
508
|
+
| P8 | `goblin-bootstrap` | adopting goblin-stack, or starting a project |
|
|
509
|
+
| P9 | `goblin-handoff` | ending a session, or picking up another's |
|
|
510
|
+
| P10 | `goblin-overnight` | an unattended run over a predicate |
|
|
511
|
+
| P11 | `goblin-sweep` | the same change across many projects |
|
|
512
|
+
| P12 | `goblin-eval` | a skill or prompt changed — did it do anything? |
|
|
513
|
+
| P13 | `goblin-bugreporter` | an event delivered a report |
|
|
514
|
+
| P14 | `goblin-drift-audit` | a recorded claim disagrees with the artifact |
|
|
515
|
+
| P15 | `goblin-re-mobile` | one shipped Android build must be understood as facts for study |
|
|
516
|
+
|
|
517
|
+
### Where the real documentation lives
|
|
518
|
+
|
|
519
|
+
| File | Read it for |
|
|
520
|
+
|---|---|
|
|
521
|
+
| `docs/DESIGN.md` | the thesis and every rejected alternative |
|
|
522
|
+
| `docs/FLOWS.md` | the playbooks in full, with reasons |
|
|
523
|
+
| `docs/ENFORCEMENT.md` | the rule matrix, rendered for a human |
|
|
524
|
+
| `docs/LIMITS.md` | **what this cannot check** — read this one early |
|
|
525
|
+
| `docs/RISKS.md` | the risk register and non-goals |
|
|
526
|
+
| `docs/CONTRACTS.md` | exact interface, exit codes, uninstall |
|
|
527
|
+
| `docs/ADOPTION.md` | classes, presets, adoption order |
|
|
528
|
+
| `docs/CI.md`, `docs/LOOP.md`, `docs/GUARDRAILS.md` | the newer lanes |
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## 12. What this will not do for you
|
|
533
|
+
|
|
534
|
+
Stated plainly, because a guide that oversells its tool is worse than no guide:
|
|
535
|
+
|
|
536
|
+
- **It cannot force an agent that never reads `HANDOFF.md`.** It can only make the file exist,
|
|
537
|
+
structured and dated, so the reading is cheap.
|
|
538
|
+
- **It cannot prove your checks test the right thing.** A green suite that asserts the wrong
|
|
539
|
+
behaviour passes. Only the REPLAY habit (prove it goes red) catches that, and only if you do it.
|
|
540
|
+
- **It cannot see a real user's device.** A performance number measured on your machine is not a
|
|
541
|
+
user's experience, and the harness says so in its own output.
|
|
542
|
+
- **Ten of its rules are labelled `advisory`** — counted, not enforced, and capped at 10 of 10. They
|
|
543
|
+
are listed by name.
|
|
544
|
+
- **It is not a product.** It is a repository of files, installed into other repositories.
|
|
545
|
+
No service, no daemon, no support contract.
|
|
546
|
+
|
|
547
|
+
The current status, if you want the honest number: a separate review pass verified the artifact at
|
|
548
|
+
**9/10** (that was 0.4.2), and the point it withheld was not a missing feature — it was sentences
|
|
549
|
+
in the record that a measurement contradicted. `docs/LIMITS.md` is the list of what the harness
|
|
550
|
+
cannot see.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## 13. Where to go next
|
|
555
|
+
|
|
556
|
+
**If you only do one thing:** install into your most active repo today, set your real `gates:`, and
|
|
557
|
+
run `goblin-verify` once a day for a week. The habit, not the tool, is what produces the result.
|
|
558
|
+
|
|
559
|
+
**Then, in order:**
|
|
560
|
+
|
|
561
|
+
1. Point `practice:` at your existing house standard and pin it.
|
|
562
|
+
2. Write one `AC:` item that a script could check, and make it pass.
|
|
563
|
+
3. Add a ban for the one pattern you are tired of seeing in agent-written code
|
|
564
|
+
(`.goblin/manifest/bans.tsv` — a ban without a mechanism is a wish, so give it one).
|
|
565
|
+
4. When you have a bug that a test could catch, walk P5 (`goblin-tdd-repro`) end to end once.
|
|
566
|
+
|
|
567
|
+
**If you are sharing this with a team:** the parts that matter are `HANDOFF.md`, the `gates:` you
|
|
568
|
+
declare, and the REPLAY habit. The rest is optional machinery you can switch off per class. Lead
|
|
569
|
+
with *"prove it was broken first"* — it is the one practice that survives contact with a deadline.
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Appendix — a 45-minute first run, on one page
|
|
574
|
+
|
|
575
|
+
# 0. get it
|
|
576
|
+
npm i -g @techgoblin/gobstack
|
|
577
|
+
|
|
578
|
+
# 1. try it somewhere disposable
|
|
579
|
+
mkdir -p /tmp/gs-try && cd /tmp/gs-try
|
|
580
|
+
git init -b main
|
|
581
|
+
goblin install --target . --class A # expect: created 50
|
|
582
|
+
|
|
583
|
+
# 2. commit and check
|
|
584
|
+
git add -A && git commit -m "chore: install goblin-stack"
|
|
585
|
+
.goblin/bin/goblin-verify # expect: mostly PASS, some SKIP
|
|
586
|
+
|
|
587
|
+
# 3. make it yours
|
|
588
|
+
$EDITOR .goblin/goblin.yaml # branch, owner_email, and YOUR real gates:
|
|
589
|
+
|
|
590
|
+
# 4. prove a check can fail (the habit that matters) - the same block §7 runs
|
|
591
|
+
# REPLAY-BEGIN (this exact block is run by tests/t-doc-guide.sh - keep the two copies identical)
|
|
592
|
+
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
593
|
+
printf '\n<!-- a deliberate edit -->\n' >> .goblin/bans/README.md
|
|
594
|
+
.goblin/bin/goblin-verify --only IN-02 # expect FAIL
|
|
595
|
+
git stash push -- .goblin/bans/README.md # path-limited: your own edits stay put
|
|
596
|
+
.goblin/bin/goblin-verify --only IN-02 # expect PASS
|
|
597
|
+
git stash drop # the break was deliberate: discard it
|
|
598
|
+
# REPLAY-END
|
|
599
|
+
|
|
600
|
+
# 5. do it for real, in a repo you care about
|
|
601
|
+
cd ~/projects/your-project
|
|
602
|
+
goblin install --target . --class A
|
|
603
|
+
git add -A && git commit -m "chore: adopt goblin-stack"
|
|
604
|
+
.goblin/bin/goblin-verify
|
|
605
|
+
$EDITOR HANDOFF.md # state / gates (dated!) / next / NOT verified
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
*This guide is part of goblin-stack. If you find a step that does not work as written, that is a
|
|
610
|
+
defect in the guide — report it the same way you would report one in the code.*
|