diff --git a/modules/claude-code/cmd/claude-code/catalogfeed.go b/modules/claude-code/cmd/claude-code/catalogfeed.go new file mode 100644 index 000000000..6191ad672 --- /dev/null +++ b/modules/claude-code/cmd/claude-code/catalogfeed.go @@ -0,0 +1,60 @@ +package main + +// claude-code's reading of which modules exist and of each placed module's manifest (design 47 G §196, §594, §614): the +// catalog half, taken through meshcatalogclient's follower of the watched branches on the part's one views reader. A +// module's manifest is the one its catalog graph entry carries whole (§443), never the indexer's state `modules` or +// `manifests`. + +import ( + "fmt" + "strings" + + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" +) + +// CatalogHalf is what this module reads of the catalog half. +type CatalogHalf interface { + // Entry is a module's entry at the trunk; an error while a watched branch has no first view yet, never "no module". + Entry(module string) (catalog.CatalogEntry, bool, error) + // Ready is nil once every watched branch has its first view (none-yet counting as one), else says what it waits for. + Ready() error +} + +// branchesHalf is the CatalogHalf over the follower of the watched branches. +type branchesHalf struct{ b *meshcatalogclient.Branches } + +func (h branchesHalf) Ready() error { + followed := h.b.Followed() + if len(followed) == 0 { + return fmt.Errorf("the catalog half names no watched branch yet (the seat mesh-catalog's registrations are asked until they answer)") + } + var waiting []string + for _, branch := range followed { + if c, ok := h.b.Catalog(branch); ok { + select { + case <-c.Ready(): + default: + waiting = append(waiting, branch) + } + } + } + if len(waiting) > 0 { + return fmt.Errorf("the catalog half has no first view of %s yet", strings.Join(waiting, ", ")) + } + return nil +} + +func (h branchesHalf) Entry(module string) (catalog.CatalogEntry, bool, error) { + if err := h.Ready(); err != nil { + return catalog.CatalogEntry{}, false, err + } + for _, branch := range h.b.Followed() { // the branch followed first names a module more than one hold + if c, ok := h.b.Catalog(branch); ok { + if e, ok := c.Entry(module); ok { + return e, true, nil + } + } + } + return catalog.CatalogEntry{}, false, nil +} diff --git a/modules/claude-code/cmd/claude-code/catalogfeed_test.go b/modules/claude-code/cmd/claude-code/catalogfeed_test.go new file mode 100644 index 000000000..3b70104f3 --- /dev/null +++ b/modules/claude-code/cmd/claude-code/catalogfeed_test.go @@ -0,0 +1,90 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "sync" + "testing" + "time" + + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + delivery "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" + "git.novox.be/novox/mesh-sdk/go/views" +) + +// wiringOf is a part's reader in miniature: it keeps the followers adopted, so a test starts them as the reader's Start does. +type wiringOf struct { + mu sync.Mutex + fs []*views.Follower +} + +func (w *wiringOf) Adopt(f *views.Follower, _, _ string, _ func(context.Context, []byte) error) error { + w.mu.Lock() + defer w.mu.Unlock() + w.fs = append(w.fs, f) + return nil +} +func (w *wiringOf) Drop(string, string) error { return nil } + +// Design 47 G §196, §594: which modules exist, and the manifest of each, are the catalog half's; a branch with no first +// view yet is an error naming it, never no module, and an entry's manifest is the one it carries whole. +func TestCatalogHalfIsTheWatchedBranchesNewestRevision(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + answered := false + var mu sync.Mutex + call := func(key string, _ any) (json.RawMessage, error) { + mu.Lock() + defer mu.Unlock() + switch key { + case meshcatalogclient.RegistrationsVerb: + return json.Marshal(catalog.RegistrationsAnswer{Registrations: []catalog.Registration{{Module: "m", Version: 1, Branches: []string{"main"}}}}) + case meshcatalogclient.SourceVerb: + return json.Marshal(delivery.SourceAnswer{}) + case meshcatalogclient.RevisionVerb: + if !answered { + return nil, fmt.Errorf("the holder is away") + } + return json.Marshal(catalog.RevisionAnswer{Revision: &catalog.CatalogRevision{Revision: 1, Kind: meshcatalogclient.KindTrunk}, + Entries: []catalog.CatalogEntry{{Module: "pg", Manifest: map[string]any{"module": "pg"}}}}) + } + return nil, fmt.Errorf("asked %s", key) + } + w := &wiringOf{} + b, err := meshcatalogclient.NewBranches(w, call) + if err != nil { + t.Fatal(err) + } + h := branchesHalf{b} + if _, _, err := h.Entry("pg"); err == nil || !strings.Contains(err.Error(), "no watched branch") { + t.Fatalf("before the registrations: %v, want an error naming the missing branch", err) + } + go func() { _ = w.fs[0].Start(ctx) }() // the registrations + for len(b.Followed()) == 0 { + select { + case <-ctx.Done(): + t.Fatal("the registrations' branch is never followed") + case <-time.After(10 * time.Millisecond): + } + } + if _, _, err := h.Entry("pg"); err == nil || !strings.Contains(err.Error(), "no first view of main") { + t.Fatalf("with no first view: %v, want an error naming the branch, never no module", err) + } + mu.Lock() + answered = true + mu.Unlock() + c, _ := b.Catalog("main") + if err := c.Start(ctx); err != nil { + t.Fatal(err) + } + got, ok, err := h.Entry("pg") + if err != nil || !ok || got.Manifest["module"] != "pg" || h.Ready() != nil { + t.Fatalf("with the first view: %v, %v, %v", got, ok, err) + } + if _, ok, err := h.Entry("absent"); ok || err != nil { + t.Fatalf("a module the half does not list: %v, %v", ok, err) + } +} diff --git a/modules/claude-code/cmd/claude-code/config_test.go b/modules/claude-code/cmd/claude-code/config_test.go index 948fb3c9b..a9579ce8e 100644 --- a/modules/claude-code/cmd/claude-code/config_test.go +++ b/modules/claude-code/cmd/claude-code/config_test.go @@ -277,9 +277,9 @@ func TestTheManifestListsEveryToolServed(t *testing.T) { _ = json.Unmarshal(raw, &m) // The hand-act seat's warranted and hand-act, the seat mesh-catalog's seat list (ADR 0635 §1), and the owners' read tools of the three followed states; no verb of // the controller, and no read of the indexer's or the assigner's state (ADR 0615 §1): their views are consumed. - if !slices.Equal(m.Invokes, []string{"seat:mesh-hand-acts.warranted", "seat:mesh-hand-acts.hand-act", "seat:mesh-catalog.seat-definitions", ModulesTool, ManifestsTool, AssignmentsTool}) || + if !slices.Equal(m.Invokes, []string{"seat:mesh-hand-acts.warranted", "seat:mesh-hand-acts.hand-act", "seat:mesh-catalog.seat-definitions", RevisionVerb, RegistrationsVerb, SourceVerb, AssignmentsTool}) || !slices.Equal(m.Reads, []string{"claude-licence-manager.bindings"}) || - !slices.Equal(m.Consumes, []string{"mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.manifests", "mesh-delivery-assigner.views.assignments", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-conditions.findings-asked"}) { + !slices.Equal(m.Consumes, []string{"mesh-catalog.catalog-revision", "mesh-catalog.registration-changed", "mesh-delivery.source-moved", "mesh-delivery-assigner.views.assignments", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-conditions.findings-asked"}) { t.Errorf("invokes = %v, reads = %v, consumes = %v", m.Invokes, m.Reads, m.Consumes) } if src, _ := os.ReadFile("proposals.go"); !strings.Contains(string(src), `"seat:mesh-hand-acts.warranted"`) || diff --git a/modules/claude-code/cmd/claude-code/insteadof_test.go b/modules/claude-code/cmd/claude-code/insteadof_test.go index 405ca61cc..94fa9a03d 100644 --- a/modules/claude-code/cmd/claude-code/insteadof_test.go +++ b/modules/claude-code/cmd/claude-code/insteadof_test.go @@ -2,13 +2,10 @@ package main import ( "context" - "crypto/sha256" "encoding/json" "errors" - "fmt" assignerclient "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" mesh "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" - catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "os" "reflect" @@ -57,7 +54,7 @@ func someSeats() []seatdef.Definition { // saying what its tool replaces, as the seat mesh-module-assigner and the indexer keep them on the bus. func aMesh(t *testing.T) aBus { t.Helper() - b := aBus{AssignmentsRead: {}, ModulesRead: {}, ManifestsRead: {}} + b := aBus{AssignmentsRead: {}} for _, p := range []mesh.Assignments{ {V: 1, Node: "anchor", Decided: mesh.NodeDecided{PublicDomain: "Anchor.Example"}, Modules: []mesh.PlacedModule{{Module: "docker"}, {Module: "zsh"}}}, @@ -71,21 +68,33 @@ func aMesh(t *testing.T) aBus { b[AssignmentsRead][k] = v } } + return b +} + +// fakeCatalog is the catalog half as the tests give it: docker and zsh, docker's manifest saying what its tool replaces. +type fakeCatalog struct { + entries map[string]catalog.CatalogEntry + notYet error +} + +func (f fakeCatalog) Entry(module string) (catalog.CatalogEntry, bool, error) { + e, ok := f.entries[module] + return e, ok, f.notYet +} + +func (f fakeCatalog) Ready() error { return f.notYet } + +func aCatalog() fakeCatalog { + f := fakeCatalog{entries: map[string]catalog.CatalogEntry{}} for module, manifest := range map[string]string{ "docker": `{"module":"docker","replaces":{"docker_logs":["docker logs"]}}`, "zsh": `{"module":"zsh"}`, } { - digest := fmt.Sprintf("%x", sha256.Sum256([]byte(manifest))) - mkey, _ := catalogclient.ManifestsKey(digest) - raw, err := json.Marshal(catalog.ManifestsByDigestValue{V: 1, Module: module, Manifest: manifest}) - if err != nil { - t.Fatal(err) - } - b[ManifestsRead][mkey] = raw - key, _ := catalogclient.ModulesKey(module) - b[ModulesRead][key], _ = json.Marshal(catalog.ModulesByModuleValue{V: 1, None: "nothing is built of it", Manifest: digest, Repository: "novox/mesh-catalog", Commit: "c"}) + var whole map[string]any + _ = json.Unmarshal([]byte(manifest), &whole) + f.entries[module] = catalog.CatalogEntry{Module: module, Manifest: whole} } - return b + return f } func resolving(name string) []string { @@ -97,7 +106,7 @@ func resolving(name string) []string { // controller: the seats' verbs, the placed modules' own tools, every machine with its domains and addresses — a // loopback address never one of them. func TestTheMeshsToolsAreReadFromTheBus(t *testing.T) { - m, err := ReadMeshToolsFrom(aMesh(t), someSeats(), resolving) + m, err := ReadMeshToolsFrom(aMesh(t), aCatalog(), someSeats(), resolving) if err != nil { t.Fatal(err) } @@ -134,7 +143,7 @@ func TestTheMeshsToolsAreReadFromTheBus(t *testing.T) { case <-time.After(10 * time.Second): t.Fatalf("the seat list was not read: %+v", seatList.Status()) } - got, err := ReadMeshToolsFrom(aMesh(t), definedSeats(), resolving) + got, err := ReadMeshToolsFrom(aMesh(t), aCatalog(), definedSeats(), resolving) var addrs []string for _, r := range got.Replacements { addrs = append(addrs, r.Address) @@ -156,7 +165,7 @@ func TestTheMeshsToolsAreReadFromTheBus(t *testing.T) { // **The table is generated, not written** (novox/hq ADR 0245): what the records say a verb replaces is a row, // the machines' seats first, and nothing is rendered where the records say nothing. func TestTheInsteadOfTableIsGeneratedFromTheRecords(t *testing.T) { - m, _ := ReadMeshToolsFrom(aMesh(t), someSeats(), resolving) + m, _ := ReadMeshToolsFrom(aMesh(t), aCatalog(), someSeats(), resolving) table := InsteadOf(&m) for _, want := range []string{ "| `/node-service-manager.` | `status` — `systemctl status`, `systemctl is-active` · `journal` — `journalctl` |", @@ -206,7 +215,7 @@ func TestTheInsteadOfTableIsGeneratedFromTheRecords(t *testing.T) { // Asked again with the same answer, nothing is written and nothing renders. func TestTheMeshsToolsAreKeptOnlyWhenTheyChange(t *testing.T) { p := Paths{State: t.TempDir()} - m, _ := ReadMeshToolsFrom(aMesh(t), someSeats(), resolving) + m, _ := ReadMeshToolsFrom(aMesh(t), aCatalog(), someSeats(), resolving) if !KeepMeshTools(p, m) { t.Fatal("the first answer was not kept") } @@ -243,7 +252,7 @@ func TestTheMachinesRunningAModuleAreReadFromAssignments(t *testing.T) { func TestTheMeshIsReadFromTheOwnersFirstViews(t *testing.T) { bus := aMesh(t) r := newMeshViews() - for _, name := range []string{ModulesRead, ManifestsRead, AssignmentsRead} { + for _, name := range []string{AssignmentsRead} { state := strings.TrimPrefix(name, strings.Split(name, ".")[0]+".") values := map[string]json.RawMessage{} for k, v := range bus[name] { @@ -257,16 +266,20 @@ func TestTheMeshIsReadFromTheOwnersFirstViews(t *testing.T) { t.Fatal(err) } } - want, err := ReadMeshToolsFrom(bus, someSeats(), resolving) + want, err := ReadMeshToolsFrom(bus, aCatalog(), someSeats(), resolving) if err != nil { t.Fatal(err) } - got, err := ReadMeshToolsFrom(viewStates{r}, someSeats(), resolving) + got, err := ReadMeshToolsFrom(viewStates{r}, aCatalog(), someSeats(), resolving) if err != nil || !reflect.DeepEqual(got, want) || len(got.Replacements) == 0 { t.Fatalf("from the views: %+v, %v\nfrom the state: %+v", got, err, want) } // A reader not yet answered says so, and reads nothing. - if _, err := ReadMeshToolsFrom(viewStates{newMeshViews()}, someSeats(), resolving); err == nil || !strings.Contains(err.Error(), "no first view") { + // A catalog half with no first view says so, and reads no module as absent. + if _, err := ReadMeshToolsFrom(bus, fakeCatalog{notYet: errors.New("the catalog half has no first view of main yet")}, someSeats(), resolving); err == nil || !strings.Contains(err.Error(), "no first view") { + t.Fatalf("an unanswered catalog half: %v", err) + } + if _, err := ReadMeshToolsFrom(viewStates{newMeshViews()}, aCatalog(), someSeats(), resolving); err == nil || !strings.Contains(err.Error(), "no first view") { t.Fatalf("an unanswered first view: %v", err) } } @@ -274,7 +287,7 @@ func TestTheMeshIsReadFromTheOwnersFirstViews(t *testing.T) { // Issue 1126 B13 AC1 (ADR 0590): over the seats the mesh defines, the rendered instructions, their table among them, // name no address of the controller, which is not placed: each job is named by the seat that owns it now. func TestTheRenderedInstructionsNameNoControllerAddress(t *testing.T) { - m, err := ReadMeshToolsFrom(aMesh(t), someSeats(), resolving) + m, err := ReadMeshToolsFrom(aMesh(t), aCatalog(), someSeats(), resolving) if err != nil { t.Fatal(err) } diff --git a/modules/claude-code/cmd/claude-code/main.go b/modules/claude-code/cmd/claude-code/main.go index df34d25ac..ebc69e76d 100644 --- a/modules/claude-code/cmd/claude-code/main.go +++ b/modules/claude-code/cmd/claude-code/main.go @@ -25,6 +25,7 @@ import ( condseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/seatlist" "git.novox.be/novox/mesh-sdk/go/views" @@ -217,12 +218,19 @@ func nodesRunningMe() ([]string, error) { return NodesRunning(viewStates{meshVie // holds them equal). const ( assignerModule = "mesh-delivery-assigner" - indexerModule = "mesh-delivery-indexer" ) +// The seat verbs the follower of the watched branches asks (design 47 G §614), as the manifest's `invokes` names them. +const ( + RevisionVerb = "seat:mesh-catalog.revision" + RegistrationsVerb = "seat:mesh-catalog.registrations" + SourceVerb = "seat:mesh-delivery.source" +) + +// meshCatalog is this module's reading of which modules exist: the catalog half, set where the reader is built. +var meshCatalog CatalogHalf + const ( - ModulesTool = indexerModule + ".indexer_read_modules" - ManifestsTool = indexerModule + ".indexer_read_manifests" AssignmentsTool = assignerModule + ".assigner_read_assignments" ) @@ -253,8 +261,6 @@ func newMeshViews() *views.Reader { default: } }} - v.Follow(indexerModule, "modules", ModulesTool) - v.Follow(indexerModule, "manifests", ManifestsTool) v.Follow(assignerModule, "assignments", AssignmentsTool) return v } @@ -335,7 +341,7 @@ func tools(p Paths, servers ServerState, view *ServerView, config ConfigState, c Description: "Write Claude Code's managed directory now, from the mesh's facts, this module's settings and the servers registered here — after reading the bus again for what each tool replaces and which machines the mesh has (the instructions' table and the shell's guard).", Run: func(map[string]any) (any, error) { answer := map[string]any{} - if m, err := ReadMeshToolsFrom(viewStates{meshViews}, definedSeats(), lookup); err != nil { + if m, err := ReadMeshToolsFrom(viewStates{meshViews}, meshCatalog, definedSeats(), lookup); err != nil { answer["asked"] = "the bus could not be read in full (" + err.Error() + "); what was kept is rendered" } else { KeepMeshTools(p, m) @@ -469,6 +475,20 @@ func main() { } p, launched := PathsFrom(os.Getenv) meshViews = newMeshViews() + branches, err := catalogclient.FollowBranches(meshViews, func(key string, body any) (json.RawMessage, error) { return stdio.Ask(key, body) }) + if err != nil { + say("%v", err) + os.Exit(1) + } + branches.Logf = say + // A change in a branch's half is told as any followed state's: the instructions' table is read again. + branches.Changed = func(string, string, json.RawMessage, bool, time.Time) { + select { + case meshChanged <- struct{}{}: + default: + } + } + meshCatalog = branchesHalf{branches} if launched { fsys = FilesFor(p) } diff --git a/modules/claude-code/cmd/claude-code/mesh.go b/modules/claude-code/cmd/claude-code/mesh.go index d543178d6..fed9b0664 100644 --- a/modules/claude-code/cmd/claude-code/mesh.go +++ b/modules/claude-code/cmd/claude-code/mesh.go @@ -18,8 +18,6 @@ import ( "fmt" assignerclient "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" mesh "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" - catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" - catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "net" "os" "path/filepath" @@ -93,8 +91,6 @@ func ReadMeshTools(p Paths) *MeshTools { // The states read, each declared in the manifest's reads. const ( AssignmentsRead = (assignerModule + "." + mesh.StateAssignments) - ModulesRead = indexerModule + "." + catalog.StateModules - ManifestsRead = indexerModule + "." + catalog.StateManifests ) // States reads the module states the manifest declares in its reads: a state's keys, and one key's value. @@ -152,44 +148,28 @@ func NodesRunning(st States, module string) ([]string, error) { return runsOn(placed)[module], nil } -// modulesTools is every placed module's own tools that replace a command, by module, from the manifest the indexer -// keeps of it. A module whose manifest cannot be read is left out and said in the error. -func modulesTools(st States, on map[string][]string) ([]ModuleTools, error) { +// modulesTools is every placed module's own tools that replace a command, by module, from the manifest its catalog +// graph entry carries. A module whose manifest cannot be read is left out and said in the error. +func modulesTools(cat CatalogHalf, on map[string][]string) ([]ModuleTools, error) { var out []ModuleTools var problems []error + if err := cat.Ready(); err != nil { + return nil, err + } modules := make([]string, 0, len(on)) for m := range on { modules = append(modules, m) } sort.Strings(modules) for _, module := range modules { - key, err := catalogclient.ModulesKey(module) - if err != nil { - continue - } - raw, found, err := st.Get(ModulesRead, key) + entry, found, err := cat.Entry(module) if err != nil || !found { if err != nil { - problems = append(problems, fmt.Errorf("%s %s: %w", ModulesRead, module, err)) + problems = append(problems, fmt.Errorf("the catalog half's entry of %s: %w", module, err)) } continue } - m, err := catalogclient.DecodeModule(raw) - if err != nil || m.Manifest == "" { - continue - } - mkey, err := catalogclient.ManifestsKey(m.Manifest) - if err != nil { - continue - } - raw, found, err = st.Get(ManifestsRead, mkey) - if err != nil || !found { - if err != nil { - problems = append(problems, fmt.Errorf("%s of %s: %w", ManifestsRead, module, err)) - } - continue - } - kept, err := catalogclient.DecodeManifest(raw) + kept, err := json.Marshal(entry.Manifest) if err != nil { problems = append(problems, fmt.Errorf("the manifest of %s: %w", module, err)) continue @@ -197,7 +177,7 @@ func modulesTools(st States, on map[string][]string) ([]ModuleTools, error) { var man struct { Replaces map[string][]string `json:"replaces"` } - if json.Unmarshal([]byte(kept.Manifest), &man) != nil || len(man.Replaces) == 0 { + if json.Unmarshal(kept, &man) != nil || len(man.Replaces) == 0 { continue } out = append(out, ModuleTools{Module: module, On: on[module], Replaces: man.Replaces}) @@ -263,14 +243,14 @@ func lookup(name string) []string { // ReadMeshToolsFrom reads the bus now: the seats' verbs, the placed modules' own tools, and every node assignments names // with its domains and addresses. A part that cannot be read is left out and said in the error; what was read is kept. -func ReadMeshToolsFrom(st States, defined []seatdef.Definition, resolve func(string) []string) (MeshTools, error) { +func ReadMeshToolsFrom(st States, cat CatalogHalf, defined []seatdef.Definition, resolve func(string) []string) (MeshTools, error) { var m MeshTools placed, err := assignments(st) if err != nil { m.Replacements = ReplacementsOf(defined, nil) return m, err } - modules, merr := modulesTools(st, runsOn(placed)) + modules, merr := modulesTools(cat, runsOn(placed)) m.Replacements = ReplacementsOf(defined, modules) for node, p := range placed { machine := Machine{Name: node, Domains: []string{node + InternalSuffix}} @@ -357,7 +337,7 @@ func refreshMeshTools(p Paths) { } startSeatList(seatlist.StdioBus()) for { - m, err := ReadMeshToolsFrom(viewStates{meshViews}, definedSeats(), lookup) + m, err := ReadMeshToolsFrom(viewStates{meshViews}, meshCatalog, definedSeats(), lookup) if err != nil { say("reading what each tool replaces and which machines the mesh has: %v", err) } diff --git a/modules/claude-code/module.json b/modules/claude-code/module.json index 5a0b83f2f..8a7386677 100644 --- a/modules/claude-code/module.json +++ b/modules/claude-code/module.json @@ -40,13 +40,15 @@ "seat:mesh-hand-acts.warranted", "seat:mesh-hand-acts.hand-act", "seat:mesh-catalog.seat-definitions", - "mesh-delivery-indexer.indexer_read_modules", - "mesh-delivery-indexer.indexer_read_manifests", + "seat:mesh-catalog.revision", + "seat:mesh-catalog.registrations", + "seat:mesh-delivery.source", "mesh-delivery-assigner.assigner_read_assignments" ], "consumes": [ - "mesh-delivery-indexer.views.modules", - "mesh-delivery-indexer.views.manifests", + "mesh-catalog.catalog-revision", + "mesh-catalog.registration-changed", + "mesh-delivery.source-moved", "mesh-delivery-assigner.views.assignments", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed",