@awebai/oats 0.42.0 → 0.43.0
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/bin/oats.mjs +66 -18
- package/docs/desktop-cli-api.md +109 -23
- package/docs/desktop.md +17 -2
- package/docs/packages.md +6 -0
- package/docs/release-notes/v0.42.1.md +110 -0
- package/docs/release-notes/v0.43.0.md +93 -0
- package/docs/schedules.md +49 -15
- package/docs/servers.md +4 -1
- package/docs/souls-and-instances.md +126 -6
- package/injects/instance-boundary.md +4 -3
- package/injects/work-checkout.md +25 -3
- package/injects/work-worktree.md +30 -7
- package/lib/automations.mjs +31 -5
- package/lib/core.mjs +181 -18
- package/lib/instance-git.mjs +22 -0
- package/lib/instance-lifecycle.mjs +17 -2
- package/lib/retire-output.mjs +14 -1
- package/lib/schedule.mjs +21 -8
- package/lib/servers.mjs +10 -2
- package/lib/triggers.mjs +22 -3
- package/package.json +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# OATS 0.42.1
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **Remote roster rows carry the local row's work, repository, branch,
|
|
6
|
+
model-from, soul and module drift facts**
|
|
7
|
+
([#675](https://github.com/awebai/oats/issues/675)). A remote instance
|
|
8
|
+
row (`oats server roster --json`, and the Desktop's remote roster) now
|
|
9
|
+
relays `work`, `repo`, `branch`, `modelFrom`, `soul` and `modules` from
|
|
10
|
+
the host's own `oats status --json` row, with the same names and meaning
|
|
11
|
+
as on a local row. `soul` and `modules` are the host's own drift
|
|
12
|
+
observation, relayed as it answered; nothing is recomputed on this side,
|
|
13
|
+
and `repo` is the host's path. Each is always present, `null` from a host
|
|
14
|
+
that doesn't report it (an older host, a fact it never recorded, or a saved
|
|
15
|
+
route the host no longer lists). See
|
|
16
|
+
[the remote roster](../desktop-cli-api.md#the-remote-roster-oats-server-roster---json).
|
|
17
|
+
|
|
18
|
+
- **The `worktree` and `checkout` briefings allow extra trees, and give the
|
|
19
|
+
command** ([#674](https://github.com/awebai/oats/issues/674)). Both
|
|
20
|
+
briefings forbade extra Git worktrees: `worktree` said "Don't create extra
|
|
21
|
+
worktrees; `work/` is your one tree", and `checkout` said to ask for a
|
|
22
|
+
worktree-mode instance for work that needs its own branch. That
|
|
23
|
+
contradicted the shipped `oats.engineering` package, whose developer
|
|
24
|
+
briefing and `/worktrees` skill say "your work-mode briefing has the
|
|
25
|
+
command", with `.work-<purpose>` in the home as the default place. No
|
|
26
|
+
briefing gave that command. The case that needs it is a developer whose
|
|
27
|
+
one task touches several repositories of the deployment.
|
|
28
|
+
|
|
29
|
+
Both briefings now carry the same short paragraph. An extra tree is created
|
|
30
|
+
in the home, from any clone of the deployment (`oats-local.yaml` `clones:`,
|
|
31
|
+
or `<deployment>/<repo>`), from the remote's current state:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git -C <clone> worktree add --detach "$OATS_INSTANCE_HOME/.work-<purpose>"
|
|
35
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" fetch --refmap= origin <base>
|
|
36
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" switch -c <branch> FETCH_HEAD
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The fetch runs inside the new tree, which has its own `FETCH_HEAD`, so
|
|
40
|
+
creating it moves none of the clone's refs. The paragraph also covers
|
|
41
|
+
branch names (the repository's rules, else `agents/<instance>-<purpose>`;
|
|
42
|
+
never `-C` or `-B`), pushing a tree without an upstream
|
|
43
|
+
(`git push origin HEAD:<remote-branch>`), and closing: merge each tree into
|
|
44
|
+
the PR branch or push it and name it in the hand-back, then
|
|
45
|
+
`git -C <clone> worktree remove`. `git switch` needs Git 2.23 or later.
|
|
46
|
+
|
|
47
|
+
`worktree` mode now says never to work in a shared checkout (the main
|
|
48
|
+
checkout, or any clone others use), and that changes happen in `work/` or
|
|
49
|
+
in the extra trees. It drops "Parallel work means your human spawns
|
|
50
|
+
another instance". `checkout` mode now says that work needing its own
|
|
51
|
+
branch goes in an extra tree, not in `work/`. The `workspace`, `directory`
|
|
52
|
+
and `attached` briefings do not change, and there is no new `oats`
|
|
53
|
+
command. The instance-boundary block every instance receives said repository
|
|
54
|
+
work happens in `work/` "and only there"; it now says "there, or in the
|
|
55
|
+
extra trees your mode block grants, and nowhere else", so the two blocks
|
|
56
|
+
no longer disagree. Instances spawned before the upgrade keep the briefing
|
|
57
|
+
they were spawned with. See
|
|
58
|
+
[souls and instances](../souls-and-instances.md#extra-trees).
|
|
59
|
+
|
|
60
|
+
- **Retire protects extra trees in the home**
|
|
61
|
+
([#674](https://github.com/awebai/oats/issues/674)). An extra tree is a
|
|
62
|
+
top-level `.work-*` entry of the home that Git confirms is a linked
|
|
63
|
+
worktree of some repository. Before, retire treated it as home bytes: it
|
|
64
|
+
copied it to recovery and removed it with the home, leaving the
|
|
65
|
+
repository's admin entry behind. Now, in every work mode:
|
|
66
|
+
|
|
67
|
+
- A verified extra tree is not copied to recovery. `workRecovery.notCopied`
|
|
68
|
+
lists it with `owner: "kernel:extra-worktree"`. A `.work-*` entry that
|
|
69
|
+
does not verify (a plain directory, a symbolic link, a full clone), or
|
|
70
|
+
that belongs to a repository inside the home, is copied as before.
|
|
71
|
+
- When the home is removed, the retire handles each tree after its hooks
|
|
72
|
+
and before the `work/` worktree step. A clean tree (empty status,
|
|
73
|
+
ignored and untracked files included; no operation in progress; HEAD
|
|
74
|
+
reached by a ref of its repository, not only by the tree's own) is
|
|
75
|
+
removed with `git worktree remove` and pruned. Its branch is kept. Any
|
|
76
|
+
other tree is moved, as `work/` is retained, to
|
|
77
|
+
`<deployment>/.agents/worktrees/<repo>/<branch>`.
|
|
78
|
+
- A locked tree refuses the retire with `E_WORK_PRESERVATION_FAILED`
|
|
79
|
+
before anything runs: no session is stopped and no retire hook runs. A
|
|
80
|
+
move or removal Git refuses refuses it after the hooks. Either way the
|
|
81
|
+
home is kept. `--force` does not bypass it, and `--discard-worktree` does
|
|
82
|
+
not apply to extra trees.
|
|
83
|
+
- With `--keep-dir`, or when the home is kept for a retry, the trees stay
|
|
84
|
+
where they are.
|
|
85
|
+
|
|
86
|
+
`oats retire <instance> --plan` adds `facts.extraWorktrees` (`[{path, repo,
|
|
87
|
+
branch, detachedAt, disposition, movedTo, reason}]`, empty when there are
|
|
88
|
+
none) and one `notes` string per tree. The trees are part of
|
|
89
|
+
`planRevision`: a tree created, removed, dirtied or cleaned between the plan
|
|
90
|
+
and the apply refuses the apply with `E_PLAN_STALE`. The retire receipt adds
|
|
91
|
+
`extraWorktrees` (the plan's row plus `outcome`), present when a tree was
|
|
92
|
+
handled. The `worktree-removed` and `worktree-retained` events for an extra
|
|
93
|
+
tree carry `extra: true` and its `path`. All keys are additive. See
|
|
94
|
+
[souls and instances](../souls-and-instances.md#extra-trees-at-retire) and
|
|
95
|
+
[the CLI API](../desktop-cli-api.md#retire).
|
|
96
|
+
|
|
97
|
+
## Fixed
|
|
98
|
+
|
|
99
|
+
- **The context panel shows the soul and teams of an instance on a server**
|
|
100
|
+
([#675](https://github.com/awebai/oats/issues/675)). The Soul tab and the
|
|
101
|
+
Messaging & Teams list were empty for an instance on a registered server.
|
|
102
|
+
They now read through the server by the instance's home, as the soul page
|
|
103
|
+
does, and say "Reading from <server>…" while they wait. The Soul tab's
|
|
104
|
+
header shows the soul's description from the server's roster. The Work
|
|
105
|
+
card, the "model from" line and the "older build" chip show the facts the
|
|
106
|
+
server relays. When a part can't be shown, it says why and what to update:
|
|
107
|
+
this computer's OATS when it can't route the read or doesn't relay the
|
|
108
|
+
facts, the server's OATS when it lacks the feature or doesn't report them.
|
|
109
|
+
A server's other refusals show its code and message, as the rest of the
|
|
110
|
+
panel does.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# OATS 0.43.0
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **A one-line summary for every schedule and trigger** (feature
|
|
6
|
+
`automation-descriptions`). One rule covers every kind and level. A
|
|
7
|
+
`description` is one line of 1 to 200 characters with no control characters
|
|
8
|
+
(no `\p{Cc}`, U+2028 or U+2029). Before this, only local schedules could
|
|
9
|
+
carry one.
|
|
10
|
+
- **Local triggers take `description`.** Out of the rule it is refused as
|
|
11
|
+
`E_TRIGGER_INVALID {field: "description"}`. `oats trigger list` and `show`
|
|
12
|
+
used to send `null` for every local trigger; they now show it.
|
|
13
|
+
- **Package trigger templates may carry `definition.description`.** It is
|
|
14
|
+
validated like any trigger's. `oats trigger add --from` copies it, and
|
|
15
|
+
`--description=<text>` overrides it. A workspace trigger made `from:` a
|
|
16
|
+
template shows its file header's description, or the template's when the
|
|
17
|
+
header has none. A kernel before 0.43.0 refuses the key, so a package
|
|
18
|
+
whose template sets it needs `compatibility.oats: ">=0.43.0"`.
|
|
19
|
+
- **`--description=<text>` on `oats schedule add` and `oats trigger add`**
|
|
20
|
+
sets the description, or overrides the one in the spec. `--description=`
|
|
21
|
+
with an empty value leaves it out. With `--workspace`, the description goes
|
|
22
|
+
to the file header, never into the body.
|
|
23
|
+
- **Change only the description:**
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
oats schedule update <id> --description=<text> # without --file/--spec-json
|
|
27
|
+
oats trigger update <id> --description=<text>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`--description=` clears it. Both work on local schedules and triggers
|
|
31
|
+
only; a workspace one is changed in Git (`E_AUTOMATION_WORKSPACE`). A
|
|
32
|
+
schedule's description may change while the job runs or has an unresolved
|
|
33
|
+
attempt, and the update runs under the same locks as any other. A
|
|
34
|
+
trigger's fired and pending events are untouched. `oats trigger update`
|
|
35
|
+
takes no other flag for now (`E_BAD_ARGS`): a full trigger update is still
|
|
36
|
+
a remove and an add. `oats schedule … --server <id>` with `--description`
|
|
37
|
+
needs the destination to advertise `automation-descriptions`
|
|
38
|
+
(`E_REMOTE_INCOMPATIBLE` otherwise).
|
|
39
|
+
|
|
40
|
+
The Desktop gates its summary writes on the feature. See
|
|
41
|
+
[schedules](../schedules.md#kinds) and the
|
|
42
|
+
[Desktop CLI API](../desktop-cli-api.md#automations-shared-rows).
|
|
43
|
+
|
|
44
|
+
### Desktop
|
|
45
|
+
|
|
46
|
+
- **Schedules and Triggers show each item's summary.** A row shows the item's
|
|
47
|
+
name beside its origin tag (`local`, or the member), in place of
|
|
48
|
+
`local/<id>`, and its summary under it. Without a summary, the row shows the
|
|
49
|
+
first non-empty line of the task or wake message in muted italics, titled "No
|
|
50
|
+
summary set — first line of the prompt". A command or operation row without
|
|
51
|
+
one shows "No summary": the Desktop never makes a label from argv.
|
|
52
|
+
- **A detail page shows everything an item does**
|
|
53
|
+
([#545](https://github.com/awebai/oats/issues/545)). The title is the name,
|
|
54
|
+
with the qualified id under it, and then the summary or "No summary".
|
|
55
|
+
- **What it sends, in full:** a spawn's or trigger's task; a wake's whole
|
|
56
|
+
message and the home it wakes (before, a wake said "No prompt"); a
|
|
57
|
+
command's argv exactly as written, one argument per chip, and its working
|
|
58
|
+
directory; an operation and the home it runs in.
|
|
59
|
+
- **Spawns** also shows a spawn schedule's purpose (never shown before),
|
|
60
|
+
harness and model, launch configuration, permissions, session backend, and
|
|
61
|
+
the spawn's own recurring wake.
|
|
62
|
+
- **Run state** shows a schedule that is running and since when. It also
|
|
63
|
+
shows a run whose state is unknown and since when, with its exit facts,
|
|
64
|
+
whether it still holds a host slot, *Check run state* and the
|
|
65
|
+
`oats schedule reconcile <id> [--clear]` command to paste. A held job lock
|
|
66
|
+
no longer hides an unknown run. It also shows a wake waiting to be delivered.
|
|
67
|
+
- **An Invalid or Unreadable item** gets a card with its code, field and
|
|
68
|
+
message. Before, they appeared only in a tag's tooltip.
|
|
69
|
+
- **Comes from** adds the package template an item was made from (package,
|
|
70
|
+
version, commit) and a local item's created and updated times.
|
|
71
|
+
- **Set a summary from the Desktop** (needs a CLI with
|
|
72
|
+
`automation-descriptions`). The schedule form has an optional **Summary**
|
|
73
|
+
field. **Edit summary** sets or clears the summary of any local schedule
|
|
74
|
+
(command ones included) or local trigger, from the detail page or the row
|
|
75
|
+
menu; it runs `oats <kind> update <id> --description=<text>`. The text is
|
|
76
|
+
saved as typed, and an empty value removes it. A workspace item's summary is
|
|
77
|
+
its file's `description:` in Git. With an older CLI, the Desktop shows the
|
|
78
|
+
summaries it reports and the form keeps a stored summary unchanged.
|
|
79
|
+
|
|
80
|
+
## Changed
|
|
81
|
+
|
|
82
|
+
- **A workspace file header whose `description` breaks the rule is a warning,
|
|
83
|
+
not a refusal.** Before, a non-string header description stopped the entry
|
|
84
|
+
(`E_AUTOMATION_SCHEMA`), and any other value was accepted unchecked. Now the
|
|
85
|
+
entry still loads and runs, with `description: null`, and the snapshot
|
|
86
|
+
reports an `E_AUTOMATION_SCHEMA` problem at `<path>#/description`:
|
|
87
|
+
"description: one line of 1 to 200 characters without control characters —
|
|
88
|
+
shorten it or remove it; the automation keeps running without one". A label
|
|
89
|
+
never stops a job. `oats trigger|schedule add --workspace` refuses an
|
|
90
|
+
out-of-rule description before it writes the file.
|
|
91
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.44.0`**, so it runs against
|
|
92
|
+
this release's kernel; the Desktop 0.42.x refuses a 0.43 CLI. Upgrade the
|
|
93
|
+
Desktop and the CLI together.
|
package/docs/schedules.md
CHANGED
|
@@ -52,14 +52,21 @@ both are evaluated by the croner library. Schedule IDs use lowercase letters,
|
|
|
52
52
|
digits and dashes, from 1 to 100 characters. Spawn schedules with a long ID
|
|
53
53
|
need an explicit shorter `purpose` to fit the instance-name limit below.
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
people reading `oats schedule list`, `
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
Every schedule and trigger, of any kind, may carry `description`: what it is
|
|
56
|
+
for, in words, for the people reading `oats schedule list`, `oats trigger
|
|
57
|
+
list`, `show` and the Desktop. It is one line of 1 to 200 characters with no
|
|
58
|
+
control characters (no CR, LF, TAB or any other C0 or C1 character, nor a
|
|
59
|
+
Unicode line or paragraph separator). A local schedule or trigger that breaks
|
|
60
|
+
the rule is refused, `E_SCHEDULE_INVALID` or `E_TRIGGER_INVALID` with `field:
|
|
61
|
+
"description"`; a workspace file's header that breaks it is only a warning
|
|
62
|
+
(see [the header](#workspace-triggers-and-schedules)). It is stored as given
|
|
63
|
+
and is informational only: it never reaches a run's argv, environment, task,
|
|
64
|
+
template or reconcile. A capability that registers jobs (knowledge harvest's
|
|
65
|
+
`run-source` jobs) sets it so that its command jobs can be told apart. Set it
|
|
66
|
+
in the definition or with `--description=<text>` on `add`; change only it with
|
|
67
|
+
`update <id> --description=<text>` (see [Commands](#commands)). Local triggers
|
|
68
|
+
take it from OATS 0.43.0 (feature `automation-descriptions`); an older kernel
|
|
69
|
+
refuses the key on a trigger.
|
|
63
70
|
|
|
64
71
|
- **spawn** `{…, agent, agentsRoot?, repo?, backend?, purpose?, task,
|
|
65
72
|
launchConfig?, harness?, model?, yolo?, wake?}` — every due minute launches
|
|
@@ -131,6 +138,7 @@ when the timer cannot reach it.
|
|
|
131
138
|
|
|
132
139
|
```json
|
|
133
140
|
{ "id": "okf-harvest-review", "enabled": true, "kind": "trigger",
|
|
141
|
+
"description": "Review every harvest PR on the knowledge base",
|
|
134
142
|
"on": { "source": "github.pull_request", "repo": "github.com/acme/knowledge",
|
|
135
143
|
"events": ["opened", "reopened", "ready_for_review"],
|
|
136
144
|
"labels": ["okf-harvest"], "base": "main", "poll": "2m" },
|
|
@@ -178,15 +186,19 @@ when the timer cannot reach it.
|
|
|
178
186
|
harness, and is recorded in `instance.json.trigger`.
|
|
179
187
|
|
|
180
188
|
```sh
|
|
181
|
-
oats trigger add --file trigger.json
|
|
182
|
-
oats trigger add --from oats.okf:harvest-review --set repo=github.com/acme/knowledge [--id <id>]
|
|
189
|
+
oats trigger add --file trigger.json [--description=<text>] # or:
|
|
190
|
+
oats trigger add --from oats.okf:harvest-review --set repo=github.com/acme/knowledge [--id <id>] [--description=<text>]
|
|
191
|
+
oats trigger update <id> --description=<text> # the description only; --description= clears it
|
|
183
192
|
oats trigger list | show <id> | enable <id> | disable <id> | remove <id>
|
|
184
193
|
oats trigger test <id> # dry run: gh credentials, repo permissions, the soul, what WOULD fire
|
|
185
194
|
oats trigger status [<id>] # last poll, next due, pending and fired events, live vs max, last error
|
|
186
195
|
```
|
|
187
196
|
|
|
188
197
|
All take `--dir` and `--json` (`triggerApi: 1`). `remove` leaves the instances
|
|
189
|
-
it spawned running. `
|
|
198
|
+
it spawned running. `update` changes only a local trigger's description (any
|
|
199
|
+
other flag is `E_BAD_ARGS`: a full trigger update is a remove and an add); it
|
|
200
|
+
sets `updatedAt` and leaves the trigger's fired and pending events as they
|
|
201
|
+
are. A workspace trigger is changed in Git (`E_AUTOMATION_WORKSPACE`). `oats schedule list` does not list triggers but counts
|
|
190
202
|
them (`triggers: { count, command: "oats trigger list" }`, and a line in text
|
|
191
203
|
mode). Errors: `E_TRIGGER_INVALID { field }`, `E_TRIGGER_EXISTS`,
|
|
192
204
|
`E_TRIGGER_UNKNOWN`, `E_TRIGGER_TEAMS`, `E_BAD_ARGS`.
|
|
@@ -196,8 +208,10 @@ in `oats-package.json`, each file `{ parameters: { <name>: { path, required?,
|
|
|
196
208
|
default?, description? } }, definition }`. `oats trigger add --from
|
|
197
209
|
<package>:<id>` reads it at the locked commit, and `--set <name>=<value>`
|
|
198
210
|
fills a parameter at its dotted `path` (a list value is comma-separated; a
|
|
199
|
-
missing required one is `E_BAD_ARGS { missing }`).
|
|
200
|
-
|
|
211
|
+
missing required one is `E_BAD_ARGS { missing }`). A template's `definition`
|
|
212
|
+
may carry `description` (validated like any trigger's): `add --from` copies
|
|
213
|
+
it, and `--description=<text>` overrides it (`--description=` leaves it out).
|
|
214
|
+
See [packages.md](packages.md#trigger-templates).
|
|
201
215
|
|
|
202
216
|
## Workspace triggers and schedules
|
|
203
217
|
|
|
@@ -246,6 +260,15 @@ owner: github.com/ana
|
|
|
246
260
|
`runsOn` and `owner`, and optionally `id`, `description` and `enabled`. A
|
|
247
261
|
candidate of the wrong kind (a schedule in `oats-triggers/`) or without one
|
|
248
262
|
is an `E_AUTOMATION_SCHEMA` problem, never silently skipped.
|
|
263
|
+
- **The description** follows the [one rule](#kinds), but a header that
|
|
264
|
+
breaks it does not stop the job: the entry still loads and runs as if it
|
|
265
|
+
had none (`description: null`), and the snapshot reports an `E_AUTOMATION_SCHEMA`
|
|
266
|
+
problem at `<path>#/description` ("description: one line of 1 to 200
|
|
267
|
+
characters without control characters — shorten it or remove it; the
|
|
268
|
+
automation keeps running without one"). A trigger made `from:` a package
|
|
269
|
+
template shows the header's description, else the template's. It lives in
|
|
270
|
+
the header only: `add --workspace` writes the flag's (else the spec's) there,
|
|
271
|
+
never into the body.
|
|
249
272
|
- **The id** is `id:`, else the filename stem. The same id twice in one member
|
|
250
273
|
for one kind is `E_AUTOMATION_DUPLICATE`, naming both paths; the second file
|
|
251
274
|
is not listed. Schedule IDs allow 1 to 100 lowercase letters, digits and dashes; trigger
|
|
@@ -312,8 +335,9 @@ check the placement and everything else on this host.
|
|
|
312
335
|
## Commands
|
|
313
336
|
|
|
314
337
|
```sh
|
|
315
|
-
oats schedule add <id> --file spec.json [--dir <deployment>] [--json]
|
|
316
|
-
oats schedule update <id> --file spec.json
|
|
338
|
+
oats schedule add <id> --file spec.json [--description=<text>] [--dir <deployment>] [--json]
|
|
339
|
+
oats schedule update <id> --file spec.json [--description=<text>]
|
|
340
|
+
oats schedule update <id> --description=<text> # the description only; --description= clears it
|
|
317
341
|
oats schedule list | show <id> | enable <id> | disable <id> | remove <id> [--force]
|
|
318
342
|
oats schedule run <id> [--force] # now, under the same lock and bound
|
|
319
343
|
oats schedule test <id> # dry run: where it runs, whether its soul resolves, when it is next due
|
|
@@ -326,6 +350,16 @@ oats schedule host status | uninstall
|
|
|
326
350
|
oats spawn <agent> ... --wake-every 15 --wake-message "Anything new?" # or --wake-file spec.json
|
|
327
351
|
```
|
|
328
352
|
|
|
353
|
+
`--description=<text>` sets the spec's `description`, or overrides it;
|
|
354
|
+
`--description=` (empty) removes it. Take the `=` form: it is the one that
|
|
355
|
+
carries an empty value, or one that starts with `-`. With `--file` or
|
|
356
|
+
`--spec-json`, `update` replaces the whole definition as before (one without a
|
|
357
|
+
description drops it). Without them, `update <id> --description=<text>`
|
|
358
|
+
changes only the description of a local schedule, under the same locks, and
|
|
359
|
+
is allowed while the job runs or has an unresolved attempt. With `--server`,
|
|
360
|
+
the destination must advertise `automation-descriptions`
|
|
361
|
+
(`E_REMOTE_INCOMPATIBLE` otherwise, before anything is forwarded).
|
|
362
|
+
|
|
329
363
|
`<id>` is `local/<id>` (or the bare id) or `<member>/<id>`. `host uninstall`
|
|
330
364
|
unregisters the deployment and removes the timer once none is registered.
|
|
331
365
|
Every `oats schedule` subcommand takes `--server <id>` instead of `--dir` to
|
package/docs/servers.md
CHANGED
|
@@ -300,7 +300,10 @@ before 0.36.0, `null` when it reports none or the probe failed), the remote soul
|
|
|
300
300
|
the host's own facts from its `status --json`: `identity`,
|
|
301
301
|
`identityAddress`, `teams`, `startedAt`, `createdAt`, `model`,
|
|
302
302
|
`runtimeState`, `parentInstance`, `siblingInstance`, `relation`,
|
|
303
|
-
`relativeTo
|
|
303
|
+
`relativeTo`, `spawnOrigin`, and (0.42.1) `work`, `repo`, `branch`,
|
|
304
|
+
`modelFrom`, `soul` and `modules`: the local row's facts, with `soul` and
|
|
305
|
+
`modules` the host's own drift observation, never recomputed here, and
|
|
306
|
+
`repo` a path on the host. A fact the host does not supply is `null`
|
|
304
307
|
(an older host, or a saved route the host no longer lists). A row also
|
|
305
308
|
carries `waitingOnYou` (needs input) when the host's kernel reports it, and
|
|
306
309
|
only then: an older host's row has no such key. A removed or edited registration keeps
|
|
@@ -809,6 +809,76 @@ commit yet cannot be read that way: a retire that would remove it, or that
|
|
|
809
809
|
has work of it to copy, refuses with `E_WORK_INSPECTION_FAILED` and removes
|
|
810
810
|
nothing.
|
|
811
811
|
|
|
812
|
+
#### Extra trees at retire
|
|
813
|
+
|
|
814
|
+
An instance can hold [extra trees](#extra-trees) in its home beside `work/`.
|
|
815
|
+
Retire handles them itself, so the home removal never deletes their work.
|
|
816
|
+
|
|
817
|
+
An **extra tree** is a top-level entry of the home named `.work-*` that is a
|
|
818
|
+
real directory (not a symbolic link), whose `.git` is a regular file, and that
|
|
819
|
+
Git confirms is a registered linked worktree of some repository: its Git
|
|
820
|
+
directory differs from its common directory, its top level is the entry
|
|
821
|
+
itself, and that repository's `git worktree list` names it. Its repository is
|
|
822
|
+
the first entry of that list (the main worktree, or the bare repository).
|
|
823
|
+
Anything named `.work-*` that does not verify (a plain directory, an orphaned
|
|
824
|
+
`.git` file, a symbolic link, a nested full clone), or that is a worktree of a
|
|
825
|
+
repository inside the home itself, is ordinary home bytes, and the home's
|
|
826
|
+
recovery copies it as before (with that repository). This holds in every work mode,
|
|
827
|
+
`directory` included.
|
|
828
|
+
|
|
829
|
+
A verified extra tree is not part of the home's recovery bytes: it does not
|
|
830
|
+
count as changed instance-home bytes, it is not copied, and the recovery's
|
|
831
|
+
`notCopied` lists it as `{scope: "home", path: ".work-<purpose>", owner:
|
|
832
|
+
"kernel:extra-worktree"}`.
|
|
833
|
+
|
|
834
|
+
The extra-tree step runs only when the home is going to be removed: not with
|
|
835
|
+
`--keep-dir`, and not when the retire keeps the home for a retry. That is the
|
|
836
|
+
condition of the `work/` worktree step (nothing outstanding, or `--force`).
|
|
837
|
+
It runs after the retire hooks and before the `work/` step, so a refusal
|
|
838
|
+
leaves `work/` untouched. For each tree:
|
|
839
|
+
|
|
840
|
+
- **A clean tree is removed.** Clean means that `git status`, ignored and
|
|
841
|
+
untracked files included, is empty; no merge, rebase, cherry-pick, revert,
|
|
842
|
+
bisect or sequencer operation is in progress; and its HEAD commit is
|
|
843
|
+
reached by a ref of its repository (the tree's own HEAD, reflog and
|
|
844
|
+
`refs/worktree/` refs do not count: they go with its admin entry). The retire runs `git worktree remove` (without
|
|
845
|
+
`--force`) and `git worktree prune`, and verifies that the tree is gone from
|
|
846
|
+
`git worktree list`. Every Git command of this step runs helper-free (no
|
|
847
|
+
fsmonitor, hooks or external diff the repository's configuration names). Its branch is never deleted: a commit on the branch that
|
|
848
|
+
was not pushed stays in the clone, on that branch.
|
|
849
|
+
- **Any other tree is re-homed**, as `work/` is by default: `git worktree
|
|
850
|
+
move` to `<deployment>/.agents/worktrees/<repo>/<leaf>`, where `<leaf>` is
|
|
851
|
+
the branch name with characters outside `A-Za-z0-9._-` replaced by `-`, or
|
|
852
|
+
`detached-<12 hex>` when HEAD is detached. A target that exists gets `-2`,
|
|
853
|
+
`-3`, and so on. A tree whose HEAD cannot be read is re-homed too.
|
|
854
|
+
- **A tree the retire cannot handle refuses it.** A locked tree (`git worktree
|
|
855
|
+
lock`) refuses before anything runs: a retire that would remove the home
|
|
856
|
+
finds the lock in its first inspection, before the session is stopped and
|
|
857
|
+
before any retire hook, and stops with "nothing was run or removed". A lock
|
|
858
|
+
that appears while the hooks run is refused at the step, before any tree is
|
|
859
|
+
touched. A move or a removal that Git refuses (a tree with submodules, for
|
|
860
|
+
example), or a removal that cannot be verified, refuses at the step, after
|
|
861
|
+
the hooks. Each refusal is `E_WORK_PRESERVATION_FAILED` naming the tree,
|
|
862
|
+
and the home is kept. At the step, trees already handled in that pass stay
|
|
863
|
+
handled, and the message says what was done. `--force` does not bypass it:
|
|
864
|
+
it forces past hook cleanup, not past local work.
|
|
865
|
+
|
|
866
|
+
`--discard-worktree` applies to `work/` only: a tree that is not clean is
|
|
867
|
+
always re-homed. The retire writes one workspace event per handled tree
|
|
868
|
+
(`worktree-removed` or `worktree-retained`, with `extra: true` and the tree's
|
|
869
|
+
`path`), and its summary prints one line per tree: removed, or re-homed to
|
|
870
|
+
the new path.
|
|
871
|
+
|
|
872
|
+
`oats retire <instance> --plan` lists the trees and what the retire would do
|
|
873
|
+
with each, and they are part of the plan's revision: a tree created, removed,
|
|
874
|
+
dirtied or cleaned between the plan and a guarded apply, or a new target for
|
|
875
|
+
it, refuses the apply with `E_PLAN_STALE` before anything runs. The retire
|
|
876
|
+
checks the trees again at the step itself and refuses with `E_PLAN_STALE`,
|
|
877
|
+
keeping the home, rather than move or remove a tree in a way the plan did
|
|
878
|
+
not say. The retire hooks have run by then, and no tree was moved or
|
|
879
|
+
removed. The fields are in
|
|
880
|
+
[the CLI API](desktop-cli-api.md#retire).
|
|
881
|
+
|
|
812
882
|
## Work modes
|
|
813
883
|
|
|
814
884
|
A work mode decides what `./work` points at and what discipline the agent must
|
|
@@ -824,7 +894,8 @@ instructions state first (`injects/instance-boundary.md`):
|
|
|
824
894
|
target another one deliberately).
|
|
825
895
|
- `<instance-home>/work` — the repository or workspace view — is where
|
|
826
896
|
repository reading, editing, building, testing, git and commits happen, to the
|
|
827
|
-
extent the mode below permits.
|
|
897
|
+
extent the mode below permits. In `worktree` and `checkout` mode, the
|
|
898
|
+
instance's [extra trees](#extra-trees) in the home serve the same purpose.
|
|
828
899
|
- The home has no soul link: the composed `AGENTS.md` already carries the
|
|
829
900
|
soul's instructions, and `instance.json` `soulDir` records the (read-only,
|
|
830
901
|
per-commit) soul directory every hook and dispatched command receives as
|
|
@@ -863,10 +934,12 @@ Use this for agents that will edit code or docs independently.
|
|
|
863
934
|
Rules:
|
|
864
935
|
|
|
865
936
|
- Build, test, and commit from `work/`, on your own branch.
|
|
866
|
-
- Never
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
937
|
+
- Never work in a shared checkout (the repo's main checkout, or any clone
|
|
938
|
+
others use): do not edit, commit or switch branches there. Against a clone,
|
|
939
|
+
run only `git worktree add` and `git worktree remove`, as in
|
|
940
|
+
[extra trees](#extra-trees).
|
|
941
|
+
- Everything you change happens in `work/` or in your extra trees.
|
|
942
|
+
- Leave your branch and the worktree list clean when your task closes.
|
|
870
943
|
|
|
871
944
|
### `checkout` — shared current branch
|
|
872
945
|
|
|
@@ -880,7 +953,10 @@ Rules:
|
|
|
880
953
|
|
|
881
954
|
- Stay on the currently checked-out branch.
|
|
882
955
|
- Do not switch branches unless explicitly asked.
|
|
883
|
-
-
|
|
956
|
+
- No destructive git operations (`reset --hard`, rebase, force-push, checkout
|
|
957
|
+
of another branch) unless the human explicitly asks.
|
|
958
|
+
- Work that needs its own branch goes in an [extra tree](#extra-trees), not
|
|
959
|
+
in `work/`.
|
|
884
960
|
|
|
885
961
|
### `attached` — another instance's tree
|
|
886
962
|
|
|
@@ -935,6 +1011,50 @@ Rules:
|
|
|
935
1011
|
|
|
936
1012
|
The instance records no branch: the workspace is not a Git tree.
|
|
937
1013
|
|
|
1014
|
+
### Extra trees
|
|
1015
|
+
|
|
1016
|
+
An instance in `worktree` or `checkout` mode can create extra trees: linked
|
|
1017
|
+
Git worktrees in its home, beside `work/`. It does so when the work needs
|
|
1018
|
+
another branch, or another repository of the deployment (one task that
|
|
1019
|
+
touches several repositories). The `worktree` and `checkout` briefings give
|
|
1020
|
+
the command; the `workspace`, `directory` and `attached` briefings do not.
|
|
1021
|
+
There is no `oats` command for it.
|
|
1022
|
+
|
|
1023
|
+
`<clone>` is any clone of the deployment (`oats-local.yaml` `clones:`, or
|
|
1024
|
+
`<deployment>/<repo>`), `origin` is its remote for that repository, and
|
|
1025
|
+
`<base>` is the remote branch the work starts from (the branch itself, to
|
|
1026
|
+
rework an existing one):
|
|
1027
|
+
|
|
1028
|
+
```bash
|
|
1029
|
+
git -C <clone> worktree add --detach "$OATS_INSTANCE_HOME/.work-<purpose>"
|
|
1030
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" fetch --refmap= origin <base>
|
|
1031
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" switch -c <branch> FETCH_HEAD
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
- The tree starts from the remote's current state, never from a local branch
|
|
1035
|
+
of the clone, which may be stale.
|
|
1036
|
+
- Creating it moves none of the clone's refs. The fetch runs inside the new
|
|
1037
|
+
linked tree, which has its own `FETCH_HEAD`, and `--refmap=` keeps it from
|
|
1038
|
+
updating remote-tracking refs. The clone's `FETCH_HEAD`, branches and work
|
|
1039
|
+
tree are not touched, so creation does not race with others who use the
|
|
1040
|
+
clone. The fetched objects go to the repository's shared object store.
|
|
1041
|
+
- `git switch` needs Git 2.23 or later.
|
|
1042
|
+
- `<branch>` follows the repository's own naming rules, else
|
|
1043
|
+
`agents/<instance>-<purpose>`. If that branch already exists in the clone,
|
|
1044
|
+
`switch -c` refuses: use `<instance>/<branch>`. Never `-C` or `-B`, which
|
|
1045
|
+
reset a branch someone else may own.
|
|
1046
|
+
- The tree has no upstream. Push with `git push origin HEAD:<remote-branch>`
|
|
1047
|
+
(`<base>` when reworking an existing branch). A push does update the
|
|
1048
|
+
clone's `refs/remotes/origin/<remote-branch>`, as any push does.
|
|
1049
|
+
- Before the task closes, merge each extra tree into the PR branch, or push
|
|
1050
|
+
its branch and name it in the hand-back; then
|
|
1051
|
+
`git -C <clone> worktree remove "$OATS_INSTANCE_HOME/.work-<purpose>"`.
|
|
1052
|
+
|
|
1053
|
+
`$OATS_INSTANCE_HOME` is set in every harness session OATS launches (Claude
|
|
1054
|
+
Code, Codex, pi), not only in hooks. Retirement removes a clean extra tree
|
|
1055
|
+
and re-homes one that holds work, but the briefing tells the agent not to
|
|
1056
|
+
rely on it: see [extra trees at retire](#extra-trees-at-retire).
|
|
1057
|
+
|
|
938
1058
|
## Agents root
|
|
939
1059
|
|
|
940
1060
|
Instance homes live under the deployment's agents root,
|
|
@@ -26,9 +26,10 @@ root, and not the work tree. Anything that says "your home" means this directory
|
|
|
26
26
|
**`<instance-home>/work` is your repository or workspace view** — whatever your
|
|
27
27
|
work mode grants you of the code.
|
|
28
28
|
|
|
29
|
-
- **Repository work happens there
|
|
30
|
-
|
|
31
|
-
or from your
|
|
29
|
+
- **Repository work happens there, or in the extra trees your mode block
|
|
30
|
+
grants, and nowhere else**: reading, editing, building, testing, git and
|
|
31
|
+
commits, on repository content. Never from the main checkout or from your
|
|
32
|
+
home root, beyond what your mode block names.
|
|
32
33
|
- **What your mode permits is the mode block's call**, immediately below. Some
|
|
33
34
|
modes are read-only, some share a tree with others, and that block is the
|
|
34
35
|
authority on which operations are yours to perform.
|
package/injects/work-checkout.md
CHANGED
|
@@ -7,6 +7,28 @@ in the same tree as the human and possibly other agents.
|
|
|
7
7
|
explicitly asked.**
|
|
8
8
|
- No destructive git operations (reset --hard, rebase, force-push, checkout
|
|
9
9
|
of another branch) without an explicit human instruction.
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
- Work that needs its own branch goes in an extra tree, not in `work/`.
|
|
11
|
+
|
|
12
|
+
### Extra trees
|
|
13
|
+
|
|
14
|
+
When the work needs another branch, or another repository of this deployment,
|
|
15
|
+
create an extra tree in your home. `<clone>` is any clone of this deployment
|
|
16
|
+
(`oats-local.yaml` `clones:`, or `<deployment>/<repo>`); `origin` is its remote
|
|
17
|
+
for that repository; `<base>` is the remote branch the work starts from (the
|
|
18
|
+
branch itself, when you rework an existing one):
|
|
19
|
+
|
|
20
|
+
git -C <clone> worktree add --detach "$OATS_INSTANCE_HOME/.work-<purpose>"
|
|
21
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" fetch --refmap= origin <base>
|
|
22
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" switch -c <branch> FETCH_HEAD
|
|
23
|
+
|
|
24
|
+
- This starts from the remote's current state and moves none of the clone's
|
|
25
|
+
refs. Never start from a local branch of the clone, which may be stale.
|
|
26
|
+
- Name `<branch>` by the repository's own rules, else `agents/<instance>-<purpose>`.
|
|
27
|
+
If it already exists in that clone, `switch -c` refuses: use `<instance>/<branch>`.
|
|
28
|
+
Never `-C`/`-B`, which reset a branch someone else may own.
|
|
29
|
+
- The tree has no upstream: push with `git push origin HEAD:<remote-branch>`
|
|
30
|
+
(`<base>` when you rework an existing branch).
|
|
31
|
+
- Before your task closes, merge each extra tree into your PR branch, or push its
|
|
32
|
+
branch and name it in your hand-back; then
|
|
33
|
+
`git -C <clone> worktree remove "$OATS_INSTANCE_HOME/.work-<purpose>"`.
|
|
34
|
+
Retirement keeps a tree that still holds uncommitted work, but don't rely on it.
|
package/injects/work-worktree.md
CHANGED
|
@@ -3,11 +3,34 @@
|
|
|
3
3
|
Your `./work` is a **git worktree on your own branch** — a full checkout that is
|
|
4
4
|
yours alone: build, test and commit there, on your branch.
|
|
5
5
|
|
|
6
|
-
- **Never
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
your human spawns another instance.
|
|
6
|
+
- **Never work in a shared checkout** (the repo's main checkout, or any clone
|
|
7
|
+
others use): don't edit, commit or switch branches there. Against a clone you
|
|
8
|
+
run only `worktree add`/`remove` below. If unsure, `pwd`.
|
|
9
|
+
- Everything you change happens in `work/` or your extra trees — **including your
|
|
10
|
+
own soul** when it lives in this repo: soul edits are branch changes, reviewed
|
|
11
|
+
and merged like code.
|
|
13
12
|
- Leave your branch and the worktree list clean when your task closes.
|
|
13
|
+
|
|
14
|
+
### Extra trees
|
|
15
|
+
|
|
16
|
+
When the work needs another branch, or another repository of this deployment,
|
|
17
|
+
create an extra tree in your home. `<clone>` is any clone of this deployment
|
|
18
|
+
(`oats-local.yaml` `clones:`, or `<deployment>/<repo>`); `origin` is its remote
|
|
19
|
+
for that repository; `<base>` is the remote branch the work starts from (the
|
|
20
|
+
branch itself, when you rework an existing one):
|
|
21
|
+
|
|
22
|
+
git -C <clone> worktree add --detach "$OATS_INSTANCE_HOME/.work-<purpose>"
|
|
23
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" fetch --refmap= origin <base>
|
|
24
|
+
git -C "$OATS_INSTANCE_HOME/.work-<purpose>" switch -c <branch> FETCH_HEAD
|
|
25
|
+
|
|
26
|
+
- This starts from the remote's current state and moves none of the clone's
|
|
27
|
+
refs. Never start from a local branch of the clone, which may be stale.
|
|
28
|
+
- Name `<branch>` by the repository's own rules, else `agents/<instance>-<purpose>`.
|
|
29
|
+
If it already exists in that clone, `switch -c` refuses: use `<instance>/<branch>`.
|
|
30
|
+
Never `-C`/`-B`, which reset a branch someone else may own.
|
|
31
|
+
- The tree has no upstream: push with `git push origin HEAD:<remote-branch>`
|
|
32
|
+
(`<base>` when you rework an existing branch).
|
|
33
|
+
- Before your task closes, merge each extra tree into your PR branch, or push its
|
|
34
|
+
branch and name it in your hand-back; then
|
|
35
|
+
`git -C <clone> worktree remove "$OATS_INSTANCE_HOME/.work-<purpose>"`.
|
|
36
|
+
Retirement keeps a tree that still holds uncommitted work, but don't rely on it.
|