Skip to content

atomic: 0.9.15 -> 0.9.16 - #2877

Merged
cameronraysmith merged 1 commit into
mainfrom
fm/vx-atomic-0-9-16
Aug 29, 2026
Merged

atomic: 0.9.15 -> 0.9.16#2877
cameronraysmith merged 1 commit into
mainfrom
fm/vx-atomic-0-9-16

Conversation

@cameronraysmith

Copy link
Copy Markdown
Owner

What

atomic 0.9.15 -> 0.9.16 via nix run .#update-atomic, plus a stale-test fix and two stale comment counts the update falsified.

The script resolved npm's latest dist-tag to exactly 0.9.16, as required. next is 0.9.17-alpha.1 and stays out of scope: the script reads the stable tag by construction and accepts no version argument.

The four derived artifacts moved coherently

artifact result
manifest.json 0.9.15 -> 0.9.16
src hash sha256-IUhR/RS0... -> sha256-eAgebHtz...
npmDepsHash sha256-EHtSTNyz... -> sha256-1UIAt5YD...
npm-dist-repairs.patch regenerated; 21 references to 0.9.16, zero 0.9.15 remnants

The patch's shape was audited rather than assumed: 10 shrinkwrap hunks each backfilling one integrity line, and one package.json hunk removing devDependencies. Re-derived independently from the 0.9.16 tarball: lockfileVersion 3, 359 packages (the header comment's claim still holds), 10 entries missing integrity, 10 devDependencies.

Two defects fixed alongside the bump

A stale test assertion, falsified by upstream. atomic.tests.help asserts three literals from --help. atomic 0.9.16 documents a -- end-of-options separator, so the usage line at packages/coding-agent/src/cli/args.ts:262 changed:

-  atomic [options] [@files...] [messages...]
+  atomic [options] [--] [@files...] [messages...]

.#atomic itself built green — versionCheckHook and postInstallCheck both pass — so this failed only in the passthru test. Worth noting how it surfaced: the first verification run was piped to tee, which reported the pipeline's exit status and showed success. The failure was visible only on a direct, unpiped nix build. The assertion is updated to the new literal rather than loosened to a substring, since its exactness is what caught the change.

Two stale counts in the package.nix header. The patch backfills integrity for 10 entries, not 9, and they are not all atomic-natives*@bastani/pi-ai is among them, and already was at 0.9.15, so "the 9 @bastani/atomic-natives* entries" was wrong before this bump. The removed devDependencies were 12 at 0.9.15 and are 10 at 0.9.16 (upstream dropped @types/diff and @types/ms). Both are now stated as mechanism rather than as numbers, because update.sh selects entries by the absence of integrity rather than by name, so any fixed count goes stale at the next bump. The same stale parenthetical in update.sh:52 is corrected.

Verification

nix build -L --no-link .#atomic .#atomic.tests.help passes, with versionCheckHook reporting 0.9.16 from the built binary.

Checks run, all green:

  • package-atomic, package-atomic-test-help — the package and its passthru test
  • treefmt — a .nix and a .sh file changed (438 files processed, 0 changed)
  • atomic-agent-environment-structural — the only other atomic-named check

Deliberately not run: the remaining ~60 checks in .#checks.aarch64-darwin. This change touches one package directory and no shared infrastructure, so none of them could fail on account of it. just check-fast belongs to pre-merge validation, not to a single-package bump.

Release review against the firstmate adapter contract

firstmate runs atomic as a worker runtime. Per-surface verdicts follow, each read against tag 0.9.16 in the local checkout at /Users/crs58/ghq/github.com/bastani-inc/atomic.

Two framing corrections first, because they change what the surfaces mean.

The PRs measured 0.9.13, not 0.9.15. firstmate PRs 26-29 name atomic 0.9.13 throughout, measured 2026-08-19/20; no PR in that repo re-verifies on 0.9.15. Reviewing only 0.9.15..0.9.16 would therefore have produced a true-but-useless "nothing invalidated" against a baseline no evidence was ever recorded on. The 0.9.13..0.9.15 window was audited as well: 1519 files, +140849/-13115, dated 2026-08-13 to 2026-08-21.

