Commit Graph
72 Commits
Author SHA1 Message Date
vhaudiquet 162cb21d7c feat(claw): move the identity_set tool spec into a TOOL.yaml manifest
identity_set loads its spec from an embedded manifest under
crates/rhema-claw/tools; the Rust side keeps only the invoke half. It
stays replay-unsafe - a resumed run must not re-write the identity.
2026-10-20 21:44:04 +02:00
vhaudiquet 2747018b8f feat(claw): move the identity store tool specs into TOOL.yaml manifests
identity_save, identity_list, and identity_forget load their specs
from embedded manifests under crates/rhema-claw/tools; the Rust side
keeps only the invoke half. identity_list declares replay_safety:
safe - the store answers with its own redacted records - while save
and forget stay unsafe: a resumed run must not re-store an account or
drop one twice. identity_exec keeps its spec inline; it is a
ToolResolver, the exec seam itself, not a registry tool.
2026-10-20 21:19:54 +02:00
vhaudiquet da483daadd fix(android): let the schedule carry a failed report, not backoff
A failed post returns retry(), which moves the next run onto
WorkManager's exponential backoff — 30s doubling, capped at five hours.
A couple of failures while the claw is unreachable is enough to leave
the fleet without a report for most of a day, which reads exactly like
the phone being gone.

The snapshot is only saved when every post landed, so the next
scheduled run re-derives the whole transition set and posts it again.
The fifteen-minute schedule is the retry; return success and keep it.
2026-10-20 07:40:43 +02:00
vhaudiquet 385788c935 fix(android): refresh the phone's own row when it reports
The fleet card reads last-seen from the claw, and only a landed report
moves it. Periodic work is deferred while the device idles — Doze
stretches the fifteen-minute floor to hours — so a phone sitting in the
human's hand could show itself as "last seen 3h ago" with nothing wrong
on the claw.

Kick a status run on resume, the same way the commands worker already
is, and reload the fleet whenever a run lands. The landed-run signal is
a new clock in StatusRepository, written in the same edit that saves the
snapshot (which only happens once every post of the run succeeded), so
the card reloads on a report that actually reached the claw.
2026-10-20 07:40:43 +02:00
vhaudiquet 4a91635730 feat(claw): move the notify tool spec into a TOOL.yaml manifest
notify loads its spec from an embedded manifest under
crates/rhema-claw/tools; the Rust side keeps only the invoke half.
The drift test pins the shipped set: the manifest loads against its
builtin, the registry serves the manifest spec, and notify stays
replay-unsafe - a resumed run must not re-send a notification.
2026-10-20 19:30:33 +02:00
vhaudiquet c1779c2423 feat(claw): move the watch tool specs into TOOL.yaml manifests
watch_repo, unwatch_repo, and list_watches load their specs from
embedded manifests under crates/rhema-claw/tools; the Rust side keeps
only the invoke half. The manifests stay name-sorted, which is the
order the registry serves and the roster shows. list_watches declares
replay_safety: safe - re-reading the table on a resumed run is free -
and the drift tests pin the shipped set: every manifest loads against
its builtin, every builtin is named by a manifest, and replay safety
reaches the resolver from the file.
2026-10-20 19:06:33 +02:00
vhaudiquet 8c14ea7749 feat(claw): move the bootstrap tool spec into a TOOL.yaml manifest
bootstrap_save now loads its spec from an embedded manifest, one
folder under crates/rhema-claw/tools; the Rust side keeps only the
invoke half, behind the run.builtin id. Drift tests follow in the
registry's own suite shape once the family grows; for now the
registry asserts the manifest loads, and the render tests exercise
the manifest-served spec through the roster.
2026-10-20 18:41:48 +02:00
vhaudiquet bcadeb193c fix(claw): follow the onboarding section rename
The section-header restructure renamed the onboarding offer from
"## Setup" to "## Onboarding" in ONBOARDING.md; three prompt
assertions still looked for the old heading and failed. They follow
the file now.
2026-10-20 18:17:09 +02:00
vhaudiquet 4d5dc3f677 fix(deploy): copy the rhema-transcript manifest into the claw build
The crate was missing from the per-member manifest COPY list, so the
workspace failed to resolve in the registry-cache layer: rhema-claw
path-depends on rhema-transcript, whose Cargo.toml never landed in
the context.
2026-10-20 08:25:20 +02:00
vhaudiquet c536952ba2 refactor(claw): structure the system prompt with section headers
The Soul, User, Environment, Tools, and Memory blocks get headings,
and the watch/notify/schedule prose moves to the TOOL.yaml manifests.
2026-10-20 08:25:12 +02:00
vhaudiquet 4692c8cddc feat(claw): move the task tool specs into TOOL.yaml manifests
schedule_task, list_tasks, and cancel_task load their specs from
embedded manifests, one folder per tool under crates/rhema-claw/tools;
the Rust side keeps only the invoke half. list_tasks declares
replay_safety: safe, so a resumed run may re-execute it. Drift tests
pin the shipped set: every manifest loads against its builtin, every
builtin is named by a manifest, and the registry serves the manifest
specs verbatim.
2026-10-20 06:42:59 +02:00
vhaudiquet 60f3b5eb9b feat(agent): the transcript on by default
First landed flag-gated: the mechanics were unproven at coding-traffic
scale and the view changes what the model sees past the budget. The
claw then ran every surface on it and nothing fell over, the only user
is the deployment's own, and the sessions most likely to hit the
budget were the ones least likely to find a flag. Below the budget the
decorator is a pass-through, so the default's only always-on cost is
the zoom spec in the prompt.

