From 2fe538a54a9db0dbf903df34d82756ba3a1e3300 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Sep 2026 13:51:39 +0000 Subject: [PATCH 1/4] delphi: create ws/argos-dev from software/application-software/argos/workspaces/argos-dev --- .../skills/code-review/SKILL.md | 0 .../skills/commit/SKILL.md | 0 .../skills/grill-with-docs/SKILL.md | 0 .../skills/implement/SKILL.md | 0 .../skills/new-worktree/SKILL.md | 0 .../new-worktree/scripts/new-worktree.sh | 0 .../skills/open-pr/SKILL.md | 0 .../skills/run-local/SKILL.md | 0 .../skills/to-spec/SKILL.md | 0 .../skills/to-tickets/SKILL.md | 0 .../skills/update-pr/SKILL.md | 0 .../argos-dev/.delphi => .delphi}/setup.sh | 0 .../.gitattributes => .gitattributes | 0 .github/CODEOWNERS | 3 - CLAUDE.md | 116 ++++++--- README.md | 82 ------- ci/check.sh | 54 ----- ci/sync.sh | 95 -------- .../argos-dev/docs => docs}/CONTEXT.md | 0 docs/design.md | 117 --------- docs/goals.md | 30 --- .../argos-dev/.github/workflows/delphi.yml | 40 ---- .../argos/workspaces/argos-dev/CLAUDE.md | 85 ------- templates/workspace/.delphi/setup.sh | 30 --- templates/workspace/.gitattributes | 2 - .../workspace/.github/workflows/delphi.yml | 40 ---- templates/workspace/CLAUDE.md | 15 -- templates/workspace/workspace.yml | 3 - tests/e2e.sh | 223 ------------------ tools/new-workspace.sh | 34 --- .../argos-dev/workspace.yml => workspace.yml | 0 31 files changed, 85 insertions(+), 884 deletions(-) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/code-review/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/commit/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/grill-with-docs/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/implement/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/new-worktree/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/new-worktree/scripts/new-worktree.sh (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/open-pr/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/run-local/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/to-spec/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/to-tickets/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.claude => .claude}/skills/update-pr/SKILL.md (100%) rename {software/application-software/argos/workspaces/argos-dev/.delphi => .delphi}/setup.sh (100%) rename software/application-software/argos/workspaces/argos-dev/.gitattributes => .gitattributes (100%) delete mode 100644 .github/CODEOWNERS delete mode 100644 README.md delete mode 100755 ci/check.sh delete mode 100755 ci/sync.sh rename {software/application-software/argos/workspaces/argos-dev/docs => docs}/CONTEXT.md (100%) delete mode 100644 docs/design.md delete mode 100644 docs/goals.md delete mode 100644 software/application-software/argos/workspaces/argos-dev/.github/workflows/delphi.yml delete mode 100644 software/application-software/argos/workspaces/argos-dev/CLAUDE.md delete mode 100755 templates/workspace/.delphi/setup.sh delete mode 100644 templates/workspace/.gitattributes delete mode 100644 templates/workspace/.github/workflows/delphi.yml delete mode 100644 templates/workspace/CLAUDE.md delete mode 100644 templates/workspace/workspace.yml delete mode 100755 tests/e2e.sh delete mode 100755 tools/new-workspace.sh rename software/application-software/argos/workspaces/argos-dev/workspace.yml => workspace.yml (100%) diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/code-review/SKILL.md b/.claude/skills/code-review/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/code-review/SKILL.md rename to .claude/skills/code-review/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/commit/SKILL.md b/.claude/skills/commit/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/commit/SKILL.md rename to .claude/skills/commit/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/grill-with-docs/SKILL.md b/.claude/skills/grill-with-docs/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/grill-with-docs/SKILL.md rename to .claude/skills/grill-with-docs/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/implement/SKILL.md b/.claude/skills/implement/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/implement/SKILL.md rename to .claude/skills/implement/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/new-worktree/SKILL.md b/.claude/skills/new-worktree/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/new-worktree/SKILL.md rename to .claude/skills/new-worktree/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/new-worktree/scripts/new-worktree.sh b/.claude/skills/new-worktree/scripts/new-worktree.sh similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/new-worktree/scripts/new-worktree.sh rename to .claude/skills/new-worktree/scripts/new-worktree.sh diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/open-pr/SKILL.md b/.claude/skills/open-pr/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/open-pr/SKILL.md rename to .claude/skills/open-pr/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/run-local/SKILL.md b/.claude/skills/run-local/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/run-local/SKILL.md rename to .claude/skills/run-local/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/to-spec/SKILL.md b/.claude/skills/to-spec/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/to-spec/SKILL.md rename to .claude/skills/to-spec/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/to-tickets/SKILL.md b/.claude/skills/to-tickets/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/to-tickets/SKILL.md rename to .claude/skills/to-tickets/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.claude/skills/update-pr/SKILL.md b/.claude/skills/update-pr/SKILL.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.claude/skills/update-pr/SKILL.md rename to .claude/skills/update-pr/SKILL.md diff --git a/software/application-software/argos/workspaces/argos-dev/.delphi/setup.sh b/.delphi/setup.sh similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.delphi/setup.sh rename to .delphi/setup.sh diff --git a/software/application-software/argos/workspaces/argos-dev/.gitattributes b/.gitattributes similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/.gitattributes rename to .gitattributes diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index fa7bd15..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Reviewers for each org folder under software/. -# TODO: replace the placeholder with the real Argos owners (GitHub users or @org/team). -software/application-software/argos/ @argos-owners-placeholder diff --git a/CLAUDE.md b/CLAUDE.md index 2646573..7444991 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,31 +1,85 @@ -# Delphi — developing it - -Delphi stores NER's AI-harness context. Each workspace is a folder `software/**/workspaces//` -on `main`; branch `ws/` has that folder as its repo root. `ci/sync.sh` keeps them in sync with -subtree merges: **refresh** (main → `ws/`) and **propose** (`ws/` → PR to main via -`propose/`). There is no CLI, just a few shell scripts run by CI. `docs/design.md` is the -source of truth; `docs/goals.md` lists what any change must keep. - -## Layout - -- `ci/sync.sh [refresh|propose] []`: refresh and/or propose each workspace (both by default). - Runs on pushes to main and `ws/**`. -- `ci/check.sh []`: validates the repo. Runs on PRs to main and on each proposal. -- `tools/new-workspace.sh`: new workspace folder from `templates/workspace/` as a PR. -- `templates/workspace/`: `workspace.yml`, `CLAUDE.md` (`{{name}}`, `{{folder}}`), and the managed - files every workspace carries unchanged: `.delphi/setup.sh`, `.github/workflows/delphi.yml`. -- `software/…/workspaces//`: the workspaces. Org structure is plain directories. -- `.github/workflows/delphi.yml` (identical to the template's copy), `.github/CODEOWNERS`. -- `tests/e2e.sh`: end-to-end test in a throwaway sandbox (bare origin, stub `gh` logging to `gh.log`). - -## Rules - -- Keep it small: fewest lines that implement the design; a short header comment per script. - Prefer deleting to adapting. Runtime tools: bash, git, and `gh` (only to open or update PRs). -- Every script: `#!/usr/bin/env bash`, `set -euo pipefail`, `shellcheck`-clean. -- `.delphi/setup.sh` runs on people's machines: bash 3.2 (macOS) and Git Bash safe. No bash-4 - features (associative arrays, `mapfile`, `${x,,}`, `|&`), POSIX awk only. -- Changing a managed file (`setup.sh`, the workflow): update the template, `.github/`, and every - workspace copy in the same PR, or `ci/check.sh` fails. -- `bash tests/e2e.sh` and `ci/check.sh` must pass. Add a test there for every behavior change. -- Never test against real GitHub repos or this checkout's origin; use the sandbox in `tests/e2e.sh`. +# Delphi workspace + +This repo root is branch `ws/argos-dev` of Delphi (or a branch cut from it): the folder `software/application-software/argos/workspaces/argos-dev/` on Delphi's `main`, as its own root. It holds two kinds of git repo, and every change belongs to exactly one: + +| Path | What it is | Changes go | +|---|---|---| +| `CLAUDE.md`, `.claude/`, `docs/`, other files here | The workspace | A branch cut from `ws/argos-dev`, then a PR into `ws/argos-dev` | +| `repos//` | A clone of a code repo listed in `workspace.yml` (git-ignored here) | That repo's git, branches, and PRs, per its conventions | + +- First time in a clone: run `.delphi/setup.sh` to clone the repos into `repos/`. +- Run a repo's git, `gh`, build, and test commands inside it (`cd repos/`), never from the root: here, `git` is the workspace branch. +- Code changes never go in the workspace. Context changes (instructions, skills, docs) never go in a code repo. +- Never push to `ws/argos-dev` or Delphi's `main` directly. After your PR merges into `ws/argos-dev`, CI proposes it to `main` as a PR; CI also refreshes `ws/argos-dev` with `main`'s changes. +- Don't edit `.delphi/setup.sh` or `.github/workflows/delphi.yml`; Delphi manages them. +- Update your branch with `git fetch origin && git merge origin/ws/argos-dev`. + +# NER Software conventions + +## Branch & Commit Conventions + +- Branch from `develop` (not `main`) unless told otherwise. Branch name format: `{issue-number}-{kebab-case-title}` (e.g. `533-csv-upload-download-rules`). +- Commit message format: `#{ticket-number} - {concise description}` (e.g. `#533 - add CSV upload endpoint`). + +## Safety Rules + +- Never modify `.env` or secret files without explicit confirmation. +- Never delete files without explicit confirmation. +- Explain reasoning before making architectural changes. + +# Argos + +Argos is a real-time telemetry platform for Northeastern Electric Racing (NER). Angular 19 frontend (`angular-client/`) and Rust backend (`scylla-server/`), with schema tooling in `charybdis/` and MQTT broker config in `siren-base/`. + +The Argos repo is checked out at `repos/argos/`. Paths below are relative to a checkout of it. The ticket number is the branch's leading number (`533-csv-upload` → `#533`). + +## Worktrees + +- `repos/argos/` is a clean reference to `develop`. Never edit, branch, commit, or run dev servers there. Only fetch, fast-forward `develop`, and manage worktrees from it. +- Every ticket gets its own worktree at `repos/worktrees//`, and every workflow (implement, test, run, commit, PR) runs there. Create or reuse one with the `new-worktree` skill; it handles new and existing branches. +- A new worktree has no `node_modules`: run `npm ci` in its `angular-client/` before testing or running the client. +- After the PR merges, remove it with `git -C repos/argos worktree remove ../worktrees/`. + +## Local Development + +- The backend stack (Postgres, MQTT, Scylla server, Calypso simulator) runs in Docker via the compose files in `compose/`, driven by `argos.sh`. +- Pick the compose profile by what changed: + - Frontend-only changes: `./argos.sh client-dev up` runs everything in Docker, including scylla-server. + - Changes to `scylla-server/`: `./argos.sh scylla-dev up` (everything except scylla-server) plus `cd scylla-server && cargo run` in a separate terminal, so you are not testing a stale binary. +- Frontend client: prefer the `run-local` skill (starts it on the next free port and checks the backend). Direct: `cd angular-client && npm run start` (default port 4200); first compile takes ~10-60s. +- The shell workflow (`argos.sh`, the `run-local` skill, and helpers like `lsof`/`pkill`) assumes a Unix shell. On Windows, run everything from WSL or Git Bash, not `cmd`/PowerShell. + +## Testing + +- Frontend: `cd angular-client && ng test` (Karma/Jasmine). +- Backend: `cd scylla-server && cargo test`. +- Lint and format (frontend): `npx prettier --check "src/**/*.{ts,html,scss}" && npx ng lint`. +- Build (backend): `cargo build`. + +## Workflow + +Idea to ship: `/grill-with-docs` → `/to-spec` → `/to-tickets` → `/implement` (test-first, then `/code-review` and `/commit`) → `/open-pr`. A trivial one-liner goes straight to `/implement`. + +## PR Convention + +- The `/commit` skill applies the commit message format. +- Open PRs against `develop` as drafts. The `/open-pr` skill runs the pre-PR checks (lint, conflict check), pushes, and opens the draft; `/update-pr` refreshes the description. +- Keep PR descriptions tight: at most three backtick usages in the body, and never commit screenshots (drag-drop them into the PR via the GitHub web UI). + +## Screenshots + +Save all Playwright screenshots under `pictures//` at the repo root, using kebab-case descriptive filenames. The `pictures/` folder is git-ignored, so screenshots are never committed; drag-drop them into the PR via the GitHub web UI instead. + +## Code Conventions + +Frontend and backend conventions live alongside their code and auto-load when editing there: +- Angular / TypeScript: see `angular-client/CLAUDE.md`. +- Rust / Axum: see `scylla-server/CLAUDE.md`. + +## Issue tracker + +Issues live in GitHub Issues on `Northeastern-Electric-Racing/Argos` via the `gh` CLI. See `docs/agents/issue-tracker.md` for title, label, and assignment conventions, and `docs/agents/triage-labels.md` for labels. Specs and tickets avoid file paths and code snippets; they go stale. + +## Domain docs + +The glossary and ADRs are workspace docs, not files in `repos/argos/`: `docs/CONTEXT.md` and `docs/adr/` at the workspace root. Edit them there and they're proposed back to Delphi. Read `docs/CONTEXT.md` and the relevant ADRs before exploring, use the glossary's terms, and flag any conflict with an ADR. ADR filenames follow `repos/argos/docs/agents/domain.md` (`--.md`). diff --git a/README.md b/README.md deleted file mode 100644 index a87ae5a..0000000 --- a/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Delphi - -NER's AI-harness context (instructions, skills, docs, settings), organized by the org chart under -`software/`. A **workspace** is a folder `software/**/workspaces//` with a `workspace.yml`. -For each one, CI keeps a branch **`ws/`** whose repo root *is* that folder, so people and -agents work on it with plain git. Design: `docs/design.md`. Goals: `docs/goals.md`. - -## How it works - -``` - ┌───────────────────────────────┐ - │ main │ - │ software/…/workspaces// │ - └───────────────────────────────┘ - │ ▲ - │ refresh │ merge PR - │ (CI) │ (after check.sh) - ▼ │ - ┌──────────────────┐ ┌──────────────────┐ - │ ws/ │ │ propose/ │ - │ root = workspace │─►│ PR to main │ - └──────────────────┘ └──────────────────┘ - │ ▲ propose (CI) - │ branch │ merge PR - ▼ │ - ┌──────────────────┐ - │ your branch │ - │ edit · commit │ - └──────────────────┘ - - refresh: main → ws/ (folder becomes root) - propose: ws/ → main PR (root goes back under folder) - CI runs both on every push to main or ws/** -``` - -Both directions are subtree merges (`git merge -Xsubtree=`) done by `ci/sync.sh`, so the -histories stay joined and edits on either side meet in normal three-way merges. - -## Using a workspace - -```sh -git clone -b ws/argos-dev https://github.com/Northeastern-Electric-Racing/Delphi.git argos-dev -cd argos-dev && .delphi/setup.sh # clones workspace.yml's repos into repos/ (git-ignored) -git switch -c my-change # edit, commit, push, open a PR into ws/argos-dev -git fetch origin && git merge origin/ws/argos-dev # update your branch any time -``` - -After your PR merges into `ws/`, CI proposes it: a PR `ws/ → main` from -`propose/`. Merge that with a merge commit or squash, never rebase. Never push to `ws/*` or -`main` directly (protect them). Code changes go in `repos/`, 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 -`git merge -Xsubtree= origin/main`, resolve, commit. - -## workspace.yml - -```yaml -harness: claude-code -repos: # cloned into repos/ by .delphi/setup.sh - argos: https://github.com/Northeastern-Electric-Racing/Argos.git -``` - -The workspace's name is its folder name (unique repo-wide). Every other file in the folder is the -workspace's own, at its normal harness path, except two Delphi manages: `.delphi/setup.sh` and -`.github/workflows/delphi.yml` (copies of the template's). - -## New workspace - -`tools/new-workspace.sh ` copies `templates/workspace/` into -`software//workspaces//` and opens a PR to `main`. Once it merges, CI creates -`ws/`. - -## CI (`.github/workflows/delphi.yml`) - -- PRs to `main`: `ci/check.sh` validates the repo. -- Pushes to `main` or `ws/**`: `ci/sync.sh` refreshes and proposes every workspace. - -Repo settings: allow GitHub Actions to create PRs. Don't make `check` a required status check: -PRs opened by CI don't trigger workflows (sync.sh runs `ci/check.sh` itself before proposing). - -Developing Delphi: see `CLAUDE.md`; `bash tests/e2e.sh` runs everything in a local sandbox. diff --git a/ci/check.sh b/ci/check.sh deleted file mode 100755 index 3f2c498..0000000 --- a/ci/check.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# ci/check.sh []: validate Delphi (CI runs it on PRs to main; sync.sh on each proposal). Rules: -# a workspace is software/**/workspaces// with a workspace.yml; names are lowercase letters, -# digits, and '-', unique repo-wide; workspaces never nest; no symlinks under software/; each -# workspace's .delphi/setup.sh and .github/workflows/delphi.yml match templates/workspace/, and the -# template workflow matches .github/workflows/delphi.yml; workspace.yml is `harness: ` plus -# an optional `repos:` map of `: `. Lists every problem; exits 1 if any. -set -euo pipefail -cd "${1:-.}" -problems=0 -bad() { echo "check: $*" >&2 && problems=$((problems + 1)); } -tpl=templates/workspace -managed=".delphi/setup.sh .github/workflows/delphi.yml" - -[ -d software ] || bad "software/ is missing" -cmp -s .github/workflows/delphi.yml $tpl/.github/workflows/delphi.yml || - bad "$tpl/.github/workflows/delphi.yml: differs from .github/workflows/delphi.yml" -while IFS= read -r f; do bad "$f: symlinks are not allowed under software/"; done \ - < <(find software -type l 2>/dev/null) - -folders="" -while IFS= read -r f; do - folder=${f%/workspace.yml} - name=${folder##*/} - case "$(dirname "$folder")" in */workspaces) ;; *) - bad "$f: must be at software/**/workspaces//workspace.yml" && continue ;; - esac - case "$folder" in *[!A-Za-z0-9._/-]*) bad "$folder: path has unsafe characters" && continue ;; esac - case "$name" in -* | *[!a-z0-9-]*) bad "$folder: name must be lowercase letters, digits, and '-'" ;; esac - for other in $folders; do - [ "${other##*/}" != "$name" ] || bad "$folder: name '$name' is also used by $other" - case "$folder/" in "$other"/*) bad "$folder: nested inside workspace $other" ;; esac - case "$other/" in "$folder"/*) bad "$other: nested inside workspace $folder" ;; esac - done - folders="$folders $folder" - for m in $managed; do cmp -s "$tpl/$m" "$folder/$m" || bad "$folder/$m: differs from $tpl/$m"; done - while IFS= read -r msg; do bad "$f: $msg"; done < <(awk ' - /^[[:space:]]*#/ || /^[[:space:]]*$/ { next } - /^harness:/ { v = $0; sub(/^harness:[[:space:]]*/, "", v); sub(/[[:space:]]*#.*$/, "", v) - if (v == "") print "harness is empty"; if (h++) print "duplicate harness"; r = 0; next } - /^repos:[[:space:]]*(#.*)?$/ { r = 1; next } - r && /^[[:space:]]/ { - if ($0 !~ /^[[:space:]]+[A-Za-z0-9._-]+:[[:space:]]+[^[:space:]"#]+[[:space:]]*(#.*)?$/) { - print "bad repos entry (want ` : `): " $0; next } - n = $1; sub(/:$/, "", n) - if (n == "." || n == "..") print "bad repo name: " n - if (seen[n]++) print "duplicate repo: " n - next } - { print "unexpected line: " $0 } - END { if (!h) print "harness is missing" }' "$f") -done < <(find software -name workspace.yml -type f 2>/dev/null | sort) - -[ "$problems" = 0 ] || { echo "check: $problems problem(s)" >&2 && exit 1; } -echo "check: ok" diff --git a/ci/sync.sh b/ci/sync.sh deleted file mode 100755 index 2c24d41..0000000 --- a/ci/sync.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash -# ci/sync.sh [refresh|propose] []: sync each workspace folder software/**/workspaces// on -# origin/main (or just ) with its branch ws/, whose root is the folder. CI runs it with -# no direction (both) on every push to main or ws/**. -# refresh: merge main into ws/ (-Xsubtree=) and push if the tree changed. A missing -# ws/ is created as one commit: tree = the folder, parent = main. -# propose: build propose/ = main + a subtree merge of ws/; if that changes main and -# isn't already on propose/, check it, force-push it, and open or update its PR. -# A conflict is reported with its files and that workspace skipped; exits 1 if any failed. ws/* -# branches without a folder get a notice, never deleted. Merges run in a temporary worktree. -set -euo pipefail -dir=both -case "${1:-}" in refresh | propose) dir=$1 && shift ;; esac -only=${1:-} -case "$only" in *[!a-z0-9-]*) echo "usage: ci/sync.sh [refresh|propose] []" >&2 && exit 2 ;; esac - -git fetch --quiet --prune origin -main=$(git rev-parse origin/main) -wt=$(mktemp -d) -git worktree add --quiet --detach "$wt" "$main" -trap 'git worktree remove --force "$wt"' EXIT -failed=0 names=" " -tree() { git -C "$wt" rev-parse --quiet --verify "$1^{tree}" || true; } # empty if is missing - -conflict() { # : report the conflicted files, abort the merge, skip this workspace - echo "$1 Files:" >&2 - git -C "$wt" diff --name-only --diff-filter=U | sed 's/^/ /' >&2 - git -C "$wt" merge --abort 2>/dev/null || true # nothing to abort if the merge never started - failed=1 ok=0 -} - -refresh() { # : leave the worktree at ws/ with main merged in - local ws=refs/remotes/origin/ws/$1 - if [ -z "$(tree "$ws")" ]; then - git -C "$wt" checkout --quiet --detach \ - "$(git commit-tree -p "$main" -m "delphi: create ws/$1 from $2" "$main:$2")" - git -C "$wt" push --quiet origin "HEAD:refs/heads/ws/$1" - echo "ws/$1: created from $2" - return - fi - git -C "$wt" checkout --quiet --detach "$ws" - # --no-ff: once ws commits are in main, a fast-forward would put main's whole tree on ws/. - if ! git -C "$wt" merge --quiet --no-ff -Xsubtree="$2" -m "delphi: refresh ws/$1 from main" "$main" >/dev/null; then - conflict "ws/$1: CONFLICT refreshing from main; merge main into ws/$1 in a PR (see README)." - elif [ "$(tree HEAD)" != "$(tree "$ws")" ]; then - git -C "$wt" push --quiet origin "HEAD:refs/heads/ws/$1" - echo "ws/$1: refreshed" - fi -} - -propose() { # : PR the worktree's ws/ into main if it changes anything - local ws title="ws/$1 → main" body pr - ws=$(git -C "$wt" rev-parse HEAD) - git -C "$wt" checkout --quiet --detach "$main" - if ! git -C "$wt" merge --quiet --no-ff -Xsubtree="$2" -m "delphi: propose ws/$1 to main" "$ws" >/dev/null; then - conflict "ws/$1: CONFLICT proposing to main; merge main into ws/$1 in a PR (see README)." - return - fi - case "$(tree HEAD)" in "$(tree "$main")" | "$(tree "refs/remotes/origin/propose/$1")") return ;; esac - "$wt/ci/check.sh" "$wt" || { echo "ws/$1: check failed; not proposed" >&2 && failed=1 && return; } - git -C "$wt" push --quiet --force origin "HEAD:refs/heads/propose/$1" - body="Proposes \`ws/$1\` into \`$2/\` (opened by ci/sync.sh; see README). - -Changed files: -$(git -C "$wt" diff --name-only "$main" HEAD | sed 's/^/- /') - -Workspace commits: -$(git log --no-merges --invert-grep --grep='^delphi: ' --format='- %s (%an)' "$main..$ws")" - pr=$(gh pr list --base main --head "propose/$1" --state open --json number --jq '.[0].number // empty') - if [ -n "$pr" ]; then - gh pr edit "$pr" --title "$title" --body "$body" >/dev/null - else - gh pr create --base main --head "propose/$1" --title "$title" --body "$body" >/dev/null - fi - echo "ws/$1: proposed (PR from propose/$1)" -} - -for folder in $(git ls-tree -r --name-only "$main" -- software | - sed -nE 's#^(software/(.+/)?workspaces/[^/]+)/workspace\.yml$#\1#p'); do - name=${folder##*/} ok=1 - names="$names$name " - [ -z "$only" ] || [ "$only" = "$name" ] || continue - if [ "$dir" != propose ]; then - refresh "$name" "$folder" - elif [ -n "$(tree "refs/remotes/origin/ws/$name")" ]; then - git -C "$wt" checkout --quiet --detach "refs/remotes/origin/ws/$name" # propose ws/ as is - else ok=0; fi - if [ "$ok" = 1 ] && [ "$dir" != refresh ]; then propose "$name" "$folder"; fi -done - -[ -z "$only" ] || case "$names" in *" $only "*) ;; *) echo "no workspace '$only' on main" >&2 && exit 1 ;; esac -for b in $(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ws/); do - case "$names" in *" ${b#ws/} "*) ;; *) echo "notice: $b has no workspace folder on main (kept)" ;; esac -done -exit "$failed" diff --git a/software/application-software/argos/workspaces/argos-dev/docs/CONTEXT.md b/docs/CONTEXT.md similarity index 100% rename from software/application-software/argos/workspaces/argos-dev/docs/CONTEXT.md rename to docs/CONTEXT.md diff --git a/docs/design.md b/docs/design.md deleted file mode 100644 index 617b962..0000000 --- a/docs/design.md +++ /dev/null @@ -1,117 +0,0 @@ -# Delphi — Design - -Goals: `docs/goals.md`. No CLI: plain git plus a few shell scripts run by GitHub Actions. - -## 1. Model - -`main` holds every workspace as a folder, `software/**/workspaces//`, with its files at their -normal harness paths (`CLAUDE.md`, `.claude/…`, `docs/…`). For each workspace, branch -**`ws/`** has **that folder as its repo root**. Two directions keep them in sync, both subtree -merges (`git merge -Xsubtree=`), so the histories stay joined and edits on either side meet -in normal three-way merges. Nothing is shared or generated between workspaces. - -``` - ┌───────────────────────────────┐ - │ main │ - │ software/…/workspaces// │ - └───────────────────────────────┘ - │ ▲ - │ refresh │ merge PR - │ (CI) │ (after check.sh) - ▼ │ - ┌──────────────────┐ ┌──────────────────┐ - │ ws/ │ │ propose/ │ - │ root = workspace │─►│ PR to main │ - └──────────────────┘ └──────────────────┘ - │ ▲ propose (CI) - │ branch │ merge PR - ▼ │ - ┌──────────────────┐ - │ your branch │ - │ edit · commit │ - └──────────────────┘ - - refresh: main → ws/ (folder becomes root) - propose: ws/ → main PR (root goes back under folder) - CI runs both on every push to main or ws/** -``` - -## 2. Repository (main) - -``` -software//…/workspaces// workspace.yml CLAUDE.md .claude/… docs/… - .delphi/setup.sh .github/workflows/delphi.yml .gitattributes -ci/sync.sh ci/check.sh CI scripts -tools/new-workspace.sh new workspace as a PR -templates/workspace/ what new-workspace copies -.github/workflows/delphi.yml CI -tests/e2e.sh sandbox test of all of the above -``` - -Org folders under `software/` are plain directories. `workspace.yml` is tiny YAML: `harness: -` and an optional `repos:` map of `: `. The name is the folder name: -lowercase letters, digits, `-`; unique repo-wide. Workspaces never nest; no symlinks under -`software/`. Each workspace's `.delphi/setup.sh` and `.github/workflows/delphi.yml` equal the -template's, and the template's workflow equals main's. `ci/check.sh` enforces all of this and lists -every problem. `.gitattributes` (`eol=lf`) keeps `setup.sh` runnable in Git Bash clones. -`.github/CODEOWNERS` assigns reviewers per org folder. - -## 3. Sync (`ci/sync.sh [refresh|propose] []`; no direction = both) - -For every workspace on `origin/main` (or just ``): - -1. **Refresh** (main → `ws/`). If `ws/` is missing, create it as one commit - (`git commit-tree
: -p
`, "delphi: create ws/ from "): root = - folder, parent = main, so no unrelated histories. Otherwise merge `origin/main` into `ws/` - with `--no-ff -Xsubtree=` and push if the tree changed. (`--no-ff` matters: once ws - commits are in main, a fast-forward would put main's whole tree on the ws branch.) -2. **Propose** (`ws/` → PR to main). Build `propose/` = `origin/main` + - `git merge --no-ff -Xsubtree= ws/`. Stop if its tree equals main's or the current - `propose/`'s (nothing new). Otherwise run `ci/check.sh` on it, force-push it, and create or - update the PR `ws/ → main` with `gh` (body: changed files, ws commit subjects and authors). - -A conflict or failed check is reported (conflicts with their files); that workspace is skipped, the -others continue, and the script exits 1. A `ws/*` branch without a folder on main gets a notice and -is never deleted. Merges happen in a temporary worktree, so the caller's checkout is untouched. - -## 4. Working on a workspace - -1. Clone or check out `ws/` (it is the workspace root); run `.delphi/setup.sh` once. -2. Branch, commit, push, open a PR into `ws/`. Update with `git merge origin/ws/`. -3. After it merges, CI proposes it; merge that PR with a merge commit or squash, never rebase - (rebasing replays folder-rooted commits onto main). CI then refreshes `ws/`. -4. On a sync conflict: on a branch cut from `ws/`, `git merge -Xsubtree= - origin/main`, resolve, and PR it into `ws/`. - -Nobody pushes to `ws/*` or `main` directly (branch protection; CI's token is the exception). - -## 5. `.delphi/setup.sh` (in every workspace) - -Reads `repos:` from `workspace.yml`, clones each into `repos/` unless present, and adds -`/repos/` to the clone's `.git/info/exclude` once. Nothing else. Must run on macOS `/bin/bash` 3.2 -and Git Bash: no bash-4 features, POSIX awk only. - -## 6. New workspaces (`tools/new-workspace.sh `) - -Validates the name (format, not on main, no leftover `ws/`), copies `templates/workspace/` -into `software//workspaces//` (filling `{{name}}`/`{{folder}}` in `CLAUDE.md`) in a -temporary worktree, runs `ci/check.sh`, pushes branch `new-workspace/`, and opens a PR. Sync -creates `ws/` once it merges. - -## 7. CI (`.github/workflows/delphi.yml`) - -- `check`: PRs to main, read-only token, runs `ci/check.sh`. -- `sync`: pushes to main or `ws/**`; checks out main (scripts never come from a ws branch) with full - history, sets the bot identity, runs `ci/sync.sh` (contents + pull-requests write; one run at a - time). - -GitHub runs a push's workflow from the pushed commit, so the same file sits at the root of every -`ws/` (via the workspace folder). Pushes and PRs made with `GITHUB_TOKEN` trigger no -workflows: no loops, and CI-opened PRs don't run `check`, which is why sync runs `ci/check.sh` -itself (so `check` can't be a required status check). - -## 8. Testing - -`tests/e2e.sh` builds a sandbox (temp dir, bare origin, stub `gh` logging to `gh.log`, two -workspaces in different org folders) and drives the scripts as CI and people would. Never test -against GitHub. diff --git a/docs/goals.md b/docs/goals.md deleted file mode 100644 index bfce84a..0000000 --- a/docs/goals.md +++ /dev/null @@ -1,30 +0,0 @@ -# Delphi — Goals - -What Delphi must achieve, not how. Any change must keep these. Details: `docs/design.md`. - -## Purpose - -Store NER's AI context (instructions, skills, docs, settings) once, organized by the org chart. Let -anyone, person or agent, work on a workspace with plain git and send improvements back. - -## Goals - -- **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` 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 - listed and resolved by a person in a normal PR; other workspaces keep syncing. -- **G5. Keep Delphi valid.** `ci/check.sh` catches bad manifests and structure before merge. New - workspaces arrive through PRs too. -- **G6. Simple.** No CLI to install: git, `gh`, and a few short shell scripts. - -## Invariants - -- **I1.** Changes reach `main` and `ws/*` only through PRs (CI's refreshes excepted). -- **I2.** `ws/` history is joined to `main`; no unrelated histories, no force-pushes to `ws/*`. -- **I3.** Code repos in `repos/` never appear as workspace changes. -- **I4.** `setup.sh` works on macOS bash 3.2 and Git Bash. -- **I5.** Everything is testable end to end without GitHub (`tests/e2e.sh`). diff --git a/software/application-software/argos/workspaces/argos-dev/.github/workflows/delphi.yml b/software/application-software/argos/workspaces/argos-dev/.github/workflows/delphi.yml deleted file mode 100644 index 09d7834..0000000 --- a/software/application-software/argos/workspaces/argos-dev/.github/workflows/delphi.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Delphi CI (docs/design.md). This file is at main's root and, identically, at the root of every -# ws/ (GitHub runs a push's workflow from the pushed commit, so ws pushes need their own copy). -# PRs to main: ci/check.sh. Pushes to main or ws/**: ci/sync.sh (refresh + propose), always run from -# main. Pushes and PRs made with GITHUB_TOKEN trigger no workflows: no loops, and CI-opened PRs -# don't run `check` (sync.sh runs it itself). -name: delphi -on: - pull_request: - branches: [main] - push: - branches: [main, 'ws/**'] -permissions: - contents: read -jobs: - check: - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - run: ci/check.sh - sync: - if: github.event_name == 'push' - runs-on: ubuntu-latest - concurrency: delphi-sync - permissions: - contents: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - with: - ref: main - fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - ci/sync.sh diff --git a/software/application-software/argos/workspaces/argos-dev/CLAUDE.md b/software/application-software/argos/workspaces/argos-dev/CLAUDE.md deleted file mode 100644 index 7444991..0000000 --- a/software/application-software/argos/workspaces/argos-dev/CLAUDE.md +++ /dev/null @@ -1,85 +0,0 @@ -# Delphi workspace - -This repo root is branch `ws/argos-dev` of Delphi (or a branch cut from it): the folder `software/application-software/argos/workspaces/argos-dev/` on Delphi's `main`, as its own root. It holds two kinds of git repo, and every change belongs to exactly one: - -| Path | What it is | Changes go | -|---|---|---| -| `CLAUDE.md`, `.claude/`, `docs/`, other files here | The workspace | A branch cut from `ws/argos-dev`, then a PR into `ws/argos-dev` | -| `repos//` | A clone of a code repo listed in `workspace.yml` (git-ignored here) | That repo's git, branches, and PRs, per its conventions | - -- First time in a clone: run `.delphi/setup.sh` to clone the repos into `repos/`. -- Run a repo's git, `gh`, build, and test commands inside it (`cd repos/`), never from the root: here, `git` is the workspace branch. -- Code changes never go in the workspace. Context changes (instructions, skills, docs) never go in a code repo. -- Never push to `ws/argos-dev` or Delphi's `main` directly. After your PR merges into `ws/argos-dev`, CI proposes it to `main` as a PR; CI also refreshes `ws/argos-dev` with `main`'s changes. -- Don't edit `.delphi/setup.sh` or `.github/workflows/delphi.yml`; Delphi manages them. -- Update your branch with `git fetch origin && git merge origin/ws/argos-dev`. - -# NER Software conventions - -## Branch & Commit Conventions - -- Branch from `develop` (not `main`) unless told otherwise. Branch name format: `{issue-number}-{kebab-case-title}` (e.g. `533-csv-upload-download-rules`). -- Commit message format: `#{ticket-number} - {concise description}` (e.g. `#533 - add CSV upload endpoint`). - -## Safety Rules - -- Never modify `.env` or secret files without explicit confirmation. -- Never delete files without explicit confirmation. -- Explain reasoning before making architectural changes. - -# Argos - -Argos is a real-time telemetry platform for Northeastern Electric Racing (NER). Angular 19 frontend (`angular-client/`) and Rust backend (`scylla-server/`), with schema tooling in `charybdis/` and MQTT broker config in `siren-base/`. - -The Argos repo is checked out at `repos/argos/`. Paths below are relative to a checkout of it. The ticket number is the branch's leading number (`533-csv-upload` → `#533`). - -## Worktrees - -- `repos/argos/` is a clean reference to `develop`. Never edit, branch, commit, or run dev servers there. Only fetch, fast-forward `develop`, and manage worktrees from it. -- Every ticket gets its own worktree at `repos/worktrees//`, and every workflow (implement, test, run, commit, PR) runs there. Create or reuse one with the `new-worktree` skill; it handles new and existing branches. -- A new worktree has no `node_modules`: run `npm ci` in its `angular-client/` before testing or running the client. -- After the PR merges, remove it with `git -C repos/argos worktree remove ../worktrees/`. - -## Local Development - -- The backend stack (Postgres, MQTT, Scylla server, Calypso simulator) runs in Docker via the compose files in `compose/`, driven by `argos.sh`. -- Pick the compose profile by what changed: - - Frontend-only changes: `./argos.sh client-dev up` runs everything in Docker, including scylla-server. - - Changes to `scylla-server/`: `./argos.sh scylla-dev up` (everything except scylla-server) plus `cd scylla-server && cargo run` in a separate terminal, so you are not testing a stale binary. -- Frontend client: prefer the `run-local` skill (starts it on the next free port and checks the backend). Direct: `cd angular-client && npm run start` (default port 4200); first compile takes ~10-60s. -- The shell workflow (`argos.sh`, the `run-local` skill, and helpers like `lsof`/`pkill`) assumes a Unix shell. On Windows, run everything from WSL or Git Bash, not `cmd`/PowerShell. - -## Testing - -- Frontend: `cd angular-client && ng test` (Karma/Jasmine). -- Backend: `cd scylla-server && cargo test`. -- Lint and format (frontend): `npx prettier --check "src/**/*.{ts,html,scss}" && npx ng lint`. -- Build (backend): `cargo build`. - -## Workflow - -Idea to ship: `/grill-with-docs` → `/to-spec` → `/to-tickets` → `/implement` (test-first, then `/code-review` and `/commit`) → `/open-pr`. A trivial one-liner goes straight to `/implement`. - -## PR Convention - -- The `/commit` skill applies the commit message format. -- Open PRs against `develop` as drafts. The `/open-pr` skill runs the pre-PR checks (lint, conflict check), pushes, and opens the draft; `/update-pr` refreshes the description. -- Keep PR descriptions tight: at most three backtick usages in the body, and never commit screenshots (drag-drop them into the PR via the GitHub web UI). - -## Screenshots - -Save all Playwright screenshots under `pictures//` at the repo root, using kebab-case descriptive filenames. The `pictures/` folder is git-ignored, so screenshots are never committed; drag-drop them into the PR via the GitHub web UI instead. - -## Code Conventions - -Frontend and backend conventions live alongside their code and auto-load when editing there: -- Angular / TypeScript: see `angular-client/CLAUDE.md`. -- Rust / Axum: see `scylla-server/CLAUDE.md`. - -## Issue tracker - -Issues live in GitHub Issues on `Northeastern-Electric-Racing/Argos` via the `gh` CLI. See `docs/agents/issue-tracker.md` for title, label, and assignment conventions, and `docs/agents/triage-labels.md` for labels. Specs and tickets avoid file paths and code snippets; they go stale. - -## Domain docs - -The glossary and ADRs are workspace docs, not files in `repos/argos/`: `docs/CONTEXT.md` and `docs/adr/` at the workspace root. Edit them there and they're proposed back to Delphi. Read `docs/CONTEXT.md` and the relevant ADRs before exploring, use the glossary's terms, and flag any conflict with an ADR. ADR filenames follow `repos/argos/docs/agents/domain.md` (`--.md`). diff --git a/templates/workspace/.delphi/setup.sh b/templates/workspace/.delphi/setup.sh deleted file mode 100755 index 22ceb98..0000000 --- a/templates/workspace/.delphi/setup.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -# .delphi/setup.sh: run in a checkout of ws/ (the workspace root). Clones each repo listed -# under `repos:` in workspace.yml into repos/ (skipping ones already there) and adds /repos/ -# to this clone's .git/info/exclude. Safe to re-run. Keep it bash 3.2 (macOS) and Git Bash safe: -# no bash-4 features, POSIX awk only. -set -euo pipefail -cd "$(dirname "$0")/.." - -list=$(awk ' - /^[[:space:]]*#/ || /^[[:space:]]*$/ { next } - /^repos:/ { r = 1; next } - /^[^[:space:]]/ { r = 0 } - r { sub(/^[[:space:]]+/, ""); sub(/[[:space:]]+#.*$/, ""); n = index($0, ":") - if (n) print substr($0, 1, n - 1), substr($0, n + 1) }' workspace.yml) - -while read -r name url; do - [ -n "$name" ] || continue - case "$name" in . | .. | *[!A-Za-z0-9._-]*) echo "setup: skipping unsafe repo name '$name'" >&2 && continue ;; esac - if [ -e "repos/$name" ]; then - echo "repos/$name: already present" - else - git clone "$url" "repos/$name" /dev/null || echo /repos/ >>"$exclude" diff --git a/templates/workspace/.gitattributes b/templates/workspace/.gitattributes deleted file mode 100644 index 4965641..0000000 --- a/templates/workspace/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# LF everywhere, so .delphi/setup.sh runs in Git Bash even with core.autocrlf=true -* text=auto eol=lf diff --git a/templates/workspace/.github/workflows/delphi.yml b/templates/workspace/.github/workflows/delphi.yml deleted file mode 100644 index 09d7834..0000000 --- a/templates/workspace/.github/workflows/delphi.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Delphi CI (docs/design.md). This file is at main's root and, identically, at the root of every -# ws/ (GitHub runs a push's workflow from the pushed commit, so ws pushes need their own copy). -# PRs to main: ci/check.sh. Pushes to main or ws/**: ci/sync.sh (refresh + propose), always run from -# main. Pushes and PRs made with GITHUB_TOKEN trigger no workflows: no loops, and CI-opened PRs -# don't run `check` (sync.sh runs it itself). -name: delphi -on: - pull_request: - branches: [main] - push: - branches: [main, 'ws/**'] -permissions: - contents: read -jobs: - check: - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - run: ci/check.sh - sync: - if: github.event_name == 'push' - runs-on: ubuntu-latest - concurrency: delphi-sync - permissions: - contents: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - with: - ref: main - fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - ci/sync.sh diff --git a/templates/workspace/CLAUDE.md b/templates/workspace/CLAUDE.md deleted file mode 100644 index ad7a097..0000000 --- a/templates/workspace/CLAUDE.md +++ /dev/null @@ -1,15 +0,0 @@ -# Delphi workspace - -This repo root is branch `ws/{{name}}` of Delphi (or a branch cut from it): the folder `{{folder}}/` on Delphi's `main`, as its own root. It holds two kinds of git repo, and every change belongs to exactly one: - -| Path | What it is | Changes go | -|---|---|---| -| `CLAUDE.md`, `.claude/`, `docs/`, other files here | The workspace | A branch cut from `ws/{{name}}`, then a PR into `ws/{{name}}` | -| `repos//` | A clone of a code repo listed in `workspace.yml` (git-ignored here) | That repo's git, branches, and PRs, per its conventions | - -- First time in a clone: run `.delphi/setup.sh` to clone the repos into `repos/`. -- Run a repo's git, `gh`, build, and test commands inside it (`cd repos/`), never from the root: here, `git` is the workspace branch. -- Code changes never go in the workspace. Context changes (instructions, skills, docs) never go in a code repo. -- Never push to `ws/{{name}}` or Delphi's `main` directly. After your PR merges into `ws/{{name}}`, CI proposes it to `main` as a PR; CI also refreshes `ws/{{name}}` with `main`'s changes. -- Don't edit `.delphi/setup.sh` or `.github/workflows/delphi.yml`; Delphi manages them. -- Update your branch with `git fetch origin && git merge origin/ws/{{name}}`. diff --git a/templates/workspace/workspace.yml b/templates/workspace/workspace.yml deleted file mode 100644 index 2194b35..0000000 --- a/templates/workspace/workspace.yml +++ /dev/null @@ -1,3 +0,0 @@ -harness: claude-code -repos: # cloned into repos/ by .delphi/setup.sh - # name: https://github.com/Northeastern-Electric-Racing/.git diff --git a/tests/e2e.sh b/tests/e2e.sh deleted file mode 100755 index 33962a7..0000000 --- a/tests/e2e.sh +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env bash -# tests/e2e.sh: end-to-end test of Delphi's scripts in a throwaway sandbox: a temp dir with a bare -# origin, a stub `gh` on PATH that logs its calls to gh.log, and a copy of this repo's scripts plus -# two workspaces in different org folders. Runs ci/sync.sh and friends as CI and people would. -# Never touches GitHub or this checkout's origin. Run: bash tests/e2e.sh -set -euo pipefail -src=$(cd "$(dirname "$0")/.." && pwd) -t=$(mktemp -d) -trap 'rm -rf "$t"' EXIT -export HOME=$t GIT_CONFIG_NOSYSTEM=1 GIT_AUTHOR_NAME=tester GIT_AUTHOR_EMAIL=t@example.com -export GIT_COMMITTER_NAME=tester GIT_COMMITTER_EMAIL=t@example.com -git config --global init.defaultBranch main -git config --global advice.detachedHead false -mkdir "$t/bin" -cat >"$t/bin/gh" <>"$t/gh.log" -EOF -chmod +x "$t/bin/gh" -export PATH=$t/bin:$PATH -touch "$t/gh.log" - -pass=0 -ok() { echo "ok - $*" && pass=$((pass + 1)); } -fail() { echo "FAIL - $*" >&2 && exit 1; } -A=software/application-software/argos/workspaces/argos-dev -B=software/electrical/workspaces/bms-dev - -# --- sandbox: seed repo -> bare origin; "ci" is the clone CI runs in, "dev" a person's clone -git init -q "$t/seed" -cp -R "$src/ci" "$src/tools" "$src/templates" "$src/.github" "$src/README.md" "$t/seed/" -mkdir -p "$t/seed/$A" "$t/seed/$B" -cp -R "$src/$A/." "$t/seed/$A/" -cp -R "$src/templates/workspace/." "$t/seed/$B/" -git -C "$t/seed" add -A && git -C "$t/seed" commit -qm init -git clone -q --bare "$t/seed" "$t/origin.git" -git clone -q "$t/origin.git" "$t/ci" -git clone -q "$t/origin.git" "$t/dev" -sync() { (cd "$t/ci" && ci/sync.sh "$@"); } -o() { git -C "$t/origin.git" "$@"; } -# commit_on : a person's PR into , merged (edit on a branch, merge --no-ff) -commit_on() { - (cd "$t/dev" && git fetch -q && git checkout -q -B "edit" "origin/$1" && printf '%s\n' "$3" >>"$2" && - git commit -qam "edit $2 on $1" && git checkout -q -B "tmp-$1" "origin/$1" && - git merge -q --no-ff --no-edit edit && git push -q origin "HEAD:$1") -} - -# --- 1. first sync creates ws branches rooted at the folder, joined to main -sync >"$t/out" -main=$(o rev-parse main) -for w in "$A" "$B"; do - n=${w##*/} - [ "$(o rev-parse "ws/$n^{tree}")" = "$(o rev-parse "main:$w")" ] || fail "ws/$n root != $w" - [ "$(o rev-parse "ws/$n^")" = "$main" ] || fail "ws/$n parent is not main" - [ "$(o log -1 --format=%s "ws/$n")" = "delphi: create ws/$n from $w" ] || fail "ws/$n message" - o cat-file -e "ws/$n:workspace.yml" || fail "ws/$n lacks workspace.yml" - { ! o cat-file -e "ws/$n:README.md" 2>/dev/null && ! o cat-file -e "ws/$n:ci" 2>/dev/null; } || - fail "ws/$n has Delphi root files" - [ "$(o rev-parse "ws/$n:.github/workflows/delphi.yml")" = "$(o rev-parse main:.github/workflows/delphi.yml)" ] || - fail "ws/$n root lacks main's workflow (pushes to ws/$n would trigger nothing)" -done -ok "sync creates ws/ rooted at its folder, parent main, no Delphi root files" -ok "ws/ root carries main's CI workflow, so GitHub runs sync on pushes to ws/**" -[ ! -s "$t/gh.log" ] || fail "gh called on creation" -before=$(o for-each-ref --format='%(objectname)' refs/heads) -sync >/dev/null -[ "$before" = "$(o for-each-ref --format='%(objectname)' refs/heads)" ] || fail "second sync changed refs" -ok "sync is a no-op when nothing changed" - -# --- 2. an edit merged into ws/argos-dev becomes propose/argos-dev and a PR to main -commit_on ws/argos-dev docs/CONTEXT.md "ws edit one" -sync argos-dev >"$t/out" -o show "propose/argos-dev:$A/docs/CONTEXT.md" | grep -qx "ws edit one" || fail "propose/argos-dev lacks the edit" -[ "$(o diff --name-only main propose/argos-dev)" = "$A/docs/CONTEXT.md" ] || fail "propose diff not just the edit" -grep -q "gh pr create --base main --head propose/argos-dev --title ws/argos-dev → main" "$t/gh.log" || - fail "no PR created: $(cat "$t/gh.log")" -grep -q -- "- edit docs/CONTEXT.md on ws/argos-dev (tester)" "$t/gh.log" || fail "PR body lacks ws commit" -ok "ws edit -> propose/argos-dev with the change under the folder, PR opened with gh" -prop=$(o rev-parse propose/argos-dev) -sleep 1 # a re-built merge commit would get a new timestamp, hence a new id -: >"$t/gh.log" -sync argos-dev >/dev/null -{ [ "$(o rev-parse propose/argos-dev)" = "$prop" ] && [ ! -s "$t/gh.log" ]; } || fail "unchanged proposal re-pushed" -ok "an unchanged proposal is not re-pushed and its PR not touched" - -# --- 3. merging propose/argos-dev into main, then sync: clean, ws unchanged, nothing to propose -(cd "$t/dev" && git fetch -q && git checkout -q -B main origin/main && - git merge -q --no-ff --no-edit origin/propose/argos-dev && git push -q origin main) -ws=$(o rev-parse ws/argos-dev) -: >"$t/gh.log" -sync >"$t/out" || fail "sync after merging propose failed" -[ "$(o rev-parse "ws/argos-dev^{tree}")" = "$(o rev-parse "$ws^{tree}")" ] || fail "ws tree changed" -[ ! -s "$t/gh.log" ] || fail "gh called after propose merged: $(cat "$t/gh.log")" -ok "after the propose PR merges into main, sync is clean and opens no PR" - -# --- 4. a main change to another file refreshes into a ws branch with its own edits, then propose -commit_on ws/argos-dev docs/CONTEXT.md "ws edit two" -commit_on main "$A/CLAUDE.md" "main edit" -commit_on main README.md "root edit" -sync >"$t/out" || fail "sync with non-overlapping edits failed" -o show ws/argos-dev:CLAUDE.md | grep -qx "main edit" || fail "main edit not in ws" -o show ws/argos-dev:docs/CONTEXT.md | grep -qx "ws edit two" || fail "ws edit lost" -! o cat-file -e ws/argos-dev:README.md 2>/dev/null || fail "root file leaked into ws" -[ "$(o diff --name-only main propose/argos-dev)" = "$A/docs/CONTEXT.md" ] || fail "propose diff wrong after refresh" -ok "main-side edit refreshes cleanly alongside ws edits; propose carries only the ws edit" -[ "$(o rev-parse "ws/bms-dev^{tree}")" = "$(o rev-parse "main:$B")" ] || fail "bms-dev drifted" -ok "unrelated main changes leave other ws branches' trees alone" - -# --- 5. a conflict on bms-dev is reported; argos-dev still syncs; exit 1 -commit_on ws/bms-dev CLAUDE.md "ws side" -commit_on main "$B/CLAUDE.md" "main side" -commit_on main "$A/CLAUDE.md" "main edit two" -bms=$(o rev-parse ws/bms-dev) -if sync 2>"$t/err" >"$t/out"; then fail "conflicting sync exited 0"; fi -{ grep -q "ws/bms-dev: CONFLICT" "$t/err" && grep -q " CLAUDE.md" "$t/err"; } || fail "conflict not reported: $(cat "$t/err")" -[ "$(o rev-parse ws/bms-dev)" = "$bms" ] || fail "conflicting ws/bms-dev was changed" -o show ws/argos-dev:CLAUDE.md | grep -qx "main edit two" || fail "argos-dev not synced" -ok "conflict: reported with files, exit 1, other workspaces still synced" - -# resolve as documented: merge main into ws/bms-dev on a branch, PR into ws/bms-dev -(cd "$t/dev" && git fetch -q && git checkout -q -B fix origin/ws/bms-dev && - { git merge -q -Xsubtree="$B" origin/main >/dev/null 2>&1 || true; } && - git checkout -q --ours CLAUDE.md && git commit -qam "resolve" && git push -q origin HEAD:ws/bms-dev) -sync >"$t/out" 2>"$t/err" || fail "sync after resolving failed: $(cat "$t/err")" -grep -q "ws/bms-dev: proposed" "$t/out" || fail "resolved ws not proposed" -ok "conflict resolved by merging main into ws in a PR; sync then succeeds" - -# --- 6. removed workspace folder: notice, branch kept -(cd "$t/dev" && git checkout -q -B main origin/main && git rm -rq "$B" && git commit -qm rm && git push -q origin main) -sync >"$t/out" -grep -q "notice: ws/bms-dev has no workspace folder on main" "$t/out" || fail "no removal notice" -o rev-parse -q --verify ws/bms-dev >/dev/null || fail "ws/bms-dev deleted" -ok "removed workspace: notice printed, branch kept" - -# --- 7. check.sh: real layout passes; each bad layout fails -"$src/ci/check.sh" "$src" >/dev/null || fail "check fails on the real repo" -ok "check passes on the real repo" -expect_bad() { #