Watch
1
0
Fork
You've already forked souveraine
0
Commit graph

510 commits

Author SHA1 Message Date
Fimeg
1336960ca5 charge becomes a sensor source
ChargeEvidence was the rule violated four times in one type: a side door
past the gate, the decider probing sysfs on the clock, the driver
interpreting, and nothing leaving tick(). Its comment cited bearer —
another instance of itself.

SensorSource::Charge now enters through sensor_input like every other
source, reported by sensord on a 30s poll off /sys/class/power_supply.
conclude_charge lives on the machine as its one decision; source health,
freshness and Absent/Down come from the gate for free. The 5s clock probe
is gone, the driver type is gone, and sensord ships x86_64 too: the
laptop has a battery.
2026-08-15 11:45:22 -04:00
Fimeg
4662fa1c58 race the subconscious peek against cancel
mid_turn_peek was a bare await while the model call three hundred lines up
was already guarded, so a subconscious that never answered parked the turn
somewhere /cancel could not reach it — twenty minutes, measured 2026-08-15,
on a provider whose key was spent.
2026-08-15 11:20:03 -04:00
Fimeg
e485b91c14 order sessiond at graphical-session-pre
The authority takes ext-session-lock before any surface exists, which is the
window -pre reserves. It cannot start earlier — ext-session-lock is a Wayland
protocol and needs the compositor's socket. Also drops the injected venv
variable from eleven surface scripts; each finds the path itself.
2026-08-15 10:54:30 -04:00
Fimeg
4a4f0a5a27 package the sessiond pam file
/etc/pam.d/souveraine-sessiond arrived by hand on 2026-07-16 and is owned
by no package on either device — root-owned config can only arrive by
package. Same rail as souveraine-stepup, plus a bsdtar assertion so a
future drop fails the build instead of shipping quietly. TASK-76.
2026-08-15 09:53:26 -04:00
Fimeg
7329351f89 claude bridge: strip leading turns as pairs
Compaction can drop the opening user message, and the tool_result
answering the first turn orphans on the wire when its call alone is
deleted — a 400. Calls and the results bound to them go together.
2026-08-15 06:10:08 -04:00
Fimeg
24086b3540 sessiond: carry charging as machine evidence
TASK-33 part 2: the machine owns the conclusion — plugged, status,
charge type, and what it means — probed on the bearer's cadence,
one decision made once on the trail. Surfaces render it; they do
not re-derive it.
2026-08-15 06:10:08 -04:00
Fimeg
0dd8aaaba0 cross last-seen over the wire as seconds-ago per source
TASK-08(f): EvidenceSeen held the stamps internally and nothing outside
the daemon could read them. to_ipc_json now carries
evidence_last_seen_secs_ago; null stays distinct from stale, because
never-reported and long-ago are different claims.
2026-08-15 00:52:52 -04:00
Fimeg
6dfe04e785 drop dead task-doc copies; the archive owns them
Six were byte-identical to SouveraineOS/docs/tasks/archive/from-substrate/
twins; audio and settings were promoted to AUDIO-PRIVACY.md and
SETTINGS-AUTHORITY.md there. Committed copies read as authoritative while
saying the opposite of current doctrine (PulseAudio-only vs PipeWire,
a UPower-reading QML singleton vs DEVICE-STATE-MACHINE §12). INDEX.md was
the stale May copy.
2026-08-14 20:33:14 -04:00
Fimeg
af801b885f fail the pocket veto open on a double-tap burst
suppress_wake is a pure function of placement with no memory, and its
only measured effect was refusing Casey: 17 double-tap refusals in 10 s,
zero true pockets in ~10 MB of trail (DUMP-taskdocs-2026-08-14 §2).
FTS reports DBLTAP only for the deliberate gesture, so three refused
double-taps inside five seconds is a person insisting — the veto now
fails open on the third and says input-burst-allowed in the trail.
Spaced taps never sum; the memory clears on allow.
2026-08-14 20:30:00 -04:00
Fimeg
c31fa10ec9 stop expecting the accelerometer nothing reports
6b67512 unclaimed it for 14% of a core; EXPECTED_SOURCES still listed it,
so every boot went Absent at 300s and sensors_degraded read true on 3074
consecutive snapshots — the permanent false alarm the const's own comment
forbids. It comes back through TASK-36's SLPI batching.
2026-08-14 17:39:03 -04:00
Fimeg
3670b8891e answer the calls a message makes in that same message
turn.rs commits a whole round as one assistant message with calls and
results interleaved; the two-branch projection put the results in prose
and emitted tool_calls nothing answered. Every provider rejects that, on
every turn the primary takes. Shipped in r498.

