From 9124559810493efb439d3e8cc72ead5b091003f1 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Fri, 14 Aug 2026 20:22:41 -0700 Subject: [PATCH] fix(hosts): delete five dead HostConfig fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit metadataFormat (generator hardcodes openai.yaml), sidecar (behavior lives in setup's create_agents_sidecar — knowledge preserved as a comment in codex.ts), install.prefixable (skill_prefix is implemented entirely in bin/gstack-config), staticFiles (docstring cited a SOUL.md that never existed anywhere), and adapter (its only would-be consumer, openclaw-adapter.ts, was fully dead — with a test asserting the field was undefined). Kept: learningsMode (wired next), linkingStrategy (validation reads it), coAuthorTrailer (consumed by resolvers/utility.ts). Proof: JSON dump diff shows ONLY the deleted keys vanishing; zero-diff regen across all 10 hosts; host-config + gen-skill-docs suites green. Note: this commit also carries chunk-23 edits to the shared hosts/claude.ts + define-host.ts + host-config.test.ts files (skipSkills collapse, stale line-number comment drops) — pathspec commits, concurrent prep. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 1 - docs/ADDING_A_HOST.md | 15 ++------------- hosts/claude.ts | 3 +-- hosts/codex.ts | 10 ++++------ hosts/define-host.ts | 25 ++++++++----------------- scripts/host-config.ts | 19 +------------------ test/host-config.test.ts | 38 +++++++++----------------------------- 7 files changed, 25 insertions(+), 86 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c25f238ad..eed012557 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,6 @@ gstack/ │ ├── gen-skill-docs.ts # Template → SKILL.md generator (config-driven) │ ├── host-config.ts # HostConfig interface + validator │ ├── host-config-export.ts # Shell bridge for setup script -│ ├── host-adapters/ # Host-specific adapters (OpenClaw tool mapping) │ ├── resolvers/ # Template resolver modules (preamble, design, review, gbrain, etc.) │ ├── skill-check.ts # Health dashboard │ ├── test-paid-shards.ts # Sharded paid-tier runner (one Bun process per shard) diff --git a/docs/ADDING_A_HOST.md b/docs/ADDING_A_HOST.md index 995803e40..26b6fce8c 100644 --- a/docs/ADDING_A_HOST.md +++ b/docs/ADDING_A_HOST.md @@ -63,14 +63,14 @@ That expands to the full `HostConfig` with these defaults: - `globalRoot` / `localSkillRoot`: `.myhost/skills/gstack`, `hostSubdir`: `.myhost` - `usesEnvVars: true` (false only for Claude, which uses literal `~` paths) - `frontmatter`: allowlist keeping `name` + `description`, no description limit -- `generation`: no metadata sidecar, `skipSkills: ['codex']` (codex skill is Claude-only) +- `generation`: no metadata file, `skipSkills: ['codex']` (codex skill is Claude-only) - `pathRewrites`: the standard trio derived from the resolved paths (`~/.claude/skills/gstack` → `~/{globalRoot}`, `.claude/skills/gstack` → `{localSkillRoot}`, `.claude/skills` → `{hostSubdir}/skills`) - `suppressedResolvers`: the GBrain pair (`GBRAIN_CONTEXT_LOAD`, `GBRAIN_SAVE_RESULTS`) - `runtimeRoot`: the shared asset list (`bin`, `browse/dist`, `browse/bin`, `gstack-upgrade`, `ETHOS.md` + review checklist files) -- `install`: `{ prefixable: false, linkingStrategy: 'symlink-generated' }` +- `install`: `{ linkingStrategy: 'symlink-generated' }` - `learningsMode: 'basic'` Override any field by passing it to `defineHost()`. Two path-rewrite options: @@ -167,17 +167,6 @@ Key fields: | `suppressedResolvers` | Resolver functions that return empty for this host | | `coAuthorTrailer` | Git co-author string for commits | | `boundaryInstruction` | Anti-prompt-injection warning for cross-model invocations | -| `adapter` | Path to adapter module for complex transformations | - -## Adapter pattern (for hosts with different tool models) - -If string-replace tool rewrites aren't enough (the host has fundamentally -different tool semantics), use the adapter pattern: set the `adapter` field -to the adapter module path. See `scripts/host-adapters/openclaw-adapter.ts` -for the reference implementation (no shipped host currently sets `adapter`). - -The adapter runs as a post-processing step after all generic rewrites. It -exports `transform(content: string, config: HostConfig): string`. ## Validation diff --git a/hosts/claude.ts b/hosts/claude.ts index 425232bbd..a02b6d64e 100644 --- a/hosts/claude.ts +++ b/hosts/claude.ts @@ -14,14 +14,13 @@ const claude = defineHost({ generation: { generateMetadata: false, - skipSkills: ['claude'], // Claude outside-voice skill is for non-Claude hosts + skipSkills: [], // overrides the default ['codex'] — the /codex skill IS a Claude skill (wrapper around codex exec) }, pathRewrites: [], // Claude is the primary host — no rewrites needed toolRewrites: {}, install: { - prefixable: true, linkingStrategy: 'real-dir-symlink', }, diff --git a/hosts/codex.ts b/hosts/codex.ts index 8a301f152..0372152cf 100644 --- a/hosts/codex.ts +++ b/hosts/codex.ts @@ -15,9 +15,12 @@ const codex = defineHost({ descriptionLimitBehavior: 'error', }, + // generateMetadata emits agents/openai.yaml (the format is hardcoded in + // gen-skill-docs.ts). Codex also gets a repo-local sidecar at + // .agents/skills/gstack (symlinked runtime assets: bin, browse, review, qa, + // ETHOS.md) — that behavior lives in setup's create_agents_sidecar, not here. generation: { generateMetadata: true, - metadataFormat: 'openai.yaml', skipSkills: ['codex'], // Codex skill is a Claude wrapper around codex exec }, @@ -34,11 +37,6 @@ const codex = defineHost({ // The cross-model resolvers all shell out to Codex — Codex can't invoke itself. suppressedResolvers: [...CROSS_MODEL_RESOLVERS, ...GBRAIN_RESOLVERS], - sidecar: { - path: '.agents/skills/gstack', - symlinks: ['bin', 'browse', 'review', 'qa', 'ETHOS.md'], - }, - coAuthorTrailer: 'Co-Authored-By: OpenAI Codex ', boundaryInstruction: 'IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.', }); diff --git a/hosts/define-host.ts b/hosts/define-host.ts index 24343377e..2be6ba4c7 100644 --- a/hosts/define-host.ts +++ b/hosts/define-host.ts @@ -9,11 +9,9 @@ * paths, the shared runtimeRoot asset list, and symlink-generated install. * * Defaults are constructed fresh per call, so no two host configs ever share - * a mutable array/object. Fields that are absent today (staticFiles, adapter, - * sidecar, toolRewrites, coAuthorTrailer, boundaryInstruction) stay absent - * unless a host explicitly sets them — the factory never default-populates - * optional fields (test/host-config.test.ts pins e.g. openclaw.adapter as - * undefined). + * a mutable array/object. Optional fields that are absent today (toolRewrites, + * coAuthorTrailer, boundaryInstruction) stay absent unless a host explicitly + * sets them — the factory never default-populates optional fields. */ import type { HostConfig } from '../scripts/host-config'; @@ -27,11 +25,11 @@ type PathRewrite = { from: string; to: string }; * non-Claude agent runtimes (OpenClaw, Hermes, GBrain). */ export const CROSS_MODEL_RESOLVERS: string[] = [ - 'DESIGN_OUTSIDE_VOICES', // design.ts:485 — invokes Codex for outside voices - 'ADVERSARIAL_STEP', // review.ts:408 — invokes Codex adversarially - 'CODEX_SECOND_OPINION', // review.ts:257 — invokes Codex - 'CODEX_PLAN_REVIEW', // review.ts:541 — invokes Codex - 'REVIEW_ARMY', // review-army.ts:180 — multi-model orchestration + 'DESIGN_OUTSIDE_VOICES', // design.ts — invokes Codex for outside voices + 'ADVERSARIAL_STEP', // review.ts — invokes Codex adversarially + 'CODEX_SECOND_OPINION', // review.ts — invokes Codex + 'CODEX_PLAN_REVIEW', // review.ts — invokes Codex + 'REVIEW_ARMY', // review-army.ts — multi-model orchestration ]; /** @@ -111,16 +109,12 @@ export function defineHost(overrides: HostOverrides): 'review': ['checklist.md', 'TODOS-format.md'], }, }, - sidecar, install = { - prefixable: false, linkingStrategy: 'symlink-generated', }, coAuthorTrailer, learningsMode = 'basic', boundaryInstruction, - staticFiles, - adapter, } = overrides; if (pathRewrites && extraPathRewrites) { @@ -156,12 +150,9 @@ export function defineHost(overrides: HostOverrides): ...(toolRewrites !== undefined ? { toolRewrites } : {}), suppressedResolvers, runtimeRoot, - ...(sidecar !== undefined ? { sidecar } : {}), install, ...(coAuthorTrailer !== undefined ? { coAuthorTrailer } : {}), learningsMode, ...(boundaryInstruction !== undefined ? { boundaryInstruction } : {}), - ...(staticFiles !== undefined ? { staticFiles } : {}), - ...(adapter !== undefined ? { adapter } : {}), }; } diff --git a/scripts/host-config.ts b/scripts/host-config.ts index c7a3ae9f3..ff64f863f 100644 --- a/scripts/host-config.ts +++ b/scripts/host-config.ts @@ -56,10 +56,8 @@ export interface HostConfig { // --- Generation --- generation: { - /** Whether to create sidecar metadata file (e.g., openai.yaml for Codex). */ + /** Whether to create a metadata file alongside skills (always openai.yaml; gen-skill-docs hardcodes the format). */ generateMetadata: boolean; - /** Metadata file format (e.g., 'openai.yaml'). */ - metadataFormat?: string | null; /** Skill directories to exclude from generation for this host. */ skipSkills?: string[]; /** Skill directories to include (allowlist). Union logic: include minus skip. */ @@ -81,18 +79,8 @@ export interface HostConfig { /** Dir → explicit file list for selective file linking. */ globalFiles?: Record; }; - /** Optional repo-local sidecar config (e.g., Codex uses .agents/skills/gstack). */ - sidecar?: { - /** Sidecar path relative to repo root (e.g., '.agents/skills/gstack'). */ - path: string; - /** Assets to symlink into sidecar (different set than global). */ - symlinks: string[]; - }; - // --- Install Behavior --- install: { - /** Whether gstack-config skill_prefix applies (Claude only). */ - prefixable: boolean; /** How skills are linked into the host dir. */ linkingStrategy: 'real-dir-symlink' | 'symlink-generated'; }; @@ -104,11 +92,6 @@ export interface HostConfig { learningsMode?: 'full' | 'basic'; /** Anti-prompt-injection boundary instruction for cross-model invocations. */ boundaryInstruction?: string; - - /** Static files to copy alongside generated skills (e.g., { 'SOUL.md': 'openclaw/SOUL.md' }). */ - staticFiles?: Record; - /** Optional path to host-adapter module for complex transformations. */ - adapter?: string; } // --- Validation --- diff --git a/test/host-config.test.ts b/test/host-config.test.ts index 1c939d7be..fd349227d 100644 --- a/test/host-config.test.ts +++ b/test/host-config.test.ts @@ -120,7 +120,7 @@ describe('validateHostConfig', () => { generation: { generateMetadata: false }, pathRewrites: [], runtimeRoot: { globalSymlinks: ['bin'] }, - install: { prefixable: false, linkingStrategy: 'symlink-generated' }, + install: { linkingStrategy: 'symlink-generated' }, }; } @@ -441,16 +441,6 @@ describe('golden-file regression', () => { // ─── Individual host config correctness ───────────────────── describe('host config correctness', () => { - test('claude is the only prefixable host', () => { - for (const config of ALL_HOST_CONFIGS) { - if (config.name === 'claude') { - expect(config.install.prefixable).toBe(true); - } else { - expect(config.install.prefixable).toBe(false); - } - } - }); - test('claude is the only host with real-dir-symlink strategy', () => { for (const config of ALL_HOST_CONFIGS) { if (config.name === 'claude') { @@ -476,14 +466,8 @@ describe('host config correctness', () => { expect(codex.frontmatter.descriptionLimitBehavior).toBe('error'); }); - test('codex generates openai.yaml metadata', () => { + test('codex generates metadata (openai.yaml, format hardcoded in gen-skill-docs)', () => { expect(codex.generation.generateMetadata).toBe(true); - expect(codex.generation.metadataFormat).toBe('openai.yaml'); - }); - - test('codex has sidecar config', () => { - expect(codex.sidecar).toBeDefined(); - expect(codex.sidecar!.path).toBe('.agents/skills/gstack'); }); test('factory has tool rewrites', () => { @@ -521,17 +505,13 @@ describe('host config correctness', () => { expect(openclaw.pathRewrites.some(r => r.from === 'CLAUDE.md' && r.to === 'AGENTS.md')).toBe(true); }); - test('openclaw has no adapter (dead code removed)', () => { - expect(openclaw.adapter).toBeUndefined(); - }); - - test('openclaw has no staticFiles (SOUL.md removed)', () => { - expect(openclaw.staticFiles).toBeUndefined(); - }); - - test('openclaw includeSkills is empty (native skills replaced generated ones)', () => { - expect(openclaw.generation.includeSkills).toBeDefined(); - expect(openclaw.generation.includeSkills!.length).toBe(0); + test('no host carries a no-op empty includeSkills allowlist', () => { + // includeSkills: [] was a no-op (the generator's `?.length` guard treats an + // empty allowlist as absent), so configs omit the field instead of + // shipping a lie about "no skills generated". + for (const config of ALL_HOST_CONFIGS) { + expect(config.generation.includeSkills).toBeUndefined(); + } }); test('every host has coAuthorTrailer or undefined', () => {