Repository navigation
Step 1 of #874: three loaded harness tiers (harness:lite/medium/heavy), per-tier budgets, offline harnessPro, and the usage.harness column #937
Description
Step 1 of the harness RFC #874, tracked as its own ticket so a pull request can say Fixes # without closing the RFC. Step 2 (the experience library) stays open with #874; this ticket does not cover it.
The implementation is pull request #933 (tip ea8dec62), already judged PASS and waiting only for an issue reference in its body.
What it delivers
Three tiers — Lite, Medium, Heavy — loaded through Step 0's resolveHarness (core/src/host/harness.js). Each names the same four modules as default and adds a declared budget. applied is false: prompt.js, tools.js, models.js and turns.js keep running exactly as they do today, and install leaves rt.harness on default.
A tier is selected by exactly one label: harness:lite, harness:medium, harness:heavy. No such label — including risk:T* and every other label — stays on default. Two tier labels, or an unknown harness:* label, throw. The labels are declared in .github/labels.json; creating them on the repository is sync-labels.mjs --apply, which is not part of this ticket.
Declared budgets (Lite < Medium < Heavy):
| Lite | Medium | Heavy | |
|---|---|---|---|
prompt maxChars |
8000 | 16000 | 48000 |
tools maxCalls |
8 | 24 | 64 |
tools allow |
six read tools | those plus work tools, still without create_bot, delete_group, stop_bot |
null (the full set the host registers) |
model maxTokens |
2048 | 8192 | 16384 |
step maxSteps |
4 | 12 | 32 |
step maxRetries |
0 | 1 | 1 |
harnessPro is the offline check for a tier config: the same four modules, verifiers.offline = ci, core/dev/eval, rsi-baseline, and no key or token field. It runs none of those checks and calls no model.
rt.noteUsageHarness(sessionId, idOrLabels) stamps the routed tier on the harness column of a usage row. The resume cursor stays { provider, model, retry }.
Acceptance
- The default path is byte-identical. With
applied: false,prompt.js/tools.js/models.js/turns.jsare untouched,defaultHarness()is byte-identical to Step 0's, andinstallleavesrt.harnessatdefault. - Selection is one label, and it fails loudly on contradiction. One
harness:*label selects that tier; two different tier labels throw; an unknownharness:*label throws;risk:T2and no label stay ondefault; the same label repeated de-dupes. - The mirrors are the source.
patches/ds-bot/files/copies andpatches/ds-bot/0001-core-fixes.patchrebuildcore/byte for byte (0001ends with a newline; the trailing-blank-line trap#934hit is checked here). usage.harnessis written only on an explicit route, andstatestays exactly{ provider, model, retry }.- The budgets are ordered and bounded: Lite < Medium < Heavy on every row, and Lite/Medium
tools.allownever containscreate_bot,delete_grouporstop_bot. harnessPronever carries a secret: no key or token field anywhere in its shape, whichever tier it is built for.
Not promised here (Step 2)
Two facts measured on the pull request, written here so nobody reads the merge as more than it is:
rt.noteUsageHarnesshas no caller today, so every live usage row still writesdefault. The label→tier path is API-only until a caller exists.harnessBySessionlives in memory only, so a restart re-folds and rewrites existing rows'harnessback todefault. Making it durable needsmarks.fold, which is Step 2 (the same lifetime problemissuePrhas had since Step 0: default harness + usage five fields (#903) #908).
Out of scope
Step 2 (experience library, durable harness marks), running sync-labels --apply on the repository, and any behaviour change to prompt/tools/models/turns.
Activity
Add Lite, Medium, and Heavy harness tiers (ThinkFlowLab#933)