Skip to content

Commit 474c74f

Browse files
committed
fix(config): graduate mural to a top-level block (#278)
The v0.22.0 graduation killed experimental.*; mural landing under it tripped the dashboard's guard exactly as designed. Top-level mural block with an in-memory legacy read (deprecation warning names the new path), model stays user-tier-only, schema + docs regenerated.
2 parents 7eb943e + f5ebc06 commit 474c74f

27 files changed

Lines changed: 258 additions & 103 deletions

‎assets/magic-context.schema.json‎

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,21 @@
1515
"description": "Allow Magic Context sessions launched from the exact canonical home directory. The home session uses its deterministic dir: identity so pre-gate memories reconnect. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace.",
1616
"type": "boolean"
1717
},
18-
"experimental": {
18+
"mural": {
1919
"default": {
20-
"mural": {
21-
"enabled": false
22-
}
20+
"enabled": false
2321
},
24-
"description": "Experimental feature switches. User and project configuration are both accepted.",
22+
"description": "Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task.",
2523
"type": "object",
2624
"properties": {
27-
"mural": {
28-
"default": {
29-
"enabled": false
30-
},
31-
"description": "Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task.",
32-
"type": "object",
33-
"properties": {
34-
"enabled": {
35-
"default": false,
36-
"type": "boolean"
37-
},
38-
"model": {
39-
"description": "Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).",
40-
"type": "string",
41-
"minLength": 1
42-
}
43-
}
25+
"enabled": {
26+
"default": false,
27+
"type": "boolean"
28+
},
29+
"model": {
30+
"description": "Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).",
31+
"type": "string",
32+
"minLength": 1
4433
}
4534
}
4635
},

‎packages/cli/src/commands/doctor-opencode.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ export async function runDoctor(
918918

919919
// Relocate graduated feature flags out of the (retired) experimental.*
920920
// namespace to their new homes:
921-
// - temporal_awareness / caveman_text_compression → top-level keys
921+
// - temporal_awareness / caveman_text_compression / mural → top-level keys
922922
// - auto_search / git_commit_indexing → memory.* (recall features)
923923
// We preserve the user's explicit values so opt-ins/opt-outs survive;
924924
// the destination wins when a user has already started graduating,
@@ -952,6 +952,7 @@ export async function runDoctor(
952952
if (experimental) {
953953
relocateGraduated("temporal_awareness", mcConfig, "");
954954
relocateGraduated("caveman_text_compression", mcConfig, "");
955+
relocateGraduated("mural", mcConfig, "");
955956
const memoryDest = (mcConfig.memory as Record<string, unknown> | undefined) ?? {};
956957
relocateGraduated("auto_search", memoryDest, "memory.");
957958
relocateGraduated("git_commit_indexing", memoryDest, "memory.");

‎packages/dashboard/src/components/ConfigEditor/ConfigEditor.tsx‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ const FIELD_DEFS: FieldDef[] = [
9292
description: "How long Magic Context TUI toasts stay visible.",
9393
section: "General",
9494
},
95+
// Mural
96+
{
97+
key: "mural.enabled",
98+
label: "Mural Enabled",
99+
type: "boolean",
100+
description:
101+
"Render a deterministic image of project memories that did not fit the context budget.",
102+
section: "Mural",
103+
},
104+
{
105+
key: "mural.model",
106+
label: "Cue Compressor Model",
107+
type: "string",
108+
description:
109+
"Model used to compress each memory into a mural cue. The mural image itself is rendered deterministically.",
110+
section: "Mural",
111+
},
95112
// Thresholds
96113
// cache_ttl and execute_threshold_percentage are rendered as custom PerModelField components
97114
// Tags & cleanup
@@ -214,6 +231,7 @@ function readFallbackModels(formData: Record<string, unknown>, path: string): st
214231

215232
const SECTION_ICONS: Record<string, string> = {
216233
General: "⚙️",
234+
Mural: "🖼️",
217235
Thresholds: "⚡",
218236
"Tags & Cleanup": "🏷️",
219237
Historian: "📜",
@@ -386,9 +404,10 @@ function ConfigForm(props: {
386404
// Section order: Tags & Cleanup goes last (rendered after agent cards)
387405
const SECTION_ORDER: Record<string, number> = {
388406
General: 0,
389-
Thresholds: 1,
390-
Historian: 2,
391-
Memory: 3,
407+
Mural: 1,
408+
Thresholds: 2,
409+
Historian: 3,
410+
Memory: 4,
392411
"Tags & Cleanup": 99,
393412
};
394413

@@ -427,6 +446,7 @@ function ConfigForm(props: {
427446
"sqlite",
428447
"system_prompt_injection",
429448
"caveman_text_compression",
449+
"mural",
430450
]) {
431451
if (typeof formData()[key] === "object" && formData()[key] != null) {
432452
merged[key] = {

‎packages/dashboard/src/components/ConfigEditor/config-field-coverage.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const RENDERED_PREFIXES: readonly string[] = [
2121
"enabled",
2222
"language",
2323
"toast_duration_ms",
24+
// Mural
25+
"mural",
2426
// Thresholds (custom PerModelField widgets)
2527
"cache_ttl",
2628
"execute_threshold_percentage",
@@ -87,6 +89,18 @@ export const OMITTED_BY_DESIGN: Readonly<Record<string, string>> = {
8789
shadow_embedding: "developer-only shadow embedding lane; raw JSONC and never a dashboard knob",
8890
transform_mode:
8991
"experimental project-wide Rust runtime cutover; requires user-level subc configuration and is not exposed in the dashboard yet",
92+
allow_home_project:
93+
"user-only home-directory identity setting; raw JSONC because it is ignored from project configs",
94+
output_reserve:
95+
"user-only output-token reservation; raw JSONC because project configs cannot change it",
96+
"storage.enforce_private_permissions":
97+
"user-only shared-storage permission policy; raw JSONC because project configs cannot change it",
98+
fail_closed_blocking:
99+
"user-only inoperability policy; raw JSONC because project configs cannot change it",
100+
"compaction.enabled":
101+
"user-only context-management ownership policy; raw JSONC because project configs cannot change it",
102+
"pi.subagent_extensions":
103+
"user-only Pi extension allowlist; raw JSONC because project configs cannot choose child extensions",
90104
};
91105

92106
/**

‎packages/docs/src/content/docs/concepts/mural.md‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The mural is a complement to the text `<project-memory>` block, not a replacemen
1919

2020
Injection is gated on all of the following:
2121

22-
1. **`experimental.mural.enabled`** is `true`.
22+
1. **`mural.enabled`** is `true`.
2323
2. The **outgoing model accepts images** (vision capability from provider metadata). Unknown capability fails closed: text-only baseline, no error.
2424
3. Enough memories have **current compressed cues** (coverage gate — see below).
2525
4. There is a non-empty **overflow set** after the normal memory text budget trim.
@@ -33,7 +33,7 @@ Each memory needs a short **cue** before it can appear on the mural. The dreamer
3333
- Runs per memory (not one giant prompt for the whole pool).
3434
- Compresses the full memory text into a mural-sized line.
3535
- **Caches by content hash** — unchanged memories are not re-compressed.
36-
- Uses **`experimental.mural.model`** when set, otherwise the dreamer model ladder.
36+
- Uses **`mural.model`** when set, otherwise the dreamer model ladder.
3737

3838
Until a memory has a current cue, it is skipped for mural selection even if it overflowed the text budget.
3939

@@ -50,20 +50,18 @@ The mural does not render from a half-empty cue pool. Rendering is skipped until
5050

5151
```jsonc
5252
{
53-
"experimental": {
54-
"mural": {
55-
"enabled": true,
56-
// Optional: model for compress-cues only. The PNG itself is deterministic.
57-
"model": "anthropic/claude-haiku-4-5"
58-
}
53+
"mural": {
54+
"enabled": true,
55+
// Optional: model for compress-cues only. The PNG itself is deterministic.
56+
"model": "anthropic/claude-haiku-4-5"
5957
}
6058
}
6159
```
6260

6361
| Key | Default | Meaning |
6462
|-----|---------|---------|
65-
| `experimental.mural.enabled` | `false` | Master switch for mural injection and compress-cues. |
66-
| `experimental.mural.model` | — | Model used by compress-cues. Falls back to the dreamer model when unset. |
63+
| `mural.enabled` | `false` | Master switch for mural injection and compress-cues. |
64+
| `mural.model` | — | Model used by compress-cues. Falls back to the dreamer model when unset. |
6765

6866
## Requirements and limits
6967

‎packages/docs/src/content/docs/reference/configuration.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ Global on/off switches for the plugin and its agent-facing surface.
4040
| `todowrite` | object | — | Pi-only todowrite tool and overlay controls. Pi registers tools and widgets at extension boot, so changing this after /cd requires /reload or restart. |
4141
| `todowrite.enabled` | boolean | `true` | Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there. |
4242
| `todowrite.overlay` | boolean | `true` | Pi only: show the persistent todo overlay above the editor while tasks are active. |
43+
| `mural` | object | — | Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task. |
44+
| `mural.enabled` | boolean | `false` | |
45+
| `mural.model` | string | — | Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model). |
4346

4447
## Context management
4548

@@ -259,9 +262,6 @@ Behavior tuning most installs never need to touch.
259262

260263
| Key | Type | Default | Description |
261264
|---|---|---|---|
262-
| `experimental.mural` | object | — | Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task. |
263-
| `experimental.mural.enabled` | boolean | `false` | |
264-
| `experimental.mural.model` | string | — | Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model). |
265265
| `toast_duration_ms` | number (0–60000) | `5000` | TUI toast lifetime in milliseconds for Magic Context notifications. Set to 0 to disable Magic Context toasts entirely (min: 0, max: 60000, default: 5000) |
266266
| `subc.connection_file` | string | — | Path to the owner-only subc connection file. |
267267
| `fail_closed_blocking` | boolean | `true` | When Magic Context cannot operate (schema fence mismatch, storage open/migration failure), block the primary-session prompt with a loud recovery error instead of silently degrading to native compaction. Default true. Set false only to restore the old degrade-silently behavior (not recommended). USER-LEVEL ONLY — ignored in project config for security. Requires a restart. |

‎packages/pi-plugin/PARITY.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ lookup. Pi does not warm that cache (see §14); when metadata is absent the gate
856856
never injects the mural image until vision metadata is available; OpenCode warms
857857
the cache from its SDK at startup.
858858

859-
**Config:** both honor `experimental.mural.enabled` (and `experimental.mural.model`
859+
**Config:** both honor `mural.enabled` (and `mural.model`
860860
for the compress-cues dreamer task). No intentional per-provider image-part
861861
blacklist today — every Pi serializer path that accepts user image content takes
862862
raw base64 the same way.

‎packages/pi-plugin/src/context-handler.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ export interface PiInjectionOptions {
957957
injectDocs?: boolean;
958958
injectionBudgetTokens: number;
959959
temporalAwareness?: boolean;
960-
/** experimental.mural.enabled — on-demand deterministic mural image on HARD folds. */
960+
/** mural.enabled — when enabled, generate a deterministic image of memories that did not fit the context budget whenever the system performs a full (HARD) context fold. */
961961
muralEnabled?: boolean;
962962
}
963963

@@ -4034,7 +4034,7 @@ interface RunPipelineArgs {
40344034
* injection budget. Drives compartment tier demotion in renderM0Pi. */
40354035
historyBudgetTokens?: number;
40364036
temporalAwareness?: boolean;
4037-
/** experimental.mural.enabled — on-demand deterministic mural image on HARD folds. */
4037+
/** mural.enabled — when enabled, generate a deterministic image of memories that did not fit the context budget whenever the system performs a full (HARD) context fold. */
40384038
muralEnabled?: boolean;
40394039
};
40404040
/**

‎packages/pi-plugin/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ async function startPiMagicContextRuntime(
10041004
injectDocs: cfg.dreamer?.inject_docs !== false,
10051005
injectionBudgetTokens: cfg.memory.injection_budget_tokens,
10061006
temporalAwareness: cfg.temporal_awareness === true,
1007-
muralEnabled: cfg.experimental?.mural?.enabled === true,
1007+
muralEnabled: cfg.mural.enabled === true,
10081008
},
10091009
scheduler: {
10101010
executeThresholdPercentage: cfg.execute_threshold_percentage,

‎packages/pi-plugin/src/inject-compartments-pi.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ export interface PiM0M1State {
376376
userProfileBudgetTokens?: number;
377377
/** Provider-side cache-eviction signals for HARD-bust detection. */
378378
hardSignals?: PiM0HardSignals;
379-
/** Experimental mural feature switch (`experimental.mural.enabled`). When
379+
/** Mural feature switch (`mural.enabled`). When
380380
* true and the fold's model accepts images, HARD materialization resolves
381381
* + renders the deterministic mural on demand and folds its image into the
382382
* cached baseline. Defer passes replay the baked-in bytes without re-render. */

0 commit comments

Comments
 (0)