@techgoblin/gobstack 0.4.4-beta.7 → 0.5.0-beta.1

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/docs/LIMITS.md CHANGED
@@ -111,7 +111,7 @@ deliberate trade or an unfilled gap.
111
111
  drift check — `IN-02`, `SK-02`, and `HS-01`'s hash of the harness dir — reads its expected
112
112
  hash out of that one file, and that file is the one file no check protects. Measured: append a
113
113
  byte to `.goblin/bin/goblin-verify`, rewrite its recorded hash in `installed.json`, commit, and
114
- the run is **fully GREEN** (`43 passed, 0 failed`, exit 0). One edit defeats three rows at
114
+ the run is **fully GREEN** (`38 passed, 0 failed`, exit 0). One edit defeats three rows at
115
115
  once, and it is the cheapest way to fake a green run. Doing better needs an anchor the target
116
116
  cannot edit — a signature, or a hash held outside the repo — and goblin-stack has no such
117
117
  trust root: the source checkout is not guaranteed to exist at verify time, and any value
@@ -275,7 +275,8 @@ Electron perf number is a host gate, and the ratchet deliberately carries a diff
275
275
  flow-style `jobs: {…}` mapping is refused, and a `#` inside a quoted string truncates the line
276
276
  it is on. Two further measured gaps: the template's job is `ubuntu-latest` with no cache, so a
277
277
  repo whose gate needs a display, a licence, a GPU or a signed-in session cannot use it at all
278
- (that is a **host** gate — the class-C rule, restated for class F), and a private repo's
278
+ (that is a **host** gate — the class-C rule, restated for class F; **corrected 2026-10-02 (W6):**
279
+ F is merged into `software`, so this is the electron opt-in), and a private repo's
279
280
  Actions minutes are billed to the account (2,000/month free). Measured ground truth at W4:
280
281
  **one** first-party workflow exists in the whole estate and it self-skips; five of the six
281
282
  repos with a remote have none.
@@ -295,7 +296,9 @@ Electron perf number is a host gate, and the ratchet deliberately carries a diff
295
296
  while the main thread went from 1.8 % to 54.5 % busy, `docs/CI.md` §3.3); an absolute FPS
296
297
  claim is not. Three further Electron failure modes are **recorded, not mechanised**, and
297
298
  `docs/CI.md` §4 says why: the dependency-graph boundary check, `ipcMain` sender validation,
298
- and fuses at package time.
299
+ and fuses at package time. **Corrected 2026-10-02 (W6):** this preset is now
300
+ `presets/electron-overlay.yaml`, rendered over `presets/software.yaml` by `--electron` (or the
301
+ `desktop`/`F` install alias) — the old `F` class was merged into `software`.
299
302
  36. **A ban's exemption reaches the probe through its environment, so a custom probe can ignore it.**
300
303
  `bans_exempt:` and the inline `// BAN-OK(<id>): <reason>` are filtered *before* the exit code is
301
304
  chosen, because a filter applied to a probe's stdout afterwards cannot change a verdict — that
@@ -589,3 +592,17 @@ the Node the gates ran under.
589
592
  a source row can never fail a user's repo, and a target row can never substitute for the
590
593
  framework's own suite. Recorded as a definition; `docs/ENFORCEMENT.md`'s scope paragraph
591
594
  carries the same sentence for the reader who arrives there first.
595
+
596
+ 53. **The sixth class is gone: the desktop shell is `software` + `electron: true`, and `A`..`E` are
597
+ read-time aliases.** W6 merged `F` into `software` because their `manifest/classes.tsv` need
598
+ columns were measured identical on all ten parts — the old column added config (the electron
599
+ `bans:`, the `perf.host_gate:`, the `app_bundle_bytes` ratchet, the `dist out release`
600
+ build-output scope), never a part. Measured on the merge tree (2026-10-02): the tsv is 50 rows
601
+ over five classes; `gob init --class desktop`, `--class F` and `--class software --electron`
602
+ render byte-identical `goblin.yaml` (`class: software`, `electron: true`); and a pre-merge repo
603
+ carrying `class: F` with no `electron:` key verifies unchanged, `38 passed, 0 failed, 11
604
+ advisory, 33 skipped`, exit 0, with `git status --porcelain` empty — zero writes, because its
605
+ bans and host gate live in its own config. What this costs, recorded rather than fixed: such a
606
+ repo gets the merge's declaration-time host-gate check only after hand-adding `electron: true`;
607
+ its bans and host gate keep running either way, so nothing fails closed, and the CLI keeps
608
+ accepting `desktop`/`F` as aliases.
@@ -1,61 +1,51 @@
1
1
  class part need
