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
8 changes: 8 additions & 0 deletions .changeset/a-change-is-committed-where-it-is-made.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@openspec-ui/core": minor
"@openspec-ui/webui": minor
"openspec-ui-vscode": minor
"@openspec-ui/server": minor
---

**A change is on the server from the moment it is made** (a-change-is-committed-where-it-is-made, ADR 0043 amended). Create Change, in both hosts, now commits the new change on its own branch and pushes it, and says so - or says why the server refused it. The rules written into `CLAUDE.md` and `AGENTS.md` tell every agent *when* to commit, not only where: the planning artifacts on the change's branch with `git push -u origin <change-id>` as soon as they are written, the work of every task ticked, nothing left unpushed at the end of a turn, nothing of a change on `main`; run **Write Agent Workflow Rules** to give an existing repository the new rules. The `git` stage commits what the stages left before it pushes. A card whose change's branch is not on the server says "not on the server", and a new action, **Commit Change**, on the card and in Show Actions..., commits everything the change's worktree holds and pushes it. A card of a change's own worktree now offers its actions in the Pipeline's arrangement by step as well, not only on the board by stage, and **Run Change...**, which runs the change in its worktree - before, such a change could be acted on but never run from its card. New messages `OSW-GIT-001`..`OSW-GIT-203`.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ the changes and goes to them: choosing one shows its card in the Pipeline,
where everything is done to it, and its one menu entry, **Show Actions...**,
lists the same actions (ADR 0044).

A change is on the server from the moment it is made: Create Change commits
it on its own branch and pushes it, the rules tell every agent to commit and
push its planning artifacts at once and its work after every task, and the
`git` stage commits what the stages left before it pushes. A card whose
change's branch is not on the server says so, and **Commit Change** commits
what the change's worktree holds and pushes it (ADR 0043).

The **Change Graph** view nests each change under the ones it
follows, marks anything waiting on a change that has not landed, folds
away the branches where every change has landed, and lets a relation be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,33 @@ decision 3), so the order was always "in the checkout first".
page can open the change it just made. Another repository's directory
is still refused.

### Amended on 2026-10-10 (a-change-is-committed-where-it-is-made)

The rules said where a change's commits go and never when. An agent made
the worktree, wrote the change's files and stopped: four changes of one
repository sat uncommitted in their worktrees, their branches where `main`
was, seen by no other directory, host or person, and their cards - drawn
in the Pipeline's arrangement by step - offered nothing at all. So:

6. **The rules say when.** As soon as the planning artifacts are written,
they are committed on the change's branch and the branch pushed with an
upstream; after every task ticked, its work with its `tasks.md`; nothing
is left uncommitted or unpushed at the end of a turn; nothing of a
change is committed to `main`; a refused push is said, not worked
around.
7. **The product commits and pushes what it makes.** "Create Change"
commits the new change on its branch and pushes it at once; where the
push is refused, the change stays made and the host says why. The `git`
stage commits what the stages left before it pushes.
8. **A card says where its change is not on the server, and commits it.**
The survey reads, from the refs it lists anyway, whether a change's own
branch was never pushed or is at another commit than the server's, and
the card says so. **Commit Change**, on the card and in Show
Actions..., commits everything the change's own worktree holds on its
branch and pushes it - never in the main checkout, never on the default
branch. A card of a change's own worktree offers its actions arranged by
step as on the board.

## Consequences

- An agent in any repository initialized by this product is told where a
Expand Down
66 changes: 66 additions & 0 deletions docs/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,72 @@ Why: The CLI's input is not a terminal, so nobody can answer, and the request is

What to do: Run it in a terminal to be asked, or from the card.

## GIT: git, branches, worktrees, push, pull requests

### OSW-GIT-001

> warning: {name} has no worktree of its own: {why}.

Why: Commit Change commits only in a change's own worktree, on its own branch.

What to do: Make the change's worktree, or work the change there.

### OSW-GIT-002

> warning: {name} is on {branch}, the default branch; nothing of a change is committed there.

Why: A change reaches the default branch through its pull request, never by a commit on it (ADR 0043).

What to do: Work the change on a branch of its own, named after it.

### OSW-GIT-101

> error: Nothing of {name} was committed: {why}

Why: git refused the commit: often no author is configured, or a hook failed.

What to do: Put right what git says, then choose Commit Change again.

### OSW-GIT-102

> error: The push of {branch} was refused{after}: {why}

Why: The server, or the credentials git uses for it, refused the branch. A commit already made stays.

What to do: Put right what the server says - access, credentials, a branch rule - then choose Commit Change again.

### OSW-GIT-103

> error: git stage failed at commit: {why}

Why: The git stage commits what the stages left before it pushes, and git refused that commit.

What to do: Put right what git says in the change's worktree, then run the chain again from git.

### OSW-GIT-201

> info: Committed {name} as {commit} and pushed it to {pushedTo}.

Why: Commit Change committed what the change's worktree held, on its branch, and pushed the branch.

What to do: Nothing: the change is on the server, where every directory, host and person can see it.

### OSW-GIT-202

> info: {name} had nothing to commit; its branch is pushed to {pushedTo}.

Why: Everything in the change's worktree was committed already, and the push brought the server up to date.

What to do: Nothing.

### OSW-GIT-203

> warning: {name} is made in {directory}, but not on the server: {why}

Why: A new change is committed on its branch and pushed at once; that failed, so no other directory, host or person sees it yet.

What to do: Put right what was refused, then choose Commit Change on the change's card.

## CLI: the CLI's arguments and subcommands

### OSW-CLI-001
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-10
62 changes: 62 additions & 0 deletions openspec/changes/a-change-is-committed-where-it-is-made/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## Context

ADR 0043 made every change live in a worktree of its own, on a branch named
after it. The survey pairs a directory with its change by the branch name
(`ownChange`), and a card of that change may act on it. What was missing is
the step between "made on this disk" and "on the server", and the actions
of such a card when the Pipeline is arranged by step.

## Goals / Non-Goals

**Goals:** a change, once made, is committed on its branch and pushed, by
whoever made it - an agent or the product; a card says where that has not
happened and does it with one press; a change of one's own worktree is
worked from its card in either arrangement.

**Non-Goals:** opening the pull request at creation (the git stage, or the
person, opens it when the change is ready for review: a pull request opened
at once would put the change in review on the board); changes of another
machine with no worktree here (a separate change, if wanted).

## Decisions

- **Commit everything in the change's worktree.** The worktree exists for
the change, on the change's branch; what it holds is the change's work.
`git add --all`, a commit where anything is staged, then a push that sets
the upstream where the branch has none. Refused on `main`/`master` and in
the main checkout.
- **Create Change shares what it made.** After `openspec new change`, the
scaffold is committed (`<id>: create the change`) and pushed. A refused
push does not undo the change; the result carries what was refused, and
the hosts say it (`OSW-GIT-203`).
- **The git stage commits before it pushes** (`<id>: commit what the stages
left`), recorded in the audit as a git action. Its failure fails the stage
before any push (`OSW-GIT-103`).
- **"Not on the server" from the refs only.** The survey already lists
`refs/heads` and `refs/remotes/origin` once; a change's branch with no
remote ref is `never-pushed`, one at another commit is `differs`. Running
`git status` in every worktree on every survey would cost a process per
directory per read, which the survey avoids by design
(the-workspace-clears-what-it-left-behind). So uncommitted files alone
are not marked; Commit Change is offered on every own-worktree card
anyway, and the rules and the git stage keep files from staying
uncommitted.
- **Commit Change is an action of core's list** (ADR 0044): verb Commit
(Set up), noun Change, its own icon `git-commit`; acts at once, so no
dots. Refused where the change is worked in the checkout, and while a run
of it is going (a run commits as it goes).
- **Arranged by step, as on the board.** The directory sections beneath the
picture draw a change's own card with the same facts and actions the
board gives it.
- **What is said is in the register** (ADR 0046): a new GIT group, so the
ratchet of messages said in place stays at 290.

