@bradheitmann/odin-sentinel 0.4.11 → 0.4.12

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 (42) hide show
  1. package/.claude-plugin/marketplace.json +23 -0
  2. package/README.md +19 -15
  3. package/dist/src/mcp/server.js +32 -0
  4. package/dist/src/mcp/server.js.map +1 -1
  5. package/dist/src/protocol/repository.d.ts +6 -0
  6. package/dist/src/protocol/repository.js +12 -2
  7. package/dist/src/protocol/repository.js.map +1 -1
  8. package/dist/src/protocol/service.js +17 -8
  9. package/dist/src/protocol/service.js.map +1 -1
  10. package/dist/src/protocol/version.d.ts +2 -2
  11. package/dist/src/protocol/version.js +2 -2
  12. package/docs/guides/quick-start.md +7 -7
  13. package/docs/guides/quickstart-prompts.md +3 -3
  14. package/docs/reference/client-compatibility.md +1 -1
  15. package/docs/reference/distribution.md +5 -5
  16. package/docs/reference/public-surface-audit.md +2 -2
  17. package/package.json +5 -3
  18. package/plugins/odin-scp/.claude-plugin/plugin.json +25 -0
  19. package/plugins/odin-scp/README.md +62 -0
  20. package/plugins/odin-scp/skills/odin-scp/CHANGELOG.md +25 -0
  21. package/plugins/odin-scp/skills/odin-scp/SKILL.md +1518 -0
  22. package/plugins/odin-scp/skills/odin-scp/agents/openai.yaml +4 -0
  23. package/plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md +439 -0
  24. package/plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md +120 -0
  25. package/plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md +56 -0
  26. package/plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md +298 -0
  27. package/plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh +233 -0
  28. package/protocol/SCP.md +3 -3
  29. package/protocol/bootstrap-skill.md +22 -13
  30. package/protocol/closeout.yaml +1 -1
  31. package/protocol/delegation.yaml +1 -1
  32. package/protocol/model-profiles.yaml +2 -2
  33. package/protocol/receipts/team-manifest.yaml +1 -1
  34. package/protocol/roles.yaml +1 -1
  35. package/protocol/skill-references/boot-receipt-examples.md +439 -0
  36. package/protocol/skill-references/canonical-introduction-prompt.md +120 -0
  37. package/protocol/skill-references/harness-skill-targets.md +56 -0
  38. package/protocol/skill-references/team-bootstrap-runbook.md +298 -0
  39. package/protocol/topology.yaml +1 -1
  40. package/scripts/audit/public-surface.mjs +5 -1
  41. package/scripts/audit/verify-pack.mjs +71 -8
  42. package/templates/team-manifest-template.yaml +6 -6
