mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-09 11:51:37 -10:00
feat(daemon+cli): slice 3.5 — runtime.codex.hooks_enabled setting + ensureCodexFeatureFlag adapter method
Per IMPL-PRD §5 + velocity-guard cadence boundary (d) Checkpoint D.1.
Makes the 7 TDD-red tests in commit 6f68042 pass.
Settings (CLI ConfigStore + daemon settings-store, lockstep):
- New SETTINGS_VALID_KEYS entry: runtime.codex.hooks_enabled (default true)
- ENV_MAP: OPENRIG_RUNTIME_CODEX_HOOKS_ENABLED primary; legacy '' empty
per banked feedback_no_rigged_legacy_for_new_keys.md (net-new key
post-rename; no RIGGED_X cargo-cult)
- KEY_TO_PATH: ['runtime', 'codex', 'hooksEnabled']
- DEFAULTS (CLI side) + getDefaultValue switch (daemon side): both return
true
CodexRuntimeAdapter:
- New ensureCodexFeatureFlag(enabled: boolean) method
- enabled=true → idempotent write of codex_hooks = true under [features]
in ~/.codex/config.toml; preserves any existing config content;
reuses existing upsertCodexHooksFeature TOML helper
- enabled=false → ZERO mutation (operator owns Codex config)
- Skips silently if no homedir available in fsOps
Existing settings-test fixtures updated:
- user-settings-store.test.ts: SETTINGS_VALID_KEYS expected list +1
- user-settings-routes.test.ts: settings count 27 → 28
Verification:
- codex-hooks-feature-flag.test.ts: 7/7 pass
- Full daemon suite: 3158 pass; 1 fail (pre-existing rsi-v2)
NOT YET in this commit (Checkpoint D.2):
- Activity-hook injection rip from both adapters (provisionActivityHooks,
upsertCommandHook, hookEntryContainsCommand) — old upsertCodexHooksFeature
TOML helper stays for now since ensureCodexFeatureFlag reuses it; rip
removes the activity-hook coupling site
- Asset deletion (openrig-activity-hook-relay.cjs)
- Daemon startup wire-up: replace activityHookRelayAssetPath wiring with
ensureCodexFeatureFlag(setting) call
Per IMPL-PRD §5 plugin-primitive Phase 3a slice 3.5.
This commit is contained in:
1 parent
6f6804267e
commit
6cb24cce19
5 files changed
+58
-2
No files matched your search
@@ -125,6 +125,12 @@ const DEFAULTS = {
|
||||
auditLog: false,
|
||||
},
|
||||
},
|
||||
// plugin-primitive Phase 3a slice 3.5 — Codex feature flag default ON.
|
||||
runtime: {
|
||||
codex: {
|
||||
hooksEnabled: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const VALID_KEYS = [
|
||||
@@ -159,6 +165,8 @@ export const VALID_KEYS = [
|
||||
"feed.subscriptions.shipped",
|
||||
"feed.subscriptions.progress",
|
||||
"feed.subscriptions.audit_log",
|
||||
// plugin-primitive Phase 3a slice 3.5 — Codex feature flag.
|
||||
"runtime.codex.hooks_enabled",
|
||||
] as const;
|
||||
|
||||
export type ValidKey = typeof VALID_KEYS[number];
|
||||
@@ -194,6 +202,9 @@ export const ENV_MAP: Record<ValidKey, { primary: string; legacy: string }> = {
|
||||
"feed.subscriptions.shipped": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_SHIPPED", legacy: "RIGGED_FEED_SUBSCRIPTIONS_SHIPPED" },
|
||||
"feed.subscriptions.progress": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_PROGRESS", legacy: "RIGGED_FEED_SUBSCRIPTIONS_PROGRESS" },
|
||||
"feed.subscriptions.audit_log": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_AUDIT_LOG", legacy: "RIGGED_FEED_SUBSCRIPTIONS_AUDIT_LOG" },
|
||||
// Net-new key post-rename: OPENRIG_X primary only; legacy field empty
|
||||
// per banked feedback_no_rigged_legacy_for_new_keys.md.
|
||||
"runtime.codex.hooks_enabled": { primary: "OPENRIG_RUNTIME_CODEX_HOOKS_ENABLED", legacy: "" },
|
||||
};
|
||||
|
||||
// Maps dotted-string config keys to the camelCase RiggedConfig path.
|
||||
@@ -228,6 +239,7 @@ const KEY_TO_PATH: Record<ValidKey, string[]> = {
|
||||
"feed.subscriptions.shipped": ["feed", "subscriptions", "shipped"],
|
||||
"feed.subscriptions.progress": ["feed", "subscriptions", "progress"],
|
||||
"feed.subscriptions.audit_log": ["feed", "subscriptions", "auditLog"],
|
||||
"runtime.codex.hooks_enabled": ["runtime", "codex", "hooksEnabled"],
|
||||
};
|
||||
|
||||
function isValidKey(key: string): key is ValidKey {
|
||||
|
||||
@@ -62,6 +62,30 @@ export class CodexRuntimeAdapter implements RuntimeAdapter {
|
||||
this.activityHookRelayAssetPath = deps.activityHookRelayAssetPath ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* plugin-primitive Phase 3a slice 3.5 — ensure Codex feature flag.
|
||||
*
|
||||
* When `enabled` is true, idempotently writes `codex_hooks = true` under
|
||||
* `[features]` in `~/.codex/config.toml`, creating the file if missing.
|
||||
* When `enabled` is false, makes ZERO modifications — the operator is
|
||||
* managing Codex config independently and the daemon does not touch it.
|
||||
*
|
||||
* Replaces the activity-hook-injection-coupled call to upsertCodexHooksFeature
|
||||
* that lived in provisionActivityHooks() pre-rip.
|
||||
*/
|
||||
ensureCodexFeatureFlag(enabled: boolean): void {
|
||||
if (!enabled) return;
|
||||
const homedir = this.fs.homedir;
|
||||
if (!homedir) return;
|
||||
const configPath = nodePath.join(homedir, ".codex", "config.toml");
|
||||
const existing = this.fs.exists(configPath) ? this.fs.readFile(configPath) : "";
|
||||
const updated = upsertCodexHooksFeature(existing);
|
||||
if (updated !== existing) {
|
||||
this.fs.mkdirp(nodePath.dirname(configPath));
|
||||
this.fs.writeFile(configPath, updated);
|
||||
}
|
||||
}
|
||||
|
||||
async listInstalled(binding: NodeBinding): Promise<InstalledResource[]> {
|
||||
const results: InstalledResource[] = [];
|
||||
const skillsDir = nodePath.join(binding.cwd, ".agents", "skills");
|
||||
|
||||
@@ -70,6 +70,12 @@ export const SETTINGS_VALID_KEYS = [
|
||||
"feed.subscriptions.shipped",
|
||||
"feed.subscriptions.progress",
|
||||
"feed.subscriptions.audit_log",
|
||||
// plugin-primitive Phase 3a slice 3.5 — Codex feature flag.
|
||||
// When true (default), daemon ensures `codex_hooks = true` in
|
||||
// ~/.codex/config.toml on launch so plugin-shipped hooks fire on
|
||||
// Codex runtime. When false, operator is managing Codex config
|
||||
// independently — daemon does NOT mutate.
|
||||
"runtime.codex.hooks_enabled",
|
||||
] as const;
|
||||
|
||||
export type SettingsValidKey = typeof SETTINGS_VALID_KEYS[number];
|
||||
@@ -102,6 +108,12 @@ const ENV_MAP: Record<SettingsValidKey, { primary: string; legacy: string }> = {
|
||||
"feed.subscriptions.shipped": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_SHIPPED", legacy: "RIGGED_FEED_SUBSCRIPTIONS_SHIPPED" },
|
||||
"feed.subscriptions.progress": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_PROGRESS", legacy: "RIGGED_FEED_SUBSCRIPTIONS_PROGRESS" },
|
||||
"feed.subscriptions.audit_log": { primary: "OPENRIG_FEED_SUBSCRIPTIONS_AUDIT_LOG", legacy: "RIGGED_FEED_SUBSCRIPTIONS_AUDIT_LOG" },
|
||||
// plugin-primitive Phase 3a slice 3.5 — net-new key post-rename;
|
||||
// OPENRIG_X primary only (no RIGGED_X legacy per banked
|
||||
// feedback_no_rigged_legacy_for_new_keys.md). Legacy field is
|
||||
// empty-string sentinel kept for type-shape uniformity with the
|
||||
// rest of ENV_MAP.
|
||||
"runtime.codex.hooks_enabled": { primary: "OPENRIG_RUNTIME_CODEX_HOOKS_ENABLED", legacy: "" },
|
||||
};
|
||||
|
||||
const KEY_TO_PATH: Record<SettingsValidKey, string[]> = {
|
||||
@@ -132,6 +144,7 @@ const KEY_TO_PATH: Record<SettingsValidKey, string[]> = {
|
||||
"feed.subscriptions.shipped": ["feed", "subscriptions", "shipped"],
|
||||
"feed.subscriptions.progress": ["feed", "subscriptions", "progress"],
|
||||
"feed.subscriptions.audit_log": ["feed", "subscriptions", "auditLog"],
|
||||
"runtime.codex.hooks_enabled": ["runtime", "codex", "hooksEnabled"],
|
||||
};
|
||||
|
||||
export type SettingSource = "env" | "file" | "default";
|
||||
@@ -240,6 +253,10 @@ function getDefaultValue(key: SettingsValidKey, workspaceRoot: string): string |
|
||||
case "feed.subscriptions.shipped": return true;
|
||||
case "feed.subscriptions.progress": return true;
|
||||
case "feed.subscriptions.audit_log": return false;
|
||||
// plugin-primitive Phase 3a slice 3.5 — Codex feature flag default ON.
|
||||
// Daemon ensures `codex_hooks = true` in ~/.codex/config.toml on
|
||||
// launch unless operator explicitly sets to false.
|
||||
case "runtime.codex.hooks_enabled": return true;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,9 @@ describe("config routes (User Settings v0)", () => {
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json() as { settings: Record<string, { value: unknown; source: string }> };
|
||||
// 18 v0 keys + 2 Phase 4 (advisor/operator) + 5 Phase 5 (feed.subscriptions.*)
|
||||
// + 2 V1 pre-release Item 1 (transcripts.lines / transcripts.poll_interval_seconds).
|
||||
expect(Object.keys(body.settings).length).toBe(27);
|
||||
// + 2 V1 pre-release Item 1 (transcripts.lines / transcripts.poll_interval_seconds)
|
||||
// + 1 plugin-primitive Phase 3a slice 3.5 (runtime.codex.hooks_enabled).
|
||||
expect(Object.keys(body.settings).length).toBe(28);
|
||||
expect(body.settings["daemon.port"]?.source).toBe("default");
|
||||
expect(body.settings["ui.preview.refresh_interval_seconds"]?.value).toBe(3);
|
||||
expect(body.settings["ui.preview.max_pins"]?.value).toBe(4);
|
||||
|
||||
@@ -79,6 +79,8 @@ describe("SettingsStore (User Settings v0)", () => {
|
||||
"feed.subscriptions.shipped",
|
||||
"feed.subscriptions.progress",
|
||||
"feed.subscriptions.audit_log",
|
||||
// plugin-primitive Phase 3a slice 3.5 — Codex feature flag.
|
||||
"runtime.codex.hooks_enabled",
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user