supercode-harness 0.4.3

The optional native Supercode agent and tool harness
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
//! §4 "Presets" (`docs/composable-harness/COMPOSABLE-HARNESS-DESIGN.md`) —
//! P2 of the composable-harness migration (design §5.2, phase **P2**).
//!
//! The six reserved built-in presets, compiled in as TOML consts, transcribed
//! faithfully from the design doc's §4.1-§4.5 TOML blocks (and §4's intro
//! paragraph for the sixth, `supercode-default`, which the doc defines by
//! prose rather than a TOML block — S10 fix: "`pi-core` MINUS `{trust,
//! session_tree, session_share, server, plugins}`", not "`pi-core` plus
//! extras").
//!
//! **Syntax fix (P2 judgment call).** The design doc's `[capabilities.X]
//! { enabled = true, ... }` lines combine a TOML table-HEADER and an
//! inline-table VALUE on one line, which is not valid TOML (verified
//! empirically against the `toml` crate: `invalid table header, expected
//! newline`). Naively rewriting them as dotted-key assignments
//! (`capabilities.X = { ... }`) is also unsafe wherever such a line appears
//! *after* an already-open `[capabilities.permissions]` table (cc-parity,
//! cx-parity, oc-parity all have this): a dotted key inside an open table is
//! relative to the CURRENT table, so `capabilities.permissions.sandbox = {..}`
//! written while inside `[capabilities.permissions]` nests as
//! `capabilities.permissions.capabilities.permissions.sandbox`, silently
//! corrupting the structure. The transcription below instead expands every
//! `[capabilities.X] { k = v, ... }` shorthand into the equivalent explicit
//! form — a real `[capabilities.X]` table header (always root-absolute,
//! never context-relative) followed by `k = v` lines — which is safe
//! regardless of surrounding context and preserves the exact same resolved
//! structure. Every block below is verified to parse into `HarnessConfig`
//! in this module's tests, and each preset's resolved shape is golden-tested
//! against §4.6's per-preset verdicts in
//! `crates/harness/tests/composable_presets.rs`.
//!
//! Comments from the design doc are preserved verbatim inside each TOML
//! block for traceability back to the source section.

/// `pi-core` — design §4.1.
pub const PI_CORE_TOML: &str = r#"# built-in preset: pi-core — the §1 core with pi's exact defaults, plus pi's four kept extras.
schema_version = 1

[core]
effort = "medium"                       # pi default thinking level (pi§3, src:core/defaults.ts:3)
max_tool_output_bytes = 51200           # pi's shared truncation policy: 50KB / 2000 lines (pi§1, truncate.ts)
project_context = true                  # AGENTS.md/CLAUDE.md global + ancestor walk (pi§2 "Context files")
env_context = true                      # pi appends Current date + cwd to the prompt (pi§2, system-prompt.ts:88-170)

[core.retry]                            # pi agent-level auto-retry (pi§3)
enabled = true
max_retries = 3
base_delay_ms = 2000

[core.tools]
enabled = ["read_file", "bash", "edit_file", "write_file"]  # pi's default-ACTIVE four (pi§1, src:core/sdk.ts:245)
schema_tier = "full"
[core.tools.read_file]
multimodal = true                       # pi read returns images as attachments (pi§1, read.ts)

[core.skills]
enabled = true                          # agentskills.io discovery + progressive disclosure (pi§2; D-7 met by read_file)

[core.compaction]
enabled = true
after_messages = 0                      # pi's trigger is token pressure, never message count (pi§2)
reserve_tokens = 16384                  # compaction.reserveTokens default (pi§2, pi§6)
keep_recent_tokens = 20000              # compaction.keepRecentTokens default (pi§2)
summarize = true                        # structured Goal/Constraints/Progress/… summary (pi§2, compaction.md)

[core.steering]
steering_mode = "one-at-a-time"         # pi delivery-mode defaults (pi§3 "Message queue", pi§6)
follow_up_mode = "one-at-a-time"

# ---- modules ON (each is on pi's kept-list, pi§10 closing) ----
[capabilities.trust]
enabled = true
default = "ask"
# pi's ONE built-in gate (pi§4, trust-manager.ts; defaultProjectTrust "ask")
[capabilities.session_tree]
enabled = true
branch_summaries = true
labels = true
# THE core pi feature (pi§10; D5)
[capabilities.session_share]
enabled = true
# /share gist public link (pi§8); human /export HTML is core now (§1.6, S6) and on regardless
[capabilities.server]
enabled = true
# --mode json / --mode rpc embedding ladder (pi§8, pi§10)
[capabilities.plugins]
enabled = true
# everything-is-an-extension (pi§7; D-10 dep satisfied by trust above)
[capabilities.tui]
enabled = true
# §1.9 recorded deviation, default-on in parity presets

