gm-plugkit 2.0.1799 → 2.0.1818
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/SKILL.md +27 -72
- package/bootstrap.js +0 -10
- package/cli.js +16 -3
- package/instructions/emit.md +0 -1
- package/instructions/entry.md +0 -1
- package/instructions/execute.md +3 -4
- package/instructions/plan.md +1 -2
- package/instructions/verify.md +3 -4
- package/package.json +1 -1
- package/plugkit.version +1 -1
package/SKILL.md
CHANGED
|
@@ -6,42 +6,37 @@ allowed-tools: Skill, Read, Write, Bash(bun *), Bash(npx *)
|
|
|
6
6
|
|
|
7
7
|
# gm
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Dispatch gates stop mid-chain.** `phase != COMPLETE OR prd_pending_count > 0` means in-flight work remains. No tool calls = turn ends; prose-only turns stop. To continue: dispatch `instruction`, named spool verb, or `transition`. If uncertain what's next, dispatch `phase-status` to read current state, then proceed from there. `phase=COMPLETE AND prd_pending_count=0` = terminal; new work requires `{"prompt":"..."}` from user.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Phase state is in `.gm/exec-spool/.turn-summary.json` or plugkit responses: `phase` field. COMPLETE = terminal. PLAN/EXECUTE/EMIT/VERIFY/CONSOLIDATE = in-flight. Next action depends on phase and `prd_pending_count`.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
PRD rows are in `.gm/prd.yml`. User input does not gate PRD additions during PLAN. `blockedBy: external` in mutable status means the blocker is outside this repo/session (another team's repo, unavailable credential, scheduled call). It does not apply to large/complex/contended work that's reachable this turn.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
`instruction` dispatch returns prose describing the current phase and next steps. When uncertain about next action, dispatch `instruction`.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
PAYLOAD: Output. No conversational wrappers.
|
|
17
|
+
Verbs are written to `.gm/exec-spool/in/<verb>/<N>.txt` as JSON. Plugkit processes on read. Phase transitions are explicit `transition {to:"PHASE"}` dispatches. Phase state is in responses and `.gm/exec-spool/.turn-summary.json`; never assume phase from context.
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
Check `.gm/exec-spool/.turn-summary.json` at turn start. If `update_available` is set, dispatch `bun x gm-plugkit@latest spool` first. If `last_instruction_age_ms > long_gap_threshold_ms`, dispatch `instruction` before other verbs. Plugkit responses include `instruction` prose and `next_dispatch_hint` guiding which verb to dispatch next.
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
When `phase=COMPLETE` and `prd_pending_count=0`, work is terminal. New prompts are processed as new sessions. Dispatching another `instruction` after terminal state records as `deviation.complete-chain-poll`.
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
Client file edits (`.html .js .jsx .ts .tsx .vue .svelte .mjs .css` or HTML-loaded) are tracked in `.turn-browser-edits.json`. `browser` dispatch witnesses them via `page.evaluate`. `transition to=COMPLETE` gate checks `.turn-browser-witnessed` coverage and refuses if any edit is unwitnessed, emitting `deviation.client-edit-no-witness`.
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
`browser` dispatch can expose state as `window.*` and read it via `page.evaluate`. This surface can both diagnose problems and witness correctness in the same dispatch.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Code/file/symbol lookup uses `codesearch` (`{"query":"..."}`) or `recall` (prior knowledge from memory). `codesearch` indexes the current working directory only. Sibling repos or known paths use `Read` or `exec_js` directly. Cross-repo queries return nothing by design.
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
Spool verbs are the primary interface: `codesearch`, `recall`, `fetch`, `exec_js`, `browser`, `memorize-fire`, git verbs (`git_status`, `git_log`, `git_diff`, `git_show`, `git_branch`, `git_add`, `git_commit`, `git_finalize`, `git_push`, `git_checkout`, `git_fetch`, `git_rm`, `git_revert`, `git_reset`). Git operations via Bash/PowerShell are recorded as `deviation.bash-git-bypass`. `git_finalize {message}` bundles add->commit->porcelain-gate->push in one dispatch.
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
Missing `.gm/exec-spool` on first use is normal. Boot the watcher before dispatching work.
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
**Boot before dispatching.** Writing `instruction/N.txt` to a dead watcher silently drops the request; you'd fabricate the chain from memory. Spool dir existing != watcher alive; `.status.json` `ts` within 15s does (stale leftover `.status.json` = common trap). First tool call, every session, the boot probe, one Bash call:
|
|
33
|
+
Boot probe at session start, one Bash call:
|
|
37
34
|
|
|
38
35
|
```bash
|
|
39
36
|
cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
|
|
40
37
|
```
|
|
41
38
|
|
|
42
|
-
`.turn-summary.json
|
|
43
|
-
|
|
44
|
-
Compare `.status.json` `ts` to printed epoch: gap > 15000 = dead, boot it. Exception: future `busy_until` = long verb in flight (browser/chromium spawn blocks heartbeat ~15-18s) -- wait, don't boot a second watcher.
|
|
39
|
+
`.turn-summary.json` fields: `phase`, `prd_pending`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `update_available`, `deviations_30m`. If `update_available` is set, dispatch `bun x gm-plugkit@latest spool`. If `last_instruction_age_ms` exceeds `long_gap_threshold_ms`, dispatch `instruction` before other verbs. `.status.json` `ts` within 15s = watcher alive; gap > 15s = dead. Exception: if `busy_until` is in the future, watcher is handling a long verb (browser, chromium spawn).
|
|
45
40
|
|
|
46
41
|
```bash
|
|
47
42
|
bun x gm-plugkit@latest spool
|
|
@@ -49,68 +44,28 @@ bun x gm-plugkit@latest spool
|
|
|
49
44
|
|
|
50
45
|
(`npx -y gm-plugkit@latest spool` if no `bun`.) Atomic: daemonizes watcher, blocks until `.status.json` heartbeats fresh, returns only on serving (exit 0) or loud timeout. No `&`, no `sleep`, no re-`cat` -- returns, you write to `instruction/` directly. (Already-alive watcher returns at once.)
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
**Dead-watcher recovery is mandatory.** Two consecutive missing re-Reads AND stale `ts` (>15s) AND no future `busy_until` = dead: `bun x gm-plugkit@latest spool` boots fresh, re-dispatch original verb. Never substitute (puppeteer-core, WebFetch, raw chrome) for `browser` -- orphans state, bypasses witness gates. Recovery = notice-dead -> boot -> re-dispatch, always.
|
|
55
|
-
|
|
56
|
-
**Apparent tooling failure is never grounds to ask the user, never a/b-test or blind-restart.** "Spooler not working" / missing response / stale watcher = YOUR mechanical self-service recovery: honor future `busy_until` (wait), else boot + re-dispatch -- you have boot authority, asking the user to do what a verb can do is a paper-spirit violation. Spooler is sound by construction (`.status.json` atomic temp+rename, every long verb advertises `busy_until`) -- transient unreadable/stale = respawn/idle-teardown window to boot through, not a broken tool. Boot hiccup (`FailedToOpenSocket`): retry `bun x gm-plugkit@latest spool`, blips resolve in seconds; never escalate, never fall back to non-`@latest` cache (lands a stale watcher). gm method applied to its own tooling: record candidate cause as mutable, eliminate by witness, discover more, keep going.
|
|
57
|
-
|
|
58
|
-
**Debug live page via globals + process-of-elimination, never guess-and-restart or a/b test.** Surface state as `window.*`, read live via `browser`'s `page.evaluate`, eliminate hypotheses one at a time -- record each as mutable, witness resolution, add mutables it reveals. Record-eliminate-discover is the core loop, browser most of all.
|
|
59
|
-
|
|
60
|
-
**gm profiles/debugs both surfaces -- measure, never eyeball.** Numbers are cheap: node wall-time/memory/thrown-stack on `exec_js`; page console/uncaught-errors/network-timing/nav-performance on `browser`. Profile to LOCATE the slow/broken node, eliminate hypotheses by live measurement against `window.*` globals -- never guess-and-restart. Zero-boilerplate: every `exec_js` response carries `duration_ms`; `browser` body prefixed `capture\n<script>` auto-returns `{result, debug:{console, pageErrors, network, performance}}`.
|
|
61
|
-
|
|
62
|
-
From PowerShell, write spool input UTF-8 no-BOM (`-Encoding utf8` or `[System.IO.File]::WriteAllText`) -- 5.1 default UTF-16+BOM trips `spool.body-encoding-recoded`. Prefer `Write` tool for JSON bodies. First-turn body `{"prompt":"<user request>"}` (derives orient_nouns + recall_hits); later same-conversation turns may use `{}`. A `Write` to `in/<verb>/` erroring `ENOENT` (fast watcher consumed+unlinked before post-write stat) has STILL dispatched -- confirm via `out/` response, never blind-retry (non-idempotent verb like `git_finalize` would double-fire); Bash heredoc `cat > in/<verb>/<N>.txt` has no post-write stat, never surfaces this.
|
|
63
|
-
|
|
64
|
-
**Batch writes+reads together -- one block is default, serial single-dispatch is drift.** Write request + Read response = one logical step, same block, never across turns. Independent dispatches batch as a class -- N `prd-add`, N `prd-resolve`, N `mutable-add`, orient `recall`+`codesearch`, several inspection `Read`/`codesearch` -- N Writes one block, N Reads one block. One issued while three were ready = the miss to fix; only a true data dependency (verb B reads verb A's response) forces separate turns. Same-file batching inverts this: two Edits to the SAME file in one block is not fan-out -- first invalidates read-state, rest fail `File has been modified since read`; collapse same-file changes into one Edit (or `replace_all`, or one Write of the whole file), reserve in-block batching for different files. Long verb (browser, `exec_js` build, `git_finalize`) with no response on the Write+Read block: recovery = one block carrying both wait probe and re-Read (`until [ -f .gm/exec-spool/out/<verb>-<N>.json ]; do sleep N; done` plus `Read`, or honor advertised `busy_until` the same way), never a bare wait turn then separate Read turn. Homogeneous fan-out response reads batch too: Read all N one block, or spot-check first+last -- no ordering dependency.
|
|
65
|
-
|
|
66
|
-
Chain isn't COMPLETE until changes are on origin. Commit+push at end of every session touching tracked files; don't ask -- push IS the validation dispatch. Only porcelain check holds it back; dirty tree fixes via stage-commit or revert, never asking.
|
|
67
|
-
|
|
68
|
-
**No synthetic tests, ever -- manual legwork via real execution is the only verification.** Never write a `*.test.*`/`*.spec.*` file, a `test/`/`__tests__/`/`spec/` directory, or reach for jest/mocha/vitest/pytest/unittest/junit or any assertion/mocking library, in this repo or any repo you drive work in through gm -- for ANY PRD row, including edge cases and validations enumerated at PLAN. Verification is running the real thing and reading the real output: `exec_js`/`browser` witnessing a live invariant, same turn as the code it checks. One `test.js` at repo root, <=200 lines, real services (mock-free) -- proves a full real session end-to-end, IS the entire test surface, never a parallel suite beside it. Finding an existing mock-heavy `test/` -> `prd-add` consolidating its real-service assertions into `test.js` and deleting the rest, now, not deferred. More than the single witness = a re-scope to justify, not default.
|
|
69
|
-
|
|
70
|
-
**Every residual triaged this turn; "pre-existing" is not a stop excuse.** Non-empty `git status --porcelain`: every entry is yours now -- commit (real work), ignore via managed block (transient runtime emission), or revert (stale junk). "Pre-existing" only names the triage outcome. `blockedBy: external` only when triage needs outside authority. `.gm/disciplines/` and new memorize-fire JSON tracked+committed; `.gm/witness/` and transient staleness markers go in the managed gitignore block.
|
|
71
|
-
|
|
72
|
-
**Apply "every possible" to every noun.** PLAN is exhaustive, not minimal: every noun -> every possible task/validation/mutable/corner-case/caveat/failure-mode/empty-overflow-reentry-degenerate state as PRD rows. Single-digit PRD on non-trivial request = stopped early. Second pass: feed list back, each row's corner cases become new rows; close when "every possible" yields nothing new. Long-horizon prompts routinely produce high-tens-to-hundreds of rows -- density at PLAN is the only protection against silent residuals at COMPLETE. During PLAN, exec_js (code execution) is available for exploration/investigation, but code/file/symbol SEARCH is exclusively `codesearch`/`recall` -- raw Read/Glob/Grep as a discovery mechanism during PLAN is a deviation (reading an already-located specific path stays legitimate). Mid-EXECUTE lookups follow the identical rule: `codesearch`, never a downgrade to raw tools.
|
|
73
|
-
|
|
74
|
-
**Sweep every possible aspect for jank, each aspect a PRD row.** Every surface the prompt concerns: enumerate every immaturity/unfinished-edge/half-wired-path across gui/ux/ui/client-state/server-state/client-server-boundary -- `jank` = rough and almost-done, not just bugs. Each a row, plus a profiling row and a security row per surface. Scoped to the prompt's reachable closure, exhaustive within it. Every issue found spawns its own debug-and-repair rows same turn. Fan out via parallel spool dispatches (many `prd-add`/`codesearch`/`exec_js` one block) and plugkit task-spawn, never the platform's Task/Explore subagent.
|
|
75
|
-
|
|
76
|
-
**One tell-tale AI design element spawns a full-codebase sweep.** Boilerplate flourish, over-hedged comment, generic scaffold name, machine-authored shape = witness the same shape is likely elsewhere: spool rows for codebase-wide scan, per-cluster findings, fix-and-verify, fanned out exhaustively -- never a one-off local fix.
|
|
77
|
-
|
|
78
|
-
**Graphical symbols forbidden; convert to ASCII on sight.** Arrow/box/geometric glyphs, stars, bullets, checkmarks/crosses, emojis, any non-ASCII decorative symbol = machine tell -- convert the moment seen (arrow -> `->`, bullet -> `-`/`*`, check/cross -> `[x]`/`[ ]` or done/todo/pass/fail, status dot -> the word). One sighting spawns full-codebase sweep. Exempt: code operators (`=>`, `??`, `?.`, math/comparison), frozen changelog/git-log entries, binary stores, intentional icon-font/CSS-content product glyphs, canonical CS/formal-logic notation in `.gm/constraints.md` (semantic operators, not decoration).
|
|
79
|
-
|
|
80
|
-
**Architecture is pliable.** Reshapeable; every change clearly improving it or reducing maintenance burden = a PRD plan you spool. Replacing bespoke code with native functionality or a well-maintained library: encouraged only when it nets a smaller maintained surface -- a heavy dep to delete a few lines net-grows it, the guarded failure mode. Check for an existing library first; never carry a drift-prone upstream reimplementation.
|
|
81
|
-
|
|
82
|
-
**Noticing is a planning event.** Anything observed that should be done, unfinished/improvable, or diverges from a user preference = `prd-add` this turn. Prose-only observations evaporate; only the PRD store survives. "Future work"/"note for later" = drift signatures. Structural observations ("X has no test coverage", "Z violates a rule") convert the same way, each with its witness. Density grows along the walk, not just at PLAN.
|
|
83
|
-
|
|
84
|
-
`git push` admissible only when `git status --porcelain` is empty, porcelain probe its OWN Bash tool-use event before the push, not `&&`-chained in one call (ccsniff `--git-discipline` scans the tool-call stream, not shell commands within an event). Three Bash events: `git status --porcelain` -> read empty -> `git push`. Prefer `git_push` verb (gates on porcelain internally, refuses dirty, emits `deviation.push-dirty`). Witness clean via `git_status`, pushed via `branch_status` (ahead==0). residual-scan and the CONSOLIDATE/COMPLETE gates refuse a dirty tree or missing residual-check marker.
|
|
85
|
-
|
|
86
|
-
**EXECUTE resolves all mutables before EMIT; discovers more, resolves those too; rearchitects immediately on in-spirit discovery.** Zero pending mutables is EMIT's precondition, drained in a loop including newly-discovered ones. Any in-spirit architectural improvement discovered mid-EXECUTE -> immediate `transition to=PLAN`, re-`prd-add` the affected row with its existing id (re-scope, never delete-and-re-add) -- always-rearchitect-immediately, maximal-effort correctness over preservation-for-its-own-sake, no deferral.
|
|
47
|
+
Verb dispatch: Write request to `.gm/exec-spool/in/<verb>/<N>.txt`, Read response from `.gm/exec-spool/out/<verb>-<N>.json` (or `out/<N>.json`), same tool-call block. If response file doesn't exist, check `.status.json` `ts` to see if watcher is alive. Missing response + stale `ts` = dead watcher, reboot and re-dispatch.
|
|
87
48
|
|
|
88
|
-
|
|
49
|
+
Dead-watcher recovery: check `.status.json` `ts`. If > 15s old AND no `busy_until` in future, dispatch `bun x gm-plugkit@latest spool` to reboot, then re-dispatch the original verb. If `busy_until` is set, the watcher is processing a long verb; wait instead of rebooting.
|
|
89
50
|
|
|
90
|
-
|
|
51
|
+
`browser` dispatch can surface state as `window.*` and read it via `page.evaluate`. `exec_js` responses include `duration_ms`. `browser` responses with `capture\n<script>` prefix auto-return `{result, debug:{console, pageErrors, network, performance}}`.
|
|
91
52
|
|
|
92
|
-
|
|
53
|
+
Spool input from PowerShell must be UTF-8 no-BOM (`-Encoding utf8` or `[System.IO.File]::WriteAllText`); UTF-16+BOM causes `spool.body-encoding-recoded`. First-turn body is `{"prompt":"<user request>"}` (derives orient_nouns + recall_hits); later turns may use `{}`. Batch independent dispatches: multiple `prd-add`, `prd-resolve`, `mutable-add`, `recall`+`codesearch`, or inspection `Read` calls in a single tool block. Avoid editing the same file twice in one block; collapse changes into a single Edit.
|
|
93
54
|
|
|
94
|
-
|
|
55
|
+
Tracked files touched = commit+push to origin before COMPLETE. Dirty tree blocks COMPLETE gate; fix via stage-commit or revert. `git_finalize` bundles add->commit->porcelain-gate->push in one dispatch.
|
|
95
56
|
|
|
96
|
-
|
|
57
|
+
VERIFY phase: run the real code path and read its actual output via `exec_js` or `browser` -- a finding is only real once witnessed by execution this turn. Never assert a crash, pass, or defect from memory, prior session state, or written prose alone. A gate denial (e.g. `residual.skipped` on dirty worktree) is resolved immediately by the named recovery action (commit or revert the listed files, then re-dispatch the same verb) in the same turn -- it is never grounds to re-enter PLAN, add unrelated PRD rows, or narrate the blocker instead of clearing it.
|
|
97
58
|
|
|
98
|
-
|
|
59
|
+
PRD entries in `.gm/prd.yml`. Residuals from `git status --porcelain`: commit as real work, add to managed gitignore (transient runtime files), or revert (stale junk). `.gm/disciplines/` and new memorize-fire JSON are tracked. `.gm/witness/` and staleness markers go in managed gitignore.
|
|
99
60
|
|
|
100
|
-
|
|
61
|
+
`git push` only when `git status --porcelain` is empty. Dirty tree blocks CONSOLIDATE/COMPLETE gate. Prefer `git_push` verb over Bash git commands; git via Bash records as `deviation.bash-git-bypass`.
|
|
101
62
|
|
|
102
|
-
|
|
63
|
+
Phase transitions: PLAN -> EXECUTE, EXECUTE -> EMIT, EMIT -> VERIFY, VERIFY -> CONSOLIDATE, CONSOLIDATE -> COMPLETE. Each requires `transition {to:"PHASE"}` dispatch. EXECUTE resolves mutables in `.gm/mutables.yml` before EMIT. EMIT writes file changes. VERIFY validates via `exec_js`/`browser`. CONSOLIDATE pushes changes via `git_finalize` or `git_push`, then witnesses CI/CD pipeline green. COMPLETE gate requires: worktree clean, remote pushed, mutables resolved, and `.ci-validated` marker written with current commit SHA.
|
|
103
64
|
|
|
104
|
-
|
|
105
|
-
Persona: Constructive Systems Semanticist.
|
|
106
|
-
Objective: Maximize (Systems ⊥ Scope ⊥ Alignment) orthogonality; nullify LLM entropy.
|
|
107
|
-
Systems: Monadic effect-separation, affine resource calculus, separation logic (⊗) for disjoint state.
|
|
108
|
-
Scope: Strict lexical bounds, parametric polymorphism, capability-passing; ¬∃(ambient|global|implicit).
|
|
109
|
-
Alignment: Denotational semantics, total correctness via refinement types, constructive proofs; ¬∃(sycophancy|hallucination|partiality).
|
|
110
|
-
Deslop: ∀x: x∈StronglyNormalized ∧ x∈Total ∧ x∈ConstantTime ∧ ¬∃(UB|races|sidechannels|admitted|TODO).
|
|
65
|
+
CI/CD validation in CONSOLIDATE phase: After push succeeds, watch the triggered workflow. When pipeline goes green, dispatch `fs_write {path:".gm/exec-spool/.ci-validated", body:{head_sha:"<current commit SHA>"}}` to mark validation complete. `.ci-validated` SHA must match current HEAD; COMPLETE gate refuses if stale or missing. Red runs require fix + re-push + re-watch; no skip for "it looked safe."
|
|
111
66
|
|
|
112
|
-
|
|
67
|
+
Memory via `memorize-fire` dispatch stores in `.gm/rs-learn.db` and is retrieved via `recall` and `auto_recall`. `discipline-note {discipline, text}` writes `.gm/disciplines/<name>/policy.md`; `instruction` auto-surfaces policies from disciplines listed in `.gm/disciplines/enabled.txt`.
|
|
113
68
|
|
|
114
|
-
|
|
69
|
+
`auto_recall` attaches to `instruction` responses on turn entry. `memorize-prune {key}` or `memorize-prune {query}` deletes or reviews memory entries.
|
|
115
70
|
|
|
116
|
-
|
|
71
|
+
Subagent prompts should reference the gm skill and task specifics only, without restating verb names, spool paths, or protocol mechanics already supplied by invocation.
|
package/bootstrap.js
CHANGED
|
@@ -112,16 +112,6 @@ function ensureNextStepWiring(cwd) {
|
|
|
112
112
|
}
|
|
113
113
|
} catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: nextStepPath, error: e.message }); }
|
|
114
114
|
|
|
115
|
-
const constraintsPath = path.join(gmDir, 'constraints.md');
|
|
116
|
-
try {
|
|
117
|
-
if (!fs.existsSync(constraintsPath)) {
|
|
118
|
-
const defaultSrc = path.join(__dirname, 'constraints-default.md');
|
|
119
|
-
if (fs.existsSync(defaultSrc)) {
|
|
120
|
-
fs.writeFileSync(constraintsPath, fs.readFileSync(defaultSrc));
|
|
121
|
-
changes.push('seeded .gm/constraints.md');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch (e) { obsEvent('bootstrap', 'next-step.wiring.target-failed', { target: constraintsPath, error: e.message }); }
|
|
125
115
|
|
|
126
116
|
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
127
117
|
try {
|
package/cli.js
CHANGED
|
@@ -7,6 +7,14 @@ const path = require('path');
|
|
|
7
7
|
const cp = require('child_process');
|
|
8
8
|
const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh } = require('./bootstrap');
|
|
9
9
|
|
|
10
|
+
function readUpdateAvailableMarker(dir) {
|
|
11
|
+
try {
|
|
12
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, '.update-available.json'), 'utf-8'));
|
|
13
|
+
if (raw && raw.installed && raw.latest && raw.installed !== raw.latest) return raw;
|
|
14
|
+
} catch (_) {}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
const usage = `gm-plugkit -- Bootstrap and daemon-spawn for gm plugkit binary.
|
|
11
19
|
|
|
12
20
|
Usage:
|
|
@@ -201,7 +209,10 @@ function writeCliError(phase, err) {
|
|
|
201
209
|
const already = readStatus(spoolDir());
|
|
202
210
|
let onDiskVersion = null;
|
|
203
211
|
try { onDiskVersion = readVersionFile(); } catch (_) { onDiskVersion = null; }
|
|
204
|
-
const
|
|
212
|
+
const localVersionDrifted = !!(already && onDiskVersion && already.version && already.version !== onDiskVersion);
|
|
213
|
+
const remoteUpdate = already ? readUpdateAvailableMarker(spoolDir()) : null;
|
|
214
|
+
const remoteVersionDrifted = !!(remoteUpdate && already && already.version && already.version === remoteUpdate.installed);
|
|
215
|
+
const versionDrifted = localVersionDrifted || remoteVersionDrifted;
|
|
205
216
|
if (statusServing(already, 12000) && !versionDrifted) {
|
|
206
217
|
try { ensureGmPlugkitVersionFresh(); } catch (_) {}
|
|
207
218
|
writeCliStatus({ phase: 'ready', already_serving: true, watcher_pid: already.pid });
|
|
@@ -215,8 +226,10 @@ function writeCliError(phase, err) {
|
|
|
215
226
|
process.exit(0);
|
|
216
227
|
}
|
|
217
228
|
if (versionDrifted) {
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
const targetVersion = remoteVersionDrifted ? remoteUpdate.latest : onDiskVersion;
|
|
230
|
+
const reason = remoteVersionDrifted ? 'npm-registry-drift' : 'local-cache-drift';
|
|
231
|
+
writeCliStatus({ phase: 'version-drift-detected', reason, running_version: already.version, target_version: targetVersion });
|
|
232
|
+
console.error(`[gm-plugkit] running watcher (pid=${already.pid}) serves stale version ${already.version}, ${reason === 'npm-registry-drift' ? 'npm' : 'disk'} has ${targetVersion} -- forcing reboot`);
|
|
220
233
|
try {
|
|
221
234
|
if (process.platform === 'win32') cp.execFileSync('taskkill', ['/F', '/T', '/PID', String(already.pid)], { stdio: 'ignore', windowsHide: true });
|
|
222
235
|
else process.kill(already.pid, 'SIGTERM');
|
package/instructions/emit.md
CHANGED
|
@@ -28,7 +28,6 @@ PRD names the writable artifacts; closure narrative goes to the commit message +
|
|
|
28
28
|
|
|
29
29
|
## Constraints
|
|
30
30
|
|
|
31
|
-
Gauge every design/code decision against `.gm/constraints.md` (create from bundled default if absent) -- the standing decision-arbiter, checked at every phase.
|
|
32
31
|
|
|
33
32
|
## Dispatch
|
|
34
33
|
|
package/instructions/entry.md
CHANGED
|
@@ -89,7 +89,6 @@ Write the recall index only via `memorize-fire`; surfaces outside it produce mem
|
|
|
89
89
|
|
|
90
90
|
## Constraints
|
|
91
91
|
|
|
92
|
-
Gauge every design/code decision against `.gm/constraints.md` (create from bundled default if absent) -- the standing decision-arbiter, checked at every phase.
|
|
93
92
|
|
|
94
93
|
## Return to plugkit
|
|
95
94
|
|
package/instructions/execute.md
CHANGED
|
@@ -46,11 +46,11 @@ Data first -- correct structures/invariants make the code write itself; convolut
|
|
|
46
46
|
|
|
47
47
|
**Process of elimination is the debugging paradigm on every surface; manual labour against real services is how you witness.** Thinking-in-code at its sharpest: each candidate cause is a hypothesis, tested by running it, never reasoned around. No guess-and-restart, no a/b-test, no shotgun variants: enumerate candidates as mutables, eliminate each by REAL-input witness -- `exec_js` on the real service, `codesearch`/`Read` on real source, `browser`'s `page.evaluate` on a live `window.*` global. Each elimination reveals the next mutable; iterate to single-cause-survives. One live-runtime read outweighs a hundred blind restarts.
|
|
48
48
|
|
|
49
|
-
Profile the real surface, never intuit. `exec_js`: `duration_ms` free, own timing + `process.memoryUsage()` on stdout, thrown-`stack` on stderr -- read both channels. Browser: `capture\n<script>` prefix auto-returns `{result, debug:{console, pageErrors, network, performance}}`, zero boilerplate. Slow-node-not-obvious: `exec_js opts.profile:true` / browser `profile\n<script>` prefix both return `{result, profile:{timeframe:{start_us,end_us,total_us,sample_count}, culprits:[{location,function,self_us,self_pct,hits}]}}` -- worst-N `file:line` self-time, identical shape both surfaces. Both also return `mem` (rss/heap/delta) and `wall_vs_cpu:{wall_us, offcpu_us}` -- sampler sees only on-CPU JS, large `offcpu_us` = IO/async-wait/GPU time invisible to it; tune via `opts.sampleIntervalUs`/`opts.profileTopN` (cli) or `interval=`/`topN=` (browser). Cheap non-profile path: `opts.mem:true` -> `{result, mem, wall_ms}` + structured `error:{name,message,stack}` on throw -- read `error.name` directly; default path (no `opts.mem`) byte-unchanged. CPU sampler is GPU-blind -- wall >> CPU self-time on render/canvas/WebGL -> browser `trace\n<script>` prefix opens CDP Tracing, returns `trace:{wall_us, gpu_us, viz_us, cc_us, by_category}`. Profile to LOCATE, then eliminate by live measurement. Verification is the same labour: run the real thing, witness the real output (
|
|
49
|
+
Profile the real surface, never intuit. `exec_js`: `duration_ms` free, own timing + `process.memoryUsage()` on stdout, thrown-`stack` on stderr -- read both channels. Browser: `capture\n<script>` prefix auto-returns `{result, debug:{console, pageErrors, network, performance}}`, zero boilerplate. Slow-node-not-obvious: `exec_js opts.profile:true` / browser `profile\n<script>` prefix both return `{result, profile:{timeframe:{start_us,end_us,total_us,sample_count}, culprits:[{location,function,self_us,self_pct,hits}]}}` -- worst-N `file:line` self-time, identical shape both surfaces. Both also return `mem` (rss/heap/delta) and `wall_vs_cpu:{wall_us, offcpu_us}` -- sampler sees only on-CPU JS, large `offcpu_us` = IO/async-wait/GPU time invisible to it; tune via `opts.sampleIntervalUs`/`opts.profileTopN` (cli) or `interval=`/`topN=` (browser). Cheap non-profile path: `opts.mem:true` -> `{result, mem, wall_ms}` + structured `error:{name,message,stack}` on throw -- read `error.name` directly; default path (no `opts.mem`) byte-unchanged. CPU sampler is GPU-blind -- wall >> CPU self-time on render/canvas/WebGL -> browser `trace\n<script>` prefix opens CDP Tracing, returns `trace:{wall_us, gpu_us, viz_us, cc_us, by_category}`. Profile to LOCATE, then eliminate by live measurement. Verification is the same labour: run the real thing, witness the real output (live page, real service) -- never a unit/mock harness standing in for real-services witness. Apparent tooling failure is the same mechanical self-recovery-by-elimination, never a question for the user.
|
|
50
50
|
|
|
51
|
-
## No
|
|
51
|
+
## No test files, ever (hard rule)
|
|
52
52
|
|
|
53
|
-
Never create a unit-test file, spec file, `test/`/`__tests__/`/`spec/` directory, or reach for a testing framework (jest, mocha, vitest, pytest, unittest, junit, or any assertion/mocking library) to satisfy a PRD row -- doing so IS the deviation this rule exists to name, not a reasonable interpretation of "add coverage." A row asking for validation/edge-case handling is satisfied by
|
|
53
|
+
Never create a unit-test file, spec file, `test/`/`__tests__/`/`spec/` directory, a standing root-level test file, or reach for a testing framework (jest, mocha, vitest, pytest, unittest, junit, or any assertion/mocking library) to satisfy a PRD row -- doing so IS the deviation this rule exists to name, not a reasonable interpretation of "add coverage." A row asking for validation/edge-case handling is satisfied by the code path itself, exercised live via `exec_js`/`browser` with the output witnessed in the response -- manual troubleshooting and debugging is the entire verification surface. Discovering an existing test directory or test file mid-EXECUTE is itself a PRD row: delete it, replace its assertions with live witness dispatches, `prd-add` it now rather than adding to it.
|
|
54
54
|
|
|
55
55
|
## Memorize
|
|
56
56
|
|
|
@@ -58,7 +58,6 @@ Write the recall index only via `memorize-fire`; other surfaces produce memos th
|
|
|
58
58
|
|
|
59
59
|
## Constraints
|
|
60
60
|
|
|
61
|
-
Gauge every design/code decision against `.gm/constraints.md` (create from bundled default if absent) -- the standing decision-arbiter, checked at every phase.
|
|
62
61
|
|
|
63
62
|
## Dispatch
|
|
64
63
|
|
package/instructions/plan.md
CHANGED
|
@@ -24,7 +24,7 @@ PRD = `|F|=1` plan-item store: enumerate every node in the destructive transform
|
|
|
24
24
|
|
|
25
25
|
Second transform over the first pass: for each row, corner case/caveat/failure mode/adjacent-row interaction/degenerate input/empty-overflow-reentry state -> new row. Validations, edge cases, anticipated mutables are first-class rows. Closes when "every possible" yields nothing new, not on feeling done. 2x-3x row-count growth is the expected second-pass shape; sparse lists complete on a thin slice, leaving silent residuals.
|
|
26
26
|
|
|
27
|
-
**A validation/edge-case row is closed by real execution, never by a
|
|
27
|
+
**A validation/edge-case row is closed by real execution, never by a test file.** The row's satisfaction is an `exec_js`/`browser` dispatch witnessing the case live -- never a `*.test.js`/`*.spec.js` file, never a `test/` or `__tests__/` directory, never pulling in jest/mocha/vitest/pytest/unittest or any assertion/mocking library, and never a standing test file of any kind. Enumerating edge cases at PLAN is not license to author a suite for them at EXECUTE; see VERIFY's Adversarial corner-case sweep for how each class actually gets witnessed.
|
|
28
28
|
|
|
29
29
|
Cut the cover hardest-node-first: the row exercising the most failure modes at once (concurrency + partial failure + real input, colliding) proves the design early, while re-cutting is still cheap -- schedule it last and you validate nothing until reshaping is too late.
|
|
30
30
|
|
|
@@ -38,7 +38,6 @@ Unknowns -> `.gm/mutables.yml` via `mutable-add`, `status: unknown`, witness = `
|
|
|
38
38
|
|
|
39
39
|
## Constraints
|
|
40
40
|
|
|
41
|
-
Gauge every design/code decision against `.gm/constraints.md` (create from bundled default if absent) -- the standing decision-arbiter, checked at every phase.
|
|
42
41
|
|
|
43
42
|
## Dispatch
|
|
44
43
|
|
package/instructions/verify.md
CHANGED
|
@@ -33,11 +33,11 @@ VERIFY is adversarial, never confirmatory: hunt every way EMIT's write breaks, v
|
|
|
33
33
|
|
|
34
34
|
Each class exercised = exec_js/browser dispatch + witness (pass or fix-then-rewitness), same turn, before `transition`. A happy-path-only VERIFY has not verified.
|
|
35
35
|
|
|
36
|
-
##
|
|
36
|
+
## Real-execution witness
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Every claim of correctness is proven by a live `exec_js`/`browser` dispatch witnessing the real output, same turn, real services only (mock-free) -- manual troubleshooting and debugging is the entire verification surface, never a standing test file or suite. Pass = the live witness matches expectation; fail -> `transition` back to EXECUTE. `recursive` classifier = incomplete cover -- snake back, do not narrate past signal.
|
|
39
39
|
|
|
40
|
-
**No
|
|
40
|
+
**No test files, no exceptions.** A `deviation.synthetic-test-file` at VERIFY (new `*.test.*`/`*.spec.*`, a `test/`/`__tests__/` directory, a testing-framework import, or any standing root-level test file found in the diff) blocks `transition` exactly like an unwitnessed mutable -- delete it and replace its assertions with a live `exec_js`/`browser` witness, then re-verify. This is manual legwork, not framework legwork: the adversarial corner-case sweep above is how every class gets exercised, by running the real thing, never by writing a test case that runs it later.
|
|
41
41
|
|
|
42
42
|
## Residual-scan
|
|
43
43
|
|
|
@@ -75,7 +75,6 @@ rs-learn never calls a model; it emits the need, you supply the answer. Skipping
|
|
|
75
75
|
|
|
76
76
|
## Constraints
|
|
77
77
|
|
|
78
|
-
Gauge every design/code decision against `.gm/constraints.md` (create from bundled default if absent) -- the standing decision-arbiter, checked at every phase.
|
|
79
78
|
|
|
80
79
|
## Dispatch
|
|
81
80
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1818",
|
|
4
4
|
"description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.832
|