summaryrefslogtreecommitdiff
path: root/docs/core-academy/ca11.md
blob: d3a52ec377f425aa227758cc5372bd59bf05b830 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
---
description: "Covers Gall, Arvo's userspace application framework vane, the agent type, running agents, and userspace software updates."
layout:
  title:
    visible: true
  description:
    visible: false
  tableOfContents:
    visible: true
  outline:
    visible: true
  pagination:
    visible: true
---

# 12. Vanes V:  Gall and Userspace

*This lesson covers Gall: the `$agent` type, running agents, and userspace software updates.*

## Gall {#gall}

Currently, the end user zone of Urbit, denoted “userspace”, is supplied primarily by the `/sys/vane/gall` vane. Much as Arvo acts as a dispatcher and state manager for a functional OS in terms of transactions between vanes, Gall acts as a dispatcher and state manager for longrunning daemons. These daemons are referred to as “agents”, and comprise the main way that users actually use Urbit. (Other parts of userspace include generators and threads.)

Gall is responsible to manage userspace applications and their state, including subscribers. To fully understand agent userspace, we want to cover a few related topics:

1. Agents
2. `/sys/vane/gall`
3. Treaty/Docket publication
4. Agent wrappers
5. Historic (dynamic) Gall

We will cover threads in the lesson on Khan and Lick. We covered generators previously in the lesson on Dill and Dojo.


## Agents {#agents}

> An agent is a piece of software that is primarily focused on maintaining and distributing a piece of state with a defined structure. It exposes an interface that lets programs read, subscribe to, and manipulate the state. Every event happens in an atomic transaction, so the state is never inconsistent. Since the state is permanent, when the agent is upgraded with a change to the structure of the state, the developer provides a migration function from the old state type to the new state type.

What is an agent in practice?  In contemporary static Gall, an agent is a core that hews to the definition:

```hoon
::                                                      ::::
::::                    ++gall                            ::  (1g) extensions
  ::                                                    ::::
++  gall  ^?
  |%
  ::
  ::  +agent: app core
  ::
  ++  agent
    =<  form
    |%
    +$  step  (quip card form)
    +$  card  (wind note gift)
    +$  note
      $%  [%agent [=ship name=term] =task]
          [%arvo note-arvo]
          [%pyre =tang]
      ::
          [%grow =spur =page]
          [%tomb =case =spur]
          [%cull =case =spur]
      ==
    +$  task
      $%  [%watch =path]
          [%watch-as =mark =path]
          [%leave ~]
          [%poke =cage]
          [%poke-as =mark =cage]
      ==
    +$  gift
      $%  [%fact paths=(list path) =cage]
          [%kick paths=(list path) ship=(unit ship)]
          [%watch-ack p=(unit tang)]
          [%poke-ack p=(unit tang)]
      ==
    +$  sign
      $%  [%poke-ack p=(unit tang)]
          [%watch-ack p=(unit tang)]
          [%fact =cage]
          [%kick ~]
      ==
    ++  form
      $_  ^|
      |_  bowl
      ++  on-init
        *(quip card _^|(..on-init))
      ::
      ++  on-save
        *vase
      ::
      ++  on-load
        |~  old-state=vase
        *(quip card _^|(..on-init))
      ::
      ++  on-poke
        |~  [mark vase]
        *(quip card _^|(..on-init))
      ::
      ++  on-watch
        |~  path
        *(quip card _^|(..on-init))
      ::
      ++  on-leave
        |~  path
        *(quip card _^|(..on-init))
      ::
      ++  on-peek
        |~  path
        *(unit (unit cage))
      ::
      ++  on-agent
        |~  [wire sign]
        *(quip card _^|(..on-init))
      ::
      ++  on-arvo
        |~  [wire sign-arvo]
        *(quip card _^|(..on-init))
      ::
      ++  on-fail
        |~  [term tang]
        *(quip card _^|(..on-init))
      --
    --
  --  ::gall

```

A Gall agent must have ten arms. (There's a fascinating bit of self-reference in the state definition going on, and to be honest I'm a little surprised that it works, but it is an iron core.)  The definitions here are for `|~` barsig arms in a `^|` ketbar core.

