Commit Graph

11 Commits

Author SHA1 Message Date
claude 5d2fd91c06 a Hexis 401 says the token was refused, not that Hexis is down (V-587)
The vendored Hexis client is a separate implementation and returns a plain
fmt.Errorf for every status at or above 400, so errors.As for *ecosystemError
never matched, Unauthorized() was never consulted, and ecosystemGap always fell
through to the outage line. A wrong token sent him to inspect a healthy service.

hexisError classifies at Maven's boundary, since the client is vendored from
another repo and a local edit there is lost on the next re-vendor. The status
text is the only signal that survives the wrapping, so that is what it reads;
anything unrecognised stays at status 0, which is what Unreachable() means. The
correct fix is a typed error upstream carrying the code, and Maven cannot land
it unilaterally.

execHexis is the second site and it did not call ecosystemGap at all. It now
does, but only for a failure that belongs to the service. An execution that Hexis
accepted and that then failed keeps the command-level line: that is the command
failing, not Hexis degrading, and calling it an outage would be the same defect
pointed the other way. Authorization is unchanged: a 401 is still a refusal, it
is not retried and nothing proceeds on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 02:48:00 +04:00
claude cfbef45feb ecosystem clients share one JSON transport (V-575)
Nexus and Praxis were the same HTTP client written twice: build the
request, stamp the headers, send it, check the status, decode the body,
and wrap each failure in an ecosystemError. They differ only in the
service name and the version header, so both now embed ecosystemHTTP and
call getJSON or postJSON.

setEcosystemHeaders took a context beside the request it was stamping.
It now reads req.Context(), which is the same value, so a caller cannot
pass a context the request never carried.

No behaviour change. Same headers, same statuses, same error types. One
error changed shape: a malformed base URL used to come back from Nexus
resolve as a plain wrapped error and is now an ecosystemError like every
other failure on that path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 01:37:20 +04:00
claude 1524991adc praxis: an empty attention list is not always an all-clear (V-540)
ECOSYSTEM-SPEC §2.6 requires list_attention to distinguish "nothing needs
attention" from "I cannot currently tell", and to say so when a source is
failed or stale. Maven said the first one unconditionally: ListAttention
decoded into []map[string]any, the word degraded appeared nowhere, and an empty
list answered "ничего не требует внимания". A Praxis with every source dead
read as calm.

Two halves, because the spec's mechanism does not exist server-side yet. The
deployed Praxis answers /api/v1/tools/attention with a bare array and no
envelope, so praxisAttention now decodes either shape and believes a degraded
array when one arrives. Until one does, an empty list triggers one read of
/api/v1/sources, and anything that is not reporting health "ok" is named
instead of the all-clear. Zero sources is the same answer: a Praxis that polls
nothing knows nothing, which is the state of this box today.

A sources read that fails is deliberately not a hedge. The attention call
succeeded, and not being able to ask about health is not evidence of a fault.

Both hedges also cover the entity-scoped digest, where a per-entity all-clear
is the more convincing of the two. New keys attention_degraded and
attention_no_sources, in acts_ru_v1.json and the floor. The fake Praxis serves
one healthy source by default, so the existing attention tests still assert an
all-clear on purpose rather than by omission.
2026-08-05 12:07:07 +04:00
kami 0db31d21b9 hexis: re-vendor the client so a configured token is actually sent
The vendored copy of github.com/kami/hexis predated Client.WithToken:
no token field, no setter, no header hook, and an unexported httpClient,
so there was no way to attach auth from outside the package. wireEcosystem
handled that by refusing to wire Hexis at all when a token was configured,
which was the honest reading of the code but left the deployment silently
without its executing service.

go.mod already replaces the module with /home/kami/apps/hexis, and that
source has had WithToken and the Bearer header for a while. Only the
checked-in vendor/ copy was stale. Refreshed it (client.go plus the new
capability.go) and wired Hexis like Nexus and Praxis.

Two tests cover the outcome the refusal was standing in for: a configured
token reaches the wire as Authorization, and no token still wires unauthed,
because Hexis without auth is a valid deployment on a trusted box.

Also corrected the discoverCapabilities comment. It claimed the client
stamped the correlation header on Execute only; do() stamps it on every
request, and did before the re-vendor too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TrVSBKe3RFDF4fGYKWYQnX
2026-08-01 16:29:56 +04:00
kami f432eb0b25 ecosystem: let the client layer read correlation IDs, never mint them
setEcosystemHeaders minted a fresh correlation ID whenever the context
carried none. Every hop of one action therefore got a different ID, so
a trace could not be followed from resolve to attention to execute.
The header layer now only reads what the caller assigned. Praxis
requests are typed the same way Nexus ones already were, so a 401 from
Praxis reports as unauthorized instead of a generic failure, and a
"resolved" response with no entity is an error rather than a silent
empty result. Hexis refuses to wire at all when a token is configured,
because the vendored client cannot send one and starting anyway would
send unauthenticated calls under the belief they were authenticated.