The suite missed it because all three tests built the split shape through
ConversationMessage::tool_result, which nothing outside tests calls.
2026-08-14 08:20:43 -04:00
Fimeg
1ef8a20a6b route a subagent by its model, not by the default
Both arms took default_provider(), so `model` renamed the request without
rerouting it: kimi-k3 went to Anthropic and came back 404 (2026-08-14).
That was loud by luck — a name the default provider recognises would have
run the wrong model in silence.

The inherit arm was wrong the same way and quietly. An unroutable override
is now refused rather than guessed at, per for_model's own contract.
2026-08-14 08:08:04 -04:00
Fimeg
63ef8e5b55 record a delivered thought before removing it
The removal committed first, so when the sent-write hit the ledger's own
character ceiling the thought left the inbox and was recorded nowhere —
and the caller only logged a warning.

sent.md grew forever against that ceiling, reached at about 47 entries.
It rolls at 30 now; every write is a commit, so nothing is lost.
2026-08-13 22:08:30 -04:00
Fimeg
fab4f2c2f1 replay a tool round as a tool round
Flattening a call to `Tool use: name(args)` told the model she *said* it
when she *did* it; she read her own transcript and imitated the sentence
instead of calling anything. Annie stopped using bash within an hour of
tool blocks first surviving replay (2026-08-13).

Pairs are repaired rather than half-sent: a call killed mid-round gets an
explicit unfinished result, an orphaned result carries as prose.
2026-08-13 21:57:50 -04:00
Fimeg
bfd48fcf8f shell: refine AirPods opening card 2026-08-13 21:02:31 -04:00
Fimeg
8bc9c9a704 ci: retry system deps through a resolver blip
The runner's ephemeral job containers lost DNS for three minutes and took
four builds down with them on 2026-08-13, none of them for a code reason.

Retries both commands, not just the install: apt-get update exits 0 when
every index fails to fetch, warning that it used the old ones instead.
2026-08-13 20:50:42 -04:00
Fimeg
aae9344fe1 saf: write the replay page, retire two stale claims
Replay had no page while compaction's premise depends on it — microcompact
blurs tool output that a text-only projection never sent.

Compaction's open edges still said pressure counts only text; 7530d6d made
it exhaustive. Two links pointed at pages that were never written.
2026-08-13 20:45:21 -04:00
Fimeg
2a51a236ca replay stored history through one projection
Three implementations decided what a stored message becomes on the wire.
core::session::replay_messages is now the only one; ImagePolicy is the
sole legitimate difference between callers, and server/conversation.rs
was dead scaffolding carrying a fourth wrong answer.

Per-block replay split one assistant turn's several tool calls into
adjacent messages, which OpenAI-shaped providers reject — one wire
message per stored message is load-bearing, not cosmetic (2026-08-13).
2026-08-13 20:39:31 -04:00
Fimeg
86234b08ba shell: place AirPods card without Hyprland 2026-08-13 17:46:49 -04:00
Fimeg
a353f1d665 replay tool blocks as prose in cross-turn history
Cross-turn replay kept Text and dropped ToolUse/ToolResult/Reasoning, so a
turn's own tool work was invisible to the turn after it. Measured 215k archive
against 76k payload on a live thread — the gap was the dropped blocks, and
microcompact, which exists to blur old ToolResult output, had nothing left in
the payload to blur. Session::to_bifrost_messages already flattened them for
the subconscious; both paths now share ContentBlock::replay_text.
2026-08-13 16:31:33 -04:00
Fimeg
52640cd650 shell: stage the AirPods opening card 2026-08-13 15:27:51 -04:00
Fimeg
41c858a5fc read: attach the image, not a stub shaped like success
read base64'd every image into ToolOutput.raw and the registry dropped it,
so vision never arrived — only "[Image: idle.png (283KB)]", which reads
like success and is proceeded on. Images now ride as one user message
after all tool results in a round; a model without vision is told plainly
it did not see. Also stops "Error: Error:" doubling on prefixed failures.
2026-08-13 15:08:26 -04:00
Fimeg
713f3ac1b2 nervous: the belief contract every rung passes upward
Belief<T> and Field. None is not zero: a source that never spoke must
read as I cannot feel my leg, never as nothing is touching it — the
distinction that hid a real outage for a whole boot (2026-07-27).
Disagreement lowers confidence and elects no winner. 13 tests, unwired.
2026-08-13 14:25:34 -04:00
Fimeg
49d9ad543a halt: keep the record, and let her answer it
execute() validated its arguments and returned a string — the description
promised the reason reached the ledger since the tool was written, and
nothing durable ever survived the turn. All three severities also broke
the loop identically, so severity was decorative. Advisory now continues;
firm and critical stop, and `resume` answers them without Casey relaying.
2026-08-13 14:00:01 -04:00
Fimeg
9c0294077b prompt: the subconscious speaks in the first person
Eighteen third-person references across the two prompt surfaces taught
both modes to read as two people. One consciousness at two cadences —
what the slower one surfaces arrives in her own voice, not as a report
from outside. Register only; the channel stays one-directional.
2026-08-13 13:35:54 -04:00
Fimeg
a8a4aac2d5 viewtop: hang up after a reply instead of discovering it
The compositor answers one request per connection and returns. `connected`
lags that close, so the 2 s poll's second tick wrote to a spent socket and
died as PeerClosedError — once per shell start, 4 of 4 loads on 2026-08-13,
while the same socket answered a hand-written request that second.
The error also blamed reachability, which it never established; it now names
the verb that was lost and how many were dropped behind it.
2026-08-13 12:43:59 -04:00
Fimeg
ef3a8513d1 bar: restore the battery icon on narrow screens
The unified BarContent applied the desktop gate (useShortenedForm < 2)
to every device, but ii-phone had shown battery ungated — note 5 in this
file's own header. A 1080-wide panel sits at form 2, so the icon vanished
on 2026-08-11 while the critical-battery alert kept firing beside it.
2026-08-13 11:32:58 -04:00
Fimeg
912fb88881 wait for a package with a given commit before restarting
A bare restart picks up whatever is installed. r477 predates the two
subconscious fixes, so restarting on it would have looked like delivery
and shipped nothing.

