greprag 5.83.2 → 5.85.0

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.
Files changed (35) hide show
  1. package/dist/commands/agent-doctrine-file.js +5 -128
  2. package/dist/commands/agents.js +125 -846
  3. package/dist/commands/app-model.js +1 -1
  4. package/dist/commands/delivery-reminder.js +1 -1
  5. package/dist/commands/discord.js +12 -61
  6. package/dist/commands/inbox-watch.js +0 -13
  7. package/dist/commands/init.js +17 -29
  8. package/dist/commands/load.js +15 -35
  9. package/dist/commands/parity.js +1 -1
  10. package/dist/commands/reminder-registry.js +1 -8
  11. package/dist/commands/session.js +2 -2
  12. package/dist/commands/skill-mirror-reminder.js +1 -19
  13. package/dist/commands/skill.js +236 -448
  14. package/dist/hook.js +31 -54
  15. package/dist/index.js +19 -152
  16. package/dist/native-adapter-refresh.js +15 -18
  17. package/dist/native-skill-mirror.js +11 -89
  18. package/dist/opencode-plugin.bundle.js +82 -282
  19. package/dist/reminder-injector.js +1 -1
  20. package/dist/skill-activation-manifest.js +14 -37
  21. package/dist/skill-git-ingest.js +297 -0
  22. package/dist/skill-landing.js +7 -173
  23. package/dist/skill-mirror-client.js +4 -251
  24. package/dist/skill-staleness.js +3 -6
  25. package/package.json +1 -1
  26. package/skill/commander/SKILL.md +10 -12
  27. package/skill/greprag/SKILL.md +1 -1
  28. package/skill/greprag/docs/discord-handoff.md +6 -30
  29. package/skill/mechanic/docs/skill-fix-conventions.md +1 -1
  30. package/skill/templates/chip-spawn.md +6 -2
  31. package/skill/templates/delivery.md +16 -0
  32. package/skill/templates/os.md +1 -1
  33. package/skill/templates/prompt-audit.md +26 -49
  34. package/skill/templates/skill-change.md +15 -11
  35. package/dist/commands/sms.js +0 -215
@@ -1,15 +1,8 @@
1
1
  "use strict";
2
- /** Skill-mirror client — the Tier-3 mirror's upload half
3
- * (docs/load-system.md §Tier 3). Runs inside the Stop hook: a skill-load turn
4
- * triggers `maybeMirrorSkill`, which content-hashes the skill's markdown and
5
- * uploads ONLY on change. Follower semantics — files canonical, the mirror
6
- * shadows "last used state." Best-effort end to end: a mirror failure never
7
- * touches the turn.
8
- *
9
- * Local freshness state (`~/.greprag/state/skill-mirror.json`) remembers the
10
- * last-mirrored hash per skill so an unchanged skill costs one hash pass and
11
- * ZERO network. Markdown only, v1 — SKILL.md + docs/**\/*.md; script-bearing
12
- * assets stay file-bound (see the spec's named gaps). */
2
+ /** Skill package reader: the markdown a mirrored skill carries (SKILL.md +
3
+ * docs/**\/*.md) and its content hash. Used to verify an export and to send a
4
+ * loadout. The mirror itself is written only by git ingestion
5
+ * (skill-git-ingest.ts, adr/skill-ownership-git.md). */
13
6
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
7
  if (k2 === undefined) k2 = k;
