test(board): pin the "field inserted before neste" class; 16 of 16 mutants die

An independent review ran 16 mutants of the PLAN column against the 461
board checks and 5 survived:
- --row taking neste from a stale index (neste=5d|...)
- --inbox-plan taking neste from a stale index (neste=5d)
- the no-STATE.md record one field short (plan=(ingen STATE.md))
- Nd measured behind NOW instead of behind the last commit
- the '?' branch (a plan with no commit) rendered as '-'

Section 34 part 8 adds a fixture for each: a NESTE with a '|' inside,
pinned exactly in --row and --inbox-plan; a repo with no STATE.md; a repo
whose last commit is 30 days old, so the two definitions give different
numbers; a repo with a plan and no commit. Rebuilt from this tree and run
against the full suite: 16 of 16 mutants now fail it.

The same fixtures exposed two defects, fixed here:
- --inbox-plan took neste as ONE field and cut the prose at its first '|'
  (also in 0.34.0); it now takes that field onward.
- a symlinked PLAN.md was aged by the link's own mtime; it now uses
  stat -L, so the plan's age is the target's.

Docs: CHANGELOG no longer says "field 17 onward" (the index is 18) and
now records the no-STATE.md record fix and the --inbox-plan fix; README's
requirement says macOS, since board.sh uses BSD stat and renders nothing
under GNU stat.

board-selftest 461 -> 478 (badge 978). Suites: coord 257, board 478,
route 73, orders 116, guard 54; npm test 12/12, run after git add.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-17 17:58:49 +02:00
commit 6f4908a5dc
5 changed files with 145 additions and 15 deletions

View file

@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **`PLAN` column in the board table, and `plan=` in `--row`.** A filesystem
reading of the repo root: `-` = no file named exactly `PLAN.md`; `Nd` =
whole days the plan's mtime lies behind the last commit (`0d` when as new or
newer); `?` = a plan with no commit to compare against. The content is never
newer); `?` = a plan with no commit to compare against. A symlinked
`PLAN.md` is aged by its target. The content is never
read, and the column does not judge whether a criterion can be falsified -
the legend says so. The name is matched byte for byte, because APFS's
case-insensitivity makes `[ -f PLAN.md ]` true for `plan.md`. Display only:
@ -25,8 +26,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The index that takes "the last field onward" was written when the record had
14 fields and was never moved as ORDRE, FLY and VOY were inserted before
`neste`, so a dispatch block read `neste=0|-|0|<the NESTE prose>`. It now
takes field 17 onward, and a selftest check pins the exact start of the
prose so the next inserted field turns it red.
takes the field after the last column onward (field 18, since the `PLAN`
column below moved it again), and a selftest check pins the exact start of
the prose so the next inserted field turns it red.
- **`--row` and `--dispatch` for a repo with no STATE.md carried shifted
values.** That record is written by its own printf, which lacked the `VOY`
field, so `--row` read `voy=(ingen STATE.md)` and an empty `neste=`. The
record is now complete; a fixture with no STATE.md pins it.
- **`--inbox-plan` cut NESTE at its first `|`.** It read `neste` as one field
instead of "that field onward"; the whole prose now comes through, as it
already did in `--row` and `--dispatch`.
- **README said "macOS or Linux".** `board.sh` uses BSD `stat` throughout and
renders no rows under GNU `stat`; the requirement now says macOS.
## [0.34.0] - 2026-09-12

View file

