Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/installed-plugin-probe.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,23 @@ A passing probe proves that Claude Code's installed-plugin stream exposes an
auditable file-access event. It does not prove that LoopSpine automatically
chooses an adaptive reference, that tiny tasks avoid it, or that the reference
improves task outcomes. Those remain separate candidate-v3 gates.

## Explicit Trigger Selection Smoke

Run the paired read-only smoke with:

```bash
npm run smoke:trigger-selection
```

Both cases load the plugin through `--plugin-dir` and verify the same
`package.json` read. The trigger case explicitly invokes LoopSpine and must
return a valid five-field compact receipt before any short explanatory context.
The non-trigger case does not invoke LoopSpine and must return only the exact
package name and version. Both cases
expose only the `Read` tool, load no user or project settings, reject extra
plugins or hook events, disable MCP servers, and require unchanged tracked
state.

This smoke proves the explicit pilot boundary. It does not claim automatic
selection, task-quality improvement, global readiness, or hook readiness.
28 changes: 27 additions & 1 deletion dogfood/register.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,33 @@
},
{
"id": "DF-02",
"status": "pending"
"status": "completed",
"repo": "uncfreak1255-code/loopspine",
"task": "Build and verify an isolated installed-plugin smoke that distinguishes explicit LoopSpine invocation from the same non-invoked readback",
"loopspine_invoked": true,
"verified": true,
"sawyer_interventions": 0,
"time_to_proof_seconds": 218,
"incorrect_stop": false,
"proof": "Claude Code 2.1.207 with claude-fable-5 loaded only the inline LoopSpine plugin in Read-only plan mode with no MCP servers or hook events. Both cases produced correlated package.json Read events with the expected SHA-256. The explicit case returned a valid LoopSpine direct-lane success receipt; the non-invoked case returned exactly loopspine@0.2.0. Tracked state and the read file were unchanged, all 12 smoke assertions passed, npm test passed 19 access-event cases, Claude plugin validation passed, and final autoreview was clean.",
"proof_references": [
{
"type": "commit",
"url": "https://github.com/uncfreak1255-code/loopspine/commit/9951a1b305289e2447f9f96361f8d1523b3dadf3",
"commit": "9951a1b305289e2447f9f96361f8d1523b3dadf3"
},
{
"type": "trigger-selection-receipt",
"url": "https://github.com/uncfreak1255-code/loopspine/blob/9951a1b305289e2447f9f96361f8d1523b3dadf3/results/trigger-selection/2026-07-13T17-25-41-096Z/receipt.json",
"commit": "9951a1b305289e2447f9f96361f8d1523b3dadf3"
},
{
"type": "smoke-runner",
"url": "https://github.com/uncfreak1255-code/loopspine/blob/9951a1b305289e2447f9f96361f8d1523b3dadf3/scripts/run-trigger-selection-smoke.mjs",
"commit": "9951a1b305289e2447f9f96361f8d1523b3dadf3"
}
],
"recorded_at": "2026-07-13T17:28:25.705Z"
},
{
"id": "DF-03",
Expand Down
2 changes: 1 addition & 1 deletion dogfood/report.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"completed_tasks": 1,
"completed_tasks": 2,
"target_tasks": 10,
"minimum_public_samples": 3,
"verified_completion_rate": null,
Expand Down
2 changes: 1 addition & 1 deletion dogfood/report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dogfood Report

Progress: **1/10 real tasks**
Progress: **2/10 real tasks**

Performance rates remain **Pending** until 3 real tasks are recorded.

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"demo:render": "node scripts/render-demo.mjs",
"dogfood:report": "node scripts/dogfood-report.mjs --write",
"dogfood:record": "node scripts/record-dogfood.mjs",
"probe:installed-plugin": "node scripts/run-installed-plugin-probe.mjs"
"probe:installed-plugin": "node scripts/run-installed-plugin-probe.mjs",
"smoke:trigger-selection": "node scripts/run-trigger-selection-smoke.mjs"
},
"engines": {
"node": ">=20"
Expand Down
111 changes: 111 additions & 0 deletions results/trigger-selection/2026-07-13T17-25-41-096Z/receipt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"schema_version": 1,
"smoke": "explicit-trigger-selection",
"claim": "Explicit LoopSpine invocation applies its compact receipt contract while the same non-invoked readback remains plain.",
"limitations": [
"This does not prove automatic LoopSpine selection.",
"This does not prove that explicit invocation improves task quality.",
"This does not authorize global installation or hooks."
],
"started_at": "2026-07-13T17:25:41.096Z",
"finished_at": "2026-07-13T17:26:14.415Z",
"claude_version": "2.1.207 (Claude Code)",
"model_alias": "fable",
"plugin_root": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke",
"readback_path": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json",
"readback_sha256": "45f58ea8b6266fa6fc1182568ddd473e77917d8e4c79337234451a41b324a2ec",
"cases": {
"trigger": {
"invoked_loopspine": true,
"prompt": "/loopspine:loopspine Use the Read tool to read the exact file /Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json. This is a direct readback. Do not edit files.",
"duration_ms": 20418,
"exit_code": 0,
"timed_out": false,
"stdout_bytes": 15211,
"stderr_bytes": 0,
"final_text": "```text\nLANE: direct\nRESULT: success\nPROOF: Read /Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json (27 lines, shown above)\nBOUNDARY: read-only readback; no edits made or authorized\nRESIDUE: none\n```\n\nThe file defines the `loopspine` package at version 0.2.0 (private, ESM, Node >=20) with no dependencies — just scripts for tests, benchmarks, demos, dogfood reporting, the installed-plugin probe, and the `smoke:trigger-selection` script relevant to this branch.",
"isolation_evidence": {
"plugin_name": "loopspine",
"plugin_root": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke",
"loaded_plugin_count": 1,
"hook_event_count": 0
},
"evidence": {
"session_id": "f179412e-9438-4175-810a-aba3c1ce2a57",
"plugin_root": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke",
"reference_path": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json",
"reference_sha256": "45f58ea8b6266fa6fc1182568ddd473e77917d8e4c79337234451a41b324a2ec",
"init_event_index": 0,
"tool_use_event_index": 8,
"tool_result_event_index": 9,
"result_event_index": 14,
"tool_use_id": "toolu_01XCSjrVgnwJPKqM3693AMct",
"available_tools": [
"Read"
],
"mcp_servers": [],
"permission_mode": "plan",
"claude_code_version": "2.1.207",
"model": "claude-fable-5"
},
"receipt_evidence": {
"LANE": "direct",
"RESULT": "success",
"PROOF": "Read /Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json (27 lines, shown above)",
"BOUNDARY": "read-only readback; no edits made or authorized",
"RESIDUE": "none",
"trailing_context": "The file defines the `loopspine` package at version 0.2.0 (private, ESM, Node >=20) with no dependencies — just scripts for tests, benchmarks, demos, dogfood reporting, the installed-plugin probe, and the `smoke:trigger-selection` script relevant to this branch."
}
},
"non_trigger": {
"invoked_loopspine": false,
"prompt": "Use the Read tool to read the exact file /Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json. Report exactly loopspine@0.2.0 and nothing else. Do not invoke LoopSpine or edit files.",
"duration_ms": 12829,
"exit_code": 0,
"timed_out": false,
"stdout_bytes": 9594,
"stderr_bytes": 0,
"final_text": "loopspine@0.2.0",
"isolation_evidence": {
"plugin_name": "loopspine",
"plugin_root": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke",
"loaded_plugin_count": 1,
"hook_event_count": 0
},
"readback_evidence": {
"session_id": "4c3ff0c8-9e5e-4f47-b0d4-fc2d9dcf5c47",
"plugin_root": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke",
"reference_path": "/Users/sawbeck/Documents/Codex/2026-07-12/loopspine-df-02-explicit-trigger-smoke/package.json",
"reference_sha256": "45f58ea8b6266fa6fc1182568ddd473e77917d8e4c79337234451a41b324a2ec",
"init_event_index": 0,
"tool_use_event_index": 3,
"tool_result_event_index": 4,
"result_event_index": 6,
"tool_use_id": "toolu_01279R67tANTdpKuao6RdsQ4",
"available_tools": [
"Read"
],
"mcp_servers": [],
"permission_mode": "plan",
"claude_code_version": "2.1.207",
"model": "claude-fable-5"
}
}
},
"assertions": {
"trigger_exit_zero": true,
"trigger_not_timed_out": true,
"trigger_plugin_isolated": true,
"trigger_readback_verified": true,
"non_trigger_exit_zero": true,
"non_trigger_not_timed_out": true,
"non_trigger_plugin_isolated": true,
"non_trigger_readback_verified": true,
"trigger_receipt_contract_followed": true,
"non_trigger_exact_readback": true,
"readback_file_unchanged": true,
"tracked_worktree_unchanged": true
},
"errors": [],
"passed": true
}
61 changes: 61 additions & 0 deletions scripts/claude-access-events.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,67 @@ export function parseClaudeStream(rawOutput) {
return events;
}

