diff --git a/packages/mosaic/framework/skills/mosaic-context-refresh/SKILL.md b/packages/mosaic/framework/skills/mosaic-context-refresh/SKILL.md index 56990a8a..4920233d 100644 --- a/packages/mosaic/framework/skills/mosaic-context-refresh/SKILL.md +++ b/packages/mosaic/framework/skills/mosaic-context-refresh/SKILL.md @@ -16,11 +16,12 @@ mutator remains behind the verified lease gate. - **Claude:** invoke only this direct command shape (no shell composition): ```bash - python3 /home/hermes/.config/mosaic/tools/lease-broker/recover-context.py begin --construction /absolute/path/to/mosaic-context-refresh-construction.json --compaction-epoch 0 --request-epoch 0 + python3 /absolute/path/to/mosaic/tools/lease-broker/recover-context.py begin --construction /absolute/path/to/mosaic-context-refresh-construction.json --compaction-epoch 0 --request-epoch 0 ``` - This is a literal argv template: replace the construction path with a literal absolute path and - replace each epoch with literal decimal digits. Do not use variables, quoting, globs, redirects, + This is a literal argv template: replace the recover-context.py path and construction JSON path + with the literal absolute paths for your install, then replace each epoch with literal decimal + digits. Do not use variables, quoting, globs, redirects, shell operators, substitutions, or line continuations. Claude's all-tools gate maps only this fully literal recovery shape to `mosaic_context_recover`; ordinary `Bash` remains gated. @@ -40,7 +41,7 @@ mutator remains behind the verified lease gate. `after_provider_response`). Then invoke completion with the same adapter form: Claude runs - `python3 /home/hermes/.config/mosaic/tools/lease-broker/recover-context.py complete`; Pi calls + `python3 /absolute/path/to/mosaic/tools/lease-broker/recover-context.py complete`; Pi calls `mosaic_context_recover` with `phase: "complete"`. Completion supplies no receipt or challenge argument. The broker observes the exact latest assistant entry, commits evidence, consumes its own fresh challenge, and promotes VERIFIED last. If observation is absent, malformed, stale, or diff --git a/packages/mosaic/src/lease-broker/framework_skill_portability_unittest.py b/packages/mosaic/src/lease-broker/framework_skill_portability_unittest.py index a8b74df9..8c5b6870 100644 --- a/packages/mosaic/src/lease-broker/framework_skill_portability_unittest.py +++ b/packages/mosaic/src/lease-broker/framework_skill_portability_unittest.py @@ -45,12 +45,12 @@ class FrameworkSkillPortabilityTest(unittest.TestCase): self.assertIn(CONSTRUCTION_PLACEHOLDER, source) resolved_recovery = "/opt/mosaic/tools/lease-broker/recover-context.py" resolved_construction = "/opt/mosaic/recovery/construction.json" - command = ( - source.replace(RECOVERY_PLACEHOLDER, resolved_recovery) - .replace(CONSTRUCTION_PLACEHOLDER, resolved_construction) - .split("```bash\n", 1)[1] - .split("\n```", 1)[0] + rendered = source.replace(RECOVERY_PLACEHOLDER, resolved_recovery).replace( + CONSTRUCTION_PLACEHOLDER, resolved_construction ) + match = re.search(r"```bash\s*\n\s*(.*?)\n\s*```", rendered, flags=re.DOTALL) + self.assertIsNotNone(match) + command = match.group(1) if match is not None else "" self.assertEqual( GATE.recovery_invocation_name( {"tool_name": "Bash", "tool_input": {"command": command}}, diff --git a/packages/mosaic/src/lease-broker/recovery_b1_adversarial_unittest.py b/packages/mosaic/src/lease-broker/recovery_b1_adversarial_unittest.py index 498b699a..3df91ec7 100644 --- a/packages/mosaic/src/lease-broker/recovery_b1_adversarial_unittest.py +++ b/packages/mosaic/src/lease-broker/recovery_b1_adversarial_unittest.py @@ -10,6 +10,7 @@ from __future__ import annotations import json import os +import re import socket import subprocess import sys @@ -156,19 +157,19 @@ class ClaudeRecoveryGateAdversarialTest(unittest.TestCase): def test_canonical_literal_and_shipped_skill_invocation_are_ungated(self) -> None: self.assertEqual(self.gate(self.canonical).returncode, 0) - shipped = ( - "python3 /home/hermes/.config/mosaic/tools/lease-broker/recover-context.py begin " - "--construction /absolute/path/to/mosaic-context-refresh-construction.json " - "--compaction-epoch 0 --request-epoch 0" - ) - self.assertIn(shipped, SKILL.read_text(encoding="utf-8")) - self.assertEqual( - self.gate( - shipped, - "/home/hermes/.config/mosaic/tools/lease-broker/recover-context.py", - ).returncode, - 0, + source = SKILL.read_text(encoding="utf-8") + recovery_placeholder = "/absolute/path/to/mosaic/tools/lease-broker/recover-context.py" + construction_placeholder = "/absolute/path/to/mosaic-context-refresh-construction.json" + self.assertIn(recovery_placeholder, source) + self.assertIn(construction_placeholder, source) + resolved_recovery = "/opt/mosaic/tools/lease-broker/recover-context.py" + rendered = source.replace(recovery_placeholder, resolved_recovery).replace( + construction_placeholder, "/opt/mosaic/recovery/construction.json" ) + match = re.search(r"```bash\s*\n\s*(.*?)\n\s*```", rendered, flags=re.DOTALL) + self.assertIsNotNone(match) + shipped = match.group(1) if match is not None else "" + self.assertEqual(self.gate(shipped, resolved_recovery).returncode, 0) def test_every_shell_active_vector_in_every_argv_position_falls_through_to_bash_deny(self) -> None: positions = (