Skip to content

Releases: CELCPG/SigmaOasis

1.11.1

Choose a tag to compare

@github-actions github-actions released this 19 Aug 22:14

Sigma Oasis v1.11.1 — project recall, measured (and the gate it was missing)

v1.10 introduced recall across a project's chats and v1.11 shipped it. Neither release carried a
number, in a project whose notes are built on them. This one does — and the measurement found the
feature working well and its safety gate not working at all. Pinned by 1,449 node checks.

It works: 21/24 against 3/24

13 questions across 3 projects, each asked in a fresh chat in that project, both arms,
temperature 0, three passes. The recall arm runs the app's own retrieval over the project's other
chats; the bare arm does not. Nothing else differs.

arm recall questions control questions control pulled off topic s/question
recall 21/24 15/15 0/15 9.3
bare 3/24 15/15 0/15 13.1

Per pass: recall [7, 7, 7], bare [1, 1, 1]zero flaky cases. That matters here because
this project has been caught before by a ±3-case movement that turned out to be the server's
nondeterminism rather than a change; a result that repeats exactly three times is a different kind
of claim.

Nearly half those questions are controls: their answers are nowhere in the project — arithmetic,
a definition. They are the half that matters, because the risk of this feature was never that it
would fail to help. It was that stuffing other conversations into a small model's context would
drag it off the question it was actually asked.

The gate was firing on everything, and only the eval knew

Because the suite scores retrieval separately from the answer, the first run reported something
the answer column could not: recall fired on 5 controls out of 5. Three passages about freight
and tariffs went in front of "what is 15% of 200?". The model ignored them and answered correctly,
so nothing looked wrong — which is exactly why it would have stayed unnoticed on the model that did
not ignore them.

Three causes, each probed on its own signal and fixed:

  • The similarity floor sat below the embedding model's own baseline. 0.35 was borrowed from
    long-term memory, where it works. Embedding models sit at different baselines and
    nomic-embed's is about 0.54 — above the floor — so it admitted every passage for every
    question. Admission is now a margin over the project corpus's own mean, which cancels whatever
    baseline a model happens to sit at: measured, controls clear it by 0.023–0.047 and real recall
    questions by 0.095–0.196. (A z-score does not separate those two; the plain margin does.)
  • One incidental shared word counted as evidence of a topic. Two selective terms are required
    now. A question with a single content word — "Which password hash did we pick?" — is admitted by
    the semantic margin instead, the hybrid design covering for the half that cannot see it.
  • Corpus-relative selectivity cannot see words that are uninformative in English. Two controls
    still leaked, both admitted on the pair what + number: rare in those transcripts, so they
    looked informative, while agreeing about nothing. A small admission-time list of canonical
    interrogatives and auxiliaries fixes it — applied inside project recall rather than in the shared
    tokenizer, because it is one path's admission policy and not a change to how the app reads text
    everywhere. Ranking still sees every term.
gate fired on recall stayed quiet on control
as shipped in v1.10/v1.11 8/8 0/5
+ margin over the corpus mean 8/8 2/5
+ two selective terms required 8/8 3/5
+ admission-time stopwords 8/8 5/5

Recall never moved while the gate tightened. That is the result worth having: everything the
gate was letting through was noise, and removing it cost nothing.

One more, found on the way: a share-based selectivity rule is meaningless on a tiny corpus, where
any term appearing twice looks universal. It threw out the word "budget" in a two-chunk project
about the budget. A term is only judged uninformative once enough chunks carry it for the share to
be evidence of anything.

Also

  • A new opt-in eval suite, EVAL_SUITES=projects, with the fixtures and the scoring. Retrieval and
    answer quality are reported separately, on purpose — they fail differently, and this release is
    what that separation is for.