## Risks / Trade-offs

- A push asks for credentials where git has none stored → the host waits
for git as any push does; a refusal is said with git's own words.
- "Commit everything" could commit a stray file in the worktree → the
worktree is the change's own; the commit is on its branch, reviewed in
its pull request.
- `differs` also reads a branch the server moved past → rare in this
workflow; the words say "not on the server as it is here", which holds.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
## Why

The owner, on 2026-10-10, about the repository `HppMCP`: its four changes,
each in a worktree of its own, all read as somebody else's, with no button
on their cards. Two causes:

- The rules agents are given (ADR 0043) say where a change's commits go -
"its planning artifacts, its code, its commits and pushes - in that
directory" - and never when. An agent made the worktree, wrote the files
and stopped. Every branch was where `main` was, and every change was
untracked files on one disk: no other directory, host or person saw it.
The product's own Create Change did the same, and the `git` stage pushes
but never commits.
- In the Pipeline's arrangement by step, a card of another working
directory is drawn with no actions at all, even for the change that
directory was made for. Only the board, by stage, gave it its actions
(a-change-is-acted-on-from-its-card).

The goal, in the owner's words: when a change is made, everything needed is
done, so the change can be worked at once as one's own.

## What Changes

- The agent workflow rules say when: commit the planning artifacts on the
change's branch and push it with an upstream as soon as they are
written; commit and push after every task ticked; leave nothing
uncommitted or unpushed at the end of a turn; nothing of a change on
`main`; a refused push is said, not worked around.
- Create Change, in both hosts, commits the new change on its branch and
pushes it at once. A refused push leaves the change made and says why
(`OSW-GIT-203`).
- The `git` stage commits what the stages left before it pushes
(`OSW-GIT-103` where that commit fails).
- The survey says where a change's own branch is not on the server: never
pushed, or at another commit. Read from the refs it lists anyway, so no
git runs in the directory.
- A card of a change's own worktree says so ("not on the server"), and a
new action, **Commit Change**, commits everything the worktree holds on
the change's branch and pushes it. On the card and in Show Actions..., in
both hosts; refused in the main checkout and on the default branch.
- A card of a change's own worktree offers its actions in the arrangement
by step as on the board, and **Run Change...**: the owner found on
2026-10-10 that such a change could be acted on but never run - a card's
Start was refused for a change this checkout does not have. It runs in
the change's worktree, in both hosts.
- Messages in the register's new GIT group (`OSW-GIT-001`..`203`). ADR 0043
amended.

## Capabilities

### New Capabilities

None.

### Modified Capabilities

- `execution-core`: the rules say when to commit and push; a new change is
committed and pushed as it is made; the git stage commits what the
stages left; Commit Change.
- `shared-ui`: a card of a change's own worktree acts arranged by step too,
and says where its branch is not on the server.

## Impact

- `packages/core`: `change-commit.ts` (new), `change-in-its-worktree.ts`,
`agent-workflow-rules.ts`, `harness-chain-runner.ts`, `worktree-survey.ts`
and its facts, `change-actions.ts`, `message-register.ts`; their tests.
- `packages/webui`: `PipelineView.tsx`, `standalone-entry.tsx`.
- `packages/extension`: the Commit Change command and its manifest entry,
Create Change's announcement.
- `packages/server`: Commit Change in the change-action route; Create
Change's answer says whether the change is on the server.
- `docs/messages.md` regenerated; ADR 0043 amended; README and the
extension's README. A changeset for core, webui, extension, server.
- Existing repositories get the new rules when Write Agent Workflow Rules
is run again; changes already made are committed with Commit Change.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
## ADDED Requirements

### Requirement: The git stage commits what the stages left

Before it pushes, the `git` stage SHALL commit everything the change's
working directory holds that is not committed, on the branch it pushes,
and record that commit as a git action. It SHALL NOT commit on the default
branch. Where the commit fails, the stage SHALL fail before any push and
say why (`OSW-GIT-103`).

