Commit Graph

884 Commits

Author SHA1 Message Date
didericis-claude cceb300d58 test: add cross-backend parity tests (PRD 0042)
Closes #139. Adds tests/unit/test_backend_parity.py which verifies that
DockerBottle and SmolmachinesBottle expose identical observable contracts
for agent_argv shape, env injection, exec user-switching, ExecResult
fields, and close() idempotency. All assertions use mock subprocess
layers — no live Docker daemon or VM required.
2026-06-02 11:30:54 -04:00
didericis-claude b63927368a docs: add PRD 0042 2026-06-02 11:30:54 -04:00
didericis 4319b4ef3b refactor(git-http): rename variable to indicate configurability
test / unit (pull_request) Successful in 38s
test / integration (pull_request) Successful in 54s
test / unit (push) Successful in 40s
test / integration (push) Successful in 57s
2026-06-02 11:24:54 -04:00
didericis-claude 71005d56e2 docs: mark PRD 0041 Active
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 53s
2026-06-02 11:23:19 -04:00
didericis-claude 96b0c3f1fa fix(git-http): validate Content-Length and cap body size (PRD 0041)
Before this change, int() on a non-numeric Content-Length raised an
unhandled ValueError, crashing the request handler. There was also no
upper bound on how much memory a POST body could consume.

After this change:
- Non-numeric or missing Content-Length returns HTTP 400.
- Negative Content-Length returns HTTP 400.
- Bodies declared larger than 1 MiB (_MAX_BODY_BYTES) return HTTP 413,
  matching the cap already in supervise_server.py.

Closes #138
2026-06-02 11:23:19 -04:00
didericis-claude 3087a9aa8b docs: add PRD 0041 2026-06-02 11:23:19 -04:00
didericis-claude e43f75dd1b refactor: rename machine_name to instance_name in _bottle_for_slug
test / unit (pull_request) Successful in 32s
test / integration (pull_request) Successful in 41s
test / unit (push) Successful in 39s
test / integration (push) Successful in 1m0s
2026-06-02 11:16:17 -04:00
didericis-claude 4ad1ff3898 docs: mark PRD 0040 Active 2026-06-02 11:16:17 -04:00
didericis-claude a3d9ac9605 feat: persist backend in BottleMetadata; use it in resume and dashboard reattach (PRD 0040)
BottleMetadata gains a backend field (default ""). Docker prepare writes
"docker"; smolmachines prepare writes "smolmachines". read_metadata
deserialises it with "" as the backward-compatible default.

resume now passes metadata.backend to _launch_bottle so a preserved
smolmachines bottle is resumed on the right backend without requiring
BOT_BOTTLE_BACKEND to be set manually.

_bottle_for_slug now reads metadata.backend and constructs a
SmolmachinesBottle for smolmachines slugs instead of always defaulting
to DockerBottle. No-metadata slugs still fall back to Docker.

Closes #137
2026-06-02 11:16:17 -04:00
didericis-claude 70c9f7254c docs: add PRD 0040 2026-06-02 11:16:17 -04:00
didericis-claude b9108339e7 docs: mark PRD 0039 Active
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 43s
test / unit (push) Successful in 30s
test / integration (push) Successful in 41s
2026-06-02 11:15:27 -04:00
didericis-claude e5b5dd16f1 feat(dashboard): guard capability-block approval for smolmachines bottles (PRD 0039)
apply_capability_change is Docker-only teardown/apply code. Before this
change it was called regardless of backend, so approving a capability-block
proposal from a smolmachines agent would run Docker commands against a
slug that has no Docker container.

After this change approve() reads the bottle's metadata: if compose_project
is empty (the smolmachines indicator) it raises CapabilityApplyError with
a clear operator message before any teardown runs. Docker bottles (non-empty
compose_project) and unknown bottles (no metadata) fall through to the
existing Docker path unchanged.

