Repository navigation
Description
Relocated from dezren39/darwin-nix#185 on 2026-10-28 (placement audit): mcpx's single source lives in developing-today/code (pkgs/mcpx); re-home tracked at #309. Original body below; issue references inside it are historical (dezren39/nix numbering).
Parent: #176
Summary
Three issues need the same settings capability and none of them owns it: a value the daemon sets as a ceiling,
which a caller may lower but never raise. #111 needs it for how much repair mcpx may do on its own, #112 for what a
hook is allowed to do, #113 for whether an intent may act or only plan. Today it exists only as a paragraph inside #111
(the ClampedBy idea). This issue owns the mechanism; the dials themselves belong to their issues, and their
vocabulary to the autonomy-dial decision record SURFACES is writing under docs/decisions/ (#181 collision 3).
Owns #181 collision 5: the one mechanism by which a daemon-set value bounds what a caller may ask for. #111, #112
and #113 consume it; none implements its own clamp.
Original notes
- mcp-owned opencode api support
- check inputs, reject any request with/without reason, [...] (full bullet quoted in Bump checkmarx/ast-github-action from 2.0.19 to 2.0.23 #111)
- need ways for server and client both to control their section maybe need auth
The nested bullet is the source. Its security half is deferred; its non-security half -- whose policy wins when the
daemon and the caller disagree -- is this issue.
Current state (05c78b2)
- A setting has a
Scope(internal/settings/schema.go:181; values daemon, client, call, plugin) andHot. It has no
notion of one layer bounding another. - Values resolve by
Layer, highest wins: default < file < env < flag < runtime (internal/settings/value.go:13-25).
A call-scoped value supplied by a caller therefore simply wins. - The only existing answer to "the caller must not decide this" is to make the setting daemon-scoped outright:
elicit.confirmDestructiveisScopeDaemon(internal/settings/consumer.go:47), anddocs/consumer.mdexplains why.
That is all-or-nothing: the caller cannot even choose to be more careful.
Approaches
A -- each consumer clamps in its own code. Repair, hooks and intent each compare the caller's value with the
daemon's before acting. Least recommended: three implementations with three orderings and three error messages,
invisible to /v1/settings and mcpx settings, and the fourth dial gets a fourth. This is what happens by default if
nobody owns it.
B -- a naming convention plus a helper. Pair each dial with a daemon-scoped twin (repair.level +
repair.maxLevel) and add settings.Clamp(call, max). Works in today's registry with no schema change. Costs: twice
the settings per dial, the pairing is only a naming convention (a guard test would have to discover pairs by name), and
every reader must remember to call the helper -- the "declared but not delivered" shape #177 exists to stop.
C -- recommended: a registry field. A call-scoped setting declares that the daemon's resolved value is its ceiling
(field name open; Clamp: Ceiling). Resolution returns min(caller, daemon) over an ordered Kind (numbers,
durations, and enums in declared order), and reports the resolved value, the layer that set it, and clamped: true
when a caller asked for more. One implementation; flags, env, /v1/settings, the MCP settings tool and help text
inherit it; a guard test asserts every clamped setting has an ordered Kind and a daemon-side value. Recommended because
it is the only option where the rule is declared once next to the setting and enforced by the resolver rather than by
each reader's memory.
Open questions
- Raise attempts: clamp and report (recommended), or refuse the call? Refusing is safer for automation that assumed it
got what it asked for; clamping is friendlier for humans. - Is a floor needed too (the daemon insists on at least "diagnose")? Same mechanism, other direction.
- Enum order: declared order, or an explicit rank per value? Declared order is free: enum settings already list their
values in order (script.typecheck:Enum: []string{"off", "on", "strict"},internal/settings/registry.go:246). - Which dials use it is decided by Bump checkmarx/ast-github-action from 2.0.19 to 2.0.23 #111/Update module gopkg.in/go-jose/go-jose.v2 to v2.6.3 [SECURITY] - autoclosed #112/Update module github.com/jackc/pgx/v5 to v5.5.4 [SECURITY] - autoclosed #113 and the autonomy-dial decision record, not here.
Ease · Impact · Related
Ease M · Impact med (high once #111, #112 or #113 implements a dial)
Related: #111, #112, #113 (consumers; #111 carries the dial vocabulary, #181 collision 3), #172 (settings being declared
now), #129 (the guard-test pattern C's test follows), #181 (collision 5).
Security/auth deferred; see tracker.
Acceptance criteria
- The daemon-set ceiling exists in settings, is enforced on every caller path, and a caller may lower but never raise it; attempts to raise it fail validation with a clear error.
- Tests cover raise/lower/equal cases and the settings docs describe the ceiling.
Activity
developing-today commentedon Oct 28, 2026
CODE-829
dezren39 commentedon Oct 28, 2026
History carried over from dezren39/darwin-nix#185 (verbatim, oldest first):
@dezren39 · 2026-09-30T11:57:51Z
Issues pass (2026-09-30, at 05c78b2).
**Owns #181 collision 5:** …).script.typecheck:off, on, strict,internal/settings/registry.go:246), so declared order costs nothing.Related:notes that Bump checkmarx/ast-github-action from 2.0.19 to 2.0.23 #111 carries the dial vocabulary (collision 3) and adds Addomasnapupstream repository to flake inputs #129 (the guard-test pattern C's test follows). Verified the other citations at 05c78b2 (schema.go:181,value.go:13-25,consumer.go:47). No change to A/B/C or Ease/Impact (M / med).dezren39 commentedon Oct 28, 2026
Intake review
🤖 Agent note · filed
Notes: Filed in the large mcpx filing batch of 2026-10-28 21:59–22:24Z and already carries a queued triage task — it is not orphaned. Verified live at intake: no merged PR references this issue and it was filed under an hour ago, so nothing can have resolved it yet. Acceptance criteria appended to the body from the issue's own stated scope; original body preserved verbatim above it.
Risks: Tracker-level scope overlaps its sibling batch issues — the triage pass must dedupe against them before any implementation packet is written, and split rather than implement if the scope is multi-PR.
Suggested approach: Triage first (task 2795): confirm the boundary against the sibling issues, then enqueue one scoped packet per deliverable slice.
dezren39 commentedon Oct 29, 2026
Triage verdict — already implemented. Closing as done.
The relocated issue's "Current state" was written at
05c78b2in the old home, before the mechanism landed. Verified fresh atdeveloping-today/codeorigin/main2c4e7434(2026-10-29, task 2795), the recommended Approach C (a registry field, enforced by the resolver) is built and wired:pkgs/mcpx/internal/settings/clamp.go— theClampedByfield on a setting declaration,Set.Clamp(path, requested)bounding a caller's request by the daemon-resolved ceiling,clampValuelowering and recording what was asked for (Value.Requested/Value.ClampedBy), rendered by the CLI atinternal/cli/commands.go:2146.pkgs/mcpx/docs/decisions/0002-autonomy-dial.md§"ClampedBy— aninternal/settingscapability (Phase 3: sync, blob gate, and relays #181 row 5)" specifies exactly this mechanism and its validation rules (Newrefuses a registry whoseClampedBynames a missing/unordered/daemon-less target); 0003 lists it as enforced from the day it lands.pkgs/mcpx/internal/settings/consumer.go:135,146,157—prompt.autonomy,repair.autonomyandhooks.autonomyall declareClampedBy: "autonomy.max"(the three consumers this issue exists for: nix Bump checkmarx/ast-github-action from 2.0.19 to 2.0.23 #111/Update module gopkg.in/go-jose/go-jose.v2 to v2.6.3 [SECURITY] - autoclosed #112/Update module github.com/jackc/pgx/v5 to v5.5.4 [SECURITY] - autoclosed #113's dials, per decision 0002's mapping table).pkgs/mcpx/internal/settings/clamp_test.go—TestNewRefusesAClampThatCannotHold(validation of malformed clamps) andTestACallerCannotRaiseAClampedSetting(the raise case), plusconsumed_test.gocoverage.pkgs/mcpx/docs/configuration.md:263documentsautonomy.maxas "the most autonomy this daemon exercises, whatever a caller asks for".One wording difference from the acceptance criteria, settled by the design record rather than by this triage: a raise attempt is clamped and reported (the resolved value carries
Requestedand the ceiling's origin), not refused outright — that was this issue's own first open question, and decision 0002 ruled for clamp-and-report. Schema-level misconfiguration is refused at validation (TestNewRefusesAClampThatCannotHold).No implementation follow-up is needed; the remaining consumer work belongs to the dial issues themselves, as this issue states. Dedupe: no other code issue covers the ClampedBy mechanism (searches return this issue and the decision records only).
agent-note:v1 · agent: repo-queue-worker-shift-b (lane B) · task 2795 · kind: triage · outcome: already_done — ClampedBy mechanism implemented (settings/clamp.go), three autonomy dials wired to autonomy.max, tests + docs in place; closing