Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e897364a73 | ||
|
|
5fa68fcf43 | ||
|
|
b1368bbc7e | ||
|
|
9432f68c8b | ||
|
|
4c921ed1ba | ||
|
|
26251badda |
@@ -168,8 +168,12 @@ Merge when, and only when, **all** hold:
|
|||||||
`block_on_outdated_branch: true` refuses it anyway; never
|
`block_on_outdated_branch: true` refuses it anyway; never
|
||||||
`force_manually_merged` around it.
|
`force_manually_merged` around it.
|
||||||
|
|
||||||
**Refresh before every merge.** In the loop worktree: fetch, then
|
**Refresh before every merge.** In the loop worktree: `git fetch origin`
|
||||||
`git merge origin/main` on the PR branch, push. A textual conflict
|
in the same breath, then `git merge origin/main` on the PR branch,
|
||||||
|
push. The fetch must be immediate — a cached `origin/main` merges
|
||||||
|
against the wrong base, CI goes green on it, and the merge comes back
|
||||||
|
405 "behind base", one whole CI cycle wasted (measured on the adoption
|
||||||
|
wave). A textual conflict
|
||||||
stops the leg there — as diff text, not as a failed merge click: hunks
|
stops the leg there — as diff text, not as a failed merge click: hunks
|
||||||
the loop authored are resolved by the loop; anything else is left with
|
the loop authored are resolved by the loop; anything else is left with
|
||||||
`⟦loop⟧` comment for a human, never forced. After any refresh push,
|
`⟦loop⟧` comment for a human, never forced. After any refresh push,
|
||||||
@@ -247,7 +251,10 @@ AVD), then `make android-emulator` per session.
|
|||||||
|
|
||||||
- **Worktree:** `git worktree add ~/.paseo/worktrees/loop/jumpy-hound
|
- **Worktree:** `git worktree add ~/.paseo/worktrees/loop/jumpy-hound
|
||||||
origin/main` (from any clone; branch from origin/main in the loop
|
origin/main` (from any clone; branch from origin/main in the loop
|
||||||
tree, never `git checkout main`).
|
tree, never `git checkout main`). **Provision it once before the
|
||||||
|
first push:** `make build-frontend` and `make testdata` — the pre-push
|
||||||
|
`go-test` hook needs `frontend/dist` (the `//go:embed` in `main.go`)
|
||||||
|
and the fixture library, and refuses the push without them.
|
||||||
- **Session:** pi in that worktree, `/name loop`. Add the job via
|
- **Session:** pi in that worktree, `/name loop`. Add the job via
|
||||||
`/schedule-prompt` (name `yj-loop`, cron
|
`/schedule-prompt` (name `yj-loop`, cron
|
||||||
`0 0 10-18 * * 1-5`, prompt: "Read `.pi/skills/yj-loop/SKILL.md` and
|
`0 0 10-18 * * 1-5`, prompt: "Read `.pi/skills/yj-loop/SKILL.md` and
|
||||||
@@ -271,3 +278,11 @@ AVD), then `make android-emulator` per session.
|
|||||||
- The job did not fire — the scheduler fires only while a session is
|
- The job did not fire — the scheduler fires only while a session is
|
||||||
open in its directory (documented); "the loop is off" is the correct
|
open in its directory (documented); "the loop is off" is the correct
|
||||||
reading, not a bug.
|
reading, not a bug.
|
||||||
|
- `error: object file … is empty` / `unpack-objects failed` / `bad
|
||||||
|
object refs/heads/…` during a fetch or checkout — the shared object
|
||||||
|
store was corrupted (a killed fetch leaves 0-byte object files, and a
|
||||||
|
local ref can end up pointing at the dead sha1). **Halt and report**;
|
||||||
|
do not retry, the churn only deepens it. Human repair: delete the
|
||||||
|
0-byte objects, `git fetch origin --prune`, delete any ref that
|
||||||
|
still dangles (`git update-ref -d refs/heads/<b>`), re-checkout the
|
||||||
|
worktree at `origin/main`, then `git fsck --full`.
|
||||||
@@ -112,7 +112,9 @@ in the job's directory — that limitation is the switch:
|
|||||||
|
|
||||||
- **Worktree:** `git worktree add` a dedicated clone at
|
- **Worktree:** `git worktree add` a dedicated clone at
|
||||||
`~/.paseo/worktrees/loop/jumpy-hound`. Loop edits happen only there; a
|
`~/.paseo/worktrees/loop/jumpy-hound`. Loop edits happen only there; a
|
||||||
dirty tree there is the loop's business and nobody else's.
|
dirty tree there is the loop's business and nobody else's. **Provision
|
||||||
|
it once before its first push:** `make build-frontend` + `make testdata`
|
||||||
|
— the pre-push `go-test` hook needs both and refuses without them.
|
||||||
- **Session:** pi in that worktree, `/name loop`. The job is bound to that
|
- **Session:** pi in that worktree, `/name loop`. The job is bound to that
|
||||||
session, so another pi elsewhere in the same directory does not
|
session, so another pi elsewhere in the same directory does not
|
||||||
double-fire it.
|
double-fire it.
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ make ui-test # Vitest component/store suite in a real browser (no app)
|
|||||||
make ui-visual # Same, including toMatchScreenshot comparisons
|
make ui-visual # Same, including toMatchScreenshot comparisons
|
||||||
make ui-setup # Install the Vitest provider's own Chromium (once)
|
make ui-setup # Install the Vitest provider's own Chromium (once)
|
||||||
make bindings-check # Fail if frontend/bindings is stale vs the Go bindings
|
make bindings-check # Fail if frontend/bindings is stale vs the Go bindings
|
||||||
make skill-check # Fail if .pi/ documents a make target that doesn't exist
|
make skill-check # Fail if a doc names a make target that doesn't exist
|
||||||
make commit-check # Fail if a commit subject is not a Conventional Commit
|
make commit-check # Fail if a commit subject is not a Conventional Commit
|
||||||
make lint # golangci-lint v2 (strict), all three build configurations
|
make lint # golangci-lint v2 (strict), all three build configurations
|
||||||
make test # All tests with race detector, all three build configurations
|
make test # All tests with race detector, all three build configurations
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ css-check: ## Fail on a css`` literal ended early by a backtick, or a nested rul
|
|||||||
# Every command in them is a make target on purpose, so this is
|
# Every command in them is a make target on purpose, so this is
|
||||||
# checkable. It also asserts AGENTS.md is a symlink to CLAUDE.md, so the
|
# checkable. It also asserts AGENTS.md is a symlink to CLAUDE.md, so the
|
||||||
# two harnesses cannot drift onto two descriptions of one project.
|
# two harnesses cannot drift onto two descriptions of one project.
|
||||||
skill-check: ## Fail if the agent docs name a missing make target, or AGENTS.md is not a symlink
|
skill-check: ## Fail if the docs name a missing make target, or AGENTS.md is not a symlink
|
||||||
@./scripts/skill-check.sh
|
@./scripts/skill-check.sh
|
||||||
|
|
||||||
# Conventional Commits, which CLAUDE.md claimed CI enforced for a long
|
# Conventional Commits, which CLAUDE.md claimed CI enforced for a long
|
||||||
|
|||||||
+7
-3
@@ -38,10 +38,14 @@ pre-commit:
|
|||||||
glob: "*.go"
|
glob: "*.go"
|
||||||
run: ./scripts/bindings-check.sh
|
run: ./scripts/bindings-check.sh
|
||||||
|
|
||||||
# .pi/ documents make targets; a stale one sends an agent off a
|
# The docs document make targets; a stale one sends an agent — or a
|
||||||
# cliff with total confidence. Instant.
|
# contributor reading CONTRIBUTING.md — off a cliff with total
|
||||||
|
# confidence. The glob is the script's own scanned set, because a
|
||||||
|
# hook that does not fire on a file the check reads is the drift the
|
||||||
|
# check exists to prevent: it was `{Makefile,.pi/**/*.md}` while the
|
||||||
|
# script already read CLAUDE.md. Instant.
|
||||||
skill-check:
|
skill-check:
|
||||||
glob: "{Makefile,.pi/**/*.md}"
|
glob: "{Makefile,.pi/**/*.md,AGENTS.md,CLAUDE.md,README.md,CONTRIBUTING.md}"
|
||||||
run: ./scripts/skill-check.sh
|
run: ./scripts/skill-check.sh
|
||||||
|
|
||||||
frontend-typecheck:
|
frontend-typecheck:
|
||||||
|
|||||||
+21
-4
@@ -14,6 +14,11 @@
|
|||||||
# missing: CLAUDE.md names 27 targets and nothing verified one of them,
|
# missing: CLAUDE.md names 27 targets and nothing verified one of them,
|
||||||
# so the file the agents trust most was the file least checked.
|
# so the file the agents trust most was the file least checked.
|
||||||
#
|
#
|
||||||
|
# README.md and CONTRIBUTING.md are in it too, and the header sentence
|
||||||
|
# above is why: a person who has *not* read the Makefile goes looking in
|
||||||
|
# the contributor-facing doc, so a renamed target sends them off the
|
||||||
|
# same cliff it sends an agent off. CONTRIBUTING.md names 21 targets.
|
||||||
|
#
|
||||||
# **AGENTS.md is a symlink to CLAUDE.md.** This repo is worked on by
|
# **AGENTS.md is a symlink to CLAUDE.md.** This repo is worked on by
|
||||||
# two agent harnesses that read different files by convention — Claude
|
# two agent harnesses that read different files by convention — Claude
|
||||||
# Code reads CLAUDE.md, others read AGENTS.md — and two harnesses
|
# Code reads CLAUDE.md, others read AGENTS.md — and two harnesses
|
||||||
@@ -43,7 +48,19 @@ if [ -e AGENTS.md ] || [ -L AGENTS.md ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -d .pi ] || exit 0
|
# The scan is over the docs that are actually there: a checkout without
|
||||||
|
# .pi/ still has README.md and CONTRIBUTING.md to check, and gating the
|
||||||
|
# whole run on .pi/ would have made the human-facing half conditional on
|
||||||
|
# the agent-facing one. This list is used twice — once to read the
|
||||||
|
# mentions out and once to say which file a missing target came from —
|
||||||
|
# because a second list is a second thing to forget.
|
||||||
|
# `ls` exits non-zero when *any* of its arguments is missing while still
|
||||||
|
# printing the ones that are there, and under `set -e` that would sink
|
||||||
|
# the assignment rather than scanning what exists, so swallow it.
|
||||||
|
docs="$({ find .pi -name '*.md' 2>/dev/null
|
||||||
|
ls CLAUDE.md README.md CONTRIBUTING.md 2>/dev/null || true; })"
|
||||||
|
|
||||||
|
[ -n "$docs" ] || exit 0
|
||||||
|
|
||||||
# `make -pq` prints the database including every rule, without running
|
# `make -pq` prints the database including every rule, without running
|
||||||
# anything. It exits non-zero when a target is out of date, and under
|
# anything. It exits non-zero when a target is out of date, and under
|
||||||
@@ -68,7 +85,7 @@ targets="$({ make -pqRr 2>/dev/null || true; } |
|
|||||||
# AGENTS.md is deliberately not in this list: it is a symlink to
|
# AGENTS.md is deliberately not in this list: it is a symlink to
|
||||||
# CLAUDE.md, asserted above, so scanning it would report every failure
|
# CLAUDE.md, asserted above, so scanning it would report every failure
|
||||||
# twice under two names.
|
# twice under two names.
|
||||||
mentioned="$({ find .pi -name '*.md' 2>/dev/null; echo CLAUDE.md; } |
|
mentioned="$(printf '%s\n' "$docs" |
|
||||||
xargs awk '
|
xargs awk '
|
||||||
FNR == 1 { fence = 0 }
|
FNR == 1 { fence = 0 }
|
||||||
/^```/ { fence = !fence; next }
|
/^```/ { fence = !fence; next }
|
||||||
@@ -93,10 +110,10 @@ for t in $mentioned; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [ -n "$missing" ]; then
|
if [ -n "$missing" ]; then
|
||||||
echo "skill-check: the agent docs name make targets that do not exist:" >&2
|
echo "skill-check: the docs name make targets that do not exist:" >&2
|
||||||
for t in $missing; do
|
for t in $missing; do
|
||||||
echo " make $t" >&2
|
echo " make $t" >&2
|
||||||
grep -rln "make $t" .pi CLAUDE.md --include='*.md' | sed 's/^/ /' >&2
|
printf '%s\n' "$docs" | xargs grep -ln "make $t" | sed 's/^/ /' >&2
|
||||||
done
|
done
|
||||||
echo "Fix the docs, or restore the target." >&2
|
echo "Fix the docs, or restore the target." >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user