gm-skill 2.0.2366 → 2.0.2367
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 +2 -2
- package/README.md +1 -1
- package/bin/install.js +13 -21
- package/gm-plugkit/package.json +1 -1
- package/gm.json +1 -1
- package/package.json +1 -1
- package/skills/gm/SKILL.md +11 -9
package/AGENTS.md
CHANGED
|
@@ -36,7 +36,7 @@ The `gm-plugkit` npm identity stays load-bearing as the thin launcher edge only:
|
|
|
36
36
|
|
|
37
37
|
**`bin/install.js` hard-requires agentplug-runner.** It downloads the sha256-verified native runner from `AnEntrypoint/agentplug-bin` for the host platform, and if none is published (or the download/check fails) it fails the install loudly with a clear message rather than leaving the user with no loader. There is no JS-host fallback to silently fall through to anymore.
|
|
38
38
|
|
|
39
|
-
**agentplug-runner auto-updates both the wasm it serves and its own executable, fully autonomously.** Two independent 600s polls; a staged `.new` runner is swapped in by a self-triggered takeover handoff on the next idle tick, so no `bin/install.js` re-run and no restart is ever required. Poll-liveness signal (`daemon-status.json` poll timestamps, never the retired JS host's cache files) and non-semver sideload protection: the recall store (`recall: agentplug daemon.rs self-update handoff protocol`, `recall: agentplug-runner update-poll observability sideload`).
|
|
39
|
+
**agentplug-runner auto-updates both the wasm it serves and its own executable, fully autonomously.** Two independent 600s polls; a staged `.new` runner is swapped in by a self-triggered takeover handoff on the next idle tick, so no `bin/install.js` re-run and no restart is ever required. A dispatch landing mid-handoff can return `dispatch_orphaned` -- expected, bare re-dispatch is the fix, never a different recovery path. The per-project `.status.json` heartbeat (read on every turn boot probe) surfaces this directly: `runner_update_in_progress`/`runner_update_waiting_ms` while a handoff is mid-flight, `last_completed_runner_swap` (`{version, swapped_at_ts}`) as the durable just-swapped record, `loaded_plugin_versions` for the currently-loaded gm/bert/libsql/treesitter versions. Poll-liveness signal (`daemon-status.json` poll timestamps, never the retired JS host's cache files) and non-semver sideload protection: the recall store (`recall: agentplug daemon.rs self-update handoff protocol`, `recall: agentplug-runner update-poll observability sideload`).
|
|
40
40
|
|
|
41
41
|
Wasm host-import link-module rule (`#[link(wasm_import_module="env")]` on every host-import extern block, every dep crate): the recall store (`recall: wasm host-import link-module trap`).
|
|
42
42
|
|
|
@@ -111,7 +111,7 @@ A task that reduces to read/investigate/report, or a change confined to files th
|
|
|
111
111
|
|
|
112
112
|
**Disciplines are isolated knowledge stores**, tracked, `@<name>` sigil-scoped, enabled list at `.gm/disciplines/enabled.txt` (one name per line; `discipline_note.rs::active_policies()` auto-surfaces each enabled discipline's `.gm/disciplines/<name>/policy.md` on every `instruction`). Detail: the recall store (`recall: gm disciplines mechanics`).
|
|
113
113
|
|
|
114
|
-
**Every runtime config surface, indexed**: `.gm/instructions/<key>.md`+`source.json` (prose vendoring, see above), `.gm/instructions/fsm/graph.json` (phase-graph override via `fsm-vendor`), `.gm/instructions/hooks/<name>.js` (jit-hook, fails closed), `.gm/browser-config.json`, `.gm/daemon-project-config.json`, `~/.agentplug/daemon-config.json`, `.gm/disciplines/enabled.txt`, env toggles (`AGENTPLUG_NO_DAEMON`/`CLAUDE_PROJECT_DIR`/`GM_PLUGKIT_SKIP_SELF_STALE_CHECK`). Field-level detail: `.gm/daemon-config-reference.md` and the recall store (`recall: gm runtime config surface field defaults`).
|
|
114
|
+
**Every runtime config surface, indexed**: `.gm/instructions/<key>.md`+`source.json` (prose vendoring, see above), `.gm/instructions/fsm/graph.json` (phase-graph override via `fsm-vendor`), `.gm/instructions/hooks/<name>.js` (jit-hook, fails closed), `.gm/browser-config.json`, `.gm/daemon-project-config.json`, `~/.agentplug/daemon-config.json`, `.gm/disciplines/enabled.txt`, env toggles (`AGENTPLUG_NO_DAEMON`/`CLAUDE_PROJECT_DIR`/`GM_PLUGKIT_SKIP_SELF_STALE_CHECK`/`AGENTPLUG_EXTRA_CA_CERTS`). `AGENTPLUG_EXTRA_CA_CERTS` (falls back to `SSL_CERT_FILE` if unset) points at a PEM file of extra trust anchors layered on top of the compiled-in webpki-roots set -- the escape hatch for a TLS-terminating proxy environment where the default rustls trust store cannot verify the proxy's own certificate; every agentplug-host HTTP call (plugin download, runner self-update poll, the `fetch` verb) shares one `ureq::Agent` built with this store. Field-level detail: `.gm/daemon-config-reference.md` and the recall store (`recall: gm runtime config surface field defaults`).
|
|
115
115
|
|
|
116
116
|
**Nothing fake in source the user runs**: stub/mock/placeholder-return/fixture-only-path/demo-mode-short-circuit/"TODO: implement" forbidden in shipped code. Scaffolds/shims permitted only delegating to real behavior (upstream API, subprocess, disk); check for an existing library before adding a shim. Detection = behavioral: always-succeeds, input-invariant, or type-signature-satisfying short-circuit = stub. Acceptance = real input through real code into real output, witnessed. `Mock*`/`Fake*`/`Stub*` names checked at DECIDE explicitly, not left prose-only (`recall: nevil false-completion mock-network incident`). A row marked `completed` by documenting it (a README "Future Optimizations" section, a design note) instead of implementing it is the same violation -- `prd-resolve` structurally refuses two PRD rows sharing byte-identical `witness_evidence` text (`deviation.prd-resolve-duplicate-witness`), the copy-paste tell of rubber-stamped completion.
|
|
117
117
|
|
package/README.md
CHANGED
|
@@ -153,7 +153,7 @@ No mocks, no fakes, no test files or test suites on disk. Real services, real re
|
|
|
153
153
|
|
|
154
154
|
### memory
|
|
155
155
|
|
|
156
|
-
`.gm/
|
|
156
|
+
`.gm/memories/*.md` (human-readable, one memo per file) is the durable per-project memory store, committed to git so it travels with the project. `gm.db`, the derived vector index built from that corpus, is deliberately untracked -- it grew past GitHub's 50MB recommended limit under normal use, so it is treated as a rebuildable derived cache, not source, the same as any other derived store. Vector embeddings via BGE-small-en-v1.5 (with proper query/passage asymmetry: queries prefixed with `"Represent this sentence for searching relevant passages: "`, passages raw). LRU query-embedding cache (64 entries, 10-min TTL) sits in front to avoid re-embedding repeat queries. `recall` triggers a one-time full-corpus sync the first time a project's memory namespace has never been synced at all (a fresh clone, before `gm.db` exists) -- every read after that first touch stays on the cheap read-only path.
|
|
157
157
|
|
|
158
158
|
## release pipeline
|
|
159
159
|
|
package/bin/install.js
CHANGED
|
@@ -221,14 +221,17 @@ function sha256Hex(buf) {
|
|
|
221
221
|
return crypto.createHash('sha256').update(buf).digest('hex');
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
224
|
+
function compareDottedSemverAscending(a, b) {
|
|
225
|
+
const pa = a.replace(/^v/, '').split('.').map(Number);
|
|
226
|
+
const pb = b.replace(/^v/, '').split('.').map(Number);
|
|
227
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
228
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
229
|
+
if (d !== 0) return d;
|
|
230
|
+
}
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function latestReleaseTagUnauthenticatedViaGitLsRemoteFallback(repo) {
|
|
232
235
|
const out = execFileSync('git', ['ls-remote', '--tags', '--refs', `https://github.com/${repo}.git`], {
|
|
233
236
|
encoding: 'utf8',
|
|
234
237
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
@@ -238,18 +241,7 @@ function latestReleaseTagViaGitLsRemote(repo) {
|
|
|
238
241
|
.filter(Boolean)
|
|
239
242
|
.map(m => m[1]);
|
|
240
243
|
if (tags.length === 0) return null;
|
|
241
|
-
|
|
242
|
-
// dotted version rather than lexically, so v0.1.9 does not sort after
|
|
243
|
-
// v0.1.10.
|
|
244
|
-
tags.sort((a, b) => {
|
|
245
|
-
const pa = a.replace(/^v/, '').split('.').map(Number);
|
|
246
|
-
const pb = b.replace(/^v/, '').split('.').map(Number);
|
|
247
|
-
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
248
|
-
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
249
|
-
if (d !== 0) return d;
|
|
250
|
-
}
|
|
251
|
-
return 0;
|
|
252
|
-
});
|
|
244
|
+
tags.sort(compareDottedSemverAscending);
|
|
253
245
|
return tags[tags.length - 1];
|
|
254
246
|
}
|
|
255
247
|
|
|
@@ -285,7 +277,7 @@ async function downloadAgentplugRunner({ silent } = {}) {
|
|
|
285
277
|
} catch (apiErr) {
|
|
286
278
|
if (!silent) err(`agentplug-runner: GitHub API tag lookup failed (${apiErr && apiErr.message || apiErr}) -- falling back to git ls-remote (works even when the REST API is scope-restricted, since it only needs plain git protocol access)`);
|
|
287
279
|
try {
|
|
288
|
-
tag =
|
|
280
|
+
tag = latestReleaseTagUnauthenticatedViaGitLsRemoteFallback('AnEntrypoint/agentplug-bin');
|
|
289
281
|
} catch (gitErr) {
|
|
290
282
|
if (!silent) err(`agentplug-runner: git ls-remote fallback also failed (${gitErr && gitErr.message || gitErr})`);
|
|
291
283
|
throw apiErr;
|
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2367",
|
|
4
4
|
"description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform wasm, verifies SHA256, and launches agentplug-runner (the native wasm host) as the spool watcher daemon.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/gm.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2367",
|
|
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",
|
package/skills/gm/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ allowed-tools: Skill, Read, Write, AskUserQuestion, Bash(bun *), Bash(npx *), Ba
|
|
|
14
14
|
|
|
15
15
|
**Done is plugkit's word, never yours.** COMPLETE gate is the sole arbiter; not-COMPLETE = a next transition to seek, never a stopping point. Idle mid-chain is a deviation, not a pause. If uncertain what's next, dispatch `phase-status`, read the phase, then keep walking -- "uncertain" is never grounds to stop.
|
|
16
16
|
|
|
17
|
-
**Standing approval: finish everything, max granularity, no budget constraints.** Every session is pre-approved through ALL requested work -- plan, every addition, every discovered/unfinished item. No stop-and-ask, no deferral, no narrowed slice. Effort is unbounded and never gated on cost, context window, session length, or "budget" of any kind -- there is no such thing as running out mid-cycle in a way that authorizes stopping; if a walk is long,
|
|
17
|
+
**Standing approval: finish everything, max granularity, no budget constraints.** Every session is pre-approved through ALL requested work -- plan, every addition, every discovered/unfinished item. No stop-and-ask, no deferral, no narrowed slice. Effort is unbounded and never gated on cost, context window, session length, or "budget" of any kind -- there is no such thing as running out mid-cycle in a way that authorizes stopping; if a walk is long, SPECIFY pushes more rows and PROVE/EMIT keep walking them, session after session if needed, never pausing to ask which subset to attempt first. A prompt offering the user a menu of "compressed/deferred/partial" options gated on running out of budget is itself the deviation -- make the decision yourself (always: proceed with the full scope, never the partial one) and dispatch, don't present it as a choice. PRD rows are in `.gm/prd.yml`; a long-horizon task means MORE rows pushed onto SPECIFY as work is discovered, never fewer turns spent working. **Everything is fixable; `blockedBy: external` is not a solution path.** An apparent external blocker (a crashing tool, a down service, a missing credential, another team's repo) is a row to BUILD PAST -- drive the crashing tool's protocol directly, spawn your own instance, retry/escalate/route around, script the credential path, open the cross-repo change. It is never a resting state that closes a turn, never a way to shrink the row count for large/complex/multi-step work; `blockedBy` may only transiently carry the concrete reach action forward while it is being built. The canonical "external" case this codebase carried for months (the playwriter browser crash) was fixed by driving Chrome's CDP endpoint directly -- proof these blockers are reachable, not terminal.
|
|
18
18
|
|
|
19
19
|
**Naming a token/context count as a reason to change behavior is itself the deviation, full stop -- this applies even while reasoning, not only in the final message.** "32k remaining," "fast-track, no delays," "due to token constraints, finalizing immediately," "given the budget, batch/skip/compress X" -- any of these phrases appearing ANYWHERE in a turn (thinking, prose, or tool-call rationale) is the exact failure this rule exists to name, independent of whether the actions taken afterward look reasonable. There is no quantity of remaining context that changes what you do next; the next action is determined solely by phase + PRD state, identically whether 500k tokens or 500 remain. If you catch yourself estimating/citing a remaining-token number for any reason, that estimate itself is the drift signal -- discard the framing entirely and re-derive the next action from `phase`/`prd_pending_count` alone. This is not a softer version of "no budget constraints" above; it is the same rule restated at the point where it actually breaks -- the rule was violated last not through an explicit stated stop, but through invoking the concept as live context for a decision (batching resolves, skipping witnesses, "fast-tracking" a chain) while still taking real dispatch actions. Real per-row witnessing (one `exec_js`/`browser` dispatch per row, actually reading its output) is mandatory regardless of how many rows remain or how much context is left; a batch `prd-resolve` for N rows justified by "given the budget" is `deviation.prd-resolve-duplicate-witness`-shaped even when each id is individually correct, because the underlying witnesses were never separately produced.
|
|
20
20
|
|
|
@@ -78,13 +78,15 @@ Two more real, code-checked env toggles beyond `GM_PLUGKIT_SKIP_SELF_STALE_CHECK
|
|
|
78
78
|
|
|
79
79
|
**Reboot-loop escape (watcher dies ~30-90s after every boot).** If a fresh `bun x gm-plugkit@latest spool` boots but the watcher dies again shortly after (heartbeat `ts` goes stale >30s with no future `busy_until`, then a new pid appears, repeatedly), the on-disk index has not finished embedding and each boot re-triggers the same synchronous code-index embed that can block the heartbeat past the supervisor's 30s stale limit (`STATUS_STALE_MS`). Confirm by reading `.gm/exec-spool/.watcher.log` for repeated `codeinsight_rebuild` + `partial pass (wall budget) ... deferred_files=N` lines whose `deferred_files` never reaches 0. The embed is genuinely converging in that case: do NOT immediately re-boot on the first stale reading -- read `.watcher.log`, and as long as `deferred_files` is strictly decreasing across `codeinsight_index_partial` events the index is converging (each accepted verb advances it one wall-budget); give it repeated single verbs until a `code_index: done` / `deferred_files=0` line appears, then normal dispatch resumes. Rebooting mid-convergence resets this progress -- the loop is the reboot, not the embed.
|
|
80
80
|
|
|
81
|
+
**A self-update in progress or just-completed is visible directly in `.status.json`, no manual log-reading needed.** `runner_update_in_progress: true` (with `runner_update_waiting_ms`) means a newer runner build is staged and mid-handoff right now -- a dispatch that lands in this window can come back `dispatch_orphaned` (`"was claimed by a daemon that died before answering... a self-update handoff that exited while this dispatch was still running"`); this is expected, not a real failure, and the fix is a bare re-dispatch of the exact same verb once `.status.json`'s `ts` is fresh again, never a different recovery path. `last_completed_runner_swap: {version, swapped_at_ts}` is the durable "a swap just happened" record -- diff it against what you last saw this session to notice a version change mid-turn (served prose, gate behavior, or verb set can differ across a swap) without needing to poll continuously. `loaded_plugin_versions: {gm, bert, libsql, treesitter}` reports each currently-loaded plugin's version string, purely informational -- a plugin content-hash recompile evicts and rebuilds in-process without a process restart, so it never orphans a dispatch the way a runner takeover does, but the version is still worth diffing against a prior read to notice gm's own behavior changed mid-session. None of these three fields change what you dispatch next; they explain WHY a dispatch might have just failed or WHY served behavior just shifted, so it is recognized and handled immediately instead of investigated as a mystery.
|
|
82
|
+
|
|
81
83
|
**Apparent tooling failure is never grounds to ask the user, never a blind restart.** "Spooler not working" / missing response / stale watcher / `gm_plugkit_stale` flagged in a response = your own mechanical self-recovery: honor a future `busy_until` (wait), else boot + re-dispatch. You have boot authority; asking the user to do what a verb can do is a deviation. Staleness of any kind (stale watcher version, stale served prose vs published source) is itself a deviation to resolve immediately, the same turn it's noticed -- `bun x gm-plugkit@latest spool` first, before any other work.
|
|
82
84
|
|
|
83
85
|
`browser` dispatch can surface state as `window.*` and read it via `page.evaluate`. `exec_js` responses include `duration_ms`.
|
|
84
86
|
|
|
85
87
|
**Correct `browser` verb body shape (real spec, not CLI-flag syntax): plain-text prefixed bodies only.** The body is NEVER `-s <id> -e "<script>"` or any other CLI-flag-style string -- that is raw playwriter CLI syntax and does not apply here; the native agentplug-host `browser` handler parses the body itself using these prefixes: `session new` (bare, no script), `session list` (bare, no script), `session close <id>` / `session reset <id>` (id required, own line, no script -- `reset` is the idempotent form, no error if the id wasn't live), `timeout=<ms>\n<expr>`, `url=<target>\n<expr>` (or a bare `https://...` URL alone), `screenshot[=name]\n<expr>`, `dom=<selector>\n<expr>`, or a bare JS expression/statement body with no prefix. Prefixes stack top-to-bottom, e.g. `timeout=90000\nurl=http://host/path?a=1&b=2\nawait page.waitForTimeout(5000);\nreturn {ok:true};`. A `?`-query-string `&` in a URL is safe to include directly (fixed, see below) -- do not URL-encode it defensively. A normal eval dispatch (no `session *` prefix) always targets the dispatch's own sessionId automatically; only `session close <id>`/`session reset <id>` take an explicit id argument, which lets one dispatch close a DIFFERENT session than the one it's nominally running under.
|
|
86
88
|
|
|
87
|
-
Sessions persist a real live Chrome process (reused directly until `session close`/`session reset`, idle-reaped via `.gm/browser-config.json`'s `session_idle_timeout_ms`, or daemon exit). `session list` reports every live session as `{session_id, port, alive, idle_ms}`. Rationale: the recall store (`recall: gm SKILL.md browser session persistence rationale`).
|
|
89
|
+
Sessions persist a real live Chrome process (reused directly until `session close`/`session reset`, idle-reaped via `.gm/browser-config.json`'s `session_idle_timeout_ms`, or daemon exit). Reconnection targets the exact same CDP page (a persisted `target_id`, not "whichever page target CDP's `/json/list` happens to return first") -- so a session stays on the correct tab even when Chrome has more than one open. `session list` reports every live session as `{session_id, port, alive, idle_ms}`. Rationale: the recall store (`recall: gm SKILL.md browser session persistence rationale`).
|
|
88
90
|
|
|
89
91
|
**`.gm/browser-config.json` is optional and per-project** -- `BrowserConfig::load` re-reads it every dispatch, and any missing file or field falls back to its default, so an unconfigured project behaves identically. Six tunable fields with their defaults: the recall store (`recall: browser-config.json field reference`).
|
|
90
92
|
|
|
@@ -102,19 +104,19 @@ If similar symptoms recur (stub-like responses, silent truncation, debug fields
|
|
|
102
104
|
|
|
103
105
|
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.
|
|
104
106
|
|
|
105
|
-
**Apply "every possible" to every noun, at
|
|
107
|
+
**Apply "every possible" to every noun, at SPECIFY and every re-entry to it.** SPECIFY is exhaustive, not minimal: every noun the request touches gets every possible task/validation/mutable/corner-case/caveat/failure-mode/empty-overflow-reentry-degenerate state as its own PRD row. A single-digit PRD count on a non-trivial or long-horizon request means it stopped early -- re-orient, re-enumerate, push more rows. Long-horizon prompts routinely produce high-tens-to-hundreds of rows; density at SPECIFY is the only protection against silently stopping with work undone. Everything is fixable: a row that looks unreachable is a row to build a way into (drive the crashing tool directly, spawn your own instance, open the cross-repo change), never a `blockedBy: external` resting state and never a way to shrink the row count for tedious/large/multi-step work.
|
|
106
108
|
|
|
107
|
-
**Every
|
|
109
|
+
**Every SPECIFY pass also asks: what architectural change makes this practical and low-maintenance going forward, not just correct right now?** For each row, before accepting the literal ask as the whole scope: is there a structural change (removing an obsolete mechanism, consolidating duplicated logic, replacing a bespoke reimplementation with a maintained one, fixing a wrong abstraction at its root instead of patching around it) that would make this and future similar work cheaper, not just this one instance? If yes, that becomes its own PRD row alongside the literal ask -- never silently skipped as "out of scope" or "nice to have." A plan that only satisfies the literal request while leaving an obvious maintenance burden in place under-covers by the same standard as a plan that misses a corner case.
|
|
108
110
|
|
|
109
|
-
**
|
|
111
|
+
**PROVE resolves all mutables before EMIT, discovers more, resolves those too, rearchitects immediately on in-spirit discovery.** Any architectural improvement discovered mid-PROVE or mid-EMIT is an immediate `transition to=SPECIFY`, re-`prd-add` the affected row with its existing id (re-scope, never remove-and-re-add) -- maximal-effort correctness over preservation-for-its-own-sake, no deferral, no "note for later."
|
|
110
112
|
|
|
111
|
-
**`prd-resolve` is bound by the false-completion rule in every phase,
|
|
113
|
+
**`prd-resolve` is bound by the false-completion rule in every phase, SPECIFY included -- not only at DECIDE.** A row resolved on a hedge ("deferred", "pending next session", "awaits recovery") is undone work labeled done -- forbidden, the same class as a mock standing in for real code. The served SPECIFY/DECIDE `instruction` prose carries the full hedge taxonomy, the `blockedBy: [external, ...]` handling for genuine outside-session blockers, and the recurring-blocker-becomes-its-own-row rule; dispatch `instruction` for it rather than re-deriving it here.
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
DECIDE is adversarial, never confirmatory: 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 SPECIFY, add unrelated PRD rows, or narrate the blocker instead of clearing it.
|
|
114
116
|
|
|
115
117
|
**Every residual is triaged this turn; "pre-existing" is not a stop excuse.** Non-empty `git status --porcelain`: every entry is yours now -- commit (real work), add to managed gitignore (transient runtime files), or revert (stale junk). PRD entries live in `.gm/prd.yml`. `.gm/disciplines/` and new memorize-fire JSON are tracked. `.gm/witness/` and staleness markers go in managed gitignore.
|
|
116
118
|
|
|
117
|
-
**Noticing is a planning event, every phase, not just
|
|
119
|
+
**Noticing is a planning event, every phase, not just SPECIFY.** Anything observed mid-walk -- outstanding work, an unfinished surface, an improvable shape, drift from these rules -- is `prd-add` this turn; response-body-only observations evaporate at turn end. Density grows along the whole walk, not just at SPECIFY; exiting DECIDE with the same row-count SPECIFY started with is lost discoveries, not a clean run.
|
|
118
120
|
|
|
119
121
|
`git push` only when `git status --porcelain` is empty. Dirty tree blocks the DECIDE -> COMPLETE gate. Prefer `git_push` verb over Bash git commands; git via Bash records as `deviation.bash-git-bypass`.
|
|
120
122
|
|
|
@@ -122,7 +124,7 @@ Phase transitions (the built-in default graph, not an exhaustive list of legal p
|
|
|
122
124
|
|
|
123
125
|
CI/CD validation in DECIDE phase: After push succeeds, watch the triggered workflow. When pipeline goes green, dispatch `fs_write {path:".gm/exec-spool/.ci-validated", content:"{\"head_sha\":\"<current commit SHA>\"}"}` to mark validation complete -- the marker's file CONTENT is the JSON string `{"head_sha":"<SHA>"}`, passed under the `content` field the fs_write handler reads (not a `body`/`head_sha` object, which the handler ignores). `.ci-validated`'s head_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."
|
|
124
126
|
|
|
125
|
-
Memory via `memorize-fire` dispatch
|
|
127
|
+
Memory via `memorize-fire` dispatch writes both `.gm/memories/<key>.md` (tracked, durable) and a vector row in `.gm/gm.db` (untracked, derived, rebuilt on first touch if missing), 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`.
|
|
126
128
|
|
|
127
129
|
`auto_recall` attaches to `instruction` responses on turn entry. `memorize-prune {key}` or `memorize-prune {query}` deletes or reviews memory entries.
|
|
128
130
|
|