# ---- notable OFFs (each a pi FIRST-PARTY omission, pi§10 / catalog §3) ----
[capabilities.tools_search]
enabled = false
# grep/find/ls exist but are OPT-IN even in pi (pi§1 "--tools"); one line re-enables
[capabilities.mcp]
enabled = false
# "intentionally does not include built-in MCP" (pi§7)
[capabilities.subagents]
enabled = false
# example extension only (pi§3 "NO subagents")
[capabilities.permissions]
enabled = false
approval = "never"
sandbox = "danger_full_access"
                                # pi has NO popups/rules/sandbox (pi§4; README "Permissions & containerization").
                                # C3 fires its MANDATORY warning here BY DESIGN — pi's own docs say containers, not trust in the harness.
[capabilities.plan_mode]
enabled = false
# example ext only (pi§10)
[capabilities.todos]
enabled = false
# example ext only (pi§10)
[capabilities.tools_background]
enabled = false
# "tmux instead" (pi§10)
[capabilities.tools_web]
enabled = false
# web search ships as a SKILL in pi (pi§10)
[capabilities.checkpoint]
enabled = false
# git-checkpoint example ext only (pi§5)
[capabilities.memory]
enabled = false
# no memory subsystem (pi§2)
[capabilities.hooks]
enabled = false
# pi's "hooks" are code extensions, not config-registered commands
[capabilities.deferred_tools]
enabled = false
[capabilities.cache]
enabled = false
[capabilities.reduction]
enabled = false
# supercode-only OPTIONAL policies off; A7 truncation + rehydrate stay always-on core regardless (§1.13, S1/S7 — no longer a D-8/D-8-error risk)
[capabilities.model_catalog]
enabled = false
[capabilities.model_oauth]
enabled = false
# pi HAS /login OAuth (pi§9) — deferred module 27, recorded gap
"#;

/// `cc-parity` — design §4.2.
pub const CC_PARITY_TOML: &str = r#"# built-in preset: cc-parity — Claude Code's default surface, composed.
schema_version = 1

[core]
model = "anthropic/claude-opus-4-8"     # CC account-default Opus 4.8 (cc§9 "Account-type defaults")
effort = "medium"
env_context = true                      # CC startup context: cwd/git status (cc§2 "Startup context")
project_context = true                  # CLAUDE.md tiers + directory walk (cc§2); global tier included (§1.4)
nested_instructions = true               # S6/S12 home: subdir CLAUDE.md loaded on demand, CC default (catalog:84) — closes a gap-ledger row
instruction_imports  = true              # S6/S12 home: `@path` imports, depth 4, CC default (catalog:85) — closes a gap-ledger row

[core.tools]
enabled = ["read_file", "bash", "edit_file", "write_file"]
schema_tier = "full"
[core.tools.read_file]
multimodal = true                       # CC Read renders images/PDFs/notebooks (cc§1 Read)
[core.tools.edit_file]
require_read_before_edit = true         # S6/S12 home: CC Edit refuses unless the file was read this conversation (catalog:32) — closes the cc-parity gap-ledger row
notebook_aware = true                   # S6/S12 home: NotebookEdit cell-level replace/insert/delete (catalog:40) — closes the cc-parity gap-ledger row
[core.tools.bash]
timeout_secs = 120                      # CC default 2 min, model-raisable (cc§1 Bash)

[core.skills]
enabled = true                          # SKILL.md dirs + commands, descriptions-only until invoked (cc§7 Skills)

[core.compaction]
enabled = true
summarize = true                        # CC auto-compaction near limit + /compact [instructions] (cc§2)
reserve_tokens = 16384                  # CC's threshold is pct-based (CLAUDE_CODE_AUTOCOMPACT_PCT_OVERRIDE, cc§2); reserve is our §1.5 equivalent

# ---- modules ON ----
[capabilities.tools_search]
enabled = true
glob = true
content_search = true
list_dir = false
                               # Glob + Grep, promptless read-class (cc§1); list_dir OFF (S15 fix) — CC lists dirs via Bash/Glob, Read rejects directories (catalog:33 fn²), so the module's dir-listing sub-tool would be a capability CC users never see
[capabilities.tools_web]
enabled = true
fetch = true
search = true
# WebFetch + WebSearch (cc§1)
[capabilities.tools_question]
enabled = true
# AskUserQuestion (cc§1)
[capabilities.todos]
enabled = true
persist = true
# Task*/TodoWrite checklist, persists across compaction (cc§1, cc§3)
[capabilities.plan_mode]
enabled = true
# Shift+Tab / EnterPlanMode read-only mode (cc§3); dep met by permissions.rules below
[capabilities.subagents]
enabled = true
max_depth = 2
background = true
background_prompts = "parent"
                               # Agent tool; background-by-default v2.1.198+, nested allowed (cc§1, cc§3)
                               # C6 resolved via the schema key (S2 fix, not prose): background_prompts = "parent" — background children surface prompts in the parent session (cc§3, claude-code.md:98)
