Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7296d92
feat(layout): scale pinned placement with spatial hash
integrate-your-mind Jan 30, 2026
f68594c
perf(ui): stabilize canvas rendering loop
integrate-your-mind Jan 30, 2026
374f06a
chore: update continuity ledger
integrate-your-mind Jan 30, 2026
dc7fe61
fix(activity): stabilize codex and opencode states
integrate-your-mind Jan 30, 2026
922e9b9
fix(dev): increment server port and refresh opencode status
integrate-your-mind Jan 30, 2026
ee9f28d
fix(activity): smooth opencode transitions and avoid wheel warnings
integrate-your-mind Jan 31, 2026
6d7aa5d
fix(opencode): treat paused statuses as idle
integrate-your-mind Jan 31, 2026
223e7ce
chore(dev): improve tmux worktree setup
integrate-your-mind Feb 1, 2026
87ede28
fix(opencode): improve subagent visibility
integrate-your-mind Feb 1, 2026
14b447c
fix(opencode): prevent stale subagents and child session binding
integrate-your-mind Feb 1, 2026
9b383a9
fix(codex): add default hold window to reduce flicker
integrate-your-mind Feb 1, 2026
05107e6
fix(codex): detect codex-cli processes
integrate-your-mind Feb 2, 2026
115124a
fix(activity): prevent false idle and dev proxy misroutes
integrate-your-mind Feb 2, 2026
d3775da
fix(codex): defer end during tool calls
integrate-your-mind Feb 3, 2026
7d908ae
fix(codex): track item work as open calls
integrate-your-mind Feb 3, 2026
72aaea8
fix(codex): keep in-flight for item work
integrate-your-mind Feb 3, 2026
5051521
fix(codex): avoid end flicker between tools
integrate-your-mind Feb 3, 2026
d36f78e
fix(codex): defer end until quiet window
integrate-your-mind Feb 3, 2026
802c8a8
fix(codex): keep in-flight while pending end
integrate-your-mind Feb 3, 2026
507900e
fix(codex): stabilize jsonl activity tracking
integrate-your-mind Feb 5, 2026
29b898c
refactor(codex): extract session assignment and add flicker harness
integrate-your-mind Feb 5, 2026
691ca56
docs: add codex data flow and state notes
integrate-your-mind Feb 5, 2026
f1cf5d2
fix(server): make /api/snapshot cached by default
integrate-your-mind Feb 5, 2026
0f901d1
chore(qa): add flicker transition logs and optional video
integrate-your-mind Feb 5, 2026
e88c62a
chore(qa): add codex TUI/video demo scenarios
integrate-your-mind Feb 6, 2026
0668785
docs: add postmortem for codex activity flicker
integrate-your-mind Feb 6, 2026
2cc155f
docs: codify feature verification workflow (tests, evidence, demo video)
integrate-your-mind Feb 6, 2026
d00a7da
fix(config): unify poll interval and opencode timeout defaults
integrate-your-mind Feb 6, 2026
44622cd
fix(ui): handle pid=0 and mock snapshot query params
integrate-your-mind Feb 6, 2026
0bcd27d
test(codex): remove lifecycle graph and cover jsonl tail
integrate-your-mind Feb 6, 2026
d7635b6
chore(dev): harden dev runner and fix worktree helper
integrate-your-mind Feb 6, 2026
d620c67
docs: align CONSENSUS_POLL_MS default
integrate-your-mind Feb 6, 2026
6d47e53
fix(codex): tail catch-up and delta fast-path
integrate-your-mind Feb 6, 2026
3ae8309
fix(activity): finalize TUI sessions and harden opencode stale cutoff
integrate-your-mind Feb 6, 2026
65b2b43
fix(codex): ignore token_count for pending-end markers
integrate-your-mind Feb 6, 2026
c98983c
fix(setup): merge required codex notifications
integrate-your-mind Feb 6, 2026
2d1c9bd
fix(ui): avoid spatial cell-key wrapping
integrate-your-mind Feb 6, 2026
c5d17d9
test(ui): filter live TUI demo lane by match query
integrate-your-mind Feb 6, 2026
cf91d42
fix(test): make npm test portable across shells
integrate-your-mind Feb 6, 2026
9232125
fix(codex): clear stale tail markers
integrate-your-mind Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CONTINUITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Key decisions:
- React UI is primary; server should serve React build when present.
- Agent lane follows vanilla JS behavior: show all open sessions (active + idle) and sort by state rank then CPU.
- Layout uses pinned positions per agent identity; only new agents are placed; existing tiles never move; placement uses max-height bounds to prevent overlap.
- Scale placement with spatial hash buckets (cell -> occupants) and persistent per-group spiral frontier; collisions checked by bounds against bucket occupants.
- Codex prompt-like titles containing temp paths/turn markers are ignored for lane labels; fallback to repo or codex#pid.
- Agent lane now shows only active/error sessions and uses stable sort (state rank then identity) to prevent reordering.
- Active lane items now glow via `is-active` class (lane item box-shadow) to make active codex sessions visibly highlighted.
Expand Down Expand Up @@ -133,6 +134,11 @@ State:
- Tests re-run: `npm run test:unit` (149 pass), `npm run test:integration` (54 pass), `npm run test:ui` (19 pass).
- Build run after pinned layout changes: `npm run build` (pass).
- Copied edited files to clipboard via pbcopy; temp bundle at `/tmp/consensus-edited-files.txt`.
- Implemented spatial hash buckets + persistent group spiral frontier; fixed cell packing and bounds-based collision checks (public/src/lib/layout.ts).
- Renderer now keeps a single RAF loop, exposes hit list, and uses correct view transform for hit tests (public/src/hooks/useCanvasRenderer.ts).
- CanvasScene no longer rebuilds hit list; uses renderer hit list and stable render loop (public/src/components/CanvasScene.tsx).
- Obstruction detection checks only front occluders with AABB guard (public/src/hooks/useCanvasRenderer.ts).
- Tests run after layout/renderer updates: `npm run test:unit` (152 pass), `npm run test:integration` (58 pass), `npm run test:ui` (19 pass).
- Now:
- Answer user question about hot reload configuration (Vite HMR + server watch + live reload SSE).
- Answer whether any additional setup is missing when using Vite.
Expand Down Expand Up @@ -161,6 +167,7 @@ State:
- Claude CLI hooks not firing for real sessions; user reports Claude does not show active when working.
- Need to validate live Claude session while recording via agent-browser and inspect hook delivery.
- Copy edited layout/test files to clipboard via pbcopy. (DONE)
- Implement spatial hash buckets + persistent group frontier with correct cell packing and bounds checks. (DONE)
- User requested a best-practice plan (no code edits) to address review comment: `npm run dev` no longer starts Vite, so TSX client fails unless dev:client or build is run.
- Q&A answered for dev workflow: always start Vite; use CONSENSUS_UI_PORT for port; on port conflict try next port.
- Review plan requested for new P2/P3 items: Codex in-flight timeout default in codexLogs; TOML [tui] notifications insertion in cli/setup.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,19 @@ consensus dev server running on http://127.0.0.1:8787
- `CONSENSUS_OPENCODE_EVENT_ACTIVE_MS`: OpenCode active window after last event in ms (default `0`).
- `CONSENSUS_OPENCODE_ACTIVE_HOLD_MS`: OpenCode hold window in ms (default `3000`).
- `CONSENSUS_OPENCODE_INFLIGHT_IDLE_MS`: OpenCode in-flight idle timeout in ms (defaults to `CONSENSUS_OPENCODE_INFLIGHT_TIMEOUT_MS`).
- `CONSENSUS_OPENCODE_INFLIGHT_TIMEOUT_MS`: OpenCode hard in-flight timeout in ms (default `15000`).
- `CONSENSUS_OPENCODE_INFLIGHT_TIMEOUT_MS`: OpenCode hard in-flight timeout in ms (default `2500`).
- `CONSENSUS_OPENCODE_INFLIGHT_STALE_MS`: OpenCode stale in-flight cutoff in ms (default `0`).
- `CONSENSUS_PROCESS_MATCH`: regex to match codex processes.
- `CONSENSUS_REDACT_PII`: set to `0` to disable redaction (default enabled).
- `CONSENSUS_UI_PORT`: dev UI port for Vite when running `npm run dev` (default `5173`).
- `CONSENSUS_DEBUG_OPENCODE`: set to `1` to log OpenCode server discovery.
- `CONSENSUS_CODEX_EVENT_ACTIVE_MS`: Codex active window after last event in ms (default `30000`).
- `CONSENSUS_CODEX_ACTIVE_HOLD_MS`: Codex hold window in ms (default `3000`).
- `CONSENSUS_CODEX_ACTIVE_HOLD_MS`: Codex hold window in ms (default `0`).
- `CONSENSUS_CODEX_INFLIGHT_IDLE_MS`: Codex in-flight idle timeout in ms (default `30000`, set to `0` to disable).
- `CONSENSUS_CODEX_CPU_SUSTAIN_MS`: sustained CPU window before Codex becomes active without logs (default `500`).
- `CONSENSUS_CODEX_CPU_SPIKE`: Codex CPU spike threshold for immediate activation (default derived).
- `CONSENSUS_CODEX_INFLIGHT_TIMEOUT_MS`: Codex in-flight timeout in ms (default `3000`).
- `CONSENSUS_CODEX_SIGNAL_MAX_AGE_MS`: Codex max event age for in-flight signals (default `CONSENSUS_CODEX_INFLIGHT_TIMEOUT_MS`).
- `CONSENSUS_CODEX_INFLIGHT_TIMEOUT_MS`: Codex in-flight timeout in ms (default `2500`).
- `CONSENSUS_CODEX_SIGNAL_MAX_AGE_MS`: Codex max event age for in-flight signals (default `max(CONSENSUS_CODEX_INFLIGHT_TIMEOUT_MS, 2500)`).
- `CONSENSUS_PROCESS_CACHE_MS`: process cache TTL in ms for full scans (default `1000`).
- `CONSENSUS_PROCESS_CACHE_FAST_MS`: process cache TTL in ms for fast scans (default `500`).
- `CONSENSUS_SESSION_CACHE_MS`: Codex session list cache TTL in ms for full scans (default `1000`).
Expand Down
74 changes: 72 additions & 2 deletions dev/dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const tsc = spawnChild("tsc", [
"--pretty",
"false",
]);
const server = spawnChild("server", [tsxPath, "watch", "src/server.ts"]);

