-
Notifications
You must be signed in to change notification settings - Fork 0
259 lines (228 loc) · 13.3 KB
/
Copy pathvalidate.yml
File metadata and controls
259 lines (228 loc) · 13.3 KB
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
name: validate
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Validate slot-skill contracts
run: node plugin/skills/faff/bin/faff validate-adapters
- name: Validate self-contained prose (no external-artifact refs in docs/guide/ + plugin/skills/*/SKILL.md)
run: node plugin/skills/faff/bin/faff lint-refs
- name: Validate out-of-tree docs links (docs/guide, docs/concept paths resolve)
run: |
# Every https://github.com/shftwst/faff/blob/main/<path> URL written in
# a routed doc tree (docs/guide, docs/concept), outside a fenced code
# block, must name a path that exists in the working tree. Docusaurus
# never resolves absolute URLs, so this is the only coverage those
# out-of-tree links get (FAFF-659). Path existence, not a network
# fetch: no token, cannot flake on github.com. Scoped to the routed
# trees ONLY — a docs/** scan would trip over illustrative placeholder
# URLs in fenced blocks of archived specs.
set -euo pipefail
missing=0
while IFS="$(printf '\t')" read -r file lineno url; do
path=${url#https://github.com/shftwst/faff/blob/main/}
path=${path%%#*}
if [ ! -e "$path" ]; then
echo "BROKEN out-of-tree docs link: $file:$lineno -> $url (path '$path' not found)"
missing=1
fi
done < <(awk '
FNR==1 { infence=0 }
{
t=$0; sub(/^[ \t]*/,"",t)
if (t ~ /^(```|~~~)/) { infence = !infence; next }
if (infence) next
s=$0
while (match(s, /https:\/\/github\.com\/shftwst\/faff\/blob\/main\/[^] )>"`]+/)) {
print FILENAME "\t" FNR "\t" substr(s, RSTART, RLENGTH)
s=substr(s, RSTART+RLENGTH)
}
}
' $(find docs/guide docs/concept -name '*.md' | sort))
if [ "$missing" -ne 0 ]; then
echo "One or more out-of-tree docs links point at a path that does not exist."
exit 1
fi
echo "All out-of-tree docs links in docs/guide and docs/concept resolve."
- name: Validate CLI reference doc covers every subcommand (docs/guide/cli.md)
run: node plugin/skills/faff/bin/faff lint-cli-doc
- name: Validate config template parses
run: |
tmp="$(mktemp -d)"
cp .faffrc.example.yaml "$tmp/.faffrc.yaml" # canonical name (FAFF-50: only .faffrc.yaml is accepted)
( cd "$tmp" && node "$GITHUB_WORKSPACE/plugin/skills/faff/bin/faff" config dump >/dev/null )
- name: Validate the repo's own records/adr tree
run: node plugin/skills/faff/bin/faff adr validate
- name: Validate the repo's configured records/prdr tree (FAFF-463 — shape + numbering + git-landing tier)
run: node plugin/skills/faff/bin/faff prdr validate
# FAFF-581: the CLI selftest battery is now DERIVED from the COMMANDS/REGION_MAP
# registry via the region sweep — no per-command `faff <cmd> --selftest` steps are
# hand-enumerated here any more (the ~65-step hand list, with no drift guard, was the
# exact `lint-cli-doc`-shaped hole this ticket closes). `regions check` asserts the
# REGION_MAP↔COMMANDS bijection + require-graph direction; `regions selftest` then
# spawns every member's own `--selftest` (map-completeness + stale-null checks built in),
# so a new factory/governance command with a broken or missing selftest reddens CI with
# NO edit to this file. The battery is two steps (interim): `--region factory` — which
# spawns the destructive `worktree-prune --selftest` (change 1's safety fix, green today)
# — plus `--region governance`. Collapse to a single `--region all` step once FAFF-561
# clears `budget`'s state-sensitive selftest (or it is quarantined). See FAFF-581 §4.
- name: Enforce the region require-graph + REGION_MAP↔COMMANDS bijection (FAFF-444)
run: node plugin/skills/faff/bin/faff regions check
- name: Registry selftest battery — factory members (incl. the destructive worktree-prune, FAFF-581)
run: node plugin/skills/faff/bin/faff regions selftest --region factory
- name: Registry selftest battery — governance members (FAFF-444)
run: node plugin/skills/faff/bin/faff regions selftest --region governance
# FAFF-581 change 3: the registry-coverage gate — every COMMANDS entry must be tested by
# a non-null selftest OR a declared test file, fail-closed and bidirectional. Distinct
# from the sweep above (which makes selftests RUN): this asserts every command is COVERED.
- name: Assert every subcommand is tested by something (registry-coverage gate, FAFF-581)
run: node plugin/skills/faff/bin/faff lint-cli-coverage
- name: Validate the code-blind evaluator spawner pure core (FAFF-384)
run: node plugin/skills/faffter-noon-evaluate/evaluate-call.mjs --selftest
- name: Prompt-size budget gate (advisory)
run: |
node eval/size-census.mjs --gate --against eval/baselines/prompt-size.json | tee -a "$GITHUB_STEP_SUMMARY"
# flip to enforcing once the post-lean floor is calibrated:
# node eval/size-census.mjs --gate --against eval/baselines/prompt-size.json --enforce
- name: Assert docker present (env integration test must not silently skip — FAFF-274)
run: |
docker info >/dev/null 2>&1 || {
echo "::error::docker unavailable on a lane that must run the env integration test (FAFF-274)"
exit 1
}
# FAFF-581 change 4: coverage measurement, PUBLISH-ONLY. NODE_V8_COVERAGE is folded into
# this one required `node --test` run (no second full-suite pass): every spawned bin/faff
# child dumps V8 coverage into the shared dir via the single runCli() seam. The number is
# published in the next step; nothing gates on it (the enforcement-floor flip is a separate
# QA decision — FAFF-581 §7 Punt).
- name: Run skill/CLI behaviour tests (node:test) — with coverage capture (FAFF-581)
env:
FAFF_REQUIRE_DOCKER: "1"
NODE_V8_COVERAGE: ${{ runner.temp }}/v8-coverage
run: |
mkdir -p "$NODE_V8_COVERAGE"
# FAFF-785: --import the hermetic-env preload so the suite is invariant to any ambient
# FAFF_*/CLAUDE_* the runner carries (the self-hosting-checkout non-hermeticity fix). A
# no-op on clean CI; FAFF_REQUIRE_DOCKER is EXEMPT so the FAFF-274 fail-loud guard holds.
node --import ./test/hermetic-env.mjs --test
- name: Publish CLI coverage number (advisory — never gates — FAFF-581)
if: always()
env:
NODE_V8_COVERAGE: ${{ runner.temp }}/v8-coverage
run: node scripts/coverage-aggregate.mjs | tee -a "$GITHUB_STEP_SUMMARY"
# FAFF-762: re-targeted at the IMPURE, OS-divergent surface — real `git worktree`
# provisioning, path resolution, and the bundled bash scripts calling POSIX utils, the
# layer that can actually behave differently on macOS vs Linux (BSD vs GNU userland).
# Pure `--selftest` cores are OS-invariant by construction and stay covered once, on the
# Linux `validate` lane above — re-running them here re-proves nothing (FAFF-580 created
# this lane on that pure surface; FAFF-762 is the retarget, not a duplicate).
#
# Docker-free by hard constraint (macos-latest has no daemon): FAFF_REQUIRE_DOCKER stays
# unset so any docker-gated `node --test` case self-skips rather than hard-failing; the
# env/holdout integration stays on the Linux `env-rootless` lane below, untouched.
#
# The manifest is every file under test/impure/ — real-git/real-fs/real-subprocess/bundled-
# bash exercises (setup-worktree.sh's worktree+config-copy lifecycle, a real `git worktree
# add` + `faff worktree-root --assert`, a genuinely dangling admin dir pruned via `faff
# worktree-prune`, `faff state`/`config spec-docs-path`/`gitignore-ensure` over seeded
# trees, and the 17-external-binary PATH checklist). Two sanity floors guard against silent
# shrinkage the same way the old `count < 40` selftest guard did: a file-count floor catches
# a broken glob, a test-count floor catches mass self-skip that a file-count floor alone
# can't see (the glob can still match a file whose tests all bail out).
validate-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Run the impure macOS exercise manifest (real git/fs/subprocess/bash, no docker)
run: |
set -uo pipefail
shopt -s nullglob
matched=(test/impure/*.test.mjs)
count=${#matched[@]}
file_floor=8
if [ "$count" -lt "$file_floor" ]; then
echo "::error::impure manifest shrank to ${count} files (expected at least ${file_floor}) — glob likely broke; check .github/workflows/validate.yml / test/impure/"
exit 1
fi
unset FAFF_REQUIRE_DOCKER
set +e
node --import ./test/hermetic-env.mjs --test "${matched[@]}" | tee /tmp/impure-macos.log
test_status=${PIPESTATUS[0]}
set -e
tests_ran=$(grep -oE '^# tests [0-9]+' /tmp/impure-macos.log | tail -1 | grep -oE '[0-9]+$')
tests_ran=${tests_ran:-0}
test_floor=50
if [ "$tests_ran" -lt "$test_floor" ]; then
echo "::error::only ${tests_ran} impure tests ran (expected at least ${test_floor}) — tests likely mass-skipped; see the log above"
exit 1
fi
exit "$test_status"
# FAFF-371: the env lifecycle must also hold against a ROOTLESS daemon reached via DOCKER_HOST —
# the host-portable stand-in for the bounded nested engine ADR-0041 decision 3 mandates in the
# L4 cage. The suite runs unmodified: passing against the rootless socket proves the ambient
# engine-context model with zero engine-specific code in faff. Required lane, no skip path —
# a rootless-setup failure fails the job loudly (setup fault, distinct from a test failure).
env-rootless:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Set up a rootless docker daemon (the bounded-engine stand-in)
run: |
sudo apt-get update
sudo apt-get install -y uidmap dbus-user-session slirp4netns
# the system daemon must be out of the way: this lane validates the ROOTLESS engine,
# and a lingering /var/run/docker.sock would let a mis-wired test silently pass on it
sudo systemctl disable --now docker.service docker.socket
sudo rm -f /var/run/docker.sock
# hosted runners have no live systemd user session for the runner user — the rootless
# daemon needs one (and XDG_RUNTIME_DIR), or the direct-launch fallback below
sudo loginctl enable-linger "$(whoami)"
sudo systemctl start "user@$(id -u)" || true
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
# Ubuntu 24.04 restricts unprivileged user namespaces via AppArmor — rootlesskit
# (the rootless daemon's namespace helper) needs them
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# user-local rootless install (runner images don't carry Docker's apt repo, so the
# docker-ce-rootless-extras package has no candidate — the official script installs
# self-contained binaries under ~/bin instead)
curl -fsSL https://get.docker.com/rootless | FORCE_ROOTLESS_INSTALL=1 sh
export PATH="$HOME/bin:$PATH"
export DOCKER_HOST="unix:///run/user/$(id -u)/docker.sock"
# start the daemon: the installed systemd user unit, else direct launch; the assert
# step below is the loud gate if neither produced a reachable rootless daemon
systemctl --user start docker || { nohup "$HOME/bin/dockerd-rootless.sh" >/tmp/dockerd-rootless.log 2>&1 & } || true
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break; sleep 2; done
echo "$HOME/bin" >> "$GITHUB_PATH"
echo "XDG_RUNTIME_DIR=/run/user/$(id -u)" >> "$GITHUB_ENV"
echo "DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock" >> "$GITHUB_ENV"
- name: Assert the daemon is rootless (a setup fault fails the job loudly — never a silent host-daemon pass)
run: |
docker info >/dev/null 2>&1 || {
echo "::error::rootless dockerd unreachable at ${DOCKER_HOST} — rootless setup failed (FAFF-371)"
exit 1
}
docker info --format '{{.SecurityOptions}}' | grep -q rootless || {
echo "::error::daemon at ${DOCKER_HOST} is not rootless — this lane must validate the rootless engine (FAFF-371)"
exit 1
}
- name: Run env + holdout integration suites against the rootless engine
env:
FAFF_REQUIRE_DOCKER: "1"
run: node --import ./test/hermetic-env.mjs --test test/env.test.mjs test/holdout-evaluate-integration.test.mjs