[capabilities.tools_background]
enabled = true
# run_in_background + Ctrl+B (cc§1, cc§8); C6: same parent-surfaced queue as subagents.background_prompts above

[capabilities.permissions]
enabled = true
approval = "untrusted"                  # CC tiered default: read-only never prompts, Bash/edits prompt first-use (cc§4 "Tiered defaults")
                                        # No C3: approval != never.
auto_approved_tools = ["read_file", "glob", "search"]   # CC read-only tier (cc§4); `list_dir` removed (S15 — module tool is off above)
# module 12 in table form (S5): OS sandbox OFF (CC's `/sandbox` is opt-in, cc§4), fs tier unconfined.
# One key `permissions.sandbox` — the table form, not the bare-scalar shorthand, so no collision.
[capabilities.permissions.sandbox]
enabled = false
tier = "danger_full_access"
# flip enabled=true + set network.*/escalation for `/sandbox` parity later
[capabilities.permissions.rules]
enabled = true                          # deny→ask→allow first-match IS the CC algebra — native, no translation (C5 decision; cc§4 "Rule sets & evaluation")
deny  = []
ask   = []
allow = []                              # CC ships empty rule sets; "don't ask again" persists into allow at runtime (cc§4)
[capabilities.permissions.protected_paths]
enabled = true                          # never-auto-approved set (cc§4 "Protected paths")
# Rule-layer floor: file-tools + bash redirect targets + apply_patch + known
# argv-writers (tee/dd/cp/mv/install/sed -i/truncate/ln); an opaque or
# dynamic bash write is forced to Ask. Complete OS-level write confinement
# is `permissions.sandbox`'s job (module 10), not this table's — see
# `crate::permissions` module doc / `Config::permissions_protected_paths`.
paths = [".git/**", ".env*", ".claude/**", ".vscode/**", ".idea/**", "~/.claude/settings*"]

[capabilities.trust]
enabled = true
default = "ask"
# workspace trust gates project allow-rules (cc§4)
[capabilities.mcp]
enabled = true
# stdio/HTTP/OAuth, resources, prompts-as-commands (cc§7)
[capabilities.deferred_tools]
enabled = true
core = ["read_file", "bash", "edit_file", "write_file", "glob", "search", "update_plan"]
                               # CC defers MCP tool definitions BY DEFAULT behind ToolSearch (cc§7 "Tool search"); builtins stay eager
[capabilities.hooks]
enabled = true
# config-registered lifecycle hooks (cc§7: 30 events; module ships the CC-compatible subset first)
[capabilities.memory]
enabled = true
# auto memory MEMORY.md + topic files (cc§2); D-9 dep → model_catalog below
[capabilities.checkpoint]
enabled = true
# per-prompt file-history-snapshot → /rewind (cc§5)
[capabilities.session_tree]
enabled = true
branch_summaries = false
labels = false
                               # CC has the tree DATA MODEL (uuid/parentUuid, cc§5) + /rewind; summaries/labels are pi-isms
[capabilities.model_catalog]
enabled = true
small_model = "anthropic/claude-haiku-4-5"
fallback = []
                               # aliases + ANTHROPIC_SMALL_FAST_MODEL + fallback chains (cc§9)
[capabilities.tui]
enabled = true

# ---- notable OFFs ----
[capabilities.tools_apply_patch]
enabled = false
# CC is edit-only (C1; catalog §5 conflict 1)
[capabilities.tools_persistent_shell]
enabled = false
[capabilities.lsp]
enabled = false
# CC's LSP is inactive until a plugin installs it (cc§1) — off matches default
[capabilities.formatters]
enabled = false
[capabilities.session_share]
enabled = false
# no PUBLIC share links in CC (D5 OC+PI-only row); `/export`+`/copy` are core now (§1.6 `export_format`, S6) and stay on regardless
[capabilities.server]
enabled = false
# CC has no local HTTP server surface; SDK is in-process
[capabilities.reduction]
enabled = false
[capabilities.cache]
enabled = false
# CC caching is provider-automatic (cc§2); plan machinery is a token-saver concern
[capabilities.structured_output]
enabled = false
# --json-schema is headless-only surface; enable per-run
[capabilities.model_oauth]
enabled = false
# recorded gap: CC's DEFAULT auth is subscription OAuth (cc§9) — module 27 deferred
"#;

/// `cx-parity` — design §4.3.
pub const CX_PARITY_TOML: &str = r#"# built-in preset: cx-parity — Codex's default surface, composed.
schema_version = 1

