Commit Graph

593 Commits

Author SHA1 Message Date
didericis-claude fcd1b34e49 docs: mark PRD 0044 Active
test / unit (pull_request) Successful in 34s
test / integration (pull_request) Successful in 41s
test / unit (push) Successful in 34s
test / integration (push) Successful in 43s
2026-06-02 12:12:08 -04:00
didericis-claude a0762ac2d3 test: add cross-backend print parity tests (PRD 0044)
Shared fixtures build DockerBottlePlan and SmolmachinesBottlePlan from
identical git_gate_plan and egress_plan inputs and assert that both
backends render the same git gate lines (name → host:port) and egress
lines (host [auth:scheme] when authenticated, host alone otherwise).
2026-06-02 12:12:08 -04:00
didericis-claude 53219a55e1 refactor: hoist plan fields and print to BottlePlan base class (PRD 0044)
Move git_gate_plan, egress_plan, supervise_plan, and agent_provision
from DockerBottlePlan and SmolmachinesBottlePlan into BottlePlan.
Replace the abstract print method with a single concrete implementation
that renders git gate entries as "name → upstream_host:upstream_port"
and egress routes with conditional "[auth:scheme]" annotations.
2026-06-02 12:12:08 -04:00
didericis-claude 71ac555f25 docs(prd): add PRD 0044 — print parity across backends 2026-06-02 12:12:08 -04:00
didericis-claude f25fa589fe fix(git-http): extract peer variable to clarify access hook call convention
test / unit (push) Successful in 31s
test / integration (push) Successful in 43s
Both remote-addr and peer-addr args to the access hook are the same
TCP peer in this non-proxied stack. Extract a `peer` variable so the
intentional repetition is visible. Closes #148.
2026-06-02 16:08:15 +00:00
didericis-claude 4fdf354b4f docs: mark PRD 0043 Active
test / unit (pull_request) Successful in 34s
test / integration (pull_request) Successful in 43s
test / unit (push) Successful in 34s
test / integration (push) Successful in 41s
2026-06-02 11:48:24 -04:00
didericis-claude 5a2011c48f fix: close child stdout pipes on restart and loop convergence (PRD 0043)
Closes #140. In restart_daemon, the old process's stdout pipe was never
explicitly closed after p.wait() returned, leaking the fd until the
supervisor object was GC'd. Similarly, when the watch loop converged
(all children dead), no pipe was closed. Both paths now call
p.stdout.close() immediately after the process is confirmed exited.
Tests enforce this with warnings.simplefilter("error", ResourceWarning)
in TestSupervisor.setUp.
2026-06-02 11:48:24 -04:00
didericis-claude 19ebcd52a1 docs: add PRD 0043 2026-06-02 11:48:24 -04:00
didericis-claude 2c061d9cd9 docs: mark PRD 0042 Active
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 55s
test / unit (push) Successful in 40s
test / integration (push) Successful in 46s
2026-06-02 11:30:54 -04:00
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