@ -467,7 +467,7 @@ marketplace plugin. Three components, one boundary:
- **Board (`scripts/board.sh`):** cross-repo attention board. Reads STATE.md
next-step blocks + board lines, `git status`, and mailbox pending counts, and
prints one line per repo. Read-only by construction: it writes to no repo, no
STATE.md and no mailbox. Pinned by `board-selftest.sh` (461 checks).
STATE.md and no mailbox. Pinned by `board-selftest.sh` (478 checks).
**It lives here because the mailbox is one of its three inputs, and it carries
the same axis distinction the mailbox does.** A pending count means *others
@ -1489,6 +1489,23 @@ marketplace plugin. Three components, one boundary:
"last field onward" index now moves with the record (17 -> 18 here). F6
counts DRT from the right, so its `NF-3` became `NF-4` for the same reason.
**An independent review then ran 16 mutants against the 461 checks and 5
survived** - three of them the same class as the dispatch defect (`--row`
and `--inbox-plan` reading `neste` from a stale index, and the no-STATE.md
record one field short), one the column's DEFINITION (days behind now
instead of behind the last commit - every fixture's commit was close enough
to now that the two coincided), and one the `?` branch. Section 34 part 8
now has a fixture for each, and all 16 die. Every "last field onward"
reader is pinned on the EXACT start of the prose with a `|` inside it,
because a reader taking one field instead of "onward" was a second,
pre-existing defect in `--inbox-plan` that the same fixture exposed.
A symlinked PLAN.md is aged by its target (`stat -L`). Stated, not closed:
the 34-character nested key still shifts its row (the section's own header
splitter would misread such a row; fixture names are short on purpose), and
the "trap is armed" fixture check is red on a case-sensitive volume - this
repo is macOS-only (BSD `stat`), and a case-sensitive APFS volume is the
remaining gap.
- **Skills (`skills/coord-send/`, `skills/board/`, `skills/route/`, `skills/dispatch/`):** natural-language front
doors mapping user intent to engine invocations. No mailbox logic lives here
either. `board` additionally owns the *ranking* — which repo wins and why —
@ -1537,7 +1554,7 @@ obligations in another repo.
builtins only in hook and tests.
- TDD: no behavior change without a failing selftest check first.
`bash scripts/coord-selftest.sh` must exit 0 (257/257),
`bash scripts/board-selftest.sh` must exit 0 (461/461),
`bash scripts/board-selftest.sh` must exit 0 (478/478),
`bash scripts/route-selftest.sh` must exit 0 (73/73),
`bash scripts/orders-selftest.sh` must exit 0 (116/116) and
`bash scripts/state-line-guard-selftest.sh` must exit 0 (54/54).

View file