#### Scenario: Work left uncommitted

- **WHEN** the `git` stage starts on branch `add-cart` and the stages left
files uncommitted
- **THEN** they are committed as "add-cart: commit what the stages left"
before the branch is pushed

#### Scenario: The commit fails

- **WHEN** git refuses that commit
- **THEN** the stage fails with "git stage failed at commit" and git's
words, and nothing is pushed

### Requirement: A change's own worktree is committed and pushed in one action

Commit Change SHALL commit everything a change's own worktree holds on the
change's branch and push the branch, setting its upstream where it has
none. It SHALL be refused, committing nothing, where the change has no
worktree of its own (`OSW-GIT-001`) and where that worktree is on the
default branch (`OSW-GIT-002`). A refused push SHALL leave the commit made
and say the server's words (`OSW-GIT-102`). Both hosts SHALL say the
result through the same function in core.

#### Scenario: A change made and never committed

- **WHEN** Commit Change is chosen for `demo`, whose worktree holds its
files untracked on a branch never pushed
- **THEN** they are committed on `demo`, the branch is on the server with
`origin/demo` as its upstream, and nothing of `demo` is on `main`

#### Scenario: A change in the checkout

- **WHEN** Commit Change is asked for a change with no worktree of its own
- **THEN** nothing is committed, and the answer is `OSW-GIT-001`

## MODIFIED Requirements

### Requirement: Every agent is told how work is done in the repository

The product SHALL write a section between its own markers into `CLAUDE.md`
and `AGENTS.md` saying that each OpenSpec change is worked in a git
worktree of its own at `<worktree root>/<repository>/<change-id>`, on a
branch named after the change cut from `origin/main`, with the rule for
the root and the commands that make it, and that the stage assignment is
in `openspec/agent-harness.json` and a change's `harness.json`, a stage
named for another agent being handed back. It SHALL also say when a
change is committed and pushed: its planning artifacts on its branch, with
the branch pushed and its upstream set, as soon as they are written; the
work of every task ticked, with its `tasks.md`; nothing left uncommitted or
unpushed at the end of a turn; nothing of a change on the default branch;
and a refused push said, not worked around. It SHALL create a file that is
missing, rewrite the section in place where it is, and add it to the end
of a file without it that somebody else wrote only where the person said
so; it SHALL NOT change anything else in a file.

#### Scenario: A repository with OpenSpec's own AGENTS.md

- **WHEN** the rules are written where `AGENTS.md` exists without the
section and `CLAUDE.md` does not exist, and the person declines
- **THEN** `CLAUDE.md` is created with the section and `AGENTS.md` is left
as it was, and the result names it

#### Scenario: Told when, not only where

- **WHEN** an agent reads the rules
- **THEN** they say to commit the planning artifacts and run `git push -u
origin <change-id>` as soon as they are written, and to commit and push
after every task ticked

### Requirement: A new change is made in a working directory of its own

A working directory SHALL be made for a change that exists nowhere yet;
one SHALL still be refused for a change that exists only as uncommitted
files in the main checkout. Making a new change through the product SHALL
fetch, cut its own directory from `origin/main` on a branch named after
it, run `openspec new change` there, commit the change on its branch and
push the branch with its upstream set. A refused push SHALL leave the
change made, and the host SHALL say why the server does not have it
(`OSW-GIT-203`). In a repository with no `origin` the change SHALL be made
in the workspace as before.

#### Scenario: Create in a repository with a remote

- **WHEN** a change `add-cart` is created in a repository `shop` with an
`origin`
- **THEN** `../.worktrees/shop/add-cart` exists on branch `add-cart` cut
from `origin/main`, the change is in it, not in the checkout, and the
branch is on the server with the change committed

#### Scenario: The push is refused

- **WHEN** the push of the new change's branch is refused
- **THEN** the change is made in its directory, and the host says it is
not on the server, and why
Loading
Loading