Commit Graph
4 Commits
Author SHA1 Message Date
NotExpectedYet 8ab3299694 fix: a delegated session inherits the facet it was spawned under
Release / release (push) Successful in 6s
The user's declaration is keyed by session id, and a session spawned or forked
from a scoped one has none of its own — so its facet was its own working
directory, one level above its parent's. Delegation was therefore a way around
the scope rather than work inside it: the parent could not read the file itself
and could have a subagent read it.

A session now inherits the root resolved for the session named by its
`parentSession` header, which the harness already records durably for both a
spawned child and a fork. The inherited root is the parent's *effective* root, so
it already includes any narrowing the parent made for itself: delegating is not
a way back out of a self-imposed focus either. Because each session records its
own root as it resolves a scope, and a scope is resolved for every model request
and every tool call, inheritance holds at any depth of delegation rather than for
one hop.

Two boundaries are deliberate. A declaration the user made *for the child* is a
more specific statement about that session and stands — inheritance only answers
the case where they made none. And a parent's recorded root outlives its session
while any child that inherited it is still running, because disposing a parent
must not quietly widen a subagent that is still working.
2026-09-27 07:43:03 +01:00
NotExpectedYet d9caf4eeec feat: scope a session from a declaration the agent cannot widen
The facet root was the session's working directory and nothing else, so the
only way to scope a conversation to a module was to make the module a
workspace — and the only way to reach anything outside it was `extraRoots`,
which is a fact about the deployment rather than about one conversation.

Scope now has two layers, and the split is the point. The **user layer** is
`config.scopes[sessionId]` — a `focus` and a list of `extras`, written only
from the browser context panel through the settings service, which is what
`settings.mutate` requiring a volatile path buys us. The **agent layer** is in
memory: a narrowing `focus` and its grants. Both can only ever reduce what the
user layer allows, so an agent that goes wrong cannot widen its own scope.

`facet action=focus` is therefore the one scope change the agent may make
unasked, bounded by the scope already in force and decided on canonical targets.
`reset` clears the agent's side only, landing on the user's declaration rather
than on the session directory — landing on the directory would have made
`reset` a widening. `extraRoots` demotes to an optional deployment-wide seed.

The binding now names the context panel as the way to widen, because it is: in
a deployment with no approval channel, `facet action=add` cannot be granted at
all.
2026-09-27 06:10:53 +01:00
NotExpectedYet 28f20a8aac fix: show the settings card, and make an edit actually take effect
`mode` and `extraRoots` carried no `volatile()` marker, so the framework's
form projection dropped the whole entry from `settings.describe` and the plugin
had no card under Plugins at all. The marker is what creates the card.

It is also what turns each field into a reference rather than a value: the
resolved config hands `apply` a `{get(), [write]}` per volatile field, which is
how a settings write lands in a running plugin without re-applying it. So the
policy and the guard now read both fields at the point of use instead of
snapshotting them at mount, and `sectionOrder`/`contextOrder` stay unmarked
because a prompt section cannot be re-ordered without a remount.

A section is also registered once as a plain string, so the rule could not
follow a live `mode` without freezing at load. The mode-specific sentence moved
into the binding, which is a callback.
2026-09-26 07:13:33 +01:00
NotExpectedYet 0858862cda init 2026-09-26 06:45:50 +01:00