- .claude/skills/xce-build/SKILL.md: --incremental flag, xceadmin wheel building, corrected the standalone-install guidance (the earlier --system-site-packages venv approach was wrong -- nix devShell packages are $PYTHONPATH entries, not merged into site-packages). - .claude/skills/xce-deploy-ha/SKILL.md: $BASH_BIN resolution, worker/scheduler startup, xceadmin usage against a deployment, troubleshooting rows for the newly-fixed bugs. - .kiro/steering/ha-async-io.md: bug-1562's standby liveness watchdog re-arm invariant. - .kiro/steering/persistence.md: bug-1563 (fsync backoff) and bug-1564 (persist_flush durable-advance callback). - .kiro/steering/build-deployment.md: xceadmin wheel docs, bug-1565's --incremental flag, deploy dir tree updates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
10 KiB
| name | description | disable-model-invocation | allowed-tools |
|---|---|---|---|
| xce-build | Build XCE components with tests. Usage: /xce-build [component] [profile] [build_dir] [--incremental]. Components: all (default), lib, controller, scheduler, worker, apiserver, xcecli, xceadmin. Profiles: asan (default), debug (non-ASAN -O0 -g3 for gdb/cores), release. | false | Bash(*) |
build xce source code
Args: [component] [profile|build_dir] [build_dir] [--incremental] — passed as free text after /xce-build, e.g. /xce-build controller debug. --incremental can appear anywhere in the words (it's stripped out before the rest are parsed positionally — see below). Parse the ARGUMENTS text yourself and run the command below with the parsed values inline.
- No
context: fork, intentionally. It was tried (twice) and reproducibly breaks this skill: whenever ARGUMENTS is non-empty (e.g.controller), the forked subagent receives no usable task — it responds as if handed only ambient project context, never runs the build, and reports nothing back. Bare/xce-build(no args) happens to still "succeed" undercontext: forkonly because the empty-arg case is indistinguishable from no task at all failing open into some default — not because forking works. Do not re-addcontext: forkhere without first confirming the harness actually threads a non-empty ARGUMENTS payload into the forked subagent's prompt. - component (1st word, default
all) —all,lib,controller,scheduler,worker,apiserver,xcecli,xceadmin - profile (2nd word, default
asan) —asan(debug + AddressSanitizer →xce.build),debug(non-ASAN-O0 -g3 -fno-inline -fno-omit-frame-pointer→build.dbg),release(-O3, no sanitizer →build.release). Each profile uses its own default build dir so the variants never clobber each other. If the 2nd word isn't one of these three keywords, treat it as a legacy explicit build_dir instead (3rd word, if present, always overrides the build dir). - Use the
debugprofile to root-cause heisenbug crashes that ASAN perturbs away and that gdb-on-release turns into a hang/timeout — the non-ASAN-O0 -g3build reproduces them and gives a fully symbolized backtrace under gdb (how bug-1282/bug-1286 were cracked). controlleralways buildsxce_testtoo (meson compile … xce_controller controller_unit_test controller/xce_test), in every profile — not justasan.xce_testis the driver/xce-test-haneeds; you never have to build it as a separate step after/xce-build controller, and the two binaries in a build dir are always the same commit/profile in lockstep.xceadminis Python, meson-driven (src/xceadmin/meson.build'scustom_targetwrapsmeson-build.sh), and ignores the ASAN/debug/release sanitizer semantics entirely —xce-build.sh'sxceadmincase just runsmeson compile -C "$BUILD_DIR" xceadmin, the same targetnix build .#xcecoreand every other component use; theprofilearg is accepted but has no effect on it (every invocation does the identical pinned, non-editable, out-of-tree install + test, regardless ofasan/debug/release— unlike the old editable-install-by-default behavior, this always exercises real packaged schema resolution, not just areleaserun). For fast iterative dev without a full meson reconfigure,xceadmincan still be built standalone:cd src/xceadmin && pip install -e .(seemeson-build.shfor the exact out-of-tree steps the meson target itself runs). Every invocation syncs the bundledcontroller.capnp/c.capnpand the 7 JSON validation schemas fromsrc/schema/andsrc/configs/schema/intoxcecommon's packagedschema_files/, installs pinned into a fresh build-dir venv, runspytestagainst that install, and — only after tests pass — builds an installable wheel from that same schema-synced tree into$BUILD_DIR/xceadmin/dist/xceadmin-<version>-py3-none-any.whl(pip wheel … --no-deps, so it's xceadmin's own wheel only, not a vendored bundle ofclick/pycapnp/etc).xce-build.shprints the wheel's path and a ready-to-runpip install "<path>"line after the build. Requires the nix dev shell (pycapnp,pyyaml,jsonschema,click,pytest,ruff— all provided bysrc/nixSupport/xce.nix).--incremental(bug-1565 escape hatch, opt-in, NOT the default): every invocation normally doesrm -rf "$BUILD_DIR"+ a full freshmeson setup, unconditionally — correct in isolation, but since the actual compile step afterward only rebuilds the ONE requested component, a plain single-component build silently deletes any OTHER already-built binary sharing that dir (e.g./xce-build xceadminafter/xce-build controllerwipesxce_controller). Pass--incrementalto reconfigure the existing dir in place instead (meson setup --reconfigure), preserving whatever else was already built there — use it specifically when you want a sibling component alongside an already-built one, e.g./xce-build controllerthen/xce-build xceadmin --incrementalto end up with both in the same dir at once (needed for/xce-deploy-ha, which wantsxce_controller+xcecd_virtual+xce_scheduler+ the xceadmin wheel all together). Deliberately not the default: this project's capnp-generationcustom_targethas documented, unfixed missing-dependency-edge races (buglog.json — normally papered over as a loud, retry-fixable failure on a clean build; skipping the wipe could instead let the same gap cause silent staleness). If anything looks wrong after an incremental build, rebuild without the flag.
Examples: /xce-build controller (ASAN), /xce-build controller debug (gdb/core build), /xce-build all release, /xce-build xceadmin (build + test + wheel, any profile arg is accepted and ignored), /xce-build xceadmin --incremental (same, but keeps other components already built in the same dir).
Run (substituting the parsed component/profile/build_dir; omit trailing empty ones; append --incremental at the end, anywhere, when requested):
nix develop ./src --command bash src/tools/xce-build.sh <component> <profile> <build_dir> [--incremental]
e.g. for /xce-build controller: nix develop ./src --command bash src/tools/xce-build.sh controller. For /xce-build xceadmin --incremental: nix develop ./src --command bash src/tools/xce-build.sh xceadmin --incremental.
Always dispatch the build via the Agent tool
Do not run the nix develop … xce-build.sh command inline in the current session's Bash tool. Instead, on every invocation of this skill, launch a subagent with the Agent tool, giving it a fully self-contained prompt: repo working directory, the exact resolved command (component/profile/build_dir already substituted — do not make the subagent re-parse ARGUMENTS itself), what the command does, and what to report back (build success/failure, compiler errors, the meson test Ok/Fail summary line, and confirmation the expected binaries were freshly rebuilt with fresh mtimes). Wait for it (do not background it — the build is synchronous and finishes in well under this session's turn budget) and relay its report.
This is deliberately a plain Agent-tool dispatch, not context: fork — that frontmatter field reproducibly failed to thread a non-empty ARGUMENTS payload into the forked subagent (see above), whereas an explicit, self-contained Agent prompt has no such dependency and is proven to work.
Installing the xceadmin wheel elsewhere
/xce-build xceadmin always leaves a fresh, tested wheel at
<build_dir>/xceadmin/dist/xceadmin-<version>-py3-none-any.whl (e.g.
xce.build/xceadmin/dist/xceadmin-0.1.0-py3-none-any.whl). It's a normal
wheel — pip install it anywhere a compatible Python is available, no nix
or meson needed at install time:
pip install /path/to/xce.build/xceadmin/dist/xceadmin-0.1.0-py3-none-any.whl
The wheel bundles its own schema_files/ (the same controller.capnp,
c.capnp, and 7 JSON schemas synced in above) so xceadmin config build
works fully standalone against any src/configs/-shaped directory — it
does not need to run from inside this repo or the nix dev shell. Its 4 real
runtime imports (click, pycapnp, PyYAML, jsonschema —
pyproject.toml's dependencies, verified against every actual import
in src/xceadmin/src/xcecommon) are declared, so a plain pip install
resolves them from PyPI on its own; the one caveat is pycapnp, whose
prebuilt PyPI wheels are usually fine outside the nix shell but aren't the
same guaranteed-consistent link against the capnproto C++ runtime the nix
shell's own python3Packages.pycapnp gets (see pyproject.toml's
dependencies comment).
For that guarantee, do NOT use a --system-site-packages venv — tried
and confirmed broken: this nix devShell injects click/pycapnp/PyYAML/
jsonschema via $PYTHONPATH, not into the base python3 interpreter's own
site-packages (it's the bare interpreter derivation; each package is a
separate /nix/store/... path added to $PYTHONPATH by the shell hook —
confirmed via nix develop … --command sh -c 'echo $PYTHONPATH'). A
--system-site-packages venv only inherits the base interpreter's own
(empty) site-packages, so it silently can't see any of them —
ModuleNotFoundError: No module named 'click' the moment you actually run
it, even though the venv's own pip install step succeeds. Instead extract
the wheel directly and bake the resolved $PYTHONPATH into a wrapper at
install time (no venv/pip needed at all) — this is exactly what
/xce-deploy-ha's deploy-ha.sh does for scripts/xceadmin, and it's
proven to work with zero nix/network dependency at runtime:
mkdir -p /path/to/xceadmin-site
python3 -m zipfile -e /path/to/xceadmin-0.1.0-py3-none-any.whl /path/to/xceadmin-site
nix develop ./src --command bash -c '
cat > /path/to/xceadmin << EOF
#!'"$(nix develop ./src --command which bash)"'
export PYTHONPATH="/path/to/xceadmin-site:$PYTHONPATH"
exec '"$(nix develop ./src --command which python3)"' -m xceadmin.cli "\$@"
EOF
chmod +x /path/to/xceadmin
'
($PYTHONPATH must be expanded inside the nix develop --command shell,
not the outer one, so the resolved nix store paths get baked into the
wrapper literally — see deploy-ha.sh's own xceadmin-install block for the
exact working pattern.)