2
- A handoff R
3
- B handoff R
4
- C handoff R
5
- D handoff R
6
- E handoff R
7
- F handoff R
8
- A spec R
9
- B spec R
10
- C spec R
11
- D spec -
12
- E spec R
13
- F spec R
14
- A gate R
15
- B gate R
16
- C gate R
17
- D gate O
18
- E gate R
19
- F gate R
20
- A replay R
21
- B replay -
22
- C replay R
23
- D replay -
24
- E replay O
25
- F replay R
26
- A ratchet R
27
- B ratchet O
28
- C ratchet O
29
- D ratchet -
30
- E ratchet O
31
- F ratchet R
32
- A pr-gate O
33
- B pr-gate -
34
- C pr-gate O
35
- D pr-gate -
36
- E pr-gate O
37
- F pr-gate O
38
- A review-panel O
39
- B review-panel -
40
- C review-panel R
41
- D review-panel -
42
- E review-panel O
43
- F review-panel O
44
- A playbooks R
45
- B playbooks R
46
- C playbooks R
47
- D playbooks R
48
- E playbooks R
49
- F playbooks R
50
- A tokens O
51
- B tokens -
52
- C tokens -
53
- D tokens -
54
- E tokens -
55
- F tokens O
56
- A ci-gate R
57
- B ci-gate -
58
- C ci-gate O
59
- D ci-gate -
60
- E ci-gate O
61
- F ci-gate R
2
+ software handoff R
3
+ service handoff R
4
+ game handoff R
5
+ research handoff R
6
+ fleet handoff R
7
+ software spec R
8
+ service spec R
9
+ game spec R
10
+ research spec -
11
+ fleet spec R
12
+ software gate R
13
+ service gate R
14
+ game gate R
15
+ research gate O
16
+ fleet gate R
17
+ software replay R
18
+ service replay -
19
+ game replay R
20
+ research replay -
21
+ fleet replay O
22
+ software ratchet R
23
+ service ratchet O
24
+ game ratchet O
25
+ research ratchet -
26
+ fleet ratchet O
27
+ software pr-gate O
28
+ service pr-gate -
29
+ game pr-gate O
30
+ research pr-gate -
31
+ fleet pr-gate O
32
+ software review-panel O
33
+ service review-panel -
34
+ game review-panel R
35
+ research review-panel -
36
+ fleet review-panel O
37
+ software playbooks R
38
+ service playbooks R
39
+ game playbooks R
40
+ research playbooks R
41
+ fleet playbooks R
42
+ software tokens O
43
+ service tokens -
44
+ game tokens -
45
+ research tokens -
46
+ fleet tokens O
47
+ software ci-gate R
48
+ service ci-gate -
49
+ game ci-gate O
50
+ research ci-gate -
51
+ fleet ci-gate O
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techgoblin/gobstack",
3
- "version": "0.4.4-beta.7",
3
+ "version": "0.5.0-beta.1",
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",
@@ -0,0 +1,38 @@
1
+ # presets/electron-overlay.yaml — the electron opt-in for the `software` class.
2
+ #
3
+ # The `desktop` class is gone; F was merged into software because their classes.tsv need
4
+ # columns are IDENTICAL on all 10 parts (measured). Everything F added beyond software lives in
5
+ # plain goblin.yaml config keys, so it is an OVERLAY, not a class: `gob install --class software
6
+ # --electron` (or the `--class desktop` / `F` alias, which sets --electron) renders the keys
7
+ # below OVER the software preset. An absent key falls through to presets/software.yaml; a key
8
+ # present here — including an empty one like sec_write_routes: "" — wins.
9
+ #
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 goblin.yaml.
12
+ label: Desktop shell
13
+ done_means: the renderer is isolated from Node, the main process is not busy, and the packaged bundle ships no dev dependency
14
+ # software's A-only second gate (the TODO ceiling) is NOT applied: F never had it.
15
+ gate2_name: ""
16
+ gate2_cmd: ""
17
+ # The perf lane is the shipped ratchet, reused verbatim; the hermetic number here is the
18
+ # packaged bundle's byte count (a fat bundle is a slow cold start on every machine).
19
+ ratchet_name: app_bundle_bytes
20
+ ratchet_cmd: find dist out release -type f -exec cat {} + 2>/dev/null | wc -c
21
+ sec_build_output: dist out release
22
+ # A renderer that reaches the filesystem or Node directly is forbidden, so there are no write
23
+ # routes to allow (software allows app/ and src/app/; the overlay blanks that).
24
+ sec_write_routes: ""
25
+ # perf_metric must EQUAL ratchet_name (PF-01 fails when the budget and the measurement disagree).
26
+ perf_metric: app_bundle_bytes
27
+ perf_cmd: find dist out release -type f -exec cat {} + 2>/dev/null | wc -c
28
+ # THE FPS NUMBER IS A HOST GATE, not the ratchet: the instrument that produces
29
+ # main_thread_busy_pct needs Playwright or Electron plus a GUI, which the no-npm contract
30
+ # forbids a shipped rule to launch. It is DECLARED here and carried in the HANDOFF with its date
31
+ # (docs/LIMITS.md #34, docs/CI.md carries the sweep). `electron: true` with this empty FAILs PF-01.
32
+ perf_host_gate: "Electron run - main_thread_busy_pct with a window open (app.getAppMetrics()[i].cpu.percentCPUUsage, or CDP Performance.getMetrics), on a machine with a display"
33
+ # The nine electron bans (G5's mechanism, G6's failure surface): nodeIntegration, context
34
+ # isolation / sandbox, the dangerous webPreferences, and synchronous IPC / @electron/remote. An
35
+ # unlisted ban SKIPs with a reason, so the other classes are unaffected; with electron: true the
36
+ # engine also treats BN-06..09 as enabled even if a hand-edited bans: list omits them.
37
+ bans: BN-01, BN-02, BN-05, BN-06, BN-07, BN-08, BN-09
38
+ notes: a perf number measured on one box is not a user's experience - the renderer half can be a CI job, the Electron half is a host gate and cannot be.
@@ -1,4 +1,4 @@
1
- # presets/E-fleet-config.yaml — class E: agent-fleet configuration.
1
+ # presets/fleet.yaml — the `fleet` class (letters/aliases E, agent).
2
2
  #
