unitbob 0.7.12 → 0.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,134 +1,142 @@
1
1
  # Unitbob
2
2
 
3
- A living map of your app's business parts. Each important seam gets an automatic
4
- test — a "lamp" on the map. Green means fine, red means something broke.
3
+ Unitbob draws a map of what your app does for people signing in, paying,
4
+ sending messages and puts a lamp on each part. Behind every lamp is an
5
+ automatic check. Green means that part still works. Red means something you
6
+ changed broke it.
5
7
 
6
- Works with Ruby on Rails (RSpec), JavaScript/TypeScript (Vitest), and Python
7
- (pytest) projects the guardrail tests are generated in your project's own
8
- language and run with its native test runner.
8
+ You do not write the checks. Your coding assistant — Claude Code or Codex —
9
+ does, and Unitbob keeps them honest. Your code stays on your computer; only
10
+ the checks and their results go to Unitbob.
9
11
 
10
- You work through a coding agent Claude Code or Codex both work. You need:
11
- Node 18+, Python 3.10+.
12
+ Works with apps written in Ruby on Rails, JavaScript/TypeScript and Python.
13
+ You need Node 18 or newer and Python 3.10 or newer installed.
12
14
 
13
15
  ---
14
16
 
15
17
  ## Install (once)
16
18
 
17
- **With a prompt:**
19
+ Tell your assistant:
20
+
18
21
  ```
19
22
  Add the Unitbob plugin marketplace: sergeygershun/unitbob-connector
20
23
  Install the unitbob plugin
21
24
  ```
22
25
 
23
- **Claude Code (in the terminal):**
26
+ Or, if you prefer the terminal — Claude Code:
27
+
24
28
  ```
25
29
  claude plugin marketplace add sergeygershun/unitbob-connector
26
30
  claude plugin install unitbob@unitbob
27
31
  ```
28
32
 
29
- **Codex (in the terminal):**
33
+ Codex:
34
+
30
35
  ```
31
36
  codex plugin marketplace add sergeygershun/unitbob-connector
32
37
  codex plugin add unitbob@unitbob
33
- npx -y unitbob@0.4.4 codex-install
38
+ npx -y unitbob@0.7.13 codex-install
34
39
  ```
35
40
 
36
- Start a new Claude Code or Codex thread so the installed skill and named agents
37
- load. After setup, the phrasings and Unitbob flow below are the same on both
38
- hosts.
41
+ Then start a new chat. A chat opened before the install does not know about
42
+ Unitbob yet. Everything below works the same in Claude Code and Codex.
39
43
 
40
- Codex compatibility: version 0.145.0 accepts the Unitbob custom-agent TOML files,
41
- but its experimental rollout budget is shared by the root and subagents rather
42
- than enforced separately for each named agent. No Codex version is currently
43
- qualified by Unitbob for a native per-agent ceiling. Before the first bounded
44
- role, Unitbob therefore asks whether to continue this invocation without that
45
- mechanical ceiling; approval is never persisted. The definitions keep the native
46
- budget values so a future Codex release can be qualified without introducing a
47
- Unitbob supervisor.
44
+ On Codex, the assistant may ask once whether it can continue without a spending
45
+ limit for its helpers. Answer yes; Codex cannot enforce that limit yet.
48
46
 
49
47
  ---
50
48
 
51
- ## Full cycle
49
+ ## Protect what your app already does
52
50
 
53
- Just type it in the chat. There is nothing to memorise and no command to get right.
51
+ Just say it in the chat. Nothing to memorise, no command to get right.
54
52
 
55
53
  | Step | Say this |
56
54
  |------|----------|
57
- | 1. Build the map | `Build my Unitbob map` |
58
- | 2. Generate the tests | `Generate the guardrail tests` |
59
- | 3. Fix a red lamp | `Fix guardrail <id>` |
60
- | 4. Open the map | `Open my Unitbob map` |
55
+ | 1. Draw the map | `Build my Unitbob map` |
56
+ | 2. Write the checks | `Generate the guardrail tests` |
57
+ | 3. Look at the map | `Open my Unitbob map` |
58
+ | 4. Fix a red lamp | `Fix guardrail <id>` the id is written on the lamp |
61
59
 
