Files
open-harness/context/rules/git.md
T
ryaneggzandClaude Opus 4.7 d14bc330eb task: cloudflared/SSH/host-overlay doc cleanup (keep postgres)
Apply PR #284's doc updates selectively — cloudflared, SSH overlays,
host-bind overlays, SANDBOX_PASSWORD, and openssh-server references
removed everywhere. Postgres mentions preserved; postgres compose
overlay restored.

Doc edits:
- AGENTS.md / CLAUDE.md (symlink): drop /cloudflared-tunnel skill row,
  rewrite "Exposing apps" to mention generic tunnels/proxies.
- README.md, docs/installation.md: drop SANDBOX_PASSWORD, cloudflared,
  openssh-server, host-overlay references.
- docs/quickstart.md: keep postgres link, drop cloudflare link, mention
  postgres overlay is opt-in.
- docs/integrations/github.md: drop SSH-overlay section.
- docs/integrations/cloudflare.md: deleted.
- docs/agents/{deepagents,opencode}.md: drop host-overlay sections.
- context/rules/git.md: drop "Advanced opt-ins" SSH section.
- Makefile: drop _check-jq / _check-config gates; jq invocation is now
  defensive (only runs when jq + config.json both present).
- apps/docs/src/pages/index.tsx: keep postgres in "composable substrate"
  copy, drop cloudflare tunnels / Caddy gateway mentions.
- .claude/skills/{agent-browser,harness-audit,strategic-proposal}/SKILL.md
  and .claude/agents/command-builder.md: drop cloudflared references
  (strategic-proposal infra row keeps PostgreSQL 16).
- CHANGELOG.md [Unreleased] § Removed: new entry for #283 listing the
  10 deleted overlays + companion infrastructure; note postgres was
  kept.

Infra:
- Restored .devcontainer/docker-compose.postgres.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 02:36:15 +00:00

6.3 KiB

Git Workflow

Issue Titles

