Skip to content

installer: the PATH line is the last thing printed, bare and bold green - #1208

Merged
AbirAbbas merged 17 commits into
devfrom
claude/codeaf-install-message-b41040
Sep 21, 2026
Merged

AbirAbbas merged 17 commits into
devfrom
claude/codeaf-install-message-b41040

Conversation

@ZeroPoint95

@ZeroPoint95 ZeroPoint95 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Install prints three lines and ends on the export PATH line. codeaf telemetry gains info; show becomes JSON. Four groups of change, 14 commits.

1. The install one-liner prints three things and nothing else

Real capture, Debian arm64 container:

$ curl -fsSL https://agentfield.ai/get/codeaf | bash
installed codeaf v0.3.0 built 2026-09-18 18:16 · go1.26.5 linux/arm64

codeaf shares anonymous performance data with AgentField
codeaf does NOT share your prompts, code, files, or any private information
see what is shared: codeaf telemetry info · turn off: CODEAF_TELEMETRY=off

export PATH="/root/.codeaf/bin:$PATH"

  • Last line is bold green on a terminal, plain when piped or with NO_COLOR.
  • Gone from a normal run: codeaf: stable v0.3.0 for …, codeaf: installed <path>, and the codeaf: add it to this shell with: prefix. --verbose still prints the first two, on stderr.
  • Unchanged: when the line prints (folder not on PATH, never on Windows), --no-modify-path, the shell-file edit, checksums, channels.

2. codeaf telemetry has five verbs, was four

  • info (new) opens on codeaf does NOT collect or share your chat, then 1. Usage Counts and 2. Model Pool: this machine's live values, one example row per event from the contract's own band constants, and an indented example pool row. No never lines inside, no band lists.
  • show is JSON only, indented by two: {"usage": {destination, off?, waiting[]}, "model_pool": {…}}. off appears only when nothing is sent there. Empty queue is [], never null.
  • The binary's notice, README and docs/TELEMETRY.md now say What is collected: codeaf telemetry info. Tests hold all four copies to the same bytes.
codeaf telemetry info on a fresh stable install
codeaf does NOT collect or share your chat. No prompts, replies, code, file names,
paths, repo names, keys, email, IP or machine name leave for AgentField. Only the
fields below do, as this machine would fill them.

