Rank a search the same whatever order discovery heard things in
A tie went to whichever tool discovery listed first, so docker logs found docker_secrets_in_logs before docker_logs on some runs. A name the query says whole wins, then the shorter name.
This commit is contained in:
@@ -68,7 +68,7 @@ address) and `mesh_runtimes` (which runtimes answered discovery: per runtime its
|
||||
answer took, its size in bytes, how many modules and tools it announced, whether it was shortened to
|
||||
fit the bus, when it was last heard — and who was expected and not heard).
|
||||
|
||||
`mesh_search` also finds a tool by the shell command it replaces (novox/hq ADR 0242): each seat verb and
|
||||
`mesh_search` also finds a tool by the shell command it replaces (novox/hq ADR 0245): each seat verb and
|
||||
module tool says what it replaces in the controller's records (`replaces`, read from the controller's
|
||||
`tools` and `modules` answers), so `journalctl -u x`, `systemctl status x` or `docker ps` puts the verb
|
||||
for it first, and says the command it matched as `instead_of`. A word also matches through a short table
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ if ! command -v node >/dev/null 2>&1 || [ ! -d node_modules/@novox/mesh-sdk ]; t
|
||||
echo "NOT TESTED HERE: internal/runtime and internal/console launch bundles that need node and @novox/mesh-sdk"
|
||||
packages=$(printf '%s\n' "$packages" | grep -v -e '/internal/runtime$' -e '/internal/console$')
|
||||
# What of the console launches no bundle runs anyway: how an address resolves (novox/hq issue 287),
|
||||
# and how a search ranks (novox/hq ADR 0242).
|
||||
# and how a search ranks (novox/hq ADR 0245).
|
||||
bundleless='^(TestASeatAndAModuleOfOneNameAreEachReached|TestSearch.*|TestASearchByCommandSaysWhatItReplaces)$'
|
||||
fi
|
||||
if command -v gcc >/dev/null 2>&1; then
|
||||
|
||||
@@ -131,7 +131,7 @@ type index struct {
|
||||
// what it serves in time, or it answered before and not now. An address it might answer is never
|
||||
// called missing while it is here (2026-10-05).
|
||||
Unheard []unheard
|
||||
// Replaces is what each verb and tool replaces, from the controller's records (novox/hq ADR 0242):
|
||||
// Replaces is what each verb and tool replaces, from the controller's records (novox/hq ADR 0245):
|
||||
// `seat:<seat>.<verb>` and `<module>.<tool>`.
|
||||
Replaces map[string][]string
|
||||
}
|
||||
@@ -225,7 +225,7 @@ type recordedModule struct {
|
||||
Module string `json:"module"`
|
||||
On []string `json:"on"`
|
||||
Tools bool `json:"tools"`
|
||||
// Replaces is what each of its own tools replaces (novox/hq ADR 0242).
|
||||
// Replaces is what each of its own tools replaces (novox/hq ADR 0245).
|
||||
Replaces map[string][]string `json:"replaces,omitempty"`
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ func indexOn(conn *bus.Conn) (*index, error) {
|
||||
wg.Add(3)
|
||||
go func() { defer wg.Done(); nodesOut, _ = controllerOutput(conn, "nodes") }()
|
||||
go func() { defer wg.Done(); modulesOut, _ = controllerOutput(conn, "modules") }()
|
||||
// The seats' verbs as the records define them, for what each replaces (novox/hq ADR 0242): answered
|
||||
// The seats' verbs as the records define them, for what each replaces (novox/hq ADR 0245): answered
|
||||
// in-process from the records, never a command.
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -832,7 +832,7 @@ func (s *Surface) discover(name string, args map[string]any) map[string]any {
|
||||
if len(hits) == 0 {
|
||||
out["matches"] = []hit{}
|
||||
out["hint"] = "nothing matched every word; try fewer words, or mesh_overview and mesh_machine to browse. " +
|
||||
"If no tool does it, one is created in the module that owns it, on its seat — never worked around (novox/hq ADR 0242)"
|
||||
"If no tool does it, one is created in the module that owns it, on its seat — never worked around (novox/hq ADR 0245)"
|
||||
}
|
||||
if len(x.Unheard) > 0 {
|
||||
out["incomplete"] = "discovery did not hear in full from " + sayUnheard(x.Unheard) +
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package console
|
||||
|
||||
// Finding a tool by the words an agent already has (novox/hq ADR 0242).
|
||||
// Finding a tool by the words an agent already has (novox/hq ADR 0245).
|
||||
//
|
||||
// An agent that wants a unit's log reaches for `journalctl`, and one that wants a container's for `docker
|
||||
// logs`; it searches with those words, or with `logs`. Matched against names and descriptions alone, none
|
||||
@@ -12,10 +12,11 @@ package console
|
||||
// every word of the replaced command, in order, among the query's words. The most specific command wins.
|
||||
// 2. **Every word is in the tool's name, its replaced commands or its description**, a word also matching
|
||||
// through a short table of the words agents use for the mesh's (logs ⇄ journal, ps ⇄ list). A word in
|
||||
// the name or a replaced command weighs more than one in the description.
|
||||
// the name or a replaced command weighs more than one in the description, and a name the query says
|
||||
// whole more than one it says in part.
|
||||
// 3. Nothing else: a search that matches nothing says so.
|
||||
//
|
||||
// Ties keep the listing's order — seats first, the place tools belong — then the modules by name.
|
||||
// Ties go to a seat — the place tools belong — then to the shorter name, then by address.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -149,6 +150,19 @@ func scoreOf(query string, qwords []string, name, description string, replaces [
|
||||
score += found
|
||||
}
|
||||
}
|
||||
// A name the query says whole — `docker logs` for docker_logs, not docker_secrets_in_logs — first.
|
||||
own := name[strings.LastIndex(name, ".")+1:]
|
||||
whole := true
|
||||
for _, part := range strings.FieldsFunc(strings.ToLower(own), func(r rune) bool { return r == '_' || r == '-' }) {
|
||||
in := false
|
||||
for _, w := range qwords {
|
||||
in = in || w == part
|
||||
}
|
||||
whole = whole && in
|
||||
}
|
||||
if whole {
|
||||
score += 2
|
||||
}
|
||||
return score, ""
|
||||
}
|
||||
|
||||
@@ -208,11 +222,22 @@ func (x *index) search(query string) []hit {
|
||||
hits = append(hits, h)
|
||||
}
|
||||
}
|
||||
sort.SliceStable(hits, func(i, j int) bool { return hits[i].score > hits[j].score })
|
||||
// Best first; on a tie the shorter name — the plainer tool — then the address, so an answer never
|
||||
// depends on the order discovery heard things in.
|
||||
sort.SliceStable(hits, func(i, j int) bool {
|
||||
if hits[i].score != hits[j].score {
|
||||
return hits[i].score > hits[j].score
|
||||
}
|
||||
a, b := hits[i].Address[strings.LastIndex(hits[i].Address, ".")+1:], hits[j].Address[strings.LastIndex(hits[j].Address, ".")+1:]
|
||||
if len(a) != len(b) {
|
||||
return len(a) < len(b)
|
||||
}
|
||||
return hits[i].Address < hits[j].Address
|
||||
})
|
||||
return hits
|
||||
}
|
||||
|
||||
// replacesOf is what every verb and tool replaces, from the controller's records (novox/hq ADR 0242):
|
||||
// replacesOf is what every verb and tool replaces, from the controller's records (novox/hq ADR 0245):
|
||||
// a seat's verbs from its `tools` answer, keyed `seat:<seat>.<verb>`, and a module's own tools from
|
||||
// its `modules` answer, keyed `<module>.<tool>`. A controller older than the field answers neither,
|
||||
// and search matches names and descriptions as before.
|
||||
|
||||
@@ -55,7 +55,7 @@ func meshToSearch(t *testing.T) *index {
|
||||
return x
|
||||
}
|
||||
|
||||
// **The words an agent has find the verb first** (novox/hq ADR 0242): the command it would have run, or
|
||||
// **The words an agent has find the verb first** (novox/hq ADR 0245): the command it would have run, or
|
||||
// the word it would have used, puts the mesh's tool for it at the top.
|
||||
func TestSearchFindsTheToolThatReplacesACommandFirst(t *testing.T) {
|
||||
x := meshToSearch(t)
|
||||
|
||||
Reference in New Issue
Block a user