claude-code: write the home scope only into an agent account's own home
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
mesh/delivery superseded: a newer head of the same pull request
On the nodes where the agent runs as the operator, the home scope wrote the operator's own ~/.claude, whose rules and skills load in every session, and any node's tool could be addressed from another (hq issue 340). Take it only for the serving node, where its home is an agent account's own; never place a bus item in the operator's home. Ignore the built binary.
This commit is contained in:
@@ -45,17 +45,56 @@ func Fingerprint(s string) string {
|
||||
}
|
||||
|
||||
// Paths are where this node's files are, from the module's words (ADR 0192).
|
||||
//
|
||||
// Agent, Operator, AgentHome and OperatorHome are the accounts the runtime names (novox/hq ADR 0266:
|
||||
// MESH_AGENT_ACCOUNT, MESH_OPERATOR_ACCOUNT, their homes). Read here only to judge whose home the home scope
|
||||
// writes (novox/hq issue 340); absent, the agent runs as the operator.
|
||||
type Paths struct {
|
||||
State, Facts, Settings, Home, Node string
|
||||
State, Facts, Settings, Home, Node string
|
||||
Agent, Operator, AgentHome, OperatorHome string
|
||||
}
|
||||
|
||||
// PathsFrom reads them, or answers false outside a launch.
|
||||
func PathsFrom(env func(string) string) (Paths, bool) {
|
||||
p := Paths{State: env("MESH_CLAUDE_CODE_STATE"), Facts: env("MESH_CLAUDE_CODE_FACTS"),
|
||||
Settings: env("MESH_CLAUDE_CODE_SETTINGS"), Home: env("MESH_OPERATOR_HOME"), Node: env("MESH_NODE")}
|
||||
Settings: env("MESH_CLAUDE_CODE_SETTINGS"), Home: env("MESH_OPERATOR_HOME"), Node: env("MESH_NODE"),
|
||||
Agent: env("MESH_AGENT_ACCOUNT"), Operator: env("MESH_OPERATOR_ACCOUNT"),
|
||||
AgentHome: env("MESH_AGENT_HOME"), OperatorHome: env("MESH_OPERATOR_HOME")}
|
||||
return p, p.State != "" && p.Facts != "" && p.Settings != "" && p.Home != "" && p.Node != ""
|
||||
}
|
||||
|
||||
// Confined says the agent runs here as an account of its own, not the operator's (novox/hq ADR 0266).
|
||||
func (p Paths) Confined() bool { return p.Agent != "" && p.Operator != "" && p.Agent != p.Operator }
|
||||
|
||||
// HomeIsTheAgentsOwn says whether the home this module writes the home scope into belongs to an agent account of
|
||||
// its own (novox/hq issue 340): the agent is confined here, and the home is the agent's, not the operator's.
|
||||
// Anywhere else the home scope would write the operator's own ~/.claude, whose rules and skills load in every
|
||||
// session the operator starts.
|
||||
func (p Paths) HomeIsTheAgentsOwn() bool {
|
||||
if !p.Confined() || p.AgentHome == "" || p.Home == "" {
|
||||
return false
|
||||
}
|
||||
home := filepath.Clean(p.Home)
|
||||
return home == filepath.Clean(p.AgentHome) && (p.OperatorHome == "" || home != filepath.Clean(p.OperatorHome))
|
||||
}
|
||||
|
||||
// HomeScopeRefused says why the home scope is not written for a node, or "": only this node's own home, and only
|
||||
// where it belongs to an agent account of its own (novox/hq issue 340). Another node's home cannot be judged
|
||||
// from here; that node's own module judges it.
|
||||
func HomeScopeRefused(p Paths, target string) string {
|
||||
if target != p.Node {
|
||||
return "the home scope reaches " + target + "'s home, and whose home that is cannot be judged from " + p.Node +
|
||||
"; call that node's own claude-code tool, which judges it (novox/hq issue 340). Nothing was registered"
|
||||
}
|
||||
if !p.HomeIsTheAgentsOwn() {
|
||||
return "the home scope on " + p.Node + " would write the operator's own ~/.claude, whose rules and skills " +
|
||||
"load in every session the operator starts there, and the tool runner cannot tell the operator from an " +
|
||||
"agent. It is taken only where the agent runs as an account of its own and the home is that account's " +
|
||||
"(novox/hq ADR 0266, issue 340). Nothing was registered"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p Paths) credentials() string { return filepath.Join(p.Home, ".claude", ".credentials.json") }
|
||||
func (p Paths) account() string { return filepath.Join(p.Home, ".claude.json") }
|
||||
func (p Paths) binding() string { return filepath.Join(p.State, "licence.json") }
|
||||
@@ -160,7 +199,18 @@ func RenderNow(p Paths, write WriteManaged) ([]string, error) {
|
||||
out = append(out, line)
|
||||
}
|
||||
// The home scope: what this module places in the account's own agent directory (ADR 0182).
|
||||
done, left := PlaceHome(p, HomeFiles(config))
|
||||
// Only into a home that belongs to an agent account of its own (novox/hq issue 340): the operator's own home is
|
||||
// never written from the bus, whoever put an item there; what the module placed there before is taken back,
|
||||
// unless the person changed it since.
|
||||
want := HomeFiles(config)
|
||||
if !p.HomeIsTheAgentsOwn() {
|
||||
if len(want) > 0 {
|
||||
out = append(out, fmt.Sprintf("home: %d file(s) registered for this home not placed: it is the operator's "+
|
||||
"own, and the home scope writes only an agent account's own home (novox/hq issue 340)", len(want)))
|
||||
}
|
||||
want = nil
|
||||
}
|
||||
done, left := PlaceHome(p, want)
|
||||
for _, line := range done {
|
||||
out = append(out, "home "+line)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user