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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ If you want to understand the product before installing anything, start with
the checked-in demo calibration package:

- [examples/demo-calibration/README.md](examples/demo-calibration/README.md)
- [examples/board-demo/README.md](examples/board-demo/README.md)
- [examples/demo-calibration/reviewer-value-report.md](examples/demo-calibration/reviewer-value-report.md)
- [docs/first-user-demo-transcript.md](docs/first-user-demo-transcript.md)

Expand Down
3 changes: 2 additions & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ on one repository and keep all reviewer lanes manual until the output is useful
on your codebase.

To see the value loop before you touch a product repository, open the
[Demo Calibration Example](../examples/demo-calibration/README.md) and the
[Demo Calibration Example](../examples/demo-calibration/README.md), the
[Board Demo Rehearsal](../examples/board-demo/README.md), and the
[First-User Demo Transcript](first-user-demo-transcript.md).

## 1. Install
Expand Down
59 changes: 59 additions & 0 deletions examples/board-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Board Demo Rehearsal

This public demo shows the native Code Mower Board shape without connecting to a
private repository or running provider CLIs. It is synthetic, small, and safe to
use in an adoption walkthrough.

## Files

- `board-events.jsonl` contains two synthetic `code_mower.boardEvent.v1`
snapshots.
- `reviewer-spend.json` contains matching synthetic
`code_mower.reviewerSpend.v1` rows.

Both files are metadata-only fixtures for demonstrating Board state.

## Try The Board Shape Locally

From the repository root:

```bash
code-mower board events --store-path examples/board-demo/board-events.jsonl
```

To open the local browser Board with the same sample history and spend data:

```bash
code-mower board serve \
--repo example/widget-service \
--store-path examples/board-demo/board-events.jsonl \
--spend-path examples/board-demo/reviewer-spend.json
```

The live GitHub panel may say the demo repository is unavailable. That is fine:
the Recent Local History, Reviewer Verdict Timeline, and Spend And Latency
panels still show the sample local artifacts.

To inspect the Board diagnostic shape without exposing local paths:

```bash
code-mower board doctor \
--repo example/widget-service \
--store-path examples/board-demo/board-events.jsonl \
--spend-path examples/board-demo/reviewer-spend.json \
--json
```

## What To Look For

- one ready PR lane with a passing Claude audit;
- one blocked PR lane with a failing Codex audit;
- a gate alert and owner queue item for the blocked lane;
- reviewer spend and latency totals by lane; and
- redacted local path posture in command output.

## Privacy Boundary