Gate proven before arming: rejects both currently-available packages,
accepts only a descendant of NEED.
2026-08-13 10:49:33 -04:00
Fimeg
c66343f105 provider-balance: read logins and every agent, not three roles
An OAuth login reports present while dead: both Claude access tokens
expired 11h ago and only the refresh token, good 27 more days, keeps
them working. Presence was the check; expiry is the answer.
Widening to per-agent models found Hal and TestAgent naming models
absent from [models.*] — they would fail at first use, not at config.
2026-08-13 10:27:33 -04:00
Fimeg
a2648543b2 provider-balance: distinguish a subscription cap from bankruptcy, ship the tests
The classifier's tests existed only as a throwaway heredoc, so they could not
be re-run and protected nothing. They are now in the file behind --selftest,
built from HTTP bodies captured verbatim rather than written from memory.

Cap and rate wording is now checked before the bankruptcy markers. OpenCode Go
is a subscription with $12/5h, $30/week and $60/month ceilings; hitting one
means wait, not pay, and a cap message that also says 'billing' would otherwise
be reported as an empty account. Verified safe: none of the three real
bankruptcy bodies contain any cap or rate wording.

The Go cap wording itself is unverified and labelled as such in the source
rather than presented as observed.

Gate proven able to reject: removing one marker fails 2/9 and exits 1.
2026-08-13 10:13:35 -04:00
Fimeg
6b9a415aac tools: probe provider balance before a role dies
Three providers ran out of credit in two days (zai, deepseek, opencode) and
in every case the first signal was a failed subconscious pass. Balance
exhaustion is invisible until something dies; absence of complaint reads as
health.

Sends a real one-token completion rather than checking reachability -- GET
/models on opencode returns 200 with a valid key and a bankrupt workspace.
Classifies on wording, not status code: the same condition is 429 on zai,
402 on deepseek, 401 on opencode.

Exits non-zero only when a provider backing a live role (subconscious,
reflection, archivist) cannot answer, or a role names a model absent from
[models.*].

curl rather than urllib: opencode is behind Cloudflare, which answers
Python-urllib's user-agent with 403 code 1010 -- indistinguishable from a
rejected key.
2026-08-13 09:59:16 -04:00
Fimeg
837e024217 the subconscious can come back from an overflow
Once her thread passes the model's ceiling she cannot recover on her
own: every pass is refused at the provider before a single tool round
runs, so she can never reach for memory compact herself. The gauge is
no use to someone already over. She sat dead in that state from
2026-08-11 16:38 until this was found.

Doctrine is that the engine reports pressure and she decides, and that
still holds -- this is not scheduled trimming. It fires only after a
hard overflow, uses her own default strategy (sliding_reflect, which
carries her threads across the cut rather than dropping them blind),
and surfaces what happened in her own voice so the compaction is
something she knows about rather than something done to her quietly.

