# Bootstrap Deep Template

> Scaffold instructions for the Deep tier.
> Called from `skills/bootstrap/SKILL.md` Phase 3 when `CONFIRMED_TIER = deep`.

Deep tier is a strict superset of Standard tier. Execute all Standard-tier steps first, then the Deep-specific steps below.

Keep the inherited `BOOTSTRAP_FILES` array. Append each new governance file by
its relative filename when created; do not append existing files or directories.

## Step 1–8: Execute Standard Tier

Read and execute `skills/bootstrap/standard-template.md` Steps 1–8 in full. Do not skip any step.

**Exception to standard-template Step 6 (bootstrap.lock):** Do not write `.orchestrator/bootstrap.lock` yet. The lock is written with `tier: deep` at Deep Step 6.

**Exception to standard-template Step 7 (git commit):** Do not run the git commit in standard-template Step 7. All files (Fast + Standard + Deep) are committed together at Deep Step 7.

---

## VCS Detection

Before executing the Deep-specific steps, resolve the `VCS` value from Session Config:

```bash
VCS=$(grep -m1 "^vcs:" "$REPO_ROOT/CLAUDE.md" 2>/dev/null | awk '{print $2}')
# Expected values: gitlab | github | none
```

If `VCS` is empty or `none`, skip CI pipeline, issue templates, MR/PR template, and branch protection. Log: "VCS not configured — CI and templates skipped."

---

## Step D1: CI Pipeline

When `PATH_TYPE = private`, keep the selected baseline's rendered CI and
`ci.profile` from `private-contract.md`. Respect `ci.required: false`; do not
create a generic Node pipeline for an exempt archetype. A required CI missing
from staging already aborts. Resolve VCS mismatch explicitly before changing
canonical CI. Skip the public D1 examples below and continue at D2.

For `PATH_TYPE = public`, create exactly one pipeline based on `VCS`.

### If `VCS = gitlab`: `.gitlab-ci.yml`

Write `.gitlab-ci.yml`:

```yaml
# .gitlab-ci.yml — generated by session-orchestrator bootstrap (deep)

stages:
  - test

default:
  image: node:22-alpine  # replace with python:3.12-slim for python-uv archetype

variables:
  FF_USE_FASTZIP: "true"

cache:
  key: "${CI_COMMIT_REF_SLUG}"
  paths:
    - node_modules/   # replace with .venv/ for python-uv

test:
  stage: test
  before_script:
    - corepack enable
    - pnpm install --frozen-lockfile
    # python-uv: replace above two lines with: pip install uv && uv sync
  script:
    - pnpm run build
    - pnpm run lint
    - pnpm run test
    # python-uv: replace above with: uv run ruff check . && uv run pytest
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    expire_in: 1 week
```

**Archetype adjustment:** If `CONFIRMED_ARCHETYPE = python-uv`, the LLM must substitute the Python variants of the `image`, `cache`, `before_script`, and `script` entries (comments in the template above mark the substitution points). If `CONFIRMED_ARCHETYPE = static-html`, replace `script` with a simple `echo "Static project — no build step."` and remove the `before_script`.

### If `VCS = github`: `.github/workflows/ci.yml`

```bash
mkdir -p "$REPO_ROOT/.github/workflows"
```

Write `.github/workflows/ci.yml`:

```yaml
# .github/workflows/ci.yml — generated by session-orchestrator bootstrap (deep)

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: latest
      # python-uv: replace above two steps with: uses: astral-sh/setup-uv@v5

      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"
      # python-uv: replace above with: uses: actions/setup-python@v5 with python-version: "3.12"

      - name: Install dependencies
        run: pnpm install --frozen-lockfile
        # python-uv: replace with: uv sync

      - name: Build
        run: pnpm run build
        # python-uv: remove this step

      - name: Lint
        run: pnpm run lint
        # python-uv: replace with: uv run ruff check .

      - name: Test
        run: pnpm run test
        # python-uv: replace with: uv run pytest
```

**Archetype adjustment:** Same rules as the GitLab template above. Apply the Python/static-html variants where indicated by comments.

---

## Step D2: CODEOWNERS

Detect the repo owner from git config or Session Config:

```bash
OWNER=$(git config user.email 2>/dev/null || echo "PLACEHOLDER_OWNER")
```

### If `VCS = gitlab`

```bash
mkdir -p "$REPO_ROOT"
```

