loadout-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/MASTER_PLAN.md +1711 -0
  3. package/README.md +453 -0
  4. package/SECURITY.md +18 -0
  5. package/SIMPLE_PLAN.md +44 -0
  6. package/catalog/discovered.json +10469 -0
  7. package/catalog/packages.json +1150 -0
  8. package/dashboard/app.js +607 -0
  9. package/dashboard/index.html +249 -0
  10. package/dashboard/styles.css +384 -0
  11. package/dist/src/cli.js +2735 -0
  12. package/dist/src/core/active-policy.js +233 -0
  13. package/dist/src/core/active-set.js +382 -0
  14. package/dist/src/core/adapters.js +254 -0
  15. package/dist/src/core/adopt.js +92 -0
  16. package/dist/src/core/agent-health-score.js +475 -0
  17. package/dist/src/core/agent-inspection.js +152 -0
  18. package/dist/src/core/agent-versions.js +131 -0
  19. package/dist/src/core/api.js +90 -0
  20. package/dist/src/core/atomic-file.js +22 -0
  21. package/dist/src/core/audit.js +174 -0
  22. package/dist/src/core/benchmark-campaign.js +496 -0
  23. package/dist/src/core/benchmark-evidence.js +458 -0
  24. package/dist/src/core/benchmark-fixtures.js +1157 -0
  25. package/dist/src/core/benchmark-runner.js +301 -0
  26. package/dist/src/core/benchmark-trust.js +795 -0
  27. package/dist/src/core/canary.js +77 -0
  28. package/dist/src/core/candidate-intelligence.js +661 -0
  29. package/dist/src/core/catalog-coverage.js +85 -0
  30. package/dist/src/core/catalog-install.js +186 -0
  31. package/dist/src/core/catalog-release.js +286 -0
  32. package/dist/src/core/catalog.js +327 -0
  33. package/dist/src/core/codex-mcp.js +122 -0
  34. package/dist/src/core/community.js +86 -0
  35. package/dist/src/core/compatibility-intelligence.js +465 -0
  36. package/dist/src/core/completion.js +160 -0
  37. package/dist/src/core/components.js +138 -0
  38. package/dist/src/core/conversion.js +56 -0
  39. package/dist/src/core/credentials.js +377 -0
  40. package/dist/src/core/demo.js +136 -0
  41. package/dist/src/core/diff.js +83 -0
  42. package/dist/src/core/discovery-connector.js +165 -0
  43. package/dist/src/core/doctor.js +95 -0
  44. package/dist/src/core/ecosystem-import.js +1072 -0
  45. package/dist/src/core/evaluate.js +72 -0
  46. package/dist/src/core/file-lock.js +140 -0
  47. package/dist/src/core/freshness-alerts.js +230 -0
  48. package/dist/src/core/github-discovery.js +97 -0
  49. package/dist/src/core/github.js +148 -0
  50. package/dist/src/core/head-to-head.js +437 -0
  51. package/dist/src/core/health-score-evidence.js +169 -0
  52. package/dist/src/core/health.js +129 -0
  53. package/dist/src/core/improve.js +223 -0
  54. package/dist/src/core/install.js +233 -0
  55. package/dist/src/core/intelligence-feed-build.js +59 -0
  56. package/dist/src/core/intelligence-feed.js +353 -0
  57. package/dist/src/core/loadout-badge.js +55 -0
  58. package/dist/src/core/loadout-card.js +87 -0
  59. package/dist/src/core/manifest.js +307 -0
  60. package/dist/src/core/mcp-recipes.js +419 -0
  61. package/dist/src/core/mcp-registry-discovery.js +455 -0
  62. package/dist/src/core/mcp.js +287 -0
  63. package/dist/src/core/model-config.js +102 -0
  64. package/dist/src/core/observations.js +87 -0
  65. package/dist/src/core/outcomes.js +164 -0
  66. package/dist/src/core/package.js +225 -0
  67. package/dist/src/core/paths.js +214 -0
  68. package/dist/src/core/portable.js +130 -0
  69. package/dist/src/core/private-discovery.js +45 -0
  70. package/dist/src/core/profiles.js +255 -0
  71. package/dist/src/core/provenance.js +278 -0
  72. package/dist/src/core/ranking.js +104 -0
  73. package/dist/src/core/recommend.js +181 -0
  74. package/dist/src/core/registry-api.js +97 -0
  75. package/dist/src/core/registry.js +316 -0
  76. package/dist/src/core/release-claims.js +175 -0
  77. package/dist/src/core/remove.js +122 -0
  78. package/dist/src/core/review-queue.js +200 -0
  79. package/dist/src/core/runtime-tool-recipe.js +315 -0
  80. package/dist/src/core/runtime-tools.js +453 -0
  81. package/dist/src/core/safety.js +234 -0
  82. package/dist/src/core/sandbox.js +78 -0
  83. package/dist/src/core/scheduler.js +264 -0
  84. package/dist/src/core/share-report.js +85 -0
  85. package/dist/src/core/signing.js +115 -0
  86. package/dist/src/core/skill-compare.js +239 -0
  87. package/dist/src/core/skill-inventory.js +193 -0
  88. package/dist/src/core/skill-security.js +340 -0
  89. package/dist/src/core/skills-sh-discovery.js +336 -0
  90. package/dist/src/core/skills.js +208 -0
  91. package/dist/src/core/snapshot.js +210 -0
  92. package/dist/src/core/source.js +305 -0
  93. package/dist/src/core/state.js +300 -0
  94. package/dist/src/core/sync.js +348 -0
  95. package/dist/src/core/terminal.js +53 -0
  96. package/dist/src/core/transaction.js +211 -0
  97. package/dist/src/core/update-watch.js +39 -0
  98. package/dist/src/core/update.js +216 -0
  99. package/dist/src/core/upgrade.js +207 -0
  100. package/dist/src/dashboard.js +418 -0
  101. package/dist/src/shared/schemas.js +336 -0
  102. package/dist/src/shared/types.js +1 -0
  103. package/docs/ACTIVE_SET.md +53 -0
  104. package/docs/ACTIVE_SET_POLICY.md +45 -0
  105. package/docs/CANDIDATE_INTELLIGENCE.md +139 -0
  106. package/docs/CATALOG.md +78 -0
  107. package/docs/CATALOG_POLICY.md +41 -0
  108. package/docs/COMMUNITY_DISCOVERY.md +14 -0
  109. package/docs/COMPATIBILITY_POLICY.md +22 -0
  110. package/docs/CONVERSION_AND_SANDBOX.md +27 -0
  111. package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +31 -0
  112. package/docs/DISCOVERED.md +280 -0
  113. package/docs/EVALUATION_PROTOCOL.md +40 -0
  114. package/docs/EVALUATION_PROTOCOL_V1.md +300 -0
  115. package/docs/FEATURE_TEST_MATRIX.md +802 -0
  116. package/docs/GITHUB_AUTHORIZATION.md +38 -0
  117. package/docs/HEAD_TO_HEAD_EVALUATION.md +79 -0
  118. package/docs/PROVENANCE_AND_COMPARISON.md +72 -0
  119. package/docs/PROVIDER_CONFIGURATION.md +45 -0
  120. package/docs/RELEASE_REVIEW.md +117 -0
  121. package/docs/SAFE_UPDATE_DEMO.md +25 -0
  122. package/docs/SCHEMA_DECISIONS.md +25 -0
  123. package/docs/TEAM_POLICY.md +18 -0
  124. package/docs/TESTING.md +190 -0
  125. package/package.json +82 -0
