gm-skill 2.0.1483 → 2.0.1485

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
@@ -88,7 +88,7 @@ Every possible skill's `allowed-tools:` frontmatter is reduced to `Skill, Read,
88
88
 
89
89
  **Nothing fake in source the user runs**: every possible stub, mock, placeholder return, fixture-only path, demo-mode short-circuit, and "TODO: implement" body is forbidden in shipped code. Scaffolds and shims are permitted only when they delegate to real behavior (real upstream API, real subprocess, real disk). Before adding a shim, check whether a published library or tool already provides that surface, maintaining a local reimplementation of an upstream solution drifts and ages. Detection is behavioral, not by keyword: code that always succeeds, returns the same value regardless of input, or short-circuits a real call to satisfy a type signature is a stub. Acceptance is real input through real code into real output, witnessed; every possible degradation from that leaves the mutable open.
90
90
 
91
- **Spool dispatch gates**: `lib/spool-dispatch.js` implements marker-file gate logic that controls tool use, writes, and git operations. `checkDispatchGates(sessionId, operation)` reads marker files (`.gm/prd.yml`, `.gm/mutables.yml`, `.gm/needs-gm`, `.gm/gm-fired-<id>`) and returns `{allowed: bool, reason: string}`. Gates are checked at the CLI/bootstrap layer before tools execute. Tool denials via gate checks report the reason text to the model so it can adjust behavior (e.g., resolve mutables before retrying). Gate denials never mutate command arguments, they surface policy as imperative instruction via reason string.
91
+ **Spool dispatch gates**: `lib/spool-dispatch.js::checkDispatchGates(sessionId, operation)` reads `.gm/` marker files and returns `{allowed, reason}`; denials surface the reason to the model as imperative instruction, never mutate args. Marker list + per-marker semantics in rs-learn (`recall: spool dispatch gates marker files`).
92
92
 
93
93
  **Done is plugkit's pronouncement, never the agent's claim**: the chain is COMPLETE only when `transition to=COMPLETE` returns COMPLETE phase and plugkit's on-disk state file reflects it. "I think we're done" is not done. "The user seems satisfied" is not done. The COMPLETE gate (gates.rs) is the single arbiter, it refuses on PRD-open, mutables-unresolved, dirty worktree, or missing residual-scan marker. The agent's job is to drive the chain into a state where the gate allows, then dispatch the verb, then read the response. Every possible alternative is narration.
94
94
 
@@ -116,11 +116,11 @@ Every possible skill's `allowed-tools:` frontmatter is reduced to `Skill, Read,
116
116
 
117
117
  **host_exec_js is synchronous**: pass a real per-call `timeoutMs` (zero/missing is a hard error); long subprocesses block the watcher; no async/background exec under wasm. Mechanism detail in rs-learn (`recall: host_exec_js synchronous`).
118
118
 
119
- **Sync-before-emit (codeinsight + search)**: outputs must come from freshly-completed indices. Cache serves only on digest match (mtime sum + git HEAD + dirty-tree marker). Default invocation runs fresh. `--read-cache` permitted only when `.codeinsight.digest` matches; mismatch auto-refreshes. rs-search runs scan + embed + sweep before first result; emits `[index fully synced: …]`. Unverified-index emit = stale ground truth.
119
+ **Sync-before-emit (codeinsight + search)**: codeinsight/search output must come from a freshly-synced index this invocation (cache serves only on digest match); unverified-index emit = stale ground truth. Digest/cache mechanics in rs-learn (`recall: sync-before-emit codeinsight search`).
120
120
 
121
- **Auto-recall on turn entry**: the `instruction` verb attaches an `auto_recall` pack `{query, hits, fired_at, turn_entry:true}` to its response on the first dispatch after a >30s idle gap or session-start. Query is derived from `.gm/last-prompt.txt` / `.gm/turn-state.json`; hits are the top recall results plugkit pulled before serving the instruction. Wasm-side `wasm_hooks::prompt_submit` exports exist for legacy hook-host integration but the current spool watcher does not invoke them, orientation comes through the instruction verb's response pack instead.
121
+ **Auto-recall on turn entry**: the `instruction` verb attaches an `auto_recall` pack to its response on the first dispatch after a >30s idle gap or session-start; orientation comes through that pack, not legacy hooks. Mechanism detail in rs-learn (`recall: auto-recall on turn entry`).
122
122
 