const parsePort = (value) => {
const num = Number(value);
Expand All @@ -66,6 +65,77 @@ const basePort =
parsePort(process.env.VITE_PORT) ??
5173;

const baseServerPort = parsePort(process.env.CONSENSUS_PORT) ?? 8787;

const startServer = async () => {
const maxAttempts = 6;

for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const port = baseServerPort + attempt;
process.stderr.write(`[dev] starting server on port ${port}\n`);

const child = spawn(
process.execPath,
[tsxPath, "watch", "src/server.ts"],
{
env: { ...process.env, CONSENSUS_PORT: String(port) },
stdio: ["ignore", "pipe", "pipe"],
}
);

let output = "";
let running = false;
const onData = (chunk) => {
const text = chunk.toString();
output += text;
process.stdout.write(text);
};
child.stdout?.on("data", onData);
child.stderr?.on("data", onData);

child.on("exit", (code) => {
if (running) {
process.stderr.write(`[dev] server exited with ${code ?? 0}\n`);
shutdown(code ?? 1);
}
});

const result = await new Promise((resolve) => {
let settled = false;
const timer = setTimeout(() => {
if (settled) return;
settled = true;
running = true;
children.push(child);
resolve({ status: "running" });
}, 1500);

child.on("exit", (code) => {
if (settled) return;
settled = true;
clearTimeout(timer);
const conflict = /EADDRINUSE|address already in use|port .* in use/i.test(
output
);
resolve({ status: conflict ? "conflict" : "exit", code });
});
});

if (result.status === "running") return;
if (result.status === "conflict") {
process.stderr.write(`[dev] port ${port} in use, trying next\n`);
continue;
}

process.stderr.write("[dev] server exited unexpectedly\n");
shutdown(1);
return;
}

process.stderr.write("[dev] failed to find an open port for server\n");
shutdown(1);
};

