Skip to content
Merged
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ flowchart LR

See [Architecture Diagrams](docs/architecture.md), [Design](DESIGN.md), [Hosted OpenCoven](HOSTED.md), [Familiar Contract](FAMILIAR-CONTRACT.md), [Roadmap](ROADMAP.md), and [Hosted vs self-hosted](docs/hosted-vs-self-hosted.md) for the operational plan.

**Want to see the whole loop run?** `examples/demo/run-demo.sh` drives the
real adapter through the full operating loop — issue assignment, Check Run,
edited-in-place status comment, maintainer steering commands, permission
gate, familiar-voice draft PR — offline and self-verifying in about a minute.
[docs/demo.md](docs/demo.md) narrates it.
Comment on lines +39 to +43

---

## Architecture
Expand Down Expand Up @@ -146,6 +152,7 @@ duplicate comments.
| Label trigger | Implemented | Routes configured `trigger_labels` such as `coven:fix`. |
| Maintainer command protocol | Implemented | Typed `@familiar <verb>` grammar; casual mentions ignored; write-access gate; self-comments never re-trigger. |
| Marker-backed status comments | Implemented | One edited-in-place status surface per issue/PR; no duplicate bot comments. |
| Reference demo of the operating loop | Implemented | Offline, self-verifying replay of the full loop with a real adapter binary — see [docs/demo.md](docs/demo.md). |
| PR lifecycle review trigger | Implemented | Policy-gated auto-review on opened/synchronize/reopened/ready_for_review plus label opt-in; familiar-authored PRs are never auto-reviewed. |
| Push / commit review trigger | Partial | Events parsed and typed with fixtures; execution lane needs headless contract v3. |
| GitHub App installation tokens | Implemented | Mints installation access tokens from the App private key. |
Expand Down
116 changes: 116 additions & 0 deletions docs/demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Reference demo: the operating loop, end to end