[core]
effort = "medium"                       # model_reasoning_effort default tier (cx§6, cx§9)
env_context = true                      # <environment_context> block: cwd/sandbox/approval (cx§2)
project_context = true                  # AGENTS.md hierarchy, root-down concat, 32KiB cap (cx§2)
# P2 placement fix: §4.3's own TOML block places `shell_env_snapshot` under
# `[core.tools]`, but §3.1's schema (the "annotated, exhaustive" canonical
# definition, line ~598) defines it as a direct `[core]` scalar, not a
# `core.tools.*` key — `CoreToolsConfig` has no such field, so a literal
# under-`[core.tools]` placement would silently parse-and-drop it. Moved
# here to match §3.1 (the schema doc doesn't have this key twice with two
# different homes; §4.3 is corrected to agree with it).
shell_env_snapshot = true               # S6/S12 home: shell-env snapshotting, cx stable-on feature (catalog:338) — closes a gap-ledger row
# C4 (catalog §5 conflict 4): Codex's base prompt VARIES BY APPROVAL MODE (cx§2: "proactively run
# tests only under never"). This preset pins prompt + approval together; when CONTINUING an
# imported rollout, the emulate path replays the rollout's own persisted base_instructions
# verbatim (session_meta carries them — cx§2:101; supercode SessionMeta.system_prompt), which is
# exact prompt parity by construction rather than imitation.

[core.tools]
enabled = ["bash", "view_image"]        # Codex has NO read/write/edit/glob/grep function tools:
                                        # reads via shell (cat, rg), writes via apply_patch (cx§1 "File reads/writes"; D1 footnote ¹).
                                        # Disabling edit/write advertising is ALSO the C1 resolution.
                                        # `view_image` (S6/S12 home, catalog:28) closes the gap-ledger row: with `read_file` off, cx-parity
                                        # would otherwise have NO image-input pathway at all, unlike stock Codex's dedicated tool.
schema_tier = "full"

[core.skills]
enabled = true                          # SKILL.md discovery, $skill mentions (cx§7 Skills).
                                        # D-7 (S3-amended, no longer a judgment call): the read pathway is bash (`cat`) in the codex
                                        # shape — §2.1's D-7 now names read_file|bash explicitly; the resolver warns, doesn't error.

[core.compaction]
enabled = true
summarize = true                        # /compact + auto-compaction at model_auto_compact_token_limit (cx§2)

# ---- modules ON ----
[capabilities.tools_persistent_shell]
enabled = true
# exec_command/write_stdin PTY unified exec (cx§1); supercode has it (builtins.rs:981-984)
[capabilities.tools_apply_patch]
enabled = true
per_model = true
                               # freeform envelope, default write path (cx§1); per_model honors C1 via model_catalog bits (cx§9)
[capabilities.todos]
enabled = true
persist = true
# update_plan is ALWAYS registered (cx§1); goals (S6/S12 home, catalog:138, cx `/goal`) is the persistent-objective variant of this same module — closes a gap-ledger row
[capabilities.tools_web]
enabled = true
fetch = false
search = true
                               # Codex has hosted web_search but NO web-fetch tool (cx§1); cached mode default
[capabilities.tools_background]
enabled = true
# background terminals, /ps //stop (cx§1); C6 (S8-corrected defense): under `model_requested`, tools run sandboxed WITHOUT prompting unless the model itself escalates — from a background task's perspective that's an auto-run default, satisfying C6's auto-policy requirement without needing a separate allow-list
[capabilities.subagents]
enabled = true
max_depth = 1
background = false
                               # multi_agent default-on, agents.max_depth default 1 (cx§1, cx§6)
[capabilities.deferred_tools]
enabled = true
core = ["bash", "shell", "apply_patch", "update_plan"]
                               # ToolExposure::Deferred + native tool_search is Codex's own mechanism (cx§1)
[capabilities.structured_output]
enabled = true
# --output-schema final-response contract (cx§8); module 33, Config.response_format

[capabilities.permissions]
enabled = true
approval = "model_requested"            # S8 fix: Codex `on-request` default is "the MODEL decides when to ask" (cx§4, protocol.rs:921-924) — NOT supercode's `OnRequest` (client-side allowlist check, config.rs:39-40, 299-302); using the wrong enum value would prompt on every non-allowlisted tool call where stock Codex prompts almost never. `model_requested` is the NEW distinct mode (§3.2) the module must re-implement escalation-initiated-by-the-model for.
sandbox  = "workspace_write"            # writes in cwd + tmp, no network (cx§4); RECOMMENDED POSTURE (S16 fix), not upstream's labeled default — codex.md names no sandbox mode "(default)" (unlike approval); this is upstream's own steered guidance ("prefer --sandbox workspace-write", the deprecated --full-auto warning)
[capabilities.permissions.rules]
enabled = true                          # execpolicy .rules allow/prompt/forbidden → translated into deny→ask→allow (C5)
deny  = []
ask   = []
allow = []
[capabilities.permissions.protected_paths]
enabled = true
# Rule-layer floor (file-tools + bash redirect targets + apply_patch + known
# argv-writers; opaque/dynamic bash writes forced to Ask) — NOT the same as
# cx's `workspace_write` OS sandbox read-only mount above; see
# `crate::permissions` module doc for exactly what is/isn't covered here.
paths = [".git/**", ".codex/**"]        # read-only even inside writable roots (cx§4 workspace-write)