123
- **Skill SKILL.md frontmatter `allowed-tools:` is harness-enforced**: If a skill omits `allowed-tools` or does not list `Skill`, the model loses the ability to invoke downstream skills that turn. The shipped surface is a single skill (`gm-skill`); this rule governs every possible future skill that participates in a chain.
123
+ **Skill SKILL.md frontmatter `allowed-tools:` is harness-enforced**: a skill must list `Skill` (and `Read`/`Write`, Write only for spool dispatch) or it loses downstream-skill invocation that turn. Detail in rs-learn (`recall: SKILL.md frontmatter allowed-tools`).
124
124
 
125
125
  **rs-learn observability**: every learning-pipeline state change emits a structured `evt:{event,sess,ts,...}` line into `.gm/exec-spool/.watcher.log` + gm-log; recall replies carry `mode`/`namespace`/`derived_query`/per-hit `score`; gmsniff/ccsniff expose the taxonomy. Learning quality is observable, not a black box. Full event taxonomy + flag list in rs-learn (`recall: rs-learn observability taxonomy`).
126
126
 
@@ -130,7 +130,7 @@ Every possible skill's `allowed-tools:` frontmatter is reduced to `Skill, Read,
130
130
 
131
131
  ## Cascade pipeline
132
132
 
133
- Push to every possible rs-* sibling repo (rs-exec, rs-search, rs-codeinsight, rs-learn) triggers `cascade.yml` which uses `gh workflow run` to invoke rs-plugkit's `release.yml` via PUBLISHER_TOKEN. rs-plugkit cargo-pulls the latest sibling crate revs at build time and emits a single `plugkit.wasm` artifact (no per-sibling npm wasm packages, that pattern was retired). Publishes to `plugkit-bin` Releases + npm `plugkit-wasm`, then auto-bumps `gm.json::plugkitVersion` and `bin/plugkit.wasm.sha256` in this repo. The version bump commit on this repo triggers `publish.yml`, which (a) `npm publish`es `gm-skill` from the repo root, (b) `npm publish`es `gm-plugkit` from `gm-plugkit/`, and (c) force-pushes `skills/gm-skill/SKILL.md` to the `AnEntrypoint/gm-skill` back-compat mirror repo.
133
+ Push to any rs-* sibling triggers `cascade.yml` rs-plugkit `release.yml` single `plugkit.wasm` (npm `plugkit-wasm` + `plugkit-bin` Releases) auto-bump `gm.json::plugkitVersion` `publish.yml` ships gm-skill + gm-plugkit + the SKILL.md mirror. Full step sequence + PUBLISHER_TOKEN setup in rs-learn (`recall: cascade pipeline`).
134
134
 
135
135
  Three npm packages publish from this repo: `gm-skill` (the skill harness), `gm-plugkit` (bootstrap + watcher), `plugkit-wasm` (wasm binary). publish.yml + the rs-plugkit cascade ships all three on every version-bump commit. The legacy 15 downstream repos (gm-cc, gm-gc, gm-oc, gm-kilo, gm-codex, gm-qwen, gm-copilot-cli, gm-hermes, gm-thebird, gm-vscode, gm-cursor, gm-zed, gm-jetbrains, gm-antigravity, gm-windsurf) are archived on GitHub, no further releases, no orphan-commit publish step.
136
136
 