@@ -0,0 +1,336 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Runtime schemas for data that crosses a trust boundary: catalog JSON,
4
+ * manifests and lockfiles shared between machines, persisted install state,
5
+ * and install plans passed between Loadout modules. These schemas deliberately
6
+ * do not model secrets; MCP environment values are never persisted here.
7
+ */
8
+ export const agentIdSchema = z.enum([
9
+ "claude-code",
10
+ "codex",
11
+ "cursor",
12
+ "gemini-cli",
13
+ "opencode",
14
+ "hermes",
15
+ "windsurf",
16
+ "cline",
17
+ "github-copilot",
18
+ "roo-code",
19
+ "kiro-cli",
20
+ "junie",
21
+ ]);
22
+ export const packageTierSchema = z.enum([
23
+ "official",
24
+ "stable",
25
+ "trending",
26
+ "community",
27
+ ]);
28
+ export const operatingSystemSchema = z.enum(["windows", "macos", "linux"]);
29
+ export const componentTypeSchema = z.enum([
30
+ "skill",
31
+ "rule",
32
+ "command",
33
+ "agent",
34
+ "mcp",
35
+ "plugin",
36
+ "root",
37
+ ]);
38
+ export const componentCompatibilitySchema = z.enum([
39
+ "native",
40
+ "adapted",
41
+ "unsupported",
42
+ ]);
43
+ export const safetyRiskLevelSchema = z.enum(["safe", "review", "blocked"]);
44
+ const text = z.string().trim().min(1, "must not be empty");
45
+ const optionalText = text.optional();
46
+ const sha256 = z.string().regex(/^[a-f0-9]{64}$/i, "must be a SHA-256 hash");
47
+ const gitSha = z.string().regex(/^[a-f0-9]{40}$/i, "must be a full Git SHA");
48
+ const repository = z
49
+ .string()
50
+ .regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, "must be owner/repository");
51
+ const safeRepositoryPath = z
52
+ .string()
53
+ .min(1)
54
+ .refine((value) => !value.startsWith("/") &&
55
+ !value.startsWith("\\") &&
56
+ !value.split(/[\\/]/).includes(".."), "must be a safe repository-relative path");
57
+ const secretLikeValue = /^(?:(?:sk|rk|pk)[_-]|ghp_|github_pat_)[A-Za-z0-9_-]{12,}$/i;
58
+ const safeModelText = z
59
+ .string()
60
+ .trim()
61
+ .min(1, "must not be empty")
62
+ .max(200, "must be at most 200 characters")
63
+ .refine((value) => !secretLikeValue.test(value), "must not contain a credential value");
64
+ const providerIdSchema = safeModelText.regex(/^[a-z0-9]+(?:[a-z0-9-]*[a-z0-9])?$/, "must be a lowercase provider identifier");
65
+ const environmentVariableNameSchema = z
66
+ .string()
67
+ .regex(/^[A-Z_][A-Z0-9_]*$/, "must be an environment variable name, not its value")
68
+ .max(128);
69
+ const httpsEndpointSchema = z.url("must be a URL").refine((value) => {
70
+ const endpoint = new URL(value);
71
+ return (endpoint.protocol === "https:" &&
72
+ !endpoint.username &&
73
+ !endpoint.password &&
74
+ !endpoint.search &&
75
+ !endpoint.hash);
76
+ }, "must be an HTTPS URL without credentials, query parameters, or fragments");
77
+ export const credentialReferenceSchema = z.discriminatedUnion("kind", [
78
+ z
79
+ .object({
80
+ kind: z.literal("environment"),
81
+ name: environmentVariableNameSchema,
82
+ })
83
+ .strict(),
84
+ z
85
+ .object({
86
+ kind: z.literal("os-keychain"),
87
+ service: safeModelText,
88
+ account: safeModelText.optional(),
89
+ })
90
+ .strict(),
91
+ ]);
92
+ export const providerModelSelectionSchema = z
93
+ .object({
94
+ id: providerIdSchema,
95
+ provider: providerIdSchema,
96
+ model: safeModelText,
97
+ endpoint: httpsEndpointSchema,
98
+ credential: credentialReferenceSchema.optional(),
99
+ targetAgents: z.array(agentIdSchema).min(1).optional(),
100
+ })
101
+ .strict();
102
+ /**
103
+ * This deliberately has no apiKey/token/header fields. It is safe to put in a
104
+ * shared Loadout because it contains only metadata and credential references.
105
+ */
106
+ export const providerModelConfigurationSchema = z
107
+ .object({
108
+ schemaVersion: z.literal(1),
109
+ selections: z.array(providerModelSelectionSchema).min(1),
110
+ })
111
+ .strict()
112
+ .superRefine((value, context) => {
113
+ const seen = new Set();
114
+ for (const [index, selection] of value.selections.entries()) {
115
+ if (seen.has(selection.id)) {
116
+ context.addIssue({
117
+ code: "custom",
118
+ path: ["selections", index, "id"],
119
+ message: "must be unique",
120
+ });
121
+ }
122
+ seen.add(selection.id);
123
+ }
124
+ });
125
+ export const catalogSourceEvidenceSchema = z
126
+ .object({
127
+ type: z.literal("github"),
128
+ url: z.url("must be a URL"),
129
+ defaultBranch: z
130
+ .string()
131
+ .regex(/^[A-Za-z0-9._/-]+$/, "contains unsupported characters"),
132
+ commit: gitSha,
133
+ evidencePaths: z.array(safeRepositoryPath).min(1),
134
+ verifiedAt: z.iso.datetime("must be an ISO timestamp"),
135
+ })
136
+ .passthrough();
137
+ export const catalogPackageSchema = z
138
+ .object({
139
+ id: z
140
+ .string()
141
+ .regex(/^[a-z0-9]+(?:[a-z0-9-]*[a-z0-9])?$/, "must be lowercase kebab-case"),
142
+ displayName: text,
143
+ repository,
144
+ description: text,
145
+ category: text,
146
+ tier: packageTierSchema,
147
+ license: optionalText,
148
+ components: z.array(componentTypeSchema).min(1).optional(),
149
+ operatingSystems: z.array(operatingSystemSchema).min(1).optional(),
150
+ source: catalogSourceEvidenceSchema.optional(),
151
+ stars: z.number().finite().nonnegative().optional(),
152
+ lastUpdatedAt: z.iso.datetime("must be an ISO timestamp").optional(),
153
+ pushedAt: z.iso.datetime("must be an ISO timestamp").optional(),
154
+ topics: z.array(text).optional(),
155
+ openIssues: z.number().int().nonnegative().optional(),
156
+ archived: z.boolean().optional(),
157
+ })
158
+ .passthrough();
159
+ export const catalogSchema = z.array(catalogPackageSchema);
160
+ export const plannedFileSchema = z
161
+ .object({
162
+ source: text,
163
+ target: text,
164
+ targetAgent: agentIdSchema.optional(),
165
+ componentType: componentTypeSchema.optional(),
166
+ compatibility: componentCompatibilitySchema.optional(),
167
+ skillName: optionalText,
168
+ })
169
+ .passthrough();
170
+ export const conflictDiagnosticSchema = z
171
+ .object({
172
+ severity: z.enum(["blocking", "warning"]),
173
+ code: z.enum(["target-collision", "duplicate-skill-name"]),
174
+ message: text,
175
+ packageIds: z.array(text).min(1),
176
+ targets: z.array(text).min(1),
177
+ })
178
+ .passthrough();
179
+ export const installPlanSchema = z
180
+ .object({
181
+ packageId: text,
182
+ files: z.array(plannedFileSchema),
183
+ targetAgents: z.array(agentIdSchema),
184
+ warnings: z.array(z.string()),
185
+ conflicts: z.array(conflictDiagnosticSchema).optional(),
186
+ })
187
+ .passthrough();
188
+ export const packageSourceSchema = z.discriminatedUnion("type", [
189
+ z.object({ type: z.literal("catalog"), id: text }),
190
+ z.object({
191
+ type: z.literal("github"),
192
+ repository,
193
+ ref: optionalText,
194
+ path: optionalText,
195
+ }),
196
+ z.object({
197
+ type: z.literal("git"),
198
+ url: text,
199
+ ref: optionalText,
200
+ path: optionalText,
201
+ }),
202
+ z.object({ type: z.literal("registry"), name: text, version: text }),
203
+ z.object({
204
+ type: z.literal("remote-registry"),
205
+ registry: text,
206
+ name: text,
207
+ version: text,
208
+ }),
209
+ z.object({ type: z.literal("local"), path: text }),
210
+ ]);
211
+ const manifestPackageSchema = z
212
+ .object({
213
+ id: text,
214
+ source: packageSourceSchema,
215
+ agents: z.array(agentIdSchema).optional(),
216
+ dependsOn: z.array(text).optional(),
217
+ includeDevDependencies: z.boolean().optional(),
218
+ mcp: z
219
+ .object({ config: text, servers: z.array(text).optional() })
220
+ .optional(),
221
+ rootFiles: z.array(z.object({ source: text, target: text })).optional(),
222
+ enabled: z.boolean().optional(),
223
+ })
224
+ .passthrough();
225
+ export const loadoutManifestSchema = z
226
+ .object({
227
+ schemaVersion: z.literal(1),
228
+ name: text,
229
+ scope: z.enum(["project", "global"]),
230
+ agents: z.array(agentIdSchema),
231
+ profile: optionalText,
232
+ packages: z.array(manifestPackageSchema),
233
+ policy: z
234
+ .object({
235
+ allowRisk: z.array(safetyRiskLevelSchema).optional(),
236
+ blockedDomains: z.array(text).optional(),
237
+ blockedCommands: z.array(text).optional(),
238
+ allowPackages: z.array(text).optional(),
239
+ allowRepositories: z.array(text).optional(),
240
+ deniedPackages: z.array(text).optional(),
241
+ deniedRepositories: z.array(text).optional(),
242
+ requiredApprovals: z.number().int().min(0).optional(),
243
+ })
244
+ // Preserve unknown policy keys so portable-export secret scanning can
245
+ // reject them rather than silently dropping them during validation.
246
+ .passthrough()
247
+ .optional(),
248
+ })
249
+ .passthrough();
250
+ const fileHashSchema = z.object({ path: text, sha256 });
251
+ export const installRecordSchema = z
252
+ .object({
253
+ packageId: text,
254
+ repository: optionalText,
255
+ resolvedCommit: optionalText,
256
+ targetAgents: z.array(agentIdSchema),
257
+ files: z.array(fileHashSchema),
258
+ snapshotId: text,
259
+ installedAt: text,
260
+ })
261
+ .passthrough();
262
+ export const mcpInstallRecordSchema = z
263
+ .object({
264
+ packageId: text,
265
+ configPath: text,
266
+ serverName: text,
267
+ fingerprint: sha256,
268
+ snapshotId: text,
269
+ installedAt: text,
270
+ })
271
+ .passthrough();
272
+ export const managedActivationRecordSchema = z
273
+ .object({
274
+ packageId: text,
275
+ unitId: optionalText,
276
+ agent: agentIdSchema,
277
+ cacheState: z.enum(["missing", "downloaded"]),
278
+ reviewState: z.enum(["unreviewed", "reviewed", "quarantined"]),
279
+ installationState: z.enum(["installed", "removed"]),
280
+ activationState: z.enum(["active", "disabled"]),
281
+ libraryPath: text,
282
+ targets: z.array(z
283
+ .object({
284
+ activePath: text,
285
+ libraryRelativePath: text.refine((value) => value !== "." && value !== ".." && !/[\\/]/.test(value), "must be a single safe path segment"),
286
+ })
287
+ .passthrough()),
288
+ libraryFiles: z.array(fileHashSchema),
289
+ updatedAt: text,
290
+ snapshotId: optionalText,
291
+ })
292
+ .passthrough();
293
+ export const installStateSchema = z
294
+ .object({
295
+ version: z.literal(1),
296
+ installs: z.array(installRecordSchema),
297
+ mcpInstalls: z.array(mcpInstallRecordSchema).default([]),
298
+ activations: z.array(managedActivationRecordSchema).default([]),
299
+ })
300
+ .passthrough();
301
+ const lockedPackageSchema = z
302
+ .object({
303
+ id: text,
304
+ source: packageSourceSchema,
305
+ repository: optionalText,
306
+ resolvedCommit: optionalText,
307
+ targetAgents: z.array(agentIdSchema),
308
+ files: z.array(fileHashSchema),
309
+ installedAt: text,
310
+ dependencies: z.array(text).optional(),
311
+ })
312
+ .passthrough();
313
+ export const loadoutLockfileSchema = z
314
+ .object({
315
+ schemaVersion: z.literal(1),
316
+ manifestName: text,
317
+ // Older lockfiles predate this field. Read them safely as an explicit
318
+ // legacy value; every new lockfile writer still emits an ISO timestamp.
319
+ generatedAt: text.optional().default("unknown"),
320
+ packages: z.array(lockedPackageSchema),
321
+ mcpServers: z
322
+ .array(z.object({
323
+ packageId: text,
324
+ configPath: text,
325
+ serverName: text,
326
+ fingerprint: sha256,
327
+ }))
328
+ .optional(),
329
+ })
330
+ .passthrough();
331
+ /** Compact, path-aware errors suitable for CLI and persisted-data diagnostics. */
332
+ export function formatSchemaError(error) {
333
+ return error.issues
334
+ .map((issue) => `${issue.path.length ? issue.path.join(".") : "value"}: ${issue.message}`)
335
+ .join("; ");
336
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ # Reviewed library and active-set contract
2
+
3
+ Loadout separates four facts that package managers often collapse into one word:
4
+
5
+ - **cache**: `missing` or `downloaded` says whether Loadout has a private byte-for-byte
6
+ library copy;
7
+ - **review**: `unreviewed`, `reviewed`, or `quarantined` records evidence policy and
8
+ never follows popularity alone;
9
+ - **installation**: `installed` or `removed` records whether Loadout still manages the
10
+ package; and
11
+ - **activation**: `active` or `disabled` is per skill unit and per agent, because one
12
+ skill from a collection can be visible to Claude Code while another stays disabled
13
+ for Codex.
14
+
15
+ Maximum Library starts reviewed skill units as `downloaded + reviewed + installed +
16
+ disabled`. It can therefore hold hundreds of candidates without exposing hundreds of
17
+ instructions to an agent. Power is an intentionally broader active profile; Stable is
18
+ the smallest active foundation.
19
+
20
+ An initial install is active and may have `cache:missing`. On the first disable,
21
+ Loadout verifies that every managed file is unchanged and that no untracked file would
22
+ be removed, copies the complete skill directories into its private library, verifies
23
+ their hashes, and only then removes the agent-visible copies. This is the explicit
24
+ migration boundary for older installations.
25
+
26
+ `enable` verifies the private library hashes and refuses to overwrite any occupied
27
+ agent target. It restores the exact cached bytes but keeps the library copy. Both
28
+ commands are dry-run by default, can change several packages and agents under one
29
+ snapshot, write a durable transaction journal before mutation, and can be reversed by
30
+ `loadout rollback --snapshot <id>`.
31
+
32
+ Only Loadout-managed skill directories participate. Existing unmanaged content is
33
+ never silently adopted, moved, or deleted. MCP entries and other executable/configured
34
+ components are deliberately outside active-set toggling; they retain their existing
35
+ explicit configuration and removal workflows.
36
+
37
+ A disabled package must be enabled before its tracked source can be updated. This
38
+ prevents the update workflow from silently recreating agent-visible files and makes the
39
+ activation transition explicit.
40
+
41
+ ## Commands
42
+
43
+ ```bash
44
+ loadout library
45
+ loadout disable <package-or-package/skill> [more-selectors] [--agents codex,claude-code]
46
+ loadout disable <package-or-package/skill> --yes
47
+ loadout enable <package-or-package/skill> --yes
48
+ loadout activate --project . --limit 40
49
+ loadout optimize --project .
50
+ loadout rollback --snapshot <id>
51
+ ```
52
+
53
+ Machine-readable planning and results are available with `--json`.
@@ -0,0 +1,45 @@
1
+ # Active-set selection policy
2
+
3
+ Loadout treats the reviewed library as inventory and the active set as a constrained,
4
+ per-agent decision. It never equates repository popularity with universal quality.
5
+ The policy is deterministic, local by default, explainable in the preview, and ordered
6
+ by these constraints:
7
+
8
+ 1. Block quarantined, removed, incompatible, missing-cache, and unreviewed candidates.
9
+ 2. Honor an explicit full `package/skill` pin before defaults. A short skill-name pin
10
+ may match a reviewed equivalent, but cannot bypass a safety blocker.
11
+ 3. Preserve verified hard conflicts and choose only one default from equivalent skill
12
+ names. Other reviewed sources remain disabled and are shown as alternatives.
13
+ 4. Score project language/framework signals and broad task families such as planning,
14
+ debugging, testing, review, security, documentation, architecture, and delivery.
15
+ A generic word match is supporting evidence, not enough to activate a specialized
16
+ domain tool by itself.
17
+ 5. Add cross-project foundations and stop at the user-specified per-agent capacity.
18
+ Capacity is a ceiling, never a quota; unused slots are safer than irrelevant skills.
19
+ 6. Use category-specific evaluation confidence when evidence exists; no benchmark is
20
+ treated as universal across tasks, agents, models, or versions.
21
+ 7. Apply explicit local human outcomes only within the same task/agent scope. Pins,
22
+ rollback, rejection, and later re-activation outweigh aggregate popularity. Until
23
+ the privacy-safe outcome store is complete, this term is neutral rather than
24
+ guessed.
25
+ 8. Use maintenance and bounded adoption evidence only as a final tie-breaker. Stars
26
+ cannot override safety, compatibility, conflicts, pins, evaluations, or local
27
+ outcomes.
28
+
29
+ The current project selector implements steps 1–5 and exposes its scores, reasons,
30
+ equivalent-source decisions, exact activation delta, hash verification, and rollback
31
+ snapshot. Steps 6–7 are deliberately connected to P12-15 through P12-20 rather than
32
+ filled with fake benchmark or feedback data.
33
+
34
+ ## Commands
35
+
36
+ ```bash
37
+ loadout activate --project . --limit 40
38
+ loadout activate --project . --pin openai-skills/playwright
39
+ loadout optimize --project .
40
+ loadout optimize --project . --yes
41
+ ```
42
+
43
+ All commands are dry-run unless `--yes` is present. Project scanning reads only
44
+ well-known root metadata files and does not require GitHub, upload source code, or
45
+ execute a skill.
@@ -0,0 +1,139 @@
1
+ # Candidate intelligence and catalog trust
2
+
3
+ Loadout deliberately separates discovery, inspection, catalog admission, and
4
+ installation. A repository can be popular or fast-growing without being useful,
5
+ compatible, licensed for the intended use, safe, or better than a catalog package.
6
+
7
+ ## Evidence pipeline
8
+
9
+ ```text
10
+ daily GitHub observations
11
+ -> candidate list (triage only)
12
+ -> immutable source snapshot
13
+ -> static dossier
14
+ -> human-reviewed proposal
15
+ -> technically screened catalog JSON
16
+ -> Ed25519-signed release
17
+ -> explicit local diff and apply
18
+ -> normal preview/install/rollback workflow
19
+ ```
20
+
21
+ No step silently skips the next authority boundary.
22
+
23
+ ## Triage today's feed
24
+
25
+ ```bash
26
+ loadout candidate list --limit 20
27
+ loadout candidate list --query "codex skills" --json
28
+ ```
29
+
30
+ The displayed `triagePriority` is deterministic discovery ordering, not a quality or
31
+ safety score. It is capped at 100 points: up to 30 for bounded-query coverage, 35 for
32
+ the disclosed star-growth signal, 25 for log-scaled adoption, and 10 for appearing in
33
+ the latest run. Before a complete observation day exists, growth is labelled
34
+ `lifetime-star-average`; it is never mislabelled as measured velocity.
35
+
36
+ ## Inspect real source safely
37
+
38
+ ```bash
39
+ loadout candidate inspect owner/repository --output ./candidate-dossier.json
40
+ ```
41
+
42
+ Loadout performs a shallow public Git clone, resolves the result to a full immutable
43
+ commit, and scans files statically. The dossier records:
44
+
45
+ - discovery date, queries, adoption and growth evidence;
46
+ - immutable repository, commit, branch, and evidence paths;
47
+ - installability as portable components, explicit runtime setup, or unsupported
48
+ source shape;
49
+ - observed skills, rules, commands, agents, plugin manifests, and MCP declarations;
50
+ - secret/instruction/static-safety findings and explicit uncertainty;
51
+ - possible catalog overlaps based on disclosed shared terms; and
52
+ - reasons a human must block or review the candidate.
53
+
54
+ Absolute cache paths are removed from the dossier. Copy the path printed by `--write`,
55
+ or use `--output` as above for a shell- and platform-independent path. Loadout does not
56
+ run install scripts, package lifecycle commands, hooks, MCP servers, candidate models,
57
+ or arbitrary repository executables.
58
+
59
+ An `explicit-runtime-setup` or `unsupported-source-shape` result is not a dead end and
60
+ does not authorize execution. A maintainer may create a separately reviewed runtime
61
+ recipe with an immutable executable artifact, bounded commands, isolated state,
62
+ credential stripping, preview, verification, rollback, and removal. Graphify 0.9.17
63
+ is the first such recipe and is invoked with `loadout tool graphify`; it is not part
64
+ of broad `setup`.
65
+
66
+ Candidate fetches use a two-minute timeout and an isolated Git environment: system
67
+ and global config, templates, hooks, credential helpers, inherited `GIT_*` overrides,
68
+ and LFS smudging cannot affect provenance or execute during checkout. Before blob
69
+ materialization, a bounded GitHub tree-API response must be complete and prove every
70
+ blob size, at most 100 MiB total, and at most 20,000 files. The checked-out snapshot
71
+ is measured again before static analysis; rejected temporary clones are removed.
72
+
73
+ ## Create a human-gated proposal
74
+
75
+ ```bash
76
+ loadout candidate propose ./candidate-dossier.json \
77
+ --id package-id --category workflow \
78
+ --platforms windows,macos,linux
79
+
80
+ loadout candidate propose ./candidate-dossier.json \
81
+ --id package-id --category workflow \
82
+ --platforms windows,macos,linux \
83
+ --approve --output ./package-id.proposal.json
84
+ ```
85
+
86
+ Platforms are mandatory human claims; Loadout does not infer them from stars or a
87
+ successful clone. Before proposal preview or output, Loadout reopens the exact pinned
88
+ commit and recomputes the complete inspection and static evaluation; edited evidence
89
+ is rejected. A blocked dossier cannot become a proposal. `--approve` only writes an
90
+ isolated proposal record—it never edits the bundled catalog and never installs the
91
+ candidate. A maintainer must still review usefulness, overlap, license, runtime
92
+ powers, platform behavior, category, and catalog policy.
93
+
94
+ ## Distribute a trusted catalog release
95
+
96
+ Maintainers sign a technically screened full catalog array with an Ed25519 key kept outside the
97
+ repository:
98
+
99
+ ```bash
100
+ loadout keygen --private-key /secure/catalog-private.pem \
101
+ --public-key ./catalog-public.pem
102
+ loadout catalog-sign --catalog ./catalog/packages.json \
103
+ --private-key /secure/catalog-private.pem \
104
+ --output ./catalog.signed.json
105
+ ```
106
+
107
+ Users preview the verified release before trusting it:
108
+
109
+ ```bash
110
+ loadout catalog-update --source ./catalog.signed.json \
111
+ --public-key ./catalog-public.pem
112
+ loadout catalog-update --source ./catalog.signed.json \
113
+ --public-key ./catalog-public.pem --yes
114
+ ```
115
+
116
+ `--source` also accepts HTTPS with a 15-second timeout and streaming 5 MiB limit; other remote
117
+ schemes are rejected. The command verifies the signature, fingerprint, complete
118
+ catalog schema, immutable evidence, and exact add/update/remove diff. Removals require
119
+ the separate `--allow-removals` acknowledgement. Apply rejects replayed/older releases,
120
+ snapshots previous state, writes atomically, journals the transaction, and re-verifies
121
+ the stored signature whenever the effective catalog loads. The first successful apply
122
+ pins the signing key and a non-rollback replay high-water mark; signer changes require
123
+ an explicit trust-reset/key-rotation decision outside the normal update command.
124
+
125
+ The public key is stored beside the signed envelope because it is verification data,
126
+ not a secret. Trust in that key still has to come from a separately authenticated
127
+ project release channel.
128
+
129
+ ## Local personalization
130
+
131
+ ```bash
132
+ loadout recommend --project . --agent codex --json
133
+ ```
134
+
135
+ When an agent is supplied, baseline project recommendations are adjusted only by the
136
+ local outcome store for the same agent and task family. The evidence contains no
137
+ project paths, prompts, code, or source content. It can reorder or lower confidence in
138
+ catalog packages, but it cannot promote a discovery candidate or create a global
139
+ popularity loop.