1. Usage Counts (https://agentfield.ai/api/oss/codeaf/telemetry)
  every event, as this machine would send it now
    codeaf_version       v0.3.0
    channel              stable
    os                   darwin
    arch                 arm64
    usage_context        local
    install_method       script
    install_id_hash      sha256 of a random id, minted on the first send
    session_id_hash      sha256 of the run id, one per session; absent on first_run
    event_id             16 random bytes as hex, one per event
    event_time           2026-09-19T18:11:40Z

  what each event adds, for example
    first_run            nothing; sent once per install
    session_started      mode=chat  resumed=false
    session_ended        mode=chat  duration=5-30m  turns=6-20  model_calls=21-100  model_calls_failed=0
                         tool_calls=6-20  tool_calls_failed=0  cost_usd=0.1-1  stop_reason=done  exit_code=0
    fault                mode=chat  scope=main  fingerprint=3fa9c1e2b7d04e85
    stop_reason          one of done · error · incomplete · budget · turn-cap · deadline · price · question · interrupted · unknown

2. Model Pool (https://codeaf.agentfield.ai/pool/v1/rows)
  one row per judged seat, after a task lands, for example
    {
      "schema": 1,
      "metric": "role_quality",
      "role": "worker",
      "model": "<the seat's model slug>",
      "score": 81,
      "judge": "<the judge's model slug>",
      "door": "task",
      "size": "M",
      "day": "2026-09-20"
    }
    nonce                16 random bytes as hex, one per row, so a resend is not a double count
    X-Codeaf-Install     a header: a random per-install id, minted on the first send; not the usage counts' id
codeaf telemetry show with one event waiting
{
  "usage": {
    "destination": "https://agentfield.ai/api/oss/codeaf/telemetry",
    "waiting": [
      {
        "event_name": "session_started",
        "event_id": "8c1f4bd3bf17bad6e701e0ece115288f",
        "install_id_hash": "cd95afc6…",
        "session_id_hash": "d6f189b2…",
        "event_time": "2026-09-19T18:11:32Z",
        "props": {
          "arch": "arm64",
          "channel": "stable",
          "codeaf_version": "v0.3.0",
          "install_method": "script",
          "mode": "chat",
          "os": "darwin",
          "resumed": false,
          "usage_context": "local"
        }
      }
    ]
  },
  "model_pool": {
    "destination": "https://codeaf.agentfield.ai/pool/v1/rows",
    "off": "model_pool read (capped by the telemetry off switch)",
    "waiting": []
  }
}

3. Merged from claude/codeaf-telemetry-disclaimer-5af1dd

  • codeaf telemetry show covers the Model Pool stream, and every telemetry-off switch caps the pool at read.
  • One follow-up fix here: the config reader law names ModelPoolAt for the model_pool row, the reader that branch left it with.

4. After review

  • internal/release's five installer tests asked a normal run for the channel, tag and platform. They ask a --verbose run now, where those lines live, and read the receipt on a normal run. The channel-picking tests still see which release was chosen.

  • A missing release names its tag in the failure line itself: no codeaf-linux-arm64 in release v9.9.9; check the tag on the Releases page.

  • An empty CODEAF_TELEMETRY_ENDPOINT caps the Model Pool at read, as docs/TELEMETRY.md already claimed. poolcfg reads it as the third environment rung, with tests.

  • The comment over telemetryRowsOff says why the former AFORGE_TELEMETRY spelling reaches the pool (ProjectBoolAt falls through to TelemetryAt, which reads the pin through internal/env), so a tidy-up cannot drop it.

  • Merged dev after a dev build installs beside codeaf as devaf and follows the dev channel #1333 (devaf). The installer keeps its --name / CODEAF_INSTALL_NAME and the quiet run: the receipt names the installed file's version, the notice follows, the export PATH line is last. a dev build installs beside codeaf as devaf and follows the dev channel #1333's new TestV1InstallerName reads the receipt and the last line on a normal run and the install path on a --verbose run. The guide and manual say the same.

5. Proof

  • test/installer-telemetry.sh: 46 checks. Notice bytes against docs/TELEMETRY.md, the hint's blank lines, no escapes off a terminal, NO_COLOR, the hint call is the script's last line.
  • New Go tests: example values held to the allowlist, the pool example parsed back into record.Row, show re-encodes to the same bytes, info opens on the chat sentence and has no never line.
  • Ran the installer for real against v0.3.0 in a Debian arm64 container under a pty; section 1 is that capture.
  • go test ./internal/release/ passes the five installer tests. make pr-ready: green except three macOS-only reds that fail identically on clean origin/dev and pass on Linux, where dev's Full check is green: TestC9SourceBuildRefusesBeforeTheNetwork and TestC9AnInstallEndsWithTheNewBinarysVersionLine (/var/folders vs /private/var/folders), and TestTheReleaseSurfaceTestSurvivesEveryLedgerState (bash 3.2 and an empty array under set -u). Not from this branch.

Try it

Installs into a throwaway folder; touches nothing under ~/.codeaf or your shell files.

T=$(mktemp -d) && CODEAF_HOME=$T/home CODEAF_INSTALL_DIR=$T/bin bash scripts/install.sh --no-modify-path

Note: agentfield.ai/get/codeaf serves scripts/install.sh from main, so the public one-liner changes on the next promotion, not on merge to dev.

🤖 Generated with Claude Code

ZeroPoint95 and others added 14 commits September 18, 2026 14:34
The installer said 'codeaf: add it to this shell with: export PATH=...'
between the install line and 'codeaf version', so the one line a person
has to paste sat mid-screen behind a prefix they had to trim. It is now
the very last thing the script prints: a blank line, then the bare
export line, bold green when stdout is a terminal and NO_COLOR is unset.
test/installer-telemetry.sh lifts print_path_hint and pins the shape.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ws too

The notice promises "see exactly what leaves: codeaf telemetry show", and two
streams leave the binary for AgentField: the anonymous usage counts, and the
Model Pool's judged seat scores, which go to codeaf.agentfield.ai under the
separate `model_pool` switch. The verb printed only the first, so a person who
read it and set CODEAF_TELEMETRY=off believed nothing more would leave while
the pool went on sending model slugs and scores.

The verb now prints both streams, each under a line naming where it goes or
why it is not sent (`off: <ladder rung>` for the counts, `model_pool read,
nothing is sent` for the pool), with the pool rows rendered in the outbox's
own line shape so the bytes are the bytes the relay receives. It reads the
outbox by path and stats first, so a reading form never creates the file.

docs/TELEMETRY.md gains a section naming the second stream, its fields, its
relay and its switch; the chat manual's telemetry and pool sections say the
same, so the corpus no longer reads as though CODEAF_TELEMETRY=off stopped
everything.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The notice says "Turn off: CODEAF_TELEMETRY=off" without qualification, and
the Model Pool's rows are the other thing this binary sends to AgentField. The
switch stopped the usage counts and nothing else: with `model_pool = on`, the
default, the pool went on posting model slugs and seat scores.

Every rung of the telemetry off ladder now caps the pool at `read` — the pool
is still read, the judge still scores into the install's own sheet, nothing is
sent — and the cap wins over an explicit `on`, because the notice's line
carries no exception. The environment rungs (CODEAF_TELEMETRY off/0/false,
DO_NOT_TRACK 1/true) are read inside poolcfg.Resolve through its injected
lookup, spelled exactly as internal/telemetry's ladder spells them, so the
resolver stays pure and the verb tests that inject an environment stay
hermetic. The disk rungs — the project file and the profile row that
`codeaf telemetry off` writes — are read by the new config.ModelPoolResolved,
the one door every caller now resolves through, and applied with
Config.Quieted. The mode's source word for the cap is `telemetry`, which
`codeaf pool status` prints as `mode read · telemetry`.

docs/TELEMETRY.md, the README and the chat manual say the switch covers both
streams; the change entry carries the claim a stale memory would get wrong.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t line last

On a normal run the installer now prints exactly three things: 'installed
codeaf v… built … · go… os/arch' (the binary naming itself), a two-line
telemetry notice, and the bare export PATH line, bold green, with a blank
line above and below. The channel/tag line and the 'codeaf: installed
<path>' line move under --verbose on stderr. docs/TELEMETRY.md carries the
installer's two-line form beside the full notice the binary still prints
at first session, and the shell test reads both back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…just the spool

The notice says "See exactly what leaves: codeaf telemetry show", and a
person runs it on the day they install, when nothing is waiting — so it
printed two empty arrays, which told them nothing about what would leave the
first time they used the program.

The verb now prints, for each stream, what a row SAYS: the six every-event
props with the values this binary on this machine would send right now, the
identity and envelope fields with what each is, what each of the four events
adds, the bands, and the never list; for the Model Pool, every field of a row
and its meaning, the two identities a batch travels under, and the never
list. Then, for each, the rows waiting to leave, or one line saying none are.

The field words live in code now — telemetry.PropDoc and record.Fields — so
the verb and docs/TELEMETRY.md's table read from one source; the doc test
holds the table's third column to PropDoc word for word, and a record test
holds Fields to the row's own JSON names. The install id hash is read without
minting one: a reading verb on a machine that has sent nothing shows
"(minted on the first send)" and leaves no file behind, and a test holds that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A third line, 'see what is shared: codeaf telemetry show · turn off:
CODEAF_TELEMETRY=off', so the install output itself says how to look and
how to stop it. docs/TELEMETRY.md and the shell test follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The telemetry branch moved every outside reader of the pool row from
ModelPoolSettingAt to ModelPoolAt / ModelPoolResolved, and the reader
law still named the old function, so it failed the row as a dial wired
to nothing. The map now names the reader the code has.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The verb printed a field-by-field table with a meaning column and a
never line per stream. It prints what a row looks like now: the rows
waiting first, then this machine's live every-event values, one example
row per event spelled from the contract's own bands, the bands and stop
reasons a row can carry, and the pool row in the relay's bytes, wrapped
at a key. No never line; the notice and docs/TELEMETRY.md carry the
disclaimer. The examples and band lists come from the telemetry
package's constants and a test holds them to the allowlist; the pool's
example row is marshalled from Row and a test parses it back. The pool
Fields table, read by nothing else, is gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The example rows carry one of each and docs/TELEMETRY.md spells the
rest; the three band lines were text a person does not need there. The
band-list helpers, read by nothing else, go with them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`codeaf telemetry info` takes over the listing — the shape of every
row, this machine's live values, one example row per event, the pool row
in the relay's bytes — and `codeaf telemetry show` prints only what is
waiting to leave, as one JSON object indented by two with a key per
destination (usage, model_pool), each carrying destination, an off reason
when nothing is sent there, and waiting, the rows themselves. The notice's
fifth line and the installer's third now point at `codeaf telemetry
info`; the binary, README, docs/TELEMETRY.md and the installer carry the
same bytes and their tests hold them together.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The first thing `codeaf telemetry info` says, before either stream, is
the one thing a person came to check: codeaf does NOT collect or share
your chat, with the never list, then only the fields that do leave.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1. Usage Counts and 2. Model Pool, capitalised alike; no blank line
between the last event and the stop reasons; the pool's example row is
indented by two, the way show prints a waiting one, instead of wrapped
at a key. The wrapper, read by nothing else, goes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ZeroPoint95
ZeroPoint95 marked this pull request as ready for review September 20, 2026 14:55
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Reviewed this on Linux: a real install against the published v0.3.0 (pty, piped, NO_COLOR, --verbose), the built binary's telemetry info / show on a throwaway home, and a read of the diff. The installer output, the colour gating and the notice bytes all match what the description says, show is valid JSON with [] never null, and info prints nothing private. The pool cap holds for the env switches, the disk rows and the legacy spelling.

One thing blocks merge:

go test ./internal/release/ fails five tests on this head and passes on dev. TestInstallerGetsLatestStableAndFinishesWithVersion, TestInstallerPicksTheNewestChannelBuildWhateverTheListOrder, TestInstallerPinsAReleaseAndNamesAMissingOne, TestDocumentedVersionPinReachesThePipedInstaller and TestInstallerUsesWgetWhenCurlIsAbsent. They assert a normal run names the channel, tag and platform (internal/release/install_test.go:266, :576, :622) and that a missing release names v9.9.9 (:590, :826), and all of that moved behind --verbose in scripts/install.sh. CI did not catch it because the touched-packages gate derives its package set from changed Go files, so a scripts-only change runs no suite at all. That is a CI gap we will fix separately, but this PR needs the tests and the script to agree before it lands. Either direction is fine by me, restore the strings or move those assertions to a --verbose run, as long as the channel-picking test can still tell which release was chosen.

Two smaller things, not blocking:

  • docs/TELEMETRY.md:99 says an empty CODEAF_TELEMETRY_ENDPOINT is one of the switches that also stops the Model Pool from sending, and codeaf --help says the same. It does not: internal/pool/poolcfg/poolcfg.go reads only CODEAF_TELEMETRY and DO_NOT_TRACK, and telemetryRowsOff in internal/config/settings.go:3411 reads only the two disk rows. With an empty endpoint the usage counts stop and the pool keeps sending. Either cap it or drop it from the list.
  • The comment above telemetryRowsOff (internal/config/settings.go:3396) says "disk only, never the process environment", but the reader it calls goes through TelemetryAt, which does read the environment, and that is exactly why the legacy AFORGE_TELEMETRY=off caps the pool. Worth making the comment say so before someone "fixes" it and drops the legacy spelling.

Noting for promotion rather than for this PR: a fresh stable install today rejects codeaf telemetry info, and the public one-liner serves the script from main, so the notice will point at a verb the installed binary does not have until the next stable carries it.

ZeroPoint95 and others added 2 commits September 21, 2026 11:48
… too

The five internal/release tests that asserted a normal run names the
channel, tag and platform now ask for --verbose, where those lines
moved, and read the receipt on a normal run. A missing release names its
tag in the failure line itself, so a person is told which tag was not
there without --verbose. The touched-packages gate never ran them for a
scripts-only change; that gap is CI's to close separately.

poolcfg reads CODEAF_TELEMETRY_ENDPOINT as the third environment rung of
the telemetry off switch: set and empty caps the pool at read, as
docs/TELEMETRY.md already claimed. And the comment over
telemetryRowsOff now says why the legacy AFORGE_TELEMETRY spelling
reaches the pool — ProjectBoolAt falls through to TelemetryAt, which
reads the pin through internal/env — so nobody drops it by tidying.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…message-b41040

# Conflicts:
#	docs/GUIDE.md
#	scripts/install.sh

@AbirAbbas AbirAbbas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-verified 2026-09-21 after the review items were addressed: each item checked in the code and on the built binary, gates green locally and in CI at this head. Details in the review ledger.

@AbirAbbas
AbirAbbas merged commit 49b4a58 into dev Sep 21, 2026
4 checks passed
@AbirAbbas
AbirAbbas deleted the claude/codeaf-install-message-b41040 branch September 21, 2026 19:00
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.

2 participants