Two surfaces as briefed do not exist. atomic has no --effort flag at any of the three tags (git grep effort in the CLI layer: zero hits); firstmate maps its effort levels onto --thinking. And --model provider/id does not pin a provider — the measured 0.9.13 fact is the opposite of the brief's premise, which is why firstmate passes two flags, --provider <p> --model <id>.

# Surface 0.9.13..0.9.15 0.9.15..0.9.16
1 Agent classification / busy determination unchanged unchanged
2 -na extension isolation unchanged unchanged
3 Turn-end and stop signalling unchanged unchanged
4 Resumable session file unchanged unchanged
5 --model / --provider acceptance unchanged unchanged
6 --thinking (not --effort) unchanged unchanged
7 registerTool / defineTool unchanged unchanged (additive)
8 Intercom always enabled n/a changed, breaking as a capability

1, 3 — classification and turn-end/stop. atomic exposes no agent-kind registry that firstmate consumes; the --kind list is herdr's own and has no atomic entry, which is why firstmate carries a fallback classifier. What it actually consumes is three events and one rendered string, all confirmed present and live at 0.9.16: agent_start, agent_end, agent_settled at core/extensions/agent-events.ts:51-64; the on() overloads and ExtensionHandler = (event, ctx) => ... at core/extensions/api-types.ts:80,114-118; isIdle(): boolean at core/extensions/context-types.ts:182, implemented isIdle: () => !this.isStreaming at core/agent-session-extension-bindings.ts:228. The ordering invariant firstmate depends on — agent_settled emitted last, from a finally block after retries, compaction, and queued continuations — holds structurally at core/agent-session-prompt.ts:246-261, and core/agent-session-summary.ts:48-53 still documents why agent_end alone is unsafe. The rendered busy token is byte-identical: core/interactive/components/footer.ts:251, this.session.isStreaming ? this.renderStyle.muted("esc to interrupt"). The 12-line footer.ts diff touches only compaction usage lines and a Copilot fast-mode parameter.

2 — -na. args.ts:216-219 sets projectTrustOverride = false, byte-identical to 0.9.15. The whole causal chain from that flag to "host repo extensions are not scanned" is byte-identical: package-manager-auto-resources.ts:118-147 (the only place a project root's extensions/ is scanned, gated on isProjectTrusted()), config.ts:243-246,387-390 (CONFIG_DIR_NAMES = [".atomic", ".pi"], both scanned when trusted, neither under -na), and trust-manager.ts:68-97 (the five-option blocking dialog, never rendered because main.ts:472-473 skips registering the resolver). An explicit outside-project -e path still loads under -na: resource-loader-reload.ts:274-278 includes cliEnabledExtensions unconditionally. That combination is the entire isolation mechanism and it survives intact.

One adjacent change worth knowing: -ne/--no-extensions no longer means "zero extensions" — mandatory bundled Intercom now loads regardless. This does not affect firstmate, which passes -na and explicitly rejected -ne as too blunt. It is changed-compatible for -na specifically because getMandatoryBuiltinExtensionPaths() resolves from the atomic install directory, never from cwd, so it cannot reintroduce host-repository extension loading.

4 — session file. Every element confirmed at 0.9.16: root ${HOME}/.atomic/agent/sessions with ATOMIC_CODING_AGENT_DIR override (config.ts:241-254,359-364,466); per-cwd directory (core/session-manager-paths.ts:8-15, empty diff); filename <timestamp>_<session-id>.jsonl (core/session-manager-entries.ts:62-65); --session-id pinning it (args.ts:136-137 -> main-session.ts:233 -> core/session-manager-core.ts:127-151); one compact JSON line per entry (core/session-manager-storage.ts:194, no pretty-printing); and the cancellation record with stopReason a field on the assistant message itself (core/agent-session-prompt.ts:319). CURRENT_SESSION_VERSION = 3 at core/session-manager-types.ts:5 — checked explicitly at all three tags, never bumped. The one real session-manager-storage.ts change (commit abb3a308) reorders persistAppendedEntry branches, but only along the flush()/workflow-stage path, unreachable from the plain interactive launch firstmate performs.

5 — model flags. --provider still pins (core/model-resolver-cli.ts:116-123, byte-identical file), --model still accepts a bare id via resolveRawExactModel (:15-56), and --list-models still prints provider, model, context, max-out, thinking, images (cli/list-models.ts:60-111, byte-identical), restricted to credential-holding providers with no network call (core/model-runtime-snapshot.ts, byte-identical). One benign delta: main.ts:668 now emits startup diagnostics before the list-models branch, on stderr only, so the stdout table a preflight parser reads is untouched.

6 — --thinking. VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] at cli/args.ts:67, byte-identical text at the same line number in all three tags. Seven levels, none added or removed. An out-of-range value still pushes type: "warning" (args.ts:158-166), and main.ts:314-320 exits only on type: "error", so a bad level still degrades to the default with exit 0 rather than failing the spawn. Per-model clamping unchanged (core/sdk.ts:213-218).

