gm-skill 2.0.1576 → 2.0.1577

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/AGENTS.md CHANGED
@@ -148,13 +148,13 @@ Push to any rs-* sibling triggers `cascade.yml` -> rs-plugkit `release.yml` -> s
148
148
 
149
149
  Orchestration state is tracked via `.gm/` marker files, not hook events; the CLI layer calls `checkDispatchGates()` before tool execution to gate Write/Edit/git. Marker set (`prd.yml, mutables.yml, needs-gm, gm-fired-<sessionId>, residual-check-fired`) + SpoolDispatcher mechanism in rs-learn (`recall: gate enforcement layer`, `recall: spool dispatch gates marker files`).
150
150
 
151
- **gm-skill tool-use sequencing**: `Skill(skill="gm-skill")` writes `.gm/gm-fired-<sessionId>` to clear the needs-gm gate (cleared at turn start to reset it). One shipped skill, no subagent variant.
151
+ **gm-skill tool-use sequencing**: `Skill(skill="gm-skill")` clears the needs-gm gate. One shipped skill, no subagent variant. Marker mechanics in rs-learn (`recall: gm-skill tool-use sequencing mechanics`).
152
152
 
153
153
  **The skill is the driver, not a post-hoc witness**: when a request carries the standing instruction to use gm-skill (every `/loop` fire, any prompt naming `/gm-skill`), the FIRST working action is `Skill(skill="gm-skill")`, and the skill prose drives the chain PLAN->COMPLETE. Dispatching spool verbs directly without first entering the skill executes the work outside the skill the user asked to drive it; entering only at the end to confirm terminal state does NOT satisfy the instruction. The boot probe (`cat .gm/exec-spool/.status.json` ...) is prescribed by the skill and may precede invocation; everything that mutates state happens inside the skill-driven session.
154
154
 
155
155
  **Dead-watcher recovery uses `bun x gm-plugkit@latest spool`, never direct-node boot** (mechanism in rs-learn: `recall: dead-watcher recovery bun x not direct-node`).
156
156
 
157
- **The first verb after a genuine multi-minute IDLE is `instruction`, to reset the long-gap clock**: the gate fires on genuine idle only (>300s since the last instruction AND >300s since any verb), so active back-to-back work verbs keep the chain alive without an interleaved `instruction` -- do not inject defensive instruction dispatches between active work. A true wait (version download, overnight, long external CI watch) trips it, and the first verb back is `instruction`. When the wait is self-inflicted and predictable (a blocking `TaskOutput`/`gh run watch`), dispatch `instruction` immediately BEFORE entering the wait, not only after. "Work verbs"/"any verb" here means SPOOL dispatches -- platform `Bash`/`Read`/`Edit`/`Grep` do NOT reset the clock, so a long investigation run purely in them (the audit `gmsniff`/`ccsniff` sweep + source reading/editing exceeding 300s) trips a false `mid-chain-stall` even while actively working; interleave a `prd-add` (convert each finding as it emerges per density-grows-along-the-walk) or an `instruction` to keep the clock warm. Mechanism in rs-learn (`recall: first verb after multi-minute wait instruction long-gap`).
157
+ **The first verb after a genuine multi-minute IDLE is `instruction`, to reset the long-gap clock**: gate fires when >300s since last instruction AND >300s since any SPOOL verb. Platform `Bash`/`Read`/`Edit`/`Grep` do NOT reset the clock -- a long investigation run in them trips a false stall; interleave `prd-add` or `instruction` to keep warm. For a predictable blocking wait (`TaskOutput`/`gh run watch`), dispatch `instruction` BEFORE entering the wait. Detail + platform-tool exception in rs-learn (`recall: first verb after multi-minute wait instruction long-gap`).
158
158
 
159
159
  **A stop-hook firing on a terminal chain does not authorize re-polling**: when a stop-hook fires while already at `phase=COMPLETE` AND `prd_pending_count=0`, re-dispatching `instruction`/`phase-status` to "re-confirm" is a deviation (`deviation.complete-chain-poll`, `instructions/mod.rs`). Two admissible responses: (a) a prose-only turn (COMPLETE is in hand), or (b) genuinely new planned work opened with a FRESH `{"prompt":...}` body (resets phase to PLAN, driven through the skill). Repeatedly answering the same hook is a loop; state the terminal facts once and stop, or open new work.
