Skip to content

Commit ffcb666

Browse files
fix(dreamer): log project identity, never absolute paths (privacy)
Dreamer/timer/git-commit log lines interpolated the full project directory (`reg.directory`, `dir=${projectDirectory}`, `cwd=${directory}`). Those absolute paths carry the OS username AND the project/client name — and they flow into `doctor --issue` GitHub reports. The issue bundler's `sanitizePathString` strips the home prefix + username but leaves the project subpath intact, so the project name still leaked into public reports. Fix at the source: every dreamer, dream-timer, and git-commit log line now emits the opaque project identity (`git:<sha>` / `dir:<hash>`) instead of the path. The identity uniquely correlates a run for debugging without exposing anything sensitive. 10 sites across 4 files: - dream-timer.ts: register/unregister/timer-tick/queue-fail + 3 git-commit sweep lines now use `projectIdentity` (already in scope on every one). - runner.ts: the dequeue line drops `dir=` entirely. - git-log-reader.ts: threaded an optional `projectIdentity` into ReadGitCommitsOptions purely for log correlation (falls back to a "<project>" placeholder); the two cwd= logs now print the label. The git `cwd` still uses the real directory — only the LOG text changed. - indexer.ts: passes projectPath as the identity to the reader. Pi inherits this automatically — it imports the shared-core dream timer and git-commit reader; no Pi-side path logging exists. Co-authored-by: Alfonso [Magic Context] <288211368+alfonso-magic-context@users.noreply.github.com>
1 parent f3f43cf commit ffcb666

4 files changed

Lines changed: 26 additions & 12 deletions

File tree

packages/plugin/src/features/magic-context/dreamer/runner.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,9 +1371,10 @@ export async function processDreamQueue(args: {
13711371
// sibling-worktree path the shared identity map might resolve to.
13721372
const projectDirectory =
13731373
args.sessionDirectoryOverride ?? resolveDreamSessionDirectory(entry.projectIdentity);
1374-
log(
1375-
`[dreamer] dequeued project ${entry.projectIdentity} (dir=${projectDirectory}), starting dream run`,
1376-
);
1374+
// Log the project identity only — never the resolved directory. The
1375+
// absolute path carries the username + project name (privacy), and the
1376+
// git:<sha>/dir:<hash> identity uniquely correlates the run for debugging.
1377+
log(`[dreamer] dequeued project ${entry.projectIdentity}, starting dream run`);
13771378

13781379
let result: DreamRunResult;
13791380
try {

packages/plugin/src/features/magic-context/git-commits/git-log-reader.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ export interface ReadGitCommitsOptions {
5959
branch?: string;
6060
/** Hard cap on returned commits. Default 5000. */
6161
maxCommits?: number;
62+
/**
63+
* Project identity (`git:<sha>` / `dir:<hash>`) used ONLY for log
64+
* correlation. We never log the absolute `directory` — it carries the
65+
* username + project name (privacy, and these logs flow into
66+
* `doctor --issue` reports). When omitted, logs fall back to a neutral
67+
* "<project>" placeholder.
68+
*/
69+
projectIdentity?: string;
6270
}
6371

6472
/**
@@ -85,6 +93,10 @@ export async function readGitCommits(
8593
`readGitCommits: refusing revision that looks like an option: "${revision}"`,
8694
);
8795
}
96+
// Privacy: logs identify the project by its opaque identity, never the
97+
// absolute cwd (which carries the username + project name and lands in
98+
// doctor --issue reports).
99+
const projectLabel = options.projectIdentity ?? "<project>";
88100
const args = [
89101
"log",
90102
revision,
@@ -116,13 +128,13 @@ export async function readGitCommits(
116128
// retry. We DO log the reason though — a silent empty-result masked a
117129
// real cwd / PATH / timeout bug during the v0.14 git-commits rollout.
118130
const message = error instanceof Error ? error.message : String(error);
119-
log(`[git-commits] readGitCommits failed at cwd=${directory}: ${message.slice(0, 500)}`);
131+
log(`[git-commits] readGitCommits failed for ${projectLabel}: ${message.slice(0, 500)}`);
120132
return [];
121133
}
122134

123135
if (stdout.trim().length === 0) {
124136
log(
125-
`[git-commits] readGitCommits returned empty stdout at cwd=${directory} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`,
137+
`[git-commits] readGitCommits returned empty stdout for ${projectLabel} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`,
126138
);
127139
}
128140

packages/plugin/src/features/magic-context/git-commits/indexer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export async function indexCommitsForProject(
9191
const commits = await readGitCommits(directory, {
9292
sinceMs,
9393
maxCommits: options.maxCommits,
94+
projectIdentity: projectPath,
9495
});
9596
result.scanned = commits.length;
9697

packages/plugin/src/plugin/dream-timer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export async function startDreamScheduleTimer(
110110

111111
if (isNewRegistration) {
112112
log(
113-
`[dreamer] registered project ${args.directory} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`,
113+
`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`,
114114
);
115115
}
116116

@@ -139,7 +139,7 @@ export async function startDreamScheduleTimer(
139139
return () => {
140140
registeredProjects.delete(args.directory);
141141
log(
142-
`[dreamer] unregistered project ${args.directory} (remaining=${registeredProjects.size})`,
142+
`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`,
143143
);
144144
if (registeredProjects.size === 0 && activeTimer) {
145145
clearInterval(activeTimer);
@@ -226,7 +226,7 @@ async function sweepProject(
226226

227227
try {
228228
log(
229-
`[dreamer] timer tick (${origin}) ${reg.directory} — checking schedule window "${reg.dreamerConfig.schedule}"`,
229+
`[dreamer] timer tick (${origin}) ${reg.projectIdentity} — checking schedule window "${reg.dreamerConfig.schedule}"`,
230230
);
231231
// Resolve THIS registration's project identity. The shared `dream_queue`
232232
// is process-global (any OpenCode/Pi instance can write to it), but
@@ -251,7 +251,7 @@ async function sweepProject(
251251
fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models),
252252
});
253253
} catch (error) {
254-
log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error);
254+
log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error);
255255
}
256256
}
257257

@@ -296,14 +296,14 @@ async function sweepGitCommits(args: {
296296
lease.reason === "cooldown_active"
297297
? `cooldown active until ${lease.nextAllowedAt}`
298298
: `lease held by ${lease.leaseHolder ?? "another holder"} until ${lease.leaseExpiresAt ?? "unknown"}`;
299-
log(`[git-commits] sweep skipped for ${projectIdentity} (${directory}): ${reason}`);
299+
log(`[git-commits] sweep skipped for ${projectIdentity}: ${reason}`);
300300
return;
301301
}
302302

303303
const startedAt = Date.now();
304304
const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
305305
log(
306-
`[git-commits] sweep starting for ${directory} (project=${projectIdentity} sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`,
306+
`[git-commits] sweep starting for ${projectIdentity} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`,
307307
);
308308
try {
309309
const result = await indexCommitsForProject(db, projectIdentity, directory, {
@@ -330,7 +330,7 @@ async function sweepGitCommits(args: {
330330
releaseGitSweepLease(db, projectIdentity, holderId);
331331
const elapsedMs = Date.now() - startedAt;
332332
log(
333-
`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error instanceof Error ? error.message : String(error)}`,
333+
`[git-commits] sweep failed for ${projectIdentity} after ${elapsedMs}ms: ${error instanceof Error ? error.message : String(error)}`,
334334
);
335335
} finally {
336336
stopRenewal();

0 commit comments

Comments
 (0)