Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e3fb88309 | ||
|
|
c8c5ce9015 | ||
|
|
293f51b914 | ||
|
|
70287db150 | ||
|
|
efedba1ff0 | ||
|
|
f35a2d5985 | ||
|
|
19b641a2b6 | ||
|
|
e422e6a13d | ||
|
|
f4d590c40d | ||
|
|
791cb6e524 | ||
|
|
3749df798e |
@@ -0,0 +1,132 @@
|
||||
# Nightly wiki maintenance for the Notes vault (k.renner/notes), replacing the
|
||||
# workstation's wiki-maintenance.timer: distill the raw/ backlog into wiki/,
|
||||
# then audit wiki/. Each agent is its own `claude` run, not a subagent of an
|
||||
# orchestrator, so nothing depends on background tasks in print mode (the
|
||||
# workstation job died on their 600 s ceiling). The pipeline commits and pushes
|
||||
# as Claude Bot; the push webhook then refreshes the wiki-index service.
|
||||
#
|
||||
# The agents and their retrieval scripts address the vault as ~/Notes, so each
|
||||
# agent step links $HOME/Notes to the checkout instead of rewriting them.
|
||||
when:
|
||||
- event: cron
|
||||
cron: wiki-maintenance
|
||||
# Hand-started runs: `woodpecker pipeline create k.renner/homelab --branch master`.
|
||||
# The typesafe_api_key secret is scoped to cron and manual, not deployment.
|
||||
- event: manual
|
||||
|
||||
steps:
|
||||
- name: checkout-notes
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
pull: true
|
||||
environment:
|
||||
WIKI_BOT_TOKEN:
|
||||
from_secret: wiki_bot_token
|
||||
commands:
|
||||
- |
|
||||
set -eu
|
||||
# Token only on the wire, never in .git/config. Secrets set from files carry
|
||||
# a trailing newline (run 10220: "could not read Username"); strip CR/LF.
|
||||
TOKEN=$$(printf '%s' "$$WIKI_BOT_TOKEN" | tr -d '\r\n')
|
||||
git -c http.extraheader="AUTHORIZATION: token $$TOKEN" \
|
||||
clone --depth 50 --branch main https://gitea.renner.uno/k.renner/notes.git notes
|
||||
echo "raw backlog: $$(ls notes/raw/*.md | grep -v _index.md | wc -l) file(s)"
|
||||
|
||||
- name: distill
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
pull: true
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
TYPESAFE_API_KEY:
|
||||
from_secret: typesafe_api_key
|
||||
WIKI_INDEX_BASIC_AUTH:
|
||||
from_secret: wiki_index_basic_auth
|
||||
commands:
|
||||
- |
|
||||
set -eu
|
||||
ln -sfn "$$PWD/notes" "$$HOME/Notes"
|
||||
mkdir -p "$$HOME/.config/wiki-index"
|
||||
printf '%s' "$$WIKI_INDEX_BASIC_AUTH" | tr -d '\r\n' > "$$HOME/.config/wiki-index/basic-auth"
|
||||
chmod 600 "$$HOME/.config/wiki-index/basic-auth"
|
||||
"$$HOME/Notes/bin/wiki_rank.py" --probe-index
|
||||
- |
|
||||
set -eu
|
||||
MODEL=$$(sh scripts/ci/pick-model.sh "distill raw source notes into atomic wiki notes: read sources, check the wiki for existing coverage, write or merge notes; careful writing, moderate reasoning")
|
||||
if [ -n "$$MODEL" ]; then echo "distill: selected model $$MODEL"; else echo "distill: using the CLI default model"; fi
|
||||
cd notes
|
||||
cat ../ci/agents/wiki-maintenance/distill.md | claude $${MODEL:+--model $$MODEL} \
|
||||
--agent wiki-distill \
|
||||
--permission-mode acceptEdits \
|
||||
--allowedTools "Read,Edit,Write,Glob,Grep,Bash(/root/Notes/bin/wiki_rank.py:*),Bash(/root/Notes/bin/wiki_ask.py:*),Bash(mv /root/Notes/raw/:*),Bash(mkdir -p /root/Notes/:*)" \
|
||||
--settings ../ci/agents/wiki-maintenance/settings.json \
|
||||
-p
|
||||
|
||||
- name: audit
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
pull: true
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
TYPESAFE_API_KEY:
|
||||
from_secret: typesafe_api_key
|
||||
WIKI_INDEX_BASIC_AUTH:
|
||||
from_secret: wiki_index_basic_auth
|
||||
commands:
|
||||
- |
|
||||
set -eu
|
||||
ln -sfn "$$PWD/notes" "$$HOME/Notes"
|
||||
mkdir -p "$$HOME/.config/wiki-index"
|
||||
printf '%s' "$$WIKI_INDEX_BASIC_AUTH" | tr -d '\r\n' > "$$HOME/.config/wiki-index/basic-auth"
|
||||
chmod 600 "$$HOME/.config/wiki-index/basic-auth"
|
||||
- |
|
||||
set -eu
|
||||
MODEL=$$(sh scripts/ci/pick-model.sh "structural audit of ~1700 markdown notes: frontmatter completeness, broken links, orphans; mechanical checks, little reasoning")
|
||||
if [ -n "$$MODEL" ]; then echo "audit: selected model $$MODEL"; else echo "audit: using the CLI default model"; fi
|
||||
cd notes
|
||||
cat ../ci/agents/wiki-maintenance/audit.md | claude $${MODEL:+--model $$MODEL} \
|
||||
--agent wiki-audit \
|
||||
--permission-mode acceptEdits \
|
||||
--allowedTools "Read,Write,Glob,Grep,Bash(/root/Notes/bin/wiki_rank.py:*),Bash(/root/Notes/bin/wiki_ask.py:*)" \
|
||||
--settings ../ci/agents/wiki-maintenance/settings.json \
|
||||
-p
|
||||
|
||||
- name: push-notes
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
pull: true
|
||||
environment:
|
||||
WIKI_BOT_TOKEN:
|
||||
from_secret: wiki_bot_token
|
||||
commands:
|
||||
- |
|
||||
set -eu
|
||||
cd notes
|
||||
git config user.email "claude-bot@renner.uno"
|
||||
git config user.name "Claude Bot"
|
||||
if git diff --quiet && git diff --cached --quiet && [ -z "$$(git ls-files --others --exclude-standard)" ]; then
|
||||
echo "wiki-maintenance: no changes"
|
||||
exit 0
|
||||
fi
|
||||
# The workstation pushes main on its own (notes-push.path), so main moves
|
||||
# while the agents run (run 10241: non-fast-forward). The run's work goes
|
||||
# to a branch and a PR that this step merges through Gitea; a conflict
|
||||
# leaves the PR open for a human and fails the run.
|
||||
BRANCH="wiki-maintenance/$$(date -u +%F)-$$CI_PIPELINE_NUMBER"
|
||||
git checkout -q -b "$$BRANCH"
|
||||
git add -A
|
||||
git commit -q -m "wiki maintenance $$(date -u +%F): distill + audit (CI run $$CI_PIPELINE_NUMBER)"
|
||||
TOKEN=$$(printf '%s' "$$WIKI_BOT_TOKEN" | tr -d '\r\n')
|
||||
git -c http.extraheader="AUTHORIZATION: token $$TOKEN" push origin "HEAD:$$BRANCH"
|
||||
API="https://gitea.renner.uno/api/v1/repos/k.renner/notes"
|
||||
jq -n --arg title "wiki maintenance $$(date -u +%F) (CI run $$CI_PIPELINE_NUMBER)" \
|
||||
--arg body "Nightly distill + audit. Merged by the pipeline; open only if the merge failed." \
|
||||
--arg head "$$BRANCH" '{title: $$title, body: $$body, head: $$head, base: "main"}' > /tmp/pr.json
|
||||
code=$$(curl -s -o /tmp/pr-resp.json -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token $$TOKEN" -H "Content-Type: application/json" --data @/tmp/pr.json "$$API/pulls")
|
||||
echo "POST pull -> HTTP $$code"
|
||||
[ "$$code" -ge 200 ] && [ "$$code" -lt 300 ] || { cat /tmp/pr-resp.json; exit 1; }
|
||||
NUM=$$(jq -r '.number' /tmp/pr-resp.json)
|
||||
code=$$(curl -s -o /tmp/merge-resp.json -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token $$TOKEN" -H "Content-Type: application/json" \
|
||||
--data '{"Do":"merge","delete_branch_after_merge":true}' "$$API/pulls/$$NUM/merge")
|
||||
echo "POST merge #$$NUM -> HTTP $$code"
|
||||
[ "$$code" -ge 200 ] && [ "$$code" -lt 300 ] || { cat /tmp/merge-resp.json; echo "PR #$$NUM left open"; exit 1; }
|
||||
@@ -0,0 +1,5 @@
|
||||
You are running as the scheduled wiki-maintenance job in CI. The Notes vault is checked out at ~/Notes (the repository k.renner/notes).
|
||||
|
||||
Run the standard audit pass over ~/Notes/wiki/ and write the dated report to ~/Notes/output/. Do not modify any wiki note. Do not commit or push; the pipeline does that.
|
||||
|
||||
End with two lines: notes scanned, and issues found by category.
|
||||
@@ -0,0 +1,7 @@
|
||||
You are running as the scheduled wiki-maintenance job in CI. The Notes vault is checked out at ~/Notes (the repository k.renner/notes). Do exactly this, nothing else:
|
||||
|
||||
Glob ~/Notes/raw/*.md (top level only, never ~/Notes/raw/_processed/). Distill every file you find into ~/Notes/wiki/ following your process, including the ranker check for every concept. Move each fully processed source to ~/Notes/raw/_processed/. If raw/ has no top-level files besides _index.md, say so and stop.
|
||||
|
||||
Do not commit or push; the pipeline does that after you finish. Do not touch anything outside ~/Notes/wiki/ and ~/Notes/raw/.
|
||||
|
||||
End with one line per file: which wiki notes it produced or merged into, or "nothing distillable".
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"//": "Permission scope for the wiki-maintenance agents (wiki-distill, wiki-audit) in CI.",
|
||||
"//2": "The step's environment holds a Gitea push token, a TypeSafe key and the wiki-index",
|
||||
"//3": "client credential, so the Read tool is kept away from /proc (env dumps), /root outside",
|
||||
"//4": "the vault symlink target, /etc and the checkout's .git/. The vault itself is the work.",
|
||||
"//5": "Mirrors ci/agents/issue-fix/settings.json; absolute globs because the vault is reached",
|
||||
"//6": "through the ~/Notes symlink the pipeline creates.",
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Read(//root/Notes/**)",
|
||||
"Edit(//root/Notes/wiki/**)",
|
||||
"Edit(//root/Notes/output/**)",
|
||||
"Edit(//root/Notes/raw/**)",
|
||||
"Bash(/root/Notes/bin/wiki_rank.py:*)",
|
||||
"Bash(/root/Notes/bin/wiki_ask.py:*)",
|
||||
"Bash(mv /root/Notes/raw/:*)",
|
||||
"Bash(mkdir -p /root/Notes/:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(date:*)",
|
||||
"Bash(wc:*)",
|
||||
"Bash(head:*)",
|
||||
"Bash(tail:*)"
|
||||
],
|
||||
"deny": [
|
||||
"Read(//proc/**)",
|
||||
"Read(//etc/**)",
|
||||
"Read(//root/.config/**)",
|
||||
"Read(//root/Notes/.git/**)",
|
||||
"Read(//root/Notes/_archive/**)",
|
||||
"Edit(//root/Notes/_archive/**)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(git:*)",
|
||||
"Bash(curl:*)",
|
||||
"Bash(wget:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,9 @@ RUN apk add --no-cache \
|
||||
git \
|
||||
jq \
|
||||
openssh-client \
|
||||
openssl
|
||||
openssl \
|
||||
python3 \
|
||||
py3-yaml
|
||||
|
||||
# Install Claude Code, then make the resulting layer byte-stable so that a
|
||||
# rebuild of the SAME version DEDUPS in the Gitea registry instead of orphaning
|
||||
|
||||
@@ -183,7 +183,7 @@ spec:
|
||||
drop: ["ALL"]
|
||||
containers:
|
||||
- name: paperless
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:3.2.0 # {"$imagepolicy": "flux-system:paperless-ngx"}
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:3.2.1 # {"$imagepolicy": "flux-system:paperless-ngx"}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
|
||||
Reference in New Issue
Block a user