Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
87213bd
feat: add evidence contract, proof config and pilot groundwork
Aug 31, 2026
b551961
feat: replay the journey proof in the verify vm and store its evidence
Aug 31, 2026
5fcacec
feat: serve and stream task recap and evidence
Aug 31, 2026
a608103
feat: make the pilot grid the default workspace shell
Aug 31, 2026
213b261
fix: seed claude credentials into microvm turns and reviews
Aug 31, 2026
456e882
fix: hydrate checks, add card actions and purge em dashes from labels
Aug 31, 2026
dab35b2
test: align label assertions and make the event registry suite hermetic
Aug 31, 2026
adaaa63
fix: hydrate task events for cards and the expanded view
Aug 31, 2026
afc2f0f
feat: emit the recap at turn end and surface the vm verification
Sep 1, 2026
6903b9b
feat: compute the recap on read for finished tasks
Sep 1, 2026
7de910b
feat: background runbook services, runbook validate and task activity…
Sep 2, 2026
2867ba2
feat: show the running phase on cards and in the evidence block
Sep 2, 2026
ade725a
feat: narrate the running phase of a turn through the task activity
Sep 2, 2026
eb7cd85
fix: scope evidence file urls to their project
Sep 2, 2026
3bbbd2c
feat: classify diff paths by ui surface
Sep 2, 2026
46b9695
docs: settle D17 visual proof and changelog 0.21.0
Sep 2, 2026
bedd484
feat: proof intent and proof review contract
Sep 2, 2026
dbdbc28
feat: show the proof intent and the reviewer verdict on the card
Sep 2, 2026
643351c
feat: proof declaration protocol in the agent reply
Sep 2, 2026
1f47116
feat: proportionate proof capture driven by the turn declaration
Sep 2, 2026
60c1289
feat: let the reviewer judge the visual proof and merge its verdict
Sep 2, 2026
b9a9c98
fix: log a missing proof verdict and harden the proof chapter
Sep 2, 2026
cf4472c
chore: drop an em dash from a review comment
Sep 2, 2026
8e38739
feat: fixed lanes on the pilot grid, closable and one expanded
Sep 2, 2026
3d58989
feat: chat-shaped task thread with the four blocks anchored in it
Sep 2, 2026
f338c44
feat: full-screen evidence viewer with zoom and pan
Sep 3, 2026
a1ecda6
docs: design note for the pilot agent (D18, open)
Sep 3, 2026
6b13966
refactor: one rem type scale for every font size in the web UI
Sep 3, 2026
6c06b31
fix: scrollable task card and framed lens panel
Sep 3, 2026
c8e22a8
feat: single column of blocks on the task card
Sep 3, 2026
adff196
fix: open the task thread at its tail
Sep 3, 2026
75b3ccb
fix: pin the autoconfig tests off the TTY and bound the serial pre-pu…
Sep 6, 2026
2edd09b
fix: bump transitive fast-uri to 3.1.7 to clear high audit advisories
Sep 7, 2026
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@
All notable changes to `codesema` (the npm package in `packages/cli`) are documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org).

## [0.21.0] - unreleased

### Added

- **The pilot grid is now the default workspace shell**, with the previous interface reachable behind a toggle and a mobile layout below 760px.
- **Decision D17: a turn's visual proof of its own effect is now part of the review.** A `microvm` task can declare and capture a screenshot or a Playwright journey each turn (`proof` in `.codesema/config.json`, a `PROOF: <none|screenshot|journey>` line in the agent's reply); the reviewer checks the declaration against the diff and raises a finding only on an unproven visible change or an unexplained failed proof.
- **`codesema runbook validate`** validates a hand-edited `.codesema/runbook.json` as-is, without asking an agent for a new proposal.
- **The runbook can declare background services**, started in the verification VM ahead of its healthchecks and tests.
- **A task now carries its own activity**: the phase a turn is currently in is broadcast live and shown on its card and in the evidence block.
- **A turn now emits its recap the moment it ends, and a finished task without one gets it computed on read**, with the microvm verification surfaced alongside it.

