Skip to content

docs+ui: make the public claims match what actually ships (runtimes, memfs, Letta surface, budget, license) - #188

Merged
juniperbevensee merged 4 commits into
mainfrom
dev/juniperbevensee/honest-public-claims
Jul 30, 2026
Merged

docs+ui: make the public claims match what actually ships (runtimes, memfs, Letta surface, budget, license)#188
juniperbevensee merged 4 commits into
mainfrom
dev/juniperbevensee/honest-public-claims

Conversation

@juniperbevensee

@juniperbevensee juniperbevensee commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

The README headline still said "Multiplayer admin and orchestrator platform for Hermes" while the product now claims two first-class runtimes. Several other surfaces went the other way and promised Letta capability we have not shipped. This PR fixes the framing and, more importantly, removes the overclaims — every correction below is checkable against main.

Do not merge without reading the evidence column. The whole point of this PR is that the claims are verifiable.


1. Headline and framing (claim was contradictory)

Claim before Status Now
"Multiplayer admin and orchestrator platform for Hermes" Contradicts the two-runtime story "…for heterogeneous agent runtimes" — Hermes (container-per-agent) and Letta (memory-first agents as rows on one shared server)
Headline linked NimbleCoAI/hermes-agent Stale org NimbleCoOrg/hermes-agent-mt
Letta link absent letta-ai/letta — verified live via gh repo view letta-ai/letta

Also repointed three NimbleCoAI/hermes-agent links in docs/migrating.md (lines 11, 203, 206) and the Requirements bullet that pointed only at NousResearch/hermes-agent (now names the MT fork we actually deploy, crediting upstream).

Not touched on purpose: the Quick Start git clone URL. That is the single README change in open PR #187 — left alone so this PR does not conflict there.

2. Overclaims corrected

memfs / git-backed "Context files" — was promising a feature that is off

Evidence: git_enabled has zero occurrences anywhere in this repository (grep -rn "git_enabled" . --exclude-dir=node_modules --exclude-dir=.git → no hits), and agents created by our deploy path come up git_enabled: false.

components/harness/letta-agent-detail.tsx rendered a "Context files (memfs)" panel headed by a paragraph describing "the agent's git-backed memory file tree", followed — for every agent we create — by an empty box and the excuse "No context files (agent may predate memfs, or files aren't exposed on this server)". That reads as "your agent happens to have no files", not "this feature is not on".

Fixed:

  • Heading now carries a not enabled badge whenever the list is empty.
  • The git-backed-memory explanation only renders when files are actually present (so it stays useful for externally-created agents that do have memfs).
  • Empty state now says git-backed memory files are not enabled for agents Swarm Map deploys, points at Core memory (blocks) as the real live memory surface, and states that enabling memfs is not shipped.
  • MemoryBlocks no longer says blocks "project into system/ files" as present tense.
  • Wizard copy app/(setup)/setup/wizard/page.tsx:590 said "Letta agents use memfs memory (git-backed context files)" → corrected to core-memory blocks, with memfs marked not enabled.
  • Wizard runtime picker (line 452) dropped "(memfs memory)".
  • The file's header comment now carries a MEMFS IS NOT ENABLED — DO NOT WRITE COPY THAT PROMISES IT note with the evidence, so the next person editing this file cannot reintroduce the claim by accident.

The Letta API surface is read-only + send-message

Evidence — full route inventory, reproduced from docs/runtimes.md:

