Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,38 @@ jobs:
if ($out -match 'listening on') { throw "a sink that failed to bind claimed it was listening: $out" }
if ($err -match '\x1b') { throw "the collision message emitted ANSI escapes; it is read in pipes and pastes: $err" }

# packaging/install.ps1 is the `irm | iex` route (design.md §8, added
# 2026-08-18). It is the one shipped file no Go test can reach, and it
# runs on a stranger's machine before anything else telltale does.
#
# This step PARSES it and never executes it: running it would download a
# release on every push. It runs under Windows PowerShell 5.1 rather than
# pwsh, because 5.1 is the shell the trap lives in — it reads a BOM-less
# file as ANSI, so a single em dash anywhere in this file breaks the parse
# before the script runs. That was measured on 2026-08-18 and it cost four
# parser errors under 5.1.26100.9168 while pwsh 7.6.5 read the same file
# fine, which is exactly the shape of defect a gate has to hold.
- name: Gate (install.ps1 parses under Windows PowerShell 5.1, and is ASCII only)
shell: powershell
run: |
$path = (Resolve-Path packaging/install.ps1).Path
$bytes = [IO.File]::ReadAllBytes($path)
$high = @($bytes | Where-Object { $_ -ge 0x80 }).Count
if ($high -ne 0) {
throw "install.ps1 carries $high byte(s) at or above 0x80. Windows PowerShell 5.1 reads this BOM-less file as ANSI and the parse breaks. Keep it ASCII."
}
$errors = $null
$null = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$errors)
if ($errors.Count -gt 0) {
throw "install.ps1 does not parse: $($errors | ForEach-Object { $_.Message })"
}
# `exit` inside a piped script ends the user's own shell session, so
# the script throws instead. A future edit that reaches for `exit` is
# the regression this line catches.
if (Select-String -Path $path -Pattern '^\s*exit\b' -Quiet) {
throw "install.ps1 calls exit; a script run through irm | iex ends the user's shell that way. Throw instead."
}

# ADR-002's fast path, on the clock rather than on anecdote (design.md §5,
# amendment 2026-08-16). This runs LAST on purpose: it drives full.json,
# which carries rate_limits and therefore writes the quota relay, and two
Expand Down
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ Every number comes from measured tool output.

> A telltale is the ribbon on a sail. It shows the air. It does not interpret it.