### Changed

- **A task card stacks its four blocks in one column** (evidence, recap, checks, criteria) instead of a two-by-two grid.
- **Every font size in the web UI is now one of seven `--fs-*` tokens declared once in `style.css`**, in rem, replacing 500 hard-coded px values; the dense sizes moved up (nothing below 11px, read text at 14px), and the tests refuse any new px font size in a component.
- **Every screenshot and video in the evidence block opens a full-screen viewer** with wheel, button and keyboard zoom, click-to-zoom and drag-to-pan, closed by Escape.
- **A widened lane, the full view and the mobile pane now share one chat-shaped thread** (`PilotThread`): the journal reads as a conversation, with the criteria, checks, evidence and recap blocks anchored where the run produced them instead of stacked at the end.
- **Task events hydrate both the card and the expanded view**, instead of only one of the two.
- **Checks hydrate the same way, and task cards gained their own actions.**

### Fixed

- **A task thread now opens scrolled to its latest message**, and keeps following new ones until the reader scrolls up.
- **A task card that is not the expanded lane now scrolls** between its header and its footer instead of clipping its blocks.
- **The lens now frames the zoomed block in an opaque panel** with a bounded width and its own scroll, instead of floating it bare over the veil.
- **Claude credentials are now seeded into microvm turns and reviews**, closing the gap where either ran without them.
- **Evidence file URLs are now scoped to their project**, so one project can no longer read another's captures.

## [0.20.0] - 2026-08-28

### Added
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ From the page you can also:

**Merging.** The workspace does not merge on its own unless you ask it to: `mergePolicy` defaults to `human`. Task state lives under `.codesema/tasks/<id>/` in the repository it belongs to.

## Visual proof

With `isolation: "microvm"` and a validated runbook whose services include one that serves the app under test, a task's turn can prove its own effect: the verification VM replays a Playwright journey against the running app and stores the resulting screenshot or video as evidence. The runbook's image needs Playwright available, for example `mcr.microsoft.com/playwright:v1.62.0-noble`.

Turn it on by adding a `proof` block to `.codesema/config.json`:

```json
{
"proof": {
"url": "http://localhost:3000",
"journey": "e2e/checkout.spec.ts"
}
}
```

`url` is required; `journey` is optional and names the default Playwright spec a turn replays when it declares `journey` proof without naming its own.

A hand-edited `.codesema/runbook.json` can be checked without asking an agent for a new proposal: `codesema runbook validate` replays install, services, healthchecks and tests as-is, and only persists the validation once all of them pass.

Each turn declares its own proof in its final message, one line of the form `PROOF: <none|screenshot|journey> [pages or spec] | <reason>`:

```
PROOF: screenshot /dashboard /settings | the new toggle changed the settings layout
PROOF: journey e2e/checkout.spec.ts | the fix touches three screens in sequence
PROOF: none | only the API handler changed, nothing rendered differently
```

The task's evidence panel shows the capture (screenshot or video), the agent's stated intent for it, and the reviewer's verdict on whether the declaration matched the diff. Only the last 5 captures of a task are kept; older ones are purged as new ones arrive.

Known limitation: evidence files are served whole, with no HTTP Range support, so scrubbing through a stored video is degraded to reloading it from the start each time.

## Runner mode

A runner is a background process that connects the workspace to the codesema hub (codesema.com, or your own instance) and works hands-off through its backlog of tickets for a repository: the hub publishes tickets, the runner codes them, ships them, reviews them and reports every transition back.
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions docs/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,26 @@ What this never does:
- **It never blocks a task.** A forge that cannot be reached, an absent `gh`/`glab`, a command that fails: the transition completes exactly as it would with the labels off — same status, same record — and the degradation is stated instead, as a journal line carrying `forge_unreachable`. The task record itself is never modified by this channel.

