# Commands: arguments, values, views (design, 2026-10-17)

A design, partly built: sysk 066 built the views, the `@` mark, letters, and `ls` saying records
(still the shell's own; see the last section). It settles how a program takes arguments, how it says what it found, and
how the person chooses how that is shown, using `ls` as the example. Its companion,
[layers.md](layers.md), settles which commands are programs at all. Both came out of the same
discussion, after sysn 004; the views were settled the same evening, after a round of examples
and a review (below).

## One rule: before `@` is the program's, after it the host's

```
ls /net --all  @long
└── program ──┘ └ host ┘
```

Everything a command line says before a view mark is the program's: its arguments, as the
manifest declares them. A view mark at the line's end, `@` and a word, is the host's: how what the
program says is shown. No switch means one thing to a program and another to the shell, so
`wc -l` is `wc`'s own and `ls @long` is the host's, and neither needs the other.

The mark joins sugar's other line-end marks, which the shell consumes and no program sees: `&`
(background, sysk 032), `> path` (into a file, sysk 029), a last `?` (explain, sysk 051), a last word `??` (help, sysk 075), and `|`.

## Arguments are lists, not dashes

sysk already writes `(ping 10.0.2.2 (times 2))`, and the manifest gives the default,
`(ping (times 3) (needs (talk icmp host)))`. Sugar's `--times 2` is the same list, as long as what
it replaces (sysk 036). Keep that, and add no `-c 3` parsed by each program.

Unix's flags are a convention that every program parses again for itself: getopt, `--long`,
bundled `-abc`, `--` to stop, and each program's exceptions. Here the shell reads a command once,
as an s-expression, and the manifest says what each argument is. So, with nothing in the
program:

- the shell checks arguments before the job starts, and refuses an unknown one, saying what the
  program takes;
- Tab completes argument names, from the manifest;
- `explain` shows what a command would run, with its defaults filled in;
- `/prg` documents every argument a program takes, and `wc ??` shows it (sysk 075).

**Letters, for the fingers.** A program may declare one-letter forms of its own flags in the
manifest, `(wc (letter l lines))`, so `wc -l /x` is `(wc /x (lines))`. Letters come right after the
program's name, as Unix types them. The shell resolves them from the catalogue, the program never
sees one, and an unknown letter is refused with the list: `wc takes -l (lines)`. No bundling
(`-la`) until a program has two flags worth bundling. (The review below had `short` and `habit`
as two clauses; built, one clause did: a letter names a flag.)

**Habits.** A letter a person will type from Unix whose meaning here is a view: `ls -l` is
`ls @long`. While `ls` is the shell's own (sysk 066), the shell knows this one habit itself; when
`ls` is a program it becomes a row in the manifest, `(ls (letter l @long))`. A habit that means a
tool, `ls -t` as `ls | sort time`, waits for `sort`.

## Say values; never format

A program says what it found, as values: whole records, every field it knows. It never decides a
width, a column, which fields to show, or whether to show a name or a whole record. The shell
knows what each answer is (sysk 044: text, a value, or a word of its own); the host on the
person's machine lays it out. Over the serial line that host is syst (`tools/syst.rkt`), which
already turns a run of records of one shape into a table at the terminal's real width; later it
is postterm, whose host folds, sorts and opens values itself.

This keeps programs small and their contracts simple: no program carries layout code, a program
written today looks right in a host written later, and a pipe always carries every field, so
`ls | sort length` works though the default shows names alone.

## Views

A view is how the host shows a run of values. There are four, for any command:

| Mark | Shows | For |
| --- | --- | --- |
| (none) | the record's default fields: packed across the line when that is one field, a table when more | `ls`: names; `cat /sys/task`: a narrow table |
| `@long` | every field: a table, or a list when the table is wider than the terminal or a field holds records, written out below it to any depth (sysk 114) | `ls -l`, and any record's whole |
| `@list` | every field, one record at a time, a field to a line | a record too wide to read as a row |
| `@raw` | the values exactly as said, one s-expression a line | the truth: what a test or another program reads |

Tab after `@` lists them. A text line is text in every view; a run with no records is shown as
written.

**A record's default fields** are declared by its head, once, in the manifest:

```lisp
(view entry (show name) (units (length bytes)))
(view channel (show name from to))
(view task (show name state))
```

`show` names the default fields; everything else is in `@long`. `units` says what a number
measures, so the host may show `12.0 K` or `0.7 ms`, while `sort` and `where` use the number. A
head with no view shows every field: a single record as written, a run of one shape as a table.
A view is a refinement, never a requirement: a new program looks right before anyone writes one.

The view is keyed by the record's head, not by the program, so the kernel's records
(`/sys/task`, `/sys/chan`) and a server's (`/net/eth/arp`) have views as a program's do, and
`cat /sys/chan @long` and `ls /net @long` mean the same thing.

## Examples

```
sysh> ls /net
addr  arp  eth/  icmp/  mask  route  stats

sysh> ls /net @long                       ls -l is the same, a habit
name   type  format  rights  length
addr   file  sexp    read      14 B
eth/   dir   sexp    read         -
...

sysh> ls /net @raw
(entry (name "addr") (type file) (format sexp) (rights read) (length 14))
...

sysh> ls /notes | sort length down        the field the pipe used is shown
name     length
big-log  12.0 K
todo      812 B

sysh> wc -l /notes/todo                   wc's letter, not a view
(wc (lines 23))

sysh> cp /notes/todo /notes/old           one file lent to read, one made and lent to write (sysk 078)
(cp (bytes 812))

sysh> cmp /notes/todo /notes/old          cmp(1)'s answer, as a value
(cmp same)

sysh> cat /sys/chan @list                 a terminal narrower than the table
channel screen
  bytes 64  depth 2  queued 0  ended no
  from terminal  to uart-driver

sysh> ls -t
ls -t is ls | sort time

sysh> ls /net @⇥
@long  @list  @raw

sysh> ping 10.0.2.2 --times 4             held ~300 ms to fix widths, then streamed
seq  from      time
1    10.0.2.2  0.8 ms
2    10.0.2.2  0.6 ms
...
(ping (sent 4) (got 4))
```

## Review (2026-10-17)

Questions asked of the design before writing it down, and what each settled.

| Question | Answer |
| --- | --- |
| A view mark is the last word; may a file be named `@long`? | Yes, names may hold any byte. As with `&`, only a last word starting with `@`, after the command's first word, is a mark; `cat ./@long` or `cat "@long"` names the file. |
| How does the mark combine with the others? | One view, before `&` or `?`: `follow /log @raw &`, `ls @long ?` (explain shows `(view long (ls))`). Only on the pipe's end: `a @long \| b` is refused. Not with `>`: a file holds values, not a view, so `ls @long > x` is refused; `cat x @long` shows it later. |
| Sugar sends as it goes; can it wrap a command it has begun? | The line-end marks are read on a command's first line, whole, before it is sent (`&` and `?` are found this way today). `@word` is found the same way, and the command becomes `(view long (command))`. |
| How does syst learn which view, and the views of each head? | The shell knows the command's view: it sends it with the region, `(region N (view long))`. The declarations are a table the catalogue already builds (`tools/wire.rkt`); the terminal sends it to the host once, after `(syst 1)`, as `(view entry (show name) ...)` messages. |
| And with no host, on the bare serial console? | The shell writes values as written, wrapped at 64 columns, as today: every view is `@raw` there. Views are a host's; sysk itself never lays out. |
| Is a view in the manifest a second description that can drift? sysk 051 refused `/sys/schema` for that reason. | A view only names fields and units, and a field a record lacks is skipped, so drift degrades to showing less, never wrong values. The catalogue's build checks a program's records; the kernel's and the servers' heads are checked by a boot test that reads each file once and compares its fields with its view. |
| Two producers, one head, different fields? | `wire.rkt` refuses two views for one head. A head names one shape; a second shape takes a second head. |
| `@long` and `@table`, `@names`: are four marks too many? | An earlier draft had five. `@names` is what the default shows of a one-field view, and `@table` is `@long` when it fits; four remain, each showing something the others do not. |
| How does a person find out views exist? | Tab after `@`; `ls -l` (a habit) shows `@long` in `explain`; and when the default view hides fields of a head for the first time in a session, syst adds one dim line under the run: `@long for every field`. |
| Is sort a view? | No: it changes what flows, so it is a tool before the mark, `\| sort time down`, and works on every command. In postterm, clicking a column reorders what is shown, which is the host's again. |
| A mixed run, `ping`'s replies then its summary? | Views apply per run, by head; a new head ends the run (syst does this today). |
| A record longer than a line (64 bytes)? | The terminal sends a whole value as one `(out ...)` message; `say` wraps only what is shown on a bare console. To be checked when `entry` records first exceed a line. |

## `ls`, the first program out of the shell

Since sysk 066 the shell's `ls` says `entry` records, and the host shows them by their view. It
stays the shell's for now: a program is a job, the shell runs one job at a time (sysk 032), and a
server serves one lease at a time, so `ls` as a program would be refused, or wait, whenever a job
runs in the background (`follow /log &`). It moves out once jobs run side by side. As a program
it:

1. says one `entry` record per name, whole, from the directory's entries (a directory reads as
   64-byte entries holding length, type, format, rights and name, so no stat per name);
