allye-pi 1.5.1 → 1.6.1

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/README.md CHANGED
@@ -29,7 +29,7 @@ Your AI agent has **68+ tools** but no idea when to use them. Allye adds the met
29
29
 
30
30
  | | Feature | Description |
31
31
  |---|---------|-------------|
32
- | **Workflow** | Guided delivery | Sandbox → Product Planning → Technical Planning → Orchestrator → Executor → Reviewer (Standards + Spec axes), connected by handovers between fresh, lean-context chats |
32
+ | **Workflow** | Adaptive toolkit | Intent-driven use of context, skills, optional planning/tasks, action, verification, and persistence; checkpoints can be skipped or repeated proportionally |
33
33
  | **Planning** | Discussion phase | Gray areas identified, options presented with trade-offs, decisions captured |
34
34
  | **Memory** | Cross-session continuity | Agent searches past context at start, saves session state at end |
35
35
  | **TDD** | Test-driven development | Red-Green-Refactor with automatic detection of when TDD applies |
@@ -81,8 +81,8 @@ Prefer the paste-into-agent or marketplace routes below? They still work — the
81
81
  After installing, you get:
82
82
  - **OAuth authentication** — browser-based login, no tokens to manage
83
83
  - **Bootstrap hook** — injects workflow methodology at session start
84
- - **5 dispatched subagents** — reviewer-standards, reviewer-spec, deep-search, code-analyzer, executor, delegated via the Agent tool for phases that don't need to pause and ask you anything (executor only runs this way if you opt into automatic mode — manual is the default)
85
- - **17 skills** — Sandbox, Planning, Technical Planning, Orchestrator, and Delivery run as skills loaded directly into your conversation, loaded on-demand by the bootstrap, so they can ask you questions when something's ambiguous
84
+ - **Optional delegation** — bounded research, review, or execution subagents may be used when the runtime supports them and delegation is beneficial; local execution remains valid
85
+ - **17 composable skills** — discovery, planning, execution, review, delivery, memory, verification, and delegation playbooks loaded as needed; no universal phase chain is required
86
86
  - **Parallel delivery, when a runtime is detected** — the Orchestrator can drive several independent stories at once, each in its own git worktree and its own watchable agent process; without a detected runtime, delivery degrades to the existing manual and automatic-subagent modes
87
87
 
88
88
  #### Multiple Allye accounts (multi-tenant)
@@ -113,7 +113,7 @@ The agent will configure the MCP server via OAuth and install the `allye-opencod
113
113
 
114
114
  After installing, you get:
115
115
  - **6 agents in the picker** — Allye, Allye Plan, Allye Orchestrator, Allye Build, Allye Review, Allye Deliver (Ctrl+T to switch)
116
- - **Auto-loaded context** — your profile and team info injected before every conversation
116
+ - **Adaptive context** — profile, team info, and relevant memories loaded when Allye is available and useful
117
117
  - **Dynamic skill discovery** — agents search for your team's standards via MCP
118
118
 
119
119
  **To update:** Paste this into your agent:
@@ -263,7 +263,7 @@ How multi-phase workflow support is implemented differs by platform, because not
263
263
  - **OpenCode** ships 6 agent-picker personas (Ctrl+T to switch) — Allye, Allye Plan, Allye Orchestrator, Allye Build, Allye Review, Allye Deliver — OpenCode's agent model supports switching personas interactively within a session, so all 6 can be full agents. The automatic-Executor dispatch mode is Claude-Code-only for now; OpenCode always runs Executor (Allye Build) as an interactive agent.
264
264
  - **Cursor, Codex, Gemini CLI** — a single agent handles all phases with the same workflow knowledge (no multi-agent picker on these platforms).
265
265
  - **Hermes Agent** reads skills from a directory (`~/.hermes/skills/allye/`) rather than fetching them over MCP, and gets the `using-allye` bootstrap injected by a small Python plugin at session start instead of a hook — otherwise the same single-agent, same-workflow-knowledge shape as Cursor/Codex/Gemini CLI.
266
- - **Pi** loads the canonical repository skills through a native Pi package, injects Allye context through the configured MCP adapter, and supports explicit executor or orchestrator mode. Executor mode is the safe default for Hermes-led work; orchestrator mode can drive Herdr through the five-primitive runtime contract.
266
+ - **Pi** loads the canonical repository skills through a native Pi package, reads `using-allye` into the first prompt, injects Allye context through the configured MCP adapter, and exposes an adaptive toolkit. Herdr delegation is optional and capability-detected; tasks are recommended rather than mandatory.
267
267
 
268
268
  Every phase, on every platform:
269
269
  - Responds in **your language** (detected from your messages, falling back to your profile only before you've said anything)
@@ -7,7 +7,7 @@ replace Pi's MCP configuration:
7
7
  - the adapter exposes that directory through Pi's `resources_discover` event;
8
8
  - Allye context and memory are loaded through the already configured `allye`
9
9
  MCP server via `pi-mcp-adapter`;
10
- - Herdr is exposed only in explicit Pi orchestrator mode.
10
+ - Herdr is exposed as an optional capability when the Pi session provides `HERDR_ENV=1`.
11
11
 
12
12
  ## Official package sources
13
13
 
@@ -74,47 +74,21 @@ Both npm and Git packages use the root Pi manifest and let Pi install
74
74
  source, canonical `skills/` directory, and Pi documentation; it is not the full
75
75
  plugin checkout and is not the installer input.
76
76
 
77
- ## Modes
77
+ ## Adaptive toolkit
78
78
 
79
- Pi is an executor by default, which is the safe mode when Hermes is the master.
80
- The mode can be selected before starting a session:
79
+ Pi does not select an executor or orchestrator mode. Allye is available as an
80
+ adaptive toolkit in the current session. Use `/allye-capabilities` to inspect
81
+ Allye/MCP, filesystem, subagent, and Herdr capabilities.
81
82
 
82
- ```text
83
- ALLYE_PI_MODE=executor pi
84
- ALLYE_PI_MODE=orchestrator pi
85
- ```
83
+ When `HERDR_ENV=1`, the optional `allye_herdr` tool provides bounded `detect`,
84
+ `spawn`, `dispatch`, `wait`, and Allye-backed `collect` operations. Without it,
85
+ Pi continues locally. Herdr and subagents are capabilities, not prerequisites.
86
+ Tasks are recommended for meaningful, delegated, multi-step, or review-heavy
87
+ work, but an explicitly approved no-task path is supported.
86
88
 
87
- `ALLYE_ORCHESTRATOR=hermes` always forces executor mode. Pi orchestrator mode
88
- also requires both `ALLYE_ORCHESTRATION_RUN_ID` and `ALLYE_WORK_ITEM_KEY`; an
89
- explicit local lock is acquired before `allye_runtime` becomes active. The claim
90
- contains run/work-item identity, PID, session identity when available, a nonce,
91
- and a 30-minute lease. Normal `session_shutdown` aborts waits and removes only
92
- the exact claim owned by that instance. If a process crashes, recovery is
93
- disabled by default; after confirming the recorded local PID is gone and the
94
- lease expired, set `ALLYE_PI_RECOVER_STALE_LOCK=1` to quarantine and replace the
95
- lock. Active, unreadable, changed, or inconclusive locks fail closed. In a
96
- session, `/allye-mode executor` and `/allye-mode orchestrator` change the active
97
- mode only when the same guard succeeds. Only orchestrator mode enables the
98
- `allye_runtime` tool.
99
-
100
- ### Pi orchestrator
101
-
102
- With `ALLYE_PI_MODE=orchestrator`, Pi coordinates the canonical workflow and may
103
- use `allye_runtime` for Herdr's `detect`, `spawn`, `dispatch`, `wait`, and
104
- Allye-backed `collect` operations. Parallel stories require separate worktrees;
105
- the pane cwd remains the plugin-enabled repository root and absolute worktree
106
- paths are placed in each briefing. The adapter does not create work items or change statuses automatically.
107
- Status updates by an assigned executor remain allowed through the canonical
108
- `execution` skill; pane/dispatch ownership is separate from status ownership.
109
-
110
- ### Pi executor / Hermes master
111
-
112
- In executor mode, Pi reads the handed-over story/tasks, implements only that
113
- scope, follows the canonical `execution` skill, and may advance its assigned
114
- tasks to `in_progress` and `review` when the work and verification are complete.
115
- It does not create or dispatch panes, assume orchestration, or change scope.
116
- Hermes can remain the master and dispatch Pi or another agent through Herdr; Pi
117
- and Hermes must not orchestrate the same work item simultaneously.
89
+ The adapter does not create work items or change statuses automatically. When a
90
+ work item exists, Allye remains the source of truth for scope, decisions, and
91
+ evidence; otherwise the user can proceed with proportional verification.
118
92
 
119
93
  ## Existing Pi startup extension
120
94
 
@@ -122,8 +96,7 @@ This package includes its own Allye bootstrap. If a user already has a separate
122
96
  `allye-memory-startup.ts`, both can be loaded without changing global files, but
123
97
  startup context may be fetched twice. To keep the existing global bootstrap as
124
98
  the only startup fetcher, set `ALLYE_PI_NATIVE_BOOTSTRAP=0`; canonical skill
125
- loading, mode handling, team-selection guidance, and the Herdr guard remain
126
- available.
99
+ loading, capability detection, and team-selection guidance remain available.
127
100
 
128
101
  When `initialize` reports multiple teams without an active team, the adapter does
129
102
  not choose one silently and skips team-scoped memory search. If initialization
@@ -150,25 +123,21 @@ The extension is at `packages/allye-pi/src/index.ts`. Test it without changing
150
123
  Pi's global settings with:
151
124
 
152
125
  ```text
