Compare commits
No commits in common. "3d46fa06b718f3d044c4179956bc7dd0b3c423a8" and "b3276f5bba595a769cce4474aaab1dbf74262df6" have entirely different histories.
3d46fa06b7
...
b3276f5bba
4 changed files with 64 additions and 368 deletions
|
|
@ -30,7 +30,6 @@ source "$(dirname "$0")/../lib/worktree.sh"
|
||||||
source "$(dirname "$0")/../lib/pr-lifecycle.sh"
|
source "$(dirname "$0")/../lib/pr-lifecycle.sh"
|
||||||
source "$(dirname "$0")/../lib/mirrors.sh"
|
source "$(dirname "$0")/../lib/mirrors.sh"
|
||||||
source "$(dirname "$0")/../lib/agent-sdk.sh"
|
source "$(dirname "$0")/../lib/agent-sdk.sh"
|
||||||
source "$(dirname "$0")/../lib/formula-session.sh"
|
|
||||||
|
|
||||||
# Auto-pull factory code to pick up merged fixes before any logic runs
|
# Auto-pull factory code to pick up merged fixes before any logic runs
|
||||||
git -C "$FACTORY_ROOT" pull --ff-only origin main 2>/dev/null || true
|
git -C "$FACTORY_ROOT" pull --ff-only origin main 2>/dev/null || true
|
||||||
|
|
@ -307,10 +306,6 @@ OPEN_ISSUES_SUMMARY=$(forge_api GET "/issues?state=open&labels=backlog&limit=20&
|
||||||
|
|
||||||
PUSH_INSTRUCTIONS=$(build_phase_protocol_prompt "$BRANCH" "$FORGE_REMOTE")
|
PUSH_INSTRUCTIONS=$(build_phase_protocol_prompt "$BRANCH" "$FORGE_REMOTE")
|
||||||
|
|
||||||
# Load lessons from .profile repo if available (pre-session)
|
|
||||||
profile_load_lessons || true
|
|
||||||
LESSONS_INJECTION="${LESSONS_CONTEXT:-}"
|
|
||||||
|
|
||||||
if [ "$RECOVERY_MODE" = true ]; then
|
if [ "$RECOVERY_MODE" = true ]; then
|
||||||
GIT_DIFF_STAT=$(git -C "$WORKTREE" diff "${FORGE_REMOTE}/${PRIMARY_BRANCH}..HEAD" --stat 2>/dev/null \
|
GIT_DIFF_STAT=$(git -C "$WORKTREE" diff "${FORGE_REMOTE}/${PRIMARY_BRANCH}..HEAD" --stat 2>/dev/null \
|
||||||
| head -20 || echo "(no diff)")
|
| head -20 || echo "(no diff)")
|
||||||
|
|
@ -341,10 +336,6 @@ ${GIT_DIFF_STAT}
|
||||||
3. Address any pending review comments or CI failures.
|
3. Address any pending review comments or CI failures.
|
||||||
4. Commit and push to \`${BRANCH}\`.
|
4. Commit and push to \`${BRANCH}\`.
|
||||||
|
|
||||||
${LESSONS_INJECTION:+## Lessons learned
|
|
||||||
${LESSONS_INJECTION}
|
|
||||||
|
|
||||||
}
|
|
||||||
${PUSH_INSTRUCTIONS}"
|
${PUSH_INSTRUCTIONS}"
|
||||||
else
|
else
|
||||||
INITIAL_PROMPT="You are working in a git worktree at ${WORKTREE} on branch ${BRANCH}.
|
INITIAL_PROMPT="You are working in a git worktree at ${WORKTREE} on branch ${BRANCH}.
|
||||||
|
|
@ -360,10 +351,6 @@ ${OPEN_ISSUES_SUMMARY}
|
||||||
$(if [ -n "$PRIOR_ART_DIFF" ]; then
|
$(if [ -n "$PRIOR_ART_DIFF" ]; then
|
||||||
printf '## Prior Art (closed PR — DO NOT start from scratch)\n\nA previous PR attempted this issue but was closed without merging. Reuse as much as possible.\n\n```diff\n%s\n```\n' "$PRIOR_ART_DIFF"
|
printf '## Prior Art (closed PR — DO NOT start from scratch)\n\nA previous PR attempted this issue but was closed without merging. Reuse as much as possible.\n\n```diff\n%s\n```\n' "$PRIOR_ART_DIFF"
|
||||||
fi)
|
fi)
|
||||||
${LESSONS_INJECTION:+## Lessons learned
|
|
||||||
${LESSONS_INJECTION}
|
|
||||||
|
|
||||||
}
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
1. Read AGENTS.md in this repo for project context and coding conventions.
|
1. Read AGENTS.md in this repo for project context and coding conventions.
|
||||||
|
|
@ -548,12 +535,6 @@ if [ "$rc" -eq 0 ]; then
|
||||||
log "PR #${PR_NUMBER} merged"
|
log "PR #${PR_NUMBER} merged"
|
||||||
issue_close "$ISSUE"
|
issue_close "$ISSUE"
|
||||||
|
|
||||||
# Capture files changed for journal entry (after agent work)
|
|
||||||
FILES_CHANGED=$(git -C "$WORKTREE" diff "${FORGE_REMOTE}/${PRIMARY_BRANCH}..HEAD" --name-only 2>/dev/null | tr '\n' ',' | sed 's/,$//') || FILES_CHANGED=""
|
|
||||||
|
|
||||||
# Write journal entry post-session (before cleanup)
|
|
||||||
profile_write_journal "$ISSUE" "$ISSUE_TITLE" "merged" "$FILES_CHANGED" || true
|
|
||||||
|
|
||||||
# Pull primary branch and push to mirrors
|
# Pull primary branch and push to mirrors
|
||||||
git -C "$REPO_ROOT" fetch "$FORGE_REMOTE" "$PRIMARY_BRANCH" 2>/dev/null || true
|
git -C "$REPO_ROOT" fetch "$FORGE_REMOTE" "$PRIMARY_BRANCH" 2>/dev/null || true
|
||||||
git -C "$REPO_ROOT" checkout "$PRIMARY_BRANCH" 2>/dev/null || true
|
git -C "$REPO_ROOT" checkout "$PRIMARY_BRANCH" 2>/dev/null || true
|
||||||
|
|
@ -567,14 +548,6 @@ else
|
||||||
# Exhausted or unrecoverable failure
|
# Exhausted or unrecoverable failure
|
||||||
log "PR walk failed: ${_PR_WALK_EXIT_REASON:-unknown}"
|
log "PR walk failed: ${_PR_WALK_EXIT_REASON:-unknown}"
|
||||||
issue_block "$ISSUE" "${_PR_WALK_EXIT_REASON:-agent_failed}"
|
issue_block "$ISSUE" "${_PR_WALK_EXIT_REASON:-agent_failed}"
|
||||||
|
|
||||||
# Capture files changed for journal entry (after agent work)
|
|
||||||
FILES_CHANGED=$(git -C "$WORKTREE" diff "${FORGE_REMOTE}/${PRIMARY_BRANCH}..HEAD" --name-only 2>/dev/null | tr '\n' ',' | sed 's/,$//') || FILES_CHANGED=""
|
|
||||||
|
|
||||||
# Write journal entry post-session (before cleanup)
|
|
||||||
outcome="blocked_${_PR_WALK_EXIT_REASON:-agent_failed}"
|
|
||||||
profile_write_journal "$ISSUE" "$ISSUE_TITLE" "$outcome" "$FILES_CHANGED" || true
|
|
||||||
|
|
||||||
CLAIMED=false
|
CLAIMED=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# planner-run.sh creates a tmux session with Claude (opus) and injects
|
# planner-run.sh creates a tmux session with Claude (opus) and injects
|
||||||
# this formula as context, plus the graph report from build-graph.py.
|
# this formula as context, plus the graph report from build-graph.py.
|
||||||
#
|
#
|
||||||
# Steps: preflight → triage-and-plan → commit-ops-changes
|
# Steps: preflight → triage-and-plan → journal-and-commit
|
||||||
#
|
#
|
||||||
# v4 changes from v3:
|
# v4 changes from v3:
|
||||||
# - Graph report (orphans, cycles, thin objectives, bottlenecks) replaces
|
# - Graph report (orphans, cycles, thin objectives, bottlenecks) replaces
|
||||||
|
|
@ -13,8 +13,7 @@
|
||||||
# - 3 steps instead of 6.
|
# - 3 steps instead of 6.
|
||||||
#
|
#
|
||||||
# AGENTS.md maintenance is handled by the gardener (#246).
|
# AGENTS.md maintenance is handled by the gardener (#246).
|
||||||
# All git writes (tree, memory) happen in one commit at the end.
|
# All git writes (tree, journal, memory) happen in one commit at the end.
|
||||||
# Journal writing is delegated to generic profile_write_journal() function.
|
|
||||||
|
|
||||||
name = "run-planner"
|
name = "run-planner"
|
||||||
description = "Planner v4: graph-driven planning with tea helpers"
|
description = "Planner v4: graph-driven planning with tea helpers"
|
||||||
|
|
@ -242,13 +241,50 @@ CRITICAL: If any part of this step fails, log the failure and continue.
|
||||||
needs = ["preflight"]
|
needs = ["preflight"]
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "commit-ops-changes"
|
id = "journal-and-commit"
|
||||||
title = "Write tree, memory, and journal; commit and push"
|
title = "Write tree, journal, optional memory; commit and PR"
|
||||||
description = """
|
description = """
|
||||||
### 1. Write prerequisite tree
|
### 1. Write prerequisite tree
|
||||||
Write to: $OPS_REPO_ROOT/prerequisites.md
|
Write to: $OPS_REPO_ROOT/prerequisites.md
|
||||||
|
|
||||||
### 2. Memory update (every 5th run)
|
### 2. Write journal entry
|
||||||
|
Create/append to: $OPS_REPO_ROOT/journal/planner/$(date -u +%Y-%m-%d).md
|
||||||
|
|
||||||
|
Format:
|
||||||
|
# Planner run — YYYY-MM-DD HH:MM UTC
|
||||||
|
|
||||||
|
## Predictions triaged
|
||||||
|
- #NNN: ACTION — reasoning (or "No unreviewed predictions")
|
||||||
|
|
||||||
|
## Prerequisite tree updates
|
||||||
|
- Resolved: <list> - Discovered: <list> - Proposed: <list>
|
||||||
|
|
||||||
|
## Top 5 constraints
|
||||||
|
1. <prerequisite> — blocks N objectives — #NNN (existing|filed)
|
||||||
|
|
||||||
|
## Stuck issues detected
|
||||||
|
- #NNN: vision-labeled (complexity test failed) — blocked on #NNN
|
||||||
|
(or "No stuck issues detected")
|
||||||
|
|
||||||
|
## Vault items filed
|
||||||
|
- $OPS_REPO_ROOT/vault/pending/<id>.md — <what> — blocks #NNN
|
||||||
|
(or "No vault items filed")
|
||||||
|
|
||||||
|
## Issues created
|
||||||
|
- #NNN: title — why (or "No new issues")
|
||||||
|
|
||||||
|
## Priority label changes
|
||||||
|
- Added/removed priority: #NNN (or "No priority changes")
|
||||||
|
|
||||||
|
## Observations
|
||||||
|
- Key patterns noticed this run
|
||||||
|
|
||||||
|
## Deferred
|
||||||
|
- Items in tree beyond top 5, why not filed
|
||||||
|
|
||||||
|
Keep concise — 30-50 lines max.
|
||||||
|
|
||||||
|
### 3. Memory update (every 5th run)
|
||||||
Count "# Planner run —" headers across all journal files.
|
Count "# Planner run —" headers across all journal files.
|
||||||
Check "<!-- summarized-through-run: N -->" in planner-memory.md.
|
Check "<!-- summarized-through-run: N -->" in planner-memory.md.
|
||||||
If (count - N) >= 5 or planner-memory.md missing, write to:
|
If (count - N) >= 5 or planner-memory.md missing, write to:
|
||||||
|
|
@ -256,19 +292,15 @@ If (count - N) >= 5 or planner-memory.md missing, write to:
|
||||||
Include: run counter marker, date, constraint focus, patterns, direction.
|
Include: run counter marker, date, constraint focus, patterns, direction.
|
||||||
Keep under 100 lines. Replace entire file.
|
Keep under 100 lines. Replace entire file.
|
||||||
|
|
||||||
### 3. Commit ops repo changes
|
### 4. Commit ops repo changes
|
||||||
Commit the ops repo changes (prerequisites, memory, vault items):
|
Commit the ops repo changes (prerequisites, journal, memory, vault items):
|
||||||
cd "$OPS_REPO_ROOT"
|
cd "$OPS_REPO_ROOT"
|
||||||
git add prerequisites.md knowledge/planner-memory.md vault/pending/
|
git add prerequisites.md journal/planner/ knowledge/planner-memory.md vault/pending/
|
||||||
git add -u
|
git add -u
|
||||||
if ! git diff --cached --quiet; then
|
if ! git diff --cached --quiet; then
|
||||||
git commit -m "chore: planner run $(date -u +%Y-%m-%d)"
|
git commit -m "chore: planner run $(date -u +%Y-%m-%d)"
|
||||||
git push origin "$PRIMARY_BRANCH"
|
git push origin "$PRIMARY_BRANCH"
|
||||||
fi
|
fi
|
||||||
cd "$PROJECT_REPO_ROOT"
|
cd "$PROJECT_REPO_ROOT"
|
||||||
|
|
||||||
### 4. Write journal entry (generic)
|
|
||||||
The planner-run.sh wrapper will handle journal writing via profile_write_journal()
|
|
||||||
after the formula completes. This step is informational only.
|
|
||||||
"""
|
"""
|
||||||
needs = ["triage-and-plan"]
|
needs = ["triage-and-plan"]
|
||||||
|
|
|
||||||
|
|
@ -129,317 +129,6 @@ ensure_profile_repo() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# _profile_has_repo
|
|
||||||
# Checks if the agent has a .profile repo by querying Forgejo API.
|
|
||||||
# Returns 0 if repo exists, 1 otherwise.
|
|
||||||
_profile_has_repo() {
|
|
||||||
local agent_identity="${1:-${AGENT_IDENTITY:-}}"
|
|
||||||
|
|
||||||
if [ -z "$agent_identity" ]; then
|
|
||||||
if ! resolve_agent_identity; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
agent_identity="$AGENT_IDENTITY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local forge_url="${FORGE_URL:-http://localhost:3000}"
|
|
||||||
local api_url="${forge_url}/api/v1/repos/${agent_identity}/.profile"
|
|
||||||
|
|
||||||
# Check if repo exists via API (returns 200 if exists, 404 if not)
|
|
||||||
if curl -sf -o /dev/null -w "%{http_code}" \
|
|
||||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
|
||||||
"$api_url" >/dev/null 2>&1; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# _count_undigested_journals
|
|
||||||
# Counts journal entries in .profile/journal/ excluding archive/
|
|
||||||
# Returns count via stdout.
|
|
||||||
_count_undigested_journals() {
|
|
||||||
if [ ! -d "${PROFILE_REPO_PATH:-}/journal" ]; then
|
|
||||||
echo "0"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
find "${PROFILE_REPO_PATH}/journal" -maxdepth 1 -name "*.md" -type f ! -path "*/archive/*" 2>/dev/null | wc -l
|
|
||||||
}
|
|
||||||
|
|
||||||
# _profile_digest_journals
|
|
||||||
# Runs a claude -p one-shot to digest undigested journals into lessons-learned.md
|
|
||||||
# Returns 0 on success, 1 on failure.
|
|
||||||
_profile_digest_journals() {
|
|
||||||
local agent_identity="${1:-${AGENT_IDENTITY:-}}"
|
|
||||||
local model="${2:-${CLAUDE_MODEL:-opus}}"
|
|
||||||
|
|
||||||
if [ -z "$agent_identity" ]; then
|
|
||||||
if ! resolve_agent_identity; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
agent_identity="$AGENT_IDENTITY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local journal_dir="${PROFILE_REPO_PATH}/journal"
|
|
||||||
local knowledge_dir="${PROFILE_REPO_PATH}/knowledge"
|
|
||||||
local lessons_file="${knowledge_dir}/lessons-learned.md"
|
|
||||||
|
|
||||||
# Collect undigested journal entries
|
|
||||||
local journal_entries=""
|
|
||||||
if [ -d "$journal_dir" ]; then
|
|
||||||
for jf in "$journal_dir"/*.md; do
|
|
||||||
[ -f "$jf" ] || continue
|
|
||||||
# Skip archived entries
|
|
||||||
[[ "$jf" == */archive/* ]] && continue
|
|
||||||
local basename
|
|
||||||
basename=$(basename "$jf")
|
|
||||||
journal_entries="${journal_entries}
|
|
||||||
### ${basename}
|
|
||||||
$(cat "$jf")
|
|
||||||
"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$journal_entries" ]; then
|
|
||||||
log "profile: no undigested journals to digest"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read existing lessons if available
|
|
||||||
local existing_lessons=""
|
|
||||||
if [ -f "$lessons_file" ]; then
|
|
||||||
existing_lessons=$(cat "$lessons_file")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build prompt for digestion
|
|
||||||
local digest_prompt="You are digesting journal entries from a developer agent's work sessions.
|
|
||||||
|
|
||||||
## Task
|
|
||||||
Condense these journal entries into abstract, transferable lessons. Rewrite lessons-learned.md entirely.
|
|
||||||
|
|
||||||
## Constraints
|
|
||||||
- Hard cap: 2KB maximum
|
|
||||||
- Abstract: patterns and heuristics, not specific issues or file paths
|
|
||||||
- Transferable: must help with future unseen work, not just recall past work
|
|
||||||
- Drop the least transferable lessons if over limit
|
|
||||||
|
|
||||||
## Existing lessons-learned.md (if any)
|
|
||||||
${existing_lessons:-<none>}
|
|
||||||
|
|
||||||
## Journal entries to digest
|
|
||||||
${journal_entries}
|
|
||||||
|
|
||||||
## Output
|
|
||||||
Write the complete, rewritten lessons-learned.md content below. No preamble, no explanation — just the file content."
|
|
||||||
|
|
||||||
# Run claude -p one-shot with same model as agent
|
|
||||||
local output
|
|
||||||
output=$(claude -p "$digest_prompt" \
|
|
||||||
--output-format json \
|
|
||||||
--dangerously-skip-permissions \
|
|
||||||
--max-tokens 1000 \
|
|
||||||
${model:+--model "$model"} \
|
|
||||||
2>>"$LOGFILE" || echo '{"result":"error"}')
|
|
||||||
|
|
||||||
# Extract content from JSON response
|
|
||||||
local lessons_content
|
|
||||||
lessons_content=$(printf '%s' "$output" | jq -r '.result // empty' 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [ -z "$lessons_content" ]; then
|
|
||||||
log "profile: failed to digest journals"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure knowledge directory exists
|
|
||||||
mkdir -p "$knowledge_dir"
|
|
||||||
|
|
||||||
# Write the lessons file (full rewrite)
|
|
||||||
printf '%s\n' "$lessons_content" > "$lessons_file"
|
|
||||||
log "profile: wrote lessons-learned.md (${#lessons_content} bytes)"
|
|
||||||
|
|
||||||
# Move digested journals to archive (if any were processed)
|
|
||||||
if [ -d "$journal_dir" ]; then
|
|
||||||
mkdir -p "${journal_dir}/archive"
|
|
||||||
local archived=0
|
|
||||||
for jf in "$journal_dir"/*.md; do
|
|
||||||
[ -f "$jf" ] || continue
|
|
||||||
[[ "$jf" == */archive/* ]] && continue
|
|
||||||
local basename
|
|
||||||
basename=$(basename "$jf")
|
|
||||||
mv "$jf" "${journal_dir}/archive/${basename}" 2>/dev/null && archived=$((archived + 1))
|
|
||||||
done
|
|
||||||
if [ "$archived" -gt 0 ]; then
|
|
||||||
log "profile: archived ${archived} journal entries"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# _profile_commit_and_push MESSAGE [FILE ...]
|
|
||||||
# Commits and pushes changes to .profile repo.
|
|
||||||
_profile_commit_and_push() {
|
|
||||||
local msg="$1"
|
|
||||||
shift
|
|
||||||
local files=("$@")
|
|
||||||
|
|
||||||
if [ ! -d "${PROFILE_REPO_PATH:-}/.git" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
(
|
|
||||||
cd "$PROFILE_REPO_PATH" || return 1
|
|
||||||
|
|
||||||
if [ ${#files[@]} -gt 0 ]; then
|
|
||||||
git add "${files[@]}"
|
|
||||||
else
|
|
||||||
git add -A
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! git diff --cached --quiet 2>/dev/null; then
|
|
||||||
git config user.name "${AGENT_IDENTITY}" || true
|
|
||||||
git config user.email "${AGENT_IDENTITY}@users.noreply.codeberg.org" || true
|
|
||||||
git commit -m "$msg" --no-verify 2>/dev/null || true
|
|
||||||
git push origin main --quiet 2>/dev/null || git push origin master --quiet 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
# profile_load_lessons
|
|
||||||
# Pre-session: loads lessons-learned.md into LESSONS_CONTEXT for prompt injection.
|
|
||||||
# Lazy digestion: if >10 undigested journals exist, runs claude -p to digest them.
|
|
||||||
# Returns 0 on success, 1 if agent has no .profile repo (silent no-op).
|
|
||||||
# Requires: ensure_profile_repo() called, AGENT_IDENTITY, FORGE_TOKEN, FORGE_URL, CLAUDE_MODEL.
|
|
||||||
# Exports: LESSONS_CONTEXT (the lessons file content, hard-capped at 2KB).
|
|
||||||
profile_load_lessons() {
|
|
||||||
# Check if agent has .profile repo
|
|
||||||
if ! _profile_has_repo; then
|
|
||||||
return 0 # Silent no-op
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Pull .profile repo
|
|
||||||
if ! ensure_profile_repo; then
|
|
||||||
return 0 # Silent no-op
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check journal count for lazy digestion trigger
|
|
||||||
local journal_count
|
|
||||||
journal_count=$(_count_undigested_journals)
|
|
||||||
|
|
||||||
if [ "${journal_count:-0}" -gt 10 ]; then
|
|
||||||
log "profile: digesting ${journal_count} undigested journals"
|
|
||||||
if ! _profile_digest_journals; then
|
|
||||||
log "profile: warning — journal digestion failed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read lessons-learned.md (hard cap at 2KB)
|
|
||||||
local lessons_file="${PROFILE_REPO_PATH}/knowledge/lessons-learned.md"
|
|
||||||
LESSONS_CONTEXT=""
|
|
||||||
|
|
||||||
if [ -f "$lessons_file" ]; then
|
|
||||||
local lessons_content
|
|
||||||
lessons_content=$(head -c 2048 "$lessons_file" 2>/dev/null) || lessons_content=""
|
|
||||||
if [ -n "$lessons_content" ]; then
|
|
||||||
# shellcheck disable=SC2034 # exported to caller for prompt injection
|
|
||||||
LESSONS_CONTEXT="## Lessons learned (from .profile/knowledge/lessons-learned.md)
|
|
||||||
${lessons_content}"
|
|
||||||
log "profile: loaded lessons-learned.md (${#lessons_content} bytes)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# profile_write_journal ISSUE_NUM ISSUE_TITLE OUTCOME [FILES_CHANGED]
|
|
||||||
# Post-session: writes a reflection journal entry after work completes.
|
|
||||||
# Returns 0 on success, 1 on failure.
|
|
||||||
# Requires: AGENT_IDENTITY, FORGE_TOKEN, FORGE_URL, CLAUDE_MODEL.
|
|
||||||
# Args:
|
|
||||||
# $1 - ISSUE_NUM: The issue number worked on
|
|
||||||
# $2 - ISSUE_TITLE: The issue title
|
|
||||||
# $3 - OUTCOME: Session outcome (merged, blocked, failed, etc.)
|
|
||||||
# $4 - FILES_CHANGED: Optional comma-separated list of files changed
|
|
||||||
profile_write_journal() {
|
|
||||||
local issue_num="$1"
|
|
||||||
local issue_title="$2"
|
|
||||||
local outcome="$3"
|
|
||||||
local files_changed="${4:-}"
|
|
||||||
|
|
||||||
# Check if agent has .profile repo
|
|
||||||
if ! _profile_has_repo; then
|
|
||||||
return 0 # Silent no-op
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Pull .profile repo
|
|
||||||
if ! ensure_profile_repo; then
|
|
||||||
return 0 # Silent no-op
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build session summary
|
|
||||||
local session_summary=""
|
|
||||||
if [ -n "$files_changed" ]; then
|
|
||||||
session_summary="Files changed: ${files_changed}
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
session_summary="${session_summary}Outcome: ${outcome}"
|
|
||||||
|
|
||||||
# Build reflection prompt
|
|
||||||
local reflection_prompt="You are reflecting on a development session. Write a concise journal entry about transferable lessons learned.
|
|
||||||
|
|
||||||
## Session context
|
|
||||||
- Issue: #${issue_num} — ${issue_title}
|
|
||||||
- Outcome: ${outcome}
|
|
||||||
|
|
||||||
${session_summary}
|
|
||||||
|
|
||||||
## Task
|
|
||||||
Write a journal entry focused on what you learned that would help you do similar work better next time.
|
|
||||||
|
|
||||||
## Constraints
|
|
||||||
- Be concise (100-200 words)
|
|
||||||
- Focus on transferable lessons, not a summary of what you did
|
|
||||||
- Abstract patterns and heuristics, not specific issue/file references
|
|
||||||
- One concise entry, not a list
|
|
||||||
|
|
||||||
## Output
|
|
||||||
Write the journal entry below. Use markdown format."
|
|
||||||
|
|
||||||
# Run claude -p one-shot with same model as agent
|
|
||||||
local output
|
|
||||||
output=$(claude -p "$reflection_prompt" \
|
|
||||||
--output-format json \
|
|
||||||
--dangerously-skip-permissions \
|
|
||||||
--max-tokens 500 \
|
|
||||||
${CLAUDE_MODEL:+--model "$CLAUDE_MODEL"} \
|
|
||||||
2>>"$LOGFILE" || echo '{"result":"error"}')
|
|
||||||
|
|
||||||
# Extract content from JSON response
|
|
||||||
local journal_content
|
|
||||||
journal_content=$(printf '%s' "$output" | jq -r '.result // empty' 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [ -z "$journal_content" ]; then
|
|
||||||
log "profile: failed to write journal entry"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure journal directory exists
|
|
||||||
local journal_dir="${PROFILE_REPO_PATH}/journal"
|
|
||||||
mkdir -p "$journal_dir"
|
|
||||||
|
|
||||||
# Write journal entry (append if exists)
|
|
||||||
local journal_file="${journal_dir}/issue-${issue_num}.md"
|
|
||||||
if [ -f "$journal_file" ]; then
|
|
||||||
printf '\n---\n\n' >> "$journal_file"
|
|
||||||
fi
|
|
||||||
printf '%s\n' "$journal_content" >> "$journal_file"
|
|
||||||
log "profile: wrote journal entry for issue #${issue_num}"
|
|
||||||
|
|
||||||
# Commit and push to .profile repo
|
|
||||||
_profile_commit_and_push "journal: issue #${issue_num} reflection" "journal/issue-${issue_num}.md"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Formula loading ──────────────────────────────────────────────────────
|
# ── Formula loading ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
# load_formula FORMULA_FILE
|
# load_formula FORMULA_FILE
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,6 @@ WORKTREE="/tmp/${PROJECT_NAME}-planner-run"
|
||||||
|
|
||||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
# Ensure AGENT_IDENTITY is set for profile functions
|
|
||||||
if [ -z "${AGENT_IDENTITY:-}" ] && [ -n "${FORGE_PLANNER_TOKEN:-}" ]; then
|
|
||||||
AGENT_IDENTITY=$(curl -sf -H "Authorization: token ${FORGE_PLANNER_TOKEN}" \
|
|
||||||
"${FORGE_URL:-http://localhost:3000}/api/v1/user" 2>/dev/null | jq -r '.login // empty' 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Guards ────────────────────────────────────────────────────────────────
|
# ── Guards ────────────────────────────────────────────────────────────────
|
||||||
check_active planner
|
check_active planner
|
||||||
acquire_cron_lock "/tmp/planner-run.lock"
|
acquire_cron_lock "/tmp/planner-run.lock"
|
||||||
|
|
@ -78,9 +72,24 @@ $(cat "$MEMORY_FILE")
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Load lessons from .profile repo (pre-session) ────────────────────────
|
# ── Read recent journal files ──────────────────────────────────────────
|
||||||
profile_load_lessons || true
|
JOURNAL_BLOCK=""
|
||||||
LESSONS_INJECTION="${LESSONS_CONTEXT:-}"
|
JOURNAL_DIR="$OPS_REPO_ROOT/journal/planner"
|
||||||
|
if [ -d "$JOURNAL_DIR" ]; then
|
||||||
|
# Load last 5 journal files (most recent first) for run history context
|
||||||
|
JOURNAL_FILES=$(find "$JOURNAL_DIR" -name '*.md' -type f | sort -r | head -5)
|
||||||
|
if [ -n "$JOURNAL_FILES" ]; then
|
||||||
|
JOURNAL_BLOCK="
|
||||||
|
### Recent journal entries (journal/planner/)
|
||||||
|
"
|
||||||
|
while IFS= read -r jf; do
|
||||||
|
JOURNAL_BLOCK="${JOURNAL_BLOCK}
|
||||||
|
#### $(basename "$jf")
|
||||||
|
$(cat "$jf")
|
||||||
|
"
|
||||||
|
done <<< "$JOURNAL_FILES"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Read scratch file (compaction survival) ───────────────────────────────
|
# ── Read scratch file (compaction survival) ───────────────────────────────
|
||||||
SCRATCH_CONTEXT=$(read_scratch_context "$SCRATCH_FILE")
|
SCRATCH_CONTEXT=$(read_scratch_context "$SCRATCH_FILE")
|
||||||
|
|
@ -96,11 +105,7 @@ build_sdk_prompt_footer "
|
||||||
PROMPT="You are the strategic planner for ${FORGE_REPO}. Work through the formula below.
|
PROMPT="You are the strategic planner for ${FORGE_REPO}. Work through the formula below.
|
||||||
|
|
||||||
## Project context
|
## Project context
|
||||||
${CONTEXT_BLOCK}${MEMORY_BLOCK}
|
${CONTEXT_BLOCK}${MEMORY_BLOCK}${JOURNAL_BLOCK}
|
||||||
${LESSONS_INJECTION:+## Lessons learned
|
|
||||||
${LESSONS_INJECTION}
|
|
||||||
|
|
||||||
}
|
|
||||||
${GRAPH_SECTION}
|
${GRAPH_SECTION}
|
||||||
${SCRATCH_CONTEXT:+${SCRATCH_CONTEXT}
|
${SCRATCH_CONTEXT:+${SCRATCH_CONTEXT}
|
||||||
}
|
}
|
||||||
|
|
@ -120,8 +125,5 @@ export CLAUDE_MODEL="opus"
|
||||||
agent_run --worktree "$WORKTREE" "$PROMPT"
|
agent_run --worktree "$WORKTREE" "$PROMPT"
|
||||||
log "agent_run complete"
|
log "agent_run complete"
|
||||||
|
|
||||||
# Write journal entry post-session
|
|
||||||
profile_write_journal "planner-run" "Planner run $(date -u +%Y-%m-%d)" "complete" "" || true
|
|
||||||
|
|
||||||
rm -f "$SCRATCH_FILE"
|
rm -f "$SCRATCH_FILE"
|
||||||
log "--- Planner run done ---"
|
log "--- Planner run done ---"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue