docs(wiki): pulling-before-work — design spec for pull-before-work skill

This commit is contained in:
2026-05-01 10:15:55 +03:00
parent 0c8ee69e90
commit 5212c303ee

View File

@@ -0,0 +1,159 @@
---
title: "pulling-before-work — pull origin once at session start"
type: concept
updated: 2026-05-01
---
# pulling-before-work — pull origin once at session start
_2026-05-01._
## Problem
When the user opens Claude in a project that has a git remote, work often starts against a stale local branch. Edits land on top of what `origin` had hours or days ago, and the divergence shows up only at push time — sometimes after a non-trivial chunk of work has been built on the wrong base. The user explicitly asked for a guardrail: "any work in a git project starts with a pull from the remote." Manual `git pull` discipline isn't reliable across machines and sessions.
The repo already has the right shape for this — policy skills (`using-wiki`, `using-tasks`, `using-projects-meta`, `active-platform`) activated by single-line triggers in each project's `CLAUDE.md`. The fix slots into that pattern.
## Decision
Two artefacts:
1. **New policy skill** `pulling-before-work` (under `skills/pulling-before-work/SKILL.md`) — owns the pull logic, activates from the `CLAUDE.md` trigger and from in-chat re-sync phrases.
2. **`project-bootstrap` v1.4.0** — adds the line `pull remote before work` to `assets/CLAUDE.md.template`, before the platform line. The existing idempotent merge in Step 5 (introduced in v1.3.0) carries the new line into existing projects on upgrade with one confirmation prompt.
This repo (`claude-skills`) gets the trigger added to its own `CLAUDE.md` as dogfood.
## Skill behaviour
### Activation (description field)
Trigger conditions:
- `CLAUDE.md` contains the line `pull remote before work` — activates at session start, runs **one** pull cycle.
- User says `sync` / `resync` / `pull` / `обнови репо` / `git pull please` / close variants — runs the pull cycle again on demand.
Stays silent (no activation, no chat output) when the cwd is not a git repo. With no `origin` remote or no upstream tracking, prints one informational line and exits — those are conditions the user might want to know about, not pure no-ops.
### One pull cycle
```
1. Inside a git work-tree? (git rev-parse --is-inside-work-tree)
no → exit silently, no chat output.
2. Has 'origin' remote? (git remote get-url origin)
no → print one line "no remote, skip", exit.
3. Working tree dirty? (git status --porcelain — non-empty)
yes → print "working tree dirty — skipping pull. commit/stash, потом скажи 'sync'."
exit.
4. Detached HEAD? (git symbolic-ref -q HEAD — empty)
yes → print "detached HEAD — skip", exit.
5. Current branch has upstream? (git rev-parse --abbrev-ref --symbolic-full-name @{u})
no → print "no upstream tracking for <branch> — skip", exit.
6. git pull --ff-only (no args — uses configured upstream)
classify result:
- "Already up to date" → "✅ already up to date with <upstream>"
- fast-forward, N commits → "✅ pulled N commits from <upstream>"
- non-ff / diverged → "⚠️ diverged from <upstream> — resolve
manually (git pull --rebase or merge);
skill never auto-merges/rebases."
```
### Out of scope
- No commit, no stash, no push.
- No submodule recursion.
- No non-`origin` remotes.
- No detached-HEAD pulls.
- Not invoked before each commit or each tool call — only at session start and on explicit re-sync.
### Why mode 3 (start + on-demand) and not "before every commit"
User chose mode 3 explicitly during brainstorm. Pulling before every commit creates churn (a 30-minute coding session can have 5+ commits — pulling each time is noise) and shifts conflict surface late. One pull at start covers the common case (work begins on stale base); the explicit re-sync trigger handles long sessions where a teammate pushed mid-flight.
### Why skip-on-dirty (option 1) and not stash-pop or prompt
Stash + pop can fail mid-pop and leave the user with a half-applied stash to resolve — exactly the kind of friction this skill is supposed to remove. Prompting at every dirty start is noise when "I've got dirty edits" is the steady state during active work. Skip-with-warning is the cheapest correct answer: it never destroys work, it tells the user what to do next, and the next `sync` after commit/stash gets them current.
### Why `--ff-only` and not auto-merge/rebase
Auto-merge writes a merge commit the user didn't ask for; auto-rebase rewrites local history silently. Both are violations of "skill never makes the user lose track of where they are." `--ff-only` either succeeds cleanly or refuses with a message — the user retains the steering wheel for non-trivial cases.
### Why explicit upstream check (step 5)
`git pull --ff-only` without args relies on `branch.<name>.merge` being set. On a branch that was created locally and never pushed (or `git switch -c` from a remote-tracking branch with `--no-track`), there is no upstream — `git pull` errors out. The explicit check turns that error into a clean one-line skip. Bonus: the upstream name is exactly what we want in success/diverged messages, so we capture it once and reuse.
## Bootstrap integration
### Template change
`assets/CLAUDE.md.template` gains one line, inserted before the platform line:
```diff
talk like a caveman
use superpowers
use project wiki
use task management system
check across all projects
+pull remote before work
we're on Windows
```
Position rationale: the platform line stays last because the bootstrap merge logic special-cases it (substitution on non-Windows hosts). Other trigger lines are unordered logically; placing the new one just before the platform line keeps the platform line as the visual end-anchor.
### Step 5 upgrade path
Already idempotent (per `bootstrap-claude-md-merge.md`). The substring + append-only diff treats `pull remote before work` like any other trigger line: existing projects on `project-bootstrap` v1.4.0+ see one diff entry, one confirmation, one appended line. Re-runs after that are no-ops.
### Version bump
`project-bootstrap`: 1.3.0 → 1.4.0. New user-visible behaviour (a new canonical trigger landing in projects) = minor bump per the versioning convention in `skill-versioning.md`.
`pulling-before-work`: starts at 1.0.0.
## Files
```
skills/pulling-before-work/
SKILL.md NEW — frontmatter + body, ~80120 lines, no assets
skills/project-bootstrap/
SKILL.md version 1.3.0 → 1.4.0; mention new trigger in
Step 5 commentary
assets/CLAUDE.md.template + "pull remote before work"
.wiki/concepts/
pulling-before-work-design.md NEW — this file
CLAUDE.md + "pull remote before work" — dogfood the trigger
in the claude-skills repo itself
```
## Validation
No unit tests — skill is markdown + shell instructions. Validation = manual smoke run on representative scenarios:
1. Clean repo with remote, current branch tracks origin → `✅ already up to date` or `✅ pulled N commits`.
2. `git init`-only, no remote → one-line `no remote, skip`.
3. Non-git folder → silent, no output.
4. Dirty tree (`echo x > new.txt`) → `working tree dirty — skipping pull`.
5. Detached HEAD (`git checkout <sha>`) → `detached HEAD — skip`.
6. Diverged branch (local commit + remote commit on same branch) → diverged warning, no auto-merge.
7. Re-sync trigger (`sync` mid-session) → cycle repeats.
8. Bootstrap upgrade on this repo → diff prompts to append `pull remote before work`; confirm → appended.
9. Branch with no upstream (`git switch -c local-only`) → `no upstream tracking for local-only — skip`.
Scenarios 1, 2, 4, 8 are the load-bearing ones. The rest are edge-case assurance.
## Composition with existing skills
- **`active-platform`** — pull command (`git pull --ff-only origin <branch>`) is identical across Windows / Linux / macOS, so no platform branching needed inside the skill body. The active-platform contract still applies for any commands the skill prints in chat (e.g. recovery hints) — but those are git-only and platform-agnostic.
- **`using-wiki` / `using-tasks`** — independent. This skill never reads or writes `.wiki/` or `.tasks/`. No ordering constraint with them.
- **`superpowers:using-superpowers`** — skill discovery loads `pulling-before-work` from the trigger line just like other policy skills. No special handling.
## Pattern: lightweight policy skill from a single CLAUDE.md trigger
Same shape as `active-platform`: a single-line trigger in `CLAUDE.md` activates a small, focused skill that runs deterministic shell logic and returns to silence. Reusable for future "do this thing once at session start" guardrails — e.g. `check pre-commit hooks installed`, `warn if main branch behind upstream`. Bootstrap-template-line + tiny-policy-skill is the cheapest way to make a habit reliable across machines and sessions.