--- name: actionstate-install description: Install Action State Receipts on this machine (capsulectl v0.1.0-rc22, the deal skill, the Action State plugin 0.0.17 and its actionstate-rules skill, rules pack asg/everyday/0.3.8), check every file's SHA-256, and write the person's rules table. Use when the person asks to install Action State Receipts or "install actionstate ai", and when they ask to uninstall Action State. Linux x86_64 only. --- # Install Action State Receipts (install skill rc22-p0.0.17-s0.0.17-r1) This skill installs Action State Receipts on this machine, on the person's request. The steps below run in order, and each one states its check. A failed check stops the install at that step; step 13 gives the result either way. A failure is never worked around, no check is skipped, and nothing piped from the network runs in a shell. This is install skill rc22-p0.0.17-s0.0.17-r1. Its SHA-256, every SHA-256 below, and the versions and URLs, are also in `https://actionstate.ai/install/rc22-p0.0.17-s0.0.17-r1/release.json`, this install's machine file, which the script reads first (step 2). What gets installed: **capsulectl** v0.1.0-rc22 (commit `e66369627c41ff54a8318b39b7d835c16181beba`), a command-line program, and the **deal** skill, the procedure an agent follows before it pays, books, signs, cancels or shares the person's details. Every step is sealed on this machine as a receipt the person can check. Nothing of the receipts' content leaves the machine. Step 11 also adds the **Action State plugin** 0.0.17, which gives capsulectl the `capsulectl actionstate` commands, and its skill, **actionstate-rules**, and sets the plugin as the rules checker every receipt check runs, with the rules pack `asg/everyday/0.3.8` (and `asg/seller/0.1.5` when the person sells), and pins the plugin's wording on the deal profile, so approval cards carry Action State's tags. The install also writes one standing rule (step 9). Action State rules run on Linux x86_64 only: on any other device, step 1 stops the install and nothing is installed. The words of the result: **rules** are what the person permits (the rules table of step 12); **receipts** are what happened (each purchase or other commitment, sealed: what they asked, what was proposed, what they approved, what was done, and any difference). The skill names (`deal`, `actionstate-rules`) appear only in the result's install details; above its `**Install details**` line it uses these plain words. What the install trusts: GitHub to deliver the files from `github.com/action-state-group/capsule-cli`, GitHub's build attestation for that repository (when gh can check it, step 3), and the SHA-256 values in this skill, which release.json also lists. For the plugin, it trusts actionstate.ai alone (step 11). The install asks no name: the rules record is labelled `actionstate-rules` (step 12). The sites the install reaches are the hosts of the URLs in release.json, the GitHub hosts those downloads are served from, and Sigstore's and GitHub's trust roots when gh checks step 3; this skill grants no site. ## Install with one script One script runs the numbered steps below, in the same order, and checks every file before it installs anything. The script's SHA-256 is in this skill and in release.json, on the same site the script comes from, so it shows the download arrived intact; it does not show that this site was not tampered with. Inside it, capsulectl is still checked against GitHub's build attestation when gh can check it (step 3); the plugin and both skills are checked by their SHA-256 only, as in the steps one at a time. 1. Step 1's check, `[ "$(uname -s)/$(uname -m)" = Linux/x86_64 ]`, runs first. If it fails, the install stops as step 1 says: nothing is installed on this device. (The script checks this first too, and installs nothing anywhere else.) 2. `SKILLS_DIR` is the folder the agent host loads skills from. The work folder is a new one, for example `~/actionstate/checkup-`; everything below runs in it. The two files for the person's rules table come **first**: download `https://actionstate.ai/plugin/actionstate-rules-skill-v0.0.17.tar.gz`, check that its SHA-256 is `f305124f3234039bc3469a75edde3a247664f7ea24d167d7307a841ea7c1751b`, unpack it **outside** `SKILLS_DIR`, and write `envelope.json` and `comparison.json` into the work folder with a file tool, as its `actionstate-rules/SKILL.md` Step 1 and Step 2 say. Neither file goes into the chat. 3. These three lines, each as written, with `SKILLS_DIR` replaced by the folder. The script is never piped into a shell. The third line takes `--repin-earlier-install` only after the person's yes to connecting an earlier install's rules to this version (step 11). ``` curl -fsSLO https://actionstate.ai/install/rc22-p0.0.17-s0.0.17-r1/install.sh && curl -fsSLO https://actionstate.ai/install/rc22-p0.0.17-s0.0.17-r1/install.sh.sha256 [ "$(awk '{print $1}' install.sh.sha256)" = 93ef0aa3bdfb1a1a7dfafaeab65410cec04c45a761b11661dc329c542ad9ee52 ] && sha256sum -c install.sh.sha256 sh install.sh --skills-dir SKILLS_DIR --envelope envelope.json ``` The second line compares the first field of `install.sh.sha256` with this skill's value, `93ef0aa3bdfb1a1a7dfafaeab65410cec04c45a761b11661dc329c542ad9ee52`, then checks the script against it (on a Mac without `sha256sum`, `shasum -a 256 -c install.sh.sha256`). If it fails, the script does not run: the install stopped (step 13). What the script prints: - **Progress**, lines `step NN [step-NN-...]: +Ns ...`, where `N` is the seconds since that step began. Each id is the id of the step of the same number below. They are progress records, not part of the result. The `step 03` line holds the provenance words of step 3, for the record. The script first reads `https://actionstate.ai/install/rc22-p0.0.17-s0.0.17-r1/release.json`. A `step 02` line that starts `WARNING` has one of two meanings. If it says release.json and the script differ, release.json names another install, capsulectl or plugin version than the script's own. If it says release.json could not be read, the summary records no release file. Either way the script goes on, and that fact is said first, in a message of its own before the result: the install's release file names other versions than the ones that ran, or it could not be read. - **The result**, one block, between `==== RESULT ====` and `==== END ====`. If the script exited 0, it is the short message the plugin wrote to `report/ready.md`: the person's spending limits, what their agent can do, what it asks them about first and, when the rules page is to be attached, a line saying the attached page has their full rules. The lines between the two markers, as printed, are the first part of step 13's result. The rules table itself is on the rules page, not in this block. - **If the script exited non-zero**, the install stopped, and the block names the step that stopped: the step of the same number in this skill. The result is step 13's result for an install that stopped. Three cases differ. If step 01 stopped it (its `install.sh:` line on stderr names `step-01-platform`), the result is step 1's. If its `install.sh:` line says `earlier-install pin`, an earlier Action State install pinned the rules checker, and the line gives the earlier versions: the pin is unchanged, and the result is step 13's earlier-install pin, held for the person's answer; only their yes to connecting their rules to this version re-pins it (step 11). With that yes, the third line runs once more with `--repin-earlier-install` added; on exit 0 the rules are connected, and the block is the result as above. If its `install.sh:` line says `capsulectl plugin install deal failed` and carries a `chmod go-w` line: that line removes other users' write access to the folders it names, which capsulectl requires before it installs a plugin (step 4); the install runs that line, then the script again. - **The evidence**, in `~/.local/share/actionstate/install-summary.json`: the release.json the script read (`page`: its address, SHA-256 and ETag), what was installed, and each file's SHA-256. For each file, how it was verified: `provenance` is `attested` or `checksum_only`, and `provenance_reason` says why (`attestation_verified`, `gh_absent`, `gh_too_old`, `gh_login_prompt`, or `no_attestation_published`). Then the exactly-one results, the `capsulectl plugin ls` output, the witness block, the first tick and its `cadence_log`, `warnings` (empty unless the first checkpoint was not delivered: then one `first_tick` entry per pending reason other than `not_attempted`, such as `witness_error`; the install still counts as installed), the rules checker pinned on the deal profile (`rules_checker`: the packs, their digests and the plugin's SHA-256), both doctors (the script runs each once, at the end), how long each step took (`steps`: each step's id and seconds, in the order run), and the report (`report`: the page, the bundle, its root, and the `capsulectl verify --bundle` verdict, which must be `VALID`). Every checksum and check of the install is quoted from this file, never from memory, and none is in the block. The rules page, when the host can attach a file, is exactly the path the script's `step 12` output printed after `Attach this file:`, and nothing if it printed none. It goes in a message of its own after the result (step 13, "How it is sent"). If the summary's `rules_profile.new_record` is not empty (step 12 started the rules record again in that file), Setup's sentence for a new record goes in its own message after the result and the rules page, before the first Checkup (step 13). The result's last part is the `**Install details**` line and the three lines and the pointer of step 13's part 2, filled from `~/.local/share/actionstate/install-summary.json`, never from memory: no hash, no JSON. After the script ends with its result, the checkpoint cadence of step 8 is set up (its scheduled command is never run directly: step 8 checks the task without waiting) and the standing rule of step 9 is saved, on a reinstall too. Without `--envelope`, the script ends after the install with a line naming the summary file; step 12 is then done one step at a time. ## Or run the steps one at a time These are the steps the script runs, one by one, in the same order. Steps 9 and 13 are never the script's. ## 1. Find the platform {#step-01-platform} Run this first, before anything else: ``` [ "$(uname -s)/$(uname -m)" = Linux/x86_64 ] ``` If it exits 0, PLATFORM is `linux-amd64`: go on to step 2. If it exits non-zero, **the whole install stops here**: nothing is installed or downloaded, capsulectl and the deal skill on their own included. The result is that this device is DEVICE (what it is, in plain words, for example "a Mac with Apple silicon"), that Action State rules need a Linux computer with an Intel or AMD (x86_64) processor, and that nothing was installed. With the script, it is the `==== RESULT ====` block's text, as printed. ## 2. Download, and check the SHA-256 {#step-02-download} In a new empty folder, download these two files, and do not run anything yet: ``` https://github.com/action-state-group/capsule-cli/releases/download/v0.1.0-rc22/capsulectl-v0.1.0-rc22-PLATFORM https://github.com/action-state-group/capsule-cli/releases/download/v0.1.0-rc22/capsulectl-v0.1.0-rc22.sigstore.json ``` Compute the SHA-256 of both files. Each must equal the value here; if either differs, delete both and stop. Remember the binary's SHA-256, in full: step 13's record holds it. | File | SHA-256 | |---|---| | `capsulectl-v0.1.0-rc22-linux-amd64` | `a870a6128bb8bee5fd537ef7b418158de5d3cd81743a9c5e80fefe74573511ed` | | `capsulectl-v0.1.0-rc22.sigstore.json` | `ff73abc0d2613ea4ef3eb62359742a43da7c3fefd5a402238bf5c5f9d81ee292` | ## 3. Check where it was built, if you can {#step-03-provenance} gh is optional, and used only for this check. The install needs no new or updated gh and no GitHub sign-in, so it leaves gh as it finds it. The check needs no account: if gh suggests `gh auth login`, that login is not needed and is skipped. Run `command -v gh`, then `gh attestation verify --help`, and follow the one case that applies. The verify command, used in case B: ``` gh attestation verify capsulectl-v0.1.0-rc22-PLATFORM --bundle capsulectl-v0.1.0-rc22.sigstore.json --repo action-state-group/capsule-cli --signer-workflow action-state-group/capsule-cli/.github/workflows/release.yml ``` It exits 0 when verified, and may print nothing. In case B, gh reads Sigstore's trust roots from **tuf-repo-cdn.sigstore.dev** and **tuf-repo.github.com** to check the build's signature; nothing is downloaded or installed from them. - **A. No gh you can use:** gh is not installed (`command -v gh` finds nothing), or it is too old: its `gh attestation verify --help` does not list both `--bundle` and `--signer-workflow`. The install leaves gh as it is. The SHA-256 from step 2 is the only check; go on. Remember the words "provenance: checksum only (gh not installed)" or "provenance: checksum only (gh too old)", whichever applies. - **B. gh lists both flags:** run the verify command, signed in or not. - It exits 0: remember "provenance checked: attestation verified (bundle); checksum matched". - It exits non-zero and its message only asks you to sign in or authenticate: the sign-in is not needed and is skipped. Continue on the SHA-256 alone, and remember "provenance: checksum only (gh asked to sign in)". - It exits non-zero for any other reason, the attestation did not verify: delete the files and stop. Whichever case applied, step 13's install details give it in the plain words step 13 lists: only "build attestation verified" means the build attestation was verified; otherwise only the checksum was. ## 4. Install the binary {#step-04-binary} If a `capsulectl` is already where you will install this one (the one `command -v capsulectl` finds, or `~/.local/bin/capsulectl`), run it with `--version` first. If it names a release newer than v0.1.0-rc22, names no release, or names one you cannot order against v0.1.0-rc22, stop and change nothing: once a newer capsulectl has opened the receipts store, an older one must not run on it. Releases order as SemVer: `v0.1.0-rc16.1` is newer than `v0.1.0-rc16` and older than `v0.1.0-rc17`, which is older than `v0.1.0`. Otherwise keep a copy of it, and of `~/.local/lib/capsulectl/plugins/capsulectl-deal` if there is one, until the deal plugin below is in place; if you cannot, stop and change nothing. Install the file as `capsulectl` with mode 0755 in `~/.local/bin` (make the folder if it is missing; replace an existing `capsulectl` at `command -v capsulectl` if there is one, in its own folder). If that folder is not on your PATH, use its full path, `~/.local/bin/capsulectl`, wherever a step below says `capsulectl`, and give the plugin that path as `CAPSULECTL_BIN` in the environment of its commands: the install never changes the shell's PATH. After that, `capsulectl --version` must print exactly: ``` capsulectl v0.1.0-rc22 (commit e66369627c41ff54a8318b39b7d835c16181beba) ``` Then install the deal plugin from the same release: ``` capsulectl plugin install deal ``` It downloads `https://github.com/action-state-group/capsule-cli/releases/download/v0.1.0-rc22/capsulectl-v0.1.0-rc22-plugin-deal-PLATFORM`, checks it against the release's SHA256SUMS, runs `gh attestation verify` on it when gh is on PATH, and puts it in `~/.local/lib/capsulectl/plugins/capsulectl-deal`. If step 3 found gh too old or asking to sign in, run this command with gh's folder left out of PATH, so the plugin is checked by its checksum, as the binary was. capsulectl itself tightens any folder above that one that other users can change, when the person owns it under their home. If it still refuses (`not healed: …`), it prints a `chmod go-w` line for those folders: run that line, then the command again. The SHA-256 of `~/.local/lib/capsulectl/plugins/capsulectl-deal` must be `4c34846617b0b5c6325b1a77d7513b1a9fb79cfb68a410c742038d91d1c8f5a1`. If the command fails for any other reason, or the SHA-256 differs, put back the capsulectl and deal plugin you kept a copy of (where there was none, remove the one this install put there) and stop, saying which of them you could not put back. Once the deal plugin is in place, delete those copies. ## 5. Install the deal skill, with no second copy {#step-05-deal-skill} Find the folder your agent host loads skills from; call it `SKILLS_DIR`. A host that has never loaded a skill has none yet: create it (on Muse, `mkdir -p ~/workspace/skills`). 1. Download `https://github.com/action-state-group/capsule-cli/archive/refs/tags/v0.1.0-rc22.tar.gz`. Its SHA-256 must be `17e689a94cac45465a07f7c0de9326a1f37c24a19001ec0702261d66dc0cfd7b`; if not, stop. 2. Unpack it **outside** `SKILLS_DIR` (a new temporary folder). 3. If `SKILLS_DIR/deal` already exists, or any other folder under `SKILLS_DIR` holds a deal skill (a renamed backup such as `deal.bak` included), **move** that folder out of `SKILLS_DIR` entirely, to a backups folder beside it (for example `SKILLS_DIR/../_backups/deal-`). Never rename it in place or leave any copy inside `SKILLS_DIR`: every SKILL.md under that folder can be loaded, and an old copy there can be the one that gets followed. 4. Copy the unpacked `skills/deal` folder to `SKILLS_DIR/deal`, then delete the unpacked archive and the download. 5. Check there is exactly one deal skill. Run this exactly, with `SKILLS_DIR` replaced by the folder; it must exit 0: ``` n=$(find SKILLS_DIR -name SKILL.md -exec grep -lx 'name: deal' {} + | tee /dev/stderr | wc -l); [ "$n" -eq 1 ] || { echo "FAIL: $n deal skills"; false; } ``` It must print one line, `SKILLS_DIR/deal/SKILL.md`. If it fails, any extra folder moves out of `SKILLS_DIR` and the check repeats; stop if it still fails. Remember its exit code (0) for step 13. ## 6. Create the deal profile, with the witness {#step-06-deal-profile} ``` capsulectl --profile deal deal init --dir ~/.local/share/capsule-deal --materiality builtin:neutral capsulectl profile update --profile deal --checkpoint-endpoint https://witness.agentactioncapsule.org --checkpoint-public-key 39bb654c9dc0afe1c0edef0deffaa69099b8518836c9ba26e0491535840f96b5 ``` `deal init` creates a local store and two signing keys (mode 0600). The second command sets the witness, its address **and** its public key in one command: always both. If a deal profile already exists, skip `deal init` and run only the second command. `--materiality builtin:neutral` pins the materiality predicate, by name, to the new profile: a quantity of 1 that you fill in no longer pauses a check; a different quantity, or a size, variant or delivery you pick, still does. Then check it: ``` capsulectl --profile deal doctor ``` Its `profile.materiality` must say `"ok":true` and the digest `05ec66b4a21025a66288cafc74e7fa1c540cc62695c5f536df84f4a7d7f66d4a`. If a deal profile already existed: - **No predicate** (`"configured":false`): pin it, and the doctor runs once more: `capsulectl profile update --profile deal --materiality builtin:neutral` - **That digest already:** change nothing. - **Another digest:** it is the person's own. Change nothing; step 13's "What runs from now on" line records it. ## 7. Reach the witness {#step-07-witness} The next two commands are the first to reach the witness, **witness.agentactioncapsule.org**. The check: ``` capsulectl doctor --profile deal --check-witness ``` Its output has a `witness` block. It must show `"ok": true`; keep the whole block for step 13. If `ok` is false, its `issue` and `consent` fields say why. An issue on this computer is fixed and the check repeated. A network grant they name is not this install's to make: the install is not complete (step 13). ## 8. The first tick, then the checkpoint cadence {#step-08-first-tick} Run once, now: ``` capsulectl --profile deal cll checkpoint cadence ``` The output's `state` should be `ticked`, with the checkpoint under `delivered`. Keep the output's `cadence_log` value (the id of this profile's cadence log) for step 13. If an entry under `pending` has the reason `network_consent_needed`, the host has no standing grant for the witness: the install is not complete (step 13). A `pending` entry with `not_attempted` or `witness_error` is retried at a later tick once its backoff has passed; the record holds it with its reason. Then the schedule. Its line is in `~/.config/actionstate/cadence-schedule`: the install script writes it in this step, with capsulectl's full path. One step at a time, write it yourself, with the capsulectl of step 4 (found on PATH, or else the one step 4 put in `~/.local/bin`): ``` mkdir -p -m 0700 ~/.config/actionstate && printf "*/5 * * * * '%s' --profile deal cll checkpoint cadence --wait-up-to 5m >> '%s/checkpoint-cadence.log'\n" "$(command -v capsulectl || echo "$HOME/.local/bin/capsulectl")" "$HOME" > ~/.config/actionstate/cadence-schedule cat ~/.config/actionstate/cadence-schedule ``` The line must name capsulectl by its full path (for example `'/home/you/.local/bin/capsulectl' --profile deal ...`), never a bare `capsulectl`: a scheduler's PATH often lacks `~/.local/bin`, and the task would run nothing. Set up a scheduled task named **Action State checkpoint cadence** that runs exactly that line. With cron, add it to the crontab as it is: `(crontab -l 2>/dev/null; cat ~/.config/actionstate/cadence-schedule) | crontab -`. With your host's scheduler, run the line's command (everything after `*/5 * * * * `, the full path and the `>> ...checkpoint-cadence.log` included) every 5 minutes. Each run appends its output, one JSON line, to `~/checkpoint-cadence.log`, waits for a tick due within its 5 minutes and publishes it on time: every 5 minutes, give or take 1, never on deal activity. A run with no tick due publishes nothing. Keep `--wait-up-to 5m`: without it a scheduled run publishes on the scheduler's grid and hides the jitter. If your host caps how long a scheduled job may run, schedule more often with a matching smaller `--wait-up-to`, and write the line you scheduled to `~/.config/actionstate/cadence-schedule`, so the file always holds the line that runs. The scheduled command is the scheduler's to run, and the install does not run it, not even once to test the task, because with `--wait-up-to 5m` it is built to block, staying alive up to 5 minutes to wait for the next tick, and the install would wait with it. The install checks the task without waiting instead: confirm your host's scheduler lists **Action State checkpoint cadence** with the line above, and after that, without `--wait-up-to`: ``` capsulectl --profile deal cll checkpoint cadence ``` It polls once and returns at once: `state` `not_due` when no tick is due yet, and it publishes only a tick that is due. After the task's first run, the first line of `~/checkpoint-cadence.log` is that run's output. This check is the agent's own and is not part of the result: the scheduled task's card is the only word on the checkpoint. ## 9. The standing rule {#step-09-memory .agent} This rule is part of what the person asked to install. Write it to your long-term memory or standing instructions, the place that applies to every future conversation, because a rule kept only in this conversation is gone in the next one. Write it on a reinstall too, since an uninstall removes it; if it is already saved there, keep one copy. Write CAPSULECTL as capsulectl's full path from step 4, written out in full, for example `/home/you/.local/bin/capsulectl` (with the script, the summary's `artifacts[0].path`). The rule names the program by its path, so it runs the capsulectl this install checked whatever PATH a later conversation has; the install never changes PATH or a shell profile. ```text When the person first asks for something that may end in paying, placing an order, booking, confirming, signing, cancelling, or sharing their contact details, address or a login with anyone (a known shop at a fixed price included), I follow the deal skill's procedure from that first ask and open the deal with `CAPSULECTL deal open --skill ...`. Before I do any of those things, however the errand began, I run `CAPSULECTL deal check ...` as the final review, opening the deal first if it is not open yet. Any request for the person's go-ahead uses the check's approval_text, as written. I do it only when the check, or the person's sealed answer to it, returns "proceed": true. If a capsulectl command fails, I hold and say so. ``` ## 10. Check the whole install {#step-10-install-check} Run, with `SKILLS_DIR` as above; it must exit 0: ``` capsulectl doctor --install-check --profile deal --skills-dir SKILLS_DIR --expect-version v0.1.0-rc22 --expect-commit e66369627c41ff54a8318b39b7d835c16181beba --expect-skill-sha256 ba05a20f71692fc104d9fbd8eb1bd7c1f18b836774a61fd69fa7c274b3d9f74b ``` It checks the binary's version and full commit, that exactly one deal skill is visible and it is this release's SKILL.md, and that the profile names the witness and its key. If it exits non-zero, each issue it names is fixed and the check repeated. ## 11. The Action State plugin {#step-11-plugin} The Action State plugin is not yet covered by a build attestation. capsulectl above is: you verify it against a signed bundle, and that check needs no account. The plugin is verified by its SHA-256 only. That checksum is published on actionstate.ai, the same place the file comes from, so it shows the download arrived intact; it does not show that the site itself was not tampered with. Attestation for the plugin is coming. Until then, the plugin is trusted on Action State's own word. The version is in the file name, so a newer version is a new file with its own SHA-256 in a newer install skill. To update later, run the lines from that version's install skill; never reuse a file you downloaded under another name. Run these six lines in order, each as written, with `WORDING` in the sixth replaced as below. If any line fails, stop the plugin here and install nothing more: the install is not complete (step 13). ``` [ "$(uname -s)/$(uname -m)" = Linux/x86_64 ] && mkdir -p ~/.cache/actionstate-plugin && cd ~/.cache/actionstate-plugin && rm -f verified capsulectl-actionstate-linux-amd64-v0.0.17 capsulectl-actionstate-linux-amd64-v0.0.17.sha256 && curl -fsSLO https://actionstate.ai/plugin/capsulectl-actionstate-linux-amd64-v0.0.17 && curl -fsSLO https://actionstate.ai/plugin/capsulectl-actionstate-linux-amd64-v0.0.17.sha256 cd ~/.cache/actionstate-plugin && sha256sum capsulectl-actionstate-linux-amd64-v0.0.17 && { [ "$(awk '{print $1}' capsulectl-actionstate-linux-amd64-v0.0.17.sha256)" = 7c2c1b16c4ef7ed74f6779b16bd41857b29f2d28db2844c3195ca6837cb6417e ] || { echo "FAIL: capsulectl-actionstate-linux-amd64-v0.0.17.sha256 does not say this skill's SHA-256"; false; }; } && sha256sum -c capsulectl-actionstate-linux-amd64-v0.0.17.sha256 && cp capsulectl-actionstate-linux-amd64-v0.0.17 verified install -d -m 0755 ~/.local/lib/capsulectl/plugins && chmod 0755 ~/.local/lib/capsulectl/plugins && install -m 0755 ~/.cache/actionstate-plugin/verified ~/.local/lib/capsulectl/plugins/capsulectl-actionstate out=$(capsulectl plugin ls) && printf '%s\n' "$out" && printf '%s\n' "$out" | sed 's/{"name":/\n{"name":/g' | grep "\"path\":\"$HOME/.local/lib/capsulectl/plugins/capsulectl-actionstate\",\"plugin_api\":" | grep -q "\"version\":\"0.0.17 (commit " || { echo "FAIL: capsulectl did not load the plugin"; false; } capsulectl actionstate presentation install --json && out=$(capsulectl plugin ls) && case $out in *'"presentations_refused"'*) printf '%s\n' "$out"; echo "FAIL: capsulectl refused the plugin's page modules"; false ;; esac S=$(capsulectl profile show deal) && P=$(printf '%s' "$S" | sed -n 's/.*"Wording":{"Path":"\([^"]*\)".*/\1/p') && case ${P##*/} in ''|actionstate.capsulectl-en-*) capsulectl profile update --profile deal --wording WORDING ;; *) echo "kept: the deal profile pins another wording pack, $P" ;; esac && capsulectl profile show deal ``` What they do: 1. Check the platform again (it fails anywhere but Linux x86_64), then download the plugin and its `.sha256` into `~/.cache/actionstate-plugin`, first removing anything an earlier run left there. Nothing runs yet. 2. Print the file's SHA-256 (remember it, in full, for step 13), check that the digest in the `.sha256` is `7c2c1b16c4ef7ed74f6779b16bd41857b29f2d28db2844c3195ca6837cb6417e`, the value in this skill (it compares the first field, so spacing does not matter), and check the file against it with `sha256sum -c`. Only then is a copy named `verified` made; if either check fails, the line names the file and stops. 3. Install `verified` as `~/.local/lib/capsulectl/plugins/capsulectl-actionstate`, mode 0755. capsulectl only loads a plugin when that file and the `plugins` folder are owned by the person (or root) and writable by nobody else, so the line sets the folder to 0755 even if it already existed. If line 2 failed, there is no `verified` file and nothing is installed. 4. Check that capsulectl loads the plugin from that folder, at version 0.0.17 (the plugin's own entry is cut out of the list first: its page modules are listed inside it). It must exit 0. If it fails, read the printed output: a launcher under `"refused"` comes with the `reason` capsulectl refused it; a `capsulectl-actionstate` under `/usr/local/lib/capsulectl/plugins` is loaded first, and replaces this one. Stop: the install is not complete (step 13). Nothing outside `~/.local/lib/capsulectl` changes. 5. Install the plugin's page modules, which the report and receipt pages are built with, beside the plugin (`~/.local/lib/capsulectl/plugins/capsulectl-actionstate.d`), and check capsulectl accepts them: `plugin ls` lists no `presentations_refused`. Without them the plugin's install check (step 12) reports `presentation-module-missing`. `presentation install --json` prints one JSON object whose `voice` is `line`: when line 5 exited 0, send its `say` to the person, word for word, as ordinary text. 6. Pin the plugin's capsulectl wording pack on the deal profile, so the approval cards capsulectl shows carry Action State's tags, such as "ACTION STATE · PAUSED FOR YOU". `WORDING` is the `capsulectl_wording.path` in line 5's JSON: the pack line 5 wrote beside the plugin, under `capsulectl-actionstate.d/wording`. Its file name holds its digest, so a later plugin writes its own pack beside it, never over it. capsulectl pins only a pack that holds every key of its built-in pack, which is why this plugin and this capsulectl come in one install. The line first reads the deal profile, and stops there if `profile show` fails. Then it takes the profile's `Wording` `Path` and follows the one case that applies: - **No `Wording`, or a `Path` whose file name starts `actionstate.capsulectl-en-`:** there is no pin yet, or it is an Action State install's. The line pins `WORDING`, and in the `profile show` output, `Wording` must show that `Path`. An update runs all six lines too, so line 6 pins the new install's pack in place of the earlier one. - **Any other `Path`:** it is the person's own pack. Change nothing. Do not run `profile update`: the line prints `kept:` and that path, and the install goes on. Line 6 runs before step 12's install check, which reports `wording-pin-stale` while the deal profile pins an earlier install's pack, and `wording-pin-unreadable` when the pinned file is gone or changed. A pack of the person's own is not an issue there. Then install the plugin's skill, **actionstate-rules**, the instructions you follow when you use the plugin. Only do this after line 6 passed. It goes in the same `SKILLS_DIR` as the deal skill, the same way as step 5: 1. Download `https://actionstate.ai/plugin/actionstate-rules-skill-v0.0.17.tar.gz`. Its SHA-256 must be `f305124f3234039bc3469a75edde3a247664f7ea24d167d7307a841ea7c1751b`; if not, stop. Remember it, in full, for step 13. This archive has no build attestation either. 2. Unpack it **outside** `SKILLS_DIR` (a new temporary folder). It holds one folder, `actionstate-rules`. 3. If `SKILLS_DIR/actionstate-rules` already exists, or any other folder under `SKILLS_DIR` holds an actionstate-rules skill (a renamed backup included), **move** that folder out of `SKILLS_DIR` entirely, to a backups folder beside it (for example `SKILLS_DIR/../_backups/actionstate-rules-`). Never rename it in place or leave any copy inside `SKILLS_DIR`: an old copy there can be the one that gets followed. 4. Copy the unpacked `actionstate-rules` folder to `SKILLS_DIR/actionstate-rules`, then delete the unpacked archive and the download. 5. Check there is exactly one actionstate-rules skill. Run this exactly, with `SKILLS_DIR` replaced by the folder; it must exit 0: ``` n=$(find SKILLS_DIR -name SKILL.md -exec grep -lx 'name: actionstate-rules' {} + | tee /dev/stderr | wc -l); [ "$n" -eq 1 ] || { echo "FAIL: $n actionstate-rules skills"; false; } ``` It must print one line, `SKILLS_DIR/actionstate-rules/SKILL.md`. If it fails, any extra folder moves out of `SKILLS_DIR` and the check repeats; stop if it still fails. Remember its exit code (0) for step 13. Then the rules checker your purchases run. The actionstate-rules skill you just installed says (`SKILLS_DIR/actionstate-rules/SKILL.md`, "The rules checker your purchases run"): "At first install, the install pins a specific program, the Action State checker (this plugin, run as `check --emit external-check-result/v0`), to the person's installed rules: the pack id and definition digest that `capsulectl actionstate rules list --json` prints." The pin is never set, changed or taken off at any other time. So it is pinned only here, at install: when there is no pin yet, or, after the person's yes, when the pin is the one an earlier Action State install wrote (the branch below). Only do this after line 6 and the skill's check passed. The plugin carries two rule sets, and a deal's `party_role` picks one: a buyer's deal runs the installed rules, `asg/everyday/0.3.8`, and a seller's deal runs `asg/seller/0.1.5` (definition digest `81013aedbc3d930dcb9a4871d932373eca9de6968e5cff9fb450158d1fcc576a`). The pin names both digests as a set, `definition_digests`: a pin that names one leaves every check of the other role not evaluated, and the plugin's install check reports `rules-checker-role-not-covered` (step 12). First look at the deal profile's pin: ``` capsulectl profile show deal ``` - **`RulesChecker` has `"Command":null`: there is no pin yet.** Run these four lines in order, each as written, in the folder you work in: ``` capsulectl actionstate rules list --json > rules-list.json && D=$(sed -n 's/^ *"definition_digest": *"\([0-9a-f]\{64\}\)".*/\1/p' rules-list.json | head -n 1) && { [ "$D" = ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488 ] || { echo "FAIL: the installed rules are $D, not this skill's ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488"; false; }; } printf '{"command":["%s/.local/lib/capsulectl/plugins/capsulectl-actionstate","check","--emit","external-check-result/v0"],"definition_digests":["%s","81013aedbc3d930dcb9a4871d932373eca9de6968e5cff9fb450158d1fcc576a"]}\n' "$HOME" "$D" > rules-checker.json capsulectl profile update --profile deal --rules-checker rules-checker.json capsulectl profile show deal ``` The first reads the installed rules' digest from `rules list --json`, as the skill says, and checks it is this skill's, `ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488` (pack `asg/everyday/0.3.8`). The second writes the pin, with the plugin root written out in full and both digests: the one the first line read, then the seller's, `81013aedbc3d930dcb9a4871d932373eca9de6968e5cff9fb450158d1fcc576a`. The third pins it: capsulectl records the SHA-256 of the plugin file, and a later check whose plugin file changed pauses instead of running it. In the fourth, `RulesChecker` must show that `Command`, `SHA256` `7c2c1b16c4ef7ed74f6779b16bd41857b29f2d28db2844c3195ca6837cb6417e` (the plugin you installed), an empty `DefinitionDigest` and `DefinitionDigests` `["ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488","81013aedbc3d930dcb9a4871d932373eca9de6968e5cff9fb450158d1fcc576a"]`. - **`RulesChecker` already shows exactly that `Command`, `SHA256` and `DefinitionDigests`:** it is already this install's pin. Change nothing and go on. - **`RulesChecker` shows exactly that `Command`, but a different `SHA256`, other digests, or one `DefinitionDigest` (every install before this one pinned one):** an earlier Action State install pinned it, and this install has just replaced the plugin, so the old pin no longer matches and purchase checks would pause. The pin changes only on the person's yes to connecting their rules to this version. The install script stops here, before it changes the pin, and names the earlier versions; with the yes, it runs once more with `--repin-earlier-install`, which re-pins with these same four lines. - **With the yes:** the four lines under "no pin yet" above, in order, as written. The first still stops if the installed rules are not this skill's. In the fourth, `RulesChecker` must show the same `Command`, `SHA256` `7c2c1b16c4ef7ed74f6779b16bd41857b29f2d28db2844c3195ca6837cb6417e` and `DefinitionDigests` `["ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488","81013aedbc3d930dcb9a4871d932373eca9de6968e5cff9fb450158d1fcc576a"]`. Remember "re-pinned on your yes" for step 13. - **Without it:** change nothing more. Do not run `profile update`. The plugin stops here; step 13's result for this case says the rules are not connected. - **Any other pin** (a different `Command`): it is the person's own: the skill says "This skill never sets, changes or removes that pin." Change nothing. Do not run `profile update`. Stop the plugin here: the install is not complete (step 13 names the cause). Step 13's install details say the pin in their third line; the packs `asg/everyday/0.3.8` and `asg/seller/0.1.5` and their definition digests are in the install record. Remember the `RulesChecker` block for step 13. The plugin's own install check runs in step 12, after Setup, because it checks the `actionstate-rules` profile that Setup creates. It also reports the pin. ## 12. The rules table {#step-12-rules} Do this step only if step 11 installed the plugin and the actionstate-rules skill. If step 11 stopped, skip this step too, and remember "rules table skipped", with step 11's reason. You follow the actionstate-rules skill you installed in step 11 (`SKILLS_DIR/actionstate-rules/SKILL.md`). First its Setup, as written: the record's operator is always the label `actionstate-rules` (the same way the deal profile is labelled `deal`), and Setup takes no input. Setup first runs `capsulectl profile show actionstate-rules`. For this install, two more things: - If the profile exists, it must be able to hold the report: its `Type` is `sqlite`, its `LogID` is not empty and its `Checkpoint` has a signing key. If not, **stop here** and change nothing: the install is not complete (step 13), and the record was left as it is. - If it exists with any other `Operator`, Setup's two lines start a new record file named for today's date, `rules-YYYY-MM-DD.db`, beside the earlier one, which stays exactly as it is (never delete, move or change it). If a file of that name already exists, put `-2` (then `-3`, ...) before `.db` in the `profile update` line instead. Setup's sentence about it is part of step 13's result. Remember "rules record started again" for step 13. Setup's lines handle a seed an earlier install left; the skill says: "If `~/.config/actionstate/seed` already exists (an earlier install made it), the lines use it as it is and read its public key with `capsulectl key show-public`. Never replace, move, rename or regenerate it: records already sealed with it are checked against that key." A profile named `actionstate-local`, from an earlier install, is not used and does not stop the install. As the skill says, leave it as it is: do not update, reuse or delete it. It holds the records of earlier runs. After Setup, run `capsulectl profile show actionstate-rules`: its `Operator` must be `actionstate-rules`. If it is not, the install is not complete (step 13). Next, the plugin's own install check, with `SKILLS_DIR` replaced by the folder; it must exit 0: ``` capsulectl actionstate doctor --install-check --skills-dir SKILLS_DIR --expect-version 0.0.17 --expect-skill-sha256 d2e99dee912bb3b16b7a8b587d343bcd5d6197a292ba4d473ac3558c17f06fbb ``` It checks the plugin's version, that exactly one actionstate-rules skill is in `SKILLS_DIR`, and that `actionstate-rules` is a SQLite profile with a log and a checkpoint key, and the deal profile's rules-checker pin (step 11). Its `pack` must name `asg/everyday/0.3.8` with `definition_digest` `ac7697061fd1c50177000648e2458f6236068679fc67ae924870ee8fb1cd8488`, and its `rules_checker.covers` must be `["buyer","seller"]`; with capsulectl v0.1.0-rc22, a pin that covers one role is the issue `rules-checker-role-not-covered`. If it reports `rules-checker-wrong-command`, `rules-checker-wrong-digest` or `rules-checker-stale`, the install is not complete, and step 13 names the cause. The checker is never changed here. A note that a legacy profile `actionstate-local` is left untouched is not an issue. It prints one JSON object with a `voice`: `silent` when the install is whole; on any issue, `line`, with a `say` that the setup isn't finished and one line per kind of issue. If it exits non-zero, the install is incomplete: each issue it names is fixed and the check repeated; stop if it still fails. Remember its exit code, and the `say` of its last run, for step 13. Then the skill's Step 1 (the envelope file), Step 2 (the comparison file) and Step 3 run, in that order, under its rules. Step 3 runs: ``` capsulectl actionstate rules compare --envelope envelope.json --comparison comparison.json --profile actionstate-rules --out-dir sealed ``` The plugin writes the table, and the list under it, to `sealed/table.md`; the sealed record holds that file's SHA-256. The table is the plugin's, word for word. It is shown on the rules page, never in chat. Then make the person's report, a page that checks its digests and completeness when opened, as the skill's Step 3 says, and check its signatures with `capsulectl verify --bundle`. This install also installed the receipt side (step 5), so `report` runs with `--receipts`, which adds the sentence about receipts to the short message: ``` capsulectl actionstate report --profile actionstate-rules --from sealed --out-dir report --receipts capsulectl verify --bundle report/report.json ``` The plugin writes `report/report.json` (the bundle), `report/report.html` (the page), `report/ready.html` (the rules page: the full rules table, the spending limits and the report) and `report/ready.md` (the short message that is the first part of this install's result, step 13), and writes every word of all four. With capsulectl v0.1.0-rc22, the page is built with the plugin's page module (step 11, line 5). If `report/` already holds a report from an earlier run, it keeps that one and writes `report-2.json` and `report-2.html`, `ready-2.md` and `ready-2.html` beside it: use the paths it prints (after `Closing message:`, `Rules page:` and `Attach this file:`), here and in step 13. `capsulectl verify --bundle` must exit 0 with the verdict `VALID`; remember its verdict line for step 13. If it does not, the report is not ready: the install is not complete (step 13), and no part of the result points at the page. ## 13. The result, given without being asked {#step-13-report .agent} **An install that did not finish** has this result, and nothing else: that Action State isn't fully installed yet, ISSUE (what could not be confirmed, in plain words, for example "that your purchases are checked against your rules", "that the downloaded files are the ones Action State published", "that this computer can reach the public record of checkpoints" or "that your rules record could be set up"), that the person's usual controls of their host still apply, and that their Everyday Rules are not claimed active. With the script, ISSUE comes from the first sentence of its `==== RESULT ====` block. It holds no rules table, no receipts sentence, no "From now on" line, no install details, and never a command, a placeholder, a checksum, an exit code or a path. The block itself is not part of this result: it is the record of which step stopped, why, and what was already changed on this computer, as printed (it names the step), for when the person asks what failed; one step at a time, that record is in plain words in `install-record.md`. When the cause is a rules checker this install did not set up (step 11's "any other pin", or step 12's `rules-checker-wrong-command`, `-wrong-digest` or `-stale`), the cause is: "Your purchases use a rules checker this install did not set up. Nothing was changed." When step 12's install check still failed for any other cause, ISSUE is the `say` of its last run, word for word. The install did not finish when either install check (step 10, step 12) exited non-zero, step 12 was skipped, a step stopped, or the install script exited non-zero. Three results differ: - **Step 1's stop** has step 1's result. - **Step 11's earlier-install pin, held for the person's answer**, has this result, and nothing else: Action State was installed before; their purchases are still connected to the earlier version of the rules checker, EARLIER; and the install holds for a decision that is the person's, to connect them to version 0.0.17, with the rules asg/everyday/0.3.8, so their rules keep being checked, or to leave the connection as it is. EARLIER is "version " and the earlier plugin version, if `capsulectl plugin ls`, the old install record or the script's `earlier-install pin` line names it, else "your earlier install". No checksum, digest or command. The install goes on only with that answer (step 11): to connect, the re-pin and then the result of a finished install; to leave it, the next result. - **The connection left as it was**, after the answer to leave it or with no answer, has this result, in place of the one above: the connection to the person's rules was left as it was, their purchases are not checked against their rules until it is connected, and installing again finds the same pin. A `step 02` `WARNING` comes first either way; for an install that finished, it goes in a message of its own before the result, because the result above `**Install details**` is the plugin's message alone. The rest of this step is for an install that finished. In the folder where step 12 ran, the result is built in `reply.md`, starting from the short message the plugin wrote, the file `report` printed after `Closing message:` (`report/ready.md` on a first run). Copying the file keeps the plugin's wording of the limits and rules: ``` cp report/ready.md reply.md ``` The rest goes below it. Above the `**Install details**` line, `reply.md` holds only `ready.md`: no rules table, no rule ids, no digest and no skill names. The full table and the list of what the host has no native control for are on the rules page; the pack id and its digest are in the install record (`install-summary.json`). 1. **The short message:** `ready.md` as the plugin wrote it. It gives the spending limits, what the agent can do, what it asks about first and, because `report` ran with `--receipts`, the sentence about receipts. When the rules page is to be attached, its last line says the attached page has the full rules, the limits and the report. 2. **A line that reads exactly `**Install details**`**, and under it three lines and one pointer, nothing more. No full hash, no JSON and no command output in them: every hash and exit code is in the record the pointer names. 1. **What you installed:** "Installed capsulectl v0.1.0-rc22 (PROVENANCE) and the Action State plugin 0.0.17 (checksum only, because no build attestation is published for it)." PROVENANCE is the plain words for capsulectl's `provenance_reason` (step 3): - `attestation_verified`: "build attestation verified"; - `gh_absent`: "checksum only, because gh is not installed"; - `gh_too_old`: "checksum only, because the installed gh is too old to check it"; - `gh_login_prompt`: "checksum only, because gh asked to sign in, and the install never signs in"; - `no_attestation_published`: "checksum only, because no build attestation is published for it". 2. **The report's signature check:** "Your report's signatures were checked: VERDICT, PATH. Install checks exited A and B; one copy of each skill." VERDICT is the verdict `capsulectl verify --bundle report/report.json` printed in step 12 (`VALID`), PATH the full path of `report/report.html`, and A and B the exit codes of step 10's `capsulectl doctor --install-check` and step 12's `capsulectl actionstate doctor --install-check`. 3. **What runs from now on:** "From now on: rules pack asg/everyday/0.3.8, the rules on your rules page, PIN." Never count the rules: the lint below refuses a count of the table's rows. PIN is the rules checker on the deal profile (step 11): "checked on every receipt, pinned by this install" (`pinned`), "checked on every receipt, already pinned by an earlier install" (`already_pinned`), "checked on every receipt, re-pinned by this install on your yes" (`repinned`), or, if step 12's install check named `rules-checker-not-pinned`, `-unsupported`, `-wrong-command`, `-wrong-digest` or `-stale`, "not pinned by this install: ISSUE; nothing was changed". If the summary's `warnings` is not empty (one step at a time: step 8's first tick delivered nothing and left a `pending` entry with a reason other than `not_attempted`), this line ends with "; the first checkpoint is waiting for the public witness and is retried every 5 minutes", and never calls the witness fine. Without a warning, that phrase is left out. If step 6 found another materiality digest (the summary's `materiality.state` is `other`), the line ends with "; your own setting for which picks pause was kept as it was"; otherwise that phrase is left out. Then the pointer, one line: "The full record, every hash and exit code, is in PATH." On the script path, PATH is `~/.local/share/actionstate/install-summary.json`, which the script wrote. One step at a time, there is no such file until it is written: before the result is complete, `install-record.md` is written in the folder where step 12 ran, with a file tool, holding all twelve of the facts below, and the pointer names its full path, as "The full record, every hash and exit code, as I noted it from the commands' output, is in PATH." If a step a line draws on was skipped, that line states it in plain words instead. The twelve facts the record holds. The script's summary file holds every one (the field is in brackets); one step at a time, `install-record.md` holds them as the commands printed them: 1. **Version:** the line `capsulectl --version` printed, and that it matches v0.1.0-rc22, commit `e66369627c41ff54a8318b39b7d835c16181beba`, in this skill (`artifacts[0].version_line`). 2. **Checksum:** the SHA-256 you computed for the binary, the full value (`artifacts[0].sha256`). 3. **Provenance:** the exact words from step 3, which say whether the build attestation was verified or only the checksum (`artifacts[0].provenance`, `provenance_reason`). 4. **Skill:** the exit code of step 5's one-deal-skill check, and the one path it printed (`exactly_one.deal`). 5. **Witness:** the `witness` block from step 7's doctor run (`witness`). It goes in the record only, never in `reply.md`. 6. **First tick:** `ticked`, with the checkpoint delivered; or `pending`, with its reason (`first_tick`). 7. **Cadence log:** the `cadence_log` id from the first tick (`cadence_log`). 8. **Plugin:** the version `capsulectl plugin ls` listed (0.0.17), the SHA-256 you computed in step 11 in full, whether line 4 found the plugin, and that the plugin has no build attestation yet, so only its checksum was checked (`artifacts[3]`, `plugin_ls`). 9. **Rules skill:** the actionstate-rules archive's SHA-256 in full, the exit code of step 11's one-actionstate-rules-skill check, and the one path it printed (`artifacts[4]`, `exactly_one.actionstate-rules`). 10. **Install checks:** both, as run: step 10's `capsulectl doctor --install-check` and step 12's `capsulectl actionstate doctor --install-check`, each with its exit code (`doctors`). 11. **Report:** the verdict line `capsulectl verify --bundle report/report.json` printed in step 12 (`VALID`), and the path of `report/report.html` (`report`). 12. **Rules checker:** the `RulesChecker` block of step 11's `capsulectl profile show deal`: its `SHA256` and `DefinitionDigests`, in full, and the packs `asg/everyday/0.3.8` and `asg/seller/0.1.5` (`rules_checker`). The plugin's lint checks the draft against the files the plugin wrote, from that folder (with the paths `report` printed, on a later run): ``` capsulectl actionstate checkup lint --against sealed/table.md --ready report/ready.md --page report/ready.html < reply.md ``` It refuses a draft whose part above `**Install details**` is not `ready.md`, a table or list that is not `sealed/table.md`'s, and a rules page that holds a link or hides one of its parts with scripts off. On `checkup lint: ok`, the result is `reply.md` as it is, whole. If the lint refuses the draft, the result is `report/ready.md` alone, then the install details as a separate part. If it refuses the rules page, the page is not attached, and the message must not point at it. The same holds when the host cannot attach a file. In either case `report` runs once more, with step 12's `--from` and `--out-dir` (`sealed` and `report` on a first run) and with `--served-root ""`: ``` capsulectl actionstate report --profile actionstate-rules --from SEALED --out-dir REPORT --receipts --served-root "" ``` It seals nothing. It writes the next `ready-N.md` beside the first, in the form for a message with no attachment, and prints its path after `Closing message:`. `reply.md` is built again from that file and linted without `--page`, since no page is sent, with READY the path printed after `Closing message:`: ``` capsulectl actionstate checkup lint --against SEALED/table.md --ready READY < reply.md ``` If the lint refuses this draft, the result is that `ready-N.md` alone, then the install details as a separate part. The install details still name the first report, the one `capsulectl verify --bundle` checked in step 12. **How it is sent.** The result goes as ordinary message text, not in a code block, so the limits and lists read as they were written. It offers no "View details" option: the rules page follows on its own. The rules page, the path the report printed after `Attach this file:` (the plugin's copy under `~/workspace/actionstate/`, where the host serves attachments from), goes in a message of its own, after the result and with no options in it. A path outside `~/workspace` is never attached, because the link would not open, and the page is never copied there. If no `Attach this file:` line was printed, nothing is attached; `ready.md` was then written for a message with no attachment. **The first Checkup.** An install that finished ends with one more message: the Checkup of the last 7 days, which the plugin speaks. It comes after the result and the rules page, never before them, so a Checkup that could not be made never reads as a failed install. In the folder where step 12 ran: ``` capsulectl actionstate checkup --out-dir checkup --after-install --json > checkup.json ``` Send its `say` as a message of its own, word for word, as ordinary text (not in a code block). It ends with how to run another Checkup and attaches no page, so it offers no "View details" either: the person gets the page when they ask for a Checkup. Whatever `checkup` exited with, the result above stands. On a non-zero exit, send the one line its `say` holds and nothing else: no error line, no command and no path. If it printed nothing, send nothing more. An install that did not finish, or one held for the person's answer (step 11), runs no Checkup. If step 12 started the rules record again, Setup's sentence for that case, copied word for word from `SKILLS_DIR/actionstate-rules/SKILL.md`, where it is in quotes, goes in its own message after the result and the rules page, before the first Checkup. ## When the person asks to uninstall {#uninstall} On the person's request to uninstall Action State, these steps run in this order, in a shell on the machine the install ran on, with `SKILLS_DIR` set to the folder the install was given. They remove the software and keep the person's data: the receipts and their keys, the rules record and its key, and the profiles that point capsulectl at them. A later install uses that data as it is, with the same keys. 1. **The schedule**, first, so no checkpoint run is left with nothing to publish from. A task named **Action State checkpoint cadence** in the agent host's scheduled tasks is deleted there; these lines take the crontab line and the file that holds it: ``` crontab -l 2>/dev/null | grep -v 'cll checkpoint cadence' | crontab - rm -f ~/.config/actionstate/cadence-schedule ``` 2. **The skills**, and the earlier copies the install moved to `_backups`: ``` rm -rf "$SKILLS_DIR/deal" "$SKILLS_DIR/actionstate-rules" "$SKILLS_DIR/actionstate-install" rm -rf "$SKILLS_DIR/../_backups/deal-"* "$SKILLS_DIR/../_backups/actionstate-rules-"* rmdir "$SKILLS_DIR/../_backups" 2>/dev/null ``` 3. **The rules checker:** the deal profile stops naming the Action State checker, which the next step removes, and stops speaking with the Action State wording pack, which goes with it: the second line clears the wording pin only when its file name starts `actionstate.capsulectl-en-`. A pack of the person's own stays, and the line prints `kept:` and its path; if `profile show` fails, the line stops. The receipts and their keys are not touched. If the install replaced a `capsulectl` that was already first on the PATH, that one runs here and is removed next, in place of `~/.local/bin/capsulectl`. ``` ~/.local/bin/capsulectl profile update --profile deal --rules-checker '' S=$(~/.local/bin/capsulectl profile show deal) && P=$(printf '%s' "$S" | sed -n 's/.*"Wording":{"Path":"\([^"]*\)".*/\1/p') && case ${P##*/} in actionstate.capsulectl-en-*) ~/.local/bin/capsulectl profile update --profile deal --wording '' ;; '') ;; *) echo "kept: the deal profile pins another wording pack, $P" ;; esac ``` 4. **capsulectl, the plugin, their caches and the install's summary.** The first line keeps a copy of the plugin for the check in 5. ``` cp ~/.local/lib/capsulectl/plugins/capsulectl-actionstate ~/capsulectl-actionstate-copy rm -f ~/.local/bin/capsulectl rm -rf ~/.local/lib/capsulectl rm -rf ~/.cache/capsulectl ~/.cache/actionstate-plugin rm -f ~/.local/share/actionstate/install-summary.json ``` 5. **The check.** Each removed path prints `absent`, each piece of data `kept`, and the crontab has no cadence line. The last line runs the plugin's own list of what the install put on this machine, from the copy: each piece of software `absent` and the data `present`, each with its path; then it deletes the copy. Its first line counts the kept data too, so it still says `installed=true`, and within 15 minutes of the last checkpoint its schedule line can still say `running`. ``` for p in ~/.config/actionstate/cadence-schedule "$SKILLS_DIR/deal" "$SKILLS_DIR/actionstate-rules" "$SKILLS_DIR/actionstate-install" "$SKILLS_DIR/../_backups" ~/.local/bin/capsulectl ~/.local/lib/capsulectl ~/.cache/capsulectl ~/.cache/actionstate-plugin ~/.local/share/actionstate/install-summary.json; do ls -d "$p" 2>/dev/null || echo "absent $p"; done for p in ~/.local/share/capsule-deal ~/.local/share/actionstate/rules.db ~/.local/share/actionstate/rules-*.db ~/.config/actionstate/seed ~/.config/capsule/profiles/deal.yaml ~/.config/capsule/profiles/actionstate-rules.yaml ~/checkpoint-cadence.log ~/actionstate ~/workspace/actionstate; do ls -d "$p" >/dev/null 2>&1 && echo "kept $p" || echo "not here $p"; done crontab -l 2>/dev/null | grep 'cll checkpoint cadence' || echo "absent crontab line" ~/capsulectl-actionstate-copy doctor --install-state --skills-dir "$SKILLS_DIR" --workspace ~/actionstate --text; rm -f ~/capsulectl-actionstate-copy ``` 6. **The standing rule** of step 9 is removed from the agent's memory. The result lists what was removed and where the data remains: the lines of the check in 5, as printed. **Deleting the data as well** runs only on the person's request for it, after the steps above. It permanently deletes the receipts, their keys and the rules record from this machine, and a later install starts over with new keys. `~/.config/capsule` holds every capsulectl profile on this machine, including any the person made. ``` rm -rf ~/.local/share/capsule-deal ~/.local/share/actionstate ~/.config/actionstate ~/.config/capsule rm -f ~/checkpoint-cadence.log rm -rf ~/actionstate ~/workspace/actionstate for p in ~/.local/share/capsule-deal ~/.local/share/actionstate ~/.config/actionstate ~/.config/capsule ~/checkpoint-cadence.log ~/actionstate ~/workspace/actionstate; do ls -d "$p" 2>/dev/null || echo "absent $p"; done ```