Every crosslink invocation on a fresh project emits:
WARN no tracker_remote configured in /path/.crosslink/hook-config.json, defaulting to \"origin\"
…and then proceeds to use origin anyway. The WARN is redundant noise that appears on every invocation until someone explicitly writes tracker_remote: \"origin\" into hook-config.json. It accumulates fast across kickoff agents, hooks, and interactive shell use.
Worse: the WARN appears to trigger SOMETHING (an auto-mutation hook?) that writes tracker_remote: \"origin\" into hook-config.json programmatically. On my Mac it did this cleanly. On a Linux GPU box in the same project today, the same auto-write also serialized form-editor placeholder values into adjacent keys:
auto_steal_stale_locks: false → \"5\" (bool flipped to string with wrong value)
reminder_drift_threshold: 3 → \"0\" (int flipped to string)
- Six bogus dot-notation duplicate keys like
\"sentinel.default_agent.model\": \"(text)\" with literal (text) placeholders as values
So the auto-mutation that's silently "fixing" the missing tracker_remote is the same code path that corrupts other keys under some unknown trigger condition. (The user had not run crosslink workflow or crosslink init per history — the mutation was hook-driven, not interactive.) Worth tracking down regardless, but the cleanest fix is to make the WARN unnecessary in the first place so no hook needs to react to it.
Proposed default behavior
When hook-config.json lacks tracker_remote, infer it without warning:
- If there's exactly one git remote: use it. Most common case (
origin).
- If there are multiple git remotes: prefer
origin if present, else use the first lexicographic. Optionally emit a DEBUG-level log (not WARN) noting the choice for visibility.
- If there are zero git remotes: this is the only case where a real user-facing message is warranted — "no git remote found; configure one before sync" or similar.
This obviates the WARN entirely for the >99% of projects with an origin remote.
Optional: opportunity to override
For projects where origin is not the desired tracker_remote (e.g. multi-remote setups where someone wants upstream or a custom name), surface a one-shot interactive override:
- On first invocation in a multi-remote project, prompt:
Multiple git remotes detected (origin, upstream). Use 'origin' as tracker_remote? [Y/n/<custom>].
- On non-interactive contexts (hooks, scripts, agents): just pick the inferred value silently and emit a DEBUG log.
The result of either path persists to hook-config.json exactly the way the current auto-mutation does — but at a deliberate, traceable point in time (CLI flow), not via a hook reacting to its own log output.
Implementation sketch
In whatever currently emits the WARN:
let tracker_remote = match hook_config.tracker_remote {
Some(name) => name,
None => {
let remotes = list_git_remotes(&repo_path)?;
match remotes.as_slice() {
[single] => single.clone(),
many if many.contains(&\"origin\".to_string()) => \"origin\".to_string(),
many if !many.is_empty() => many[0].clone(),
[] => bail!(\"no git remote configured; run `git remote add origin <url>` first\"),
}
}
};
Plus optionally: a one-shot persist of the chosen value back to hook-config.json only on first interactive run (gated behind isatty(stdin) and --no-persist or similar opt-out).
Cross-refs
Happy to send a PR if useful.
Every
crosslinkinvocation on a fresh project emits:…and then proceeds to use
originanyway. The WARN is redundant noise that appears on every invocation until someone explicitly writestracker_remote: \"origin\"intohook-config.json. It accumulates fast across kickoff agents, hooks, and interactive shell use.Worse: the WARN appears to trigger SOMETHING (an auto-mutation hook?) that writes
tracker_remote: \"origin\"intohook-config.jsonprogrammatically. On my Mac it did this cleanly. On a Linux GPU box in the same project today, the same auto-write also serialized form-editor placeholder values into adjacent keys:auto_steal_stale_locks: false → \"5\"(bool flipped to string with wrong value)reminder_drift_threshold: 3 → \"0\"(int flipped to string)\"sentinel.default_agent.model\": \"(text)\"with literal(text)placeholders as valuesSo the auto-mutation that's silently "fixing" the missing tracker_remote is the same code path that corrupts other keys under some unknown trigger condition. (The user had not run
crosslink workfloworcrosslink initperhistory— the mutation was hook-driven, not interactive.) Worth tracking down regardless, but the cleanest fix is to make the WARN unnecessary in the first place so no hook needs to react to it.Proposed default behavior
When
hook-config.jsonlackstracker_remote, infer it without warning:origin).originif present, else use the first lexicographic. Optionally emit a DEBUG-level log (not WARN) noting the choice for visibility.This obviates the WARN entirely for the >99% of projects with an
originremote.Optional: opportunity to override
For projects where
originis not the desired tracker_remote (e.g. multi-remote setups where someone wantsupstreamor a custom name), surface a one-shot interactive override:Multiple git remotes detected (origin, upstream). Use 'origin' as tracker_remote? [Y/n/<custom>].The result of either path persists to
hook-config.jsonexactly the way the current auto-mutation does — but at a deliberate, traceable point in time (CLI flow), not via a hook reacting to its own log output.Implementation sketch
In whatever currently emits the WARN:
Plus optionally: a one-shot persist of the chosen value back to
hook-config.jsononly on first interactive run (gated behindisatty(stdin)and--no-persistor similar opt-out).Cross-refs
hook-config.jsonon the GPU box. Likely same root cause once the auto-mutation hook stops needing to fire.Happy to send a PR if useful.