mixdog 0.9.138 → 0.9.139

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/package.json +3 -3
  2. package/scripts/model-catalog-audit.mjs +2 -2
  3. package/scripts/tool-stress.mjs +3 -3
  4. package/src/defaults/mixdog-config.template.json +2 -2
  5. package/src/defaults/skills/setup/SKILL.md +11 -9
  6. package/src/help.mjs +1 -1
  7. package/src/lib/rules-builder.cjs +187 -32
  8. package/src/output-styles/common.md +54 -0
  9. package/src/output-styles/detailed.md +8 -17
  10. package/src/output-styles/extreme-minimal.md +9 -6
  11. package/src/output-styles/minimal.md +9 -8
  12. package/src/output-styles/simple.md +10 -14
  13. package/src/rules/lead/01-general.md +4 -2
  14. package/src/rules/lead/02-persona.md +7 -0
  15. package/src/rules/shared/00-general.md +4 -0
  16. package/src/rules/shared/10-tool-workflow.md +20 -0
  17. package/src/rules/shared/20-research.md +6 -0
  18. package/src/rules/shared/30-exploration.md +44 -0
  19. package/src/rules/shared/40-editing.md +12 -0
  20. package/src/rules/shared/50-execution.md +5 -0
  21. package/src/rules/shared/60-verification.md +10 -0
  22. package/src/rules/shared/70-delivery.md +6 -0
  23. package/src/rules/shared/80-memory.md +11 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +8 -4
  25. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  26. package/src/runtime/agent/orchestrator/config.mjs +37 -9
  27. package/src/runtime/agent/orchestrator/context/collect.mjs +19 -9
  28. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +12 -26
  29. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +9 -5
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +6 -2
  31. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +297 -0
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +311 -0
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +383 -0
  34. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +165 -0
  35. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +12 -3
  36. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
  37. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +20 -7
  38. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +29 -2
  39. package/src/runtime/agent/orchestrator/providers/registry.mjs +11 -1
  40. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +1 -1
  41. package/src/runtime/agent/orchestrator/session/context-utils.mjs +24 -10
  42. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -5
  43. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +63 -8
  44. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +23 -10
  45. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -7
  46. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -2
  47. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +69 -6
  48. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  49. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +48 -33
  50. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +4 -4
  51. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +6 -1
  52. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +3 -2
  53. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +79 -2
  54. package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +57 -0
  55. package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +57 -0
  56. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +28 -3
  57. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  58. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +8 -32
  59. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -2
  60. package/src/runtime/memory/lib/core-memory-file.mjs +8 -1
  61. package/src/runtime/memory/lib/http-router.mjs +5 -4
  62. package/src/runtime/memory/lib/tool-call-handler.mjs +1 -1
  63. package/src/runtime/memory/tool-defs.mjs +17 -29
  64. package/src/runtime/shared/agent-route-config.mjs +46 -1
  65. package/src/runtime/shared/err-text.mjs +42 -1
  66. package/src/runtime/shared/llm/http-agent.mjs +1 -1
  67. package/src/runtime/shared/pristine-execution-contract.json +6 -0
  68. package/src/runtime/shared/shell-display.mjs +17 -0
  69. package/src/runtime/shared/task-notification-envelope.mjs +2 -1
  70. package/src/runtime/shared/tool-card-model.mjs +5 -5
  71. package/src/runtime/shared/tool-result-summary.mjs +0 -8
  72. package/src/runtime/shared/tool-surface.mjs +0 -4
  73. package/src/runtime/shared/turn-snapshot.mjs +41 -18
  74. package/src/runtime/{search → web-search}/index.mjs +52 -52
  75. package/src/runtime/{search → web-search}/lib/cache.mjs +2 -2
  76. package/src/runtime/{search → web-search}/lib/config.mjs +3 -7
  77. package/src/runtime/{search → web-search}/lib/formatter.mjs +4 -4
  78. package/src/runtime/{search → web-search}/lib/http-fetch.mjs +1 -1
  79. package/src/runtime/{search → web-search}/lib/ssrf-guard.mjs +1 -0
  80. package/src/runtime/{search → web-search}/lib/state.mjs +2 -2
  81. package/src/runtime/{search → web-search}/lib/web-tools.mjs +1 -1
  82. package/src/runtime/{search → web-search}/tool-defs.mjs +6 -6
  83. package/src/session-runtime/agent-disable.test.mjs +121 -0
  84. package/src/session-runtime/config-lifecycle.mjs +8 -8
  85. package/src/session-runtime/effort.mjs +3 -0
  86. package/src/session-runtime/model-capabilities.mjs +6 -6
  87. package/src/session-runtime/model-recency.mjs +3 -3
  88. package/src/session-runtime/model-route-api.mjs +47 -41
  89. package/src/session-runtime/model-settings-persist.test.mjs +4 -4
  90. package/src/session-runtime/{native-search.mjs → native-web-search.mjs} +72 -61
  91. package/src/session-runtime/output-styles.mjs +13 -3
  92. package/src/session-runtime/provider-models.mjs +31 -31
  93. package/src/session-runtime/quick-model-rows.mjs +50 -50
  94. package/src/session-runtime/{quick-search-models.mjs → quick-web-search-models.mjs} +2 -2
  95. package/src/session-runtime/runtime-core.mjs +82 -72
  96. package/src/session-runtime/runtime-paths.mjs +2 -2
  97. package/src/session-runtime/runtime-tool-routing.mjs +7 -7
  98. package/src/session-runtime/settings-api.mjs +12 -8
  99. package/src/session-runtime/skills-api.mjs +10 -7
  100. package/src/session-runtime/tool-catalog-data.mjs +5 -5
  101. package/src/session-runtime/tool-catalog-schema.mjs +1 -1
  102. package/src/session-runtime/tool-defs.mjs +8 -8
  103. package/src/session-runtime/tool-policy-refresh.mjs +22 -3
  104. package/src/session-runtime/tool-policy-surface.test.mjs +78 -45
  105. package/src/session-runtime/workflow-agents-api.mjs +33 -13
  106. package/src/session-runtime/workflow.mjs +36 -23
  107. package/src/standalone/agent-tool/spawn-flow.mjs +6 -0
  108. package/src/standalone/agent-tool/tool-def.mjs +11 -11
  109. package/src/standalone/hook-bus/handlers.mjs +1 -1
  110. package/src/standalone/memory-runtime-proxy.mjs +1 -1
  111. package/src/standalone/provider-admin.mjs +8 -0
  112. package/src/standalone/session-protocol.mjs +3 -3
  113. package/src/tui/App.jsx +15 -15
  114. package/src/tui/app/app-format.mjs +8 -7
  115. package/src/tui/app/app-view.jsx +1 -1
  116. package/src/tui/app/create-app-pickers.mjs +6 -6
  117. package/src/tui/app/maintenance-pickers.mjs +36 -1
  118. package/src/tui/app/model-options.mjs +2 -0
  119. package/src/tui/app/model-picker.mjs +5 -5
  120. package/src/tui/app/onboarding-steps.mjs +37 -37
  121. package/src/tui/app/route-pickers.mjs +62 -18
  122. package/src/tui/app/settings-picker.mjs +11 -10
  123. package/src/tui/app/slash-commands.mjs +2 -2
  124. package/src/tui/app/slash-dispatch.mjs +6 -6
  125. package/src/tui/app/transcript-row-estimate.mjs +1 -1
  126. package/src/tui/app/use-transcript-activity.mjs +10 -10
  127. package/src/tui/app/use-welcome-prompt-hint.mjs +8 -8
  128. package/src/tui/components/StatusLine.jsx +31 -82
  129. package/src/tui/dist/index.mjs +1635 -1157
  130. package/src/tui/markdown/format-token.mjs +80 -13
  131. package/src/tui/markdown/render-ansi.mjs +27 -3
  132. package/src/tui/markdown/streaming-markdown.mjs +133 -29
  133. package/src/tui/session/agent-envelope.mjs +2 -2
  134. package/src/tui/session/context-state.mjs +1 -1
  135. package/src/tui/session/session-api-ext.mjs +10 -10
  136. package/src/tui/session/session-api.mjs +4 -2
  137. package/src/tui/session/turn.mjs +15 -2
  138. package/src/tui/session-local.mjs +6 -6
  139. package/src/ui/markdown.mjs +200 -11
  140. package/src/ui/statusline-agents.mjs +1 -1
  141. package/src/ui/statusline.mjs +13 -13
  142. package/src/workflows/default/WORKFLOW.md +2 -2
  143. package/src/workflows/solo/WORKFLOW.md +2 -2
  144. package/src/rules/shared/01-tool.md +0 -113
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.138",
3
+ "version": "0.9.139",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -52,6 +52,7 @@
52
52
  "smoke:boot": "node scripts/boot-smoke.mjs",
53
53
  "smoke:compact": "node scripts/compact-smoke.mjs",
54
54
  "test:compact": "node --test scripts/suite-compact-test.mjs",
55
+ "test:markdown": "node --test scripts/markdown-surface-test.mjs",
55
56
  "smoke:loop": "node scripts/smoke-loop.mjs",
56
57
  "smoke:loop:final": "node scripts/smoke-loop-report.mjs --require-complete --min-elapsed 5h --min-iterations 400 --max-gap 60s --max-smoke-ms 10000 --max-avg-smoke-ms 8500 --max-step-ms smoke.mjs=4000 --max-step-ms boot-smoke.mjs=8000 --max-rss-mb 140 --max-rss-growth-mb 50",
57
58
  "smoke:loop:report": "node scripts/smoke-loop-report.mjs",
@@ -63,11 +64,10 @@
63
64
  "test:embedding-runtime:warmup": "node scripts/verify-embedding-runtime.mjs --warmup",
64
65
  "test:release-assets": "node --check scripts/verify-release-assets.mjs && node --check scripts/release-gate-test.mjs && node --check scripts/prepare-native-assets.mjs && node --test scripts/release-gate-test.mjs scripts/prepare-native-assets-test.mjs",
65
66
  "test:native-edit-wire": "node scripts/native-edit-wire-test.mjs",
66
- "test:release-focused": "npm run test:release-critical && npm run test:compact && npm run test:shellhardening && npm run test:tool-contracts && npm run test:session && npm run test:session-transport",
67
67
  "test:release-critical": "npm run test:release-assets && npm run smoke:patch && npm run test:providers",
68
68
  "test:spec-advisory": "node scripts/advisory-spec-test.mjs && npm run test:spec-advisory --prefix apps/desktop",
69
69
  "test:session-transport": "node --test scripts/session-transport-test.mjs scripts/daemon-bootstrap-test.mjs",
70
- "test:session": "node --test scripts/runtime-turn-contract-test.mjs scripts/session-save-fault-store-test.mjs src/runtime/shared/tool-surface.test.mjs",
70
+ "test:session": "node --test scripts/runtime-turn-contract-test.mjs scripts/session-save-fault-store-test.mjs scripts/turn-review-revert-test.mjs src/runtime/shared/tool-surface.test.mjs",
71
71
  "test:media": "node --test src/runtime/media/store.test.mjs src/runtime/media/renditions.test.mjs src/runtime/media/adapters/codex-image.test.mjs",
72
72
  "failures": "node scripts/tool-failures.mjs",
73
73
  "trace:llm": "node scripts/llm-trace-summary.mjs",
@@ -5,7 +5,7 @@ import {
5
5
  loadModelsDevCatalog,
6
6
  warmModelMetadataCatalogs,
7
7
  } from '../src/runtime/agent/orchestrator/providers/model-catalog.mjs';
8
- import { QUICK_SEARCH_MODELS } from '../src/session-runtime/quick-search-models.mjs';
8
+ import { QUICK_WEB_SEARCH_MODELS } from '../src/session-runtime/quick-web-search-models.mjs';
9
9
 
10
10
  const args = new Set(process.argv.slice(2));
11
11
  const json = args.has('--json');
@@ -155,7 +155,7 @@ for (const [name, provider] of getAllProviders()) {
155
155
  }
156
156
 
157
157
  const quickRows = [];
158
- for (const [provider, models] of Object.entries(QUICK_SEARCH_MODELS)) {
158
+ for (const [provider, models] of Object.entries(QUICK_WEB_SEARCH_MODELS)) {
159
159
  for (const model of models) {
160
160
  if (model?.contextWindow == null) continue;
161
161
  const external = externalMeta(modelsDev, litellm, provider, model.id);
@@ -87,11 +87,11 @@ try {
87
87
  timed(metric('find'), /tool-defs|no fuzzy match/, () => executeBuiltinTool('find', {
88
88
  query: 'tool-defs', limit: 8,
89
89
  }, root, opts)),
90
- timed(metric('list'), /01-tool\.md|file/, () => executeBuiltinTool('list', {
90
+ timed(metric('list'), /10-tool-workflow\.md|file/, () => executeBuiltinTool('list', {
91
91
  path: 'src/rules/shared',
92
92
  }, root, opts)),
93
- timed(metric('read'), /Tool Use|read/, () => executeBuiltinTool('read', {
94
- path: [['src/rules/shared/01-tool.md', 0, 10], ['package.json', 0, 5]],
93
+ timed(metric('read'), /Tool Workflow|read/, () => executeBuiltinTool('read', {
94
+ path: [['src/rules/shared/10-tool-workflow.md', 0, 10], ['package.json', 0, 5]],
95
95
  }, root, opts)),
96
96
  timed(metric('code_graph'), /symbol|binding|files|edges/i, () => executeCodeGraphTool('code_graph', {
97
97
  mode: 'symbols', files: 'scripts/smoke.mjs',
@@ -1,8 +1,8 @@
1
1
  {
2
- "outputStyle": "default",
2
+ "outputStyle": "simple",
3
3
  "agent": {
4
4
  "mcpServers": {},
5
- "profile": { "title": "", "language": "system" },
5
+ "profile": { "title": "", "language": "system", "experienceLevel": "" },
6
6
  "recap": { "enabled": true }
7
7
  },
8
8
  "channels": {},
@@ -31,9 +31,9 @@ description: Use this skill only to inspect or modify a Mixdog user's persisted
31
31
  |---|---|
32
32
  | Lead 모델 | `agent.presets[id=workflow-lead]` + `agent.default` |
33
33
  | 에이전트 라우트 | `agent.agents.<id>` |
34
- | Search 모델 | `agent.searchRoute` |
34
+ | Web Search 모델 | `agent.webSearchRoute` |
35
35
  | Provider·MCP·프로필·스킬·업데이트·셸·autoClear·compaction | `agent.*` |
36
- | Web search 도구 | `agent.modules.search.enabled` |
36
+ | Web search 도구 | `agent.modules.webSearch.enabled` |
37
37
  | Recap(배경 사이클) | `agent.recap.enabled` |
38
38
  | 출력 스타일 | 루트 `outputStyle` |
39
39
  | Webhook server | `channels.webhook.*` |
@@ -98,11 +98,11 @@ API 키·토큰·OAuth 자격은 config에 쓰지 않는다.
98
98
 
99
99
  ### 웹 검색 모델
100
100
 
101
- 1. **확인**: Desktop **Workflows → Default agents → Web Search**, TUI `/search`, `getSearchRoute()`, 디스크 `agent.searchRoute`.
102
- 2. **변경**: Desktop **Web Search → Edit** 또는 TUI `/search` → `setSearchRoute()`.
103
- 3. **검증**: 저장 route가 native-search 가능 provider/model인지 확인하고 다음 `search` 도구 호출에서 사용되는지 확인한다.
101
+ 1. **확인**: Desktop **Workflows → Default agents → Web Search**, TUI `/websearch`, `getWebSearchRoute()`, 디스크 `agent.webSearchRoute`.
102
+ 2. **변경**: Desktop **Web Search → Edit** 또는 TUI `/websearch` → `setWebSearchRoute()`.
103
+ 3. **검증**: 저장 route가 native web-search 가능 provider/model인지 확인하고 다음 `web_search` 도구 호출에서 사용되는지 확인한다.
104
104
 
105
- `/search` 인자는 route를 직접 지정하지 않으며 피커를 연다.
105
+ `/websearch` 인자는 route를 직접 지정하지 않으며 피커를 연다.
106
106
 
107
107
  ### Web search 도구 토글
108
108
 
@@ -110,7 +110,7 @@ API 키·토큰·OAuth 자격은 config에 쓰지 않는다.
110
110
 
111
111
  1. **확인**: Desktop **Settings → General → Web search**, TUI `/setting → Web search`.
112
112
  2. **변경**: toggle → `setWebSearchEnabled()`.
113
- 3. **저장**: `agent.modules.search.enabled`.
113
+ 3. **저장**: `agent.modules.webSearch.enabled`.
114
114
  4. **검증**: 다음 세션의 tool surface.
115
115
 
116
116
  ### reasoning effort / Fast
@@ -119,7 +119,7 @@ API 키·토큰·OAuth 자격은 config에 쓰지 않는다.
119
119
  - Fast: `/fast [on|off]` 또는 model picker의 Tab → 현재 Main route에 저장
120
120
  - Desktop 세션 헤더에서도 같은 Main route를 바꾼다.
121
121
  - 진행 중 turn은 시작 값을 유지하고 새 값은 다음 turn부터 적용된다.
122
- - agent/search route의 effort/Fast는 각 route에 격리되며 Main의 `modelSettings`를 덮어쓰지 않는다.
122
+ - agent/web-search route의 effort/Fast는 각 route에 격리되며 Main의 `modelSettings`를 덮어쓰지 않는다.
123
123
 
124
124
  ### 워크플로 팩
125
125
 
@@ -188,7 +188,9 @@ API 키·토큰·OAuth 자격은 config에 쓰지 않는다.
188
188
  3. **사용자 정의**: `<mixdogData>/output-styles/<id>.md`.
189
189
  4. **검증**: 대화 이력이 있으면 현재 chat에는 적용되지 않으므로 `/clear` 후 확인한다.
190
190
 
191
- `agent.outputStyle`은 읽기 호환용 레거시 경로다. 변경 시 루트 `outputStyle`에 저장하고 `agent.outputStyle`은 제거한다.
191
+ 루트 `outputStyle`만 읽고 쓴다. `agent.outputStyle`은 더 이상 읽지 않으므로 발견하면 제거한다.
192
+
193
+ 사용자 정의 스타일은 기본적으로 빌트인 공통 포맷(`output-styles/common.md`)을 상속한다. 전면 교체는 frontmatter에 `keep-shared-format: false`를 넣고, 공통 포맷 자체를 바꾸려면 `<mixdogData>/output-styles/common.md`를 둔다.
192
194
 
193
195
  ### 테마
194
196
 
package/src/help.mjs CHANGED
@@ -33,7 +33,7 @@ export const HELP_LINES = [
33
33
  ' /context show current context surface',
34
34
  ' /usage [refresh] show total provider quota / balance',
35
35
  ' /model [name|refresh] switch model for subsequent turns',
36
- ' /search set the web search provider/model',
36
+ ' /websearch set the web search provider/model',
37
37
  ' /workflow [name] switch the active workflow',
38
38
  ' /OutputStyle [name] switch Lead output style (alias: /style)',
39
39
  ' /theme [id] change the TUI color theme',
@@ -18,11 +18,13 @@
18
18
  * BP4 = live user/task messages and compacted tail
19
19
  *
20
20
  * Source files (rules/):
21
- * - shared/01-tool.md — universal tool policy (Lead + agent BP1, identical full set)
21
+ * - shared/*.md ordered universal policies (Lead + agent BP1, identical full set)
22
22
  * - lead/lead-tool.md — Lead-specific control-tower / delegation / ToolSearch guidance
23
23
  * - lead/lead-brief.md — Lead brief contract (delegating workflows only)
24
24
  * - lead/01-general.md — Lead general
25
- * - output-styles/<name>.md — Lead output style, selected by config outputStyle
25
+ * - lead/02-persona.md — Lead communication personality
26
+ * - output-styles/common.md — shared built-in output composition policy
27
+ * - output-styles/<name>.md — selected built-in depth variant or standalone user style
26
28
  * - agent/00-core.md — universal agent constraints (BP3, all profiles)
27
29
  * - agent/00-common.md — public-agent-only extras (BP3 full profile)
28
30
  * - agent/10..50-*.md — per-hidden-agent bodies (consumed by loadScopedRoleInstructions)
@@ -53,6 +55,19 @@ function readOptional(filePath) {
53
55
  try { return fs.readFileSync(filePath, 'utf8').trim(); } catch { return ''; }
54
56
  }
55
57
 
58
+ function readMarkdownDirectory(dirPath) {
59
+ try {
60
+ return fs.readdirSync(dirPath)
61
+ .filter((name) => name.endsWith('.md'))
62
+ .sort()
63
+ .map((name) => readOptional(path.join(dirPath, name)))
64
+ .filter(Boolean)
65
+ .join('\n');
66
+ } catch {
67
+ return '';
68
+ }
69
+ }
70
+
56
71
  function readUnifiedConfig(dataDir) {
57
72
  try {
58
73
  const unified = JSON.parse(fs.readFileSync(path.join(dataDir, 'mixdog-config.json'), 'utf8'));
@@ -90,13 +105,36 @@ const PROFILE_LANGUAGE_PROMPTS = Object.freeze({
90
105
  });
91
106
 
92
107
  const PROFILE_TITLE_MAX = 64;
108
+ const PROFILE_EXPERIENCE_LEVELS = Object.freeze({
109
+ beginner: {
110
+ label: 'Beginner',
111
+ prompt: 'Assume no development background; briefly explain only the terms and prerequisites needed to understand the answer.',
112
+ },
113
+ 'vibe-coder': {
114
+ label: 'Vibe coder',
115
+ prompt: 'Lead with what the result does and how to use it; briefly unpack implementation jargon when it is needed for understanding.',
116
+ },
117
+ junior: {
118
+ label: 'Junior',
119
+ prompt: 'Assume basic development knowledge; make otherwise implicit connections clear when they are needed for easy understanding.',
120
+ },
121
+ expert: {
122
+ label: 'Expert',
123
+ prompt: 'Do not unnecessarily unpack familiar basics, but preserve the explanations needed for accurate understanding and judgment. Use familiar technical terminology naturally.',
124
+ },
125
+ });
93
126
 
94
127
  function normalizeProfileConfig(value = {}) {
95
128
  const raw = value && typeof value === 'object' ? value : {};
96
129
  const title = String(raw.title ?? raw.name ?? '').trim().slice(0, PROFILE_TITLE_MAX);
97
130
  const requested = String(raw.language ?? raw.lang ?? 'system').trim();
98
131
  const language = requested === 'system' || PROFILE_LANGUAGE_PROMPTS[requested] ? requested : 'system';
99
- return { title, language };
132
+ const requestedExperienceLevel = String(raw.experienceLevel ?? '')
133
+ .trim().toLowerCase().replace(/[\s_]+/g, '-');
134
+ const experienceLevel = Object.prototype.hasOwnProperty.call(PROFILE_EXPERIENCE_LEVELS, requestedExperienceLevel)
135
+ ? requestedExperienceLevel
136
+ : '';
137
+ return { title, language, experienceLevel };
100
138
  }
101
139
 
102
140
  function systemLocaleId(locale) {
@@ -131,6 +169,11 @@ function buildProfilePreferencesContent(dataDir) {
131
169
  lines.push(`- User title: ${profile.title}.`);
132
170
  lines.push(`- Use "${profile.title}" when directly addressing the user; do not repeat it in routine progress updates or pre-tool preambles.`);
133
171
  }
172
+ const experience = PROFILE_EXPERIENCE_LEVELS[profile.experienceLevel];
173
+ if (experience) {
174
+ lines.push(`- Development experience: ${experience.label}. ${experience.prompt}`);
175
+ lines.push('- Adapt vocabulary and assumed background to this level without adding lessons, examples, or tips unless the task needs them; output style still controls information depth.');
176
+ }
134
177
  return `# Profile Preferences${lines.length ? `\n\n${lines.join('\n')}` : ''}`;
135
178
  }
136
179
 
@@ -152,9 +195,12 @@ function stripFrontmatter(markdown) {
152
195
  return String(markdown || '').replace(/^---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)/, '').trim();
153
196
  }
154
197
 
155
- const SEARCH_ROUTE_RE = /web\/current→`search`;\s*returned URL body→`web_fetch`;\s*/g;
156
- const RECALL_ROUTE_RE = /prior work→`recall`\s*\(history only, never current local state\);\s*/g;
157
- const MEMORY_ROUTE_RE = /durable compact English memory→`memory`;\s*/g;
198
+ const WEB_SEARCH_ROUTE_RE = /^[ \t]*current or external information discovery→`web_search`;[ \t]*\r?\n?/gm;
199
+ const WEB_FETCH_ROUTE_RE = /^[ \t]*page or documentation body retrieval from a known URL→`web_fetch`\.[ \t]*\r?\n?/gm;
200
+ const RECALL_ROUTE_RE = /^-[ \t]*past facts recorded in prior work or sessions→`recall`[ \t]*\r?\n[ \t]*\(stored history only, never current local state\)\.[ \t]*\r?\n?/gm;
201
+ const MEMORY_ROUTE_RE = /^-[ \t]*Durable memory creation or update→`memory`; store a compact English[ \t]*\r?\n[ \t]*statement\.[\s\S]*$/m;
202
+ const EMPTY_RESEARCH_RE = /^# Research[ \t]*\r?\n(?:[ \t]*\r?\n)*-[ \t]*Research routes:[ \t]*\r?\n?/gm;
203
+ const EMPTY_MEMORY_RE = /^# Memory[ \t]*\r?\n(?:[ \t]*\r?\n)*/gm;
158
204
 
159
205
  function omitKeySet(omitTools) {
160
206
  return new Set((Array.isArray(omitTools) ? omitTools : []).map((name) => String(name || '').toLowerCase()).filter(Boolean));
@@ -164,51 +210,159 @@ function omitKeySet(omitTools) {
164
210
  function omitToolRoutes(text, omitTools = []) {
165
211
  const deny = omitKeySet(omitTools);
166
212
  let out = String(text || '');
167
- if (deny.has('search') || deny.has('web_fetch')) out = out.replace(SEARCH_ROUTE_RE, '');
213
+ if (deny.has('web_search')) out = out.replace(WEB_SEARCH_ROUTE_RE, '');
214
+ if (deny.has('web_fetch')) out = out.replace(WEB_FETCH_ROUTE_RE, '');
168
215
  if (deny.has('recall')) out = out.replace(RECALL_ROUTE_RE, '');
169
216
  if (deny.has('memory')) out = out.replace(MEMORY_ROUTE_RE, '');
217
+ if (deny.has('web_search') && deny.has('web_fetch')) {
218
+ out = out.replace(EMPTY_RESEARCH_RE, '');
219
+ }
220
+ if (deny.has('recall') && deny.has('memory')) out = out.replace(EMPTY_MEMORY_RE, '');
170
221
  return out.replace(/[ \t]+\n/g, '\n').replace(/\n{3,}/g, '\n\n');
171
222
  }
172
223
 
173
- function normalizeOutputStyleName(value) {
174
- const name = String(value || 'simple').trim();
175
- return /^[A-Za-z0-9_.-]+$/.test(name) ? name : 'simple';
224
+ const DEFAULT_OUTPUT_STYLE_ID = 'simple';
225
+ const OUTPUT_STYLE_ORDER = ['detailed', 'simple', 'minimal', 'extreme-minimal'];
226
+ const OUTPUT_STYLE_ALIASES = new Map([
227
+ ['extreme', 'extreme-minimal'],
228
+ ['extremesimple', 'extreme-minimal'],
229
+ ['extreme-simple', 'extreme-minimal'],
230
+ ['extreme_simple', 'extreme-minimal'],
231
+ ['extrememinimal', 'extreme-minimal'],
232
+ ['extreme_minimal', 'extreme-minimal'],
233
+ ['mono', 'extreme-minimal'],
234
+ ['oneline', 'extreme-minimal'],
235
+ ['one-line', 'extreme-minimal'],
236
+ ['one_line', 'extreme-minimal'],
237
+ ]);
238
+
239
+ function normalizeOutputStyleId(value) {
240
+ const raw = String(value || '').trim().toLowerCase();
241
+ if (!raw) return '';
242
+ const slug = raw.replace(/[_\s]+/g, '-').replace(/^-+|-+$/g, '');
243
+ const compact = slug.replace(/[_.-]+/g, '');
244
+ if (OUTPUT_STYLE_ALIASES.has(slug)) return OUTPUT_STYLE_ALIASES.get(slug);
245
+ if (OUTPUT_STYLE_ALIASES.has(compact)) return OUTPUT_STYLE_ALIASES.get(compact);
246
+ return /^[a-z0-9.-]+$/.test(slug) ? slug : '';
176
247
  }
177
248
 
178
- function loadOutputStyle({ PLUGIN_ROOT, DATA_DIR }) {
179
- const config = readUnifiedConfig(DATA_DIR);
180
- const configured = config.outputStyle || (config.agent && config.agent.outputStyle);
181
- const styleName = normalizeOutputStyleName(configured);
182
- const candidates = [
183
- path.join(DATA_DIR, 'output-styles', `${styleName}.md`),
184
- path.join(PLUGIN_ROOT, 'output-styles', `${styleName}.md`),
185
- ];
186
- for (const candidate of candidates) {
187
- const body = stripFrontmatter(readOptional(candidate));
188
- if (body) return body;
249
+ function outputStyleCompactKey(value) {
250
+ return normalizeOutputStyleId(value).replace(/[_.-]+/g, '');
251
+ }
252
+
253
+ function titleCaseOutputStyle(id) {
254
+ return String(id || '')
255
+ .split(/[_.-]+/)
256
+ .filter(Boolean)
257
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
258
+ .join(' ') || 'Default';
259
+ }
260
+
261
+ function parseOutputStyleFrontmatter(markdown) {
262
+ const match = String(markdown || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
263
+ const meta = {};
264
+ if (!match) return meta;
265
+ for (const line of match[1].split(/\r?\n/)) {
266
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*?)\s*$/);
267
+ if (!kv) continue;
268
+ meta[kv[1]] = kv[2].replace(/^['"]|['"]$/g, '').trim();
189
269
  }
190
- if (styleName !== 'simple') {
191
- const fallback = [
192
- path.join(DATA_DIR, 'output-styles', 'simple.md'),
193
- path.join(PLUGIN_ROOT, 'output-styles', 'simple.md'),
194
- ];
195
- for (const candidate of fallback) {
196
- const body = stripFrontmatter(readOptional(candidate));
197
- if (body) return body;
270
+ return meta;
271
+ }
272
+
273
+ function outputStyleFlag(value, fallback) {
274
+ const raw = String(value ?? '').trim().toLowerCase();
275
+ if (!raw) return fallback;
276
+ if (/^(?:true|1|yes|on)$/.test(raw)) return true;
277
+ if (/^(?:false|0|no|off)$/.test(raw)) return false;
278
+ return fallback;
279
+ }
280
+
281
+ function readOutputStyleEntry(filePath, source) {
282
+ const raw = readOptional(filePath);
283
+ if (!raw) return null;
284
+ const meta = parseOutputStyleFrontmatter(raw);
285
+ if (/^(?:true|1|yes)$/i.test(String(meta.partial || '').trim())) return null;
286
+ const fileId = normalizeOutputStyleId(path.basename(filePath).replace(/\.md$/i, ''));
287
+ const id = normalizeOutputStyleId(meta.name) || fileId;
288
+ if (!id) return null;
289
+ return {
290
+ id,
291
+ label: String(meta.title || meta.label || '').trim() || titleCaseOutputStyle(id),
292
+ aliases: String(meta.aliases || '').split(',')
293
+ .map((alias) => normalizeOutputStyleId(alias))
294
+ .filter(Boolean),
295
+ // Built-in and custom styles both inherit the shared format partial; a
296
+ // style opts out only by declaring `keep-shared-format: false`.
297
+ keepSharedFormat: outputStyleFlag(meta['keep-shared-format'], true),
298
+ source,
299
+ raw,
300
+ };
301
+ }
302
+
303
+ function listOutputStyleEntries({ PLUGIN_ROOT, DATA_DIR }) {
304
+ const byId = new Map();
305
+ for (const { dir, source } of [
306
+ { dir: path.join(PLUGIN_ROOT, 'output-styles'), source: 'builtin' },
307
+ { dir: path.join(DATA_DIR, 'output-styles'), source: 'user' },
308
+ ]) {
309
+ let files = [];
310
+ try { files = fs.readdirSync(dir).filter((name) => name.toLowerCase().endsWith('.md')).sort(); } catch {}
311
+ for (const name of files) {
312
+ const style = readOutputStyleEntry(path.join(dir, name), source);
313
+ if (style) byId.set(style.id, style);
198
314
  }
199
315
  }
200
- return '';
316
+ return [...byId.values()].sort((a, b) => {
317
+ const ai = OUTPUT_STYLE_ORDER.indexOf(a.id);
318
+ const bi = OUTPUT_STYLE_ORDER.indexOf(b.id);
319
+ if (ai !== bi) return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
320
+ return a.label.localeCompare(b.label, 'en', { sensitivity: 'base' });
321
+ });
322
+ }
323
+
324
+ function findOutputStyleEntry(value, styles) {
325
+ const id = normalizeOutputStyleId(value);
326
+ const compact = outputStyleCompactKey(value);
327
+ if (!id && !compact) return null;
328
+ return styles.find((style) => {
329
+ if (style.id === id || outputStyleCompactKey(style.id) === compact) return true;
330
+ if (outputStyleCompactKey(style.label) === compact) return true;
331
+ return style.aliases.some((alias) => alias === id || outputStyleCompactKey(alias) === compact);
332
+ }) || null;
333
+ }
334
+
335
+ /** Shared format partial: a user copy in DATA_DIR overrides the built-in one. */
336
+ function readSharedFormatPartial({ PLUGIN_ROOT, DATA_DIR }) {
337
+ return stripFrontmatter(readOptional(path.join(DATA_DIR, 'output-styles', 'common.md')))
338
+ || stripFrontmatter(readOptional(path.join(PLUGIN_ROOT, 'output-styles', 'common.md')));
339
+ }
340
+
341
+ function loadOutputStyle({ PLUGIN_ROOT, DATA_DIR }) {
342
+ // Root `outputStyle` is the only configured location; the retired
343
+ // `agent.outputStyle` key is dropped by config canonicalization.
344
+ const configured = String(readUnifiedConfig(DATA_DIR).outputStyle || '').trim() || DEFAULT_OUTPUT_STYLE_ID;
345
+ const styles = listOutputStyleEntries({ PLUGIN_ROOT, DATA_DIR });
346
+ const style = findOutputStyleEntry(configured, styles)
347
+ || findOutputStyleEntry(DEFAULT_OUTPUT_STYLE_ID, styles)
348
+ || styles[0];
349
+ if (!style) return '';
350
+ const selected = stripFrontmatter(style.raw);
351
+ if (!selected) return '';
352
+ const common = style.keepSharedFormat ? readSharedFormatPartial({ PLUGIN_ROOT, DATA_DIR }) : '';
353
+ return [`# Output Style: ${style.label}`, common, selected].filter(Boolean).join('\n\n');
201
354
  }
202
355
 
203
356
  function buildSharedToolContent({ PLUGIN_ROOT, omitTools = [] } = {}) {
204
357
  const SHARED_DIR = path.join(PLUGIN_ROOT, 'rules', 'shared');
205
- return omitToolRoutes(readOptional(path.join(SHARED_DIR, '01-tool.md')), omitTools);
358
+ return omitToolRoutes(readMarkdownDirectory(SHARED_DIR), omitTools);
206
359
  }
207
360
 
208
361
  function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR, includeLeadBrief = true }) {
209
362
  const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
210
363
  const LEAD_DIR = path.join(RULES_DIR, 'lead');
211
364
  const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
365
+ const persona = readOptional(path.join(LEAD_DIR, '02-persona.md'));
212
366
  const parts = [];
213
367
 
214
368
  if (includeLeadBrief) {
@@ -217,6 +371,7 @@ function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR, includeLeadBrief = true }
217
371
  }
218
372
 
219
373
  if (general) parts.push(general);
374
+ if (persona) parts.push(persona);
220
375
 
221
376
  return parts.join('\n\n');
222
377
  }
@@ -305,7 +460,7 @@ function buildAgentRoleContent({ PLUGIN_ROOT, profile = 'full' }) {
305
460
  function buildAgentRetrievalInjectionContent({ PLUGIN_ROOT }) {
306
461
  const AGENT_DIR = path.join(PLUGIN_ROOT, 'rules', 'agent');
307
462
  const core = readOptional(path.join(AGENT_DIR, '00-core.md'));
308
- // Full shared tool policy (01-tool.md) now ships via BP1 for retrieval
463
+ // Full ordered shared policy now ships via BP1 for retrieval
309
464
  // roles too; no compact duplicate here.
310
465
  const parts = [];
311
466
  if (core) parts.push(core.trim());
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: common
3
+ title: Shared Output Format
4
+ description: Shared structure, formatting, and readability policy
5
+ partial: true
6
+ ---
7
+
8
+ ## Shared Output Format
9
+
10
+ - Lead with the answer or action. Write for a person, not as a console log. Match
11
+ the request's language, tone, and apparent expertise; use complete grammatical
12
+ sentences and explain unfamiliar terms without talking down to the reader.
13
+ - Select content according to the active depth variation before formatting it.
14
+ Formatting must never restore information that variation discarded.
15
+ - Choose the presentation from the content, the reader, and the task. Markdown is
16
+ available, not mandatory. Use paragraphs, line breaks, bullets, numbered steps,
17
+ headings, tables, or code in any combination that makes the particular answer
18
+ easier to understand; no response shape is the default or a required mapping
19
+ from question type to format.
20
+ - Output renders as GitHub-flavored Markdown — tables, task lists, footnotes,
21
+ fenced code with a language tag, and `$$…$$` math — in a proportional-width
22
+ surface. Space-aligned columns and ASCII art do not line up there; use a real
23
+ table or a fenced block instead.
24
+ - Keep simple answers visually simple. For longer answers, add only the structure
25
+ that creates useful grouping, order, contrast, or emphasis. Do not rotate
26
+ formats for variety, force a recurring template, label the opening answer or
27
+ closing paragraph, create headings for tiny blocks, or nest lists without need.
28
+ - Never produce an essay-shaped wall of text. When a block contains several
29
+ separable ideas or becomes hard to scan, split it or choose a clearer structure.
30
+ Do not overcorrect into fragments or excessive sections when a compact answer
31
+ already reads cleanly.
32
+ - Use tables only for short enumerable or quantitative facts when they genuinely
33
+ improve comparison. Keep explanatory reasoning outside table cells.
34
+ - Keep the flow linear so each sentence builds on what came before without
35
+ semantic backtracking. Keep related ideas together and give each paragraph or
36
+ list item one main idea. Split or list a block as soon as it serves multiple
37
+ roles. Avoid essay-shaped walls of text, fragments, excessive dashes,
38
+ symbol-heavy shorthand, dense paragraphs, and decorative emphasis.
39
+ - Use an inverted pyramid when useful: answer first, then the context needed to
40
+ understand it, then lower-priority detail. State each material point once; do
41
+ not repeat the opening conclusion in a `Summary`, `Conclusion`, or `In short`
42
+ ending, and never force a recurring response template.
43
+ - Keep established facts separate from plausible inference. Do not invent a
44
+ mechanism, value, consequence, or certainty to make an explanation feel
45
+ complete.
46
+ - Use user-facing terms without unexplained internal shorthand, routine process
47
+ narration, raw output dumps, or emojis unless requested. Show code and file
48
+ references only when their exact form helps the reader.
49
+ - Reader understanding outranks terseness, but every retained sentence must earn
50
+ its place. Do not restate the request or announce compliance with constraints
51
+ such as language or tool use. Remove preamble, filler, obvious statements, and
52
+ repetition. Once every requested point is covered, stop; do not append a recap,
53
+ solution, recommendation, next step, or offer of more help unless requested or
54
+ necessary to prevent a misleading answer. Preserve exact technical literals.
@@ -1,25 +1,16 @@
1
1
  ---
2
2
  name: detailed
3
3
  title: Detailed
4
- description: Claude Code default-depth responses
4
+ description: Concrete explanation with full requested depth
5
5
  aliases: verbose, full
6
- keep-coding-instructions: true
7
6
  ---
8
7
 
9
- # Output Style
8
+ ## Depth Variation
10
9
 
11
- Detailed — a teammate's update; clarity outranks terseness.
10
+ Detailed — concrete explanation.
12
11
 
13
- - Outcome first, then what a cold reader needs: complete user-language
14
- sentences readable in one pass, expanded jargon, rationale only where it
15
- adds value; no filler or process narration.
16
- - Structure matches complexity: casual answers stay plain prose; reports
17
- order outcome → changes → verification → next steps.
18
- - 3+ parallel facts become a flat `- ` list: one fact per bullet, one line
19
- when possible, ordered by importance, parallel phrasing, related points
20
- merged, never nested; tables only for short enumerable facts.
21
- - No hard cap, but brevity first: the shortest report understood without
22
- rereads (~10 lines is plenty); trivial results stay 1–2 sentences.
23
- - Cite `file:line`; snippets only when load-bearing; never dump raw tool
24
- output; blockers and failures in one clause each; never name this style
25
- unless asked.
12
+ - Information depth: retain the answer plus every established explanatory layer
13
+ that materially improves understanding—mechanism, evidence, trade-offs,
14
+ implications, and useful examples. Deepen the requested subject without
15
+ filling evidentiary gaps or broadening into fixes, recommendations, or adjacent
16
+ lessons unless asked.
@@ -1,14 +1,17 @@
1
1
  ---
2
2
  name: extreme-minimal
3
3
  title: Extreme Minimal
4
- description: Single sentence under 100 characters
4
+ description: Final decision or direct answer in one or two sentences
5
5
  aliases: extreme, oneline, one-line, one line, mono
6
- keep-coding-instructions: true
7
6
  ---
8
7
 
9
- # Output Style
8
+ ## Depth Variation
10
9
 
11
- Extreme minimal — exactly one sentence under 100 characters.
10
+ Extreme minimal — final decision or answer.
12
11
 
13
- - Net result only; no second sentence, list, heading, label, or follow-up.
14
- - At most one decisive path, command, symbol, or error verbatim if it fits.
12
+ - Information depth: the entire response is one or two short sentences containing
13
+ only the final decision, result, or direct answer. Include no heading, label,
14
+ list, explanation, supporting fact, scope, caveat, recap, or follow-up, even
15
+ when asked to explain or report. For a cause question, the direct answer is the
16
+ highest-level cause, not its concrete causal chain. Never pack discarded detail
17
+ into a longer sentence.
@@ -1,15 +1,16 @@
1
1
  ---
2
2
  name: minimal
3
3
  title: Minimal
4
- description: Net result within 400 characters
5
- keep-coding-instructions: true
4
+ description: Conclusion, core cause, and one decisive fact
6
5
  ---
7
6
 
8
- # Output Style
7
+ ## Depth Variation
9
8
 
10
- Minimal — net result in plain sentences; hard cap 400 characters.
9
+ Minimal — conclusion and core cause.
11
10
 
12
- - Outcome sentence first, then only decisive follow-ups; concept level only:
13
- no headings, bullets, labels, or per-file detail, even for reports.
14
- - Only decisive paths, commands, symbols, or errors verbatim; user-language
15
- sentences; never name this style unless asked.
11
+ - Information depth: retain the conclusion, the single core cause that determines
12
+ it, and one or two decisive concrete facts that make the cause clear. Include
13
+ the immediate consequence when it completes the answer, then stop before a
14
+ full explanation. Do not add secondary evidence, deployment state, unaffected
15
+ scope, broader impact, exceptions, remediation, or process unless that category
16
+ is the requested answer.
@@ -1,21 +1,17 @@
1
1
  ---
2
2
  name: simple
3
3
  title: Simple
4
- description: Lightly trimmed default-style updates within 800 characters
5
- aliases: concise, handoff
6
- keep-coding-instructions: true
4
+ description: Concise summary with the essential explanation
5
+ aliases: default, concise, handoff
7
6
  ---
8
7
 
9
- # Output Style
8
+ ## Depth Variation
10
9
 
11
- Simple — a teammate's update, tightened; hard cap 800 characters.
10
+ Simple — concise summary.
12
11
 
13
- - Outcome first in complete user-language sentences; no filler or process
14
- narration; reports order outcome changes verification.
15
- - Short answers stay plain prose; 3+ parallel facts become a flat `- `
16
- list one fact per bullet, one line, ordered by importance, never nested.
17
- - Each material fact once, secondary detail and non-load-bearing rationale
18
- dropped; `file:line` anchors, technical literals verbatim; the cap is a
19
- ceiling, not a target.
20
- - Never dump raw tool output; blockers and failures in one clause each.
21
- Never name this style unless asked.
12
+ - Information depth: preserve enough context for the answer to stand on its own.
13
+ Retain the main answer, the essential causal sequence, strongest supporting
14
+ evidence, direct consequences, and material scope or caveat. Prefer a complete
15
+ explanation over compression that makes the reader infer a missing step. Omit
16
+ only secondary evidence, exhaustive implementation detail, examples, edge
17
+ cases, adjacent implications, alternatives, and unrequested remediation.
@@ -2,8 +2,10 @@
2
2
 
3
3
  - You are Mixdog, the coding-agent CLI/TUI assistant for multi-provider
4
4
  workflows; never generic OpenAI/ChatGPT.
5
- - Preamble: one useful sentence maximum; no direct names, honorifics, headings,
6
- labels, or routine lookup narration.
5
+ - Before the first tool call, state in one short sentence what you are about
6
+ to do; add a short update when you find something load-bearing, change
7
+ direction, or work a stretch without one. No direct names, honorifics,
8
+ headings, labels, or a colon before a tool call.
7
9
  - Confirm destructive/hard-to-reverse actions against explicit validated paths;
8
10
  never `~`, a root, or unresolved variables/globs; report material deletion
9
11
  recoverability.