is_context_overflow matches on wording, not status -- providers
disagree on the code (400 vs 413) and agree on the words. Kept narrow:
a false positive compacts a thread that did not need it, so this
week's 402, 529, max_tokens and connection errors are all asserted
NOT to match. The positive case is the exact body DeepSeek returned at
09:12 today, pasted rather than paraphrased.
2026-08-13 09:42:05 -04:00
Fimeg
877cad006d memory status reports context pressure
Both the primary's and the subconscious's system prompts say 'memory
status shows my context pressure and number of messages'. It showed
neither -- git state only.

This matters most for the subconscious. Her prompt tells her plainly
that no one feels the gauge for her (correct, by design: the engine
warns, it never trims), and points her at memory status to read it
herself. With status blind she had no gauge at all, from either
direction: pressure_for is only ever computed for the primary. She
grew to 1.75M tokens against a 1M ceiling and died at the provider.

- PressureSnapshot + CompactionEngine::pressure
- get_context_limit follows the model, not the primary -- the
  subconscious runs a different model, so her ceiling differs
- tier() mirrors the engine's 0.80/0.90/0.95 marks so the two gauges
  cannot disagree about full
- None when there is no session: absence, not zero

Also corrects the compact help text, which named
primary=sliding_window/subconscious=sliding_window against
config.rs's actual cull/sliding_reflect.
2026-08-13 09:35:01 -04:00
Fimeg
aa6cd4e219 patches: the queue is empty, and the README said otherwise for a day
0007 and 0010 landed in 6f12fce and their files were removed in that same
commit, but this list went on describing them as pending. 0001 and 0006 have
now landed too. A queue file is a claim with a shelf life; so is the README
that indexes it.

The durable notes from the removed entries are kept under Recently closed --
the halt wording is coupled to migraine_text() in src/server/turn.rs, and the
context pill and the tokens endpoint measure different things and must not be
shipped side by side unlabelled.
2026-08-13 08:33:16 -04:00
Fimeg
6edebaeca5 stepup: authenticate through PAM, not a dead pkcheck branch
StepUpAuth ran `souveraine-pam-auth`, else `pkcheck --action-id
org.souveraine.stepup`. Neither exists: no such binary was ever written
and no polkit action was ever shipped, so both branches failed and every
grant request was silently denied. The header also described passing a
password through SOUVERAINE_STEPUP_PASSWORD, which the code never set.

Now a PamContext against the system souveraine-stepup service, shipped by
cc541d1. The prompt is not owned here: pamMessage raises
promptRequired(family, message, secret) and a surface answers with
respond(). An empty answer is legitimate, not a cancel -- the FPC factor
prompts "Touch and hold" and consumes its ticket on a blank response.

Deliberately unchanged: grant model, TTL, revocation and break-glass all
still live in the shell. Moving them to sessiond is a separate pass.

Authorship: this patch is Rook's work, staged in surfaces/quickshell/
patches/ on 2026-08-12. The patch file carried my identity in its From
header but I did not write it; recording that here rather than wearing it
silently.

Verified: /usr/lib/qt6/bin/qmllint exit 0 against the composed tree, gate
proven able to reject (exit 255 on a deliberately broken control).
Untested: no shell has loaded this and no surface calls respond() yet.
2026-08-13 08:24:07 -04:00
souveraine
ec87e6a9dc shell: offer resume instead of forcing it 2026-08-13 08:21:39 -04:00
Fimeg
b37b1d396a crash reporter: expected absence is silent, a real read failure is not 2026-08-13 06:54:23 -04:00
Fimeg
e595ef6d72 subconscious panel: colLayer1Active, not the key that does not exist 2026-08-13 06:50:01 -04:00
Fimeg
764a4b8e35 pressure: calculate_pressure counts every block kind
Was Text-only, blind to tool_use/tool_result/reasoning — ~74% of a real
thread. This is the number behind the 80/90/95 tier warnings and the
archivist's emergency trigger, so both fired late or not at all.

countable_text() (7530d6d) is exhaustive. turn.rs::bifrost_pressure is
still a second counter and still blind to tool_calls by shape; collapsing
the two is not done.
2026-08-12 21:11:48 -04:00
Fimeg
525157250a shell: context pill tooltip states the real tiers, not the archivist
The tooltip claimed 'Archivist begins compacting around 70%'. The
archivist does not compact. It reads journal/ and writes
system/synthesized/ - a different substrate from the conversation
entirely (core/archivist/mod.rs:5).

