Skip to content

feat(install): the one-command terminal path, and how to read it before you run it (#86) - #88

Merged
eaitbrahim merged 4 commits into
mainfrom
feat/install-terminal-installer-86
Aug 25, 2026
Merged

feat(install): the one-command terminal path, and how to read it before you run it (#86)#88
eaitbrahim merged 4 commits into
mainfrom
feat/install-terminal-installer-86

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Closes #86.

keel merged its terminal installer in CodeGateSoftware/keel#479. Until now the only route this page documented was the manual one: download eight wheels from the release page by hand, then pip install --find-links .. The script does the whole job in one command, and the page said nothing about it.

Why this is announceable, when most of the new installer work is not

I reviewed all seven keel commits that landed after v0.11.2. Almost none of them can go on the site yet, because they have shipped to zero users:

Upstream State on 2026-08-25 On the site?
curl|bash installer (#479) Script on main, resolves 200, installs v0.11.2's wheels this PR
Windows Inno Setup installer + gated signing (#438) Code on main, unreleased; signing environment does not exist ❌ → #87
Per-release update path for packaged installs (#439) Code on main, unreleased ❌ → #87
Web UI rewrite Status: proposal, zero code shipped
BracketGTC (#502 stage 1) Type exists, no caller in keel/
Crash-ledger fix (#519) Internal correctness ❌ — arrives via the auto-fetched changelog

What makes this one different: the script resolves 200 from main and installs from the latest release's wheels, and v0.11.2 carries all five production wheels plus config.yaml. So the path works end to end today. keel update is registered in that same tag (keel/cli.py:198,1414), so the "keeping it current" half is true of the released engine too.

The pipe is never offered alone

A reader here may hand keel exchange API keys, so curl … | bash is not presented as the only way in. It sits beside a download-read-run variant, and the audit comment is localized on its own line so the Arabic build never interleaves RTL prose inside an LTR command line.

One correction to keel's own words

scripts/install.sh's header claims it "writes nothing outside the invoking user's home". Line 83 stages downloads in ${TMPDIR:-/tmp} — removed on exit by the trap, but outside $HOME while it runs. Rather than repeat the stronger phrasing, the page says what is checkable line by line: downloads land in a temporary folder deleted on exit, and the installed deployment lives only under your home folder.

Worth reporting upstream (not fixed here): docs/desktop-install.md and the release-notes template both promise a singular SHA256SUMS.txt, but release.yml emits one file per leg — SHA256SUMS-macOS-arm64.txt, SHA256SUMS-macOS-x86_64.txt, SHA256SUMS-Windows-x86_64.txt.

Honesty boundaries held (FR-9)

  • The sha256 line is described as an audit trail, not a verification — no wheel checksums are published anywhere, so there is nothing to compare against.
  • The script is described as served from the default branch, never as "shipped in v0.11.2".
  • No .dmg, .exe, signed/notarised build, or SHA256SUMS is mentioned. The existing unsigned block and the browserTitle/browserBody v0.11.0 claim are byte-identical to before.

Structure

Two files. src/pages/{en,ar,fr}/install.astro are untouched — they are 4-line wrappers with no copy, per the design contract in docs/DEPLOYMENT.md.

  • src/i18n/pages/install.ts — new TerminalInstallCopy interface, terminal field on InstallContent, copy in all three locales.
  • src/components/pages/InstallPage.astro — command strings in frontmatter, new <section id="terminal-install"> placed ahead of the wheels-by-hand ceremony. #from-source stays last.

FR-8: all three rev2026-08-25.1, both translatedFromRev2026-08-25.1, so no stale-translation banner renders.

Verification

  • npm run check0 errors, 0 warnings (5 pre-existing hints in files this PR does not touch)
  • npm run build56 pages built, complete
  • #terminal-install renders in dist/{en,ar,fr}/install/index.html
  • Prohibited-claims sweep from LAUNCH-CHECKLIST §7 — clean
  • "five production wheels" checked against WHEEL_PREFIXES at scripts/install.sh:39
  • ./.venv/bin/keel update form matches the installer's own "Next steps" output (install.sh:227-235)

Reviewer attention

  1. Placement. The section sits before "All release files" and before #from-source. Is one command ahead of the wheels ceremony the right order, or should the download grid keep the first word?
  2. Is the pipe acceptable at all on a page for readers who will hand this program API keys? The read-it-first variant is beside it, but the one-liner is still shown first, under its own heading.
  3. The branch-note framing — "it is whatever is on main, and it works today". Honest, but it does invite the question of what happens when main moves. Worth a stronger caveat?

🤖 Generated with Claude Code

https://claude.ai/code/session_015cBJ2syTN4VoE73F9sBNGW

…re you run it (#86)

Closes #86.

keel merged its terminal installer in CodeGateSoftware/keel#479. Until now the
only route this page documented was the manual one: download eight wheels from
the release page by hand, then `pip install --find-links .`. The script does the
whole job in one command, and the page said nothing about it.

### Why this is announceable, when most of the new installer work is not

Verified 2026-08-25. The script resolves 200 from `main` and installs from the
latest release's wheels — and v0.11.2 carries all five production wheels plus
`config.yaml`, so the path works end to end today. `keel update` is registered in
that same tag (`keel/cli.py:198,1414`), so the "keeping it current" half is true
of the released engine too.

Everything else that landed upstream after v0.11.2 is unreleased code on `main`
and stays off the site: no `.dmg`, no `.exe`, no signed or notarised build, no
`SHA256SUMS`. That is #87, deliberately not this PR.

### The pipe is never offered alone

A reader here may hand keel exchange API keys. `curl … | bash` sits beside a
download-read-run variant, and the audit comment is localized on its own line so
the Arabic build never interleaves RTL prose inside an LTR command line.

### One correction to keel's own words

`scripts/install.sh`'s header says it "writes nothing outside the invoking user's
home". Line 83 stages downloads in `${TMPDIR:-/tmp}`, so on Linux that is not
quite true — removed on exit by the `trap`, but outside `$HOME` while it runs.
The page says what is checkable instead: downloads land in a temporary folder
deleted on exit, and the *installed deployment* lives only under your home
folder.

### Honesty boundaries held

The sha256 line is described as an audit trail, **not** a verification — no wheel
checksums are published anywhere, so there is nothing to compare against. The
script is described as served from the default branch, never as shipped in a
release.

### Verification

- `npm run check` — 0 errors, 0 warnings (5 pre-existing hints, untouched)
- `npm run build` — 56 pages, complete
- `#terminal-install` renders in all three locales
- `rev` and both `translatedFromRev` at `2026-08-25.1`, so no stale banner
- Prohibited-claims sweep from LAUNCH-CHECKLIST §7 — clean

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015cBJ2syTN4VoE73F9sBNGW
eaitbrahim and others added 3 commits August 25, 2026 19:11
…loor is 3.14 (#86)

Review found two factual errors in the terminal-installer copy. Both were
verified against live sources, not a local clone, and both are corrected in all
three locales.

### The config.yaml it lands is not the paper profile

The copy said "The config.yaml it lands is the paper profile. Nothing in it can
place a live order." Downloading the actual release asset shows otherwise:

    # keel PRODUCTION configuration -- the `config.yaml` attached to a GitHub Release.
    # Shipped in `auto_trade.mode: confirm`: keel previews every order and waits for
    # your explicit approval before placing it. It is ready for live use...
    auto_trade:
      mode: confirm

`keel/cli.py` at v0.11.2 draws the line plainly: `paper` simulates, `confirm` is
live. So it is the live profile behind a per-order gate. Telling a reader it
"cannot place a live order" is wrong in the direction that costs money, and it
sat directly above the line telling them to add a CDP key.

The page now says what it is, and points at the real remedy: from `~/.keel`,
`./.venv/bin/keel init-config --force` writes the `mode: paper` template, since
without `--live` that is what `template_config_text` returns.

**This came from upstream.** `scripts/install.sh` calls it "the paper profile"
at the copy step and again in its next-steps output. The site had mirrored it
faithfully. Reported for the keel repo separately.

### The Python floor is 3.14, not 3.11

The local keel clone this copy was written against was 12 commits stale. The
live script — the one the page's own one-liner fetches — enforces 3.14:

    say "step 2/7: finding Python >= 3.14"
    for candidate in python3 python3.14; do

Raised in keel `2d3934f`, on `main` but not in the v0.11.2 tag. A reader on 3.12
would have followed this page into a hard failure.

The interesting half is kept rather than smoothed over: the script's floor is
stricter than the wheels it installs, which still declare `>=3.11`. The manual
path above accepts a Python this script turns away, and the page now says so.
The hero and from-source requirements are untouched — 3.11 is still right there.

### Two smaller corrections

- `keel update` installs only through uv and stops with a plain error when uv is
  absent, yet the installer works fine without it. A pip-path deployment cannot
  self-update until uv is added. Said out loud rather than left as a surprise.
- uv detection needs `uv --version` to actually succeed — a shim that cannot run
  is deliberately treated as absent — and the fallback uses the interpreter the
  script found, not literally `python3`.

### The branch note earns its keep

"It works today" was falsified during its own review: `main` moved the floor
while the copy sat in a PR. The note now says the script changes when `main`
changes, cites that very move as the example, and tells the reader to read the
copy they download rather than trust this page to have kept up. It also names
what CI does and does not prove — `install-smoke.yml` runs end to end on macOS
and Linux, but only on `workflow_dispatch` and PRs touching the script.

### Translations

ar and fr carry every caveat intact; the two sentences asserting the old false
claim are gone from both. Also dropped a stale "both serve this venv deployment"
clause that English no longer has and that the uv caveat now contradicts, and
removed a redundant "(checkout)" parenthetical from the Arabic. Arabic bidi
marks brought in line with the convention already used elsewhere in the file.

### Verification

- `npm run check` — 0 errors, 0 warnings
- `npm run build` — 56 pages, complete
- All three locales: 13 fields, 8 `does` items, `rev`/`translatedFromRev` all
  `2026-08-25.1`
- `dist` greps: old claim absent in all three; `Python 3.14` and the confirm-mode
  caveat present in all three

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015cBJ2syTN4VoE73F9sBNGW
…n this today (#86)

An adversarial verification pass was run against live sources rather than a
local clone. It falsified two claims in the previous commit — including one that
commit had itself introduced as a correction — and surfaced a third problem the
page had no business staying quiet about.

### "previews every order and waits for your typed approval" was wrong twice

**Not typed.** `v0.11.2:keel/commands/confirm.py:196` is
`click.confirm("Place this order?", default=False)` — an ordinary y/N prompt
defaulting to no. A typed phrase does exist, `DEGRADED_PREVIEW_PHRASE = "place
anyway"` at `confirm.py:41`, but only for previews keel considers unreliable.
The previous commit had borrowed `keel update`'s gate, which really does demand
the word `yes`, and attached it to order placement.

**Not every order.** Three money-moving paths hard-code `"autonomous"` with
`confirm_fn=None` and place with no prompt even under `mode: confirm`:
`executor.py:1556` (`place_bracket`), `:1638` (`scale_out`), `:1719`
(`_roll_stop`). Each is a follow-on to a position a human already approved,
which is defensible design — but "every" is not what the code does.

### The bullet also traded away its strongest true claim

"places nothing until you add venue credentials" is true but is about the fifth
gate down. The binding one is `agent.py:1247`:

    if repo.get_state("kill_switch", default=True):

It fails closed. A fresh deployment has no `kill_switch` row, reads `True`, and
skips every cycle before evaluating anything — so it trades nothing even with
full credentials, until `keel resume`. Seeded rules are `candidate` besides, and
only `live` rules trade. The bullet is now split in two: what the config is, and
why a fresh install still does nothing.

### The Linux claim was the real find

The previous `branchNote` said keel runs the script end to end on macOS and
Linux. It is configured to, but has not done so since the floor moved. Run
32714791913, the only one since:

    smoke (ubuntu-latest)  ==> step 2/7: finding Python >= 3.14
    smoke (ubuntu-latest)  installer: FAIL: no Python >= 3.14 found on PATH

macOS found Homebrew's 3.14.6 and installed cleanly; ubuntu-latest died at the
version check and took the macOS leg with it via fail-fast. Python 3.14 is new
enough that mainstream distributions do not package it, so the one-liner this
page publishes fails for most Linux readers today. Saying "you need 3.14" while
implying Linux works was accurate word by word and misleading as a whole. The
note now says plainly: on Linux, expect to install 3.14 yourself first.

### One pre-existing falsehood, fixed while adjacent

`fromSource` promised "any Python 3.11+". That path clones `main`, where
`requires-python` is `>=3.14` and `.python-version` is `3.14.4`, so `uv sync` on
3.11 fails. Outside this PR's scope but false in the same way and three lines
away; fixed in all three locales rather than left standing.

The hero `requirements` and the `unsigned` block keep 3.11 — both describe the
published wheels, which still declare `>=3.11`.

### Verification

- `npm run check` — 0 errors, 0 warnings
- `npm run build` — 56 pages, complete
- All three locales: 9 `does` items, 13 fields, revs matched
- `dist` greps: y/N prompt, kill-switch and Linux-3.14 warning present in en, ar
  and fr; the old CI claim absent

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015cBJ2syTN4VoE73F9sBNGW
…and (#86)

Polish from the independent review, which returned MERGE. Two of these touch
FR-9 directly, so they are applied rather than merged past.

### The warning was in the wrong place

It sat four sentences into `branchNote`, a paragraph that opens on branch
pinning. A reader scanning for the command copies it long before reaching a
"this fails today" buried under provenance prose. The warning now lives in
`requires`, beside the other platform requirements. Verified in the rendered
HTML: it precedes the first `curl` line in all three locales.

`branchNote` goes back to what it is for — the script tracks a moving branch,
and the description may not have kept up.

### One clause was not verifiable

"most Linux distributions do not package it yet" — Fedora 43+, Arch and
openSUSE Tumbleweed do package 3.14. What is true, and what the page now says,
is that most distributions still *default* to an older Python. FR-9 asks for
claims verifiable the day they ship, and the CI evidence beside it carries the
sentence without needing the stronger version.

### The script contradicts the page, so the page says so

`install.sh` prints "the paper profile" and "nothing in it can place a live
order" for a config that is byte-identical to `config.live.yaml` in
`mode: confirm`. A reader who runs the one-liner watches the tool contradict
this page with no way to adjudicate, and the error runs in the dangerous
direction. The bullet now names it and points at `auto_trade.mode` in the file.
Reported upstream separately.

### Two smaller ones

- The `keel update` caveat was 40 words, past the ~35 ceiling in CONTRIBUTING
  and not the parallel enumeration the exception allows. Split in three locales.
- The dark `unsigned` block said the five-minute path needs "Python 3.11 or
  later" while linking `#from-source`, which this branch corrected to 3.14. It
  renders only when a release carries a platform bundle, so it is latent rather
  than live — but the rev is already bumped and it would have become a
  contradiction the day a bundle ships.

### Left alone deliberately

The hero `requirements` keeps 3.11: it describes the published wheels, which
declare `>=3.11`. So does the reconciliation clause in `requires`, and the
"moved from 3.11 to 3.14" in `branchNote` is a historical statement.

The review also noted `does[6]` understates the gating — `execute()` routes
every intent through `confirm_fn` and only the post-fill bracket bypasses it,
and a degraded preview demands a typed phrase. Both errors run in the safe
direction, claiming less gating than exists, so the shorter line stands.

### Verification

- `npm run check` — 0 errors, 0 warnings
- `npm run build` — 56 pages, complete
- All three locales: 9 `does` items, 13 fields
- Rendered: warning before the curl block in en, ar and fr

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015cBJ2syTN4VoE73F9sBNGW
@sonarqubecloud

Copy link
Copy Markdown

@eaitbrahim
eaitbrahim merged commit 5a2ecbd into main Aug 25, 2026
2 checks passed
@eaitbrahim
eaitbrahim deleted the feat/install-terminal-installer-86 branch August 25, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install page: add keel's no-warning terminal installer (keel #479)

1 participant