feat(mavweb): /ecosystem page consuming Nexus/Praxis/Hexis + shell fixes

Add a read-only /ecosystem page that consumes the sibling services'
JSON APIs (Nexus entities, Praxis attention, Hexis capabilities),
fetched concurrently with honest per-panel error states. Siblings stay
headless — mavweb is their human surface (arch §16). Wired via mavweb
-nexus/-praxis/-hexis flags; mavweb joins the ecosystem compose network.

Fix mobile horizontal overflow across all pages: .content is a flex
child with default min-width:auto, so it refused to shrink below the
tables' intrinsic width. min-width:0 lets wide tables pan inside .scroll
instead of dragging the page sideways. Verified via CDP geometry check
(scrollWidth === clientWidth at 430px).

Also includes in-progress Ethos UI redesign, ecosystem deploy compose,
and planning docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kami
2026-07-19 22:04:23 +04:00
parent 6c92f85d10
commit 5fe8f228c1
43 changed files with 1782 additions and 668 deletions
+62
View File
@@ -71,6 +71,7 @@ func TestRequirement_Table(t *testing.T) {
ipc.MethodPresence, ipc.MethodRecentOutcomes,
ipc.MethodCreateReminder, ipc.MethodMarkReminder,
ipc.MethodRecordNudge, ipc.MethodResolveNudge,
ipc.MethodChat,
}
for _, m := range reads {
if got := Requirement(m); got != AuthRead {
@@ -169,6 +170,7 @@ func TestGate_FloorEnrollment_PreservesPreAuth(t *testing.T) {
for _, m := range []ipc.Method{
ipc.MethodPresence, ipc.MethodSince, ipc.MethodRecentOutcomes,
ipc.MethodCreateReminder, ipc.MethodRecordNudge,
ipc.MethodChat,
} {
if err := g.Check(ctx, m, nil); err != nil {
t.Errorf("floor gate Check(%s) = %v; want nil (pre-auth preserved)", m, err)
@@ -310,6 +312,49 @@ func TestGate_IpcServer_CheckWiredThroughSocket(t *testing.T) {
if fake.writes != 0 {
t.Errorf("auth refused but CoreAPI was called %d time(s); refused calls must not reach CoreAPI", fake.writes)
}
_, err = cli.Chat(context.Background(), "привет")
if !errors.Is(err, ipc.ErrForbidden) {
t.Errorf("wire: chat from unenrolled uid = %v; want ipc.ErrForbidden", err)
}
if fake.chats != 0 {
t.Errorf("auth refused chat but CoreAPI.Chat was called %d time(s)", fake.chats)
}
}
func TestGate_IpcServer_ChatAllowedForEnrolledCaller(t *testing.T) {
gate := &Gate{Enrollment: NewFloorEnrollment()}
fake := &recordingAPI{}
sock := filepath.Join(t.TempDir(), "maven.sock")
srv, err := ipc.Listen(sock, fake)
if err != nil {
t.Fatalf("listen: %v", err)
}
srv.Check = gate.Check
done := make(chan struct{})
go func() {
_ = srv.Serve()
close(done)
}()
t.Cleanup(func() {
_ = srv.Close()
<-done
})
cli, err := ipc.Dial(sock)
if err != nil {
t.Fatalf("dial: %v", err)
}
t.Cleanup(func() { _ = cli.Close() })
reply, err := cli.Chat(context.Background(), "привет")
if err != nil {
t.Fatalf("Chat: %v", err)
}
if reply != "echo: привет" {
t.Fatalf("Chat reply = %q; want %q", reply, "echo: привет")
}
if fake.chats != 1 {
t.Fatalf("CoreAPI.Chat calls = %d; want 1", fake.chats)
}
}
// recordingAPI — a no-op CoreAPI that counts WriteFact invocations; the auth
@@ -317,6 +362,7 @@ func TestGate_IpcServer_CheckWiredThroughSocket(t *testing.T) {
// fake's counts and we fail.
type recordingAPI struct {
writes int
chats int
}
func (r *recordingAPI) WriteFact(_ context.Context, _ ipc.WriteFactReq) (int64, error) {
@@ -339,6 +385,9 @@ func (r *recordingAPI) CreateReminder(_ context.Context, _ time.Time, _, _ strin
return 1, nil
}
func (r *recordingAPI) MarkReminder(_ context.Context, _ int64, _ string) error { return nil }
func (r *recordingAPI) ListReminders(_ context.Context, _ int) ([]ipc.Reminder, error) {
return nil, nil
}
func (r *recordingAPI) TickTrace(_ context.Context) (ipc.TickTrace, error) {
return ipc.TickTrace{}, nil
}
@@ -378,6 +427,9 @@ func (r *recordingAPI) EnableTool(_ context.Context, _ string, _ []string, _ boo
func (r *recordingAPI) DisableTool(_ context.Context, _ string) error {
return nil
}
func (r *recordingAPI) DeleteTool(_ context.Context, _ string) error {
return nil
}
func (r *recordingAPI) LookupTool(_ context.Context, _ string) (ipc.Tool, error) {
return ipc.Tool{}, ipc.ErrToolNotFound
}
@@ -388,6 +440,16 @@ func (r *recordingAPI) ListTools(_ context.Context, _ string) ([]ipc.Tool, error
func (r *recordingAPI) RevertFact(_ context.Context, _ string) (int64, error) {
return 0, nil
}
func (r *recordingAPI) ListProposedRoutines(_ context.Context) ([]ipc.ProposedRoutine, error) {
return nil, nil
}
func (r *recordingAPI) DismissProposedRoutine(_ context.Context, _ int64) error {
return nil
}
func (r *recordingAPI) Chat(_ context.Context, text string) (string, error) {
r.chats++
return "echo: " + text, nil
}
// mustWriteFactParams — minimal WriteFactReq JSON with only the source field,
// matching what ipc.dispatch hands to Server.Check (the raw params frame).