diff --git a/onboarding/.codewhale/commands/surf-setup.md b/onboarding/.codewhale/commands/surf-setup.md new file mode 100644 index 0000000000..200a8808eb --- /dev/null +++ b/onboarding/.codewhale/commands/surf-setup.md @@ -0,0 +1,4 @@ +--- +execute: .codewhale/skills/surf/scripts/catch-wave.sh +description: "🌊 Catch a new wave. Clones a repository and initializes a testbed." +--- \ No newline at end of file diff --git a/onboarding/.codewhale/commands/surf.md b/onboarding/.codewhale/commands/surf.md new file mode 100644 index 0000000000..f7a85268c8 --- /dev/null +++ b/onboarding/.codewhale/commands/surf.md @@ -0,0 +1,35 @@ +--- +execute: .codewhale/skills/surf/scripts/surf.sh +description: "🌊 Ride the CodeWhale wave. Updates, builds, and tests the testbed." +--- + +# 🌊 Surf β€” Ride the CodeWhale Wave + +This command runs the deterministic core of the Surf suite. It checks the current environment, updates the testbed if possible, and outputs a receipt. + +## Behavior + +| State | Action | +|---|---| +| **Empty directory** | Tells you to run `/surf setup` | +| **Clean testbed** | Pulls, builds, tests, writes receipt | +| **Dirty testbed** | Stops with a warning | +| **Unknown repo** | Stops with guidance | + +## Sub-Commands + +| Command | Action | +|---|---| +| `/surf` | Runs the main orchestrator | +| `/surf setup` | Clones the repo and initializes a testbed | + +## Example + +```text +/surf +🌊 Checking the wave... +🌊 Wave is clean. Riding... +... +βœ… Surf complete. +πŸ“„ Receipt written: receipts/latest_receipt.json +``` diff --git a/onboarding/.codewhale/skills/surf/SKILL.md b/onboarding/.codewhale/skills/surf/SKILL.md new file mode 100644 index 0000000000..ea1cbfedab --- /dev/null +++ b/onboarding/.codewhale/skills/surf/SKILL.md @@ -0,0 +1,80 @@ +# Skill: Onboarding Suite πŸ‹ + +## Name +`onboarding-suite` + +## Description +Keeps your isolated CodeWhale testbed in sync with the latest `main` branch. It runs a deterministic core (no LLM required) to pull, build, verify, and generate a digest. Optionally, it can enhance the digest with an LLM‑generated summary if you request it. + +## Triggers +- `/onboarding-suite` – runs the deterministic core only. +- `/onboarding-suite --summary` – runs the deterministic core and appends an LLM summary. + +--- + +## Deterministic Core (Always Runs) + +The core uses three Bash scripts located in the same directory as this skill under `scripts/`. It does not require an internet connection except for `git pull`. + +1. **Environment Check** + Runs `scripts/check-testbed.sh`. + - If `STATUS=empty-or-no-git` β†’ ask the user if they want to clone and set up a testbed here. + - If `STATUS=testbed` + `DIRTY=false` β†’ proceed to update. + - If `STATUS=testbed` + `DIRTY=true` β†’ stop and instruct the user to clean the working tree. + - If `STATUS=unknown-repo` β†’ stop and explain that the directory isn't a recognised testbed. + +2. **Setup (if needed)** + If the user agrees to create a testbed in an empty directory, run `scripts/setup-testbed.sh`. This clones the repository and creates `.onboarding-init`. + +3. **Update & Verify** + Run `scripts/update-testbed.sh`. This does: + - `git pull --ff-only` + - `cargo fmt --check` + - `cargo clippy -- -D warnings` + - `cargo test --workspace` + - Generates a factual digest from `CHANGELOG.md` (first version entry). + +4. **Output Receipt** + Write a JSON receipt to `receipts/latest_receipt.json` with: + - timestamp + - branch name + - commit hash + - test counts (passed/failed) + - the digest text + +--- + +## Optional LLM Enhancement + +If the `--summary` flag is provided, the skill will append a human‑readable summary after the deterministic output. The summary is generated by the LLM using the following context: + +- The digest text from the receipt. +- Recent PR titles and milestone names (fetched from the local repo or provided in the receipt). +- Any error or warning messages from the verification steps. + +The LLM should: +- Explain what changed since the last sync (if anything) +- Highlight any potential impacts on the user's previous work (e.g., Phase 1 fixes) +- Suggest next steps if there are warnings or failures + +The enhancement is purely additive; the deterministic receipt remains the source of truth. + +--- + +## Usage Examples + +```text +/onboarding-suite # deterministic only +/onboarding-suite --summary # deterministic + LLM summary +/onboarding-suite --help # shows this help +``` + +--- + +## Notes + +- The testbed is identified by the presence of `.onboarding-init` at its root. +- The skill never automatically pulls over a dirty working tree – it stops and asks. +- All scripts are self‑contained and require no external dependencies beyond standard Unix tools (`git`, `cargo`, `sed`). + +_All of it. Fine._ πŸ‹ \ No newline at end of file diff --git a/onboarding/.codewhale/skills/surf/scripts/catch-wave.sh b/onboarding/.codewhale/skills/surf/scripts/catch-wave.sh new file mode 100644 index 0000000000..658ae13050 --- /dev/null +++ b/onboarding/.codewhale/skills/surf/scripts/catch-wave.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -n "$(ls -A)" ]; then + echo "ERROR: Directory not empty. Refusing to install." + exit 1 +fi + +# Ask for repo URL (with default) +read -p "Enter repository URL (default: https://github.com/Hmbown/CodeWhale.git): " REPO_URL +REPO_URL=${REPO_URL:-https://github.com/Hmbown/CodeWhale.git} + +read -p "Enter branch (default: main): " BRANCH +BRANCH=${BRANCH:-main} + +echo "Cloning $REPO_URL (branch: $BRANCH)..." +git clone --branch "$BRANCH" "$REPO_URL" . + +# Create config file +cat > .surf-config << EOF +# Surf configuration +REPO_URL=$REPO_URL +BRANCH=$BRANCH +ONBOARDING_INIT=true +EOF + +echo "Testbed initialized. Config written to .surf-config." \ No newline at end of file diff --git a/onboarding/.codewhale/skills/surf/scripts/check-wave.sh b/onboarding/.codewhale/skills/surf/scripts/check-wave.sh new file mode 100644 index 0000000000..90f8e64b88 --- /dev/null +++ b/onboarding/.codewhale/skills/surf/scripts/check-wave.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ ! -d ".git" ]; then + echo "STATUS=empty-or-no-git" + echo "MESSAGE=No Git repository found." + exit 0 +fi + +if [ -f ".surf-config" ]; then + # Load config + source .surf-config + if [ "${ONBOARDING_INIT:-false}" = "true" ]; then + echo "STATUS=testbed" + echo "MESSAGE=Testbed detected (${REPO_URL:-unknown})" + else + echo "STATUS=unknown-repo" + echo "MESSAGE=.surf-config found but ONBOARDING_INIT=false" + fi +else + echo "STATUS=unknown-repo" + echo "MESSAGE=Git repository without .surf-config marker." +fi + +if [ -n "$(git status --porcelain)" ]; then + echo "DIRTY=true" +else + echo "DIRTY=false" +fi \ No newline at end of file diff --git a/onboarding/.codewhale/skills/surf/scripts/ride-wave.sh b/onboarding/.codewhale/skills/surf/scripts/ride-wave.sh new file mode 100644 index 0000000000..997ddf1ce6 --- /dev/null +++ b/onboarding/.codewhale/skills/surf/scripts/ride-wave.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# ride-wave.sh β€” Update the testbed, build, verify, and generate a digest +# Reads .surf-config for repo URL and branch info + +set -euo pipefail + +# -- Load config if it exists -- +if [ -f ".surf-config" ]; then + source .surf-config + echo "🌊 Surfing ${REPO_URL:-unknown} (branch: ${BRANCH:-main})" +else + echo "⚠️ No .surf-config found. Using defaults." + REPO_URL="${REPO_URL:-https://github.com/Hmbown/CodeWhale.git}" + BRANCH="${BRANCH:-main}" +fi + +# -- Check if this is a valid testbed -- +if [ ! -f ".surf-config" ] || [ "${ONBOARDING_INIT:-false}" != "true" ]; then + echo "❌ ERROR: Not a valid Surf testbed. Run /surf setup first." + exit 1 +fi + +# -- Check if working directory is clean -- +if [ -n "$(git status --porcelain)" ]; then + echo "❌ ERROR: Working directory is dirty. Please commit or stash changes." + exit 1 +fi + +# -- Ensure we're on the right branch -- +CURRENT_BRANCH=$(git branch --show-current) +if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then + echo "⚠️ Currently on branch '$CURRENT_BRANCH', but config expects '$BRANCH'." + echo "Switching to '$BRANCH'..." + git checkout "$BRANCH" +fi + +# -- Pull latest -- +echo "πŸ“¦ Pulling latest changes from $BRANCH..." +git pull --ff-only origin "$BRANCH" + +# -- Show current state -- +COMMIT=$(git rev-parse --short HEAD) +echo "πŸ“ At commit: $COMMIT" + +# -- Run verification -- +echo "" +echo "πŸ”§ Running verification..." +echo "----------------------------------------" + +echo "▢️ cargo fmt --check" +cargo fmt --check + +echo "" +echo "▢️ cargo clippy -- -D warnings" +cargo clippy -- -D warnings + +echo "" +echo "▢️ cargo test --workspace" +cargo test --workspace + +echo "----------------------------------------" +echo "βœ… All checks passed." + +# -- Generate digest from CHANGELOG -- +echo "" +echo "πŸ“‹ Digest (from CHANGELOG.md):" +echo "----------------------------------------" +if [ -f "CHANGELOG.md" ]; then + # Extract the first version entry + sed -n '/## \[/,/## \[/p' CHANGELOG.md | head -n -1 | tail -n +2 | head -n 10 +else + echo "⚠️ CHANGELOG.md not found." +fi +echo "----------------------------------------" + +# -- Write receipt -- +RECEIPTS_DIR="receipts" +mkdir -p "$RECEIPTS_DIR" +TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + +cat > "$RECEIPTS_DIR/latest_receipt.json" << EOF +{ + "timestamp": "$TIMESTAMP", + "repo": "$REPO_URL", + "branch": "$BRANCH", + "commit": "$COMMIT", + "status": "success", + "message": "All checks passed." +} +EOF + +echo "πŸ“„ Receipt written: $RECEIPTS_DIR/latest_receipt.json" +echo "🌊 Ride complete. πŸ„" \ No newline at end of file diff --git a/onboarding/.codewhale/skills/surf/scripts/surf.sh b/onboarding/.codewhale/skills/surf/scripts/surf.sh new file mode 100644 index 0000000000..20626cca0d --- /dev/null +++ b/onboarding/.codewhale/skills/surf/scripts/surf.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RECEIPTS_DIR="receipts" +TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + +mkdir -p "$RECEIPTS_DIR" + +echo "🌊 Checking the wave..." +STATE=$("$SCRIPT_DIR/check-wave.sh") +echo "$STATE" + +STATUS=$(echo "$STATE" | grep "^STATUS=" | cut -d= -f2) +MESSAGE=$(echo "$STATE" | grep "^MESSAGE=" | cut -d= -f2-) +DIRTY=$(echo "$STATE" | grep "^DIRTY=" | cut -d= -f2 || echo "false") + +case "$STATUS" in + empty-or-no-git) + echo "🌊 The water is calm. No wave detected." + echo "πŸ“‹ Run /surf setup to catch a wave." + exit 0 + ;; + testbed) + if [ "$DIRTY" = "true" ]; then + echo "⚠️ The wave is choppy. Uncommitted changes detected." + echo "πŸ“‹ Clean up or stash changes before riding." + exit 1 + else + echo "🌊 Wave is clean. Riding..." + "$SCRIPT_DIR/ride-wave.sh" | tee "$RECEIPTS_DIR/latest_ride.log" + COMMIT=$(git rev-parse --short HEAD) + BRANCH=$(git branch --show-current) + cat > "$RECEIPTS_DIR/latest_receipt.json" << EOF +{ + "timestamp": "$TIMESTAMP", + "branch": "$BRANCH", + "commit": "$COMMIT", + "status": "success", + "digest": "Ride complete. See latest_ride.log for details." +} +EOF + echo "πŸ“„ Receipt written: $RECEIPTS_DIR/latest_receipt.json" + echo "βœ… Surf complete." + exit 0 + fi + ;; + unknown-repo) + echo "⚠️ Unknown territory: $MESSAGE" + echo "πŸ“‹ Not a recognized surf spot. Run /surf setup in an empty directory." + exit 1 + ;; + *) + echo "❌ Unknown state: $STATUS" + exit 1 + ;; +esac \ No newline at end of file diff --git a/onboarding/Skill_Flow_Design (old).md b/onboarding/Skill_Flow_Design (old).md new file mode 100644 index 0000000000..ecbba52917 --- /dev/null +++ b/onboarding/Skill_Flow_Design (old).md @@ -0,0 +1,79 @@ +# SKILL: Onboarding Suite + +Step 0: Environment Check + - Check if .git/ exists in the current directory + - Check if .onboarding-init exists + + [Case 1: Empty directory] + - Prompt user: "This appears to be an empty directory. Would you like to create a testbed here?" + - If yes β†’ git clone . && touch .onboarding-init β†’ proceed to Step 1 + - If no β†’ Exit with clear instructions + + [Case 2: Testbed (.git + .codewhale-init present)] + - βœ… Proceed to Step 1 + + [Case 3: Normal repo (.git present, but no .onboarding-init)] + - Error: "This appears to be a main repository, not a test/onboarding bed/suit." + - Exit with instructions to create a testbed using git worktree + + [Case 4: Unknown / cluttered] + - Error: "No valid Git repository or testbed state detected." + - Exit + +Step 1: Update the Testbed + - Check if the working directory is clean (git status --porcelain) + - If dirty β†’ Stop with a clear receipt + - If clean β†’ git pull --ff-only + +Step 2: Build and Verify + - cargo fmt --check + - cargo clippy -- -D warnings + - cargo test --workspace + +Step 3: Generate Digest + - Parse the latest entry from CHANGELOG.md + - Fetch milestone metadata from GitHub + - Generate a concise "what's new" summary + +Step 4: Output Receipt + - Write receipt to testbed/receipts/latest_receipt.json + - Include: branch, commit hash, test status, CHANGELOG summary + - Display report to user + +Step 5: Done + - Output status report + + +# User wants to use the onboarding suite + +# Option 1: They already have a testbed +cd ../codewhale-onboarding +/onboarding-suite # Skill runs, updates, verifies, digests + +# Option 2: They don't have a testbed +cd ~/projects +mkdir codewhale-onboarding +cd codewhale-onboarding +/onboarding-suite # Skill sees empty dir, prompts to clone and init + +# Option 3: They're in the main repo (by accident?) +cd codewhale +/onboarding-suite # Skill warns: "This is the main repo, not a testbed." + # "Recommended: git worktree add ../codewhale-onboarding main" + # "Then cd ../codewhale-onboarding and run again." + +# Architecture Draft + +.codewhale/ +β”œβ”€β”€ commands/ +β”‚ β”œβ”€β”€ surf.md # /surf (orchestrator) +β”‚ └── surf-setup.md # /surf setup (clone/init) +β”œβ”€β”€ skills/ +β”‚ └── surf/ +β”‚ β”œβ”€β”€ SKILL.md # $surf (LLM-enhanced) +β”‚ └── scripts/ +β”‚ β”œβ”€β”€ surf.sh # MAIN ORCHESTRATOR +β”‚ β”œβ”€β”€ check-wave.sh +β”‚ β”œβ”€β”€ catch-wave.sh +β”‚ └── ride-wave.sh +└── config.toml \ No newline at end of file diff --git a/onboarding/Surf_Audit_2026-07-24.md b/onboarding/Surf_Audit_2026-07-24.md new file mode 100644 index 0000000000..7f5932a32f --- /dev/null +++ b/onboarding/Surf_Audit_2026-07-24.md @@ -0,0 +1,295 @@ +# Surf Module β€” Code & Architecture Audit + +**Date:** 2026-07-24 +**Branch:** `wip/onboarding_suit` +**Scope:** `onboarding/` directory β€” design docs, bash scripts, command/skill scaffolding +**Artifacts reviewed:** `Surf_Skill_Flow_Design.md`, `Skill_Flow_Design (old).md`, `surf.md`, `surf-setup.md`, `SKILL.md`, `surf.sh`, `check-wave.sh`, `catch-wave.sh`, `ride-wave.sh` +**Codebase surfaces traced:** `crates/tui/src/commands/mod.rs`, `crates/tui/src/commands/user_registry.rs`, `crates/tui/src/commands/user_commands.rs`, `crates/tui/src/commands/groups/skills/skills.rs`, `crates/tui/src/skills/mod.rs`, `docs/architecture/command-dispatch.md` + +--- + +## Summary + +You have a well-designed **draft** with 4 working bash scripts, a design doc, and scaffolding files β€” but the module is **not wired to the TUI runtime** and has several critical architecture gaps. The entire `onboarding/` directory is untracked on branch `wip/onboarding_suit`. No parts of this are discoverable or executable from inside CodeWhale today. + +The design thinking is solid β€” clean state machine, good decomposition, fork-aware, receipt-based. But the implementation rests on an `execute:` frontmatter mechanism in user commands that **does not exist** in CodeWhale's current codebase. That's the root blocker. The bash scripts work in isolation, but the bridge to the TUI is missing. + +--- + +## What Exists (inventory) + +| Artifact | State | Notes | +|---|---|---| +| `Surf_Skill_Flow_Design.md` | Complete (v2.0) | Clean state machine, good principles, clear metaphor | +| `Skill_Flow_Design (old).md` | Stale | References old "onboarding-suite" naming β€” should be archived or deleted | +| `surf.sh` (orchestrator) | Working | Delegates to check/ride, writes receipt, handles all 4 states | +| `check-wave.sh` | Working | Correctly identifies 4 states via `.git` + `.surf-config` β€” minor logic issue noted below | +| `catch-wave.sh` (setup) | Needs work | Uses `read -p` β€” won't work in TUI dispatch | +| `ride-wave.sh` (update & verify) | Working | Pulls, switches branches, runs fmt/clippy/test, writes receipt | +| `surf.md` (`/surf` command) | Broken | `execute:` frontmatter is silently ignored by the runtime | +| `surf-setup.md` (`/surf setup` command) | Broken | Same `execute:` problem | +| `SKILL.md` (`$surf` skill) | Stale | Uses old "onboarding-suite" name + wrong script references | + +--- + +## Critical Gaps + +### GAP 1 β€” `execute:` frontmatter does not exist in CodeWhale + +The command `.md` files use frontmatter like: + +```yaml +--- +execute: .codewhale/skills/surf/scripts/surf.sh +description: "🌊 Ride the CodeWhale wave. Updates, builds, and tests the testbed." +--- +``` + +But the metadata parser in `crates/tui/src/commands/user_registry.rs` (lines 245–262) only recognizes these frontmatter keys: + +- `description` +- `argument-hint` +- `allowed-tools` +- `pausable` +- `alias` / `aliases` +- `hidden` + +The `execute:` key falls into the catch-all arm `_ => {}` on line 261 and is **silently ignored**. The body text after the frontmatter closing `---` is sent as an LLM prompt via `AppAction::SendMessage` (line 508–509 in `user_registry.rs`): + +```rust +// crates/tui/src/commands/user_registry.rs:508-509 +let message = user_commands::apply_template(&metadata.body, args); +Some(CommandResult::action(AppAction::SendMessage(message))) +``` + +**This is the root blocker.** The entire Surf design hinges on a shell-script execution model that CodeWhale's user command system does not support. User commands dispatch by sending their markdown body as a prompt to the LLM, not by spawning a subprocess. + +**Resolution paths:** +- (A) Add `execute:` support to the user command registry β€” a new feature requiring Rust changes in `user_registry.rs` (`parse_metadata`, `UserCommandMetadata`, and `try_dispatch`) +- (B) Make surf a **native built-in command** β€” add a group in `crates/tui/src/commands/groups/` that spawns the scripts directly +- (C) Ship it as a standalone CLI tool invoked via `codewhale exec` rather than through the TUI command namespace +- (D) Package it as a CodeWhale skill that instructs the LLM to call the scripts β€” but this defeats the "deterministic, no LLM" principle stated in the design doc + +### GAP 2 β€” Discovery path mismatch + +Commands and skills are discovered at these workspace-relative paths: + +- **Commands:** `/.codewhale/commands/` (see `crates/tui/src/commands/user_commands.rs:52-63`, `commands_dirs` function) +- **Skills:** `/.codewhale/skills/` (see `crates/tui/src/skills/mod.rs:711-718`, `skill_directories_for_workspace_and_dir` function) + +Your artifacts live at `onboarding/.codewhale/{commands,skills}/`. The repo root has no `.codewhale/` subdirectory (confirmed by inspection). So **neither `/surf` nor `$surf` would be found** from within the CodeWhale repo workspace. The files are two directory levels too deep. + +### GAP 3 β€” SKILL.md is stale and references wrong names + +`onboarding/.codewhale/skills/surf/SKILL.md` still uses: + +| Field | SKILL.md value | Design doc expects | +|---|---|---| +| Skill name | `onboarding-suite` | `surf` | +| Triggers | `/onboarding-suite`, `/onboarding-suite --summary` | `/surf`, `$surf` | +| Script names | `check-testbed.sh`, `setup-testbed.sh`, `update-testbed.sh` | `check-wave.sh`, `catch-wave.sh`, `ride-wave.sh` | +| Marker file | `.onboarding-init` | `.surf-config` | +| Output format | `Test passed: X, failed: Y, skipped: Z` | JSON receipt in `receipts/latest_receipt.json` | + +The description text also references relative directory paths that don't match the actual layout on disk. If this SKILL.md were loaded as a skill, it would inject incorrect instructions into the LLM prompt. + +### GAP 4 β€” Skills inject text into prompts, never execute scripts + +Even if SKILL.md were correct and discoverable, the skill activation path in `crates/tui/src/commands/groups/skills/skills.rs` (lines 380–383) only injects the SKILL.md body as a system instruction into the LLM prompt: + +```rust +// crates/tui/src/commands/groups/skills/skills.rs:380-383 +let instruction = format!( + "You are now using a skill. Follow these instructions:\n\n# Skill: {}\n\n{}\n\n---\n\nNow respond to the user's request following the above skill instructions.", + skill.name, skill.body +); +``` + +It does **not** execute any bash scripts. The `Skill` struct (defined in `crates/tui/src/skills/mod.rs:70-79`) has fields for `name`, `description`, `body`, `localized_descriptions`, and on-disk `path` β€” no `execute` or `script` field exists. + +The LLM would need to voluntarily find and run the scripts via `exec_shell`, which contradicts the design doc's "**Deterministic by default** β€” The core flow works without LLM" principle. + +--- + +## Secondary Issues + +### check-wave.sh: DIRTY logic after early exit + +```bash +if [ ! -d ".git" ]; then + echo "STATUS=empty-or-no-git" + echo "MESSAGE=No Git repository found." + exit 0 # exits here for empty-dir +fi + +# ... (testbed/unknown-repo detection) ... + +if [ -n "$(git status --porcelain)" ]; then # runs for all non-empty states + echo "DIRTY=true" +else + echo "DIRTY=false" +fi +``` + +This works correctly because `exit 0` fires before the `git status` check for the empty-dir case. But the `DIRTY` output is appended for the `unknown-repo` state, making the output for `unknown-repo` look like: + +``` +STATUS=unknown-repo +MESSAGE=Git repository without .surf-config marker. +DIRTY=false +``` + +The design doc doesn't specify whether `DIRTY` is meaningful for `unknown-repo`. The orchestrator (`surf.sh`) only inspects `DIRTY` for the `testbed` case, so this is benign β€” but the output contract is looser than documented. + +### ride-wave.sh: dual receipt ownership + +Both `surf.sh` and `ride-wave.sh` independently write `receipts/latest_receipt.json`: + +- **`surf.sh`** (line ~48-55): Writes a simplified receipt with `timestamp`, `branch`, `commit`, `status`, and a static `digest` field +- **`ride-wave.sh`** (lines ~76-85): Writes a richer receipt with `timestamp`, `repo`, `branch`, `commit`, `status`, and `message` (omits `digest`) + +Because `surf.sh` runs `ride-wave.sh` first (via pipe to `tee` on line 43), then writes its own receipt after, the `surf.sh` version is what persists. The richer output from `ride-wave.sh` is lost. Pick one owner β€” either let `ride-wave.sh` own the receipt exclusively, or have `surf.sh` merge/enrich the ride-wave receipt. + +### ride-wave.sh: fragile CHANGELOG extraction + +```bash +sed -n '/## \[/,/## \[/p' CHANGELOG.md | head -n -1 | tail -n +2 | head -n 10 +``` + +This sed range depends on exact `## [version]` formatting in `CHANGELOG.md`. If: +- The format changes (e.g., `## v0.9.0` instead of `## [0.9.0]`) +- The file uses a different heading level +- The file only has one entry (the range won't close) + +…the pipeline silently produces empty output. There's no fallback or validation. + +### catch-wave.sh: interactive prompts won't work in TUI + +```bash +read -p "Enter repository URL (default: https://github.com/Hmbown/CodeWhale.git): " REPO_URL +read -p "Enter branch (default: main): " BRANCH +``` + +When dispatched through the TUI's user command path, stdin is not a terminal β€” it's the prompt body. These `read` calls would hang or fail. The script needs to accept arguments (`catch-wave.sh `) or the TUI would need to present a modal for input. + +### No tests for any script + +Zero test coverage. Shell scripts that perform `git clone`, `cargo test --workspace`, and `git pull --ff-only` need smoke tests at minimum β€” even simple assertions like `[[ -f .surf-config ]]` after a setup run, or testing the state machine transitions with a mock git repo. + +### CHANGELOG.md dependency + +`ride-wave.sh` assumes `CHANGELOG.md` exists and matches the expected format. On a sparse checkout or a repo that doesn't use `CHANGELOG.md`, the digest step silently degrades with a warning β€” but the receipt still claims `"status": "success"`. + +### No dry-run or partial-verify mode + +`ride-wave.sh` is all-or-nothing: `cargo fmt --check` + `cargo clippy -- -D warnings` + `cargo test --workspace`. A full workspace test suite on a large Rust project can take 10+ minutes. There's no flag to do a quick "just pull and check formatting" or "just test one crate." This limits the usefulness for rapid daily use. + +### Receipt directory is CWD-relative + +Both `surf.sh` and `ride-wave.sh` create `receipts/` in the current working directory, not relative to the testbed root or `.surf-config` location. If you run the script from a subdirectory, the receipt ends up in the wrong place. + +--- + +## Design Assessment + +### Pros + +- **Clean state machine** β€” 4 states (`empty-or-no-git`, `testbed` clean, `testbed` dirty, `unknown-repo`) with deterministic transitions, easy to reason about and test +- **Fork-aware** β€” `.surf-config` stores repo URL + branch, no hardcoded upstream. Users can track forks or custom branches +- **Receipt-based** β€” every run produces traceable JSON, good for CI audit trails and automated tooling +- **Self-contained** β€” one `.codewhale` subdirectory holds all scripts, commands, and the skill β€” no scattering +- **Well-decomposed** β€” orchestrator (`surf.sh`) delegates to single-responsibility scripts: `check-wave.sh` for state detection, `catch-wave.sh` for setup, `ride-wave.sh` for update+verify +- **Defense in depth** β€” `check-wave.sh` catches dirty trees before `surf.sh` decides to ride; `ride-wave.sh` independently double-checks cleanliness before pulling +- **`set -euo pipefail`** β€” all scripts use it, which is correct shell hygiene and prevents silent failures +- **`--ff-only` pull** β€” uses fast-forward-only pulls, which prevents merge conflicts from silently appearing in the testbed +- **Good metaphor** β€” "Surf" / wave riding is memorable, developer-friendly, and creates a clear mental model + +### Cons + +- **Overloaded metaphor at the script level** β€” scripts named `catch-wave.sh`, `ride-wave.sh`, `check-wave.sh` are clever but lack a shared prefix like `surf-`. Grepping for "all surf scripts" requires knowing all the wave verbs +- **All-or-nothing verification** β€” no way to do a targeted `cargo test -p ` or skip clippy for rapid iteration +- **No dry-run mode** β€” no flag to preview what would happen without executing. Helpful for first-time users and debugging +- **Git-only** β€” the `.surf-config` marker requires a `.git` directory. The design doesn't support non-git directories or other VCSs +- **Interactive prompts in setup** β€” `catch-wave.sh` uses `read -p`, which conflicts with TUI dispatch (see Gap above) +- **SKILL.md / design doc naming drift** β€” old onboarding-suite naming persists in the skill file while the design doc has moved to Surf. The `Skill_Flow_Design (old).md` file should be archived or deleted to avoid confusion +- **Duplicate receipt writes** β€” two scripts write to the same path with different schemas (see Secondary Issues) + +--- + +## Codebase Context + +### How user commands work today + +The dispatch flow from `crates/tui/src/commands/mod.rs` (line 109, `execute` function): + +1. `$skillname` β†’ resolved as `/skill name` (line 114–133) +2. User commands checked first via `user_registry::try_dispatch` (line 148) +3. Permanent compatibility aliases (`/jihua`, `/zidong`, `/slop`, `/canzha`) (line 154–171) +4. Built-in registry lookup (line 173–175) +5. Legacy migration hints (`/set`, `/deepseek`, `/doctor`) (line 177–188) +6. Skills fallback via `groups::skills::run_skill_by_name` (line 193) + +When a user command matches (step 2), `try_dispatch` in `user_registry.rs:444-509`: +- Validates the command exists +- Resets hunt state, todos, plan state +- Applies `allowed-tools` restriction if present +- Applies `pausable` flag if present +- Sends the markdown body (after frontmatter stripping) as a user message via `AppAction::SendMessage` + +There is no code path that spawns a subprocess based on frontmatter. The `execute:` concept would need to be added here. + +### How skills work today + +Skills are discovered from these workspace directories (see `crates/tui/src/skills/mod.rs:711-718`): + +1. `/.agents/skills` +2. `/skills` +3. `/.opencode/skills` +4. `/.claude/skills` +5. `/.cursor/skills` +6. `/.codewhale/skills` + +When a skill is activated (`activate_skill` in `skills.rs:361-395`): +- The SKILL.md body is injected as a system instruction into the LLM prompt +- A system message is added to the chat history: `"Activated skill: \n\n"` +- `app.active_skill` is set so the next user turn includes the skill instructions +- No scripts are executed; no subprocess is spawned + +### Permanent exceptions (from `docs/architecture/command-dispatch.md`) + +The command dispatch system has several permanent exceptions (compatibility aliases, migration hints) that bypass the normal registry. A new built-in command for surf would be a regular addition, not an exception. + +--- + +## Suggestions + +1. **Decide on the execution model first.** The `execute:` frontmatter doesn't exist. Options: + - **Add `execute:` support** β€” modify `parse_metadata` in `crates/tui/src/commands/user_registry.rs` to recognize `execute:`, add a field to `UserCommandMetadata`, and change `try_dispatch` to spawn the subprocess instead of (or in addition to) sending the body as a prompt. This is the smallest surface change but needs careful security review (path traversal, what CWD, what shell). + - **Make surf a native built-in command** β€” add a `surf` group under `crates/tui/src/commands/groups/` that spawns the scripts directly. This is the cleanest integration but moves the scripts out of the markdown file and into Rust. + - **Ship as a standalone CLI tool** β€” keep the bash scripts, ship them as a companion tool invoked via `codewhale exec` or a shell alias. Simplest to build, but loses TUI integration. + - **Skill-only approach** β€” rewrite SKILL.md so the LLM is instructed to find and run the scripts. Simple to implement but violates the "deterministic by default" principle. + +2. **Fix SKILL.md** β€” rename to `surf`, update triggers to `/surf` and `$surf --summary`, reference wave-named scripts and `.surf-config` + +3. **Fix discovery path** β€” move files to `/.codewhale/{commands,skills}/` (or the user-global `~/.codewhale/{commands,skills}/`) so they're actually discoverable + +4. **Replace `read -p` in catch-wave.sh** β€” accept positional args: `catch-wave.sh ` with defaults when omitted + +5. **Consolidate receipt writing** β€” let `ride-wave.sh` own the receipt; remove the duplicate simplified write in `surf.sh` + +6. **Add `--dry-run` and `--quick` flags** to `ride-wave.sh`: + - `--dry-run`: print what would happen without executing + - `--quick`: skip clippy and test, only fmt-check (useful for daily "did anything break?" checks) + +7. **Write smoke tests** β€” even simple assertions go a long way: + - `check-wave.sh` returns correct states for a mock directory tree + - `.surf-config` is created with correct fields after `catch-wave.sh` + - `surf.sh` refuses to run on a dirty testbed + +8. **Archive or delete** `Skill_Flow_Design (old).md` β€” having both old and new design docs in the same directory is confusing and risks contributors editing the wrong one + +9. **Document the distribution model** β€” where does this live in the final product? As an installable CodeWhale skill? As a template users copy? As a built-in? The `onboarding/` directory's role in the repo needs to be explicit. + +--- + +_Version: 1.0 β€” based on codebase at `wip/onboarding_suit` as of 2026-07-24_ diff --git a/onboarding/Surf_Closing_Reflection.md b/onboarding/Surf_Closing_Reflection.md new file mode 100644 index 0000000000..dce5c4c9dd --- /dev/null +++ b/onboarding/Surf_Closing_Reflection.md @@ -0,0 +1,198 @@ +# Surf β€” Closing Reflection + +**Date:** 2026-07-25 +**Branch:** `wip/onboarding_suit` (PR #4762) +**Status:** Draft β€” architectural direction document, not a feature spec + +--- + +## Where We Are + +Three documents now sit in this branch: + +| Document | Role | +|---|---| +| `Surf_Skill_Flow_Design.md` | The blueprint β€” state machine, scripts, entry points | +| `Surf_Audit_2026-07-24.md` | The reality check β€” what works, what doesn't, what the codebase actually supports | +| `Surf_PR4762_Response_Report.md` | The map β€” existing CodeWhale structures that Surf echoes or could align with | + +Together they trace an arc: design β†’ audit β†’ contextualization. This document closes that arc. + +--- + +## The Reflection + +Jay's closing thought from the PR conversation: + +> *"I'm not sure what exactly I would need per se. But the myriad of possibilities are where my head is at. I'm thinking all the best from a myriad of worlds, so to speak. Mirroring fleet structure (not using it directly for now) for example. Both full integrations as well as maximum separation. I like being able to pipe surf into fleet into surf back into fleet. Whatever the eventual content/features of surf will be. This would be the vision: be able to exactly pick the right tool at the right moment, with receipts running along the quiet stream."* + +This is not indecision. It's a clear architectural instinct: **Surf should be a composable primitive, not a monolithic suite.** + +The right tool at the right moment means Surf doesn't need to be *the* testbed manager, or *the* Fleet task, or *the* CLI toolkit. It needs to be a shape that fits into all of those positions β€” pipeable, inspectable, receipt-producing β€” so the user can compose it into whatever workflow the moment calls for. + +--- + +## Cross-Check: Is This Vision Feasible? + +Three claims to verify against the actual codebase: + +### Claim 1: "Mirroring fleet structure (not using it directly for now)" + +**Verdict: Feasible and well-precedented.** + +Fleet's CLI surface is a set of verbs on a noun: `fleet status`, `fleet run`, `fleet inspect`, `fleet artifacts`, `fleet resume`. Surf can adopt the same pattern with different nouns: `surf status`, `surf ride`, `surf inspect`, `surf catch`. + +This is not duplication β€” it's a shared vocabulary. The verbs mean the same thing in both tools (status = "what's the current state", inspect = "show me the last result", resume = "pick up where I left off"). A user who knows Fleet already knows Surf. And Surf's output can be piped to Fleet because they speak the same structural language (JSON receipts, status codes, commit hashes). + +The key design constraint: **Surf must not depend on Fleet internals.** Mirroring the shape is fine. Importing Fleet's Rust types or requiring a Fleet ledger to exist is not. Surf's bash scripts should produce the same JSON shapes that Fleet expects, not call Fleet's APIs. This keeps the door open for both "use Surf standalone" and "pipe Surf into Fleet." + +### Claim 2: "Piping surf into fleet into surf back into fleet" + +**Verdict: Structurally compatible with one minor CLI gap.** + +Here's the pipeline vision, annotated with what's real today: + +```bash +# Step 1: Surf inspects the testbed, outputs JSON +surf status --json +# β†’ {"state": "testbed", "dirty": false, "branch": "my-feature", "commit": "abc123"} +# βœ… Surf's bash scripts can emit this. Fleet's status can read it. + +# Step 2: Surf's output feeds a Fleet task spec +surf status --json | jq '{tasks: [{id: "verify", worker: {role: "builder"}, workspace: {required_files: ["Cargo.toml"]}}]}' > task.json +codewhale fleet run task.json +# ⚠️ Fleet's `run` takes a file path, not stdin. `surf status --json | fleet run -` isn't +# supported today. But writing to a temp file is trivial, and stdin support is a +# minor CLI change β€” not a structural barrier. + +# Step 3: Fleet produces a receipt, Surf inspects it +codewhale fleet inspect --json | surf digest - +# β†’ "🌊 abc123: All checks passed. 0 failures." +# βœ… Fleet outputs JSON with commit/status/test_counts. Surf's bash scripts can parse it. + +# Step 4: Surf catches a new wave based on Fleet's result +surf digest - < receipt.json | jq '.commit' | xargs surf catch +# βœ… surf catch already accepts positional args (after the planned fix). +``` + +The only real blocker is Fleet's `run` not accepting stdin β€” and that's a feature request, not a design flaw. Everything else is JSON in, JSON out, across both tools. + +### Claim 3: "Receipts running along the quiet stream" + +**Verdict: The receipt shapes are naturally compatible.** + +| Receipt field | Surf (`latest_receipt.json`) | Fleet (`fleet inspect --json`) | +|---|---|---| +| `timestamp` | ISO 8601 | Present in worker record | +| `branch` | From `.surf-config` | From task spec or worktree | +| `commit` | `git rev-parse --short HEAD` | Present in worker events | +| `status` | `"success"` / `"failure"` | `succeeded` / `failed` / `interrupted` | +| `message` | Human-readable summary | Worker completion payload | + +The fields don't need to match exactly β€” they need to be mappable. A `jq` one-liner can translate Surf's receipt into a Fleet task spec, and another `jq` one-liner can extract a digest from Fleet's worker record into Surf's format. The "quiet stream" is JSON flowing between tools without either one needing to know about the other's internals. + +--- + +## The Two Poles (and Why Both Matter) + +Jay described "both full integrations as well as maximum separation." These are not contradictions β€” they're two valid operating modes for a composable tool: + +### Maximum Separation (Mode A: Standalone) + +Surf as a bash toolkit. No CodeWhale dependency. No TUI integration. No `execute:` frontmatter needed. + +```bash +surf status # human-readable +surf status --json # machine-readable +surf ride # pull + verify +surf ride --json # pull + verify, output receipt +surf inspect # read latest receipt +surf catch # clone + init +``` + +This mode works **today** β€” the scripts already do this, they just need `--json` flags and the `read -p` fix in `catch-wave.sh`. No Rust changes. No TUI changes. No frontmatter gap to solve. + +### Full Integration (Mode B: Inside CodeWhale) + +Surf as a Fleet-aware companion. Commands dispatch through the TUI. Receipts land in the Fleet ledger. + +```bash +/surf # TUI command β†’ runs surf.sh +/surf setup # TUI command β†’ runs catch-wave.sh +$surf --summary # skill β†’ LLM reads receipt, adds context +``` + +This mode requires the `execute:` frontmatter gap to be solved (or Surf to become a native built-in command). It's the heavier lift but gives the slickest UX. + +### The Bridge (Mode C: Piped) + +The middle ground. Surf runs standalone but produces/consumes Fleet-compatible JSON. + +```bash +surf status --json | codewhale fleet run - # Surf β†’ Fleet +codewhale fleet inspect --json | surf digest - # Fleet β†’ Surf +``` + +This is where the vision lives. Neither tool needs to know about the other. They just need to speak the same JSON dialect. + +--- + +## What This Means for the PR + +The PR (#4762) is correctly positioned as a draft. It is not ready to merge. But it is ready to **converse**. The three documents in this branch give reviewers a complete picture: + +1. Here's the design (what I want to build) +2. Here's the audit (what actually works today) +3. Here's the response (what existing structures can help) + +The PR doesn't need to resolve every open question before merging. It needs to land the design doc and the audit so the conversation has a stable reference point. The bash scripts can stay as scaffolding β€” they demonstrate the shape without claiming completeness. + +**Recommended PR scope for merge-readiness:** +- Keep `Surf_Skill_Flow_Design.md` (the blueprint) +- Keep `Surf_Audit_2026-07-24.md` (the reality check) +- Keep `Surf_PR4762_Response_Report.md` (the map) +- Keep this document (the closing reflection) +- Keep the bash scripts as working prototypes +- **Drop or archive** `SKILL.md` (stale) and `Skill_Flow_Design (old).md` (superseded) +- **Drop** `surf.md` and `surf-setup.md` (the `execute:` frontmatter doesn't work β€” they're dead scaffolding until that gap is resolved) +- Mark the PR as ready for review with a note: "Design sketch and prototype scripts. Not functional from inside CodeWhale yet. Seeking architectural feedback." + +--- + +## The Shape of the Thing + +After three documents and two days of tracing code, the shape is clear: + +> **Surf is a receipt-producing, JSON-emitting, pipe-friendly verification surface that sits between a contributor's local checkout and the Fleet/CI infrastructure. It mirrors Fleet's verb vocabulary without depending on Fleet's internals. It produces structured evidence that any downstream tool β€” Fleet, `jq`, a Workflow, a CI runner, a human reading a terminal β€” can consume.** + +That's not a testbed manager. That's not an onboarding suite. That's not a skill. It's a **composable verification primitive**. The right tool at the right moment, with receipts running along the quiet stream. + +--- + +## What's Left to Decide + +| Decision | Status | Blocked by | +|---|---|---| +| Standalone CLI first, or TUI integration first? | Open | Nothing β€” both paths are clear | +| Mirror Fleet's JSON receipt schema exactly, or define Surf's own? | Open | Need to compare Fleet's worker receipt format in detail | +| Keep the bash scripts or rewrite in Rust as a `codewhale surf` subcommand? | Open | Depends on integration depth decision | +| Does Surf's `.surf-config` stay, or does it move to `.codewhale/surf.json`? | Open | Namespace question β€” `.codewhale/` is the project-local convention | +| Archive or delete the stale SKILL.md and old design doc? | Deferred | Should be done before merging the PR | + +--- + +## Closing Note + +Jay: + +> *"Maybe the geometry is begging the question. Not: 'What should I build next?' But: 'What is the shape of the thing that I'm building toward?'"* + +The shape, as best I can trace it from the code and the conversation, is this: **a tool that doesn't care whether it's being used by a human, a Fleet worker, a `jq` pipeline, or another tool.** It takes input, produces a receipt, and gets out of the way. That's the geometry. Everything else β€” the TUI commands, the skill, the state machine, the wave metaphor β€” is a surface for that geometry. + +--- + +*Cross-checked against: `docs/FLEET.md`, `docs/FLEET_WORKFLOW_TUTORIAL.md`, `docs/SUBAGENTS.md`, `crates/tui/src/commands/user_registry.rs`, `crates/tui/src/skills/mod.rs`, PR #4762 conversation, issues #4227/#4032/#4042* + +*Credit: written with CodeWhale (deepseek-v4-pro) assistant, guided by Jay, and shaped by multiple agents and assistants, human and otherwise.* + +*All of it. Fine.* πŸ„πŸ‹ diff --git a/onboarding/Surf_PR4762_Response_Report.md b/onboarding/Surf_PR4762_Response_Report.md new file mode 100644 index 0000000000..c23a4305c3 --- /dev/null +++ b/onboarding/Surf_PR4762_Response_Report.md @@ -0,0 +1,236 @@ +# Surf PR #4762 β€” Response Report + +**Date:** 2026-07-24 +**Based on:** PR #4762 conversation, linked issues (#4227, #4032, #4042), current milestone state, and repo docs +**Scope:** Responding to Jay's open questions with repo-grounded answers + +--- + +## What Jay Asked + +From the PR conversation, Jay raised several explicit and implicit questions: + +1. *"Am I reinventing the same wheel, in slightly different packaging, over and over again?"* +2. *"Do I need to be more aware of structures that are already there? Concepts I can mirror, align with, or incorporate β€” rather than building alongside them?"* +3. *"Which execution model makes sense?"* (from the audit report's resolution paths) +4. *"What is the shape of the thing that I'm building toward?"* +5. *"Is this a new tool, or the same tool, being surfed from a different angle?"* +6. The CLI pipeline idea: `surf status | jq`, `surf ride --json`, `surf inspect --receipt` β€” "a toolkit, not just a script" + +--- + +## Existing Structures That Surf Echoes (or Could Align With) + +Jay's instinct that "very similar structures already exist, possibly on multiple levels" is correct. Here's what's in the codebase today that resonates with Surf's shape: + +### 1. Agent Fleet β€” the durable worker runtime + +`docs/FLEET.md` describes a local-first control plane for durable multi-worker runs with: + +- `codewhale fleet init` β€” initialize a ledger +- `codewhale fleet run tasks.json --max-workers 4` β€” run workers +- `codewhale fleet status` β€” inspect state +- `codewhale fleet inspect/logs/artifacts ` β€” drill into results +- `codewhale fleet interrupt/restart/resume ` β€” lifecycle management +- State stored in `.codewhale/fleet.jsonl` (JSONL ledger) and `.codewhale/fleet/` (logs) +- Workers are headless `codewhale exec` runs β€” not a separate engine + +**Resonance with Surf:** The "launch something β†’ check status β†’ inspect receipt" pattern is exactly Fleet's CLI surface. Surf's orchestrator (`surf.sh` β†’ check β†’ ride β†’ receipt) is a simpler, single-worker version of Fleet's multi-worker orchestration. The `.surf-config` file is structurally parallel to Fleet's task spec JSON. + +**Question this raises:** Should Surf be a Fleet profile rather than a standalone suite? A Fleet task spec that says "clone this repo, run fmt/clippy/test, write a receipt" does what `ride-wave.sh` does, but with Fleet's ledger, retry, and resume baked in. + +### 2. Git worktree isolation (sub-agents and Fleet workers) + +`docs/SUBAGENTS.md:95-106` describes worktree isolation: + +> For parallel edit lanes, launch the child with `worktree: true`. Codewhale creates a fresh git worktree and branch for that child, runs the child from the isolated checkout, and reports the resulting workspace/branch… By default the branch is `codex/agent--` and the checkout lives beside the parent repo under `.codewhale-worktrees/`, so the parent checkout stays clean. + +**Resonance with Surf:** Surf's entire value proposition is "isolated testbed that stays clean." That's literally what `worktree: true` on a Fleet worker or sub-agent does β€” except CodeWhale already has the mechanism, the naming convention (`codex/agent--`), and the lifecycle management. Surf's `catch-wave.sh` (clone β†’ `.surf-config`) is a manual version of Fleet's automatic worktree creation. + +**Question this raises:** Should `surf setup` just be `codewhale fleet run` with a task that creates a worktree? Does Surf need its own `.surf-config` marker when CodeWhale already has `.codewhale/fleet.jsonl`? + +### 3. Repo-local constitution (`.codewhale/constitution.json`) + +`docs/CONFIGURATION.md:32-33`: + +> **Repo-local constitution** β€” optional project policy in `.codewhale/constitution.json`… Compiled into write holds that even Full Access can't skip. + +**Resonance with Surf:** Surf's `.surf-config` (a repo-URL-and-branch marker) sits in the same namespace as `.codewhale/constitution.json`. The constitution mechanism is how CodeWhale enforces repo policy statically β€” exactly the kind of "write hold" that prevents a testbed from being accidentally mutated. If Surf wants to protect a testbed, a `.codewhale/constitution.json` that gates writes is the existing mechanism, not a custom `.surf-config` marker. + +### 4. Hooks (pre/post-turn shell scripts) + +`docs/CONFIGURATION.md` covers hooks β€” user-configured shell commands that run before or after turns. The config format: + +```toml +[hooks] +pre_turn = "cargo fmt --check" +post_turn = "scripts/receipt.sh" +``` + +**Resonance with Surf:** Surf's `ride-wave.sh` is essentially a manually-triggered pre-commit hook: fmt β†’ clippy β†’ test. CodeWhale hooks automate this per-turn. The difference is that hooks are reactive (run on every turn) while Surf is proactive (user decides when to ride). But the verification pipeline is identical. + +### 5. User memory (`~/.codewhale/memory.md`) + +`docs/MEMORY.md` describes persistent notes injected into the system prompt: + +> It's the place to put preferences and conventions that should survive across sessions β€” "I prefer pytest over unittest", "always run `cargo fmt` before committing" + +**Resonance with Surf:** Jay's original issue #4227 said "I'm spending more time catching up than building." Memory is how CodeWhale already handles "persistent contributor context." If the real problem is "I forget what changed since yesterday," memory + a Fleet worker that generates a digest might be lighter than a full testbed suite. + +### 6. Issue #4032 β€” Constitutional crisis (now closed, milestone v0.9.1) + +This is the issue that started this whole thread. Jay wanted CodeWhale to follow his SKILL.md instructions (including "no ad-hoc scripts") deterministically. The resolution was structural enforcement through `--disallowed-tools` and the gate chain (#4042), not prompt-based persuasion. + +**Resonance with Surf:** This is the core insight of Surf's design: "deterministic by default, LLM optional." The lesson from #4032 is that structural mechanisms (tool restrictions, gate chains, sandboxes) work; prompt instructions don't guarantee compliance. Surf's bash scripts are the structural mechanism β€” they can't go off-script because they're not LLM-driven. This validates Surf's deterministic-core approach. + +### 7. Issue #4042 β€” Tool sandboxing (closed, milestone v0.9.0) + +This was the structural enforcement counterpart to #4032. It validated that `--disallowed-tools` flows across sessions, sub-agents, and Fleet workers. The key finding: "the mechanism already exists." + +**Resonance with Surf:** If Surf needs to run in a restricted environment (e.g., a testbed that shouldn't write to the parent repo), the `--disallowed-tools` + worktree isolation is the existing CodeWhale answer. Surf's `.surf-config` marker and Surf's clean/dirty checks are doing the same thing at the bash level that CodeWhale already does at the Rust/tool-gate level. + +--- + +## Answering Jay's Questions + +### "Am I reinventing the same wheel?" + +**Partially, yes β€” but the wheel is worth reinventing if it's lighter.** + +Surf's bash scripts (`surf.sh`, `check-wave.sh`, `ride-wave.sh`) are doing what Fleet + worktrees + hooks do, but with zero dependencies, zero Rust, and zero LLM. That's not wasted effort β€” it's a different point on the complexity spectrum. + +However, the CLI pipeline idea Jay mentioned is where the duplication becomes visible: + +```bash +surf status | jq '.status' +surf ride --json | jq '.digest' +``` + +This is almost verbatim what Fleet already has: + +```bash +codewhale fleet status +codewhale fleet inspect --json | jq '.digest' +codewhale fleet artifacts +``` + +If Surf's CLI pipeline is the goal, Fleet already has the JSON output, the ledger, the retry, the resume. Building Surf as a Fleet profile or a Workflow task gets you all of that for free. + +**The synthesis:** The bash scripts are fine as plumbing. The question is whether the user-facing surface should be `/surf` (a new command namespace) or a Fleet task spec that says "here's my testbed: clone, verify, receipt." The latter integrates with existing infrastructure; the former creates a parallel surface. + +### "Do I need to be more aware of structures that are already there?" + +**Yes. Here's a map:** + +| Surf Concept | Existing CodeWhale Structure | Location | +|---|---|---| +| `.surf-config` marker | `.codewhale/constitution.json`, `.codewhale/fleet.jsonl` | `docs/CONFIGURATION.md`, `docs/FLEET.md` | +| Isolated testbed | `worktree: true` on Fleet worker / sub-agent | `docs/SUBAGENTS.md:95-106` | +| `ride-wave.sh` (fmt/clippy/test) | Hooks (`[hooks] pre_turn`) | `docs/CONFIGURATION.md` | +| `receipts/latest_receipt.json` | Fleet ledger + worker artifacts | `docs/FLEET.md` | +| State machine (empty/testbed/dirty/unknown) | Fleet worker status (pending/running/succeeded/failed/interrupted) | `docs/FLEET.md` | +| Deterministic execution (no LLM) | `--disallowed-tools` + gate chain (#4042) | `docs/SANDBOX.md` | +| Fork-aware config | Fleet task `repo` + `branch` fields | `docs/FLEET.md` | +| Digest from CHANGELOG | Workflow post-task aggregation | `docs/WORKFLOW_AUTHORING.md` | + +**The most under-explored alignment:** Fleet worktree isolation. If you give Fleet a task spec that says "clone this fork, branch this feature, run these verification commands, write a receipt," you get: + +- Worktree isolation (no dirty-tree risk) +- Ledgered audit trail (survives restarts) +- `codewhale fleet resume` (pick up where you left off) +- Existing CLI surface (no new `/surf` command needed) +- Structured JSON output (already pipeable to `jq`) + +### "Which execution model makes sense?" + +Based on what exists today, the answer depends on what you're optimizing for: + +**If you want something that works today (zero code changes):** +- Ship Surf as a standalone bash toolkit (CLI pipelines). `surf status`, `surf ride --json`, `surf inspect`. No TUI integration needed. Just `alias surf=./scripts/surf.sh`. + +**If you want TUI integration with existing infrastructure:** +- Make Surf a **Fleet task spec** + a **Workflow**. Users do `codewhale fleet run surf-testbed.json`. The Workflow handles the state machine, the Fleet handles the ledger/retry. No new commands needed. + +**If you want the full `/surf` command experience:** +- The `execute:` frontmatter gap is real (see audit). You'd need to add it to `user_registry.rs`. This is the heaviest lift but gives the slickest UX. + +**If you want the `$surf` skill experience:** +- Skills can't execute scripts (see audit). You'd be instructing the LLM to run them, which contradicts the deterministic principle. This path doesn't align with your design goals. + +**Recommendation from the evidence:** Start with the Fleet/Workflow path. It gives you the most features (ledger, retry, resume, worktree isolation, pipeable JSON) for the least new code. The bash scripts become the verification commands inside a Fleet task spec. If that proves too heavy for daily use, the standalone CLI pipeline is the lighter fallback. + +### "What is the shape of the thing I'm building toward?" + +Based on the trajectory from #4032 β†’ #4227 β†’ PR #4762, the shape is: + +> **A contributor-local verification surface that sits between "I just cloned the repo" and "I'm ready to submit a PR."** It should be deterministically reproducible, fork-aware, and produce structured evidence that the contributor's environment is in a known-good state. + +This is a real need. It bridges the gap between CI (which runs on GitHub's machines) and local development (where "works on my machine" is the norm). The question isn't whether to build it β€” it's whether to build it *inside* CodeWhale (as a command/skill) or *alongside* CodeWhale (as a Fleet profile or standalone CLI). + +The CLI pipeline vision (`surf status | jq`, `surf ride --json`) suggests "alongside" is the natural shape. The tool doesn't need to be inside the TUI to be useful β€” it needs to be composable. + +### "Is this a new tool, or the same tool, surfed from a different angle?" + +**It's both.** Every iteration has clarified the shape: + +- v0 (original #4032 SKILL.md): "Constitution-enforcing skill with deterministic scripts" +- v1 (#4227 onboarding issue): "Sync/build/verify/digest for contributors" +- v2 (old `Skill_Flow_Design.md`): "Onboarding Suite with 5-step flow" +- v3 (current `Surf_Skill_Flow_Design.md`): "Surf β€” deterministic testbed management" + +Each version strips away assumptions and gets closer to the irreducible core: **isolated checkout β†’ pull β†’ verify β†’ receipt.** The remaining question is whether that core belongs in bash scripts, in a Fleet task spec, or as a CLI pipeline. The answer might be "all three, at different levels of the stack." + +--- + +## The CLI Pipeline Vision (with real evidence) + +Jay's comment about CLI pipelines is the most promising direction in the PR conversation: + +> *"The more I think about the CLI integration side of this, the more excited I get."* + +This aligns with how CodeWhale's own Fleet CLI works today: + +```bash +# Fleet already does this pattern: +codewhale fleet status | jq '.workers[].status' +codewhale fleet inspect --json | jq '.receipt' +codewhale fleet artifacts | grep "test_results" + +# Surf could follow the same pattern: +surf status --json | jq '.state' +surf ride --json | jq '.tests.passed' +surf inspect --receipt receipts/latest_receipt.json | jq '.commit' +``` + +The Fleet CLI proves this model works in the CodeWhale ecosystem. Surf's bash scripts could adopt the same `--json` flag convention and become a lightweight companion tool β€” no TUI changes needed. + +--- + +## What the Audit Revealed (in context of the PR discussion) + +The audit (`Surf_Audit_2026-07-24.md`) identified the `execute:` frontmatter gap as the root blocker. In the context of Jay's broader questions: + +- **The gap is real** β€” but it may be a signal that the TUI command path is the wrong integration point +- **The scripts are sound** β€” they work, they're well-structured, they do what the design says +- **The discovery issue** (`onboarding/` vs `.codewhale/` root) is a packaging question, not an architecture question +- **The SKILL.md drift** confirms Jay's instinct: the design has been iterating faster than the implementation, and some artifacts are stale + +The audit validates that the current PR is exactly what Jay said it is: "a design sketch and work-in-progress." It is not ready to merge. But it is coherent enough to ground a conversation about direction. + +--- + +## Suggested Next Steps (informed by repo evidence) + +1. **Experiment with Fleet worktree isolation.** Create a Fleet task spec that does what `catch-wave.sh` + `ride-wave.sh` do: clone (or worktree), verify, receipt. Compare the ergonomics to the bash-script approach. The Fleet path gives you ledger, retry, and resume for free. + +2. **Prototype the CLI pipeline surface.** Write `surf status`, `surf ride --json`, `surf inspect --receipt` as standalone bash scripts with `--json` output. Test pipeability with `jq`. This doesn't require any CodeWhale changes. + +3. **Archive the stale SKILL.md** and either update it or delete it. The `Skill_Flow_Design (old).md` should also be removed β€” it's confusing to have both in the tree. + +4. **Decide on ownership of the PR.** If this is staying as a design sketch, mark it as such in the PR description and link to this report. If it's evolving toward a mergeable artifact, pick one of the execution models (Fleet profile, CLI pipeline, or `execute:` feature) and narrow the PR to just that. + +5. **Close the loop with #4227.** The original onboarding issue is in milestone v0.9.2 and still open. If Surf is the answer to that issue, link the PR. If Surf is a separate thing, say so explicitly so the issue can be triaged independently. + +--- + +*Sources: PR #4762 conversation, issues #4227/#4032/#4042, `docs/FLEET.md`, `docs/SUBAGENTS.md`, `docs/CONFIGURATION.md`, `docs/MEMORY.md`, `docs/architecture/command-dispatch.md`, `crates/tui/src/commands/user_registry.rs`, `crates/tui/src/commands/groups/skills/skills.rs`, `crates/tui/src/skills/mod.rs`* diff --git a/onboarding/Surf_Skill_Flow_Design.md b/onboarding/Surf_Skill_Flow_Design.md new file mode 100644 index 0000000000..66d6e33c8a --- /dev/null +++ b/onboarding/Surf_Skill_Flow_Design.md @@ -0,0 +1,216 @@ +# Surf β€” Skill Flow Design πŸ„ + +**Version:** 2.0 +**Date:** 2026-07-21 +**Status:** Draft + +--- + +## Overview + +Surf is a self‑contained suite for managing an isolated CodeWhale testbed. It provides both **deterministic** (no LLM) and **LLM‑enhanced** entry points. + +**The metaphor:** The CodeWhale repo moves like a wave. Surf is the practice of riding itβ€”not fighting it. + +--- + +## Core Principles + +| Principle | Description | +|---|---| +| **Deterministic by default** | The core flow works without LLM, network (except git pull), or external APIs. | +| **Self‑identifying** | The testbed is marked by `.surf-config` at its root. | +| **Fork‑aware** | The config stores the repo URL and branch. | +| **Dirty‑tree safety** | Never auto‑pulls over uncommitted changes. | +| **Receipt‑based** | Every run produces a JSON receipt for auditing. | +| **LLM optional** | The LLM is only used for summaries and guidance, never for execution. | + +--- + +## Entry Points + +| Entry Point | What It Does | LLM? | +|---|---|---| +| **`/surf`** | Orchestrator (surf.sh) | ❌ No | +| **`/surf setup`** | Clone and init (catch-wave.sh) | ❌ No | +| **`$surf`** | Skill with optional LLM summary | βœ… Yes (optional) | +| **`$surf --summary`** | Skill with forced summary | βœ… Yes | + +--- + +## Environment State Machine + +The orchestrator (`surf.sh`) uses `check-wave.sh` to determine the state of the current directory. + +| State | Condition | Action | +|---|---|---| +| **empty-or-no-git** | No `.git` directory | Prompt: run `/surf setup` | +| **testbed (clean)** | `.git` + `.surf-config` + clean worktree | Run `ride-wave.sh` | +| **testbed (dirty)** | `.git` + `.surf-config` + dirty worktree | Stop with warning | +| **unknown-repo** | `.git` but no `.surf-config` | Stop with guidance | + +--- + +## File Structure + +``` +.codewhale/ +β”œβ”€β”€ commands/ +β”‚ β”œβ”€β”€ surf.md # /surf (orchestrator) +β”‚ └── surf-setup.md # /surf setup (clone/init) +β”œβ”€β”€ skills/ +β”‚ └── surf/ +β”‚ β”œβ”€β”€ SKILL.md # $surf (LLM-enhanced) +β”‚ β”œβ”€β”€ SKILL_FLOW_DESIGN.md # This document +β”‚ └── scripts/ +β”‚ β”œβ”€β”€ surf.sh # MAIN ORCHESTRATOR +β”‚ β”œβ”€β”€ check-wave.sh +β”‚ β”œβ”€β”€ catch-wave.sh +β”‚ └── ride-wave.sh +└── config.toml # not touched +``` + +--- + +## Configuration File: `.surf-config` + +This file is created by `/surf setup` and sits at the root of the testbed. + +```bash +# Surf configuration +REPO_URL=https://github.com/Hmbown/CodeWhale.git +BRANCH=main +ONBOARDING_INIT=true +``` + +| Field | Purpose | +|---|---| +| `REPO_URL` | The Git repository URL (fork or upstream) | +| `BRANCH` | The branch to track | +| `ONBOARDING_INIT` | Marker that this is a valid testbed | + +--- + +## Deterministic Scripts + +### `surf.sh` β€” Orchestrator + +Called by `/surf`. It checks the environment, decides the action, and calls the appropriate script. + +- If `STATUS=empty-or-no-git` β†’ tells user to run `/surf setup` +- If `STATUS=testbed` + `DIRTY=false` β†’ calls `ride-wave.sh` +- If `STATUS=testbed` + `DIRTY=true` β†’ stops with warning +- If `STATUS=unknown-repo` β†’ stops with guidance + +### `check-wave.sh` β€” Environment Check + +Reports the state of the current directory. Outputs: + +```text +STATUS=testbed +MESSAGE=Testbed detected +DIRTY=false +``` + +### `catch-wave.sh` β€” Setup + +Called by `/surf setup`. It prompts for the repo URL and branch, clones the repository, and creates `.surf-config`. + +### `ride-wave.sh` β€” Update & Verify + +Called by `surf.sh` when the testbed is clean. It: + +1. Loads `.surf-config` +2. Checks that the worktree is clean +3. Switches to the configured branch if needed +4. Pulls the latest changes (`git pull --ff-only`) +5. Runs `cargo fmt --check` +6. Runs `cargo clippy -- -D warnings` +7. Runs `cargo test --workspace` +8. Extracts the latest entry from `CHANGELOG.md` +9. Writes a receipt to `receipts/latest_receipt.json` + +--- + +## Receipt Format + +```json +{ + "timestamp": "2026-07-21T10:00:00Z", + "repo": "https://github.com/JayBeest/CodeWhale.git", + "branch": "my-feature", + "commit": "abc123", + "status": "success", + "message": "All checks passed." +} +``` + +--- + +## User Flow + +### First‑time setup + +```text +$ mkdir codewhale-testbed +$ cd codewhale-testbed +$ codewhale /surf setup + +Enter repository URL (default: https://github.com/Hmbown/CodeWhale.git): https://github.com/JayBeest/CodeWhale.git +Enter branch (default: main): my-feature + +Cloning... +Testbed initialized. Config written to .surf-config. +``` + +### Daily use + +```text +$ cd codewhale-testbed +$ codewhale /surf + +🌊 Checking the wave... +🌊 Wave is clean. Riding... +πŸ“¦ Pulling latest... +... +βœ… Surf complete. +πŸ“„ Receipt written: receipts/latest_receipt.json +``` + +### If dirty + +```text +$ /surf + +🌊 Checking the wave... +⚠️ The wave is choppy. Uncommitted changes detected. +πŸ“‹ Clean up or stash changes before riding. +``` + +--- + +## Design Decisions + +| Decision | Rationale | +|---|---| +| **Bash scripts for deterministic core** | Reliable, fast, no dependencies, easy to debug | +| **`.surf-config` as config + marker** | Simple, fork‑aware, human‑editable | +| **Receipts stored in testbed** | Persistent, self‑contained | +| **Commands for deterministic flow** | TUI‑integrated, no LLM required | +| **Skills for LLM enhancement** | Optional, additive, non‑blocking | +| **`/surf` as the main command** | Short, memorable, fits the metaphor | + +--- + +## Future Extensions + +| Extension | Description | +|---|---| +| **`/surf inspect`** | View the latest receipt without re‑running | +| **`/surf diff`** | Show what changed since last sync | +| **`$surf --suggest`** | LLM suggests next steps based on receipt | +| **Integration with constitutional testbed** | Reuse Surf for #4032-like reproduction | + +--- + +_All of it. Fine._ πŸ„πŸ‹ \ No newline at end of file