Compare commits
5 Commits
step35/339
...
step35/429
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
682a1c836d | ||
|
|
ba4220d5ed | ||
|
|
2451f38bee | ||
|
|
54093991ab | ||
|
|
1ea6bf6e33 |
@@ -129,20 +129,42 @@ Preserved by timmy-orchestrator to prevent loss." 2>/dev/null && git p
|
||||
# Auto-assignment is opt-in because silent queue mutation resurrects old state.
|
||||
if [ "$unassigned_count" -gt 0 ]; then
|
||||
if [ "$AUTO_ASSIGN_UNASSIGNED" = "1" ]; then
|
||||
log "Assigning $unassigned_count issues to claude..."
|
||||
while IFS= read -r line; do
|
||||
local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*/\1/')
|
||||
local num=$(echo "$line" | sed 's/.*NUM=\([^ ]*\).*/\1/')
|
||||
curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$repo/issues/$num" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"assignees":["claude"]}' >/dev/null 2>&1 && \
|
||||
log " Assigned #$num ($repo) to claude"
|
||||
done < "$state_dir/unassigned.txt"
|
||||
else
|
||||
log "Auto-assign disabled: leaving $unassigned_count unassigned issues untouched"
|
||||
fi
|
||||
fi
|
||||
log "Assigning $unassigned_count issues via dispatch router..."
|
||||
DISPATCH_LOG="$LOG_DIR/dispatch_decisions.log"
|
||||
while IFS= read -r line; do
|
||||
local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*//')
|
||||
local num=$(echo "$line" | sed 's/.*NUM=\([^ ]*\).*//')
|
||||
local title=$(echo "$line" | sed 's/.*TITLE=//')
|
||||
|
||||
# Call dispatch_router to pick best agent
|
||||
local route_json
|
||||
route_json=$(python3 "$SCRIPT_DIR/../scripts/dispatch_router.py" "$title" "$repo" 2>/dev/null) || route_json=""
|
||||
|
||||
local recommended_agent="claude" # fallback
|
||||
local route_category="unknown"
|
||||
local route_score="0"
|
||||
local route_reason="fallback"
|
||||
|
||||
if [ -n "$route_json" ]; then
|
||||
recommended_agent=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('recommended_agent','claude'))" 2>/dev/null || echo "claude")
|
||||
route_score=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('score',0))" 2>/dev/null || echo "0")
|
||||
route_category=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('category','unknown'))" 2>/dev/null || echo "unknown")
|
||||
route_reason=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('reason',''))" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Assign via API
|
||||
curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$repo/issues/$num" \\
|
||||
-H "Authorization: token $GITEA_TOKEN" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d "{\"assignees\":[\"$recommended_agent\"]}" >/dev/null 2>&1 && \\
|
||||
log " Assigned #$num ($repo) to $recommended_agent [score=$route_score cat=$route_category]"
|
||||
|
||||
# Log dispatch decision for audit (RFC3339 timestamp)
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$num" "$repo" "$title" "$recommended_agent" "$route_score" "$route_category|$route_reason" \
|
||||
>> "$DISPATCH_LOG"
|
||||
done < "$state_dir/unassigned.txt"
|
||||
else fi
|
||||
|
||||
# Phase 2: PR review via Timmy (LLM)
|
||||
if [ "$pr_count" -gt 0 ]; then
|
||||
|
||||
38
wizards/bezalel/emacs-daemon-start.sh
Executable file
38
wizards/bezalel/emacs-daemon-start.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# emacs-daemon-start.sh — Bezalel Emacs daemon launcher
|
||||
# =============================================================================
|
||||
# Starts Emacs daemon with a shared socket for multi-user access.
|
||||
# Creates socket dir with group=fleet, mode=2775 (setgid) so all agents
|
||||
# can connect via emacsclient -s bezalel.
|
||||
#
|
||||
# Acceptance: emacsclient -s bezalel -e "(+ 1 1)" returns 2
|
||||
# =============================================================================
|
||||
|
||||
DAEMON_NAME="bezalel"
|
||||
SOCKET_DIR="/srv/fleet/emacs/socket"
|
||||
LOG_DIR="/srv/fleet/logs"
|
||||
EMACS_CONFIG="/root/wizards/bezalel/emacs-daemon.el"
|
||||
|
||||
# Create shared directories with correct group/permissions
|
||||
# (fleet group created separately; ignore errors if already correct)
|
||||
mkdir -p "$SOCKET_DIR" "$LOG_DIR"
|
||||
chmod 2775 "$SOCKET_DIR" 2>/dev/null || true
|
||||
chmod 755 "$LOG_DIR" 2>/dev/null || true
|
||||
chgrp fleet "$SOCKET_DIR" "$LOG_DIR" 2>/dev/null || true
|
||||
|
||||
# Ensure config exists
|
||||
if [ ! -f "$EMACS_CONFIG" ]; then
|
||||
echo "ERROR: Emacs config not found at $EMACS_CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start daemon if not already running
|
||||
if ! pgrep -f "emacs --daemon=$DAEMON_NAME" > /dev/null 2>&1; then
|
||||
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting Emacs daemon '$DAEMON_NAME'..."
|
||||
exec emacs --daemon="$DAEMON_NAME" --socket-dir="$SOCKET_DIR" -l "$EMACS_CONFIG"
|
||||
else
|
||||
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Emacs daemon already running."
|
||||
fi
|
||||
60
wizards/bezalel/emacs-daemon.el
Normal file
60
wizards/bezalel/emacs-daemon.el
Normal file
@@ -0,0 +1,60 @@
|
||||
;; Emacs Daemon Configuration — Bezalel
|
||||
;; - Shared socket at /srv/fleet/emacs/socket/bezalel
|
||||
;; - Audit trail to /srv/fleet/logs/emacs-audit.log
|
||||
;; - Per-user identity via $USER environment variable
|
||||
;; - All evals are logged with user, expression, result, timestamp
|
||||
|
||||
;; Server setup
|
||||
(require 'server)
|
||||
(setq server-auth-dir "/srv/fleet/emacs/socket/") ; shared socket dir
|
||||
(setq server-name "bezalel") ; socket = bezalel
|
||||
(setq server-socket-dir server-auth-dir)
|
||||
;; Do not alter socket prefix — keep exact name
|
||||
(setq server-socket-mod-group-permissions t) ; allow group access
|
||||
|
||||
;; Start server if not already running
|
||||
(unless (server-running-p)
|
||||
(server-start))
|
||||
|
||||
;; Audit log setup
|
||||
(defvar bezalel-audit-log "/srv/fleet/logs/emacs-audit.log"
|
||||
"Path to Emacs daemon audit log for per-user traceability.")
|
||||
|
||||
(defun bezalel-audit-log-entry (user expression result status)
|
||||
"Log an audit entry: timestamp, user, expression, result status."
|
||||
(with-temp-buffer
|
||||
(insert (format "[%s] user=%s expr=%s status=%s\n"
|
||||
(format-time-string "%Y-%m-%dT%H:%M:%SZ" (current-time))
|
||||
(or user (getenv "USER") "unknown")
|
||||
(replace-regexp-in-string "\n" "\\n" expression)
|
||||
status))
|
||||
(write-region (point-min) (point-max) bezalel-audit-log t)))
|
||||
|
||||
;; Wrap server-eval-and-print to audit every request
|
||||
(defun bezalel--server-eval-and-print-around (orig-fun &rest args)
|
||||
"Around advice for `server-eval-and-print': log user, expression, result."
|
||||
(let* ((expr (car args))
|
||||
(user (or (getenv "SUDO_USER") (getenv "USER") "unknown"))
|
||||
result
|
||||
status)
|
||||
(condition-case err
|
||||
(progn
|
||||
(setq result (apply orig-fun args))
|
||||
(setq status "success")
|
||||
(bezalel-audit-log-entry user expr (prin1-to-string result) status)
|
||||
result)
|
||||
(error
|
||||
(setq result (error-message-string err))
|
||||
(setq status "error")
|
||||
(bezalel-audit-log-entry user expr result status)
|
||||
(signal (car err) (cdr err))))))
|
||||
|
||||
(advice-add 'server-eval-and-print :around #'bezalel--server-eval-and-print-around)
|
||||
|
||||
;; Ensure audit directory exists at startup
|
||||
(let ((log-dir (file-name-directory bezalel-audit-log)))
|
||||
(when (and (not (file-exists-p log-dir))
|
||||
(file-writable-p (expand-file-name "../.." log-dir)))
|
||||
(make-directory log-dir t)))
|
||||
|
||||
(provide 'emacs-daemon)
|
||||
Reference in New Issue
Block a user