This example contains no source code, raw diffs, transcripts, issue body text,
raw stdout/stderr, auth output, browser history, local paths, local secret
values, or secrets.
2 changes: 2 additions & 0 deletions examples/board-demo/board-events.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"schema":"code_mower.boardEvent.v1","type":"status_snapshot","created_at":"2026-09-01T12:00:00Z","repo":"example/widget-service","snapshot_schema":"code_mower.laneStatus.v1","board_schema":"code_mower.board.v1","summary":{"next_action":"waiting for checks","remote_available":true,"open_prs":2,"gate_alerts":0,"local_boards":0,"local_processes":0},"snapshot":{"schema":"code_mower.laneStatus.v1","repo":"example/widget-service","generated_at":"2026-09-01T12:00:00Z","remote":{"available":true,"errors":[],"pull_requests":[{"number":12,"title":"Add export retry","url":"https://github.com/example/widget-service/pull/12","branch":"codex/export-retry","head_sha":"1111111111111111111111111111111111111111","author":"codex-bot","is_draft":false,"merge_state":"CLEAN","updated_at":"2026-09-01T11:58:00Z","labels":{"builder":["builder:codex"],"dispatched":[],"needs":["needs-claude-audit"],"done":[],"blocked":[]},"checks":[{"name":"package","state":"success"},{"name":"code-mower/gate","state":"pending"}],"stale":false,"next_action":"waiting for checks","gate_rerun_command":""},{"number":13,"title":"Tighten import validation","url":"https://github.com/example/widget-service/pull/13","branch":"cursor/import-validation","head_sha":"2222222222222222222222222222222222222222","author":"cursor-bot","is_draft":false,"merge_state":"CLEAN","updated_at":"2026-09-01T11:57:00Z","labels":{"builder":["builder:cursor"],"dispatched":[],"needs":["needs-codex-audit"],"done":["claude-audit-done"],"blocked":[]},"checks":[{"name":"package","state":"success"},{"name":"code-mower/gate","state":"pending"}],"stale":false,"next_action":"waiting for checks","gate_rerun_command":""}],"workflow_runs":[{"id":9001,"workflow":"Code Mower gate","title":"Code Mower gate","status":"completed","conclusion":"success","event":"pull_request_target","branch":"codex/export-retry","created_at":"2026-09-01T11:58:30Z","updated_at":"2026-09-01T11:58:45Z","url":"https://github.com/example/widget-service/actions/runs/9001"}],"gate_health":{"status":"pass","alerts":[]}},"agenttrail":{"available":false,"boards":[],"message":"local listener inventory unavailable"},"local_processes":{"available":false,"processes":[],"message":"local process inventory unavailable"},"board":{"schema":"code_mower.board.v1","mode":"local_read_only","refresh_seconds":15,"local_paths":"redacted","recording":{"enabled":false,"interval_seconds":60}}}}
{"schema":"code_mower.boardEvent.v1","type":"status_snapshot","created_at":"2026-09-01T12:05:00Z","repo":"example/widget-service","snapshot_schema":"code_mower.laneStatus.v1","board_schema":"code_mower.board.v1","summary":{"next_action":"fix BLOCKED audit","remote_available":true,"open_prs":2,"gate_alerts":1,"local_boards":0,"local_processes":0},"snapshot":{"schema":"code_mower.laneStatus.v1","repo":"example/widget-service","generated_at":"2026-09-01T12:05:00Z","remote":{"available":true,"errors":[],"pull_requests":[{"number":12,"title":"Add export retry","url":"https://github.com/example/widget-service/pull/12","branch":"codex/export-retry","head_sha":"1111111111111111111111111111111111111111","author":"codex-bot","is_draft":false,"merge_state":"CLEAN","updated_at":"2026-09-01T12:04:00Z","labels":{"builder":["builder:codex"],"dispatched":[],"needs":[],"done":["claude-audit-done"],"blocked":[]},"checks":[{"name":"package","state":"success"},{"name":"code-mower/gate","state":"success"}],"stale":false,"next_action":"ready for merge or auto-merge","gate_rerun_command":""},{"number":13,"title":"Tighten import validation","url":"https://github.com/example/widget-service/pull/13","branch":"cursor/import-validation","head_sha":"2222222222222222222222222222222222222222","author":"cursor-bot","is_draft":false,"merge_state":"CLEAN","updated_at":"2026-09-01T12:03:00Z","labels":{"builder":["builder:cursor"],"dispatched":[],"needs":[],"done":["claude-audit-done"],"blocked":["codex-audit-blocked"]},"checks":[{"name":"package","state":"success"},{"name":"code-mower/gate","state":"failure"}],"stale":false,"next_action":"fix BLOCKED audit","gate_rerun_command":""}],"workflow_runs":[{"id":9002,"workflow":"Code Mower gate","title":"Code Mower gate","status":"completed","conclusion":"failure","event":"pull_request_target","branch":"cursor/import-validation","created_at":"2026-09-01T12:03:30Z","updated_at":"2026-09-01T12:03:45Z","url":"https://github.com/example/widget-service/actions/runs/9002"}],"gate_health":{"status":"warn","alerts":[{"pr_number":13,"kind":"blocked-audit","message":"PR #13 has a blocked audit label"}]}},"agenttrail":{"available":false,"boards":[],"message":"local listener inventory unavailable"},"local_processes":{"available":false,"processes":[],"message":"local process inventory unavailable"},"board":{"schema":"code_mower.board.v1","mode":"local_read_only","refresh_seconds":15,"local_paths":"redacted","recording":{"enabled":false,"interval_seconds":60}}}}
29 changes: 29 additions & 0 deletions examples/board-demo/reviewer-spend.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"schema": "code_mower.reviewerSpend.v1",
"runs": [
{
"created_at": "2026-09-01T12:04:10Z",
"lane": "claude-audit",
"repo": "example/widget-service",
"pr_number": 12,
"head_sha": "1111111111111111111111111111111111111111",
"model": "sonnet",
"wall_seconds": 31.2,
"cost_usd": 0.18,
"total_tokens": 4200,
"verdict": "PASS"
},
{
"created_at": "2026-09-01T12:04:50Z",
"lane": "codex-audit",
"repo": "example/widget-service",
"pr_number": 13,
"head_sha": "2222222222222222222222222222222222222222",
"model": "gpt-5",
"wall_seconds": 44.8,
"cost_usd": 0.24,
"total_tokens": 5100,
"verdict": "BLOCKED"
}
]
}
39 changes: 39 additions & 0 deletions tests/test_release_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from code_mower import __version__
from code_mower import audit_labeler_lib
from code_mower import audit_progress
from code_mower import board as code_mower_board
from code_mower import board_store as code_mower_board_store
from code_mower import bootstrap as code_mower_bootstrap
from code_mower import calibration as calibration_pkg
from code_mower import cloud as code_mower_cloud
Expand Down Expand Up @@ -4591,6 +4593,43 @@ def test_public_demo_calibration_artifacts_are_parseable_and_sanitized(self) ->
with self.subTest(fragment=fragment):
self.assertNotIn(fragment, public_text)

