Skip to content
Open
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 src/kiro_crew/apps/builtins/aws_control/crew/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Bundle curation for Share My Crew: what of an owner's crew travels into an image.

``packaging/`` holds all of it -- the curator, its deny-by-default guards on what
must not travel, and its tests. Nothing else lives here yet. The deploy driver, the
CloudFormation templates and the container's own build context arrive with the two
pieces that follow this one, each with the tests that pin it.

This ``__init__.py`` is load-bearing in one non-obvious way. It makes
``packaging/tests/`` a fully-qualified subpackage, so pytest resolves those tests
without putting this directory on ``sys.path``. Without it, pytest prepends this
directory instead, and ``packaging`` here then SHADOWS the PyPA ``packaging``
distribution for every other test in the same worker -- a name nothing in this
repository imports today, which is exactly the kind of landmine that goes off in an
unrelated change months later.

The curator is invoked as ``python -m packaging.build`` with this directory as cwd.
That runs in a CHILD process, so the shadow it relies on is scoped to that child and
cannot reach the gateway. No in-repo caller invokes it yet; the driver that will is
part of a later piece.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""The crew bundle producer.

Owns curation (deny-by-default) and the four-entry bundle the image layer copies
in. See ``PACKAGING-CONTRACT.md`` section "T1 -- curation and the bundle
producer" for the interface the other tracks depend on, and the module docstring
of :mod:`packaging.build` for why this is a fresh, self-contained port rather
than a copy of ``serving/smc/bundle.py``.
"""
5,997 changes: 5,997 additions & 0 deletions src/kiro_crew/apps/builtins/aws_control/crew/packaging/build.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""Building must not delete the signed plan it just read.

`build` stages the bundle and then replaces `--out` wholesale, which is what makes
a failed build leave nothing half-written. But `plan` writes its review template
into that same `--out`, so the documented flow -- plan, sign, build with the same
`--out` -- had the build delete the signed plan, silently. Reproduced end to end
before it was fixed: after the build, `curation-plan.json` was simply gone, and the
owner had to regenerate and re-sign with nothing telling them why.

Two rules keep the atomic swap without eating anything: the plan is carried through
staging so it lands back in the new directory, and a directory holding files the
build does not own is REFUSED by name rather than absorbed. The refusal matters
more than it looks: pointing `--out` at a directory of unrelated files is exactly
the case where a silent recursive delete does the most damage.
"""

from __future__ import annotations

import json
import os

import pytest

from .test_producer import load_build, make_crew

_posix_only = pytest.mark.skipif(
os.name != "posix",
reason="the crew bundle builder is POSIX-only; guarded off on platforms without an "
"atomic no-follow primitive (Windows). See the POSIX-only entry guard.",
)


def _signed_plan(mod, home, out):
"""Run the plan command, then sign what it wrote, as an owner would."""
mod._cmd_plan("frontdesk", out, [], home)
p = out / mod.PLAN_FILENAME
doc = json.loads(p.read_text(encoding="utf-8"))
doc["reviewed_by"] = "an owner"
doc["reviewed_at"] = "2026-09-04"
p.write_text(json.dumps(doc), encoding="utf-8")
return p


@_posix_only
def test_the_signed_plan_survives_the_build(tmp_path):
mod = load_build()
home = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ"}})
out = tmp_path / "out"
out.mkdir()
plan = _signed_plan(mod, home, out)

mod._cmd_build("frontdesk", out, [plan], home)

assert plan.is_file(), "the build deleted the signed plan it had just read"
doc = json.loads(plan.read_text(encoding="utf-8"))
assert doc["reviewed_by"] == "an owner", "the plan survived but lost its signature"


@_posix_only
def test_the_bundle_is_still_written(tmp_path):
"""Carrying the plan must not have broken what the build is for."""
mod = load_build()
home = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ"}})
out = tmp_path / "out"
out.mkdir()
plan = _signed_plan(mod, home, out)

mod._cmd_build("frontdesk", out, [plan], home)

for entry in ("agent.json", "mcp.json", "manifest.json", "skills"):
assert (out / entry).exists(), f"{entry} missing from the bundle"


@_posix_only
def test_an_unrelated_file_is_refused_not_deleted(tmp_path):
mod = load_build()
home = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ"}})
out = tmp_path / "out"
out.mkdir()
plan = _signed_plan(mod, home, out)
stranger = out / "my-notes.txt"
stranger.write_text("something the owner cares about", encoding="utf-8")

with pytest.raises(mod.ExportRefused) as exc:
mod._cmd_build("frontdesk", out, [plan], home)

# Named, so the owner knows which file stopped the build.
assert "my-notes.txt" in str(exc.value)
assert stranger.is_file(), "the build deleted a file it had refused to delete"
assert stranger.read_text(encoding="utf-8") == "something the owner cares about"


@_posix_only
def test_rebuilding_over_a_previous_bundle_still_works(tmp_path):
"""A previous bundle IS owned, so a rebuild must not be refused."""
mod = load_build()
home = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ"}})
out = tmp_path / "out"
out.mkdir()
plan = _signed_plan(mod, home, out)

mod._cmd_build("frontdesk", out, [plan], home)
mod._cmd_build("frontdesk", out, [plan], home) # must not raise

assert (out / "manifest.json").is_file()
assert plan.is_file()


@_posix_only
def test_MUTATION_the_plan_is_not_carried(tmp_path):
"""Drop the carry and the signed plan disappears again."""
home = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ"}})
out = tmp_path / "out"
out.mkdir()

bad = load_build(
mutate=(
" _write_bytes_nofollow(\n"
" staging / PLAN_FILENAME, carried_plan, staging_fd=_sfd, "
"rel=PLAN_FILENAME\n"
" )",
" pass",
)
)
plan = _signed_plan(bad, home, out)
bad._cmd_build("frontdesk", out, [plan], home)