7 — tool registration. registerTool at core/extensions/api-types.ts:141-143 and defineTool at core/extensions/tool-types.ts:120 are byte-identical (the latter file has an empty diff); only line numbers shifted, from new event types added earlier in the file. All 22 registerTool and 2 defineTool location deltas are line-number churn or internal call-site restructuring within atomic's own bundled packages. The @earendil-works/pi-coding-agent alias still resolves to the same target as @bastani/atomic (core/extensions/loader-virtual-modules.ts:458-460), and the public barrel's create* export set changed only by addition (the PowerShell family): zero removals, zero renames. createExtensionAPI did change return shape, but it is exported from no package.json entry, so no out-of-tree extension touches it.

For the record on a pre-existing trap: createGrepToolDefinition is defined internally at both tags but was never in the public barrel at either, so the pi-vs-atomic naming divergence PR 26 worked around predates this window and is unaffected by it.

8 — intercom always enabled. This is the one genuine behaviour change, and it is breaking as a capability: every opt-out is gone. The enabled field, its true default, and its parse branch were deleted from packages/intercom/config.ts outright, along with both consumption sites (index-heavy.ts:395, lifecycle.ts:71). packages/intercom/CHANGELOG.md:23 states it plainly. Enforcement is layered and deliberate: mandatory: true on the package (core/builtin-packages.ts:33), a new core/mandatory-runtime-tools.ts with a WeakSet anti-spoofing guard, an isMandatoryRuntimeTool short-circuit ahead of allow/denylist checks (core/agent-session-tool-registry.ts:21), re-injection plus a hard throw in core/mandatory-resource-loader.ts:13-32, called unconditionally from core/sdk.ts:145, and stageHasIntercomAccess() reduced to return true (packages/workflows/src/shared/intercom-group.ts:46). Upstream's own acceptance matrix enumerates every plausible opt-out and confirms each fails, so this is intentional and exhaustively checked, not an oversight.

What it does not do: no footer change, no startup dialog, no new approval surface. Intercom's only confirm is gated on confirmSend (default false) and ctx.hasUI, which is false for a headless session, so that branch is structurally unreachable for an unattended worker. Registration alone is inert; the broker stays lazy until a surface fires.

Three consequences a maintainer should weigh before deploying, none of which invalidate recorded evidence:

  1. The model can now always call intercom, including the ask action, which blocks the tool call for up to DEFAULT_REPLY_TIMEOUT_MS = 10 * 60 * 1000 (packages/intercom/reply-waiter.ts:27). For a worker with no peer that is a real ten-minute stall per call. It would present as busy, not wedged, so a supervisor watching liveness would not flag it.
  2. Two workers sharing an agent directory converge on one broker socket at ~/.atomic/agent/intercom/broker.sock, keyed by ATOMIC_CODING_AGENT_DIR. The socket scheme predates 0.9.16; what is new is that enabled: false no longer avoids it.
  3. If bundled @bastani/intercom fails to load, mandatory-resource-loader.ts:31 now throws and aborts session creation, where at 0.9.15 an optional bundled extension's load failure did not.