Found in review of #84.
2026-08-01 14:14:19 +04:00
kami 927e46bca3 Version, authenticate and fully trace ecosystem calls (#273)
Every Nexus and Praxis request now carries the contract version, an
X-Requested-By identifying Maven, a correlation ID (generated per request
when the call is not part of a traced action), and a bearer token when
one is configured. Nexus/Praxis/Hexis config blocks grew an optional
token field, env-expandable so the secret stays out of the committed
config; the vendored hexis client predates bearer auth, so a configured
Hexis token logs a loud warning instead of pretending to authenticate.

Client failures are now a typed *ecosystemError carrying service,
operation and HTTP status, classifying unauthorized, contract-mismatch
and unreachable without matching on message text.

Trace records are written for resolution, discovery, confirmation and
execution — on failure as well as success — with status, duration,
correlation and causation ids, HTTP status and failure class, and the
utterance redacted to its length. Traces were never actually persisted
before: both trace writers used fact kind "system", which the store's
CHECK constraint rejects, and the error was discarded.
2026-08-01 06:57:52 +04:00
kami 0579ef94f7 Add entity-scoped attention query to Praxis client
Complements Praxis's new entity_id filter on /tools/attention: lets
callers that already hold a resolved Nexus entity_id (e.g. after
resolveEntityReference) ask what needs attention for that entity
directly, instead of filtering the unscoped list client-side.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ghELqYhZNLub2TXGMazqA
2026-07-20 11:22:35 +04:00
kami b743860fdd Add version/correlation headers to Nexus and Praxis ecosystem clients
mavend's hand-rolled Nexus and Praxis HTTP clients sent bare requests
with no version or correlation headers, unlike the Hexis client.
Added a shared context-based correlation ID mechanism and version
headers (X-Nexus-Version, X-Praxis-Version) across all Nexus/Praxis
call sites, and threaded the correlation ID already generated in
executeCapability through to the Nexus/Praxis calls in the same
request chain. Synced vendor/ copy of hexis/pkg/client after its
WithCorrelationID addition.

Part of Vikunja #273.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ghELqYhZNLub2TXGMazqA
2026-07-20 11:12:01 +04:00
kami 398997f5c1 Add typed Praxis lifecycle client + acknowledge/resolve/ignore/pin/surface verbs
Maven previously only called Praxis list_attention/list_changes and read
untyped maps. Adds a typed praxisItem struct plus GetItem/Search/Surface/
Acknowledge/Resolve/Ignore/Pin client methods, and routes new dialogue
verbs (RU + EN aliases) through handlePraxisAct to each.

Also fixes a lifecycle-invariant bug: reading attention items aloud now
calls Surface, not nothing — per ECOSYSTEM-SPEC.md §2.3 surfaced !=
acknowledged, and previously the digest path didn't record surfacing at
all, so 'Maven mentioned it' left no trace distinguishable from 'never
came up'.

Vikunja #271.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ghELqYhZNLub2TXGMazqA
2026-07-20 03:05:41 +04:00
kami d9fa4d6613 Fail closed on Nexus/Hexis dependency errors, accept flat resolve shape
Vikunja #268 (P0): handleHexisAct swallowed genuine Nexus resolve errors
and Hexis capability-discovery errors into "" or an empty capability list,
which fell through to the local system command executor — a dependency
outage silently looked identical to "not an ecosystem entity" or "no
capabilities registered", violating the spec's degrade-independently /
never-silent-all-clear invariant.

- resolveEntityReference's error is now distinguished from a legitimate
  not_found: only the latter falls through.
- discoverCapabilities now returns (caps, err) instead of collapsing a
  Hexis failure into an empty slice; a real error stops the action with
  a degraded-mode spoken reply instead of reaching h.tools.Exec.
- nexusResolveResult gains a custom UnmarshalJSON to accept the flat
  entity_id/entity_type/display_name shape from ECOSYSTEM-SPEC.md §1.5
  (Nexus now emits both shapes; Maven now reads both).
- Added regression tests: flat-shape resolve, Nexus error fails closed,
  Hexis error fails closed, not_found still falls through to local exec.
2026-07-20 01:08:11 +04:00
kami 6c92f85d10 feat(ecosystem): compliant Praxis/Hexis integration + vendored build
Bring the Nexus/Praxis/Hexis integration in line with
MAVEN_ECOSYSTEM_ARCHITECTURE.md:

- Praxis over HTTP: drop the in-process praxis.db open (praxisstore/
  praxistools) and call praxisd's /api/v1/tools/* API via a new praxisClient.
  Honors the "no component reads another's DB" invariant (AC#12).
  PraxisConfig.DBPath -> URL.
- Hexis confirmation gate: mutating capabilities (ReadOnly=false) now park a
  bound pendingHexis confirmation and require a spoken "да" before executing;
  read-only run immediately (AC#7, no auto attention->action).
- Capability safety: >1 verb match is ambiguous -> ask instead of firing the
  first; ambiguous Nexus resolution asks for clarification (AC#2).
- Correlation IDs on Hexis execute, recorded in the cross-service trace.
- Bug: importance arrives as JSON float64 over HTTP, not int.
- Tests: confirm-gate, decline, read-only, and ambiguity paths.

Build: vendor/ bakes in the hexis client (replace-directed at a sibling repo
outside the Docker context); Dockerfile builds from vendor and no longer
`go mod download`s the unreachable replace paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 20:24:33 +04:00