ppkit 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (63) hide show
  1. ppkit-0.2.3/CHANGELOG.md +755 -0
  2. {ppkit-0.2.2 → ppkit-0.2.3}/CONTRIBUTING.md +55 -15
  3. {ppkit-0.2.2 → ppkit-0.2.3}/PKG-INFO +70 -16
  4. {ppkit-0.2.2 → ppkit-0.2.3}/README.md +69 -15
  5. {ppkit-0.2.2 → ppkit-0.2.3}/ci/check +2 -2
  6. ppkit-0.2.3/ci/suite +88 -0
  7. {ppkit-0.2.2 → ppkit-0.2.3}/pp/__init__.py +1 -1
  8. {ppkit-0.2.2 → ppkit-0.2.3}/pp/cli.py +6 -1
  9. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/http.py +802 -195
  10. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/kill.py +19 -3
  11. ppkit-0.2.3/pp/commands/memory.py +1023 -0
  12. ppkit-0.2.3/pp/commands/queue.py +621 -0
  13. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/config.py +9 -1
  14. ppkit-0.2.3/pp/lib/guarded.py +163 -0
  15. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/jobcore.py +123 -108
  16. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/jobpolicy.py +260 -83
  17. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/jobq.py +871 -220
  18. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/jobstore.py +98 -63
  19. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/lock.py +28 -11
  20. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/paths.py +38 -11
  21. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/proc.py +164 -46
  22. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/ui.py +4 -2
  23. ppkit-0.2.3/pp/lib/words.py +69 -0
  24. {ppkit-0.2.2 → ppkit-0.2.3}/pyproject.toml +1 -1
  25. {ppkit-0.2.2 → ppkit-0.2.3}/tests/journey.py +13 -50
  26. {ppkit-0.2.2 → ppkit-0.2.3}/tests/sandbox.py +64 -11
  27. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_cli.py +26 -93
  28. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_cmd.py +92 -5
  29. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_constructs.py +45 -23
  30. ppkit-0.2.3/tests/test_http.py +1357 -0
  31. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_batch.py +2 -26
  32. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_cli.py +33 -18
  33. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_cmd.py +22 -68
  34. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_http.py +81 -21
  35. ppkit-0.2.3/tests/test_journey_memory.py +180 -0
  36. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_promote.py +6 -24
  37. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_queue.py +339 -286
  38. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_kill.py +79 -79
  39. ppkit-0.2.3/tests/test_memory.py +671 -0
  40. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_paths.py +22 -0
  41. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_proc.py +44 -13
  42. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_promote.py +8 -1
  43. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_queue.py +1722 -337
  44. ppkit-0.2.2/CHANGELOG.md +0 -370
  45. ppkit-0.2.2/pp/commands/queue.py +0 -451
  46. ppkit-0.2.2/tests/test_http.py +0 -699
  47. {ppkit-0.2.2 → ppkit-0.2.3}/.pp.toml +0 -0
  48. {ppkit-0.2.2 → ppkit-0.2.3}/LICENSE +0 -0
  49. {ppkit-0.2.2 → ppkit-0.2.3}/bin/pp +0 -0
  50. {ppkit-0.2.2 → ppkit-0.2.3}/ci/package +0 -0
  51. {ppkit-0.2.2 → ppkit-0.2.3}/pp/__main__.py +0 -0
  52. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/__init__.py +0 -0
  53. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/batch.py +0 -0
  54. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/cmd.py +0 -0
  55. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/completion.py +0 -0
  56. {ppkit-0.2.2 → ppkit-0.2.3}/pp/commands/promote.py +0 -0
  57. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/__init__.py +0 -0
  58. {ppkit-0.2.2 → ppkit-0.2.3}/pp/lib/session.py +0 -0
  59. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_batch.py +0 -0
  60. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_journey_kill.py +0 -0
  61. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_lock.py +0 -0
  62. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_session.py +0 -0
  63. {ppkit-0.2.2 → ppkit-0.2.3}/tests/test_ui.py +0 -0