@@ -144,8 +144,6 @@ Three npm packages publish from this repo: `gm-skill` (the skill harness), `gm-p
144
144
 
145
145
  **To update every possible thing**: push to the relevant repo. No manual version bumps, no local cargo builds. Never run `cargo update` or `cargo build` locally, push and let CI build.
146
146
 
147
- **PUBLISHER_TOKEN required** in `rs-exec`, `rs-codeinsight`, `rs-search` for cascade.yml to trigger rs-plugkit. Set with: `gh secret set PUBLISHER_TOKEN --repo AnEntrypoint/<repo>`.
148
-
149
147
  **Timeout enforcement**: every possible `exec_js` dispatch carries a positive `timeoutMs`. The host treats missing or zero as a hard error.
150
148
 
151
149
  ## Spool-dispatch architecture replaces hooks
@@ -154,7 +152,7 @@ Orchestration state is tracked via marker files in `.gm/` instead of hook events
154
152
 
155
153
  **Marker files**: `.gm/prd.yml` (existence triggers needs-gm gate), `.gm/mutables.yml` (every possible unresolved entry blocks Write/Edit/git), `.gm/needs-gm` (written by bootstrap, read by dispatcher), `.gm/gm-fired-<sessionId>` (written by gm skill/agent, cleared at turn start), `.gm/residual-check-fired` (ensures one-shot residual-scan per stop window).
156
154
 
157
- **Gate enforcement**: CLI layer (plugkit, rs-exec, downstream platforms) calls `checkDispatchGates()` before tool execution. On denial, reason text surfaces to the model. Bootstrap (lib/skill-bootstrap.js) handles daemon initialization and marker setup. Marker-driven dispatch replaces hook event pump entirely, no session event callbacks needed.
155
+ **Gate enforcement**: the CLI layer calls `checkDispatchGates()` before tool execution; marker-driven dispatch replaces the hook event pump entirely. Detail in rs-learn (`recall: gate enforcement layer`).
158
156
 
159
157
  **gm-skill tool-use sequencing**: Invoking `Skill(skill="gm-skill")` writes `.gm/gm-fired-<sessionId>` to clear the needs-gm gate. The marker is cleared at turn start to reset the gate. There is one shipped skill; no subagent variant exists.
160
158
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1483",
3
+ "version": "2.0.1485",
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": {
@@ -2757,6 +2757,16 @@ async function runSpoolWatcher(instance, spoolDir) {
2757
2757
  const relPath = path.relative(inDir, filePath);
2758
2758
  const dir = path.dirname(relPath);
2759
2759
  const verb = dir === '.' ? path.basename(filePath, path.extname(filePath)) : dir;
2760
+ // Defense-in-depth beyond walkDir's dot-dir skip: a real verb is a single clean
2761
+ // segment (e.g. instruction, prd-resolve). A derived verb containing a path
2762
+ // separator or a dot-segment means the file lives under a stray nested spool
2763
+ // (in/prd-resolve/.gm/exec-spool/…); dispatching it builds a bogus verb+outName
2764
+ // and ENOENT-storms every tick. Skip + unmark so it never re-enters the loop.
2765
+ if (/[\\/]/.test(verb) || verb.split(/[\\/]/).some(seg => seg.startsWith('.'))) {
2766
+ try { logEvent('plugkit', 'spool.skip-nested-verb', { rel: relPath, derived_verb: verb }); } catch (_) {}
2767
+ unmarkProcessed(key);
2768
+ return;
2769
+ }
2760
2770
  let body = content.trim() || '{}';
2761
2771
  const taskBase = path.basename(filePath, path.extname(filePath));
2762
2772
 
@@ -2876,18 +2886,24 @@ async function runSpoolWatcher(instance, spoolDir) {
2876
2886
  }
2877
2887
  };
2878
2888
 
2879
- function walkDir(dir) {
2889
+ function walkDir(dir, depth = 0) {
2880
2890
  const files = [];
2881
2891
  try {
2882
2892
  for (const entry of fs.readdirSync(dir)) {
2883
2893
  if (/\.tmp\.\d+(\.|$)/.test(entry)) continue;
2894
+ // The verb tree is in/<verb>/[<sub>/]<N>.<ext> — at most two levels deep. A
2895
+ // dot-prefixed dir (e.g. a stray nested .gm/exec-spool/ created by a misfire)
2896
+ // is never a verb dir; recursing into it derives a bogus verb like
2897
+ // `prd-resolve\.gm\exec-spool` and dispatch-errors on every tick forever.
2898
+ // Skip dot-dirs and cap depth so a spool-inside-spool cannot wedge the watcher.
2899
+ if (entry.startsWith('.')) continue;
2884
2900
  const fullPath = path.join(dir, entry);
2885
2901
  let stat;
2886
2902
  try { stat = fs.statSync(fullPath); } catch (_) { continue; }
2887
2903
  if (stat.isFile()) {
2888
2904
  files.push(fullPath);
2889
- } else if (stat.isDirectory()) {
2890
- files.push(...walkDir(fullPath));
2905
+ } else if (stat.isDirectory() && depth < 2) {
2906
+ files.push(...walkDir(fullPath, depth + 1));
2891
2907
  }
2892
2908
  }
2893
2909
  } catch (e) {
@@ -3329,6 +3345,10 @@ async function runSpoolWatcher(instance, spoolDir) {
3329
3345
  watch(inDir, { recursive: true }, (eventType, filename) => {
3330
3346
  if (!filename) return;
3331
3347
  if (/\.tmp\.\d+(\.|$)/.test(filename)) return;
3348
+ // Skip any path with a dot-prefixed segment (e.g. a stray nested
3349
+ // prd-resolve/.gm/exec-spool/…): it is not a real verb dispatch and walking it
3350
+ // derives a bogus verb that dispatch-errors on every tick. Matches walkDir's guard.
3351
+ if (filename.split(/[\\/]/).some(seg => seg.startsWith('.'))) return;
3332
3352
  const fullPath = path.join(inDir, filename);
3333
3353
  markActivity('watch');
3334
3354
 
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1483",
3
+ "version": "2.0.1485",
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1483",
3
+ "version": "2.0.1485",
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",