def test_public_board_demo_artifacts_are_parseable_and_sanitized(self) -> None:
demo_dir = ROOT / "examples/board-demo"
readme = (ROOT / "README.md").read_text(encoding="utf-8")
quickstart = (ROOT / "docs/quickstart.md").read_text(encoding="utf-8")
required = {"README.md", "board-events.jsonl", "reviewer-spend.json"}
self.assertTrue(demo_dir.is_dir())
self.assertTrue(required.issubset({path.name for path in demo_dir.iterdir()}))
self.assertIn("examples/board-demo/README.md", readme)
self.assertIn("../examples/board-demo/README.md", quickstart)

report = code_mower_board_store.event_report(
path=demo_dir / "board-events.jsonl",
limit=10,
)
timelines = code_mower_board.timelines_payload(
code_mower_board.BoardConfig(
repo="example/widget-service",
store_path=str(demo_dir / "board-events.jsonl"),
spend_path=str(demo_dir / "reviewer-spend.json"),
)
)
spend = json.loads((demo_dir / "reviewer-spend.json").read_text(encoding="utf-8"))
public_text = "\n".join(path.read_text(encoding="utf-8") for path in sorted(demo_dir.iterdir()))

self.assertEqual(report["schema"], code_mower_board_store.BOARD_EVENT_STORE_SCHEMA)
self.assertEqual(report["event_count"], 2)
self.assertEqual(report["malformed"], 0)
self.assertEqual(spend["schema"], "code_mower.reviewerSpend.v1")
self.assertIn(
("codex-audit", "BLOCKED"),
{(entry["lane"], entry["verdict"]) for entry in timelines["verdicts"]["entries"]},
)
self.assertEqual(timelines["spend"]["groups"][0]["lane"], "claude-audit")
self.assertIn("metadata-only", public_text)
self.assertNotIn("/" + "Users/", public_text)
self.assertNotIn("/private" + "/tmp", public_text)

def test_public_demo_calibration_generates_expected_value_report(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
output = Path(tmp) / "reviewer-value-report.md"
Expand Down
Loading