62
- Step 2 lights the lamps by itself: generating the tests also runs them and sends
63
- the results. You do not have to ask for a run to see the first result.
60
+ Step 2 also runs the checks, so the lamps light up right away. Later, whenever
61
+ you want to know whether your latest changes broke anything, say `Run the
62
+ checks`.
64
63
 
65
- Say `Run the checks` later, whenever you want the lamps refreshed against your
66
- current code or to finish the job if a generation was interrupted after the
67
- tests were saved but before they ran.
64
+ Your project connects itself to Unitbob the first time. There is nothing to
65
+ set up by hand.
68
66
 
69
- In Claude Code there are also `/unitbob:map`, `/unitbob:suite` and friends, but
70
- they work only in a terminal session started after the plugin was installed — in
71
- a browser or desktop window they are not recognised at all. The phrasings above
72
- work everywhere, so they are the ones documented here.
67
+ ---
68
+
69
+ ## Build something new
70
+
71
+ The steps above protect what your app does today. When you want to add
72
+ something new, Unitbob can protect the new part too — before it exists. Then
73
+ "done" is not a feeling; it is the checks turning green.
74
+
75
+ Do steps 1 and 2 above first. Then, in the chat, in this order:
76
+
77
+ | Step | Say this | What happens |
78
+ |------|----------|--------------|
79
+ | 1. Name it | `I want to add <feature>` | Unitbob writes down what you want and which parts of the map it may touch. You get a link to the feature's page. |
80
+ | 2. Talk it through | `Let's talk through <feature>` | The assistant asks a few numbered questions about how the feature should behave — in plain words, with options and a recommendation. Answer with a letter, a word, or `do as you recommend`. Then it shows you the list of things that will be true when the feature is done and asks: is this it? Say `yes`. |
81
+ | 3. Write the checks | `Write the checks for <feature>` | Each item from that list becomes a check. They are all red now — on purpose, the feature is not built yet. |
82
+ | 4. Build it | `Build <feature>` | Ordinary work in the same chat. Say `Run the checks` at any time to see how many of the feature's checks already pass, and whether anything else broke. |
83
+ | 5. Wrap up | `I'm done with <feature>` | The checks run once more, a second assistant reads them over, and you get the link to the feature's page. |
84
+ | 6. Finish | Press **Finish this feature** on that page | Only you can press it, and only when everything is green. The feature gets its own lamp on the map, already green. |
85
+
86
+ Good to know:
87
+
88
+ - **If the button is grey, the page says why** — a check of the feature still
89
+ fails, the checks have not been read over yet, or something you said must
90
+ keep working is red.
91
+ - **A red lamp you expected is fine.** If in step 2 you said a part would
92
+ change, its row on the feature page is amber, not red, with a button
93
+ `Accept the new behaviour`. Press it and it is green again. A red lamp you
94
+ did not expect is shown too, with the usual `Fix this`.
95
+ - **The checks say exactly what you agreed to in step 2.** Nobody can quietly
96
+ bend them to fit the code. If you change your mind about the feature, say
97
+ `Let's talk through <feature>` again and the checks are rewritten.
98
+ - **Work in your project's main folder.** Unitbob keeps the checks in a folder
99
+ called `.unitbob` next to your code. A copy of the project in another folder
100
+ does not have it.
101
+ - **After Finish, nothing more to do.** From now on the feature's checks run
102
+ with all the others.
73
103
 
74
104
  ### If the assistant says it cannot find the Unitbob instructions
75
105
 
76
- A session that started *before* the plugin was installed does not pick up the
77
- skill, so the assistant has nothing to follow. Restarting the session is the
78
- clean fix. If that is inconvenient, the instructions are ordinary files on disk
79
- and the assistant can read them directly — tell it:
106
+ The chat was started before the plugin was installed. Start a new chat. If you
107
+ would rather not, tell the assistant:
80
108
 
81
109
  ```
82
110
  Read ~/.claude/plugins/cache/unitbob/unitbob/<version>/skills/unitbob/SKILL.md
83
111
  and follow the workflow it names for this job.
84
112
  ```
