From mason
Author chisel slice definition files (SDFs) for canonical/chisel-releases. Covers dependency-tree-first workflow, package inspection via deb-list, slice design, SDF authoring, formatting, and testing. Stops at local commits; user opens PR themselves. Use when user says "add slice", "chisel slice", "slice pkg", or works inside a `canonical/chisel-releases` checkout.
How this skill is triggered — by the user, by Claude, or both
Slash command
/mason:write-sliceThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Skill for authoring slices against [`canonical/chisel-releases`](https://github.com/canonical/chisel-releases).
Skill for authoring slices against canonical/chisel-releases.
Scope: author + test + commit slices locally. Do NOT open PRs -- user opens PR themselves.
Existing slices are append-only. Only modify a published slice if strictly necessary (e.g. fixing a bug, adding a missing dependency, or accommodating an upstream packaging change). Never reorganise, rename, or remove paths from existing slices without a concrete reason -- downstream consumers depend on the current layout. When in doubt, create a new slice rather than changing an existing one.
Prerequisites: read @./CHISEL.md for chisel/SDF format reference, branch model, schema versions, and canonical naming conventions. This skill focuses on the workflow of writing slices.
When this skill and the repo disagree, trust the repo. Read slices/bash.yaml or slices/base-files.yaml on the target branch as canonical reference.
Follow these steps in order. Do NOT skip steps.
ubuntu-24.04). This determines which chisel-releases branch to target.chisel.yaml on the target branch: maintenance.end-of-life must be in the future.format: version in chisel.yaml. This gates available features (see @./CHISEL.md schema versions table). Do not use v2+/v3+ features on older formats.slices/<pkg>.yaml does not already exist on the target branch. If it does, stop and inform the user.Before designing anything, check whether the package already has slices on other release branches. Existing SDFs inform the design and are required context for forward-porting.
List all live release branches.
git ls-remote --heads https://github.com/canonical/chisel-releases.git 'ubuntu-*' \
| awk '{print $2}' | sed 's|refs/heads/||'
Check which branches already have an SDF for the target package.
# For each branch:
curl -fsSL -o /dev/null -w "%{http_code}" \
https://raw.githubusercontent.com/canonical/chisel-releases/<branch>/slices/<pkg>.yaml
Fetch and study existing SDFs. If the package has slices on any branch, download them:
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-releases/<branch>/slices/<pkg>.yaml
Note the structural decisions: slice names, grouping approach (by-type vs by-function), dependency choices, mutate: patterns. These should be carried forward unless there is a concrete reason to diverge.
Compare .deb contents across releases. Run scripts/deb-list for the target release and for each release that already has an SDF. Look for cross-release differences (see @./CHISEL.md Cross-Release Differences table):
/bin/ -> /usr/bin/)libssl3 -> libssl3t64)Document the differences. Record what will need adaptation when writing the SDF and when forward-porting. Present a summary to the user.
Carry forward structural decisions from existing SDFs. Consistency across releases matters for forward-port reviewability. Diverge only when:
.deb contents changed enough to require a different structureIf no existing SDFs are found on any branch, this is a net-new package -- proceed to Step 3.
Before inspecting or designing anything, build the complete dependency tree. Dependencies MUST be sliced before the target package.
Get the full recursive dependency list. Use apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances <package> to resolve all transitive Depends:. Alternatively, run scripts/deb-list <package> to get direct Depends: and recurse manually.
Check which dependencies already have slices on the target chisel-releases branch (ls slices/ | sed 's/\.yaml$//' or chisel info --release <release> <dep> ...).
Identify unsliced dependencies. Produce an ordered list of packages that need slicing, sorted leaves-first (packages with no unsliced dependencies come first).
Present the plan to the user. Show:
Get confirmation before proceeding.
IMPORTANT: Slice dependencies bottom-up. A package cannot reference slices that do not exist. Work from the leaves of the dependency tree toward the root.
Note: only Depends: matter. Not Recommends: or Suggests:. Including Recommends: is rejected by reviewers.
For EACH package that needs slicing (starting from leaf dependencies), inspect it using the bundled scripts/deb-list script:
scripts/deb-list <package> [arch] [--scripts]
This downloads the .deb from the local apt cache and prints:
Depends: line (feeds directly into essential: entries)[x] executable, [f] regular file, [l] symlink (with target)--scripts to print full bodies)Example:
$ scripts/deb-list bash
package: bash version: 5.3-2ubuntu1 arch: amd64
Depends: base-files (>= 2.1.12), debianutils (>= 5.6-0.1)
files (lexicographic): [x]=executable [f]=file [l]=symlink
[f] 0644 root/root /etc/bash.bashrc
[f] 0644 root/root /etc/skel/.bash_logout
...
[x] 0755 root/root /usr/bin/bash
[l] 0777 root/root /usr/bin/rbash -> bash
[f] 0644 root/root /usr/share/doc/bash/copyright
...
maintainer scripts present: postinst (re-run with --scripts to view)
Reading the output:
[l] path -> target means the deb ships that symlink -- use a bare path entry, no explicit symlink: needed.[x] marks executables (go in bins); [f] marks regular files.mode: to a slice entry only when the permission is non-standard (not 0644/0755/0777).--scripts shows postinst calling update-alternatives, ldconfig, or update-mime-database, those side-effects don't run in a chisel rootfs -- either drop the dep or write a mutate: equivalent.scripts/deb-list libfoo amd64, then scripts/deb-list libfoo arm64).Requires apt-get + dpkg-deb and a populated apt cache (sudo apt-get update).
With this output, analyse:
Understand what the package ships: binaries, libraries, config files, data files, scripts, headers, etc. Note architecture-specific paths.
Chisel does not run maintainer scripts. Whatever postinst/preinst do (create symlinks, generate files, register alternatives), you must reproduce via:
contents declarations for simple cases (symlinks, directories)mutate: scripts for logicNo explicit symlink: if the deb already ships it. Chisel preserves deb symlinks. Manual symlink: only for paths the deb doesn't ship (e.g. those created by maintainer scripts).
For ELF binaries, determine shared library dependencies (via ldd output from the script). Cross-reference against the dependency tree to catch transitive runtime deps.
Use the source to:
Before designing new slices, study existing SDFs on the target branch.
Read representative SDFs for similar packages. Use slices/bash.yaml, slices/base-files.yaml, slices/openssl.yaml, slices/dpkg.yaml as references.
Follow naming conventions from @./CHISEL.md (Canonical Slice Names table). Use libs never lib, bins never bin, etc.
Check shared dependencies. If the target package depends on packages with multiple slices (e.g. libc6_libs, libc6_config), determine which specific slice is needed. Do not over-depend.
Verify no path conflicts. Multiple slices from different packages can declare the same path ONLY if:
{make: true}, {text: ...}) and the inline definitions match exactlySearch existing slices: grep -r "/path/you/want" slices/
Respect the append-only principle. Removing files from existing published slices is a regression. If you need a slimmer variant, create a new slice (core, minimal, etc.) rather than removing from an existing one.
Choose the approach that fits the package best.
Best for most packages. Group files by their type. See the Canonical Slice Names table in @./CHISEL.md.
Typical structure:
copyright slice (mandatory, always present)bins for executableslibs for shared objectsconfig for configuration filesdata, scripts, var, etc. as neededBest for complex packages with distinct functional subsets (e.g. Python standard library, large runtime frameworks).
Typical structure:
core -- minimum-functional subsetstandard -- fuller-featured above corefile-formats, networking, crypto)Do NOT mix approaches arbitrarily within a single SDF.
Create slices/<package>.yaml:
package: <package-name>
essential:
- <package-name>_copyright
slices:
bins:
essential:
- <dep-package>_libs
- <package-name>_config
contents:
/usr/bin/<binary>:
config:
contents:
/etc/<package>/config-file:
copyright:
contents:
/usr/share/doc/<package-name>/copyright:
Key rules:
package: must match the filename stemessential: lists <pkg>_copyright so every slice transitively ships itcopyright slice placed last by conventionlicense / notice slicesUpstream LICENSE.txt, NOTICE, ThirdPartyNotices.txt are not the deb copyright. They get separate license: / notice: slices that depend on <pkg>_copyright.
These are mandatory. CI and reviewers reject non-conforming SDFs.
contents paths in bytewise ASCII (lexicographic) order within each slice.essential (global) at the top of the file, right after package:.copyright slice at the bottom of the slices: block.a-z, 0-9, -, must start with a letter./.*-linux-*, not explicit triples. E.g. /usr/lib/*-linux-*/libfoo.so.1:.* for single-version sonames: libfoo.so.1: not libfoo.so.1*:.{arch: [amd64, arm64]} -- not {arch: [ amd64, arm64 ]}./path: {arch: [amd64, arm64]}./usr/bin/foo: # Symlink to ../lib/foo/foo.Testing is mandatory. Depth depends on what the package provides.
Testing blocks commit. Do NOT proceed to Step 10 (commit) without tests landed. A feat: slice and its test: tests form one series -- both must exist before you stop. If tests aren't feasible yet (e.g. waiting on user clarification), leave the slice uncommitted and stop; do not commit the slice alone.
libssl3, libc6): verify .so files exist and are valid ELF. Minimal testing acceptable.grep, sed): test --version + one representative functional test.python3, nginx, curl, git): requires a thorough test suite.Use the bundled scripts/try-cut helper to run a cut from the current checkout without managing the temp root manually:
scripts/try-cut [--arch ARCH] <package>_<slice>
Or manually:
mkdir rootfs/
chisel cut --release ./ --root rootfs/ <package>_<slice>
For applications, CLI tools, servers, interpreters -- any package providing user-facing functionality:
Research phase (before writing tests):
test*/, tests/, t/ directories./etc/passwd, /tmp, timezone data, locale data, etc.?Write the test suite at tests/spread/integration/<package>/task.yaml:
summary: Integration tests for <package>
execute: |
# Test 1: Basic invocation
rootfs="$(install-slices <package>_bins)"
chroot "${rootfs}/" <command> --version
# Test 2: Core functionality
# (for curl: fetch a URL; for python3: import core modules; for vim: edit a file)
# Test 3: Configuration
# (verify config files are picked up)
# Test 4: Key features
# (test primary use cases)
Test design principles:
bins slice must prove its binaries actually work.bins and scripts, both need tests.bins slice must be exercised. Reviewers reject untested binaries.Run with: spread lxd:tests/spread/integration/<package>
Precondition: verify tests/spread/integration/<pkg>/task.yaml exists and passes (spread lxd:tests/spread/integration/<pkg>). If missing or failing, stop -- do not commit a feat: slice without working tests.
Commit in two steps (one category per commit): the feat: slice first, then the test: tests. Both must land before you stop.
git -C <repo> commit -m "feat(<pkg>): add <slice-list> slices"
Follow conventional commits: feat:, fix:, test:, ci:, chore:, docs:. Subject lowercase, imperative, <=50 chars, no trailing period. Body wrap 72.
Stop here. User opens PR themselves.
Reminder: all PRs must be forward-ported oldest -> newest across all maintained release branches.
package: vim-tiny
essential:
- vim-tiny_copyright
slices:
bins:
essential:
- libacl1_libs
- libc6_libs
- libselinux1_libs
- libtinfo6_libs
- vim-common_addons
- vim-common_config
- vim-tiny_config
contents:
/usr/bin/vim.tiny:
config:
contents:
/etc/vim/vimrc.tiny:
copyright:
contents:
/usr/share/doc/vim-tiny/copyright:
package: libc6
essential:
- libc6_copyright
slices:
config:
contents:
/etc/ld.so.conf.d/*-linux-*.conf:
libs:
essential:
- base-files_lib
contents:
/usr/lib*/ld*.so.*:
/usr/lib/*-linux-*/ld*.so.*:
/usr/lib/*-linux-*/libc.so.*:
/usr/lib/*-linux-*/libdl.so.*:
/usr/lib/*-linux-*/libm.so.*:
/usr/lib/*-linux-*/libmvec.so.*: {arch: [amd64, arm64]}
/usr/lib/*-linux-*/libpthread.so.*:
/usr/lib/*-linux-*/libresolv.so.*:
/usr/lib/*-linux-*/librt.so.*:
copyright:
contents:
/usr/share/doc/libc6/copyright:
package: ca-certificates
essential:
- ca-certificates_copyright
slices:
data:
essential:
- openssl_data
contents:
/etc/ssl/certs/ca-certificates.crt: {text: FIXME, mutable: true}
/usr/share/ca-certificates/mozilla/: {until: mutate}
/usr/share/ca-certificates/mozilla/**: {until: mutate}
mutate: |
certs_dir = "/usr/share/ca-certificates/mozilla/"
certs = [
content.read(certs_dir + path) for path in content.list(certs_dir)
]
content.write("/etc/ssl/certs/ca-certificates.crt", "".join(certs))
copyright:
contents:
/usr/share/doc/ca-certificates/copyright:
package: dpkg
essential:
- dpkg_copyright
slices:
bins:
essential:
- diffutils_bins
- dpkg_config
- dpkg_tables
- dpkg_var
- libbz2-1.0_libs
- libc-bin_ldconfig
- libc6_libs
- liblzma5_libs
- libmd0_libs
- libselinux1_libs
- libzstd1_libs
- tar_tar
- zlib1g_libs
contents:
/usr/bin/dpkg:
/usr/bin/dpkg-deb:
/usr/bin/dpkg-divert:
/usr/bin/dpkg-maintscript-helper:
/usr/bin/dpkg-query:
/usr/bin/dpkg-realpath:
/usr/bin/dpkg-split:
/usr/bin/dpkg-statoverride:
/usr/bin/dpkg-trigger:
/usr/bin/update-alternatives:
/usr/libexec/dpkg/*:
/usr/sbin/start-stop-daemon:
config:
contents:
/etc/dpkg/dpkg.cfg:
/etc/dpkg/dpkg.cfg.d/:
tables:
contents:
/usr/share/dpkg/*table:
var:
contents:
/var/lib/dpkg/alternatives/:
/var/lib/dpkg/info/:
/var/lib/dpkg/parts/:
/var/lib/dpkg/updates/:
copyright:
contents:
/usr/share/doc/dpkg/copyright:
Recommends:/Suggests: in essential:. Only Depends: matter. Reviewers reject the rest.ldd output..deb contents alone.libs not lib, bins not bin, etc./usr/lib/python3.*/foo/** might conflict with other packages. Be specific.copyright slice. Every SDF must have one; every other slice must depend on it.postinst creates symlinks or generates files, your slices must do that too.symlink: for paths the deb already ships. Chisel preserves deb symlinks; only use symlink: for paths created by maintainer scripts.{arch: ...} for arch-specific paths.--version alone is not sufficient for applications. Every binary in bins must be exercised.After the work is fully complete (SDFs written, tests passing, commit made), propose to the user that they review the result against the official chisel documentation. The chisel-docs are the authoritative source of truth on how to write slices.
Present this to the user:
The slices are committed. Before opening a PR, I'd recommend we check the result against the official chisel documentation to make sure everything aligns. Want me to fetch the current docs and compare?
If the user accepts, perform the following checks:
Fetch the current upstream documentation and compare the authored SDFs against it:
# The authoritative slicing guide
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-docs/main/docs/how-to/slice-a-package.md
# SDF format reference
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-docs/main/docs/reference/chisel-releases/slice-definitions.md
# chisel.yaml reference (schema version rules)
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-docs/main/docs/reference/chisel-releases/chisel.yaml.md
# Slice design approaches
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-docs/main/docs/explanation/slice-design-approaches.md
Check and report to the user:
mutate: functions in the docs that could improve the result?format: version on the target branch compatible with all features used?Compare the output against canonical reference SDFs on the target branch:
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-releases/<branch>/slices/bash.yaml
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-releases/<branch>/slices/base-files.yaml
curl -fsSL https://raw.githubusercontent.com/canonical/chisel-releases/main/CONTRIBUTING.md
Check and report:
If anything behaved unexpectedly during chisel cut (a field was ignored, a wildcard didn't match, mutate ran differently than documented):
curl -fsSL https://raw.githubusercontent.com/canonical/chisel/main/internal/setup/setup.go
The tool's actual behaviour overrides any written convention.
If the review found discrepancies between the docs and this skill's guidance, update the relevant file:
@./CHISEL.mdwrite-slice/SKILL.md)review-slice/SKILL.mdWhen updating, follow these principles:
hint: validation; using it on v1 branches causes a parse error").Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub rockcrafters/mason --plugin mason