@techgoblin/gobstack 0.5.0-beta.8 → 0.6.0-alpha.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +161 -124
  3. package/VERSION +1 -1
  4. package/automations/drift-audit.sh +4 -4
  5. package/bans/layer-check.sh +10 -8
  6. package/bin/goblin +69 -57
  7. package/bin/goblin-audit +11 -13
  8. package/bin/goblin-bans +11 -11
  9. package/bin/goblin-extras +342 -0
  10. package/bin/goblin-init +382 -711
  11. package/bin/goblin-install +160 -114
  12. package/bin/goblin-lib.sh +234 -1
  13. package/bin/goblin-map +226 -21
  14. package/bin/goblin-mcp.js +492 -0
  15. package/bin/goblin-model +4 -4
  16. package/bin/goblin-upgrade +1 -1
  17. package/bin/goblin-verify +159 -145
  18. package/bin/goblin.js +35 -51
  19. package/docs/ADOPTION.md +15 -15
  20. package/docs/CONTRACTS.md +16 -15
  21. package/docs/DESIGN.md +1 -1
  22. package/docs/ENFORCEMENT.md +89 -90
  23. package/docs/FLOWS.md +1 -1
  24. package/docs/GLOSSARY.md +3 -3
  25. package/docs/GUARDRAILS.md +5 -5
  26. package/docs/GUIDE.md +194 -177
  27. package/docs/INTEGRATION.md +1 -1
  28. package/docs/LIMITS.md +25 -0
  29. package/docs/LOOP.md +12 -12
  30. package/docs/RE-PLAYBOOK.md +3 -3
  31. package/docs/ROLES.md +5 -5
  32. package/extras-catalogue/catalogue.tsv +42 -0
  33. package/extras-catalogue/payload/README.md +14 -0
  34. package/extras-catalogue/payload/taste-skill/taste/REFERENCE.md +3 -0
  35. package/extras-catalogue/payload/taste-skill/taste/SKILL.md +9 -0
  36. package/manifest/bans.tsv +8 -8
  37. package/manifest/classes.tsv +3 -3
  38. package/manifest/enforcement.tsv +40 -40
  39. package/manifest/glossary.tsv +3 -3
  40. package/manifest/playbooks.tsv +1 -1
  41. package/package.json +3 -1
  42. package/presets/electron-overlay.yaml +2 -2
  43. package/presets/fleet.yaml +8 -7
  44. package/presets/game.yaml +1 -1
  45. package/presets/research.yaml +1 -1
  46. package/presets/service.yaml +1 -1
  47. package/presets/software.yaml +1 -1
  48. package/skills/goblin-bootstrap/SKILL.md +2 -2
  49. package/templates/AGENTS.md.tmpl +8 -18
  50. package/templates/HANDOFF.md.tmpl +5 -5
  51. package/templates/agents-block.tmpl +45 -0
  52. package/templates/audit-waiver.tsv.tmpl +2 -2
  53. package/templates/boundary-waivers.tmpl +1 -1
  54. package/templates/checks/gate.sh.tmpl +6 -6
  55. package/templates/install-hooks.allowlist.tmpl +1 -1
  56. package/templates/ci/goblin-gate.yml.tmpl +0 -46
  57. package/templates/goblin.yaml.tmpl +0 -146
  58. package/templates/loop/decisions.tsv.tmpl +0 -1
  59. package/templates/loop/predicate.tmpl +0 -16
@@ -78,7 +78,7 @@ test that no shipped skill tells a worker to reach another profile with a bare s
78
78
 
79
79
  ## The referenced standard
80
80
 
81
- Referenced and hash-pinned, never moved, never superseded, never vendored. `.goblin/goblin.yaml`
81
+ Referenced and hash-pinned, never moved, never superseded, never vendored. `.gob/goblin.yaml`
82
82
  carries `practice:` and `practice_sha256:`; `goblin-verify` compares. The `practice` skill's body
83
83
  is a pointer and a mandate — read the standard at the configured path before starting work; if
84
84
  the path is absent, say so and continue with the goblin-stack rules alone.
package/docs/LIMITS.md CHANGED
@@ -607,3 +607,28 @@ the Node the gates ran under.
607
607
  repo gets the merge's declaration-time host-gate check only after hand-adding `electron: true`;
608
608
  its bans and host gate keep running either way, so nothing fails closed, and the CLI keeps
609
609
  accepting `desktop`/`F` as aliases.
