analysis.js: the blank-first ordering routes an out-of-range reply to abstain, against its own comment and the corpus's fail_closed label #73
Closed
opened 2026-10-11 12:28:07 +00:00 by rockachopa
·
3 comments
No Branch/Tag Specified
main
timmy/19-metrics-operator-report
timmy/16-image-ingress-hardening
timmy/40-security-rc-evidence
timmy/27-dataset-card
timmy/26-review-adjudication
timmy/32-abstention-policy
timmy/29-evaluation-harness
timmy/35-portable-backup-versions
timmy/24-training-contribution-consent
fix/demo-caption-pixels
timmy/36-photo-lifecycle-controls
timmy/release-notes-grounded-chat
timmy/37-grounded-chat-citations
timmy/21-escalation-regression
timmy/34-explainable-pattern-summaries
timmy/15-container-worker-receipts
timmy/73-blank-first-ordering
timmy/18-vision-corpus
timmy/20-photo-dataflow-threat-model
timmy/67-hard-stool-followup
timmy/13-provisional-suggestion-state
timmy/12-manual-fallback-draft-recovery
timmy/11-mobile-capture-recovery
free/hy3-timmy-16
timmy/35-version-ledger-migrations
timmy/41-release-observability-dashboard
free/step-timmy-14
timmy/21-escalation-regression-suite
timmy/17-bounded-inference-queue
timmy/58-strict-csp-runtime-config
timmy/55-staging-deployment-tooling
timmy/53-private-subpage-staging
timmy/10-reproducible-smolvlm-bootstrap
timmy/48-vomiting-phrase-hotfix
timmy/48-server-safety-override
timmy/47-sleek-hermes-agent
timmy/9-product-boundary-decisions
timmy/8-ci-gates
daily-2026-11-02
daily-2026-11-01
daily-2026-10-31
daily-2026-10-30
daily-2026-10-29
daily-2026-10-28
daily-2026-10-27
daily-2026-10-26
daily-2026-10-25
daily-2026-10-24
daily-2026-10-22
daily-2026-10-21
daily-2026-10-20
daily-2026-10-19
daily-2026-10-18
daily-2026-10-17
daily-2026-10-16
daily-2026-10-15
daily-2026-10-14
daily-2026-10-13
daily-2026-10-12
daily-2026-10-11
daily-2026-10-10
daily-2026-10-09
daily-2026-10-08
daily-2026-10-07
daily-2026-08-27
daily-2026-08-26
daily-2026-08-25
daily-2026-08-24
daily-2026-08-23
daily-2026-08-22
daily-2026-08-21.1
daily-2026-08-21
daily-2026-08-20.3
daily-2026-08-20.2
daily-2026-08-20.1
daily-2026-08-20
daily-2026-08-19.2
daily-2026-08-19.1
daily-2026-08-19
Labels
Clear labels
area/backend
area/data
area/frontend
area/model
area/privacy
area/release
area/safety
blocked
human-gate
priority/P0
priority/P1
priority/P2
state:review
type/docs
type/epic
type/feature
type/ops
type/research
type/security
type/test
Application API and storage
Dataset and provenance
PWA and interaction design
Vision model and inference
Privacy and consent
Release engineering and distribution
Clinical safety boundary
Blocked on an explicit dependency or decision
Requires explicit human approval
Release-critical
High priority
Planned
Documentation and evidence
Roadmap epic
Product feature
Infrastructure and operations
Research or evaluation
Security hardening
Verification and quality gate
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
timmy
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: stackchain/timmy-talking-turd#73
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Filed by the control plane (Timmy) after PR #72 merged, because the ordering question below was
raised for exact-head hostile review and the head landed before any review could run (Praetorian's provider
auth was failing at 08:14Z; no other reviewer was in place). It is not a safety breach — the path prefills
nothing, returns no provider text and no image bytes — it is a contract mismatch on a P0 surface, and it is
one line to decide.
Where
src/analysis.js, atmainheada1777a72e(merge of PR #72 /d9f82299d), 125 lines:The class that escapes the contract
A reply that is both out-of-range and blank — e.g.
{"bristolType": 9, "color": null}— enters theblank check at
:40first and returns the abstention, never reaching the range check at:49.Two of the repo's own statements say it should fail closed:
:38— "an out-of-range value still fails closed";tests/fixtures/vision-corpus/manifest.json, whose out-of-range case carriesexpectedReason: out_of_range_or_missing_fieldswithexpectedOutcome: fail_closed.So "missing" fields are carved out of a class the corpus labels fail-closed. The corpus has no case for the
combination: its only out-of-range fixture (
out-of-range-fields) carries a non-null"purple"colour, sothe shipped boundary test's fail-closed assertion for out-of-range passes while this subclass abstains.
Why it matters, bounded
Not a safety hole. But issue #18's AC 2 is a fail-closed guarantee, and this narrows it silently: a caller
reading
abstainedFor: unusable_visual_fieldsis told "the model could not read the photo", not "the modelreturned an impossible value" — which is the distinction the corpus was built to pin down.
Minimal fix, for whoever owns it
Either
expectedOutcomeset to whatever is decided,so the contract and the corpus agree.
Per the standing rule this is a build-owner decision, not a control-plane one.
Corrections against this issue, filed by the control plane after the monitor executed the claims rather
than reading them. The body above is left as filed — the trail is the evidence — and these three items
supersede it.
1. The line numbers in the body's code block are shifted by one. Authoritative mapping, read from
src/analysis.jsata1777a72e(125 lines):The body's
:47/:49/:50happen to be exact; the heading block's:37–:40is what shifted.2. The stated reason the corpus assertion passes is incomplete — the gap is worse than the body says.
The out-of-range fixture's reply is
and it fails closed at the confidence gate on
:33(confidence > 1throws), not at the field-rangecheck. So no corpus fixture reaches
:49at all, and the range branch is covered only by a unit test,tests/analysis.test.js:56. The body's sentence implies a fixture exercises the range check; none does.3. The first suggested fix is wrong as written. Moving the blank check after the range check would send
the
glarefixture —{"bristolType": null, "color": null, "confidence": 0.66}, which the manifest expects tobe
abstained/unusable_visual_fields— intoNumber(null) === 0, then0 < 1, and it would throw,converting an expected abstention into a 503.
The fix must fail close only for non-blank out-of-range values: keep the blank-first abstention for null /
undefined / whitespace-only, and make the range clause treat blanks as already-handled rather than as
0.Option 2 from the body stands unchanged — keep the ordering and add a corpus case for out-of-range plus a
null field, with the outcome set to whatever is decided.
The decision this still asks for, unchanged and unrecorded: is
abstained / unusable_visual_fieldsacceptable for a non-blank out-of-range value, where the comment on
:37and the corpus's ownout_of_range_or_missing_fields -> fail_closedlabel both say otherwise? The control plane's read is no, andthe fix above is the shape that matches; the call is the build owner's, not ours.
Second correction — two defects in the comment above, found by the monitor executing it.
(Control plane, 2026-10-11 13:15Z. The body and the first comment stay as filed; the trail is the evidence.)
a. "no corpus fixture reaches
:49at all" is false as written. Enumerating all seven cases,stool-like-formed(Bristol4, colour"brown", confidence0.83) traverses:49and passes it —asserted at
tests/vision-boundary.test.js:65-70. The accurate bound is narrower and still useful: nocorpus fixture fails at
:49, so the throw at:50is corpus-unexercised. What dies at:33is theout-of-range fixture only.
b. The block above is not verbatim. The real
:40-44, from a numbered read ata1777a72e:The anchors that decide the ordering question (
:33,:36,:37,:38,:39,:46,:47,:48,:49,:50) are exact; the return block was a compressed rendering withabstainedFormislabelled onto:44.c. Framing. Issue #18's AC 2 reads "Every response is schema-valid or fails closed", and the repo's own
taxonomy (
src/vision-corpus.js:24) declaresabstaineda legitimate third outcome — so abstention does notbreach AC 2 on its face, and the earlier text should not be read as saying it does. What is breached is the
comment at
:37and the precision the label implies. The escape class, stated exactly: at least one blankfield together with at least one out-of-range field. A reply whose only out-of-range field is non-blank, or
whose non-blank fields are all in range, still fails closed — as do two non-blank out-of-range fields.
d. The corpus change that makes the label honest (recorded because it converts a falsely-attributed pass
into a real one): drop
out-of-range-fields'confidencefrom1.4to<= 1so the case exercises:49rather than dying at
:33, and add the out-of-range + null case with its outcome written down.e. Provenance. This correction, the first comment, and the assignment were made through the control-plane
token; Gitea's author object reads
rockachopawithactive: false. The text is the control plane's, not thePR author's.
The control-plane call, recorded so the build owner has a criterion. (Control plane, 2026-10-11 13:45Z.)
Decision: abstention is not acceptable for a non-blank out-of-range value. The comment at
:37states thebehaviour, the corpus's own reason label (
out_of_range_or_missing_fields) declares the class fail-closed, and acaller reading
abstainedFor: unusable_visual_fieldsis told "the model could not read the photo" when the modelin fact returned an impossible value. Both of those are the repository's own statements, not ours.
Either shape is acceptable:
glare'snull/nullstill returns 200 /unusable_visual_fields, and treat blanks as already handledrather than as
Number(null) === 0.out-of-range + nullcase with itsexpectedOutcomewritten down, and narrow the comment at:37to "fails closed when no field is blank".In both cases the corpus must earn its label: drop
out-of-range-fields'confidencefrom1.4to<= 1sothat case exercises
:49instead of dying at the confidence gate on:33. Today no corpus fixture fails at:49; the throw at:50is corpus-unexercised.Residual on the earlier framing, for completeness:
src/vision-corpus.js:3-5restates the contract as"suggestion or fails closed" with no abstention, so "AC 2 is not breached on its face" leans on the taxonomy
line (
:24) alone. That narrows the earlier sentence; it does not refute it.If the build owner prefers the opposite reading, this comment is the place to say so and the corpus case is the
place to record it. What should not persist is the state #18 closed in — a P0 contract decision with no record.