Caveats, which are real

  • One model class. qwen3.8-9b reasons internally. The second family — mistral-7b-instruct, the
    model where think-harder showed its effect in v1.9.1 — would not load beside it ("insufficient
    system resources"), so the answer numbers are single-family. The retrieval numbers do not
    depend on the answering model at all.
  • One embedding model. The margin rule is designed to be less model-dependent than a floor, but
    it has been measured against nomic-embed-text-v1.5 only.
  • Small corpora, 4–6 chunks per project.

Everything above, including the failed intermediate gates, is in
docs/evals.md.

Upgrade notes

Auto-update from v1.11.0. No settings changed and no UI moved. If you use projects with recall on,
you should see the "🗂 From this project's other chats" line appear on fewer replies — on the ones
where the project genuinely has something to say, and no longer on questions it does not.

Full changelog: v1.11.0...v1.11.1

1.11.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 20:48

Sigma Oasis v1.11.0 — two chats, side by side

A focused release: split view. Put two conversations next to each other with ⌘\ — two
branches of the same question compared line by line, or a reference chat kept open while you work
in another. Pinned by 1,444 node checks.

The pane you are typing into is never in doubt

Only the focused pane carries the composer. The other is a reader, one click from becoming the
writer. That is not a limitation dressed up as a feature — it is the reason the rest of the app
needed no changes at all: activeConversationId still means exactly what it meant, "the focused
chat", and it is still the only thing the composer, the chat panel and every turn entry point read.
The panes are a view; the machinery that runs a turn never learned they exist.

Focus moves by swapping the two conversation ids and flipping which side the unfocused pane sits on,
so the chat you were reading stays where it was on screen while the id that names the focused
pane changes underneath it. The chat panel (⌘J) follows the focus, so strategy, memory scope and
details always describe the chat you are about to type into.

Open a split with ⌘\, with ⊞ on any conversation in the rail, or from the command palette. The rail
dash-outlines whichever chat is in the other pane. ✕ on a pane header closes it and keeps the other.
Selecting a chat that is already in the other pane focuses that pane instead of showing it twice —
no chat is ever in both — and deleting a chat a pane is showing promotes the survivor rather than
leaving an empty pane beside a live one.

One turn at a time, unchanged and deliberate. There is one abort handle, one streaming flag, and
one local model server that pins the answering slot's model for the duration of a turn. Two
generations at once would fight over that pin and trade eject/reload cycles, so a split gives you two
views, not two engines.

What a second pane exposed

Three of these were latent the whole time and only a second mounted chat could show them; the fourth
turned up driving the real app rather than reading the code.

  • The stream-scroll subscription was never scoped to its own conversation. With one chat on
    screen that is invisible. With two, the pane you were reading scrolled itself to the bottom on
    every token the other pane emitted.
  • The composer does not fit at half width. Five fixed-size buttons plus Send left the input a
    sliver and rendered its placeholder as a single clipped letter. In split view the input takes its
    own full-width row and the controls wrap beneath it — every control still reachable, none hidden
    at narrow widths.
  • Message action rows ran off the edge of a half-width bubble. They wrap.
  • Closing a pane ran one event behind. A click focuses whichever pane it landed in, in the
    capture phase, so that a message's own buttons act on the right chat. The ✕ handler was reading
    the focus as it had been before that, so closing the pane you meant to dismiss kept it and threw
    away the one you were reading. Closing is unconditional now — focus the other, then close — which
    is correct from either pane.

Also

  • CI and the release pipeline now run their actions on Node 24 for real: checkout v7,
    setup-node v7, upload-artifact v7, download-artifact v8. A flat bump to v5 is the obvious-looking
    fix and is not enough — upload-artifact v5 and download-artifact v5/v6 shipped "preliminary Node 24
    support" while still defaulting their own runtime to Node 20, so the deprecation warning survives
    it. The build toolchain stays on Node 20; that is a separate thing from the runtime the actions
    themselves use.
  • download-artifact v8 defaults to failing on a digest mismatch, so a corrupted transfer now stops
    the release instead of attaching a bad installer to it.

Upgrade notes

Auto-update from v1.10.x. Nothing changes until you press ⌘\ — with no split open the middle of the
window renders exactly what v1.10 did. No settings were added and none changed meaning; the split is
per-session and not persisted across restarts.

Full changelog: v1.10.0...v1.11.0

1.10.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 19:40

Sigma Oasis v1.10.0 — projects that know things

Through v1.9 the conversation rail had two jobs: list your chats, and hold the controls for the
open one in its bottom-left corner. This release gives each job its own place, and gives the
"folder" a memory. Pinned by 1,428 node checks.

A chat panel on the right (⌘J)

Everything scoped to the open conversation now lives in a collapsible right-hand panel, the
mirror of the rail (⌘B): project, strategy and roles, memory scope (now an inline
checklist, not a pop-up), rollback and export, and a details readout — sent/replies, tool
calls, who answered, context in use, tokens generated, compaction state, when it started, files
shared in the chat, and its branches (one click to jump). Both layouts are remembered across
restarts. The rail is now only a list.

Projects

Group chats under named, colour-coded projects: fold a project, start a chat inside it, move a
chat with the 📁 menu or from the command palette (⌘K). Deleting a project keeps its chats.
Branches stay in their parent's project.

A project is a folder until it carries context. These carry four kinds, all edited under ⚙ on
the project or Edit project… in the panel:

  • Instructions — appended to the role's system prompt for every chat in the project. Stable
    across turns, so it sits with the role prompt rather than in the per-turn notes and costs
    nothing to re-read.
  • Pinned files — paths only, never content. Each is read from its path and indexed in RAM the
    first time a chat needs it (or now, with ⟳), then retrieved per turn exactly like an attached
    document. The editor says where each file stands — on disk, indexed, size — and a file that has
    moved is flagged in words, and in the panel.
  • Defaults for new chats — strategy, role, memory scope. Applied when a chat is started inside
    the project; existing chats are never changed.
  • Recall across chats — before each reply, the passages of the project's other chats most
    relevant to your message are handed to the model and shown under the reply as "🗂 From this
    project's other chats", expandable to exactly what was surfaced. Per-project toggle.

How recall stays private and stays quiet

Sibling chats are read from their JSON files in the main process — chat ids cross the IPC
boundary, transcripts never do — and a chat is re-indexed only when it changed. Ephemeral chats are
never on disk, so they are never recallable: the no-trace promise holds by construction.

Recall runs over a project-level index: every sibling transcript is chunked once per change
and cached; per query, one BM25 index is built over all of them (so a term rare across the project
is rare — IDF is shared), cosine comes from the same loopback embedding model memory uses, and the
two are fused by reciprocal rank across the whole corpus. A passage rides only on a shared term or
a cosine above the memory floor, so a chat with nothing to say about your message contributes
nothing; MMR trims near-duplicates. Keyword-only when the embedding model is unavailable. Verified
end to end: instructions in the system prompt, a sibling chat's figure recalled into another chat,
an unrelated question producing no block at all.

The details panel says what all of this costs: "↳ project share: ~1.6k tokens" under Context
in use
, with the split — instructions · pinned files · recall — in the tooltip. The composer's
context meter counts the project's instructions too.

Also

  • Think-channel recovery (v1.9.x) now uses the reasoning channel as direct evidence: reasoning and
    no content is an answer on the wrong channel (recovered, once); neither is a model with nothing
    to say (left alone). The tool-has-run rule remains as the fallback.
  • scripts/test.sh resolves the bundled Electron by absolute path, which is what stops macOS
    aborting the suite with "NSBundle initWithURL:: non-file URL argument" once the .app has been
    registered with LaunchServices.

Upgrade notes

Auto-update from v1.9.x. Existing conversations appear under Unfiled; nothing is grouped until
you make a project. Two settings were added (projects, rightPanelCollapsed) and one
per-conversation field (projectId); nothing existing changed meaning. The "This chat" controls
moved from the rail's bottom-left to the right panel — ⌘J if you have hidden it.

Full changelog: v1.9.1...v1.10.0

1.9.1

Choose a tag to compare

@github-actions github-actions released this 18 Aug 22:17

Sigma Oasis v1.9.1 — think harder, on the models where it works

Sigma Oasis has offered a think harder button since v1.6: draft an answer, review it, revise.
Three suites had measured it and found nothing. This release explains why, and — for the first
time — measures a case where it plainly works, so the button can tell you which of those two you
are about to get before you press it. Pinned by 1,369 node checks.

The answer depends entirely on which model you loaded

The earlier nulls were asking a model that already deliberates internally whether it would like to
deliberate again. Measured on three models across two classes, 14 multi-step reasoning problems
each, no tools — draft versus the same draft after one review pass:

model draft correct after review review fixed review broke cost
qwen3.8-9b · reasons internally 14/14 14/14 0 0/14 1.7x
gemma-4-12b-qat · reasons internally 10/10 completed 10/10 0 0/10 1.7x
mistral-7b-instruct · does not 6/42 · 14% 15/42 · 36% 9/36 0/6 4.8x

On a model that thinks before it answers, the internal deliberation is the think-harder pass, so
an external one has nothing to add — two families agree, and no revision has ever broken a correct
answer. On a model that does not, one review pass more than doubled correctness and broke none
of the answers that were already right
. The mistral run is three passes, identical in all three
with zero flaky cases, because a 3-of-12 single-pass result is exactly the size this project has
repeatedly caught being noise.

So the button now says what it will do for the model you have loaded — no change expected at
~1.7x, or about a quarter of wrong answers fixed at ~5x — instead of staying silent on the branch
where the feature works. On a model it does not recognise it says nothing at all: the classifier is
a name heuristic, and an unknown name is not evidence.

Two qualifications the same numbers show, kept in the notes rather than rounded off: the reviewer
revises 39 of 42 drafts rather than detecting errors — it rewrites by default and happens to help a
quarter of the time — and 4.8x is a fixed review cost sitting on top of a 3-second draft, about 16
seconds against 3 in absolute terms.

A research run no longer dies quietly when planning fails

A real chain, found by instrumenting a failed run. The planner fails on a reasoning model → the
fallback re-uses your raw question as the search query → the privacy filter correctly refuses to
send a first-person sentence to a search engine → and the error blamed the search provider for a
search that was never sent. Every link behaved as designed and the result was a dead end with a
misleading explanation.

Now the fallback derives keywords instead of sending your sentence verbatim, and when nothing
survives the filter the run says so in those terms: "No search was sent — the query was refused
by the privacy filter."
A round that surfaces no new sources ends the loop and writes the brief
from what it has.

Runaway thinking is reported as itself

A 12B reasoning model handed the four hardest problems in the suite spent 1497 of 1500 tokens
thinking and returned an empty answer
. Uncapped, it ran until the connection dropped and surfaced
as fetch failed — a network error for something that was never a network problem. Empty answers
now name the cause and the token split, are not retried (asking again at temperature 0 spends the
same minutes to fail the same way), and generation is capped so a runaway cannot masquerade as a
transport fault.

That cap took two attempts and the first one is in the notes too: 4000 tokens, chosen against the
model's context window, changed nothing — because the binding limit is the transport, not the
context.

Also

  • The thin-sources research regime is now measurable, and was measured: still null, with the reason
    identified in the synthesis prompt rather than left as a shrug.
  • The eval harness probes the model server for liveness before a long run, so an unloaded model
    fails in a second instead of after several minutes.

Every measurement above, including the nulls and the failed first attempt at the cap, is in
docs/evals.md.

Upgrade notes

Auto-update from v1.9.0 or earlier. Nothing changes by default: think harder remains opt-in per
message, it just now describes what it was measured to do on your model. No settings changed.

Full changelog: v1.9.0...v1.9.1

1.9.0

Choose a tag to compare

@github-actions github-actions released this 18 Aug 17:31

Sigma Oasis v1.9.0 — every stated fact has a source

The app's promise is that a small model does not have to guess. Four kinds of fact reach a reply —
computed, retrieved from your offline library, remembered from earlier in the conversation, and
researched from the web — and until this release only the first two were checked against the
evidence they came from. v1.9 closes both gaps: the research brief is now verified against the
pages it was written from, and the conversation ledger records the decisions you made alongside
the facts. Pinned by 1,360 node checks, and every claim below carries a measurement — including
the ones that came out null.

Deep research under the grounding ladder

  • The gap, stated plainly. deep_research writes its brief with a model, from the passages it
    read. That brief then becomes tool output — and every downstream check trusts tool output as
    its corpus. So a figure the synthesizer invented passed tool grounding, passed the recompute
    check, passed the claim check, and reached you wearing a citation. The evidence to catch it was
    sitting in the tool's own memory. Nothing looked.
  • The check. Mechanical, inside the tool, before the brief leaves it: every figure, every
    measurement (a dose, a duration, a temperature — the dangerous class) and every [n] citation
    in the brief must appear in a passage the run actually read. Roundings pass. A citation to a
    source the run never read is a fabricated reference, and is caught as one. Findings go back
    to the synthesizer for one revision, kept only if strictly better; what remains is disclosed
    first among the tool's notes, so an unsupported specific reaches you flagged rather than
    laundered into a finding.
  • A real bug it surfaced. Instrumenting a research run phase by phase showed 50 of 112 seconds
    spent on two replan rounds that re-asked a sub-question the search provider had already answered
    "nothing" — each replan a full model call taken out of the time left to write the brief. A round
    that finds no new sources now ends the loop and writes from what it has. The same run afterwards:
    one round, six pages, 92 seconds.

The ledger remembers decisions, not just facts

  • Decisions. Alongside computed figures, files and your stated constraints, the ledger now
    records the choices you make — "use the median rather than the mean", "go with the West
    region"
    — verbatim, in your own words. Later decisions supersede earlier ones on the same
    subject, because "actually, use the mean" is what you now want and a record that still says
    "median" is worse than none.
  • Measured where it matters. A long-regime case states two decisions, buries them under six
    off-topic turns, and then — with the establishing turn compacted out of the model's context by
    the app's own history planner — asks for them back. Nothing is recomputable, so there is no
    fallback. Three passes: ledger 3/3, bare 0/3. With the ledger: "You chose to use the median
    rather than the mean for every summary statistic, and you selected the West region as the
    focus."
    Without it: "Nothing yet — I haven't loaded any CSV or computed anything."
  • Python session state now reaches the model from the second turn, so a follow-up can build on the
    dataframe already loaded instead of re-deriving it.

Two null results, reported as such

This project's rule is that a measurement decides, including when it disagrees with the feature.

  • The research check flagged nothing on a clean corpus. Across 24 briefs the 9B synthesizer,
    told to cite only from numbered sources, invented nothing — so the rung had nothing to catch.
    Its value is in the regime the suite does not reach (thin or contradictory sources), where it is
    now measurable, and its unit tests pin what it catches.
  • Telling the model it already has the data does not stop it re-reading the file. With the
    session variables named in front of it and an explicit instruction, a 9B still re-read on 73% of
    follow-ups (67% with prompting alone — no difference). Reading the code it ran explained why:
    pandas had cached the read, so the habit costs 22 milliseconds. Prompting and a mechanical nudge
    have both now been measured against it; neither moves it, and the notes say so instead of trying
    a third thing until one flatters.

Both are in docs/evals.md,
along with the two bugs the research suite caught — one in the app, one in the eval's own scoring
(a decoy pattern that flagged a correct brief, the same class of error as v1.6's
"Never thaw on the counter").

Upgrade notes

Auto-update from v1.8.x or earlier. No settings changed; the research check is part of
deep_research and the ledger's decisions are part of the ledger, both on by default with the
existing off switches (Settings → Models).

Full changelog: v1.8.1...v1.9.0

1.8.1

Choose a tag to compare

@github-actions github-actions released this 18 Aug 12:37

Sigma Oasis v1.8.1 — the app remembers

A small model's fourth weakness, after thin knowledge, weak arithmetic and shallow procedure, is
that it loses the thread of a long conversation: it re-remembers a figure from ten turns ago
approximately and states it confidently. This release gives the app its own memory of what a
conversation has established — mechanical, exact, never paraphrased — and measures it in the one
regime where memory actually matters. It also closes out the v1.8 sessions story with a measured
improvement to how the model uses them. Pinned by 1,345 node checks (28 new since v1.8.0).

The conversation ledger

  • What it is. Every turn, the app rebuilds a running record of what this conversation has
    established — figures a tool computed (total revenue: 139306.12 — run_python, turn 1), files
    attached, Python session variables still defined, and constraints you stated in your own words
    ("My budget is $2,000", "I have a nut allergy"). No model call; the exact strings from tool
    results and your messages, never reformatted.
  • What it refuses to record. Anything an assistant said. A reply's figures already pass
    through the grounding ladder; repeating one into the ledger would launder an unverified number
    into a "fact". The ledger is the app's memory of the conversation, not the model's.
  • How it reaches the model. From the fourth turn on, only when it has something to say, as
    turn notes: use these exact values when a question refers back; do not restate them from
    memory; if a figure is not here, compute it.
    Disclosed under the reply — 📒 Ledger: 2 computed
    facts, 1 file, 1 constraint from 7 turns.
    Settings → Models → Conversation ledger, on by default.
  • Why not the summary that already existed? That one is written by a model, only when history
    is dropped, and it paraphrases — which is exactly the failure. The ledger keeps 139306.12.

Measured, in the regime that matters

A new eval suite (EVAL_SUITES=ledger) establishes a fact on turn 1, buries it, then asks for it
back without restating it — ledger arm vs. bare, identical otherwise. The first suite was a null
result
— five turns is short enough that the bare model just reads the turn-1 tool result out
of history — and it is recorded as such. So a long-regime variant compacts the establishing turn
out of the wire history using the app's own history planner and asserts it is gone, exactly as
a real long conversation loses it. Against a 9B, three passes:

establishing turn compacted out recall stability
ledger 15/15 · 100% 0 flaky
bare 3/15 · 20% 2 flaky

Bare's three successes are all recomputation against a still-attached CSV, not memory. Where
nothing is recomputable — a stated budget and deadline — bare said "I don't have any record of a
project with a budget or deadline in this conversation"
three passes out of three; the ledger arm
said "You told me at the start that your budget is $2,000 and the deadline is Friday."

The measurement also caught a real gap on the way: a 9B answered a total straight off
analyze_file's profile without ever running Python, and the ledger — which read only
run_python — recorded nothing. The model then told the truth about its own empty ledger rather
than inventing, which is the design working; the extractor was too narrow, and profile statistics
are now facts. docs/evals.md
has the full sequence, including what did not work first.

Sessions: the habit, measured and moved

v1.8.0 measured that a 9B told variables persist still re-read the data file on 6 of 10
follow-ups. One step in the data-analysis playbook now says: run_python keeps its variables —
build on the dataframe you already loaded; check the Session variables list before reading a file
again.
Two 3-pass runs, differing only in that step: session follow-up re-reads fell from
100% to 67% while the stateless control stayed at 100% in both. Narrowed, not closed — and
now a number future work is judged against.

Also

  • The eval harness refuses to start unless the model answers a probe — a 90-minute baseline
    once ran against a stopped LM Studio server and produced 0/0 across the board; it now fails in
    about a second with the fix named. Multi-pass headlines report the aggregate, not pass 1.

Upgrade notes

Auto-update from v1.8.0 or earlier. One new setting, on by default (Settings → Models →
Conversation ledger). Nothing else changes.

Full changelog: v1.8.0...v1.8.1

1.8.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 20:10

Sigma Oasis v1.8.0 — the analysis session

v1.6 gave a small model a Python sandbox; this release gives the sandbox a memory. run_python
is now a session scoped to the conversation — the dataframe loaded in turn one is simply
there in turn five — and, as always, the claim comes with a number: follow-up questions over a
dataset run 24% faster with fewer tool calls, measured by a new eval suite that ships in this
release. Pinned by 1,324 node checks and 40 real-sandbox checks.

Persistent Workbench sessions

  • A REPL scoped to the conversation. Variables and /work files persist between run_python
    calls within one conversation, like a notebook: "now filter that to Q4" works on the
    dataframe already loaded instead of forcing the model to rewrite the whole load-and-clean
    preamble — and each rewrite was a fresh chance for a small model to err (the measured failure
    class from the v1.6 eval).
  • The isolation properties do not move. The sandbox window, its CSP, the network refusal
    (not even loopback) and the virtual filesystem are exactly as before — a session is one kept
    globals dictionary, nothing more. One session lives at a time (the newest conversation wins,
    bounding memory), and switching conversations resets both globals and /work: nothing crosses
    between two conversations, pinned by real-sandbox checks in both directions.
  • Checks stay stateless by construction. analyze_file profiles, the recompute and
    code-check verifiers, and docx extraction all run with fresh globals — a check that could see
    session state would not be checking the reply. A stateless job running mid-session neither
    sees nor disturbs the session.
  • Loss is disclosed, never silent. A sandbox restart, the ten-minute idle teardown, or
    displacement by another conversation is reported in the next result — "Session reset …
    re-run your setup"
    — instead of surfacing as a bare NameError. Every result also lists the
    variables currently defined, so the model can see its own state.
  • REPL semantics on error: an exception leaves earlier definitions standing, like a notebook cell
    that failed.

Measured: a new multi-turn analysis suite

EVAL_SUITES=multiturn runs follow-up questions over one dataset through two arms — sessions on
vs. the old stateless sandbox — with each arm's tool description telling the truth about its
sandbox. Scored mechanically per turn, plus the behavioral columns the feature exists to change.
Against a 9B at temperature 0:

arm first turn follow-ups follow-up re-reads s/turn calls/turn
session 5/5 10/10 6/10 30.3 1.3
stateless 5/5 9/10 10/10 40.1 1.5

Reported the way this project reports things: the one-turn correctness edge is within single-pass
noise and the notes say so. The solid findings are speed (24% faster follow-ups, fewer calls) and
the re-read column — where the model leaned on the session, follow-ups re-read nothing at all.
The honest finding is that a 9B told variables persist still re-reads out of habit more often
than not: sessions enable building on state, they do not force it, and that gap is now a
measured number future work can be judged against
(docs/evals.md).

Upgrade notes

  • Auto-update from v1.7.x, v1.6.1 or v1.4.7. No settings changed; sessions are simply how
    run_python now behaves, and the tool's own description tells the model so.
  • Everything in the v1.7.1 notes
    (bundled curated packs, .docx, prose tool-call recovery) and the
    v1.7.0 notes
    (tracked personal packs, section-aware retrieval, the energy layer) applies unchanged.

Full changelog: v1.7.1...v1.8.0

1.7.1

Choose a tag to compare

@github-actions github-actions released this 17 Aug 18:59

Sigma Oasis v1.7.1 — the Almanac, included

v1.5 built an offline reference library and v1.7 made it personal — but the curated packs
themselves only existed for people who cloned the repository. An installed app had an empty
Almanac. This release closes that gap and rounds out the document story: the packs ship inside
the app, Word documents work everywhere a document can enter, a measured small-model failure mode
is mechanically recovered, and the eval harness learned to measure its own noise. Pinned by 1,320
node checks (17 new) and verified live in the built app against a 9B.

The curated packs, one click away

  • Bundled with the app. The seven curated packs — first aid, health, emergency preparedness,
    food safety, personal finance & tax, home safety, US civic basics; 105 public-domain documents,
    ~700 KB of text — now ship inside the installer, next to the Python runtime.
  • Settings → Library → "Curated packs". Each pack shows its contents and license with an
    Install button: a disk-to-disk copy that uses no network, embeds itself automatically, and
    becomes "✓ installed". When a future build ships a newer pack version, the button reads
    "Update to vX" instead.
  • packs.zip on every release — the same content as a standalone download, for anything else
    that can read the documented pack format.

Word documents, everywhere a document can enter

  • .docx works in personal packs and chat attachments. A docx is a zip of XML, and the app
    already has a place where zips of XML are read safely: the sandboxed Workbench (XLSX profiling
    has always worked this way). Extraction is a standard-library Python script running inside
    WebAssembly with no filesystem and no network — a malformed file blows up in the sandbox, not
    in the app, and the app gains zero new dependencies.
  • Word headings become real sections. Heading 1–6 and Title styles come back as Markdown
    headings, so a Word document gets the same section-aware chunking and pack › document ›
    section
    citations as everything else. Measured live: an inventory docx produced per-section
    passages, and a warranty question came back citing its section with the original .docx path
    as provenance.
  • Without the Workbench runtime the file is refused up front with the fix named, instead of
    paying a sandbox cold start to fail.

A measured failure mode, recovered

In the v1.7 eval, a 9B model answered a first-aid question with — in its entirety —
web_search("hypothermia what to do while waiting for help nhs"): a tool call written as prose,
with perfect retrieval already in hand. The turn scored zero. The app now recognizes exactly this
shape and executes it through the normal tool loop — same budgets, same visible record, same
follow-up round for the actual answer. The recognition is deliberately narrow, because prose and
code are full of f("x"): it fires only when the entire reply is one such call, the name is a
tool actually offered that turn, and the tool's schema has a single unambiguous string parameter.
At most once per turn. A reply that merely mentions a call, or a call that would require
guessing between parameters, stays prose.

The eval measures its own noise now

Re-measuring v1.7's retrieval change surfaced something uncomfortable: three full runs at
temperature 0 produced mostly-disjoint failure sets — cases flipped with identical retrieval. So
EVAL_PASSES=N now repeats a suite and classifies every case stable-pass, stable-fail, or
flaky
, with the flaky ones named and a median over passes. The rule, recorded in
docs/evals.md: judge a change by the stable set; the flaky list is the suite's measured noise
floor. No more single-run victory laps — including ours.

Upgrade notes

  • Auto-update from v1.7.0, v1.6.1 or v1.4.7. Installed size grows by under a megabyte (the packs).
  • Already-installed packs are untouched; the curated section simply offers what this build
    bundles. Everything in the
    v1.7.0 notes
    (tracked personal packs, section-aware retrieval, the energy layer) applies unchanged.

Full changelog: v1.7.0...v1.7.1

1.7.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 17:29

Sigma Oasis v1.7.0 — your documents, alive

v1.5 gave a small model something to read; v1.6 gave it something to compute with. This release
makes the reading personal and current: a folder of your own documents becomes a tracked
reference library that notices when the folder changes and updates in place without re-paying for
what didn't change — and retrieval got sharper about sections, which the eval suite then
re-measured honestly. Plus a visual pass: light in motion wherever the app is actually working.
Pinned by 1,303 node checks (35 new since v1.6.1) and verified live in the built app against a 9B.

Personal packs: tracked, not snapshotted

  • The folder is remembered. "Add folder…" (Settings → Library) has built a pack from your
    .md/.txt/.pdf files since v1.5 — but as a dead snapshot. Now the pack tracks its source
    folder: opening the Library tab stat-walks it (no file contents read) and says plainly when it
    has drifted — "Source folder has changed: 1 edited, 1 new (warranty.txt, lease.txt)."
  • Update in place; unchanged documents keep their embeddings. "Update" rebuilds the pack from
    the folder as it is now. Vector carry-over is by content hash, not filename — a renamed or
    moved file keeps its embeddings — so updating a 500-document pack after editing three files
    re-embeds three files. Sound because chunking is deterministic: identical text, identical chunks.
  • Embedding is automatic. After an add or an update, embedding starts on its own (progress bar
    and Cancel as before). No embedding model loaded? A calm note — keyword search already works.
  • "My lease" now consults the library. The trigger vocabulary grew to what personal documents
    actually are: my lease, the warranty, my insurance policy, according to the spec. Each
    noun stays anchored to a possessive or "the" — "foreign policy" does not fire. Measured live:
    "does my lease allow subletting the second bedroom?" pulled the user's own lease, and the reply
    quoted the current version's term with the citation Home papers › lease.

Retrieval: sections, not blends

  • Section-aware chunking. No chunk ever spans a Markdown heading boundary, so a short section
    ("### Boiling") is its own crisp passage that starts with its own heading, instead of blending
    into its neighbor and matching nothing well.
  • One passage per (document, section). The first change alone let a strong section place two
    near-twin chunks in the top five and crowd out another section — the inverse disease — so
    lookups now spend their passages on distinct sections, backfilled from the next-best sections.
  • Re-measured, reported honestly (docs/evals.md): both recorded
    wrong-section failures are fixed and unsupported measurements went 1/28 → 0/28 — the class
    where an invented "30 minutes" on a boil-water question is worse than no answer. The aggregate
    answered-rate did not move outside run-to-run noise, and the doc says so with three runs of
    evidence rather than picking the flattering one.
  • The re-measurement also caught two defects that were never retrieval, both fixed: the pack
    builder was splitting styled first letters ("F ace drooping" in the stroke FAST mnemonic — the
    model failed the eval by quoting its reference faithfully), and a reply that opened by echoing
    the app's internal turn-notes scaffold now has that echo stripped and disclosed (🧾), by a guard
    that shares its marker string with the prompt so the two cannot drift.

The energy layer

Motion now means exactly one thing: computation in flight. Nothing animates at rest.

  • The thinking core gained a thin HUD scan arc sweeping its rim and two sparks orbiting at
    different periods, colored by the running tool; light sweeps through the THINKING label.
  • The streaming reply carries a live wire — a teal-into-lavender light running its border —
    and the answering model's avatar breathes in rhythm. Both stop dead when the reply completes.
  • A running tool block shows a light traveling its base in that tool's color; the composer
    breathes while a reply is generating; the ambient orbs drift on a 48/62-second cycle.
  • All of it is compositor-friendly (transform/opacity only), and prefers-reduced-motion stills
    every animation while keeping its meaning.

Upgrade notes

  • Re-embed once. The new section-aware chunk geometry orphans vectors embedded before v1.7:
    packs show "Embed" again in Settings → Library and stay keyword-ranked until re-embedded (user
    packs re-embed themselves the first time you press Update). This is the same graceful path that
    already existed for switching embedding models.
  • Pre-v1.7 user packs predate folder tracking: remove and re-add the folder once; updates work
    from then on. New fields in the pack format are additive; format version stays 1
    (docs/library-pack-format.md).
  • Auto-update: from v1.6.1 (or v1.4.7) this appears as an update automatically.

Full changelog: v1.6.1...v1.7.0

1.6.1

Choose a tag to compare

@github-actions github-actions released this 17 Aug 14:28

Sigma Oasis v1.6.1 — the Workbench

This is the v1.6.0 release. The app is unchanged from the v1.6.0 tag except
for the release pipeline that builds it: the v1.6.0 run hit two independent CI
failures and its installers were never published, so the same content ships
here under the next patch number. Everything in
RELEASE-NOTES-v1.6.0.md — the sandboxed Python
Workbench, run_python / analyze_file, the "Ran code" block, the Workbench
verifier, the Data Analyst role, and the measured 56% → 100% on the
quantitative suite — applies to this build verbatim.

What v1.6.1 actually changes (build & release only)

  • Windows installer restored. The Windows job failed on v1.6.0 because
    Windows Python writes CRLF line endings and the Pyodide wheel list reached
    curl with a trailing \r on every URL (curl: (3) URL rejected). The
    fetch script now strips CR and .gitattributes pins scripts to LF.
  • One job owns the release. On 2026-08-17 GitHub's list-releases REST
    endpoint began returning an empty array for every repository, which made
    each platform job in the v1.6.0 run create its own draft release — the
    signed DMGs landed on one, the AppImage on another, and the Homebrew bump
    failed against the one with no DMGs. The platform jobs now build with
    --publish never and hold no write permission; a single publish job creates
    one draft, attaches all nine assets by release id, and fails unless every
    expected asset is present
    — so an incomplete release fails its own run
    instead of being discovered on the release page.
  • Asset names are now literal. artifactName hard-codes Sigma-Oasis
    rather than interpolating the product name ("Sigma Oasis"); the dashed form
    is what every published asset has always carried and what the auto-update
    metadata points at.

Upgrade notes

Identical to v1.6.0's, with one correction: v1.6.0 was never published, so
auto-update takes you from v1.4.7 (the last released build) straight to v1.6.1.

Full changelog: v1.4.7...v1.6.1