@techgoblin/gobstack 0.0.0-stage → 0.4.4-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +351 -0
- package/LICENSE +21 -0
- package/README.md +217 -2
- package/VERSION +1 -0
- package/adapters/_template/adapter.tsv +16 -0
- package/adapters/_template/detect.sh +10 -0
- package/adapters/_template/emit.sh +5 -0
- package/adapters/_template/verify.sh +4 -0
- package/adapters/claude/adapter.tsv +8 -0
- package/adapters/claude/detect.sh +8 -0
- package/adapters/claude/verify.sh +47 -0
- package/adapters/codex/adapter.tsv +12 -0
- package/adapters/codex/detect.sh +9 -0
- package/adapters/codex/verify.sh +45 -0
- package/adapters/copilot/adapter.tsv +10 -0
- package/adapters/copilot/detect.sh +8 -0
- package/adapters/copilot/verify.sh +45 -0
- package/adapters/cursor/adapter.tsv +11 -0
- package/adapters/cursor/detect.sh +10 -0
- package/adapters/cursor/verify.sh +45 -0
- package/adapters/gemini/adapter.tsv +15 -0
- package/adapters/gemini/detect.sh +11 -0
- package/adapters/gemini/verify.sh +49 -0
- package/adapters/hermes/adapter.tsv +9 -0
- package/adapters/hermes/detect.sh +8 -0
- package/adapters/hermes/verify.sh +27 -0
- package/adapters/opencode/adapter.tsv +14 -0
- package/adapters/opencode/detect.sh +9 -0
- package/adapters/opencode/verify.sh +45 -0
- package/automations/README.md +53 -0
- package/automations/bugreporter-intake.sh +145 -0
- package/automations/drift-audit.sh +139 -0
- package/automations/report.schema.tsv +10 -0
- package/bans/README.md +82 -0
- package/bans/grep-ban.sh +84 -0
- package/bans/layer-check.sh +57 -0
- package/bin/goblin +119 -0
- package/bin/goblin-audit +145 -0
- package/bin/goblin-bans +178 -0
- package/bin/goblin-doctor +233 -0
- package/bin/goblin-emit +484 -0
- package/bin/goblin-init +519 -0
- package/bin/goblin-install +720 -0
- package/bin/goblin-lib.sh +289 -0
- package/bin/goblin-model +105 -0
- package/bin/goblin-upgrade +572 -0
- package/bin/goblin-verify +2798 -0
- package/bin/goblin.js +103 -0
- package/docs/ADOPTION.md +168 -0
- package/docs/CI.md +187 -0
- package/docs/CONTRACTS.md +197 -0
- package/docs/DESIGN.md +92 -0
- package/docs/ENFORCEMENT.md +225 -0
- package/docs/FLOWS.md +164 -0
- package/docs/GUARDRAILS.md +126 -0
- package/docs/GUIDE.md +610 -0
- package/docs/INTEGRATION.md +92 -0
- package/docs/LIMITS.md +591 -0
- package/docs/LOOP.md +165 -0
- package/docs/RE-PLAYBOOK.md +183 -0
- package/docs/RISKS.md +70 -0
- package/docs/ROLES.md +105 -0
- package/manifest/bans.tsv +9 -0
- package/manifest/classes.tsv +61 -0
- package/manifest/enforcement.tsv +88 -0
- package/manifest/glossary.tsv +25 -0
- package/manifest/playbooks.tsv +16 -0
- package/package.json +37 -4
- package/presets/A-shipped-software.yaml +48 -0
- package/presets/B-service-config.yaml +40 -0
- package/presets/C-game.yaml +38 -0
- package/presets/D-knowledge.yaml +41 -0
- package/presets/E-fleet-config.yaml +42 -0
- package/presets/F-electron.yaml +67 -0
- package/roles.yaml +54 -0
- package/skills/goblin-bootstrap/SKILL.md +51 -0
- package/skills/goblin-bugfix/SKILL.md +26 -0
- package/skills/goblin-bugreporter/SKILL.md +52 -0
- package/skills/goblin-drift-audit/SKILL.md +43 -0
- package/skills/goblin-eval/SKILL.md +68 -0
- package/skills/goblin-feature/SKILL.md +26 -0
- package/skills/goblin-feature-map/SKILL.md +140 -0
- package/skills/goblin-handoff/SKILL.md +28 -0
- package/skills/goblin-investigation/SKILL.md +26 -0
- package/skills/goblin-judge/SKILL.md +74 -0
- package/skills/goblin-loop/SKILL.md +88 -0
- package/skills/goblin-mode/SKILL.md +70 -0
- package/skills/goblin-overnight/SKILL.md +42 -0
- package/skills/goblin-pr-gate/SKILL.md +42 -0
- package/skills/goblin-re-mobile/SKILL.md +51 -0
- package/skills/goblin-refactor/SKILL.md +23 -0
- package/skills/goblin-sweep/SKILL.md +23 -0
- package/skills/goblin-tdd-repro/SKILL.md +27 -0
- package/skills/goblin-verify-author/SKILL.md +50 -0
- package/skills/practice/SKILL.md +37 -0
- package/templates/AGENTS.md.tmpl +23 -0
- package/templates/HANDOFF.md.tmpl +43 -0
- package/templates/SPEC.md.tmpl +34 -0
- package/templates/audit-waiver.tsv.tmpl +10 -0
- package/templates/boundary-waivers.tmpl +8 -0
- package/templates/checks/assert.mjs.tmpl +60 -0
- package/templates/checks/gate.sh.tmpl +29 -0
- package/templates/ci/goblin-gate.yml.tmpl +46 -0
- package/templates/goblin.yaml.tmpl +138 -0
- package/templates/install-hooks.allowlist.tmpl +9 -0
- package/templates/loop/decisions.tsv.tmpl +1 -0
- package/templates/loop/predicate.tmpl +16 -0
- package/templates/report.yaml.tmpl +16 -0
|
@@ -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
|