**One asymmetry, documented rather than smoothed over (D8):** the prefix uses a **simple** colon, not GitLab's scoped-label form (`scope::value`). A scoped label would give GitLab forge-side mutual exclusion for free, but GitHub has no such notion, so half the users would get an exclusion the other half would not. The exclusion is therefore computed by codesema, identically on both forges; the consequence on GitLab is that `codesema:` labels are ordinary labels there — no scoped behaviour, no forge-side exclusion, no scoped rendering in its UI.

## The checks chapter of a review (D16)

**Decision D16 is settled: a task's checks run once, and that single result becomes a mandatory chapter of the review and fix prompts and the evidence for the mechanical `command` criteria, read once from disk and never re-derived from the diff.**

## Visual proof (D17)

**Decision D17 is settled: the visual proof is proportionate, declared by the agent each turn, executed mechanically, judged by the reviewer.**

A screenshot or a recorded journey costs a turn its own time to capture and a reviewer real attention to read, so it is not owed for every turn: only a turn whose effect is visible earns one, and whether an effect is visible is closer to a judgment call than something a fixed rule could settle.

**Declaration.** Every turn opens its final message with one line, `PROOF: <none|screenshot|journey> [pages or spec] | <reason>`, placed after the `BRANCH:`/`CRITERION:` lines when those apply.

**The decision grid the agent follows:**

- the interface changed and the change is visible: `screenshot` naming the pages, or `journey` naming the spec when the change is a sequence rather than one screen;
- a UI file changed with no visible effect (a refactor with no rendered difference, a prop threaded through with no new output): `none`, with the reason stated;
- nothing outside the interface was touched: `none`;
- doubt: proof, not `none`. The grid resolves a tie toward capturing rather than skipping.

**Judgment.** The reviewer is handed one mechanical fact, whether UI files were touched, read straight from the diff with no model call, and renders one verdict, `proof_review { expected, coherent, reason }`. Only two shapes of that verdict are a blocking incoherence: a visible change with no proof and no acceptable reason, or a proof that came back failed with nothing said about it. Both are raised as a `design`/`major` finding, anchored on the diff line that made the change visible. A proof supplied when none was owed is never blocking, since over-proving costs nothing the review needs to act on.

The mechanical fact and the judgment stay on separate sides of the same verdict: what changed is read off the diff, whether the response to it was reasonable is decided by the model. A project with no `proof.url` configured turns every declaration into `skipped`, reason `no_target`, and that is never blocking: there is nothing to replay the proof against. An `undeclared` turn is not the same thing as a declared `none`: skipping the line entirely falls back to the project's own configured default, and that substitution is itself recorded as a finding, non-blocking, rather than silently read as if the agent had chosen `none` itself.
95 changes: 95 additions & 0 deletions docs/pilot-agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# The pilot agent (D18, open)

**Status: design note, not implemented, not scheduled.** Written on 2026-09-03 after a
reflection session. Nothing here is settled; the code does not know this agent exists.

## The idea

Today the human is the scheduler: they read the backlog, decide an issue is ready, start
a task, come back to see whether it landed in `waiting_for_you`. The runner mode already
removes that loop for hub tickets, but the local workspace still waits for a click.

The pilot is a single conversational agent that becomes the entry point of the product:
the home page of the local web UI first, later the same interlocutor on Telegram or Slack.
You ask it where things stand, you tell it what to start, it reports back. It never codes
and never replaces the deterministic machinery underneath (queue, concurrency cap, claim,
transitions, cycle labels, fix rounds). It sits on top of it as a remote control.

## Constraints that make the idea defensible

