Commit40e39016Recorded1 Sep 2026Repositorysigil-json
Add a native kiln pipeline definition
Message
Defines CI as a kiln .kiln/ci.sgv pipeline with no wrapping shell script: deps / build / test / floor as four named steps of one job, plus an artifact step for the captured test output.
The header records what a step name does and does not buy today, why this is one node rather than three, and why the floor step is shell.
Changed
.kiln/ci.sgv | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 110 insertions(+)Diff
.kiln/ci.sgvadded
@@ -0,0 +1,110 @@
+1
;;; ci.sgv -- sigil-json CI, defined NATIVELY in kiln's job language.+2
;;;+3
;;; No `.kiln/ci.sh`. The steps below ARE the pipeline; there is no shell+4
;;; script wrapping them. This is the pilot shape intended to be copied+5
;;; across the extracted repos, so the reasoning is recorded here rather+6
;;; than only in the task note.+7
;;;+8
;;; `on:` IS MANDATORY AND ITS ABSENCE IS SILENT. A workflow with no `on:`+9
;;; matches no event, produces no error and no run, and reads exactly like+10
;;; a controller fault rather than like this file.+11
;;;+12
;;; NO `branch:` FILTER ON `push`, deliberately. A filter would narrow+13
;;; coverage to one ref; this library has none to spare. `(pull-request)`+14
;;; is NOT declared even though the `.woodpecker.yml` it replaces named+15
;;; `pull_request`: kiln's matcher accepts the trigger, but no run of that+16
;;; kind has ever been observed on this controller, and declaring coverage+17
;;; nobody has seen fire is how a green comes to mean less than it looks.+18
;;;+19
;;; SINGLE NODE, FOUR NAMED STEPS -- not four jobs. Every node gets its+20
;;; OWN workspace and its own delivered tree (runner.sgl `provide-source!`+21
;;; per node), and `sigil deps install` writes its state under $HOME, not+22
;;; into the workspace. Split across nodes, `build` and `test` would each+23
;;; re-deliver the tree and depend on the worker's $HOME surviving between+24
;;; leases -- an unstated dependency on worker statefulness -- or else ship+25
;;; the whole dependency tree between nodes as artifacts, which is the+26
;;; artifact fan-in path kiln's own `.kiln/ci.sgv` records as unproven.+27
;;; Three nodes also take three leases against a pool of 4 at+28
;;; max_concurrent 2, to buy status for stages that are strictly serial.+29
;;;+30
;;; WHAT A STEP NAME BUYS TODAY: readability, and nothing structural.+31
;;; Measured 2026-09-01 on kiln ad9b9c5: `nodeexec.sgl` wraps a named+32
;;; shell step in `##[kiln:section-start:<name>]`, while the log viewer's+33
;;; parser (`web/views.sgl` `section-marker`) requires the prefix+34
;;; `##[kiln:section-start ` -- space and timestamp, the LEGACY `.run`+35
;;; grammar. The two do not meet, so a named step renders as a plain log+36
;;; line, live and completed. The names are still worth having: they are+37
;;; the contract a future comparison would rest on, a rename is a breaking+38
;;; change even when it reads better, and they cost nothing to carry until+39
;;; the grammar is reconciled. Do not assume they fold.+40
;;;+41
;;; THERE IS NO CHECK LEDGER HERE, AND THAT IS STRUCTURAL. The+42
;;; `$KILN_ARTIFACTS/checks.ledger` contract that `kiln-diff-checks` and+43
;;; the conversion plan's floors are built on is written entirely by a+44
;;; repo's own `.kiln/ci.sh` -- `grep -rn checks.ledger src/` in kiln+45
;;; returns nothing. A natively-defined pipeline emits no ledger by+46
;;; construction. The `floor` step below is therefore NOT that contract+47
;;; and must not be read as it: it asserts only that the suite ran and+48
;;; reported at least the counts a green local run reported.+49
;;;+50
;;; TIMEOUT 1800, CHOSEN TO BE CLAMP-IMMUNE. `runner.sgl node-worker-fm`+51
;;; applies (min declared ceiling) with NO diagnostic, where ceiling is+52
;;; `safety.max_job_timeout`. The effective value is printed exactly once,+53
;;; in the `Persistent worker ready ... timeout=N` line. 1800 is the+54
;;; config default (`config.sgl:345`) and at or below every ceiling this+55
;;; controller has been recorded with, so the declared value survives the+56
;;; clamp whatever the key is set to. Local half-warm cost (shared $HOME+57
;;; dep cache, fresh delivered tree) is far below it; a cold CI worker+58
;;; will be slower, and 1800 is the headroom for that.+59
+60
(pipeline ci+61
on: #[ (push) (manual) ]+62
+63
(job ci+64
worker: (worker timeout: 1800)+65
steps: #[+66
+67
(shell "sigil deps install" name: deps)+68
+69
(shell "sigil build" name: build)+70
+71
;; `sigil test` is captured rather than piped: a pipeline's exit+72
;; status is the LAST command's, so `sigil test | tee` reports tee's+73
;; success and loses the failure. The `|| { rc=$?; ...; exit \"$rc\"; }`+74
;; form keeps the real status while still printing the log.+75
(shell "sigil test --no-color > kiln-test.out 2>&1 || { rc=$?; cat kiln-test.out; exit \"$rc\"; }+76
cat kiln-test.out"+77
name: test)+78
+79
;; THE FLOOR. `sigil test` already exits 1 when it finds NO test+80
;; files at all (measured), so this exists for the partial case:+81
;; a file that stops matching the `test-*.sgl` / `*-test.sgl`+82
;; discovery pattern is dropped SILENTLY and the run stays green on+83
;; a smaller suite. Both numbers come from a green local run of+84
;; e667078: `Files: 1 of 1 run`, `Tests: 57 passed`.+85
;;+86
;; The counts are read with a numeric guard because a numeric+87
;; comparison in POSIX sh fails OPEN on an empty or non-numeric+88
;; value; an unreadable count is SETUP-FAILED (exit 2), which is a+89
;; distinct outcome from the floor being breached (exit 1).+90
;;+91
;; NOTE FOR THE NEXT REPO: this step is the only part of the+92
;; pipeline the job language cannot express. It is shell because+93
;; there is nowhere else to put it.+94
(shell "files=$(sed -n 's/^[[:space:]]*Files:[[:space:]]*\\([0-9][0-9]*\\) of .*/\\1/p' kiln-test.out | head -1)+95
tests=$(sed -n 's/^[[:space:]]*Tests:[[:space:]]*\\([0-9][0-9]*\\) passed.*/\\1/p' kiln-test.out | head -1)+96
for v in \"$files\" \"$tests\"; do+97
case \"$v\" in '' | *[!0-9]*) echo \"SETUP-FAILED: floor could not read a count from kiln-test.out\"; exit 2 ;; esac+98
done+99
echo \"floor: files=$files (min 1), tests=$tests (min 57)\"+100
[ \"$files\" -ge 1 ] || { echo \"FLOOR BREACHED: $files test files ran, expected at least 1\"; exit 1; }+101
[ \"$tests\" -ge 57 ] || { echo \"FLOOR BREACHED: $tests tests ran, expected at least 57\"; exit 1; }+102
echo 'floor: ok'"+103
name: floor)+104
+105
;; Staged last on purpose, and note what that costs: steps run under+106
;; `set -e` and there is no post/always construct, so this does NOT+107
;; run when an earlier step fails -- exactly the run whose output+108
;; you would most want kept.+109
(artifact "kiln-test.out" as: "test-output.txt")+110
]))