[capabilities.trust]
enabled = true
default = "ask"
# [projects] trust_level gate + hook hash-trust (cx§4:153, cx§7)
[capabilities.mcp]
enabled = true
serve = true
# full client stack (cx§7); serve = codex mcp-server analog (module 16)
[capabilities.hooks]
enabled = true
# CC-compatible 10-event shape, hash-trusted (cx§7 "Lifecycle hooks")
[capabilities.model_catalog]
enabled = true
# capability bits (apply_patch_tool_type, supports_search_tool) drive
                               # per-model tool swaps — the C1 resolution machinery (cx§9 "Model catalog")
[capabilities.tui]
enabled = true

# ---- notable OFFs ----
[capabilities.tools_search]
enabled = false
# no glob/grep tools; "prefer rg" via shell is prompt guidance (cx§2)
[capabilities.tools_question]
enabled = false
# request_user_input is experimental-gated at the pin (cx§1)
[capabilities.plan_mode]
enabled = false
# /plan is effort-tier steering, not a CC/OC restriction mode (cx§6; catalog D1 CC+OC)
[capabilities.memory]
enabled = false
# [features].memories = false default (cx§6, cx§7)
[capabilities.checkpoint]
enabled = false
# no shadow-git; ghost_snapshot is a legacy no-op (cx§6)
[capabilities.session_tree]
enabled = false
# rollout is STRICTLY LINEAR (C7); fork = truncate+copy (D5 footnote ¹³)
[capabilities.session_share]
enabled = false
[capabilities.lsp]
enabled = false
[capabilities.formatters]
enabled = false
[capabilities.server]
enabled = false
# app-server parity is out of preset scope — see gaps
[capabilities.reduction]
enabled = false
[capabilities.cache]
enabled = false
[capabilities.model_oauth]
enabled = false
# ChatGPT-subscription login (cx§9) — module 27 deferred
"#;

/// `oc-parity` — design §4.4.
pub const OC_PARITY_TOML: &str = r#"# built-in preset: oc-parity — opencode's default surface, composed.
schema_version = 1

[core]
env_context = true
project_context = true                  # AGENTS.md + instructions[] concat (oc§6)
nested_instructions = true              # S6/S12 home: nested AGENTS.md auto-attached only for touched-file dirs, oc default (catalog:84; opencode.md:34 "nested-AGENTS.md") — closes an oc-parity gap-ledger row
instruction_imports  = true             # S6/S12 home: `instructions[]` config imports, oc default (catalog:85; opencode.md:367) — closes an oc-parity gap-ledger row
max_tool_output_bytes = 51200           # tool_output.max_bytes default 51200 / 2000 lines (oc§1 Truncate service)

[core.session]
auto_title = true                       # S6/S12 home: hidden title+summary agents (deny-all utility agents) on small_model, oc default (catalog:150; opencode.md:169-170,235) — closes an oc-parity gap-ledger row; small_model is "" below so this falls back to the main model per D-9 until a cheap model is configured

[core.tools]
enabled = ["read_file", "bash", "edit_file", "write_file"]  # oc registry core (oc§1; read subsumes ls)
schema_tier = "full"
[core.tools.read_file]
multimodal = true                       # images/PDFs as attachments (oc§1 read)
[core.tools.bash]
timeout_secs = 120                      # flags.bashDefaultTimeoutMs default 120000 (oc§1 bash)

[core.skills]
enabled = true                          # skill tool + .opencode/skills + remote registries (oc§7)

[core.compaction]
enabled = true
summarize = true                        # compaction{auto,prune,…} (oc§6)

# ---- modules ON ----
[capabilities.tools_search]
enabled = true
# glob + grep via ripgrep (oc§1)
[capabilities.todos]
enabled = true
persist = true
# todowrite → SQLite todo table (oc§1)
[capabilities.tools_web]
enabled = true
fetch = true
search = false
                               # webfetch is default; websearch only under the Zen provider / exa flags (oc§1 "webSearchEnabled")
[capabilities.subagents]
enabled = true
max_depth = 2
background = false
                               # task tool → child session via parentID, resumable task_id (oc§1); background is env-gated experimental → off
[capabilities.tools_apply_patch]
enabled = true
per_model = true
                               # THE C1 precedent: swapped in (edit/write out) for gpt-* models (oc§1 apply_patch; catalog §5 conflict 1)