Closes #136
2026-06-02 11:15:27 -04:00
didericis-claude cf76d1a245 docs: add PRD 0039 2026-06-02 11:15:27 -04:00
didericis-claude 717a9126e1 docs: mark PRD 0038 Active
test / integration (pull_request) Successful in 56s
test / unit (pull_request) Successful in 38s
test / unit (push) Successful in 31s
test / integration (push) Successful in 42s
2026-06-02 14:38:44 +00:00
didericis-claude 8830306101 feat(smolmachines): resolve manifest env through resolve_env() (PRD 0038)
Before this change smolmachines prepare.py spliced bottle.env directly
into guest_env, so ?prompt and ${HOST_VAR} entries reached the VM as
raw sentinels rather than being prompted or interpolated.

After this change prepare.py calls resolve_env(), matching the Docker
backend's contract. Forwarded (secret/interpolated) values still flow
through smolvm -e K=V argv — the known exposure gap documented in PRD
0038's open question.

Closes #135
2026-06-02 14:38:36 +00:00
didericis-claude 1c242b0ad9 docs: add PRD 0038
test / unit (pull_request) Successful in 52s
test / integration (pull_request) Successful in 1m2s
2026-06-02 10:28:04 -04:00
didericis-codex f95ef0c446 complete(prd): mark PRD 0037 active
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 44s
test / unit (push) Successful in 29s
test / integration (push) Successful in 47s
2026-06-02 08:15:20 +00:00
didericis-codex 6e954da9b7 fix(pipelock): validate yaml render config 2026-06-02 08:15:20 +00:00
didericis-codex 9185c145a1 docs(prd): add pipelock yaml contract
test / unit (pull_request) Successful in 31s
test / integration (pull_request) Successful in 42s
2026-06-02 04:14:45 -04:00
didericis-codex a79ef61b62 complete(prd): mark PRD 0036 active
test / unit (pull_request) Successful in 32s
test / integration (pull_request) Successful in 44s
test / unit (push) Successful in 31s
test / integration (push) Successful in 45s
2026-06-02 08:10:34 +00:00
didericis-codex 0a8bba58c7 fix(codex): harden auth redaction 2026-06-02 08:10:34 +00:00
didericis-codex 2247d730cd docs(prd): add codex auth redaction policy
test / unit (pull_request) Successful in 35s
test / integration (pull_request) Successful in 42s
2026-06-02 04:09:18 -04:00
didericis-codex 3472e06efb complete(prd): mark PRD 0035 active
test / integration (pull_request) Successful in 1m4s
test / unit (pull_request) Successful in 45s
test / unit (push) Successful in 36s
test / integration (push) Successful in 46s
2026-06-02 08:06:53 +00:00
didericis-codex 82ce5d3034 fix(supervise): bound response waits 2026-06-02 08:06:45 +00:00
didericis-codex 7c260eeff9 docs(prd): add supervise wait bounds
test / unit (pull_request) Successful in 36s
test / integration (pull_request) Successful in 54s
2026-06-02 07:58:39 +00:00
didericis-codex fe6059e4a6 complete(prd): mark PRD 0034 active
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 52s
test / unit (push) Successful in 34s
test / integration (push) Successful in 50s
2026-06-02 07:52:38 +00:00
didericis-codex 31708abfad fix(sidecar): queue restart signals 2026-06-02 07:52:19 +00:00
didericis-codex 1b34b1df85 docs(prd): add sidecar restart semantics
test / unit (pull_request) Successful in 42s
test / integration (pull_request) Successful in 59s
2026-06-02 07:43:34 +00:00
didericis-codex 51831bf9c0 complete(prd): mark PRD 0033 active
test / unit (pull_request) Successful in 36s
test / integration (pull_request) Successful in 57s
test / unit (push) Successful in 39s
test / integration (push) Successful in 56s
2026-06-02 07:32:29 +00:00
didericis-codex 8f28bd81a7 refactor(manifest): split schema boundaries 2026-06-02 07:32:06 +00:00
didericis-codex 662e3e1f95 docs(prd): point manifest boundaries to issue 125
test / unit (pull_request) Successful in 41s
test / integration (pull_request) Successful in 57s
2026-06-02 07:31:29 +00:00
didericis-codex 6315456a59 docs(prd): add manifest schema boundaries
test / unit (pull_request) Successful in 48s
test / integration (pull_request) Successful in 1m4s
2026-06-02 07:23:04 +00:00
didericis-claude a81f0ffa49 fix(smolmachines): raise SmolvmError instead of die() on wait_exec_ready timeout
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 58s
test / unit (push) Successful in 38s
test / integration (push) Successful in 55s
die() raises Die(SystemExit), which implies a process exit. A timeout in
wait_exec_ready is a bringup failure — raising SmolvmError lets the caller
decide whether it's fatal, consistent with how machine_start failures propagate.
2026-06-02 06:29:05 +00:00
didericis-claude c39bbe265b complete(prd): mark PRD 0032 active
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 58s
All three issues implemented and 805 tests passing.
2026-06-02 06:23:46 +00:00
didericis-claude 0d922371b0 refactor(smolmachines): decompose launch(), add wait_exec_ready, file-lock allocate() (PRD 0032)
Decompose the 207-line launch() into six named helpers: _allocate_resources,
_mint_certs, _start_bundle, _discover_urls, _launch_vm, _init_vm. Each has
explicit inputs/outputs and is independently testable.

Replace time.sleep(1.5) with smolvm.wait_exec_ready(), which polls
`machine exec true` with exponential backoff. Exits as soon as the exec
channel is ready; dies loudly with a timeout message instead of silently
leaving the VM in an unknown state.

File-lock loopback_alias.allocate() with fcntl.flock(LOCK_EX) so concurrent
bottle launches can't race on docker state and claim the same alias.
2026-06-02 06:23:39 +00:00
didericis-claude fe97b6014d docs(prd): PRD 0032 — smolmachines launch decomposition
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 44s
Split launch() into named per-step helpers, replace time.sleep(1.5) with
a readiness poll, and file-lock loopback alias allocation. Addresses the
three actionable items from the #117 hotspot review of smolmachines/launch.py.
2026-06-02 06:14:16 +00:00
didericis-claude 07c8593999 refactor(egress): EgressRoute inherits Route from egress_addon_core
test / unit (pull_request) Successful in 32s
test / unit (push) Successful in 31s
test / integration (push) Successful in 38s
test / integration (pull_request) Successful in 47s
EgressRoute now extends egress_addon_core.Route, which holds the four
wire-visible fields (host, path_allowlist, auth_scheme, token_env).
EgressRoute adds only the three host-side fields (token_ref, roles,
tls_passthrough) that are never serialised to the sidecar.

_route_to_yaml_fields is typed as Route -> dict, making the host→wire
boundary explicit: only fields declared on the base class cross into the
YAML the addon reads.
2026-06-02 05:58:59 +00:00
didericis-claude f15721b424 complete(prd): mark PRD 0031 active
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 46s
Provisioned-wins merge and _route_to_yaml_fields are implemented and all
tests pass.
2026-06-02 05:45:28 +00:00
didericis-claude 10d0872043 refactor(egress): provisioned-wins merge + _route_to_yaml_fields (PRD 0031)
Replace _merge_provider_route's five-case nested conditional with a flat
provisioned-wins merge: provider routes claim their hosts outright, manifest
routes for unclaimed hosts append unchanged. Token slot assignment moves to a
single _assign_token_slots pass over the merged list.

Add _route_to_yaml_fields as the single authoritative EgressRoute→YAML mapping,
eliminating the risk of EgressRoute and egress_addon_core.Route silently
drifting apart when new fields are added.

egress_manifest_routes is now a pure lifter with no slot assignment.
_merge_provider_route and _find_or_alloc_token_env are removed.

Tests updated: conflict-die case removed, upgrade-bare replaced with
provider-wins semantics, slot-assignment tests moved to TestSlotAssignment.
2026-06-02 05:45:20 +00:00
didericis-claude ae33d1abfb docs(prd): revise PRD 0031 — provisioned-wins merge + Route type consolidation
test / unit (pull_request) Successful in 42s
test / integration (pull_request) Successful in 1m0s
Expands scope to cover both remaining egress hotspot tasks from #117:
- Replaces the named-helper design with a flat provisioned-wins merge
  (provider routes own their hosts; manifest fills gaps; no upgrade or
  conflict-detection logic needed).