2. declares `(ls (needs (read dir)) (habit l @long))`, so the shell lends it the directory to read
   and nothing else; with no path, the directory it starts from (`cd`, sysk 056);
3. has its view, `(view entry (show name) (units (length bytes)))`.

One feature, and it sets the pattern every later program follows: lists for arguments, records
for answers, views in the host. It is worth doing while the catalogue has ten programs to bring
along.

## Prior art

| From | Taken | Left |
| --- | --- | --- |
| Unix | small tools joined by the caller; `ls -l` in the fingers, kept as a habit | flags parsed by each program; text as the thing joined; layout in every tool, chosen by `isatty` |
| FreeBSD libxo | a program says fields once; text, JSON or HTML is the caller's choice | a library inside each program |
| Plan 9 | `ls` and `ls -l` from one stat | flags |
| PowerShell | objects through a pipe; a default display set, with every field still in the pipe; formatting only at the pipeline's end; wait briefly, then stream a table | views in XML apart from the code; `Format-Table` output that is no longer data; columns dropped when they do not fit |
| nushell | records and units (`filesize`, `duration`) shown humanly and sorted exactly; one `sort` for every command | a shell language of its own; collecting a whole stream before drawing |
| Jupyter | the producer offers, the host chooses how much it can show | |
| postterm, CLIM | commands as data; the host lays out, folds and sorts; a shown value remembers what it is | |
| Oberon | commands as procedures with parameters; text as values to act on | |

## Order of work

| Step | What | Needs |
| --- | --- | --- |
| 1 | done in sysk 066, but `ls` is still the shell's: `entry` records; `@` in sugar; views by head, sent to syst; syst's default (with the dim hint), `@long`, `@list`, `@raw`; letters in the manifest | the catalogue, syst |
| 1b | jobs side by side: a foreground job beside a background one; servers that lease to several jobs; fsd and netd keep a lend made during a lease | the pool, sysk 031; trees with several clients, sysk 063 |
| 1c | `ls` as a program, `(ls (letter l @long))` | 1b |
| 2 | views for the kernel's and the servers' heads, with their boot test | step 1 |
| 3 | `where` over any run of values; `sort` and `pick`; a field a pipe uses is shown | the shell's pipes of values |
| 4 | results kept as files, `/res/N`, shown again in another view (`#3 @long`) | syst |
| 5 | the other built-ins out ([layers.md](layers.md), step 2) | step 1's pattern |
