Commit Graph

637 Commits

Author SHA1 Message Date
didericis-claude a6733921c7 docs(prd): add PRD 0049 — named/labelled agents
test / unit (pull_request) Successful in 42s
test / integration (pull_request) Successful in 1m7s
Draft PRD for prompting operators for a custom label and optional
ANSI color at agent launch time, storing both in metadata.json, and
surfacing the label (in color) in the dashboard's active-agents pane.

Closes #171
2026-06-03 13:49:49 -04:00
didericis-codex f12b0f754e docs(prd): reactivate PRD 0049 without tmux alert
test / unit (pull_request) Successful in 37s
test / integration (pull_request) Successful in 55s
test / unit (push) Successful in 51s
test / integration (push) Successful in 59s
2026-06-03 17:35:10 +00:00
didericis-codex a593b157d6 fix(cli): remove supervise tmux alert handling
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 55s
2026-06-03 17:34:41 +00:00
didericis-codex 15b54cdff2 docs(prd): reactivate PRD 0049 without queue highlight
test / unit (pull_request) Successful in 44s
test / integration (pull_request) Successful in 54s
2026-06-03 17:31:49 +00:00
didericis-codex d3bc463295 fix(cli): remove supervise queue highlight
test / unit (pull_request) Successful in 43s
test / integration (pull_request) Successful in 52s
2026-06-03 17:31:19 +00:00
didericis-codex 50ec920243 docs(prd): activate PRD 0049 strip dashboard to supervise
test / unit (pull_request) Successful in 37s
test / integration (pull_request) Successful in 55s
2026-06-03 17:27:56 +00:00
didericis-codex 4372b8a6dd docs(cli): update supervise code references
test / unit (pull_request) Successful in 35s
test / integration (pull_request) Successful in 52s
2026-06-03 17:27:14 +00:00
didericis-codex 63a7e63ce9 test(cli): clean up supervise test naming
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 48s
2026-06-03 17:26:15 +00:00
didericis-codex c0e1f5fd70 docs(prd): supersede dashboard agent PRDs
test / unit (pull_request) Successful in 36s
test / integration (pull_request) Successful in 54s
2026-06-03 17:25:32 +00:00
didericis-codex 41570e04c0 test(cli): update supervise triage coverage
test / unit (pull_request) Successful in 40s
test / integration (pull_request) Successful in 51s
2026-06-03 17:25:09 +00:00
didericis-codex 6f0a42159f refactor(cli): rename dashboard command to supervise
test / unit (pull_request) Failing after 38s
test / integration (pull_request) Successful in 54s
2026-06-03 17:23:40 +00:00
didericis-codex 5c17f0de95 docs(prd): rename strip dashboard PRD to 0049 2026-06-03 17:19:41 +00:00
didericis 8a09e32fcc docs(prd): add PRD 0050 -- strip dashboard to supervisor tui
test / unit (pull_request) Successful in 51s
test / integration (pull_request) Successful in 54s
2026-06-03 13:15:05 -04:00
didericis-claude 83463f1cc8 docs(prd): activate PRD 0048 — SSH deploy-key provisioning
test / unit (push) Successful in 40s
test / integration (push) Successful in 41s
2026-06-03 11:58:36 -04:00
didericis-claude 0b5d59cf9e feat(prd-0048): implement SSH deploy-key provisioning with contrib/gitea
- manifest_git.py: add ProvisionedKeyConfig dataclass; extend GitEntry
  with ProvisionedKey field (optional); make IdentityFile default to ""
  so provisioned_key entries can be constructed without a static path;
  add _parse_provisioned_key_config; update from_repos_entry to accept
  provisioned_key as an alternative to identity (mutually exclusive,
  parser rejects both-or-neither)

- deploy_key_provisioner.py (new): DeployKeyProvisioner ABC with create()
  and delete() abstract methods; get_provisioner() factory with lazy
  contrib import for gitea

- contrib/gitea/deploy_key_provisioner.py (new): GiteaDeployKeyProvisioner
  generating ed25519 keypairs via ssh-keygen and managing them through
  the Gitea deploy-key API (POST/DELETE); 404 on delete is success;
  all other errors raise RuntimeError

