Import publish-grant runs locally; leave the push to Activity Core (ACTIVITY-WP-0046-T02)
Some checks failed
Governed runtime contract / contract (push) Failing after 11s
Some checks failed
Governed runtime contract / contract (push) Failing after 11s
The artifact transfer now accepts a repository grant v2. It still imports exactly one commit, refuses any changed path outside publish_scope.path_prefixes, never pushes, and records published: false for Activity Core's publisher (hub decision 1c9817ee). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 269692@bnt-lap001 Assistant-Session: 8077e68a-0985-4b41-898c-2dc602b0d6be
This commit is contained in:
parent
4695649cc6
commit
5c36d80756
2 changed files with 60 additions and 2 deletions
|
|
@ -119,10 +119,14 @@ class RepositoryArtifactTransfer:
|
|||
raise RepositoryArtifactError(
|
||||
"artifact transfer requires the active repository transaction"
|
||||
)
|
||||
if grant.min_commits != 1 or grant.max_commits != 1 or grant.publish:
|
||||
if grant.min_commits != 1 or grant.max_commits != 1:
|
||||
raise RepositoryArtifactError(
|
||||
"artifact transfer v1 requires exactly one local commit"
|
||||
)
|
||||
# A publish grant (v2) only marks the imported commit for Activity
|
||||
# Core's publisher; this transfer never pushes.
|
||||
if grant.publish and grant.publish_scope is None:
|
||||
raise RepositoryArtifactError("publish grant has no publish_scope")
|
||||
self.transaction = transaction
|
||||
self.grant = grant
|
||||
self.cancel = resolve_cancel(cancel)
|
||||
|
|
@ -284,7 +288,16 @@ class RepositoryArtifactTransfer:
|
|||
_git(
|
||||
staging, "merge", "--ff-only", "--no-edit", "--no-stat", self._head
|
||||
)
|
||||
inspection.validate_acceptance(self.grant.acceptance_policy())
|
||||
inspected = inspection.validate_acceptance(
|
||||
self.grant.acceptance_policy()
|
||||
)
|
||||
scope = self.grant.publish_scope
|
||||
if scope is not None and not all(
|
||||
scope.covers_path(path) for path in inspected.changed_paths
|
||||
):
|
||||
raise RepositoryArtifactError(
|
||||
"artifact changes paths outside the publish scope"
|
||||
)
|
||||
self._check()
|
||||
_git(
|
||||
baseline.repo_root,
|
||||
|
|
@ -316,5 +329,7 @@ class RepositoryArtifactTransfer:
|
|||
"head": accepted.head,
|
||||
"imported": True,
|
||||
}
|
||||
if self.grant.publish:
|
||||
evidence["published"] = False
|
||||
self._bundle = None
|
||||
return evidence
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from rein_aharness.execution_cancel import ExecutionCancel, ExecutionCancelled
|
|||
from rein_aharness.repository_artifact import (
|
||||
RepositoryArtifactError,
|
||||
RepositoryArtifactTransfer,
|
||||
_git as _git_spy,
|
||||
)
|
||||
from rein_aharness.repository_grant import RepositoryGrant
|
||||
from rein_aharness.repository_transaction import (
|
||||
|
|
@ -310,3 +311,45 @@ def test_ignored_sandbox_cache_is_not_imported(tmp_path):
|
|||
transfer.import_after_teardown(head)
|
||||
assert not (source / "cache").exists()
|
||||
assert tx.acceptance.changed_paths == ("result.txt",)
|
||||
|
||||
|
||||
def _publish_grant():
|
||||
return RepositoryGrant.from_mapping(
|
||||
{
|
||||
"version": "2",
|
||||
"allowed_paths": ["out/"],
|
||||
"commit_count": {"min": 1, "max": 1},
|
||||
"publish": True,
|
||||
"publish_scope": {
|
||||
"remote": "origin",
|
||||
"branch": "main",
|
||||
"path_prefixes": ["out/pub/"],
|
||||
"fast_forward_only": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_publish_grant_imports_locally_and_leaves_push_to_publisher(tmp_path):
|
||||
source, sandbox, _ = fixture(tmp_path)
|
||||
with RepositoryTransaction(source, state_dir=tmp_path / "state") as tx:
|
||||
transfer = RepositoryArtifactTransfer(tx, _publish_grant())
|
||||
head = commit(sandbox, "out/pub/brief.md")
|
||||
capture(transfer, sandbox, head)
|
||||
with patch("rein_aharness.repository_artifact._git", wraps=_git_spy) as spy:
|
||||
result = transfer.import_after_teardown(head)
|
||||
assert not any("push" in call.args for call in spy.call_args_list)
|
||||
assert result["imported"] and result["published"] is False
|
||||
assert git(source, "rev-parse", "HEAD") == head
|
||||
|
||||
|
||||
def test_publish_grant_refuses_paths_outside_publish_scope(tmp_path):
|
||||
source, sandbox, _ = fixture(tmp_path)
|
||||
with RepositoryTransaction(source, state_dir=tmp_path / "state") as tx:
|
||||
baseline = tx.baseline.head
|
||||
transfer = RepositoryArtifactTransfer(tx, _publish_grant())
|
||||
head = commit(sandbox, "out/private.md")
|
||||
capture(transfer, sandbox, head)
|
||||
with pytest.raises(RepositoryArtifactError, match="publish scope"):
|
||||
transfer.import_after_teardown(head)
|
||||
assert git(source, "rev-parse", "HEAD") == baseline
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue