Files
xh1-research-fork/xh1-research
T
2026-08-25 19:52:29 +02:00

1018 lines
18 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# XH-1 Research Harness
# Pure Bash / OpenAI-compatible API
# ============================================================
ROOT="${XH1_RESEARCH_ROOT:-research}"
STATE="${ROOT}/.xh1"
CONFIG="${XH1_RESEARCH_CONFIG:-xh1-research.conf}"
mkdir -p "$STATE"/{logs,responses}
# ------------------------------------------------------------
# Defaults
# ------------------------------------------------------------
API_BASE_URL="${API_BASE_URL:-https://api.openai.com/v1}"
API_KEY_ENV="${API_KEY_ENV:-OPENAI_API_KEY}"
MODEL="${MODEL:-}"
MAX_RETRIES="${MAX_RETRIES:-3}"
DELAY_SECONDS="${DELAY_SECONDS:-5}"
MAX_ITERATIONS="${MAX_ITERATIONS:-0}"
REVIEW_ENABLED="${REVIEW_ENABLED:-true}"
AUTO_COMMIT="${AUTO_COMMIT:-false}"
# ------------------------------------------------------------
# Helpers
# ------------------------------------------------------------
die() {
echo "ERROR: $*" >&2
exit 1
}
log() {
local msg="$*"
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $msg" \
| tee -a "$STATE/logs/harness.log"
}
require() {
command -v "$1" >/dev/null 2>&1 ||
die "Required command not found: $1"
}
load_config() {
if [[ -f "$CONFIG" ]]; then
# shellcheck disable=SC1090
source "$CONFIG"
fi
}
require_dependencies() {
require bash
require curl
require jq
}
get_api_key() {
local key="${!API_KEY_ENV:-}"
[[ -n "$key" ]] ||
die "API key missing. Set ${API_KEY_ENV}."
printf '%s' "$key"
}
timestamp() {
date -u '+%Y%m%dT%H%M%SZ'
}
# ------------------------------------------------------------
# Configuration
# ------------------------------------------------------------
init_config() {
if [[ -f "$CONFIG" ]]; then
echo "Configuration already exists:"
echo " $CONFIG"
return
fi
cat > "$CONFIG" <<'EOF'
# ============================================================
# XH-1 Research Harness Configuration
# ============================================================
# OpenAI-compatible API
API_BASE_URL="https://api.openai.com/v1"
# Environment variable containing API key
API_KEY_ENV="OPENAI_API_KEY"
# Model name
MODEL=""
# Research behavior
MAX_RETRIES=3
DELAY_SECONDS=5
MAX_ITERATIONS=0
# Run reviewer after researcher
REVIEW_ENABLED=true
# Automatically create git commits
AUTO_COMMIT=false
EOF
echo "Created $CONFIG"
}
# ------------------------------------------------------------
# API
# ------------------------------------------------------------
api_call() {
local system_prompt="$1"
local user_prompt="$2"
local output="$3"
local key
key="$(get_api_key)"
[[ -n "$MODEL" ]] ||
die "MODEL is not configured."
local payload
payload="$(
jq -n \
--arg model "$MODEL" \
--arg system "$system_prompt" \
--arg user "$user_prompt" \
'{
model: $model,
messages: [
{
role: "system",
content: $system
},
{
role: "user",
content: $user
}
],
temperature: 0.2
}'
)"
local attempt=1
while (( attempt <= MAX_RETRIES )); do
if curl \
--fail \
--silent \
--show-error \
--connect-timeout 30 \
--max-time 1800 \
-H "Authorization: Bearer ${key}" \
-H "Content-Type: application/json" \
"${API_BASE_URL%/}/chat/completions" \
-d "$payload" \
> "${output}.json"
then
if jq -er \
'.choices[0].message.content' \
"${output}.json" \
> "$output"
then
return 0
fi
fi
log "API attempt $attempt failed."
sleep $((attempt * attempt))
((attempt++))
done
return 1
}
# ------------------------------------------------------------
# Research discovery
# ------------------------------------------------------------
is_research_file() {
[[ "$1" == *.md ]] &&
[[ "$1" != "$STATE"/* ]]
}
is_soon() {
grep -qE \
'^SOON[[:space:]]*$' \
"$1"
}
find_next_task() {
while IFS= read -r -d '' file; do
if is_research_file "$file" &&
is_soon "$file"
then
printf '%s\n' "$file"
return 0
fi
done < <(
find "$ROOT" \
-type f \
-name '*.md' \
-not -path "$STATE/*" \
-print0
)
return 1
}
# ------------------------------------------------------------
# Research context
# ------------------------------------------------------------
build_context() {
local file="$1"
local relative
relative="${file#"$ROOT"/}"
local area
area="${relative%%/*}"
cat <<EOF
PROJECT
=======
Name:
XH-1
Architecture:
Custom 128-core RISC-V CPU
Research repository:
XH-1 Research
Research document:
$file
Research area:
$area
CURRENT DOCUMENT
================
EOF
cat "$file"
cat <<EOF
RELATED DOCUMENTS
=================
EOF
find "$ROOT/$area" \
-maxdepth 1 \
-type f \
-name '*.md' \
! -path "$file" \
| sort \
| head -20 \
| while read -r related; do
echo
echo "----- $related -----"
cat "$related"
done
}
# ------------------------------------------------------------
# Research prompt
# ------------------------------------------------------------
research_system_prompt() {
cat <<'EOF'
You are a senior computer architecture researcher working on XH-1.
XH-1 is a custom 128-core RISC-V processor.
Your job is to perform rigorous technical research for exactly one assigned research topic.
You are NOT allowed to invent:
- sources
- papers
- URLs
- measurements
- benchmarks
- experiments
- hardware capabilities
- implementation details
Clearly distinguish:
FACT
ASSUMPTION
PROPOSAL
RECOMMENDATION
OPEN QUESTION
Prefer primary and authoritative sources.
For RISC-V topics, prioritize official RISC-V specifications.
For architecture topics, consider academic literature, processor manuals,
technical papers, and reputable engineering documentation.
The fact that XH-1 contains 128 cores is extremely important.
Do not provide generic CPU advice without explaining how it affects
a 128-core processor.
Research should investigate multiple alternatives where applicable.
Consider:
- performance
- latency
- bandwidth
- area
- power
- scalability
- implementation complexity
- verification complexity
- software implications
- failure modes
- future extensibility
You are researching the architecture.
Do not assume an architectural decision has already been made.
If evidence is insufficient, explicitly say:
INSUFFICIENT EVIDENCE
Do not force a recommendation when the evidence does not justify one.
EOF
}
research_user_prompt() {
local file="$1"
build_context "$file"
cat <<'EOF'
RESEARCH TASK
=============
Investigate the assigned topic thoroughly.
Produce a technically detailed Markdown research document.
Use this structure where applicable:
# Topic
## Status
## Abstract
## Research Question
## Background
## Existing Approaches
## Alternative Designs
## Comparison
## Advantages
## Disadvantages
## XH-1 Considerations
## 128-Core Scalability
## Performance Considerations
## Implementation Considerations
## Verification Considerations
## Recommendation
## Confidence
## Open Questions
## Sources
Do not include conversational commentary.
The output must be suitable for insertion into the research repository.
EOF
}
# ------------------------------------------------------------
# Reviewer
# ------------------------------------------------------------
review_system_prompt() {
cat <<'EOF'
You are the independent technical reviewer for the XH-1
128-core RISC-V processor research project.
Your job is to aggressively review another research agent's work.
Check for:
1. factual errors
2. unsupported claims
3. hallucinated sources
4. incorrect RISC-V information
5. missing alternatives
6. weak reasoning
7. contradictions
8. unexplained assumptions
9. failure to consider 128 cores
10. unrealistic implementation claims
11. unsupported performance claims
12. missing verification concerns
13. unjustified recommendations
Do not rewrite the research.
Return exactly:
VERDICT: PASS
or:
VERDICT: FAIL
Then provide:
ISSUES:
- ...
REQUIRED_FIXES:
- ...
CONFIDENCE: HIGH/MEDIUM/LOW
EOF
}
review_document() {
local document="$1"
local output="$2"
api_call \
"$(review_system_prompt)" \
"$(cat "$document")" \
"$output"
}
# ------------------------------------------------------------
# File safety
# ------------------------------------------------------------
replace_soon() {
local target="$1"
local replacement="$2"
# Make absolutely sure we are still replacing a SOON document.
if ! is_soon "$target"; then
die "Refusing to overwrite modified document: $target"
fi
local temporary
temporary="$(mktemp)"
cat "$replacement" > "$temporary"
mv "$temporary" "$target"
}
# ------------------------------------------------------------
# Git
# ------------------------------------------------------------
git_commit() {
local file="$1"
[[ "$AUTO_COMMIT" == "true" ]] ||
return 0
git rev-parse \
--is-inside-work-tree \
>/dev/null 2>&1 ||
return 0
git add "$file"
local title
title="$(
basename "$file" .md |
tr '-' ' '
)"
git commit \
-m "research: ${title}" \
|| true
}
# ------------------------------------------------------------
# Research one document
# ------------------------------------------------------------
run_one() {
local file="${1:-}"
if [[ -z "$file" ]]; then
file="$(find_next_task || true)"
fi
[[ -n "$file" ]] ||
{
echo "No incomplete research topics."
return 0
}
[[ -f "$file" ]] ||
die "Research file does not exist: $file"
log "Starting research: $file"
local id
id="$(timestamp)_$(basename "$file" .md)"
local response
response="$STATE/responses/${id}.md"
log "Running researcher."
if ! api_call \
"$(research_system_prompt)" \
"$(research_user_prompt "$file")" \
"$response"
then
log "Research agent failed."
return 1
fi
log "Research completed."
# --------------------------------------------------------
# Review
# --------------------------------------------------------
if [[ "$REVIEW_ENABLED" == "true" ]]; then
local review
review="${response}.review"
log "Running reviewer."
if ! review_document \
"$response" \
"$review"
then
log "Reviewer failed."
return 1
fi
local verdict
verdict="$(
grep -m1 '^VERDICT:' "$review" |
tr -d '\r'
)"
echo
echo "=========================================="
echo "RESEARCH REVIEW"
echo "=========================================="
cat "$review"
echo "=========================================="
echo
if [[ "$verdict" != "VERDICT: PASS" ]]; then
log "Research rejected by reviewer."
return 2
fi
log "Research passed review."
fi
# --------------------------------------------------------
# Replace SOON
# --------------------------------------------------------
replace_soon "$file" "$response"
printf '%s\t%s\t%s\n' \
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
"completed" \
"$file" \
>> "$STATE/state.tsv"
git_commit "$file"
log "Completed: $file"
return 0
}
# ------------------------------------------------------------
# Loop
# ------------------------------------------------------------
run_loop() {
local iteration=0
while true; do
if (( MAX_ITERATIONS > 0 &&
iteration >= MAX_ITERATIONS )); then
log "Maximum iterations reached."
break
fi
local file
file="$(find_next_task || true)"
if [[ -z "$file" ]]; then
log "No SOON documents remain."
break
fi
echo
echo "=========================================="
echo "XH-1 RESEARCH ITERATION $((iteration + 1))"
echo "=========================================="
echo
echo "Topic:"
echo " $file"
echo
if run_one "$file"; then
log "Iteration successful."
else
log "Iteration failed."
echo
echo "Research failed."
echo "The document was NOT modified."
echo
fi
((iteration += 1))
sleep "$DELAY_SECONDS"
done
}
# ------------------------------------------------------------
# Status
# ------------------------------------------------------------
status() {
[[ -d "$ROOT" ]] ||
die "Research directory not found: $ROOT"
local total
local soon
local researched
total="$(
find "$ROOT" \
-type f \
-name '*.md' \
-not -path "$STATE/*" |
wc -l |
tr -d ' '
)"
soon="$(
grep -RIl \
'^SOON[[:space:]]*$' \
"$ROOT" \
--include='*.md' \
--exclude-dir='.xh1' \
2>/dev/null |
wc -l |
tr -d ' '
)"
researched=$((total - soon))
echo
echo "╔══════════════════════════════════════╗"
echo "║ XH-1 RESEARCH STATUS ║"
echo "╚══════════════════════════════════════╝"
echo
printf " Total documents : %s\n" "$total"
printf " Research ready : %s\n" "$researched"
printf " Remaining SOON : %s\n" "$soon"
if (( total > 0 )); then
awk \
-v done="$researched" \
-v total="$total" \
'BEGIN {
printf " Progress : %.1f%%\n",
(done / total) * 100
}'
fi
echo
echo "Next topic:"
find_next_task || echo " None"
echo
}
# ------------------------------------------------------------
# Review command
# ------------------------------------------------------------
review_command() {
local file="${1:-}"
[[ -n "$file" ]] ||
die "Usage: $0 review <file>"
[[ -f "$file" ]] ||
die "File does not exist: $file"
local output
output="$STATE/responses/manual_review_$(timestamp).txt"
review_document "$file" "$output"
cat "$output"
}
# ------------------------------------------------------------
# Initialization
# ------------------------------------------------------------
initialize() {
mkdir -p \
"$STATE/logs" \
"$STATE/responses"
touch \
"$STATE/state.tsv"
init_config
echo
echo "XH-1 research harness initialized."
echo
echo "Configuration:"
echo " $CONFIG"
echo
echo "State:"
echo " $STATE"
echo
}
# ------------------------------------------------------------
# Help
# ------------------------------------------------------------
help() {
cat <<'EOF'
XH-1 RESEARCH HARNESS
=====================
Pure Bash autonomous research system.
USAGE
./xh1-research <command>
COMMANDS
init
Initialize the harness.
status
Show research progress.
next
Show the next SOON document.
run
Research the next topic.
run <file>
Research a specific document.
run-all
Continuously research every SOON document.
loop
Alias for run-all.
resume
Resume autonomous research.
review <file>
Manually review a research document.
help
Show this help.
EXAMPLES
./xh1-research init
./xh1-research status
./xh1-research next
./xh1-research run
./xh1-research run research/04-128-core/topology.md
./xh1-research run-all
./xh1-research review research/05-memory/cache-coherency.md
ENVIRONMENT
XH1_RESEARCH_ROOT
Research directory.
Default:
research
XH1_RESEARCH_CONFIG
Configuration file.
Default:
xh1-research.conf
CONFIGURATION
API_BASE_URL
OpenAI-compatible API base.
API_KEY_ENV
Environment variable containing API key.
MODEL
Model identifier.
MAX_RETRIES
API retry count.
DELAY_SECONDS
Delay between research iterations.
MAX_ITERATIONS
Maximum autonomous iterations.
0 = unlimited.
REVIEW_ENABLED
Enable reviewer agent.
AUTO_COMMIT
Automatically commit completed research.
SAFETY
Existing research is never overwritten.
A document is only modified if it still contains
a standalone SOON marker.
Git push is never performed automatically.
EOF
}
# ------------------------------------------------------------
# Main
# ------------------------------------------------------------
load_config
require_dependencies
COMMAND="${1:-status}"
case "$COMMAND" in
init)
initialize
;;
status)
status
;;
next)
find_next_task || true
;;
run)
run_one "${2:-}"
;;
run-all|loop|resume)
run_loop
;;
review)
review_command "${2:-}"
;;
help|-h|--help)
help
;;
*)
die "Unknown command: $COMMAND. Use --help."
esac