@@ -0,0 +1,755 @@
1
+ # Changelog
2
+
3
+ ## v0.2.3
4
+
5
+ `pp memory` is a new command for a memory that agents share: it ranks the
6
+ notes for a situation, follows and proposes links, drafts groups, checks the
7
+ index, and runs an agent's own edit while no other edit runs. `pp queue`
8
+ adds job keys, events, resizes, card pins and retries that take changes,
9
+ and its help is a short page with four topic pages. `pp http` serves under a
10
+ proxy's path, keeps pinned tabs, answers sooner and searches the tabs.
11
+ `pp kill` asks first when a number is also a job's id.
12
+
13
+ `pp memory` is a new command for a memory that agents share: a folder of
14
+ markdown notes, one fact per file, with `[[links]]` between them.
15
+
16
+ - `pp memory find WORDS` ranks the notes for a situation by BM25 over their
17
+ words. Links bring in the notes the words miss, through personalized
18
+ PageRank, each marked `via` the note it came through.
19
+ - A pointer, `§G:NAME`, is the link to write: one rare character, the
20
+ memory's scope (`[memory] scope`, `G` by default) and the note's name. A
21
+ pointer of another scope, such as `§pp:NAME` or a project's `§P:`, is
22
+ left alone. `check` reports a `§G:` pointer to no note as broken.
23
+ `[[NAME]]` and markdown links to `.md` files are links too.
24
+ - `pp memory related NAME` shows what a note links and what links it, the
25
+ notes most like it that it does not link, and the hubs that hold it or
26
+ might.
27
+ - `pp memory groups` drafts about √N groups for N notes, for the hub notes
28
+ that `MEMORY.md` lists. `--in HUB` drafts groups for one hub's notes.
29
+ - `pp memory check` lists links to no file, two notes with one name, notes
30
+ nothing links, notes the root does not reach in two steps, and pairs of
31
+ notes alike enough to compare. It prints the sizes against √N. Its exit
32
+ status is 1 only for a broken link or a name two notes share.
33
+ - `pp memory edit -- CMD` runs an agent's own command that changes notes,
34
+ such as `sed -i` or a python heredoc, under the memory's write lock, so
35
+ two edits at once take turns. The command finds the memory in
36
+ `PP_MEMORY_DIR`. The edit is the command's whole session: what the command
37
+ leaves running is ended with it. When the command fails, leaves something
38
+ running, runs past `--timeout` (a minute, `[memory] edit_timeout`) or is
39
+ stopped (Ctrl-C, SIGTERM or SIGHUP), every note it changed, added or
40
+ removed is put back. `--json` names them. An edit or import that an edit's
41
+ command runs is part of that edit, and those take turns among themselves.
42
+ - A memory whose folder is a git repository of its own keeps a history:
43
+ each kept edit and import is one commit of its `*.md` files, named for
44
+ the notes it changed, with the command, the project and the session in
45
+ its message. What changed outside `pp memory edit` since the last commit
46
+ is committed first, on its own. An undone edit commits nothing. `--json`
47
+ of edit and import gains `commit` and `outside_commit`.
48
+ - `pp memory check` lists the open tasks in notes (unchecked boxes, TODO
49
+ and FIXME lines, "next step:" lines): a memory holds what is true, and what
50
+ is to do belongs in a to-do list.
51
+ - `pp memory edit` runs its command with no input where pp has none to hand
52
+ on, instead of failing.
53
+ - `pp memory import FOLDER...` copies notes in, with `origin:` in their
54
+ front matter. It never writes over a file, and it lists a name that is
55
+ taken by other text instead of copying it.
56
+ - Every verb has `--json` and `--dir`. `[memory] dir`, `root` and
57
+ `edit_timeout` go in `config.toml`.
58
+
59
+ `pp queue`: a job's session is what a stop reaches, and what its command
60
+ leaves running ends with it.
61
+
62
+ - A kill or a timeout signals every process in the job's session, not only
63
+ its process group. A command that moves itself to a group of its own,
64
+ such as GNU `timeout`, no longer outlives the job. Only a process that
65
+ starts a session of its own (setsid) leaves it.
66
+ - When a job's command ends and something it started still runs in its
67
+ session, pp ends that the way a stop ends a job: SIGTERM, the grace, then
68
+ SIGKILL. The job is recorded with its command's own outcome once nothing
69
+ of it is left, and its slot, counters and card stay held until then,
70
+ or until pp's SIGKILL for what it left has had its grace.
71
+ - `--keep-leftovers`, or `keep_leftovers = true` in a project's `[queue]`
72
+ or in `[queue.NAME]`, keeps what a command that ended on its own leaves
73
+ running. A kill or a timeout still reaches it. `--no-keep-leftovers`
74
+ ends it for one job whose project keeps it.
75
+ - A job's `stop` gains `leftovers`, set on pp's own stop for what a command
76
+ left. Its `by` is "leftovers". A job record gains `keep_leftovers`.
77
+ - A job record's file begins with its state. A listing reads the records it
78
+ does not hand out no further than that, so ended jobs cost a worker's pass
79
+ and `status` little. A record an earlier pp wrote is read whole until a
80
+ pass that writes its queue's records writes it again in the new order,
81
+ with nothing else changed.
82
+ - A worker's launch pass reads its queued jobs again in one pass, not one
83
+ look per job. It decides on a read every reader shares whether anything
84
+ starts, and takes the store alone only when something does.
85
+ - `pp queue` reads whole only the jobs it shows. A record's head carries its
86
+ priority beside its state and end, which the counts and the run order
87
+ need. `--json` still lists every live job whole.
88
+ - A built-in command named exactly runs without reading every command file,
89
+ so every `pp NAME` starts sooner.
90
+
91
+ `pp http` can sit behind a proxy that serves it under a path.
92
+
93
+ - `--prefix /notes`, or `[http] prefix`, puts the path before every link,
94
+ redirect and fetch pp writes. A request is answered with the path or
95
+ without it, so the proxy may strip it or pass it on.
96
+ - `--bind unix:PATH`, or `[http] bind = "unix:PATH"`, listens on a unix
97
+ socket. The socket is made 0660, so its group and its folder decide who
98
+ connects. A socket left by a server that is gone is replaced. A live
99
+ one, or any other file, is refused. A request on the socket gets the
100
+ same Host check as one on a loopback port.
101
+ - Every answer says `Cache-Control: no-store` and `CDN-Cache-Control:
102
+ no-store`, so no cache on the way keeps a copy.
103
+ - `/raw/` serves scripts and stylesheets as `text/plain`, and any other
104
+ binary file as `application/octet-stream`. Only media keep their own
105
+ type. HTML and XML keep their sandbox whatever bytes they hold.
106
+ - Each library pp loads from cdnjs is pinned by its SRI hash, and the
107
+ loader refuses a file it has no hash for. The page's
108
+ Content-Security-Policy names each library file, and the folder of
109
+ highlight.js languages, instead of all of cdnjs.
110
+ - `.credentials*` joins the files pp never serves.
111
+
112
+ `pp http` searches the tabs: the box at the top of every page.
113
+
114
+ - `/search?q=WORDS` lists the files whose text or path holds every word,
115
+ each a plain substring in any case, those named for them first, then the
116
+ newest. Each comes with how many of its lines hold the rarest of the
117
+ words, counted up to 16, and up to 3 of those lines, those holding the
118
+ most words first, linked to the line. It reads the first 8 words and says so when it is given
119
+ more. `/_pp/search` gives the same as JSON.
120
+ - A markdown page opened at a line shows its source, where the line is.
121
+ - It reads only what the tabs serve, through the walk Recent takes, so a
122
+ hidden, excluded or denied file is never read, nor a link unless
123
+ `follow_symlinks` is on, when it reads what the file views serve.
124
+ - A search reads no file. The walk reads the text of each file that
125
+ changed, for up to `scan_budget` CPU seconds, and leaves the rest to the
126
+ next walk. A search answers from that text, so a change is found once a
127
+ walk has read it. The page says how long ago the walk listed the files.
128
+ - A binary file, one over `search_cap` (1 MB), and one past the 256 MB of
129
+ distinct text search keeps are matched by their path alone, and the page
130
+ says how many. A text two files share is kept once.
131
+ - It reads prose and Python by default: `*.md *.txt *.rst *.org *.tex
132
+ *.py`. `search` in `[http]` names other patterns.
133
+
134
+ `pp http` answers sooner.
135
+
136
+ - A walk lists a folder again only when its mtime has changed. A folder
137
+ changed in the last two seconds is listed again either way.
138
+ - The Recent walk and the search walk run when the server starts. A request
139
+ that comes before they end waits for them.
140
+ - The deny list and the globs are matched as one pattern each, and a path
141
+ is resolved by the kernel in one call.
142
+
143
+ `pp http` keeps the tabs you pin in its bar.
144
+
145
+ - "▾ all N" at the end of the bar lists every tab, with a filter box and a
146
+ pin toggle. A pinned tab stays in the bar, and its handle drags it into
147
+ the order you want, by mouse or by finger. The pins are kept by your
148
+ browser, so the server still only reads. With no pins the bar holds
149
+ every tab, as before.
150
+ - A folder page's headings sort it: click name, size or modified, and click
151
+ the same heading again for the other way round. Folders stay first.
152
+
153
+ `pp queue resize` changes what a job reserves: `pp queue resize 14 --gpu-gb
154
+ 16`. It takes `--gpu-gb`, `--cpu`, `--mem-gb` and `--need NAME=N`.
155
+
156
+ - A queued or held job takes any size a pool holds, from its next start.
157
+ A size a retry of it could not get is refused, as at `add`.
158
+ - A running job can only give back what it reserves. The room is free at
159
+ once, so a job waiting for it starts. The run keeps what it was started
160
+ with, such as its cards and `PP_QUEUE_MEM_GB`.
161
+ - A retry of it, and `retry`, reserve what it reserved before the resize.
162
+ `show --json` and `status --json` gain `asked`, which holds that.
163
+
164
+ `pp queue`: a job says where it has got to with a line of its output that
165
+ begins `pp-event:`.
166
+
167
+ - `pp queue` shows a running job's latest event in place of its last log
168
+ line, and a recent job's beneath its row. `status --json` gives it as
169
+ `event` on each running and recent job.
170
+ - `show` names each attempt's latest event, and the values its events
171
+ state as NAME=NUMBER words, such as `peak_gpu_gb=13.2`. `show --json`
172
+ gives them as `event` and `values`, on the job and on each history
173
+ entry.
174
+ - `wait -f` prints each event as it comes. `wait ID --event REGEX` returns
175
+ 0 once each job's current attempt has stated an event the pattern
176
+ matches, 1 where a job failed first, and 4 where one ended well without
177
+ stating it.
178
+ - Events are read from the job's own part of its log, so a container's
179
+ output carries them with no mount, and a retry starts with none of the
180
+ attempt before it.
181
+
182
+ `pp queue log` and `show` read a job's own part of a log file several jobs
183
+ write.
184
+
185
+ - A job's lines run from the line pp writes before each attempt to the
186
+ next such line, or to the end of the file. pp records where the log
187
+ ended once the run is over, as `log_end` on the record and on each
188
+ history entry. What lands after that and before the next attempt, such
189
+ as a stop command's output, is still the attempt's.
190
+ - `log` without -f printed the file's last lines, whoever wrote them, so
191
+ a file a lane's jobs share showed the latest job's lines under any id.
192
+ `log -f` read on past the job's end. `show` and a failure's tail could
193
+ show the next job's lines, and `status` the next job's last line.
194
+ - The line pp writes when it requeues a job now names the job, so it
195
+ counts as that job's.
196
+ - Jobs that write one file at the same time still mix their lines in it.
197
+ So can the jobs of two queues that write it one after the other.
198
+
199
+ `pp kill N` asks first when N is also the id of a queued, held or running
200
+ `pp queue` job.
201
+
202
+ - Job ids and pids are both small numbers, so `pp kill 14366` meant for a
203
+ job could signal an unrelated process with that pid at once. Such a pid
204
+ now shows and asks as a pattern does, and names `pp queue kill`. A
205
+ number that is no process but is any job's id says the same.
206
+
207
+ `pp queue add --retry-gpu-gb N`: every retry reserves at least N GB of a
208
+ card.
209
+
210
+ - `--retries` with `--retry-if "CUDA out of memory"` retried at the size
211
+ the run had run out at, which failed again. The requeue now writes the
212
+ larger size on the record with the attempt, and the retry waits for room
213
+ that size.
214
+ - It is a key too, `retry_gpu_gb`, in a project's `[queue]` and in
215
+ `[queue.NAME]`. Given as the flag it needs a `--gpu-gb`, and a size no
216
+ card holds is refused at add.
217
+ - Each history entry now carries the `needs` its attempt reserved, and
218
+ `show` names them.
219
+
220
+ `pp queue` hands a job's `--mem-gb` to it as `PP_QUEUE_MEM_GB`.
221
+
222
+ - It is in the job's environment and in its runner's env file, and unset
223
+ where the job declared none. The help's docker runner turns it into
224
+ `--memory` and `--memory-swap`, so a container is capped at the RAM its
225
+ job reserved.
226
+
227
+ Smaller changes to `pp queue`:
228
+
229
+ - Every verb takes selections separated by commas, as `--after` does:
230
+ `wait 41,42`. A word that holds nothing but commas is refused.
231
+ - `--gpu-gb 0` says that a job that needs no card leaves the flag out.
232
+ - The help says a container sees only what its runner mounts.
233
+ - The help's docker runner copies as printed. Its first line carried a
234
+ comment, which the kernel hands bash as the name of a script to run.
235
+ - `log -f` on a queued job says it waits for the job to start, and that no
236
+ worker is up when none is. `drop` of an ended job names `clean`.
237
+ - `pp help queue` is a short page, and `pp help queue containers`,
238
+ `settings`, `agents` and `reference` hold the rest. `pp help queue all`
239
+ prints every page.
240
+
241
+ `pp queue add --key KEY` queues a job once, however many times it is added.
242
+
243
+ - An add whose key a job in the store carries already adds nothing. It
244
+ prints that job's id on stdout, as an add does, and says so on stderr.
245
+ So an agent that runs an add again after losing its answer, or two
246
+ sessions that add one key at once, queue one job.
247
+ - The key is read in the transaction that stores the job. A key takes the
248
+ characters a tag takes, selects nothing, and is free again once `drop`
249
+ or `clean` removes its job. `retry` queues a job with no key.
250
+ - `show --json` and `status --json` gain `key`, null where none was given.
251
+
252
+ `pp queue`: the next job no longer starts beside work a lost run left.
253
+
254
+ - A runner's work outside the job's group, such as a container, outlives
255
+ a run that ends with no exit code of its own: the docker client was
256
+ killed, or pp's SIGKILL came at the end of the grace. pp recorded such a
257
+ job and let go of its slot, counters and card at once, and the
258
+ project's stop command ran after that. So the next job of a lane, or of
259
+ a shared counter, could start beside that container until the command
260
+ had ended it.
261
+ - What the attempt held now stays held until that stop command has
262
+ returned, and `pp queue status` counts it. A worker that dies before
263
+ starting the command leaves it to the next pass of any queue's worker,
264
+ which runs it once.
265
+
266
+ `pp queue clean` keeps a job whose hook has not run yet.
267
+
268
+ - A worker that dies between a job's outcome and its hook leaves the hook
269
+ to the next worker. A `clean` before that worker came removed the record,
270
+ and the hook never ran. Such a record now stays, and clean says which
271
+ ones it kept and how to run their hooks.
272
+
273
+ `pp queue`: a stop command that ends its job at once is still that stop's
274
+ outcome.
275
+
276
+ - pp read a stop's time from a finer clock than the one the system stamps
277
+ an exit code's file with. That clock moves in ticks of 1 to 10 ms, so a
278
+ code written within one tick after a stop began going out could read as
279
+ written before it. Such a job was recorded as its own failure, exit code
280
+ 137, and its `on_fail` hook ran. A timeout's stop in that window was
281
+ recorded as the job's own failure instead of a timeout.
282
+ - A stop's `going`, `sent` and `kill9` are now read from the clock the
283
+ system stamps files with. An exit code written in the same tick as
284
+ `going` counts as written after it, and its code is kept.
285
+
286
+ `pp queue wait --fail-fast` returns at the first failure.
287
+
288
+ - A wait or a watch over a batch hears of a job that fails in seconds then,
289
+ and not when the batch's last job ends. It tells that outcome as it tells
290
+ any, exits 1, and leaves the jobs still to end running. Over named jobs a
291
+ drop counts as a failure too. `--idle` takes it as well.
292
+ - A wait that gives up at its `--timeout` tells first how each job that
293
+ ended has ended, as a watch does as it goes. It used to say only how many
294
+ had not ended, so a bounded wait in a loop never heard of a failure.
295
+ - A wait or a watch over a queued job whose queue launches nothing, because
296
+ `config.toml` is wrong or left empty, says so once, with the file and why.
297
+ - The line `add` ends with names one command, `pp queue wait IDS`, with
298
+ `--fail-fast` for several jobs, and `release` for held ones.
299
+ - `wait --mine` tells every failure this session has not heard of and the
300
+ latest 20 successes, and counts the rest, which later waits tell.
301
+
302
+ `pp queue --mine` shows only the queues that hold this session's jobs.
303
+
304
+ - With `--mine`, `--here` or `--project` the text view leaves out a queue
305
+ none of whose jobs it would list. `--json` still lists every queue.
306
+ - `pp queue ID` shows that job, as `pp queue show ID` does. `pp queue status
307
+ ID` says to use show, exit 2.
308
+
309
+ `pp queue add --ok-rc 12,13`: exit codes besides 0 that count as success.
310
+
311
+ - A job whose command ends with a code it names is done: `on_done` fires,
312
+ `wait` exits 0 and `--after` treats it as any ended job. The code is kept
313
+ on the record, so a model checker's counterexample (rc 13) is done with
314
+ rc 13. 0 always counts. A command a signal ended exits 128 and the
315
+ signal's number, so `--ok-rc 137` would count a SIGKILL as done.
316
+ - `ok_rc` in a project's `[queue]` or in `[queue.NAME]` sets it for every
317
+ job, as every per-job flag's key does.
318
+ - `--ok-rc 10 --ok-rc 20` is both codes, as `--ok-rc 10,20` is. A campaign
319
+ file's line that gives its own replaces the add's.
320
+ - `show` and the line `add` prints name the codes.
321
+ - A job record gains `ok_rc`, `[]` where none was given.
322
+
323
+ `pp queue wait -f` follows jobs, from where this session's last follow got to.
324
+
325
+ - `pp queue watch` is `wait -f`: one verb, with one set of flags.
326
+
327
+ - A monitor that runs `watch` is ended after a while and started again. The
328
+ next watch of the same session says every event of a job since the last
329
+ one it heard, and how each attempt that ended in between ended, where it
330
+ used to say only where the job had got to. A watch ended in the middle of
331
+ a pass may say that pass's lines again. A watch of another session, or
332
+ of none, starts where the job is, as before. A monitor process that is
333
+ itself started again is a new session unless `PP_SESSION` or the agent's
334
+ own session names it.
335
+ - pp keeps, on each job, where the follows of its last 8 sessions got to.
336
+ It writes a position when an attempt starts, every 30 seconds and as the
337
+ follow ends, so a follow killed outright may say its last lines again.
338
+ That field is pp's own and no reader is promised its shape.
339
+
340
+ `pp queue`: a job's stated peak is on its outcome line, and an add well
341
+ past a kin job's peak says so.
342
+
343
+ - A job whose events state `peak_gpu_gb`, `peak_mem_gb` or `peak_cpu` has
344
+ that beside what it reserved on the line `wait` and `watch` tell its
345
+ outcome on: `peak gpu_gb 16.2/32`.
346
+ - An add that reserves well past the peak the newest done job of its
347
+ family stated says so on stderr, in one line naming every job of the add
348
+ it covers, with the `resize` that gives room back while they run. A
349
+ family is one tag, or tags that differ only after their last dot.
350
+
351
+ `pp queue retry` runs ended jobs again as they ran, or with changes.
352
+
353
+ - `pp queue retry 12-14 -- --eval-ckpt last` queues each of them again with
354
+ one argument more. The flags `add` takes for a job's settings are taken
355
+ too, and win over what the job ran with, `--env` and `--need` key by key.
356
+ - Each new job runs in its job's directory and queue, with its tag, runner
357
+ and stop, and every setting it ran with, read off its record. It reserves
358
+ what the job asked before a resize, and records `retry_of`.
359
+ - A job a range, a tag or a prefix names that cannot be queued again (its
360
+ directory is gone, no pool holds it) is left as it is and named, and a
361
+ retry that queued none exits 2. One named by its id refuses the retry,
362
+ and nothing is queued.
363
+ - A retry of a `sh -c` job with words added warns that the shell reads the
364
+ first word as `$0`.
365
+
366
+ `pp queue add --card N` pins a job's `--gpu-gb` to card N.
367
+
368
+ - The job reserves on that card alone and waits for it, though another
369
+ card has room, so the arms of a timing comparison run on one card.
370
+ - A pin keeps no other job off its card. `card = 1` in `[queue.NAME]`
371
+ sends a queue's jobs to card 1, and `slots = 1` in its resources runs
372
+ them one at a time. A job of another queue may still take that card.
373
+ - A card pp does not see, `--card` on a job with no `--gpu-gb`, and a
374
+ size the card cannot hold are refused at `add` and at `resize`.
375
+ - A job record gains `card`, null where the job may take any card. `show`
376
+ and the line `add` prints name it.
377
+
378
+ `pp help queue` says how to read the past and run a host tool.
379
+
380
+ - `pp queue --json -n N` is a history: `recent` holds the N jobs that
381
+ ended latest, each with its tag, rc, started, ended and seconds.
382
+ - `--no-runner` runs one tool of this machine's, java for one, in a
383
+ project whose runner is a container.
384
+ - A `--no-runner` job that starts a container itself hands it its card with
385
+ `docker run --gpus device=$CUDA_VISIBLE_DEVICES -e CUDA_VISIBLE_DEVICES=0`.
386
+ `--gpus all` shows it every card, and its device 0 may be another job's.
387
+
388
+ ## v0.2.2
389
+
390
+ `pp queue` records a kill as a kill when the worker sending it dies before
391
+ it records the send. Such a job no longer ends as its own failure, and its
392
+ `on_fail` hook does not run. A stop's new `going` field says when pp began
393
+ sending it.
394
+
395
+ `pp queue`: a kill whose worker dies while sending it is still a kill.
396
+
397
+ - A worker that died between sending a kill's stop and recording it left the
398
+ next worker to read the job's end as the job's own. That job was recorded
399
+ as its own failure, with exit code 137 or reason `lost`, and its `on_fail`
400
+ hook ran. It is now recorded `killed`, and no hook runs. A timeout's stop
401
+ in the same window now ends as a timeout.
402
+ - A stop gains `going` in `show --json` and `status --json`: when pp began
403
+ sending it, recorded before anything goes out. An exit code the attempt
404
+ wrote after that time is the stop's outcome, with the code kept, unless pp
405
+ was refused the stop. A job that ends on its own just as its worker dies
406
+ before sending is recorded the same way.
407
+ - `show` says "sending began" for a stop pp began sending and has nothing
408
+ marked sent for. A stop pp was refused still says "nothing signalled".
409
+ - `kill` reports a job that ended in that window as killed, "ended as its
410
+ stop was being sent", instead of saying the job had already ended.
411
+
412
+ ## v0.2.1
413
+
414
+ pp finds the session that runs it with nothing set, so `--mine` works for
415
+ any agent and in a terminal. `pp queue` reads the cards with a command of
416
+ yours, and any command answers to an alias of your own. A `pp-NAME` program
417
+ is a command only in pp's own folders, and no longer on your `PATH`.
418
+
419
+ `pp`: a program named `pp-NAME` on your `PATH` is no longer a command.
420
+
421
+ - pp looks for such a program in `~/.config/pp/commands/`, in each plugin
422
+ and in each `PP_PATH` folder, and reads no `PATH` for one. Other software
423
+ installs programs named `pp-*`, and on `PATH` such a program takes a
424
+ command's name or a unique prefix of it (`pp q`) and shows in `pp -h`. A
425
+ program of yours stays a command by moving it into
426
+ `~/.config/pp/commands/`, or by listing the folder it is in in `PP_PATH`.
427
+ - Every Python command is still found before any such program, and the
428
+ programs in that folder order, so nothing pp found in those folders
429
+ moves.
430
+
431
+ `pp queue`: the card reading and the calling session are named by you, with
432
+ nvidia-smi and the process pp runs under as the defaults.
433
+
434
+ - `[resources] gpu_gb = { probe = "CMD" }` reads the machine's cards with a
435
+ command of yours instead of nvidia-smi. CMD prints one line a card,
436
+ `index,name,total_MiB,used_MiB,util`, which is nvidia-smi's own
437
+ `--format=csv,noheader,nounits` line, and the index it prints is the one pp
438
+ reserves and publishes in `CUDA_VISIBLE_DEVICES`. The reading is cached for
439
+ the pool's `every` and taken under the probe's own claim, as any probe's is.
440
+ A probe in a `[queue.NAME.resources] gpu_gb` is still refused: one machine
441
+ has one set of cards.
442
+ - `pp queue status --json` gains `cards_why`: why `cards` is null, and `""`
443
+ where the cards were read.
444
+ - pp finds the calling session from the process it runs under: the nearest
445
+ process above pp that is neither a shell nor a wrapper, named with its pid,
446
+ its start time and this boot. So `--mine` and a job's `owner` are any
447
+ agent's with nothing set, since every command an agent runs is a child of
448
+ the agent's own process, and a person's terminal is a session the same way.
449
+ `PP_SESSION`, where it is set and not empty, is the name an agent gives its
450
+ own session instead, and wins over `CLAUDE_CODE_SESSION_ID`. A process pp
451
+ started itself, a job or its runner or a hook or a stop command, is no
452
+ session. pid 1 and an init are no session either, so a `wait` left running
453
+ by a shell that has returned, which the kernel reparents to an init, says
454
+ there is none instead of answering for another session's jobs. The session
455
+ is found once in a run, so such a wait reports the outcomes it selected to
456
+ the session that queued them and no later `wait --mine` of that session
457
+ reports them again. pp strips those variables from everything it starts.
458
+ - `pp queue status --mine`, which `pp queue --mine` and `ls --mine` are too,
459
+ is the jobs this session queued, in every state, in the text view and in
460
+ `--json`'s `running`, `queued`, `held` and `recent`. It applies together
461
+ with `--here` and `--project`, and narrows the jobs shown and not what the
462
+ queues, the workers and the pools hold. Where there is no session it exits
463
+ 2, as `wait --mine` does.
464
+
465
+ `pp`: a command answers to a name of your own.
466
+
467
+ - `[alias]` in `~/.config/pp/config.toml` names any command anything on this
468
+ machine: `st = "promote status"`, or a list of words. A word is resolved
469
+ as a command's own name, then `[alias]`, then a module's `ALIASES`, then a
470
+ unique prefix, so an alias hides no command and beats a prefix. `pp help
471
+ st` and `pp st <TAB>` follow the expansion.
472
+ - An alias runs no other alias. One whose key is a command's name or `help`,
473
+ or outside `[a-z][a-z0-9_-]*`, or whose first word names no command, is
474
+ skipped and listed under `pp -h`'s "broken (skipped)". The table is read
475
+ only where a word is not a command's exact name, so a config file that
476
+ cannot be parsed leaves every command pp ships working.
477
+ - `pp cmd alias` lists every alias of both kinds, with what it runs and
478
+ where it is declared (`--json` for the same as data). `pp cmd alias NAME
479
+ CMD [ARGS...]` adds one, `pp cmd alias NAME --rm` takes it back, and `-n`
480
+ prints the change and writes nothing. Those flags count wherever they
481
+ stand, so `pp cmd alias st promote status -n` is a dry run. A command line
482
+ of its own `-n`, `--rm` or `--json` goes after a `--`: `pp cmd alias dq --
483
+ queue drop -n`.
484
+ - `pp cmd alias` is the one place pp writes `config.toml`: one line of the
485
+ `[alias]` table under a lock, the file's mode kept, every other byte as it
486
+ was. A change it cannot make as one line names the file to edit by hand.
487
+ - `pp cmd ls` shows each command's aliases of both kinds, and its `--json`
488
+ rows gain `config_aliases` beside `aliases`.
489
+
490
+ `pp queue`: the views, the plans and the help say more of what pp knows
491
+ about your cards, your queues and your projects.
492
+
493
+ - `status --json` publishes the gpu_gb pool of each scope in `pools` and
494
+ `shared`, an entry like every other pool's with one field of its own:
495
+ `per_card`, that scope's budget on each card of the reading and what its
496
+ running jobs reserve on that card. The text view shows those numbers card
497
+ by card. So a job is sized against the smallest budget of every scope its
498
+ queue draws from before it is queued, instead of being refused first.
499
+ - `pp queue` covers every queue a `[queue.NAME]` table names, beside the
500
+ queues that hold records and the ones a worker is up for. Such a queue
501
+ with nothing in it is a section with nothing in it and an entry in
502
+ `--json`, and reading it creates no directory and no file. "no queues yet"
503
+ is for a machine with none of the three.
504
+ - `pp queue add` warns on stderr where the job has no runner, no `.pp.toml`
505
+ is in or above its directory, and a directory directly below is a project
506
+ whose `[queue]` sets one. The warning names that directory and `--cwd`,
507
+ once per directory, and the job is queued as it asks.
508
+ - The command `add` hands an agent names the ids it queued as ranges (`wait
509
+ 8-13 20`), which is one word for a campaign of a hundred lines.
510
+ - `clean` and `drop` say in their plan that they remove the records and pp's
511
+ own files for them, and that a log at a path a `log` pattern or `--log`
512
+ named stays.
513
+ - The extra run of the project's stop command once a run is over writes to
514
+ the worker's log instead of the job's log. The stop a kill or a timeout
515
+ asked for still writes in the job's log.
516
+ - `pp help queue` names the shape of each `--json`: one object with its keys
517
+ for `status`, one job and its watchers for `show`, and a list of `{queue,
518
+ pid, how, log, up}` for `worker status`.
519
+ - `pp help queue` says that several selection forms are several words on one
520
+ verb, that a tag or a prefix selects every job of that name the store
521
+ holds, an earlier round's ended ones too, and that a campaign line's
522
+ `--cwd` is that line's own directory and so its project.
523
+
524
+ ## v0.2.0
525
+
526
+ The interface this minor version keeps. `pp help queue` says what is promised
527
+ and what may change in a patch. `pp queue` stops what a container runner
528
+ started, takes a campaign from a file with one job's flags per line, and runs
529
+ a stage after the one it names.
530
+
531
+ `pp queue`: a job is named and selected the same way by every verb.
532
+
533
+ - A job added without `--tag` has no name. Views show its command, and a
534
+ selection by tag never matches it.
535
+ - A `--tag` is at most 64 characters of letters, digits and `.`, `_`, `-`
536
+ and `:`. It may not hold `..`, be `.` on its own, or read as an id or a
537
+ range, because a tag can be part of a log file's name.
538
+ - `counter` is not a queue name, beside `probes` and `watchers`: a queue of
539
+ that name took the place of the job id counter, and every later `add`
540
+ failed.
541
+ - Every verb that takes jobs takes the same five forms: an id, a range
542
+ (`12-15`), a tag matched whole, `--prefix P` (repeatable), and `--all`
543
+ with `-q NAME`. `--all` without `-q` is a usage error, and a tag or a
544
+ prefix that names jobs in two queues is an error that lists the ids.
545
+ `pp queue note` is the exception, and takes one job by its id or by a
546
+ tag that names one.
547
+ - `pp queue clean` takes a selection as well as `--older AGE`.
548
+
549
+ `pp queue`: a project says how its work is launched and stopped, so a kill
550
+ and a timeout reach work that is not in the job's process group.
551
+
552
+ - `[queue] stop` in a project's `.pp.toml`, and `--stop CMD` on `add`:
553
+ `"group"` where the runner's work stays in the job's own process group,
554
+ else a shell command that ends it. `pp queue add` is refused in a project
555
+ that sets `[queue] runner` and no stop, naming both spellings.
556
+ `--runner CMD` on `add` sets the runner for one job.
557
+ - In a project with a command stop, `pp queue kill` and a `--timeout` run
558
+ that command in place of the SIGTERM to the job's process group. The
559
+ grace, the SIGKILL and the sweep of the group are unchanged, so a project
560
+ with `stop = "group"` behaves as it did. The kill line says which ran, and
561
+ a stop command that failed is reported with its exit code.
562
+ - A job added with `--no-runner` carries `stop = "group"` whatever its
563
+ project says.
564
+ - The sweep that runs when a job's run is over also runs the project's stop
565
+ command, where there is one and the attempt wrote no exit code of its own,
566
+ and it takes the job's process group with it. So nothing the runner
567
+ started outlives the attempt that started it, and a leaked process no
568
+ longer holds a reservation the next job is told it may use.
569
+ - A job and its runner are given `PP_QUEUE_CONTAINER`, a name no other run
570
+ on the machine has, one per attempt, beginning `pp-<queue>-<id>-`; and
571
+ `PP_QUEUE_ENVFILE`, the path of a file of `KEY=VALUE` lines readable by
572
+ its owner alone. That file holds the project's `[queue] env`, the job's
573
+ `--env`, `CUDA_VISIBLE_DEVICES` where a card was reserved, `PP_QUEUE`,
574
+ `PP_QUEUE_JOB` and `PP_QUEUE_CONTAINER`, and nothing else of the
575
+ machine's: no `PATH`, `VIRTUAL_ENV`, `CONDA_PREFIX`, `CONDA_DEFAULT_ENV`
576
+ or `PP_QUEUE_RCFILE`. The record names it, and it goes with the record.
577
+ - An `--env` value, or a `[queue] env` value, holding a newline is refused,
578
+ and so is a name holding `=`, NUL or a newline.
579
+ - An exit code an attempt wrote while a stop of it was going out is that
580
+ stop's outcome, killed or timeout, with the code kept as `rc`.
581
+ - `pp queue kill --grace DUR` takes a duration and overrides the job's own
582
+ grace for the jobs that kill names.
583
+
584
+ `pp queue`: a job's output goes where the project says.
585
+
586
+ - `log` in a project's `[queue]`, and `--log PATH` on `add`: a path with
587
+ `{tag}` or `{id}` in it, relative to the project root or absolute. Any
588
+ other placeholder, a path holding `..`, and a relative path that leaves
589
+ the project root are refused. `{tag}` on a job with no tag is filled in
590
+ with the job's id.
591
+ - Every attempt appends to that one file, after a `[pp queue]` line naming
592
+ the job and its command, and several jobs may name one file. The record
593
+ names the file; `log`, `show`, a failure's tail and `PP_QUEUE_LOG` all
594
+ read it from there. `drop` and `clean` remove the files pp put in its own
595
+ store, and never a log at a path you named.
596
+ - `--retry-if` matches from the byte offset where the failed attempt's own
597
+ output begins, so it no longer matches an earlier attempt's lines or pp's
598
+ own line.
599
+
600
+ `pp queue`: a campaign file carries one job's flags per line, and a job can
601
+ wait for the jobs it names.
602
+
603
+ - A line of a `--from` file is the rest of an `add` command line: its own
604
+ flags, then `--`, then the command. A flag on the `add` is the default for
605
+ every line and a line's own flag wins over it; `--env` and `--need` merge
606
+ key by key. A line that is only a command behaves as before.
607
+ - A line is split into words the way a shell splits them, with quotes and
608
+ backslashes honoured. A line holding `|`, `&`, `;`, `<`, `>`, `(`, `)`,
609
+ `` ` `` or `$(` outside quotes is refused with its line number, and so is a
610
+ line with no `--` whose first word begins with `-`. A file one line of
611
+ which is refused queues nothing. A command that wants a shell operator
612
+ passes it quoted: `-- sh -c 'python x.py | grep -v noise'`.
613
+ - `--from FILE` together with a command on the same command line is a usage
614
+ error, and so is a line that names the queue, the file, `--hold` or
615
+ `--no-start`, which are the batch's and no line's.
616
+ - `--after SELECTION` on `add`: the job waits until every job that selection
617
+ named has ended, whatever the outcome. The value is one or more selections
618
+ separated by commas (`41,42`, `41-66`, `s1_eval,s1_report`), read as the
619
+ job is added and kept as ids on the record. An id no job has is a usage
620
+ error and nothing is queued. A job that is waiting is skipped rather than
621
+ waited for, so it never becomes the head of its queue, and a job whose
622
+ record is gone counts as ended.
623
+ - `pp queue retry` over a range, a tag, a prefix or `--all` shows a plan and
624
+ asks, as `kill` and `drop` do; `-n` shows the plan and `-y` does not ask.
625
+ A job that succeeded has ended, so it suits `retry`. The new job is owned
626
+ by the session that asked for it, and a priority is set with
627
+ `pp queue reprio` on the job the retry queued.
628
+ - A bare `pp queue wait` waits for every job that is queued or running
629
+ instead of returning at the first outcome. It names no held job; a
630
+ selection that names one waits for it and says so. `--timeout` is honoured
631
+ whatever `--poll` is, and ids together with `--idle` or `--mine` is a
632
+ usage error.
633
+ - A worker that adopts a running job takes the offset its output begins at
634
+ from the job's own run claim, so `--retry-if` reads that attempt's lines
635
+ and never pp's own line.
636
+
637
+ `pp queue`: every setting has one declaration, one default and one order of
638
+ its sources.
639
+
640
+ - Each setting of a job is a key of a project's `.pp.toml [queue]` as well
641
+ as of `[queue.NAME]`: `timeout`, `retries`, `retry_delay`, `retry_if`,
642
+ `on_fail`, `on_done`, `prio`, `gpu_gb`, `cpu`, `mem_gb`, `need`, `env`,
643
+ `log` and `grace`. The first source that gives a setting wins, in this
644
+ order: `add`'s flag, the project's `[queue]`, the queue's `[queue.NAME]`,
645
+ the built-in default. `env` and `need` merge key by key.
646
+ - `grace` is one setting of the job: `--grace DUR` on `add`, the `grace`
647
+ key, the resolved value on the record, and 10s by default. A timeout's
648
+ stop and a kill of that job both take it.
649
+ - Durations are read one way everywhere: `90s`, `30m`, `2h`, `1d`, a bare
650
+ number of seconds, `.5` and `1.5h`, and `none` where a setting offers it.
651
+ An empty value, a value that is not finite and one over 1e9 seconds are
652
+ refused, in a config key as on a command line.
653
+ - Numbers are bounded: `retries` at most 1000, `retry_delay` at most the
654
+ one-hour cap, a queue's `poll` longer than 0. A need that is 0, negative
655
+ or nan is refused.
656
+ - `pp queue work` takes no `--backfill` and no `--poll`: both are keys of
657
+ `[queue.NAME]`, which the queue's worker re-reads every loop, so they
658
+ change under running jobs with no restart.
659
+
660
+ `pp queue`: the store and the machine surface.
661
+
662
+ - A queue's `FORMAT` is 2. A pp that reads another format refuses the queue
663
+ where it opens it, naming the format the queue says and the one that pp
664
+ reads, rather than reading it.
665
+ - The `--json` field `gpu`, an integer, becomes `cards`, a list of card
666
+ indices; a job that reserved no card has `cards: []`.
667
+ - New `--json` fields: `stop_cmd`, `grace`, `after`, `container`, `envfile`,
668
+ `log_at`, and `cards`, `container`, `envfile` and `log_at` in each
669
+ `history` entry. `show --json` carries every promised field on a job in
670
+ each of the five states: a field the record does not hold yet is null, or
671
+ an empty list. The two exceptions are `after`, which is there only on a
672
+ job that names the jobs it waits for, and `stop`, only on a job asked to
673
+ stop.
674
+ - `pp help queue` is one page: a first job, a project whose work runs in a
675
+ container in four steps, names and selection, campaign files, the settings
676
+ table, how a job ends, workers, and what is left to you.
677
+
678
+ `pp kill`: `--grace DUR` takes a duration in the spelling every duration pp
679
+ takes has. A bare number is still seconds.
680
+
681
+ `pp promote`, `pp cmd` and `pp http`: plans under `-n`, help by flag, and
682
+ links one target holds.
683
+
684
+ - `pp -h NAME` and `pp --help NAME` print that command's help, as
685
+ `pp help NAME` does. `pp -h NAME VERB` prints the verb's.
686
+ - `pp promote rollback`, `pp promote link`, `pp promote init` and
687
+ `pp cmd new` take `-n`: each one shows its plan and changes nothing.
688
+ - `pp promote` does not take over a link path another target holds: the
689
+ refusal names that target, and `--force-links` moves the link.
690
+ - `pp promote status --json` has `failed`: the current release's
691
+ activation failed, or nothing gated it.
692
+ - `pp promote diff --json` has `source_gone`. A target whose project
693
+ directory is gone exits 1 there, in place of git's error.
694
+ - `pp cmd ls --json` has `aliases`, each command's short names.
695
+ - An unknown key in `[[http.roots]]` or `[[http.discover]]` is a config
696
+ error naming the file and the key, so a misspelt `marker` stops the
697
+ server instead of serving every folder under `under`.
698
+ - `pp help http` documents every `[http]` key with its default.
699
+
700
+ ## v0.1.1
701
+
702
+ Fixes. A few command lines and config values that did harm are refused.
703
+
704
+ - `pp batch run` refuses a file-name field where a shell would read the
705
+ name again as code: inside `$( )`, backticks, `${ }`, arithmetic, a
706
+ `[[ ]]` test, a case pattern, a comment, `eval`, `trap`, a shell's `-c`
707
+ script, or a template with a heredoc or a function. The message names the
708
+ spelling that works: `sh -c 'cmd "$1"' sh {}`. Its plan lists every
709
+ command.
710
+ - A `pp batch rename` that is interrupted leaves a journal of what landed,
711
+ and `pp batch undo` takes that journal and says so.
712
+ - `pp promote WORD` releases nothing when the word is neither a verb nor a
713
+ path. It used to release the project in the current directory.
714
+ - A wrong value in `[promote]` is refused, naming the file and the key.
715
+ `name` must be one path component: `name = ".."` let `gc` remove every
716
+ release and plugin.
717
+ - `pp promote` keeps the release it flips away from, so the one you rolled
718
+ back to is still there after the next promote.
719
+ - A socket or fifo in a tree project no longer hangs `pp promote`, and a
720
+ file pp cannot read is a blocker that offers `exclude`.
721
+ - A hook that leaves a process running no longer holds `pp promote`. Each
722
+ hook's output is a file beside the run's log.
723
+ - A release that `pp promote gc` cannot remove stays whole, and gc exits 1.
724
+ - `pp queue`: a job that cannot start fails on its own and the rest start.
725
+ A job whose wrapper is still ending reads running, not lost.
726
+ - `pp queue --json` and `pp queue show --json` give a job's variables by
727
+ name, with each value `***`.
728
+ - An empty duration such as `--timeout ""` is a usage error, and so is an
729
+ empty `pp kill` target, which selected every process you own.
730
+ - `pp http` never serves `.git`, serves a file whose name is not UTF-8, and
731
+ sorts by name when `?sort=` names anything else.
732
+ - pp's messages and the `pp cmd add` and `update` screens escape the text
733
+ they name. TAB completion runs nothing in a command's name: run
734
+ `pp completion bash` or `zsh` again to replace a saved script.
735
+ - A command file whose name is not UTF-8 no longer breaks `pp -h`.
736
+ - pp needs Python 3.11.4 or later, which `pp promote` always needed.
737
+
738
+ ## v0.1.0
739
+
740
+ The first release.
741
+
742
+ - `pp queue` runs commands later, in named queues, when the GPU memory,
743
+ CPUs, RAM or counters they ask for are free. Jobs have timeouts, retries
744
+ for the failures you name, and hooks. Tags and ranges select groups of
745
+ jobs. A project's `.pp.toml` can run its jobs through a runner, such as a
746
+ container.
747
+ - `pp queue wait`, `watch` and `wait --mine` tell a person or an agent how
748
+ jobs ended, with the end of the log for a failure.
749
+ - `pp promote` releases a project from a git commit when its check passes
750
+ on that copy, and rolls back.
751
+ - `pp kill`, `pp batch` and `pp http` stop processes, rename or convert
752
+ files, and show project folders in a browser.
753
+ - `pp cmd` writes commands, and adds other people's from git, pinned to the
754
+ commit you read.
755
+ - Completion for zsh and bash.