85
113
 
86
- `<version>` is whatever `claude plugin list` reports (for example `0.3.2`). The
87
- workflow files sit next to it under `workflows/`, one per job, and each is
88
- self-contained — that is what they are designed for.
89
-
90
- ---
91
-
92
- ## How to read it
93
-
94
- - **Green lamp** — the behavior works.
95
- - **Red lamp** — something the structure relied on broke. Copy its `id` and run
96
- step 3.
97
- - The project links itself by folder name — nothing to set up by hand.
114
+ `<version>` is the number that `claude plugin list` shows.
98
115
 
99
116
  ---
100
117
 
101
- ## If your tests only run inside Docker
118
+ ## If your app only runs inside Docker
102
119
 
103
- Some projects keep the code on this machine and everything that runs it — the
104
- interpreter, the packages, the database inside a container. Name that
105
- container in `.unitbob.json` and Unitbob starts the project's own commands in
106
- there:
120
+ Some projects keep the code on your computer but run it inside a Docker
121
+ container. Tell Unitbob the container's name in the file `.unitbob.json` in
122
+ your project folder:
107
123
 
108
124
  ```json
109
125
  { "server": "…", "repo_id": 3, "token": "…",
110
126
  "exec": { "docker": { "container": "myapp-web-1" } } }
111
127
  ```
112
128
 
113
- Nothing else changes. Files are still read and written here, and the path inside
114
- the container is worked out from the container's own mounts, so there is nothing
115
- else to configure. Leave the field out and everything runs on this machine,
116
- exactly as before.
117
-
118
- The project folder has to be **mounted** into the container rather than copied
119
- into the image — which it already is in any setup where you can edit a file and
120
- see the change. If it is not, Unitbob says so and stops before writing anything.
121
-
122
- Known limits of running in a container, all of them deliberate for now:
123
-
124
- - **A run that times out can leave a process alive inside the container.** The
125
- timeout stops the `docker exec`, not necessarily what it started. A report
126
- such a process writes afterwards is never counted as a later run's result.
127
- - **On a Linux host, files the container writes belong to `root`.** Unitbob does
128
- not map users: guessing there breaks images that installed their packages as a
129
- user of their own.
130
- - **Only a container that is already running.** A project whose tests go through
131
- `docker compose run --rm` is not supported yet.
132
- - **Review at a fixed revision is not supported with a container.** It works in
133
- a git worktree under the system's temporary directory, which is outside the
134
- mount. Ordinary review works in the project itself and is unaffected.
129
+ Keep the other three values as they are; Unitbob wrote them. Now the checks
130
+ run inside the container. Without that line everything runs on your computer,
131
+ as before.
132
+
133
+ This works only when the project folder is shared with the container — which
134
+ it already is if editing a file on your computer changes the running app. If
135
+ it is not, Unitbob tells you and stops.
136
+
137
+ A few things Unitbob does not do in a container yet:
138
+
139
+ - The container has to be running already; Unitbob does not start it.
140
+ - On Linux, files written from inside the container belong to `root`.
141
+ - If a check takes too long and is stopped, the container may keep running it
142
+ in the background for a while.
package/dist/cli.js CHANGED
@@ -73,7 +73,9 @@ Verbs:
73
73
  Internal: run the suite you just wrote, before publishing it, with the same runner
74
74
  that will run it afterwards. No argument runs every branch the build asked for;
75
75
  --feature runs one feature's checks alone, by their tag.
76
- fix-prepare <id> Internal: fetch the per-capability repair packet for one red guard (by interface_id).
76
+ fix-prepare [<id>] Without an id: every red guard on both maps, with its suite digest and history — the
77
+ list a fix starts from. With an id: the per-capability repair packet for one
78
+ structural guard (spec 26).
77
79
  contract-prompt <digest>|feature:<id> <test_id> [fix|accept]
78
80
  Internal: fetch the fix/accept brief for one red check on either map — or, with
79
81
  feature:<id>, the fix brief for a feature's own failing checks.