transcript: None is the off switch; rhema-code spells it
--no-transcript, the claw's transcript: false now writes the resolved
value so false still means off. Saved sessions view fine: the log
stays complete and the view derives per request. The system preview
names zoom too, so what is previewed is what turns send.
2026-10-20 06:01:24 +02:00
vhaudiquet 50b8feba07 feat(claw): run the bounded request view by default
The claw's sessions never end by design — one interactive chat, forge
threads alive as long as their pull requests, coding jobs stashed
across runs — so off-by-default would mean the view is never on where
it matters most. On: chat turns wrap the provider seam with the view
and join zoom to the resolver chain, the mirror living on the handler
so it survives the per-dispatch agent rebuild; forge threads and
coding jobs set the coding agent's transcript field. The config turns
it off with transcript: false.
2026-10-20 06:01:24 +02:00
vhaudiquet b70a56c325 refactor(calendar): move the reminder source beside the feeds
CalendarWatch, the client resolution, and the slot policy were all
feed domain living in the claw's config and source tree; only the
extension adapter was the claw's. The reminder source now ships in
the integration as the default answer to "what does a start time
mean": one slot per lead time, calendar:<name> keys, fed to the
consumer's dispatcher. The crate doc drops its no-policy claim to
match, and the claw's resident prefixes take the calendar prefix
from the integration so the spelling cannot drift.
2026-10-20 05:53:43 +02:00
vhaudiquet fc0cbbcb6b refactor(forge): classify account notifications beside the vocabulary
The triage mapped the forges' notification reasons onto the claw's
verdicts, but it co-evolves with the vocabulary, not with the claw:
a reason that enters it (Gitea's collapsed shape) needs a verdict in
the same change, and its tests pin the mapping against the
vocabulary's own types. It now lives in the integration crate as the
default classification; a consumer may reclassify on top. The claw
imports it like the rest of the forge seam.
2026-10-20 05:49:14 +02:00
vhaudiquet 5fdd17b901 refactor(claw): assemble the daemon from an extension set
The daemon named every feature twice: once in the interactive
handler's sixteen-argument constructor, once across the source list,
the tick, and the boot pass. Both assemblies are now loops over one
ClawExtension trait, so an integration is a struct plus a line in
the standard set. The chain order, the detect order, and the boot
order are unchanged; the surface suite pins them. Each extension
struct is the boundary a later move into the integration crates
would lift wholesale.
2026-10-20 05:39:57 +02:00
vhaudiquet 8ab513d49f fix(claw): list the subagents manifest in the claw image
rhema-code grew a path dependency on the new rhema-subagents crate, but
the image's manifest-only staging layer never copied its Cargo.toml, so
workspace resolution died before cargo fetch could run.
2026-10-19 23:14:31 +02:00
vhaudiquet cb30ba9469 style(claw): rustfmt the watch-credential change
The credential-resolution commit landed with two violations this
editor's format-on-save caught — an unsorted import in the handler and
wrapping in the daemon and source — reformatted here so the tree is
cargo-fmt clean.
2026-10-19 20:14:07 +02:00
vhaudiquet 1cdc2e8648 feat(claw): derive a blank watch credential from the matching account
A repo watch row carries no account link: its only credentials were the
token stored on the row and the forge's environment variable, so a
watch added without either polled — and replied — anonymously. Against
a private repository that is a 404 on every pass, and nothing ever
lands: no dispatch, no reply, only a per-pass warning in the logs.

The watch credential now resolves in three tiers: the row's token,
then the env var, then the token-kind account identity of the same
forge — instance-scoped on a self-hosted forge (the gitea watch of
git.example borrows the gitea account saved with that base URL). A
password-kind account is skipped, never borrowed. ForgeSource and the
ClawHandler resolve through the same chain and key their client caches
on the resolved value, so the reply authenticates as the identity the
poll did — and a rotation or a later-saved account is a new client on
the next pass, no restart.
2026-10-19 19:44:39 +02:00
vhaudiquet 7c707e7a09 feat(claw): list account watches beside repo watches on the surface
A saved account whose forge notifications are not actually followed was
invisible: the save's watch flag defaults to false on the tool and the
HTTP route, and GET /v1/watches listed only the repo-watch table. The
first question a silent forge thread raises — is my account even
subscribed? — was unanswerable from outside. The route now answers it.
2026-10-19 19:40:59 +02:00
vhaudiquet 04549a7df5 fix(claw): dispatch reason-less forge notifications to the thread's agent
Gitea's notifications feed names no per-row reason, so the adapter
collapses every row under one fixed NotificationReason::Other spelling
— and the claw's triage mapped every Other to Surface: the notification
reached the device queue and never a dispatch. A comment on a thread
the account authored could therefore never wake the agent on a
reason-less forge, whatever the watches said.

The adapter now publishes that spelling as gitea::COLLAPSED_REASON —
it is the seam's contract, not a detail — and the triage treats it as
the known shape it is: a notification on a thread of the account's own
dispatches the implementer. A spelling no adapter owns still surfaces;
an unknown shape remains the user's to judge, never the agent's to
guess at.
2026-10-19 19:40:44 +02:00
vhaudiquet 81da26b955 feat(rhema-code): add the colors setting with four palettes
The stored `colors` key names a palette — rhema (the default,
Jadeite), aquamarine, black-and-white, or dracula — and the
Appearance page of /settings cycles it live, like the theme and
icon toggles beside it. Each palette carries a dark and a light
appearance under the existing `theme` key; dracula's light side is
derived, the official palette ships only dark.

The appearance decision moves off token comparison: Theme now
answers is_dark() from the ground/text luminance, so palettes
beyond the original two get the right per-profile fallback tones.
2026-10-17 04:28:31 +08:00