diff --git a/.agents/skills/boatstack-autoplan/SKILL.md b/.agents/skills/boatstack-autoplan/SKILL.md index fcadbe3..2573814 100644 --- a/.agents/skills/boatstack-autoplan/SKILL.md +++ b/.agents/skills/boatstack-autoplan/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.agents/skills/boatstack-run/SKILL.md b/.agents/skills/boatstack-run/SKILL.md index e386e21..09a0a22 100644 --- a/.agents/skills/boatstack-run/SKILL.md +++ b/.agents/skills/boatstack-run/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.agents/skills/boatstack-update/SKILL.md b/.agents/skills/boatstack-update/SKILL.md index 7d58a13..fba8993 100644 --- a/.agents/skills/boatstack-update/SKILL.md +++ b/.agents/skills/boatstack-update/SKILL.md @@ -14,6 +14,12 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request only checksum-verified installation authority. Do not +request or materialize repository, provider, publication, product-delivery, or +merge authority. Installation receipts cannot be reused to broaden this scope. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +27,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.claude/skills/boatstack-autoplan/SKILL.md b/.claude/skills/boatstack-autoplan/SKILL.md index fcadbe3..2573814 100644 --- a/.claude/skills/boatstack-autoplan/SKILL.md +++ b/.claude/skills/boatstack-autoplan/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.claude/skills/boatstack-run/SKILL.md b/.claude/skills/boatstack-run/SKILL.md index e386e21..09a0a22 100644 --- a/.claude/skills/boatstack-run/SKILL.md +++ b/.claude/skills/boatstack-run/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.claude/skills/boatstack-update/SKILL.md b/.claude/skills/boatstack-update/SKILL.md index 7d58a13..fba8993 100644 --- a/.claude/skills/boatstack-update/SKILL.md +++ b/.claude/skills/boatstack-update/SKILL.md @@ -14,6 +14,12 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request only checksum-verified installation authority. Do not +request or materialize repository, provider, publication, product-delivery, or +merge authority. Installation receipts cannot be reused to broaden this scope. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +27,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.cursor/commands/boatstack-autoplan.md b/.cursor/commands/boatstack-autoplan.md index fcadbe3..2573814 100644 --- a/.cursor/commands/boatstack-autoplan.md +++ b/.cursor/commands/boatstack-autoplan.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.cursor/commands/boatstack-run.md b/.cursor/commands/boatstack-run.md index e386e21..09a0a22 100644 --- a/.cursor/commands/boatstack-run.md +++ b/.cursor/commands/boatstack-run.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.cursor/commands/boatstack-update.md b/.cursor/commands/boatstack-update.md index 7d58a13..fba8993 100644 --- a/.cursor/commands/boatstack-update.md +++ b/.cursor/commands/boatstack-update.md @@ -14,6 +14,12 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request only checksum-verified installation authority. Do not +request or materialize repository, provider, publication, product-delivery, or +merge authority. Installation receipts cannot be reused to broaden this scope. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +27,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.gemini/skills/boatstack-autoplan/SKILL.md b/.gemini/skills/boatstack-autoplan/SKILL.md index fcadbe3..2573814 100644 --- a/.gemini/skills/boatstack-autoplan/SKILL.md +++ b/.gemini/skills/boatstack-autoplan/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.gemini/skills/boatstack-run/SKILL.md b/.gemini/skills/boatstack-run/SKILL.md index e386e21..09a0a22 100644 --- a/.gemini/skills/boatstack-run/SKILL.md +++ b/.gemini/skills/boatstack-run/SKILL.md @@ -14,6 +14,22 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass `--repository-authority` +until the current configuration has exact verified fingerprint evidence. + +After a complete `installation.initialize`, `configuration.initialize`, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding `--repository-authority` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +37,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.gemini/skills/boatstack-update/SKILL.md b/.gemini/skills/boatstack-update/SKILL.md index 7d58a13..fba8993 100644 --- a/.gemini/skills/boatstack-update/SKILL.md +++ b/.gemini/skills/boatstack-update/SKILL.md @@ -14,6 +14,12 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `next`, `apply`, `recover`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +For this operation, request only checksum-verified installation authority. Do not +request or materialize repository, provider, publication, product-delivery, or +merge authority. Installation receipts cannot be reused to broaden this scope. Begin each cycle with an untargeted authority-bearing `next`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -21,7 +27,9 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `FRONTIER`, `BLOCKED`, `REFUSED`, or `UNRESOLVED` result for this operation. Treat `TERMINAL` as exact goal evidence. If recovery is active, use only a transition in `recovery_info.permitted` and the exact transaction ID. Never choose maintenance, correction, abandonment, diff --git a/.github/tests/test_detached_supervision.py b/.github/tests/test_detached_supervision.py index a9c67e8..6ef2943 100644 --- a/.github/tests/test_detached_supervision.py +++ b/.github/tests/test_detached_supervision.py @@ -284,6 +284,108 @@ def test_authority_free_frontier_does_not_block_authorized_plan_creation(self) - self.assertEqual(resolved["decision"]["kind"], "PRESCRIBED") self.assertEqual(resolved["decision"]["transition"]["id"], "plan.validate") + def test_one_delivery_context_rematerializes_repository_authority_after_initialization(self) -> None: + # control-law: retained-repository-source-crosses-maintenance-receipt-once + goal = ( + "--goal-id", "preserve-repository-authority-context", + "--goal-kind", "open-or-updated-pr", + "--delivery", "preserve-repository-authority-context", + ) + flow = ("--flow", "flow-preserve-repository-authority-context") + actor = ("--human", "contract") + self.helper_json( + "attach", "--repo", self.repo, *goal, *flow, *actor, + "--param", "topology=detached", "--param", "config_authority=repository", + ) + + prescribed = self.helper_json( + "next", "--repo", self.repo, *goal, *flow, *actor, + ) + self.assertEqual(prescribed["decision"]["kind"], "PRESCRIBED") + self.assertEqual( + prescribed["decision"]["transition"]["id"], "installation.initialize" + ) + + config = Path(self.work.name) / "retained-authority-project.json" + config.write_text( + json.dumps( + { + "schema_version": 2, + "project": { + "name": "retained-authority-fixture", + "default_branch": "main", + "commands": {}, + }, + "policy": {"plan_approval": "human", "visual_evidence": "optional"}, + "hosts": ["cli", "codex"], + } + ) + ) + initialized_process = self.run_helper( + "init", "--repo", self.repo, *goal, *flow, *actor, + "--param", f"config_path={config}", + ) + initialized = json.loads(initialized_process.stdout) + self.assertEqual(initialized["receipt"]["transition_id"], "installation.initialize") + self.assertEqual(initialized["receipt"]["flow_id"], flow[1]) + self.assertEqual(initialized["snapshot"]["configuration"]["value"], "verified") + for field in ('"admission"', '"receipt"', '"snapshot"', '"target_fingerprint"', '"recovery"'): + self.assertIn(field, initialized_process.stdout) + + engagement = self.helper_json( + "next", "--repo", self.repo, *goal, *flow, *actor, + "--repository-authority", + ) + self.assertEqual(engagement["decision"]["kind"], "PRESCRIBED") + self.assertEqual(engagement["decision"]["transition"]["id"], "engagement.begin") + + engaged_process = self.run_helper( + "apply", "--repo", self.repo, "--transition", "engagement.begin", + *goal, *flow, *actor, "--repository-authority", + ) + engaged = json.loads(engaged_process.stdout) + self.assertEqual(engaged["receipt"]["transition_id"], "engagement.begin") + self.assertEqual(engaged["receipt"]["flow_id"], flow[1]) + self.assertEqual( + {receipt["class"] for receipt in engaged["admission"]["authority"]["receipts"]}, + {"human", "repository-policy"}, + ) + for field in ('"admission"', '"receipt"', '"snapshot"', '"target_fingerprint"', '"recovery"'): + self.assertIn(field, engaged_process.stdout) + + plan = self.helper_json( + "next", "--repo", self.repo, *goal, *flow, *actor, + "--repository-authority", + ) + self.assertEqual(plan["decision"]["kind"], "PRESCRIBED") + self.assertEqual(plan["decision"]["transition"]["id"], "plan.create") + + def test_repository_authority_rematerialization_fails_closed_without_verified_config(self) -> None: + # control-law: repository-authority-requires-exact-verified-fingerprint + root = Path(self.work.name) / "unverified" + root.mkdir() + self._git(root, "init", "-b", "main") + self._git(root, "config", "user.name", "Boatstack Test") + self._git(root, "config", "user.email", "boatstack@example.invalid") + (root / "README.md").write_text("# unverified fixture\n") + self._git(root, "add", "README.md") + self._git(root, "commit", "-m", "fixture") + before = self.porcelain(root) + result = self.run_helper( + "next", "--repo", root, + "--goal-id", "unverified-authority", + "--goal-kind", "open-or-updated-pr", + "--delivery", "unverified-authority", + "--flow", "flow-unverified-authority", + "--human", "contract", "--repository-authority", + cwd=root, expected=1, + ) + self.assertIn( + "repository authority requires current verified configuration evidence", + result.stderr, + ) + self.assertEqual(self.porcelain(root), before) + if __name__ == "__main__": unittest.main() diff --git a/.github/tests/test_repository_contract.py b/.github/tests/test_repository_contract.py index 5814072..4815923 100644 --- a/.github/tests/test_repository_contract.py +++ b/.github/tests/test_repository_contract.py @@ -159,14 +159,33 @@ def test_operation_skills_are_three_distinct_authority_preserving_surfaces(self) "authority-free\n`FRONTIER`", "command-scoped context", "every `next`, `apply`, `recover`, and re-resolution", + "requested authority sources separately from currently\nmaterialized authority receipts", "untargeted authority-bearing `next`", "immediately preceding prescription", "complete apply response and stderr", "authority-bearing `FRONTIER`", + "every requested authority source is materialized\nor conclusively rejected against the post-receipt state", ): for name, skill in skills.items(): self.assertIn(contract, skill, name) self.assertIn("never grants merge authority", skills["boatstack-run"]) + for name in ("boatstack-autoplan", "boatstack-run"): + for contract in ( + "request human and repository-policy authority sources", + "repository-policy source remains requested", + "do not pass `--repository-authority`\nuntil the current configuration has exact verified fingerprint evidence", + "one bounded attempt for that receipt", + "The kernel must derive the\nreceipt from that exact verified fingerprint", + "do not retry it\nagain for the same receipt", + ): + self.assertIn(contract, skills[name], name) + update = skills["boatstack-update"] + self.assertIn("request only checksum-verified installation authority", update) + self.assertIn( + "Do not\nrequest or materialize repository, provider, publication, product-delivery, or\nmerge authority", + update, + ) + self.assertNotIn("repository-policy source remains requested", update) self.assertIn("Untargeted resolution selects\nonly a transition that advances the configured goal", readme) self.assertIn("exactly three operation skills", readme) diff --git a/boatstack/internal/effects/host_skills.go b/boatstack/internal/effects/host_skills.go index f41dc0f..af56d79 100644 --- a/boatstack/internal/effects/host_skills.go +++ b/boatstack/internal/effects/host_skills.go @@ -19,34 +19,56 @@ type hostSkillManifest struct { } type hostSkillMode struct { - Slug string - DisplayName string - Description string - Target string - Extra string + Slug string + DisplayName string + Description string + Target string + Extra string + AuthorityContract string } var hostSkillModes = []hostSkillMode{ { Slug: "boatstack-autoplan", DisplayName: "Boatstack Autoplan", - Description: "Create and approve a verified delivery plan.", - Target: "the `approved-plan` terminal", - Extra: "This trigger supplies planning intent only. It never approves unseen plan bytes.", + Description: "Create and approve a verified delivery plan.", + Target: "the `approved-plan` terminal", + Extra: "This trigger supplies planning intent only. It never approves unseen plan bytes.", + AuthorityContract: deliveryAuthorityContract, }, { Slug: "boatstack-run", DisplayName: "Boatstack Run", - Description: "Drive delivery to an open or updated pull request.", - Target: "the `open-or-updated-pr` terminal", - Extra: "This trigger never grants merge authority. Provider authority remains a separate verified receipt.", + Description: "Drive delivery to an open or updated pull request.", + Target: "the `open-or-updated-pr` terminal", + Extra: "This trigger never grants merge authority. Provider authority remains a separate verified receipt.", + AuthorityContract: deliveryAuthorityContract, }, { Slug: "boatstack-update", DisplayName: "Boatstack Update", - Description: "Apply a checksum-verified Boatstack update.", - Target: "the `installation.update` transition", - Extra: "This trigger does not reclassify or advance a product delivery.", + Description: "Apply a checksum-verified Boatstack update.", + Target: "the `installation.update` transition", + Extra: "This trigger does not reclassify or advance a product delivery.", + AuthorityContract: updateAuthorityContract, }, } +const deliveryAuthorityContract = `For this operation, request human and repository-policy authority sources. The +repository-policy source remains requested when configuration is +stale, uninitialized, or under recovery; do not pass ` + "`--repository-authority`" + ` +until the current configuration has exact verified fingerprint evidence. + +After a complete ` + "`installation.initialize`" + `, ` + "`configuration.initialize`" + `, or recovery +receipt, re-observe the post-receipt state. If configuration is now verified, +make one bounded attempt for that receipt to materialize the retained source by +adding ` + "`--repository-authority`" + ` to the next resolution. The kernel must derive the +receipt from that exact verified fingerprint. Never derive repository authority +from file presence, authentication, or prior conversation. If the source remains +unverifiable, record it as conclusively rejected and fail closed; do not retry it +again for the same receipt.` + +const updateAuthorityContract = `For this operation, request only checksum-verified installation authority. Do not +request or materialize repository, provider, publication, product-delivery, or +merge authority. Installation receipts cannot be reused to broaden this scope.` + func renderHostSkill(mode hostSkillMode) []byte { return []byte(fmt.Sprintf(`--- name: %s @@ -65,6 +87,10 @@ Bind one command-scoped context containing the exact goal, delivery, repository, worktree, flow, actor, and supplied authority receipts. Preserve that context through every `+"`next`"+`, `+"`apply`"+`, `+"`recover`"+`, and re-resolution. Never synthesize missing authority or infer it from authentication, files, branches, or prior conversation. +Within that context, track requested authority sources separately from currently +materialized authority receipts. + +%s Begin each cycle with an untargeted authority-bearing `+"`next`"+`. Apply only the stable transition ID from the immediately preceding prescription and only its @@ -72,12 +98,14 @@ declared parameters. Preserve the complete apply response and stderr, including admission, receipt, postcondition, error, recovery, and transaction fields. Re-resolve with the same context after every complete receipt. -Stop only on an authority-bearing `+"`FRONTIER`"+`, `+"`BLOCKED`"+`, `+"`REFUSED`"+`, or +Evaluate a frontier only after every requested authority source is materialized +or conclusively rejected against the post-receipt state. Stop only on an +authority-bearing `+"`FRONTIER`"+`, `+"`BLOCKED`"+`, `+"`REFUSED`"+`, or `+"`UNRESOLVED`"+` result for this operation. Treat `+"`TERMINAL`"+` as exact goal evidence. If recovery is active, use only a transition in `+"`recovery_info.permitted`"+` and the exact transaction ID. Never choose maintenance, correction, abandonment, merge, provider, or destructive authority as an escape from a frontier. -`, mode.Slug, mode.Description, mode.DisplayName, mode.Target, mode.Extra)) +`, mode.Slug, mode.Description, mode.DisplayName, mode.Target, mode.Extra, mode.AuthorityContract)) } func renderOpenAIMetadata(mode hostSkillMode) []byte { diff --git a/boatstack/internal/effects/host_skills_test.go b/boatstack/internal/effects/host_skills_test.go index f964019..2aa487b 100644 --- a/boatstack/internal/effects/host_skills_test.go +++ b/boatstack/internal/effects/host_skills_test.go @@ -49,7 +49,9 @@ func TestHostSkillProjectionPreservesAuthorityBoundaries(t *testing.T) { } for _, contract := range []string{ "authority-free\n`FRONTIER`", "command-scoped context", "every `next`, `apply`, `recover`, and re-resolution", + "requested authority sources separately from currently\nmaterialized authority receipts", "complete apply response and stderr", "authority-bearing `FRONTIER`", "Never synthesize missing\nauthority", + "every requested authority source is materialized\nor conclusively rejected against the post-receipt state", } { if !strings.Contains(value, contract) { t.Fatalf("%s is missing authority contract %q", path, contract) @@ -58,6 +60,60 @@ func TestHostSkillProjectionPreservesAuthorityBoundaries(t *testing.T) { } } +func TestHostSkillProjectionPreservesDeferredRepositoryAuthority(t *testing.T) { + // control-law: retained-repository-source-rematerializes-once-after-verification + files := desiredHostSkillFiles([]string{"cursor", "codex", "claude", "gemini"}) + for path, raw := range files { + if strings.HasSuffix(path, "openai.yaml") { + continue + } + value := string(raw) + isDelivery := strings.Contains(path, "boatstack-autoplan") || strings.Contains(path, "boatstack-run") + for _, contract := range []string{ + "request human and repository-policy authority sources", + "repository-policy source remains requested", + "do not pass `--repository-authority`\nuntil the current configuration has exact verified fingerprint evidence", + "one bounded attempt for that receipt", + "The kernel must derive the\nreceipt from that exact verified fingerprint", + "do not retry it\nagain for the same receipt", + } { + if isDelivery && !strings.Contains(value, contract) { + t.Fatalf("%s is missing deferred authority contract %q", path, contract) + } + if !isDelivery && strings.Contains(value, contract) { + t.Fatalf("%s improperly gains delivery authority contract %q", path, contract) + } + } + if !isDelivery { + for _, contract := range []string{ + "request only checksum-verified installation authority", + "Do not\nrequest or materialize repository, provider, publication, product-delivery, or\nmerge authority", + } { + if !strings.Contains(value, contract) { + t.Fatalf("%s is missing update authority boundary %q", path, contract) + } + } + } + } +} + +func TestHostSkillProjectionInventoriesEveryDriverPath(t *testing.T) { + // control-law: generated-driver-event-slice-is-complete + for path, raw := range desiredHostSkillFiles([]string{"cursor", "codex", "claude", "gemini"}) { + if strings.HasSuffix(path, "openai.yaml") { + continue + } + value := string(raw) + for _, pathEvent := range []string{ + "status", "`next`", "`apply`", "`recover`", "materialize", "Re-resolve", "Stop only", + } { + if !strings.Contains(value, pathEvent) { + t.Fatalf("%s omits driver path event %q", path, pathEvent) + } + } + } +} + func TestHostSkillProjectionFailsClosedOnUnmanagedCollision(t *testing.T) { // control-law: unmanaged-host-file-cannot-be-overwritten-by-installation repository := t.TempDir() diff --git a/release-notes/2026-08-11-preserve-repository-authority-context.md b/release-notes/2026-08-11-preserve-repository-authority-context.md new file mode 100644 index 0000000..9800d54 --- /dev/null +++ b/release-notes/2026-08-11-preserve-repository-authority-context.md @@ -0,0 +1,3 @@ +### Preserve repository authority through recovery + +Autoplan and Run now retain requested repository-policy authority across installation, configuration, and recovery receipts, then re-materialize it from verified configuration evidence so one invocation can continue safely without a second trigger.