Development record
A fresh machine installs abcd through one small starter that hands over to a trusted abcd binary
Typed links: related_issues iss-377 (the demotion record this draft plans); builds_on itd-130 (the update verb the starter hands over to).
Press Release
A fresh machine installs abcd with one small starter script that hands over to the abcd binary the moment one it trusts exists. The session-start bootstrap, the one-line installer and the update verb stop being three downloaders: the starter fetches a binary only when no abcd exists anywhere on the machine, checks that any binary it hands over to belongs to the person running it, and falls back to its own fetch when the hand-over fails. "I used to wonder which of three scripts had put abcd on my laptop," said Frank, a DevOps engineer. "Now there is one path that fetches, and the starter only runs on a machine that has nothing yet."
Why This Matters
abcd reaches a machine through three downloaders: the session-start hook
hooks/bootstrap.sh, the one-line installer the site serves from
site-src/install.sh.tmpl, and the abcd update verb itd-130 shipped. Each
fetches and verifies a release on its own, so a fix to one (a checksum rule, the
release-asset layout, an ownership check) has to be made three times, and a
person cannot tell which of them put the binary they are running in place.
iss-377 asked for the demotion once abcd update shipped; that trigger is met.
The product thinker ruled on 2026-09-29 (ruling J2) to plan it: the installer
becomes a minimal starter with an ownership-checked hand-over.
What's In Scope
- One starter that fetches a binary only when no abcd binary is resolvable on the machine: the cold start, the one state a Go updater cannot serve.
- An ownership-checked hand-over: when a binary is resolvable, the starter
delegates provisioning to it (
abcd updateor its successor), and only to a binary that passes an ownership check. - A fallback: when the hand-over fails, the starter's own fetch still runs, loudly.
- A frozen release-asset layout, written down as a contract, so an older binary provisioning a newer plugin root keeps working across cuts (carried from itd-130's Decisions).
What's Out of Scope
- Distribution channels beyond the release assets (a Homebrew tap is iss-380, ruled "decide later" the same day).
- The curated plugin payload itd-108 drafts: the starter provisions the binary, not the plugin surface.
Mechanism
Prompted (the claim-recording gradient): why the authors expect this to work, as a falsifiable "we expect X because Y" — not the outcome restated. Replace this line with the claim, or with the exact token
None stated.alone on its line to record the claim as considered and declined.
Scope Conditions
Required (the claim-recording gradient): the population, platform, scale, or assumptions this claim holds under, one per top-level bullet —
abcd intent planstamps each with a persistent identity. Replace this line with those bullets, or with the exact tokenNone stated.alone on its line.
Acceptance Criteria
Seeded by the run from iss-377 and the ruling, unconfirmed: the planning interview walks each one.
- Given a machine with no abcd binary anywhere, when the starter runs, then it fetches and verifies the release binary itself and says so.
- Given a machine where an abcd binary owned by the person running the starter is resolvable, when the starter runs, then it hands provisioning to that binary and performs no download of its own.
- Given a resolvable abcd binary the person running the starter does not own, when the starter runs, then it refuses the hand-over, names the binary and its owner, and falls back to its own fetch.
- Given a hand-over that fails, when the starter runs, then it falls back to its own fetch and says the hand-over failed.
- Given the release-asset layout contract, when a cut changes an asset name or path the contract names, then the release gate refuses the cut.
SOTA
Surveyed on 2026-10-23; sources, evidence tiers and the local fit risks are in the install-update-release SOTA note.
- The field has settled on installer + receipt + self-update that refuses
installs it did not make. uv's standalone installer is the only route with
uv self update("will re-run the installer"); every other route names its package manager's upgrade, andUV_UNMANAGED_INSTALLdisables self-update. cargo-dist's updater reads an install receipt in~/.config/<app>(Linux, macOS) or%LOCALAPPDATA%\<app>(Windows) before any check. abcd already holds the same device:path-entry,release-mark, and the three ownership proofs in the update package. - Checksums travel with the script, never from the vendor's domain.
mise: "Checksums never come from mise.jdx.dev"; the script embeds them and
stops "if it cannot get that checksum or if the archive does not match it".
install.sh.tmplalready readschecksums.txtfrom the same release and runsgh attestation verifywhenghis present, one step past the field. - Ownership checks in the wild take both halves, one precedent each.
Homebrew's installer is the uid-and-directory precedent: it refuses root
and inspects each target directory's owner before queuing
chown. The harness's native installer is the "did I make it" precedent: it leaves a launcher it did not create in place, keeps installing new versions beside it, and reports it in its doctor output rather than overwriting it, a provenance rule closer to the receipt-and-digest half than to a uid check (the harness's setup page). - A minimum release age is a cheap defence against a bad cut: mise skips
releases younger than 24 hours by default; the harness offers a
stablechannel about a week behind. - The
curl | shobjections have specific answers: wrap everything in functions so a truncated download runs nothing (whichinstall.sh.tmpldoes), and do not trust a browser view of the script, since a server can serve curl a different file (a 2012 proof of concept). - The swap primitive is understood: minio/selfupdate (already a
dependency) writes
.new, renames the running file to.old, and restores it if the final rename fails; on Windows the.oldfile is hidden, not removed.
Path 2 — native starter shaped to the cargo-dist and uv installer-plus-receipt
pattern, with the receipt as the seam. One starter rendered to both surfaces
(open question 1, as install.sh.tmpl is already rendered to /install.sh);
"owned" means both halves (open question 2): path-entry records the file's
digest and the file and its directory belong to the invoking uid; the
asset-layout contract is a Go test over release.yml plus one shared constant,
the way TestBootstrapUpdateLineIsTheSharedWording already binds the script
to the core (open question 3). No new dependency. The independent
fit-challenge runs at plan time.
Open Questions
- Which surfaces become the starter: the session-start hook and the site's one-line installer both, or one starter that the other calls?
- What "owned" means on the hand-over: the binary's file owner, its directory's owner, or the recorded hash itd-130's store keeps?
- Where the asset-layout contract lives: a brief chapter the release gate reads, or a test over the release workflow.
- (SOTA 2026-10-23) Does the starter adopt a minimum release age for the cold-start fetch, as mise does, or does the release mark's "newest" stay the only notion of latest? Offline either way; a second notion of latest is the cost.
- (SOTA 2026-10-23) Does the cold-start fetch resolve the tag from the pinned
catalog or the release mark rather than
releases/latest, which the forge picks by commit date and so skips a hotfix cut from an older commit? - (SOTA 2026-10-23) On a Windows machine there is no uid to check; does the hand-over there rest on the receipt and digest alone, and does the record say so?
Audit Notes
Empty. Populated by intent-auditor when intent moves to shipped/.
Frontmatter
| id | itd-2609292106557115 |
|---|---|
| type | intent |
| lifecycle | drafts |
| severity | minor |
.abcd/development/intents/drafts/itd-2609292106557115-a-fresh-machine-installs-abcd-with-one.md
open on GitHub ↗ · commit history ↗
References1
- itd-130`abcd update` completes a chosen update in one verbbuilds on
Referenced by1
- iss-377Demote hooks/bootstrap.sh to a cold-start trampoline once abcd update ships (itd-130 follow-on rung). Trigger: itd-130 shipped. When any abcd binary is resolvable, the hook delegates provisioning to the Go core (abcd update); the raw curl fetch survives only for the no-binary-anywhere cold start — the one state a Go updater cannot serve. Seam hardening carried from itd-130's Decisions: delegate only to a binary passing an ownership check, keep the raw fetch as the fallback when delegation fails, and freeze the release-asset layout as a contract so an old PATH binary provisioning a new plugin root keeps working across cuts. Goal: one canonical downloader instead of three (bootstrap.sh, the README one-liner, the Go verb).related
mentions4
- dcn-26092900000000112026-09-29 — The product thinker answered the twenty-six Group J rulings (plan or close), J1 to J26 (recorded by lane recGroupJ of autonomous run A, for orchestrator abcd-fc). The answers are kept verbatim in the local-tier file `.abcd/.work.local/scratch/reports/rulings-answered-2609-29.md`, section "Group J", dated 2026-09-29, and are restated here because that file is not committed. PLAN: the installer script becomes a minimal starter with an ownership-checked hand-over, planned as a new intent with its planning interview owed (J2, iss-377, draft itd-2609292106557115); token metering and size classes are planned as one intent (J3, iss-2608301744251874 and iss-2608301856299268), and session token accounting in the history store joins that metering intent (J4, iss-2608220150157508; all three in draft itd-2609292107351737); the sources tooling moves into abcd's core with ingest and consult as abcd verbs (J5, iss-27, whose corpus half itd-76 already delivered), and the backfill of the placeholder source entries is part of that intent (J6, iss-55; both in draft itd-2609292108089653); the grill hands off to an installed external interview skill and falls back to its own (J11, iss-165, draft itd-2609292108373494); an opt-in local-model prompt sanitiser is planned (J14, iss-2608261543489261, draft itd-2609292109005937); decision-to-transcript links are planned, with the anchor kind and the link's home settled in the planning interview owed for draft itd-171 (J16, iss-2608290819228175); the dredge synthesis, its write-ups as their own memory source class, is planned together with draft itd-25, whose planning interview is owed (J19, iss-2609211905340006); one provenance register across ingest and vendoring is planned together with draft itd-26, whose planning interview is owed (J20, iss-2609211905346507); release retention, keeping the newest release per version line, is automated by planning draft itd-70 (J21, iss-282); a rules-backend seam with an opt-in CARL adapter is planned, the native loader staying the default (J22, iss-64, draft itd-2609292109214516); core-owned managed pre-commit gates are planned with draft itd-62 (J23, iss-84); a behavioural end-to-end scenario suite per verb family is planned (J24, iss-48, draft itd-2609292109475690). Every draft stays in drafts/ until a person plans it, and each source issue links its draft and names that planning interview in its deferral. BUILD: the teaching plane, the rules-loader safety domain generated from the hazard registry, is built as a lane, the registry being the single source (J10, iss-151 and itd-103; a code lane owns it). RULED DETAILS: `source_kind` carries both, as two separate labels, one for the tool and one for the route (J13, iss-2608230752354928); the prompt router's removal signal is the full list of active domains every time, a domain's absence meaning it has stopped (J15, iss-2608261550580260); setup offers an outside AI service (for example OpenRouter) at install, skippable, once the API adapter ships (J18, iss-2609081951416843, which waits on the API adapter); the salvage-hook timeout is 120 seconds, the wait must show a counter or a message while it runs and never stall silently, and every per-event timeout is pinned in a test (J25, iss-323). J13, J15 and J25 are carried by code lanes. CLOSE: the quick-tunnel preview protocol is closed unless the need recurs (J26, iss-2608230617385431, wontfix with that reason). PARKED ON A TRIGGER: agent payment protocols are kept under watch, not now, revisited as the protocols mature (J8, iss-138); the findings-only skill waits for the skill format to be versioned and stewarded (J9, iss-139); the disposition worksheet as an intent and a site page waits until after the first study (J17, iss-2609021815529020). DECIDE LATER: the Homebrew tap stays parked (J1, iss-380); pluggable search back ends (J7, iss-26); forge-backed record numbering (J12, iss-2608210737264758).
- dcn-26092900000000202026-09-29 — The product thinker answered the twenty-six Group J rulings (plan or close), J1 to J26 (recorded by lane recGroupJ of autonomous run A, for orchestrator abcd-fc). The answers are kept verbatim in the local-tier file `.abcd/.work.local/scratch/reports/rulings-answered-2609-29.md`, section "Group J", dated 2026-09-29, and are restated here because that file is not committed. PLAN: the installer script becomes a minimal starter with an ownership-checked hand-over, planned as a new intent with its planning interview owed (J2, iss-377, draft itd-2609292106557115); token metering and size classes are planned as one intent (J3, iss-2608301744251874 and iss-2608301856299268), and session token accounting in the history store joins that metering intent (J4, iss-2608220150157508; all three in draft itd-2609292107351737); the sources tooling moves into abcd's core with ingest and consult as abcd verbs (J5, iss-27, whose corpus half itd-76 already delivered), and the backfill of the placeholder source entries is part of that intent (J6, iss-55; both in draft itd-2609292108089653); the grill hands off to an installed external interview skill and falls back to its own (J11, iss-165, draft itd-2609292108373494); an opt-in local-model prompt sanitiser is planned (J14, iss-2608261543489261, draft itd-2609292109005937); decision-to-transcript links are planned, with the anchor kind and the link's home settled in the planning interview owed for draft itd-171 (J16, iss-2608290819228175); the dredge synthesis, its write-ups as their own memory source class, is planned together with draft itd-25, whose planning interview is owed (J19, iss-2609211905340006); one provenance register across ingest and vendoring is planned together with draft itd-26, whose planning interview is owed (J20, iss-2609211905346507); release retention, keeping the newest release per version line, is automated by planning draft itd-70 (J21, iss-282); a rules-backend seam with an opt-in CARL adapter is planned, the native loader staying the default (J22, iss-64, draft itd-2609292109214516); core-owned managed pre-commit gates are planned with draft itd-62 (J23, iss-84); a behavioural end-to-end scenario suite per verb family is planned (J24, iss-48, draft itd-2609292109475690). Every draft stays in drafts/ until a person plans it, and each source issue links its draft and names that planning interview in its deferral. BUILD: the teaching plane, the rules-loader safety domain generated from the hazard registry, is built as a lane, the registry being the single source (J10, iss-151 and itd-103; a code lane owns it). RULED DETAILS: `source_kind` carries both, as two separate labels, one for the tool and one for the route (J13, iss-2608230752354928); the prompt router's removal signal is the full list of active domains every time, a domain's absence meaning it has stopped (J15, iss-2608261550580260); setup offers an outside AI service (for example OpenRouter) at install, skippable, once the API adapter ships (J18, iss-2609081951416843, which waits on the API adapter); the salvage-hook timeout is 120 seconds, the wait must show a counter or a message while it runs and never stall silently, and every per-event timeout is pinned in a test (J25, iss-323). J13, J15 and J25 are carried by code lanes. CLOSE: the quick-tunnel preview protocol is closed unless the need recurs (J26, iss-2608230617385431, wontfix with that reason). PARKED ON A TRIGGER: agent payment protocols are kept under watch, not now, revisited as the protocols mature (J8, iss-138); the findings-only skill waits for the skill format to be versioned and stewarded (J9, iss-139); the disposition worksheet as an intent and a site page waits until after the first study (J17, iss-2609021815529020). DECIDE LATER: the Homebrew tap stays parked (J1, iss-380); pluggable search back ends (J7, iss-26); forge-backed record numbering (J12, iss-2608210737264758).
- iss-380Homebrew tap for abcd (a personal tap, brew install <personal account>/tap/abcd). Trigger conditions are now MET: the repo is public and abcd update exists (its install-channel refusal already prints brew upgrade abcd on a Cellar-resolved path). Recorded and parked in [dcn-2608200000000006](../../decisions/dcn-2608200000000006-homebrew-distribution-recorded-and.md); this ledger entry makes it queryable rather than prose. abcd's release pipeline is bespoke (semantic-gate attestations, no goreleaser), so the tap push is a hand-rolled release-workflow step, not goreleaser's homebrew_casks pipe. Ready for triage rather than deferral.
- itd-108The plugin installs from the curated release artifact, not the repo, and every cut release reaches users automatically