153
- pi -e ./packages/allye-pi/src/index.ts -p "Report the active Allye mode"
126
+ pi -e ./packages/allye-pi/src/index.ts -p "Report the available Allye capabilities"
154
127
  ```
155
128
 
156
- When `allye_runtime dispatch` registers a wait, the wait runs inside the Pi
129
+ When `allye_herdr dispatch` registers a wait, the wait runs inside the Pi
157
130
  process through managed `pi.exec`. On settlement it persists a durable session
158
131
  entry, shows a UI notification/message when available, and queues a follow-up
159
- turn to the Pi orchestrator. That message is evidence only and explicitly tells
160
- the orchestrator to run `allye_runtime collect` and inspect Allye
161
- `work_children`, Review memories, and Implementation memories before declaring
162
- completion. Normal completion, timeout, error, and abort are distinguished.
132
+ turn to the current Pi session. That message is evidence only and explicitly
133
+ tells the agent to run `allye_herdr collect` when a managed work item exists and
134
+ inspect Allye `work_children`, Review memories, and Implementation memories
135
+ before declaring completion. Normal completion, timeout, error, and abort are
136
+ distinguished.
163
137
  Manual `herdr agent wait` commands started outside this Pi session (for example
164
138
  with `nohup` or Bash) do not wake Pi and do not generate this notification.
165
139
 
166
140
  A real Herdr dispatch still requires `HERDR_ENV=1`, a compatible Herdr session,
167
- an existing isolated worktree, explicit orchestrator mode, and an orchestration
168
- run/work-item claim. Pi records workspace/tab/pane/agent ownership in memory for
169
- future safe teardown; it never adopts foreign panes, stops Herdr, closes panes,
170
- merges, pushes, or publishes. The lock is local to the Pi checkout/agent
171
- configuration and is **not** a distributed Allye lock: Hermes and Pi must still
172
- coordinate the run externally when operating on different machines/process
173
- homes. A stale lock can be manually investigated using its PID, lease, and owner
174
- token; automatic recovery is intentionally opt-in.
141
+ and an existing isolated worktree. Pi records workspace/tab/pane/agent ownership
142
+ for safe teardown; it never adopts foreign panes, stops Herdr, closes panes,
143
+ merges, pushes, or publishes without explicit authorization.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allye-pi",
3
- "version": "1.5.1",
3
+ "version": "1.6.1",
4
4
  "description": "Pi package adapter for the canonical Allye workflow",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,10 +1,8 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { dirname, resolve } from "node:path";
4
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { existsSync, readFileSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
- import { homedir } from "node:os";
7
- import { randomUUID } from "node:crypto";
8
6
  import { Type } from "typebox";
9
7
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
10
8
 
@@ -14,7 +12,13 @@ const MAX_CONTEXT_CHARS = 12_000;
14
12
  const RUNTIME_TIMEOUT_MS = 30_000;
15
13
  const AGENT_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
16
14
 
17
- type PiMode = "orchestrator" | "executor";
15
+ export type AllyeCapabilities = {
16
+ piSession: true;
17
+ allyeMcp: boolean;
18
+ filesystem: true;
19
+ subagents: boolean;
20
+ herdr: boolean;
21
+ };
18
22
  type McpResult = { content?: Array<{ type?: string; text?: string }> };
19
23
  type StartupContext = {
20
24
  text: string;
@@ -57,32 +61,18 @@ export type WaitEventDeliveryResult = {
57
61
  sent: boolean;
58
62
  errors: string[];
59
63
  };
60
- export type OrchestrationMode = "pi" | "hermes";
61
- export type OrchestrationClaim = {
62
- runId: string;
63
- workItemKey: string;
64
- master: OrchestrationMode;
65
- pid: number;
66
- sessionId?: string;
67
- ownerToken: string;
68
- claimedAt: string;
69
- leaseExpiresAt: string;
70
- };
71
64
  export type OwnedPane = {
72
65
  workspaceId: string;
73
66
  tabId: string;
74
67
  paneId: string;
75
68
  agentName?: string;
76
69
  };
77
- export type OwnershipState = {
78
- claim?: OrchestrationClaim;
70
+ export type DelegationState = {
79
71
  panes: OwnedPane[];
80
72
  waits: Set<string>;
81
73
  };
82
- const RUNTIME_TOOL_NAME = "allye_runtime";
83
- const DEFAULT_LOCK_DIR = ".allye/pi-orchestration-locks";
84
- const DEFAULT_LEASE_MS = 30 * 60 * 1000;
85
74
  const WAIT_EVENT_TYPE = "allye-herdr-wait";
75
+ const RUNTIME_TOOL_NAME = "allye_herdr";
86
76
 
87
77
  function resultText(result: unknown): string {
88
78
  const content = (result as McpResult | null)?.content;
@@ -97,15 +87,32 @@ function limitText(text: string, maxChars: number): string {
97
87
  return text.length <= maxChars ? text : `${text.slice(0, maxChars)}\n[context truncated]`;
98
88
  }
99
89
 
100
- function initialMode(): PiMode {
101
- if (resolveOrchestrationMode(process.env) === "hermes") return "executor";
102
- const explicit = process.env.ALLYE_PI_MODE?.toLowerCase();
103
- if (explicit === "orchestrator"
104
- && resolveOrchestrationMode(process.env) === "pi"
105
- && process.env.ALLYE_ORCHESTRATION_RUN_ID
106
- && process.env.ALLYE_WORK_ITEM_KEY) return explicit;
107
- if (explicit === "executor") return explicit;
108
- return "executor";
90
+ export function describeCapabilities(env: NodeJS.ProcessEnv = process.env): AllyeCapabilities {
91
+ return {
92
+ piSession: true,
93
+ allyeMcp: env.ALLYE_PI_MCP !== "0",
94
+ filesystem: true,
95
+ subagents: env.ALLYE_PI_SUBAGENTS === "1",
96
+ herdr: env.HERDR_ENV === "1",
97
+ };
98
+ }
99
+
100
+ export function adaptiveInstructions(capabilities: AllyeCapabilities): string {
101
+ const available = [
102
+ capabilities.allyeMcp ? "Allye/MCP" : "no Allye/MCP",
103
+ capabilities.filesystem ? "filesystem" : "no filesystem",
104
+ capabilities.subagents ? "subagents" : "no subagents",
105
+ capabilities.herdr ? "Herdr" : "no Herdr",
106
+ ].join(", ");
107
+ return `Allye is an adaptive toolkit, not a mandatory workflow. Available capabilities: ${available}. Use each only when it helps; continue locally when an optional capability is absent. Recommend tasks when they add traceability, delegation, review, or coordination value; if the user explicitly approves working without a task, proceed and verify proportionally. Ask for consent before consequential mutations and keep Allye as the source of truth.`;
108
+ }
109
+
110
+ export function loadUsingAllyeSkill(): string {
111
+ try {
112
+ return readFileSync(resolve(canonicalSkillsPath(), "using-allye", "SKILL.md"), "utf8");
113
+ } catch {
114
+ return "";
115
+ }
109
116
  }
110
117
 
111
118
  function canonicalSkillsPath(): string {
@@ -189,8 +196,8 @@ export function formatWaitEvent(event: WaitEvent): string {
189
196
  const output = event.stdout?.trim() || "(no stdout)";
190
197
  const error = event.error || event.stderr?.trim() || "(none)";
191
198
  return `Herdr wait settled for agent ${event.name}. Outcome: ${event.outcome}. `
192
- + `This is runtime evidence only, not a completion verdict. `
193
- + `Use allye_runtime collect now: read work_children for the story and search Allye memories for Review and Implementation evidence before declaring completion.\n\n`
199
+ + `This is delegation evidence only, not a completion verdict. `
200
+ + `Use allye_herdr collect when a managed work item exists: read work_children for the story and search Allye memories for Review and Implementation evidence before declaring completion.\n\n`
194
201
  + `timeout_ms=${event.timeoutMs} code=${event.code ?? "n/a"} killed=${event.killed ?? false} timestamp=${event.timestamp}\n`
195
202
  + `stdout:\n${output}\nerror/stderr:\n${error}`;
196
203
  }
@@ -255,159 +262,10 @@ export function invalidStartupContext(message: string): StartupContext {
255
262
  };
256
263
  }
257
264
 
258
- export function resolveOrchestrationMode(env: NodeJS.ProcessEnv): OrchestrationMode {
259
- if (env.ALLYE_ORCHESTRATOR?.toLowerCase() === "hermes") return "hermes";
260
- return "pi";
261
- }
262
-
263
- export function orchestrationLockPath(cwd: string, runId: string): string {
264
- const safeRunId = runId.replace(/[^a-zA-Z0-9._-]/g, "_");
265
- return resolve(cwd, DEFAULT_LOCK_DIR, `${safeRunId}.json`);
266
- }
267
-
268
- export function canUseOwnedPane(state: OwnershipState, paneId: string): boolean {
265
+ export function canUseOwnedPane(state: DelegationState, paneId: string): boolean {
269
266
  return state.panes.some((pane) => pane.paneId === paneId);
270
267
  }
271
268
 
272
- export function claimConflict(existing: OrchestrationClaim | undefined, requested: OrchestrationClaim): string | null {
273
- if (!existing) return null;
274
- if (existing.runId === requested.runId
275
- && existing.workItemKey === requested.workItemKey
276
- && existing.master === requested.master
277
- && existing.pid === requested.pid
278
- && existing.ownerToken === requested.ownerToken) return null;
279
- return `orchestration ownership conflict: ${existing.master}/${existing.runId}/${existing.workItemKey} owns this lock`;
280
- }
281
-
282
- function lockRoot(): string {
283
- return process.env.ALLYE_PI_LOCK_DIR
284
- ?? process.env.PI_CODING_AGENT_DIR
285
- ?? resolve(homedir(), ".pi", "agent");
286
- }
287
-
288
- function processState(pid: number): "alive" | "dead" | "unknown" {
289
- try {
290
- process.kill(pid, 0);
291
- return "alive";
292
- } catch (error) {
293
- const code = (error as NodeJS.ErrnoException).code;
294
- if (code === "ESRCH") return "dead";
295
- return "unknown";
296
- }
297
- }
298
-
299
- function claimFromEnvironment(sessionId?: string): OrchestrationClaim {
300
- const runId = process.env.ALLYE_ORCHESTRATION_RUN_ID?.trim();
301
- const workItemKey = process.env.ALLYE_WORK_ITEM_KEY?.trim();
302
- if (!runId || !workItemKey) {
303
- throw new Error("Pi orchestrator requires ALLYE_ORCHESTRATION_RUN_ID and ALLYE_WORK_ITEM_KEY");
304
- }
305
- if (resolveOrchestrationMode(process.env) === "hermes") {
306
- throw new Error("Hermes owns orchestration for this process; Pi remains executor");
307
- }
308
- const claimedAt = new Date();
309
- return {
310
- runId,
311
- workItemKey,
312
- master: "pi",
313
- pid: process.pid,
314
- ...(sessionId ? { sessionId } : {}),
315
- ownerToken: randomUUID(),
316
- claimedAt: claimedAt.toISOString(),
317
- leaseExpiresAt: new Date(claimedAt.getTime() + DEFAULT_LEASE_MS).toISOString(),
318
- };
319
- }
320
-
321
- function readClaim(path: string): OrchestrationClaim {
322
- try {
323
- const claim = JSON.parse(readFileSync(path, "utf8")) as Partial<OrchestrationClaim>;
324
- if (typeof claim.runId !== "string" || typeof claim.workItemKey !== "string"
325
- || (claim.master !== "pi" && claim.master !== "hermes")
326
- || typeof claim.pid !== "number" || typeof claim.ownerToken !== "string"
327
- || typeof claim.leaseExpiresAt !== "string") {
328
- throw new Error("missing required owner, PID, or lease fields");
329
- }
330
- return claim as OrchestrationClaim;
331
- } catch (error) {
332
- throw new Error(`orchestration lock ${path} is unreadable or invalid (${error instanceof Error ? error.message : String(error)}); refusing to take ownership`);
333
- }
334
- }
335
-
336
- export function acquireLocalClaim(_cwd: string, requested: OrchestrationClaim): void {
337
- const path = orchestrationLockPath(lockRoot(), requested.workItemKey);
338
- mkdirSync(resolve(path, ".."), { recursive: true });
339
- try {
340
- writeFileSync(path, JSON.stringify(requested, null, 2), { encoding: "utf8", mode: 0o600, flag: "wx" });
341
- return;
342
- } catch (error) {
343
- if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
344
- }
345
-
346
- const existing = readClaim(path);
347
- const conflict = claimConflict(existing, requested);
348
- if (!conflict) return;
349
-
350
- const leaseExpiresAt = Date.parse(existing.leaseExpiresAt);
351
- const staleRecoveryEnabled = process.env.ALLYE_PI_RECOVER_STALE_LOCK === "1";
352
- const ownerPidState = processState(existing.pid);
353
- if (!staleRecoveryEnabled || !Number.isFinite(leaseExpiresAt) || Date.now() <= leaseExpiresAt || ownerPidState !== "dead") {
354
- const reason = ownerPidState === "alive"
355
- ? "owner process is active"
356
- : ownerPidState === "unknown"
357
- ? "owner process state is inconclusive"
358
- : "stale recovery is disabled, lease is active, or required lease data is invalid";
359
- throw new Error(`${conflict}; refusing stale-lock recovery (${reason}). Set ALLYE_PI_RECOVER_STALE_LOCK=1 only after confirming the local PID is gone and the lease expired.`);
360
- }
361
-
362
- const recoveryMarker = `${path}.recovering`;
363
- try {
364
- writeFileSync(recoveryMarker, JSON.stringify({ ownerToken: existing.ownerToken, pid: existing.pid }), { encoding: "utf8", mode: 0o600, flag: "wx" });
365
- } catch {
366
- throw new Error(`orchestration lock ${path} is already being recovered; refusing concurrent stale-lock recovery`);
367
- }
368
- try {
369
- const current = readClaim(path);
370
- if (current.ownerToken !== existing.ownerToken) {
371
- throw new Error(`orchestration lock ${path} changed owner during stale recovery; refusing takeover`);
372
- }
373
- const quarantine = `${path}.stale-${existing.ownerToken}-${randomUUID()}`;
374
- renameSync(path, quarantine);
375
- try {
376
- writeFileSync(path, JSON.stringify(requested, null, 2), { encoding: "utf8", mode: 0o600, flag: "wx" });
377
- } catch (writeError) {
378
- try { renameSync(quarantine, path); } catch { /* fail closed; quarantine remains for manual recovery */ }
379
- throw writeError;
380
- }
381
- try { unlinkSync(quarantine); } catch { /* retained quarantine is safe for manual audit */ }
382
- } finally {
383
- try { unlinkSync(recoveryMarker); } catch { /* marker is advisory; stale recovery remains fail-closed */ }
384
- }
385
- }
386
-
387
- export function releaseLocalClaim(claim: OrchestrationClaim): boolean {
388
- const path = orchestrationLockPath(lockRoot(), claim.workItemKey);
389
- if (!existsSync(path)) return false;
390
- const existing = readClaim(path);
391
- if (claimConflict(existing, claim) !== null) return false;
392
- try {
393
- unlinkSync(path);
394
- return true;
395
- } catch (error) {
396
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
397
- throw error;
398
- }
399
- }
400
-
401
- export function shutdownOwnership(ownership: OwnershipState, waits: Map<string, AbortController>): boolean {
402
- for (const controller of waits.values()) controller.abort();
403
- waits.clear();
404
- ownership.waits.clear();
405
- if (!ownership.claim) return false;
406
- const released = releaseLocalClaim(ownership.claim);
407
- if (released) ownership.claim = undefined;
408
- return released;
409
- }
410
-
411
269
  async function loadStartupContext(): Promise<StartupContext> {
412
270
  const sections: string[] = [];
413
271
  try {
@@ -454,25 +312,14 @@ async function loadPromptContext(prompt: string): Promise<string> {
454
312
  }
455
313
  }
456
314
 
457
- function modeInstructions(mode: PiMode): string {
458
- if (mode === "orchestrator") {
459
- return `## Allye Pi role: orchestrator
460
- You are the explicitly selected Pi master for this work item.
461
- - Allye is the source of truth for work items, skills, memories, decisions, and review/delivery state.
462
- - Follow the canonical Allye workflow and gates; do not invent a second workflow.
463
- - Do not implement a story yourself when coordinating it. Dispatch exactly one story at a time to an isolated worktree and executor pane.
464
- - Use the allye_runtime tool only for the Herdr detect/spawn/dispatch/wait/collect contract. The pane cwd stays at the plugin-enabled repository root; absolute worktree paths belong in the briefing.
465
- - Never close panes you did not create, stop Herdr, merge, push, or publish without explicit user approval.
466
- - Hermes and Pi are alternative masters. Confirm that Hermes is not coordinating the same work item before taking control.`;
467
- }
468
- return `## Allye Pi role: executor
469
- You are the executor for this session (the safe default).
470
- - Read the canonical Allye skills and the exact story/tasks or handover before changing code.
471
- - Implement only the assigned story/task scope, with the execution skill's verification and reporting rules.
472
- - Follow the canonical execution skill's status protocol: the executor may use Allye to advance its assigned tasks to in_progress and review when the corresponding work and verification are actually complete.
473
- - Do not create or dispatch panes, assume orchestration, or change the assigned scope. Hermes and Pi may both manage status; being allowed to advance assigned task status does not make this session a master.
474
- - If Hermes is the master, follow Hermes' briefing and return a durable implementation/review trace through Allye; do not take control of the work item.
475
- - Stop and report when the scope or a locked decision is unclear; do not guess.`;
315
+ function toolkitInstructions(): string {
316
+ return `## Allye Pi toolkit
317
+ ${adaptiveInstructions(describeCapabilities())}
318
+ - Use canonical Allye skills as composable playbooks and choose the smallest useful next step for the user's intent.
319
+ - Discovery and Product Planning may produce research, documents, memories, or proposed work items; do not create work items until the user approves the proposal.
320
+ - Tasks are recommended for meaningful, delegated, multi-step, or review-heavy work, not universally required. If the user approves a no-task path, record the choice when useful and verify the result.
321
+ - Use subagents when available and beneficial. Use Herdr only when available and beneficial; never require either one.
322
+ - Use Allye for durable context, decisions, work items, and verification evidence. Ask before consequential mutations.`;
476
323
  }
477
324
 
478
325
  function sleep(ms: number): Promise<void> {
@@ -552,9 +399,9 @@ async function waitForInteractiveShell(pane: string): Promise<void> {
552
399
  throw new Error(`Herdr pane ${pane} did not reach an interactive shell within 30 seconds`);
553
400
  }
554
401
 
555
- export function activeToolsForMode(activeTools: string[], mode: PiMode): string[] {
402
+ export function activeToolsForCapabilities(activeTools: string[], capabilities: AllyeCapabilities): string[] {
556
403
  const next = new Set(activeTools);
557
- if (mode === "orchestrator") next.add(RUNTIME_TOOL_NAME);
404
+ if (capabilities.herdr) next.add(RUNTIME_TOOL_NAME);
558
405
  else next.delete(RUNTIME_TOOL_NAME);
559
406
  return [...next];
560
407
  }
@@ -564,10 +411,8 @@ export function dispatchStateIsAcceptable(stateText: string): boolean {
564
411
  return /working|idle|done/i.test(stateText);
565
412
  }
566
413
 
567
- function requireOrchestrator(mode: PiMode): void {
568
- if (mode !== "orchestrator") {
569
- throw new Error("Herdr orchestration is disabled in executor mode. Set ALLYE_PI_MODE=orchestrator explicitly for the Pi master.");
570
- }
414
+ function requireHerdr(capabilities: AllyeCapabilities): void {
415
+ if (!capabilities.herdr) throw new Error("Herdr is not available in this Pi session; use another available capability or continue locally.");
571
416
  }
572
417
 
573
418
  function requireAbsolutePath(value: unknown, name: string): string {
@@ -577,9 +422,7 @@ function requireAbsolutePath(value: unknown, name: string): string {
577
422
  return value;
578
423
  }
579
424
 
580
- async function runtimeOperation(mode: PiMode, params: Record<string, unknown>, registerWait: WaitRegistrar, cancelWait: WaitCanceller, ownership: OwnershipState): Promise<unknown> {
581
- requireOrchestrator(mode);
582
- if (!ownership.claim) throw new Error("Pi orchestrator has no explicit orchestration claim; provide ALLYE_ORCHESTRATION_RUN_ID and ALLYE_WORK_ITEM_KEY");
425
+ async function runtimeOperation(capabilities: AllyeCapabilities, params: Record<string, unknown>, registerWait: WaitRegistrar, cancelWait: WaitCanceller, ownership: DelegationState): Promise<unknown> {
583
426
  const operation = params.operation;
584
427
  if (operation === "detect") {
585
428
  if (process.env.HERDR_ENV !== "1") return { available: false, reason: "HERDR_ENV is not 1" };
@@ -591,9 +434,7 @@ async function runtimeOperation(mode: PiMode, params: Record<string, unknown>, r
591
434
  }
592
435
  }
593
436
 
594
- if (process.env.HERDR_ENV !== "1") {
595
- throw new Error("Herdr runtime is not active in this Pi session (HERDR_ENV=1 is required).");
596
- }
437
+ requireHerdr(capabilities);
597
438
 
598
439
  if (operation === "spawn") {
599
440
  const cwd = requireAbsolutePath(params.cwd, "cwd");
@@ -610,7 +451,10 @@ async function runtimeOperation(mode: PiMode, params: Record<string, unknown>, r
610
451
  const newPane = collectValues(afterLayout, "pane_id").find((id) => !beforePanes.has(id));
611
452
  if (!newPane) throw new Error("Herdr split completed but no new pane id was returned");
612
453
  const paneRecords = collectPaneRecords(afterLayout);
613
- const record = paneRecords.find((candidate) => candidate.paneId === newPane && candidate.workspaceId === String((afterLayout as Record<string, unknown>).workspace_id ?? candidate.workspaceId) && candidate.tabId === String((afterLayout as Record<string, unknown>).tab_id ?? candidate.tabId));
454
+ const layoutMetadata = afterLayout as Record<string, unknown>;
455
+ const record = paneRecords.find((candidate) => candidate.paneId === newPane
456
+ && candidate.workspaceId === String(layoutMetadata.workspace_id ?? candidate.workspaceId)
457
+ && candidate.tabId === String(layoutMetadata.tab_id ?? candidate.tabId));
614
458
  if (!record) throw new Error("Herdr returned a pane without workspace/tab ownership metadata");
615
459
  ownership.panes.push(record);
616
460
  await waitForInteractiveShell(newPane);
@@ -690,11 +534,11 @@ async function runtimeOperation(mode: PiMode, params: Record<string, unknown>, r
690
534
  throw new Error(`Unknown runtime operation: ${String(operation)}`);
691
535
  }
692
536
 
693
- function registerRuntimeTool(pi: ExtensionAPI, getMode: () => PiMode, registerWait: WaitRegistrar, cancelWait: WaitCanceller, state: OwnershipState): void {
537
+ function registerRuntimeTool(pi: ExtensionAPI, getCapabilities: () => AllyeCapabilities, registerWait: WaitRegistrar, cancelWait: WaitCanceller, state: DelegationState): void {
694
538
  pi.registerTool({
695
- name: "allye_runtime",
539
+ name: RUNTIME_TOOL_NAME,
696
540
  label: "Allye Runtime",
697
- description: "Drive Herdr from Pi orchestrator mode using detect, spawn, dispatch, wait, and Allye-backed collect.",
541
+ description: "Use optional Herdr capabilities for delegation, waiting, and collecting results when available.",
698
542
  parameters: Type.Object({
699
543
  operation: Type.String({ description: "detect | spawn | dispatch | wait | collect" }),
700
544
  cwd: Type.Optional(Type.String({ description: "Absolute plugin-enabled repository root for spawn" })),
@@ -711,7 +555,7 @@ function registerRuntimeTool(pi: ExtensionAPI, getMode: () => PiMode, registerWa
711
555
  }),
712
556
  async execute(_toolCallId, params) {
713
557
  try {
714
- const result = await runtimeOperation(getMode(), params as Record<string, unknown>, registerWait, cancelWait, state);
558
+ const result = await runtimeOperation(getCapabilities(), params as Record<string, unknown>, registerWait, cancelWait, state);
715
559
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], details: result };
716
560
  } catch (error) {
717
561
  const message = error instanceof Error ? error.message : String(error);
@@ -722,14 +566,15 @@ function registerRuntimeTool(pi: ExtensionAPI, getMode: () => PiMode, registerWa
722
566
  }
723
567
 
724
568
  export default function allyePiAdapter(pi: ExtensionAPI): void {
725
- let mode = initialMode();
569
+ const capabilities = describeCapabilities();
726
570
  let startupContext: StartupContext = { text: "", teamSelectionRequired: false, allyeUnavailable: true, teams: [] };
727
571
  let firstPrompt = true;
572
+ let usingAllyeBootstrap = "";
728
573
  let shuttingDown = false;
729
574
  let activeContext: ExtensionContext | undefined;
730
575
  const waits = new Map<string, AbortController>();
731
576
  const settledWaits = new Set<string>();
732
- const ownership: OwnershipState = { panes: [], waits: new Set() };
577
+ const ownership: DelegationState = { panes: [], waits: new Set() };
733
578
 
734
579
  const registerWait: WaitRegistrar = (name, timeoutMs) => {
735
580
  if (settledWaits.has(name)) return { registered: false, name, timeoutMs, duplicate: true, settled: true };
@@ -785,8 +630,8 @@ export default function allyePiAdapter(pi: ExtensionAPI): void {
785
630
  waits.delete(name);
786
631
  };
787
632
 
788
- const syncModeTools = () => {
789
- pi.setActiveTools(activeToolsForMode(pi.getActiveTools(), mode));
633
+ const syncCapabilityTools = () => {
634
+ pi.setActiveTools(activeToolsForCapabilities(pi.getActiveTools(), capabilities));
790
635
  };
791
636
 
792
637
  pi.registerMessageRenderer(WAIT_EVENT_TYPE, () => undefined);
@@ -796,49 +641,34 @@ export default function allyePiAdapter(pi: ExtensionAPI): void {
796
641
  pi.on("session_start", async (_event, ctx) => {
797
642
  shuttingDown = false;
798
643
  activeContext = ctx;
799
- mode = initialMode();
800
644
  firstPrompt = true;
801
- syncModeTools();
802
- if (ctx.hasUI) ctx.ui.setStatus("allye-pi", `loading Allye context (${mode})…`);
645
+ usingAllyeBootstrap = loadUsingAllyeSkill();
646
+ syncCapabilityTools();
647
+ if (ctx.hasUI) ctx.ui.setStatus("allye-pi", "loading Allye context…");
803
648
  if (process.env.ALLYE_PI_NATIVE_BOOTSTRAP !== "0") startupContext = await loadStartupContext();
804
- if (mode === "orchestrator") {
805
- try {
806
- const claim = claimFromEnvironment(ctx.sessionManager.getSessionId());
807
- acquireLocalClaim(ctx.cwd, claim);
808
- ownership.claim = claim;
809
- } catch (error) {
810
- mode = "executor";
811
- syncModeTools();
812
- startupContext = {
813
- ...startupContext,
814
- text: `${startupContext.text}\n## Pi orchestration blocked\n${error instanceof Error ? error.message : String(error)}\nPi remains executor; status management for assigned tasks remains allowed.`,
815
- };
816
- if (ctx.hasUI) ctx.ui.notify("Pi orchestration blocked; no compatible ownership claim was acquired. Executor mode remains active.", "warning");
817
- }
818
- }
819
649
  if (startupContext.teamSelectionRequired && ctx.hasUI) {
820
650
  ctx.ui.notify("Allye has multiple teams and no active team. Use /allye-team <name|prefix|id> before team-scoped work.", "warning");
821
651
  }