const startVite = async () => {
const maxAttempts = 6;

Expand Down Expand Up @@ -127,6 +197,7 @@ const startVite = async () => {
shutdown(1);
};

void startServer();
void startVite();

const shutdown = (code = 0) => {
Expand All @@ -138,7 +209,6 @@ const shutdown = (code = 0) => {
};

tsc.on("exit", (code) => shutdown(code ?? 0));
server.on("exit", (code) => shutdown(code ?? 0));

process.on("SIGINT", () => shutdown(0));
process.on("SIGTERM", () => shutdown(0));
11 changes: 10 additions & 1 deletion dev/tmux-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@
set -euo pipefail

SCOUT_DIR="$(pwd -P)"
exec env SCOUT_DIR="$SCOUT_DIR" tmux -f ./dev/tmux.conf new -A -s scout

if tmux has-session -t scout 2>/dev/null; then
tmux set-environment -g SCOUT_DIR "$SCOUT_DIR"
tmux set-environment -g PATH "$PATH"
tmux set-environment -t scout SCOUT_DIR "$SCOUT_DIR"
tmux set-environment -t scout PATH "$PATH"
exec tmux -f ./dev/tmux.conf attach -t scout
fi

exec env SCOUT_DIR="$SCOUT_DIR" PATH="$PATH" tmux -f ./dev/tmux.conf new -s scout
9 changes: 6 additions & 3 deletions dev/tmux.conf
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Keep PATH in sync between shell and tmux.
set -ga update-environment "PATH"

# Show useful window names automatically: "<command>:<directory>"
# This makes Ctrl+b w show both the agent (command) and the worktree (dir).
setw -g automatic-rename on
setw -g automatic-rename-format '#{pane_current_command}:#{b:pane_current_path}'

# New windows should always start in the default worktree ("scout directory" in the demo).
# We set SCOUT_DIR in the session start command (see next section).
bind c new-window -c "#{env:SCOUT_DIR}"
# New windows should start in the default worktree when SCOUT_DIR is set.
# Fallback to the current pane path if SCOUT_DIR is missing.
bind c new-window -c "#{?env:SCOUT_DIR,#{env:SCOUT_DIR},#{pane_current_path}}"
Loading
Loading