huskies: rename project from storkit to huskies

Rename all references from storkit to huskies across the codebase:
- .storkit/ directory → .huskies/
- Binary name, Cargo package name, Docker image references
- Server code, frontend code, config files, scripts
- Fix script/test to build frontend before cargo clippy/test
  so merge worktrees have frontend/dist available for RustEmbed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-04-03 16:12:52 +01:00
parent a7035b6ba7
commit 2d8ccb3eb6
572 changed files with 1340 additions and 1220 deletions
@@ -73,7 +73,7 @@ impl AgentPool {
on feature branch. Writing merge_failure and blocking."
);
let story_path = project_root
.join(".storkit/work")
.join(".huskies/work")
.join(stage_dir)
.join(format!("{story_id}.md"));
let empty_diff_reason = "Feature branch has no code changes — the coder agent \
@@ -221,7 +221,7 @@ mod tests {
#[tokio::test]
async fn auto_assign_picks_up_story_queued_in_current() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
let current = sk.join("work/2_current");
std::fs::create_dir_all(&current).unwrap();
std::fs::write(
@@ -260,7 +260,7 @@ mod tests {
let root = tmp.path();
// Create project.toml with a QA agent.
let sk = root.join(".storkit");
let sk = root.join(".huskies");
std::fs::create_dir_all(&sk).unwrap();
std::fs::write(
sk.join("project.toml"),
@@ -269,7 +269,7 @@ mod tests {
.unwrap();
// Put a spike in 3_qa/ with review_hold: true.
let qa_dir = root.join(".storkit/work/3_qa");
let qa_dir = root.join(".huskies/work/3_qa");
std::fs::create_dir_all(&qa_dir).unwrap();
std::fs::write(
qa_dir.join("20_spike_test.md"),
@@ -298,7 +298,7 @@ mod tests {
#[tokio::test]
async fn auto_assign_ignores_coder_preference_when_story_is_in_qa_stage() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
let qa_dir = sk.join("work/3_qa");
std::fs::create_dir_all(&qa_dir).unwrap();
std::fs::write(
@@ -345,7 +345,7 @@ mod tests {
#[tokio::test]
async fn auto_assign_respects_coder_preference_when_story_is_in_current_stage() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
let current_dir = sk.join("work/2_current");
std::fs::create_dir_all(&current_dir).unwrap();
std::fs::write(
@@ -392,7 +392,7 @@ mod tests {
#[tokio::test]
async fn auto_assign_stage_mismatch_with_no_fallback_starts_no_agent() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
let qa_dir = sk.join("work/3_qa");
std::fs::create_dir_all(&qa_dir).unwrap();
// Only a coder agent is configured — no QA agent exists.
@@ -431,7 +431,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path().to_path_buf();
let sk_dir = root.join(".storkit");
let sk_dir = root.join(".huskies");
// Two stories waiting in 2_current, one coder agent.
fs::create_dir_all(sk_dir.join("work/2_current")).unwrap();
fs::write(
+10 -10
View File
@@ -18,7 +18,7 @@ impl AgentPool {
/// (called immediately after) picks up the right next-stage agents.
///
/// Algorithm:
/// 1. List all worktree directories under `{project_root}/.storkit/worktrees/`.
/// 1. List all worktree directories under `{project_root}/.huskies/worktrees/`.
/// 2. For each worktree, check whether its feature branch has commits ahead of the
/// base branch (`master` / `main`).
/// 3. If committed work is found AND the story is in `2_current/` or `3_qa/`:
@@ -153,7 +153,7 @@ impl AgentPool {
.unwrap_or_default()
.default_qa_mode();
let story_path = project_root
.join(".storkit/work/2_current")
.join(".huskies/work/2_current")
.join(format!("{story_id}.md"));
crate::io::story_metadata::resolve_qa_mode(&story_path, default_qa)
}
@@ -210,7 +210,7 @@ impl AgentPool {
});
} else {
let story_path = project_root
.join(".storkit/work/3_qa")
.join(".huskies/work/3_qa")
.join(format!("{story_id}.md"));
if let Err(e) =
crate::io::story_metadata::write_review_hold(&story_path)
@@ -268,7 +268,7 @@ impl AgentPool {
true
} else {
let story_path = project_root
.join(".storkit/work/3_qa")
.join(".huskies/work/3_qa")
.join(format!("{story_id}.md"));
let default_qa = crate::config::ProjectConfig::load(project_root)
.unwrap_or_default()
@@ -282,7 +282,7 @@ impl AgentPool {
if needs_human_review {
let story_path = project_root
.join(".storkit/work/3_qa")
.join(".huskies/work/3_qa")
.join(format!("{story_id}.md"));
if let Err(e) =
crate::io::story_metadata::write_review_hold(&story_path)
@@ -416,13 +416,13 @@ mod tests {
let root = tmp.path();
// Set up story in 2_current/.
let current = root.join(".storkit/work/2_current");
let current = root.join(".huskies/work/2_current");
fs::create_dir_all(&current).unwrap();
fs::write(current.join("60_story_test.md"), "test").unwrap();
// Create a worktree directory that is a fresh git repo with no commits
// ahead of its own base branch (simulates a worktree where no work was done).
let wt_dir = root.join(".storkit/worktrees/60_story_test");
let wt_dir = root.join(".huskies/worktrees/60_story_test");
fs::create_dir_all(&wt_dir).unwrap();
init_git_repo(&wt_dir);
@@ -447,7 +447,7 @@ mod tests {
init_git_repo(root);
// Set up story in 2_current/ and commit it so the project root is clean.
let current = root.join(".storkit/work/2_current");
let current = root.join(".huskies/work/2_current");
fs::create_dir_all(&current).unwrap();
fs::write(current.join("61_story_test.md"), "test").unwrap();
Command::new("git")
@@ -470,7 +470,7 @@ mod tests {
.unwrap();
// Create a real git worktree for the story.
let wt_dir = root.join(".storkit/worktrees/61_story_test");
let wt_dir = root.join(".huskies/worktrees/61_story_test");
fs::create_dir_all(wt_dir.parent().unwrap()).unwrap();
Command::new("git")
.args([
@@ -518,7 +518,7 @@ mod tests {
// and the story stays in 2_current/. The important assertion is that
// reconcile ran without panicking and the story is in a consistent state.
let in_current = current.join("61_story_test.md").exists();
let in_qa = root.join(".storkit/work/3_qa/61_story_test.md").exists();
let in_qa = root.join(".huskies/work/3_qa/61_story_test.md").exists();
assert!(
in_current || in_qa,
"story should be in 2_current/ or 3_qa/ after reconciliation"
+2 -2
View File
@@ -19,7 +19,7 @@ pub(in crate::agents::pool) fn is_agent_free(
}
pub(super) fn scan_stage_items(project_root: &Path, stage_dir: &str) -> Vec<String> {
let dir = project_root.join(".storkit").join("work").join(stage_dir);
let dir = project_root.join(".huskies").join("work").join(stage_dir);
if !dir.is_dir() {
return Vec::new();
}
@@ -169,7 +169,7 @@ mod tests {
fn scan_stage_items_returns_sorted_story_ids() {
use std::fs;
let tmp = tempfile::tempdir().unwrap();
let stage_dir = tmp.path().join(".storkit").join("work").join("2_current");
let stage_dir = tmp.path().join(".huskies").join("work").join("2_current");
fs::create_dir_all(&stage_dir).unwrap();
fs::write(stage_dir.join("42_story_foo.md"), "---\nname: foo\n---").unwrap();
fs::write(stage_dir.join("10_story_bar.md"), "---\nname: bar\n---").unwrap();
@@ -13,7 +13,7 @@ pub(super) fn read_story_front_matter_agent(
) -> Option<String> {
use crate::io::story_metadata::parse_front_matter;
let path = project_root
.join(".storkit")
.join(".huskies")
.join("work")
.join(stage_dir)
.join(format!("{story_id}.md"));
@@ -25,7 +25,7 @@ pub(super) fn read_story_front_matter_agent(
pub(super) fn has_review_hold(project_root: &Path, stage_dir: &str, story_id: &str) -> bool {
use crate::io::story_metadata::parse_front_matter;
let path = project_root
.join(".storkit")
.join(".huskies")
.join("work")
.join(stage_dir)
.join(format!("{story_id}.md"));
@@ -43,7 +43,7 @@ pub(super) fn has_review_hold(project_root: &Path, stage_dir: &str, story_id: &s
pub(super) fn is_story_blocked(project_root: &Path, stage_dir: &str, story_id: &str) -> bool {
use crate::io::story_metadata::parse_front_matter;
let path = project_root
.join(".storkit")
.join(".huskies")
.join("work")
.join(stage_dir)
.join(format!("{story_id}.md"));
@@ -61,7 +61,7 @@ pub(super) fn is_story_blocked(project_root: &Path, stage_dir: &str, story_id: &
pub(super) fn has_merge_failure(project_root: &Path, stage_dir: &str, story_id: &str) -> bool {
use crate::io::story_metadata::parse_front_matter;
let path = project_root
.join(".storkit")
.join(".huskies")
.join("work")
.join(stage_dir)
.join(format!("{story_id}.md"));
@@ -84,7 +84,7 @@ mod tests {
#[test]
fn has_review_hold_returns_true_when_set() {
let tmp = tempfile::tempdir().unwrap();
let qa_dir = tmp.path().join(".storkit/work/3_qa");
let qa_dir = tmp.path().join(".huskies/work/3_qa");
std::fs::create_dir_all(&qa_dir).unwrap();
let spike_path = qa_dir.join("10_spike_research.md");
std::fs::write(
@@ -98,7 +98,7 @@ mod tests {
#[test]
fn has_review_hold_returns_false_when_not_set() {
let tmp = tempfile::tempdir().unwrap();
let qa_dir = tmp.path().join(".storkit/work/3_qa");
let qa_dir = tmp.path().join(".huskies/work/3_qa");
std::fs::create_dir_all(&qa_dir).unwrap();
let spike_path = qa_dir.join("10_spike_research.md");
std::fs::write(&spike_path, "---\nname: Research spike\n---\n# Spike\n").unwrap();