--- app/api/letta/harnesses/route.ts            8:export async function GET() {
--- app/api/letta/agents/route.ts               7:export async function GET() {
--- app/api/letta/agents/[id]/blocks/route.ts   6:export async function GET(
--- app/api/letta/agents/[id]/messages/route.ts 6:export async function POST(
--- app/api/letta/agents/[id]/files/route.ts    7:export async function GET(

Four GETs and one POST. No PATCH, PUT or DELETE anywhere under app/api/letta/. So there is no lifecycle or memory-management parity to claim:

  • README now tabulates the Letta routes exactly, labelled "read-only plus send-message… there is no PATCH, PUT, or DELETE".
  • Detail-view copy no longer says a Letta agent is "managed over REST" — it now says read-only plus sending a message, with editing memory / reconfiguring / deleting called out as not shipped.
  • Stated honestly where creation does live: POST /api/setup/deploy with the Letta runtime selected (deployLettaAgent). The one services.letta.deleteAgent call is rollback-only for a failed deploy — docs/runtimes.md says so explicitly so nobody reads it as a user-facing delete.

Budget does not inherit on the default path

Approval and audit do inherit through a Hermes front — they gate the turn before it runs. Budget does not, because streaming is on by default and a streamed turn reports zero tokens to the enforcement hook, so spend is never counted and a soft cap is never approached.

Every place this could be read as three-for-three inheritance now says two-for-three and names the streaming reason:

  • README Runtimes table + a "Budget caveat, stated plainly" paragraph.
  • docs/runtimes.md §3, with the explicit instruction "Do not write 'the bridge inherits the door's approval, audit and budget'."
  • Wizard step 3 Letta panel — the place an operator would most naturally assume the door's guarantees carry over.

New: docs/runtimes.md

The shipped/not-shipped matrix with the verification commands inline, so this stops being tribal knowledge. Linked from the README headline and the Documentation section.

3. Repo metadata (done via gh, outside this diff)

  • Description was Multiplayer admin and orchestration layer for Hermes agents. → now names both runtimes and the control-plane guarantees.
  • Topics were agentic-ai, ai, hermes, local-ai, multiplayer → now agent-orchestration, agentic-ai, ai, letta, local-ai, multi-agent, multiplayer, swarm-map. hermes removed (Hermes-only framing), letta and swarm-map added.

4. License detection — Other/NOASSERTION vs the AGPL v3 claim

The repo shipped an AGPL v3 LICENSE, but GitHub's licence API returned {"key":"other","spdx_id":"NOASSERTION"} — contradicting both the README and the website.

Cause: the file was not verbatim. Three lines were prepended above the licence text:

Hermes Swarm Map
Copyright (C) 2025-2026 Juniper Bevensee and contributors
<blank>

Everything from line 4 down was already byte-identical to https://www.gnu.org/licenses/agpl-3.0.txt (661 lines, verified by diff). Only the header deviated, and that is the sole plausible reason the classifier failed.

Fix: dropped the three-line header, so LICENSE is now byte-identical to gnu.org's agpl-3.0.txt (diff -q clean). The project attribution moved to a new NOTICE file — which also records why, so it does not get re-prepended — plus a copyright line in the README's License section.

Verified against GitHub's own classifier, not just asserted — same endpoint, two refs:

$ gh api repos/NimbleCoOrg/swarm-map/license                                  # main
{"key": "other",     "spdx_id": "NOASSERTION", "url": null}

$ gh api "repos/NimbleCoOrg/swarm-map/license?ref=dev/juniperbevensee/honest-public-claims"
{"key": "agpl-3.0",  "spdx_id": "AGPL-3.0",    "name": "GNU Affero General Public License v3.0"}

The three-line header was the entire cause. On merge the repo sidebar and the licence API will read AGPL-3.0, matching the README and the website.

⚠️ Merge-order note: PR #187 also touches LICENSE — its only change there is line 1, Hermes Swarm MapSwarm Map. That line no longer exists in this branch, so the two will conflict. Resolution: keep this PR's LICENSE (verbatim AGPL) — #187's intent is preserved, because NOTICE already carries the corrected Swarm Map name.

5. docs/migrating.md GHCR namespace

The Tier 3 image mapping already pointed at ghcr.io/nimblecoorg/..., so there was nothing to repoint. Added a callout making that path explicitly canonical ("what the wizard deploys, what lib/seed.ts defaults to, and the only path we publish to"), and telling readers to repoint any compose file or runbook pinned under another namespace. No other namespace is named or advertised, and no package was deleted.


Verification

  • npx tsc --noEmit — clean.
  • npx vitest run893/893 tests pass, 86/86 files.
  • npx eslint on both changed components — the single set-state-in-effect error in the wizard is pre-existing on main (confirmed by re-running the lint against a stashed tree; it is at line 189, nowhere near the copy edits).
  • LICENSE vs canonical: diff -q agpl-3.0.txt LICENSE → identical; and GitHub's licence API returns AGPL-3.0 for this branch's ref (other/NOASSERTION on main).
  • CI on this branch: sanitization SUCCESS, Supply Chain Audit SUCCESS, OSV-Scanner SUCCESS.
  • Letta route inventory: reproduced above; the exact command is in docs/runtimes.md so a reviewer can re-run it.

Not merging — flagging the #187 LICENSE conflict for a human to sequence.

🤖 Generated with Claude Code

The README headline said "Multiplayer admin and orchestrator platform
for Hermes" while the product now claims two first-class runtimes, and
several surfaces promised Letta capability we have not shipped. This
corrects the framing and, more importantly, corrects the overclaims —
without inventing new ones.

Headline / framing
- README leads with heterogeneous runtimes: Hermes (container-per-agent)
  and Letta (memory-first agents as rows on one shared server).
- Stale NimbleCoAI/hermes-agent link repointed to NimbleCoOrg/hermes-agent-mt
  (three more in docs/migrating.md).

Overclaims corrected (each verified against main, see docs/runtimes.md)
- memfs is NOT enabled. `git_enabled` has zero occurrences in this repo
  and our deploy path creates agents with it false. The "Context files
  (memfs)" panel rendered an empty box under a heading promising a
  git-backed memory tree; it now carries a "not enabled" badge and says
  so, with core memory named as the live memory surface. Wizard copy
  claiming "Letta agents use memfs memory" corrected likewise.
- The Letta API surface is read-only + send-message. Four GETs and one
  POST /messages, no PATCH/PUT/DELETE anywhere under app/api/letta.
  README now tabulates that surface exactly; UI copy no longer says
  Letta agents are "managed over REST".
- Budget does not inherit on the default path. Approval and audit gate
  the turn before it runs and do inherit through a Hermes front; a
  streamed turn reports zero tokens, and streaming is on by default, so
  budget only holds on the blocking path. Qualified in both places.

License detection
- LICENSE had the project name + copyright prepended above the AGPL
  text, which is why GitHub reported Other/NOASSERTION against a README
  and website that both say AGPL v3. LICENSE is now byte-identical to
  gnu.org's agpl-3.0.txt; attribution moved to a new NOTICE file and the
  README license section.

New: docs/runtimes.md — the shipped/not-shipped matrix with the
verification commands, so this does not silently rot again.

Verified: tsc --noEmit clean, 893/893 tests pass, eslint clean on both
changed components (the one wizard lint error is pre-existing on main).
…vation

The zero-hits grep proves we never REQUEST git_enabled, so agents take
the server default. That the default is false is a separate fact, from a
live-server observation rather than from this repo. Say which is which,
so a reader can tell what is checkable here and what is not.
Independent audit of the honest-claims PR found it fixed old overclaims but
introduced two new false statements. Both are corrected here.

1. "Streaming is on by default, so budget does not inherit" — the premise is
   false and so is the implied remedy.

   Gateway token streaming is OFF unless an operator enables it. It follows the
   top-level `streaming` config, whose `StreamingConfig.enabled` defaults to
   false (hermes-agent-mt gateway/config.py). The `display.streaming: true` that
   lib/templates/config-yaml.ts writes is CLI-only — gateway/display_config.py
   explicitly skips `display.streaming` when resolving gateway streaming. So a
   proxied turn takes the blocking client by default, and
   _setup_platform_stream_consumer returns None.

   Forcing the blocking path does not restore budget either. The blocking client
   does extract real prompt/completion tokens, but records them as
   `last_prompt_tokens` (context size). Swarm Map's budget check sums
   `input_tokens`/`output_tokens` from the agent's state.db `sessions` table
   (lib/services/usage.ts), which the Letta bridge never writes.

   Net: budget does not inherit on ANY path, and streaming is not the cause.
   Stating "only meaningful on the non-streaming path" told operators a
   non-existent workaround would bound their spend. Now stated as: approval and
   audit inherit, budget does not.

2. "`git_enabled` has zero occurrences in this repository, executable code or
   otherwise" was self-falsifying once the PR added the term to README, a doc
   comment and runtimes.md itself. Narrowed to executable code, which is the
   verifiable and load-bearing claim.

Verified: tsc --noEmit clean; vitest 893/893 across 86 files;
check_sanitization.py --diff --deterministic-only clean; LICENSE byte-identical
to gnu.org agpl-3.0.txt (sha256 0d96a4ff…); GitHub licence API returns AGPL-3.0
for this ref vs NOASSERTION on main; app/api/letta route inventory is GET x4 +
POST messages with no PATCH/PUT/DELETE.
@juniperbevensee

Copy link
Copy Markdown
Collaborator Author

Independent audit — 2 new false claims found and fixed, then merged

I re-ran every check in the description against origin/main and this head rather than trusting it. Most of it holds up. Two things did not, and both were new overclaims introduced by the very PR that set out to remove overclaims.

Confirmed as described

Claim Result
git_enabled absent from executable code Confirmed — 0 hits on main; on this head it appears only in docs/comments
Letta route inventory: GET ×4 + POST messages, no PATCH/PUT/DELETE Confirmed — inventoried independently on both refs; grep -E 'export (async )?(function|const) (PUT|PATCH|DELETE)' app/api/letta returns nothing
LICENSE now verbatim AGPL v3 Confirmedsha256 0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0, byte-identical to gnu.org's agpl-3.0.txt, 661 lines
Licence API flips to AGPL-3.0 ConfirmedNOASSERTION on main, AGPL-3.0 on this ref
Repo description + topics updated Confirmed via gh repo view; hermes removed, letta/swarm-map/agent-orchestration/multi-agent added
docs/migrating.md GHCR namespace Confirmed — no nimblecoai image path is advertised; the canonical callout matches lib/seed.ts:20
Letta wizard deploy path is real Confirmed — radio → state.runtimePOST /api/setup/deploydeployLettaAgent. So "deploy from the wizard: yes" is honest, and the README headline does not overclaim (it explicitly says support is not symmetrical)
tsc clean, 893/893 tests, 86 files Reproduced locally

Corrected in 58c98fd

1. The budget claim was backwards, and its workaround does not exist.

The PR said "streaming is on by default and a streamed turn reports zero tokens… budget enforcement is only meaningful on the blocking / non-streaming path." Both halves are wrong:

  • Streaming is off by default. Gateway token streaming follows the top-level streaming config, and StreamingConfig.enabled defaults to false. The display.streaming: true that lib/templates/config-yaml.ts writes is CLI-onlygateway/display_config.py explicitly skips display.streaming when resolving gateway streaming. _setup_platform_stream_consumer therefore returns None and the default proxied turn takes the blocking client.
  • The blocking path does not fix budget either. It extracts real prompt/completion tokens, but records them as last_prompt_tokens (context size). Swarm Map's spend check sums input_tokens/output_tokens from the agent's state.db sessions table (lib/services/usage.ts) — columns the Letta bridge never writes.

Net: budget does not inherit on any path, and streaming is not the cause. The original wording was the more dangerous error of the two, because it pointed operators at a non-streaming "safe path" that would not in fact bound their spend. Now stated as: approval and audit inherit, budget does not.

2. A self-falsifying grep claim. docs/runtimes.md asserted git_enabled has "zero occurrences in this repository, executable code or otherwise" — untrue the moment this PR added the term to the README, a doc comment, and that same file. Narrowed to executable code, which is the verifiable and load-bearing claim.

On the merge-order note

The description says "not merging — flagging the #187 LICENSE conflict for a human to sequence." That is now stale: #187 merged as c6f455d. I merged main in (no force-push, no history rewrite) and resolved the one real conflict — LICENSE — in this PR's favour, keeping the verbatim AGPL text. #187's intent survives because NOTICE already carries the corrected Swarm Map name. README.md auto-merged, preserving #187's clone-URL fixes.

Merge conditions

CI green (4/4). No other open PR touches any of these seven files (#185, #183, #55 checked). No review requested. Squash-merging.

Out of scope, worth a follow-up

lib/services/harness-compose.ts:257 still defaults camofoxImage to ghcr.io/nimblecoai/camofox:latest — the frozen namespace, in executable code, pre-existing on main. Not touched here: PR #183 is in flight on that exact file. Should be repointed there or in a follow-up, not by disrupting #183.

@juniperbevensee
juniperbevensee merged commit d9e416d into main Jul 30, 2026
4 checks passed
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