@techgoblin/gobstack 0.0.0-stage → 0.4.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/LICENSE +21 -0
  3. package/README.md +217 -2
  4. package/VERSION +1 -0
  5. package/adapters/_template/adapter.tsv +16 -0
  6. package/adapters/_template/detect.sh +10 -0
  7. package/adapters/_template/emit.sh +5 -0
  8. package/adapters/_template/verify.sh +4 -0
  9. package/adapters/claude/adapter.tsv +8 -0
  10. package/adapters/claude/detect.sh +8 -0
  11. package/adapters/claude/verify.sh +47 -0
  12. package/adapters/codex/adapter.tsv +12 -0
  13. package/adapters/codex/detect.sh +9 -0
  14. package/adapters/codex/verify.sh +45 -0
  15. package/adapters/copilot/adapter.tsv +10 -0
  16. package/adapters/copilot/detect.sh +8 -0
  17. package/adapters/copilot/verify.sh +45 -0
  18. package/adapters/cursor/adapter.tsv +11 -0
  19. package/adapters/cursor/detect.sh +10 -0
  20. package/adapters/cursor/verify.sh +45 -0
  21. package/adapters/gemini/adapter.tsv +15 -0
  22. package/adapters/gemini/detect.sh +11 -0
  23. package/adapters/gemini/verify.sh +49 -0
  24. package/adapters/hermes/adapter.tsv +9 -0
  25. package/adapters/hermes/detect.sh +8 -0
  26. package/adapters/hermes/verify.sh +27 -0
  27. package/adapters/opencode/adapter.tsv +14 -0
  28. package/adapters/opencode/detect.sh +9 -0
  29. package/adapters/opencode/verify.sh +45 -0
  30. package/automations/README.md +53 -0
  31. package/automations/bugreporter-intake.sh +145 -0
  32. package/automations/drift-audit.sh +139 -0
  33. package/automations/report.schema.tsv +10 -0
  34. package/bans/README.md +82 -0
  35. package/bans/grep-ban.sh +84 -0
  36. package/bans/layer-check.sh +57 -0
  37. package/bin/goblin +119 -0
  38. package/bin/goblin-audit +145 -0
  39. package/bin/goblin-bans +178 -0
  40. package/bin/goblin-doctor +233 -0
  41. package/bin/goblin-emit +484 -0
  42. package/bin/goblin-init +519 -0
  43. package/bin/goblin-install +720 -0
  44. package/bin/goblin-lib.sh +289 -0
  45. package/bin/goblin-model +105 -0
  46. package/bin/goblin-upgrade +572 -0
  47. package/bin/goblin-verify +2798 -0
  48. package/bin/goblin.js +103 -0
  49. package/docs/ADOPTION.md +168 -0
  50. package/docs/CI.md +187 -0
  51. package/docs/CONTRACTS.md +197 -0
  52. package/docs/DESIGN.md +92 -0
  53. package/docs/ENFORCEMENT.md +225 -0
  54. package/docs/FLOWS.md +164 -0
  55. package/docs/GUARDRAILS.md +126 -0
  56. package/docs/GUIDE.md +610 -0
  57. package/docs/INTEGRATION.md +92 -0
  58. package/docs/LIMITS.md +591 -0
  59. package/docs/LOOP.md +165 -0
  60. package/docs/RE-PLAYBOOK.md +183 -0
  61. package/docs/RISKS.md +70 -0
  62. package/docs/ROLES.md +105 -0
  63. package/manifest/bans.tsv +9 -0
  64. package/manifest/classes.tsv +61 -0
  65. package/manifest/enforcement.tsv +88 -0
  66. package/manifest/glossary.tsv +25 -0
  67. package/manifest/playbooks.tsv +16 -0
  68. package/package.json +37 -4
  69. package/presets/A-shipped-software.yaml +48 -0
  70. package/presets/B-service-config.yaml +40 -0
  71. package/presets/C-game.yaml +38 -0
  72. package/presets/D-knowledge.yaml +41 -0
  73. package/presets/E-fleet-config.yaml +42 -0
  74. package/presets/F-electron.yaml +67 -0
  75. package/roles.yaml +54 -0
  76. package/skills/goblin-bootstrap/SKILL.md +51 -0
  77. package/skills/goblin-bugfix/SKILL.md +26 -0
  78. package/skills/goblin-bugreporter/SKILL.md +52 -0
  79. package/skills/goblin-drift-audit/SKILL.md +43 -0
  80. package/skills/goblin-eval/SKILL.md +68 -0
  81. package/skills/goblin-feature/SKILL.md +26 -0
  82. package/skills/goblin-feature-map/SKILL.md +140 -0
  83. package/skills/goblin-handoff/SKILL.md +28 -0
  84. package/skills/goblin-investigation/SKILL.md +26 -0
  85. package/skills/goblin-judge/SKILL.md +74 -0
  86. package/skills/goblin-loop/SKILL.md +88 -0
  87. package/skills/goblin-mode/SKILL.md +70 -0
  88. package/skills/goblin-overnight/SKILL.md +42 -0
  89. package/skills/goblin-pr-gate/SKILL.md +42 -0
  90. package/skills/goblin-re-mobile/SKILL.md +51 -0
  91. package/skills/goblin-refactor/SKILL.md +23 -0
  92. package/skills/goblin-sweep/SKILL.md +23 -0
  93. package/skills/goblin-tdd-repro/SKILL.md +27 -0
  94. package/skills/goblin-verify-author/SKILL.md +50 -0
  95. package/skills/practice/SKILL.md +37 -0
  96. package/templates/AGENTS.md.tmpl +23 -0
  97. package/templates/HANDOFF.md.tmpl +43 -0
  98. package/templates/SPEC.md.tmpl +34 -0
  99. package/templates/audit-waiver.tsv.tmpl +10 -0
  100. package/templates/boundary-waivers.tmpl +8 -0
  101. package/templates/checks/assert.mjs.tmpl +60 -0
  102. package/templates/checks/gate.sh.tmpl +29 -0
  103. package/templates/ci/goblin-gate.yml.tmpl +46 -0
  104. package/templates/goblin.yaml.tmpl +138 -0
  105. package/templates/install-hooks.allowlist.tmpl +9 -0
  106. package/templates/loop/decisions.tsv.tmpl +1 -0
  107. package/templates/loop/predicate.tmpl +16 -0
  108. package/templates/report.yaml.tmpl +16 -0
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: goblin-mode
3
+ description: Route a request to one goblin-stack playbook, seed the todos, and keep the two reply habits.
4
+ ---
5
+
6
+ # goblin-mode
7
+
8
+ The router. Load this first in any repo that has goblin-stack installed. It turns a request
9
+ into exactly one playbook, and it refuses to invent machinery that does not exist here.
10
+
11
+ ## The index — one request, one playbook
12
+
13
+ | request | playbook | skill |
14
+ |---|---|---|
15
+ | a read-only question, or "why is this happening" | P1 | `goblin-investigation` |
16
+ | a reported defect | P2 | `goblin-bugfix` |
17
+ | new behaviour | P3 | `goblin-feature` |
18
+ | a behaviour-preserving reshape | P4 | `goblin-refactor` |
19
+ | a defect where a regression test is cheap | P5 | `goblin-tdd-repro` |
20
+ | a project has no live lane, or its gates drift | P6 | `goblin-verify-author` |
21
+ | a feature map is missing, or its entry points have drifted | P6 | `goblin-verify-author` + `goblin-feature-map` |
22
+ | anything that should be reviewed before it lands | P7 | `goblin-pr-gate` |
23
+ | adopting goblin-stack in a repo, or starting one | P8 | `goblin-bootstrap` |
24
+ | ending a session, or picking up another's | P9 | `goblin-handoff` |
25
+ | an unattended run over a predicate | P10 | `goblin-overnight` |
26
+ | the same change or question across projects | P11 | `goblin-sweep` |
27
+ | a skill or prompt changed, and you want to know if it did anything | P12 | `goblin-eval` |
28
+ | an event delivered a report (a bug report, a chat message, a webhook) | P13 | `goblin-bugreporter` |
29
+ | a recorded claim disagrees with the artifact (drift) | P14 | `goblin-drift-audit` |
30
+
31
+ If a request matches none of these, say so and ask — do not stretch a playbook to fit.
32
+
33
+ ## The two reply habits
34
+
35
+ 1. **Lead with the answer, evidence after.** The first line is the outcome; the command and
36
+ its output follow. Never open with what you are about to do.
37
+ 2. **Say what is NOT verified.** Every report ends with the surface the checks did not reach.
38
+ A claim you cannot source is marked `[unverified]`; it is never quietly implied.
39
+
40
+ ## Todo seeding
41
+
42
+ Before the first tool call of a non-trivial task, seed the todo list from the playbook's
43
+ required steps — one todo per step, in order, with the verification step last. A todo is
44
+ closed only by the measurement it names, never by "looks right". If a step turns out to be
45
+ unnecessary, close it with the reason rather than deleting it: the list is the record.
46
+
47
+ ## The read-vs-write fan-out rule
48
+
49
+ The axis that decides fan-out is **read versus write**, not difficulty.
50
+
51
+ - **Read-only work** (P1, P12, a P11 sweep) may fan out: independent readers cannot corrupt
52
+ each other, and the cost is bounded by the number of targets.
53
+ - **Write work** (P2, P3, P4, P5) does not fan out by default. Parallel writers to one tree
54
+ cost about N times the tokens and produce conflicts a single careful pass would not.
55
+
56
+ **Role-pinned fan-out goes through the kanban.** A bare subagent spawn has no model or
57
+ provider parameter, so it cannot honour a role: it would silently run every lane on one
58
+ model, which is exactly the review failure the panel exists to prevent. Use a board card per
59
+ lane — each carrying the resolved provider/model, the pinned SHA, the diff and one focus —
60
+ never a subagent spawn. `docs/ROLES.md` states this as a rule.
61
+
62
+ ## Forbidden code
63
+
64
+ `.goblin/bin/goblin-bans` runs the ban table `.goblin/manifest/bans.tsv`; the bans this repo
65
+ turns on are named by `bans:` in `.goblin/goblin.yaml`. Run it before you write the line —
66
+ `goblin-verify` only reports a ban once the code already exists.
67
+
68
+ ## What this cannot see
69
+
70
+ Whether the request was the right one to make. It routes; it does not judge intent.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: goblin-overnight
3
+ description: P10: an unattended run over a checkable predicate written before iteration 1.
4
+ ---
5
+
6
+ # goblin-overnight (P10)
7
+
8
+ 1. **The exit condition is a checkable predicate written before iteration 1**, and it is a
9
+ command — in the card body, or in `.goblin/loop/predicate`. Run it once before starting, so
10
+ you know it is runnable; record that run as `exit=<n> ts=<ISO8601>` in `.goblin/loop/first-run`
11
+ and pin the predicate (`LP-01`, `LP-02`).
12
+ 2. **It never gets relaxed.** If the predicate turns out to be wrong, stop and write up why; do
13
+ not edit the predicate to fit the result. Relaxing it is closing this loop and opening
14
+ another, with the old predicate archived under `.goblin/loop/closed-<date>/`.
15
+ 3. **An escape hatch.** A genuine dead end writes up why and stops: `.goblin/loop/stuck.md`, at
16
+ least three non-blank lines naming the predicate, committed. The exit a worker can actually
17
+ reach is **`kanban_block`** (naming the predicate) — there is no `kanban_edit` tool in a
18
+ worker's toolset. An unattended run that cannot stop is a runaway.
19
+ 4. **The morning audit reads the `Attention` section first**, then the record's rows whose
20
+ `result` is not `predicate:green`, in order.
21
+
22
+ ## Verification
23
+
24
+ - The predicate is one command, and its first run is recorded at or before the first log row
25
+ (`LP-01`).
26
+ - The predicate is pinned by digest and the pin has not moved (`LP-02`).
27
+ - `goal_max_turns` is set, at or under `loop_max_turns_ceiling`, and the record never exceeds it
28
+ (`LP-03`).
29
+ - No three consecutive verdict rows share an evidence pointer without reaching
30
+ `predicate:green` (`LP-04`).
31
+ - A run whose last row is not `predicate:green` carries `.goblin/loop/stuck.md` naming the
32
+ predicate (`LP-05`).
33
+ - Every landed change has a P7 verdict row, and any verdict that says `done` cites a handle the
34
+ repo resolves (`JG-01`).
35
+ - The four record artifacts — `predicate`, `predicate.sha256`, `first-run`, `decisions.tsv` —
36
+ are committed, so tomorrow's reader can re-derive every decision.
37
+
38
+ ## What this cannot see
39
+
40
+ Whether the predicate was the right one. It measures only what it was told to measure — and a
41
+ predicate that was **vacuously true from the start** runs green on nothing, which is why the
42
+ run-once rule exists and why a human reads the predicate.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: goblin-pr-gate
3
+ description: P7: classify stakes S0-S4, run the gate at the candidate SHA, write a SHA-named verdict.
4
+ ---
5
+
6
+ # goblin-pr-gate (P7)
7
+
8
+ Use for anything that should be reviewed before it lands.
9
+
10
+ 1. **Classify the stakes S0-S4.** The gate is chosen by the *change*, not by the repo.
11
+ - S0: no review. A typo in a comment.
12
+ - S1: self-review. A local edit with no shared surface.
13
+ - S2: one reviewer, gate set run at the candidate SHA.
14
+ - S3: a panel of two or more independent lanes, each its own card and its own model.
15
+ - S4: a panel plus a human decision before landing.
16
+ 2. **Run the gate set at the candidate SHA and record the numbers.**
17
+ 3. **Write `reviews/<slug>-<head7>.md`** with `head:`, `base:`, `patch-id:`, `stakes:`,
18
+ `checks-run:`, `lanes:`. The file is the artifact; a board card is only the routing record.
19
+ At S2+, add a `security:` line naming three things the checks cannot tell a reader: the audit
20
+ record's date, the number of matched waivers, and the perf number with its baseline commit.
21
+ It is prose on purpose — a review prompt, not a check (`SC-07`, `PF-01`).
22
+ 4. **Evaluate the panel rule for S3+.** One lane is not a panel. Lanes go through the kanban
23
+ (see `goblin-mode`), each carrying the resolved provider/model.
24
+ 5. **At S3+, the foreman is `role-judge`.** N lane verdicts are **opinions**; the foreman turns
25
+ them into one **decision**, and it is a lane disjoint from the author's — a panel that grades
26
+ its own author is the author. A vote count is not a decision.
27
+ 6. **Re-check the patch-id before landing.**
28
+
29
+ ## Verification
30
+
31
+ - `patch-id:` of `base..head` still matches. A new head voids the verdict — a matching commit
32
+ message does not restore it, and a green check from an older SHA is not a substitute.
33
+ - The named `head:` exists in `git rev-list --all`.
34
+ - For S2+, a check actually ran on that SHA.
35
+ - At S3+, the deciding lane is disjoint from the author's (`JG-02`).
36
+
37
+ ## What this cannot see
38
+
39
+ A required check that self-skips (it reports Success), and a protected branch whose only
40
+ admin is the person pushing (it protects nothing). Nor can it see *which* lane produced a
41
+ verdict: the review note names the declared lanes, and no file in the repo observes the profile
42
+ that actually ran (`MD-03`).
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: goblin-re-mobile
3
+ description: P15: understand one shipped Android build as facts for study - triage, carve, manifest, dossier.
4
+ ---
5
+
6
+ # goblin-re-mobile (P15)
7
+
8
+ Use when one shipped Android build must be understood as facts for study, with a
9
+ reproducible, hash-manifested corpus. The full procedure is `docs/RE-PLAYBOOK.md`; the
10
+ steps:
11
+
12
+ 1. **S0 preflight.** The dedicated sandbox exists and is the one the fences describe - a
13
+ disposable LXC/VM, never the host.
14
+ 2. **S1 acquire.** Owned or free builds only, from a store listing with a published hash.
15
+ A cracked or pirated APK is a hard fence - a stop, not a judgment call.
16
+ 3. **S2 provenance.** Verify the download against the store's published md5/sha256 before
17
+ anything else touches it; record it in the acquisition record.
18
+ 4. **S3 triage.** The engine verdict, cheapest test first: `.so` markers
19
+ (`lib/*/libunity.so` + `global-metadata.dat` = Unity IL2CPP, `libue4.so` = Unreal,
20
+ `libcocos2d*.so` = Cocos, `libgodot*` = Godot); no `.so` plus packed assets = custom
21
+ Java engine.
22
+ 5. **S4 static decompile.** apktool/jadx into the quarantine, never into any repo.
23
+ 6. **S5 carve.** A carver written from decompiled evidence, never guessed - find the
24
+ loader, recover the transform, reimplement, cross-check against the container's own
25
+ size tables (the Kairosoft `.dat` precedent).
26
+ 7. **S6 manifest.** sha256 of every extracted payload into `manifests/*.sha256`, header
27
+ naming target + version + source store + anchor, plus the apk row itself.
28
+ 8. **S7 dossier.** Facts and numbers, never expression: measurements, counts,
29
+ class/method citations. No extracted art, no copied text.
30
+ 9. **S8 runtime analysis.** Deferred by design: frida hooking needs a device/emulator and
31
+ an owned build; static facts first.
32
+ 10. **S9 retention/teardown.** The corpus lives only in the sandbox quarantine; the lab
33
+ repo keeps scripts, notes and hashes only. Delete or keep is a recorded decision.
34
+
35
+ ## Verification
36
+
37
+ - The corpus manifest verifies `sha256sum -c` where the corpus lives.
38
+ - `goblin-verify` against the lab repo needs the harness installed there first: one
39
+ `goblin-install --target <lab-repo> --class A`, after which `--only RC-03` / `RC-04` run
40
+ (without the install, goblin-verify exits 2 `not installed`).
41
+ - `goblin-verify --only RC-01` / `RC-02` / `RC-03` / `RC-04` return the exits their rows
42
+ define - an exact hash inside the build output, a weak manifest and a tracked payload
43
+ each fail the build.
44
+ - Every negative control NC-1..NC-6 was shown RED and then restored.
45
+
46
+ ## What this cannot see
47
+
48
+ - A re-encoded or resized asset passes `RC-01`'s exact-hash gate (level 2); copied text
49
+ inside a shipped string is level 3.
50
+ - A weak manifest authored by hand is `RC-02`'s problem only if malformed.
51
+ - Nothing here proves a fact is CORRECT - only that it is traceable to the corpus.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: goblin-refactor
3
+ description: P4: pin the contract first, reshape, delete the legacy path in the same change.
4
+ ---
5
+
6
+ # goblin-refactor (P4)
7
+
8
+ 1. **Pin the contract first** — a characterization test, a snapshot, or an equivalence
9
+ harness that asserts the *current* behaviour. A type check and lint are not a pin: they
10
+ say the code still compiles, not that it still does the same thing.
11
+ 2. **Shape only, no behaviour.** If the pin changes, this is not a refactor; stop and treat it
12
+ as a feature (P3) or a bug fix (P2).
13
+ 3. **Delete the legacy path in the same change.** A refactor that leaves the old path behind
14
+ has doubled the surface, not reshaped it.
15
+
16
+ ## Verification
17
+
18
+ - The pin is a real assertion, run before and after, with both results captured.
19
+ - The legacy path is gone: the pin no longer references it and `grep` for it returns nothing.
20
+
21
+ ## What this cannot see
22
+
23
+ Performance characteristics a functional pin does not cover.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: goblin-sweep
3
+ description: P11: the same change or question across projects - one card each, one line back.
4
+ ---
5
+
6
+ # goblin-sweep (P11)
7
+
8
+ 1. **Enumerate targets with a shell glob, not a memory.** A list typed from memory is a list
9
+ that is already wrong.
10
+ 2. **Classify each target A-F.** An `archive` project is **skipped, not processed** — say so.
11
+ 3. **One card per project, parented to the sweep card.**
12
+ 4. **Collect one line per project**: what changed / what was refused / what is unfindable.
13
+
14
+ ## Verification
15
+
16
+ - The per-project line carries the command it ran.
17
+ - The sweep report states its own coverage: `n of m projects`, and names the ones it skipped.
18
+ - A refusal is a result. Report it as one.
19
+
20
+ ## What this cannot see
21
+
22
+ Projects that exist but were not enumerated by the glob — a sweep can only report on the set
23
+ it looked at, which is why it must state that set.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: goblin-tdd-repro
3
+ description: P5: write the failing test, confirm it fails for the intended reason, fix, replay RED.
4
+ ---
5
+
6
+ # goblin-tdd-repro (P5)
7
+
8
+ Use when a defect is reproducible and a regression test is cheap.
9
+
10
+ 1. **Write the failing test.**
11
+ 2. **Confirm it fails for the intended reason** — not a typo, not a missing import. Paste the
12
+ failure and say why it is the right failure.
13
+ 3. **Smallest production fix.** No drive-by edits.
14
+ 4. **Revert the fix -> the test MUST fail -> restore.** This is the negative control; without
15
+ it the test is decoration.
16
+
17
+ ## Verification
18
+
19
+ - The RED-again step is captured, with the command and the failure.
20
+ - Prefer no new test over a bad test. A test that asserts an implementation detail pins the
21
+ bug in place.
22
+ - If you skip this playbook because a regression test is not cheap, say so explicitly. The
23
+ skip path is never silent.
24
+
25
+ ## What this cannot see
26
+
27
+ Defects that need a device, a network, or a human to trigger.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: goblin-verify-author
3
+ description: P6: author a project's gate set, harness and feature map, then execute them once end to end.
4
+ ---
5
+
6
+ # goblin-verify-author (P6)
7
+
8
+ Use when a project has no live lane, or its gates have drifted from what it actually does.
9
+
10
+ 1. **Read the repo, not the user, for entry points.** The declared gate must be a command the
11
+ repo can actually run. One inferred command is wrong for a repo with no runner, a repo that
12
+ cannot run its own typecheck read-only, and a repo whose runner lives elsewhere — all at
13
+ once.
14
+ 2. **Write the gate/check set into the harness dir**, in the house harness shape: a
15
+ `failures` counter, an `assert(name, ok, detail)` printer, a non-zero exit on failure, and
16
+ a REPLAY block at the bottom.
17
+ 3. **Execute it once end to end.** A generated skill that was never executed is a draft, not
18
+ a deliverable.
19
+ 4. **Add the REPLAY block.** Run each assertion against the pinned pre-change commit and
20
+ require it to be RED there. Pin the pre-change commit, never `HEAD`.
21
+ 5. **Seed the feature map.** The harness needs a list of what there is to drive, and the map is
22
+ that list (`goblin-feature-map`): one file per user-facing feature, each with its entry points
23
+ and the exact command that drives it. Declare `feature_map:` and `source_root:` in
24
+ `.goblin/goblin.yaml`. An **empty `feature_map:` is the honest state of a project with no map
25
+ yet** (`FM-01`/`FM-02` then skip with that reason); a declared path that names no map is a hole
26
+ and is reported as one.
27
+ 6. **Hand the generated skill to P12 before calling it verified.** goblin-stack installs
28
+ agent-authored skills, and a generated skill accepted after a skim is a different proposition
29
+ from a written one — the record P12 produces (`evals/<slug>/`) is what makes "verified" a
30
+ measurement rather than a date. **`verified:` does not advance until that record exists.** The
31
+ row that checks a record's shape is not shipped yet, so this is a stated requirement here and
32
+ in `goblin-eval`, not a claim that a command enforces it.
33
+
34
+ ## Verification
35
+
36
+ - The harness prints `PASS`/`FAIL` per assertion and exits non-zero on any failure.
37
+ - The REPLAY shows RED pre-change, with the commit named.
38
+ - The gate set is declared in `.goblin/goblin.yaml`, never inferred.
39
+ - The map's index and four-H2 entry contract hold (`FM-01`), every declared entry path still
40
+ resolves under `source_root:` and none changed after its `verified:` date (`FM-02`), and the
41
+ declared `verify_doctor:` exits 0 (`VA-01`).
42
+ - A `verified:` date advances only for a feature that was actually driven, and only after the P12
43
+ record for the generated skill itself exists.
44
+
45
+ ## What this cannot see
46
+
47
+ Whether an assertion tests the right path rather than merely passing. That needs a human read
48
+ of the diff, and `goblin-verify` says so on every run. Neither does this skill know whether the
49
+ map lists **every** feature: `FM-01` checks the index against the files that exist, so a feature
50
+ nobody wrote down is invisible to it.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: practice
3
+ description: The composition hook: read the referenced standard at the path pinned in .goblin/goblin.yaml.
4
+ ---
5
+
6
+ # practice
7
+
8
+ goblin-stack owns the **mechanism**: which rule is enforced by what, how it is installed, how
9
+ it is verified, which class a project is, which flow applies, which role runs it.
10
+
11
+ The **house style** — the HANDOFF shape and the stale-sentence rule, the SPEC lifecycle, the
12
+ harness house style and the pinned-commit REPLAY, the gate vocabulary, commit discipline, the
13
+ delegation tiers, data safety, the documentation duty, adopt-don't-replace — belongs to the
14
+ standard this repo references. goblin-stack carries **no copy of it**.
15
+
16
+ ## Mandate
17
+
18
+ 1. Read `.goblin/goblin.yaml` and take `practice:`.
19
+ 2. If that path exists, **read the standard before starting work** in this repo. Its hash is
20
+ pinned in `practice_sha256:`; `goblin-verify` re-checks it, so a silently edited standard
21
+ is visible rather than assumed. If the standard has been edited **deliberately**, re-record
22
+ the pin deliberately: `goblin-install --target <repo> --re-pin` rewrites that one line and
23
+ prints the old and new hash. Nothing re-pins on its own — not `goblin-verify`, not
24
+ `--upgrade` (`docs/CONTRACTS.md`).
25
+ 3. If the path is absent or unset, **say so** and continue with the goblin-stack rules alone.
26
+ Absent is not an error: goblin-stack is portable, and another machine has no such file.
27
+
28
+ ## Why it is a pointer and not a copy
29
+
30
+ Two living copies of the same rule with no equality check is the rot goblin-stack exists to
31
+ remove. One copy plus a recorded hash is not. Do not paste the standard's text into any file
32
+ here, and do not supersede it: split by kind, not by ownership.
33
+
34
+ ## What this cannot see
35
+
36
+ Whether the standard itself is still right. That is a human's edit, and this skill can only
37
+ notice that it changed.
@@ -0,0 +1,23 @@
1
+ # AGENTS.md
2
+
3
+ This repository uses **goblin-stack** (class `{{CLASS}}`, installed {{DATE}}). It is a pointer,
4
+ not a rule dump — the rules live in one executable place, and facts beat requirements.
5
+
6
+ - **Rules and their checks:** `.goblin/manifest/enforcement.tsv` (one row per rule; every row
7
+ carries a runnable check or the literal `advisory`).
8
+ - **Forbidden code (the ban list):** `.goblin/bin/goblin-bans` runs the bans named by `bans:` in
9
+ `.goblin/goblin.yaml`; each ban's mechanism lives in `.goblin/manifest/bans.tsv`. Run it before
10
+ you write the line, not after — a ban is a gate, and `goblin-verify` only reports it once the
11
+ code exists.
12
+ - **Verify:** `.goblin/bin/goblin-verify` — exit 0 pass, 1 a check failed, 2 could not run,
13
+ 3 the manifest is broken.
14
+ - **Procedures:** the skills installed at `.hermes/skills/` (Hermes project tier — highest
15
+ precedence, repo-owned). Start with `goblin-mode`, which routes a request to a playbook.
16
+ - **Session state:** `HANDOFF.md` at the repo root. Read it before touching anything.
17
+ - **Project config:** `.goblin/goblin.yaml` (class, branch, gates, ratchet, runtime data,
18
+ replay, opt-outs). Edit it in place; the installer never overwrites it.
19
+ - **The referenced standard**, if configured, is named by `practice:` in `.goblin/goblin.yaml`.
20
+ Read it before starting work; this repo carries no copy of it.
21
+
22
+ Two things this repo does not do: it does not choose models (a role resolves through the
23
+ mapping file named by `models_file:`), and it does not write outside its own tree.
@@ -0,0 +1,43 @@
1
+ # HANDOFF — the session-boundary contract. A new worker reads this FIRST.
2
+
3
+ > Template. Written once by goblin-install and never overwritten. Update it every round:
4
+ > every gate number carries `measured <date>`, the State line names the current HEAD, and the
5
+ > NOT verified section is non-empty or says "nothing outstanding".
6
+
7
+ ## ▶ START HERE (new session)
8
+
9
+ - Working dir: the repo root.
10
+ - Gate set: `{{GATE_NAME}}` — `{{GATE_CMD}}`, plus whatever you add in `.goblin/goblin.yaml`.
11
+ - Run the gates and the manifest checks: `.goblin/bin/goblin-verify`
12
+ - One line of measured numbers for the round: `.goblin/bin/goblin-verify --only GT-02` writes `.goblin/last-gate-line`.
13
+ - Read before working: the referenced standard, if `.goblin/goblin.yaml` declares `practice:`.
14
+
15
+ ## State
16
+
17
+ - Branch: `{{BRANCH}}` · class: `{{CLASS}}` · goblin-stack installed {{DATE}}.
18
+ - HEAD when this file was written: `{{HEAD}}`
19
+ - In flight: nothing yet.
20
+ - Done: the harness is installed.
21
+ - Pushed: not yet.
22
+
23
+ ## Gates
24
+
25
+ Every number here is a measurement with a date, never a copy from a previous round.
26
+
27
+ - `{{GATE_NAME}}` = 0 · measured {{DATE}}
28
+ - ratchet — declared in `.goblin/goblin.yaml`; re-measure it, do not copy it.
29
+ - Example of the required form: `typecheck=0 · build=0 · ratchet <n> (ceiling <c>) · <n>/<n> harnesses green · measured {{DATE}}`
30
+
31
+ ## Next steps
32
+
33
+ - Replace the default gate with this project's real commands (playbook P8 step 3).
34
+ - Write the first round's SPEC (`*-SPEC.md`) before any code moves (playbook P3).
35
+ - Write the first asserting harness in the harness dir, and pin its pre-change commit so
36
+ `replay.commit` can be set (playbook P6, then HS-02).
37
+
38
+ ## NOT verified
39
+
40
+ - Nothing in this project has been proven by a human yet: the harness exists, its assertions
41
+ do not. Every gate number above is the value the declared command printed, and nothing more.
42
+ - A stale sentence in this file is corrected in place with a dated parenthetical, never
43
+ silently deleted: deleting it erases the fact that it was once believed true.
@@ -0,0 +1,34 @@
1
+ # ROUND-000-SPEC — <one line: what this round changes>
2
+
3
+ > Template. Written once by goblin-install for classes that require a SPEC, then never
4
+ > overwritten. Write one per round BEFORE implementation, and commit it with — or before —
5
+ > the code it describes: an untracked `*-SPEC.md` is lost by a run that dies.
6
+
7
+ Class: {{CLASS}} · written {{DATE}}
8
+
9
+ ## Measured root cause
10
+
11
+ State *why*, backed by a command whose output is pasted here. A report of "X is broken" is
12
+ not a root cause; a screenshot or a complaint is a hint for what to measure, never the
13
+ measurement.
14
+
15
+ <the command>
16
+ <its real output>
17
+
18
+ ## What changes
19
+
20
+ <the smallest change that removes the measured cause, and the data shape it introduces>
21
+
22
+ ## AC: acceptance criteria (each checkable without a human)
23
+
24
+ - AC1: `<a command>` prints `<the expected value>` — i.e. the criterion is a comparison, not a feeling.
25
+ - AC2: the same command exits 0 before the change and non-zero after it (the negative control).
26
+ - AC3: `git status --short` is empty after the round's commit.
27
+
28
+ ## Device-test items (not checkable here)
29
+
30
+ <the things only a human can judge, listed separately so they are never mistaken for the ACs>
31
+
32
+ ## What this spec cannot see
33
+
34
+ <the surface the checks above do not reach>
@@ -0,0 +1,10 @@
1
+ # .goblin/audit-waiver.tsv — one line per accepted dependency advisory (SC-07).
2
+ #
3
+ # package severity id date reason
4
+ #
5
+ # The date is the day the decision was taken, and SC-07 refuses a waiver older than
6
+ # security.waiver_max_age_days: a waiver with no expiry is a permanent blind spot. A
7
+ # high|critical line in .goblin/audit.tsv with no matching row here is a RED.
8
+ #
9
+ # example:
10
+ # next high GHSA-xxxx-yyyy-zzzz 2026-09-24 no fixed release yet; the vulnerable path is the dev server only
@@ -0,0 +1,8 @@
1
+ # .goblin/boundary-waivers — write routes that deliberately do NOT validate input (SC-05).
2
+ #
3
+ # One repo-relative path per line, with the reason after a '#'. A waiver is a DECISION recorded
4
+ # where the row can read it; it is not a silence. SC-05 prints the waiver count on its gate line
5
+ # so the debt is visible on every run.
6
+ #
7
+ # example:
8
+ # src/app/api/webhook/route.ts # signed by the provider's HMAC, verified in lib/hmac.ts
@@ -0,0 +1,60 @@
1
+ // checks/assert.mjs — the house assertion harness (PROJECT-PRACTICE section 3 shape).
2
+ //
3
+ // Rename it to checks/<round>-<slug>.mjs and write real assertions. This file ships the
4
+ // SHAPE, not the checks: it currently asserts one thing that is true by construction, so it
5
+ // is a scaffold, not a deliverable. P6: "a generated skill that was never executed is a
6
+ // draft, not a deliverable."
7
+ //
8
+ // WHAT THIS CANNOT SEE: anything that needs a browser, a device, a network service or a
9
+ // human. Say so at the top of every harness you write here.
10
+
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const failures = [];
14
+ let checked = 0;
15
+
16
+ function assert(name, ok, detail = '') {
17
+ checked += 1;
18
+ if (ok) {
19
+ console.log(`PASS ${name}${detail ? ' - ' + detail : ''}`);
20
+ } else {
21
+ console.log(`FAIL ${name}${detail ? ' - ' + detail : ''}`);
22
+ failures.push(name);
23
+ }
24
+ }
25
+
26
+ // Source probes read text with comments BLANKED first, not stripped: blanking keeps line
27
+ // numbers and byte offsets true, and stops a comment that quotes a removed control from
28
+ // reading as the control still being present.
29
+ function blankComments(src) {
30
+ return src
31
+ .replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '))
32
+ .replace(/(^|[^:])\/\/[^\n]*/g, (m, p1) => p1 + ' '.repeat(m.length - p1.length));
33
+ }
34
+
35
+ // ---- REPLAY ------------------------------------------------------------------
36
+ // A check green on both trees proves NOTHING. Before this harness is trusted, run it against
37
+ // the pinned pre-change commit and require it to be RED there:
38
+ //
39
+ // git worktree add --detach /tmp/pre <commit>
40
+ // GOBLIN_PRE_COMMIT=<commit> node /tmp/pre/checks/<this-file>.mjs # must exit non-zero
41
+ //
42
+ // goblin-verify does exactly this for every harness when replay.commit is set (rule HS-02).
43
+ // Pin the pre-change commit, never HEAD: once the round is committed, HEAD IS the fixed tree.
44
+ const REPLAY = Boolean(process.env.GOBLIN_PRE_COMMIT);
45
+
46
+ // ---- probes ------------------------------------------------------------------
47
+ // Replace this block. Each assertion must be RED on the pre-change tree, or it is not a
48
+ // check - it is a description.
49
+ const self = readFileSync(new URL(import.meta.url), 'utf8');
50
+ assert('harness reads its own source with comments blanked',
51
+ blankComments(self).includes('function assert('),
52
+ `blanked ${blankComments(self).length} bytes`);
53
+
54
+ // ---- verdict -----------------------------------------------------------------
55
+ if (failures.length > 0) {
56
+ console.log(`${checked - failures.length}/${checked} passed${REPLAY ? ' (replay: this tree is pre-change)' : ''}`);
57
+ process.exit(1);
58
+ }
59
+ console.log(`${checked}/${checked} passed${REPLAY ? ' (replay: this tree is pre-change)' : ''}`);
60
+ process.exit(0);
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # checks/gate.sh — run the declared gate set and print ONE line of measured numbers.
3
+ # Convenience wrapper around the same declarations goblin-verify uses (GT-02/GT-05).
4
+ # Rename or replace it; it is a starting point, not a requirement.
5
+ set -uo pipefail
6
+ ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
7
+ # shellcheck source=/dev/null
8
+ . "$ROOT/.goblin/bin/goblin-lib.sh"
9
+ CONFIG="$ROOT/.goblin/goblin.yaml"
10
+
11
+ line=""
12
+ bad=0
13
+ while IFS=$'\t' read -r name cmd; do
14
+ [ -n "$name" ] || continue
15
+ ( cd "$ROOT" && bash -c "$cmd" ) >/dev/null 2>&1
16
+ rc=$?
17
+ line="$line $name=$rc"
18
+ [ "$rc" -eq 0 ] || bad=1
19
+ done < <(g_yaml_gates "$CONFIG")
20
+
21
+ rname=$(g_yaml_block_scalar "$CONFIG" ratchet name)
22
+ rcmd=$(g_yaml_block_scalar "$CONFIG" ratchet cmd)
23
+ if [ -n "$rname" ] && [ -n "$rcmd" ]; then
24
+ rval=$( cd "$ROOT" && bash -c "$rcmd" 2>/dev/null | tr -d '[:space:]' )
25
+ line="$line · $rname $rval (ceiling $(g_yaml_block_scalar "$CONFIG" ratchet ceiling))"
26
+ fi
27
+
28
+ printf '%s · measured %s\n' "${line# }" "$(date +%F)"
29
+ exit $bad
@@ -0,0 +1,46 @@
1
+ # .github/workflows/goblin-gate.yml — written once by goblin-stack for class {{CLASS}}.
2
+ #
3
+ # WHAT THIS IS. One job that runs the gate set this project DECLARES in `.goblin/goblin.yaml`.
4
+ # `.goblin/bin/goblin-verify` executes every declared gate (GT-02) and every rule that carries a
5
+ # command, so CI and a local verify cannot report two different truths about one SHA (PG-06).
6
+ # The job carries NO `if:` at any level, on purpose: GitHub reports a SKIPPED job as Success even
7
+ # when it is a required check, so a conditional here would be a green light with no bulb (PG-05).
8
+ #
9
+ # A WORKFLOW FILE IS NOT A GATE. Four things make it one, and all four are yours to do:
10
+ # 1. REQUIRED CHECK — mark the `gate` job required for this branch (Settings -> Branches, or a
11
+ # ruleset). Until then the job runs and nothing waits for it.
12
+ # 2. NO BYPASS — tick "Do not allow bypassing the above settings". Without it the restriction
13
+ # does not apply to admins, and an admin push skips the check entirely.
14
+ # 3. A PUSH IDENTITY — a required check binds nobody while the only committer is the repo's
15
+ # sole admin. Push from a non-admin account or token, or accept that the gate is advisory
16
+ # for your own pushes. This is a forge change, not a file change: that is why PG-04 is the
17
+ # row that records it, and why PG-04 is advisory rather than a gate.
18
+ # 4. NEVER CONDITIONAL — keep every job and step unconditional (PG-05 enforces it).
19
+ #
20
+ # WHAT IT CANNOT SEE. It runs on a runner you do not own, from a checkout of your repo. Any gate
21
+ # that needs a display, a licence, a GPU or a signed-in session is a HOST gate: declare it in the
22
+ # HANDOFF's measured numbers, do not try to run it here. On a private repo the minutes are billed
23
+ # to your account, so a long job is not free.
24
+ #
25
+ # For a zizmor-clean workflow, pin `uses:` to a commit SHA instead of a tag:
26
+ # gh api repos/actions/checkout/git/refs/tags/v4 --jq .object.sha
27
+ # (left as a tag here so the file is readable; nothing in goblin-stack reads a workflow's SHA).
28
+ name: goblin-gate
29
+ on:
30
+ push:
31
+ branches: [{{BRANCH}}]
32
+ pull_request:
33
+ permissions:
34
+ contents: read
35
+ concurrency:
36
+ group: goblin-gate-{{BRANCH}}
37
+ cancel-in-progress: true
38
+ jobs:
39
+ gate:
40
+ name: the declared gate set
41
+ runs-on: ubuntu-latest
42
+ timeout-minutes: 20
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - name: run the declared gate set
46
+ run: bash .goblin/bin/goblin-verify