@@ -1,20 +1,32 @@
1
1
  import { writeFixRequest } from "../files/fix.js";
2
2
  import { Wire } from "../wire.js";
3
- // Fetch the per-capability repair packet and write the host's task to
4
- // `.unitbob/fix/request.json`. No recipe fetch, no upload — the host reads its own
5
- // source and the local spec file, then either fixes code (next `/unitbob check`
6
- // shows the result) or accepts the change and republishes the suite (spec 26). A
7
- // 422 from the server (non-failed / stale / no suite) surfaces via WireError;
8
- // nothing is written.
3
+ // Two forms, one verb.
4
+ //
5
+ // Without an id (spec 54-1): print the list a fix starts from every red guard
6
+ // of both maps under the digest it is red on, with the line that says whether
7
+ // it ever passed on that suite. The server words it; the connector prints it
8
+ // as it is. Nothing is written: the list is read, the brief comes next from
9
+ // `contract-prompt` with the digest copied off this list.
10
+ //
11
+ // With an id (spec 26): fetch the per-capability repair packet and write the
12
+ // host's task to `.unitbob/fix/request.json`. No recipe fetch, no upload — the
13
+ // host reads its own source and the local spec file, then either fixes code
14
+ // (next `/unitbob check` shows the result) or accepts the change and
15
+ // republishes the suite. A 422 from the server (non-failed / stale / no suite)
16
+ // surfaces via WireError; nothing is written.
9
17
  export async function fixPrepare(config, args = [], deps) {
10
- const interfaceId = (args[0] ?? '').trim();
11
- if (!interfaceId)
12
- throw new Error('Usage: unitbob fix-prepare <interface_id>');
13
18
  const d = {
19
+ getRedList: () => new Wire(config).getRedList(),
14
20
  getFixPacket: (id) => new Wire(config).getFixPacket(id),
15
21
  stdout: process.stdout,
16
22
  ...deps,
17
23
  };
24
+ const interfaceId = (args[0] ?? '').trim();
25
+ if (!interfaceId) {
26
+ const list = await d.getRedList();
27
+ d.stdout.write(`${list.message}\n`);
28
+ return;
29
+ }
18
30
  const packet = await d.getFixPacket(interfaceId);
19
31
  writeFixRequest(config.projectRoot, interfaceId, packet);
20
32
  d.stdout.write(`${packet.message}\n`);
package/dist/wire.js CHANGED
@@ -159,6 +159,15 @@ export class Wire {
159
159
  await this.ensureOk(res, `PUT ${this.repoPath('suite_build')}`);
160
160
  return (await res.json());
161
161
  }
162
+ // GET /repos/:id/red_lamps — the red list (spec 54-1). No parameters: the
163
+ // server names the current version of each map itself, which is the whole
164
+ // point — a fix no longer guesses a digest.
165
+ async getRedList() {
166
+ const url = this.repoPath('red_lamps');
167
+ const res = await this.send('GET', url);
168
+ await this.ensureOk(res, `GET ${url}`);
169
+ return (await res.json());
170
+ }
162
171
  // GET /repos/:id/fix_packet?interface_id= — the per-capability repair packet.
163
172
  // Relayed down for the host to fix code or accept the change; 422 (non-failed /
164
173
  // stale / no suite) surfaces as a WireError carrying the server's business reason.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unitbob",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "description": "Unitbob connector — thin local hands for the Unitbob Rails brain. Owns no domain logic: it runs tools, relays bytes over the wire, and prints what the server returns.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,7 +21,7 @@ markers, or paths. Do not edit production code, host-owned shared files, the
21
21
  connector-owned harness, or another slice.
22
22
 
23
23
  After every owned edit, run
24
- `npx -y --loglevel=error unitbob@0.7.12 run-local <branch>` and inspect the machine
24
+ `npx -y --loglevel=error unitbob@0.7.13 run-local <branch>` and inspect the machine
25
25
  report. Look only at examples or scenarios matching your owned paths or case
26
26
  markers. Do not require a green exit code from the whole branch: foreign failures
27
27
  and an already-confirmed product red do not widen your scope. Repeat the bounded