@@ -0,0 +1,298 @@
1
+ # SCP Team Bootstrap Runbook
2
+
3
+ Use this when a single `EXEC PM` pane must create, operate, and tear down SCP teams in CMUX or another terminal surface manager.
4
+
5
+ ## Required Inputs
6
+
7
+ - Objective and phase boundary.
8
+ - Target repo/worktree/branch/SHA.
9
+ - Pod count, default 3 federated pods unless human operator specifies otherwise.
10
+ - Team exceptions, especially UX/design teams.
11
+ - Allowed harness/model pool and any budget/cost priority.
12
+
13
+ If any input is missing, choose the conservative default and record it in `SCP-TEAM-MANIFEST`. Ask human operator only when missing information changes authority, scope, destructive cleanup, or cost materially.
14
+
15
+ ## Skill Composition
16
+
17
+ - Use `team-composition-patterns` to choose minimum viable pod shape.
18
+ - Use `dispatching-parallel-agents` only after workstreams are independent and write scopes do not conflict.
19
+ - Use `delegate` for harness/model selection, preflight probes, fallback ladder, and instruction bundles.
20
+ - Use `handoff` for parked retained panes.
21
+ - Use `qa-swarm-review` for cleanup or closure review.
22
+ - Use `atlas-synthesis` for post-run SCP improvement packets.
23
+
24
+ ## Default Three-Pod Layout
25
+
26
+ ```yaml
27
+ executive_office:
28
+ - pane: A/EXEC-PM
29
+ role: EXEC PM
30
+ - pane: A/EXEC-ASST
31
+ role: EXEC ASST
32
+ - pane: A/EXEC-RSCH
33
+ role: EXEC RSCH
34
+ - pane: A/EXEC-QA
35
+ role: EXEC QA
36
+ pods:
37
+ - team: B
38
+ panes: [B/TEAM-PM, B/TEAM-SENTINEL, B/DEV-1, B/QA-1, B/SHADOW-1]
39
+ - team: C
40
+ panes: [C/TEAM-PM, C/TEAM-SENTINEL, C/DEV-1, C/QA-1, C/SHADOW-1]
41
+ - team: D
42
+ panes: [D/TEAM-PM, D/TEAM-SENTINEL, D/DEV-1, D/QA-1, D/SHADOW-1]
43
+ floaters:
44
+ - A/INTEGRATION-STEWARD
45
+ - A/QUEUE-TRIAGE
46
+ ```
47
+
48
+ Start smaller when the objective is narrow. Add pods horizontally only when the queue has independent work, non-overlapping write scopes, and enough QA capacity.
49
+
50
+ `TEAM PM` and `TEAM SENTINEL` are separate control roles. `TEAM PM` routes pod assignments and activates workers. `TEAM SENTINEL` watches delivery, scope, role discipline, branch proof, and intervention health. Neither implements or QA-closes by default.
51
+
52
+ ## Terminal Setup Pattern
53
+
54
+ Use live CMUX commands only after identifying the current workspace. If operating in tmux, WezTerm, iTerm2, Ghostty, Warp, Cursor, Zed, VS Code, libghostyy, or another surface manager, capture the equivalent `terminal_locator` fields and mark unsupported fields `unavailable`.
55
+
56
+ If the surface manager exposes libghostty-vt or a congruent terminal-state API, also capture `vt_state_snapshot`. Keep this separate from `terminal_locator`: libghostty-vt models terminal emulator state such as terminal instances, rows/cols, active screen, cursor, scrollback, render dirty state, formatter output, semantic prompt state, and input encoding; it does not by itself define SCP team identity or CMUX-style workspace routing.
57
+
58
+ ```bash
59
+ /Applications/cmux.app/Contents/Resources/bin/cmux --json --id-format both current-workspace
60
+ /Applications/cmux.app/Contents/Resources/bin/cmux --json --id-format both identify --workspace <workspace> --surface <surface>
61
+ /Applications/cmux.app/Contents/Resources/bin/cmux --json --id-format both list-pane-surfaces --workspace <workspace>
62
+ /Applications/cmux.app/Contents/Resources/bin/cmux new-surface --type terminal --workspace <workspace>
63
+ /Applications/cmux.app/Contents/Resources/bin/cmux rename-tab --workspace <workspace> --surface <surface> 'C/TEAM-SENTINEL'
64
+ /Applications/cmux.app/Contents/Resources/bin/cmux send --workspace <workspace> --surface <surface> '<launch command or boot prompt>'
65
+ /Applications/cmux.app/Contents/Resources/bin/cmux send-key --workspace <workspace> --surface <surface> Enter
66
+ /Applications/cmux.app/Contents/Resources/bin/cmux read-screen --workspace <workspace> --surface <surface> --lines 80 --scrollback
67
+ ```
68
+
69
+ Do not claim delivery until send, enter, read-screen, and ack state are recorded in `[SCP-CMUX-DELIVERY]` or `[SCP-TERMINAL-DELIVERY]`.
70
+
71
+ Use these delivery verdicts:
72
+
73
+ - `DELIVERED_ACKED`: send/enter/read-screen completed and ack observed.
74
+ - `DELIVERED_NO_ACK`: message landed but no ack yet; poll again.
75
+ - `INPUT_BAR_ONLY`: text was not submitted; not delivered.
76
+ - `PANE_BLOCKED_ON_PERMISSION`: plan mode, auth, quota, modal, or permission blocker.
77
+ - `PANE_STILL_THINKING`: instruction landed and pane is still processing.
78
+
79
+ ## Fast Bootstrap Mode
80
+
81
+ For initial team creation, do not require every idle pane to emit the full `SCP_BOOT_RECEIPT`. Use `SCP_MIN_BOOT_RECEIPT` to park panes quickly, then require full `SCP_BOOT_RECEIPT` only before activation, dispatch, mutation, QA, commit, push, or closure language.
82
+
83
+ ```yaml
84
+ SCP_MIN_BOOT_RECEIPT:
85
+ agent_id: c-dev-1
86
+ team: C
87
+ role: DEV WORKER
88
+ reports_to: C/TEAM-PM
89
+ cwd: <pwd or EXEC PM supplied>
90
+ branch: <branch or EXEC PM supplied>
91
+ head_sha: <sha or EXEC PM supplied>
92
+ may_implement: false
93
+ may_qa_accept: false
94
+ permission_mode: DEGRADED_READ_ONLY | READ_ONLY | WRITE_WHEN_ACTIVATED
95
+ current_state: BOOTSTRAPPED_IDLE
96
+ ```
97
+
98
+ Minimal boot receipt states:
99
+
100
+ - `BOOTSTRAPPED_IDLE`: pane launched, role acknowledged, no work active.
101
+ - `BOOT_RECEIPT_PARTIAL`: minimal receipt present, full receipt deferred until activation.
102
+ - `BOOT_RECEIPT_BLOCKED`: pane paused on permission, auth, quota, context, or plan-mode issue.
103
+
104
+ When EXEC PM has exact CMUX/tmux/terminal refs, provide them in the boot prompt. Pane self-report is secondary and should not override adapter-captured locator ids.
105
+
106
+ ## Plan-Mode Bootstrap
107
+
108
+ Claude Code and similar harnesses may pause for approval on reads or shell proof. During bootstrap-only runs:
109
+
110
+ - Safe to approve or pre-supply: SCP/AGENTS/handoff reads, `pwd`, `git status --short --branch --untracked-files=all`, `git rev-parse HEAD @{u}`, `cmux identify`, `cmux read-screen`, and receipt acknowledgment.
111
+ - Keep blocked: writes, lifecycle moves, evidence/verdict creation, implementation, QA acceptance, commits, pushes, destructive cleanup, and secret output.
112
+ - If the pane is stuck in plan mode, EXEC PM may supply cwd/branch/SHA/locator proof and request `SCP_MIN_BOOT_RECEIPT` only.
113
+ - Do not dispatch real work to a plan-mode pane until full receipt and activation proof are present.
114
+
115
+ ## SCP-TEAM-MANIFEST
116
+
117
+ Record the manifest before dispatching work.
118
+
119
+ During no-product-work bootstrap, the manifest may remain runtime-only in the EXEC PM transcript, screen report, or status ledger. Before dispatch, mutation, QA activation, commit, push, finish, or clean/ready claims, promote the manifest to a durable handoff, ledger, or branch-visible artifact appropriate to the run.
120
+
121
+ ```yaml
122
+ SCP-TEAM-MANIFEST:
123
+ created_by: A/EXEC-PM
124
+ workspace: <semantic workspace label>
125
+ workspace_ref: <workspace:1 | session name | unavailable>
126
+ workspace_id: <uuid-or-stable-id | unavailable>
127
+ objective: <bounded objective>
128
+ branch_authority: <branch and sha>
129
+ executive_office:
130
+ - pane: A/EXEC-PM
131
+ agent_id: a-exec-pm
132
+ terminal_locator:
133
+ terminal_app: cmux
134
+ terminal_adapter: cmux
135
+ workspace_ref: <workspace:1>
136
+ workspace_id: <uuid>
137
+ window_ref: <window:1>
138
+ window_id: <uuid>
139
+ pane_ref: <pane>
140
+ pane_id: <uuid>
141
+ surface_ref: <surface>
142
+ surface_id: <uuid>
143
+ tab_ref: <tab>
144
+ tab_id: <uuid>
145
+ surface_type: terminal
146
+ title: A/EXEC-PM
147
+ route_command: cmux send --workspace <workspace> --surface <surface>
148
+ locator_source: cmux --json --id-format both identify
149
+ locator_captured_at: <ISO-8601 timestamp>
150
+ vt_state_snapshot:
151
+ vt_provider: unavailable
152
+ vt_api_stability: unknown
153
+ terminal_instance_ref: unavailable
154
+ terminal_instance_id: unavailable
155
+ pty_ref: unavailable
156
+ capture_source: cmux read-screen
157
+ formatter_format: plain
158
+ rows: unavailable
159
+ cols: unavailable
160
+ total_rows: unavailable
161
+ scrollback_rows: unavailable
162
+ width_px: unavailable
163
+ height_px: unavailable
164
+ active_screen: unavailable
165
+ cursor_x: unavailable
166
+ cursor_y: unavailable
167
+ cursor_visible: unavailable
168
+ cursor_pending_wrap: unavailable
169
+ title: A/EXEC-PM
170
+ pwd: unavailable
171
+ render_dirty: unavailable
172
+ semantic_prompt_observed: unavailable
173
+ semantic_input_observed: unavailable
174
+ semantic_output_observed: unavailable
175
+ paste_safety_checked: unavailable
176
+ paste_safe: unavailable
177
+ key_encoding_provider: unavailable
178
+ mouse_encoding_provider: unavailable
179
+ focus_encoding_provider: unavailable
180
+ snapshot_captured_at: <ISO-8601 timestamp>
181
+ role: EXEC PM
182
+ harness_model: <model/harness>
183
+ disposition: retain
184
+ pods:
185
+ - team: C
186
+ purpose: <workstream>
187
+ surfaces:
188
+ - pane: C/TEAM-PM
189
+ terminal_locator: <same schema>
190
+ role: TEAM PM
191
+ harness_model: <model/harness>
192
+ disposition: retain_or_close_on_finish
193
+ - pane: C/TEAM-SENTINEL
194
+ terminal_locator:
195
+ terminal_app: <cmux|tmux|wezterm|iterm2|ghostty|warp|cursor|zed|vscode|unknown>
196
+ terminal_adapter: <cmux|tmux|libghostyy|apple_script|cli|ide_terminal|unavailable>
197
+ workspace_ref: <workspace/session/ref-or-unavailable>
198
+ workspace_id: <uuid-or-stable-id-or-unavailable>
199
+ window_ref: <window-ref-or-unavailable>
200
+ window_id: <uuid-or-stable-id-or-unavailable>
201
+ pane_ref: <pane-ref-or-unavailable>
202
+ pane_id: <uuid-or-stable-id-or-unavailable>
203
+ surface_ref: <surface-ref-or-unavailable>
204
+ surface_id: <uuid-or-stable-id-or-unavailable>
205
+ tab_ref: <tab-ref-or-unavailable>
206
+ tab_id: <uuid-or-stable-id-or-unavailable>
207
+ surface_type: terminal
208
+ title: C/TEAM-SENTINEL
209
+ route_command: <non-secret route command or unavailable>
210
+ locator_source: <command/tool/observation used>
211
+ locator_captured_at: <ISO-8601 timestamp or unavailable>
212
+ vt_state_snapshot:
213
+ vt_provider: <libghostty-vt|terminal-capture|unavailable>
214
+ vt_api_stability: <work_in_progress_unstable|stable|unknown>
215
+ terminal_instance_ref: <GhosttyTerminal handle/ref or unavailable>
216
+ terminal_instance_id: <product-generated id or unavailable>
217
+ pty_ref: <pty/process route or unavailable>
218
+ capture_source: <formatter|render_state|grid_ref|read_screen|unavailable>
219
+ formatter_format: <plain|vt|html|unavailable>
220
+ rows: <rows or unavailable>
221
+ cols: <cols or unavailable>
222
+ total_rows: <total_rows or unavailable>
223
+ scrollback_rows: <scrollback_rows or unavailable>
224
+ width_px: <width_px or unavailable>
225
+ height_px: <height_px or unavailable>
226
+ active_screen: <primary|alternate|unavailable>
227
+ cursor_x: <cursor_x or unavailable>
228
+ cursor_y: <cursor_y or unavailable>
229
+ cursor_visible: <true|false|unavailable>
230
+ cursor_pending_wrap: <true|false|unavailable>
231
+ title: C/TEAM-SENTINEL
232
+ pwd: <pwd or unavailable>
233
+ render_dirty: <false|partial|full|unavailable>
234
+ semantic_prompt_observed: <true|false|unavailable>
235
+ semantic_input_observed: <true|false|unavailable>
236
+ semantic_output_observed: <true|false|unavailable>
237
+ paste_safety_checked: <true|false|unavailable>
238
+ paste_safe: <true|false|unavailable>
239
+ key_encoding_provider: <libghostty-vt|terminal|unavailable>
240
+ mouse_encoding_provider: <libghostty-vt|terminal|unavailable>
241
+ focus_encoding_provider: <libghostty-vt|terminal|unavailable>
242
+ snapshot_captured_at: <ISO-8601 timestamp or unavailable>
243
+ role: TEAM SENTINEL
244
+ harness_model: <model/harness>
245
+ disposition: retain_or_close_on_finish
246
+ - pane: C/DEV-1
247
+ terminal_locator: <same schema>
248
+ role: DEV WORKER
249
+ harness_model: <model/harness>
250
+ disposition: close_on_finish
251
+ exclusions:
252
+ - UX/design teams unless separately profiled
253
+ teardown_policy:
254
+ close_temp_panes_after_finish: true
255
+ retain_dirty_or_blocked_panes: true
256
+ require_approval_for_worktree_delete: true
257
+ ```
258
+
259
+ ## Harness Launch Rules
260
+
261
+ Before launching a harness:
262
+
263
+ 1. Run the `delegate` preflight for that harness/model.
264
+ 2. Prefer cached successful model/harness choices for the same task class.
265
+ 3. Use high-reasoning/costly models for executive, sentinel, architecture, integration, and high-risk QA.
266
+ 4. Use cheaper/bounded models for DEV, routine QA, scans, and shadow review.
267
+ 5. If a harness fails, substitute by role contract, not by pane identity.
268
+
269
+ Each launched pane must receive:
270
+
271
+ - role-specific boot prompt,
272
+ - `SCP_MIN_BOOT_RECEIPT` requirements for initial parking,
273
+ - full `SCP_BOOT_RECEIPT` requirements for activation or mutation,
274
+ - write/read/prohibited scopes,
275
+ - reports-to chain,
276
+ - `may_implement` and `may_qa_accept`,
277
+ - exact current objective,
278
+ - first expected receipt.
279
+
280
+ ## Dispatch Rules
281
+
282
+ `EXEC PM` dispatches to `TEAM PM`; `TEAM PM` activates and routes pod workers; `TEAM SENTINEL` monitors, polls, intervenes, and may relay corrective prompts. Workers do not self-select work.
283
+
284
+ Every downstream assignment requires `[SCP-DELEGATE]` and `[SCP-CMUX-DELIVERY]` or `[SCP-TERMINAL-DELIVERY]` with a delivery verdict. During active work, each sentinel emits `[SCP-POLL]` on the assigned cadence.
285
+
286
+ ## Teardown Rules
287
+
288
+ On `$odin-scp --finish`:
289
+
290
+ 1. `EXEC PM` broadcasts finish to all manifest panes.
291
+ 2. Every pane emits `[SCP-FINISH]` or is recorded as non-responsive.
292
+ 3. `EXEC ASST` captures final read-screen snapshots and pane list.
293
+ 4. `EXEC QA` or a QA swarm reviews cleanup evidence.
294
+ 5. Close only panes marked `close_on_finish` and only after their state is captured.
295
+ 6. Retain UX/design, dirty, blocked, or explicitly parked panes.
296
+ 7. Record a final manifest with closed/retained/deferred disposition.
297
+
298
+ Never delete worktrees or local files during automated teardown unless exact entries were approved and proof was captured.
@@ -1,4 +1,4 @@
1
- version: 0.4.11
1
+ version: 0.4.12
2
2
  default_topology:
3
3
  executive_office:
4
4
  team: A
@@ -9,6 +9,7 @@ const PUBLIC_ROOTS = [
9
9
  "CLAUDE.md",
10
10
  "LICENSE",
11
11
  "package.json",
12
+ ".claude-plugin/",
12
13
  "docs/",
13
14
  "protocol/",
14
15
  "templates/",
@@ -57,7 +58,10 @@ const BUNDLED_DOC = new Set([
57
58
  "README.md",
58
59
  "docs/guides/quickstart-prompts.md",
59
60
  "protocol/bootstrap-" + "sk" + "ill.md",
60
- "plugins/sentinel-coordination-protocol/" + "sk" + "ills/sentinel-coordination-protocol/SK" + "ILL.md"
61
+ "protocol/skill-references/harness-skill-targets.md",
62
+ "plugins/odin-scp/" + "sk" + "ills/odin-scp/SK" + "ILL.md",
63
+ "plugins/odin-scp/" + "sk" + "ills/odin-scp/references/harness-skill-targets.md",
64
+ "plugins/odin-scp/" + "sk" + "ills/odin-scp/scripts/sync-installations.sh"
61
65
  ]);
62
66
 
63
67
  const forbidden = [
@@ -13,7 +13,11 @@ const requiredProtocolFiles = [
13
13
  "protocol/delegation.yaml",
14
14
  "protocol/receipts/boot-receipt.yaml",
15
15
  "protocol/receipts/team-manifest.yaml",
16
- "protocol/bootstrap-skill.md"
16
+ "protocol/bootstrap-skill.md",
17
+ "protocol/skill-references/boot-receipt-examples.md",
18
+ "protocol/skill-references/canonical-introduction-prompt.md",
19
+ "protocol/skill-references/harness-skill-targets.md",
20
+ "protocol/skill-references/team-bootstrap-runbook.md"
17
21
  ];
18
22
 
19
23
  const requiredTemplateFiles = [
@@ -24,6 +28,7 @@ const requiredTemplateFiles = [
24
28
  ];
25
29
 
26
30
  export const requiredPackageFiles = [
31
+ ".claude-plugin/marketplace.json",
27
32
  "dist/src/bin/index.js",
28
33
  "dist/src/mcp/server.js",
29
34
  "dist/src/protocol/index.js",
@@ -40,6 +45,16 @@ export const requiredPackageFiles = [
40
45
  "docs/reference/distribution.md",
41
46
  "docs/reference/public-surface-audit.md",
42
47
  ...requiredProtocolFiles,
48
+ "plugins/odin-scp/.claude-plugin/plugin.json",
49
+ "plugins/odin-scp/skills/odin-scp/SKILL.md",
50
+ "plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
51
+ "plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
52
+ "plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
53
+ "plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
54
+ "plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
55
+ "plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
56
+ "plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
57
+ "plugins/odin-scp/README.md",
43
58
  ...requiredTemplateFiles,
44
59
  "scripts/audit/public-surface.mjs",
45
60
  "scripts/audit/verify-pack.mjs",
@@ -89,7 +104,7 @@ export function validatePackageMetadata(packageJson) {
89
104
  if (!packageJson.license) errors.push("package.json missing license");
90
105
  if (!packageJson.engines?.node) errors.push("package.json missing engines.node");
91
106
  if (!Array.isArray(packageJson.files) || packageJson.files.length === 0) errors.push("package.json missing files allowlist");
92
- for (const file of ["docs", "protocol", "templates", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE"]) {
107
+ for (const file of [".claude-plugin", "docs", "plugins", "protocol", "templates", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE"]) {
93
108
  if (!packageJson.files?.includes(file)) errors.push(`package.json files allowlist missing ${file}`);
94
109
  }
95
110
  if (packageJson.odin?.publicVersion !== packageJson.version) {
@@ -217,6 +232,9 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
217
232
  manifest = {};
218
233
  }
219
234
 
235
+ if (manifest.name !== "odin-scp") {
236
+ errors.push(`Claude plugin manifest name ${manifest.name ?? "<missing>"} must be odin-scp`);
237
+ }
220
238
  if (manifest.version !== currentVersion) {
221
239
  errors.push(`Claude plugin manifest version ${manifest.version ?? "<missing>"} must match package version ${currentVersion}`);
222
240
  }
@@ -244,6 +262,39 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
244
262
  return errors;
245
263
  }
246
264
 
265
+ export function validateMarketplaceSync({ marketplaceText, currentVersion }) {
266
+ const errors = [];
267
+ let marketplace;
268
+ try {
269
+ marketplace = JSON.parse(marketplaceText);
270
+ } catch {
271
+ errors.push("Claude marketplace manifest must be valid JSON");
272
+ marketplace = {};
273
+ }
274
+
275
+ const plugins = Array.isArray(marketplace.plugins) ? marketplace.plugins : [];
276
+ const odinScp = plugins.find((plugin) => plugin?.name === "odin-scp");
277
+ if (!odinScp) {
278
+ errors.push("Claude marketplace manifest must advertise odin-scp");
279
+ } else {
280
+ if (odinScp.source !== "./plugins/odin-scp") {
281
+ errors.push(`Claude marketplace odin-scp source ${odinScp.source ?? "<missing>"} must be ./plugins/odin-scp`);
282
+ }
283
+ if (odinScp.version !== currentVersion) {
284
+ errors.push(`Claude marketplace odin-scp version ${odinScp.version ?? "<missing>"} must match package version ${currentVersion}`);
285
+ }
286
+ }
287
+
288
+ const legacyName = `sentinel-${"coordination"}-${"protocol"}`;
289
+ const legacySource = `./plugins/${legacyName}`;
290
+ const staleLegacy = plugins.find((plugin) => plugin?.name === legacyName || plugin?.source === legacySource);
291
+ if (staleLegacy) {
292
+ errors.push("Claude marketplace manifest must not advertise the legacy long-name plugin");
293
+ }
294
+
295
+ return errors;
296
+ }
297
+
247
298
  export function validatePackagedProtocolVersions(fileTextByPath, currentVersion) {
248
299
  const errors = [];
249
300
  for (const [file, text] of Object.entries(fileTextByPath)) {
@@ -280,11 +331,19 @@ function readPublicVersionFiles() {
280
331
  "docs/reference/client-compatibility.md",
281
332
  "docs/reference/distribution.md",
282
333
  "docs/reference/public-surface-audit.md",
334
+ ".claude-plugin/marketplace.json",
283
335
  "protocol/SCP.md",
284
336
  "protocol/bootstrap-skill.md",
285
- "plugins/sentinel-coordination-protocol/.claude-plugin/plugin.json",
286
- "plugins/sentinel-coordination-protocol/skills/sentinel-coordination-protocol/SKILL.md",
287
- "plugins/sentinel-coordination-protocol/README.md"
337
+ "plugins/odin-scp/.claude-plugin/plugin.json",
338
+ "plugins/odin-scp/skills/odin-scp/SKILL.md",
339
+ "plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
340
+ "plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
341
+ "plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
342
+ "plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
343
+ "plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
344
+ "plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
345
+ "plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
346
+ "plugins/odin-scp/README.md"
288
347
  ].map((file) => [file, readFileSync(file, "utf8")]));
289
348
  }
290
349
 
@@ -309,10 +368,14 @@ export function runVerifyPack({ pack, packageJson, publicVersionFiles, costPriva
309
368
  bootstrapText: publicVersionFiles["protocol/bootstrap-skill.md"],
310
369
  currentVersion: packageJson.version
311
370
  }),
371
+ ...validateMarketplaceSync({
372
+ marketplaceText: publicVersionFiles[".claude-plugin/marketplace.json"],
373
+ currentVersion: packageJson.version
374
+ }),
312
375
  ...validatePluginSync({
313
- pluginManifestText: publicVersionFiles["plugins/sentinel-coordination-protocol/.claude-plugin/plugin.json"],
314
- pluginSkillText: publicVersionFiles["plugins/sentinel-coordination-protocol/skills/sentinel-coordination-protocol/SKILL.md"],
315
- pluginReadmeText: publicVersionFiles["plugins/sentinel-coordination-protocol/README.md"],
376
+ pluginManifestText: publicVersionFiles["plugins/odin-scp/.claude-plugin/plugin.json"],
377
+ pluginSkillText: publicVersionFiles["plugins/odin-scp/skills/odin-scp/SKILL.md"],
378
+ pluginReadmeText: publicVersionFiles["plugins/odin-scp/README.md"],
316
379
  currentVersion: packageJson.version,
317
380
  expectedToolCount: extractToolCount(packageJson.description)
318
381
  }),
@@ -30,7 +30,7 @@ role_slots:
30
30
  workspace: "<workspace-ref>"
31
31
  pane: "<pane-ref>"
32
32
  surface: "<surface-ref>"
33
- scp_context_source: "native sentinel-coordination-protocol skill"
33
+ scp_context_source: "native odin-scp skill"
34
34
  mcp_available: true
35
35
  mcp_version: "0.4.5"
36
36
  scp_skill_available: true
@@ -43,7 +43,7 @@ role_slots:
43
43
  workspace: "<workspace-ref>"
44
44
  pane: "<pane-ref>"
45
45
  surface: "<surface-ref>"
46
- scp_context_source: "native sentinel-coordination-protocol skill"
46
+ scp_context_source: "native odin-scp skill"
47
47
  mcp_available: true
48
48
  mcp_version: "0.4.5"
49
49
  scp_skill_available: true
@@ -58,7 +58,7 @@ role_slots:
58
58
  workspace: "<workspace-ref>"
59
59
  pane: "<pane-ref>"
60
60
  surface: "<surface-ref>"
61
- scp_context_source: "native sentinel-coordination-protocol skill"
61
+ scp_context_source: "native odin-scp skill"
62
62
  mcp_available: true
63
63
  mcp_version: "0.4.5"
64
64
  scp_skill_available: true
@@ -73,7 +73,7 @@ role_slots:
73
73
  workspace: "<workspace-ref>"
74
74
  pane: "<pane-ref>"
75
75
  surface: "<surface-ref>"
76
- scp_context_source: "native sentinel-coordination-protocol skill"
76
+ scp_context_source: "native odin-scp skill"
77
77
  mcp_available: true
78
78
  mcp_version: "0.4.5"
79
79
  scp_skill_available: true
@@ -121,7 +121,7 @@ user_defined_criteria:
121
121
  # workspace: "workspace:1"
122
122
  # pane: "pane:1"
123
123
  # surface: "surface:1"
124
- # scp_context_source: "native sentinel-coordination-protocol skill"
124
+ # scp_context_source: "native odin-scp skill"
125
125
  # mcp_available: true
126
126
  # mcp_version: "0.4.5"
127
127
  # scp_skill_available: true
@@ -134,7 +134,7 @@ user_defined_criteria:
134
134
  # workspace: "workspace:1"
135
135
  # pane: "pane:2"
136
136
  # surface: "surface:2"
137
- # scp_context_source: "native sentinel-coordination-protocol skill"
137
+ # scp_context_source: "native odin-scp skill"
138
138
  # mcp_available: true
139
139
  # mcp_version: "0.4.5"
140
140
  # scp_skill_available: true