backpass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +406 -0
- package/bin/backpass.js +4 -0
- package/package.json +62 -0
- package/src/acpx.js +576 -0
- package/src/agents.js +389 -0
- package/src/analyze.js +289 -0
- package/src/apply/lavish.js +128 -0
- package/src/apply/terminal.js +119 -0
- package/src/apply/writer.js +101 -0
- package/src/bootstrap.js +74 -0
- package/src/cli.js +261 -0
- package/src/commands/analyze.js +88 -0
- package/src/commands/apply.js +103 -0
- package/src/commands/bootstrap.js +172 -0
- package/src/commands/init.js +59 -0
- package/src/commands/propose.js +136 -0
- package/src/commands/run.js +95 -0
- package/src/commands/scan.js +90 -0
- package/src/commands/status.js +143 -0
- package/src/commands/usage.js +25 -0
- package/src/config.js +249 -0
- package/src/diff.js +305 -0
- package/src/discovery/adapters/claude.js +77 -0
- package/src/discovery/adapters/codex.js +162 -0
- package/src/discovery/adapters/cursor-cli.js +109 -0
- package/src/discovery/adapters/cursor-ide.js +130 -0
- package/src/discovery/adapters/grok.js +107 -0
- package/src/discovery/adapters/opencode.js +151 -0
- package/src/discovery/adapters/pi.js +87 -0
- package/src/discovery/adapters/shared.js +195 -0
- package/src/discovery/adapters/sqlite.js +50 -0
- package/src/discovery/association.js +100 -0
- package/src/discovery/index.js +226 -0
- package/src/discovery/self.js +62 -0
- package/src/distill.js +182 -0
- package/src/fold.js +214 -0
- package/src/gap-ledger.js +174 -0
- package/src/logger.js +74 -0
- package/src/memory.js +244 -0
- package/src/progress.js +29 -0
- package/src/prompts/analysis.md +48 -0
- package/src/prompts/annotate.md +48 -0
- package/src/prompts/synthesis.md +98 -0
- package/src/prompts.js +36 -0
- package/src/proposal.js +430 -0
- package/src/redact.js +36 -0
- package/src/repo.js +118 -0
- package/src/sample.js +99 -0
- package/src/skills.js +207 -0
- package/src/state.js +202 -0
- package/src/subprocess.js +47 -0
- package/src/synthesize.js +287 -0
- package/src/tokens.js +48 -0
- package/src/tui/index.js +336 -0
- package/src/tui/render.js +487 -0
- package/src/tui/term.js +130 -0
- package/src/tui/theme.js +111 -0
- package/src/workspace.js +162 -0
- package/templates/apply.html +928 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kun Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
<h1 align="center">backpass</h1>
|
|
2
|
+
<p align="center">
|
|
3
|
+
<a href="https://github.com/kunchenguid/backpass/actions/workflows/ci.yml"
|
|
4
|
+
><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/kunchenguid/backpass/ci.yml?style=flat-square&label=ci"
|
|
5
|
+
/></a>
|
|
6
|
+
<a href="https://github.com/kunchenguid/backpass/actions/workflows/release-please.yml"
|
|
7
|
+
><img alt="Release" src="https://img.shields.io/github/actions/workflow/status/kunchenguid/backpass/release-please.yml?style=flat-square&label=release"
|
|
8
|
+
/></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/backpass"
|
|
10
|
+
><img alt="npm" src="https://img.shields.io/npm/v/backpass?style=flat-square"
|
|
11
|
+
/></a>
|
|
12
|
+
<a href="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-blue?style=flat-square"
|
|
13
|
+
><img alt="Platform" src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-blue?style=flat-square"
|
|
14
|
+
/></a>
|
|
15
|
+
<a href="https://x.com/kunchenguid"
|
|
16
|
+
><img alt="X" src="https://img.shields.io/badge/X-@kunchenguid-black?style=flat-square"
|
|
17
|
+
/></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<h3 align="center">Gradient descent for your agent memory.</h3>
|
|
21
|
+
|
|
22
|
+
Your `AGENTS.md` is a set of weights. Every agent session is a forward pass. The
|
|
23
|
+
transcript that session leaves on disk is the loss signal - and today nothing reads it.
|
|
24
|
+
The loop only closes when a human happens to remember a failure and edits the file by hand.
|
|
25
|
+
|
|
26
|
+
`backpass` closes it. It finds the agent sessions that actually ran in your repo, reads
|
|
27
|
+
what happened in them, and proposes evidence-backed edits to your memory file - under a
|
|
28
|
+
token budget, gated by you.
|
|
29
|
+
|
|
30
|
+
- **Local-first** - Reads the transcript stores of six agent harnesses directly from disk.
|
|
31
|
+
No API, no upload; transcripts never leave your machine except into an agent you already
|
|
32
|
+
authenticated, and obvious secrets are redacted before they do.
|
|
33
|
+
- **Evidence-gated** - Every proposed edit carries verbatim quotes from real sessions, a
|
|
34
|
+
new instruction needs evidence from at least two independent sessions, and one run
|
|
35
|
+
proposes at most five edits. Small, noisy, repeated steps - not a rewrite.
|
|
36
|
+
- **Human in the loop** - Analysis never writes. `backpass apply` is the only writing
|
|
37
|
+
command, and it shows each edit with its evidence for you to accept or reject.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
AGENTS.md / CLAUDE.md (the weights)
|
|
41
|
+
→ agent session (forward pass)
|
|
42
|
+
→ transcript on disk (loss signal)
|
|
43
|
+
→ backpass: collect samples, distill, calculate loss, aggregate gradients
|
|
44
|
+
→ backpass: gradient descent (diffs + skill extractions)
|
|
45
|
+
→ you accept or reject (the human gate)
|
|
46
|
+
→ back to the weights
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
One run is one gradient step: at most five edits, and a new instruction needs evidence
|
|
50
|
+
from at least two independent sessions.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npm install -g backpass
|
|
56
|
+
# or run it without installing
|
|
57
|
+
npx backpass
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Requires **Node >= 22.5** and [`acpx`](https://github.com/openclaw/acpx) on your PATH.
|
|
61
|
+
|
|
62
|
+
backpass has **no API keys of its own**. Every model call goes through acpx to a harness
|
|
63
|
+
you have already authenticated.
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
cd your-repo
|
|
67
|
+
backpass init # write .backpassrc.json, exclude .backpass/ via .git/info/exclude
|
|
68
|
+
backpass # collect samples → calculate loss → aggregate gradients → gradient descent (never writes)
|
|
69
|
+
backpass apply # review each edit, accept or reject, then write
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## How It Works
|
|
73
|
+
|
|
74
|
+
### 1. Collect samples - which sessions belong to this repo
|
|
75
|
+
|
|
76
|
+
backpass reads the local transcript stores of six harnesses directly. No API, no upload.
|
|
77
|
+
|
|
78
|
+
| Harness | Store | Repo tie |
|
|
79
|
+
| -------------- | ---------------------------------------------- | ------------------------------------- |
|
|
80
|
+
| **claude** | `~/.claude/projects/<munged-cwd>/<uuid>.jsonl` | per-line `cwd` |
|
|
81
|
+
| **codex** | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `cwd` + recorded `git.repository_url` |
|
|
82
|
+
| **pi** | `~/.pi/agent/sessions/<escaped-cwd>/*.jsonl` | session-header `cwd` |
|
|
83
|
+
| **opencode** | `~/.local/share/opencode/opencode.db` (sqlite) | `session.directory` |
|
|
84
|
+
| **grok** | `~/.grok/sessions/<encoded-cwd>/<uuid>/` | `summary.json` `cwd` + `git_remotes` |
|
|
85
|
+
| **cursor CLI** | `~/.cursor/chats/<md5(cwd)>/<uuid>/` | `meta.json` `cwd` |
|
|
86
|
+
|
|
87
|
+
Association runs in three tiers:
|
|
88
|
+
|
|
89
|
+
1. **Tier 1 - deterministic.** The session's cwd is (or sits inside) one of this repo's
|
|
90
|
+
worktrees.
|
|
91
|
+
2. **Tier 2 - deterministic, survives deletion.** A git remote recorded in the transcript
|
|
92
|
+
matches one of the repo's remotes. This is how codex and grok stay attributable long
|
|
93
|
+
after the worktree is gone.
|
|
94
|
+
3. **Tier 3 - best-effort.** A dead path whose last segment is the repo's directory name,
|
|
95
|
+
or one matching a glob you configured. Labelled as such, and excluded by `--strict`.
|
|
96
|
+
|
|
97
|
+
Collection is incremental. Codex alone can hold 10,000+ rollouts, so verdicts are cached in
|
|
98
|
+
`.backpass/scan-cache.json` by path, mtime and size - re-scans cost only the new files.
|
|
99
|
+
A harness whose store is missing or has drifted into an unrecognised shape produces a
|
|
100
|
+
warning and is skipped; the run continues. backpass's own loss and gradient-descent calls land
|
|
101
|
+
in these same stores under the repo's cwd; every prompt it sends is tagged, and tagged
|
|
102
|
+
sessions are excluded from the corpus (the `SELF` column in `backpass scan`).
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
backpass scan --since 7d --strict
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. Distillation - cheap first
|
|
109
|
+
|
|
110
|
+
Raw transcripts are mostly tool-call noise; a megabyte of session is a few thousand tokens
|
|
111
|
+
of actual signal. Before any model sees anything, backpass reduces each session
|
|
112
|
+
deterministically: user and assistant turns verbatim, each tool call collapsed to one line
|
|
113
|
+
(`tool: Bash "npm test" -> 1 failing`), tool output truncated, injected harness scaffolding
|
|
114
|
+
dropped, secrets redacted. Typical reduction is **96-99%**.
|
|
115
|
+
|
|
116
|
+
The distilled trace ends with the path to the raw transcript, so the analysis agent can
|
|
117
|
+
open the original when - and only when - a specific claim needs it.
|
|
118
|
+
|
|
119
|
+
### 3. Calculate loss - one cheap call per transcript
|
|
120
|
+
|
|
121
|
+
Calculating loss costs one call per transcript, so the set is capped first: past `maxTranscripts`
|
|
122
|
+
(default 100, `--max-transcripts`) a **recency-weighted sample** is analyzed instead of
|
|
123
|
+
everything. Each transcript's weight halves every `sampleHalfLife` (default 14d), and the
|
|
124
|
+
sample is drawn without replacement, so recent sessions are almost always kept and old
|
|
125
|
+
ones stay represented in proportion. When this happens the run says so on stderr
|
|
126
|
+
(`discovered 340 transcript(s), analyzing a recency-weighted sample of 100`); pass
|
|
127
|
+
`--max-transcripts all` to analyze every transcript, or `--seed <n>` to reproduce a sample.
|
|
128
|
+
|
|
129
|
+
Each distilled trace goes to a cheap model with the memory file and a rubric. It returns
|
|
130
|
+
strict JSON: which instructions helped, which were violated, and what mistakes no current
|
|
131
|
+
instruction covers.
|
|
132
|
+
|
|
133
|
+
**Every claim must carry a verbatim quote.** Quoteless items are discarded - the single
|
|
134
|
+
most important defence against a model confabulating influence. Negative evidence (a
|
|
135
|
+
visible violation) is weighted highest.
|
|
136
|
+
|
|
137
|
+
Results are cached per transcript, keyed to both the transcript's content _and_ the memory
|
|
138
|
+
file's hash: edit the weights and the evidence correctly re-computes; change nothing and
|
|
139
|
+
the next run is free.
|
|
140
|
+
|
|
141
|
+
### 4. Aggregate gradients - deterministic, no model
|
|
142
|
+
|
|
143
|
+
Evidence is grouped by instruction, giving each one a positive/negative count and a
|
|
144
|
+
**relevance** figure: the share of analyzed sessions in which it mattered at all. Duplicate
|
|
145
|
+
gaps across sessions are clustered, and clusters seen in fewer than `minGapEvidence`
|
|
146
|
+
sessions (default 2) are dropped. One bad session never rewrites the weights.
|
|
147
|
+
|
|
148
|
+
Those sessions are counted across runs, not per run: every gap sighting is kept in
|
|
149
|
+
`.backpass/gap-ledger.json` by gap and session, so a gap seen in one session today and in
|
|
150
|
+
another session next week graduates on the later run. The same session never counts twice,
|
|
151
|
+
a sighting retires once the memory file gains an instruction that covers it, and a session's
|
|
152
|
+
sightings expire after `gapLedgerMaxAge` (default 90d). Until a gap corroborates it stays
|
|
153
|
+
out of the proposal entirely.
|
|
154
|
+
|
|
155
|
+
### 5. Gradient descent - one session, native edits
|
|
156
|
+
|
|
157
|
+
A single high-reasoning session turns the aggregated gradients into concrete edits: ADD,
|
|
158
|
+
REMOVE, REWRITE, or EXTRACT→SKILL. The agent does not describe edits for backpass to
|
|
159
|
+
splice in - it makes them, with its harness's own file tools, in a **staging copy** of the
|
|
160
|
+
memory file under `.backpass/synthesis/` (the repo itself is read-only to it, for
|
|
161
|
+
grounding). backpass then diffs the copy against the original and shows the agent the
|
|
162
|
+
measured changes by id; the agent annotates each one with a title, rationale, and the
|
|
163
|
+
verbatim evidence behind it. Nothing textual is ever taken from the model: every hunk's
|
|
164
|
+
text is copied out of your file by construction, so an edit can never "not appear" in it.
|
|
165
|
+
Then mechanical gates run, and they are not negotiable:
|
|
166
|
+
|
|
167
|
+
- at most `maxEditsPerRun` edits (the learning rate). By default the cap is adaptive: 5
|
|
168
|
+
when the file is near or under budget, and in a shrink plan (file over budget) one edit
|
|
169
|
+
per ~40 tokens of overage, capped at 20, so badly overgrown files recover in fewer runs.
|
|
170
|
+
An explicit `--max-edits` or config value always pins it.
|
|
171
|
+
- every measured change belongs to exactly one annotated edit - an unexplained change
|
|
172
|
+
is a violation, so is an edit that names no change
|
|
173
|
+
- new instructions need evidence from `minGapEvidence` distinct sessions (an edit that
|
|
174
|
+
only adds text is a new instruction, whatever the model calls it)
|
|
175
|
+
- every edit carries a verbatim quote
|
|
176
|
+
- the post-edit file must fit the budget, measured on the staged file
|
|
177
|
+
|
|
178
|
+
A violation triggers a re-prompt naming the exact breach (at most two). If those also
|
|
179
|
+
fail, backpass **fails loudly** and saves the rejected proposal. It never silently
|
|
180
|
+
truncates. A harness that writes past the staging copy into the repo is an error, never
|
|
181
|
+
an apply.
|
|
182
|
+
|
|
183
|
+
Token deltas shown to you are measured by backpass from the actual text - never taken from
|
|
184
|
+
the model's own arithmetic.
|
|
185
|
+
|
|
186
|
+
### 6. The budget - "model size"
|
|
187
|
+
|
|
188
|
+
Every always-loaded token is paid on every future session, forever, and instruction
|
|
189
|
+
following dilutes as the file grows. So the budget is the constraint the whole backward
|
|
190
|
+
pass optimizes under.
|
|
191
|
+
|
|
192
|
+
**Default: 5,000 estimated tokens (~20KB)** per always-loaded memory file, configurable.
|
|
193
|
+
The estimator is bytes/4 - harness-neutral, ±15%.
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
AGENTS.md [###############.................] 2,412 / 5,000 tok · 63 instructions
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
At or over budget, the synthesis prompt goes zero-sum: every addition must name the
|
|
200
|
+
removal or extraction that pays for it.
|
|
201
|
+
|
|
202
|
+
### 7. Skills as overflow
|
|
203
|
+
|
|
204
|
+
A skill's description **is** its when-useful condition: the description is always loaded
|
|
205
|
+
and cheap, the body is free until the trigger fires. That makes extraction the release
|
|
206
|
+
valve for the budget.
|
|
207
|
+
|
|
208
|
+
| | Trigger fits one description line | Trigger not detectable |
|
|
209
|
+
| ------------------------------------------------ | --------------------------------- | ---------------------- |
|
|
210
|
+
| **Broad** (≥20% of sessions, or safety-critical) | memory file | memory file |
|
|
211
|
+
| **Conditional / narrow** | **skill** | deletion candidate |
|
|
212
|
+
|
|
213
|
+
"Matters in N% of sessions" is measured, not guessed - it falls straight out of the
|
|
214
|
+
aggregate-gradients stage. A 640-token procedure relevant to 4% of sessions becomes a 35-token description
|
|
215
|
+
line, and backpass reports the arithmetic: `−611 tok always-loaded, +35 tok description`.
|
|
216
|
+
|
|
217
|
+
Skill descriptions are weights too. If the evidence shows an agent lacked knowledge a
|
|
218
|
+
skill already contains, that is a _failed trigger_ - backpass proposes a description edit,
|
|
219
|
+
not duplicate content.
|
|
220
|
+
|
|
221
|
+
### 8. Apply - the human gate
|
|
222
|
+
|
|
223
|
+
`backpass apply` is the only command that writes. It serves a review surface through
|
|
224
|
+
[`lavish-axi`](https://github.com/kunchenguid/lavish-axi): one card per edit with the diff,
|
|
225
|
+
the evidence quotes and their sources, a live budget gauge, and ACCEPT / REJECT.
|
|
226
|
+
|
|
227
|
+
The surface is a static template shipped in the package - the CLI injects one JSON payload,
|
|
228
|
+
so it is instant, deterministic, and identical every run. Nothing there is model-generated.
|
|
229
|
+
|
|
230
|
+
There is no DEFER button, and it isn't missing: **rejections are remembered.** A rejected
|
|
231
|
+
edit is not proposed again unless materially new evidence arrives.
|
|
232
|
+
|
|
233
|
+
```sh
|
|
234
|
+
backpass apply --no-ui # same decision, in the terminal
|
|
235
|
+
backpass apply --dry-run # show what would be written
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 9. Which file is the weights
|
|
239
|
+
|
|
240
|
+
`memoryFiles` is an ordered list (default `["AGENTS.md", "CLAUDE.md"]`); the first one
|
|
241
|
+
that exists is the file a run optimizes, so **AGENTS.md is canonical**. Resolution is
|
|
242
|
+
pointer-aware:
|
|
243
|
+
|
|
244
|
+
- `CLAUDE.md` containing only `@AGENTS.md` (the standard import) is a pointer: optimizing
|
|
245
|
+
AGENTS.md covers both harness families and the pointer stays valid. Nothing to report.
|
|
246
|
+
- Two separate full files are a divergence hazard. backpass optimizes AGENTS.md, leaves
|
|
247
|
+
CLAUDE.md untouched, and warns each run until you consolidate: move CLAUDE.md's content
|
|
248
|
+
into AGENTS.md and make CLAUDE.md the one-line pointer.
|
|
249
|
+
- A repo with **no memory file** is bootstrapped on the first `backpass` run: a starter
|
|
250
|
+
AGENTS.md (purpose, an empty `## Learnings` section, a `## Maintaining this file`
|
|
251
|
+
section) plus a CLAUDE.md pointer. The starter is then run through the ordinary backward pass, so recurring gaps
|
|
252
|
+
from your real transcripts become its first evidence-backed instructions. With no
|
|
253
|
+
transcripts it is seeded from defaults alone and says so. Bootstrap only ever creates
|
|
254
|
+
files; review it with `git diff`.
|
|
255
|
+
|
|
256
|
+
## CLI Reference
|
|
257
|
+
|
|
258
|
+
| Command | What it does |
|
|
259
|
+
| ------------------ | ---------------------------------------------------------------------------------------- |
|
|
260
|
+
| `backpass` | collect samples → calculate loss → aggregate gradients → gradient descent. Never writes. |
|
|
261
|
+
| `backpass scan` | collect samples only: the transcript table with a confidence column |
|
|
262
|
+
| `backpass analyze` | calculate loss: the tier-1 pass over pending transcripts |
|
|
263
|
+
| `backpass propose` | aggregate gradients + gradient descent: the tier-2 pass from cached evidence |
|
|
264
|
+
| `backpass apply` | review and write the accepted edits |
|
|
265
|
+
| `backpass status` | cache state, failed transcripts, budget bars |
|
|
266
|
+
| `backpass init` | write `.backpassrc.json`, exclude `.backpass/` locally |
|
|
267
|
+
|
|
268
|
+
Run `backpass --help` for the full flag list.
|
|
269
|
+
|
|
270
|
+
### Live progress
|
|
271
|
+
|
|
272
|
+
On an interactive terminal the default run renders a live progress view: the budget gauge,
|
|
273
|
+
a stage rail (collect samples → calculate loss → aggregate gradients → gradient descent),
|
|
274
|
+
per-store collection counts, one lane
|
|
275
|
+
per analysis job with its distillation receipt, and a running evidence tally. It draws to
|
|
276
|
+
stderr only and collapses into the plain line summary when the run ends, so scrollback and
|
|
277
|
+
piped output are identical to a run without it.
|
|
278
|
+
|
|
279
|
+
The view never gets in the way of automation: no TTY, `NO_COLOR`, `CI`, `--quiet`, `--json`,
|
|
280
|
+
or a terminal under 60 columns all mean plain lines, unchanged. Truecolor terminals get the
|
|
281
|
+
backpass theme; everything else falls back to the nearest ANSI-16 colors. The ink set adapts
|
|
282
|
+
to light backgrounds automatically (queried via OSC 11, `COLORFGBG` as fallback); force one
|
|
283
|
+
with `--theme dark|light` or `"theme"` in `.backpassrc.json`.
|
|
284
|
+
|
|
285
|
+
### Two-tier models
|
|
286
|
+
|
|
287
|
+
Cheap analysis, smart synthesis. Both go through acpx, so backpass uses the harnesses you
|
|
288
|
+
already have - and by default it works out which ones those are. Each pass has an ordered
|
|
289
|
+
ladder of candidates, and the first one that is installed, logged in, and serves the model
|
|
290
|
+
wins:
|
|
291
|
+
|
|
292
|
+
| pass | effort | 1st | 2nd | 3rd |
|
|
293
|
+
| --------- | ------ | -------------------------------------- | ---------------------------- | --------------------------------- |
|
|
294
|
+
| analysis | medium | `gpt-5.6-luna` via pi, opencode, codex | `claude-sonnet-5` via claude | `grok-4.6` via pi, opencode, grok |
|
|
295
|
+
| synthesis | high | `gpt-5.6-sol` via pi, opencode, codex | `claude-opus-5` via claude | `grok-4.6` via pi, opencode, grok |
|
|
296
|
+
|
|
297
|
+
Each candidate is checked with a ~1.5s zero-token acpx probe (claude via `claude auth status`,
|
|
298
|
+
because its adapter accepts sessions while logged out); verdicts are cached in
|
|
299
|
+
`.backpass/agent-probe-cache.json` for 12h (30min for negatives) and re-probed with `--force`.
|
|
300
|
+
The probe is a filter, not a promise: if the chosen harness answers `AUTH_REQUIRED` or rejects
|
|
301
|
+
the model mid-run, backpass falls through to the next candidate and says so. When a whole
|
|
302
|
+
ladder is exhausted the error lists every candidate with what to run to fix it.
|
|
303
|
+
|
|
304
|
+
Bare model ids are resolved against what each adapter advertises (`openai-codex/gpt-5.6-luna`
|
|
305
|
+
on pi, `openai/gpt-5.6-luna` on opencode, `gpt-5.6-luna` on codex), so nothing is hardcoded
|
|
306
|
+
per harness. Ladders are ordinary config - reorder or shorten them under `"ladders"`.
|
|
307
|
+
|
|
308
|
+
Pinning an agent skips its ladder entirely:
|
|
309
|
+
|
|
310
|
+
```sh
|
|
311
|
+
backpass \
|
|
312
|
+
--analysis-agent codex --analysis-model gpt-5.5 --analysis-effort low \
|
|
313
|
+
--synthesis-agent claude --synthesis-model claude-opus-5 --synthesis-effort high
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`--no-auto-agent` pins the pre-ladder defaults (codex / claude). If an adapter does not
|
|
317
|
+
advertise reasoning effort, backpass says so in the run report rather than pretending it
|
|
318
|
+
applied.
|
|
319
|
+
|
|
320
|
+
### Configuration
|
|
321
|
+
|
|
322
|
+
`.backpassrc.json` in the repo root, layered over `~/.config/backpass/config.json`, with
|
|
323
|
+
CLI flags on top:
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"memoryFiles": ["AGENTS.md"],
|
|
328
|
+
"budgetTokens": 5000,
|
|
329
|
+
"skillsDir": ".agents/skills",
|
|
330
|
+
"maxEditsPerRun": null,
|
|
331
|
+
"minGapEvidence": 2,
|
|
332
|
+
"gapLedgerMaxAge": "90d",
|
|
333
|
+
"maxTranscripts": 100,
|
|
334
|
+
"sampleHalfLife": "14d",
|
|
335
|
+
"analysis": { "agent": null, "model": null, "effort": null },
|
|
336
|
+
"synthesis": { "agent": null, "model": null, "effort": null },
|
|
337
|
+
"ladders": {
|
|
338
|
+
"analysis": [
|
|
339
|
+
{ "model": "gpt-5.6-luna", "agents": ["pi", "opencode", "codex"] },
|
|
340
|
+
{ "model": "claude-sonnet-5", "agents": ["claude"] },
|
|
341
|
+
{ "model": "grok-4.6", "agents": ["pi", "opencode", "grok"] }
|
|
342
|
+
],
|
|
343
|
+
"synthesis": [
|
|
344
|
+
{ "model": "gpt-5.6-sol", "agents": ["pi", "opencode", "codex"] },
|
|
345
|
+
{ "model": "claude-opus-5", "agents": ["claude"] },
|
|
346
|
+
{ "model": "grok-4.6", "agents": ["pi", "opencode", "grok"] }
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
"discovery": {
|
|
350
|
+
"harnesses": ["claude", "codex", "pi", "opencode", "grok", "cursor"],
|
|
351
|
+
"since": "30d",
|
|
352
|
+
"worktreeGlobs": [],
|
|
353
|
+
"minUserTurns": 2
|
|
354
|
+
},
|
|
355
|
+
"jobs": 4
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### State
|
|
360
|
+
|
|
361
|
+
Everything mutable lives in `.backpass/`, kept out of git via the repo's local exclude
|
|
362
|
+
(`.git/info/exclude`, written by `backpass init`) rather than the tracked `.gitignore`:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
.backpass/
|
|
366
|
+
scan-cache.json collect-samples verdicts by path + mtime + size
|
|
367
|
+
evidence/<id>.json per-transcript loss
|
|
368
|
+
evidence-summary.json aggregated gradients
|
|
369
|
+
proposal.json the latest gradient-descent step
|
|
370
|
+
synthesis/ the staging copy the gradient-descent agent edited (memory file + skills)
|
|
371
|
+
prompts/ the exact prompts of the last run
|
|
372
|
+
agent-probe-cache.json which harnesses were available and logged in, and when
|
|
373
|
+
rejections.json edits you turned down, and the evidence behind them
|
|
374
|
+
gap-ledger.json gap sightings by gap and session, accumulated across runs
|
|
375
|
+
apply/apply.html the rendered review surface
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Limitations
|
|
379
|
+
|
|
380
|
+
- **Causal attribution is genuinely hard.** A model can confabulate influence. The
|
|
381
|
+
mitigations are structural - mandatory verbatim quotes, the two-session rule, negative
|
|
382
|
+
evidence weighted highest, and a human gate - but read the evidence, not just the title.
|
|
383
|
+
- **Transcript formats are undocumented** and can change without notice. Each adapter is
|
|
384
|
+
pinned by a golden fixture and fails soft.
|
|
385
|
+
- **Cursor IDE is deferred to v1.1.** Its composer→workspace link is version-dependent;
|
|
386
|
+
`--include-cursor-ide` enables a best-effort pass, but it is not a v1 guarantee.
|
|
387
|
+
- Global memory (`~/.claude/CLAUDE.md`) is treated as context, never an edit target.
|
|
388
|
+
- Paths are verified on macOS and Linux.
|
|
389
|
+
|
|
390
|
+
## Development
|
|
391
|
+
|
|
392
|
+
```sh
|
|
393
|
+
git clone https://github.com/kunchenguid/backpass.git
|
|
394
|
+
cd backpass
|
|
395
|
+
pnpm install --frozen-lockfile
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
```sh
|
|
399
|
+
pnpm run check # Run all verification commands
|
|
400
|
+
pnpm test # Run node:test tests
|
|
401
|
+
pnpm run lint # Run ESLint
|
|
402
|
+
pnpm run format:check # Check Prettier formatting
|
|
403
|
+
pnpm run typecheck # Run TypeScript checkJs validation
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contributor workflow.
|
package/bin/backpass.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "backpass",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"packageManager": "pnpm@11.5.0",
|
|
5
|
+
"description": "Gradient descent for your agent memory - analyzes past agent session transcripts and proposes evidence-backed edits to AGENTS.md / CLAUDE.md",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Kun Chen",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/kunchenguid/backpass.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/kunchenguid/backpass#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/kunchenguid/backpass/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"agents",
|
|
19
|
+
"agent-memory",
|
|
20
|
+
"AGENTS.md",
|
|
21
|
+
"CLAUDE.md",
|
|
22
|
+
"transcripts",
|
|
23
|
+
"acpx",
|
|
24
|
+
"cli",
|
|
25
|
+
"llm"
|
|
26
|
+
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"backpass": "./bin/backpass.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin",
|
|
32
|
+
"src",
|
|
33
|
+
"templates",
|
|
34
|
+
"LICENSE",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"check": "npm run lint && npm run format:check && npm run typecheck && npm test",
|
|
42
|
+
"start": "node bin/backpass.js",
|
|
43
|
+
"lint": "eslint bin src test",
|
|
44
|
+
"format": "prettier --write .",
|
|
45
|
+
"format:check": "prettier --check .",
|
|
46
|
+
"test": "node --test \"test/**/*.test.js\"",
|
|
47
|
+
"typecheck": "tsc --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@eslint/js": "^10.0.1",
|
|
52
|
+
"@types/node": "^25.6.2",
|
|
53
|
+
"eslint": "^10.3.0",
|
|
54
|
+
"eslint-config-prettier": "^10.1.8",
|
|
55
|
+
"globals": "^17.6.0",
|
|
56
|
+
"prettier": "^3.8.3",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=22.5.0"
|
|
61
|
+
}
|
|
62
|
+
}
|