fix: auto-assign after merge, persistent server logs, remove duplicate pnpm install
- Call auto_assign_available_work at end of merge_agent_work so the next story gets picked up without waiting for the PTY exit handler - Add persistent file logging to .story_kit/logs/server.log so server logs survive restarts - Remove duplicate pnpm install block in run_squash_merge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1283,6 +1283,12 @@ impl AgentPool {
|
||||
false
|
||||
};
|
||||
|
||||
// Mergemaster slot is now free — trigger auto-assign so remaining
|
||||
// items in 4_merge/ (or other stages) get picked up. The normal
|
||||
// server-owned completion handler won't run because we already
|
||||
// removed the agent entry above.
|
||||
self.auto_assign_available_work(project_root).await;
|
||||
|
||||
Ok(MergeReport {
|
||||
story_id: story_id.to_string(),
|
||||
success: true,
|
||||
@@ -2797,57 +2803,17 @@ fn run_squash_merge(
|
||||
});
|
||||
}
|
||||
|
||||
// ── Install frontend dependencies for quality gates ──────────────
|
||||
let frontend_dir_for_install = merge_wt_path.join("frontend");
|
||||
if frontend_dir_for_install.exists() {
|
||||
// Ensure frontend/dist/ exists so cargo clippy (RustEmbed) can compile
|
||||
// even before `pnpm build` has run.
|
||||
let dist_dir = frontend_dir_for_install.join("dist");
|
||||
std::fs::create_dir_all(&dist_dir)
|
||||
.map_err(|e| format!("Failed to create frontend/dist: {e}"))?;
|
||||
|
||||
all_output.push_str("=== pnpm install (merge worktree) ===\n");
|
||||
let pnpm_install = Command::new("pnpm")
|
||||
.args(["install"])
|
||||
.current_dir(&frontend_dir_for_install)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run pnpm install: {e}"))?;
|
||||
|
||||
let install_out = format!(
|
||||
"{}{}",
|
||||
String::from_utf8_lossy(&pnpm_install.stdout),
|
||||
String::from_utf8_lossy(&pnpm_install.stderr)
|
||||
);
|
||||
all_output.push_str(&install_out);
|
||||
all_output.push('\n');
|
||||
|
||||
if !pnpm_install.status.success() {
|
||||
all_output.push_str("=== pnpm install FAILED — aborting merge ===\n");
|
||||
cleanup_merge_workspace(project_root, &merge_wt_path, &merge_branch);
|
||||
return Ok(SquashMergeResult {
|
||||
success: false,
|
||||
had_conflicts,
|
||||
conflicts_resolved,
|
||||
conflict_details,
|
||||
output: all_output,
|
||||
gates_passed: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Install frontend dependencies for quality gates ──────────
|
||||
let frontend_dir = merge_wt_path.join("frontend");
|
||||
if frontend_dir.exists() {
|
||||
// Ensure frontend/dist exists so RustEmbed (cargo clippy) doesn't fail
|
||||
// even before pnpm build runs.
|
||||
let dist_dir = frontend_dir.join("dist");
|
||||
if !dist_dir.exists() {
|
||||
let _ = std::fs::create_dir_all(&dist_dir);
|
||||
}
|
||||
let _ = std::fs::create_dir_all(&dist_dir);
|
||||
|
||||
all_output.push_str("=== pnpm install (merge worktree) ===\n");
|
||||
let pnpm_install = Command::new("pnpm")
|
||||
.args(["install", "--frozen-lockfile"])
|
||||
.args(["install"])
|
||||
.current_dir(&frontend_dir)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run pnpm install: {e}"))?;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
//! `get_server_logs` MCP tool.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
const CAPACITY: usize = 1000;
|
||||
@@ -68,12 +71,22 @@ impl LogEntry {
|
||||
|
||||
pub struct LogBuffer {
|
||||
entries: Mutex<VecDeque<LogEntry>>,
|
||||
log_file: Mutex<Option<PathBuf>>,
|
||||
}
|
||||
|
||||
impl LogBuffer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
entries: Mutex::new(VecDeque::with_capacity(CAPACITY)),
|
||||
log_file: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the persistent log file path. Call once at startup after the
|
||||
/// project root is known.
|
||||
pub fn set_log_file(&self, path: PathBuf) {
|
||||
if let Ok(mut f) = self.log_file.lock() {
|
||||
*f = Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +99,15 @@ impl LogBuffer {
|
||||
message,
|
||||
};
|
||||
eprintln!("{}", entry.colored_formatted());
|
||||
|
||||
// Append to persistent log file (best-effort).
|
||||
if let Ok(guard) = self.log_file.lock()
|
||||
&& let Some(ref path) = *guard
|
||||
&& let Ok(mut file) = OpenOptions::new().create(true).append(true).open(path)
|
||||
{
|
||||
let _ = writeln!(file, "{}", entry.formatted());
|
||||
}
|
||||
|
||||
if let Ok(mut buf) = self.entries.lock() {
|
||||
if buf.len() >= CAPACITY {
|
||||
buf.pop_front();
|
||||
@@ -188,6 +210,7 @@ mod tests {
|
||||
fn evicts_oldest_at_capacity() {
|
||||
let buf = LogBuffer {
|
||||
entries: Mutex::new(VecDeque::with_capacity(CAPACITY)),
|
||||
log_file: Mutex::new(None),
|
||||
};
|
||||
// Fill past capacity
|
||||
for i in 0..=CAPACITY {
|
||||
|
||||
@@ -100,6 +100,13 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable persistent server log file now that the project root is known.
|
||||
if let Some(ref root) = *app_state.project_root.lock().unwrap() {
|
||||
let log_dir = root.join(".story_kit").join("logs");
|
||||
let _ = std::fs::create_dir_all(&log_dir);
|
||||
log_buffer::global().set_log_file(log_dir.join("server.log"));
|
||||
}
|
||||
|
||||
let workflow = Arc::new(std::sync::Mutex::new(WorkflowState::default()));
|
||||
|
||||
// Filesystem watcher: broadcast channel for work/ pipeline changes.
|
||||
|
||||
Reference in New Issue
Block a user