This is the ClawSweeper-style reference demo of the coven-github operating
loop (issue #19): the **real adapter binary**, driven by signed webhook
deliveries, publishing to an in-memory GitHub API stub through a
contract-conformant fake `coven-code` runtime. No network, no GitHub App
registration, no credentials — one command, about a minute, self-verifying.
Comment on lines +3 to +7

```bash
examples/demo/run-demo.sh
```

A green exit code *is* the demo's claim: every property below is asserted
programmatically against the stub's recorded state, not eyeballed. Set
`KEEP=1` to keep the scratch directory (config, server log, stub log) for
inspection.

## Why "ClawSweeper-style"

ClawSweeper is the reference pattern for conservative GitHub automation in the
OpenClaw ecosystem (see [`DESIGN.md`](../DESIGN.md)): narrow promises, durable
state, marker-backed comments edited in place, explicit maintainer commands,
and deterministic gates before mutation. This demo shows coven-github honoring
each of those disciplines with its own machinery.

## What runs

| Piece | Real or stand-in | Role |
|---|---|---|
| `coven-github serve` | **Real** — the same binary you self-host | Webhook receiver, command router, worker pool, publication |
| Webhook deliveries | Real HMAC-SHA256 signatures, GitHub payload shapes | Drive every act |
| GitHub API | [`github-stub.py`](../examples/demo/github-stub.py) — in-memory stand-in | Records every mutation in an audit trail |
| `coven-code --headless` | [`fake-coven-code`](../examples/demo/fake-coven-code) — headless contract v2 conformant | Fabricates a familiar-voice fix instead of running a model |
| App credentials | Throwaway RSA key + random webhook secret, generated per run | Prove the real JWT → installation-token path |

The stub answers exactly the API surface the adapter uses — scoped token
minting, repo metadata, branch resolution, collaborator permission, Check
Runs, issue comments, pull requests — and exposes its world state at
`/_demo/state` and the mutation log at `/_demo/audit`.

## The acts

**Act 1 — issue assigned: the full loop.** `octocat` assigns issue #42 to
`@coven-cody`. The adapter mints an **orchestration** token, resolves the
default branch and head SHA from live state, creates a Check Run against that
immutable SHA, posts the one marker-backed status comment, flips the check to
`in_progress`, mints a separate **agent-git** token injected only via
`COVEN_GIT_TOKEN` (the fake runtime verifies the brief itself is tokenless),
runs the session, then mints a **publication** token and opens a draft PR back
to the issue — in Cody's voice. Asserted: one comment, edited in place to
`Status: done`; Check Run concluded `success`; PR is a draft; three distinct
token scopes minted.

**Act 2 — casual mention.** "thanks @coven-cody, great work on this!" triggers
*nothing*. Asserted: the audit trail did not grow by a single API call.

**Act 3 — self-trigger loop guard.** The familiar's own comment containing
`@coven-cody status` is ignored. Asserted: audit unchanged.

**Act 4 — unknown verb.** `@coven-cody explain` is a command-position mention
with an unknown verb, so the familiar replies with the real command list
instead of guessing. Asserted: the clarification *edited the same status
comment* — still exactly one surface.

**Act 5 — steering: `status`.** Answered from the durable task store (the same
state Cave polls), listing the issue's tasks and their lifecycle states.
Comment on lines +65 to +66

**Act 6 — permission gate.** `mallory` (read-only) comments
`@coven-cody retry`. The worker checks collaborator permission *pre-flight*
and declines on the status surface. Asserted: `Status: declined` and **no new
Check Run** — no session was spent below the write-access bar.

**Act 7 — steering: `retry`.** `octocat` (admin) retries. A second full run
executes: second Check Run to `success`, second draft PR. Asserted: **still
exactly one status comment** — repeated runs never stack duplicates.

## The closing surfaces

After the acts, the demo prints two oversight views:

- **The audit trail** — every GitHub mutation in order, attributed to the
token role that performed it. You can watch the authority split from
issue #4 in action: `orchestration` drives checks and comments,
`publication` opens the PR, `agent-git` never touches the API at all.
- **The Cave view** — `GET /api/github/tasks`, the adapter's task API that
the CovenCave dashboard (issue #18) polls: task ids, branches, PR links,
Check Run links, session ids, lifecycle status.

Cave *intervention* — pausing or steering a live session from the dashboard —
lands with #18; today the oversight loop closes through the maintainer
commands demonstrated above plus the session deep links on every Check Run
and status comment.

## Sample audit trail

```text
1 app-jwt mint orchestration token (repo-scoped: demo-service)
2 orchestration read repo metadata OpenCoven/demo-service
3 orchestration resolve branch 'main' head SHA
4 orchestration create check run 1001 'Cody — Fix issue #42: …' (queued)
5 orchestration list #42 comments (0 found)
6 orchestration post comment 5001 on #42
7 orchestration check run 1001 -> in_progress ('Running')
8 app-jwt mint agent-git token (repo-scoped: demo-service)
9 app-jwt mint publication token (repo-scoped: demo-service)
10 publication open draft PR #101 (cody/fix-issue-42 -> main)
11 orchestration list #42 comments (1 found)
12 orchestration edit comment 5001 in place (edit #1: 'Status: done')
13 orchestration check run 1001 -> success ('Done')
```

## Requirements

`cargo`, `python3` (stdlib only), `openssl`, `curl`. The script builds the
adapter with `cargo build -p coven-github`, picks free ports, and cleans up
after itself (scratch dir is kept on failure, or with `KEEP=1`).
Comment on lines +114 to +116
Binary file not shown.
Binary file not shown.
137 changes: 137 additions & 0 deletions examples/demo/fake-coven-code
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Contract-conformant stand-in for `coven-code --headless` (demo, issue #19).

Speaks headless contract v2 (`docs/headless-contract.md`): reads the tokenless
session brief from `--context`, writes the structured result envelope to
`--output`, and exits 0. Instead of running a real agent session, it fabricates
a small, plausible fix in the familiar's voice so the adapter's publication
path (Check Run, status comment, draft PR) can be demonstrated offline.

It also proves two security invariants of the real integration on every run:

* the brief is tokenless — no credential material appears in the JSON;
* git authority arrives only via the COVEN_GIT_TOKEN environment variable.
"""

import json
import os
import sys


def parse_args(argv):
args = {"headless": False, "context": None, "output": None}
it = iter(argv)
for arg in it:
if arg == "--headless":
args["headless"] = True
elif arg == "--context":
args["context"] = next(it, None)
elif arg == "--output":
args["output"] = next(it, None)
if not (args["headless"] and args["context"] and args["output"]):
sys.exit("usage: fake-coven-code --headless --context BRIEF --output RESULT")
return args


def no_review():
return {
"mode": "none",
"evidence_status": "not_applicable",
"reviewed_files": [],
"supporting_files": [],
"findings": [],
"tests_run": [
{
"command": "cargo test -p auth refresh",
"status": "passed",
"output_summary": "9/9 passing",
}
],
Comment on lines +43 to +49
"no_findings_reason": None,
"limitations": [],
}


def main():
args = parse_args(sys.argv[1:])
with open(args["context"], encoding="utf-8") as f:
raw_brief = f.read()
brief = json.loads(raw_brief)

if brief.get("contract_version") != "2":
sys.exit(f"unsupported brief contract_version: {brief.get('contract_version')}")

# Invariant checks (issue #4): tokenless brief, env-injected git authority.
git_token = os.environ.get("COVEN_GIT_TOKEN", "")
if not git_token:
sys.exit("COVEN_GIT_TOKEN missing — the worker must inject git authority")
if git_token in raw_brief:
sys.exit("SECURITY: git token leaked into the session brief")
print(
"fake-coven-code: brief is tokenless; git authority arrived via env only",
file=sys.stderr,
)

familiar = brief["familiar"]["display_name"]
task = brief["task"]
kind = task["kind"]

if kind == "fix_issue":
number = task["issue_number"]
branch = f"cody/fix-issue-{number}"
summary = (
f"Fixed the OAuth token refresh by adding a 60-second "
f"clock-skew buffer (issue #{number})."
)
pr_body = (
f"## Hey, I'm {familiar} \U0001f43e\n\n"
f"Issue #{number} traced to the OAuth refresh path not accounting "
f"for clock skew between the client and the auth server. I added a "
f"60-second buffer to the expiry check and regression tests for "
f"both skew directions.\n\n"
f"**Changed:** `src/auth/refresh.rs` (+14 / -3)\n"
f"**Tests:** 9/9 passing (2 new regression cases)\n\n"
f"I kept the diff scoped to the refresh path; the token store "
f"itself is untouched. Shout if you want the buffer configurable."
)
commits = [
{
"sha": "f1e2d3c4b5a6",
"message": f"fix: add clock-skew buffer to OAuth refresh (#{number})",
}
]
files = ["src/auth/refresh.rs", "src/auth/refresh_test.rs"]
elif kind == "address_review_comment":
number = task["pr_number"]
branch = f"cody/pr-{number}-feedback"
summary = f"Addressed review feedback on PR #{number}."
pr_body = (
f"## {familiar} here \U0001f43e\n\n"
f"Follow-up for the review feedback on #{number} — renamed the "
f"helper and added the missing error-path test."
)
commits = [
{"sha": "0a1b2c3d4e5f", "message": "refactor: address review feedback"}
]
files = ["src/auth/refresh.rs"]
else:
sys.exit(f"demo runtime does not handle task kind: {kind}")

result = {
"contract_version": "2",
"status": "success",
"branch": branch,
"commits": commits,
"files_changed": files,
"summary": summary,
"pr_body": pr_body,
"review": no_review(),
"exit_reason": None,
}
with open(args["output"], "w", encoding="utf-8") as f:
json.dump(result, f, indent=2)
print(f"fake-coven-code: wrote result for {kind} -> {branch}", file=sys.stderr)


if __name__ == "__main__":
main()
Loading
Loading