Context
RemoteClaw is middleware that delegates LLM execution to CLI agents (Claude Code, Gemini, Codex, OpenCode). CLI agents own LLM API routing, capability detection, memory/RAG, and model-specific input handling. Any config field describing those concerns is, by definition, outside middleware's responsibility.
After the #2477-#2482 batch (#2483-#2488 PRs), 8 z.unknown().optional() stubs remain across the config schema. All 8 describe LLM-platform concerns. All 8 have zero production readers (one has a dead-end passthrough). Their existence is a fig leaf that pretends middleware has concerns it doesn't.
This issue consolidates their removal into a single hard-break cleanup. No migration infrastructure is added — the features these fields describe were gutted months ago; users carrying them in their configs see a zod strict-validation error on upgrade and must remove the stale sections manually, one-time.
Why no migrations
The fork has used Option B (rule + auto-strip migration) for schema renames (#2479, #2480, #2481). That pattern is appropriate when a field is renamed — users need a bridge. It is not appropriate for gut removals of platform concerns: auto-stripping prolongs the pretense that these fields mean anything to middleware. The correct signal is a clear rejection.
This issue also reverts #2478's Option-A additions (compat/api stubs added during the same batch where the fork pivoted to Option B — these were inconsistent with the new direction from the moment they landed).
Per-field audit
Cluster A — LLM API routing + capability flags
Zero production readers. No shadow surfaces. Added by #2478 as "kept for config parse compatibility."
| # |
Field |
Location |
What it described |
| 1 |
api (model) |
src/config/zod-schema.core.ts:200 |
LLM API protocol (openai-completions / anthropic-messages / …) |
| 2 |
compat (model) |
src/config/zod-schema.core.ts:201 |
LLM capability flags (supportsTools, thinkingFormat, …) |
| 3 |
api (provider) |
src/config/zod-schema.core.ts:218 |
Provider-level LLM API |
Cluster B — LLM PDF processing
Zero production readers. TS types still structured (divergent from zod).
- Zod stubs (
src/config/zod-schema.agent-defaults.ts:20-22):
pdfModel: z.unknown().optional()
pdfMaxBytesMb: z.unknown().optional()
pdfMaxPages: z.unknown().optional()
- Divergent TS types (
src/config/types.agent-defaults.ts:125,127,129): still declare pdfModel?: AgentModelConfig, pdfMaxBytesMb?: number, pdfMaxPages?: number
- Shadow docs:
schema.help.ts:1006-1011 (4 entries), schema.labels.ts:452-455 (4 entries)
Cluster C — Top-level memory backend
Zero production readers. plugins.slots.memory and doctor.memory.status are unrelated paths (plugin-slot identifier and gateway health RPC — not memory-backend config).
- Zod stub:
src/config/zod-schema.ts:690 — memory: z.unknown().optional() at root of RemoteClawSchema
- Shadow docs:
schema.help.ts:862 ("Memory backend configuration (global)."), schema.labels.ts:374 (memory: "Memory")
Cluster D — RAG / memory-search
Zero production reads. Only a dead-end passthrough in agent-scope.ts (copies entry.memorySearch into ResolvedAgentConfig.memorySearch; scope.memorySearch is never read anywhere).
- Zod stubs:
src/config/zod-schema.agent-defaults.ts:52 — memorySearch: z.unknown().optional() (defaults)
src/config/types.agents.ts:70-71 — memorySearch?: unknown (per-agent, @deprecated)
- TS type:
src/config/types.agent-defaults.ts:174 — memorySearch?: unknown
- Shadow help:
src/config/schema.help.ts — ~29 entries describing the full nested tree (sources, multimodal.{enabled,modalities,maxFileBytes}, experimental.sessionMemory, provider, model, outputDimensionality, remote.{baseUrl,apiKey,headers,batch.*}, local.modelPath, fallback, store.{path,vector.*}, chunking.{tokens,overlap}, query.{maxResults,minScore,hybrid.*})
- Shadow labels:
src/config/schema.labels.ts — ~20 mirroring entries
- Test drift:
src/config/schema.help.quality.test.ts:71-87 asserts 17 of those help paths exist — assertions must be removed
- Dead passthrough:
src/agents/agent-scope.ts:73 (type) + :179 (value copy) in ResolvedAgentConfig
Non-goals
- No
LEGACY_CONFIG_RULES entries. These fields describe functionality that doesn't exist. A rule produces a friendlier error but cannot fix strict validation on its own, so it adds code with no value.
- No
legacy.migrations.ts entries. Auto-stripping would prolong the pretense that these fields matter to middleware. We want the hard break.
- No new stubs. Fixing
compat/api means removing them, not substituting them.
Accepted consequence: existing user configs containing memory: {…}, memorySearch: {…}, pdfModel: …, pdfMaxBytesMb: …, pdfMaxPages: …, or a model/provider with api: … / compat: … will fail to load on upgrade with "Unrecognized key(s) in object: '<field>'" (zod strict-mode behavior). Users perform a one-time manual edit of their config.
Tasks
- Delete the 8 zod stub lines:
src/config/zod-schema.core.ts:200 — api (ModelDefinitionSchema)
src/config/zod-schema.core.ts:201 — compat (ModelDefinitionSchema)
src/config/zod-schema.core.ts:218 — api (ModelProviderSchema)
src/config/zod-schema.agent-defaults.ts:20-22 — pdfModel, pdfMaxBytesMb, pdfMaxPages
src/config/zod-schema.agent-defaults.ts:52 — memorySearch
src/config/zod-schema.ts:690 — memory
- Remove the 4 paired
// Stub kept for config parse compatibility… comment blocks introduced by prior waves.
- Remove paired TS type declarations:
src/config/types.agent-defaults.ts:125,127,129 — pdfModel, pdfMaxBytesMb, pdfMaxPages
src/config/types.agent-defaults.ts:174 — memorySearch
src/config/types.agents.ts:70-71 — per-agent memorySearch
- Remove paired help entries in
src/config/schema.help.ts:
- All
agents.defaults.memorySearch.* (~29 entries)
agents.defaults.pdfModel.primary, .fallbacks, agents.defaults.pdfMaxBytesMb, agents.defaults.pdfMaxPages
- Top-level
memory (line ~862)
- Remove paired label entries in
src/config/schema.labels.ts (~25 matching entries)
- Update
src/config/schema.help.quality.test.ts:
- Delete lines 71-87 (17
memorySearch.* path assertions)
- Remove dead passthrough in
src/agents/agent-scope.ts:
- Line 73:
memorySearch?: AgentEntry["memorySearch"] — remove
- Line 179:
memorySearch: entry.memorySearch — remove
- Regenerate
src/config/schema.base.generated.ts and docs/.generated/config-baseline.json via the project's generation command
- Add adversarial regression tests to
src/config/config-misc.test.ts (or closest fit) verifying that strict validation rejects each removed field with "Unrecognized key". One test per cluster is sufficient; covers the hard-break contract.
Acceptance Criteria
rg "memorySearch|pdfModel|pdfMaxBytesMb|pdfMaxPages" src/ returns only references in the new rejection tests (no hits in zod-schema*, types.*, schema.help.ts, schema.labels.ts, agent-scope.ts, or schema.help.quality.test.ts).
rg "^\s+compat:\s*z\." src/config/ returns no hits.
rg "^\s+api:\s*z\.unknown" src/config/ returns no hits.
rg "^\s+memory:\s*z\.unknown" src/config/zod-schema.ts returns no hits.
src/config/schema.help.quality.test.ts contains zero memorySearch references.
src/agents/agent-scope.ts contains zero memorySearch references.
- A config fixture like
{ memorySearch: { enabled: true } } fails to parse with a zod error matching /Unrecognized key.*memorySearch/ (asserted by new regression test).
- Same adversarial parse-and-reject test for: top-level
memory, pdfModel under agents.defaults, compat/api on a model definition, api on a model provider.
pnpm tsgo clean.
pnpm lint clean.
pnpm format:check clean.
pnpm test passes (expect a net decrease in assertions — memorySearch help-path block removed; new regression tests added).
- All required CI gates pass:
rebrand-gate, zombie-import-gate, stub-debt-gate, throwing-stub-callers-gate, obsolescence-audit-gate, attestation-gate, docs, build, test, lint.
Out of scope
src/agents/transcript-policy.ts: contains isGoogleModelApi stub at line 14 shadowing the real function in agent-helpers/google.ts, and exports applyTranscriptPolicy which has zero production callers. Entire file appears to be zombie. Separate issue — confirm death, file gut ticket.
- Any future Option-B conversion for a truly-renamed field. This issue's no-migration policy applies specifically to gutted platform concerns; genuine renames remain a legitimate use case for rule + migration.
References
Context
RemoteClaw is middleware that delegates LLM execution to CLI agents (Claude Code, Gemini, Codex, OpenCode). CLI agents own LLM API routing, capability detection, memory/RAG, and model-specific input handling. Any config field describing those concerns is, by definition, outside middleware's responsibility.
After the #2477-#2482 batch (#2483-#2488 PRs), 8
z.unknown().optional()stubs remain across the config schema. All 8 describe LLM-platform concerns. All 8 have zero production readers (one has a dead-end passthrough). Their existence is a fig leaf that pretends middleware has concerns it doesn't.This issue consolidates their removal into a single hard-break cleanup. No migration infrastructure is added — the features these fields describe were gutted months ago; users carrying them in their configs see a zod strict-validation error on upgrade and must remove the stale sections manually, one-time.
Why no migrations
The fork has used Option B (rule + auto-strip migration) for schema renames (#2479, #2480, #2481). That pattern is appropriate when a field is renamed — users need a bridge. It is not appropriate for gut removals of platform concerns: auto-stripping prolongs the pretense that these fields mean anything to middleware. The correct signal is a clear rejection.
This issue also reverts #2478's Option-A additions (
compat/apistubs added during the same batch where the fork pivoted to Option B — these were inconsistent with the new direction from the moment they landed).Per-field audit
Cluster A — LLM API routing + capability flags
Zero production readers. No shadow surfaces. Added by #2478 as "kept for config parse compatibility."
api(model)src/config/zod-schema.core.ts:200openai-completions/anthropic-messages/ …)compat(model)src/config/zod-schema.core.ts:201supportsTools,thinkingFormat, …)api(provider)src/config/zod-schema.core.ts:218Cluster B — LLM PDF processing
Zero production readers. TS types still structured (divergent from zod).
src/config/zod-schema.agent-defaults.ts:20-22):pdfModel: z.unknown().optional()pdfMaxBytesMb: z.unknown().optional()pdfMaxPages: z.unknown().optional()src/config/types.agent-defaults.ts:125,127,129): still declarepdfModel?: AgentModelConfig,pdfMaxBytesMb?: number,pdfMaxPages?: numberschema.help.ts:1006-1011(4 entries),schema.labels.ts:452-455(4 entries)Cluster C — Top-level
memorybackendZero production readers.
plugins.slots.memoryanddoctor.memory.statusare unrelated paths (plugin-slot identifier and gateway health RPC — not memory-backend config).src/config/zod-schema.ts:690—memory: z.unknown().optional()at root ofRemoteClawSchemaschema.help.ts:862("Memory backend configuration (global)."),schema.labels.ts:374(memory: "Memory")Cluster D — RAG / memory-search
Zero production reads. Only a dead-end passthrough in
agent-scope.ts(copiesentry.memorySearchintoResolvedAgentConfig.memorySearch;scope.memorySearchis never read anywhere).src/config/zod-schema.agent-defaults.ts:52—memorySearch: z.unknown().optional()(defaults)src/config/types.agents.ts:70-71—memorySearch?: unknown(per-agent,@deprecated)src/config/types.agent-defaults.ts:174—memorySearch?: unknownsrc/config/schema.help.ts— ~29 entries describing the full nested tree (sources,multimodal.{enabled,modalities,maxFileBytes},experimental.sessionMemory,provider,model,outputDimensionality,remote.{baseUrl,apiKey,headers,batch.*},local.modelPath,fallback,store.{path,vector.*},chunking.{tokens,overlap},query.{maxResults,minScore,hybrid.*})src/config/schema.labels.ts— ~20 mirroring entriessrc/config/schema.help.quality.test.ts:71-87asserts 17 of those help paths exist — assertions must be removedsrc/agents/agent-scope.ts:73(type) +:179(value copy) inResolvedAgentConfigNon-goals
LEGACY_CONFIG_RULESentries. These fields describe functionality that doesn't exist. A rule produces a friendlier error but cannot fix strict validation on its own, so it adds code with no value.legacy.migrations.tsentries. Auto-stripping would prolong the pretense that these fields matter to middleware. We want the hard break.compat/apimeans removing them, not substituting them.Accepted consequence: existing user configs containing
memory: {…},memorySearch: {…},pdfModel: …,pdfMaxBytesMb: …,pdfMaxPages: …, or a model/provider withapi: …/compat: …will fail to load on upgrade with"Unrecognized key(s) in object: '<field>'"(zod strict-mode behavior). Users perform a one-time manual edit of their config.Tasks
src/config/zod-schema.core.ts:200—api(ModelDefinitionSchema)src/config/zod-schema.core.ts:201—compat(ModelDefinitionSchema)src/config/zod-schema.core.ts:218—api(ModelProviderSchema)src/config/zod-schema.agent-defaults.ts:20-22—pdfModel,pdfMaxBytesMb,pdfMaxPagessrc/config/zod-schema.agent-defaults.ts:52—memorySearchsrc/config/zod-schema.ts:690—memory// Stub kept for config parse compatibility…comment blocks introduced by prior waves.src/config/types.agent-defaults.ts:125,127,129—pdfModel,pdfMaxBytesMb,pdfMaxPagessrc/config/types.agent-defaults.ts:174—memorySearchsrc/config/types.agents.ts:70-71— per-agentmemorySearchsrc/config/schema.help.ts:agents.defaults.memorySearch.*(~29 entries)agents.defaults.pdfModel.primary,.fallbacks,agents.defaults.pdfMaxBytesMb,agents.defaults.pdfMaxPagesmemory(line ~862)src/config/schema.labels.ts(~25 matching entries)src/config/schema.help.quality.test.ts:memorySearch.*path assertions)src/agents/agent-scope.ts:memorySearch?: AgentEntry["memorySearch"]— removememorySearch: entry.memorySearch— removesrc/config/schema.base.generated.tsanddocs/.generated/config-baseline.jsonvia the project's generation commandsrc/config/config-misc.test.ts(or closest fit) verifying that strict validation rejects each removed field with"Unrecognized key". One test per cluster is sufficient; covers the hard-break contract.Acceptance Criteria
rg "memorySearch|pdfModel|pdfMaxBytesMb|pdfMaxPages" src/returns only references in the new rejection tests (no hits inzod-schema*,types.*,schema.help.ts,schema.labels.ts,agent-scope.ts, orschema.help.quality.test.ts).rg "^\s+compat:\s*z\." src/config/returns no hits.rg "^\s+api:\s*z\.unknown" src/config/returns no hits.rg "^\s+memory:\s*z\.unknown" src/config/zod-schema.tsreturns no hits.src/config/schema.help.quality.test.tscontains zeromemorySearchreferences.src/agents/agent-scope.tscontains zeromemorySearchreferences.{ memorySearch: { enabled: true } }fails to parse with a zod error matching/Unrecognized key.*memorySearch/(asserted by new regression test).memory,pdfModelunderagents.defaults,compat/apion a model definition,apion a model provider.pnpm tsgoclean.pnpm lintclean.pnpm format:checkclean.pnpm testpasses (expect a net decrease in assertions — memorySearch help-path block removed; new regression tests added).rebrand-gate,zombie-import-gate,stub-debt-gate,throwing-stub-callers-gate,obsolescence-audit-gate,attestation-gate,docs,build,test,lint.Out of scope
src/agents/transcript-policy.ts: containsisGoogleModelApistub at line 14 shadowing the real function inagent-helpers/google.ts, and exportsapplyTranscriptPolicywhich has zero production callers. Entire file appears to be zombie. Separate issue — confirm death, file gut ticket.References
compat+apistubs on ModelDefinition/ModelProvider