- **MCP tools only.** The pilot has no shell, no file access and no network. Its whole
surface is a set of tools exposed by a codesema MCP server, which is the only holder of
the local API token, the hub token and the forge credentials. Removing a power means
removing a tool.
- **Reads are free, writes are confirmed.** Listing projects, tasks, recaps, reviews and
issues needs no approval. Starting a task, replying to one, resuming, stopping,
shipping and merging come back as a proposal the human confirms (a button on the web,
an inline keyboard on Telegram), except for an explicit allowlist in the config such as
"start a task on an issue carrying the ready label".
- **Confirmation is enforced by the host, never by the model and never by MCP tool
annotations.** `readOnlyHint` and `destructiveHint` are untrusted hints in the MCP
specification (2026-07-28). The documented mechanism on the Claude Agent SDK side is
`permissionMode: "default"`, an allowlist of the read tools, and every other call
falling into `canUseTool`. Write tools should also carry
`_meta["anthropic/requiresUserInteraction"]` so a future allowlist cannot skip the
prompt. `dontAsk` is out: it refuses instead of asking.
- **Trust boundary.** The pilot acts on two inputs only: a sentence from the human in the
chat, or a signal a human put on the forge (label, assignee). The body of an issue is
data, never an instruction. Issue content reaches the model only inside `tool_result`
blocks, labelled with its source, and the MCP server sanitizes what it returns.
- **Short runs, no permanent session.** One message is one short agent run fed with a
compact snapshot of the state; the conversation itself is persisted server side as one
thread per workspace so the web and a chat channel continue the same conversation. The
pilot keeps an append-only journal of every action it triggered and why.
- **Isolated like any other agent.** It holds no forge or API secret, only the provider
key, sealed the same way as for the coding agents, and runs as an ephemeral turn.

## What the home page must keep

The state grid stays deterministic and instant next to the chat. The chat is for
questions, summaries and orders, not the only way to read the state: otherwise every
"where are we" costs tokens and seconds, and a chat channel would have nothing else.

## First increment worth building

The morning triage. It reads every task in `waiting_for_you`, sorts them into three
piles (restartable with a precise instruction, blocked on a question, to abandon), posts
one summary with the unblocking question for each, and acts only after a yes. Success
metric: the share of waiting tasks that restart with no intervention beyond one click.
Then "start whatever is tagged ready" within the existing concurrency cap.

Not first: a meta-agent that prioritizes the backlog on its own, a hierarchy of agents,
a new piloting UI, a wider auto-merge.

## What was verified on 2026-09-03

- The Claude Agent SDK runs an agent with MCP tools only through `tools: []`
(`disallowedTools: ["*"]` would remove the MCP tools too). Session resume is
documented via `resume` and `sessionStore`.
https://code.claude.com/docs/en/agent-sdk/custom-tools
https://code.claude.com/docs/en/agent-sdk/permissions
https://code.claude.com/docs/en/agent-sdk/session-storage
- The MCP specification forbids token passthrough, requires servers to sanitize outputs,
and says clients should keep a human able to refuse an invocation. Elicitation lets a
server ask the user for input mid call but has no confirmation mode as such.
https://modelcontextprotocol.io/specification/2026-07-28/server/tools
https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices
- Anthropic's guidance on prompt injection: third party content only in `tool_result`,
source labelled, least privilege, tool output treated as data.
https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks
https://www.anthropic.com/engineering/building-effective-agents
- Market: GitHub Copilot (issue assignment, MCP tools used without approval, review moved
to the PR), Cursor (Slack mention), Claude in Slack, Docker MCP Gateway (one proxy
holding the credentials). None interposes a non coding pilot agent that confirms before
each write.
https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp
https://docs.docker.com/ai/mcp-gateway/

## Open questions

- The MCP only restriction is verified for the Claude Agent SDK. Whether the pilot can
also run on `opencode` with the same restriction is not verified.
- One shared MCP server with the hub side toolbox, or two implementations of one
contract.
- Telegram identity: a single authorized chat id, everything else ignored; long polling
needs no inbound network.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codesema-tools",
"version": "0.20.0",
"version": "0.21.0",
"private": true,
"type": "module",
"workspaces": [
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codesema",
"version": "0.20.0",
"version": "0.21.0",
"description": "Local merge request review, step by step. Your AI agent reviews, codesema displays.",
"license": "MIT",
"author": "Hasan TASKIN",
Expand Down
Loading
Loading