822
- if (ctx.hasUI) ctx.ui.setStatus("allye-pi", `Allye ${mode} ready`);
652
+ if (ctx.hasUI) ctx.ui.setStatus("allye-pi", "Allye ready");
823
653
  });
824
654
 
825
655
  pi.on("session_shutdown", () => {
826
656
  shuttingDown = true;
827
657
  activeContext = undefined;
828
- try {
829
- shutdownOwnership(ownership, waits);
830
- } catch (error) {
831
- console.error(`Allye Pi: could not release orchestration lock safely: ${error instanceof Error ? error.message : String(error)}`);
832
- }
658
+ for (const controller of waits.values()) controller.abort();
659
+ waits.clear();
660
+ ownership.waits.clear();
661
+ ownership.panes.length = 0;
833
662
  });
834
663
 
835
664
  pi.on("before_agent_start", async (event) => {
836
665
  const sections = [
837
- `<allye-pi-adapter>\n${modeInstructions(mode)}\n\nCanonical Allye skills are loaded from ${canonicalSkillsPath()} — do not create copies.\n${startupContext.teamSelectionRequired ? "\n" + startupContext.text : ""}\n</allye-pi-adapter>`,
666
+ `<allye-pi-adapter>\n${toolkitInstructions()}\n\nCanonical Allye skills are loaded from ${canonicalSkillsPath()} — do not create copies.\n${startupContext.teamSelectionRequired ? "\n" + startupContext.text : ""}\n</allye-pi-adapter>`,
838
667
  ];
839
- if (firstPrompt && process.env.ALLYE_PI_NATIVE_BOOTSTRAP !== "0") {
668
+ if (firstPrompt) {
840
669
  firstPrompt = false;
841
- if (startupContext.text) sections.push(`<allye-pi-startup-context>\n${startupContext.text}\n</allye-pi-startup-context>`);
670
+ if (usingAllyeBootstrap) sections.push(`<allye-using-allye-bootstrap>\n${usingAllyeBootstrap}\n</allye-using-allye-bootstrap>`);
671
+ if (process.env.ALLYE_PI_NATIVE_BOOTSTRAP !== "0" && startupContext.text) sections.push(`<allye-pi-startup-context>\n${startupContext.text}\n</allye-pi-startup-context>`);
842
672
  if (!startupContext.teamSelectionRequired && !startupContext.allyeUnavailable) {
843
673
  const relevant = await loadPromptContext(event.prompt);
844
674
  if (relevant) sections.push(`<allye-pi-relevant-memory>\n${relevant}\n</allye-pi-relevant-memory>`);
@@ -847,45 +677,16 @@ export default function allyePiAdapter(pi: ExtensionAPI): void {
847
677
  return { systemPrompt: `${event.systemPrompt}\n\n${sections.join("\n\n")}` };
848
678
  });
849
679
 
850
- pi.registerCommand("allye-mode", {
851
- description: "Select executor or orchestrator mode for this Pi session",
852
- getArgumentCompletions: (prefix) => ["executor", "orchestrator"]
853
- .filter((value) => value.startsWith(prefix.trim()))
854
- .map((value) => ({ value, label: value })),
855
- handler: async (args, ctx) => {
856
- const selected = args?.trim().toLowerCase();
857
- if (selected !== "executor" && selected !== "orchestrator") {
858
- ctx.ui.notify("Usage: /allye-mode executor|orchestrator", "error");
859
- return;
860
- }
861
- if (selected === "orchestrator" && resolveOrchestrationMode(process.env) === "hermes") {
862
- ctx.ui.notify("Hermes owns orchestration for this process; Pi remains executor.", "warning");
863
- return;
864
- }
865
- if (selected === mode) {
866
- ctx.ui.notify(`Allye mode is already ${mode} for this session`, "info");
867
- return;
868
- }
869
- if (selected === "executor") {
870
- // Downgrading must release the orchestration claim and abort waits so
871
- // this session cannot keep coordinating after becoming an executor.
872
- shutdownOwnership(ownership, waits);
873
- ownership.panes.length = 0;
874
- }
875
- if (selected === "orchestrator" && !ownership.claim) {
876
- try {
877
- const claim = claimFromEnvironment(ctx.sessionManager.getSessionId());
878
- acquireLocalClaim(ctx.cwd, claim);
879
- ownership.claim = claim;
880
- } catch (error) {
881
- ctx.ui.notify(`Pi orchestration blocked: ${error instanceof Error ? error.message : String(error)}`, "error");
882
- return;
883
- }
884
- }
885
- mode = selected;
886
- syncModeTools();
887
- ctx.ui.setStatus("allye-pi", `Allye ${mode}`);
888
- ctx.ui.notify(`Allye mode set to ${mode} for this session`, "info");
680
+ pi.registerCommand("allye-capabilities", {
681
+ description: "Show capabilities available to the Allye toolkit in this Pi session",
682
+ handler: async (_args, ctx) => {
683
+ const available = [
684
+ `Allye/MCP: ${capabilities.allyeMcp ? "available" : "unavailable"}`,
685
+ "filesystem: available",
686
+ `subagents: ${capabilities.subagents ? "available" : "unavailable"}`,
687
+ `Herdr: ${capabilities.herdr ? "available" : "unavailable"}`,
688
+ ].join("; ");
689
+ ctx.ui.notify(`${available}. Tasks are recommended, not mandatory.`, "info");
889
690
  },
890
691
  });
891
692
 
@@ -907,5 +708,5 @@ export default function allyePiAdapter(pi: ExtensionAPI): void {
907
708
  },
908
709
  });
909
710
 
910
- registerRuntimeTool(pi, () => mode, registerWait, cancelWait, ownership);
711
+ registerRuntimeTool(pi, () => capabilities, registerWait, cancelWait, ownership);
911
712
  }
@@ -11,8 +11,7 @@ An **agent runtime** owns real agent processes the human can watch, attach to, a
11
11
  over. This skill defines what the plugin needs from one, so the Orchestrator can drive
12
12
  any runtime that satisfies the contract rather than one specific tool.
13
13
 
14
- Load this only when the session hook reported a runtime. Without one, dispatch falls back
15
- to the manual handover and the dispatched-subagent modes, both of which work unchanged.
14
+ Load this only when a compatible runtime is actually detected and the user benefits from delegation. Without one, continue locally or use another available subagent mechanism; never let the absence or degradation of a runtime block delivery.
16
15
 
17
16
  Concrete commands for the runtimes we implement live in `references/`. This file is the
18
17
  contract; the reference is the implementation.
@@ -105,19 +104,19 @@ through.
105
104
 
106
105
  ## The obligation on the dispatched side
107
106
 
108
- The contract closes only because the dispatched agent carries the other half:
107
+ When a managed work item exists, the dispatched agent should leave a durable trace in
108
+ Allye before settling: task statuses and an implementation/review memory as applicable.
109
+ For an explicitly approved no-task path, return a durable result through the host session
110
+ or another agreed channel and do not invent work items merely to satisfy this contract.
109
111
 
110
- > **Before settling, leave a durable trace in Allye.** Executor: task statuses advanced,
111
- > plus an implementation memory. Reviewer: a memory holding the findings.
112
-
113
- This is already what `execution` and `review` do. Here it becomes load-bearing rather than
114
- good practice — it is the entire result channel.
112
+ This keeps Allye authoritative when it is in scope while allowing local, low-risk work
113
+ to complete without a mandatory work-item workflow.
115
114
 
116
115
  ## Teardown
117
116
 
118
- Destroy only what you created, and only after the work it held is merged and verified.
119
- Never close a pane, tab, workspace, or session the plugin did not create. Never stop the
120
- runtime's server.
117
+ Destroy only what you created, and only after the work it held is merged and verified or
118
+ the user explicitly authorizes cleanup. Never close a pane, tab, workspace, or session the
119
+ plugin did not create. Never stop the runtime's server.
121
120
 
122
121
  ## Implementations
123
122
 
@@ -7,8 +7,8 @@ category: methodology
7
7
 
8
8
  # Technical Development Workflow
9
9
 
10
- Implements one task with discipline: read the existing code first, drive it with TDD,
11
- verify it actually works, and track progress in Allye.
10
+ Implements a scoped change with discipline: read the existing code first, drive it with TDD when applicable,
11
+ verify it actually works, and use Allye task tracking when a task exists or the user approves creating one. A small or explicitly approved no-task change still follows the same read, test, and verification discipline.
12
12
 
13
13
  **Scope, if you arrived via a `story-execution` or `correction` handover (see `handover-protocol`):** read only the one story and its tasks named in the handover — nothing else. A `correction` handover carries only the failed findings, not the whole story again; fix exactly what it lists.
14
14
 
@@ -24,6 +24,8 @@ Get task → Search context → Move to in_progress → Read code → TDD (Red
24
24
 
25
25
  ## Step 1: Get the Task and Context
26
26
 
27
+ If a task or handover exists, fetch it and treat its acceptance criteria and locked decisions as authoritative. If no task exists, confirm that the user approved the no-task path, define the scope and verification command locally, and continue without inventing a work item.
28
+
27
29
  Fetch the task you're about to implement:
28
30
 
29
31
  ```
@@ -74,6 +76,8 @@ If there are multiple independent tasks (same wave), they can be done in any ord
74
76
 
75
77
  ## Step 3: Move Task to In Progress
76
78
 
79
+ If a task exists, signal that work has started. If no task exists, record the local scope in the implementation plan and do not make a status call.
80
+
77
81
  Signal that work has started:
78
82
 
79
83
  ```
@@ -301,6 +305,8 @@ Examples of things that need human action:
301
305
 
302
306
  ## Step 7: Move Task to Review
303
307
 
308
+ For a tracked task, advance it to review only after the verification evidence is green. For an approved no-task change, report the same evidence directly and do not create or advance a status.
309
+
304
310
  <!-- adapted from superpowers:verification-before-completion (MIT) — evidence before assertions -->
305
311
  **Evidence before assertions.** Don't advance a task because it looks right — run the tests, read the actual output, and confirm each acceptance criterion against that output before proceeding. "Should work" is not the same as "ran and passed."
306
312
 
@@ -308,13 +314,13 @@ The task's verification command is what supplies that evidence. Advancing a task
308
314
  `review` without having run it green — or without having recorded a `verification:
309
315
  manual` observation — is the assertion this rule exists to forbid.
310
316
 
311
- Once all acceptance criteria are verifiably met and tests pass:
317
+ Once all acceptance criteria are verifiably met and tests pass for a tracked task:
312
318
 
313
319
  ```
314
320
  work_status_next(id: "{task uuid}")
315
321
  ```
316
322
 
317
- This advances the task forward to the "review" status. **Do NOT call `work_status_done` here** — passing its own tests makes a task ready for review, not done. "Done" only happens after the Reviewer returns ✅, and that final move (`review` → `done`) is the Orchestrator's, not yours (see the `orchestrator` skill §7).
323
+ This advances the task forward to the "review" status. **Do NOT call `work_status_done` here** — passing its own tests makes a task ready for review, not done. For a no-task change, completion means the agreed scope and verification are reported; no status transition is implied.
318
324
 
319
325
  ---
320
326
 
@@ -7,7 +7,7 @@ category: methodology
7
7
 
8
8
  # Handover Protocol
9
9
 
10
- A handover is how context moves between Allye workflow phases without carrying a chat's full history forward. Each phase runs in its own fresh, lean-context conversation; when it finishes, it emits a handover — a block of chat text the user reads, approves, and pastes as the first message of the next chat. The next chat's bootstrap detects the marker and loads the right skill automatically.
10
+ A handover is one optional way context moves between Allye playbooks without carrying a chat's full history forward. Use it for meaningful, multi-step, delegated, or cross-session work where a durable briefing adds value. For short local work, stay in the current session rather than manufacturing phase boundaries. When a handover is used, the user reads, approves, and pastes the block as the first message of the next chat; the bootstrap detects the marker and loads the right skill automatically.
11
11
 
12
12
  **A handover is never saved as a memory or a file.** The Allye doc and work items it references are the durable record — the handover text itself is a disposable transport vehicle, copied by hand in both directions (including Orchestrator ↔ Executor's correction loop).
13
13
 
@@ -50,17 +50,17 @@ Six types. Each is a distinct handoff with its own objective and field set — n
50
50
  **Executor is reachable both ways.** In manual mode it receives `story-execution`/`correction` handovers as documented above. In automatic mode (Orchestrator's choice, offered per story) it's dispatched via the `Agent` tool instead, the same way the reviewer axes are — see `agents/executor.md`. Either way the content is the same; only the transport differs.
51
51
  <!-- opencode-exclude:end -->
52
52
 
53
- ## 3. Writing a good handover (mandatory checklist)
53
+ ## 3. Writing a good handover checklist
54
54
 
55
55
  <!-- adapted from humanlayer/humanlayer create_handoff (Apache-2.0) and EveryInc/compound-engineering-plugin artifact_readiness contracts (MIT) -->
56
56
 
57
- Before emitting any handover, confirm:
57
+ Before emitting a handover, confirm the following when applicable:
58
58
 
59
59
  - **Concrete keys, not vague pointers.** "The story we discussed" is not acceptable — write the actual `STORY-KEY`. The receiving chat has zero memory of this conversation.
60
60
  - **References carry an explicit read instruction, not just keys.** The handover must tell the receiver to actually fetch and read what it lists — work items via `work_get`/`work_children` (the entire subtree under the parent, when the handover is scoped to a parent item like a feature or epic), docs via `doc_get`. A key list without a "read all of this" instruction invites skimming, and the receiving chat has no other way to know reading is mandatory.
61
61
  - **Locked decisions are carried forward, verbatim.** If a decision was locked in this phase, restate it in the handover — don't make the next chat re-derive or, worse, re-litigate it.
62
62
  - **Scope matches the type.** `story-execution` carries exactly one story; `correction` carries only the failed findings, not the whole story again. Padding a handover with everything "just in case" defeats the purpose of a lean next chat.
63
- - **The reminder line is never dropped.** Every handover ends with the "stop and ask" line — it's the single most important sentence in this whole protocol.
63
+ - **The reminder line is preserved.** A handover should end with the "stop and ask" line — it prevents guessed decisions in the next session.
64
64
  <!-- adapted from mattpocock/skills triage AGENT-BRIEF (MIT) — durability of handoff artifacts -->
65
65
  - **Name interfaces and contracts, not file paths and line numbers.** Write "the `SkillConfig`
66
66
  type gains an optional `schedule` field", never "open `src/types/skill.ts` and edit line 42."
@@ -48,17 +48,15 @@ Every `memory_save` passes through conflict resolution: Allye checks for similar
48
48
 
49
49
  ## 2. When to search
50
50
 
51
- ### Conversation start (mandatory)
51
+ ### Conversation start (proportional)
52
52
 
53
- <EXTREMELY_IMPORTANT>
54
- At the start of EVERY conversation, run at least these searches before doing anything else:
53
+ For consequential, multi-step, resumed, or team-scoped work, search before acting:
55
54
 
56
55
  1. `memory_search(query: "Session State")` — find where the user left off
57
56
  2. `memory_search(query: "{topic of user's request}")` — find relevant context
58
57
  3. `memory_search(query: "{work item key}")` — if a specific item is mentioned (e.g., "PROJ-123")
59
- </EXTREMELY_IMPORTANT>
60
58
 
61
- React to what you find: session state → summarize it for the user ("Last time we were working on X, you completed Y, and the next step was Z"); decisions → keep them in mind and respect locked ones; nothing → proceed, noting this is a fresh context.
59
+ For a short, low-risk, local request, the search may be skipped. Never claim a search was performed when the Allye memory capability is unavailable. React to what you find: session state → summarize it; decisions → respect locked ones; nothing → proceed with the available context.
62
60
 
63
61
  ### Before decisions and implementation
64
62
 
@@ -97,15 +95,13 @@ Save decisions and blockers immediately — not batched at session end. **Locked
97
95
 
98
96
  ### Session state — handled by the /save protocol
99
97
 
100
- <EXTREMELY_IMPORTANT>
101
- Before the conversation ends (or whenever /save is invoked), run the full /save protocol in §4. Session state is not a single ad hoc save — it's the first of four coordinated steps that also promote durable knowledge from the session into team sectors.
102
- </EXTREMELY_IMPORTANT>
98
+ When the session is consequential, resumed, or contains durable decisions, use the full /save protocol in §4 before ending. For short, low-risk, local work, saving may be skipped. If Allye persistence is unavailable, report that continuity was not saved rather than implying it was.
103
99
 
104
100
  ---
105
101
 
106
102
  ## 4. The /save protocol
107
103
 
108
- `/save` is a 4-step protocol that turns a session's raw activity into (a) a personal continuity snapshot and (b) correctly-scoped, durable team knowledge. Run all four steps, in order, every time /save is invoked or a conversation is about to end.
104
+ `/save` is a 4-step protocol that turns a session's raw activity into (a) a personal continuity snapshot and (b) correctly-scoped, durable team knowledge. Run all four steps, in order, whenever the user explicitly invokes `/save` or when a consequential session is being closed. Do not force it for trivial local work.
109
105
 
110
106
  ### Step 1 — Consolidate the session (sector: `sessions`, personal)
111
107
 
@@ -276,7 +272,7 @@ The relocation flow surfaces that backlog and offers to promote it — **once pe
276
272
 
277
273
  ### When to offer it
278
274
 
279
- Offer at most once per user, ever — not once per session. A good moment is early in a session, right after the mandatory memory searches (§2), at a natural pause before the actual task. Never interrupt an in-progress task to offer this; if the user is mid-implementation or mid-debugging, wait for a natural break or skip it for that session — it isn't urgent, and it'll still be there next time unless already offered.
275
+ Offer at most once per user, ever — not once per session. A good moment is early in a consequential session, after any relevant memory searches (§2), at a natural pause before the actual task. Never interrupt an in-progress task to offer this; if the user is mid-implementation or mid-debugging, wait for a natural break or skip it for that session. If the relocation capability is unavailable, do not present the flow.
280
276
 
281
277
  ### Check the guard first
282
278
 
@@ -7,10 +7,12 @@ category: methodology
7
7
 
8
8
  # Orchestrator
9
9
 
10
- You drive delivery. You don't plan — that already happened in Technical Planning — and you don't implement — that's Executor's job, one fresh chat at a time. You coordinate: who owns what, what's in progress, whether a story is genuinely done, and when to bring in review.
10
+ You coordinate delivery when the user chooses a durable, multi-step work-item workflow. You don't have to be invoked for every change, and you don't plan or implement when coordinating — Technical Planning and Executor playbooks remain available as needed. You coordinate: who owns what, what's in progress, whether a story is genuinely done, and when to bring in review. If the user chooses a local no-task path, stop coordinating rather than creating work items to justify this skill.
11
11
 
12
12
  ## 1. On start
13
13
 
14
+ If no approved work item or handover exists, do not invent one. Explain the available lightweight local path or ask whether the user wants to opt into tracked delivery.
15
+
14
16
  If you arrived via a `technical-to-orchestration` handover (see `handover-protocol`), read it in full — it's your only context, there is no prior conversation to fall back on.
15
17
 
16
18
  Then load the actual work. **The handover's list is an index to verify against, not a substitute for reading:**
@@ -16,9 +16,11 @@ Use this when the user talks about: requirements, business needs, product scope,
16
16
  ## Workflow Overview
17
17
 
18
18
  ```
19
- Understand context → Search memories → Define hierarchy → Create items → Save decisions
19
+ Understand intent → Search context when useful → Propose hierarchy when traceability helps → Get approval → Create items → Save decisions
20
20
  ```
21
21
 
22
+ Product Planning is recommended for durable product scope, not a mandatory prerequisite for every local implementation. Never create items without explicit approval.
23
+
22
24
  ---
23
25
 
24
26
  ## Step 0: Check for a Discovery Doc
@@ -29,7 +31,7 @@ If you arrived via a `discovery-to-planning` handover (see the `handover-protoco
29
31
 
30
32
  ## Step 1: Understand the Business Context
31
33
 
32
- Before creating anything, have a conversation with the user to understand:
34
+ Before proposing or creating anything, have a conversation with the user to understand:
33
35
 
34
36
  1. **What problem are we solving?** — The business need or opportunity
35
37
  2. **Who is it for?** — Target users or stakeholders
@@ -114,7 +116,7 @@ Epic: User Authentication System
114
116
  └── Story: User can log out from all devices
115
117
  ```
116
118
 
117
- Wait for the user to approve, modify, or add to this structure before proceeding.
119
+ Wait for the user to approve, modify, or add to this structure before proceeding. If the user explicitly chooses a no-work-item path for a small or local change, do not create a hierarchy; keep the scope and verification visible instead.
118
120
 
119
121
  ---
120
122
 
@@ -11,7 +11,7 @@ This is the entry point for ideas that aren't ready to become work items yet. Us
11
11
 
12
12
  <!-- adapted from superpowers:brainstorming (MIT) -->
13
13
  <HARD-GATE>
14
- Do not create any Allye work item, and do not treat any direction as decided, until the user has explicitly approved it. This skill's only output is a Discovery Doc and a handover — never an Epic, Feature, or Story. If you catch yourself about to call `work_create`, stop: that's Product Planning's job, one conversation later.
14
+ Do not create any Allye work item, and do not treat any direction as decided, until the user has explicitly approved it. This skill's default output is a Discovery Doc and a handover, but a user may explicitly choose an informal local research note or no persistence when that is sufficient. If you catch yourself about to call `work_create`, stop and ask for approval first.
15
15
  </HARD-GATE>
16
16
 
17
17
  ## 1. The core rule: ask, don't decide
@@ -59,4 +59,4 @@ When a direction is approved — explicitly, by the user, not inferred — synth
59
59
 
60
60
  ## 4. What this skill is not
61
61
 
62
- This is not Technical Planning — no stack decisions, no architecture, no tasks here. It is not Product Planning — no Epic/Feature/Story structure gets created here (see the HARD-GATE above). If the user arrives already knowing exactly what they want and just needs it turned into work items, route to `product-planning` directly instead — Sandbox isn't the right entry point for that.
62
+ This is not Technical Planning — no stack decisions, no architecture, no tasks here. It is not Product Planning — no Epic/Feature/Story structure gets created here without approval. If the user arrives already knowing exactly what they want and just needs it turned into work items, recommend `product-planning`; if they only need a small local change, the adaptive toolkit may proceed without forcing a planning phase.
@@ -7,7 +7,7 @@ category: methodology
7
7
 
8
8
  # Technical Planning Workflow
9
9
 
10
- Turns a story into actionable tasks through a **Discussion Phase** — surfacing gray areas, capturing decisions, and writing tasks with verifiable acceptance criteria and a runnable verification command.
10
+ Turns an approved, meaningful story into actionable tasks through a **Discussion Phase** — surfacing gray areas, capturing decisions, and writing tasks with verifiable acceptance criteria and a runnable verification command. It is recommended for durable, multi-step, shared, or review-heavy work, not mandatory for every local change.
11
11
 
12
12
  **Never assume — always ask.** The user knows the direction they want, sometimes only after researching further. Your job is to surface the gray areas and present real options, not to quietly pick one and present it as the plan.
13
13
 
@@ -23,6 +23,8 @@ Get story → Search context → Discussion Phase → Create tasks → Move stor
23
23
 
24
24
  ## 0. Scope: one feature, story by story
25
25
 
26
+ If the user explicitly chooses a no-task path, do not enter this workflow or create tasks. State the local scope, assumptions, and verification plan instead.
27
+
26
28
  The steps below plan **one story at a time** — but the unit Technical Planning hands to the Orchestrator is the **feature**. When the target feature has multiple stories (a `planning-to-technical` handover usually lists several), work through every story under that feature in sequence, one at a time, running the full workflow (Steps 1–6) for each before starting the next.
27
29
 
28
30
  **Emit only ONE `technical-to-orchestration` handover, at the very end — after the last story in the feature is planned, never after each individual story.** That handover must cover every story and every task, grouped by wave, which is only honest once all of the feature's stories have actually been planned. If the session ends before all stories are planned, save session state (Step 6) and resume in a fresh chat — don't emit a partial feature-level handover.
@@ -73,9 +75,9 @@ If tasks already exist, review them instead of creating new ones. The user may w
73
75
  ## Step 3: Discussion Phase
74
76
 
75
77
  <EXTREMELY_IMPORTANT>
76
- Complete the discussion phase — surfacing ambiguity before committing to a plan —
77
- before creating any task. Skipping it leads to rework, wrong assumptions, and
78
- wasted effort.
78
+ For tracked work, complete the discussion phase — surfacing ambiguity before committing
79
+ to a plan — before creating any task. A user-approved no-task path may skip task creation,
80
+ but must still surface scope-changing ambiguity and define proportional verification.
79
81
  </EXTREMELY_IMPORTANT>
80
82
 
81
83
  ### 3.1 Identify Gray Areas
@@ -90,7 +92,7 @@ Examples of gray areas:
90
92
  - Should we optimize for read speed or write speed?
91
93
  - How much backward compatibility do we need?
92
94
 
93
- **Mandatory:** whenever a story implies infrastructure, stack, or architecture choices, that is always a gray area to raise explicitly — never left to chance or agent discretion by default.
95
+ Whenever tracked work implies infrastructure, stack, or architecture choices, raise them explicitly as gray areas. For a small no-task change, use established defaults only when the choice is low-risk and reversible; otherwise ask before acting.
94
96
 
95
97
  **Need more than a hunch to evaluate an option?** Dispatch the `deep-search` agent (web research) or the `code-analyzer` agent (analyze a public repo), both via the `Agent` tool — research is available here, not only in Sandbox. Bring findings back into the discussion before locking a decision.
96
98
 
@@ -9,9 +9,9 @@ category: bootstrap
9
9
 
10
10
  You have access to the **Allye platform** — a project management and knowledge system with 12 MCP tools covering work items, documentation, sprints, boards, memories, skills, and more.
11
11
 
12
- This skill teaches you **when and how** to use those tools effectively. It does NOT replace the tools — it gives you methodology and workflow discipline on top of them.
12
+ This skill teaches you **when and how** to use those tools effectively. It does NOT replace the tools — it gives you portable playbooks and proportional guardrails on top of them.
13
13
 
14
- **What you must do:** search memories at the start of every conversation, detect which workflow phase applies, load the matching skill for that phase, and save memories before the conversation ends.
14
+ **Default posture:** Allye is an adaptive toolkit, not a mandatory phase chain. Use Allye MCP, filesystem, subagents, Herdr, work items, and memories when available and useful; degrade honestly when a capability is absent. Recommend traceable tasks for meaningful work, but support an explicitly approved no-task path.
15
15
 
16
16
  ---
17
17
 
@@ -23,51 +23,25 @@ These instructions are written in English, but every response, question, suggest
23
23
 
24
24
  ## Asking Questions
25
25
 
26
- <!-- adapted from superpowers:brainstorming (MIT) — "prefer multiple choice questions when possible" -->
27
- <HARD-GATE>
28
- This applies in every phase — Sandbox exploration, Technical Planning's gray-area discussion, Orchestrator's assignee/status calls, Product Planning's scope forks, anywhere you'd otherwise type a question in prose. Whenever a fork has a small set of nameable options — who owns something, which of two approaches, narrow vs. broad scope, yes/no confirmations — use the `AskUserQuestion` tool instead of typing the question out in prose. It's faster to answer by picking than by typing, and its free-text "Other" option means nothing is lost versus asking in prose.
26
+ When a fork has a small set of nameable options — ownership, approach, scope, or confirmation — prefer the runtime's native structured-question mechanism when one exists. If the runtime has no such tool, ask one focused question in concise prose and make the options explicit. Never silently choose a consequential option.
29
27
 
30
- **Never bundle an open-ended sub-question with an enumerable one in the same message.** If a question has both parts — "what's driving this, and who's the audience?" — split them: ask the open part in prose, and the enumerable part (audience: internal/external/both) via `AskUserQuestion`, as two separate turns. Bundling them is what makes the whole thing read as "open-ended" and skip the tool entirely — don't let the open half smuggle the enumerable half out of a structured question.
28
+ **Never bundle an open-ended sub-question with an enumerable one in the same message.** Split them into separate turns. Reserve plain conversational questions for genuinely open-ended forks where enumerating options would require guessing.
31
29
 
32
- Reserve plain conversational questions for forks that are genuinely open-ended on their own — where enumerating options would just be guessing at what the user might say (e.g. "what's driving this?", "describe the current process").
33
- </HARD-GATE>
30
+ ## 1. Context and Memory Protocol
34
31
 
35
- Note: `AskUserQuestion` inside skill-loaded contexts has known reliability issues in Claude Code itself (tracked upstream, e.g. anthropics/claude-code#29547, #30544) — it can silently return an empty answer or fall back to prose regardless of this instruction. Treat a prose fallback as a degraded but acceptable outcome, not a failure to correct mid-conversation; the underlying discipline (ask before deciding) still holds either way.
36
-
37
- ---
38
-
39
- ## 1. Memory First Protocol
40
-
41
- <EXTREMELY_IMPORTANT>
42
- At the START of every conversation, you MUST search for relevant memories before doing anything else.
43
- At the END of every conversation, you MUST run the `memory-protocol` skill's /save process.
44
- </EXTREMELY_IMPORTANT>
32
+ Use durable Allye context when the Allye MCP capability is available and the work benefits from continuity. Do not pretend a memory was searched or saved when the capability is unavailable.
45
33
 
46
34
  ### On conversation start
47
35
 
48
- **Step 1: Initialize Allye** — Call `initialize` (action: `init`) to load user context, team info, and core documents. This is mandatory and must happen before anything else.
49
-
50
- **Step 2: Check active team** — If the init response shows the user belongs to multiple teams and no team is active, ask the user which team they want to work with and call `team_switch` with the team name. Do not proceed until a team is selected — most tools require a team context.
51
-
52
- **Step 3: Search for memories** using `memory_search`:
53
-
54
- 1. `"Session State"` — find where the user left off last time
55
- 2. `"decision {topic}"` — find previous decisions about the current topic
56
- 3. `"{work item key}"` — find context for the specific item being discussed (e.g., "PROJ-123")
57
-
58
- **Step 4: Greet the user** — Summarize what you know (from init + memories) before proceeding. If no memories are found, proceed normally with the context from init.
59
-
60
- **Step 5: Note the runtime, if one was reported.** The session hook may have injected a line beginning `Agent runtime: `. If it did, the `agent-runtime` skill is available and the Orchestrator will dispatch through it. Do not load that skill now — it loads when work is about to be dispatched, which is the whole point of it being a separate skill.
36
+ 1. Initialize when available. If it fails, state the limitation and continue with local context unless Allye is required.
37
+ 2. Resolve team context proportionally: ask before a team-scoped operation when multiple teams have no active selection; non-team-scoped work may continue.
38
+ 3. Search relevant memories when useful, especially for consequential, multi-step, or resumed work.
39
+ 4. Distinguish confirmed Allye context, local repository context, and assumptions.
40
+ 5. Use subagents or Herdr only when detected and beneficial; never make them prerequisites.
61
41
 
62
42
  ### On conversation end (and saving in general)
63
43
 
64
- Follow the `memory-protocol` skill's current save process — load it if you haven't already, and don't rely on an outdated inline copy of it here. That skill is the single source of truth for:
65
-
66
- - The **/save protocol** for session state (a 4-step process: consolidate the session into one `sector: "sessions"` memory, mine it for knowledge worth promoting to team sectors, react to each save's outcome, and promote any session todo that outlives the session)
67
- - **When to save mid-conversation** (decisions, trade-offs, blockers, non-obvious context — each in its correct sector)
68
- - **Tag conventions** and sector selection
69
-
70
- One rule worth internalizing before you even load it: **always pass `sector` explicitly** — it's optional and defaults to `knowledge`, which is rarely the sector you actually want.
44
+ Follow the `memory-protocol` skill when Allye memory is available and the context is worth preserving. Save decisions, trade-offs, blockers, and durable session state; do not save trivial noise. Always pass `sector` explicitly. If persistence is unavailable, report the unsaved state rather than implying continuity.
71
45
 
72
46
  ---
73
47
 
@@ -94,7 +68,7 @@ If no handover marker is present, fall through to the decision table below as be
94
68
  | Define product requirements, create epics/features/stories | Product Planning | `allye-product-planning` |
95
69
  | Plan technical tasks for a story, discuss approach | Technical Planning | `allye-technical-planning` |
96
70
  | Coordinate delivery of an already-planned feature — assign work, track status, drive tasks through review | Orchestrator | `orchestrator` |
97
- | Implement code, write tests, develop features | Technical Development | `allye-technical-development` |
71
+ | Implement code, write tests, develop features | Technical Development or a lightweight local implementation loop | `allye-technical-development` when a task/work item exists |
98
72
  | Review code, check implementation quality | Technical Review | `allye-technical-review` |
99
73
  | Finalize delivery, close story, update docs | Technical Delivery | `allye-technical-delivery` |
100
74
 
@@ -127,21 +101,27 @@ Then read the skill content and follow its instructions. The loaded skill takes
127
101
 
128
102
  If the user's request doesn't match any workflow phase (e.g., general questions, quick lookups, team management), use the Allye tools directly without loading a specific skill. The tools reference in section 4 gives you a quick overview.
129
103
 
130
- ---
104
+ ### Direct implementation path
105
+
106
+ When the user asks for a small or low-risk local change and no task exists, recommend the lightweight execution loop: state scope, get explicit approval to bypass task creation, read first, test and verify, then report. Do not create a task merely because the execution skill exists.
131
107
 
132
- ## 3. Workflow Gates
108
+ ---
133
109
 
134
- <HARD-GATE>
135
- These rules are non-negotiable. Do not proceed past a gate without meeting its condition.
110
+ ## 3. Adaptive Checkpoints and Guardrails
136
111
 
137
- 1. **No implementation without tasks.** Do not write code for a story that has no tasks created. Run Technical Planning first.
112
+ There is no universal phase gate. Choose the smallest useful loop for the user's intent:
138
113
 
139
- 2. **No skipping the discussion phase.** When planning tasks for a story, always identify gray areas and present options to the user before creating tasks. Locked decisions must be captured as memories.
114
+ `intent → context → research (optional) → decision/consent → action → verification → persistence (optional)`
140
115
 
141
- 3. **No status changes without work.** Do not move a work item to "done" (`work_status_done`) unless the actual work is completed and verified.
116
+ Checkpoints may be skipped or repeated. Use this policy:
142
117
 
143
- 4. **TDD when applicable.** If you can write `expect(fn(input)).toBe(output)` before writing `fn`, you MUST write the test first (Red → Green → Refactor). If not (UI, infra, integration), test after implementation — but always test.
144
- </HARD-GATE>
118
+ - **Low-risk, local, reversible work:** proceed with a concise plan and proportionate verification. Do not create work items merely to satisfy the toolkit.
119
+ - **Meaningful, multi-step, delegated, shared, or review-heavy work:** recommend a task or work item for traceability. Explain the benefit and ask for approval before creating it.
120
+ - **No-task path:** if the user explicitly approves bypassing task creation, record that choice when useful, keep scope explicit, and verify the result proportionally.
121
+ - **Consequential mutations** (external systems, destructive operations, publication, deployment, status changes, or broad scope): obtain explicit consent unless the user already clearly authorized that exact action. Do not create or change work items/statuses without approval.
122
+ - **Ambiguity that changes scope, risk, or architecture:** stop and ask. Minor implementation details may use established defaults, stated assumptions, and a reversible change.
123
+ - **TDD:** use Red → Green → Refactor when the behavior can be specified as a deterministic example; otherwise test after implementation, but do not skip verification.
124
+ - **Done claims:** only claim completion after reading actual verification output and distinguishing implementation evidence from review, deployment, or other pending gates.
145
125
 
146
126
  ---
147
127
 
@@ -172,11 +152,11 @@ If you catch yourself thinking any of these, STOP:
172
152
 
173
153
  | Rationalization | Why it's wrong |
174
154
  |-----------------|---------------|
175
- | "I don't need to search memories, this is a fresh conversation." | The user may have worked on this topic before. Context from past sessions prevents rework and contradictions. Always search. |
176
- | "I'll just start coding, tasks aren't necessary for something this small." | Tasks create accountability, enable progress tracking, and force you to think before acting. Planning first, always. |
155
+ | "I don't need to search memories, this is a fresh conversation." | For consequential or resumed work, prior context may prevent rework; search when useful and available, but do not pretend an unavailable capability was used. |
156
+ | "I'll just start coding, tasks aren't necessary for something this small." | For meaningful work, tasks improve accountability; for small work, use the approved no-task path with explicit scope and verification. |
177
157
  | "I'll skip the tests, the implementation is straightforward." | Straightforward code still breaks. TDD catches assumptions. Write the test. |
178
158
  | "I'll move the story to done, the tasks are mostly complete." | "Mostly" is not "done". Verify all tasks are complete first. |
179
- | "I don't need to save memories, I covered everything in the conversation." | Conversations are ephemeral. The next session starts from zero without memories. Always save state. |
159
+ | "I don't need to save memories, I covered everything in the conversation." | Save durable decisions and consequential session state when persistence is available; skip trivial noise and report when persistence is unavailable. |
180
160
  | "I'll load all skills at once to be prepared." | Skills are loaded on-demand for a reason — loading everything bloats context and degrades quality. Load only what's needed. |
181
161
 
182
162
  ---