Audit what forcing a coder login would have to buy

66776acd098c · AtlantisPleb · · parent a9a251e3ebfd

Audit what forcing a coder login would have to buy

`openagents coder` is local-first past the point of working offline: with no
model it answers from a local Ollama, with no credential it answers from a
stand-in, its shell and skills read this machine, and its children run on the
harness's own free models. Only two things in a session reach the account at
all. That was right for a tool nobody had signed into and is wrong for one whose
point is the account.

The audit reads what the server already has to hang the other posture on —
`Leaderboard`, the experience, graph and profile memory planes, the issue and
receipt graph from #10, and the fenced grant — and finds three gaps.

The sharp one is small: coder work does not reach the leaderboard.
`account_totals/0` unions `TurnReceipt` and `Voice.Session`, both joined through
`Conversation` and `Visitor`, and a coder session runs on a thread whose spend
sits on `Inference.Grant.usage`. A person can run the coder all day against
their own account and appear nowhere, so forcing a login today would be asking
for a credential in exchange for nothing. It is one arm on an existing union,
with the double-count question to answer against DATA-002 first.

The second is that all three memory planes hang off `Conversation` and
`Visitor`, which a coder session does not have. The material they want is
already produced and already written out — the ATIF export is that material —
so the work is the join, and the open question is what a session's unit of
experience is. The issue looks like the answer, since #10 already links it to
commits and receipts.

The third is that `total_tokens` measures spend rather than contribution, and
adding coder tokens to it makes a placeholder more load-bearing rather than
less.

The recommendation is to close the first gap, write session outcomes into
experience memory keyed by issue, force the login once it buys the board and the
memory, and only then ask what a score should measure — with the data to answer
it. `--offline` should survive as a named mode rather than as what you get when
the login fails.

Nothing here is built; the gaps are read from the code at the commit named in
the document.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

pushed
by user · WAL seq 288 · 2026-08-24T17:37:18.363098Z

Changed files

  • added docs/2026-08-24-coder-account-integration-audit.md

Diff

1 file changed, +142 -0

docs/2026-08-24-coder-account-integration-audit.md added +142

@@ -0,0 +1,142 @@

1
# From local-first coder to an account-shaped one
2
3
**Date:** 2026-08-24
4
**Commit measured:** `a9a251e` on `openagents/main` (the forge), with the CLI at
5
`94e4cad8e7` on `OpenAgentsInc/openagents`
6
**Status:** audit and initial direction; nothing here is built yet
7
**Question:** `openagents coder` works with no account at all. We want the
8
opposite posture — sign in on open, work inside the account, and have the work
9
count. What already exists on the server to hang that on, what exists but is
10
not connected, and what does not exist?
11
12
## What the coder is today
13
14
Deliberately local-first, and it goes further than "works offline":
15
16
- With no `--model`, it answers from a local Ollama server if one is running.
17
- With no credential, it answers from a built-in stand-in and says so.
18
- `shell` runs commands on this machine. `skill` reads files from this machine.
19
- Children run on the harness's own free models, which need no credential from
20
  us at all.
21
22
Only two things reach the account: `openagents` CLI calls, and a thread-backed
23
turn. Everything else in a session can happen with nobody signed in.
24
25
That was the right default for a terminal tool nobody had signed into. It is the
26
wrong default for a tool whose point is the account.
27
28
## What exists on the server
29
30
**A leaderboard.** `OpenAgents.Leaderboard` publishes rank, the GitHub display
31
fields, and one integer: `total_tokens`. It is a real, bounded, public
32
projection with an invariant behind it (`LEADERBOARD-001`).
33
34
**Three memory planes**, all authoritative and all already governed:
35
36
- `OpenAgents.ExperienceMemory` — private, source-linked work outcomes and
37
  frozen advisory pattern banks.
38
- `OpenAgents.GraphMemory` — a derived, generation-pinned relationship index
39
  over that private experience.
40
- `OpenAgents.ProfileMemory` — durable profile claims, confined to one
41
  authenticated owner, which conversation history cannot enter without an
42
  explicit candidate call.