[![CI](https://github.com/sanlee-ys/telltale/actions/workflows/ci.yml/badge.svg)](https://github.com/sanlee-ys/telltale/actions/workflows/ci.yml)

<!-- BADGE SLOT. One rule, and it is the honest-gauge rule wearing a different
hat: a badge states a fact somebody measured, and it names who measured it.
The CI badge above is GitHub rendering GitHub's own run result, so it needs
no third-party host and cannot go stale.
Allowed here later: a directory-inclusion badge, once that listing has
actually merged. Never here: a star count, a download count, an install
count, or any "used by" figure — telltale measures none of them, and a
third-party render of an unmeasured number is the badge form of a rendered
guess. See docs/design.md §8, the 2026-08-18 amendment. -->

<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/telltale-council-dark.svg">
Expand All @@ -15,6 +27,17 @@ Every number comes from measured tool output.
</picture>
</p>

<!-- HERO SLOT, for the animated capture that replaces or joins the still above.
It is the last open piece of adoption item 1 (docs/design.md §8). Two
conditions bind whatever lands here, and neither is negotiable by the
session that lands it:
1. The owner drives the eight beats. A scripted race is an invented
recording (design.md §8, the recording chain).
2. Every frame gets a review for workspace paths, session names and seat
identity before the capture is committed (owner's ruling, 2026-08-17).
The runbook is packaging/tape/README.md. No cast or GIF is in this
repository today. -->

**v0.2.0** (2026-08-14). Windows is verified on every commit.
Intel macOS is smoke-checked. `darwin_arm64` and `linux_amd64` are built, not run.
No binary is signed. Check `checksums.txt` on the release.
Expand All @@ -31,6 +54,18 @@ go build -o telltale.exe ./cmd/telltale

A source build reports `dev` from `telltale version`. A release binary reports its tag.

**Windows, one paste** (measured against `v0.2.0`, 2026-08-18)

```powershell
irm https://raw.githubusercontent.com/sanlee-ys/telltale/main/packaging/install.ps1 | iex
```

It downloads the latest release, checks the archive against `checksums.txt`,
refuses on a mismatch, and puts `telltale.exe` on your user `PATH`.
It needs no administrator rights. The binary it installs is **not signed**,
and the script says so before it names the next command.
Source and knobs: [packaging/install.ps1](packaging/install.ps1).

**Windows, scoop** (exercised once, 2026-08-14)

```
Expand All @@ -57,8 +92,8 @@ passes, run `xattr -d com.apple.quarantine telltale`. Do not add that line
to the `curl` block: `curl` does not set the mark, and the command then
exits 1. The measured walk is in [SECURITY.md](SECURITY.md).

**Windows, winget.** Not submitted. Use scoop or a source build.
Draft: [packaging/](packaging/).
**Windows, winget.** Not submitted. Use the one paste above, scoop, or a
source build. Draft: [packaging/](packaging/).

Run `telltale` with no arguments for the first frame.
`telltale doctor` is the preflight. `telltale council` opens the room.
Expand Down Expand Up @@ -109,6 +144,28 @@ HUD flags: `--vendor all|claude|codex|gemini|agy|cursor|grok`,
`--hide gemini,cursor` (default from `TELLTALE_HUD_HIDE`),
`--ascii` (`TELLTALE_ASCII=1`), `--no-title`. `NO_COLOR` is honoured.

## First five minutes

Run `telltale doctor` first. It reports what is installed here, it probes no
login and makes no network call, and it **exits 0 even when every seat is
missing**. Read its own words before you read this table: each row below is
keyed to a line `doctor` actually prints.

| What you see | What it means | What to do |
|---|---|---|
| `0 checks passed`, and `no seat above passed every check that ran` | telltale is working. `council` drives a vendor CLI, and this machine has none. | Install one vendor CLI and run `doctor` again. `telltale hud` runs either way. |
| `binary FAILED not found on PATH (looked for codex)` | This shell cannot resolve that vendor. | Open the shell you normally run the vendor in, or put its binary on `PATH`. `doctor` also finds a vendor at a known install location and says `a known install location, not on this shell's PATH`. |
| `drivable FAILED` under a `binary ok` | The binary is here and council will not seat it. "Is it there" and "can it be driven" have different fixes, so `doctor` refuses to collapse them. | Read the reason on that row. It names the entry point and why: usually a shell shim that takes its prompt as an argument, which council will not put through `cmd.exe`. |
| `auth not checked` and `network not checked`, on every seat, always | Not a failure and not a soft pass. This report probes neither. | Nothing. A seat that is installed and signed out reports its own auth failure on its column the first time you dispatch to it. |
| `re-measure §3.x before trusting the fields this adapter sources` | Your vendor runs a version other than the one telltale surveyed. | Nothing on this machine. It is a staleness fact about telltale: no check failed, the tally is unchanged, and the command still exits 0. |
| `telltale version` says `dev`, or an older tag, after the install | Another `telltale.exe` is earlier on `PATH`. The install script appends its directory rather than jumping the queue. | Run `Get-Command telltale`. It names the one that runs. Remove the other one, or set `TELLTALE_INSTALL_DIR` to the directory it already lives in. |
| A column in the room stays empty after a dispatch | The seat answered nothing, or the vendor refused the turn. | The column carries the reason. [docs/council.md](docs/council.md) reads the badges and the phase words. |
| Windows warns before the first run | The binary is unsigned. No telltale release carries an Authenticode signature ([docs/design.md §8](docs/design.md#s8), item 8). | Verify the archive against `checksums.txt`, which is the whole verification this release offers. [SECURITY.md](SECURITY.md) states what that does and does not prove. |
| The statusline shows nothing, or `bad statusline input: unexpected end of JSON input` | The statusline is wired, not run. The vendor calls it and hands it JSON on stdin, so by hand it gets no payload. | Paste the `statusLine.command` block above, then start a session. |

`telltale doctor` output pastes into an issue as it stands: it is plain text
with no colour and no alternate screen, for exactly that reason.

## What it is

- **`telltale council`:** one brief, five vendor columns. This is the product.
Expand Down
14 changes: 8 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ not change any statement in this section.

The consequences, per platform:

- **Windows.** The binary carries no Authenticode signature. `scoop` and `winget`
install that same unsigned binary. A direct download through a browser can
raise a Microsoft Defender SmartScreen prompt, because SmartScreen weighs the
signature and the download reputation. This project has not measured that
prompt.
- **Windows.** The binary carries no Authenticode signature. `scoop`, `winget`
and `packaging/install.ps1` install that same unsigned binary, and the script
says so in its own output before it names the next command. A direct download
through a browser can raise a Microsoft Defender SmartScreen prompt, because
SmartScreen weighs the signature and the download reputation. This project has
not measured that prompt.
- **macOS.** The `darwin_amd64` and `darwin_arm64` archives are unsigned and not
notarized. macOS applies the `com.apple.quarantine` attribute to a file that a
browser downloads, and Gatekeeper then refuses to run an unsigned, un-notarized
Expand All @@ -111,7 +112,8 @@ The consequences, per platform:
**Verify the checksum.** Every release attaches `checksums.txt` with a SHA-256
for each archive. That file tells you the archive is the one the release workflow
produced. It does not tell you who produced it. `scoop` verifies the SHA-256
itself from the manifest.
itself from the manifest, and `packaging/install.ps1` verifies it against
`checksums.txt` before it unpacks anything, deleting the download on a mismatch.

Signing is not planned work with a date. It needs a certificate or an Apple
Developer account that the owner holds, plus release secrets, so it is an owner
Expand Down
24 changes: 24 additions & 0 deletions STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,30 @@ Nothing open. The last one here was the 44 seconds, and it was measured

## Known gaps, not yet owned

- **The launch playbook's outward half is OWNER WORK and is unowned
(2026-08-18).** [design.md §8](docs/design.md#s8)'s 2026-08-18 amendment
records the cadence; recording it is all a session may do. Three pieces wait
on the owner. **Directory listings** (`awesome-claude-code` and its
neighbours) are pull requests to other people's repositories, which takes
winget's ruling: a human action, never automated, never opened by a
contributor session — and the README badge slot fills only after a listing
merges. **The Show HN cadence** is sequenced behind chain link 3 and is
pinned to one hypothesis, so a second post tests its own feature's question
and says so. **The run-evidence bar's threshold is undecided**: §8 item 2
already fixes the KIND of evidence, and the count, the window, and what a
miss means are the owner's to name. The sweep's "10 runs in 30 days" is a
proposal with no measurement behind it and was deliberately not adopted.

- **`packaging/install.ps1`'s mismatch refusal has no end-to-end live trial
(2026-08-18).** The happy path ran on PowerShell 7.6.5 and Windows
PowerShell 5.1.26100.9168 against the published `v0.2.0`, and two refusals
ran end to end (an arm64 machine, and a tag with no release). Driving the
checksum mismatch needs a host that serves a corrupted archive. The
comparison itself was measured live over the real `checksums.txt` and a
tampered copy of the real archive, and the hashes differed; the three lines
that act on that result are unexercised. §8's 2026-08-18 amendment carries
the record.

- **A live ordinary-turn give-up is owed on the reference box before
2026-09-30.** `x` on an ordinary turn shipped 2026-08-17 with offline tests
only. Whether a real vendor's interrupt lands mid-turn, and whether the
Expand Down
Loading