[capabilities.plan_mode]
enabled = false
# S18 fix (flipped from `true`): opencode's plan_enter/plan_exit TOOLS — exactly what this module is defined by (§2 module 8) — are DENY-BY-DEFAULT at the pin (opencode.md:251), and this preset's own translated rule set below denies them. What oc actually runs by default is the LEGACY generation: the plan agent is a permission-ruleset agent (edit denied) — already expressible as an agent-scoped `permissions.rules` restriction, not the tool-based `plan_mode` module. Enabling `plan_mode` here would contradict oc's own deny-default; off is the honest reading.

[capabilities.permissions]
enabled = true
approval = "on_request"                 # ask-flow with once|always|reject replies (oc§4 "Ask/approve flow")
sandbox  = "danger_full_access"         # opencode has NO OS sandbox (catalog D4: sandbox is CC+CX only)
[capabilities.permissions.rules]
enabled = true
# opencode's default policy, TRANSLATED per the C5 decision (last-match-wins → deny→ask→allow
# first-match). Source policy (oc§4 "Default policy"): {"*": allow} with carve-outs
# doom_loop: ask, external_directory: ask, question: deny, plan_enter/plan_exit: deny,
# read {*.env: ask, *.env.*: ask, *.env.example: allow}.
#
# S4 fix — this is NOT "the same fixed point" as oc's last-match algebra, and is recorded honestly
# as THREE NAMED DEVIATIONS rather than claimed as exact parity:
#   1. `.env.example` → ASK here, not ALLOW. Under first-match deny→ask→allow, a read of
#      `.env.example` matches the ask-rule `read_file(*.env.*)` (glob matches) BEFORE the allow
#      list is ever consulted, so it asks where stock opencode allows. The engine's rule grammar
#      has no specificity/negation to express "ask unless a more-specific allow" — fixing this
#      would require adding that to the grammar (not done here); the deviation is in the SAFE
#      direction (stricter) and is named, not hidden.
#   2. `doom_loop` is NOT a rule-language pattern at all — it's a repetition TRIGGER (same tool
#      call repeated), not a tool/path match. Routed instead to its actual mechanism: the P4
#      doom-loop breaker (a call-repetition counter + PreToolHook default, §5.2 P4) — no rule
#      entry for it below.
#   3. `external_directory` is an oc PERMISSION CATEGORY (any tool touching paths outside the
#      worktree), not a tool name — routed instead to its actual permission category: `[core]
#      additional_dirs` (Config.additional_dirs, config.rs:169) governs which extra roots are
#      writable at all; paths outside cwd AND outside `additional_dirs` are simply not reachable,
#      which is a stricter (not equivalent) reading of oc's ask-by-default.
deny  = ["plan_enter", "plan_exit"]      # matches module 8's off-by-default above (S18) and oc's own "plan_enter/plan_exit: deny"
ask   = ["read_file(*.env)", "read_file(*.env.*)"]   # includes .env.example per deviation 1 above (glob matches before any allow)
allow = ["*"]
[capabilities.permissions.protected_paths]
enabled = false
# oc does .env protection through rules (above), not a path module

[capabilities.trust]
enabled = true
default = "ask"
# DELIBERATE SAFETY DEVIATION: opencode LACKS a project trust gate (catalog §3 closing) yet loads
# .opencode/ plugins/tools/commands from the repo. Our resolver treats plugins→trust as a HARD dep
# (D-10: "config-borne code execution without a trust gate is an injection hole") — so oc-parity
# ships the gate ON. This only NARROWS behavior (§3.3 monotonic-tightening spirit); recorded, not hidden.

[capabilities.mcp]
enabled = true
# local/remote/OAuth servers (oc§7)
[capabilities.plugins]
enabled = true
# .opencode/plugin + npm specs (oc§7); dep on trust satisfied above
[capabilities.lsp]
enabled = true
# 38 auto-spawned servers; diagnostics into edit/write results (oc§7, oc§10)
[capabilities.formatters]
enabled = true
diff_back = true
# ~27 format-on-write formatters; diff_back honors C10 (oc§7; oc§10; catalog §5 conflict 10)
[capabilities.checkpoint]
enabled = true
# shadow-git snapshots + revert/unrevert (oc§4 "Snapshots"/"Revert")
[capabilities.session_share]
enabled = true
# share manual|auto|disabled, default manual (oc§5, oc§6)
[capabilities.server]
enabled = true
# the client/server split: every frontend is an HTTP client (oc§8)
[capabilities.model_catalog]
enabled = true
small_model = ""
# models.dev catalog + small_model config key (oc§6, oc§9)
[capabilities.tui]
enabled = true

