From 118ce1d1a285ea24964755befb696aca8297971e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 22:09:37 +0000 Subject: [PATCH] setup.sh: repos: are plain clones at repos/, no default worktree new-worktree.sh makes worktrees from that clone; older bare stores are left as they are. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019JfJ7chmBadZ9hkhEGFN2h --- .delphi/manifest.yml | 2 +- .delphi/new-worktree.sh | 2 +- .delphi/paths.sh | 2 +- .delphi/setup.sh | 25 ++--------- .delphi/versions | 1 + README.md | 8 ++-- docs/design.md | 12 ++--- docs/goals.md | 2 +- .../argos/workspace-defaults/workspace.yml | 2 +- .../argos/workspaces/argos-dev/CLAUDE.md | 2 +- .../argos/workspaces/argos-dev/workspace.yml | 2 +- .../argos/workspaces/experimental/CLAUDE.md | 2 +- .../workspaces/experimental/workspace.yml | 2 +- .../workspaces/nero-experimental/CLAUDE.md | 2 +- .../nero-experimental/workspace.yml | 2 +- .../workspaces/hub/workspace.yml | 2 +- .../calypso/workspaces/calypso-dev/CLAUDE.md | 2 +- .../workspaces/calypso-dev/workspace.yml | 2 +- .../.claude/skills/new-worktree/SKILL.md | 3 +- templates/workspace/CLAUDE.md | 2 +- templates/workspace/workspace.yml | 2 +- tests/e2e.sh | 45 ++++++++++--------- 22 files changed, 55 insertions(+), 71 deletions(-) diff --git a/.delphi/manifest.yml b/.delphi/manifest.yml index dc3beb2..6718697 100644 --- a/.delphi/manifest.yml +++ b/.delphi/manifest.yml @@ -6,7 +6,7 @@ # MINOR something new that existing clones need nothing for # MAJOR clones need cleanup (MINOR while 0.x): add .delphi/migrations/-.sh, which # setup.sh runs once per workspace in each clone (rules in Delphi's CLAUDE.md) -version: 1.0.0 +version: 1.1.0 files: # repo-relative paths; a trailing / means everything under it - .delphi/manifest.yml - .delphi/.gitattributes diff --git a/.delphi/new-worktree.sh b/.delphi/new-worktree.sh index 7760f90..04c3713 100755 --- a/.delphi/new-worktree.sh +++ b/.delphi/new-worktree.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # .delphi/new-worktree.sh []: create or reuse worktrees// from -# the store .delphi/setup.sh made at repos/, and print its path. An existing branch +# the clone .delphi/setup.sh made at repos/, and print its path. An existing branch # (local or on origin) is checked out; a new one starts from (default: origin's default # branch). Runs on bash 3.2 and Git Bash. set -euo pipefail diff --git a/.delphi/paths.sh b/.delphi/paths.sh index 327d6c8..b8ef606 100644 --- a/.delphi/paths.sh +++ b/.delphi/paths.sh @@ -6,7 +6,7 @@ # Bash and zsh safe. # shellcheck shell=bash DELPHI_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd) -DELPHI_REPOS=$DELPHI_ROOT/repos # bare stores: repos/ +DELPHI_REPOS=$DELPHI_ROOT/repos # clones: repos/ DELPHI_WORKTREES=$DELPHI_ROOT/worktrees # checkouts: worktrees// DELPHI_REFERENCES=$DELPHI_ROOT/references # read-only dependency sources: references/ DELPHI_WORKSPACES=$DELPHI_ROOT/workspaces # Delphi clones to read: workspaces/, workspaces/main diff --git a/.delphi/setup.sh b/.delphi/setup.sh index 0e7f3af..dc0ff21 100755 --- a/.delphi/setup.sh +++ b/.delphi/setup.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # .delphi/setup.sh: run in a checkout of ws/ (the workspace root; .delphi/ comes from Delphi's # main, which sync keeps on every ws/* branch, so edit it only there; on main this does nothing). For each repo listed under -# `repos:` in workspace.yml, fetches it into a bare store at repos/ (unless present) and checks -# out its default branch as the first worktree, worktrees//. For each repo under +# `repos:` in workspace.yml, clones it at repos/ (unless present), on its default branch, and +# adds nothing else (.delphi/new-worktree.sh makes worktrees from it). For each repo under # `references-git:` (read-only dependency sources, `: []`), checks out a shallow, # detached copy of (default: origin's default branch) at references/, refreshed on every # run, with pushes disabled. Each entry under `workspaces:` (`- `, a workspace's dotted name @@ -99,25 +99,8 @@ while read -r kind name url ref; do if [ "$kind" = link ]; then [ -n "$inside" ] || link "$name"; continue; fi case "$name" in . | .. | *[!A-Za-z0-9._-]*) echo "setup: skipping unsafe repo name '$name'" >&2 && continue ;; esac if [ "$kind" = ref ]; then readonly_copy "references/$name" "$url" ${ref:+"$ref"} && continue; fi - store=repos/$name - if [ -e "$store" ]; then - echo "repos/$name: already present" - else - git init -q --bare "$store" - git -C "$store" remote add origin "$url" - git -C "$store" fetch -q origin /dev/null &2 && continue; } - def=${def#origin/} - dest=worktrees/$name/$def - if [ -e "$dest" ]; then continue - elif git -C "$store" rev-parse -q --verify "refs/heads/$def" >/dev/null; then - git -C "$store" worktree add -q "$PWD/$dest" "$def" || - echo "setup: could not check out $dest (is $def checked out in repos/$name?)" >&2 - else - git -C "$store" worktree add -q --track -b "$def" "$PWD/$dest" "origin/$def" + if [ -e "repos/$name" ]; then echo "repos/$name: already present" + else git clone -q "$url" "repos/$name" &2 fi done < | - main, copied to linked/; sync carries ws/ over once (migration) 0.9.0 b850821eaa9a linked/ are full clones on ws/ (or main) tracking origin, pushes allowed; setup.sh switches each back to its branch if switched away and pulls it (migration) 1.0.0 44693a895995 1.0: workspaces: clones live at workspaces/ (was linked/; DELPHI_WORKSPACES, was DELPHI_LINKED); setup.sh also runs each one's own setup.sh, and a workspace inside another's workspaces/ skips its own workspaces: (migration) +1.1.0 a3446f7f763c setup.sh clones each repos: entry at repos/ (a normal clone on its default branch, was a bare store) and adds no worktree; new-worktree.sh makes worktrees from that clone; older bare stores are left as they are diff --git a/README.md b/README.md index 88f557a..ad4f6d4 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ histories stay joined and edits on either side meet in normal three-way merges. ```sh git clone -b ws/s.app.argos.argos-dev https://github.com/Northeastern-Electric-Racing/Delphi.git delphi -cd delphi && .delphi/setup.sh # repos/ stores + default-branch worktrees in worktrees/ (git-ignored) +cd delphi && .delphi/setup.sh # clones repos: into repos/ (git-ignored) .delphi/switch.sh s.app.nero.nero-experimental # switch freely (or to main; --list lists them): the hook parks them (and workspace.yml's park: folders) in .delphi/parked/ claude plugin install delphi@delphi --scope user # then /ws-delphi in Claude Code: a pane to keep open that switches workspaces (/ws-delphi --web: in a browser) git switch -c my-change # edit, commit, push, open a PR into ws/s.app.argos.argos-dev @@ -53,7 +53,7 @@ git fetch origin && git merge origin/ws/s.app.argos.argos-dev # update your br After your PR merges into `ws/`, CI proposes it: a PR `ws/ → main` from `propose/`, labelled `by: ai` (change the label if you wrote it; CI keeps it). Merge that with a merge commit or squash, never rebase. Never push to `ws/*` or -`main` directly (protect them). Code changes go in a worktree, `worktrees//` (`.delphi/new-worktree.sh`), through that repo's own PRs. +`main` directly (protect them). Code changes go in the clone at `repos/` (one thing at a time) or a worktree from it, `worktrees//` (`.delphi/new-worktree.sh`), through that repo's own PRs. **Conflicts.** If `main` and `ws/` changed the same lines, CI lists the files and skips that workspace. Fix it in a PR into `ws/`: on a branch cut from `ws/`, run @@ -64,7 +64,7 @@ workspace. Fix it in a PR into `ws/`: on a branch cut from `ws/`, ru ```yaml name: s.app.argos.argos-dev # its dotted name harness: claude-code -repos: # stored in repos/, checked out in worktrees// by .delphi/setup.sh +repos: # cloned at repos// by .delphi/setup.sh; worktrees from it in worktrees// argos: https://github.com/Northeastern-Electric-Racing/Argos.git references-git: # read-only dependency sources, shallow-cloned into references// socketioxide: https://github.com/Totodore/socketioxide.git v0.18.7 # optional tag or branch @@ -74,7 +74,7 @@ workspaces: # Delphi workspaces (by name) or main, cloned into works `repos:` are the code you change. `references-git:` are open-source dependencies the agent reads for context: `setup.sh` checks each out shallow and detached at its pin (or the default branch), with -pushes disabled, and refreshes them on every run. They get no store and no worktrees. Names are +pushes disabled, and refreshes them on every run. They get no clone in `repos/` and no worktrees. Names are unique across both maps. The `-git` suffix leaves room for other kinds of references later. `workspaces:` are other workspaces (`- `) or `main` to read: full clones of Delphi itself, each on its branch (`ws/` or `main`) tracking origin, so `git status` and `git log` work as usual. Every diff --git a/docs/design.md b/docs/design.md index a3aa6e6..ad6c24f 100644 --- a/docs/design.md +++ b/docs/design.md @@ -196,14 +196,14 @@ Nobody pushes to `ws/*` or `main` directly (branch protection; CI's token is the ## 5. `.delphi/setup.sh` (on every branch; does nothing on main, which has no `workspace.yml`) -First runs the workspace's pending migrations (section 5b). Then reads `repos:` from `workspace.yml` and, for each not yet present, fetches it into a bare store at -`repos/`, then checks out its default branch as the first worktree, -`worktrees///` (re-created if missing). Adds the folders `park.sh` parks +First runs the workspace's pending migrations (section 5b). Then reads `repos:` from `workspace.yml` and, for each not yet present, clones it at +`repos//` on its default branch, for people working on one thing at a time, and adds nothing +else (an older clone's bare store there is left as it is). Adds the folders `park.sh` parks and `.DS_Store` to the clone's `.git/info/exclude` once. For each repo under `references-git:` (read-only dependency sources), it fetches `` (default: origin's `HEAD`) with `--depth 1` into `references//`, checks it out detached, disables pushes, and does this again on every run, so the copy tracks its pin; a failed fetch is reported and the old copy kept. Adds `/references/` to the exclude too; references get -no store and no worktrees (`new-worktree.sh` refuses them). Each entry under `workspaces:` (`- ` +no clone in `repos/` and no worktrees (`new-worktree.sh` refuses them). Each entry under `workspaces:` (`- ` or `- main`; the workspace itself is skipped) gets a full clone of the clone's own origin at `workspaces//`, on that branch (`ws/` or `main`) tracking origin, so it behaves as a normal checkout (`git status` shows its branch). Every run switches it back to that branch if it was switched @@ -218,7 +218,7 @@ and each copy parks with its workspace. Writes the clone's `post-checkout` hook, runs `.delphi/park.sh` from the worktree that checked out (replacing an older link to or copy of a `park.sh`), unless another hook is there (it says so and leaves it). `.delphi/` is on every branch, so the hook always runs the `park.sh` just checked out, never a stale copy, and each worktree of the clone -its own: the worktrees share one hook, so a link would run one checkout's `park.sh` for all of them. Nothing else: code is edited only in worktrees. Must run on macOS `/bin/bash` 3.2 +its own: the worktrees share one hook, so a link would run one checkout's `park.sh` for all of them. Nothing else. Must run on macOS `/bin/bash` 3.2 and Git Bash: no bash-4 features, POSIX awk only. `.delphi/park.sh` (the hook) lets one clone switch freely between workspaces and `main`. When a @@ -236,7 +236,7 @@ out; nothing is repaired or rebuilt. Branches of the same workspace move nothing overwrites: a folder in the way is reported and the other copy stays parked. `.delphi/new-worktree.sh []` creates or reuses `worktrees//` -from that store: checks out an existing branch, else starts one from `` (default `origin/HEAD`). Same shell rules. +from the clone at `repos/`: checks out an existing branch, else starts one from `` (default `origin/HEAD`). Same shell rules. The inherited `new-worktree` skill makes worktrees the default way to start a branch. `.delphi/paths.sh` is the layout in one place: sourced, it sets and exports `DELPHI_ROOT` (from the diff --git a/docs/goals.md b/docs/goals.md index 7f9f4da..c8a8377 100644 --- a/docs/goals.md +++ b/docs/goals.md @@ -12,7 +12,7 @@ anyone, person or agent, work on a workspace with plain git and send improvement - **G1. Workspaces live in Delphi.** Each workspace is a folder on `main` with every file at its normal harness location. - **G2. The workspace is the repo root.** Checking out `ws/` gives exactly that workspace at - the root. `.delphi/setup.sh` checks out the code repos it lists as worktrees. + the root. `.delphi/setup.sh` clones the code repos it lists. - **G3. Both directions, automatically.** Refresh: changes on `main` reach `ws/`. Propose: merged workspace changes reach `main` as a PR. - **G4. Conflicts are reported, never guessed.** A conflicting workspace is skipped with its files diff --git a/software/application-software/argos/workspace-defaults/workspace.yml b/software/application-software/argos/workspace-defaults/workspace.yml index 3515a9d..95ff13d 100644 --- a/software/application-software/argos/workspace-defaults/workspace.yml +++ b/software/application-software/argos/workspace-defaults/workspace.yml @@ -6,7 +6,7 @@ park: # `- `: root folders that park with this works - .playwright-mcp - .playwright-cli # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) argos: https://github.com/Northeastern-Electric-Racing/Argos.git odyssey-definitions: https://github.com/Northeastern-Electric-Racing/Odyssey-Definitions.git # CAN definitions for verify-telemetry workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at workspaces/ diff --git a/software/application-software/argos/workspaces/argos-dev/CLAUDE.md b/software/application-software/argos/workspaces/argos-dev/CLAUDE.md index 3665c2a..e3a6240 100644 --- a/software/application-software/argos/workspaces/argos-dev/CLAUDE.md +++ b/software/application-software/argos/workspaces/argos-dev/CLAUDE.md @@ -1,6 +1,6 @@ # Delphi workspace -You're in Delphi workspace `s.app.argos.argos-dev` (branch `ws/s.app.argos.argos-dev`); code repos are checked out as worktrees under `worktrees//` (`.delphi/setup.sh` makes the default branch's). Make new branches as worktrees (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. +You're in Delphi workspace `s.app.argos.argos-dev` (branch `ws/s.app.argos.argos-dev`); code repos are cloned at `repos//` (`.delphi/setup.sh`), for people working on one thing at a time; leave those clones alone and make new branches as worktrees under `worktrees//` (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. Always work from the workspace root (this clone's top, where `workspace.yml` is); the layout exists so one session can reach every worktree from there. Name worktrees by path: `git -C worktrees/argos/ …` runs git there without moving, and a one-off subshell like `(cd worktrees/argos//angular-client && npx ng lint)` runs anything else. If you `cd` into a worktree to look closely at something, `cd` back to the root as soon as you're done. Skill scripts take only names (repo, branch) and find the rest from `$DELPHI_ROOT`, which a session hook sets in every shell (`$DELPHI_WORKTREES` and the other `.delphi/paths.sh` folders too). diff --git a/software/application-software/argos/workspaces/argos-dev/workspace.yml b/software/application-software/argos/workspaces/argos-dev/workspace.yml index 3c4d817..8fb92bd 100644 --- a/software/application-software/argos/workspaces/argos-dev/workspace.yml +++ b/software/application-software/argos/workspaces/argos-dev/workspace.yml @@ -8,7 +8,7 @@ park: # `- `: root folders that park with this works - .playwright-mcp - .playwright-cli # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) argos: https://github.com/Northeastern-Electric-Racing/Argos.git odyssey-definitions: https://github.com/Northeastern-Electric-Racing/Odyssey-Definitions.git references-git: # `: []`: dependency sources to read, never edit; shallow copy at references/ diff --git a/software/application-software/argos/workspaces/experimental/CLAUDE.md b/software/application-software/argos/workspaces/experimental/CLAUDE.md index b45ba2a..6fd4d1c 100644 --- a/software/application-software/argos/workspaces/experimental/CLAUDE.md +++ b/software/application-software/argos/workspaces/experimental/CLAUDE.md @@ -1,6 +1,6 @@ # Delphi workspace -You're in Delphi workspace `s.app.argos.experimental` (branch `ws/s.app.argos.experimental`); code repos are checked out as worktrees under `worktrees//` (`.delphi/setup.sh` makes the default branch's). Make new branches as worktrees (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. +You're in Delphi workspace `s.app.argos.experimental` (branch `ws/s.app.argos.experimental`); code repos are cloned at `repos//` (`.delphi/setup.sh`), for people working on one thing at a time; leave those clones alone and make new branches as worktrees under `worktrees//` (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. # NER Software conventions diff --git a/software/application-software/argos/workspaces/experimental/workspace.yml b/software/application-software/argos/workspaces/experimental/workspace.yml index 68d1cbb..7ce25e9 100644 --- a/software/application-software/argos/workspaces/experimental/workspace.yml +++ b/software/application-software/argos/workspaces/experimental/workspace.yml @@ -8,7 +8,7 @@ park: # `- `: root folders that park with this works - .playwright-mcp - .playwright-cli # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) argos: https://github.com/Northeastern-Electric-Racing/Argos.git odyssey-definitions: https://github.com/Northeastern-Electric-Racing/Odyssey-Definitions.git workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at workspaces/ diff --git a/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md b/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md index 3a545ab..256164e 100644 --- a/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md +++ b/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md @@ -1,6 +1,6 @@ # Delphi workspace -You're in Delphi workspace `s.app.nero.nero-experimental` (branch `ws/s.app.nero.nero-experimental`); code repos are checked out as worktrees under `worktrees//` (`.delphi/setup.sh` makes the default branch's). Make new branches as worktrees (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. +You're in Delphi workspace `s.app.nero.nero-experimental` (branch `ws/s.app.nero.nero-experimental`); code repos are cloned at `repos//` (`.delphi/setup.sh`), for people working on one thing at a time; leave those clones alone and make new branches as worktrees under `worktrees//` (`new-worktree` skill) unless the user says not to. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. # NER Software conventions diff --git a/software/application-software/nero/workspaces/nero-experimental/workspace.yml b/software/application-software/nero/workspaces/nero-experimental/workspace.yml index 07aa46f..216698d 100644 --- a/software/application-software/nero/workspaces/nero-experimental/workspace.yml +++ b/software/application-software/nero/workspaces/nero-experimental/workspace.yml @@ -6,7 +6,7 @@ park: # `- `: root folders that park with this works - pictures - .playwright-mcp # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) nero: https://github.com/Northeastern-Electric-Racing/Nero-2.0.git workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at workspaces/ # - main diff --git a/software/application-software/workspaces/hub/workspace.yml b/software/application-software/workspaces/hub/workspace.yml index 9816e66..9c7e0ca 100644 --- a/software/application-software/workspaces/hub/workspace.yml +++ b/software/application-software/workspaces/hub/workspace.yml @@ -6,7 +6,7 @@ plugins: # `- [.]`: plugin sets to enable; CI lists opt-out: # `- `: files from the workspace-inherit/ folders above that CI leaves alone here, so the workspace keeps its own copy (or none) # - .claude/skills/journal # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) # name: https://github.com/Northeastern-Electric-Racing/.git references-git: # `: []`: dependency sources to read, never edit; shallow copy at references/ # name: https://github.com//.git v1.2.3 # no tag or branch: its default branch diff --git a/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md b/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md index ac48d97..8aa680b 100644 --- a/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md +++ b/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md @@ -1,6 +1,6 @@ # Delphi workspace -You're in Delphi workspace `s.firmware.calypso.calypso-dev` (branch `ws/s.firmware.calypso.calypso-dev`); code repos are checked out as worktrees under `worktrees//` (`.delphi/setup.sh` makes the default branch's). Make new branches as worktrees (`new-worktree` skill) unless the user says not to. Read-only dependency sources from `references-git:` are checked out under `references//` at the version the workspace pins: read them to learn how a dependency behaves (prefer them over web docs), and never edit, branch, or push them. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. +You're in Delphi workspace `s.firmware.calypso.calypso-dev` (branch `ws/s.firmware.calypso.calypso-dev`); code repos are cloned at `repos//` (`.delphi/setup.sh`), for people working on one thing at a time; leave those clones alone and make new branches as worktrees under `worktrees//` (`new-worktree` skill) unless the user says not to. Read-only dependency sources from `references-git:` are checked out under `references//` at the version the workspace pins: read them to learn how a dependency behaves (prefer them over web docs), and never edit, branch, or push them. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. # Calypso diff --git a/software/firmware/calypso/workspaces/calypso-dev/workspace.yml b/software/firmware/calypso/workspaces/calypso-dev/workspace.yml index 21b82b1..46b614c 100644 --- a/software/firmware/calypso/workspaces/calypso-dev/workspace.yml +++ b/software/firmware/calypso/workspaces/calypso-dev/workspace.yml @@ -5,7 +5,7 @@ plugins: # `- [.]`: plugin sets to enable; CI lists opt-out: # `- `: files from the workspace-inherit/ folders above that CI leaves alone here, so the workspace keeps its own copy (or none) # - .claude/skills/journal # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) calypso: https://github.com/Northeastern-Electric-Racing/Calypso.git workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at workspaces/ - main diff --git a/software/workspace-inherit/.claude/skills/new-worktree/SKILL.md b/software/workspace-inherit/.claude/skills/new-worktree/SKILL.md index 9fd3ac7..9ee9e83 100644 --- a/software/workspace-inherit/.claude/skills/new-worktree/SKILL.md +++ b/software/workspace-inherit/.claude/skills/new-worktree/SKILL.md @@ -10,6 +10,5 @@ bash .delphi/new-worktree.sh [] ``` - An existing branch (local or on origin, e.g. a PR head) is checked out; anything else is created from `` (default: the repo's default branch). Safe to re-run. -- Stay at the workspace root and reach into the worktree by the printed path. Never switch branches in an existing worktree or work in the bare store `repos/`. -- Keep `worktrees///` a clean reference: fetch and fast-forward only. +- Stay at the workspace root and reach into the worktree by the printed path. Never switch branches in an existing worktree or work in the clone `repos/` (it's for people working on one thing at a time). - After the branch merges: `git -C repos/ worktree remove ../../worktrees//`. diff --git a/templates/workspace/CLAUDE.md b/templates/workspace/CLAUDE.md index 1ce493f..7a5c872 100644 --- a/templates/workspace/CLAUDE.md +++ b/templates/workspace/CLAUDE.md @@ -1,3 +1,3 @@ # Delphi workspace -You're in Delphi workspace `{{name}}` (branch `ws/{{name}}`); code repos are checked out as worktrees under `worktrees//` (`.delphi/setup.sh` makes the default branch's). Make new branches as worktrees (`new-worktree` skill) unless the user says not to. Read-only dependency sources from `references-git:` are checked out under `references//` at the version the workspace pins: read them to learn how a dependency behaves (prefer them over web docs), and never edit, branch, or push them. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. +You're in Delphi workspace `{{name}}` (branch `ws/{{name}}`); code repos are cloned at `repos//` (`.delphi/setup.sh`), for people working on one thing at a time; leave those clones alone and make new branches as worktrees under `worktrees//` (`new-worktree` skill) unless the user says not to. Read-only dependency sources from `references-git:` are checked out under `references//` at the version the workspace pins: read them to learn how a dependency behaves (prefer them over web docs), and never edit, branch, or push them. Delphi workspaces (or `main`) under `workspaces:` in `workspace.yml` (`- `, a workspace's dotted name like `s.app.argos.argos-dev`, or `- main`) are clones at `workspaces//`, each on its branch, for reading their context (don't edit them from here; setup switches each back to its branch and pulls it); change what's checked out with the `setup-workspace` skill. To work in another workspace, switch this clone to it (`switch-workspace` skill). Park rough notes with the `journal` skill. Work here unless the task clearly belongs to another project. diff --git a/templates/workspace/workspace.yml b/templates/workspace/workspace.yml index 43df9f5..9902780 100644 --- a/templates/workspace/workspace.yml +++ b/templates/workspace/workspace.yml @@ -7,7 +7,7 @@ opt-out: # `- `: files from the workspace-inherit/ folder park: # `- `: root folders that park with this workspace on a switch (.delphi/park.sh), besides the ones it always parks # - pictures # .delphi/setup.sh checks out everything below (git-ignored); re-run it after any change. -repos: # `: `: code you change; bare store at repos/, default branch at worktrees// +repos: # `: `: code you change; a clone at repos/ (worktrees from it: worktrees//) # name: https://github.com/Northeastern-Electric-Racing/.git references-git: # `: []`: dependency sources to read, never edit; shallow copy at references/ # name: https://github.com//.git v1.2.3 # no tag or branch: its default branch diff --git a/tests/e2e.sh b/tests/e2e.sh index 3f0fee9..17940a4 100644 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -331,7 +331,7 @@ expect_bad unknown-workspace "lists nope, which isn't a workspace on main" sh -c expect_bad template-delphi "templates/workspace/.delphi: scripts live only" mkdir templates/workspace/.delphi expect_bad template-workflow-drift "differs from .github/workflows/delphi.yml" sh -c "echo x >>.github/workflows/delphi.yml" -# --- 8. setup.sh fetches workspace.yml repos into repos/, checks out the default branch under worktrees/, and is idempotent +# --- 8. setup.sh clones workspace.yml repos at repos/ on the default branch, adds no worktree, and is idempotent for r in lib1 lib2; do git init -q "$t/$r" && git -C "$t/$r" commit -q --allow-empty -m "$r" && git clone -q --bare "$t/$r" "$t/$r.git" done @@ -342,21 +342,23 @@ printf 'name: s.app.argos.argos-dev\nharness: claude-code\nplugins:\n - x.y\npa /bin/bash "$t/ws/.delphi/setup.sh" >/dev/null 2>&1 || fail "setup.sh failed" /bin/bash "$t/ws/.delphi/setup.sh" >"$t/out" 2>&1 || fail "setup.sh re-run failed" for r in lib1 lib2; do - [ "$(git -C "$t/ws/worktrees/$r/$def" rev-parse HEAD)" = "$(git -C "$t/$r" rev-parse HEAD)" ] || fail "$r default worktree missing" - [ "$(git -C "$t/ws/worktrees/$r/$def" branch --show-current)" = "$def" ] || fail "$r default worktree not on $def" + [ "$(git -C "$t/ws/repos/$r" rev-parse HEAD)" = "$(git -C "$t/$r" rev-parse HEAD)" ] || fail "repos/$r not cloned" + [ "$(git -C "$t/ws/repos/$r" branch --show-current)" = "$def" ] || fail "repos/$r not on $def" done -[ "$(git -C "$t/ws/repos/lib1" rev-parse --is-bare-repository)" = true ] || fail "repos/lib1 not a bare store" +[ "$(git -C "$t/ws/repos/lib1" rev-parse --is-bare-repository)" = false ] || fail "repos/lib1 is a bare store" +[ ! -e "$t/ws/worktrees" ] || fail "setup.sh made worktrees/" grep -q "repos/lib1: already present" "$t/out" || fail "re-run did not skip" for d in /repos/ /worktrees/ /.journal/ /.playwright-mcp/ /pictures/ .DS_Store; do [ "$(grep -cx "$d" "$t/ws/.git/info/exclude")" = 1 ] || fail "$d not excluded exactly once"; done mkdir -p "$t/ws/.playwright-mcp" "$t/ws/pictures/b" "$t/ws/docs" && touch "$t/ws/.playwright-mcp/p.yml" "$t/ws/pictures/b/s.png" "$t/ws/.DS_Store" "$t/ws/docs/.DS_Store" [ "$(git -C "$t/ws" status --porcelain)" = " M workspace.yml" ] || fail "repos/, worktrees/ or a tool's files show in status: $(git -C "$t/ws" status --porcelain)" -rm -rf "$t/ws/worktrees/lib2" && git -C "$t/ws/repos/lib2" worktree prune && /bin/bash "$t/ws/.delphi/setup.sh" >/dev/null 2>&1 && - [ -d "$t/ws/worktrees/lib2/$def" ] || fail "re-run did not restore a missing default worktree" -ok "setup.sh makes a bare store in repos/ plus a default-branch worktree in worktrees/, excludes both (and tools' files) once, and is idempotent" -w=$t/legacy && git clone -q -b ws/$AN "$t/origin.git" "$w" && cp "$t/ws/workspace.yml" "$w/" && git clone -q "$t/lib1.git" "$w/repos/lib1" -/bin/bash "$w/.delphi/setup.sh" >/dev/null 2>"$t/err" || fail "setup.sh failed on a pre-existing clone" -{ grep -q "could not check out worktrees/lib1/$def" "$t/err" && [ -d "$w/worktrees/lib2/$def" ]; } || fail "pre-existing clone not reported" -ok "setup.sh warns and carries on when repos/ is an older plain clone" +rm -rf "$t/ws/repos/lib2" && /bin/bash "$t/ws/.delphi/setup.sh" >/dev/null 2>&1 && + [ -d "$t/ws/repos/lib2/.git" ] || fail "re-run did not restore a missing clone" +ok "setup.sh clones repos: at repos/ on the default branch, adds no worktree, excludes repos/ and worktrees/ (and tools' files) once, and is idempotent" +w=$t/legacy && git clone -q -b ws/$AN "$t/origin.git" "$w" && cp "$t/ws/workspace.yml" "$w/" && git init -q --bare "$w/repos/lib1" +/bin/bash "$w/.delphi/setup.sh" >"$t/out" 2>&1 || fail "setup.sh failed with an older bare store" +{ grep -q "repos/lib1: already present" "$t/out" && [ "$(git -C "$w/repos/lib1" rev-parse --is-bare-repository)" = true ] && + [ -d "$w/repos/lib2/.git" ]; } || fail "older bare store not left as it is" +ok "setup.sh leaves an older clone's bare store in repos/ as it is" git clone -q -c core.autocrlf=true -b ws/$AN "$t/origin.git" "$t/ws-crlf" cp "$t/ws/workspace.yml" "$t/ws-crlf/" /bin/bash "$t/ws-crlf/.delphi/setup.sh" >/dev/null 2>"$t/err" || fail "setup.sh fails with core.autocrlf=true" @@ -392,8 +394,7 @@ local_ws() { # : a local branch of git -C "$w" checkout -q -b "$1" "origin/$2" && printf 'name: %s\nharness: claude-code\npark:\n - pictures\nrepos:\n %s: %s\nreferences-git:\n %s-ref: file://%s\nworkspaces:\n - main\n' \ "$3" "$4" "$t/$4.git" "$4" "$t/$4.git" >"$w/workspace.yml" && git -C "$w" commit -qam "$1" && /bin/bash "$w/.delphi/setup.sh" >/dev/null 2>&1 } -live() { [ -d "$w/worktrees/$1/$def" ] && [ "$(git -C "$w/worktrees/$1/$def" branch --show-current)" = "$def" ] && - ! git -C "$w/repos/$1" worktree list --porcelain | grep -q prunable && git -C "$w/references/$1-ref" rev-parse -q HEAD >/dev/null && +live() { [ "$(git -C "$w/repos/$1" branch --show-current 2>/dev/null)" = "$def" ] && git -C "$w/references/$1-ref" rev-parse -q HEAD >/dev/null && [ -f "$w/workspaces/main/ci/sync.sh" ]; } local_ws a1 "ws/$AN" "$AN" lib1 || fail "park: setup on argos-dev failed" ours() { [ ! -L "$w/.git/hooks/post-checkout" ] && grep -qx '\[ ! -f .delphi/park.sh \] || exec bash .delphi/park.sh "$@"' "$w/.git/hooks/post-checkout"; } @@ -405,14 +406,14 @@ local_ws b1 "ws/$BN" "$BN" lib2 || fail "park: setup on bms-dev failed" [ -f "$w/.delphi/parked/$AN/pictures/a2/s.png" ] && [ -f "$w/notes/x" ]; } || fail "park: .journal/ and park: folders not parked with argos-dev, or an unlisted folder moved" rm -r "$w/notes" -{ [ ! -e "$w/worktrees/lib1" ] && [ ! -e "$w/references/lib1-ref" ] && [ -d "$w/.delphi/parked/$AN/worktrees/lib1/$def" ] && +{ [ ! -e "$w/repos/lib1" ] && [ ! -e "$w/references/lib1-ref" ] && [ -d "$w/.delphi/parked/$AN/repos/lib1/.git" ] && [ -d "$w/.delphi/parked/$AN/workspaces/main/.git" ] && live lib2; } || fail "park: argos-dev not parked on bms-dev" [ -z "$(git -C "$w" status --porcelain)" ] || fail "park: parked clones show in status: $(git -C "$w" status --porcelain)" { git -C "$w" checkout -q main && [ ! -e "$w/repos" ] && [ ! -e "$w/worktrees" ] && [ ! -e "$w/references" ] && [ ! -e "$w/workspaces" ]; } || fail "park: clones visible on main: $(ls -A "$w")" [ -z "$(git -C "$w" status --porcelain)" ] || fail "park: main not clean" -{ git -C "$w" checkout -q a2 && live lib1 && [ ! -e "$w/worktrees/lib2" ] && [ -d "$w/.delphi/parked/$BN/repos/lib2" ] && +{ git -C "$w" checkout -q a2 && live lib1 && [ ! -e "$w/repos/lib2" ] && [ -d "$w/.delphi/parked/$BN/repos/lib2" ] && [ ! -e "$w/.delphi/parked/$AN" ]; } || fail "park: argos-dev not restored" { [ -f "$w/.journal/ideas/n.md" ] && [ -f "$w/pictures/a2/s.png" ]; } || fail "park: .journal/ and pictures/ not restored" @@ -421,12 +422,12 @@ git -C "$w" checkout -q -b a4 a2 && printf 'name: %s\nharness: claude-code\npark { [ -d "$w/.git" ] && grep -q "won't park '.git'" "$t/err" && grep -q "won't park '../up'" "$t/err" && git -C "$w" checkout -q a2 && live lib1; } || fail "park: a bad park: entry was not skipped: $(cat "$t/err")" ok "park.sh skips a park: entry that isn't a folder name at the root (.git, ../x)" -git -C "$w/worktrees/lib1/$def" commit -q --allow-empty -m "after restore" || fail "park: restored worktree unusable" +git -C "$w/repos/lib1" commit -q --allow-empty -m "after restore" || fail "park: restored clone unusable" { git -C "$w" checkout -q b1 && live lib2 && git -C "$w" checkout -q a1 && live lib1; } || fail "park: second round trip" -git -C "$w" checkout -q main && mkdir "$w/worktrees" && git -C "$w" checkout -q a1 2>"$t/err" -{ grep -q "worktrees/ is in the way" "$t/err" && [ -d "$w/repos/lib1" ] && [ -d "$w/.delphi/parked/$AN/worktrees" ]; } || +git -C "$w" checkout -q main && mkdir "$w/references" && git -C "$w" checkout -q a1 2>"$t/err" +{ grep -q "references/ is in the way" "$t/err" && [ -d "$w/repos/lib1" ] && [ -d "$w/.delphi/parked/$AN/references" ]; } || fail "park: a folder in the way was not reported and kept: $(cat "$t/err")" -{ rmdir "$w/worktrees" && git -C "$w" checkout -q main && git -C "$w" checkout -q a1 && live lib1; } || fail "park: no recovery once cleared" +{ rmdir "$w/references" && git -C "$w" checkout -q main && git -C "$w" checkout -q a1 && live lib1; } || fail "park: no recovery once cleared" ok "park.sh parks a workspace's clones on switching to another workspace or main and restores them at the same paths" ok "parked clones sit git-ignored in .delphi/parked//, the workspace's name" ok "park.sh also parks .journal/ and the folders under the leaving workspace's park:, and restores all a workspace parked" @@ -518,7 +519,7 @@ nw code feat >/dev/null && [ "$(git -C "$w/worktrees/code/feat" rev-parse HEAD)" nw code 2-dev origin/develop >/dev/null && [ "$(git -C "$w/worktrees/code/2-dev" rev-parse HEAD)" = "$(git -C "$t/code" rev-parse develop)" ] || fail " ignored" [ "$(nw code feat)" = "$w/worktrees/code/feat" ] || fail "rerun did not reuse" -[ "$(git -C "$w/worktrees/code/$def" branch --show-current)" = "$def" ] || fail "default worktree left its branch" +[ "$(git -C "$w/repos/code" branch --show-current)" = "$def" ] || fail "repos/code left its branch" ok "new-worktree.sh makes new branches from the default or , checks out existing ones, reuses on rerun" # skill scripts read the workspace from $DELPHI_ROOT (the session hook evals paths.sh's export line) and every folder from paths.sh (. "$w/.delphi/paths.sh" && [ "$DELPHI_ROOT" = "$w" ] && [ "$DELPHI_REPOS" = "$w/repos" ] && @@ -584,7 +585,7 @@ printf 'name: s.electrical.bms-dev\nharness: claude-code\nrepos:\n code: %s\nwo printf 'name: %s\nharness: claude-code\nrepos:\n lib1: %s\nworkspaces:\n - main\n - %s\n' "$AN" "$t/lib1.git" "$BN" >workspace.yml && git commit -qam "sandbox repos" && git push -q origin HEAD:ws/$AN) bash "$w/.delphi/setup.sh" >/dev/null 2>"$t/err" || fail "setup.sh failed with links: $(cat "$t/err")" -{ [ -d "$w/workspaces/$AN/worktrees/lib1" ] && [ ! -e "$w/workspaces/$AN/workspaces" ]; } || +{ [ -d "$w/workspaces/$AN/repos/lib1/.git" ] && [ ! -e "$w/workspaces/$AN/workspaces" ]; } || fail "setup.sh did not run a link's own setup, or that setup cloned its own workspaces: $(cat "$t/err")" [ "$(git -C "$w/workspaces/$AN" rev-parse HEAD)" = "$(o rev-parse "ws/$AN")" ] || fail "copy not at ws/$AN" { [ "$(git -C "$w/workspaces/$AN" branch --show-current)" = "ws/$AN" ] && [ "$(git -C "$w/workspaces/main" branch --show-current)" = main ] && @@ -662,7 +663,7 @@ sw $BN >"$t/out" 2>&1 || fail "switch to bms-dev failed: $(cat "$t/out")" commit_on ws/$AN docs/CONTEXT.md "while away" sw "ws/$AN" >"$t/out" 2>&1 || fail "switch back (by branch) failed: $(cat "$t/out")" { [ "$(git -C "$w" branch --show-current)" = ws/$AN ] && grep -qx "while away" "$w/docs/CONTEXT.md" && - [ -d "$w/worktrees/lib1/$def" ] && [ -f "$w/workspaces/main/ci/sync.sh" ]; } || fail "switch: argos-dev not restored and fast-forwarded" + [ -d "$w/repos/lib1/.git" ] && [ -f "$w/workspaces/main/ci/sync.sh" ]; } || fail "switch: argos-dev not restored and fast-forwarded" { sw main >/dev/null 2>&1 && [ "$(git -C "$w" branch --show-current)" = main ] && [ ! -e "$w/repos" ] && [ -f "$w/ci/sync.sh" ]; } || fail "switch to main" git -C "$w" switch -q ws/$AN