Write `CODEOWNERS`:

```
# CODEOWNERS — generated by session-orchestrator bootstrap (deep)
# Format: <pattern> <@user-or-group>
# GitLab docs: https://docs.gitlab.com/ee/user/project/codeowners/

* @<OWNER or PLACEHOLDER_OWNER>
```

### If `VCS = github`

Write `.github/CODEOWNERS`:

```bash
mkdir -p "$REPO_ROOT/.github"
```

```
# CODEOWNERS — generated by session-orchestrator bootstrap (deep)
# Format: <pattern> @<github-username>
# GitHub docs: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners

* @<OWNER or PLACEHOLDER_OWNER>
```

Replace `<OWNER or PLACEHOLDER_OWNER>` with the detected `git config user.email` or the literal string `PLACEHOLDER_OWNER` if detection fails. Add an inline comment: `# Replace PLACEHOLDER_OWNER with your GitLab/GitHub username`.

---

## Step D3: CHANGELOG.md

Write `CHANGELOG.md` in the repo root:

```markdown
# Changelog

All notable changes to this project will be documented in this file.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [0.1.0] — <YYYY-MM-DD>

### Added

- Initial bootstrap via /plan (deep)
```

Replace `<YYYY-MM-DD>` with today's date in ISO 8601 format (e.g., `2026-04-16`).

---

## Step D4: Issue Templates

### If `VCS = gitlab`

```bash
mkdir -p "$REPO_ROOT/.gitlab/issue_templates"
```

In the consuming repository, create `.gitlab/issue_templates/Bug.md`: <!-- path-check: example -->

```markdown
## Summary

<!-- One-sentence description of the bug -->

## Steps to Reproduce

1.
2.
3.

## Expected Behaviour

<!-- What should happen -->

## Actual Behaviour

<!-- What happens instead -->

## Environment

- Version / branch:
- OS / platform:

## Additional Context

<!-- Screenshots, logs, related issues -->
```

In the consuming repository, create `.gitlab/issue_templates/Feature.md`: <!-- path-check: example -->

```markdown
## Summary

<!-- One-sentence description of the feature request -->

## Motivation

<!-- Why is this feature valuable? Who benefits? -->

## Proposed Solution

<!-- Describe how you envision the implementation -->

## Acceptance Criteria

- [ ]
- [ ]

## Additional Context
```

### If `VCS = github`

```bash
mkdir -p "$REPO_ROOT/.github/ISSUE_TEMPLATE"
```

Write `.github/ISSUE_TEMPLATE/bug_report.md`:

```markdown
---
name: Bug Report
about: Report a bug or unexpected behaviour
labels: bug
---

## Summary

<!-- One-sentence description of the bug -->

## Steps to Reproduce

1.
2.
3.

## Expected Behaviour

## Actual Behaviour

## Environment

- Version / branch:
- OS / platform:

## Additional Context
```

Write `.github/ISSUE_TEMPLATE/feature_request.md`:

```markdown
---
name: Feature Request
about: Propose a new feature or improvement
labels: enhancement
---

## Summary

<!-- One-sentence description of the feature request -->

## Motivation

## Proposed Solution

## Acceptance Criteria

- [ ]
- [ ]

## Additional Context
```

---

## Step D5: MR / PR Template

### If `VCS = gitlab`

```bash
mkdir -p "$REPO_ROOT/.gitlab/merge_request_templates"
```

In the consuming repository, create `.gitlab/merge_request_templates/Default.md`: <!-- path-check: example -->

```markdown
## Summary

<!-- What does this MR do and why? -->

## Changes

- 
- 

## Test Plan

- [ ] Existing tests pass (`pnpm test` / `uv run pytest`)
- [ ] New tests added where appropriate
- [ ] Manually tested

## Related Issues

Closes #

## Checklist

- [ ] Code follows project conventions
- [ ] No secrets or credentials committed
- [ ] CHANGELOG updated if user-facing change
```

### If `VCS = github`

Write `.github/pull_request_template.md`:

```markdown
## Summary

<!-- What does this PR do and why? -->

## Changes

- 
- 

## Test Plan

- [ ] Existing tests pass (`pnpm test` / `uv run pytest`)
- [ ] New tests added where appropriate
- [ ] Manually tested

## Related Issues

Closes #

## Checklist

- [ ] Code follows project conventions
- [ ] No secrets or credentials committed
- [ ] CHANGELOG updated if user-facing change
```