assert not plan.exists(), "mutation did not take effect; this test proves nothing"


@_posix_only
def test_the_carried_plan_write_refuses_a_planted_symlink(tmp_path):
"""GPT/Opus :3426 -- the carried plan is written through the no-follow primitive.

The staging tree lives beside --out in a directory this build does not own, so a same-UID
process can plant a symlink at ``staging/curation-plan.json`` in the mkdir->write window.
A following ``write_bytes`` would truncate whatever the link named and ship a redirect as
the plan. The write now goes through ``_write_bytes_nofollow``; this pins that primitive's
contract directly -- a link at the leaf is refused at open, and its target is untouched.
"""
mod = load_build()
victim = tmp_path / "victim.txt"
victim.write_bytes(b"an external file the build user can write\n")
staging = tmp_path / "staging"
staging.mkdir()
leaf = staging / mod.PLAN_FILENAME
leaf.symlink_to(victim)

with pytest.raises(mod.ExportRefused) as caught:
mod._write_bytes_nofollow(leaf, b'{"reviewed_by": "an owner"}\n')
assert "symlink" in str(caught.value)
# The link's target is untouched -- the write was refused at open, not followed through.
assert victim.read_bytes() == b"an external file the build user can write\n"


@_posix_only
def test_the_carried_plan_lands_byte_for_byte(tmp_path):
"""Non-vacuity: the no-follow write is byte-exact, so the signed plan's bytes are preserved.

The plan carries a signature over its own bytes; a decode/re-encode round-trip could
corrupt it, so the primitive writes raw bytes.
"""
mod = load_build()
staging = tmp_path / "staging"
staging.mkdir()
leaf = staging / mod.PLAN_FILENAME
signed = b'{"reviewed_by": "an owner", "sig": "\xe2\x9c\x93 unicode check"}\n'
mod._write_bytes_nofollow(leaf, signed)
assert leaf.read_bytes() == signed
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""``build`` replaces ``--out`` recursively, so it must know the whole directory.

The first version of that guard listed owned TOP-LEVEL names. ``skills`` is one of
them, so a directory holding only ``skills/notes.txt`` passed the check and then had
notes.txt deleted by the recursive replace -- the check examined the container while
the delete reached the contents. These tests pin the nested case, and pin that
closing it did not break the documented plan/sign/build flow, which legitimately
re-uses the same ``--out``.
"""

from __future__ import annotations

import json
import os

import pytest

from .test_producer import load_build, make_crew

_posix_only = pytest.mark.skipif(
os.name != "posix",
reason="the crew bundle builder is POSIX-only; guarded off on platforms without an "
"atomic no-follow primitive (Windows). See the POSIX-only entry guard.",
)


def _build(mod, crew, out):
"""One real build into ``out``, using the suite's deny-all (no plan) shape."""
spec = mod.read_agent_spec(crew)
return mod.build_bundle(crew, spec, mod.enumerate_all(crew, spec), None, out)


def _built_bundle(tmp_path):
"""Run one real build and return its output directory."""
mod = load_build()
src = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ\nhours"}})
crew = mod.resolve_crew("frontdesk", src)
out = tmp_path / "bundle"
_build(mod, crew, out)
return mod, crew, out


@_posix_only
def test_a_nested_stranger_is_refused(tmp_path):
"""A file the build never wrote, nested under an owned directory name."""
mod, crew, out = _built_bundle(tmp_path)
stray = out / "skills" / "notes.txt"
stray.write_text("the owner's own notes\n", encoding="utf-8")

with pytest.raises(mod.ExportRefused) as excinfo:
_build(mod, crew, out)

assert "did not write" in str(excinfo.value) or "does not match" in str(excinfo.value)
assert stray.is_file(), "the refusal must happen BEFORE the delete, not after"
assert stray.read_text(encoding="utf-8") == "the owner's own notes\n"


@_posix_only
def test_a_bundle_shaped_directory_without_a_manifest_is_refused(tmp_path):
"""No manifest means the build cannot prove it produced what it is deleting."""
mod = load_build()
src = make_crew(tmp_path / "home", skills={"faq": {"SKILL.md": "# FAQ\nhours"}})
crew = mod.resolve_crew("frontdesk", src)
out = tmp_path / "handmade"
(out / "skills").mkdir(parents=True)
(out / "skills" / "notes.txt").write_text("mine\n", encoding="utf-8")

with pytest.raises(mod.ExportRefused, match="no manifest.json"):
_build(mod, crew, out)
assert (out / "skills" / "notes.txt").is_file()


@_posix_only
def test_rebuilding_a_clean_previous_bundle_still_works(tmp_path):
"""The ordinary case must not become a refusal."""
mod, crew, out = _built_bundle(tmp_path)
_build(mod, crew, out) # must not raise
assert (out / "manifest.json").is_file()


@_posix_only
def test_the_plan_flow_still_works(tmp_path):
"""plan, then build with the same --out: the plan survives and is not a stranger.

The plan is written into staging AFTER the manifest digest is taken, so it is
absent from the recorded digest. The verification skips it for exactly that
reason; if it stopped skipping it, this test fails rather than the flow silently
breaking again.
"""
mod, crew, out = _built_bundle(tmp_path)
plan_path = out / mod.PLAN_FILENAME
plan_path.write_text(json.dumps({"select": []}) + "\n", encoding="utf-8")

_build(mod, crew, out) # must not raise

assert plan_path.is_file(), "the plan must be carried across the swap"
assert json.loads(plan_path.read_text(encoding="utf-8")) == {"select": []}
Loading
Loading