3
3
  # "Done" means a config change is applied, verified against the ARTIFACT, and versioned.
4
4
  # The gate therefore checks the artifact (a commit exists, dated), never the intention.
@@ -1,4 +1,4 @@
1
- # presets/C-game.yaml — class C: game.
1
+ # presets/game.yaml — the `game` class (letter/alias C).
2
2
  #
3
3
  # "Done" means a suite green in the Editor AND a human feel verdict. The verdict is a
4
4
  # first-class deliverable, which is why this class requires the review-panel part: the panel
@@ -1,4 +1,4 @@
1
- # presets/D-knowledge.yaml — class D: knowledge / research.
1
+ # presets/research.yaml — the `research` class (letter/alias D).
2
2
  #
3
3
  # "Done" means a question is answered with sources and the answer is findable. A research
4
4
  # note is not a spec, so the SPEC part is OFF, not optional. So are the REPLAY and the PR
@@ -1,4 +1,4 @@
1
- # presets/B-service-config.yaml — class B: service / configuration.
1
+ # presets/service.yaml — the `service` class (letter/alias B).
2
2
  #
3
3
  # "Done" means a contract (schema, route, API) is unchanged, or the change is intentional
4
4
  # and migrated. The class turns off the REPLAY and the PR gate, because a config repo has no
@@ -1,4 +1,4 @@
1
- # presets/A-shipped-software.yaml — class A: shipped software.
1
+ # presets/software.yaml — the `software` class (letters/aliases A, app).
2
2
  #
3
3
  # A class is not a stringency level. It selects WHICH PARTS are required, optional or off
4
4
  # (manifest/classes.tsv), and it supplies the default gate/ratchet shape below. The gate
@@ -7,13 +7,21 @@ description: P8: adopt goblin-stack in a repo - classify, install, verify, then
7
7
 
8
8
  Use when adopting goblin-stack in a repo, or starting one.
9
9
 
10
- 1. **Classify the project A-F.** The class selects which parts are required, optional or off;
11
- it is not a stringency level. F is a desktop shell: it adds the electron bans and a host gate.
12
- 2. **`goblin-install --target <dir> --class <x>`**
13
- 3. **`goblin-verify`** — a class-A install verifies green: `43 passed, 0 failed, 11
14
- advisory, 28 skipped`, exit 0, once `HANDOFF.md` names a commit that exists; before that edit the
15
- scaffold's `0000000` placeholder is `HP-05`'s one expected day-one red (`42 passed, 1 failed`).
16
- Twenty-eight rows skip with a reason, and the reason matters: `HS-02`
10
+ 1. **Classify the project into one of five classes.** `software`, `service`, `game`, `research` or
11
+ `fleet` — the class selects which parts are required, optional or off; it is not a stringency
12
+ level. `software` also carries the electron opt-in (`--electron`): an Electron app is `software`
13
+ with the electron bans and a host gate, not a sixth class. The letters `A`-`E` and the older
14
+ names are read-time aliases.
15
+ 2. **`goblin-install --target <dir> --class <x>`** — the default install is a NEUTRAL harness:
16
+ no agent skills. Opt in per platform afterwards with `gob emit --platform <p>` (or vendor the
17
+ Hermes project tier with `--skills yes`).
18
+ 3. **`goblin-verify`** — a default software-class install (no agent skills) verifies green:
19
+ `38 passed, 0 failed, 11 advisory, 33 skipped`, exit 0, once `HANDOFF.md` names a commit that
20
+ exists; before that edit the
21
+ scaffold's `0000000` placeholder is `HP-05`'s one expected day-one red (`37 passed, 1 failed`).
22
+ Thirty-three rows skip with a reason, and the reason matters: the five skill rows
23
+ (`SK-01`..`SK-04`, `AU-04`) skip on the `playbooks` opt-out a skills-free install records,
24
+ then `HS-02`
17
25
  (no pinned pre-change commit yet, so the REPLAY is not provable), `AU-02`/`AU-03` (no report
18
26
  has been filed in this repo), `SC-06`/`SC-07`/`SC-08` (no dependency manifest, no lockfile, no
19
27
  audit record), `PF-01` (no perf baseline measured yet), `BN-01`/`BN-02`/`BN-05` (the ban table is
@@ -1,23 +1,32 @@
1
1
  # AGENTS.md
2
2
 
3
- This repository uses **goblin-stack** (class `{{CLASS}}`, installed {{DATE}}). It is a pointer,
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:** `.goblin/bin/goblin-verify` — exit 0 pass, 1 a check failed,
7
+ 2 could not run, 3 the manifest is broken. Run it before you commit; the gate is the
8
+ same run.
6
9
  - **Rules and their checks:** `.goblin/manifest/enforcement.tsv` (one row per rule; every row
7
10
  carries a runnable check or the literal `advisory`).
8
11
  - **Forbidden code (the ban list):** `.goblin/bin/goblin-bans` runs the bans named by `bans:` in
9
12
  `.goblin/goblin.yaml`; each ban's mechanism lives in `.goblin/manifest/bans.tsv`. Run it before
10
13
  you write the line, not after — a ban is a gate, and `goblin-verify` only reports it once the
11
14
  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
15
  - **Session state:** `HANDOFF.md` at the repo root. Read it before touching anything.
17
16
  - **Project config:** `.goblin/goblin.yaml` (class, branch, gates, ratchet, runtime data,
18
17
  replay, opt-outs). Edit it in place; the installer never overwrites it.
19
18
  - **The referenced standard**, if configured, is named by `practice:` in `.goblin/goblin.yaml`.
20
19
  Read it before starting work; this repo carries no copy of it.
21
20
 
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 emit --platform <p>
26
+
27
+ (`--scope project` writes them inside this repo; `--scope global` writes them for your user.)
28
+ Run `gob emit` with no platform to see the list. If skills were installed here, they live
29
+ under the platform's own directory, and `gob verify` hashes them.
30
+
22
31
  Two things this repo does not do: it does not choose models (a role resolves through the
23
32
  mapping file named by `models_file:`), and it does not write outside its own tree.
@@ -4,9 +4,17 @@
4
4
  # Format: a flat, line-oriented YAML subset, parsed by .goblin/bin/goblin-lib.sh. No YAML
5
5
  # library, no network, no npm. Keep one key per line and keep comments on their own line.
6
6
 
7
- # Which class this project is (A|B|C|D|E). Selects the required parts (manifest/classes.tsv).
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).
8
11
  class: {{CLASS}}
9
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
+
10
18
  # The default branch, DECLARED, never assumed. A preset that assumes the wrong branch
11
19
  # silently skips a repo.
12
20
  branch: {{BRANCH}}