---

## Step D5.5: GitHub Mirror Remote

Establishes the `github` remote convention at repo-creation time, so that later mirror-push (session-end §4.4) and mirror-drift auditing (repo-audit `github-mirror-sync`, see below) have something to work against from day one. Skip this step if `VCS = none`.

Run this step when either condition holds:
- `VCS = gitlab` (GitHub is the natural mirror target for a GitLab-primary repo), OR
- `mirror: github` is already set in Session Config (an explicit mirror signal, regardless of primary VCS).

Otherwise (`VCS = github` with no mirror signal), skip — the repo's primary remote already IS GitHub, so no mirror is needed.

```bash
if ! git remote | grep -q '^github$'; then
  git remote add github https://github.com/<org>/<repo>.git
  echo "GitHub mirror remote added: github -> https://github.com/<org>/<repo>.git"
else
  echo "GitHub mirror remote already present — skipping."
fi

# Best-effort: establish github/HEAD so repo-audit's github-mirror-sync check can
# resolve the mirror's default branch. No-op while the mirror is still empty (the
# GitHub repo may not exist yet); becomes effective once the mirror has content.
# Never abort bootstrap on failure.
git fetch github --quiet 2>/dev/null || true
git remote set-head github --auto 2>/dev/null || true
```

Replace `<org>/<repo>` with the actual mirror's org/repo slug before running — this step only records the remote pointer, it does not create the GitHub repo itself (that is an out-of-band operator action, e.g. `gh repo create`). No push happens here: the first (and every subsequent) `git push github HEAD` happens at session-end Step 4.4 (`skills/session-end/SKILL.md` § "4.4 GitHub Mirror"), gated on the same `mirror: github` Session Config key. If the remote add fails (e.g. `github` already points elsewhere), log and continue — do not abort bootstrap. The `git fetch`/`set-head` lines are best-effort: until the mirror actually has commits, `github/HEAD` will not resolve and the `github-mirror-sync` audit check simply skips-as-pass (it never false-fails). The `github-mirror-sync` check also falls back to the local default branch, so it works after the first push even if `set-head` was skipped.

**See also:** repo-audit's `github-mirror-sync` check (`scripts/lib/harness-audit/categories/category6.mjs`, Category 6 / Config Hygiene) WARNS post-hoc when local commits have not reached this `github` remote. This step establishes the convention; the audit check is its enforcement counterpart.

---

## Step D6: Branch Protection

Invoke exactly ONE API call to set branch protection on `main`. Skip this step if `VCS = none`.

### If `VCS = gitlab`

Detect the project path from the git remote:

```bash
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
# Extract namespace/project from URL, e.g. "mygroup/myrepo"
PROJECT_PATH=$(echo "$REMOTE_URL" | sed 's|.*gitlab[^/]*/||; s|\.git$||')
ENCODED_PATH=$(python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.argv[1],safe=''))" "$PROJECT_PATH" 2>/dev/null || \
  node -e "process.stdout.write(encodeURIComponent(process.argv[1]))" "$PROJECT_PATH" 2>/dev/null || \
  echo "$PROJECT_PATH" | sed 's|/|%2F|g')
```

```bash
_API_ERR=$(mktemp)
if glab api "projects/$ENCODED_PATH/protected_branches" \
  --method POST \
  -f name="main" \
  -f push_access_level=40 \
  -f merge_access_level=40 \
  -f allow_force_push=false >/dev/null 2>"$_API_ERR"; then
  echo "Branch protection on main: OK"
else
  echo "Branch protection on main: failed (branch may already be protected or insufficient permissions)"
fi
rm -f "$_API_ERR"
```

Access level 40 = Maintainer. If the call fails (e.g., branch already protected, insufficient permissions), log the structured message above and continue — do not abort bootstrap. Raw API response bodies are never echoed to avoid leaking token or header information.

### If `VCS = github`

```bash
REPO_SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*github\.com[:/]||; s|\.git$||')
```

```bash
_API_ERR=$(mktemp)
if gh api "repos/$REPO_SLUG/branches/main/protection" \
  --method PUT \
  --field required_status_checks=null \
  --field enforce_admins=true \
  --field required_pull_request_reviews='{"required_approving_review_count":1}' \
  --field restrictions=null >/dev/null 2>"$_API_ERR"; then
  echo "Branch protection on main: OK"
else
  echo "Branch protection on main: failed (branch may already be protected or insufficient permissions)"
fi
rm -f "$_API_ERR"
```