610
+
611
+ 54. **The vendored engine wins over a global `engine_dir` by design, and in v2 that is the
612
+ whole story.** The resolution chain (goblin-verify §"engine resolution") puts
613
+ `$ROOT/.gob/...` at stage 2 and a declared `engine_dir:` at stage 3, so a repo carrying
614
+ BOTH judges itself with the vendored manifest — the thing `IN-02` hashes — and never with
615
+ the global one. That is not a leftover: the pre-v2 hazard (a machine-level upgrade
616
+ silently re-judging repos through a shadowing global engine) is closed the same way the
617
+ pin closes silent re-pinning — the rules a repo is judged by must be the rules its own
618
+ install record hashes, and any change to them is a diff the repo owner reviews. The
619
+ corresponding suite (`t-engine-dir.sh`) is deleted with the v2 surface cut rather than
620
+ migrated: v2 has no `upgrade` command to move a global engine under a repo, and the
621
+ global-engine lane itself is session-2/3 scope — when it returns it returns re-measured
622
+ against the AGENTS.md frontmatter shape.
623
+
624
+ 55. **CI is out of the v2 product, and `docs/CI.md` is its LIMITS candidate, not a kept promise.**
625
+ v2 installs nothing under `.github/` — the `ci-gate` part carries `-` for every class in
626
+ `manifest/classes.tsv` (kept as the recorded W4 remnant), `PG-05`/`PG-06` SKIP with no
627
+ workflow to read, and no flag configures the lane. The file that carried the lane's contract
628
+ (`docs/CI.md`: the four forge settings that make a workflow a gate, the Electron perf
629
+ deviation) still exists in the checkout as REFERENCE — the argument it makes ("a required
630
+ check that reports Success after skipping its gate is a green light for a commit whose gate
631
+ never ran") is about CI in general and survives the cut — but no document the README's table
632
+ promises sends a reader there, and the lane returns with the CI surface in a later alpha or
633
+ not at all. What this admits: the verifier's "cannot see" footer still names the CI lane's
634
+ blind spots, for the day the lane comes back re-measured.
package/docs/LOOP.md CHANGED
@@ -58,18 +58,18 @@ One unattended run, one committed record. `templates/loop/` ships a copy-ready `
58
58
  `decisions.tsv` header; nothing installs them, because a fresh install must not be born with a
59
59
  loop record — every `LP-` row skips until a loop has actually run here.
60
60
 
61
- .goblin/loop/predicate one command; exits 0 == the loop is finished
62
- .goblin/loop/predicate.sha256 its digest, recorded at loop start (the pin); after a close,
61
+ .gob/loop/predicate one command; exits 0 == the loop is finished
62
+ .gob/loop/predicate.sha256 its digest, recorded at loop start (the pin); after a close,
63
63
  the chain follows on `previous: <digest>` lines
64
- .goblin/loop/first-run exit=<n> ts=<ISO8601>, run BEFORE iteration 1
65
- .goblin/loop/budget the turn budget this run declares
66
- .goblin/loop/decisions.tsv ts · phase · decision · why · evidence · result
67
- .goblin/loop/stuck.md the write-up when the run ended without predicate:green
68
- .goblin/loop/closed-<date>/ a relaxed predicate AND the pin it was closed under, archived
64
+ .gob/loop/first-run exit=<n> ts=<ISO8601>, run BEFORE iteration 1
65
+ .gob/loop/budget the turn budget this run declares
66
+ .gob/loop/decisions.tsv ts · phase · decision · why · evidence · result
67
+ .gob/loop/stuck.md the write-up when the run ended without predicate:green
68
+ .gob/loop/closed-<date>/ a relaxed predicate AND the pin it was closed under, archived
69
69
 
70
70
  An iteration row's `evidence` is a **pointer that resolves**: `sha:<hex>` (a commit in
71
71
  `git rev-list --all`), `file:<path>` (a path under the root), `sha256:<hex>` (the digest of a file
72
- under `.goblin/loop/`). A `cmd:` token resolves **nothing** on purpose: the command ran, its
72
+ under `.gob/loop/`). A `cmd:` token resolves **nothing** on purpose: the command ran, its
73
73
  output is not in the record, and a verdict resting on it is the self-report `JG-01` refuses.
74
74
 
75
75
  ## 4. The five rules
@@ -80,7 +80,7 @@ output is not in the record, and a verdict resting on it is the self-report `JG-
80
80
  green mean anything (`LP-01`).
81
81
  2. **Never relax.** The predicate's digest is recorded at loop start (`LP-02`). Changing it
82
82
  mid-loop is not an edit; it is closing this loop and opening another, with the old predicate
83
- **and the pin it was closed under** archived under `.goblin/loop/closed-<date>/` and committed,
83
+ **and the pin it was closed under** archived under `.gob/loop/closed-<date>/` and committed,
84
84
  and the new `predicate.sha256` naming the archived digest on a `previous: <digest>` line. That
85
85
  chain is what `LP-02` checks: not that the new bar is as strong — no digest can say that
86
86
  (`docs/LIMITS.md` #39) — but that the supersession is **recorded**, so a silent relaxation is a
@@ -89,13 +89,13 @@ output is not in the record, and a verdict resting on it is the self-report `JG-
89
89
  "because a self-updating pin would be the silent edit it exists to catch"
90
90
  (`docs/CONTRACTS.md`).
91
91
  3. **The escape hatch is a write-up, not a silence.** A run whose last row's `result` is not
92
- `predicate:green` carries `.goblin/loop/stuck.md` — at least three non-blank lines naming the
92
+ `predicate:green` carries `.gob/loop/stuck.md` — at least three non-blank lines naming the
93
93
  predicate (`LP-05`). Wording matters: the exits a worker can actually reach are `kanban_block`
94
94
  and `kanban_complete`; **there is no `kanban_edit` tool** in the worker toolset, so the remedy
95
95
  the rejection message names is not one a worker holds. `kanban_block` names the predicate, and
96
96
  the write-up makes the stop visible.
97
97
  4. **The budget.** `goal_max_turns` on the card, mirrored into the record, capped by
98
- `loop_max_turns_ceiling` in `.goblin/goblin.yaml`, default **20** — the value measured at
98
+ `loop_max_turns_ceiling` in `.gob/goblin.yaml`, default **20** — the value measured at
99
99
  `hermes_cli/goals.py:31`. A ceiling that does not match the engine's own default is a number
100
100
  someone made up (`LP-03`).
101
101
  5. **The morning audit reads the rows that did not reach the goal, in order**, then the last five
@@ -123,7 +123,7 @@ repo can resolve and re-check tomorrow (`JG-01`).
123
123
 
124
124
  **What does a genuinely stuck loop do?** Today: it burns the budget and **blocks for review**,
125
125
  carrying only the last judge reason, which came from a self-report (`hermes_cli/goals.py:1692-1695`).
126
- Approved shape: write `.goblin/loop/stuck.md`, commit it, `kanban_block` naming the predicate.
126
+ Approved shape: write `.gob/loop/stuck.md`, commit it, `kanban_block` naming the predicate.
127
127
  `LP-05` makes the write-up mandatory and therefore visible; **nothing can make it true.**
128
128
 
129
129
  ## 6. The judge's failure modes, and what is mechanical
@@ -77,7 +77,7 @@ target + version + source store + anchor, and the **apk row itself**. This `.sha
77
77
  manifest is the sha256sum-format artifact: it is what `sha256sum -c` verifies and what
78
78
  `RC-03`/`RC-04` read. It is **not** what `RC-01`/`RC-02` consume - those rows parse a
79
79
  separate `reference-manifest/1` **JSON** (`generated_from`, `reference_app`, `entries[]`,
80
- `entry_count`), declared via `reference_manifest:` in `.goblin/goblin.yaml`. That JSON is an
80
+ `entry_count`), declared via `reference_manifest:` in `.gob/goblin.yaml`. That JSON is an
81
81
  **optional** artifact, produced only if this lab ever ships a build output that could carry
82
82
  corpus bytes; a study-only lab never ships, so none is produced here - a deliberate
83
83
  omission, not a gap. Near-duplicate manifest rows are not linted - a known limitation.
@@ -113,14 +113,14 @@ still be scripts/notes/manifests/docs only, and no tracked file may hash to a ma
113
113
  ## Verifying the lab repo - the harness must be installed there first
114
114
 
115
115
  `goblin-verify` requires an **installed harness** in the repo it points at: against a bare
116
- lab repo it exits 2 with `not installed: <root>/.goblin/goblin.yaml is absent`. `--source`
116
+ lab repo it exits 2 with `not installed: <root>/.gob/goblin.yaml is absent`. `--source`
117
117
  relocates the enforcement manifest, not the target's config requirement, so the bridge is a
118
118
  one-time install **into the lab repo itself**:
119
119
 
120
120
  goblin-install --target <lab-repo> --class A
121
121
 
122
122
  A class-A install is sufficient and violates nothing - the lab's own files are not touched;
123
- it only adds the harness scaffolding: `.goblin/` (config + the verifier), `checks/`,
123
+ it only adds the harness scaffolding: `.gob/` (config + the verifier), `checks/`,
124
124
  `reviews/`, `.github/workflows/`, and the `HANDOFF.md` scaffolding. Note: `HANDOFF.md`'s
125
125
  placeholder commit gives `HP-05`'s one expected day-one red until a real commit is named.
126
126
  After that one-time install, `goblin-verify --only RC-03` / `RC-04` run against the lab repo
package/docs/ROLES.md CHANGED
@@ -32,7 +32,7 @@ disjoint from the author's (`JG-02`).
32
32
  ## The model-mapping contract
33
33
 
34
34
  The machine-specific mapping (`profile → provider/model/effort`) is read at run time from the
35
- file declared as `models_file:` in `.goblin/goblin.yaml`. That is **the single documented
35
+ file declared as `models_file:` in `.gob/goblin.yaml`. That is **the single documented
36
36
  install-time machine input**, and goblin-stack **reads it and never writes it**.
37
37
 
38
38
  Why not ship a copy of the mapping: the mapping file is a live routing source owned and
@@ -47,10 +47,10 @@ Resolution:
47
47
  bin/goblin-model review-panel # the panel: one line per lane
48
48
 
49
49
  `bin/goblin-model` is **checkout-only**. `goblin-install` copies four scripts into a target's
50
- `.goblin/bin/` — `goblin-verify`, `goblin-lib.sh`, `goblin-audit` and `goblin-bans` — so an
51
- adopted repo has no `goblin-model` command (`ls .goblin/bin/` →
50
+ `.gob/bin/` — `goblin-verify`, `goblin-lib.sh`, `goblin-audit` and `goblin-bans` — so an
51
+ adopted repo has no `goblin-model` command (`ls .gob/bin/` →
52
52
  `goblin-audit goblin-bans goblin-lib.sh goblin-verify`). The installed path
53
- for the same resolution is the `resolve_role_models` helper inside `.goblin/bin/goblin-verify`,
53
+ for the same resolution is the `resolve_role_models` helper inside `.gob/bin/goblin-verify`,
54
54
  which is what `MD-02` calls; `bin/goblin-model` exists for a human at a checkout, is covered only
55
55
  by `bash -n` in `tests/run-tests.sh`, and has no `enforcement.tsv` row because it enforces
56
56
  nothing — it prints. Naming it here is the alternative R6 §2.2 allows to shipping a row for it.
@@ -58,7 +58,7 @@ nothing — it prints. Naming it here is the alternative R6 §2.2 allows to ship
58
58
  Absent on this machine, every role resolves to `unknown` and the model-dependent checks report
59
59
  advisory. Absent is not an error — goblin-stack is portable, and another machine has no such
60
60
  file. That is the one documented exception to the portability rule, and `PT-01` enforces it:
61
- the path is a config *value* in `.goblin/goblin.yaml`, never a literal inside a rule.
61
+ the path is a config *value* in `.gob/goblin.yaml`, never a literal inside a rule.
62
62
 
63
63
  ## The fan-out rule: role-pinned work goes through the kanban
64
64
 
@@ -0,0 +1,42 @@
1
+ id category kind name source_repo path_within_repo has_skill_md license stars last_push matches conflicts requires install_hint verdict reviewed_by reviewed_date
2
+ taste-skill frontend skill taste-skill Leonxlnx/taste-skill . yes MIT 93730 2026-10-07 react nextjs vue nuxt frontend ui design - none copy-folder RECOMMEND harvey 2026-10-08
3
+ vercel-agent-skills frontend skill vercel-labs agent skills (react pack) vercel-labs/agent-skills skills/ skill none (verify per-skill) 32078 2026-08-28 react nextjs frontend ui design - none copy-folder RECOMMEND harvey 2026-10-08
4
+ anthropic-frontend-design frontend skill frontend-design (anthropics/skills) anthropics/skills frontend-design yes none (verify per-skill) 180150 2026-10-05 frontend ui react vue design - none copy-folder RECOMMEND harvey 2026-10-08
5
+ vuejs-skills frontend skill vuejs-ai skills (vue 3 pack) vuejs-ai/skills skills/ skill MIT 2889 2026-05-30 vue nuxt frontend - none copy-folder RECOMMEND harvey 2026-10-08
6
+ ux-ui-agent-skills frontend skill ux-ui agent skills (design tokens, WCAG) plugin87/ux-ui-agent-skills skills/ skill MIT 1551 2026-10-08 frontend ui design accessibility - none copy-folder RECOMMEND harvey 2026-10-08
7
+ impeccable frontend skill impeccable (design language) pbakaus/impeccable .dotagent-dir/ skill Apache-2.0 78552 2026-10-07 frontend ui design - none copy-folder RECOMMEND harvey 2026-10-08
8
+ playwright-mcp testing mcp Playwright MCP (Microsoft, official) microsoft/playwright-mcp - no Apache-2.0 37929 2026-10-08 playwright e2e testing browser automation - node >= 18 npx @playwright/mcp@latest RECOMMEND harvey 2026-10-08
9
+ chrome-devtools-mcp testing mcp Chrome DevTools MCP (official) ChromeDevTools/chrome-devtools-mcp - no Apache-2.0 53117 2026-10-08 browser debugging cdp e2e performance - node >= 18 npx chrome-devtools-mcp@latest RECOMMEND harvey 2026-10-08
10
+ playwright-skill testing skill playwright-skill (no MCP registration) lackeyjb/playwright-skill . yes MIT 3180 2026-10-01 playwright e2e testing - playwright installed copy-folder RECOMMEND harvey 2026-10-08
11
+ webapp-testing testing skill webapp-testing (anthropics/skills) anthropics/skills webapp-testing yes none (verify per-skill) 180150 2026-10-05 e2e testing browser playwright - playwright installed copy-folder RECOMMEND harvey 2026-10-08
12
+ godot-ai game mcp Godot AI (hi-godot) hi-godot/godot-ai - no MIT 2862 2026-10-08 godot game - see repo README see repo README RECOMMEND harvey 2026-10-08
13
+ godot-mcp game mcp Godot MCP (community default) Coding-Solo/godot-mcp - no MIT 5976 2026-04-16 godot game godot-ai see repo README see repo README RECOMMEND harvey 2026-10-08
14
+ godotprompter game skill GodotPrompter (66 godot skills) jame581/GodotPrompter skills/ skill MIT 798 2026-10-06 godot game - none copy-folder RECOMMEND harvey 2026-10-08
15
+ unity-mcp game mcp Unity MCP (CoplayDev) CoplayDev/unity-mcp - no MIT 14767 2026-10-07 unity game - unity editor + see repo README see repo README RECOMMEND harvey 2026-10-08
16
+ awesome-gamedev-skills game skill awesome gamedev agent skills (74 skills, harvest source) gamedev-skills/awesome-gamedev-agent-skills skills/ skill Apache-2.0 1365 2026-09-27 game godot unity unreal phaser threejs bevy pygame - none copy-folder RECOMMEND harvey 2026-10-08
17
+ supabase-agent-skills node skill Supabase agent skills (official) supabase/agent-skills skills/ skill MIT 2708 2026-10-02 node backend supabase database auth - none copy-folder RECOMMEND harvey 2026-10-08
18
+ pg-aiguide node mcp pg-aiguide (Timescale, postgres) timescale/pg-aiguide - no Apache-2.0 1860 2026-10-07 node postgres database backend - see repo README see repo README RECOMMEND harvey 2026-10-08
19
+ hono-skills node skill Hono agent skills (official) honojs/skills skills/ skill MIT 13 2026-10-08 node hono backend api - none copy-folder RECOMMEND harvey 2026-10-08
20
+ superdesign-skill frontend skill superdesign-skill (design iteration loop) superdesigndev/superdesign-skill . yes MIT 633 2026-08-21 frontend ui design taste-skill vercel-agent-skills none copy-folder MAYBE harvey 2026-10-08
21
+ garden-skills frontend skill garden-skills (collection, harvest source) ConardLi/garden-skills skills/ skill MIT 12771 2026-07-12 frontend ui design - none copy-folder MAYBE harvey 2026-10-08
22
+ dev-browser testing skill dev-browser (general web browser skill) SawyerHood/dev-browser . yes MIT 6656 2026-09-05 browser e2e testing playwright-mcp none copy-folder MAYBE harvey 2026-10-08
23
+ currents-playwright-best-practices testing skill playwright best practices (Currents.dev) currents-dev/playwright-best-practices-skill . yes MIT 393 2026-07-21 playwright e2e testing testdino-playwright-skill none copy-folder MAYBE harvey 2026-10-08
24
+ testdino-playwright-skill testing skill playwright best practices (TestDino) testdino-hq/playwright-skill . yes MIT 389 2026-09-06 playwright e2e testing currents-playwright-best-practices none copy-folder MAYBE harvey 2026-10-08
25
+ unity-mcp-alt game mcp Unity-MCP (IvanMurzak, skills + CLI) IvanMurzak/Unity-MCP - no Apache-2.0 4400 2026-10-04 unity game unity-mcp unity editor + see repo README see repo README MAYBE harvey 2026-10-08
26
+ cc-game-studios game workflow Game-Studios mega-pack (49 agents, 72 skills) Donchitos/Cc-S-B . skill MIT 25900 2026-10-08 game godot unity - none copy-folder MAYBE harvey 2026-10-08
27
+ godot-ui-integration game skill godot-ui-integration (design to godot UI) zimo-xiao-zheng/godot-ui-integration . yes MIT 275 2026-08-23 godot game ui - none copy-folder MAYBE harvey 2026-10-08
28
+ agent-nestjs-skills node skill NestJS agent skills Kadajett/agent-nestjs-skills skills/ skill none (verify) 289 2026-07-23 node nestjs backend api - none copy-folder MAYBE harvey 2026-10-08
29
+ openapi-to-skills node skill openapi-to-skills (spec to skills generator) neutree-ai/openapi-to-skills . skill Apache-2.0 342 2026-06-23 node api openapi - none copy-folder MAYBE harvey 2026-10-08
30
+ do-app-platform-skills node skill DigitalOcean App Platform skills digitalocean-labs/do-app-platform-skills skills/ skill MIT 37 2026-10-04 node deployment database - none copy-folder MAYBE harvey 2026-10-08
31
+ agent-skills-standard node skill agent-skills-standard (per-framework best practice) HoangNguyen0403/agent-skills-standard skills/ skill MIT 572 2026-10-08 node backend typescript - none copy-folder MAYBE harvey 2026-10-08
32
+ frontend-design-toolkit frontend workflow Frontend-Design-Toolkit (list, harvest source) wilwaldon/cc-frontend-design-toolkit . skill none 1176 2026-04-11 frontend ui design - none copy-folder SKIP harvey 2026-10-08
33
+ nuxt-skills frontend skill nuxt-skills (vue/nuxt/nuxthub) onmax/nuxt-skills skills/ skill none 719 2026-10-06 vue nuxt frontend - none copy-folder SKIP harvey 2026-10-08
34
+ design-audit frontend skill design-audit (UI/UX checklist) wonjyou/design-audit . yes none 8 2026-03-28 frontend ui design - none copy-folder SKIP harvey 2026-10-08
35
+ vibe-coded-website-review frontend skill vibe-coded-website-review (YC design review) zuocharles/vibe-coded-website-review . yes none 0 2026-03-29 frontend ui design - none copy-folder SKIP harvey 2026-10-08
36
+ qa-skills testing workflow qa-skills (e2e generation pipeline) neonwatty/qa-skills . skill MIT 33 2026-05-03 e2e testing - none copy-folder SKIP harvey 2026-10-08
37
+ agentmantis-test-skills testing skill test-skills (playwright e2e pack) agentmantis/test-skills skills/ skill MIT 12 2026-04-08 playwright e2e testing - none copy-folder SKIP harvey 2026-10-08
38
+ invisible-playwright-mcp testing mcp invisible_playwright_mcp (anti-detect) feder-cr/invisible_playwright_mcp - no none 2660 2026-10-08 browser e2e playwright - see repo README see repo README SKIP harvey 2026-10-08
39
+ mcp-unity game mcp mcp-unity (CoderGamester, superseded) CoderGamester/mcp-unity - no MIT 1922 2026-09-03 unity game unity-mcp unity editor + see repo README see repo README SKIP harvey 2026-10-08
40
+ godot-mcp-native game mcp Godot-MCP-Native (duplicate function) yurineko73/Godot-MCP-Native - no MIT 836 2026-08-03 godot game godot-mcp see repo README see repo README SKIP harvey 2026-10-08
41
+ fastify-skills node skill fastify-skills (undocumented) TheCodePace/fastify-skills skills/ skill none 12 2026-10-08 node fastify backend - none copy-folder SKIP harvey 2026-10-08
42
+ drizzle-orm-skill node skill drizzle-orm-skill (no credible alternative exists) oldirty/drizzle-orm-skill . yes none 0 2026-02-24 node drizzle orm database - none copy-folder SKIP harvey 2026-10-08
@@ -0,0 +1,14 @@
1
+ # extras-catalogue/payload/ — the vendored extras payload
2
+
3
+ `gob extras install` needs **no network**: every skill or workflow it writes is copied
4
+ from this directory. One directory per catalogue id:
5
+
6
+ payload/<id>/<skill-name>/SKILL.md # a skill row: one directory per skill
7
+ payload/<id>/… # a workflow row: the playbook files verbatim
8
+
9
+ `mcp` rows carry no payload — their install is the generated mcp.json snippet.
10
+
11
+ Until a row's payload is vendored, installing it is REFUSED with exit 2 and a named fix
12
+ (never a download): vendor the accepted upstream copy here first, respecting the row's
13
+ license cell. The placeholder payloads shipped today mark the layout only — the curator
14
+ replaces them with the reviewed upstream bytes.
@@ -0,0 +1,3 @@
1
+ # taste — reference
2
+
3
+ Placeholder reference page for the vendored payload layout.
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: taste
3
+ description: Design-taste guardrails so agent UI output is not generic. Vendored from the catalogue row 'taste-skill'; replace with the curated upstream copy.
4
+ ---
5
+ # taste
6
+
7
+ Vendored placeholder payload — the curator replaces this directory with the accepted
8
+ upstream skill (catalogue row `taste-skill`, MIT). Layout contract: one directory per
9
+ skill, each with a SKILL.md.
package/manifest/bans.tsv CHANGED
@@ -1,9 +1,9 @@
1
1
  id ban globs detect replacement escape reviewer source
2
- BN-01 No `any` in application TypeScript. src bash .goblin/bans/grep-ban.sh -e ':[[:space:]]*any\b' -e '<any>' -e '<any,' -e ',any>' -e 'any\[\]' src Use the real type, or narrow an `unknown` at the boundary `// BAN-OK(BN-01): <reason>` on the offending line (a non-empty reason is required), or list the path under bans_exempt: architecture R2 section A12 (Dune rule 2); R1 section 3
3
- BN-02 No `@ts-ignore` / `@ts-expect-error` suppressions. src bash .goblin/bans/grep-ban.sh -e '@ts-(ignore|expect-error|nocheck)' src Fix the type, or narrow with `unknown` at the boundary `// BAN-OK(BN-02): <reason>` architecture R1 section 6 (suppressions are stink)
4
- BN-03 No direct network call from a component. src/components app bash .goblin/bans/grep-ban.sh -e '\bfetch[[:space:]]*\(' src/components app Put the call in the data layer (src/data/*) `// BAN-OK(BN-03): <the endpoint, and when it migrates>` architecture R2 section A12; E1 (overreach)
5
- BN-05 No import across a declared layer boundary. src bash .goblin/bans/layer-check.sh Move the code, or import through the declared accessor declare the pair under layers:, or move the file architecture R2 section A12 (Dune's dependency-graph check)
6
- BN-06 No renderer with Node access (`nodeIntegration: true`). src app electron bash .goblin/bans/grep-ban.sh -e 'nodeIntegration[[:space:]]*:[[:space:]]*true' src app electron Pass data over IPC through a preload script (contextBridge); leave nodeIntegration false `// BAN-OK(BN-06): <why the renderer needs a Node primitive>` architecture G6 section B.2 item 1 (Electron security S13/S14)
7
- BN-07 No renderer with context isolation or the process sandbox turned off. src app electron bash .goblin/bans/grep-ban.sh -e 'contextIsolation[[:space:]]*:[[:space:]]*false' -e 'sandbox[[:space:]]*:[[:space:]]*false' src app electron Keep `contextIsolation: true` and `sandbox: true`; expose a narrow API from the preload script `// BAN-OK(BN-07): <why isolation is off, and what replaces it>` architecture G6 section B.2 item 2 (Electron security S14)
8
- BN-08 No dangerous webPreferences. src app electron bash .goblin/bans/grep-ban.sh -e 'webSecurity[[:space:]]*:[[:space:]]*false' -e 'allowRunningInsecureContent[[:space:]]*:[[:space:]]*true' -e 'enableBlinkFeatures' -e 'allowpopups' src app electron Fix the origin or the CSP; a disabled web security model is not a workaround `// BAN-OK(BN-08): <the check, and when it goes>` architecture G6 section B.2 item 9 (Electron security S14 checklist items 6, 8, 10, 11)
9
- BN-09 No synchronous IPC and no `@electron/remote`. src app electron bash .goblin/bans/grep-ban.sh -e 'sendSync[[:space:]]*\(' -e '@electron/remote' src app electron Use `ipcRenderer.invoke` / `ipcMain.handle` (async), and a preload-exposed API instead of @electron/remote `// BAN-OK(BN-09): <why a blocking call is the only option here>` architecture G6 section B.2 item 11 (Electron performance S13)
2
+ BN-01 No `any` in application TypeScript. src bash .gob/bans/grep-ban.sh -e ':[[:space:]]*any\b' -e '<any>' -e '<any,' -e ',any>' -e 'any\[\]' src Use the real type, or narrow an `unknown` at the boundary `// BAN-OK(BN-01): <reason>` on the offending line (a non-empty reason is required), or list the path under bans_exempt: architecture R2 section A12 (Dune rule 2); R1 section 3
3
+ BN-02 No `@ts-ignore` / `@ts-expect-error` suppressions. src bash .gob/bans/grep-ban.sh -e '@ts-(ignore|expect-error|nocheck)' src Fix the type, or narrow with `unknown` at the boundary `// BAN-OK(BN-02): <reason>` architecture R1 section 6 (suppressions are stink)
4
+ BN-03 No direct network call from a component. src/components app bash .gob/bans/grep-ban.sh -e '\bfetch[[:space:]]*\(' src/components app Put the call in the data layer (src/data/*) `// BAN-OK(BN-03): <the endpoint, and when it migrates>` architecture R2 section A12; E1 (overreach)
5
+ BN-05 No import across a declared layer boundary. src bash .gob/bans/layer-check.sh Move the code, or import through the declared accessor declare the pair under layers:, or move the file architecture R2 section A12 (Dune's dependency-graph check)
6
+ BN-06 No renderer with Node access (`nodeIntegration: true`). src app electron bash .gob/bans/grep-ban.sh -e 'nodeIntegration[[:space:]]*:[[:space:]]*true' src app electron Pass data over IPC through a preload script (contextBridge); leave nodeIntegration false `// BAN-OK(BN-06): <why the renderer needs a Node primitive>` architecture G6 section B.2 item 1 (Electron security S13/S14)
7
+ BN-07 No renderer with context isolation or the process sandbox turned off. src app electron bash .gob/bans/grep-ban.sh -e 'contextIsolation[[:space:]]*:[[:space:]]*false' -e 'sandbox[[:space:]]*:[[:space:]]*false' src app electron Keep `contextIsolation: true` and `sandbox: true`; expose a narrow API from the preload script `// BAN-OK(BN-07): <why isolation is off, and what replaces it>` architecture G6 section B.2 item 2 (Electron security S14)
8
+ BN-08 No dangerous webPreferences. src app electron bash .gob/bans/grep-ban.sh -e 'webSecurity[[:space:]]*:[[:space:]]*false' -e 'allowRunningInsecureContent[[:space:]]*:[[:space:]]*true' -e 'enableBlinkFeatures' -e 'allowpopups' src app electron Fix the origin or the CSP; a disabled web security model is not a workaround `// BAN-OK(BN-08): <the check, and when it goes>` architecture G6 section B.2 item 9 (Electron security S14 checklist items 6, 8, 10, 11)
9
+ BN-09 No synchronous IPC and no `@electron/remote`. src app electron bash .gob/bans/grep-ban.sh -e 'sendSync[[:space:]]*\(' -e '@electron/remote' src app electron Use `ipcRenderer.invoke` / `ipcMain.handle` (async), and a preload-exposed API instead of @electron/remote `// BAN-OK(BN-09): <why a blocking call is the only option here>` architecture G6 section B.2 item 11 (Electron performance S13)
@@ -44,8 +44,8 @@ service tokens -
44
44
  game tokens -
45
45
  research tokens -
46
46
  fleet tokens O
47
- software ci-gate R
47
+ software ci-gate -
48
48
  service ci-gate -
49
- game ci-gate O
49
+ game ci-gate -
50
50
  research ci-gate -
51
- fleet ci-gate O
51
+ fleet ci-gate -
@@ -1,28 +1,28 @@
1
1
  id scope rule enforced_by artifact check if_not_why
2
- IN-01 target The install exists and records its version + every file's hash. script .goblin/installed.json goblin-verify --only IN-01 — (W1: the check is a builtin so the engine.mode=global clause can run — a global, declaration-only repo has no install record and SKIPs with `global engine mode — no per-repo install record`; the vendored clauses are the old one-liner: the record exists and names its version)
3
- IN-02 target Every installed file still matches its recorded hash. script .goblin/installed.json goblin-verify --only IN-02 recover: gob install --upgrade, or git checkout -- .goblin/installed.json; for a file it names drifted: git checkout -- <path> (if the edit is yours and intended, re-pin it: gob install --target <dir> --re-pin)
2
+ IN-01 target The install exists and records its version + every file's hash. script .gob/installed.json goblin-verify --only IN-01 — (W1: the check is a builtin so the engine.mode=global clause can run — a global, declaration-only repo has no install record and SKIPs with `global engine mode — no per-repo install record`; the vendored clauses are the old one-liner: the record exists and names its version)
3
+ IN-02 target Every installed file still matches its recorded hash. script .gob/installed.json goblin-verify --only IN-02 recover: gob install --upgrade, or git checkout -- .gob/installed.json; for a file it names drifted: git checkout -- <path> (if the edit is yours and intended, re-pin it: gob install --target <dir> --re-pin)
4
4
  IN-03 target The verifier's own manifest is complete: every rule has a check or is advisory. script manifest/enforcement.tsv goblin-verify --only IN-03 — (this row is the reason the matrix cannot rot; the second clause is D6's shape in general: a row that carries no check must be labelled advisory, or it claims verification it does not perform. The third clause is Z1-5: `enforced_by` is documented as a closed enum in docs/ENFORCEMENT.md and was read by NOTHING, so a typo in that cell changed nothing - `script|lint|gate|advisory` plus `test`, the source-scope value whose check is tests/run-tests.sh. W1: the check runs against whichever manifest the engine actually resolved (the chain in bin/goblin-verify), no longer the hardcoded per-repo path — the rule's meaning is untouched, only the path input follows the engine)
5
- IN-04 target No file goblin-stack did not create has been overwritten. script .goblin/installed.json goblin-verify --only IN-04 Detects a file the installer recorded as pre-existing (a `refused` entry) that has since vanished, or that is listed as installed anyway. The second clause is an internal-consistency guard: with correct code a refused path is never written, so it fires only if the installer regresses. The negative control exercises the vanished branch.
5
+ IN-04 target No file goblin-stack did not create has been overwritten. script .gob/installed.json goblin-verify --only IN-04 Detects a file the installer recorded as pre-existing (a `refused` entry) that has since vanished, or that is listed as installed anyway. The second clause is an internal-consistency guard: with correct code a refused path is never written, so it fires only if the installer regresses. The negative control exercises the vanished branch.
6
6
  HP-01 target HANDOFF.md exists at the root. gate HANDOFF.md test -f HANDOFF.md —
7
7
  HP-02 target The HANDOFF carries its five required sections. lint HANDOFF.md 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 Partial: proves a heading exists whose first word names the slot, not that the section's content is complete. The slot may use the repo's own vocabulary (the model repo's `### NEXT` passes; `Status`, `Gate`, `Unverified`, `Pending <x> device test` are accepted); a heading that merely contains the word (`## BOARD STATE`) does not satisfy `State`.
8
- HP-03 target Every gate number is a measurement with a date, never a copy. lint HANDOFF.md goblin-verify --only HP-03 Builtin, anchored on the gate NAMES `.goblin/goblin.yaml` declares (GT-01's source of truth) rather than on a hardcoded keyword list: the shipped gates are named `commit` and `todo_ceiling`, neither of which the old list matched, so on a fresh install the only line it could see was the template's own example sentence, and a real gate line could lose its `date` and the row stayed GREEN (G8-2). A line whose text says "example of the required form" is template prose, not a gate number, and is skipped, so the template cannot satisfy the row. Partial: it proves a dated line inside the `Gates` section exists and that every gate-bearing line there carries a date - not that the number was re-measured that day, and not a gate-bearing line that names no declared gate and carries no gate-shaped keyword (so a line the config does not declare and the keyword list does not recognise is unseen). Historical gate lines outside that section are exempt by design - PROJECT-PRACTICE section 1's stale-sentence rule requires them to be kept.
8
+ HP-03 target Every gate number is a measurement with a date, never a copy. lint HANDOFF.md goblin-verify --only HP-03 Builtin, anchored on the gate NAMES `AGENTS.md` declares (GT-01's source of truth) rather than on a hardcoded keyword list: the shipped gates are named `commit` and `todo_ceiling`, neither of which the old list matched, so on a fresh install the only line it could see was the template's own example sentence, and a real gate line could lose its `date` and the row stayed GREEN (G8-2). A line whose text says "example of the required form" is template prose, not a gate number, and is skipped, so the template cannot satisfy the row. Partial: it proves a dated line inside the `Gates` section exists and that every gate-bearing line there carries a date - not that the number was re-measured that day, and not a gate-bearing line that names no declared gate and carries no gate-shaped keyword (so a line the config does not declare and the keyword list does not recognise is unseen). Historical gate lines outside that section are exempt by design - PROJECT-PRACTICE section 1's stale-sentence rule requires them to be kept.
9
9
  HP-04 target A stale sentence is corrected in place with a dated parenthetical, never deleted. advisory HANDOFF.md advisory Detecting a silent deletion needs semantic judgement; a diff heuristic (>=5 removed non-empty lines with no 'corrected' addition) is too noisy to gate on. goblin-verify --only HP-04 prints the heuristic as a warning only.
10
10
  HP-05 target The HANDOFF names the HEAD it describes. gate HANDOFF.md goblin-verify --only HP-05 Deviation from the design spec, with the reason: the spec's literal check is `grep -q "$(git rev-parse --short HEAD)" HANDOFF.md`, which can never pass - committing the HANDOFF moves HEAD, so the file can only name a commit that is now an ancestor. The mechanised form is therefore 'the HANDOFF names a commit that exists in this repo AND is an ancestor of HEAD', which still catches the defect it exists for (a review/handoff artifact that names no commit at all).
11
11
  SP-01 target A *-SPEC.md file exists at the repo root (any round, not the current one - round-scoping arrives with the W6 staged chain). gate *-SPEC.md ls ./*-SPEC.md >/dev/null 2>&1 Skipped when class: D and disabled: [spec].
12
12
  SP-02 target The SPEC is committed, not left untracked. script git ls-files test -z "$(git ls-files --others --exclude-standard -- '*-SPEC.md')" —
13
13
  SP-03 target Every AC: item is checkable without a human. lint *-SPEC.md awk '/^[[:space:]]*[-*][[:space:]]/ && /AC[0-9]*:/ && !/`|==|===|exit|<|>/ {print; bad=1} END{exit bad}' ./*-SPEC.md Partial: structural only - a checkable-looking bullet can still be unfalsifiable. The matcher is `AC[0-9]*:` because the shipped template writes `- AC1: ...`; the literal `/AC:/` only saw a bullet that spelled the label without a number.
14
- GT-01 target The gate set is declared, never inferred from the stack. gate .goblin/goblin.yaml goblin-verify --only GT-01 Builtin: every `- name:` under `gates:` is a DECLARED gate and each one must carry a `cmd:` - a gate whose `cmd:` was deleted, blanked or re-indented FAILs here instead of vanishing from the count (G8-3, the condition of G8's own 9/10 sentence). The count is of declarations, not of runnable pairs. It cannot see whether a declared command is the RIGHT gate for the project: it proves a command exists, not that it is meaningful.
15
- GT-02 target Every declared gate runs and exits 0. gate goblin.yaml gates: goblin-verify --only GT-02 —
16
- GT-03 target The round reports one line of measured numbers. script .goblin/last-gate-line test -f .goblin/last-gate-line && [ .goblin/last-gate-line -nt "$(git rev-parse --git-dir)/logs/HEAD" ] The freshness reference is HEAD's reflog (`.git/logs/HEAD`), which every HEAD movement rewrites - a commit in an attached or a detached worktree included, and independently of whether the refs are packed. The clause it replaced read `.git/HEAD`, a file a commit never rewrites (only branch operations do), so a commit landing after the measured line left the row GREEN while the round had moved on (D2, measured: `.git/HEAD`'s mtime unchanged across a real commit, `--only GT-03` exit 0). Two limits remain, recorded rather than hidden: a repo with the reflog disabled (`core.logAllRefUpdates=false`) has no reference to compare against, and `-nt` against a missing path is true, so the clause passes vacuously and the row then proves only that a measured line EXISTS (a repo with no commit yet is the same case); and the clause reads ANY HEAD movement as staleness, so a checkout, a branch rename or a reset FAILs it until the next gate run rewrites the line. That second behaviour is what makes a full run self-freshening: `GT-02` writes the line earlier in the same pass, so the row asserts that the line in front of you came from THIS run. `tests/t-gt03-freshness.sh` is the control, in both directions.
17
- GT-04 target A ratchet is declared with a ceiling. gate goblin.yaml ratchet: goblin-verify --only GT-04 —
14
+ GT-01 target The gate set is declared, never inferred from the stack. gate AGENTS.md goblin-verify --only GT-01 Builtin: every `gate_<name>_cmd:` key in the AGENTS.md gob block is a DECLARED gate and its declaration and command are one line - a gate cannot lose its cmd and survive the count (G8-3, the condition of G8's own 9/10 sentence). The count is of declarations, not of runnable pairs. It cannot see whether a declared command is the RIGHT gate for the project: it proves a command exists, not that it is meaningful.
15
+ GT-02 target Every declared gate runs and exits 0. gate AGENTS.md gates (gate_<name>_cmd:) goblin-verify --only GT-02 —
16
+ GT-03 target The round reports one line of measured numbers. script .gob/last-gate-line test -f .gob/last-gate-line && [ .gob/last-gate-line -nt "$(git rev-parse --git-dir)/logs/HEAD" ] The freshness reference is HEAD's reflog (`.git/logs/HEAD`), which every HEAD movement rewrites - a commit in an attached or a detached worktree included, and independently of whether the refs are packed. The clause it replaced read `.git/HEAD`, a file a commit never rewrites (only branch operations do), so a commit landing after the measured line left the row GREEN while the round had moved on (D2, measured: `.git/HEAD`'s mtime unchanged across a real commit, `--only GT-03` exit 0). Two limits remain, recorded rather than hidden: a repo with the reflog disabled (`core.logAllRefUpdates=false`) has no reference to compare against, and `-nt` against a missing path is true, so the clause passes vacuously and the row then proves only that a measured line EXISTS (a repo with no commit yet is the same case); and the clause reads ANY HEAD movement as staleness, so a checkout, a branch rename or a reset FAILs it until the next gate run rewrites the line. That second behaviour is what makes a full run self-freshening: `GT-02` writes the line earlier in the same pass, so the row asserts that the line in front of you came from THIS run. `tests/t-gt03-freshness.sh` is the control, in both directions.
17
+ GT-04 target A ratchet is declared with a ceiling. gate AGENTS.md ratchet.*: goblin-verify --only GT-04 —
18
18
  GT-05 target The ratchet has not risen. gate ratchet.cmd goblin-verify --only GT-05 —
19
19
  HS-01 target Asserting harnesses follow the house shape. lint harness_dir/* goblin-verify --only HS-01 A declared harness_dir that is absent FAILS when the class scaffolds one (config `scaffold_checks: yes`, classes A and C); a class that ships no harness dir (B/D/E) SKIPs with a reason. Keying the skip off the path alone let one config line switch this row and HS-02 off. A report utility in the same dir is counted and reported separately rather than failing the run.
20
20
  HS-02 target A check green on both trees proves nothing - the REPLAY must show RED pre-change. gate harness_dir/*, replay.env, replay.cmd goblin-verify --only HS-02 The declared `replay.cmd` is EXECUTED with `{name}` replaced by each harness's name, in the pre-change worktree, with `replay.env=<commit>` set (Z1-4). Two clauses that used to be unheard: a command that interpolates no `{name}` FAILs (it cannot be running the harness it names, so nothing was replayed), and a command that cannot be executed at all - exit 126 or 127 - FAILs rather than counting as a RED harness. The harness's name is substituted SHELL-QUOTED (`printf %q`), because the name is part of the command text the shell parses; unquoted, a name carrying `;` or `#` reached the shell as syntax (Z2-3, the G8-1 surface class), and the control for it carries a metacharacter-bearing file name. What it cannot see: a command that runs *something else* under the harness's name and exits non-zero, and whether the harness tests the right path rather than merely failing on this tree.
21
21
  HS-03 target Source probes read text with comments blanked first. advisory harness_dir/* advisory Recognising 'this probe reads source text' is semantic; a grep for the blanking helper produces false FAILs on harnesses that do not probe source.
22
- CM-01 target Commits carry the owner identity, not an ambient one. Current scope: this gates the identity of HEAD (the last commit) at the moment of the run - earlier commits by other authors are not scanned. gate git log test "$(git log -1 --format='%ae')" = "$(grep '^owner_email:' .goblin/goblin.yaml | cut -d' ' -f2)" note: this repo records a different owner (you are probably new here) — commit with git -c user.email=<owner_email> --author=<owner_email>, or update owner_email: in .goblin/goblin.yaml
22
+ CM-01 target Commits carry the owner identity, not an ambient one. Current scope: this gates the identity of HEAD (the last commit) at the moment of the run - earlier commits by other authors are not scanned. gate git log test "$(git log -1 --format='%ae')" = "$(grep '^owner_email:' AGENTS.md | cut -d' ' -f2)" note: this repo records a different owner (you are probably new here) — commit with git -c user.email=<owner_email> --author=<owner_email>, or update owner_email: in AGENTS.md
23
23
  CM-02 target The commit message was written to a file, not passed with -m. advisory git log advisory A backtick lost to command substitution leaves no trace a later check can read. Reported as a heuristic (unbalanced backticks in a body) only.
24
24
  CM-03 target Commit-as-you-go: the working tree is not carrying a dead run's work. script git status --porcelain goblin-verify --only CM-03 —
25
- MD-01 target No model name is hardcoded in any reusable rule. lint skills/ manifest/ bin/ templates/ presets/ .goblin/ .hermes/ 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 — (the pattern is written with character classes so this row cannot match itself; tests/t-verify-red.sh proves it still catches a real model name. SCOPE, stated so a repo-wide grep is not re-reported as a hole (G8-10): this row reads the seven directories an install writes rules into - skills/ manifest/ bin/ templates/ presets/ .goblin/ .hermes/. It does NOT read tests/, docs/, or the source automations/ directory; tests/ is where a control must be free to WRITE the thing it guards, and the source directories are covered by MD-01's own body in tests/run-tests.sh, which scans skills manifest bin templates presets automations. Both control strings are assembled at run time, so a repo-wide grep over the whole tree is 0 and the row's own scope is 0 by construction.)
25
+ MD-01 target No model name is hardcoded in any reusable rule. lint skills/ manifest/ bin/ templates/ presets/ .gob/ .hermes/ for d in skills manifest bin templates presets .gob .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 — (the pattern is written with character classes so this row cannot match itself; tests/t-verify-red.sh proves it still catches a real model name. SCOPE, stated so a repo-wide grep is not re-reported as a hole (G8-10): this row reads the seven directories an install writes rules into - skills/ manifest/ bin/ templates/ presets/ .gob/ .hermes/. It does NOT read tests/, docs/, or the source automations/ directory; tests/ is where a control must be free to WRITE the thing it guards, and the source directories are covered by MD-01's own body in tests/run-tests.sh, which scans skills manifest bin templates presets automations. Both control strings are assembled at run time, so a repo-wide grep over the whole tree is 0 and the row's own scope is 0 by construction.)
26
26
  MD-02 target The review lane is a different model family from the code lane. advisory roles.yaml + mapping file goblin-verify --only MD-02 goblin-stack cannot choose the fleet's models; today's map resolves both roles to the same family. Reported as ADV, never gated. W5-6: the JUDGE lane's resolved model is compared with the code lane's too, because `JG-02` proves only that the declared profile NAMES are disjoint; it is still ADV, never gated, and the measured state of this box is recorded in `docs/LIMITS.md` #38.
27
27
  MD-03 target Role-pinned fan-out goes through kanban, not a model-less subagent spawn. advisory skills/* advisory It is a fleet-runtime property: no repo-local file can observe which tool created a worker. Enforced at board level, described in docs/INTEGRATION.md.
28
28
  PG-01 target The reviewed artifact is named by SHA, and that SHA exists. gate reviews/*.md goblin-verify --only PG-01 —
@@ -30,8 +30,8 @@ PG-02 target The gate is chosen by the change, not the repo, and the tier's evid
30
30
  PG-03 target A new head voids the verdict. gate reviews/*.md + git goblin-verify --only PG-03 —
31
31
  PG-04 target Never bypass what the forge enforces. advisory — advisory Needs the forge: GitHub's restrictions do not apply to admins, and a sole-admin repo has nobody the gate binds. Not observable from the repo.
32
32
  PG-05 target No required check that self-skips. lint .github/workflows/* goblin-verify --only PG-05 Text, not a YAML parser. Three clauses per job: the job declares at least one `run:`/`uses:` step (else the check runs nothing); the JOB carries no `if:` (else the whole required check self-skips); and no STEP carries an `if:` (else that step - possibly the gate step - self-skips). The old body flagged only 'every step guarded', which PASSED the real shape: in the estate's one existing workflow a deliberately UNGUARDED credential step decides whether the guarded compile step runs, so `guarded < steps` and the row reported PASS on the workflow it exists to catch (G8 section 3, re-measured V3-4). Deliberately strict, and the strictness is the point: GitHub reports a SKIPPED job as Success even when it is a required check (docs S1/S2), so a conditional step is a step that can green-light a commit whose gate never ran. False positives it cannot avoid: a `#` inside a quoted string is read as a comment, a flow-style (`jobs: {...}`) mapping is refused rather than parsed, and a conditional step that is genuinely safe is indistinguishable from the trap - the remedy is to move the condition into the declared command, or into a second job that is not the required check. It cannot see branch-protection state, the required-check list, or whether the workflow ever ran (`PG-04`), and a repo with no workflow passes with the count printed on the line.
33
- PG-06 target The gate CI runs is the gate the project declares. gate .github/workflows/* + goblin.yaml gates: goblin-verify --only PG-06 The declared set comes from `g_yaml_gates`, the reader `GT-01` uses, so a gate cannot vanish from the comparison in silence (G8-3). A workflow passes when the whole declared set is RUN: the verifier with no `--only` (a full run executes every declared gate through `GT-02`), or each declared gate command verbatim. It reads TEXT with comments blanked first, and only `run:` payloads and block-scalar bodies count - a command sitting in a `name:`, `env:` or `with:` value is dropped, which is what stops a comment or a label from satisfying the row. What it cannot see: that the forge marks that job a REQUIRED check, that the job is the one the forge waits on, or that the workflow can fail at all (`PG-05`). A repo with no workflow reports SKIP with that reason instead of a vacuous pass. The lane's own enforcement is the TARGET repo's, not goblin-stack's (`docs/LIMITS.md` #34): goblin-stack can prove the file invokes the gate and cannot make the forge run it, so this row is a text reading of the target's CI and never a claim that CI gated the SHA.
34
- DS-01 target Runtime data is not test fixture: a gate run must not write it. script goblin.yaml runtime_data: goblin-verify --only DS-01 —
33
+ PG-06 target The gate CI runs is the gate the project declares. gate .github/workflows/* + AGENTS.md gates (gate_<name>_cmd:) goblin-verify --only PG-06 The declared set comes from `g_yaml_gates`, the reader `GT-01` uses, so a gate cannot vanish from the comparison in silence (G8-3). A workflow passes when the whole declared set is RUN: the verifier with no `--only` (a full run executes every declared gate through `GT-02`), or each declared gate command verbatim. It reads TEXT with comments blanked first, and only `run:` payloads and block-scalar bodies count - a command sitting in a `name:`, `env:` or `with:` value is dropped, which is what stops a comment or a label from satisfying the row. What it cannot see: that the forge marks that job a REQUIRED check, that the job is the one the forge waits on, or that the workflow can fail at all (`PG-05`). A repo with no workflow reports SKIP with that reason instead of a vacuous pass. The lane's own enforcement is the TARGET repo's, not goblin-stack's (`docs/LIMITS.md` #34): goblin-stack can prove the file invokes the gate and cannot make the forge run it, so this row is a text reading of the target's CI and never a claim that CI gated the SHA.
34
+ DS-01 target Runtime data is not test fixture: a gate run must not write it. script AGENTS.md runtime_data: goblin-verify --only DS-01 —
35
35
  DS-02 target Snapshot before, verify after. gate same as DS-01 goblin-verify --only DS-02 —
36
36
  DOC-01 target A significant change updates the docs that teach it. advisory README.md, docs/ advisory 'Significant' is a judgement; a diff-size heuristic fails on the cases that matter.
37
37
  DOC-02 target System-level changes are recorded wherever the project's standard says they live. advisory — advisory Where the recording lives may be owned by a stricter external rule than goblin-stack may add; the repo can only state it.
@@ -39,47 +39,47 @@ SK-01 target Every shipped skill has name + description frontmatter. lint .herme
39
39
  SK-02 target The installed skills match their recorded hashes (no drift). script .hermes/skills/, installed.json goblin-verify --only SK-02 —
40
40
  SK-03 target A rule with no mechanism is labelled advisory, and the advisory count is reported. script manifest/enforcement.tsv goblin-verify --only SK-03 —
41
41
  SK-04 target Every shipped skill says what it cannot see. lint .hermes/skills/*/SKILL.md goblin-verify --only SK-04 Partial: proves the section exists, not that what it says is complete or true - the limit every prose rule carries. Every shipped skill already carries it, so the row is GREEN on a fresh install and RED only under a real violation. W1: the check is a builtin so the engine.mode=global clause can run - in global mode the procedure tier is emitted per platform (not carried in this repo) and the row SKIPs with that reason.
42
- PT-01 target No tenant-specific string inside a reusable rule. lint skills/ manifest/ bin/ templates/ presets/ .goblin/ .hermes/ 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 — (the same directory list MD-01 uses: the rules an install actually writes live in `.goblin/` and `.hermes/`, not in the source layout. Two documented exceptions: `.goblin/goblin.yaml`, which holds `models_file:`/`practice:` - per-machine config, not a rule - and `.goblin/installed.json`, which since W1 records the machine's absolute `engine_dir` in its `engine:` block - both per-machine facts, not rules - each excluded by name. The pattern is written with character classes so this row cannot match itself.)
43
- PT-02 target The default branch is declared, not assumed. gate goblin.yaml branch: goblin-verify --only PT-02 —
44
- CL-01 target Every part the class requires is present, and every part it forbids is absent. script goblin.yaml class: + manifest/classes.tsv goblin-verify --only CL-01 —
45
- CL-02 target An archive: true project verifies GREEN without a HANDOFF or gates. script goblin.yaml archive: goblin-verify --only CL-02 Falsifiable: FAILs when `archive:` is not `true`/`false`, and when the config's value disagrees with the one the install recorded in `.goblin/installed.json` (so the waiver cannot be flipped on by hand). It cannot observe the *effect* of the waiver on the other rows without re-entering the runner.
42
+ PT-01 target No tenant-specific string inside a reusable rule. lint skills/ manifest/ bin/ templates/ presets/ .gob/ .hermes/ for d in skills manifest bin templates presets .gob .hermes; do [ -d "$d" ] || continue; grep -rniE --exclude=AGENTS.md --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 — (the same directory list MD-01 uses: the rules an install actually writes live in `.gob/` and `.hermes/`, not in the source layout. Two documented exceptions: `AGENTS.md`, which holds `models_file:`/`practice:` - per-machine config, not a rule - and `.gob/installed.json`, which since W1 records the machine's absolute `engine_dir` in its `engine:` block - both per-machine facts, not rules - each excluded by name. The pattern is written with character classes so this row cannot match itself.)
43
+ PT-02 target The default branch is declared, not assumed. gate AGENTS.md branch: goblin-verify --only PT-02 —
44
+ CL-01 target Every part the class requires is present, and every part it forbids is absent. script AGENTS.md class: + manifest/classes.tsv goblin-verify --only CL-01 —
45
+ CL-02 target An archive: true project verifies GREEN without a HANDOFF or gates. script AGENTS.md archive: goblin-verify --only CL-02 Falsifiable: FAILs when `archive:` is not `true`/`false`, and when the config's value disagrees with the one the install recorded in `.gob/installed.json` (so the waiver cannot be flipped on by hand). It cannot observe the *effect* of the waiver on the other rows without re-entering the runner.
46
46
  SC-01 target No secret file is tracked. lint repo n=$(git ls-files | grep -iE '(^|/)\.env|\.pem$|\.key$' | grep -vcE '\.(example|sample|template)$'); printf '%s tracked secret file(s)\n' "$n"; [ "$n" = 0 ] Partial: it sees tracked PATHS, never contents - a secret pasted into a tracked file is invisible here, and the pattern is a name family, so a credential inside `config.ts` is missed by construction.
47
47
  SC-02 target The ignore rules cover the whole secret family. gate .gitignore + git check-ignore goblin-verify --only SC-02 Builtin, and behavioural: clause 1 reads `.gitignore`; clause 2 asks git's own matcher (`git check-ignore`) for `.env`, `.env.local` and `.env.production` one path at a time, so a rule that looks right but does not match still fails. It cannot see a secret already in git history, or one committed under a name the family does not cover. SKIPs with a reason when there is no `.gitignore` and no `package.json`.
48
- SC-03 target No client-visible name is secret-shaped, and no build output carries a secret literal. lint repo + security's build_output goblin-verify --only SC-03 Builtin, two clauses: the `NEXT_PUBLIC_*_(SECRET|TOKEN|KEY|PASSWORD|PRIVATE)` name pattern over source, and known secret prefixes over the declared build output. Partial: a prefix pattern cannot see a secret that does not look like one, and clause 2 says "no build output to scan" on the line rather than skipping silently. The source scan excludes `.goblin/` and `.hermes/`, so it cannot match the row text that describes it.
48
+ SC-03 target No client-visible name is secret-shaped, and no build output carries a secret literal. lint repo + security's build_output goblin-verify --only SC-03 Builtin, two clauses: the `NEXT_PUBLIC_*_(SECRET|TOKEN|KEY|PASSWORD|PRIVATE)` name pattern over source, and known secret prefixes over the declared build output. Partial: a prefix pattern cannot see a secret that does not look like one, and clause 2 says "no build output to scan" on the line rather than skipping silently. The source scan excludes `.gob/` and `.hermes/`, so it cannot match the row text that describes it.
49
49
  SC-04 target Every cookie write carries its flags. lint repo goblin-verify --only SC-04 Builtin, same-statement only: a write spread over three lines is not seen, and the row says so. It REPORTS that a JS-written cookie is readable by any script rather than failing on it - that is a design fact, not a bug - so the pass is about the flags, never about the choice.
50
- SC-05 target Every write route validates its input, or is waived. gate security's write_routes goblin-verify --only SC-05 Builtin: it proves a validator is CALLED (`safeParse|zod|valibot|yup|ajv|superstruct|validate(`), never that the schema is right - a schema that accepts everything passes. `.goblin/boundary-waivers` is the escape hatch, and the waived count is printed, so a silent pile-up is visible.
50
+ SC-05 target Every write route validates its input, or is waived. gate security's write_routes goblin-verify --only SC-05 Builtin: it proves a validator is CALLED (`safeParse|zod|valibot|yup|ajv|superstruct|validate(`), never that the schema is right - a schema that accepts everything passes. `.gob/boundary-waivers` is the escape hatch, and the waived count is printed, so a silent pile-up is visible.
51
51
  SC-06 target A lockfile exists, and the repo tracks it. gate package.json + lockfile goblin-verify --only SC-06 Builtin: presence, then `git ls-files --error-unmatch`. It cannot see that the lockfile is STALE relative to `package.json` - resolving that needs the package manager, which is a deliberate network-shaped step, not a check. SKIPs with a reason when there is no `package.json`.
52
- SC-07 target The dependency audit record is present, dated, fresh, and clean-or-waived. gate .goblin/audit.tsv, .goblin/audit-waiver.tsv goblin-verify --only SC-07 Builtin, OFFLINE by construction: it reads the record and never the network. The record comes from `.goblin/bin/goblin-audit`, run once, deliberately; the waiver count is printed on the gate line so the debt is loud even when the row passes. It cannot see an advisory the registry did not know on the day the record was taken. SKIPs with a reason when no record exists yet.
53
- SC-08 target No dependency runs an install-time script that is not on the allowlist. gate package-lock.json + .goblin/install-hooks.allowlist goblin-verify --only SC-08 Builtin over `package-lock.json`'s `hasInstallScript`: a pnpm/yarn lockfile has no such field, so those repos get a SKIP with that reason rather than a vacuous pass, and a MINIFIED (one-line) lockfile is read like a pretty-printed one - the text is normalised into the pretty shape before the line-anchored reader sees it, so a hook inside a one-line lock FAILs instead of being reported as `0 install hook(s)` (Z2-2: before that, a one-line lock PASSED vacuously). A `lockfileVersion` 1 lock records its hooks under `dependencies`, not under `node_modules/` keys, so the line-anchored reader enumerates nothing there either and prints the same `0 install hook(s), 0 allowlisted` PASS over a surface it did not read (measured at 0.4.2; npm <= 6 records no `hasInstallScript` at all, so for a genuine v1 lock this is blindness rather than a wrong answer). Lowest-value row of the ten - regression detection - and the first to cut if the matrix gets heavy.
52
+ SC-07 target The dependency audit record is present, dated, fresh, and clean-or-waived. gate .gob/audit.tsv, .gob/audit-waiver.tsv goblin-verify --only SC-07 Builtin, OFFLINE by construction: it reads the record and never the network. The record comes from `.gob/bin/goblin-audit`, run once, deliberately; the waiver count is printed on the gate line so the debt is loud even when the row passes. It cannot see an advisory the registry did not know on the day the record was taken. SKIPs with a reason when no record exists yet.
53
+ SC-08 target No dependency runs an install-time script that is not on the allowlist. gate package-lock.json + .gob/install-hooks.allowlist goblin-verify --only SC-08 Builtin over `package-lock.json`'s `hasInstallScript`: a pnpm/yarn lockfile has no such field, so those repos get a SKIP with that reason rather than a vacuous pass, and a MINIFIED (one-line) lockfile is read like a pretty-printed one - the text is normalised into the pretty shape before the line-anchored reader sees it, so a hook inside a one-line lock FAILs instead of being reported as `0 install hook(s)` (Z2-2: before that, a one-line lock PASSED vacuously). A `lockfileVersion` 1 lock records its hooks under `dependencies`, not under `node_modules/` keys, so the line-anchored reader enumerates nothing there either and prints the same `0 install hook(s), 0 allowlisted` PASS over a surface it did not read (measured at 0.4.2; npm <= 6 records no `hasInstallScript` at all, so for a genuine v1 lock this is blindness rather than a wrong answer). Lowest-value row of the ten - regression detection - and the first to cut if the matrix gets heavy.
54
54
  SC-09 target Auth is applied consistently across sibling routes. advisory — advisory Prose on purpose: "consistently" is a semantic judgement about a private surface no repo here has yet. Counted (9 of ceiling 10) so the matrix cannot quietly grow prose.
55
- PF-01 target The perf baseline names the commit it measured. lint .goblin/goblin.yaml perf: + ratchet: goblin-verify --only PF-01 Builtin: the metric must equal `ratchet.name` so the budget and the measurement cannot silently disagree, the value must be numeric, the date must exist, the baseline commit must exist AND be an ancestor of HEAD (`HP-05`'s mechanic, reused rather than re-derived), and `ratchet.ceiling` must equal `perf.baseline_value` - otherwise a one-line ceiling raise passes while the row prints the contradiction, which is `I raised the budget and never measured again` (G8-6b). It cannot see whether the metric is the right one for the product, and it never re-measures: re-anchoring is a deliberate operator action. SKIPs with a reason when the class declares no metric, or none has been recorded yet.
55
+ PF-01 target The perf baseline names the commit it measured. lint AGENTS.md perf: + ratchet: goblin-verify --only PF-01 Builtin: the metric must equal `ratchet.name` so the budget and the measurement cannot silently disagree, the value must be numeric, the date must exist, the baseline commit must exist AND be an ancestor of HEAD (`HP-05`'s mechanic, reused rather than re-derived), and `ratchet.ceiling` must equal `perf.baseline_value` - otherwise a one-line ceiling raise passes while the row prints the contradiction, which is `I raised the budget and never measured again` (G8-6b). It cannot see whether the metric is the right one for the product, and it never re-measures: re-anchoring is a deliberate operator action. SKIPs with a reason when the class declares no metric, or none has been recorded yet.
56
56
  AU-01 target An automation's producer is deterministic and network-free. lint automations/*.sh goblin-verify --only AU-01 Partial: proves that no line of a producer begins with a network or forge verb, not that the script is otherwise deterministic. W1: in global mode the first producer glob resolves under the engine dir (the resolution chain); the repo-local globs stay. New clause: a repo with NO producer anywhere (repo or engine) SKIPs with `no automation producer found (repo or engine)` instead of FAILing — the old FAIL was a born-RED artifact of the glob; a repo that declares its own automations but has no producer still FAILs.
57
57
  AU-02 target A report's dedup key is a function of content only - no date, no run id. script reports/*/report.yaml goblin-verify --only AU-02 Builtin: it recomputes the key from the report's own `repo` and `symptom` and requires the recorded `dedup_key` to equal it, then refuses a key carrying a date. Skipped with a reason when the repo holds no report - nothing to dedup. It cannot see whether two reports should have been one: a normalisation that merges two genuinely different symptoms is a duplicate card, not a lost report.
58
58
  AU-03 target A reporter run leaves the tree and the harness untouched. gate git status, harness_dir goblin-verify --only AU-03 Builtin: asserts a clean working tree, and - when HEAD is a reporter commit - that no path under the declared harness_dir appears in it. Skipped with a reason when the repo holds no reports/ - no reporter has run here. It cannot see a reporter that edited the tree and committed the edit as part of the report.
59
59
  AU-04 target An automation's skill declares its own write surface. lint .hermes/skills/goblin-bugreporter, .hermes/skills/goblin-drift-audit goblin-verify --only AU-04 Partial: proves every installed automation skill carries a `## Write surface` section. W1: the check is a builtin so the engine.mode=global clause can run - in global mode the procedure tier is emitted per platform (not carried in this repo) and the row SKIPs with that reason.
60
- BN-00 target Every ban has an enforcement row, every ban row names a replacement, and the ban table is not empty. script .goblin/manifest/bans.tsv + .goblin/manifest/enforcement.tsv goblin-verify --only BN-00 — (this row is the reason the ban list cannot decay into prose: IN-03's shape applied to bans.tsv, and it agrees in both directions)
61
- BN-01 target No `any` in application TypeScript. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-01 Text probe, not an AST: a `: any` inside a string or a comment is reported, and `Record<string, any>` (no leading colon) is missed. The AST form needs a parser the no-npm contract (docs/CONTRACTS.md) forbids (docs/LIMITS.md #27). SKIPs when the ban is not in `bans:` or its globs match no file.
62
- BN-02 target No `@ts-ignore` / `@ts-expect-error` suppressions. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-02 Text probe: it sees the directive wherever it appears, including inside a string, and cannot tell a suppression hiding a real error from one on a line that would compile anyway. SKIPs when the ban is not in `bans:` or its globs match no file.
63
- BN-03 target No direct network call from a component. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-03 Text probe over the declared component globs: it stops the call and cannot tell whether a data layer was written or the call merely moved into a helper. SKIPs when the ban is not in `bans:` or its globs match no file.
64
- BN-05 target No import across a declared layer boundary. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-05 Reads the `layers:` list; an empty list SKIPs with a reason, never a vacuous pass. It matches an import path naming the target directory's last segment - module aliases and dynamic imports are not seen. SKIPs when no file matches its globs.
65
- BN-06 target No renderer with Node access (`nodeIntegration: true`). lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-06 Text probe over the ban table's globs, the same mechanism as BN-01..BN-05: it sees `nodeIntegration: true` wherever it appears, including inside a string, and cannot see a webPreferences object built at run time or spread in from another module. The STRONGER form is a runtime measurement - the renderer prints `process.contextIsolated` and `process.sandboxed` and the check requires true/true - and that needs a real Electron process, which the dependency contract (docs/CONTRACTS.md) does not allow a shipped rule to launch: it is the project's host gate (docs/LIMITS.md #34). SKIPs when the ban is not in `bans:` or its globs match no file.
66
- BN-07 target No renderer with context isolation or the process sandbox turned off. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-07 One probe for two properties because Electron's own documentation makes them one: disabling `contextIsolation` "also disables process sandboxing", so a repo that has turned either off has lost both. Text probe, with the same false-positive set as BN-06. SKIPs when the ban is not in `bans:` or its globs match no file.
67
- BN-08 target No dangerous webPreferences. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-08 Four one-line patterns from Electron's own security checklist (`webSecurity: false`, `allowRunningInsecureContent: true`, `enableBlinkFeatures`, `<webview allowpopups>`). Text probe: `enableBlinkFeatures` is banned by name rather than by value, so the string is reported even in a comment. SKIPs when the ban is not in `bans:` or its globs match no file.
68
- BN-09 target No synchronous IPC and no `@electron/remote`. lint .goblin/manifest/bans.tsv + .goblin/bans/ goblin-verify --only BN-09 The banned-list shape the wave's note 9 asks for, applied to Electron: `sendSync(` and `@electron/remote` block the renderer's own thread, which is the freeze the class exists to prevent. Text probe - it sees the call site, not the call graph, so a wrapper around `sendSync` in a file the globs do not match is missed. SKIPs when the ban is not in `bans:` or its globs match no file.
60
+ BN-00 target Every ban has an enforcement row, every ban row names a replacement, and the ban table is not empty. script .gob/manifest/bans.tsv + .gob/manifest/enforcement.tsv goblin-verify --only BN-00 — (this row is the reason the ban list cannot decay into prose: IN-03's shape applied to bans.tsv, and it agrees in both directions)
61
+ BN-01 target No `any` in application TypeScript. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-01 Text probe, not an AST: a `: any` inside a string or a comment is reported, and `Record<string, any>` (no leading colon) is missed. The AST form needs a parser the no-npm contract (docs/CONTRACTS.md) forbids (docs/LIMITS.md #27). SKIPs when the ban is not in `bans:` or its globs match no file.
62
+ BN-02 target No `@ts-ignore` / `@ts-expect-error` suppressions. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-02 Text probe: it sees the directive wherever it appears, including inside a string, and cannot tell a suppression hiding a real error from one on a line that would compile anyway. SKIPs when the ban is not in `bans:` or its globs match no file.
63
+ BN-03 target No direct network call from a component. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-03 Text probe over the declared component globs: it stops the call and cannot tell whether a data layer was written or the call merely moved into a helper. SKIPs when the ban is not in `bans:` or its globs match no file.
64
+ BN-05 target No import across a declared layer boundary. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-05 Reads the `layers:` list; an empty list SKIPs with a reason, never a vacuous pass. It matches an import path naming the target directory's last segment - module aliases and dynamic imports are not seen. SKIPs when no file matches its globs.
65
+ BN-06 target No renderer with Node access (`nodeIntegration: true`). lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-06 Text probe over the ban table's globs, the same mechanism as BN-01..BN-05: it sees `nodeIntegration: true` wherever it appears, including inside a string, and cannot see a webPreferences object built at run time or spread in from another module. The STRONGER form is a runtime measurement - the renderer prints `process.contextIsolated` and `process.sandboxed` and the check requires true/true - and that needs a real Electron process, which the dependency contract (docs/CONTRACTS.md) does not allow a shipped rule to launch: it is the project's host gate (docs/LIMITS.md #34). SKIPs when the ban is not in `bans:` or its globs match no file.
66
+ BN-07 target No renderer with context isolation or the process sandbox turned off. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-07 One probe for two properties because Electron's own documentation makes them one: disabling `contextIsolation` "also disables process sandboxing", so a repo that has turned either off has lost both. Text probe, with the same false-positive set as BN-06. SKIPs when the ban is not in `bans:` or its globs match no file.
67
+ BN-08 target No dangerous webPreferences. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-08 Four one-line patterns from Electron's own security checklist (`webSecurity: false`, `allowRunningInsecureContent: true`, `enableBlinkFeatures`, `<webview allowpopups>`). Text probe: `enableBlinkFeatures` is banned by name rather than by value, so the string is reported even in a comment. SKIPs when the ban is not in `bans:` or its globs match no file.
68
+ BN-09 target No synchronous IPC and no `@electron/remote`. lint .gob/manifest/bans.tsv + .gob/bans/ goblin-verify --only BN-09 The banned-list shape the wave's note 9 asks for, applied to Electron: `sendSync(` and `@electron/remote` block the renderer's own thread, which is the freeze the class exists to prevent. Text probe - it sees the call site, not the call graph, so a wrapper around `sendSync` in a file the globs do not match is missed. SKIPs when the ban is not in `bans:` or its globs match no file.
69
69
  FM-01 target Every feature file is indexed from the map README, declares its slug and at least one entry path, and carries the four-H2 entry contract. lint <feature_map dir> goblin-verify --only FM-01 SKIPs (exit 3) when feature_map: is empty - a fresh install has no map and must not be born RED (the D8 shape). When a map IS declared: the README must exist, every features/*.md must be linked from it in the (./<slug>.md) form and every relative .md link must resolve, each feature file's `feature:` must equal its filename stem, it must declare >=1 `entry_paths:`, and its H2s must be exactly Sub-features / How to get to it (user POV) / Driving it with <harness> / Gotchas, in that order. Partial: the README's own H2s are prose this row does not read, and 'the map lists every user-facing feature' is not mechanically checkable - that is docs/LIMITS.md #30, not a row.
70
- FM-02 target Every entry point a feature declares still resolves in source, and no entry path changed after the map was verified. lint <feature_map dir> + git goblin-verify --only FM-02 SKIPs (exit 3) when feature_map: is empty. A tripwire, not a proof. The token is searched under source_root with occurrences under the map's own directory excluded - without that exclusion the map's own entry-path list satisfies the search and the row could never go RED. Freshness is `git log -1 --format=%cs` on the resolved file against the feature's `verified:` date, and git sees a FILE change, not a behaviour change: the row can be RED-when-stale and never GREEN-means-fresh. A token that also occurs in a vendored copy or a build artifact is read as resolved, and a `verified:` date is itself a claim the row cannot test (docs/LIMITS.md #30). W5-4: the search skips the harness's own directories (`.goblin/`, `.hermes/`, the declared `harness_dir`) and the map's own directory, so a stub map whose token occurs only in the install no longer resolves; a token that occurs only in the target's own `docs/`, `tests/` or build output still does (docs/LIMITS.md #37). Z1-6: the resolved file must be TRACKED (`git ls-files --error-unmatch`) before its date is compared - an untracked file used to make the freshness clause skip in silence, so a map could claim `verified: 2020-01-01` over source that was never committed.
71
- VA-01 target The generated verification skill's doctor command runs and exits 0. gate .goblin/goblin.yaml verify_doctor: goblin-verify --only VA-01 SKIPs (exit 3) when verify_doctor: is empty (the replay.commit: "" shape). Runs the DECLARED command exactly as GT-02 runs a declared gate, and never a string read out of file content (the v0.2 blocker). Closes P6's stated-but-unenforced clause 'a generated skill that was never executed is a draft': the doctor is the smallest executable proof that the skill's own instructions still run - and it proves only that, never that the doctor tests the right path.
72
- JG-01 target A judge verdict is recorded as a row whose evidence resolves. script .goblin/loop/decisions.tsv goblin-verify --only JG-01 Builtin: it proves the named handle EXISTS - a commit `git rev-list --all` knows, a path under the root, or a `sha256:` matching a file under `.goblin/loop/` - never that the handle SUPPORTS the verdict beside it: a judge may cite a real commit that has nothing to do with the claim. It also cannot see whether the verdict was reached by the declared judge lane. A row with fewer columns than the header is a FAIL, not a silent skip. SKIPs (exit 3) when there is no loop record: nothing to check is reported as a reason, never as a pass.
70
+ FM-02 target Every entry point a feature declares still resolves in source, and no entry path changed after the map was verified. lint <feature_map dir> + git goblin-verify --only FM-02 SKIPs (exit 3) when feature_map: is empty. A tripwire, not a proof. The token is searched under source_root with occurrences under the map's own directory excluded - without that exclusion the map's own entry-path list satisfies the search and the row could never go RED. Freshness is `git log -1 --format=%cs` on the resolved file against the feature's `verified:` date, and git sees a FILE change, not a behaviour change: the row can be RED-when-stale and never GREEN-means-fresh. A token that also occurs in a vendored copy or a build artifact is read as resolved, and a `verified:` date is itself a claim the row cannot test (docs/LIMITS.md #30). W5-4: the search skips the harness's own directories (`.gob/`, `.hermes/`, the declared `harness_dir`) and the map's own directory, so a stub map whose token occurs only in the install no longer resolves; a token that occurs only in the target's own `docs/`, `tests/` or build output still does (docs/LIMITS.md #37). Z1-6: the resolved file must be TRACKED (`git ls-files --error-unmatch`) before its date is compared - an untracked file used to make the freshness clause skip in silence, so a map could claim `verified: 2020-01-01` over source that was never committed.
71
+ VA-01 target The generated verification skill's doctor command runs and exits 0. gate AGENTS.md verify_doctor: goblin-verify --only VA-01 SKIPs (exit 3) when verify_doctor: is empty (the replay.commit: "" shape). Runs the DECLARED command exactly as GT-02 runs a declared gate, and never a string read out of file content (the v0.2 blocker). Closes P6's stated-but-unenforced clause 'a generated skill that was never executed is a draft': the doctor is the smallest executable proof that the skill's own instructions still run - and it proves only that, never that the doctor tests the right path.
72
+ JG-01 target A judge verdict is recorded as a row whose evidence resolves. script .gob/loop/decisions.tsv goblin-verify --only JG-01 Builtin: it proves the named handle EXISTS - a commit `git rev-list --all` knows, a path under the root, or a `sha256:` matching a file under `.gob/loop/` - never that the handle SUPPORTS the verdict beside it: a judge may cite a real commit that has nothing to do with the claim. It also cannot see whether the verdict was reached by the declared judge lane. A row with fewer columns than the header is a FAIL, not a silent skip. SKIPs (exit 3) when there is no loop record: nothing to check is reported as a reason, never as a pass.
73
73
  JG-02 target The judge lane is disjoint from the author lane. gate roles.yaml + the mapping file goblin-verify --only JG-02 Builtin: it proves the DECLARED lane sets are disjoint (profile names), not that a judge ran on one - no repo-local file observes which profile ran (the blindness `MD-03` records), and family equality is not this row's business (`MD-02` owns it). An unresolved lane is an ADV carrying the one-line remedy, never a FAIL: a box with no mapping file must not be failed for the fleet's routing. Both sentinels count as unresolved - `?` (the installed path) and `unknown` (the checkout path) - because a check that greps for one passes on the other.
74
- JG-03 target A judge lane that has never returned a non-`done` verdict is escalated. advisory .goblin/loop/decisions.tsv advisory A lane that has returned two verdicts cannot be called always-yes, and a repo sees only its own rows; the history that would show a bad lane lives across cards and repos. The counter-measure is policy - one known-red control verdict per wave, recorded in docs/LOOP.md - and it is counted, not enforced.
75
- LP-01 target The exit predicate is a command, and it ran before iteration 1. script .goblin/loop/predicate, .goblin/loop/first-run goblin-verify --only LP-01 Builtin: it proves the predicate is exactly ONE command and that a recorded first run exists whose timestamp is at or before the first log row - not that the command was actually run, and not that the `exit=` value was measured rather than typed (`HP-03`'s defect, one artifact over). It deliberately does NOT re-run the predicate: a stop condition is legitimately red before the loop finishes, so re-running it would fail a repo whose loop is working, and PROJECT-PRACTICE section 7 forbids a probe that writes. Timestamps compare on their `YYYY-MM-DDThh:mm:ss` prefix, so records written in different UTC offsets compare as text.
76
- LP-02 target The predicate is pinned at loop start and is never relaxed. script .goblin/loop/predicate.sha256 goblin-verify --only LP-02 Builtin: it proves the predicate file on disk still hashes to the recorded pin - never that the predicate is the RIGHT one, and never who edited it. The digest covers the predicate file alone: a loop that quietly relaxes a predicate living somewhere else is not seen. The remedy is printed with the failure and is never automatic, the `IN-02` `--re-pin` shape. W5-7: a close-and-reopen is auditable - every `closed-<date>/` archive must hold its predicate AND the pin it was closed under, and the live pin must name the archived digest on a `previous:` line - which catches a SILENT relaxation, never a WEAKER one (nothing in bash judges that: `docs/LIMITS.md` #39).
77
- LP-03 target The loop declares a budget, and the record never exceeds it. gate .goblin/loop/budget, .goblin/loop/decisions.tsv goblin-verify --only LP-03 Builtin: it proves the declared budget is a positive integer at or under the configured `loop_max_turns_ceiling`, and that the record holds no more verdict rows than the budget - not that the budget is affordable. Cost is not a field the record carries: a turn budget bounds turns, and the auxiliary judge call per turn is unpriced (`docs/LIMITS.md` #32). A worker cannot edit its own card body, so a CARD predicate is already protected; this row is the file-predicate half.
78
- LP-04 target A loop making no progress stops instead of thrashing. gate .goblin/loop/decisions.tsv goblin-verify --only LP-04 Builtin: it measures a CHANGED EVIDENCE POINTER, which is a proxy for progress, not progress. Three consecutive verdict rows with an identical non-empty pointer and a non-`predicate:green` result is a FAIL that names the row numbers. A loop that edits a file each turn to keep the pointer moving is not caught, which is why LP-05 and the budget sit beside it. Nothing in the Hermes kanban goal loop detects a lack of progress at all - `run_kanban_goal_loop` carries no progress state.
79
- LP-05 target A loop that ended without its predicate green carries a written-up reason. script .goblin/loop/stuck.md, .goblin/loop/decisions.tsv goblin-verify --only LP-05 Builtin: the three-non-blank-line threshold is arbitrary and stated as such - it proves a write-up EXISTS, the way `SP-03` proves a bullet LOOKS checkable. Nothing can make the write-up true, and the row cannot see whether it names the right dead end or whether the loop stopped too early. A loop whose last row IS `predicate:green` needs no write-up and the row passes without reading one.
74
+ JG-03 target A judge lane that has never returned a non-`done` verdict is escalated. advisory .gob/loop/decisions.tsv advisory A lane that has returned two verdicts cannot be called always-yes, and a repo sees only its own rows; the history that would show a bad lane lives across cards and repos. The counter-measure is policy - one known-red control verdict per wave, recorded in docs/LOOP.md - and it is counted, not enforced.
75
+ LP-01 target The exit predicate is a command, and it ran before iteration 1. script .gob/loop/predicate, .gob/loop/first-run goblin-verify --only LP-01 Builtin: it proves the predicate is exactly ONE command and that a recorded first run exists whose timestamp is at or before the first log row - not that the command was actually run, and not that the `exit=` value was measured rather than typed (`HP-03`'s defect, one artifact over). It deliberately does NOT re-run the predicate: a stop condition is legitimately red before the loop finishes, so re-running it would fail a repo whose loop is working, and PROJECT-PRACTICE section 7 forbids a probe that writes. Timestamps compare on their `YYYY-MM-DDThh:mm:ss` prefix, so records written in different UTC offsets compare as text.
76
+ LP-02 target The predicate is pinned at loop start and is never relaxed. script .gob/loop/predicate.sha256 goblin-verify --only LP-02 Builtin: it proves the predicate file on disk still hashes to the recorded pin - never that the predicate is the RIGHT one, and never who edited it. The digest covers the predicate file alone: a loop that quietly relaxes a predicate living somewhere else is not seen. The remedy is printed with the failure and is never automatic, the `IN-02` `--re-pin` shape. W5-7: a close-and-reopen is auditable - every `closed-<date>/` archive must hold its predicate AND the pin it was closed under, and the live pin must name the archived digest on a `previous:` line - which catches a SILENT relaxation, never a WEAKER one (nothing in bash judges that: `docs/LIMITS.md` #39).
77
+ LP-03 target The loop declares a budget, and the record never exceeds it. gate .gob/loop/budget, .gob/loop/decisions.tsv goblin-verify --only LP-03 Builtin: it proves the declared budget is a positive integer at or under the configured `loop_max_turns_ceiling`, and that the record holds no more verdict rows than the budget - not that the budget is affordable. Cost is not a field the record carries: a turn budget bounds turns, and the auxiliary judge call per turn is unpriced (`docs/LIMITS.md` #32). A worker cannot edit its own card body, so a CARD predicate is already protected; this row is the file-predicate half.
78
+ LP-04 target A loop making no progress stops instead of thrashing. gate .gob/loop/decisions.tsv goblin-verify --only LP-04 Builtin: it measures a CHANGED EVIDENCE POINTER, which is a proxy for progress, not progress. Three consecutive verdict rows with an identical non-empty pointer and a non-`predicate:green` result is a FAIL that names the row numbers. A loop that edits a file each turn to keep the pointer moving is not caught, which is why LP-05 and the budget sit beside it. Nothing in the Hermes kanban goal loop detects a lack of progress at all - `run_kanban_goal_loop` carries no progress state.
79
+ LP-05 target A loop that ended without its predicate green carries a written-up reason. script .gob/loop/stuck.md, .gob/loop/decisions.tsv goblin-verify --only LP-05 Builtin: the three-non-blank-line threshold is arbitrary and stated as such - it proves a write-up EXISTS, the way `SP-03` proves a bullet LOOKS checkable. Nothing can make the write-up true, and the row cannot see whether it names the right dead end or whether the loop stopped too early. A loop whose last row IS `predicate:green` needs no write-up and the row passes without reading one.
80
80
  RC-01 target No file in the shipping tree matches a reference-corpus hash. gate reference-manifest.json + security: build_output: goblin-verify --only RC-01 Four clauses: (1) `reference_manifest:` empty -> SKIP with that reason (the `FM-01`/`VA-01` shape - not born RED); (2) declared but missing or unparseable -> FAIL, never a SKIP; (3) any file under the declared `security: build_output:` whose sha256 appears in an entry's sha256 -> FAIL, naming path + entry; (4) the manifest itself must not sit inside the build output, at the declared path or as a byte-identical copy - it is a listing of every corpus hash, so shipping it ships the corpus's shape. Cannot see: a re-encoded/resized/recoloured asset (level 2), copied text in a shipped string (level 3), or a manifest authored weak - that is `RC-02`.
81
81
  RC-02 target The reference manifest is shaped so `RC-01` cannot pass vacuously. lint reference-manifest.json goblin-verify --only RC-02 Schema `reference-manifest/1`; `generated_from` non-empty; `reference_app.package`/`.version`/`apk_sha256` (64 hex); `entries` non-empty; every entry carries `path`, 64-hex `sha256`, numeric `bytes`; `entry_count` equals `len(entries)`. SKIPs with `RC-01`'s reason when the key is empty. Exists because `RC-01` alone carries the `bans.tsv` weakness: a weakened input passes the check it feeds (`LIMITS.md` #28). It cannot see whether the entries are the *right* hashes - that is as strong as tamper hashing, and `installed.json` is unsigned (`LIMITS.md` #18).
82
- RC-03 target A lab repo tracks no extracted byte: scripts, notes, manifests, docs only. script manifests/*.sha256 + git ls-files goblin-verify --only RC-03 Two clauses: every tracked path falls under a declared allowlist (`scripts/`, `notes/`, `manifests/`, root docs - the harness's own `.goblin/` and `.hermes/` files and the `.gitignore` block it appends are the install, not the lab's content, and are excluded the way `FM-02` excludes them); and no tracked file's sha256 equals any hash in a `manifests/*.sha256`. The manifest itself lists paths and hashes, which the lab repo's own README explicitly permits - the check is about *bytes*, and it must not read the manifest as a violation. SKIPs with a reason when no `manifests/` exists. Cannot see a payload renamed and re-encoded - which is why the allowlist is a second, independent trap.
82
+ RC-03 target A lab repo tracks no extracted byte: scripts, notes, manifests, docs only. script manifests/*.sha256 + git ls-files goblin-verify --only RC-03 Two clauses: every tracked path falls under a declared allowlist (`scripts/`, `notes/`, `manifests/`, root docs - the harness's own `.gob/` and `.hermes/` files and the `.gitignore` block it appends are the install, not the lab's content, and are excluded the way `FM-02` excludes them); and no tracked file's sha256 equals any hash in a `manifests/*.sha256`. The manifest itself lists paths and hashes, which the lab repo's own README explicitly permits - the check is about *bytes*, and it must not read the manifest as a violation. SKIPs with a reason when no `manifests/` exists. Cannot see a payload renamed and re-encoded - which is why the allowlist is a second, independent trap.
83
83
  RC-04 target The acquisition record exists, and the manifest names the target and its source. lint manifests/*.sha256 header goblin-verify --only RC-04 The header block must name a target slug + version, a source store, and a checksum (md5 or sha256 hex); the manifest must hold a `*.apk` row, the header's named apk must be that row, and a 64-hex token in the header, if present, must equal the row's sha256 (a header carrying only the md5 passes - the comparison is conditional in the engine). Weakest of the four: it proves a record EXISTS, not that the number came from the store (`HP-03`'s defect; the `JG-01` shape). It is the first to cut if the matrix gets heavy.
84
84
  PR-01 source The installer never writes outside its target. test tests/t-install-* tests/run-tests.sh —
85
85
  PR-02 source A second install is a no-op, and an upgrade reports created/updated/unchanged. test tests/t-install-idempotent.sh tests/run-tests.sh —
@@ -21,8 +21,8 @@ overnight P10: an unattended run over a checkable predicate written before itera
21
21
  review-panel N independent verdict lanes, each its own card with its own resolved model. Only at stakes S3+. R6 sec.4.3
22
22
  judge A role that decides whether a PROCESS met its own predicate, from a command's output and a pointer it can resolve - never from a report. One lane per verdict; never the author's lane. docs/LOOP.md
23
23
  predicate One shell command that exits 0 when the loop is finished. Written before iteration 1 and run once before it, so its first state is known to be red. A duration is not a predicate. docs/LOOP.md + pstack guide/07
24
- loop One unattended run over a predicate, recorded in the committed .goblin/loop/ (predicate, pin, first-run, budget, one decisions.tsv row per iteration). docs/LOOP.md
25
- never-relax The predicate's digest is recorded at loop start and never updates itself; relaxing it is closing this loop and opening another, with the old predicate archived under .goblin/loop/closed-<date>/. docs/LOOP.md
24
+ loop One unattended run over a predicate, recorded in the committed .gob/loop/ (predicate, pin, first-run, budget, one decisions.tsv row per iteration). docs/LOOP.md
25
+ never-relax The predicate's digest is recorded at loop start and never updates itself; relaxing it is closing this loop and opening another, with the old predicate archived under .gob/loop/closed-<date>/. docs/LOOP.md
26
26
  round One full pass of the work cycle: plan, implement, gate, replay, hand off. The unit a wave code counts. UX-review-2026-10-06
27
27
  vendored Copied into the repo rather than read from the package, so it survives offline and is hashed by IN-02 like any installed file. UX-review-2026-10-06
28
28
  forge The hosting platform a repo pushes to (GitHub, GitLab, ...). PG-05 and PG-06 read the workflow text but can never see what the forge itself enforces. UX-review-2026-10-06
@@ -30,4 +30,4 @@ drift A file whose current bytes no longer match the hash recorded at install ti
30
30
  preimage The input that produces a known hash. The hash checks here prove non-drift, not preimage resistance - they are tamper-evidence, not signatures (docs/LIMITS.md #18). UX-review-2026-10-06
31
31
  lane A family of rules that share a mechanism and a blind spot: the ban lane, the judge/loop lane, the CI lane, the reference lane. The cannot-see footer reports per lane. UX-review-2026-10-06
32
32
  foreman The orchestrating agent that delegates work to the fleet; the role that reads HANDOFF.md first and writes it last. UX-review-2026-10-06
33
- engine The bash programs plus the manifest a verify run actually resolved: per-repo (.goblin/bin) or global (engine_dir:). Named in every run's footer. UX-review-2026-10-06
33
+ engine The bash programs plus the manifest a verify run actually resolved: per-repo (.gob/bin) or global (engine_dir:). Named in every run's footer. UX-review-2026-10-06
@@ -8,7 +8,7 @@ P6 goblin-verify-author a project has no live lane, or its gates drift 1 read th
8
8
  P7 goblin-pr-gate anything that should be reviewed before it lands 1 classify stakes S0-S4 | 2 run the gate set at the candidate SHA and record the numbers | 3 write reviews/<slug>-<head7>.md with head/base/patch-id/stakes/checks-run | 4 evaluate the panel rule for S3+ | 5 at S3+ the foreman is role-judge: one decision from N lane verdicts, never a vote count | 6 re-check the patch-id before landing the patch-id of base..head still matches the recorded one; the review note names a SHA that exists in git rev-list; for S2+ a check ran on that SHA; at S3+ the judge is a lane disjoint from the author's (JG-02) reviewer (+ architect for S3) review-panel
9
9
  P8 goblin-bootstrap adopting goblin-stack in a repo, or starting one 1 classify the project (A-E) | 2 goblin-install --class <x> | 3 goblin-verify GREEN | 4 fix .gitignore BEFORE any git init | 5 first HANDOFF, first SPEC, first check script goblin-verify exits 0 and the created-file list matches installed.json; a repo with no gate declares one and records its first measured numbers architect judgment
10
10
  P9 goblin-handoff ending a session, or picking up another's 1 commit uncommitted edits as one internally-consistent wip: commit | 2 write intent / verified state / next steps / what is NOT verified | 3 stale sentences get a dated parenthetical, never deletion | 4 on pickup: verify inherited claims against the artifact every gate number in the HANDOFF carries 'measured <date>'; the named HEAD matches git rev-parse --short HEAD; the NOT-verified section is non-empty or says 'nothing outstanding' any judgment
11
- P10 goblin-overnight an unattended run over a predicate 1 the exit condition is a checkable predicate written before iteration 1 | 2 it never gets relaxed | 3 an escape hatch: a genuine dead end writes up why and stops | 4 the morning audit reads the Attention section first the predicate is a command in the card body and was run at least once (its first run recorded before iteration 1); goal_max_turns is set and at or under loop_max_turns_ceiling; every landed change has a P7 verdict row; any verdict that says done cites a handle the repo resolves (JG-01); a run that ends without its predicate green carries .goblin/loop/stuck.md (LP-05) default + coder code + judge
11
+ P10 goblin-overnight an unattended run over a predicate 1 the exit condition is a checkable predicate written before iteration 1 | 2 it never gets relaxed | 3 an escape hatch: a genuine dead end writes up why and stops | 4 the morning audit reads the Attention section first the predicate is a command in the card body and was run at least once (its first run recorded before iteration 1); goal_max_turns is set and at or under loop_max_turns_ceiling; every landed change has a P7 verdict row; any verdict that says done cites a handle the repo resolves (JG-01); a run that ends without its predicate green carries .gob/loop/stuck.md (LP-05) default + coder code + judge
12
12
  P11 goblin-sweep the same change or question across projects 1 enumerate targets with a shell glob, not a memory | 2 classify each (A-E); an archive project is skipped, not processed | 3 one card per project, parents=[sweep] | 4 collect one line per project: what changed / what was refused / what is unfindable the per-project line carries the command it ran; the sweep report states its own coverage (n of m projects, and names the skipped ones) default synthesis
13
13
  P12 goblin-eval a skill or prompt changed, and you want to know if it did anything 1 candidate and control run in sanitized directories | 2 no eval/test/judge/rubric token anywhere the candidate sees | 3 grade the chain from the transcript (which files it actually opened), never self-report | 4 the judge runs on a different model family | 5 write the record to evals/<slug>/ (prompt, rubric, manifest.tsv, transcripts/, verdict.md) | 6 a change must improve the evaluated cases or add new evaluations; a generated verification skill passes only on a measured sensitivity the judge's verdict is reproducible from the transcripts; candidates never learn other candidates exist; the record exists and every lane names a transcript file that exists; a generated verification skill is verified only by a record, with its sensitivity (detected of seeded) printed beside the control's number researcher synthesis
14
14
  P13 goblin-bugreporter an event delivered a report 1 validate intake | 2 freeze repo+revision | 3 reproduce (R1 then R2 then R3) | 4 write repro.md | 5 create the fix card only on `reproduced` | 6 complete with the verdict repro.md carries a command, a revision that exists in git rev-list, and a RED `pre` row; the fix card exists iff the verdict is `reproduced`; the tree is clean after the run researcher investigate