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 +5 -5
- package/docs/install-pi.md +24 -55
- package/package.json +1 -1
- package/packages/allye-pi/src/index.ts +89 -288
- package/skills/agent-runtime/SKILL.md +10 -11
- package/skills/execution/SKILL.md +10 -4
- package/skills/handover-protocol/SKILL.md +4 -4
- package/skills/memory-protocol/SKILL.md +6 -10
- package/skills/orchestrator/SKILL.md +3 -1
- package/skills/product-planning/SKILL.md +5 -3
- package/skills/sandbox/SKILL.md +2 -2
- package/skills/technical-planning/SKILL.md +7 -5
- package/skills/using-allye/SKILL.md +31 -51
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** |
|
|
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
|
-
- **
|
|
85
|
-
- **17 skills** —
|
|
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
|
-
- **
|
|
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
|
|
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)
|
package/docs/install-pi.md
CHANGED
|
@@ -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
|
|
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
|
-
##
|
|
77
|
+
## Adaptive toolkit
|
|
78
78
|
|
|
79
|
-
Pi
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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,
|
|
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
|
|
126
|
+
pi -e ./packages/allye-pi/src/index.ts -p "Report the available Allye capabilities"
|
|
154
127
|
```
|
|
155
128
|
|
|
156
|
-
When `
|
|
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
|
|
160
|
-
the
|
|
161
|
-
`work_children`, Review memories, and Implementation memories
|
|
162
|
-
completion. Normal completion, timeout, error, and abort are
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
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,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,
|
|
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
|
|
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
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
193
|
-
+ `Use
|
|
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
|
|
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
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
- Use
|
|
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
|
|
402
|
+
export function activeToolsForCapabilities(activeTools: string[], capabilities: AllyeCapabilities): string[] {
|
|
556
403
|
const next = new Set(activeTools);
|
|
557
|
-
if (
|
|
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
|
|
568
|
-
if (
|
|
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(
|
|
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
|
-
|
|
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
|
|
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,
|
|
537
|
+
function registerRuntimeTool(pi: ExtensionAPI, getCapabilities: () => AllyeCapabilities, registerWait: WaitRegistrar, cancelWait: WaitCanceller, state: DelegationState): void {
|
|
694
538
|
pi.registerTool({
|
|
695
|
-
name:
|
|
539
|
+
name: RUNTIME_TOOL_NAME,
|
|
696
540
|
label: "Allye Runtime",
|
|
697
|
-
description: "
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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
|
|
789
|
-
pi.setActiveTools(
|
|
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
|
-
|
|
802
|
-
|
|
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",
|
|
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
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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${
|
|
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
|
|
668
|
+
if (firstPrompt) {
|
|
840
669
|
firstPrompt = false;
|
|
841
|
-
if (
|
|
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-
|
|
851
|
-
description: "
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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, () =>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
|
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
|
|
11
|
-
verify it actually works, and
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
51
|
+
### Conversation start (proportional)
|
|
52
52
|
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
package/skills/sandbox/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
77
|
-
before creating any task.
|
|
78
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
**
|
|
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
|
-
|
|
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.**
|
|
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
|
-
|
|
33
|
-
</HARD-GATE>
|
|
30
|
+
## 1. Context and Memory Protocol
|
|
34
31
|
|
|
35
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
108
|
+
---
|
|
133
109
|
|
|
134
|
-
|
|
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
|
-
|
|
112
|
+
There is no universal phase gate. Choose the smallest useful loop for the user's intent:
|
|
138
113
|
|
|
139
|
-
|
|
114
|
+
`intent → context → research (optional) → decision/consent → action → verification → persistence (optional)`
|
|
140
115
|
|
|
141
|
-
|
|
116
|
+
Checkpoints may be skipped or repeated. Use this policy:
|
|
142
117
|
|
|
143
|
-
|
|
144
|
-
|
|
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." |
|
|
176
|
-
| "I'll just start coding, tasks aren't necessary for something this small." |
|
|
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." |
|
|
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
|
---
|