Skip to content

Make the region-count injector audible, and stop double-writing the answer - #30

Merged
Robbie1977 merged 4 commits into
mainfrom
fix/neuron-counts-and-duplicate-answers
Aug 14, 2026
Merged

Make the region-count injector audible, and stop double-writing the answer#30
Robbie1977 merged 4 commits into
mainfrom
fix/neuron-counts-and-duplicate-answers

Conversation

@Robbie1977

Copy link
Copy Markdown
Contributor

Two defects from the 11 August review, both on the flagship "how many neurons are in the fly brain?" question.

The injector was never actually silent about declining — it was inaudible

maybeInjectRegionNeuronCountStep called log() only on success. log() appends to the per-request trace, and the trace is printed only under VFB_HARNESS_TRACE=true. So the absence of an inject line in the three production runs we read was not evidence that the injector declined. It was not evidence either way, and the diagnosis built on it could not have been supported.

The decision now goes on stderr, on both paths, alongside ABSENCE ESCALATION and GROUNDING — and only once the question has count intent, so it stays quiet on the questions it has nothing to say about. Ontology ids, gate names and term flags only; the question is the user's text and is not logged.

While there: the gate regionTerms.length === 1 declined whenever a second region resolved. VFB answers "fly brain" with 224 documents that are almost all painted domains on JRC_FlyEM_HemibrainAB(L), AL(R), AL-DA1(R) — and every one of them is Anatomy-and-not-a-neuron, so every one satisfies isRegionTerm exactly as brain (FBbt:00005095) does. One survivor was enough to take the curated counts off the table. It now prefers the ontology class; two classes is a real ambiguity and still declines, audibly.

The answer could be written out twice

Absence escalation runs a second synthesis. Synthesis streams token by token into one live bubble on the client and the only event that ends that bubble is result, so the second draft was appended to the first — the same paragraph twice, in two slightly different wordings.

The tokens are already sent, so the server cannot unsend them. It tells the client instead: a draft_discarded event fires the moment escalation is accepted, and the client drops the bubble so the replacement opens a fresh one. Best-effort like onStatus — an unwired or throwing hook costs the run nothing.

How to test

npm test (5 new cases in orchestrator_run.test.mjs, 3 in absence.test.mjs). Live: ask "How many neurons are in the fly brain?" on the deployed build and read the container log for REGION COUNT INJECTOR. That line now names the gate whatever happens.

Follow-ups

  • The injector fix is unproven against production. If the line comes back no-region-term or already-planned, the remaining fault is upstream of this gate.
  • /docs/concepts/neuron-counts/ still 404s until VFB2 is redeployed.

Robbie1977 and others added 4 commits August 13, 2026 12:23
The curated neuron counts in config/fly-neuron-counts.json never reached an
answer, and the reason could not be established from the container log:
maybeInjectRegionNeuronCountStep called log() only on success, and log()
appends to the per-request trace, which is printed only under
VFB_HARNESS_TRACE=true. The absence of an inject line in three production
runs was therefore not evidence that the injector declined. It was not
evidence either way.

Put the decision on stderr, on both paths, alongside the grounding audit
and the absence gate — and only once the question has count intent, so it
stays quiet on questions this has nothing to say about. Ontology ids, gate
names and term flags only; the question is the user's text and is not
logged.

Also stop declining on a second resolved region. VFB answers "fly brain"
with 224 documents that are almost all painted domains on
JRC_FlyEM_Hemibrain, and each one is Anatomy-and-not-a-neuron, so each one
satisfies isRegionTerm exactly as brain (FBbt_00005095) does. One survivor
was enough to take the count off the table. Prefer the ontology class; two
classes is a real ambiguity and still declines, now audibly.
Absence escalation runs a second synthesis when a draft denies data the run
never looked for. Synthesis streams token by token into one live bubble on
the client, and the only event that ends that bubble is the final result,
so the second draft was appended to the first. Production, 11 August, on
"How many neurons are in the fly brain?": the same paragraph twice, in two
slightly different wordings, one after the other.

The tokens are already sent, so the server cannot unsend them. It tells the
client instead: a draft_discarded event fires the moment escalation is
accepted, and the client drops the bubble and clears its ref so the
replacement opens a fresh one. The status line that already accompanies
escalation says what is happening while the second answer is written.

Best-effort like onStatus — an unwired or throwing hook costs the run
nothing, so a client that has not been updated behaves exactly as before.
Resolves the generated task-battery latest.json in favour of this branch's
run; it is regenerated on main by the battery workflow.

# Conflicts:
#	test-results/task-battery/latest.json
@Robbie1977
Robbie1977 merged commit 8a4304d into main Aug 14, 2026
2 of 6 checks passed
Robbie1977 pushed a commit that referenced this pull request Aug 14, 2026
lib/orchestrator.mjs: #30 added discardDraft() immediately after the ABSENCE
ESCALATION log line that this branch rewrites. Both are kept — the line no
longer quotes the user's sentence, and the streamed draft is still withdrawn.
latest.json is generated and takes this branch's run.

# Conflicts:
#	lib/orchestrator.mjs
#	test-results/task-battery/latest.json
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.

2 participants