loadout-ai 0.3.1 → 0.4.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/CHANGELOG.md +67 -0
- package/MASTER_PLAN.md +177 -13
- package/README.md +157 -284
- package/dashboard/app.js +4 -4
- package/dashboard/index.html +4 -4
- package/dist/src/cli.js +27 -21
- package/dist/src/core/active-policy.js +172 -38
- package/dist/src/core/active-set.js +13 -4
- package/dist/src/core/adapters.js +10 -0
- package/dist/src/core/adopt.js +165 -32
- package/dist/src/core/agent-health-score.js +2 -2
- package/dist/src/core/catalog-coverage.js +2 -1
- package/dist/src/core/catalog-install.js +8 -1
- package/dist/src/core/catalog-release.js +2 -1
- package/dist/src/core/conformance.js +74 -0
- package/dist/src/core/install.js +11 -11
- package/dist/src/core/profiles.js +9 -4
- package/dist/src/core/ranking.js +1 -1
- package/dist/src/core/readme-claims.js +10 -0
- package/dist/src/core/readme-facts.js +40 -0
- package/dist/src/core/recommend.js +104 -12
- package/dist/src/core/runtime-tools.js +5 -2
- package/dist/src/core/scheduler.js +2 -1
- package/dist/src/core/snapshot.js +58 -13
- package/dist/src/core/state.js +8 -1
- package/dist/src/core/target-occupancy.js +50 -0
- package/dist/src/core/transaction.js +2 -1
- package/dist/src/core/uninstall.js +26 -2
- package/dist/src/dashboard.js +5 -2
- package/dist/src/shared/schemas.js +57 -0
- package/docs/FEATURE_TEST_MATRIX.md +16 -0
- package/docs/README_RESEARCH.md +36 -0
- package/docs/RELEASE_REVIEW.md +31 -5
- package/docs/REPOSITORY_STABILIZATION.md +190 -0
- package/docs/TESTING.md +50 -0
- package/docs/USER_TEST_GUIDE.md +42 -4
- package/docs/assets/loadout-hero.svg +259 -0
- package/docs/assets/loadout-mark.svg +54 -0
- package/docs/evidence/live-checks-2026-07-19.json +22 -0
- package/docs/evidence/live-checks.schema.json +28 -0
- package/docs/evidence/readme-claims.json +286 -0
- package/docs/superpowers/plans/2026-07-19-relatable-readme-hero.md +283 -0
- package/docs/superpowers/plans/2026-07-20-project-activation-safety.md +469 -0
- package/docs/superpowers/specs/2026-07-19-relatable-readme-hero-design.md +80 -0
- package/docs/superpowers/specs/2026-07-20-project-activation-safety-design.md +228 -0
- package/package.json +8 -4
- package/SIMPLE_PLAN.md +0 -44
- package/docs/plans/2026-07-18-release-0.3.md +0 -42
- package/docs/superpowers/plans/2026-07-18-cli-ux-polish.md +0 -86
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# Project Activation Safety Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Make project activation respect every agent's real active-skill capacity, tolerate recursively empty rollback residue, and recommend a compact project-relevant set.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Extract the existing bounded target-occupancy rule into one shared filesystem module used by setup and activation. Build per-agent budgets from the read-only installed-skill inventory, then score and slice reviewed library candidates per agent. Extend deterministic local project signals and recommendation metadata without introducing network or model calls.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript 5.7, Node.js 20+, Commander 12, Vitest 4, existing Loadout transaction/state/inventory modules.
|
|
10
|
+
|
|
11
|
+
## Global Constraints
|
|
12
|
+
|
|
13
|
+
- No model or external API call is required for recommendation or activation.
|
|
14
|
+
- No project source, filename, dependency, or outcome data leaves the machine.
|
|
15
|
+
- Never execute package code while scanning, recommending, or activating.
|
|
16
|
+
- Missing and recursively empty targets are unoccupied; files, symlinks, special entries, unreadable directories, and scans beyond 10,000 entries are occupied.
|
|
17
|
+
- `--limit` is a per-agent ceiling over managed plus unmanaged skills containing `SKILL.md`.
|
|
18
|
+
- Preview remains read-only; apply revalidates inside one rollback-safe transaction.
|
|
19
|
+
- Existing CLI flags remain valid; the project-plan JSON schema may add per-agent budgets in 0.4.1.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### Task 1: Share the bounded target-occupancy rule
|
|
24
|
+
|
|
25
|
+
**Files:**
|
|
26
|
+
|
|
27
|
+
- Create: `src/core/target-occupancy.ts`
|
|
28
|
+
- Create: `tests/target-occupancy.test.ts`
|
|
29
|
+
- Modify: `src/core/install.ts`
|
|
30
|
+
- Modify: `src/core/active-set.ts`
|
|
31
|
+
- Modify: `tests/active-set.test.ts`
|
|
32
|
+
|
|
33
|
+
**Interfaces:**
|
|
34
|
+
|
|
35
|
+
- Produces: `inspectTargetOccupancy(path: string, maximumEntries?: number): Promise<TargetOccupancy>`.
|
|
36
|
+
- Consumed by: setup collision checks and activation preview/apply checks.
|
|
37
|
+
|
|
38
|
+
- [x] **Step 1: Write failing occupancy tests**
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { mkdir, mkdtemp, symlink, writeFile } from "node:fs/promises";
|
|
42
|
+
import { tmpdir } from "node:os";
|
|
43
|
+
import { join } from "node:path";
|
|
44
|
+
import { expect, it } from "vitest";
|
|
45
|
+
import { inspectTargetOccupancy } from "../src/core/target-occupancy.js";
|
|
46
|
+
|
|
47
|
+
it("treats missing and recursively empty targets as unoccupied", async () => {
|
|
48
|
+
const root = await mkdtemp(join(tmpdir(), "loadout-target-"));
|
|
49
|
+
const empty = join(root, "skill");
|
|
50
|
+
await mkdir(join(empty, "nested"), { recursive: true });
|
|
51
|
+
await expect(
|
|
52
|
+
inspectTargetOccupancy(join(root, "missing")),
|
|
53
|
+
).resolves.toMatchObject({ occupied: false });
|
|
54
|
+
await expect(inspectTargetOccupancy(empty)).resolves.toMatchObject({
|
|
55
|
+
occupied: false,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("treats content, symlinks, and the inspection bound as occupied", async () => {
|
|
60
|
+
const root = await mkdtemp(join(tmpdir(), "loadout-target-"));
|
|
61
|
+
const content = join(root, "content");
|
|
62
|
+
const linked = join(root, "linked");
|
|
63
|
+
await mkdir(content);
|
|
64
|
+
await writeFile(join(content, "SKILL.md"), "content");
|
|
65
|
+
await symlink(content, linked);
|
|
66
|
+
await expect(inspectTargetOccupancy(content)).resolves.toMatchObject({
|
|
67
|
+
occupied: true,
|
|
68
|
+
reason: "content",
|
|
69
|
+
});
|
|
70
|
+
await expect(inspectTargetOccupancy(linked)).resolves.toMatchObject({
|
|
71
|
+
occupied: true,
|
|
72
|
+
reason: "symlink",
|
|
73
|
+
});
|
|
74
|
+
await expect(inspectTargetOccupancy(join(root), 1)).resolves.toMatchObject({
|
|
75
|
+
occupied: true,
|
|
76
|
+
reason: "inspection-limit",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- [x] **Step 2: Run the occupancy tests and verify RED**
|
|
82
|
+
|
|
83
|
+
Run: `npx vitest run tests/target-occupancy.test.ts`
|
|
84
|
+
Expected: FAIL because `src/core/target-occupancy.ts` does not exist.
|
|
85
|
+
|
|
86
|
+
- [x] **Step 3: Implement the shared predicate**
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { lstat, readdir } from "node:fs/promises";
|
|
90
|
+
import { join } from "node:path";
|
|
91
|
+
|
|
92
|
+
export interface TargetOccupancy {
|
|
93
|
+
occupied: boolean;
|
|
94
|
+
reason?:
|
|
95
|
+
"content" | "symlink" | "unsupported" | "unreadable" | "inspection-limit";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function inspectTargetOccupancy(
|
|
99
|
+
path: string,
|
|
100
|
+
maximumEntries = 10_000,
|
|
101
|
+
): Promise<TargetOccupancy> {
|
|
102
|
+
let root;
|
|
103
|
+
try {
|
|
104
|
+
root = await lstat(path);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (
|
|
107
|
+
error &&
|
|
108
|
+
typeof error === "object" &&
|
|
109
|
+
"code" in error &&
|
|
110
|
+
(error as { code?: string }).code === "ENOENT"
|
|
111
|
+
)
|
|
112
|
+
return { occupied: false };
|
|
113
|
+
return { occupied: true, reason: "unreadable" };
|
|
114
|
+
}
|
|
115
|
+
if (root.isSymbolicLink()) return { occupied: true, reason: "symlink" };
|
|
116
|
+
if (!root.isDirectory()) return { occupied: true, reason: "unsupported" };
|
|
117
|
+
const queue = [path];
|
|
118
|
+
let inspected = 0;
|
|
119
|
+
while (queue.length) {
|
|
120
|
+
const directory = queue.pop()!;
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
124
|
+
} catch {
|
|
125
|
+
return { occupied: true, reason: "unreadable" };
|
|
126
|
+
}
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
inspected += 1;
|
|
129
|
+
if (inspected > maximumEntries)
|
|
130
|
+
return { occupied: true, reason: "inspection-limit" };
|
|
131
|
+
if (entry.isDirectory() && !entry.isSymbolicLink())
|
|
132
|
+
queue.push(join(directory, entry.name));
|
|
133
|
+
else
|
|
134
|
+
return {
|
|
135
|
+
occupied: true,
|
|
136
|
+
reason: entry.isSymbolicLink() ? "symlink" : "content",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { occupied: false };
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- [x] **Step 4: Replace duplicate setup logic and activation `pathExists` checks**
|
|
145
|
+
|
|
146
|
+
In `src/core/install.ts`, import `inspectTargetOccupancy` and replace the recursive block in `assertActiveTargetsUnoccupied` with:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
for (const target of targets) {
|
|
150
|
+
if ((await inspectTargetOccupancy(target)).occupied) occupied.push(target);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
In `src/core/active-set.ts`, use the same predicate when enabling and include the reason in the blocker. Re-run the check immediately before the transaction copies any target; remove only targets proven recursively empty.
|
|
155
|
+
|
|
156
|
+
- [x] **Step 5: Add the activation regression**
|
|
157
|
+
|
|
158
|
+
Extend `tests/active-set.test.ts` so a disabled library entry with `empty/nested` under its active target previews without blockers and applies, while a target containing `notes.txt` remains blocked and unchanged.
|
|
159
|
+
|
|
160
|
+
- [x] **Step 6: Verify GREEN and commit**
|
|
161
|
+
|
|
162
|
+
Run: `npx vitest run tests/target-occupancy.test.ts tests/install.test.ts tests/active-set.test.ts`
|
|
163
|
+
Expected: all tests pass.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
git add src/core/target-occupancy.ts src/core/install.ts src/core/active-set.ts tests/target-occupancy.test.ts tests/active-set.test.ts
|
|
167
|
+
git commit -m "fix: share safe target occupancy checks"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Task 2: Enforce per-agent total active capacity
|
|
171
|
+
|
|
172
|
+
**Files:**
|
|
173
|
+
|
|
174
|
+
- Modify: `src/core/active-policy.ts`
|
|
175
|
+
- Modify: `src/core/active-set.ts`
|
|
176
|
+
- Modify: `tests/active-policy.test.ts`
|
|
177
|
+
|
|
178
|
+
**Interfaces:**
|
|
179
|
+
|
|
180
|
+
- Consumes: `detectAgents()`, `scanInstalledSkills()`, and reviewed activation records.
|
|
181
|
+
- Produces: `AgentActiveSetPlan` records with total, managed, unmanaged, capacity, and selected candidates.
|
|
182
|
+
|
|
183
|
+
- [x] **Step 1: Write failing mixed-agent capacity tests**
|
|
184
|
+
|
|
185
|
+
Add a fixture with 12 unmanaged `SKILL.md` directories under Claude's skill root, zero under Codex, and 40 reviewed disabled candidates per agent. Assert:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const plan = await planProjectActivation(project, {
|
|
189
|
+
agents: ["claude-code", "codex"],
|
|
190
|
+
limit: 30,
|
|
191
|
+
});
|
|
192
|
+
expect(
|
|
193
|
+
plan.agentPlans.find((item) => item.agent === "claude-code"),
|
|
194
|
+
).toMatchObject({
|
|
195
|
+
activeBefore: 12,
|
|
196
|
+
unmanagedBefore: 12,
|
|
197
|
+
capacity: 18,
|
|
198
|
+
});
|
|
199
|
+
expect(
|
|
200
|
+
plan.agentPlans.find((item) => item.agent === "claude-code")!.selected,
|
|
201
|
+
).toHaveLength(18);
|
|
202
|
+
expect(plan.agentPlans.find((item) => item.agent === "codex")).toMatchObject({
|
|
203
|
+
activeBefore: 0,
|
|
204
|
+
capacity: 30,
|
|
205
|
+
});
|
|
206
|
+
expect(
|
|
207
|
+
plan.agentPlans.find((item) => item.agent === "codex")!.selected,
|
|
208
|
+
).toHaveLength(30);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Add a second test where Claude already has 30 unmanaged skills and Codex has none. Claude must receive zero additions and Codex must still receive candidates.
|
|
212
|
+
|
|
213
|
+
- [x] **Step 2: Run the capacity tests and verify RED**
|
|
214
|
+
|
|
215
|
+
Run: `npx vitest run tests/active-policy.test.ts`
|
|
216
|
+
Expected: FAIL because the plan has one managed-only global budget and no `agentPlans`.
|
|
217
|
+
|
|
218
|
+
- [x] **Step 3: Add per-agent plan types and inventory-backed budgets**
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
export interface AgentActiveSetPlan {
|
|
222
|
+
agent: AgentId;
|
|
223
|
+
activeBefore: number;
|
|
224
|
+
managedBefore: number;
|
|
225
|
+
unmanagedBefore: number;
|
|
226
|
+
capacity: number;
|
|
227
|
+
selected: ActiveSetCandidate[];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface ProjectActiveSetPlan {
|
|
231
|
+
project: ProjectSignals;
|
|
232
|
+
limit: number;
|
|
233
|
+
agents?: AgentId[];
|
|
234
|
+
agentPlans: AgentActiveSetPlan[];
|
|
235
|
+
activation?: ActivationPlan;
|
|
236
|
+
warnings: string[];
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Resolve requested agents through `detectAgents`, call `scanInstalledSkills`, and create one budget from each inventory summary. Score only that agent's reviewed disabled records, diversify them, and slice to that agent's capacity.
|
|
241
|
+
|
|
242
|
+
- [x] **Step 4: Merge exact per-agent activation plans**
|
|
243
|
+
|
|
244
|
+
Call `planActivationChange("enable", selectors, { agents: [agent] })` once per non-empty agent selection and combine `changes`, `skipped`, `warnings`, `blocked`, and unique package IDs into one transaction plan. `applyProjectActivation` continues to call `applyActivationChange` exactly once.
|
|
245
|
+
|
|
246
|
+
Before copying, re-scan inventory and abort if any agent's current total would make the planned additions exceed `limit`.
|
|
247
|
+
|
|
248
|
+
- [x] **Step 5: Format truthful per-agent budgets**
|
|
249
|
+
|
|
250
|
+
Replace the global budget line with:
|
|
251
|
+
|
|
252
|
+
```text
|
|
253
|
+
Claude Code: 12 active (0 managed, 12 unmanaged); 18/30 slots available
|
|
254
|
+
Codex: 0 active (0 managed, 0 unmanaged); 30/30 slots available
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Group additions beneath their target agent and do not duplicate one global list that implies identical capacities.
|
|
258
|
+
|
|
259
|
+
- [x] **Step 6: Verify GREEN and commit**
|
|
260
|
+
|
|
261
|
+
Run: `npx vitest run tests/active-policy.test.ts tests/active-set.test.ts tests/skill-inventory.test.ts`
|
|
262
|
+
Expected: all tests pass, including 18 Claude additions and 30 Codex additions.
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
git add src/core/active-policy.ts src/core/active-set.ts tests/active-policy.test.ts
|
|
266
|
+
git commit -m "fix: enforce per-agent active skill limits"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Task 3: Detect bounded Node CLI and package signals
|
|
270
|
+
|
|
271
|
+
**Files:**
|
|
272
|
+
|
|
273
|
+
- Modify: `src/shared/types.ts`
|
|
274
|
+
- Modify: `src/core/recommend.ts`
|
|
275
|
+
- Modify: `tests/recommend.test.ts`
|
|
276
|
+
|
|
277
|
+
**Interfaces:**
|
|
278
|
+
|
|
279
|
+
- Extends `ProjectSignals` with `roles: string[]` and `tools: string[]`.
|
|
280
|
+
- Consumed by package recommendations and skill ranking.
|
|
281
|
+
|
|
282
|
+
- [x] **Step 1: Write failing signal tests**
|
|
283
|
+
|
|
284
|
+
Create a package fixture containing `bin`, `publishConfig`, `commander`, `zod`, `vitest`, `@playwright/test`, `prepack`, and an `mcp` keyword, plus `SECURITY.md`. Assert:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
expect(signals.roles).toEqual(
|
|
288
|
+
expect.arrayContaining([
|
|
289
|
+
"node-cli",
|
|
290
|
+
"npm-package",
|
|
291
|
+
"release",
|
|
292
|
+
"mcp",
|
|
293
|
+
"security",
|
|
294
|
+
]),
|
|
295
|
+
);
|
|
296
|
+
expect(signals.tools).toEqual(
|
|
297
|
+
expect.arrayContaining(["commander", "zod", "vitest", "playwright"]),
|
|
298
|
+
);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- [x] **Step 2: Run the recommendation tests and verify RED**
|
|
302
|
+
|
|
303
|
+
Run: `npx vitest run tests/recommend.test.ts`
|
|
304
|
+
Expected: FAIL because `roles` and `tools` are absent.
|
|
305
|
+
|
|
306
|
+
- [x] **Step 3: Extend project signals and parse only known metadata**
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
export interface ProjectSignals {
|
|
310
|
+
root: string;
|
|
311
|
+
languages: string[];
|
|
312
|
+
frameworks: string[];
|
|
313
|
+
roles: string[];
|
|
314
|
+
tools: string[];
|
|
315
|
+
files: string[];
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
In `scanProject`, derive roles and tools only from root entry names, known manifest fields, dependencies/devDependencies, scripts, publish metadata, and keywords. Do not recursively read arbitrary source content.
|
|
320
|
+
|
|
321
|
+
- [x] **Step 4: Format readable detected roles**
|
|
322
|
+
|
|
323
|
+
Add display labels so human output says `TypeScript, Node CLI, npm package, Vitest, Playwright, MCP tooling` while JSON retains stable lowercase identifiers.
|
|
324
|
+
|
|
325
|
+
- [x] **Step 5: Verify GREEN and commit**
|
|
326
|
+
|
|
327
|
+
Run: `npx vitest run tests/recommend.test.ts tests/outcomes.test.ts`
|
|
328
|
+
Expected: all tests pass.
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
git add src/shared/types.ts src/core/recommend.ts tests/recommend.test.ts
|
|
332
|
+
git commit -m "feat: detect local cli and package signals"
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Task 4: Diversify project skill selection
|
|
336
|
+
|
|
337
|
+
**Files:**
|
|
338
|
+
|
|
339
|
+
- Modify: `src/core/active-policy.ts`
|
|
340
|
+
- Modify: `tests/active-policy.test.ts`
|
|
341
|
+
|
|
342
|
+
**Interfaces:**
|
|
343
|
+
|
|
344
|
+
- Consumes: extended `ProjectSignals`.
|
|
345
|
+
- Produces: evidence-threshold candidates grouped by capability family.
|
|
346
|
+
|
|
347
|
+
- [x] **Step 1: Write failing relevance tests**
|
|
348
|
+
|
|
349
|
+
Add reviewed candidates named `javascript-typescript-jest`, `vitest-testing`, five Playwright variants, `cli-design`, `npm-package`, and `mcp-security`. For a Vitest Node CLI fixture, assert Jest is absent, CLI/npm/MCP candidates are present, and no more than three browser-testing candidates are selected.
|
|
350
|
+
|
|
351
|
+
- [x] **Step 2: Run the active-policy tests and verify RED**
|
|
352
|
+
|
|
353
|
+
Run: `npx vitest run tests/active-policy.test.ts`
|
|
354
|
+
Expected: FAIL because current ranking selects Jest and redundant Playwright variants.
|
|
355
|
+
|
|
356
|
+
- [x] **Step 3: Add exact signal rules and mismatch rejection**
|
|
357
|
+
|
|
358
|
+
Add role/tool rules for Node CLI, npm package, Vitest, Commander, Zod, MCP, release, and security. Reject a candidate matching `jest` when `vitest` is present and `jest` is absent.
|
|
359
|
+
|
|
360
|
+
- [x] **Step 4: Add deterministic family caps**
|
|
361
|
+
|
|
362
|
+
Implement `candidateFamily(unitId)` and deterministic caps: browser testing 3; documentation 2; code review 2; architecture 2; planning 3; security 3; language/tooling 5; uncategorized 3. Explicit full-selector pins bypass family caps but still consume capacity. Stop after eligible candidates are exhausted; never fill unused slots with candidates below the existing evidence threshold.
|
|
363
|
+
|
|
364
|
+
- [x] **Step 5: Verify GREEN and commit**
|
|
365
|
+
|
|
366
|
+
Run: `npx vitest run tests/active-policy.test.ts`
|
|
367
|
+
Expected: all relevance, diversity, pin, outcome, and per-agent capacity tests pass.
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
git add src/core/active-policy.ts tests/active-policy.test.ts
|
|
371
|
+
git commit -m "feat: diversify project skill selection"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Task 5: Label recommendation component types
|
|
375
|
+
|
|
376
|
+
**Files:**
|
|
377
|
+
|
|
378
|
+
- Modify: `src/shared/types.ts`
|
|
379
|
+
- Modify: `src/core/recommend.ts`
|
|
380
|
+
- Modify: `tests/recommend.test.ts`
|
|
381
|
+
|
|
382
|
+
**Interfaces:**
|
|
383
|
+
|
|
384
|
+
- Extends `PackageRecommendation` with `kind: "skill-library" | "mcp-runtime" | "unavailable"`.
|
|
385
|
+
- Uses catalog `components` to classify suggestions.
|
|
386
|
+
|
|
387
|
+
- [x] **Step 1: Write failing type-label tests**
|
|
388
|
+
|
|
389
|
+
Assert Superpowers formats as `skill library`, while Playwright MCP and GitHub MCP format as `MCP/runtime setup` and include a separate preview/setup hint rather than activation wording.
|
|
390
|
+
|
|
391
|
+
- [x] **Step 2: Run the recommendation tests and verify RED**
|
|
392
|
+
|
|
393
|
+
Run: `npx vitest run tests/recommend.test.ts`
|
|
394
|
+
Expected: FAIL because recommendations have no `kind`.
|
|
395
|
+
|
|
396
|
+
- [x] **Step 3: Classify catalog-backed recommendations**
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
function recommendationKind(
|
|
400
|
+
pkg: CatalogPackage,
|
|
401
|
+
): PackageRecommendation["kind"] {
|
|
402
|
+
if (pkg.components?.includes("skill")) return "skill-library";
|
|
403
|
+
if (pkg.components?.some((item) => item === "mcp" || item === "plugin"))
|
|
404
|
+
return "mcp-runtime";
|
|
405
|
+
return "unavailable";
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Attach the kind when adding each catalog recommendation and preserve it through local-outcome personalization.
|
|
410
|
+
|
|
411
|
+
- [x] **Step 4: Format kinds and next actions**
|
|
412
|
+
|
|
413
|
+
Human output must label every line and end MCP/runtime suggestions with the read-only recipe or explicit setup command supported by the package. It must not claim those integrations are automatically activatable skills.
|
|
414
|
+
|
|
415
|
+
- [x] **Step 5: Verify GREEN and commit**
|
|
416
|
+
|
|
417
|
+
Run: `npx vitest run tests/recommend.test.ts tests/outcomes.test.ts`
|
|
418
|
+
Expected: all tests pass.
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
git add src/shared/types.ts src/core/recommend.ts tests/recommend.test.ts
|
|
422
|
+
git commit -m "feat: label recommendation component types"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Task 6: Verify the product path and prepare the release candidate
|
|
426
|
+
|
|
427
|
+
**Files:**
|
|
428
|
+
|
|
429
|
+
- Modify: `CHANGELOG.md`
|
|
430
|
+
- Modify: `master_plan.md`
|
|
431
|
+
- Modify: `docs/USER_TEST_GUIDE.md`
|
|
432
|
+
- Modify: `scripts/cli-product-flow.mjs`
|
|
433
|
+
|
|
434
|
+
**Interfaces:**
|
|
435
|
+
|
|
436
|
+
- Consumes: all corrected preview/apply behavior.
|
|
437
|
+
- Produces: repeatable release and founder acceptance evidence.
|
|
438
|
+
|
|
439
|
+
- [x] **Step 1: Extend the CLI product-flow regression**
|
|
440
|
+
|
|
441
|
+
Add a journey that creates one agent with unmanaged skills and one empty agent, restores recursively empty snapshot residue, installs a disabled library, previews project activation, applies it, and rolls back the explicit activation snapshot. Assert unmanaged bytes are unchanged at every point.
|
|
442
|
+
|
|
443
|
+
- [x] **Step 2: Run the focused product flow after the unit-level RED/GREEN cycles**
|
|
444
|
+
|
|
445
|
+
Run: `npm run test:e2e:cli`
|
|
446
|
+
Expected: PASS for per-agent capacity, empty-target activation, atomic apply, and explicit rollback.
|
|
447
|
+
|
|
448
|
+
- [x] **Step 3: Update user-facing documentation**
|
|
449
|
+
|
|
450
|
+
Document that `--limit` includes unmanaged skills, Maximum remains disabled by default, recommendations distinguish skills from MCP/runtime setup, and project activation may choose different set sizes per agent.
|
|
451
|
+
|
|
452
|
+
- [x] **Step 4: Run the full local release gate**
|
|
453
|
+
|
|
454
|
+
Run: `npm run verify`
|
|
455
|
+
Expected: formatting, lint, typecheck, evidence checks, unit tests, CLI/readme/package flows, and performance checks all pass.
|
|
456
|
+
|
|
457
|
+
- [x] **Step 5: Build and inspect the packed npm artifact without publishing**
|
|
458
|
+
|
|
459
|
+
Run: `npm pack --dry-run`
|
|
460
|
+
Expected: the package contains the corrected compiled CLI and no untracked secret or local-state files.
|
|
461
|
+
|
|
462
|
+
- [x] **Step 6: Record completion and commit**
|
|
463
|
+
|
|
464
|
+
Mark only the implemented portions of `P18-27` complete, record exact test counts and remaining founder/npm steps, and keep real-profile activation blocked until the corrected package is published.
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
git add CHANGELOG.md MASTER_PLAN.md docs/USER_TEST_GUIDE.md scripts/cli-product-flow.mjs
|
|
468
|
+
git commit -m "test: cover safe project activation journey"
|
|
469
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Relatable README and Hero Design
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Make Viraj's current Loadout README more memorable to a first-time visitor without weakening its evidence boundaries. Replace the small slot mark with a clearly stronger original hero, add a concise human explanation of the loadout metaphor, correct canonical repository links, and document the actual GitHub Actions failure without pretending it is a code defect.
|
|
6
|
+
|
|
7
|
+
## Verified starting point
|
|
8
|
+
|
|
9
|
+
- Source: `VirajMishra1/loadout` default branch `main` at merge commit `194676910327176272ebe42982d13c6e8246f0aa`.
|
|
10
|
+
- The README already presents the verified `Choose -> Inspect -> Preview -> Apply -> Undo` flow and bounded warnings about unpublished `0.3.2`, preview/apply identity, catalog evidence, native-agent execution, security, and persistence.
|
|
11
|
+
- The failed upstream Actions job `88250042904` in run `29708871932` executed zero steps. Its sole annotation says the job did not start because recent account payments failed or the spending limit must be increased.
|
|
12
|
+
- Because no runner step started, there is no failing repository command to reproduce locally. Verification must instead run the workflow's intended commands locally and report that this validates the code but cannot reproduce GitHub account billing state.
|
|
13
|
+
- Canonical README/package links still point to the temporary `reddynitish/loadout` fork and must point to `VirajMishra1/loadout`.
|
|
14
|
+
|
|
15
|
+
## README story
|
|
16
|
+
|
|
17
|
+
Keep `## Why Loadout` in its current location after the verified product journey. Add one short paragraph before the existing evidence-backed benefits:
|
|
18
|
+
|
|
19
|
+
> Skills, plugins, MCP servers, and agent settings tend to accumulate one experiment at a time. Eventually it becomes hard to remember what is installed, where it came from, or how to undo it. In a game, a loadout is the deliberate set of tools chosen before a mission. Loadout brings that same discipline to AI coding agents: inspect the available equipment, choose intentionally, apply it through managed changes, and remove or roll it back later.
|
|
20
|
+
|
|
21
|
+
The final copy may be tightened for rhythm but must preserve these facts and must not introduce a founder narrative, generic marketing superlatives, universal safety, native-host execution, or stronger rollback guarantees than the implementation proves. Retain the three existing bullets for managed inventory, preview-first operations, and recoverable managed changes.
|
|
22
|
+
|
|
23
|
+
## Hero composition
|
|
24
|
+
|
|
25
|
+
Create `docs/assets/loadout-hero.svg` as a wide, dependency-free, theme-aware SVG and replace the README's small `loadout-mark.svg` reference only after rendered comparison shows the hero is materially stronger.
|
|
26
|
+
|
|
27
|
+
The composition reads left to right:
|
|
28
|
+
|
|
29
|
+
1. **Unmanaged edge:** a restrained cluster of loose extension/config tiles, crossing paths, and small labels sits outside a dashed management boundary. It is visibly disorganized but not cartoonishly chaotic.
|
|
30
|
+
2. **Developer choice:** a simple, original geometric developer figure at a compact workbench reaches toward one extension tile. The figure is symbolic rather than a detailed character, avoiding any resemblance to Ponytail's artwork or composition.
|
|
31
|
+
3. **Managed loadout:** five aligned equipment slots sit inside a clear rail. Selected slots use recognizable code-native symbols such as `>_`, a plug, linked nodes, or braces; the remaining slot can be empty. A small directional cue connects the chosen tile to the rail.
|
|
32
|
+
4. **Outcome:** the organized rail is visually calmer and more regular than the unmanaged edge. The image communicates selection and control, not an unsupported claim that every external tool is safe.
|
|
33
|
+
|
|
34
|
+
Use a wide `viewBox` suitable for a GitHub README hero, approximately 960 by 300. Use SVG paths and basic shapes only, with no raster content, scripts, animation, gradients, external fonts, remote references, or copied assets. Every visible symbol should remain legible when the hero is rendered near 720 pixels wide and at a smaller mobile width.
|
|
35
|
+
|
|
36
|
+
Use `currentColor` plus an internal `prefers-color-scheme` fallback so strokes and restrained fills have adequate contrast on GitHub light and dark themes. Include one meaningful `<title>` and `<desc>`, `role="img"`, and matching `aria-labelledby`. The README `<img>` needs concise alt text describing the developer moving extension tiles from a messy group into organized loadout slots.
|
|
37
|
+
|
|
38
|
+
The existing `docs/assets/loadout-mark.svg` remains available as a compact mark unless repository cleanup later proves it unused and removal is explicitly covered by tests and links. The task does not need to delete it.
|
|
39
|
+
|
|
40
|
+
## Canonical repository corrections
|
|
41
|
+
|
|
42
|
+
Update current canonical product metadata and visitor destinations from `reddynitish/loadout` to `VirajMishra1/loadout`:
|
|
43
|
+
|
|
44
|
+
- README CI workflow badge and image URL.
|
|
45
|
+
- README clone command.
|
|
46
|
+
- README issue-tracker link.
|
|
47
|
+
- `package.json` repository, homepage, and bugs URLs.
|
|
48
|
+
- `docs/evidence/live-checks.schema.json` `$id`.
|
|
49
|
+
- Tests/fixtures that intentionally assert canonical package metadata.
|
|
50
|
+
|
|
51
|
+
Do not rewrite historical evidence that accurately identifies the fork or dated fork CI runs. Historical links in `docs/REPOSITORY_STABILIZATION.md` remain historical evidence, not canonical product metadata.
|
|
52
|
+
|
|
53
|
+
## Check failure handling
|
|
54
|
+
|
|
55
|
+
No workflow bypass is allowed. Do not delete, skip, weaken, or condition the required verification job. The smallest correct repository action is to leave CI logic intact and accurately report the external billing/spending-limit root cause.
|
|
56
|
+
|
|
57
|
+
Before PR creation:
|
|
58
|
+
|
|
59
|
+
- Reconfirm the failed check annotation and absence of job steps.
|
|
60
|
+
- Run each intended required job command locally, including the exact `npm test -- --run` invocation.
|
|
61
|
+
- Run `npm run verify`.
|
|
62
|
+
- Run `npm run verify:full` because Playwright dependencies are available locally.
|
|
63
|
+
- Treat any local failure as a real defect and fix it test-first; do not call the external billing annotation a locally reproduced failure.
|
|
64
|
+
|
|
65
|
+
## Regression and rendering coverage
|
|
66
|
+
|
|
67
|
+
Update README-focused tests only for intended structure/content changes:
|
|
68
|
+
|
|
69
|
+
- require the new hero reference and genuine metaphor language;
|
|
70
|
+
- require canonical Viraj badge/clone/issue destinations;
|
|
71
|
+
- reject canonical README/package links to the temporary fork;
|
|
72
|
+
- preserve all six generated marker pairs, current hierarchy, executable offline product flow, warnings, and truth-boundary assertions.
|
|
73
|
+
|
|
74
|
+
Update package metadata tests for Viraj's canonical repository. Do not add a test that pretends GitHub billing can be reproduced locally.
|
|
75
|
+
|
|
76
|
+
Validate all README relative targets and heading fragments, SVG XML/accessibility structure, forbidden external SVG content, and light/dark renders at desktop and mobile sizes. Review the complete README as a first-time visitor for hierarchy, clarity, and unsupported implications.
|
|
77
|
+
|
|
78
|
+
## Delivery
|
|
79
|
+
|
|
80
|
+
Commit implementation on `codex/relatable-readme-hero`, push it to Viraj's repository, and open a ready pull request against `main`. Include the Actions billing root cause, local command evidence, rendered hero evidence, and truth boundaries in the PR body. Do not merge the pull request.
|