15
8
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -46,16 +39,8 @@ var __importStar = (this && this.__importStar) || (function () {
46
39
  Object.defineProperty(exports, "__esModule", { value: true });
47
40
  exports.collectSkillFiles = collectSkillFiles;
48
41
  exports.computeMirrorHash = computeMirrorHash;
49
- exports.dryRunNativeSkillAdoption = dryRunNativeSkillAdoption;
50
- exports.syncNativeSkillAdoptions = syncNativeSkillAdoptions;
51
- exports.readMirrorState = readMirrorState;
52
- exports.writeMirrorState = writeMirrorState;
53
- exports.maybeMirrorSkill = maybeMirrorSkill;
54
42
  const fs = __importStar(require("fs"));
55
43
  const path = __importStar(require("path"));
56
- const skill_landing_1 = require("./skill-landing");
57
- const native_skill_adoption_1 = require("./native-skill-adoption");
58
- const native_skill_mirror_1 = require("./native-skill-mirror");
59
44
  const skill_mirror_files_1 = require("./skill-mirror-files");
60
45
  /** Mirrors the server's caps (SKILL_MIRROR_* in @greprag/core — the CLI is
61
46
  * core-free, so the numbers are restated; the server re-validates anyway). */
@@ -116,235 +101,3 @@ function collectSkillFiles(skillDir) {
116
101
  function computeMirrorHash(files) {
117
102
  return (0, skill_mirror_files_1.computeSkillFileHash)(files);
118
103
  }
119
- function dryRunNativeSkillAdoption(params) {
120
- return (0, native_skill_adoption_1.planNativeSkillAdoption)({ ...params, collectFiles: collectSkillFiles });
121
- }
122
- function adoptedSkillId(candidate) {
123
- return candidate.scope === 'project'
124
- ? `project:${candidate.projectId}:${candidate.skillName}`
125
- : `global:${candidate.skillName}`;
126
- }
127
- async function responseJson(response) {
128
- try {
129
- return await response.json();
130
- }
131
- catch {
132
- return {};
133
- }
134
- }
135
- async function ensureNativeAdoptionOriginalSnapshot(params) {
136
- const skillId = adoptedSkillId(params.candidate);
137
- const snapshotPath = `/v1/skillmirror/${encodeURIComponent(params.candidate.skillName)}/snapshots/original`;
138
- const body = {
139
- scope: params.candidate.scope,
140
- projectId: params.candidate.projectId,
141
- skillId,
142
- source: 'disk',
143
- sourceRef: path.join(params.candidate.root, params.candidate.skillName),
144
- reason: 'captured native skill package before GrepRAG adapter adoption',
145
- };
146
- const created = await fetch(`${params.apiUrl}${snapshotPath}`, {
147
- method: 'POST',
148
- headers: { 'Authorization': `Bearer ${params.apiKey}`, 'Content-Type': 'application/json' },
149
- body: JSON.stringify(body),
150
- });
151
- if (created.ok)
152
- return true;
153
- if (created.status !== 409)
154
- return false;
155
- const query = new URLSearchParams({
156
- scope: params.candidate.scope,
157
- skillId,
158
- ...(params.candidate.projectId ? { projectId: params.candidate.projectId } : {}),
159
- });
160
- const existing = await fetch(`${params.apiUrl}${snapshotPath}?${query.toString()}`, {
161
- method: 'GET',
162
- headers: { 'Authorization': `Bearer ${params.apiKey}` },
163
- });
164
- if (!existing.ok)
165
- return false;
166
- const data = await responseJson(existing);
167
- const snapshot = data.snapshot;
168
- return snapshot?.contentHash === params.candidate.sourceHash;
169
- }
170
- async function syncNativeSkillAdoptions(params) {
171
- const plan = dryRunNativeSkillAdoption(params);
172
- const result = {
173
- plan,
174
- adopted: 0,
175
- blocked: plan.blocked.length,
176
- skipped: plan.skipped.length,
177
- };
178
- if (params.dryRun)
179
- return result;
180
- for (const candidate of plan.candidates) {
181
- const body = {
182
- files: candidate.files,
183
- contentHash: candidate.sourceHash,
184
- scope: candidate.scope,
185
- projectId: candidate.projectId,
186
- projectName: candidate.projectName,
187
- };
188
- const response = await fetch(`${params.apiUrl}/v1/skillmirror/${encodeURIComponent(candidate.skillName)}`, {
189
- method: 'POST',
190
- headers: { 'Authorization': `Bearer ${params.apiKey}`, 'Content-Type': 'application/json' },
191
- body: JSON.stringify(body),
192
- });
193
- if (!response.ok) {
194
- result.blocked++;
195
- continue;
196
- }
197
- const snapshotted = await ensureNativeAdoptionOriginalSnapshot({
198
- apiUrl: params.apiUrl,
199
- apiKey: params.apiKey,
200
- candidate,
201
- });
202
- if (!snapshotted) {
203
- result.blocked++;
204
- continue;
205
- }
206
- const installed = (0, native_skill_adoption_1.installAdoptedNativeSkill)({
207
- platform: params.platform,
208
- root: candidate.root,
209
- expectedSourceHash: candidate.sourceHash,
210
- collectFiles: collectSkillFiles,
211
- skill: {
212
- skillName: candidate.skillName,
213
- skillId: adoptedSkillId(candidate),
214
- scope: candidate.scope,
215
- projectId: candidate.projectId,
216
- projectName: candidate.projectName,
217
- files: candidate.files,
218
- },
219
- });
220
- if (installed === 'installed')
221
- result.adopted++;
222
- else if (installed === 'blocked')
223
- result.blocked++;
224
- else
225
- result.skipped++;
226
- }
227
- return result;
228
- }
229
- function materializeLocally(skillName, files, cwd) {
230
- for (const platform of native_skill_mirror_1.NATIVE_SKILL_ADAPTER_PLATFORMS) {
231
- try {
232
- (0, native_skill_mirror_1.applyNativeSkillMirror)({
233
- platform,
234
- skills: [{
235
- skillName,
236
- skillId: `global:${skillName}`,
237
- scope: 'global',
238
- contentHash: computeMirrorHash(files),
239
- files,
240
- }],
241
- projectDir: cwd,
242
- prune: false,
243
- });
244
- }
245
- catch { /* local adapter failure never breaks turn capture */ }
246
- }
247
- }
248
- function statePath() {
249
- const home = process.env.HOME || process.env.USERPROFILE || '';
250
- return path.join(home, '.greprag', 'state', 'skill-mirror.json');
251
- }
252
- function readMirrorState() {
253
- try {
254
- const parsed = JSON.parse(fs.readFileSync(statePath(), 'utf-8'));
255
- if (parsed && typeof parsed.skills === 'object' && parsed.skills) {
256
- return { skills: parsed.skills };
257
- }
258
- }
259
- catch { /* missing/unreadable → empty */ }
260
- return { skills: {} };
261
- }
262
- function writeMirrorState(state) {
263
- try {
264
- const file = statePath();
265
- fs.mkdirSync(path.dirname(file), { recursive: true });
266
- fs.writeFileSync(file, JSON.stringify(state, null, 2) + '\n');
267
- }
268
- catch { /* best-effort — worst case is one redundant upload next use */ }
269
- }
270
- async function fetchRemoteSkillHash(params) {
271
- try {
272
- const res = await fetch(`${params.apiUrl}/v1/skillmirror/${encodeURIComponent(params.skill)}?scope=global`, {
273
- headers: { 'Authorization': `Bearer ${params.apiKey}` },
274
- });
275
- if (res.status === 404)
276
- return null;
277
- if (!res.ok)
278
- return null;
279
- const data = await responseJson(res);
280
- return typeof data.contentHash === 'string' ? data.contentHash : null;
281
- }
282
- catch {
283
- return null;
284
- }
285
- }
286
- /** Mirror one skill if its content changed since the last mirror. Never throws. */
287
- async function maybeMirrorSkill(params) {
288
- try {
289
- if (!/^[A-Za-z0-9._-]{1,64}$/.test(params.skill))
290
- return 'skipped';
291
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
292
- const dir = (0, skill_landing_1.resolveSkillDir)(params.skill, params.cwd, homeDir);
293
- if (!dir)
294
- return 'skipped';
295
- const files = collectSkillFiles(dir);
296
- if (!files)
297
- return 'skipped';
298
- // The disk copy of an already-mirrored skill is the generated ADAPTER that
299
- // `materializeLocally` wrote below — a launcher whose whole body says "run
300
- // `greprag load <name>`". Uploading it makes it canon, and `greprag load`
301
- // then serves the launcher back to the agent that was told to run it: the
302
- // skill's real instructions are gone and nothing errors. That loop closed
303
- // here, one turn after every first mirror, and had already eaten the
304
- // canonical body of skills including anti-ai, chrome, gmail and root-cause.
305
- //
306
- // An adapter is never a mirror source. Canon changes through the guarded
307
- // edit path (`greprag skill edit` / `skill mirror apply`), never by
308
- // shadowing a file greprag itself generated. adr: adr/skill-adapter-never-canon.md
309
- const main = files.find(file => file.path === 'SKILL.md');
310
- if (main && (0, skill_mirror_files_1.isGeneratedSkillAdapter)(main.content))
311
- return 'skipped';
312
- const hash = computeMirrorHash(files);
313
- const state = readMirrorState();
314
- const localLease = state.skills[params.skill]?.hash;
315
- if (localLease === hash) {
316
- materializeLocally(params.skill, files, params.cwd);
317
- return 'fresh';
318
- }
319
- if (!localLease) {
320
- const remoteHash = await fetchRemoteSkillHash(params);
321
- if (remoteHash === hash) {
322
- state.skills[params.skill] = { hash, mirroredAt: new Date().toISOString() };
323
- writeMirrorState(state);
324
- materializeLocally(params.skill, files, params.cwd);
325
- return 'fresh';
326
- }
327
- if (remoteHash)
328
- return 'skipped';
329
- }
330
- const res = await fetch(`${params.apiUrl}/v1/skillmirror/${encodeURIComponent(params.skill)}`, {
331
- method: 'POST',
332
- headers: { 'Authorization': `Bearer ${params.apiKey}`, 'Content-Type': 'application/json' },
333
- body: JSON.stringify({
334
- files,
335
- contentHash: hash,
336
- scope: 'global',
337
- expectedHash: localLease,
338
- }),
339
- });
340
- if (!res.ok)
341
- return 'skipped';
342
- state.skills[params.skill] = { hash, mirroredAt: new Date().toISOString() };
343
- writeMirrorState(state);
344
- materializeLocally(params.skill, files, params.cwd);
345
- return 'mirrored';
346
- }
347
- catch {
348
- return 'skipped';
349
- }
350
- }
@@ -358,7 +358,7 @@ function formatSkillStaleness(report, options = {}) {
358
358
  lines.push('1. Pause trusting this skill; read the changed watched source files/diffs first.');
359
359
  lines.push('2. Classify relevance: low relevance or small delta -> use repo source as truth; no chip.');
360
360
  lines.push('3. If this task depends on the stale area and the delta is material, spawn a skill-refresh chip.');
361
- lines.push('4. The refresh chip must run `greprag load skill-change`, edit canonical GrepRAG skill source, and never edit local generated adapters.');
361
+ lines.push('4. The refresh chip edits the skill\'s git folder (~/.claude/skills/<name>/ in dotclaude, or <repo>/.claude/skills/<name>/) and commits; the mirror follows.');
362
362
  }
363
363
  if (report.projects.some(p => p.dirtyPaths.length > 0)) {
364
364
  lines.push('Baseline rule: do not stamp a new baseline while watched local edits are dirty.');
@@ -372,11 +372,8 @@ function formatSkillNoSourceBinding(skillName) {
372
372
  return [
373
373
  '[greprag skill-staleness - no source binding]',
374
374
  `/${skillName} has no source bindings.`,
375
- 'Authority: canonical mirrored payload exists in GrepRAG; local Codex/Claude/OpenCode SKILL.md files are generated adapter-only mirrors.',
376
- 'Do not edit a local native adapter as skill content.',
377
- `Agent-safe edit path: \`greprag skill mirror export ${skillName} --to <dir>\`, patch the exported Markdown files non-interactively, then \`greprag skill mirror apply <dir> --skill ${skillName} --expected-hash <exported-hash>\`.`,
378
- `Human editor shortcut: \`greprag skill edit ${skillName}\`; Codex auto-skips the editor, and agents may pass \`--no-open\` explicitly.`,
379
- `After binding a real source repo, stamp it with \`greprag skill source stamp ${skillName} --project <p> --path <rel>\`.`,
375
+ `Authority: git. Edit ~/.claude/skills/${skillName}/ (dotclaude) or <repo>/.claude/skills/${skillName}/ and commit; GrepRAG mirrors the committed files read only. Codex/OpenCode SKILL.md files are generated launchers, never content.`,
376
+ `To watch a source repo, stamp a binding into the git SKILL.md with \`greprag skill source stamp ${skillName} --project <p> --path <rel>\`, then commit.`,
380
377
  ].join('\n') + '\n';
381
378
  }
382
379
  function stampSourceBinding(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greprag",
3
- "version": "5.83.2",
3
+ "version": "5.85.0",
4
4
  "description": "Private GrepRAG client for existing Claude Code, Codex, OpenCode, and Grok Build tenants.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,11 +1,10 @@
1
1
  ---
2
2
  name: commander
3
3
  description: |
4
- DEPRECATED 2026-06-07 — superseded by the Front Desk Switchboard
5
- (docs/front-desk-switchboard.md). Discord DMs now route through the
6
- per-tenant receptionist brain (menu / connect / ring), the same as SMS;
7
- the handoff-pin + emergency-router model this skill wrapped has been
8
- retired. Pairing still works; "handoff" is a no-op. Kept for reference.
4
+ DEPRECATED 2026-06-07, retired 2026-10-06 — the handoff-pin +
5
+ emergency-router model this skill wrapped is gone, and so is the Front
6
+ Desk that replaced it. Nothing reads DMs sent to the bot any more.
7
+ Pairing still works, so sessions can DM the operator. Kept for reference.
9
8
 
10
9
  Field-commander interface over greprag's Discord DM bridge. Pair your
11
10
  Discord account once, then DM the bot to put any open Claude session
@@ -28,13 +27,12 @@ metadata:
28
27
  license: MIT
29
28
  ---
30
29
 
31
- > **⚠ DEPRECATED (2026-06-07).** Superseded by the **Front Desk Switchboard**
32
- > (`docs/front-desk-switchboard.md`). Discord DMs now go through the same
33
- > per-tenant receptionist brain as SMS — text `MENU` to the bot for the
34
- > deterministic menu (live sessions, connect, settings), or just talk to the
35
- > receptionist. The **handoff pin + emergency router this skill describes are
36
- > retired** (`/handoff` is a no-op). Pairing is unchanged. This file is kept for
37
- > historical reference only; do not build on it.
30
+ > **⚠ RETIRED (2026-10-06).** The **handoff pin + emergency router this skill
31
+ > describes are gone**, and so is the Front Desk that replaced them on
32
+ > 2026-06-07 (`adr/front-desk-retirement.md`). Nothing reads DMs sent to the
33
+ > bot; it answers each with a fixed line. Pairing is unchanged, so sessions can
34
+ > still DM the operator with `greprag send --to discord:<snowflake>`. This file
35
+ > is kept for historical reference only; do not build on it.
38
36
 
39
37
  # Commander — Discord DM bridge
40
38
 
@@ -237,7 +237,7 @@ Child watch events may appear on the parent TUI — resume the child to act. Do
237
237
  - `docs/inbox-watch.md` — SSE watcher patterns, liveness model, parent-side / post-send patterns
238
238
  - `docs/doctor.md` — `greprag doctor` (identity drift, orphan consolidation)
239
239
  - `docs/corpus.md` — upload + search arbitrary text (books, codebases, voice samples)
240
- - `docs/discord-handoff.md` — Commander DM bridge (`greprag discord pair/handoff`)
240
+ - `docs/discord-handoff.md` — Discord DM bridge (`greprag discord pair`, `send --to discord:`; the handoff is retired)
241
241
  - `docs/memory-advanced.md` — raw curl, `/v1/memory/by-period` `--type` values, naming note
242
242
  - `docs/fix.md` — log + query project-specific fixes (the Mechanic's queue)
243
243
  - `docs/discover.md` — cross-project advisor lookup (`greprag discover`)
@@ -1,8 +1,6 @@
1
- # Discord Handoff (Commander)
1
+ # Discord DM bridge
2
2
 
3
- Discord DM bridge — user steps away from desk, conversation continues on phone. Orchestrator-side summary; operator-facing flow lives at `~/.claude/skills/commander/SKILL.md`.
4
-
5
- Two surfaces: one-time pairing (identity) + per-session handoff (active routing). Emergency-router fallback: if the user DMs the bot with no handoff active, the bot auto-links to the most-recently-attached session watcher under their tenant. Tag `references.discord.emergency_route` on the inbox row flags the auto-link; reply with self-identification on first response.
3
+ The operator pairs their Discord account once. After that, any session can DM them. The bot does not read DMs sent to it: the Front Desk that used to answer them was retired 2026-10-06, so the bot replies to each with a fixed line. The handoff pin, `/switch` and the CLI's handoff commands were retired the same day.
6
4
 
7
5
  ## Pairing (one-time per tenant)
8
6
 
@@ -10,38 +8,16 @@ Two surfaces: one-time pairing (identity) + per-session handoff (active routing)
10
8
  greprag discord pair
11
9
  ```
12
10
 
13
- Generates a 6-char code. User DMs `@greprag` on Discord with `/pair <code>`. After that, the user's snowflake is bound to their greprag tenant. DMs from that snowflake land as inbox messages on `default_project_id` (the project they paired from).
11
+ Generates a 6-char code. User DMs `@greprag` on Discord with `/pair <code>`. After that, the user's snowflake is bound to their greprag tenant.
14
12
 
15
13
  You don't run this — one-time setup the user does themselves. Detect "I'm paired" via `greprag discord me`: returns `paired: true` and the pairing's project_name.
16
14
 
17
- ## Handoff (per-session routing pin)
18
-
19
- The pattern the user almost always means by "let's continue on Discord":
20
-
21
- ```bash
22
- greprag discord handoff --ttl 60 --json
23
- ```
24
-
25
- Run under `Monitor` (persistent: true). The command pins DMs from the user's snowflake to (this project, this session) for 60 min, fires a confirmation DM, then **blocks** streaming the session's inbox to stdout as JSON. Each line that prints is one Discord DM — Monitor delivers them as notifications back into your turn. Pin slides forward 30+ min on every inbound; an active thread stays anchored.
26
-
27
- Replies go out via:
15
+ ## Messaging the operator
28
16
 
29
17
  ```bash
30
18
  greprag send --to discord:<snowflake> "your message body"
31
19
  ```
32
20
 
33
- CLI handles UTF-8, Discord's 2000-char limit, optional `--ref-json '{"discord":{"reply_to_message_id":"..."}}'` for threading. **Do not use curl for this** — bash on Windows mangles em-dashes / curly quotes / emoji into Win-1252 bytes that render as `?` on Discord. Node fetch (which the CLI uses) is UTF-8 native.
34
-
35
- For long agent turns where auto-typing's 10-second window expires, call `greprag discord typing` between work steps to refresh the indicator. Silent on success.
36
-
37
- `greprag discord unhandoff` releases the pin early (DMs revert to default project). Pin also expires naturally via TTL.
38
-
39
- ## Canonical handoff flow
40
-
41
- User says "let's keep going on Discord" / "DM me" / "I'm stepping out":
21
+ It arrives as a DM from the bot. CLI handles UTF-8 and Discord's 2000-char limit. **Do not use curl for this** — bash on Windows mangles em-dashes / curly quotes / emoji into Win-1252 bytes that render as `?` on Discord. Node fetch (which the CLI uses) is UTF-8 native.
42
22
 
43
- 1. Confirm pairing with `greprag discord me`. If unpaired, tell the user to run `greprag discord pair` and DM `/pair <code>` first.
44
- 2. Resolve current session_id (`CLAUDE_SESSION_ID` env, or the 8-hex from SessionStart context).
45
- 3. Arm: `Monitor` with command `greprag discord handoff --session <8-hex> --project <name> --ttl 60 --json`, persistent.
46
- 4. Tell the user the pin is live; the bot already DMed them. Each reply from Discord fires as a Monitor notification.
47
- 5. On each notification, parse the JSON body (`body` field = user's message text). Draft your reply. Send via `greprag send --to discord:<snowflake> "..."`. Snowflake is in `references.discord.snowflake`.
23
+ During long agent turns, call `greprag discord typing` between work steps to show the typing indicator (it lasts about 10 seconds). Silent on success.
@@ -53,6 +53,6 @@ Each: **signature** (how it shows up in the run) · **location** (where it lives
53
53
 
54
54
  - **One friction, one edit.** If a run surfaced two defects, make two separate edits, each shown for its own accept/revert. Never batch.
55
55
  - **Apply + show diff.** Make the edit, then show the unified diff and stop for one accept/revert. (This is the operator-chosen gate; the Mechanic "always confirm" rule still holds.)
56
- - **Edit the canonical source.** Managed skills: `greprag skill edit <name> --skill-id <id>`; greprag-bundled skills: `packages/cli/skill/<name>/`. Never edit a generated `~/.claude/skills/<name>/SKILL.md` adapter or install artifact.
56
+ - **Edit the git source and commit.** Global skills: `~/.claude/skills/<name>/` (dotclaude repo); project skills: `<repo>/.claude/skills/<name>/`; greprag-bundled skills: `packages/cli/skill/<name>/`. The commit message names the friction this fixes. Never edit a generated Codex/OpenCode launcher or the installed copy of a bundled skill.
57
57
  - **Line budget:** a fix must not grow SKILL.md without adding a real proactive-fire rule or per-task step. Converting wallpaper → Convention B is net-neutral or smaller; mis-route adds at most one line.
58
58
  - **Out of taxonomy → spawn, don't improvise.** Anything not matching the six becomes one `greprag fix spawn --type doctrine "<unit>"` chip. `greprag fix log` is only for deliberate audit/design notes.
@@ -78,7 +78,7 @@ Your parent session is named `<parent-name>`. When you finish, report to it with
78
78
  greprag session retitle <your-own-8hex> "Chip <Label> — <workstream>"
79
79
  ```
80
80
 
81
- The leader dictates the exact string (e.g. `Chip A — Converter format coverage`); substitute your own 8-hex from SessionStart. This force-sets the registry title so the watcher list + Discord `/switch` line up with the leader's labels. Single chips skip this.
81
+ The leader dictates the exact string (e.g. `Chip A — Converter format coverage`); substitute your own 8-hex from SessionStart. This force-sets the registry title so the watcher list lines up with the leader's labels. Single chips skip this.
82
82
 
83
83
  ## Report back — native `SendMessage`
84
84
 
@@ -87,7 +87,9 @@ Close the task body with this (substitute `<slug>` + `<parent-name>`):
87
87
  ````
88
88
  ---
89
89
 
90
- **Report back:** when the work is committed, send your result to the parent with native `SendMessage`, then end your turn:
90
+ **Rebase before you report:** when the work is committed, rebase this branch onto the current tip of your merge target (the default branch, or the integration branch on a multi-chip mission), resolve any conflicts here, and rerun the affected checks. Only you can edit this worktree, so the parent lands your branch by fast-forward alone and sends it back if the tip has moved again.
91
+
92
+ **Report back:** then send your result to the parent with native `SendMessage`, then end your turn:
91
93
 
92
94
  - `to`: `"<parent-name>"`
93
95
  - `message`: status, commit hash, branch `chip/<slug>`, and a one-line summary.
@@ -116,6 +118,8 @@ Chips never `npm run deploy` (or Wrangler / another provider deploy) from the wo
116
118
 
117
119
  ## Parent merge discipline — prune at the merge (HARD RULE)
118
120
 
121
+ Land a chip branch with `git merge --ff-only chip/<slug>` under the merge lock, nothing more: the chip rebased and retested it before reporting. Never rebase inside the chip's worktree; you cannot edit there, so a conflict strands a half-finished rebase. A refused fast-forward goes back to the chip over `SendMessage` (free the lock first).
122
+
119
123
  When you (the parent) merge a chip branch — through the canonical commit path or a profile-declared merge — prune in the same breath: find the worktree holding `chip/<slug>` in `git worktree list` (the app's own worktree when the chip reused it, `.claude/worktrees/<slug>` on a local start), junction guard it (`~/.claude/skills/commit/guard-junctions.sh <worktree>`), `git worktree remove <worktree>`, and `git branch -d chip/<slug>` once merged. A chip that closed itself has already removed its own worktree, so no worktree holds the branch — delete the branch and move on, don't go hunting for it. Deferred cleanup = stale worktrees piling up (field state 2026-06-10: 3 leftovers). Multi-chip missions: worktree dies at the integration-branch merge; the branch lives until the configured default-branch merge (chip-leader Phase 4).
120
124
 
121
125
  **FIX chips report to the mission owner.** A `greprag fix spawn` mission is the source of truth for FIX behavior. `fix spawn` detects usable Git history and emits `workspaceMode`: Git uses an isolated worktree; non-Git, unavailable Git, or no commit uses the project-local task with serialized writes. Dispatch honors that mode without trying a worktree first. The FIX chip identifies the exact friction, makes the smallest durable root-cause fix, verifies and commits useful work, then reports the commit/result and cleanup parameters to the parent mission owner. It creates no human landing gate. If no live parent exists and the chip carries the full-goal mission, it follows the repo profile as mission owner.
@@ -96,6 +96,22 @@ again. Never rebase a shared default branch. A busy merge lock means retry
96
96
  after its holder finishes — never bypass it, and never take over someone
97
97
  else's in-progress merge.
98
98
 
99
+ ### Another session's branch is rebased by that session
100
+
101
+ Step 2 happens in the checkout of whoever owns the branch, because only that
102
+ session can edit there and so only it can resolve a conflict. A chip rebases
103
+ onto its merge target's tip and retests before it reports, so its parent lands
104
+ it with steps 3 to 5 alone. Never run `git -C <their worktree> rebase`: the
105
+ write guard refuses your edits there, so the first conflict leaves you
106
+ aborting, or leaves a half-finished rebase in someone else's checkout. If the
107
+ fast-forward is refused because the tip moved, free the lock and send the
108
+ branch back to its owner to rebase and retest. A chip that already closed left
109
+ no worktree behind, so rebase its branch in a checkout you can edit.
110
+
111
+ Field trigger (2026-10-06): a parent landing chip branches with
112
+ `git -C <chip-worktree> rebase main` hit the same ADR conflict twice and could
113
+ not resolve it either time.
114
+
99
115
  This lock is a short Git critical section. It is not a delivery lease, and it
100
116
  is not peer-acknowledgement polling.
101
117
 
@@ -31,7 +31,7 @@ conversation. The registration is part of the work, not an extra:
31
31
 
32
32
  - **Docs** mirror through `greprag doc mirror sync` and can still auto-register
33
33
  pointers when edited in a live session. Search/read them through the CLI.
34
- - **Skills** auto-mirror on use (`greprag load` serves them cross-machine).
34
+ - **Skills** live in git (`~/.claude/skills/<name>/` or `<repo>/.claude/skills/<name>/`); commit them and the mirror follows (`greprag load` serves them cross-machine).
35
35
  - **Decisions** get a dated entry in the owning ADR / decision log, in the
36
36
  same commit as the code.
37
37
  - **Everything else** (state files, artifacts, conventions) gets its path
@@ -1,4 +1,4 @@
1
- # Prompt Audit — dated-model cruft in canonical instructions
1
+ # Prompt Audit — dated-model cruft in agent instructions
2
2
 
3
3
  <!-- adr: adr/prompt-audit-canon.md -->
4
4
 
@@ -14,56 +14,35 @@ local skill adapters may not override it.
14
14
 
15
15
  ## The one invariant
16
16
 
17
- **AUDIT FINDINGS LAND IN CANON, NEVER IN A FOLLOWER.** Global and repo
18
- instructions are served by `greprag load instructions`; `~/.claude/CLAUDE.md`,
19
- `~/.codex/AGENTS.md`, `<repo>/CLAUDE.md`, `<repo>/AGENTS.md` are one-way
20
- rendered followers. Editing a follower directly produces drift: the next
21
- `greprag instructions push` blocks with `local-change`, and the parity guard
22
- routes the repair back to `instructions pull`. So an accepted audit edit is
23
- applied to a **proposed revision file** in scratch and promoted with
24
- `greprag instructions pull <file> --scope <scope> --harness <harness> --push`.
25
- That single command advances canon (optimistic concurrency, `expectedRevision`)
26
- and materializes every follower for the scope. Nothing else writes a follower.
17
+ **AUDIT FINDINGS LAND IN THE GIT SOURCE, NEVER IN A RENDERED FILE.**
18
+ `~/.claude/CLAUDE.md` in the dotclaude repo is the global source; a repo's
19
+ `CLAUDE.md` and `AGENTS.md` are ordinary files in that repo.
20
+ `~/.codex/AGENTS.md` and `~/.config/opencode/AGENTS.md` are rendered from
21
+ `~/.claude/CLAUDE.md` plus `~/.claude/instructions/<codex|opencode>.md` by
22
+ `greprag instructions render` (session start runs it), so an edit there is
23
+ overwritten. GrepRAG's copy (`greprag load instructions`) is read only and
24
+ follows the last commit.
27
25
 
28
26
  ## Procedure
29
27
 
30
- 1. **Scope and target model.** `--scope global` audits the tenant-global
31
- revision; `--scope repo` audits the current repo's root revision. Target
28
+ 1. **Scope and target model.** Global audits `~/.claude/CLAUDE.md`; repo
29
+ audits the current repo's `CLAUDE.md`/`AGENTS.md`. Target
32
30
  model is the one Claude Code sessions run today (Claude Fable 5.1 as of
33
31
  2026-09). State both at the top of the report; do not ask.
34
- 2. **Render canon, not disk.**
35
- `greprag load instructions --scope global --harness claude-code > <scratch>/canon-global.md`
36
- (repo: `--scope repo`). Confirm followers are fresh first with
37
- `greprag instructions status --scope <scope>`; if any follower is
38
- `local-change`, stop and have the operator `instructions pull` it before
39
- auditing, or the audit will grade a stale canon.
40
- Overlays: `instructions` canon is `shared` + per-harness raw suffixes. When
41
- a suffix is non-empty, audit each harness render separately and land
42
- suffix edits with `instructions overlay set|derive`, never by pulling a
43
- rendered file as `shared`. When all suffixes are empty (check the document
44
- via the API or `overlay` history), the Claude render **is** `shared` and
45
- `pull` of the revised Claude render is exact.
46
- 3. **Provenance.** The Claude follower is usually Git-tracked
47
- (`~/.claude` dotclaude repo; repo files in the repo). `git log`/`git blame`
32
+ 2. **Read the source.** Global: `~/.claude/CLAUDE.md` plus any
33
+ `~/.claude/instructions/<harness>.md` addition; audit each addition
34
+ separately. Repo: that repo's `CLAUDE.md` and `AGENTS.md`. Confirm the
35
+ rendered files are current with `greprag instructions status`.
36
+ 3. **Provenance.** Every source file is in git. `git log`/`git blame`
48
37
  each emphatic or prohibitive line: which failure, on which model, did it
49
38
  prevent, and does it still reproduce on the target model?
50
39
  4. **Classify every line** with the rubric below. Produce the report and the
51
40
  proposed diff, both, always.
52
- 5. **Land.** Copy the canon render to `<scratch>/proposed-<scope>.md`, apply
53
- ONLY high-confidence hunks, then:
54
- ```text
55
- greprag instructions snapshot create --name pre-prompt-audit-<YYYY-MM-DD> --scope <scope>
56
- greprag instructions pull <scratch>/proposed-<scope>.md --scope <scope> --harness claude-code --push
57
- greprag instructions status --scope <scope> # every follower fresh
58
- ```
59
- Medium findings and flags stay in the report for the operator; they are
60
- applied by a later `pull` of an operator-approved revision, the same way.
61
- If the follower lives in a Git repo, the push dirties it; the operator
62
- commits that repo (the audit never runs git there).
63
- 6. **Write the report** to `tasks/prompt-audit-<scope>-<date>.md` in the
64
- repo you are working in (or scratch if the repo has no `tasks/`), in the
65
- shape under "Report shape". Persona (`greprag persona show`) is a separate
66
- system: note Persona findings in the report; never edit it from here.
41
+ 5. **Land.** Apply ONLY high-confidence hunks to the source file, show the
42
+ diff, and leave the commit to the operator (the audit never runs git in
43
+ their repos). After the commit, `greprag instructions render` rewrites the
44
+ AGENTS.md files and refreshes GrepRAG's copy. Medium findings and flags
45
+ stay in the report for the operator.
67
46
 
68
47
  ## Rubric (shipped copy — Anthropic prompt-audit, condensed, binding)
69
48
 
@@ -166,12 +145,12 @@ returns 400; prefill is replaced by structured outputs.
166
145
  ## Report shape
167
146
 
168
147
  Top: Assumptions (scope, target model), Inventory (what was audited and its
169
- canon revision), Summary (counts per group; two or three highest-impact
148
+ source commit), Summary (counts per group; two or three highest-impact
170
149
  findings in prose). Then one entry per finding, ordered by confidence:
171
150
 
172
151
  | Field | Content |
173
152
  |---|---|
174
- | Location | `file:line` in the canon render |
153
+ | Location | `file:line` in the source file |
175
154
  | Evidence | exact quoted text |
176
155
  | Pattern | the group/row above |
177
156
  | Why obsolete | one or two sentences tied to the target model's documented behavior |
@@ -185,12 +164,10 @@ only when tests, docs, and references to the removed text go too).
185
164
 
186
165
  ## Guardrails
187
166
 
188
- - Never edit `~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`,
189
- `~/.config/opencode/AGENTS.md`, or a repo `CLAUDE.md`/`AGENTS.md` by hand.
190
- The only follower writer is `instructions push` (or `pull --push`).
167
+ - Never edit `~/.codex/AGENTS.md` or `~/.config/opencode/AGENTS.md` by
168
+ hand: they are rendered. Edit `~/.claude/CLAUDE.md` or the harness addition.
191
169
  - Apply high-confidence hunks only. Medium and flag items wait for the
192
170
  operator.
193
- - Snapshot before pulling. Rollback is `greprag instructions restore
194
- --snapshot <name> --scope <scope>` followed by `pull` of the restored file.
171
+ - Rollback is `git revert` on the source file.
195
172
  - Persona is out of scope for edits; note findings only.
196
173
  - Out of taxonomy friction: `greprag fix spawn "<one unit>"`.