43
44
**A work graph.** Issues, projects, project fields and items, repositories, and
45
the receipt linkage designed in `2026-08-23-issue-work-receipt-linkage-design.md`
46
against issue `#10` — issues connected to jobs, conversations, commits, tests,
47
releases and deployments without a second work record.
48
49
**Metered inference.** A thread mints a fenced grant
50
(`sarah.inference_grant.v1`) that is budgeted in tokens and metered against the
51
owner's account, and the provider key never leaves the server.
52
53
## The three gaps, in the order they matter
54
55
### 1. Coder work does not reach the leaderboard
56
57
This is the sharp one, and it is small.
58
59
`Leaderboard.account_totals/0` unions two sources: `TurnReceipt` joined through
60
`Turn` → `Conversation` → `Visitor`, and `Voice.Session` joined the same way.
61
Both are the chat surface. A coder session runs on a **thread**, and a thread's
62
spend is recorded on `Inference.Grant.usage` — a map on the grant row, which the
63
leaderboard does not read.
64
65
So today a person can run the coder all day against their own account, spend
66
real tokens through the proxy, and appear nowhere. "Sign in and you are on the
67
board" is not true yet, and forcing the login without closing this would be
68
asking for a credential in exchange for nothing.
69
70
The fix is a third arm on the union, not a new counter. Grant usage is already
71
per-account and already authoritative; the question is only whether it is summed
72
in the same projection and whether that double-counts anything the chat arms
73
already claim. It should not — a thread is not a conversation — but that is the
74
thing to prove before shipping, and `DATA-002` is the rule to prove it against.
75
76
### 2. Memory is scoped to conversations, not to threads
77
78
All three planes hang off `Conversation` and `Visitor`. The coder does not have
79
either: it has a thread, a repository, a branch, and a working directory.
80
81
That is not a missing feature so much as a missing join. A coder session already
82
produces exactly the material the experience plane wants — a task, the tools
83
called, what came back, whether it worked — and throws it away when the process
84
exits. The ATIF export the CLI writes is that material, in a format the rest of
85
the system already reads.
86
87
The interesting question is not "how do we store memories" but **what a coder
88
session's unit of experience is**. A turn is too small and a session is too
89
coarse. The candidate that fits the work graph is the issue: one issue worked,
90
one outcome, linked to the commits and receipts `#10` already connects.
91
92
### 3. There is no experience, only tokens
93
94
`total_tokens` is a measure of spend, not of contribution. It rewards a long
95
session over a good one, and it will reward a fan-out of thirty children over a
96
person who thought for a minute and fixed the bug.
97
98
If the leaderboard is going to be the reason to sign in, it should count
99
something worth competing on. The pieces to compute that already exist —
100
issues closed, receipts anchored, tests passing, releases promoted — and none
101
of them are currently expressed as a score.
102
103
I would not design the score in this document. I would note that tokens are a
104
placeholder that has become load-bearing, and that adding coder tokens to it
105
(gap 1) makes it more load-bearing, not less.
106
107
## On forcing the login
108
109
Worth doing, with two conditions.
110
111
**It should buy something on the first run.** A person who signs in should get
112
the board, their history, and their memory — not a gate followed by the same
113
session they could have had anonymously. Gap 1 is the minimum for that to be
114
honest.
115
116
**It should not break the local lane.** The strongest thing about the coder
117
today is that it runs on a local model against a local repository with no
118
network. That is worth keeping as a deliberate, named mode rather than losing as
119
a side effect: `--offline` should stay, and it should stay the thing you reach
120
for on a plane rather than the thing you accidentally get when the login fails.
121
122
The device flow is already there and already mints both scopes, so the mechanism
123
costs nothing. What costs something is deciding what happens when the login is
124
declined: refuse to start, start in a named local mode, or start and nag. My
125
view is the second, and that the nag is the leaderboard being visible and empty.
126
127
## What I would do first
128
129
1. **Put thread spend on the leaderboard.** One arm on an existing union, with
130
   the double-count question answered against `DATA-002`. Nothing else on this
131
   list is honest until this is done.
132
2. **Write a coder session's outcome into experience memory, keyed by issue.**
133
   The ATIF export already holds it; the join is the work.
134
3. **Then** force the login, because by then it buys the board and the memory.
135
4. **Then** ask what a score should measure, with the data from 1 and 2 to
136
   answer it rather than guess.
137
138
## What this document does not claim
139
140
None of this is built. The gaps are read from the code as it stands at the
141
commit above, and the ordering is a view rather than a decision. The leaderboard
142
arm is the only part I would call obvious.

This page updates live while a promote is in flight · changelog