@@ -1,67 +0,0 @@
1
- # presets/F-electron.yaml — class F: desktop shell (Electron).
2
- #
3
- # A class is not a stringency level: it selects WHICH PARTS are required, optional or off
4
- # (manifest/classes.tsv) and supplies the default gate/ratchet shape. A desktop shell needs a
5
- # part no other class has — a HOST gate, a number measured on a machine with a display — and it
6
- # forbids a thing the others allow: a renderer that reaches the filesystem or Node directly.
7
- # That is a new row-set, not a flag on class A (G6 section B.2, Part B).
8
- #
9
- # THE PERF LANE IS THE SHIPPED RATCHET, NOT A SECOND ONE (G6 section B.3). There is one
10
- # mechanism — `ratchet: {name, cmd, ceiling}`, enforced by GT-04/GT-05 and pinned to a commit by
11
- # PF-01 — and this class reuses it verbatim. What the ratchet measures here is the one hermetic
12
- # perf number a desktop shell has: the packaged bundle's byte count. A fat bundle is a slow
13
- # cold start on every machine, and the number needs no browser, no display and no dependency.
14
- #
15
- # THE FPS NUMBER IS A HOST GATE, and this is a DEVIATION from G6 section B.3, recorded with its
16
- # measured reason. G6 wants `main_thread_busy_pct` in the ratchet. It cannot be: the instrument
17
- # that produces it (CDP `Performance.getMetrics` over a real Chromium, or
18
- # `app.getAppMetrics()[i].cpu.percentCPUUsage` inside a real Electron) needs Playwright or
19
- # Electron plus a GUI, and the dependency contract (docs/CONTRACTS.md) allows a shipped rule
20
- # nothing but bash/git/awk/sed/grep/python3. A `ratchet.cmd` that cannot run on a fresh install
21
- # makes a fresh install BORN RED, which is the one thing every class must not be. So the probe
22
- # belongs to the project, next to the code it measures, and the number it produces is declared
23
- # here as a host gate and carried in the HANDOFF with its date (the class C pattern).
24
- #
25
- # Why frame time is the WRONG number, measured in G6 on a real Chromium (CDP, 0 to 32 ms of
26
- # work per frame): p50 frame time stayed FLAT at 16.70 ms while the main thread went from 1.8%
27
- # to 54.5% busy, and the dropped-frame count was non-monotone (0,0,0,1,3,11,0 — the worst
28
- # workload read 0). A frame-time gate at 16.7 ms is green on the idle tree AND on the loaded
29
- # tree: PROJECT-PRACTICE section 3, reproduced live in the exact metric the note proposed.
30
- # `main_thread_busy_pct` is the only monotone instrument in that sweep (1.8 -> 13.5 -> 26.0 ->
31
- # 54.5 -> 76.9 -> 99.5%), which is why it is named here as the host gate's metric.
32
- # docs/CI.md carries the sweep; docs/LIMITS.md #34 carries the gap.
33
- label: Desktop shell
34
- done_means: the renderer is isolated from Node, the main process is not busy, and the packaged bundle ships no dev dependency
35
- harness_dir: checks
36
- scaffold_checks: yes
37
- gate_name: commit
38
- gate_cmd: git rev-parse --verify --quiet HEAD
39
- ratchet_name: app_bundle_bytes
40
- ratchet_cmd: find dist out release -type f -exec cat {} + 2>/dev/null | wc -c
41
- ratchet_ceiling: measure
42
- replay_env: GOBLIN_PRE_COMMIT
43
- replay_cmd: node checks/{name}.mjs
44
- runtime_data: .goblin/state.json
45
- sec_gitignore_family: yes
46
- sec_build_output: dist out release
47
- sec_audit_cmd: npm audit --json
48
- sec_audit_max_age_days: 90
49
- sec_waiver_max_age_days: 180
50
- sec_write_routes: ""
51
- # perf_metric must EQUAL ratchet_name (PF-01 fails when the budget and the measurement disagree),
52
- # so the hermetic metric is named here too, and the host gate carries the FPS number beside it.
53
- perf_metric: app_bundle_bytes
54
- perf_cmd: find dist out release -type f -exec cat {} + 2>/dev/null | wc -c
55
- perf_baseline_commit: ""
56
- perf_baseline_value: 0
57
- perf_measured: ""
58
- perf_host_gate: "Electron run - main_thread_busy_pct with a window open (app.getAppMetrics()[i].cpu.percentCPUUsage, or CDP Performance.getMetrics), on a machine with a display"
59
- # The nine electron bans (G5's mechanism, G6's failure surface): nodeIntegration, context
60
- # isolation / sandbox, the dangerous webPreferences, and synchronous IPC / @electron/remote.
61
- # An unlisted ban SKIPs with a reason, so the other classes are unaffected by this list.
62
- bans: BN-01, BN-02, BN-05, BN-06, BN-07, BN-08, BN-09
63
- notes: a perf number measured on one box is not a user's experience - the renderer half can be a CI job, the Electron half is a host gate and cannot be.
64
-
65
- # The loop ceiling (G2, LP-03): the most turns ONE loop record may declare in its budget.
66
- # The same number for every class - a ceiling, not a per-class policy. 20 is the engine default.
67
- loop_max_turns_ceiling: 20