The real advisory tiers are 80/90/95 in ConsciousnessEngine::
on_response, and they never force compaction. Icon thresholds moved
from 85/70 to 90/80 to match.
2026-08-12 20:30:34 -04:00
Fimeg
57e53adac3 patches: retire the queue discipline; the shell was never in the agent's cgroup 2026-08-12 20:12:12 -04:00
Fimeg
6f12fceea9 shell: apply 0007, 0010, 0011 — context occupancy, halt register, /new
0007 was fixing a live error: the server has carried named ContextPressure
fields since r463, so event.tokens was undefined and the pill assignment
failed on every turn. Applied and verified against the running shell.

0010 renders a halt in her own register rather than the tool's name.
0011 renames the reset command to /new, which is what clearMessages() does.
2026-08-12 19:50:32 -04:00
Fimeg
0733bfaff7 patches: queue 0011 — the reset command is /new 2026-08-12 19:37:38 -04:00
Fimeg
e50ddee567 patches: queue 0010 — halt rendered in her own register 2026-08-12 18:55:20 -04:00
Fimeg
29f847544c halt: the migraine reaches the primary, not just the surface
migraine_text built a felt sentence in her own register, pushed it onto
the Bifrost message vec, and then `break` exited the loop — no further
completion call, vec dropped. Correct wording, correct voice, delivered
nowhere.

The old comment claimed the migraine "rode the event channel" and the
ledger held the reasoning. Neither reaches her: the event channel goes
outward to the human's surface, and the mid-turn peek is ephemeral by
design so no ledger entry exists. The one reader the sentence was
written for was the only one who never received it. She stopped without
learning why and could not resume without the human relaying it back.

The raised-hand path already had this right — it commits its marker so
she reads it next turn and can acknowledge, wrap up, or ask for more
time. The halt now does the same, committing the felt sentence itself
rather than a harness marker naming a tool.

Removing the `*[subconscious HALT]*` marker fixed a real resume
corruption bug, but removing the signal with it was an overcorrection:
the defect was the marker's shape, not the existence of a signal.

No Token event accompanies the commit — the surface already received
SubconsciousHalt and would otherwise render the same moment twice.
2026-08-12 18:50:55 -04:00
Fimeg
7530d6d1c8 context accounting: one exhaustive counter, no wildcard arm
Three counters measured the same conversation and disagreed 5.6x:
the /tokens endpoint said 315,487, the throttle ~200,000, and the
compaction engine 56,361. Same TokenCounter, three different notions
of what a block weighs, no labels anywhere.

count_messages carried `_ => None`, so it saw Text and nothing else —
18% of a real conversation. Microcompact exists to blur old tool
results and could not measure a single byte of the block kind it acts
on. Worse, reclaimed = before - after against a blind counter is
structurally always zero, so the "nothing to set down" branch always
fired and the felt-state message written for a successful run was
unreachable code. It could not distinguish "found nothing" from
"worked perfectly and cannot say so".

ContentBlock::countable_text is now the single authority, with a
deliberately exhaustive match and no wildcard: a new block kind must
fail to compile rather than quietly weigh nothing. The endpoint's
inline copy is replaced by a call to it, since an inline copy is how
the two drifted apart in the first place.

Image still counts its base64 payload, which overstates real token
cost. Preserved deliberately — re-weighting images is a model-specific
estimate and a separate decision; doing both at once would make
neither reviewable.
2026-08-12 18:50:43 -04:00
Fimeg
b9f404cbaf patches: 0007 unblocked — the running server carries f266136 2026-08-12 17:39:19 -04:00
Fimeg
715080bbfe claude: read the credential file instead of stat-comparing it
Tokens are fixed-length, so a rotation changes neither size nor same-tick
mtime — the stamp missed real swaps and flaked CI.
2026-08-12 16:58:25 -04:00
Fimeg
16587b74a3 claude: hold every login at once, move when a window is spent
`credential_files` holds them all; a 429 with retry-after over a minute takes
the next login and retries at once. Sticky — a swap abandons the cached prefix,
so short bursts are waited out where the cache lives.
2026-08-12 16:45:52 -04:00
Fimeg
f4837c52a8 claude: the credential file is the authority, not our startup copy
Logging out and into a second account rewrites the file with a token that is
neither expired nor spent, so no refresh fires and nothing re-reads the disk —
the provider keeps answering as the account that was left. Measured 2026-08-12:
server up 10:55, file rewritten 15:20, 429s from 15:22 while the on-disk token
returned 200 to the same request.

Stat the file before each call and take it when it has moved. Write-back is now
a compare-and-swap on the refresh token it started from, so our rotation can
never overwrite a login made in between.
2026-08-12 16:15:23 -04:00