Format: <prefix>(<issue#>): <shortdesc>

<prefix> ∈ feat · bug · task · audit · skill · agent (matches .github/ISSUE_TEMPLATE/<prefix>.md)

Example: feat(#42): slack thread replies

Create issue first so <issue#> exists, then branch.

Branch Names

Format: <prefix>/<issue#>-<short-desc>

  • <short-desc>: kebab-case, ≤5 words
  • Base off default target branch (see below)

Example: feat/42-slack-thread-replies

Default Target Branch

Use first existing in repo:

  1. development (preferred)
  2. main (fallback)
  3. master (fallback)

Detect via git show-ref --verify --quiet refs/heads/<name> (or remote refs/remotes/origin/<name>). PRs target this branch; new branches cut from it.

Git Authentication

Inside sandbox, run gh auth login && gh auth setup-git during onboarding. GitHub CLI installs credential helper — git push / git fetch use your GitHub token — no SSH keys required.

PR Titles

Format: FROM <source-branch> TO <target-branch> (literal)

Example: FROM feat/42-slack-thread-replies TO development

PR Bodies

  • Link issue: Closes #<issue#> (or Fixes/Resolves)
  • Target default target branch (development → main → master, whichever exists)

Commit Messages

Format: <type>: <description> where <type> ∈ feat · fix · task · audit · skill

Changelog

Root CHANGELOG.md follows Keep a Changelog with CalVer tags.

Every PR with user-visible impact MUST add entry under ## [Unreleased] heading, in same commit as change. Categories: ### Added · ### Changed · ### Fixed · ### Removed · ### Deprecated · ### Security.

Skip entries only for pure chores with no runtime or workflow effect (internal refactors, test-only changes, typo fixes). When in doubt, add entry.

Entry format: one line, imperative mood, link PR or issue.

### Added
- Slack thread replies in multi-channel mode ([#42](https://github.com/ryaneggz/open-harness/pull/42)).

At release time, /release promotes [Unreleased] to new ## [<VERSION>] - YYYY-MM-DD section and re-seeds empty [Unreleased] block. Do not hand-edit versioned sections after tag ships.

Worktrees

Default path: .worktrees/<branch> at project root. Create .worktrees/ if missing. Independent project clones (own .git, not harness branches) live under .worktrees/project/<project-name>/<repo>/ — see .worktrees/README.md.

mkdir -p .worktrees
git worktree add .worktrees/<branch> <branch>                # existing branch
git worktree add -b <prefix>/<issue#>-<short-desc> \
  .worktrees/<prefix>/<issue#>-<short-desc> $BASE            # new branch off $BASE

Example path: .worktrees/feat/42-slack-thread-replies

Cleanup: git worktree remove .worktrees/<branch>.

.worktrees/ gitignored (see .gitignore); only .worktrees/README.md tracked.

Stale worktree policy

Worktrees older than 30 days without a corresponding open PR may be removed via git worktree remove; corrupted worktree directories may be removed with rm -rf after confirming they are not valid git worktree list entries. The /harness-audit skill flags stale-worktree candidates for review before cleanup.

Isolating in-flight work

When main checkout has unstaged changes you shouldn't commit in current PR, do not stash-and-switch-branches (risk of losing context). Instead:

  1. Cut worktree off target base: git worktree add -b <new> .worktrees/<new> $BASE.
  2. Copy in-flight files into worktree: plain cp preserves main checkout's working tree untouched.
  3. Commit in worktree. Main checkout stays exactly as-is.

Before discarding duplicated state from main checkout, verify byte-equivalence with committed branch:

for f in <changed-files>; do
  a=$(md5sum "$f" | awk '{print $1}')
  b=$(git show <branch>:"$f" | md5sum | awk '{print $1}')
  [ "$a" = "$b" ] && echo "same:  $f" || echo "DRIFT: $f"
done

Only after all files show same: run git restore / rm -f to clean main checkout.

Stacked PRs

When PR needs work from another open PR (e.g. feature depending on in-flight docs or infra changes), stack instead of waiting:

  1. git fetch origin <parent-branch>
  2. In worktree: git rebase origin/<parent-branch>. Resolve conflicts; tests may need re-running.
  3. git push --force-with-lease
  4. gh pr edit <pr#> --base <parent-branch>
  5. gh pr edit <pr#> --title "FROM <branch> TO <parent-branch>"

When parent PR merges, GitHub auto-rebases stacked PR's base to parent's target (development). Do not force-push again after parent merges — let GitHub handle retarget.

Keep stacks shallow: one level routine, two levels rare, three levels means something wrong with sequencing.

Releases

Versioning: CalVer YYYY.M.D for first release of day, then YYYY.M.D-N (N starts at 2) for subsequent releases.

Release branch: release/<VERSION> (e.g., release/2026.4.18-2).

Pushing tag triggers .github/workflows/release.yml — runs lint + type-check + tests, builds ghcr.io/ryaneggz/openharness:<VERSION>, pushes to GHCR, creates GitHub Release.

Pre-flight before tagging:

  • On intended source branch, no uncommitted changes

Procedure:

VERSION=$(date '+%Y.%-m.%-d')                             # append -N if tag exists
git checkout -b "release/$VERSION"
git push origin "release/$VERSION"
git tag "$VERSION" && git push origin "$VERSION"          # triggers CI release

After pushing tag, monitor .github/workflows/release.yml and verify both GitHub Release and GHCR image. Use /release skill for full automated procedure (version detection, pre-flight, tag, CI polling, verification).

After Push

If .claude/skills/ci-status/ exists, invoke /ci-status after every git push to confirm pipeline green before declaring work done. Push failing CI is not done.

Workflow

Let $BASE = default target branch (detected per rule above).

  1. Create GitHub issue → record <issue#>
  2. git checkout -b <prefix>/<issue#>-<short-desc> $BASE
  3. Add CHANGELOG.md entry under ## [Unreleased] (see § Changelog) — unless change is pure chore
  4. Commit with <type>: <description>
  5. git push -u origin <branch> → then /ci-status (if skill exists)
  6. gh pr create --base $BASE --title "FROM <branch> TO $BASE" --body "Closes #<issue#>"