#!/usr/bin/env bash # # split-s00.sh — one-off: split issue S-00 (#1) into sub-slices S-00-a..e per # CLAUDE.md §13. Creates the five sub-slice issues, then closes #1 with a # comment listing the replacements. # # Idempotent: sub-slices already present (matched by title) are skipped; the # closing comment is posted once (guarded by a marker); #1 is closed only if open. # # Usage: # GITEA_TOKEN= bash tools/split-s00.sh # # Token scopes: write:issue. # set -euo pipefail : "${GITEA_TOKEN:?Set GITEA_TOKEN to a Gitea personal access token (scope: write:issue)}" BASE="https://git.labs.respellion.tech/api/v1" OWNER="eho" REPO="register-referentie" REPO_API="$BASE/repos/$OWNER/$REPO" PARENT=1 # S-00 issue number MARKER="" AUTH=(-H "Authorization: token $GITEA_TOKEN") JSON=(-H "Content-Type: application/json" -H "Accept: application/json") say() { printf '%s\n' "$*"; } code=$(curl -s -o /dev/null -w '%{http_code}' "${AUTH[@]}" "$REPO_API") [ "$code" = "200" ] || { say "ERROR: cannot access $OWNER/$REPO (HTTP $code)."; exit 1; } say "Authenticated against $REPO_API" labels_json=$(curl -s "${AUTH[@]}" "$REPO_API/labels?limit=100") milestones_json=$(curl -s "${AUTH[@]}" "$REPO_API/milestones?state=all&limit=100") existing_issues=$(curl -s "${AUTH[@]}" "$REPO_API/issues?state=all&type=issues&limit=100") MILESTONE="Iteration 0 — Foundations" DOD=$(cat <<'EOF' ## Definition of Done - [ ] A linked Gitea issue exists (this one). - [ ] Failing test written and committed first. - [ ] Implementation makes the test pass. - [ ] Refactor commit follows if structure improved. - [ ] Conventional Commit messages referencing this issue (`refs #NN`). - [ ] All Gitea Actions CI jobs green. - [ ] `docker compose up` from a fresh clone reaches green health checks within 3 minutes. - [ ] Docs touched if behaviour, contracts, or operations changed. - [ ] ADR added in `docs/architecture/` if a non-obvious decision was made. - [ ] This issue closed by the merging PR (`closes #NN`). EOF ) create_issue() { local title="$1" labels_csv="$2" body="$3" if echo "$existing_issues" | jq -e --arg t "$title" 'any(.[]; .title == $t)' >/dev/null; then say " skip $title" return fi local ms_id; ms_id=$(echo "$milestones_json" | jq -r --arg m "$MILESTONE" '.[] | select(.title == $m) | .id') local want label_ids want=$(printf '%s' "$labels_csv" | jq -R 'split(",")') label_ids=$(echo "$labels_json" | jq -c --argjson want "$want" '[ .[] | select(.name as $n | $want | index($n)) | .id ]') local payload payload=$(jq -n --arg t "$title" --arg b "${body}${DOD}" --argjson ms "$ms_id" --argjson ls "$label_ids" \ '{title:$t, body:$b, milestone:$ms, labels:$ls}') curl -s "${AUTH[@]}" "${JSON[@]}" -X POST "$REPO_API/issues" -d "$payload" >/dev/null say " create $title" } say "" say "== Sub-slice issues ==" create_issue "S-00-a · Placeholder BFF + health endpoint" \ "type:slice,area:bff" "$(cat <<'EOF' **Outcome:** A minimal .NET BFF service exposing `GET /health` that returns a green (`Healthy`) status. Runnable with `dotnet run`. **Acceptance:** - A failing xUnit test asserts `GET /health` returns 200 with a `Healthy` payload (red commit first). - Implementation makes it pass; `dotnet run` then `curl /health` returns `Healthy`. - Project follows the §9 layering seed (minimal `Api` project; `Application`/`Domain` added as they earn their place). **Touches:** `services/bff/`, tests. **Out of scope:** Docker, CI, OIDC, any real endpoints. _Split from #1 (S-00) per CLAUDE.md §13._ EOF )" create_issue "S-00-b · Dockerfile + compose skeleton + compose-up smoke" \ "type:slice,area:bff,area:infra" "$(cat <<'EOF' **Outcome:** The BFF is containerized and `infra/docker-compose.yml` brings it up; health goes green within 3 minutes from a fresh clone. **Acceptance:** - `docker build` of the BFF image succeeds. - `docker compose up` brings the BFF to a healthy state; a compose-up smoke script curls `/health` and passes. - README documents the `docker compose up` path. **Touches:** `services/bff/Dockerfile`, `infra/docker-compose.yml`, smoke script. **Out of scope:** other services; CI wiring (S-00-c). _Split from #1 (S-00) per CLAUDE.md §13._ EOF )" create_issue "S-00-c · Gitea Actions CI (lint, build, unit, compose-up smoke)" \ "type:slice,area:infra" "$(cat <<'EOF' **Outcome:** `.gitea/workflows/ci.yaml` runs on PRs and `main` with lint, build, unit tests, and the compose-up smoke test — all green. **Acceptance:** - Pipeline green on the branch. - Jobs: lint, build, unit, compose-up smoke. `uses:` references are absolute URLs pinned to a tag (CLAUDE.md §8.7 / §15). - Self-hosted runner label documented in `docs/runbooks/ci.md`. **Touches:** `.gitea/workflows/ci.yaml`, `docs/runbooks/ci.md`. **Out of scope:** mutation, e2e, container build + push (later slices). _Split from #1 (S-00) per CLAUDE.md §13._ EOF )" create_issue "S-00-d · Contributor workflow: issue/PR templates, git-cliff, CHANGELOG" \ "type:slice,area:docs,area:infra" "$(cat <<'EOF' **Outcome:** Issue templates (`slice`/`bug`/`adr-proposal`), a PR template enforcing the DoD checklist, a `git-cliff` config producing an (empty) `CHANGELOG.md`, and `docs/gitea-workflow.md`. **Acceptance:** - `.gitea/ISSUE_TEMPLATE/{slice,bug,adr-proposal}.md` and `.gitea/PULL_REQUEST_TEMPLATE.md` present; opening a new issue offers the templates. - `cliff.toml` present; `git-cliff` generates `CHANGELOG.md`. - `docs/gitea-workflow.md` documents the issue → milestone → PR flow. **Touches:** `.gitea/ISSUE_TEMPLATE/`, `.gitea/PULL_REQUEST_TEMPLATE.md`, `cliff.toml`, `CHANGELOG.md`, `docs/gitea-workflow.md`. **Out of scope:** app code. _Split from #1 (S-00) per CLAUDE.md §13._ EOF )" create_issue "S-00-e · Docs scaffold: MkDocs + ADR-0001 + README quickstart" \ "type:slice,area:docs" "$(cat <<'EOF' **Outcome:** MkDocs Material builds the `docs/` site; `docs/architecture/adr-0001-loose-coupling.md` exists as the ADR template; README gains a sub-10-minute quickstart. **Acceptance:** - `mkdocs build` succeeds (`mkdocs.yml` + nav). - `adr-0001-loose-coupling.md` present, Nygard template. - A new developer following `README.md` reaches a green local environment in under 10 minutes. **Touches:** `mkdocs.yml`, `docs/` nav, `docs/architecture/adr-0001-loose-coupling.md`, `README.md`. **Out of scope:** Gitea Pages publish workflow (later). _Split from #1 (S-00) per CLAUDE.md §13._ EOF )" # --- Close #1 with a comment listing the replacements ----------------------- say "" say "== Close parent #1 ==" # Resolve the numbers of the sub-slices we now have. all_issues=$(curl -s "${AUTH[@]}" "$REPO_API/issues?state=all&type=issues&limit=100") refs=$(echo "$all_issues" | jq -r ' [ .[] | select(.title | startswith("S-00-")) ] | sort_by(.title) | map("- #\(.number) — \(.title)") | join("\n")') comments=$(curl -s "${AUTH[@]}" "$REPO_API/issues/$PARENT/comments?limit=100") if echo "$comments" | jq -e --arg m "$MARKER" 'any(.[]; .body | contains($m))' >/dev/null; then say " skip split comment (already posted)" else body="$MARKER Split into sub-slices per CLAUDE.md §13. Replacement issues: $refs" curl -s "${AUTH[@]}" "${JSON[@]}" -X POST "$REPO_API/issues/$PARENT/comments" \ -d "$(jq -n --arg b "$body" '{body:$b}')" >/dev/null say " create split comment on #$PARENT" fi state=$(echo "$all_issues" | jq -r --argjson n "$PARENT" '.[] | select(.number == $n) | .state') if [ "$state" = "closed" ]; then say " skip #$PARENT already closed" else curl -s "${AUTH[@]}" "${JSON[@]}" -X PATCH "$REPO_API/issues/$PARENT" \ -d "$(jq -n '{state:"closed"}')" >/dev/null say " closed #$PARENT" fi say "" say "Done."