@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.
- package/CHANGELOG.md +54 -0
- package/README.md +161 -124
- package/VERSION +1 -1
- package/automations/drift-audit.sh +4 -4
- package/bans/layer-check.sh +10 -8
- package/bin/goblin +69 -57
- package/bin/goblin-audit +11 -13
- package/bin/goblin-bans +11 -11
- package/bin/goblin-extras +342 -0
- package/bin/goblin-init +382 -711
- package/bin/goblin-install +160 -114
- package/bin/goblin-lib.sh +234 -1
- package/bin/goblin-map +226 -21
- package/bin/goblin-mcp.js +492 -0
- package/bin/goblin-model +4 -4
- package/bin/goblin-upgrade +1 -1
- package/bin/goblin-verify +159 -145
- package/bin/goblin.js +35 -51
- package/docs/ADOPTION.md +15 -15
- package/docs/CONTRACTS.md +16 -15
- package/docs/DESIGN.md +1 -1
- package/docs/ENFORCEMENT.md +89 -90
- package/docs/FLOWS.md +1 -1
- package/docs/GLOSSARY.md +3 -3
- package/docs/GUARDRAILS.md +5 -5
- package/docs/GUIDE.md +194 -177
- package/docs/INTEGRATION.md +1 -1
- package/docs/LIMITS.md +25 -0
- package/docs/LOOP.md +12 -12
- package/docs/RE-PLAYBOOK.md +3 -3
- package/docs/ROLES.md +5 -5
- package/extras-catalogue/catalogue.tsv +42 -0
- package/extras-catalogue/payload/README.md +14 -0
- package/extras-catalogue/payload/taste-skill/taste/REFERENCE.md +3 -0
- package/extras-catalogue/payload/taste-skill/taste/SKILL.md +9 -0
- package/manifest/bans.tsv +8 -8
- package/manifest/classes.tsv +3 -3
- package/manifest/enforcement.tsv +40 -40
- package/manifest/glossary.tsv +3 -3
- package/manifest/playbooks.tsv +1 -1
- package/package.json +3 -1
- package/presets/electron-overlay.yaml +2 -2
- package/presets/fleet.yaml +8 -7
- package/presets/game.yaml +1 -1
- package/presets/research.yaml +1 -1
- package/presets/service.yaml +1 -1
- package/presets/software.yaml +1 -1
- package/skills/goblin-bootstrap/SKILL.md +2 -2
- package/templates/AGENTS.md.tmpl +8 -18
- package/templates/HANDOFF.md.tmpl +5 -5
- package/templates/agents-block.tmpl +45 -0
- package/templates/audit-waiver.tsv.tmpl +2 -2
- package/templates/boundary-waivers.tmpl +1 -1
- package/templates/checks/gate.sh.tmpl +6 -6
- package/templates/install-hooks.allowlist.tmpl +1 -1
- package/templates/ci/goblin-gate.yml.tmpl +0 -46
- package/templates/goblin.yaml.tmpl +0 -146
- package/templates/loop/decisions.tsv.tmpl +0 -1
- package/templates/loop/predicate.tmpl +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techgoblin/gobstack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-alpha.2",
|
|
4
4
|
"description": "Agent-discipline toolkit: one verify command, an enforcement matrix, and LIMITS. bash engine, npm shim.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"goblin": "bin/goblin.js",
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"automations/",
|
|
24
24
|
"bans/",
|
|
25
25
|
"docs/",
|
|
26
|
+
"extras-catalogue/catalogue.tsv",
|
|
27
|
+
"extras-catalogue/payload/",
|
|
26
28
|
"roles.yaml",
|
|
27
29
|
"VERSION",
|
|
28
30
|
"CHANGELOG.md"
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
#
|
|
3
3
|
# The `desktop` class is gone; F was merged into software because their classes.tsv need
|
|
4
4
|
# columns are IDENTICAL on all 10 parts (measured). Everything F added beyond software lives in
|
|
5
|
-
# plain
|
|
5
|
+
# plain frontmatter config keys, so it is an OVERLAY, not a class: `gob install --class software
|
|
6
6
|
# --electron` (or the `--class desktop` / `F` alias, which sets --electron) renders the keys
|
|
7
7
|
# below OVER the software preset. An absent key falls through to presets/software.yaml; a key
|
|
8
8
|
# present here — including an empty one like sec_write_routes: "" — wins.
|
|
9
9
|
#
|
|
10
10
|
# The prose F carried (label / done_means / notes) is preserved here for the record; it is not a
|
|
11
|
-
# rendered template key, so it never reaches a target's
|
|
11
|
+
# rendered template key, so it never reaches a target's gob block.
|
|
12
12
|
label: Desktop shell
|
|
13
13
|
done_means: the renderer is isolated from Node, the main process is not busy, and the packaged bundle ships no dev dependency
|
|
14
14
|
# software's A-only second gate (the TODO ceiling) is NOT applied: F never had it.
|
package/presets/fleet.yaml
CHANGED
|
@@ -9,16 +9,17 @@ scaffold_checks: no
|
|
|
9
9
|
gate_name: commit
|
|
10
10
|
gate_cmd: git rev-parse --verify --quiet HEAD
|
|
11
11
|
ratchet_name: todo_markers
|
|
12
|
-
# The harness's own vendored directories are excluded
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# `*.yaml`/`*.yml`/`*.sh` at the root and
|
|
17
|
-
|
|
12
|
+
# The harness's own vendored directories are excluded, and so is root AGENTS.md: in v2 the
|
|
13
|
+
# config IS the AGENTS.md gob block, so the ratchet command line itself (the literal
|
|
14
|
+
# TODO|FIXME inside `ratchet_cmd:`) lives in a *.md file at the root — without the exclude a
|
|
15
|
+
# fresh install counts its own gate and is born RED against a ceiling measured before the
|
|
16
|
+
# file was written (D8). The fleet's real config — `*.yaml`/`*.yml`/`*.sh` at the root and
|
|
17
|
+
# under `profiles/` — is still counted.
|
|
18
|
+
ratchet_cmd: grep -rniE '\b(TODO|FIXME)\b' --include='*.sh' --include='*.yaml' --include='*.yml' --include='*.md' --exclude-dir=.gob --exclude-dir=.hermes --exclude=AGENTS.md . 2>/dev/null | wc -l
|
|
18
19
|
ratchet_ceiling: measure
|
|
19
20
|
replay_env: GOBLIN_PRE_COMMIT
|
|
20
21
|
replay_cmd: node checks/{name}.mjs
|
|
21
|
-
runtime_data: .
|
|
22
|
+
runtime_data: .gob/state.json
|
|
22
23
|
# A fleet config has no client surface; the secret family still matters (a profile file is one
|
|
23
24
|
# `git add` away from a live token), so SC-01/SC-02 are the rows that bite here.
|
|
24
25
|
sec_gitignore_family: yes
|
package/presets/game.yaml
CHANGED
|
@@ -14,7 +14,7 @@ ratchet_cmd: grep -rniE '\b(TODO|FIXME)\b' --include='*.cs' --include='*.ts' --i
|
|
|
14
14
|
ratchet_ceiling: measure
|
|
15
15
|
replay_env: GOBLIN_PRE_COMMIT
|
|
16
16
|
replay_cmd: node checks/{name}.mjs
|
|
17
|
-
runtime_data: .
|
|
17
|
+
runtime_data: .gob/state.json
|
|
18
18
|
sec_gitignore_family: yes
|
|
19
19
|
sec_build_output: Builds Library
|
|
20
20
|
sec_audit_cmd: ""
|
package/presets/research.yaml
CHANGED
|
@@ -15,7 +15,7 @@ ratchet_cmd: ""
|
|
|
15
15
|
ratchet_ceiling: 0
|
|
16
16
|
replay_env: GOBLIN_PRE_COMMIT
|
|
17
17
|
replay_cmd: node checks/{name}.mjs
|
|
18
|
-
runtime_data: .
|
|
18
|
+
runtime_data: .gob/state.json
|
|
19
19
|
# A knowledge repo has no client surface and no dependency manifest, so SC-03's build-output
|
|
20
20
|
# clause, SC-05..SC-08 skip with a reason. The SECRET FAMILY is not one of those: a note file is
|
|
21
21
|
# one `git add` away from a pasted token, so SC-02 is required here too and the installer writes
|
package/presets/service.yaml
CHANGED
|
@@ -14,7 +14,7 @@ ratchet_cmd: grep -rniE '\b(TODO|FIXME)\b' --include='*.ts' --include='*.tsx' --
|
|
|
14
14
|
ratchet_ceiling: measure
|
|
15
15
|
replay_env: GOBLIN_PRE_COMMIT
|
|
16
16
|
replay_cmd: node checks/{name}.mjs
|
|
17
|
-
runtime_data: .
|
|
17
|
+
runtime_data: .gob/state.json
|
|
18
18
|
# A service has real inputs and real dependencies, so the security rows bite here.
|
|
19
19
|
sec_gitignore_family: yes
|
|
20
20
|
sec_build_output: dist
|
package/presets/software.yaml
CHANGED
|
@@ -24,7 +24,7 @@ ratchet_cmd: find .next/static dist/assets dist -type f -name '*.js' -exec cat {
|
|
|
24
24
|
ratchet_ceiling: measure
|
|
25
25
|
replay_env: GOBLIN_PRE_COMMIT
|
|
26
26
|
replay_cmd: node checks/{name}.mjs
|
|
27
|
-
runtime_data: .
|
|
27
|
+
runtime_data: .gob/state.json
|
|
28
28
|
# SC-02 requires the whole secret family to be ignored; SC-03 clause 2 scans `sec_build_output`
|
|
29
29
|
# for secret-shaped literals; SC-07 runs `sec_audit_cmd` once, deliberately, via goblin-audit.
|
|
30
30
|
sec_gitignore_family: yes
|
|
@@ -16,10 +16,10 @@ Use when adopting goblin-stack in a repo, or starting one.
|
|
|
16
16
|
no agent skills. Opt in per platform afterwards with `gob emit --platform <p>` (or vendor the
|
|
17
17
|
Hermes project tier with `--skills yes`).
|
|
18
18
|
3. **`goblin-verify`** — a default software-class install (no agent skills) verifies green:
|
|
19
|
-
`
|
|
19
|
+
`37 passed, 0 failed, 11 advisory, 34 skipped`, exit 0, once `HANDOFF.md` names a commit that
|
|
20
20
|
exists; before that edit the
|
|
21
21
|
scaffold's `0000000` placeholder is `HP-05`'s one expected day-one red (`37 passed, 1 failed`).
|
|
22
|
-
Thirty-
|
|
22
|
+
Thirty-four rows skip with a reason, and the reason matters: the five skill rows
|
|
23
23
|
(`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks` opt-out a skills-free install records,
|
|
24
24
|
then `HS-02`
|
|
25
25
|
(no pinned pre-change commit yet, so the REPLAY is not provable), `AU-02`/`AU-03` (no report
|
package/templates/AGENTS.md.tmpl
CHANGED
|
@@ -3,31 +3,21 @@
|
|
|
3
3
|
This repository uses **gobstack** (class `{{CLASS}}`, installed {{DATE}}). It is a pointer,
|
|
4
4
|
not a rule dump — the rules live in one executable place, and facts beat requirements.
|
|
5
5
|
|
|
6
|
-
- **Harness entry point:** `.
|
|
6
|
+
- **Harness entry point:** `.gob/bin/goblin-verify` — exit 0 pass, 1 a check failed,
|
|
7
7
|
2 could not run, 3 the manifest is broken. Run it before you commit; the gate is the
|
|
8
8
|
same run.
|
|
9
|
-
- **Rules and their checks:** `.
|
|
9
|
+
- **Rules and their checks:** `.gob/manifest/enforcement.tsv` (one row per rule; every row
|
|
10
10
|
carries a runnable check or the literal `advisory`).
|
|
11
|
-
- **Forbidden code (the ban list):** `.
|
|
12
|
-
|
|
11
|
+
- **Forbidden code (the ban list):** `.gob/bin/goblin-bans` runs the bans named by `bans:` in
|
|
12
|
+
this file's gob block; each ban's mechanism lives in `.gob/manifest/bans.tsv`. Run it before
|
|
13
13
|
you write the line, not after — a ban is a gate, and `goblin-verify` only reports it once the
|
|
14
14
|
code exists.
|
|
15
15
|
- **Session state:** `HANDOFF.md` at the repo root. Read it before touching anything.
|
|
16
|
-
- **Project config:**
|
|
17
|
-
replay, opt-outs). Edit it in place; the
|
|
18
|
-
|
|
16
|
+
- **Project config:** the `<!-- gob:begin --> … <!-- gob:end -->` block in this file
|
|
17
|
+
(class, branch, gates, ratchet, runtime data, replay, opt-outs). Edit it in place; the
|
|
18
|
+
installer never rewrites the block after the first install and never touches the prose.
|
|
19
|
+
- **The referenced standard**, if configured, is named by `practice:` in the gob block.
|
|
19
20
|
Read it before starting work; this repo carries no copy of it.
|
|
20
21
|
|
|
21
|
-
**Agent skills are opt-in, per platform.** A default install ships none — this is a neutral
|
|
22
|
-
harness on purpose, so nothing here assumes which coding agent you use. To install the
|
|
23
|
-
procedure skills for your tool:
|
|
24
|
-
|
|
25
|
-
gob sync --platform <p>
|
|
26
|
-
|
|
27
|
-
(`--scope project` writes them inside this repo; `--scope global` writes them for your user.)
|
|
28
|
-
Run `gob sync` with no platform to see the list (`gob emit` is the same command under its
|
|
29
|
-
original name). If skills were installed here, they live
|
|
30
|
-
under the platform's own directory, and `gob verify` hashes them.
|
|
31
|
-
|
|
32
22
|
Two things this repo does not do: it does not choose models (a role resolves through the
|
|
33
23
|
mapping file named by `models_file:`), and it does not write outside its own tree.
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
## ▶ START HERE (new session)
|
|
8
8
|
|
|
9
9
|
- Working dir: the repo root.
|
|
10
|
-
- Gate set: `{{GATE_NAME}}` — `{{GATE_CMD}}`, plus whatever you add in
|
|
11
|
-
- Run the gates and the manifest checks: `.
|
|
12
|
-
- One line of measured numbers for the round: `.
|
|
13
|
-
- Read before working: the referenced standard, if
|
|
10
|
+
- Gate set: `{{GATE_NAME}}` — `{{GATE_CMD}}`, plus whatever you add in `AGENTS.md`.
|
|
11
|
+
- Run the gates and the manifest checks: `.gob/bin/goblin-verify`
|
|
12
|
+
- One line of measured numbers for the round: `.gob/bin/goblin-verify --only GT-02` writes `.gob/last-gate-line`.
|
|
13
|
+
- Read before working: the referenced standard, if `AGENTS.md` declares `practice:`.
|
|
14
14
|
|
|
15
15
|
## State
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
Every number here is a measurement with a date, never a copy from a previous round.
|
|
26
26
|
|
|
27
27
|
- `{{GATE_NAME}}` = 0 · measured {{DATE}}
|
|
28
|
-
|
|
28
|
+
{{GATE2_LINE}}
|
|
29
29
|
- Example of the required form: `typecheck=0 · build=0 · ratchet <n> (ceiling <c>) · <n>/<n> harnesses green · measured {{DATE}}`
|
|
30
30
|
|
|
31
31
|
## Next steps
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!-- gob:begin (gobstack config — edit in place; the parser reads only this block) -->
|
|
2
|
+
class: {{CLASS}}
|
|
3
|
+
electron: {{ELECTRON}}
|
|
4
|
+
branch: {{BRANCH}}
|
|
5
|
+
models_file: {{MODELS_FILE}}
|
|
6
|
+
practice: {{PRACTICE}}
|
|
7
|
+
practice_sha256: {{PRACTICE_SHA}}
|
|
8
|
+
owner_email: {{OWNER_EMAIL}}
|
|
9
|
+
archive: {{ARCHIVE}}
|
|
10
|
+
max_dirty: {{MAX_DIRTY}}
|
|
11
|
+
advisory_ceiling: {{ADVISORY_CEILING}}
|
|
12
|
+
security.gitignore_family: {{SEC_GITIGNORE_FAMILY}}
|
|
13
|
+
security.build_output: {{SEC_BUILD_OUTPUT}}
|
|
14
|
+
security.audit_cmd: {{SEC_AUDIT_CMD}}
|
|
15
|
+
security.audit_max_age_days: {{SEC_AUDIT_MAX_AGE}}
|
|
16
|
+
security.waiver_max_age_days: {{SEC_WAIVER_MAX_AGE}}
|
|
17
|
+
security.write_routes: {{SEC_WRITE_ROUTES}}
|
|
18
|
+
perf.metric: {{PERF_METRIC}}
|
|
19
|
+
perf.cmd: {{PERF_CMD}}
|
|
20
|
+
perf.baseline_commit: {{PERF_BASELINE_COMMIT}}
|
|
21
|
+
perf.baseline_value: {{PERF_BASELINE_VALUE}}
|
|
22
|
+
perf.measured: {{PERF_MEASURED}}
|
|
23
|
+
perf.host_gate: {{PERF_HOST_GATE}}
|
|
24
|
+
harness_dir: {{HARNESS_DIR}}
|
|
25
|
+
scaffold_checks: {{SCAFFOLD_CHECKS}}
|
|
26
|
+
feature_map: {{FEATURE_MAP}}
|
|
27
|
+
source_root: {{SOURCE_ROOT}}
|
|
28
|
+
verify_doctor: {{VERIFY_DOCTOR}}
|
|
29
|
+
reference_manifest: {{REFERENCE_MANIFEST}}
|
|
30
|
+
quarantine_root: {{QUARANTINE_ROOT}}
|
|
31
|
+
loop_max_turns_ceiling: {{LOOP_MAX_TURNS}}
|
|
32
|
+
gate_{{GATE1_NAME}}_cmd: {{GATE1_CMD}}
|
|
33
|
+
gate_{{GATE2_NAME}}_cmd: {{GATE2_CMD}}
|
|
34
|
+
ratchet.name: {{RATCHET_NAME}}
|
|
35
|
+
ratchet.cmd: {{RATCHET_CMD}}
|
|
36
|
+
ratchet.ceiling: {{RATCHET_CEILING}}
|
|
37
|
+
runtime_data: [{{RUNTIME_DATA}}]
|
|
38
|
+
bans: [{{BANS}}]
|
|
39
|
+
bans_exempt:
|
|
40
|
+
layers:
|
|
41
|
+
replay.env: {{REPLAY_ENV}}
|
|
42
|
+
replay.cmd: {{REPLAY_CMD}}
|
|
43
|
+
replay.commit: ""
|
|
44
|
+
disabled: [{{DISABLED}}]
|
|
45
|
+
<!-- gob:end -->
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# .
|
|
1
|
+
# .gob/audit-waiver.tsv — one line per accepted dependency advisory (SC-07).
|
|
2
2
|
#
|
|
3
3
|
# package severity id date reason
|
|
4
4
|
#
|
|
5
5
|
# The date is the day the decision was taken, and SC-07 refuses a waiver older than
|
|
6
6
|
# security.waiver_max_age_days: a waiver with no expiry is a permanent blind spot. A
|
|
7
|
-
# high|critical line in .
|
|
7
|
+
# high|critical line in .gob/audit.tsv with no matching row here is a RED.
|
|
8
8
|
#
|
|
9
9
|
# example:
|
|
10
10
|
# next high GHSA-xxxx-yyyy-zzzz 2026-09-24 no fixed release yet; the vulnerable path is the dev server only
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# .
|
|
1
|
+
# .gob/boundary-waivers — write routes that deliberately do NOT validate input (SC-05).
|
|
2
2
|
#
|
|
3
3
|
# One repo-relative path per line, with the reason after a '#'. A waiver is a DECISION recorded
|
|
4
4
|
# where the row can read it; it is not a silence. SC-05 prints the waiver count on its gate line
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
set -uo pipefail
|
|
6
6
|
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
7
7
|
# shellcheck source=/dev/null
|
|
8
|
-
. "$ROOT/.
|
|
9
|
-
CONFIG="$ROOT
|
|
8
|
+
. "$ROOT/.gob/bin/goblin-lib.sh"
|
|
9
|
+
CONFIG="$ROOT/AGENTS.md"
|
|
10
10
|
|
|
11
11
|
line=""
|
|
12
12
|
bad=0
|
|
@@ -16,13 +16,13 @@ while IFS=$'\t' read -r name cmd; do
|
|
|
16
16
|
rc=$?
|
|
17
17
|
line="$line $name=$rc"
|
|
18
18
|
[ "$rc" -eq 0 ] || bad=1
|
|
19
|
-
done < <(
|
|
19
|
+
done < <(g_agents_gates "$CONFIG")
|
|
20
20
|
|
|
21
|
-
rname=$(
|
|
22
|
-
rcmd=$(
|
|
21
|
+
rname=$(g_agents_read "$CONFIG" ratchet.name)
|
|
22
|
+
rcmd=$(g_agents_read "$CONFIG" ratchet.cmd)
|
|
23
23
|
if [ -n "$rname" ] && [ -n "$rcmd" ]; then
|
|
24
24
|
rval=$( cd "$ROOT" && bash -c "$rcmd" 2>/dev/null | tr -d '[:space:]' )
|
|
25
|
-
line="$line · $rname $rval (ceiling $(
|
|
25
|
+
line="$line · $rname $rval (ceiling $(g_agents_read "$CONFIG" ratchet.ceiling))"
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
28
|
printf '%s · measured %s\n' "${line# }" "$(date +%F)"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# .
|
|
1
|
+
# .gob/install-hooks.allowlist — dependencies whose install-time script is accepted (SC-08).
|
|
2
2
|
#
|
|
3
3
|
# One package name per line. SC-08 enumerates package-lock.json for "hasInstallScript": true and
|
|
4
4
|
# requires every one of them to appear here. An install hook is arbitrary code that runs on every
|
|
@@ -1,46 +0,0 @@
|
|
|
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
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
# .goblin/goblin.yaml — the project config. Generated once by goblin-install, then YOURS.
|
|
2
|
-
# It is never overwritten after the first install: edit it in place.
|
|
3
|
-
#
|
|
4
|
-
# Format: a flat, line-oriented YAML subset, parsed by .goblin/bin/goblin-lib.sh. No YAML
|
|
5
|
-
# library, no network, no npm. Keep one key per line and keep comments on their own line.
|
|
6
|
-
|
|
7
|
-
# Which class this project is: software | service | game | research | fleet.
|
|
8
|
-
# The letters A-E and the older names app/agent/desktop are READ-TIME aliases, so an old
|
|
9
|
-
# value keeps verifying; `desktop`/`F` resolve to software. Selects the required parts
|
|
10
|
-
# (manifest/classes.tsv).
|
|
11
|
-
class: {{CLASS}}
|
|
12
|
-
|
|
13
|
-
# Electron opt-in (software class). true turns on the electron ban set (BN-06..09) even if the
|
|
14
|
-
# bans: list below omits them, and requires a declared perf.host_gate (PF-01 fails without one).
|
|
15
|
-
# The `desktop`/`F` install alias sets this true; a text-editor app leaves it false.
|
|
16
|
-
electron: {{ELECTRON}}
|
|
17
|
-
|
|
18
|
-
# The default branch, DECLARED, never assumed. A preset that assumes the wrong branch
|
|
19
|
-
# silently skips a repo.
|
|
20
|
-
branch: {{BRANCH}}
|
|
21
|
-
|
|
22
|
-
# The model mapping — the ONE install-time machine input. goblin-stack READS this file and
|
|
23
|
-
# never writes it. Absent on another machine: every model-dependent check reports advisory.
|
|
24
|
-
models_file: {{MODELS_FILE}}
|
|
25
|
-
|
|
26
|
-
# The global engine (optional, W1). Where the ENGINE tier lives on this machine — an
|
|
27
|
-
# absolute path or ~/-prefixed. Absent by default: a fresh per-repo install is
|
|
28
|
-
# byte-compatible with today, and verify resolves its engine exactly as it always did.
|
|
29
|
-
# Declared but unusable (relative path, missing directory, no manifest inside) is
|
|
30
|
-
# verify exit 2 with NO fallback — a repo must not be judged by an engine it did not
|
|
31
|
-
# declare. Authored by hand (or by `goblin upgrade`, W3); goblin-install never writes it.
|
|
32
|
-
# engine_dir: ~/.goblin/engine
|
|
33
|
-
|
|
34
|
-
# The referenced standard (optional). goblin-stack carries no copy of it; it pins its hash.
|
|
35
|
-
practice: {{PRACTICE}}
|
|
36
|
-
practice_sha256: {{PRACTICE_SHA}}
|
|
37
|
-
|
|
38
|
-
# The commit identity this repo expects (CM-01).
|
|
39
|
-
owner_email: {{OWNER_EMAIL}}
|
|
40
|
-
|
|
41
|
-
# archive: true -> verify requires no HANDOFF and no gates, and says so.
|
|
42
|
-
archive: {{ARCHIVE}}
|
|
43
|
-
|
|
44
|
-
# Uncommitted entries allowed at verify time (CM-03). 0 means commit as you go.
|
|
45
|
-
max_dirty: {{MAX_DIRTY}}
|
|
46
|
-
|
|
47
|
-
# Unenforceable rules are counted; this is the cap that stops the matrix decaying into prose.
|
|
48
|
-
advisory_ceiling: {{ADVISORY_CEILING}}
|
|
49
|
-
|
|
50
|
-
# Whether this class requires a client-visible surface to be secret-free (SC-01..SC-09 read the
|
|
51
|
-
# `security:` block below). Declared, never inferred: a stack-specific rule guessed from the
|
|
52
|
-
# files on disk is how a matrix starts lying.
|
|
53
|
-
security:
|
|
54
|
-
gitignore_family: {{SEC_GITIGNORE_FAMILY}}
|
|
55
|
-
build_output: {{SEC_BUILD_OUTPUT}}
|
|
56
|
-
audit_cmd: {{SEC_AUDIT_CMD}}
|
|
57
|
-
audit_max_age_days: {{SEC_AUDIT_MAX_AGE}}
|
|
58
|
-
waiver_max_age_days: {{SEC_WAIVER_MAX_AGE}}
|
|
59
|
-
write_routes: {{SEC_WRITE_ROUTES}}
|
|
60
|
-
|
|
61
|
-
# The performance budget (PF-01). The NUMBER lives in `ratchet:` above — this block only names
|
|
62
|
-
# the commit it was measured on, so "I raised the ceiling" cannot silently become "I never
|
|
63
|
-
# measured again". An empty baseline_commit means "not measured yet" and PF-01 skips with that
|
|
64
|
-
# reason; a baseline_commit that does not resolve to an ancestor of HEAD is a RED.
|
|
65
|
-
perf:
|
|
66
|
-
metric: {{PERF_METRIC}}
|
|
67
|
-
cmd: {{PERF_CMD}}
|
|
68
|
-
baseline_commit: {{PERF_BASELINE_COMMIT}}
|
|
69
|
-
baseline_value: {{PERF_BASELINE_VALUE}}
|
|
70
|
-
measured: {{PERF_MEASURED}}
|
|
71
|
-
host_gate: {{PERF_HOST_GATE}}
|
|
72
|
-
|
|
73
|
-
# Where the asserting harnesses live.
|
|
74
|
-
harness_dir: {{HARNESS_DIR}}
|
|
75
|
-
|
|
76
|
-
# Whether this class scaffolds that directory. A declared harness_dir that is absent FAILS
|
|
77
|
-
# (HS-01) when this is yes; a class that ships no harness dir skips with a reason.
|
|
78
|
-
scaffold_checks: {{SCAFFOLD_CHECKS}}
|
|
79
|
-
|
|
80
|
-
# The feature map (P6's inventory, authored by skills/goblin-feature-map).
|
|
81
|
-
# feature_map: the map's README, relative to this repo. EMPTY means "no map declared yet" and
|
|
82
|
-
# makes FM-01/FM-02 SKIP with that reason - a fresh install has no map and must not be born RED.
|
|
83
|
-
# source_root: entry paths in the map resolve against this.
|
|
84
|
-
# verify_doctor: the command the map's own instructions say to run. EMPTY -> VA-01 SKIPs.
|
|
85
|
-
feature_map: {{FEATURE_MAP}}
|
|
86
|
-
source_root: {{SOURCE_ROOT}}
|
|
87
|
-
verify_doctor: {{VERIFY_DOCTOR}}
|
|
88
|
-
|
|
89
|
-
# The reference corpus (P15). `reference_manifest:` names the manifest RC-01/RC-02/RC-04 read: a
|
|
90
|
-
# hash-only record of the quarantined corpus, never the corpus itself. EMPTY means "no reference
|
|
91
|
-
# corpus is declared" and makes RC-01, RC-02 and RC-04 SKIP with that reason - a repo with no
|
|
92
|
-
# corpus must not be born RED. A key that IS set but names a missing or unparseable file is a
|
|
93
|
-
# FAIL, never a skip, so losing the manifest cannot read as "no corpus". RC-01 also reads the
|
|
94
|
-
# `security: build_output:` list above: a file there whose sha256 appears in an entry fails the
|
|
95
|
-
# build.
|
|
96
|
-
# `quarantine_root:` is the quarantine directory the procedure extracts into (outside this repo).
|
|
97
|
-
# No row reads it; it is declared so the procedure has a machine-readable anchor instead of a path
|
|
98
|
-
# in prose.
|
|
99
|
-
reference_manifest: {{REFERENCE_MANIFEST}}
|
|
100
|
-
quarantine_root: {{QUARANTINE_ROOT}}
|
|
101
|
-
|
|
102
|
-
# The loop ceiling (G2, LP-03). The most turns ONE loop record may declare in its budget, so a
|
|
103
|
-
# runaway loop cannot buy itself unlimited iterations. 20 is the engine's own default for a
|
|
104
|
-
# kanban goal loop, measured; a ceiling that does not match the engine's default is a number
|
|
105
|
-
# someone made up. It bounds one loop's turns, never how many loops exist.
|
|
106
|
-
loop_max_turns_ceiling: {{LOOP_MAX_TURNS}}
|
|
107
|
-
|
|
108
|
-
# The gate set. DECLARED, never inferred from the stack. Replace the default below with the
|
|
109
|
-
# real commands for this project; each must exit 0. The whole block is rendered by one
|
|
110
|
-
# placeholder (a class may declare a second gate - A's TODO ceiling), so a second, empty
|
|
111
|
-
# placeholder line below it leaked an unsubstituted token into every class's installed config
|
|
112
|
-
# and nothing rendered it (Z1-3).
|
|
113
|
-
gates:
|
|
114
|
-
{{GATES}}
|
|
115
|
-
|
|
116
|
-
# A count that must not rise, with its ceiling. The ceiling was measured at install time.
|
|
117
|
-
ratchet:
|
|
118
|
-
name: {{RATCHET_NAME}}
|
|
119
|
-
cmd: {{RATCHET_CMD}}
|
|
120
|
-
ceiling: {{RATCHET_CEILING}}
|
|
121
|
-
|
|
122
|
-
# Paths a probe must never write (DS-01/DS-02 hash them around every gate run).
|
|
123
|
-
runtime_data:
|
|
124
|
-
{{RUNTIME_DATA}}
|
|
125
|
-
|
|
126
|
-
# The ban list (G5). Which bans this project turns on; an unlisted ban SKIPs with a reason.
|
|
127
|
-
# A ban's mechanism lives in .goblin/manifest/bans.tsv; the engine is .goblin/bin/goblin-bans.
|
|
128
|
-
bans: [{{BANS}}]
|
|
129
|
-
|
|
130
|
-
# Narrow, explicit, reviewed exceptions (Dune rule 5): "- <ban id> <path prefix>".
|
|
131
|
-
bans_exempt:
|
|
132
|
-
{{BANS_EXEMPT}}
|
|
133
|
-
|
|
134
|
-
# What BN-05 reads: "<from> <to>" layer pairs. Empty -> BN-05 SKIPs with a reason.
|
|
135
|
-
layers:
|
|
136
|
-
{{LAYERS}}
|
|
137
|
-
|
|
138
|
-
# How a harness proves RED on the pre-change tree (HS-02). Set commit: to the pinned
|
|
139
|
-
# pre-change SHA once the round it guards has landed; until then HS-02 is skipped with a reason.
|
|
140
|
-
replay:
|
|
141
|
-
env: {{REPLAY_ENV}}
|
|
142
|
-
cmd: {{REPLAY_CMD}}
|
|
143
|
-
commit: ""
|
|
144
|
-
|
|
145
|
-
# Parts recorded as opt-out. Their required checks report SKIP (opt-out) rather than passing.
|
|
146
|
-
disabled: [{{DISABLED}}]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ts phase decision why evidence result
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# .goblin/loop/predicate — the loop's exit condition. Copy this file to that path and edit it.
|
|
2
|
-
#
|
|
3
|
-
# ONE command. Exits 0 == the loop is finished. Comments are allowed here; the command is the
|
|
4
|
-
# single non-blank, non-comment line (LP-01 checks exactly that).
|
|
5
|
-
#
|
|
6
|
-
# A duration is NOT a finish condition, and neither is a description of one.
|
|
7
|
-
# Run it once BEFORE iteration 1 and record the result in .goblin/loop/first-run as
|
|
8
|
-
# "exit=<n> ts=<ISO8601>": the run-once rule is how you know the command is runnable and
|
|
9
|
-
# currently red, which is what makes a later green mean anything.
|
|
10
|
-
# Then pin it: sha256sum .goblin/loop/predicate | cut -d' ' -f1 > .goblin/loop/predicate.sha256
|
|
11
|
-
# Never edit the predicate mid-loop — archive it under .goblin/loop/closed-<date>/ together with
|
|
12
|
-
# the pin it was closed under, then open a new loop whose predicate.sha256 names the archived
|
|
13
|
-
# digest on a `previous: <digest>` line (LP-02: a silent relaxation FAILs; the chain is the record).
|
|
14
|
-
#
|
|
15
|
-
# Replace the line below with the real condition. Example shape only:
|
|
16
|
-
node tools/check-cycles.mjs
|