shell-redesign V1 pre-release CLI/daemon Item 1 fix: project ConfigStore rotation tunables into daemon launch env

Closes the production-reachability gap where transcripts.lines and
transcripts.poll_interval_seconds were resident in ConfigStore +
SETTINGS_VALID_KEYS but never reached the spawned daemon process. The
rotation hook only consulted shell env vars, so
`rig config set transcripts.lines 500` followed by `rig daemon
restart` had no observable effect on the rotation cadence.

Wire-up:
- StartOptions (cli/daemon-lifecycle.ts) extends with optional
  transcriptsLines + transcriptsPollIntervalSeconds.
- buildDaemonEnv emits OPENRIG_TRANSCRIPTS_LINES and
  OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS only when the opts are
  defined; explicit opts override any inherited base-env value, same
  shape as the existing OPENRIG_PORT / OPENRIG_DB / OPENRIG_TRANSCRIPTS_*
  override pattern.
- The `rig daemon start` command path reads
  config.transcripts.lines and config.transcripts.pollIntervalSeconds
  from the resolved ConfigStore and threads them through startDaemon
  → buildDaemonEnv → daemon process env, where
  getTranscriptRotationOptionsFromEnv inside the rotation module
  picks them up at session-start.

Tests:
- 3 new buildDaemonEnv unit tests:
  - emits OPENRIG_TRANSCRIPTS_LINES + OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS
    when opts provided.
  - omits both env vars when opts undefined (rotation falls back to
    its own defaults).
  - explicit opts override inherited OPENRIG_TRANSCRIPTS_* from base
    env (file-stored ConfigStore wins over stale shell vars).
- 1 new ConfigStore roundtrip test:
  ConfigStore.set('transcripts.lines', '500') +
  set('transcripts.poll_interval_seconds', '5') → resolve() returns
  500 / 5 (proves the file-stored values reach the daemon-launch
  read site).
- VALID_KEYS exhaustive-list test refreshed to include the 2 V1
  pre-release keys plus the previously-stale Phase 4 / Phase 5
  additions, matching the equivalent fix in the daemon settings-
  store test.

Verification:
- npx tsc --noEmit on packages/cli/tsconfig.json: clean
- npx vitest run daemon-lifecycle + config-store-extended: 85/85 PASS

SC-29 EXCEPTION #4 scope unchanged: the env-var emission path is the
existing OPENRIG_TRANSCRIPTS_* mechanism, just with two more allowed
keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
mvschwarzandClaude Opus 4.7 committed 2026-05-07 09:31:11 -07:00
1 parent a7a34ca1ed
commit 5270545432
4 files changed
+102 -2

No files matched your search

