> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting Work done

> Take one piece of work from proposal to merged change: plan it as a project issue, deliver it on a stack of branches, and know what each kind of move does.

This tutorial follows one improvement to the Casework sample from idea to merge.
On the way it shows how planning (proposals, issues, cycles) and execution
(branches, workspaces, stacks, reviews) fit together, and what "moving" work
means in each place.

It builds on the overdue filter from
[Ten minutes with Casework](/workflow/casework-walkthrough) and goes one step
further: the rule and its tests on one branch, then a visible count on a second
branch that depends on it. The code is small on purpose; the subject is the
work around it.

**Before you start.** You need:

* **A Casework project hosted in ReasonOS.** Set one up from
  [The Casework sample](/workflow/casework-sample): an **Empty repository**
  project with the sample pushed to it. Moving a workspace and submitting a
  stack need a project hosted here. A project imported from another Git server
  works for the planning steps (1 and 2) but not for the rest.
* **The tutorial proposal** **Getting Work done: show overdue case counts**
  under **Work › Proposals**. A demo stack seeded for the tutorial has it. In
  your own project, create it: choose **New proposal**, use that title, paste
  this as its argument, and choose **File proposal**:

  ```markdown theme={null}
  ## Goal
  Show how many open cases are overdue, using the sample's fixed demonstration date.

  ## Scope
  Deliver the rule and its tests first, then the visible count on a dependent branch.
  Keep the existing All, Open and Closed filters working.

  ## Acceptance
  - Only open cases strictly before 2026-09-14 count; CW-101 is the one baseline match.
  - Due-today, future and closed cases are excluded, with maintained RBS tests.
  - The displayed count agrees with the rule and the existing filters still work.
  - Record actual test, review and merge evidence on the resulting project issue.
  ```

Nothing in this tutorial needs an AI model; the one optional agent step says
so.

## The principles

Four rules explain everything below:

* **One issue, owned by the project.** An issue lives in the project's Work. A
  workspace, a branch or an agent shows that same issue; none of them makes a
  copy.
* **Cycles are optional plans.** Putting an issue in a cycle schedules it. The
  issue list and the cycle's board are two views of the same issues.
* **Branches are linked, not owners.** Linking a branch to an issue records
  where the work happens. It doesn't move the issue or create a second one.
* **The same permissions everywhere.** What you can see and change is the same
  in the project, in a workspace and through an agent.

## 1. Start from a proposal

A proposal is how work is suggested before anyone commits to it.

1. Open the project, then **Work › Proposals**, and open **Getting Work done:
   show overdue case counts**. Read its goal, scope and acceptance criteria.
2. Choose **Accept & file issue**.

**Expected:** the proposal shows as accepted, with one issue under it. The new
issue is in **Todo**, unassigned and in no cycle, and it links back to the
proposal. Note its key, shown before its title: the project's first letters and
a number. This page writes it as **CAS-6**, the number in the tutorial's demo;
use your own wherever CAS-6 appears below. The proposal's scope and acceptance criteria are now
the issue's to deliver.

**Why it matters:** intake (should we do this?) is separate from planning (when
and by whom?) and from execution (the code). Accepting records the decision; it
doesn't schedule anything.

## 2. Plan it

1. On the issue, set **Assignee** to yourself, **Priority** to High and
   **Cycle** to **Demo cycle 1**, the cycle in the tutorial's demo. In a project
   with no cycles yet, make some first under **Work › Cycles**: if it says
   cycles are off, choose **Turn on cycles**, switch on **Enable cycles** in the
   settings that open, and go back to **Cycles**; then choose **Add upcoming
   cycles**. Pick the first cycle it lists, and use it wherever this page says
   Demo cycle 1.
2. Open **Work › Cycles** and choose **Demo cycle 1**.

**Expected:** the issue appears on the cycle's board in the **Todo** column,
next to the cycle's other issues. Move it to **In Progress** from its card's
menu or with the keyboard, then open **Work › Issues**: the same issue shows
**In Progress** there too.