# ---- notable OFFs ----
[capabilities.tools_question]
enabled = false
# question tool is DENY-by-default outside build/plan agents (oc§1, oc§4)
[capabilities.tools_background]
enabled = false
# background subagents are env-gated experimental at the pin (oc§1)
[capabilities.session_tree]
enabled = false
# oc sessions are parent/child linear, no in-place tree (D5; C7)
[capabilities.memory]
enabled = false
[capabilities.hooks]
enabled = false
# no config-registered hooks; the plugin API is the interception layer (oc§7)
[capabilities.deferred_tools]
enabled = false
# opencode advertises eagerly (D1: deferred is CC+CX)
[capabilities.cache]
enabled = false
[capabilities.reduction]
enabled = false
# oc "prune" is the LOSSY analog (catalog §1 UNIQUE OC note); ours stays off to match, mechanism on per §1.13
[capabilities.structured_output]
enabled = false
[capabilities.model_oauth]
enabled = false
# provider /login flows (oc§9) — module 27 deferred
"#;

/// `token-saver` — design §4.5.
pub const TOKEN_SAVER_TOML: &str = r#"# built-in preset: token-saver — the reduction spine over the minimal core.
schema_version = 1
extends = "pi-core"                     # smallest surface = cheapest surface; every knob below overrides it

[core.tools]
schema_tier = "minimal"                 # TR-8/T5 schema tiering (config.rs:219-225)
# C9 (catalog §5 conflict 9): a GLOBAL minimal tier is a footgun for models trained on exact
# schemas. Per-tool override survives the global — pin any load-bearing tool back:
[core.tools.edit_file]
schema_tier = "full"                    # exact-string edit is the least forgiving schema; keep it verbatim

[core.compaction]
enabled = true
reserve_tokens = 24576                  # trigger earlier than pi's 16384 — spend the summary, save the window
keep_recent_tokens = 10000              # aggressive: half of pi's keep budget (recall traded — see caveats)
summarize = true                        # SpanSummary side-call (reduce.rs:274-289) → small_model below (D-9)

[capabilities.reduction]                # module 23 — ALL genuinely-optional passes on (≡ CLI reduce=true, userconfig.rs:33-38)
enabled = true
# NOTE (S7): no `truncation` key here — A7 ToolOutputTruncated (reduce.rs:95-103) is always-on core
# plumbing (§1.13), never a `[capabilities.reduction]` toggle, in token-saver same as every other preset.
stale_reads = true                      # A8 FileReadElided (reduce.rs:104-111)
diff_reads = true                       # TR-3 FileReadDiffed (reduce.rs:202-217)
duplicates = true                       # TR-2 DuplicateOutput (reduce.rs:228-234)
supersede = true                        # TR-6 Superseded (reduce/supersede.rs)
tool_input_elision = true               # TR-10 ToolInputElided (reduce.rs:148-169)
normalize_output = true                 # T30 OutputNormalized (reduce/normalize.rs)
image_redaction = true                  # A9 ImageRedacted (reduce.rs:112-116) — ON here, off everywhere else
span_summaries = true                   # TR-7 (reduce/summarize.rs; D-9)
handoff = true                          # reduce/handoff.rs — smallest-faithful-context model handoff

[capabilities.deferred_tools]           # module 24 — the FLAGSHIP lever (SPEC.md B6)
enabled = true
core = ["read_file", "bash", "edit_file", "write_file"]  # builtins stay eager; everything else behind tool_search

[capabilities.cache]                    # module 25 — the C2 referee
enabled = true
plan = "imported_prefix"                # CachePlan::ImportedPrefix (config.rs:88-96)
warnings = true                         # cache_warnings (config.rs:227-239): every prefix-churning feature must answer to this

[capabilities.model_catalog]            # module 26 — D-9 consumer
enabled = true
small_model = "anthropic/claude-haiku-4-5"  # compaction summaries + span summaries route here, not the main model
"#;

/// `supercode-default` — design §4 intro (S10 fix).
pub const SUPERCODE_DEFAULT_TOML: &str = r#"# built-in preset: supercode-default — pi-core MINUS {trust, session_tree,
# session_share, server, plugins}, PLUS the six extra with_builtins() builtins
# ON, notify available, reduction off (design §4 intro paragraph, S10 fix: NOT
# "pi-core plus extras" — pi-core itself turns those five modules ON to match
# pi's kept-list, so this preset is pi-core's core knobs UNCHANGED with a
# capability delta). This is what `supercode` resolves to with NO config file
# at all — "today's defaults, named and warned" rather than implicit
# (design:958-960).
schema_version = 1
extends = "pi-core"

# core knobs identical to pi-core (§4.1's [core]/[core.retry]/[core.tools]/
# [core.skills]/[core.compaction]/[core.steering] blocks) — unchanged, nothing
# to override here; inherited verbatim via `extends`.