@ -14,7 +14,7 @@ Session A in repo X leaves a message for repo Y; the next session in repo Y gets
![Hooks](https://img.shields.io/badge/hooks-1-green)
![Skills](https://img.shields.io/badge/skills-4-orange)
![CLI scripts](https://img.shields.io/badge/CLI_scripts-8-blue)
![Selftest checks](https://img.shields.io/badge/selftest_checks-961-blue)
![Selftest checks](https://img.shields.io/badge/selftest_checks-978-blue)
---
@ -152,7 +152,7 @@ It makes **zero model calls**, which is the point rather than a detail. Under su
**`board.sh --row <repo>` renders one repo's row as `field=value` lines**, a seventh rendering of the same scan and the only one that answers about a single repo. It exists because the table's columns *were* misread: the fixed-width view is for a human, and reading a column off it by eye is how `FLY` was read as `ORDRE` on 2026-09-11 — while every other rendering a program consumes is already `key=value`. `inn`, `ordre` and `fly` are three separate fields because they are three separate facts; `status` is the bare token rather than the table's `blocked>target` display, with `blocked-on` beside it; `neste` is last and uncut. An unknown repo exits **2** and writes nothing, because an empty block would read as a repo whose every column is blank — a real and different state. One field is deliberately *not* a rendering of the scan and is named as such: `upushet` is measured once, for that repo only, from the remote-**tracking** ref rather than the remote, so a repo with no upstream reports `?` and never `0`.
**The table's `PLAN` column shows whether a repo keeps a destination where sessions actually look.** It is a filesystem reading and nothing more: `-` means no file named exactly `PLAN.md` in the repo root, `Nd` means the plan's mtime lies `N` whole days behind the last commit (`0d` when it is as new or newer), and `?` means a plan with no commit to compare against. The file's content is never read, so no wording inside it can move the cell — and the column does not judge whether the criterion inside can be falsified. The name is matched **byte for byte** through a glob rather than with `[ -f PLAN.md ]`, because APFS is case-insensitive by default and that test is true for a `plan.md`. It is display only: never a sort key, never in `--plan`, never summed; `--row` carries it as `plan=`. `touch` and a checkout reset the mtime, the same caveat `ALDER` carries. It replaced nothing: a heuristic on "activity without a release" was measured and rejected first, because a release made it go silent on the one known case it existed to catch.
**The table's `PLAN` column shows whether a repo keeps a destination where sessions actually look.** It is a filesystem reading and nothing more: `-` means no file named exactly `PLAN.md` in the repo root, `Nd` means the plan's mtime lies `N` whole days behind the last commit (`0d` when it is as new or newer), and `?` means a plan with no commit to compare against; a symlinked `PLAN.md` is aged by its target. The file's content is never read, so no wording inside it can move the cell — and the column does not judge whether the criterion inside can be falsified. The name is matched **byte for byte** through a glob rather than with `[ -f PLAN.md ]`, because APFS is case-insensitive by default and that test is true for a `plan.md`. It is display only: never a sort key, never in `--plan`, never summed; `--row` carries it as `plan=`. `touch` and a checkout reset the mtime, the same caveat `ALDER` carries. It replaced nothing: a heuristic on "activity without a release" was measured and rejected first, because a release made it go silent on the one known case it existed to catch.
**`board.sh --plan` renders the day plan**, a *third* rendering of that same scan and the only one that takes a position: which repos to open a tab for today, in what order, and the command to start each. The order is the position, and there is no cutoff — nothing is hidden, and **five ordered groups** decide it, each a lookup over a field the scan already read rather than a weighted score: (1) **chain-root credit**, most repos released first; (2) **debt**, most-owed-first, whatever the status; (3) `planned`; (4) `in-progress`; (5) `?`/`MALFORMED` — undeclared, last and labelled. Within a group, ties break on a cheap Sonnet `next-cost` row, then oldest plan first. A 0.19.0 weighted score (`40 ×` repos released, `15 ×` unhandled inbox messages) briefly stood in this spot and *could* express "this repo owes one message and releases two others" as a single number — but re-tuning those two coefficients would have silently reordered a parser living in another repo, with no test here able to hold a ranking stable for a consumer it can't see; the operator replaced it with the group order in 0.20.0 for that reason. Groups 3 and 4 are `planned` above `in-progress`, inverted from every earlier version by the same decision: turning a decision into motion is the slow step, live work is already moving. **Chain-root credit** follows `blocked-on` transitively to the first repo that is not itself blocked and credits only that root: opening a blocked repo releases nobody, since its own next step is by definition waiting. A cycle, or a `blocked-on` naming a repo the scan never produced, credits nobody rather than inventing a root — a plan that looks correct while sending you to the wrong repo is worse than one that says nothing. Repos owing mail still rank high *whatever their status*, and debt is deliberately **uncapped**: excluding `blocked` or `done` is a statement about a repo's own next step, which cannot be moved, while owing an answer is a different axis and answering is often what unblocks it. Repos with no board line come last and labelled — the table already prints a note about those, so a plan that dropped them silently would repeat exactly that defect. `why=` names the group that placed the repo, so a block reads `unblocks:2` rather than the `inbox:N` every block used to repeat. Still zero model calls, still read-only, and still cross-checked against `coord-count.sh`.
@ -194,14 +194,14 @@ Note that raising the inbox's priority (Rule 7) deliberately does **not** widen
## Requirements
- macOS or Linux with bash 3.2+ (the scripts are deliberately bash-3.2-safe and ASCII-only).
- macOS with bash 3.2+ (the scripts are deliberately bash-3.2-safe and ASCII-only). `board.sh` uses BSD `stat` (`stat -f`), so it does not run on Linux as written.
- Node.js >= 18 for the SessionStart and PreToolUse hooks (zero npm dependencies).
- `git` is required to derive repo identity automatically. Without it, pass `--from`/`--repo` explicitly; the engine refuses to guess an identity from the working directory.
## Development
bash scripts/coord-selftest.sh # 257 checks against a throwaway mailbox
bash scripts/board-selftest.sh # 461 checks against a throwaway repo tree
bash scripts/board-selftest.sh # 478 checks against a throwaway repo tree
bash scripts/route-selftest.sh # 73 checks, incl. the route->board round trip
bash scripts/orders-selftest.sh # 116 checks, incl. the 20-way barriered claim race
bash scripts/state-line-guard-selftest.sh # 54 checks, incl. the Edit replace_all projection and the ratchet

View file

@ -3483,7 +3483,7 @@ pc_state() {
echo "# STATE - $2"
echo "## NESTE - START HER"
echo "<!-- board: status=$3; blocked-on=-; next-cost=Sonnet 5/high -->"
echo "Next step for $2."
echo "${4:-Next step for $2.}"
} > "$1/STATE.md"
}
# pc_commit_at <dir> <epoch> <msg>: commit everything with a fixed committer date.
@ -3524,7 +3524,14 @@ pc_touch_at "$PC_ROOT/p-fresh/PLAN.md" "$PC_NOW"
# p-stale: PLAN.md 5 days and 3 hours older than the last commit -> 5d. Its
# content looks like a value on purpose ("PLAN=99d"); it must not be read.
mkrepo "$PC_ROOT/p-stale"; pc_state "$PC_ROOT/p-stale" p-stale planned
# Its NESTE carries a literal '|' and it holds one unhandled message, so the
# same repo pins where every "last field onward" reader starts AND that it
# really reads onward (section 8 below).
mkrepo "$PC_ROOT/p-stale"
pc_state "$PC_ROOT/p-stale" p-stale planned "Next step for p-stale | then the rest."
mkdir -p "$PC_COORD/p-stale/inbox"
printf -- '---\nfrom: x\nto: p-stale\nsubject: s\nreply-expected: yes\n---\nm\n' \
> "$PC_COORD/p-stale/inbox/20260901T101010Z-1-from-x.md"
printf '# PLAN\nPLAN=99d\n' > "$PC_ROOT/p-stale/PLAN.md"
PC_P_STALE_CT=$((PC_NOW - 3600))
pc_commit_at "$PC_ROOT/p-stale" "$PC_P_STALE_CT" init
@ -3544,6 +3551,46 @@ mkrepo "$PC_ROOT/p-lower"; pc_state "$PC_ROOT/p-lower" p-lower planned
echo "# plan" > "$PC_ROOT/p-lower/plan.md"
pc_commit_at "$PC_ROOT/p-lower" $((PC_NOW - PC_DAY)) init
# The next four fixtures exist because an independent review ran 16 mutants of
# the column against this suite and 5 survived. Each fixture below is the one
# a surviving mutant could not get past.
# p-nostate: a repo with NO STATE.md. Its record is written by a separate
# printf, which is exactly where a field was once missing (0.34.0 printed
# voy=(ingen STATE.md) and an empty neste=), so a short record must show here.
# The last commit is 40 days old.
mkrepo "$PC_ROOT/p-nostate"
: > "$PC_ROOT/p-nostate/PLAN.md"
PC_NS_CT=$((PC_NOW - 40 * PC_DAY))
pc_commit_at "$PC_ROOT/p-nostate" "$PC_NS_CT" init
pc_touch_at "$PC_ROOT/p-nostate/PLAN.md" $((PC_NS_CT - 3 * PC_DAY - 3600))
# p-old: the column's DEFINITION. The last commit is 30 days old, so "days
# behind the last commit" (10) and "days behind now" (40) are different
# numbers; every fixture above has a commit close enough to now that the two
# readings coincide.
mkrepo "$PC_ROOT/p-old"; pc_state "$PC_ROOT/p-old" p-old planned
: > "$PC_ROOT/p-old/PLAN.md"
PC_OLD_CT=$((PC_NOW - 30 * PC_DAY))
pc_commit_at "$PC_ROOT/p-old" "$PC_OLD_CT" init
pc_touch_at "$PC_ROOT/p-old/PLAN.md" $((PC_OLD_CT - 10 * PC_DAY - 5 * 3600))
# p-nocommit: a plan and no commit at all. There is nothing to lag behind, so
# the honest cell is '?' - never '-', which would claim there is no plan.
mkrepo "$PC_ROOT/p-nocommit"; pc_state "$PC_ROOT/p-nocommit" p-nocommit planned
: > "$PC_ROOT/p-nocommit/PLAN.md"
# p-link: PLAN.md is a symlink. The cell must read the PLAN's age, i.e. the
# target's mtime; the link's own mtime is when the link was made, and reading
# it turns an 8-day-old plan into 0d.
mkrepo "$PC_ROOT/p-link"; pc_state "$PC_ROOT/p-link" p-link planned
mkdir -p "$PC_ROOT/p-link/docs"; echo "# PLAN" > "$PC_ROOT/p-link/docs/real-plan.md"
ln -s docs/real-plan.md "$PC_ROOT/p-link/PLAN.md"
PC_LINK_CT=$((PC_NOW - 2 * 3600))
pc_commit_at "$PC_ROOT/p-link" "$PC_LINK_CT" init
pc_touch_at "$PC_ROOT/p-link/docs/real-plan.md" $((PC_LINK_CT - 8 * PC_DAY - 3600))
touch -h "$PC_ROOT/p-link/PLAN.md"
# --- Ground truth for the fixtures (these must be green today) -------------
[ -z "$(git -C "$PC_ROOT/p-stale" status --porcelain)" ]
check "plan: fixture - p-stale is committed clean" $?
@ -3559,7 +3606,7 @@ ls -1 "$PC_ROOT/p-lower" | grep -qx 'PLAN.md'; PC_rc=$?
PC_TABLE="$(pc_board)"
PC_N_REPOS="$(printf '%s\n' "$PC_TABLE" | sed -n 's/^BOARD .*(\([0-9][0-9]*\) repo).*/\1/p' | head -1)"
[ "${PC_N_REPOS:-0}" -eq 5 ]; check "plan: fixture - board scanned the 5 fixture repos (header says ${PC_N_REPOS:-nothing})" $?
[ "${PC_N_REPOS:-0}" -eq 9 ]; check "plan: fixture - board scanned the 9 fixture repos (header says ${PC_N_REPOS:-nothing})" $?
# --- 1. Three different values from three repos ----------------------------
printf '%s\n' "$PC_TABLE" | grep -q '^REPO .* PLAN '
@ -3577,7 +3624,7 @@ check "plan 1: the three fixtures show three DIFFERENT values ($PC_V3)" $?
[ "$(pc_table_cell "$PC_TABLE" p-lower)" = "-" ]; check "plan 2: lowercase plan.md in the root -> '-' (byte-exact name)" $?
# --- 3. --row carries plan= with the table's value ---------------------------
for PC_r in p-none p-fresh p-stale p-docs p-lower; do
for PC_r in p-none p-fresh p-stale p-docs p-lower p-nostate p-old p-nocommit p-link; do
PC_RV="$(pc_row_plan "$(pc_board --row "$PC_r")")"
PC_TV="$(pc_table_cell "$PC_TABLE" "$PC_r")"
[ -n "$PC_RV" ] && [ "$PC_RV" = "$PC_TV" ]
@ -3645,7 +3692,7 @@ done
# --- 6. Denominator: one value per scanned repo ------------------------------
PC_WITH=0
for PC_r in p-none p-fresh p-stale p-docs p-lower; do
for PC_r in p-none p-fresh p-stale p-docs p-lower p-nostate p-old p-nocommit p-link; do
[ -n "$(pc_table_cell "$PC_TABLE" "$PC_r")" ] && PC_WITH=$((PC_WITH + 1))
done
# A non-empty, non-zero denominator first: with an empty scan both sides are 0
@ -3665,6 +3712,55 @@ check "plan 7: the legend says touch/checkout reset the mtime" $?
if printf '%s' "$PC_LEG" | LC_ALL=C grep -q '[^ -~]'; then PC_rc=1; else PC_rc=0; fi
[ -n "$PC_LEG" ] && [ "$PC_rc" -eq 0 ]; check "plan 7: the legend is ASCII like the rest of the table" $?
# --- 8. What the surviving mutants got past --------------------------------
# neste= start, pinned EXACTLY in the two renderings that take "the last field
# onward" besides --dispatch (which has its own check in section 18). A field
# inserted before `neste` with an index left behind shows up as a prefix
# ("5d|Next step ..."), and a reader that takes ONE field instead of "onward"
# cuts the prose at its first '|'.
PC_NESTE='Next step for p-stale | then the rest.'
pc_board --row p-stale | grep -qxF "neste=$PC_NESTE"
check "plan 8: --row neste= is the NESTE prose exactly, '|' included" $?
PC_IP="$(pc_board --inbox-plan)"
printf '%s\n' "$PC_IP" | grep -qx 'repo=p-stale'
check "plan 8: fixture - --inbox-plan has a block for p-stale" $?
printf '%s\n' "$PC_IP" | awk 'BEGIN{RS=""} /(^|\n)repo=p-stale(\n|$)/' | grep -qxF "neste=$PC_NESTE"
check "plan 8: --inbox-plan neste= is the NESTE prose exactly, '|' included" $?
# A repo with no STATE.md still gets its measured value, and its neste= is the
# placeholder rather than an empty line or a value shifted from another field.
PC_NS_EXP="$(( (PC_NS_CT - $(pc_mtime "$PC_ROOT/p-nostate/PLAN.md")) / PC_DAY ))d"
PC_NS_ROW="$(pc_board --row p-nostate)"
printf '%s\n' "$PC_NS_ROW" | grep -qx "plan=$PC_NS_EXP"
check "plan 8: no STATE.md - --row plan= is the measured value ($PC_NS_EXP)" $?
printf '%s\n' "$PC_NS_ROW" | grep -qxF 'neste=(ingen STATE.md)'
check "plan 8: no STATE.md - --row neste= is the placeholder, not empty or shifted" $?
[ "$(pc_table_cell "$PC_TABLE" p-nostate)" = "$PC_NS_EXP" ]
check "plan 8: no STATE.md - the table cell is the measured value too" $?
# The definition: behind the LAST COMMIT, not behind now.
PC_OLD_EXP="$(( (PC_OLD_CT - $(pc_mtime "$PC_ROOT/p-old/PLAN.md")) / PC_DAY ))d"
PC_OLD_NOW="$(( (PC_NOW - $(pc_mtime "$PC_ROOT/p-old/PLAN.md")) / PC_DAY ))d"
[ "$PC_OLD_EXP" != "$PC_OLD_NOW" ]
check "plan 8: fixture - p-old separates 'behind commit' ($PC_OLD_EXP) from 'behind now' ($PC_OLD_NOW)" $?
[ "$(pc_table_cell "$PC_TABLE" p-old)" = "$PC_OLD_EXP" ]
check "plan 8: Nd counts days behind the LAST COMMIT, not behind now ($PC_OLD_EXP)" $?
# No commit: '?' in both renderings.
git -C "$PC_ROOT/p-nocommit" log -1 >/dev/null 2>&1
[ $? -ne 0 ]; check "plan 8: fixture - p-nocommit really has no commit" $?
[ "$(pc_table_cell "$PC_TABLE" p-nocommit)" = "?" ]
check "plan 8: a plan with no commit reads '?', never '-'" $?
pc_board --row p-nocommit | grep -qxF 'plan=?'
check "plan 8: --row carries the same '?'" $?
# Symlink: the target's age, not the link's.
PC_LINK_EXP="$(( (PC_LINK_CT - $(pc_mtime "$PC_ROOT/p-link/docs/real-plan.md")) / PC_DAY ))d"
[ "$(stat -f %m "$PC_ROOT/p-link/PLAN.md")" -ge "$PC_LINK_CT" ] && [ "$PC_LINK_EXP" != "0d" ]
check "plan 8: fixture - the link itself is newer than the commit, its target is $PC_LINK_EXP behind" $?
[ "$(pc_table_cell "$PC_TABLE" p-link)" = "$PC_LINK_EXP" ]
check "plan 8: a symlinked PLAN.md reads the TARGET's age ($PC_LINK_EXP), not the link's" $?
/bin/rm -rf "$PC_ROOT" "$PC_COORD" 2>/dev/null
echo ""

View file

@ -838,7 +838,9 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
plancol="-"
for pl_f in "$d"/PLAN.m[d]; do
[ "${pl_f##*/}" = "PLAN.md" ] && [ -f "$pl_f" ] || continue
pl_m="$(stat -f %m "$pl_f" 2>/dev/null)"
# -L: the PLAN's age is the target's mtime; a symlink's own mtime is only
# when the link was made.
pl_m="$(stat -L -f %m "$pl_f" 2>/dev/null)"
if [ -z "$pl_m" ] || [ -z "$lastct" ]; then
plancol="?"
elif [ "$pl_m" -ge "$lastct" ]; then
@ -1982,7 +1984,11 @@ inbox_plan() {
FILENAME==RCF {
name = $3
rc_bucket[name] = $1; rc_status[name] = $4; rc_cost[name] = $5
# neste is the LAST field and may itself contain a pipe: take field 18
# ONWARD, never field 18 alone - a single field cuts the prose at its
# first pipe. The index moves with the record.
rc_dir[name] = $10; rc_neste[name] = $18
for (i = 19; i <= NF; i++) rc_neste[name] = rc_neste[name] "|" $i
next
}
{