- Adds _route_to_yaml_fields as the single authoritative EgressRoute→Route
  mapping to prevent silent type drift between host and addon.
- Notes that the mitmproxy pure-function split is already clean (decide +
  is_git_push_request) and requires no structural change.
2026-06-02 05:26:15 +00:00
didericis-claude f596464f3f docs(prd): add PRD 0031 — split _merge_provider_route into named case helpers
test / unit (pull_request) Successful in 31s
test / integration (pull_request) Successful in 41s
2026-06-02 05:08:59 +00:00
didericis-claude e528d5c5af docs(prd): update PRD 0030 design to reflect provisioned_env approach
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 45s
test / unit (push) Successful in 31s
test / integration (push) Successful in 43s
Revises the Design section to describe the implemented solution:
provisioned_env on AgentProvisionPlan rather than an intermediate
egress_resolve_token_values_with_provider function. Drops the old
sentinel/lazy-import design narrative.
2026-06-02 04:54:09 +00:00
didericis-claude 0e29bcc829 refactor(egress): use provisioned_env instead of sentinel for Codex token (PRD 0030)
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 45s
Add `provisioned_env: dict[str, str]` to `AgentProvisionPlan`. When
`forward_host_credentials=True`, `agent_provision_plan` reads the host
Codex access token at prepare time and stores it under
`CODEX_HOST_CREDENTIAL_TOKEN_REF`. Both backends merge `provisioned_env`
over `os.environ` before calling `egress_resolve_token_values`, so the
token slot resolves like any other manifest-declared token ref.

Removes `egress_resolve_token_values_with_provider` and the sentinel
`continue` skip from `egress_resolve_token_values`. The function is now
fully generic — it neither knows nor cares about provider identity.
2026-06-02 04:53:23 +00:00
didericis-claude 8c2b59ca94 complete(prd): mark PRD 0030 active
test / unit (push) Successful in 45s
test / integration (push) Successful in 1m0s
test / unit (pull_request) Successful in 39s
test / integration (pull_request) Successful in 58s
2026-06-02 04:22:52 +00:00
didericis-claude 75f0f9d907 refactor(egress): deduplicate token resolution across backends (PRD 0030)
Extract egress_resolve_token_values_with_provider into bot_bottle/egress.py.
Both docker and smolmachines launch paths now call the shared function
instead of duplicating the forward_host_credentials / CODEX_HOST_CREDENTIAL_TOKEN_REF
resolution block.

Also fixes the host_env: object annotation on smolmachines._resolve_token_env
to the correct dict[str, str].

Closes #118.
2026-06-02 04:22:43 +00:00
didericis-claude 6682357fbb docs(prd): add PRD 0030 — deduplicate egress token resolution
Extracts the forward_host_credentials / CODEX_HOST_CREDENTIAL_TOKEN_REF
resolution block, currently copy-pasted in both docker and smolmachines
launch files, into a single shared function in bot_bottle/egress.py.

Closes #118. Found via #117 hotspot review.
2026-06-02 04:17:39 +00:00
didericis 2dd8113f7c fix(smolmachines): retry CA install after exec SIGKILL
test / unit (push) Successful in 38s
test / integration (push) Successful in 54s
2026-06-01 23:28:33 -04:00
didericis-codex 36e3443d2e fix(codex): defer workspace trust handling
test / unit (pull_request) Successful in 29s
test / integration (pull_request) Successful in 42s
test / unit (push) Successful in 30s
test / integration (push) Successful in 44s
2026-06-02 03:11:51 +00:00
didericis-codex d6ebd0d2eb fix(egress): skip token slots for unauth provider routes
test / unit (pull_request) Successful in 30s
test / integration (pull_request) Successful in 43s
2026-06-02 03:06:10 +00:00
didericis-codex eb6bace84f complete(prd): mark PRD 0029 active
test / unit (pull_request) Successful in 35s
test / integration (pull_request) Successful in 42s
2026-06-02 03:00:47 +00:00