- [`|~` barsig](../../hoon/rune/bar.md#barsig) yields an iron gate.
- [`^|` ketbar](../../hoon/rune/ket.md#ketbar) yields an iron core from a gold core.

So we must at last really grapple with the [core variance model](../hoon-school/R-metals.md) in Urbit. This is often notorious to understand because we don't have great metaphors or analogues to type variance in real life. Right now, what we need to understand is that an iron/contravariant core is _opaque_:  That is, we use this to define an interface in which the argument can be less specific than the interface and the result can be more specific. Contravariance is useful for flexibility in input values (`sample`s).

> An `%iron` core `i` has a write-only sample (payload head, `+6.i`) and an opaque context (payload tail, `+7.i`). A core `j` which nests within it must be a `%gold` or `%iron` core, such that `+6.i` nests within `+6.j`. Hence, **contravariant**.

The archetypal Gall agents in `/sys/lull` are composed using iron gates since they will be used as examples for building actual agent cores. Likewise, the `+rs` and sister gates in `/sys/hoon` are built using iron doors with specified rounding behavior so when you actually use the core (like `+add:rs`) the core you are using has been built as an example.

- How are the iron gate runes actually implemented in the Hoon type system?  (See `+deem:nest:ut` and `+peel:ut`.)
- Try to implement an agent missing an arm, like `+on-fail`.
- Bonus question:  what half-implemented rune produces an `%iron` core?

We construct an agent explicitly in an `/app` file by applying `%-  agent:gall` to a correctly-shaped core.

- What does each arm produce?

```hoon
++  on-init   (quip card _agent)
++  on-save   (vase)
++  on-load   (quip card _agent)
++  on-poke   (quip card _agent)
++  on-watch  (quip card _agent)
++  on-leave  (quip card _agent)
++  on-peek   (unit (unit cage))
++  on-agent  (quip card _agent)
++  on-arvo   (quip card _agent)
++  on-fail   (quip card _agent)
```

Finally, we can take a gander at what that ubiquitous `+quip` is:

```hoon
++  quip
  |$  [item state]
  [(list item) state]
```

(It's just a wrapper for `(list item) state`.)

Basically, every arm must produces a list of effects and a state change, if any.

- What does each arm expect?

```hoon
++  on-init   ::  not a gate, only an arm
++  on-save   ::  not a gate, only an arm
++  on-load   |=  =vase
++  on-poke   |=  =cage
++  on-watch  |=  =path
++  on-leave  |=  =path
++  on-peek   |=  =path
++  on-agent  |=  [=wire =sign:agent:gall]
++  on-arvo   |=  [=wire =sign-arvo]
++  on-fail   |=  [=term =tang]
```

We'll need to differentiate the Gall `$sign`s and the Arvo `$sign`s in a moment.

When `+ford:clay` reads in a Gall agent file from `/app`, it automatically composes cores together using `=>` tisgar. (This leads to a slightly disconcerting situation in which the cores are simply present serially in a file.)

- Compare the definition of `$agent:shoe` with `$agent:gall`. How does this correctly extend the Gall agent definition for the type system?


## Vane {#vane}

While Gall facilitates very complex userspace apps, the vane itself is rather modest, weighing in at less than half the size of Clay or Ames. Gall knows how to route events to the handler arms in a standard agent core, and it instruments upgrades and subscriptions.

However, we have to consider Gall at two levels:  the vane level, which manages top-level state like the set of running agents and queued moves, and the agent level, which manages agents as doors.

Gall is a landlocked vane. It has no runtime counterpart.

### `/sys/lull` Definition {#syslull-definition}

```hoon
::                                                      ::::
::::                    ++gall                            ::  (1g) extensions
  ::                                                    ::::
++  gall  ^?
  |%
  +$  boar  (map [=wire =ship =term] nonce=@)           ::  and their nonces
  +$  dude  term                                        ::  server identity
  +$  gill  (pair ship term)                            ::  general contact
  +$  load  (list [=dude =beak =agent])                 ::  loadout
  +$  scar                                              ::  opaque duct
    $:  p=@ud                                           ::  bone sequence
        q=(map duct bone)                               ::  by duct
        r=(map bone duct)                               ::  by bone
    ==                                                  ::
  +$  suss  (trel dude @tas @da)                        ::  config report
  +$  well  (pair desk term)                            ::
  +$  deal
    $%  [%raw-poke =mark =noun]
        task:agent
    ==
  +$  unto
    $%  [%raw-fact =mark =noun]
        sign:agent
    ==
  --  ::gall
```

Most of the important types have been separated and are called out below.

(Variance again:  [`^?` ketwut](../../hoon/rune/ket.md#ketwut) is for a lead/bivariant core.)

### Vane State {#vane-state}

```hoon
+$  state
  $:  system-duct=duct
      outstanding=(map [wire duct] (qeu remote-request))
      contacts=(set ship)
      yokes=(map term yoke)
      blocked=(map term (qeu blocked-move))
      =bug
  ==
```

- `system-duct` is the set of outbound moves to other vanes (like Ames for subscriptions) or remote agent contacts.
- `outstanding` is the outstanding request queue.
- `contacts` is the set of other ships with which we are in communication.
- `yokes` is the set of running agents.
- `blocked` is the set of moves to agents that haven't been started yet.
- `bug` is the debug print configuration.

### Vane Moves {#vane-moves}

```hoon
|%
+$  gift                                              ::  outgoing result
  $%  [%boon payload=*]                               ::  ames response
      [%done error=(unit error:ames)]                 ::  ames message (n)ack
      [%unto p=unto]                                  ::
  ==                                                  ::
+$  task                                              ::  incoming request
  $~  [%vega ~]                                       ::
  $%  [%deal p=sock q=term r=deal]                    ::  full transmission
      [%sear =ship]                                   ::  clear pending queues
      [%jolt =desk =dude]                             ::  (re)start agent
      [%idle =dude]                                   ::  suspend agent
      [%load =load]                                   ::  load agent
      [%nuke =dude]                                   ::  delete agent
      [%doff dude=(unit dude) ship=(unit ship)]       ::  kill subscriptions
      [%rake dude=(unit dude) all=?]                  ::  reclaim old subs
      $>(%init vane-task)                             ::  set owner
      $>(%trim vane-task)                             ::  trim state
      $>(%vega vane-task)                             ::  report upgrade
      $>(%plea vane-task)                             ::  network request
      [%spew veb=(list verb)]                         ::  set verbosity
      [%sift dudes=(list dude)]                       ::  per agent
  ==                                                  ::
--
```

### Agent State {#agent-state}

```hoon
+$  bitt  (map duct (pair ship path))                 ::  incoming subs
+$  boat  (map [=wire =ship =term] [acked=? =path])   ::  outgoing subs
+$  bowl                                              ::  standard app state
  $:  $:  our=ship                                    ::  host
          src=ship                                    ::  guest
          dap=term                                    ::  agent
      ==                                              ::
      $:  wex=boat                                    ::  outgoing subs
          sup=bitt                                    ::  incoming subs
          $=  sky                                     ::  scry bindings
          %+  map  path                               ::
          ((mop @ud (pair @da (each page @uvI))) lte) ::
      ==                                              ::
      $:  act=@ud                                     ::  change number
          eny=@uvJ                                    ::  entropy
          now=@da                                     ::  current time
          byk=beak                                    ::  load source
  ==  ==                                              ::                     
```

Every agent needs two parts of its state:  the `$bowl`, which is the information outside of the agent that Gall needs to communicate for the vane, and the 

### Agent Moves {#agent-moves}

```hoon
|%
+$  card  (wind note gift)
+$  note
  $%  [%agent [=ship name=term] =task]
      [%arvo note-arvo]
      [%pyre =tang]
  ::
      [%grow =spur =page]
      [%tomb =case =spur]
      [%cull =case =spur]
  ==
+$  task
  $%  [%watch =path]
      [%watch-as =mark =path]
      [%leave ~]
      [%poke =cage]
      [%poke-as =mark =cage]
  ==
+$  gift
  $%  [%fact paths=(list path) =cage]
      [%kick paths=(list path) ship=(unit ship)]
      [%watch-ack p=(unit tang)]
      [%poke-ack p=(unit tang)]
  ==
+$  sign
  $%  [%poke-ack p=(unit tang)]
      [%watch-ack p=(unit tang)]
      [%fact =cage]
      [%kick ~]
  ==
--
```

A Gall `$card` differs from an Arvo card:

```hoon
::  Arvo
+$  card  (cask)                          ::  tagged, untyped event
++  cask  |$  [a]  (pair mark a)          ::  marked data builder
::
::  Gall
+$  card  (wind note gift)
++  wind
  |$  ::  a: forward
      ::  b: reverse
      ::
      [a b]
  $%  ::  %pass: advance
      ::  %slip: lateral
      ::  %give: retreat
      ::
      [%pass p=path q=a]
      [%slip p=a]
      [%give p=b]
  ==
+$  note
  $%  [%agent [=ship name=term] =task]
      [%arvo note-arvo]
      [%pyre =tang]
  ::
      [%grow =spur =page]
      [%tomb =case =spur]
      [%cull =case =spur]
  ==
+$  gift
  $%  [%fact paths=(list path) =cage]
      [%kick paths=(list path) ship=(unit ship)]
      [%watch-ack p=(unit tang)]
      [%poke-ack p=(unit tang)]
  ==
```

Gall does not permit a `%slip`, so a card is either:

- `[%pass path note]`
- `[%give gift]`


### Structure {#structure}

The two main engine cores within `/sys/vane/gall` are:

- `+mo` Arvo move handler
- `+ap` agent-level core

The `+abet` pattern used in Gall prefixes each arm with the containing door abbreviation so you can remain more easily oriented within `/sys/vane/gall`.

#### `+mo` Arvo move handler

Many `+mo` calls resolve into `+ap` calls. It mainly sets things up around particular per-agent calls.

#### `+ap` agent-level core

To run an agent, we have to know the state of the agent, which includes its state and relevant bowl information:

```hoon
::  $yoke: agent runner state
::
+$  yoke
  $%  [%nuke sky=(map spur @ud)]
      $:  %live
          control-duct=duct
          run-nonce=@t
          sub-nonce=_1
          =stats
          =bitt
          =boat
          =boar
          code=*
          agent=(each agent vase)
          =beak
          marks=(map duct mark)
          sky=(map spur path-state)
          ken=(jug spar:ames wire)
  ==  ==
```

- `control-duct` is the duct of 
- `run-nonce` is a unique nonce for each build.
- `sub-nonce` is  global `%watch` nonce.
- `stats`
- `bitt` is the set of incoming subscriptions (for the `bowl`).
- `boat` is the set of outgoing subscriptions (for the `bowl`).
- `code` is the most recently loaded code as a noun.
- `agent` is the agent core, possibly as a vase.
- `beak` is the compilation source.
- `marks` is the map of mark conversion requests.
- `sky` is the map of scry bindings.
- `ken` is the map of sets of open `%keen` remote scry requests.

A typical call from `+mo` to `+ap` will be predicated on `+ap` setting up a Gall agent with its state and processing the incoming move through the appropriate arm.

For instance, this is the lifecycle of a scry call to Gall:

- A scry handler (`roof`) produces a call to Gall's `+scry` arm.
  - `+mo` as a door needs a duct and a set of moves.
    - `+mo-peek` sets up a call to `+ap-peek` along the given path with the appropriate care.
      - `+ap-abed` sets up the agent noun for evaluation.
        - `+ap-yoke` loads the actual agent state; an agent is a door with a state and bowl sample.
      - `+ap-peek` parses the scry path appropriately.
        - `+ap-mule-peek` evaluates the code using `[9 2 0 1]` and `+mock` (see `ca01` for a refresher).
          - `+ap-agent-core` sets up the agent core with its current bowl and state; this includes a `+on-peek` arm since we know the shape of the `$agent` core.
            - `+ap-construct-bowl` produces the agent-ready bowl from Gall-level information.

The lifecycle of a poke looks like this:

- A move is injected targeting Gall's `+call` arm as a `%deal` (indicating that the move goes to an agent).
  - `+call` dispatches to `+mo-handle-use` for an agent.
    - `+mo-handle-local` is for running local agents.
      - `+mo-apply` and `+mo-apply-sure` prepare to call `+ap`.
        - `+ap-abed` sets up the agent noun for evaluation.
          - `+ap-apply` dispatches several kinds of operations, including pokes.
            - `+ap-poke` queues a `%poke-ack` (since it's first among moves) and calls `+ap-ingest`.
              - `+ap-ingest` calls the agent arm.
                - `+ap-handle-result`  and `+ap-handle-peers` take care of watches etc.
                  - `+ap-agent-core` sets up the agent core with its current bowl and state; this includes a `+on-poke` arm since we know the shape of the `$agent` core.
        - `+ap-abet` yields the list of cards to resolve back to `+mo`, but also the `$yoke`, which is the new agent state for Gall's `state`.
      - `+mo-abet` finalizes.

What about an agent modification like `|nuke`?  Let's see the lifecycle of that call.

- `/gen/hood/nuke` → `%kiln-nuke`
  - `/lib/hood/kiln` → `+poke-nuke`
    - `[%pass /nuke %arvo %g [%nuke dude]]`
    - `/sys/vane/gall`:
      - `+call`
        - `+mo`
        - `+mo-nuke`
          - `+ap`
            - `+ap-abed`
            - `+ap-nuke` is where the real work is done. Review it.
              - `+ap-ingest`
            - `+ap-abet`
        - `+mo-abet`

Clay actually governs which agents can run on a given desk. How does `|install` instigate this?

- See `+goad` in `/sys/vane/clay`.

There is additionally some plumbing around Gall receiving responses in `+take` for the vane (`/sys`) versus for an agent (`/use`).

We'll need to differentiate the Gall `$sign`s and the Arvo `$sign`s in a moment.

### Scry interface {#scry-interface}

Gall brokers two kinds of scries:  vane scries and agent scries.

> In order to hit the vane-level endpoints, the beginning of the the `spur` (e.g. the `path` after the `beak`) _must_ be a `%$` empty element. For example:

```hoon
.^(desk %gd /=acme=/$)
.^((set [=dude:gall live=?]) %ge /=base=/$)
.^((list path) %gt /=acme=//foo)
```

- [`%d`: get desk of app](../../urbit-os/kernel/gall/scry.md#d-get-desk-of-app)
- [`%e`: running apps](../../urbit-os/kernel/gall/scry.md#e-running-apps)
- [`%f`: nonces of apps](../../urbit-os/kernel/gall/scry.md#f-nonces-of-apps)
- [`%n`: get nonce of subscription](../../urbit-os/kernel/gall/scry.md#n-get-nonce-of-subscription)
- [`%t`: remote scry subpaths](../../urbit-os/kernel/gall/scry.md#t-remote-scry-subpaths)
- [`%u`: check if installed](../../urbit-os/kernel/gall/scry.md#u-check-if-installed)
- [`%w`: latest revision of path](../../urbit-os/kernel/gall/scry.md#w-latest-revision-of-path)
- [`%x`: remote scry file](../../urbit-os/kernel/gall/scry.md#x-remote-scry-file)
- [`%z:` hash of value at path](../../urbit-os/kernel/gall/scry.md#z-hash-of-value-at-path)

An agent scry has the form `/=agent=/path/to/scry` and may accept any care. `%x` cares must include a terminal mark in the path, however.

Gall also dispatches scries to agents' `+on-peek` arms. This takes place via `+mo-peek`→`+ap-peek`→`+on-peek`→`+ap-mule-peek`.

See `+scry` for details of both.

- [“Gall Scry Reference”](../../urbit-os/kernel/gall/scry.md)


## Treaty and Docket {#treaty-and-docket}

There are two ways to distribute nouns over Ames today:

1. Mark a desk `|public` and use Clay to directly synchronize desks.
2. Use the `/app/treaty` agent from `%landscape` to discover and install agents.

Landscape (formerly Grid) is formally a Tlon product. It primarily consists of two agents (and associated marks, libraries, etc.):

- `/app/treaty` handles publishing and advertising application desks.
- `/app/docket` handles retrieving, validating, and installing application desks.

Together they query a remote `%treaty` instance to `+install` a particular desk.

- Examine `$alliance` in `/sur/treaty`.
- Examine `+publish:so` and `+watch:tr` in `/app/treaty`.
- Examine `+install:ch` in `/app/docket`.

`/app/treaty` in particular has pretty tight construction and I commend its style to you.

- [“Software Distribution · Userspace”](../userspace/dist/software-distribution.md)

### Updates {#updates}

> When Gall receives a newly rebuilt agent from Clay, it calls the gate produced by the `+on-load` arm of the new agent with the state extracted from the old agent. If there is a crash in any `+on-load` calls or in the handling of any effects they emit (which can include more agent activations), then the whole event crashes, canceling the commit. This effectively gives any agent the ability to abort a commit by crashing.

- Gall's `+call` arm receives a `%load` move with a noun of a core built by `+ford:clay`.
  - `+mo-core` is a handle to `+mo` because no `+abet` is needed.
    - `+mo-load` installs agents pretty mechanically, by simply `+skim`ming over the `%live` agents.
      - `+mo-receive-core` checks whether the agent is running.
      - If it is, then `+ap` is invoked to update the agent:
        - `+ap-abed`
        - `+ap-reinstall`
          - `+on-save:ap-agent-core`
          - `+ap-install` is the install wrapper.
            - `+ap-upgrade-state`
              - `+on-init:ap-agent-core`
              - `+on-load:ap-agent-core`
        - `+ap-abet`
        - `+mo-clear-queue` flushes the blocked tasks pending for a new agent.
      - If it isn't, then we have to create it in `+ap`:
        - `+ap-abed`
        - `+ap-upgrade-state`
          - `+on-init:ap-agent-core`
          - `+on-load:ap-agent-core`
      - `+mo-idle` puts the agent to sleep if it's in the kill list (to be retired due to `desk/bill`).
  - `+mo-abet` finalizes moves and state changes.

Note that this is independent of Treaty and Docket once the remote desk has been installed.


## Agent Wrappers as Core Modifiers {#agent-wrappers-as-core-modifiers}

.An agent wrapper (like `dbug`) is a tool to wrap additional handlers around an agent core. These can wrap the internal Gall agent with new functionality by catching pokes and other standard moves, then re-dispatching to the normal arms if no special behavior is needed.

```hoon
> :my-agent +dbug [%state 'value']
```

- Examine `/lib/dbug`.
  - In particular, how does the `+on-poke` wrapper arm work?
  - How does the `+dbug` generator work?

> Using agent transformers to extend agents is a very nice conceptual pattern. But in practice, there are three pretty big problems with it:
> 1. You need to edit the agent code yourself.
> 2. Stateful transformers can break the agent.
> 3. The agent's world will also get transformed.

- [“Debugging Wrapper”](../userspace/examples/dbug.md)
- [~wicrum-wicrun, “Gall Middleware (Assembly 2022”](https://www.quartus.co/blog/gall-middleware-wicrum-wicrun-assembly-2022)


## Dynamic Gall {#dynamic-gall}

An earlier incarnation of Gall, dynamic Gall, specified its arms in terms of the names of the move coming back or the mark of the poke coming in. (This is what I learned on, way back when.)

For instance, this agent was an earlier version of the egg timer app:

```hoon
::  |start %egg
::  :egg ~s5
|%
+$  effect   (pair bone syscall)
+$  syscall  [%wait path @da]
--
|_  [bowl:gall ~]
++  poke-noun
  |=  t=@dr
  ^+  [*(list effect) +>.$]
  :_  +>.$  :_  ~
  [ost %wait /egg-timer (add now t)]
++  wake
  |=  [=wire error=(unit tang)]
  ^+  [*(list effect) +>.$]
  ~&  "Timer went off!"
  [~ +>.$]
--
```


## Exercises {#exercises}

- Your assignment is to produce a minimalist Gall-like agent handler:  a userspace framework for producing “toy” agent-like applications. Let's call them “scamps”.

    The scamp's state is defined in a `$state` block at the top of its file, e.g.:
  
    ```hoon
    +$  state
      $:  scores=(list @)
          hi-score=@
      ==
    ```
  
    Scamps do not support state upgrades, so no version tag is provided.
  
    A scamp requires the following arms for the developer:
  
    ```hoon
    |%
    ++  on-init
    ++  on-poke
    ++  on-peek
    --
    ```
  
    You should be able to poke and peek into a scamp. It has no subscription model.
  
    A scamp specification file is NOT implicitly chained with a running  `=>` tisgal. Compose explicitly.

As a final aside, I believe that building an `%aqua`/`%pyro` testbed along the lines of Gall should also be feasible for you at this point.