diff --git a/.delphi/manifest.yml b/.delphi/manifest.yml index 5fe7475..5f84b1e 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: 0.8.0 +version: 0.9.0 files: # repo-relative paths; a trailing / means everything under it - .delphi/manifest.yml - .delphi/.gitattributes diff --git a/.delphi/migrations/0.9.0-linked-clones.sh b/.delphi/migrations/0.9.0-linked-clones.sh new file mode 100755 index 0000000..c7ae000 --- /dev/null +++ b/.delphi/migrations/0.9.0-linked-clones.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# 0.9.0: linked/ used to be a shallow, detached, push-disabled copy of ws/ (or main); +# setup.sh now keeps a full clone there, on that branch and tracking origin, with pushes allowed. Turns +# each old copy here (its push URL is DISABLED-read-only-copy) into one in place: fetches its full +# history, puts it on a local ws/ (or main) at the commit it's on, so nothing in it moves, and +# allows pushes. setup.sh then pulls it. Parked copies are their own workspace's, migrated when it's +# set up. Stops at one it can't fetch or that already has a local branch of that name. +set -euo pipefail +for d in linked/*/; do + d=${d%/} + [ -d "$d/.git" ] || continue + [ "$(git -C "$d" config remote.origin.pushurl || true)" = DISABLED-read-only-copy ] || continue # a clone already + n=${d#linked/} ref=ws/${d#linked/} unshallow= + [ "$n" != main ] || ref=main + [ "$(git -C "$d" rev-parse --is-shallow-repository)" = false ] || unshallow=--unshallow + git -C "$d" fetch -q ${unshallow:+"$unshallow"} origin "+refs/heads/$ref:refs/remotes/origin/$ref" &2 && exit 1; } + if [ -z "$(git -C "$d" branch --show-current)" ]; then + git -C "$d" switch -q -c "$ref" 2>/dev/null || + { echo "migrate: $d already has a branch $ref; git -C $d switch $ref (or delete it), then re-run setup" >&2 && exit 1; } + fi + ! git -C "$d" rev-parse -q --verify "refs/heads/$ref" >/dev/null || git -C "$d" branch -q -u "origin/$ref" "$ref" + git -C "$d" config --unset-all remote.origin.pushurl + echo "migrate: $d is a full clone on $ref now" +done diff --git a/.delphi/paths.sh b/.delphi/paths.sh index e763ad3..c502e67 100644 --- a/.delphi/paths.sh +++ b/.delphi/paths.sh @@ -9,7 +9,7 @@ DELPHI_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd) DELPHI_REPOS=$DELPHI_ROOT/repos # bare stores: repos/ DELPHI_WORKTREES=$DELPHI_ROOT/worktrees # checkouts: worktrees// DELPHI_REFERENCES=$DELPHI_ROOT/references # read-only dependency sources: references/ -DELPHI_LINKED=$DELPHI_ROOT/linked # read-only Delphi copies: linked/, linked/main +DELPHI_LINKED=$DELPHI_ROOT/linked # Delphi clones to read: linked/, linked/main export DELPHI_ROOT DELPHI_REPOS DELPHI_WORKTREES DELPHI_REFERENCES DELPHI_LINKED [ "${BASH_SOURCE[0]:-}" != "$0" ] || printf 'export DELPHI_ROOT=%q DELPHI_REPOS=%q DELPHI_WORKTREES=%q DELPHI_REFERENCES=%q DELPHI_LINKED=%q\n' \ "$DELPHI_ROOT" "$DELPHI_REPOS" "$DELPHI_WORKTREES" "$DELPHI_REFERENCES" "$DELPHI_LINKED" diff --git a/.delphi/setup.sh b/.delphi/setup.sh index 185ef31..7fbcb06 100755 --- a/.delphi/setup.sh +++ b/.delphi/setup.sh @@ -6,8 +6,9 @@ # `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 -# from .delphi/names.sh, or `- main`) gets the same kind of copy of that branch of this clone's -# origin (Delphi, ws/ or main), at linked/; to edit one, switch this clone to it (.delphi/switch.sh). Adds the +# from .delphi/names.sh, or `- main`) gets a full clone of this clone's origin (Delphi) at linked/, +# on ws/ (or main) tracking origin; every run switches it back to that branch if it was switched +# away (its own .delphi/switch.sh) and pulls it. By convention it's read from here, not edited. Adds the # folders park.sh parks and macOS's .DS_Store to this clone's .git/info/exclude, and writes its post-checkout hook, which runs .delphi/park.sh (on # every branch) as just checked out in the worktree that checked out (unless another hook is there), so switching # to another workspace or main parks those folders. First @@ -68,11 +69,22 @@ readonly_copy() { # []: shallow, detached, push-disabled checko fi } -link() { # |main: a read-only copy of that workspace's branch of Delphi (ws/), or main, at linked/ +link() { # |main: a clone of Delphi on that workspace's branch (ws/), or main, at linked/ case "$1" in "$self") return ;; "" | [-.]* | *. | *..* | *[!a-z0-9.-]*) echo "setup: skipping bad workspaces: entry '$1' (want a workspace's name, like s.app.argos.argos-dev, or main)" >&2 && return ;; esac - readonly_copy "linked/$1" "$(git remote get-url origin)" "$([ "$1" = main ] && echo main || echo "ws/$1")" + local dir=linked/$1 ref=ws/$1 + [ "$1" != main ] || ref=main + [ ! -f "$dir/.git" ] || { echo "setup: $dir is a worktree, not a clone; skipping it" >&2 && return; } + if [ ! -d "$dir/.git" ]; then + git clone -q --branch "$ref" "$(git remote get-url origin)" "$dir" &2 && return; } + elif [ "$(git -C "$dir" branch --show-current)" != "$ref" ]; then # switched away: back to its own branch first + bash "$dir/.delphi/switch.sh" "$1" /dev/null || + { echo "setup: could not switch $dir back to $ref; left it as it is" >&2 && return; } + fi + git -C "$dir" pull -q --ff-only origin "$ref" &2 + echo "$dir: on $ref ($(git -C "$dir" rev-parse --short HEAD))" } while read -r kind name url ref; do diff --git a/.delphi/versions b/.delphi/versions index 4f5c8a5..ed6df10 100644 --- a/.delphi/versions +++ b/.delphi/versions @@ -13,3 +13,4 @@ 0.6.0 c65ffc578bfa setup.sh (.delphi/plugins.sh) makes each clone's @delphi plugins what its workspace enables (installs them, uninstalls local ones it doesn't) and uninstalls ones the marketplace dropped, instead of warning; plugin changes need no migration 0.7.0 84df5ce802db park.sh also parks .journal/ and the root folders under a workspace's park: in workspace.yml (each workspace its own), restores everything parked for a workspace, and skips bad entries; setup.sh excludes them 0.8.0 05d4c954a946 workspaces are named by their folders' short names and leaf, joined by dots (.delphi/names.sh, short-name files; ws/s.app.argos.argos-dev, was ws/software/…/argos-dev); workspaces: entries are - | - 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) diff --git a/README.md b/README.md index 9cd19f4..591daec 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ repos: # stored in repos/, checked out in worktrees// socketioxide: https://github.com/Totodore/socketioxide.git v0.18.7 # optional tag or branch -workspaces: # Delphi workspaces (by name) or main, read-only, shallow-cloned into linked// +workspaces: # Delphi workspaces (by name) or main, cloned into linked// to read - main ``` @@ -76,8 +76,10 @@ workspaces: # Delphi workspaces (by name) or main, read-only, shallo 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 unique across both maps. The `-git` suffix leaves room for other kinds of references later. -`workspaces:` are other workspaces (`- `) or `main` to read: the same kind of read-only copy, of Delphi itself. -To change one, switch to it instead. +`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 +run switches a clone back to its branch if it was switched away and pulls it. By convention they aren't +edited from the workspace that links them; to change one, work in its own clone or switch to it. The workspace's dotted name is repeated in `name:` so a checkout knows which workspace it is; names are unique (give a folder a `short-name` that no sibling's name or short name clashes with). Every other file in the folder is the workspace's own, at its normal harness diff --git a/docs/design.md b/docs/design.md index e769297..f4d0d13 100644 --- a/docs/design.md +++ b/docs/design.md @@ -204,8 +204,12 @@ sources), it fetches `` (default: origin's `HEAD`) with `--depth 1` into `r 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:` (`- ` -or `- main`; the workspace itself is skipped) gets the same kind of copy of that branch (`ws/` -or `main`) of the clone's own origin, at `linked//`, also refreshed every run; `/linked/` is excluded too. +or `- main`; the workspace itself is skipped) gets a full clone of the clone's own origin at +`linked//`, 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 +away (with its own `.delphi/switch.sh`, which refuses over uncommitted changes) and pulls it +(`--ff-only`); a failure is reported and the clone left as it is. By convention a workspace reads its +links and doesn't edit them; `/linked/` is excluded too. Links are separate clones, not worktrees of the Delphi clone, so two workspaces can both link `main` and each copy parks with its workspace. Writes the clone's `post-checkout` hook, a 3-line script that runs `.delphi/park.sh` from the worktree that checked out (replacing an older link to or copy of a diff --git a/software/application-software/argos/AUTHORING.md b/software/application-software/argos/AUTHORING.md index 5bd2f2e..5bc3eea 100644 --- a/software/application-software/argos/AUTHORING.md +++ b/software/application-software/argos/AUTHORING.md @@ -25,7 +25,7 @@ a PR to `main`. Trim what the new workspace doesn't need before merging. ## Change the defaults or this folder Edit on a branch of `main` and open a PR to `main` (from a workspace: `bash .delphi/switch.sh main`; -`linked/main/` is read-only). `workspace-defaults/` +don't edit `linked/main/` from a workspace). `workspace-defaults/` only affects workspaces created afterwards; to change existing ones, PR each workspace too. `workspace-inherit/` reaches every Argos workspace on the next sync. diff --git a/software/application-software/argos/workspace-defaults/workspace.yml b/software/application-software/argos/workspace-defaults/workspace.yml index 86d5603..966f89a 100644 --- a/software/application-software/argos/workspace-defaults/workspace.yml +++ b/software/application-software/argos/workspace-defaults/workspace.yml @@ -9,5 +9,5 @@ park: # `- `: root folders that park with this works repos: # `: `: code you change; bare store at repos/, default branch at 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, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ - main diff --git a/software/application-software/argos/workspaces/argos-dev/CLAUDE.md b/software/application-software/argos/workspaces/argos-dev/CLAUDE.md index a4ca48d..3e5bedf 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 read-only copies at `linked//` for reading their context; 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 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 `linked//`, 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 6a90f7a..c4f037b 100644 --- a/software/application-software/argos/workspaces/argos-dev/workspace.yml +++ b/software/application-software/argos/workspaces/argos-dev/workspace.yml @@ -16,5 +16,5 @@ references-git: # `: []`: dependency so socketioxide: https://github.com/Totodore/socketioxide.git socketioxide-v0.18.7 socket.io: https://github.com/socketio/socket.io.git socket.io-client@4.8.1 # monorepo: client, server, engine.io, parser zenoh: https://github.com/eclipse-zenoh/zenoh.git 1.10.1 -workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ - main diff --git a/software/application-software/argos/workspaces/experimental/CLAUDE.md b/software/application-software/argos/workspaces/experimental/CLAUDE.md index a7b6b24..802a1e0 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 read-only copies at `linked//` for reading their context; 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 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 `linked//`, 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 c5de52f..bb0bd18 100644 --- a/software/application-software/argos/workspaces/experimental/workspace.yml +++ b/software/application-software/argos/workspaces/experimental/workspace.yml @@ -11,5 +11,5 @@ park: # `- `: root folders that park with this works repos: # `: `: code you change; bare store at repos/, default branch at 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, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ - main diff --git a/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md b/software/application-software/nero/workspaces/nero-experimental/CLAUDE.md index 1b11166..de0223e 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 read-only copies at `linked//` for reading their context; 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 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 `linked//`, 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 d12ffe0..8b431b1 100644 --- a/software/application-software/nero/workspaces/nero-experimental/workspace.yml +++ b/software/application-software/nero/workspaces/nero-experimental/workspace.yml @@ -8,5 +8,5 @@ park: # `- `: root folders that park with this works # .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// nero: https://github.com/Northeastern-Electric-Racing/Nero-2.0.git -workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ # - main diff --git a/software/application-software/workspaces/hub/CLAUDE.md b/software/application-software/workspaces/hub/CLAUDE.md index 78b91b8..b567812 100644 --- a/software/application-software/workspaces/hub/CLAUDE.md +++ b/software/application-software/workspaces/hub/CLAUDE.md @@ -6,7 +6,7 @@ one under `repos:` in `workspace.yml` to make worktrees here (`new-worktree` ski Each workspace listed under `workspaces:` in `workspace.yml` is also cloned separately on this machine, on its own `ws/` branch, with its own Claude Code session. The user works in those in parallel. Here you -only see their read-only copies under `linked//` (refresh with `bash .delphi/setup.sh`; change which are +only see their clones under `linked//`, each on its branch (refresh with `bash .delphi/setup.sh`; change which are listed with the `setup-workspace` skill). Park rough notes with the `journal` skill. ## What you do here @@ -24,4 +24,4 @@ listed with the `setup-workspace` skill). Park rough notes with the `journal` sk - Switch this clone to another workspace unless the user explicitly asks (`switch-workspace`). By default, hand off: the user relaunches Claude in that workspace's own clone. -- Edit anything under `linked/`. +- Edit, commit, or push anything under `linked/`: the user may work in those clones, but not this session. diff --git a/software/application-software/workspaces/hub/workspace.yml b/software/application-software/workspaces/hub/workspace.yml index e907a5c..22d772b 100644 --- a/software/application-software/workspaces/hub/workspace.yml +++ b/software/application-software/workspaces/hub/workspace.yml @@ -10,7 +10,7 @@ repos: # `: `: code you change; bare store at # 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 -workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ - main - s.app.argos.argos-dev - s.firmware.calypso.calypso-dev diff --git a/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md b/software/firmware/calypso/workspaces/calypso-dev/CLAUDE.md index f84877e..888939d 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 read-only copies at `linked//` for reading their context; 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 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 `linked//`, 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 089b30b..51f298f 100644 --- a/software/firmware/calypso/workspaces/calypso-dev/workspace.yml +++ b/software/firmware/calypso/workspaces/calypso-dev/workspace.yml @@ -7,5 +7,5 @@ opt-out: # `- `: files from the workspace-inherit/ folder # .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// calypso: https://github.com/Northeastern-Electric-Racing/Calypso.git -workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ - main diff --git a/software/workspace-inherit/.claude/skills/setup-workspace/SKILL.md b/software/workspace-inherit/.claude/skills/setup-workspace/SKILL.md index 5343a5a..795e46a 100644 --- a/software/workspace-inherit/.claude/skills/setup-workspace/SKILL.md +++ b/software/workspace-inherit/.claude/skills/setup-workspace/SKILL.md @@ -1,6 +1,6 @@ --- name: setup-workspace -description: Change what this workspace checks out and re-run its setup. workspace.yml lists code repos (repos:), read-only dependency sources (references-git:), and read-only copies of other Delphi workspaces or main (workspaces:). Use when the user wants to read or compare another workspace's or main's instructions, skills, or docs (link it), add or remove a repo or dependency reference, or run setup after cloning or pulling. +description: Change what this workspace checks out and re-run its setup. workspace.yml lists code repos (repos:), read-only dependency sources (references-git:), and clones of other Delphi workspaces or main to read (workspaces:). Use when the user wants to read or compare another workspace's or main's instructions, skills, or docs (link it), add or remove a repo or dependency reference, or run setup after cloning or pulling. --- `workspace.yml` at the workspace root lists what `.delphi/setup.sh` checks out, all git-ignored: code repos (`repos:`), dependency sources (`references-git:`), and other Delphi workspaces or main (`workspaces:`, entries `- ` or `- main`; a workspace's name is the short names of its folders (each folder's `short-name` file, else its name) and its own folder name, joined by dots, like `s.app.argos.argos-dev`; `bash .delphi/switch.sh --list` lists them). The comment on each section gives its entry format and where it lands; read it, then edit the file. @@ -11,7 +11,7 @@ Run from the workspace root after any change, and to refresh references and work bash .delphi/setup.sh ``` -- The copies under `linked/` are for reading: never edit, branch, or push them. To change one of those workspaces or main, switch to it (`switch-workspace` skill). +- The clones under `linked/` are full clones, each on its branch (`ws/` or `main`) tracking origin, for reading: don't edit, branch, or push them from this workspace. Each setup run switches a clone back to its branch if it was switched away (refused while it has uncommitted changes) and pulls it. To change one of those workspaces or main, work in its own clone or switch to it (`switch-workspace` skill). - This workspace's project folder (shared context, `AUTHORING.md`, `workspace-defaults/`) is the folder above `workspaces//` in `linked/main/` (`bash .delphi/switch.sh --list` prints it): for `s.app.argos.argos-dev`, `linked/main/software/application-software/argos/`. List workspaces and their project folders: `bash .delphi/switch.sh --list`. - `workspace.yml` is shared: change it on a branch and open a PR into `ws/` (the `name:` in `workspace.yml`). For a one-off look, add the entry, run setup, then `git restore workspace.yml`. - Setup never deletes what you'd lose. A removed entry's `linked//`, `references//`, or repo store and worktrees stay until you delete them (ask first). diff --git a/templates/workspace/CLAUDE.md b/templates/workspace/CLAUDE.md index 39aacda..4524f28 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 read-only copies at `linked//` for reading their context; 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 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 `linked//`, 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 3f29181..f9e960d 100644 --- a/templates/workspace/workspace.yml +++ b/templates/workspace/workspace.yml @@ -11,5 +11,5 @@ repos: # `: `: code you change; bare store at # 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 -workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, never edit; shallow copy at linked/ +workspaces: # `- ` (dotted) or `- main`: Delphi workspaces to read, not edit from here; a clone on its branch at linked/ # - main # recommended: this project's shared context, AUTHORING.md and workspace-defaults/ diff --git a/tests/e2e.sh b/tests/e2e.sh index 88335ed..70dcaaf 100644 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -576,21 +576,32 @@ pl app@delphi user - 0 argos@delphi local "$w" 0 x@other user - 0 >"$t/plugins.j { [ -z "$(plugins)" ] && [ ! -s "$t/claude.log" ]; } || fail "plugins.sh did something with every plugin in order: $(cat "$t/claude.log")" ok "setup.sh (plugins.sh) makes this clone's @delphi plugins what the workspace enables and uninstalls ones the marketplace dropped; warns offline or for other marketplaces; quiet when all is in order" -# --- 12. workspaces: setup.sh checks out listed workspaces and main read-only at linked/, refreshed on re-run; +# --- 12. workspaces: setup.sh clones listed workspaces and main at linked/, on their branch, pulled on re-run; # migrations: setup.sh runs the pending ones (0.0.1 replaces old link.sh worktrees), stopping where one can't go on safely printf 'name: s.electrical.bms-dev\nharness: claude-code\nrepos:\n code: %s\nworkspaces:\n - s.app.argos.argos-dev # a comment\n - main\n - s.electrical.bms-dev\n - Bad\n - ws/argos-dev\n' \ "$t/code" >"$w/workspace.yml" bash "$w/.delphi/setup.sh" >/dev/null 2>"$t/err" || fail "setup.sh failed with links: $(cat "$t/err")" [ "$(git -C "$w/linked/$AN" rev-parse HEAD)" = "$(o rev-parse "ws/$AN")" ] || fail "copy not at ws/$AN" +{ [ "$(git -C "$w/linked/$AN" branch --show-current)" = "ws/$AN" ] && [ "$(git -C "$w/linked/main" branch --show-current)" = main ] && + [ "$(git -C "$w/linked/$AN" rev-parse --abbrev-ref '@{u}')" = "origin/ws/$AN" ] && + [ "$(git -C "$w/linked/main" rev-parse --is-shallow-repository)" = false ]; } || fail "a link is not a full clone on its branch, tracking origin" [ -f "$w/linked/main/ci/sync.sh" ] || fail "link main" { [ ! -e "$w/linked/$BN" ] && [ ! -e "$w/linked/Bad" ] && [ ! -e "$w/linked/ws" ] && grep -q "skipping bad workspaces: entry 'Bad'" "$t/err" && grep -q "skipping bad workspaces: entry 'ws/argos-dev'" "$t/err"; } || fail "copied itself or a bad entry" -! git -C "$w/linked/$AN" push -q origin HEAD:refs/heads/x 2>/dev/null || fail "push from a link succeeded" +git -C "$w/linked/$AN" push -q --dry-run origin HEAD:refs/heads/x 2>/dev/null || fail "pushes from a link are disabled" commit_on ws/$AN docs/CONTEXT.md "linked update" { bash "$w/.delphi/setup.sh" >/dev/null 2>&1 && grep -qx "linked update" "$w/linked/$AN/docs/CONTEXT.md"; } || fail "re-run did not refresh a link" +git -C "$w/linked/main" switch -q -c scratch && commit_on main README.md "main update" +{ bash "$w/.delphi/setup.sh" >/dev/null 2>&1 && [ "$(git -C "$w/linked/main" branch --show-current)" = main ] && + grep -qx "main update" "$w/linked/main/README.md"; } || fail "re-run did not switch a link back to its branch and pull it" +git -C "$w/linked/main" switch -q scratch && echo x >>"$w/linked/main/README.md" +{ bash "$w/.delphi/setup.sh" >/dev/null 2>"$t/err" && grep -q "could not switch linked/main back to main" "$t/err" && + [ "$(git -C "$w/linked/main" branch --show-current)" = scratch ] && grep -qx x <(tail -n 1 "$w/linked/main/README.md"); } || + fail "re-run moved a link with uncommitted changes, or failed: $(cat "$t/err")" +git -C "$w/linked/main" checkout -q README.md && git -C "$w/linked/main" switch -q main && git -C "$w/linked/main" branch -q -D scratch [ "$(grep -cx /linked/ "$w/.git/info/exclude")" = 1 ] || fail "/linked/ not excluded exactly once" ! git -C "$w" status --porcelain | grep -q linked || fail "linked/ shows in status" -ok "setup.sh checks out workspaces: read-only at linked/, skips itself and bad entries (old ws/ ones too), refreshes on re-run" +ok "setup.sh clones workspaces: at linked/ on their branch, skips itself and bad entries (old ws/ ones too), switches back and pulls on re-run, keeps local changes" [ "$(git -C "$w" config delphi.$BN.version)" = "$VN" ] || fail "setup.sh did not record the version" old_link() { rm -rf "$w/linked/main" && git -C "$w" worktree add -q --detach "$w/linked/main" origin/main && git -C "$w" config --unset delphi.$BN.version; } old_link && echo x >>"$w/linked/main/README.md" @@ -610,7 +621,20 @@ git -C "$w" config --unset delphi.$BN.version { /bin/bash "$w/.delphi/setup.sh" >"$t/out" 2>&1 && grep -q "migrating $BN to 0.0.1" "$t/out" && [ -d "$w/linked/main/.git" ] && [ -f "$w/linked/main/ci/sync.sh" ] && ! git -C "$w" worktree list | grep -q linked && [ "$(git -C "$w" config delphi.$BN.version)" = "$VN" ]; } || fail "0.0.1 did not replace an old link.sh worktree: $(cat "$t/out")" -ok "setup.sh runs pending migrations (0.0.1: an old link.sh worktree in linked/ becomes a read-only copy) and records the version" +ok "setup.sh runs pending migrations (0.0.1: an old link.sh worktree in linked/ becomes a clone) and records the version" +old_copy() { # : a link as setup.sh made it before 0.9.0: shallow, detached, pushes disabled + rm -rf "$1" && git init -q "$1" && git -C "$1" remote add origin "$t/origin.git" && + git -C "$1" remote set-url --push origin DISABLED-read-only-copy && git -C "$1" fetch -q --depth 1 origin "$2" && + git -C "$1" checkout -q --detach FETCH_HEAD +} +old_copy "$w/linked/main" main && old_copy "$w/linked/$AN" "ws/$AN" && echo x >>"$w/linked/$AN/CLAUDE.md" && git -C "$w" config delphi.$BN.version 0.8.0 +{ /bin/bash "$w/.delphi/setup.sh" >"$t/out" 2>&1 && grep -q "migrating $BN to 0.9.0" "$t/out" && + [ "$(git -C "$w/linked/main" branch --show-current)" = main ] && [ "$(git -C "$w/linked/$AN" branch --show-current)" = "ws/$AN" ] && + [ "$(git -C "$w/linked/main" rev-parse --is-shallow-repository)" = false ] && ! git -C "$w/linked/main" config remote.origin.pushurl >/dev/null && + [ "$(git -C "$w/linked/$AN" rev-parse --abbrev-ref '@{u}')" = "origin/ws/$AN" ] && grep -qx x <(tail -n 1 "$w/linked/$AN/CLAUDE.md"); } || + fail "0.9.0 did not turn old read-only copies into clones on their branch, keeping local changes: $(cat "$t/out")" +git -C "$w/linked/$AN" checkout -q CLAUDE.md +ok "0.9.0 turns old shallow, detached, push-disabled copies in linked/ into full clones on their branch, keeping what's in them" echo "# local" >>"$w/.delphi/switch.sh" { /bin/bash "$w/.delphi/setup.sh" >/dev/null 2>"$t/err" && grep -q "not Delphi $VN as released" "$t/err"; } || fail "setup.sh did not warn about an edited .delphi/" git -C "$w" checkout -q .delphi/switch.sh