- git_gate.py: add _provision_dynamic_key() called in GitGate.prepare()
  for entries with ProvisionedKey — generates key, writes private key
  and key ID files to stage_dir, patches GitGateUpstream.identity_file;
  add revoke_git_gate_provisioned_keys() for teardown — raises on failure

- docker/launch.py: call revoke_git_gate_provisioned_keys() in teardown()
  after stack.close() so revocation runs after containers stop and
  failures propagate (not suppressed)

- smolmachines/launch.py: extract _teardown_smolmachines() helper that
  catches stack.close() errors (warn + re-raise) then calls revocation;
  same fatal-on-failure contract as docker backend

- test_manifest_git.py: 9 new cases for provisioned_key parsing
- test_deploy_key_provisioner.py (new): factory smoke tests
- test_contrib_gitea_deploy_key.py (new): create/delete/error/split tests

Closes #169
2026-06-03 11:58:36 -04:00
didericis-claude 464012d97c docs(prd): address review feedback on PRD 0048
- Rename deploy_key → provisioned_key throughout (manifest key,
  dataclass names, internal field names, test descriptions)
- Revocation failure at teardown now halts cleanup and propagates
  loudly; a stranded key is a security concern that must surface
2026-06-03 11:58:36 -04:00
didericis-claude b5f8a27c47 docs(prd): add SSH deploy-key provisioning plan (PRD 0048)
Introduces the design for short-lived deploy keys provisioned at spin-up
and revoked at teardown, plus the contrib package structure for
platform-specific provisioner implementations. First contrib provider
targets the Gitea deploy-key API.

Closes #169
2026-06-03 11:58:36 -04:00
didericis-claude f0ca4e3527 refactor: extract dashboard state/model layer into dashboard_model.py
test / unit (pull_request) Successful in 41s
test / integration (pull_request) Successful in 47s
test / unit (push) Successful in 35s
test / integration (push) Successful in 44s
Splits the 2103-line dashboard.py into two modules. Pure data
structures (QueuedProposal), discovery helpers (discover_pending,
discover_active_agents), derived-value helpers (_is_recent,
_approval_status, _format_agent_row, _detail_lines, etc.), and
argv-builder helpers (_build_split_pane_argv, _build_respawn_pane_argv,
_build_resume_argv_with_fallback, _agent_runtime_args) all move to
dashboard_model.py. The curses TUI, $EDITOR integration, tmux
subprocess flows, and action handlers (approve, reject,
operator_edit_routes, operator_edit_allowlist) remain in dashboard.py,
which re-imports everything from dashboard_model so existing callers and
tests are unaffected.

Adds tests/unit/test_dashboard_model.py covering _approval_status,
_proposed_payload_label, and _suffix_for_tool — three helpers that had
no prior coverage. All 894 unit tests pass.