160
160
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1576",
3
+ "version": "2.0.1577",
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": {
@@ -2139,8 +2139,8 @@ async function runSpoolWatcher(instance, spoolDir) {
2139
2139
  }
2140
2140
  function lockBody() { return `${process.pid}|${Date.now()}|${_ownWrapperSha12}`; }
2141
2141
  function acquireLock() {
2142
- try {
2143
- if (fs.existsSync(LOCK_PATH)) {
2142
+ function checkExistingHolder() {
2143
+ try {
2144
2144
  const content = fs.readFileSync(LOCK_PATH, 'utf-8').trim();
2145
2145
  const parts = content.split('|');
2146
2146
  const pidStr = parts[0];
@@ -2164,6 +2164,7 @@ async function runSpoolWatcher(instance, spoolDir) {
2164
2164
  }));
2165
2165
  } catch (_) {}
2166
2166
  try { process.kill(parseInt(pidStr, 10), 'SIGTERM'); } catch (_) {}
2167
+ return 'takeover';
2167
2168
  } else {
2168
2169
  const msg = JSON.stringify({ ok: false, reason: 'another-watcher-active', pid: pidStr, age_ms: age });
2169
2170
  console.error(`[plugkit-wasm] ${msg}; refusing to start`);
@@ -2181,11 +2182,32 @@ async function runSpoolWatcher(instance, spoolDir) {
2181
2182
  } else if (!holderAlive) {
2182
2183
  console.error(`[plugkit-wasm] stale lock (holder pid=${pidStr} dead, age=${age}ms); taking over`);
2183
2184
  try { logEvent('plugkit', 'watcher.lock-pid-dead-takeover', { stale_pid: pidStr, lock_age_ms: age }); } catch (_) {}
2185
+ return 'takeover';
2184
2186
  } else {
2185
2187
  console.error(`[plugkit-wasm] stale lock (age=${age}ms); taking over`);
2188
+ return 'takeover';
2186
2189
  }
2190
+ } catch (_) {
2191
+ return 'takeover';
2192
+ }
2193
+ }
2194
+ try {
2195
+ let fd;
2196
+ try {
2197
+ fd = fs.openSync(LOCK_PATH, 'wx');
2198
+ } catch (e) {
2199
+ if (e.code !== 'EEXIST') throw e;
2200
+ const action = checkExistingHolder();
2201
+ if (action !== 'takeover') return;
2202
+ try { fs.unlinkSync(LOCK_PATH); } catch (_) {}
2203
+ fd = fs.openSync(LOCK_PATH, 'wx');
2204
+ }
2205
+ try {
2206
+ const body = Buffer.from(lockBody(), 'utf-8');
2207
+ fs.writeSync(fd, body);
2208
+ } finally {
2209
+ fs.closeSync(fd);
2187
2210
  }
2188
- fs.writeFileSync(LOCK_PATH, lockBody());
2189
2211
  } catch (e) {
2190
2212
  console.error(`[plugkit-wasm] lock acquire failed: ${e.message}`);
2191
2213
  process.exit(1);
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1576",
3
+ "version": "2.0.1577",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/lib/spool.js CHANGED
@@ -49,7 +49,7 @@ function writeSpool(body, lang = 'nodejs', options = {}) {
49
49
  fs.mkdirSync(inDir, { recursive: true });
50
50
 
51
51
  const sessionId = options.sessionId || process.env.CLAUDE_SESSION_ID;
52
- const code = sessionId ? `const SESSION_ID = '${sessionId}';\n${body}` : body;
52
+ const code = sessionId ? `const SESSION_ID = ${JSON.stringify(sessionId)};\n${body}` : body;
53
53
 
54
54
  fs.writeFileSync(inFile, code, 'utf8');
55
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1576",
3
+ "version": "2.0.1577",
4
4
  "description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",