Evidence invalidated in firstmate PRs 26, 27, 28, 29

None. Empty, and evidenced across the full 0.9.13..0.9.16 chain rather than the narrower window the task named.

All seven surfaces those PRs rest on are unchanged in both windows. The 0.9.13..0.9.15 window is large (+140849 lines) but the size is one structural event: @earendil-works/pi-ai, an external dependency pinned to 0.84.1, was vendored into the monorepo as packages/ai and every import site mechanically rewritten to @bastani/pi-ai. Nearly every touched file shows only import-line churn.

That vendoring created the one case needing cross-repository proof. clampThinkingLevel, which surface 6 depends on, did not exist in the atomic repo at 0.9.13 — it lived in the external dependency. So "unchanged" could not be established from atomic's history alone. Diffing the vendored packages/ai/src/models.ts at atomic 0.9.15 against v0.84.1 of the upstream earendil-works/pi checkout returned empty: a byte-for-byte copy of the exact source 0.9.13 depended on.

Surface 8 is the only changed surface, and no recorded evidence rests on it: firstmate contains zero references to intercom in any file — code, config, doc, or test. Its risk is prospective operational exposure, itemised above, not retroactive invalidation.

Not done

No deploy. No clan machines update, no host state change of any kind. No other package bumped; 0.9.17-alpha.1 untouched.

Run via `nix run .#update-atomic`, which resolved npm's latest dist-tag to
0.9.16 (next is 0.9.17-alpha.1, out of scope: the script consumes the stable
tag by construction and takes no version argument). All four artifacts the
script derives moved together: manifest.json, the src hash, npmDepsHash, and a
regenerated npm-dist-repairs.patch carrying 21 references to 0.9.16 and no
0.9.15 remnants.

Fix the tests.help usage assertion, which upstream falsified. atomic 0.9.16
documents a `--` end-of-options separator, so the usage line at
packages/coding-agent/src/cli/args.ts:262 went from

  atomic [options] [@files...] [messages...]

to

  atomic [options] [--] [@files...] [messages...]

The literal grep therefore failed while --version and the other two help
assertions passed. Updating the literal keeps the assertion severe rather than
loosening it to a substring: it is what caught this change.

Correct two stale counts in the header comment. The patch backfills integrity
for 10 shrinkwrap entries, not 9, and they are not all atomic-natives* -
@bastani/pi-ai is among them, and already was at 0.9.15, so "the 9
@bastani/atomic-natives* entries" was wrong before this bump. The removed
devDependencies were 12 at 0.9.15 and are 10 at 0.9.16 (upstream dropped
@types/diff and @types/ms). Both counts are now stated as mechanism rather than
as numbers, since update.sh selects entries by the absence of `integrity`
rather than by name and any fixed count goes stale at the next bump. The
"lockfileVersion 3, 359 packages" claim was re-checked against the 0.9.16
tarball and still holds.

Verification: `nix build -L --no-link .#atomic .#atomic.tests.help` passes,
with versionCheckHook reporting 0.9.16 from the built binary. Checks run:
package-atomic, package-atomic-test-help, treefmt (a .nix and a .sh file
changed), and atomic-agent-environment-structural as the only other
atomic-named check. The full check set is deliberately not run: this change
touches one package directory and no shared infrastructure, so the remaining
checks could not fail on account of it.
@mergify

mergify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@cameronraysmith
cameronraysmith merged commit b129b90 into main Aug 29, 2026
8 checks passed
@cameronraysmith
cameronraysmith deleted the fm/vx-atomic-0-9-16 branch August 29, 2026 18:59
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.

1 participant