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:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user