aboutsummaryrefslogtreecommitdiff
path: root/.ai/workflows/startup.org
diff options
context:
space:
mode:
Diffstat (limited to '.ai/workflows/startup.org')
-rw-r--r--.ai/workflows/startup.org31
1 files changed, 21 insertions, 10 deletions
diff --git a/.ai/workflows/startup.org b/.ai/workflows/startup.org
index 929d482..2262eea 100644
--- a/.ai/workflows/startup.org
+++ b/.ai/workflows/startup.org
@@ -29,10 +29,16 @@ Inside a rulesets session, the project-repo refresh below covers this — the ru
#+begin_src bash
rs="$HOME/code/rulesets"
if [ -d "$rs/.git" ]; then
- if (cd "$rs" && git diff --quiet --ignore-submodules HEAD -- 2>/dev/null); then
+ gate="$rs/claude-templates/bin/git-worktree-gate"
+ if [ -x "$gate" ] && "$gate" sync-safe "$rs"; then
+ (cd "$rs" && git pull --ff-only origin main 2>&1) | tail -3
+ elif [ ! -x "$gate" ] \
+ && (cd "$rs" && git diff --quiet --ignore-submodules HEAD -- 2>/dev/null); then
+ # Bootstrap fallback for a checkout old enough not to have the shared
+ # gate yet. The pull that follows installs it for subsequent starts.
(cd "$rs" && git pull --ff-only origin main 2>&1) | tail -3
else
- echo "rulesets: dirty working tree — using as-is, skipping pull"
+ echo "rulesets: changes beyond untracked inbox deliveries — using as-is, skipping pull"
fi
else
echo "rulesets: not a git checkout — skipping"
@@ -40,11 +46,11 @@ fi
#+end_src
Behavior:
-- *Clean working tree* → fast-forward pull. =git pull --ff-only= refuses any merge or rebase, so the operation is either a no-op (already current) or a clean advance.
-- *Dirty working tree* → skip the pull. Don't auto-stash and don't auto-merge — those would either lose work or invite conflicts at the worst possible moment (session start).
+- *Clean working tree, or untracked deliveries only beneath =inbox/=* → fast-forward pull. =git pull --ff-only= refuses any merge or rebase, so the operation is either a no-op (already current) or a clean advance. Inbox files are queue input, not source-tree work, and do not block other projects from receiving rulesets updates.
+- *Any staged or tracked change, dirty submodule, Git operation in progress, or untracked file outside =inbox/=* → skip the pull. Don't auto-stash and don't auto-merge — those would either lose work or invite conflicts at the worst possible moment (session start).
- *Non-fast-forward history* → =--ff-only= aborts with an error. Surface that to the user; the rsync still proceeds against the working tree as-is.
-*Template-freshness policy (applies to every dirty-check in the synced workflows).* "Dirty" means *tracked modifications only*. Untracked and gitignored files — an inbox drop, a file left in the tree to read, scratch output — never block a template pull, a fast-forward, or a monitoring gate. Projects were falling behind on templates because somebody sent them a task; that's the failure this policy closes. The checks here already comply (=git diff --quiet HEAD= sees only tracked changes; the ff gate uses =--untracked-files=no=), and any dirty-check added to a synced workflow follows the same rule. One deliberate exception: the rsync WIP-guard below counts untracked files *within rulesets' own synced source paths*, because an untracked half-written template is exactly the WIP it exists to hold back — that guard is about rulesets' outbound content, not the consuming project's local state.
+*Template-freshness policy (applies to every dirty-check in the synced workflows).* The shared =git-worktree-gate sync-safe= policy is the source of truth: untracked files beneath =inbox/= and gitignored files do not block a pull, fast-forward, or monitoring gate; every other staged, tracked, untracked, submodule, or in-progress-operation state does. Projects must not fall behind merely because somebody sent them a task, but an arbitrary scratch file is not silently treated as safe. One deliberate exception remains: the rsync WIP-guard below is narrower than the repository gate and counts untracked files within rulesets' own synced source paths, because an untracked half-written template is exactly the WIP it exists to hold back.
*** Install rulesets symlinks into ~/.claude (idempotent)
@@ -74,8 +80,11 @@ if [ -d .git ]; then
current=$(git symbolic-ref --short HEAD 2>/dev/null)
dirty=0
- if ! git diff --quiet --ignore-submodules HEAD -- 2>/dev/null \
- || [ -n "$(git status --porcelain --untracked-files=no)" ]; then
+ gate="$HOME/code/rulesets/claude-templates/bin/git-worktree-gate"
+ if [ -x "$gate" ]; then
+ "$gate" sync-safe "$PWD" >/dev/null 2>&1 || dirty=1
+ elif ! git diff --quiet --ignore-submodules HEAD -- 2>/dev/null \
+ || [ -n "$(git status --porcelain --untracked-files=no)" ]; then
dirty=1
fi
@@ -107,8 +116,8 @@ fi
#+end_src
Behavior, per branch:
-- *Behind only, current branch, clean tree* → =git merge --ff-only= advances HEAD.
-- *Behind only, current branch, dirty tree* → fetched but not advanced. Surface so Craig can ff manually after dealing with the dirty state.
+- *Behind only, current branch, sync-safe tree* → =git merge --ff-only= advances HEAD. An untracked =inbox/= delivery is sync-safe.
+- *Behind only, current branch, sync-blocking tree* → fetched but not advanced. Surface so Craig can ff manually after dealing with the reported state.
- *Behind only, non-checkout branch* → =git fetch . upstream:branch= advances the ref without touching the working tree.
- *Diverged* (ahead and behind) → leave alone. Surface for Craig to resolve. Don't auto-rebase or auto-merge.
- *Ahead only* or *up to date* → silent no-op.
@@ -170,12 +179,14 @@ These calls have no dependencies on each other. Issue them all together in one m
10. =[ -f "$HOME/org/roam/inbox.org" ] && grep -cE '^\*\* ' "$HOME/org/roam/inbox.org" || true= — count items in the roam global inbox (=~/org/roam/inbox.org=), the roam-mode startup nudge. Silent if the roam clone isn't on this machine. Phase C reads the file when the count is non-zero, splits total vs items related to this project, and surfaces the offer (see =inbox.org= roam mode). Read-only; never files at startup.
11. KB surface prep (the read + contribute startup nudges; see =docs/specs/2026-06-16-encourage-kb-contribution-spec.org=). Gated on the agent KB clone. Counts =:agent:= nodes, lists up to 5 whose content matches the current project basename (titles only; a few most-recent nodes as a fallback when nothing matches), and resolves the best-practices node path. Read-only; silent when the clone is absent. Phase C surfaces the relevant titles (consult) and the best-practices link (contribute).
+ The best-practices lookup matches the node's *filename*, not its content. A roam node's slug lives only in its filename, so the earlier content-grep (=rg -l 'agent-kb-best-practices'=) matched nothing and the contribute nudge silently pointed at an empty path in every project, every session, for as long as it shipped. =find= rather than a glob keeps the probe identical under bash and zsh (zsh aborts on an unmatched glob) — the same reason the spec-sort probe below uses =find=.
+
#+begin_src bash
ra="$HOME/org/roam/agents"
if [ -d "$ra" ]; then
proj=$(basename "$PWD")
echo "kb-total: $(rg -l '#\+filetags:.*:agent:' "$ra" 2>/dev/null | wc -l)"
- echo "kb-bestpractices: $(rg -l 'agent-kb-best-practices' "$ra" 2>/dev/null | head -1)"
+ echo "kb-bestpractices: $(find "$ra" -maxdepth 1 -name '*agent-kb-best-practices*.org' -print -quit 2>/dev/null)"
matches=$(rg -il "$proj" "$ra" 2>/dev/null | head -5)
[ -z "$matches" ] && matches=$(\ls -t "$ra"/*.org 2>/dev/null | head -3)
echo "kb-relevant-titles:"