Closes #158
2026-06-03 15:52:27 +00:00
didericis-claude ca6d257f30 test(git-gate): add shell-escaping regression tests (issue #159)
test / unit (pull_request) Successful in 36s
test / integration (pull_request) Successful in 44s
test / unit (push) Successful in 35s
test / integration (push) Successful in 42s
Cover all six pathological character classes (single-quote,
double-quote, space, semicolon, newline, backtick) in both
upstream URL and name positions.  Each case validates rendered
output via `sh -n` and asserts the original value is preserved
verbatim after shlex.quote encoding.  Also add `sh -n` smoke
tests for the static pre-receive and access-hook scripts.
2026-06-03 14:51:23 +00:00
didericis-claude cc0c952d0b fix(security): harden git_gate.py shell rendering with shlex.quote and name validation
test / unit (pull_request) Successful in 35s
test / integration (pull_request) Successful in 44s
test / unit (push) Successful in 32s
test / integration (push) Successful in 41s
Use shlex.quote() on name and upstream_url in git_gate_render_entrypoint()
so special characters (single quotes, spaces, semicolons) cannot break or
inject into the generated sh script.

Add _GIT_NAME_RE validation in GitEntry.from_repos_entry() to restrict
repo names to [A-Za-z0-9._-]+, making the manifest the first line of
defence and shlex.quote() the belt-and-suspenders backstop.

Closes #155
2026-06-03 04:40:21 +00:00
didericis-claude 8c9d4fbc46 refactor: address PR review feedback — de-privatize helpers and rename modules
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 43s
- Rename _manifest_util.py → manifest_util.py (module isn't private)
- Rename _as_json_object → as_json_object, _parse_git_upstream → parse_git_upstream,
  _parse_git_gate_config → parse_git_gate_config,
  _validate_unique_git_names → validate_unique_git_names,
  _validate_egress_routes → validate_egress_routes (none are private at
  module boundary — underscore prefix was a carry-over from the old
  monolithic manifest.py where everything lived in one namespace)
- Move _is_ip_literal → util.is_ip_literal (generic, belongs in the
  top-level util module)
- Update all import sites across manifest_*.py, manifest_extends.py,
  manifest_schema.py; existing callers of manifest.py are unaffected

All 867 unit tests pass.
2026-06-03 00:33:02 -04:00
didericis-claude b9ab1263c2 refactor: split manifest.py into domain-specific modules
Closes #157. Distributes the 1,026-line manifest.py across four
focused modules:

- _manifest_util.py: ManifestError + _as_json_object (shared base)
- manifest_git.py: GitEntry, GitUser, git-gate config helpers
- manifest_egress.py: EgressRoute, EgressConfig, PipelockRoutePolicy
- manifest_agent.py: AgentProvider, Agent

manifest.py is now the residual orchestration layer: Bottle, Manifest,
and re-exports of all public names so existing callers are unaffected.
All 867 unit tests pass.
2026-06-03 00:33:02 -04:00
didericis-claude 9282bceaf8 fix: emit WARNING when Docker teardown ExitStack raises (issue #156)
test / unit (pull_request) Successful in 37s
test / integration (pull_request) Successful in 40s
test / unit (push) Successful in 32s
test / integration (push) Successful in 43s
Replace the bare `except BaseException: pass` in the `teardown` closure
with a `warn()` call that includes the container name and operation type
("compose-down"), so cleanup failures are visible in the log rather than
silently discarded.  Non-blocking: the exception is consumed and teardown
continues, preserving the original error-propagation contract.

Add test_docker_launch_teardown.py to lock the new behaviour: it injects
a RuntimeError via a mocked `compose_down` callback and asserts the
WARNING message contains the container name and operation label.
2026-06-03 04:13:53 +00:00
didericis-claude 3e50079bcc docs(prd): activate git-gate manifest redesign
test / unit (pull_request) Successful in 41s
test / integration (pull_request) Successful in 1m10s
test / unit (push) Successful in 39s
test / integration (push) Successful in 54s
PRD 0047 is now shipped to main.
2026-06-02 23:59:34 -04:00
didericis-claude cf9aaf68e7 chore: update demo manifest and example agent to git-gate (PRD 0047)
bot-bottle.demo.json: git array → git-gate.repos with url/identity/host_key
examples/agents/implementer.md: git.user → git-gate.user
2026-06-02 23:59:34 -04:00
didericis-claude 4cf2cfc55d test: update test suite for git-gate manifest redesign (PRD 0047)
- fixtures.py: fixture_with_git_dict uses git-gate.repos + url/identity/host_key
- test_manifest_git: rewrite to use git-gate.repos; replace duplicate-name
  test (names = dict keys, always unique) with two-repos-different-hosts test
- test_manifest_git_user: _manifest → git-gate.user; update error message assertions
- test_manifest_agent_git_user: git → git-gate throughout; repos rejection test
- test_manifest_extends: git.remotes/git.user → git-gate.repos/git-gate.user
- test_provision_git: IP test updated — no host alias, single insteadOf
- test_compose: git.remotes → git-gate.repos + new field names
- test_docker_provision_git_user: git.user → git-gate.user
- test_git_gate: inline manifest dict updated to git-gate.repos
- test_smolmachines_provision: git_json → git_gate_json; remove _remote_host
2026-06-02 23:59:34 -04:00
didericis-claude 7c285fde7a feat(manifest): replace git key with git-gate (PRD 0047)
- BOTTLE_KEYS and AGENT_KEYS_OPTIONAL: "git" → "git-gate"
- GitEntry: remove from_dict/from_remote_dict; add from_repos_entry
  parsing url/identity/host_key with repo name as the dict key
- GitUser.from_dict: error messages updated to git-gate.user
- _parse_git_config → _parse_git_gate_config; repos/user subkeys
- Bottle.from_dict: reads git-gate key; "git" key raises a migration error
- Agent.from_dict: reads git-gate key; repos rejected at agent level
- manifest_extends: _child_declares_git_remotes → _child_declares_git_gate_repos
- manifest_loader: threads git-gate frontmatter key into agent_dict
2026-06-02 23:59:34 -04:00
didericis-claude 64ac204c05 docs(prd): consolidate git.user into git-gate per review
Move git.user under git-gate and remove git as a top-level key
entirely, so all git configuration lives under a single section.
2026-06-02 23:59:34 -04:00
didericis-claude 59fd132b9d docs(prd): add git-gate manifest redesign plan
PRD 0047 proposes replacing git.remotes with a top-level git-gate.repos
section and snake_case field names to make clear the config is
specifically for git-gate routing, not generic git or SSH config.

Closes #160
2026-06-02 23:59:34 -04:00
didericis f427d35e72 fix(git-http): log access-hook denial detail to stdout
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 39s
test / unit (push) Successful in 43s
test / integration (push) Successful in 59s
Previously when the access-hook returned non-zero, git-http would pipe
the hook's stderr into the 403 body sent back to the agent's git
client but never log it locally, so docker logs just showed
`"GET ... 403 -"` with no explanation. Operators had to shell into
the sidecar and re-run the hook by hand to find out why a clone was
being refused (e.g. upstream SSH unreachable, missing credentials).

Route the hook's stderr/stdout through the existing log_message
channel before sending the 403, one log line per output line so the
default request-log format stays readable. When the hook exits
non-zero with no output, log the exit code so the line is still
informative.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 23:29:39 -04:00
didericis-codex 1105d9a269 chore(skills): add quality evaluation skill
test / unit (push) Successful in 48s
test / integration (push) Successful in 56s
2026-06-02 18:42:48 +00:00
didericis-codex 46e596d0b1 docs(prd): renumber host override removal to 0046
test / unit (push) Successful in 46s
test / integration (push) Successful in 56s
2026-06-02 18:32:55 +00:00
didericis-codex a3a8a01b09 docs(prd): activate git remote host override removal
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 44s
test / unit (push) Successful in 36s
test / integration (push) Successful in 52s
2026-06-02 18:17:29 +00:00
didericis-codex 941f316462 feat(git-gate): remove git remote host override plumbing 2026-06-02 18:17:24 +00:00
didericis-codex be3defe5d8 docs(prd): add git remote host override removal plan 2026-06-02 18:16:24 +00:00
didericis 3885e2f5ad fix(workspace): include hidden cwd files in docker layer
test / unit (pull_request) Successful in 32s
test / integration (pull_request) Successful in 42s
test / unit (push) Successful in 33s
test / integration (push) Successful in 40s
2026-06-02 13:12:40 -04:00
didericis-codex a08829573d docs(prd): activate workspace porting plan
test / unit (pull_request) Successful in 33s
test / integration (pull_request) Successful in 40s
2026-06-02 17:01:53 +00:00
didericis-codex d5fcbe53ef feat(workspace): port cwd across backends 2026-06-02 17:01:19 +00:00
didericis-codex 6150497b47 feat(workspace): trust resolved project path 2026-06-02 16:57:52 +00:00
didericis-codex 5308d53288 feat(workspace): add shared workspace plan 2026-06-02 16:56:57 +00:00
didericis-codex d01f4b6613 docs(prd): add workspace porting plan 2026-06-02 16:54:50 +00:00
didericis 44273be9eb fix(dashboard): stop agents in dashboard from moving during selection
test / unit (push) Successful in 38s
test / integration (push) Successful in 58s
2026-06-02 12:47:00 -04:00
didericis 096c7b8196 fix(codex): update cli image version
test / unit (push) Successful in 37s
test / integration (push) Successful in 57s
2026-06-02 12:42:09 -04:00
didericis 0432a5d3ff fix(codex): keep dummy auth refresh timestamp valid
test / unit (push) Successful in 49s
test / integration (push) Successful in 1m0s
2026-06-02 12:40:14 -04:00
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