If the call fails, log the structured message above and continue. Raw API response bodies are never echoed to avoid leaking token or header information.

---

<!-- @include _shared-template.md#parallel-sessions-rule -->
## Step 3a: Install Parallel-Sessions Rule

Canonical implementation in [`_shared-template.md#parallel-sessions-rule`](_shared-template.md).

Execute the shared partial's `syncBootstrapRules` action. It supplies private
required basenames to `scripts/lib/rules-sync.mjs`, preserving its manifest,
pre-write validation and local-override checks. The partial appends actual newly
created rule paths, and creates `.claude/loop.md` only when missing. See the
shared partial for the executable shell command. Issues #155, #633, #1060.

Note: Runs before D99. D99 (via inherited S99) no longer fetches `parallel-sessions.md` from the
baseline — that entry was removed from the S99 manifest in #1060. The baseline copy carries no
provenance header, so letting it win would make the next `--sync-rules` classify the target as a
repo-private override and preserve it permanently, i.e. the plugin could never update that rule
again. `rules/` is the single source for it.

## Step 3b: Initialize .orchestrator/metrics/ (Deep) (#185)

Deep tier creates both learnings and sessions metrics plus a schema-linking README:

```bash
mkdir -p "$REPO_ROOT/.orchestrator/metrics"
for _file in learnings.jsonl sessions.jsonl; do
  if [[ ! -e "$REPO_ROOT/.orchestrator/metrics/$_file" && ! -L "$REPO_ROOT/.orchestrator/metrics/$_file" ]]; then
    : > "$REPO_ROOT/.orchestrator/metrics/$_file"
    BOOTSTRAP_FILES+=(".orchestrator/metrics/$_file")
  fi
done
if [[ ! -e "$REPO_ROOT/.orchestrator/metrics/README.md" && ! -L "$REPO_ROOT/.orchestrator/metrics/README.md" ]]; then
cat > "$REPO_ROOT/.orchestrator/metrics/README.md" <<'README'
# Metrics

- `learnings.jsonl` — session-scoped learnings (confidence-weighted patterns).
  Managed by the `/evolve` skill.
- `sessions.jsonl` — per-session execution summaries.
  Written by `/close` via session-end.

Schema: https://github.com/Kanevry/session-orchestrator
(see skills/evolve/SKILL.md and skills/session-end/).

**Do NOT gitignore** — these files are project artifacts intended to persist
across sessions and contributors.
README
  BOOTSTRAP_FILES+=(.orchestrator/metrics/README.md)
fi
```

**Idempotent.** Existing files are preserved.

**Gitignore guidance:** same as Standard — `.orchestrator/metrics/*.jsonl` MUST remain tracked.

> Note: Standard tier (Step 3b) only creates `learnings.jsonl`. Deep adds `sessions.jsonl` + the `README.md`. This step is NOT a shared partial because the Deep variant is a superset, not identical.

<!-- @include _shared-template.md#baseline-fetch -->
## Step D99: (Optional) Baseline Fetch — Inherited from Standard

Canonical implementation in [`_shared-template.md#baseline-fetch`](_shared-template.md).

Standard-template Step S99 already executed as part of "Step 1–8: Execute Standard Tier" above.
No additional fetch action is needed here.

If S99 ran successfully, `.claude/rules/*.md` and `.claude/.baseline-fetch.lock` are already written
to `$REPO_ROOT`. Their individual newly created paths remain in `BOOTSTRAP_FILES`
for the Deep commit at Step D8. If the private contract applies, S99 already
delivered its local filtered rule union. If public S99 was skipped, rules arrive
via the legacy Clank weekly sync MR path.

---

<!-- @include _shared-template.md#agents-scaffold -->
## Step D6.5: .claude/agents/ Scaffold (#189)

Canonical implementation in [`_shared-template.md#agents-scaffold`](_shared-template.md).

Copy 3 opinionated agent templates (`project-discovery`, `project-code-review`,
`project-quality-gate`) into `$REPO_ROOT/.claude/agents/`. Idempotent: skip existing files.
See shared partial for full shell command. Issue #189.

---