export function verifyLoopSpineReceipt({ text, expectedLane, expectedProofTerms = [] }) {
const requiredFields = ["LANE", "RESULT", "PROOF", "BOUNDARY", "RESIDUE"];
let lines = String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
let trailingContext = "";
if (/^```/.test(lines[0] || "")) {
const closingFence = lines.indexOf("```", 1);
if (closingFence < 0) throw new Error("LoopSpine receipt has an unclosed code fence");
trailingContext = lines.slice(closingFence + 1).join(" ").trim();
lines = lines.slice(1, closingFence);
} else if (lines.length > requiredFields.length) {
trailingContext = lines.slice(requiredFields.length).join(" ").trim();
lines = lines.slice(0, requiredFields.length);
}
if (lines.length !== requiredFields.length) {
throw new Error("LoopSpine receipt must contain exactly five field lines");
}
if (trailingContext.length > 500 || /\b(?:cannot|can't|unable|failed|refus(?:e|ed|al)|error)\b/i.test(trailingContext)) {
throw new Error("LoopSpine receipt has invalid trailing context");
}
const fields = {};
for (let index = 0; index < requiredFields.length; index += 1) {
const field = requiredFields[index];
const match = lines[index].match(new RegExp(`^${field}:\\s*(\\S.*)$`));
if (!match) throw new Error(`LoopSpine receipt is missing ordered ${field}`);
fields[field] = match[1].trim();
}
if (expectedLane && fields.LANE !== expectedLane) {
throw new Error(`LoopSpine receipt lane must be ${expectedLane}`);
}
if (!/^(?:success|clean-no-op)$/.test(fields.RESULT)) {
throw new Error("LoopSpine receipt result is not successful");
}
for (const term of expectedProofTerms) {
if (!fields.PROOF.includes(term)) throw new Error(`LoopSpine receipt proof is missing ${term}`);
}
return { ...fields, trailing_context: trailingContext || null };
}

export function verifyClaudeIsolation({ rawOutput, pluginRoot }) {
const canonicalRoot = canonicalPath(pluginRoot, "plugin root");
const events = parseClaudeStream(rawOutput).map(({ event }) => event);
const init = events.find((event) => event.type === "system" && event.subtype === "init");
if (!init) throw new Error("Claude stream is missing the init event");
if (!Array.isArray(init.plugins) || init.plugins.length !== 1) {
throw new Error("Claude init event must contain only the LoopSpine plugin");
}
const [plugin] = init.plugins;
if (plugin?.name !== "loopspine" || typeof plugin.path !== "string"
|| canonicalPath(plugin.path, "loaded plugin path") !== canonicalRoot) {
throw new Error("Claude init event does not contain the expected isolated LoopSpine plugin");
}
const hookEvents = events.filter((event) => event.type === "system" && /^hook_/.test(event.subtype || ""));
if (hookEvents.length) throw new Error("Claude stream contains hook events during the isolated smoke");
return {
plugin_name: plugin.name,
plugin_root: canonicalRoot,
loaded_plugin_count: 1,
hook_event_count: 0
};
}

function verifyClaudeSession(events, canonicalRoot) {
const sessionIds = new Set(events.map((event) => event.session_id).filter((value) => typeof value === "string" && value));
if (sessionIds.size !== 1) throw new Error("Claude stream must contain exactly one session id");
Expand Down
Loading
Loading