feat(web): Zorp mode runs one investigate attempt and shows the ledger - #82
Conversation
221ef78 to
65edcd2
Compare
There is no aryabhatta engine to call. aryabhatta is record plus readers, nine modules inside zorp-track, and it ships no command on purpose. What writes to it is investigate: every attempt records the conditions it ran under before the work starts, and, behind ZORP_FORECAST, a forecast before that. So Zorp mode in the browser is one pre-registered investigate attempt plus a read of what landed, and not a fifth capability. It mirrors panel. An attempt occupies the session exactly as a turn does, shares the session's sequence counter, answers the existing stop endpoint, and closes with investigate_done then done. Three routes: POST /api/sessions/:id/investigate, GET /api/investigate/status, GET /api/investigate/ledger. All of it sits behind a new, non-default research feature on zorp-web, and the routes answer 501 without it so the page can say why the button is off instead of showing a 404 nobody can explain. A person launches a run and no model can. An attempt writes to a pre-registered evidence record and to the ledger, so a model that could start one could feed the record it is later read against. There is no tool that reaches the endpoint; agent.rs asserts it over the unfiltered builtin set and zorp-web asserts it over the set this server hands out. Checkpoints are auto-approved from the browser because there is no terminal to ask, which is the CLI's --yes chosen explicitly rather than fallen back to. The pre-registered kill threshold is still enforced in code and auto-approve cannot wave it through, and the choice is recorded: checkpoint_mode reads auto-approve in the ledger. Forecasting is reported and never set. It costs a model call on every attempt, it stays off by default, and one page flipping it would change what the server does for everyone using it. The ledger view says out loud that an empty expectations list is why nothing can be scored. The ledger reader names no model-authored text column, so expectations.assumptions is not in what it returns, and a test asserts the serialized ledger never carries it. Nothing read back is fed to a model: the lines on the page are recorded rows and arithmetic over them. The view builds DOM nodes and puts every string through textContent. Claude-Session: https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4
65edcd2 to
f197b99
Compare
|
Rebased onto Conflicts and how they were resolved
Spot checks that nothing was lost: On It was not stuck and it was not failing to trigger. It ran and passed in
The reason it rebuilds when the That does leave a real risk I introduced: on a genuinely cold cache the job Local verification, on the rebased head
Toolchain: One note on scope: CI's clippy line excludes |
The browser gets a "Zorp mode" button. It runs one pre-registered
investigateattempt on the session, streams it on the event stream a turn already uses, and
reads back what landed in the aryabhatta ledger.
Three routes, all behind a new non-default
researchfeature onzorp-web:POST /api/sessions/:id/investigate,GET /api/investigate/status,GET /api/investigate/ledger.It is not a fifth capability, on purpose
The ask was to "call the aryabhatta engine". There is no aryabhatta engine.
aryabhatta is record plus readers, nine modules inside
zorp-track, and itships no command on purpose. What writes to it is
investigate.So the faithful browser-facing shape is one
investigateattempt plus a read.Inventing an engine to sit in front of the record would have added the one
thing the design says not to add.
It mirrors
panelAn attempt occupies the session exactly as a turn does, shares the session's
sequence counter, answers the existing stop endpoint, and closes with
investigate_donethendone. That is the shapepanelalready establishedfor a non-turn operation driven from the browser. A second transport would
have given the page a third state machine to keep in step with the first two.
A person launches it and no model can
The same rule
panelholds, for a sharper reason: an attempt writes to apre-registered evidence record and to the ledger, so a model that could start
one could feed the record it is later read against.
No tool reaches the endpoint.
agent.rscarriesno_builtin_tool_can_start_an_investigation, asserted against the unfilteredbuiltin set rather than against the filter, and
zorp-webcarries a secondtest over the set this server actually hands out.
Opt-in, and the routes exist either way
researchonzorp-webpulls inzorp-trackand DuckDB, the most expensivebuild in the workspace, and the ordinary chat server has no use for it.
The routes are registered unconditionally and answer 501, not 404, without
the feature. A 404 cannot tell "this server does not do that" apart from "you
typed the URL wrong", and
GET /api/investigate/statuslets the page say whichone it is before anybody clicks.
Checkpoints are auto-approved, and the page says so
There is no terminal behind a browser, so
CheckpointMode::terminalrefusesoutright. This is the CLI's
--yes, chosen explicitly rather than fallen backinto.
What it cannot do is skip the pre-registered Kill Threshold: a breach kills the
track unconditionally without consulting the checkpoint mode at all, so the
commitment still holds from the browser. What is missing is the human judgement
call on top of it, and that gap is recorded rather than hidden, because
checkpoint_modeis one of the conditions every attempt writes and it readsauto-approvein the ledger.Ruled out: a browser checkpoint decider on the existing approval card. It would
have meant an unanswered prompt killing a track after five minutes, which is a
product decision worth taking on purpose rather than acquiring as a side effect
of building this.
Forecasting is reported and never set from the browser
The status endpoint says whether
ZORP_FORECASTis on where the server runs.There is no control that turns it on: it costs a model call on every attempt,
and one page flipping it would change what the server does for everyone else
using it. Off stays the default, and the ledger view says out loud that an
empty expectations list is why nothing can be scored.
The ledger view names no model-authored column
Integrity rules 5 and 7 bind detectors and the search layer, not a page, but
the cheapest way to keep them checkable is for no read path anywhere to name
such a column.
expectations.assumptionsis not in the frame, and a testasserts the serialized ledger never carries it.
Nothing read back is fed to a model either. The lines on the page are recorded
rows and arithmetic over them, the same split
critiqueand the detectorsalready use.
Tests
Rust,
zorp-web/tests/investigate.rs: an attempt runs and lands in the ledger;an attempt is refused while a turn is running; a request that cannot run is
refused without recording anything; an unknown session cannot be investigated;
the status endpoint reports forecasting and never sets it; reading a ledger
creates nothing.
Rust,
zorp-agent/src/agent.rs:no_builtin_tool_can_start_an_investigation.TypeScript,
web/test/zorp-mode.test.ts: an approved attempt and a killed trackdo not read the same; a missing run record and an empty ledger say different
things; the ledger shows the conditions an attempt ran under; an attempt with no
forecast says so rather than showing nothing; a recorded forecast shows its
interval and stated coverage; forecasting off is said plainly, because it is why
the ledger is empty; nothing rendered becomes markup.
Open
The ledger is read by question rather than browsed, so there is no way to see a
track you did not just run. Deliberate for now: a browser that can list every
track is a different feature with its own scope.
Rebased
Rebased onto
origin/maintwice, since #83 landed partway through. The branchfast-forwards onto
195c8cc0and nothing from #79, #80, #81 or #83 was dropped.zorp-web/Cargo.tomlwas the real conflict:search(#81),recall(#83) andresearch(this PR) now sit under one[features]block, and[dependencies]carries
zorp-recall,zorp-trackandsha2together. Also resolved:zorp-web/src/api.rs(kept/api/capabilitiesand the three investigateroutes),
web/src/api.ts(kept #81'sgetCapabilitiesand this PR'sbindings),
web/src/main.ts(one import line, keptRecallStatusandPreregistration),docs/DECISIONS.md(entries newest first), andCargo.lock(regenerated from main's rather than hand-merged, one line ofdiff). The full account is in a comment below.
Why
research-prsat pending on the first pushNot stuck, and not failing to trigger. It ran and passed in 20m49s, finishing
after the other five. The cache restore was a partial hit rather than a full
one, because this PR moves
zorp-web/Cargo.tomlandCargo.lock. The stepthis PR adds,
Test zorp-web (research feature), then spent about twelve and ahalf minutes compiling
libduckdb-sysfrom source:-p zorp-web --features researchresolves a different feature set across the shared graph than-p zorp-trackdoes, so DuckDB is a different unit of work and the zorp-trackstep's build does not cover it.
That left a real risk this PR introduced. On a genuinely cold cache the job
would pay a cold
zorp-trackbuild plus that new twelve and a half minutes,and the workflow's own comment puts a cold DuckDB build at about 35 minutes,
which is over the old 45 minute cap. A job killed by the cap reads like a
broken test rather than a cold cache, so both research jobs now cap at 60
minutes with a comment saying why. Nothing about how they trigger changed.
Local verification
Run on the rebased head,
f197b99:cargo fmt --all --checkcargo build --workspace --lockedcargo test --workspace --lockedcargo test -p zorp-agent --features researchcargo test -p zorp-web --features researchcargo test -p zorp-web -p zorp-recall --features zorp-web/recall --lockedcargo clippy --workspace --exclude zorp-track --all-targets --locked -- -D warningsnpm run check(fromweb/)npm test(fromweb/)npm run build(fromweb/)Three guard tests were mutation-checked, since a guard test that passes with
its guard deleted is worth nothing: the model-authored-column test fails when
assumptionsis added to the frame, the no-tool test fails when a real toolname is added to its list, and the no-create test fails when
read_ledger'sexistence check is bypassed.
Also smoke-tested in a browser against a scratch server on a throwaway
workspace: the panel opens, the status line reports forecasting off, the empty
question and missing pre-registration paths produce the right errors, the
ledger read reports no run record without creating one, and the console is
clean.
Provenance
The agent that wrote this branch pushed it and then stalled on a harness
watchdog before it could open the PR. The commit and the branch are its work,
and this description was assembled from the
docs/DECISIONS.mdentry itcommitted. It has since returned, rebased, and reported the results above.
https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4