Commit Graph
2 Commits
Author SHA1 Message Date
dongarandClaude Opus 5 d508e9a9b7 Externalise plans to Gitea and accept approvals there (A4, Part H)
CI / checks (push) Successful in 43s
CI / checks (pull_request) Successful in 41s
- approvals.py: PlanBoard opens one issue per project and posts each
  plan revision as a comment carrying its rev tag. The state store
  keeps only a pointer, so gate state can be re-derived from Gitea
  after a state-volume loss — external truth wins
- Gitea comments are a second approval source: revision binding, the
  typed word approve, and first-approval-wins work exactly as over
  transport, with a per-channel allowlist because a Gitea login is
  not a Matrix MXID
- Outcomes are echoed to the other channel, so the two records cannot
  silently disagree about what was approved
- A Gitea outage degrades durability of the record, not the ability
  to approve: planning proceeds and Matrix remains a complete channel
- Live test now asserts the server refuses the agent's own merge and
  its push to the protected branch. That property was previously
  true only because our code did not attempt it; branch protection
  makes it structural, and this test fails if the protection is ever
  relaxed
- Fixed a race in the process-group test: killing a group is
  asynchronous and the grandchild is an orphan, so its exit is not
  synchronised with our return; the test now waits for death

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 22:15:08 +00:00
dongarandClaude Opus 5 2071e6ab6d Add Gitea client and git operations (B2) — step 6a, part 1
CI / checks (push) Successful in 16s
CI / checks (pull_request) Successful in 14s
The standalone half of the Gitea layer; orchestrator wiring follows
once the executor loop lands, to avoid colliding with it in
control.py.

- gitea.py: GiteaClient over httpx — open PRs, list and post issue
  comments, close PRs. find_pull_request is the A3 external-truth
  query that makes opening a PR idempotent across a crash, and it
  checks closed PRs too, since a crash after opening must not be
  answered with "nothing is open, so open another"
- pull_request_reconciler: an unreachable Gitea returns None rather
  than a guess, so the agent gates to the human instead of risking a
  duplicate side effect
- git.py: branch, commit, push for the workspace. The token is passed
  per-invocation rather than stored as a remote, so it never lands in
  .git/config, and anything raised is scrubbed of it
- commit_all returns None when there is nothing to commit: an empty
  milestone is a fact to report, not an error
- Live test does the real round trip against the sandbox repo and
  cleans up after itself

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 20:27:34 +00:00