# ---- the S10 delta over pi-core: OFF (today's CLI has none of these — "—"
# across the board in §2's Today column) ----
[capabilities.trust]
enabled = false
[capabilities.session_tree]
enabled = false
[capabilities.session_share]
enabled = false
[capabilities.server]
enabled = false
[capabilities.plugins]
enabled = false

# ---- the six extra with_builtins() builtins (tools/mod.rs:179-192), ON ----
# list_dir/glob/search -> tools_search; apply_patch -> tools_apply_patch;
# persistent_shell -> tools_persistent_shell; update_plan -> todos.
[capabilities.tools_search]
enabled = true
[capabilities.tools_apply_patch]
enabled = true
# NOT per_model: with_builtins() registers every tool struct unconditionally
# with no per-model filtering at all (§4.6 "faithful to today's actual
# unfiltered default stack") — this is what makes C1's warning fire here,
# honestly, rather than suppressing it with a `per_model` bit today's CLI
# doesn't actually have.
[capabilities.tools_persistent_shell]
enabled = true
[capabilities.todos]
enabled = true

# notify available (today's CLI already ships full notify support end to end
# — userconfig.rs:61-71 — unlike pi-core, which doesn't mention it at all).
[capabilities.notify]
enabled = true

# reduction off (policies only; A7 truncation + rehydrate stay always-on core
# regardless, §1.13) — already off by inheritance from pi-core; restated for
# clarity per the design intro's explicit "reduction off" callout.
[capabilities.reduction]
enabled = false

# permissions stays off too (approval = never, sandbox = danger_full_access)
# — identical to pi-core's own values (config.rs:36-38, tools/mod.rs:40-42);
# restated verbatim so the C3 mandatory warning fires here by the same
# mechanism as pi-core's, naming today's actual default stack rather than
# leaving it implicit (design:971-974).
[capabilities.permissions]
enabled = false
approval = "never"
sandbox = "danger_full_access"
"#;

/// The six reserved built-in preset names (design §4, opening paragraph).
pub const RESERVED_PRESET_NAMES: &[&str] = &[
    "pi-core",
    "cc-parity",
    "cx-parity",
    "oc-parity",
    "token-saver",
    "supercode-default",
];

/// Look up a built-in preset's compiled-in TOML text by name. Returns `None`
/// for anything not one of the six [`RESERVED_PRESET_NAMES`] — the resolver
/// (`configfile.rs` §3.5) falls back to treating the name as a file path in
/// that case (user/global layer only, §3.3).
pub fn lookup(name: &str) -> Option<&'static str> {
    match name {
        "pi-core" => Some(PI_CORE_TOML),
        "cc-parity" => Some(CC_PARITY_TOML),
        "cx-parity" => Some(CX_PARITY_TOML),
        "oc-parity" => Some(OC_PARITY_TOML),
        "token-saver" => Some(TOKEN_SAVER_TOML),
        "supercode-default" => Some(SUPERCODE_DEFAULT_TOML),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::configfile::HarnessConfig;

    /// Every reserved preset's compiled-in TOML must be valid TOML that
    /// parses into a `HarnessConfig` — the design's own claim ("they were
    /// made TOML-valid in the final design commit") verified mechanically
    /// rather than trusted, since the doc's literal `[capabilities.X] { .. }`
    /// shorthand is NOT valid TOML as written (see the module doc comment).
    #[test]
    fn every_reserved_preset_parses() {
        for name in RESERVED_PRESET_NAMES {
            let toml = lookup(name).unwrap_or_else(|| panic!("no TOML for preset `{name}`"));
            HarnessConfig::from_toml_str(toml)
                .unwrap_or_else(|e| panic!("preset `{name}` failed to parse: {e}"));
        }
    }

    /// `lookup` returns `None` for anything not a reserved name (the
    /// resolver's built-in-vs-path branch point, §3.5 step 1).
    #[test]
    fn lookup_returns_none_for_non_preset_names() {
        assert!(lookup("not-a-real-preset").is_none());
        assert!(lookup("./some/path.toml").is_none());
        assert!(lookup("").is_none());
    }

    /// `pi-core` has no `extends` (it is a root); the other five all resolve
    /// somewhere (four are roots too, `token-saver`/`supercode-default`
    /// extend `pi-core`) — sanity-checking the chain shape golden tests will
    /// exercise in full.
    #[test]
    fn token_saver_and_supercode_default_extend_pi_core() {
        let ts = HarnessConfig::from_toml_str(TOKEN_SAVER_TOML).unwrap();
        assert_eq!(ts.extends.as_deref(), Some("pi-core"));
        let sd = HarnessConfig::from_toml_str(SUPERCODE_DEFAULT_TOML).unwrap();
        assert_eq!(sd.extends.as_deref(), Some("pi-core"));
        let pc = HarnessConfig::from_toml_str(PI_CORE_TOML).unwrap();
        assert_eq!(pc.extends, None);
    }
}