<!-- @include _shared-template.md#vault-registration -->
## Step D6.6: Vault-Registration Prompt (Product Repos) (#190)

Canonical implementation in [`_shared-template.md#vault-registration`](_shared-template.md).

Mirrors standard-template Step 5.5. If standard already ran it and the user accepted,
`hasVaultConfig` returns true and this step is a no-op. See shared partial for full detection
script, prompt text, YAML block, and examples. Issue #190.

---

## Step D7: Write bootstrap.lock (Deep)

Write `.orchestrator/bootstrap.lock` **atomically** (mktemp + mv prevents a corrupt lock if the
process is interrupted mid-write):

```bash
_LOCK_CREATED=false
[[ -e "$REPO_ROOT/.orchestrator/bootstrap.lock" || -L "$REPO_ROOT/.orchestrator/bootstrap.lock" ]] || _LOCK_CREATED=true
_LOCK_TMP=$(mktemp "$REPO_ROOT/.orchestrator/bootstrap.lock.XXXXXX")
cat > "$_LOCK_TMP" << LOCK
# .orchestrator/bootstrap.lock
version: 1
tier: deep
archetype: <CONFIRMED_ARCHETYPE>
timestamp: <current ISO 8601 UTC — e.g., 2026-04-16T09:30:00Z>
source: <claude-init | plugin-template | projects-baseline>
plugin-version: <session-orchestrator plugin version — read from $PLUGIN_ROOT/package.json .version field>
bootstrapped-at: <current ISO 8601 UTC — same value as timestamp; distinct field for age-validation probe>
LOCK
mv "$_LOCK_TMP" "$REPO_ROOT/.orchestrator/bootstrap.lock"
if [[ "$_LOCK_CREATED" = true ]]; then BOOTSTRAP_FILES+=(.orchestrator/bootstrap.lock); fi
```

Set `source` using the same logic as fast-template Step 5.

---

## Step D8: Git Commit

Stage all created files and commit:

```bash
cd "$REPO_ROOT"
# Add only the files bootstrap created — no sweeping -u/-A to avoid catching pre-existing files
for _f in ${BOOTSTRAP_FILES[@]+"${BOOTSTRAP_FILES[@]}"}; do
  [[ -f "$_f" && ! -L "$_f" ]] && git add -- "$_f"
done
git commit -m "chore: bootstrap (deep)"
```

The commit message is fixed — do not vary it.

---

## Step D9: Report Created Files

After the commit succeeds, output a concise summary of all files created (Fast + Standard + Deep):

```
Bootstrap (deep, <archetype>) complete. Created:
  CLAUDE.md (or AGENTS.md)             — Session Config
  .gitignore                            — stack-appropriate rules
  README.md                             — expanded with Installation, Usage, Dev
  .editorconfig                         — consistent editor settings
  <manifest>                            — package.json / pyproject.toml
  <tsconfig.json>                       — if JS/TS archetype
  <eslint.config.mjs + .prettierrc>    — if JS/TS archetype
  <tests/sanity.test.ts or equiv>       — sanity test
  .claude/rules/parallel-sessions.md   — vendored PSA rule (issue #155)
  .claude/STATE.md                      — idle placeholder (issue #184)
  CHANGELOG.md                          — initial entry
  CODEOWNERS (or .github/CODEOWNERS)   — placeholder owner
  <.gitlab-ci.yml or .github/workflows/ci.yml> — CI pipeline
  <issue templates>                     — Bug + Feature templates
  <MR/PR template>                      — Default merge/pull request template
  .orchestrator/bootstrap.lock          — version: 1, tier: deep
  .orchestrator/policy/quality-gates.json — test/typecheck/lint commands (issue #183)
  Branch protection on main             — via glab/gh api (result logged above)
Committed: "chore: bootstrap (deep)"
```

Then return control to `SKILL.md` Phase 5.

---

## Not in Deep

The following are intentionally excluded from the Deep tier per the YAGNI-trim in the spec. They add value but are not in the 10-minute-Grundstruktur scope:

- **Renovate / Dependabot** — opt-in via `plan-baseline-path` templates or manual add
- **`docker-compose.yml`** — archetype concern, not tier concern; bring it when the feature needs it
- **Vault-Sync integration** — Meta-Vault is a separate system, integrated via its own skill
- **Pre-commit hooks / husky / lefthook** — personal-preference layer; opt-in manually