+7
View File
@@ -112,6 +112,13 @@ export function daemonCommand(depsOverride?: LifecycleDeps): Command {
db: opts.db ?? config.db.path,
transcriptsEnabled: config.transcripts.enabled,
transcriptsPath: config.transcripts.path,
// V1 pre-release CLI/daemon Item 1 — project the
// ConfigStore-resolved rotation tunables into the daemon
// process env so file-stored values
// (`rig config set transcripts.lines 500`) actually
// reach the rotation hook.
transcriptsLines: config.transcripts.lines,
transcriptsPollIntervalSeconds: config.transcripts.pollIntervalSeconds,
},
getDeps(),
);
+25 -1
View File
@@ -31,6 +31,11 @@ export interface StartOptions {
db?: string;
transcriptsEnabled?: boolean;
transcriptsPath?: string;
// V1 pre-release CLI/daemon Item 1 — capture-pane rotation tunables.
// Threaded through to the daemon process env so the rotation hook
// picks up file-stored ConfigStore values, not just shell env.
transcriptsLines?: number;
transcriptsPollIntervalSeconds?: number;
}
export interface LifecycleDeps {
@@ -193,7 +198,18 @@ const ENV_SCRUB_EXACT = new Set([
export function buildDaemonEnv(
baseEnv: Record<string, string>,
opts: { port: number; host: string; db: string; transcriptsEnabled?: boolean; transcriptsPath?: string },
opts: {
port: number;
host: string;
db: string;
transcriptsEnabled?: boolean;
transcriptsPath?: string;
// V1 pre-release CLI/daemon Item 1 — projected from ConfigStore so
// the rotation hook honors file-stored values, not just inherited
// shell env.
transcriptsLines?: number;
transcriptsPollIntervalSeconds?: number;
},
): Record<string, string> {
const env: Record<string, string> = {};
@@ -213,6 +229,12 @@ export function buildDaemonEnv(
if (opts.transcriptsPath) {
env["OPENRIG_TRANSCRIPTS_PATH"] = opts.transcriptsPath;
}
if (opts.transcriptsLines !== undefined) {
env["OPENRIG_TRANSCRIPTS_LINES"] = String(opts.transcriptsLines);
}
if (opts.transcriptsPollIntervalSeconds !== undefined) {
env["OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS"] = String(opts.transcriptsPollIntervalSeconds);
}
return env;
}
@@ -261,6 +283,8 @@ export async function startDaemon(opts: StartOptions, deps: LifecycleDeps): Prom
db,
transcriptsEnabled: opts.transcriptsEnabled,
transcriptsPath: opts.transcriptsPath,
transcriptsLines: opts.transcriptsLines,
transcriptsPollIntervalSeconds: opts.transcriptsPollIntervalSeconds,
}),
stdio: ["ignore", logFd, logFd],
detached: true,
@@ -68,20 +68,45 @@ describe("ConfigStore — extended namespaces (User Settings v0)", () => {
restoreEnv();
});
it("VALID_KEYS includes legacy, user-settings, ui.preview, and recovery keys", () => {
it("VALID_KEYS includes legacy, user-settings, ui.preview, recovery, agents, feed, and transcript-rotation keys", () => {
const expected = [
"daemon.port", "daemon.host", "db.path",
"transcripts.enabled", "transcripts.path",
// V1 pre-release CLI/daemon Item 1 — capture-pane rotation tunables.
"transcripts.lines", "transcripts.poll_interval_seconds",
"workspace.root", "workspace.slices_root", "workspace.steering_path",
"workspace.field_notes_root", "workspace.specs_root",
"files.allowlist", "progress.scan_roots",
"ui.preview.refresh_interval_seconds", "ui.preview.max_pins", "ui.preview.default_lines",
"recovery.auto_drive_provider_prompts",
"recovery.provider_auth_env_allowlist",
// V1 attempt-3 Phase 4 — Advisor/Operator placeholders.
"agents.advisor_session", "agents.operator_session",
// V1 attempt-3 Phase 5 P5-3 — For You feed subscription toggles.
"feed.subscriptions.action_required",
"feed.subscriptions.approvals",
"feed.subscriptions.shipped",
"feed.subscriptions.progress",
"feed.subscriptions.audit_log",
];
expect([...VALID_KEYS]).toEqual(expected);
});
it("transcripts.lines + transcripts.poll_interval_seconds roundtrip — set → resolve reflects file-stored values for daemon launch projection", () => {
const store = new ConfigStore(configPath);
// Defaults BEFORE set.
const before = store.resolve();
expect(before.transcripts.lines).toBe(1000);
expect(before.transcripts.pollIntervalSeconds).toBe(2);
store.set("transcripts.lines", "500");
store.set("transcripts.poll_interval_seconds", "5");
const after = store.resolve();
expect(after.transcripts.lines).toBe(500);
expect(after.transcripts.pollIntervalSeconds).toBe(5);
});
it("legacy 5-key behavior preserved: get/set/reset still work", () => {
const store = new ConfigStore(configPath);
store.set("daemon.port", "9999");
@@ -917,4 +917,48 @@ describe("buildDaemonEnv", () => {
expect(result["OPENRIG_PORT"]).toBe("7433");
expect(result["OPENRIG_DB"]).toBe("/new/path.db");
});
describe("V1 pre-release CLI/daemon Item 1 — transcript rotation tunables projection", () => {
it("projects transcriptsLines + transcriptsPollIntervalSeconds into OPENRIG_* env vars when provided", () => {
const baseEnv: Record<string, string> = { HOME: "/Users/tester", PATH: "/usr/bin" };
const result = buildDaemonEnv(baseEnv, {
port: 7433,
host: "127.0.0.1",
db: "/tmp/test.db",
transcriptsLines: 500,
transcriptsPollIntervalSeconds: 5,
});
expect(result["OPENRIG_TRANSCRIPTS_LINES"]).toBe("500");
expect(result["OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS"]).toBe("5");
});
it("omits the transcript rotation env vars when the opts are undefined so the rotation hook falls back to its own defaults", () => {
const baseEnv: Record<string, string> = { HOME: "/Users/tester", PATH: "/usr/bin" };
const result = buildDaemonEnv(baseEnv, {
port: 7433,
host: "127.0.0.1",
db: "/tmp/test.db",
});
expect(result["OPENRIG_TRANSCRIPTS_LINES"]).toBeUndefined();
expect(result["OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS"]).toBeUndefined();
});
it("scrubs inherited OPENRIG_TRANSCRIPTS_* from the base env when opts override is set, so the daemon honors the file-stored ConfigStore value", () => {
const baseEnv: Record<string, string> = {
HOME: "/Users/tester",
PATH: "/usr/bin",
OPENRIG_TRANSCRIPTS_LINES: "9999",
OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS: "60",
};
const result = buildDaemonEnv(baseEnv, {
port: 7433,
host: "127.0.0.1",
db: "/tmp/test.db",
transcriptsLines: 500,
transcriptsPollIntervalSeconds: 5,
});
expect(result["OPENRIG_TRANSCRIPTS_LINES"]).toBe("500");
expect(result["OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS"]).toBe("5");
});
});
});