**Why it matters:** the list and the board are views, not copies. A change made
in one is the change everyone sees. See [Cycle boards](/platform/work#cycle-boards).

## 3. Create the first branch

The change has two parts: the rule and its tests, then the visible count that
depends on it. Each part gets its own branch, the second stacked on the first,
so they can be reviewed and merged in order.

On the issue, under **Development**, choose **Create branch or stack**. Pick
**Stacked branch** on `main` and name it `overdue-rule`; the issue key is added
for you, giving `CAS-6/overdue-rule`. Leave **Start a workspace for this
branch** on, and choose **Create stacked branch**.

**Expected:** the branch appears under the issue's links, and it is still one
issue. The page opens the new branch's workspace, a branch server of its own.

**Why it matters:** a stack lets dependent changes be reviewed and merged in
order, with each change request targeting the branch below it. A **Feature
branch** would work too, but it records no parent, so it isn't part of a stack.
See [Stacks](/workflow/stacks).

## 4. Work in the workspace

1. Choose the **Work** tab and the branch's name. The issue is listed because
   the branch name carries its key. Choose it: the issue opens in a panel beside
   your code, with the same status, comments and links.
2. Describe the behaviour as a test first. Add to `cases_test.go`:

   ```go theme={null}
   func TestOverdueRuleAndCountContract(t *testing.T) {
       selected, err := selectCases(sampleCases(), "overdue")
       if err != nil || len(selected) != 1 || selected[0].ID != "CW-101" {
           t.Fatalf("only the past-due open case belongs: %v, %v", selected, err)
       }
       for _, tc := range []struct {
           filter string
           count  int
       }{{"all", 4}, {"open", 3}, {"closed", 1}, {"overdue", 1}} {
           rec := httptest.NewRecorder()
           handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api/cases?status="+tc.filter, nil))
           var result struct {
               Cases []Case `json:"cases"`
               Count int    `json:"count"`
               Total int    `json:"total"`
           }
           if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &result) != nil {
               t.Fatalf("%s response: %d %s", tc.filter, rec.Code, rec.Body.String())
           }
           if len(result.Cases) != tc.count || result.Count != tc.count || result.Total != 4 {
               t.Fatalf("%s count and total disagree: %+v", tc.filter, result)
           }
       }
   }
   ```

   Run `rbs test //:test --no-cache` and watch it fail: the service doesn't
   know `overdue` yet.
3. Make it pass. In `cases.go`, replace `selectCases`:

   ```go theme={null}
   func selectCases(cases []Case, status string) ([]Case, error) {
       if status != "" && status != "all" && status != "open" && status != "closed" && status != "overdue" {
           return nil, fmt.Errorf("status must be all, open, closed or overdue")
       }
       selected := make([]Case, 0, len(cases))
       for _, c := range cases {
           if status == "overdue" {
               if c.Status == "open" && c.Due < demoToday {
                   selected = append(selected, c)
               }
               continue
           }
           if status == "" || status == "all" || c.Status == status {
               selected = append(selected, c)
           }
       }
       return selected, nil
   }
   ```

   Then in `main.go`, have `/api/cases` say how many cases matched and how many
   there are, so the page can show a count that agrees with the rule:

   ```go theme={null}
           _ = json.NewEncoder(w).Encode(struct {
               Today string `json:"today"`
               Cases []Case `json:"cases"`
               Count int    `json:"count"`
               Total int    `json:"total"`
           }{demoToday, cases, len(cases), len(sampleCases())})
   ```

   Run `rbs test //:test --no-cache` again until it passes, then commit `cases.go`,
   `main.go` and `cases_test.go` from **Source Control**. Leave `web.go` alone;
   the page belongs to the next branch.
4. Publish the commit: open a terminal (**Show Terminal**) and run
   `git push origin HEAD`. The next branch starts from this one as published, so
   an unpublished commit wouldn't be in it.
5. In the issue panel, comment with what you did and the test result.

**Expected:** the test passes, the commit is published on the rule branch, and
the comment is on the issue in the project too.

**Optional, uses an AI model:** **Run with agent** on the issue starts a coding
agent on this branch with the issue as its brief. It needs an AI account set
up for the organization; see [AI accounts and keys](/platform/ai-accounts-and-keys).

See [Work in the workspace](/platform/work#work-in-the-workspace).

## 5. Stack the next branch, and move the workspace to it

Open the stack pill in the top bar and choose **New stacked branch**. This box
takes the full branch name: enter `CAS-6/overdue-count`, keeping the issue key
so the branch links to the issue, and create it. It starts from the rule branch
as published. ReasonOS then asks where you want to work:

* **Move here** moves *this* workspace onto the new branch.
* **Open in its own workspace** starts a separate workspace for it, and this one
  stays on the rule branch.

Choose **Move here**.

**Expected:** the workspace is now on the count branch, on top of your rule
commit. Uncommitted changes that don't conflict come along. The rule branch
keeps its commit.

In a ReasonOS version with automatic workspace following, everyone else in
this workspace moves with it. The person who moves goes straight away; anyone
else connected follows within a few seconds, keeping the issue and view they
had open, and is told the workspace was moved there. Your
files and uncommitted changes are where they were: it is the same branch
server.

A tab with unsaved files, or with a file still uploading to the open issue,
doesn't leave them behind. It stays, says the workspace was moved, and follows
on its own once they're saved or discarded, or the upload finishes.

In a version without automatic following, others aren't moved: they see that the server's
checkout is on another branch, with **Open its workspace** to follow, or, in
older versions still, open the new branch's workspace themselves from the
branch list.

A move is always a person's deliberate choice. Choosing a branch from the
branch list, or anything an agent does, opens or uses a branch's own workspace;
it never moves one. A branch that already has its own workspace can't be moved
onto: open that workspace instead.

Now make the visible count on this branch, in `web.go`:

1. After **Closed cases**, add `<option value="overdue">Overdue cases</option>`.
2. After the `<select>` line, add
   `<p id="count" role="status" aria-live="polite"></p>`.
3. In `refresh()`, after the line that sets the demo date, add
   `document.getElementById('count').textContent=data.count+' of '+data.total+' cases';`
4. In the `catch` block, clear it too, before the error is shown:
   `document.getElementById('count').textContent='';`

Run `rbs test //:test --no-cache`, start the page with `rbs run //:server`, and
open `http://127.0.0.1:8077` in **Tools → Browser**. **Expected:** **All cases**
says 4 of 4 cases, **Open cases** 3 of 4, **Closed cases** 1 of 4, and
**Overdue cases** 1 of 4, listing only **CW-101**. Commit `web.go`, run
`git push origin HEAD`, and comment the result on the issue.

## 6. When a move is refused

A move checks your uncommitted changes first, and refuses before anything
changes if one would be lost.

1. Change `cases.go` without committing.
2. Open the stack pill, point at the `main` row (or move to it with the
   keyboard) to show its actions, and choose **Move here**.

**Expected:** the move is refused: "Main changes files you have changed here
(cases.go); commit or discard those changes before moving." Nothing moved.
You're still on the count branch, with your edit and everything else as it
was.

**Recover** by deciding what the edit is. Commit it if it belongs to this
branch, or discard it in **Source Control**. Then you can move.

The same care applies elsewhere: unsent writing in the issue panel is kept
across closing it and reloading the page, and a save that was refused or might
not have completed says so rather than pretending it worked.

## 7. Review, merge and close

1. From the stack pill, choose **Submit stack**. It shows what it will do,
   including any uncommitted changes it leaves out, then opens a change request
   for each branch, each into the branch below it.
2. A change request starts with no reviewers. On each one, choose **Ask
   someone** (or **Add**) under **Reviewers** and pick a teammate. It then
   appears in their **Inbox** under **Needs your review**.
3. Run the gates. Where the project has CI or QA checks set up, they run on each
   request; otherwise the evidence is the `rbs test` runs you recorded.
4. Merge from the bottom of the stack up: the rule first. The count's request
   then targets `main` by itself, rebased onto the merged rule; review and merge
   it next.
5. Finish on the issue in the project's Work. Once a branch is merged it is
   removed, along with its workspace, so its old workspace address no longer
   opens. Move the issue to **Done** and record the evidence on it: the test
   runs, the reviews and the merged change requests.

See [Change requests](/platform/change-requests) and [CI](/platform/ci).

## What "done" means

Keep these four apart:

* **Tested:** the gates passed on the branch.
* **Merged:** the change is on the main branch.
* **Documented:** the public docs describe it, where people would look.
* **Deployed:** it is running for customers. Merging doesn't deploy anything.

## Find what's next

Your **Inbox** lists the review requests waiting on you and **Your active
issues**: the issues assigned to you that are Todo or In progress, across every
project you can read. **All work** finds any existing issue across projects. See
[All work](/platform/work#all-work-issues-across-projects) and
[the inbox](/platform/change-requests#the-inbox).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.