diff --git a/.env.example b/.env.example index 6af0c6d..81a1bac 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ ############################################################################### -# GLPI NEUROFORGE MEGA v1.5.9 - VOLLSTÄNDIGE .ENV.example +# GLPI NEUROFORGE MEGA v1.6.0 - VOLLSTÄNDIGE .ENV.example # # Diese Datei ist die zentrale Konfiguration für docker compose. # Sie enthält: @@ -29,7 +29,7 @@ # 01. MEGA STACK - RELEASE / HOST PORTS / PFADE ############################################################################### # Immutable Registry-Tag der sechs Projekt-Images. "latest" ist produktiv verboten. -IMAGE_TAG=1.5.9 +IMAGE_TAG=1.6.2 CONTROL_HOST_PORT=8070 AGENT_HOST_PORT=8080 @@ -193,6 +193,50 @@ NEUROFORGE_KB_STAGING_MAX_ARTICLE_CHARS=10000 NEUROFORGE_KB_STAGING_MIN_ANSWER_CHARS=160 NEUROFORGE_KB_STAGING_MAX_ANSWER_CHARS=1200 +############################################################################### +# 07B. MASTER / SUBAGENTS / DURABLE ORCHESTRATOR / KNOWLEDGE GRAPH +############################################################################### +# Der NeuroForge-Master hält den autoritativen Zustand. Subagents ziehen nur +# capability-passende Jobs und arbeiten unter lease/heartbeat fencing. +NEUROFORGE_WORKER_LEASE_SECONDS=120 +NEUROFORGE_WORKER_HEARTBEAT_SECONDS=15 +NEUROFORGE_WORKER_STALE_AFTER_SECONDS=60 +NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS=3 +NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS=15 +NEUROFORGE_WORKER_MAX_QUEUED_JOBS=5000 +NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128 +# Worker-Ergebnisse, die Master-State verändern (z.B. vector.relink), werden +# zweiphasig persistiert und bei Master-Neustart idempotent weiter angewendet. +NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS=5 +NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS=5 +NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24 +NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000 +# Lokale Compose-Subagents. Zusätzliche Remote-Subagents können mit +# docker-compose.subagent.yml auf anderen Hosts gestartet werden. +NEUROFORGE_CPU_WORKER_CONCURRENCY=2 +NEUROFORGE_GPU_WORKER_CONCURRENCY=1 +NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s +# Bestehende/importierte knowledge.chunk-Memories werden kontrolliert zu einem +# n:m-Graphen verlinkt. Kein O(N²)-Queue-Burst: ANN-Kandidaten + bounded queue. +NEUROFORGE_GRAPH_BACKFILL_ENABLED=true +NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS=10 +NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16 +NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64 +NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE=3 +NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER=6 +NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES=360 +NEUROFORGE_GRAPH_REQUIRE_WORKER=true +# Retrieval kann echte mehrstufige Assoziationsketten verfolgen. +NEUROFORGE_GRAPH_MAX_HOPS=3 +NEUROFORGE_GRAPH_HOP_DECAY=0.60 +NEUROFORGE_GRAPH_MAX_EXPANSION=64 +NEUROFORGE_GRAPH_MIN_EDGE_WEIGHT=0.05 +# LLM/Embedding-Aufgaben werden bevorzugt an GPU-Subagents delegiert. Bei nicht +# verfügbarem GPU-Subagent fällt der Master auf seinen Provider-Router zurück. +NEUROFORGE_OFFLOAD_CHAT=true +NEUROFORGE_OFFLOAD_EMBEDDINGS=true +NEUROFORGE_DISTRIBUTED_INFERENCE_WAIT_SECONDS=180 + ############################################################################### # 08. OPTIONAL CODEBASE MEMORY MCP / ENGINEERING UI ############################################################################### diff --git a/.env.subagent.example b/.env.subagent.example new file mode 100644 index 0000000..3276990 --- /dev/null +++ b/.env.subagent.example @@ -0,0 +1,20 @@ +# Remote NeuroForge subagent host (v1.6.1) +IMAGE_TAG=1.6.1 +NEUROFORGE_MASTER_URL=https://neuroforge.internal.example +NEUROFORGE_WORKER_TOKEN=CHANGE_ME_WORKER_TOKEN_SHARED_WITH_MASTER +NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s + +# CPU profile +NEUROFORGE_CPU_WORKER_ID=cpu-node-01 +NEUROFORGE_CPU_WORKER_CAPABILITIES=cpu,vector.relink +NEUROFORGE_CPU_WORKER_CONCURRENCY=2 + +# GPU profile +NEUROFORGE_GPU_WORKER_ID=gpu-node-01 +NEUROFORGE_GPU_WORKER_CAPABILITIES=gpu,model.chat,model.embed +NEUROFORGE_GPU_WORKER_CONCURRENCY=1 +NEUROFORGE_WORKER_OLLAMA_URL=http://host.docker.internal:11434 +OLLAMA_MODEL=gemma4 +OLLAMA_EMBEDDING_MODEL=embeddinggemma +NEUROFORGE_OLLAMA_NUM_CTX=8192 +OLLAMA_KEEP_ALIVE=10m diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..1c80f04 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,86 @@ +name: mega-ci + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + go-quality: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + module: + - platform/neuroforge + - services/agent + - services/knowledge + - services/control + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: ${{ matrix.module }}/go.mod + cache-dependency-path: ${{ matrix.module }}/go.mod + + - name: Test + working-directory: ${{ matrix.module }} + run: go test ./... + + - name: Vet + working-directory: ${{ matrix.module }} + run: go vet ./... + + - name: Build + working-directory: ${{ matrix.module }} + run: go build ./... + + static-release-gates: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PyYAML + run: python3 -m pip install --user PyYAML + + - name: Static production preflight + run: ./scripts/preflight.sh --static + + - name: Compose environment isolation + run: ./scripts/check-compose-env.py + + - name: Secret scan + run: ./scripts/secret-scan.sh + + - name: YAML syntax check + shell: bash + run: | + python3 - <<'PY' + from pathlib import Path + import yaml + files = list(Path('.').rglob('*.yml')) + list(Path('.').rglob('*.yaml')) + for p in files: + if '.git' in p.parts: + continue + with p.open(encoding='utf-8') as f: + yaml.safe_load(f) + print(f'parsed {len(files)} YAML files') + PY + + docker-build: + runs-on: ubuntu-latest + needs: [go-quality, static-release-gates] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build all project images + run: docker buildx bake --set '*.output=type=cacheonly' diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..d69e1d5 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,145 @@ +name: mega-release + +on: + push: + tags: + - 'v*' + +jobs: + release-images: + runs-on: ubuntu-latest + env: + REGISTRY: git.send.nrw/sendnrw + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate immutable release version + id: meta + shell: bash + run: | + set -euo pipefail + REF_NAME="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" + VERSION="${REF_NAME#v}" + test -n "$VERSION" + FILE_VERSION="$(tr -d '\r\n' < VERSION)" + test "$VERSION" = "$FILE_VERSION" || { + echo "Tag version $VERSION does not match VERSION=$FILE_VERSION" >&2 + exit 1 + } + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + config-inline: | + [registry."git.send.nrw"] + http = true + insecure = true + + - name: Login to registry + uses: docker/login-action@v3 + with: + registry: git.send.nrw + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + + - name: Push NeuroForge server + uses: docker/build-push-action@v6 + with: + context: ./platform/neuroforge + file: ./platform/neuroforge/Dockerfile + target: server + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-neuroforge:${{ steps.meta.outputs.version }} + + - name: Push NeuroForge worker + uses: docker/build-push-action@v6 + with: + context: ./platform/neuroforge + file: ./platform/neuroforge/Dockerfile + target: worker + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-neuroforge-worker:${{ steps.meta.outputs.version }} + + - name: Push Agent + uses: docker/build-push-action@v6 + with: + context: ./services/agent + file: ./services/agent/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-agent:${{ steps.meta.outputs.version }} + + - name: Push Agent data-init + uses: docker/build-push-action@v6 + with: + context: ./services/agent + file: ./services/agent/Dockerfile + target: data-init + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-agent-data-init:${{ steps.meta.outputs.version }} + + - name: Push Knowledge + uses: docker/build-push-action@v6 + with: + context: ./services/knowledge + file: ./services/knowledge/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-knowledge:${{ steps.meta.outputs.version }} + + - name: Push Control + uses: docker/build-push-action@v6 + with: + context: ./services/control + file: ./services/control/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-control:${{ steps.meta.outputs.version }} + + release-archive: + needs: release-images + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate and package + shell: bash + run: | + set -euo pipefail + REF_NAME="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" + VERSION="${REF_NAME#v}" + test "$VERSION" = "$(tr -d '\r\n' < VERSION)" + ./scripts/secret-scan.sh + find . -type f ! -path './.git/*' ! -name MANIFEST.sha256 -print0 \ + | sort -z \ + | xargs -0 sha256sum > MANIFEST.sha256 + ROOT_DIR="$(pwd)" + PKG_DIR="../glpi-neuroforge-mega-v${VERSION}-gitea" + rm -rf "$PKG_DIR" + mkdir -p "$PKG_DIR" + tar --exclude=.git -cf - . | (cd "$PKG_DIR" && tar -xf -) + cd .. + zip -qr "glpi-neuroforge-mega-v${VERSION}-gitea.zip" "glpi-neuroforge-mega-v${VERSION}-gitea" + sha256sum "glpi-neuroforge-mega-v${VERSION}-gitea.zip" > "glpi-neuroforge-mega-v${VERSION}-gitea.zip.sha256" + + - name: Upload release archive + uses: actions/upload-artifact@v4 + with: + name: glpi-neuroforge-mega-${{ gitea.ref_name }} + path: | + ../glpi-neuroforge-mega-*.zip + ../glpi-neuroforge-mega-*.zip.sha256 diff --git a/BUILD-AND-RELEASE.md b/BUILD-AND-RELEASE.md new file mode 100644 index 0000000..8aa6a12 --- /dev/null +++ b/BUILD-AND-RELEASE.md @@ -0,0 +1,54 @@ +# Build & Release — GLPI NeuroForge Mega v1.6.2 + +The repository has one canonical image/release pipeline. Nested service workflows are intentionally not used. + +## Project images + +`docker buildx bake` builds all six immutable project images: + +- `glpi-neuroforge-mega-neuroforge` +- `glpi-neuroforge-mega-neuroforge-worker` +- `glpi-neuroforge-mega-agent` +- `glpi-neuroforge-mega-agent-data-init` +- `glpi-neuroforge-mega-knowledge` +- `glpi-neuroforge-mega-control` + +Default registry/tag: + +```text +git.send.nrw/sendnrw/:1.6.2 +``` + +Override without editing the bake file: + +```bash +IMAGE_TAG=1.6.2 REGISTRY=git.send.nrw/sendnrw docker buildx bake +``` + +## Gitea Actions + +`.gitea/workflows/ci.yml` runs test/vet/build for all four Go modules, static release checks and Docker builds. + +`.gitea/workflows/release.yml` runs only on immutable `v*` tags. The tag must exactly match the root `VERSION` file. It pushes all six project images with the version tag only; no production dependency on `latest` is introduced. + +Required registry secrets: + +```text +DOCKER_USERNAME +DOCKER_PASSWORD +``` + +Release example: + +```bash +git tag v1.6.2 +git push origin v1.6.2 +``` + +## Local source gate + +```bash +./scripts/release-gate.sh +``` + +This executes static production checks, Compose environment isolation, secret scanning, graph reproducibility, test/vet/build for every module, and targeted race checks. diff --git a/DISTRIBUTED-DEPLOYMENT-BUNDLE.md b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md index e76aab7..812078f 100644 --- a/DISTRIBUTED-DEPLOYMENT-BUNDLE.md +++ b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md @@ -1,5 +1,13 @@ -# v1.6.0 Distributed Deployment Bundle +# Distributed Deployment — v1.6.2 -This is the unchanged v1.6.0 application release plus ready-to-edit deployment kits under `deployments/`. No application code or persisted data format was changed. +The distributed deployment is part of the canonical release, not a separate code fork. -See `deployments/README.md`. +- `deployments/master`: authoritative NeuroForge Master, GLPI Agent, Knowledge, Control, optional Research, Prometheus and Grafana. +- `deployments/cpu-subagent`: remote CPU execution node for graph/relink work. +- `deployments/gpu-subagent`: remote GPU execution node plus Ollama for chat/embedding work. + +All three roles ship with complete `.env` templates containing `CHANGE_ME_...` placeholders. The same real `NEUROFORGE_WORKER_TOKEN` must be configured on Master and both worker roles. + +Recommended startup order: GPU subagent, CPU subagent, then Master. Keep the NeuroForge data volume authoritative on the Master only. + +The v1.6.1 recovery/OOM fixes are included unchanged in v1.6.2. Do not delete the existing `neuroforge-data` volume when upgrading. diff --git a/MANIFEST.sha256 b/MANIFEST.sha256 index f5fab8e..1031d68 100644 --- a/MANIFEST.sha256 +++ b/MANIFEST.sha256 @@ -1,615 +1,640 @@ -27dc46be5cbb1b171deff7fbd2f28bff1be802dff403797535fd8968bb98c8eb ./.cbmignore -94765e7c36c2e99c38af0b0d1193583b472fe49df014206b1f4c41b6e4f343ee ./.env.example -948661c795abfc3ed92726b43406ad2ca51cb4a2cd32d22dbc4093db75f74b62 ./.env.subagent.example -ed22fda7661db8203563611dc144998161cd024e161b0471d615eaf0defeb7db ./.gitea/workflows/release-tag.yml -e1ff71187cc3411a85067b964264011db7bd109a585ef7b9ea5b08bda039d813 ./.gitignore -9c4258c6f68d01688ca1ea6959ed423601fab6b1523217c1d643f7d99acdd0c2 ./DISTRIBUTED-DEPLOYMENT-BUNDLE.md -ccfc4c139345a69d05cd2b809b0d09b4332d98e5b4f79aace4db0b648e3ea814 ./Makefile -339c16bbeacd70b1ab21bd1e482a31999594b32ed3ea865591f345e843e99c0e ./README.md -4858caa52c0fb6cf302a1c581d07d448e5e90e0daa797e5819610fd6223bd348 ./RELEASE-NOTES-v1.1.0.md -01163462f46314f57660677fdef407c6c2884412ea850aab13a4f650e8c29f50 ./RELEASE-NOTES-v1.2.0.md -4da388ce660aa3b7a0d8075ec066a025b5437960973397360fcb9a5d4cb58c96 ./RELEASE-NOTES-v1.3.0.md -257d6d3ea1d22b8ab97adb5d35ca5ac806702d75cea66982dc711754040fc7a2 ./RELEASE-NOTES-v1.4.0.md -0723a0151b72d492899a5c5409e1fef37de1fe4c458c3053f2f7eccd4b6e70c5 ./RELEASE-NOTES-v1.4.1.md -58923d3292f11487526b8dc4d6c0992025c0f946df8435fde5c91ef4581d46fd ./RELEASE-NOTES-v1.4.2.md -548f3168f8f29add4f510c4c8048a74465f311dbc3ba2436e5b8db5cc898c552 ./RELEASE-NOTES-v1.4.3.md -61ceebe5a891388336795fda2fd0d1ad10373b4ebaae7d2e670f35766115c604 ./RELEASE-NOTES-v1.4.4.md -15a3defdd5bbf07ebaec1a2e8626347a027a3622f10afb508f7a21c9b0bdfa71 ./RELEASE-NOTES-v1.4.5.md -d10b1dbd87585d72144d816449800dc17b6d7f8faba2dfb4eca93ae3def2c631 ./RELEASE-NOTES-v1.5.0.md -d069259c99322966b525ab451defd6386ea182bdbdba58122e287df395e2396e ./RELEASE-NOTES-v1.6.0.md -cb1d9a30bc4ab8a397f39b7d786bb82aca0b816077eefc35d7c822017d75f6a5 ./VERSION -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./backups/.gitkeep -8127e9db5e5e0af1d88770dc8fa60b381de45dbcc843262698cf9501409b4d58 ./deploy/searxng/settings.yml -db75f62ad947313abd1f0b9018a8bb3f7c05dadca2e97ffd429cf64ddaf72d31 ./deployments/README.md -433d44ed0ca5f2133246ad73818c6e48bb676cfcb3f635a51aec0c38380b19c3 ./deployments/cpu-subagent/.env -c14fe9d60e1e9c216fef80d3b596b670c56231dae72f94f9461c809ac4f24504 ./deployments/cpu-subagent/README.md -fa1f4ebcf753f7d3ba7b49a1bf9b9738d2f9f3cf1b88b3ed4403a819de196b4a ./deployments/cpu-subagent/docker-compose.yml -5ebe674fff45df57eb2ade09565d732c39f094d3bc4168b3ac8db5504b86094b ./deployments/cpu-subagent/preflight.sh -433a91b265b4f9daf1b964b3cca859905b58de7f846771ddfdd8b5e7c112b876 ./deployments/gpu-subagent/.env -8bd16c76f2da34f50e827e131eefb260b1c56b7263f8b92b42795fdb719e9f40 ./deployments/gpu-subagent/README.md -89dbaa1fa401a02ac9242450fa3119caf3d4f63e66d5b25af4cb100f1a8be351 ./deployments/gpu-subagent/docker-compose.yml -82803ed2628259b8f0cb8df5236099ef21dbd2bae299d2b0664dc8822b01b222 ./deployments/gpu-subagent/preflight.sh -77b97a11eb962a5e855a80b63b4598b2f35a5110cfba552d6cd509f3ca49bfd6 ./deployments/master/.env -16f8e8224c0c46e5c79f35b322695046edb13e608d176cb0b9f25590f11b6b01 ./deployments/master/README.md -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./deployments/master/backups/.gitkeep -8127e9db5e5e0af1d88770dc8fa60b381de45dbcc843262698cf9501409b4d58 ./deployments/master/deploy/searxng/settings.yml -bb00045a607dab67892030674567d3e3ab9c1a9e9f1b0a7177f127edc1747059 ./deployments/master/docker-compose.yml -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./deployments/master/knowledge/.gitkeep -6cd2e5bdf4c2a213f548e4f7a139faeeb56ff74f54e6da72e430fbd71342d64d ./deployments/master/monitoring/README.md -161c46febb7c00b7e8d3f71c231d673eb9ca20ad39d03a22de6f5f00a70ab322 ./deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json -2c7c42913333222438dc3dd9baaab21aa28bd9aff033a75f85c6981da00325be ./deployments/master/monitoring/grafana/provisioning/dashboards/dashboards.yml -725d7ae42e32230bc315c45b09d250332d8617b5c65b03e4da906c669fe20905 ./deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml -f2aea5d7ba2c22e4de4aef4d1c1dca7f4d5998a4ceae16eff0ab1a05e79bd8f7 ./deployments/master/monitoring/prometheus/alerts.yml -aac90d7e75a79256f77d730ca80fc1824e86f4299192526910b6f0226cedcbb9 ./deployments/master/monitoring/prometheus/prometheus.yml.template -5e3459ce74ef9f754e4a8439977f7da21563dc6863f760f49ef8699fe56ff603 ./deployments/master/monitoring/validate.sh -a59ce38c9b9d3d9a6abf22111530335a1e23a9b45665091655530b86d8ba2ae1 ./deployments/master/preflight.sh -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./deployments/master/staging/.gitkeep -58db2a87b703a5bbddc20b140907d187f8388e7db87b079ae9f6ffaac9c121fc ./docker-compose.dev.yml -3764108384c468f4f90f45458f554db7194e7d25cee2e20cdbfe8cf257acc620 ./docker-compose.subagent.yml -3155a644e4e3019959b6e20b4bcf3eef4e5e9bb78362b0e42a63ef8e704422d3 ./docker-compose.yml -e4c516375e450c43b5193b1257880c02f2132b05a16e19ab1c96d57182fa070c ./docs/ARCHITECTURE.md -34e2c7ac5992389cc51b40f740c1e206f20bffc67bdf19cb59d94b5df67a7082 ./docs/BACKUP-RESTORE-v1.5.0.md -9467a3c0796a87bada0eab6ad191913e628b4abc05520cbb4b73fac76d334481 ./docs/CODEBASE-MEMORY-MCP.md -74cab9b3ce3f6ed2bb5f1de99e1fe5c6f311e90550d078e10e140b726674957e ./docs/CONTROL-CENTER.md -88955b0ea289ccc63cbd3404f5b00bb446b38270408dcee23a6103a698f1f76b ./docs/CONTROL-MATRIX.md -323402668da4d8e1d97a29c45db753834f6c3a16739eebebf7e016f06af160bf ./docs/CONTROLLED-AUTONOMY.md -51b4a8d7daff787f099b77bd5a33ebf8038c0812bd3056f0ee6afe17e103681d ./docs/ENV-MIGRATION-FROM-LEGACY.md -a19522149d097ede5751dd6b9db515146ac47d14aaa7a7b49515e495388d0e0e ./docs/ENVIRONMENT.md -9c49332c6d0e23cd7f82adf23f61bcbd795ad30401990ae99b23295b2ab4cd09 ./docs/GO-LIVE-v1.5.0.md -6a9862c2ffa95420a3f8d2c3901a46f00c77a38826913280bd0e367e3133498f ./docs/IMPLEMENTED.md -65ab928c3a0c283131e48322685d240507a085c1fb059d4c114465774473c335 ./docs/MASTER-SUBAGENT-ORCHESTRATOR.md -9c38807cc12fba6f94cadc4694996de58e6d2d5396e55fc0c4bd404bb13f53ad ./docs/MIGRATION-CUTOVER.md -a71d28353529906cee08cf90f35e0b0cf97bbc94ccfcef830145a2d561b21a9c ./docs/MIGRATION-MANIFEST.md -4330b3adefd8c40d174e1e878ff7cf075d99a44ec51251953d1a58d3a0fa8ca3 ./docs/MIGRATION-v1.1.0-to-v1.2.0.md -46672e5984cdcf5f31e88dccc273041aa277552cc9c5ecbf59a46b3de133da7b ./docs/MIGRATION-v1.2.0-to-v1.3.0.md -3738dc79be0598316dc397f1fcc71cedda67b604a3038b116c2ea001dc3105ce ./docs/MIGRATION-v1.3.0-to-v1.4.0.md -b285d050223844f5fd05014c2278199eaccd7525fbbf8a6dae724ed87ced0ee8 ./docs/MIGRATION-v1.4.4-to-v1.4.5.md -57858f4f2b6670106edf5a88ce705649d547c65c26407d6117040296122a0e26 ./docs/MIGRATION-v1.4.5-to-v1.5.0.md -c905084e03788887894e37c040492b8ce2386ecc25ac3ab2e10bfd4b0a7cd167 ./docs/MIGRATION-v1.5.0-to-v1.5.1.md -12619874bce206ed6e817685d2def62244d6bcceb6eeccbca9b0936fed74b8a2 ./docs/MIGRATION-v1.5.1-to-v1.5.2.md -44219c5fe32a0d6d2c32719e243793a54f3b7624cb45a497cda78c4a443774ae ./docs/MIGRATION-v1.5.2-to-v1.5.3.md -fd78b77e18277a8bb81cf6ad33e67c0e7fae3977dc1bbf74787b0cb0cb9a4b4e ./docs/MIGRATION-v1.5.3-to-v1.5.4.md -4e2e595c0c1b78271a971f6e039c48416114ad9916ad7afc5293c2cff4ec8f9d ./docs/MIGRATION-v1.5.4-to-v1.5.5.md -b38aaf9d352ad70a9701566fc24d73d26c50c407291fe1c38b08b1afad9435bc ./docs/MIGRATION-v1.5.5-to-v1.5.6.md -b57e66a589cff7331d547afc0e031accd1d576c46c013143f76311a84eacbcfe ./docs/MIGRATION-v1.5.6-to-v1.5.7.md -6c234e8bfcfa63f879f0748ccf9ca006595f5ee905b97d316f685154a4a5a2b9 ./docs/MIGRATION-v1.5.7-to-v1.5.8.md -91e8541a86f3b6b2080439b3177309b584f5d6c637baa286d7ad9d40afbbdba9 ./docs/MIGRATION-v1.5.8-to-v1.5.9.md -1d114256ef855284dd5a11b40682ce67ad27e17b41045625cae251a16531d0bd ./docs/MIGRATION-v1.5.9-to-v1.6.0.md -2a01fb10a3e04eae1800a7c7e0aafc31e9bbb23584cea54d849b004716ef81b4 ./docs/OBSIDIAN-EXPORT.md -040010a807178d33797106e04716822f8d147d7d5833c1ba70865a1827f8484a ./docs/OPERATIONS.md -69ea49bc76690ac489aea908f4e784a14293e92d62478a240eabea53c5f90820 ./docs/QUALITY-REPLAY-example.json -bd2d3c43a09a89fefd0e844433064d7d7384a414179a754c6de94232d738fb04 ./docs/QUALITY-REPLAY.md -6d72b49b5b0ce93b94597cf5688403c468cd3b82f4d87470d80f108c5a7ec531 ./docs/RELEASE-v1.5.1.md -84b12cafd6320d781b99970cb48b7252da5aff94797d45af164f8a56dee94e5c ./docs/RELEASE-v1.5.2.md -f0ab4fa6b353059acf6b41a1e5a484ebd698684102724eddf8f8b91d7ff321a3 ./docs/RELEASE-v1.5.3.md -bc9067063b13788393fc3389ac60c0db50e35cc9d7d733311fca2fc6923834f1 ./docs/RELEASE-v1.5.4.md -677bf7eacb64adce080dcc1184dca0d7c088727b1c49242df14a7694eabbb593 ./docs/RELEASE-v1.5.5.md -4cde69f2875d5f97544fb85ff066eb671a40d9670302c81b2a9d9b9a95a9bee3 ./docs/RELEASE-v1.5.6.md -f7bbf78e76592762fe6e2a08eff9110a273962ae3145da287c5875355672db7f ./docs/RELEASE-v1.5.7.md -5757dd833f1f8b7b0a7b90b09324cb8989496ea6326d652d659a59f215a3172d ./docs/RELEASE-v1.5.8.md -245f942b72da17cb6c953720bd9c6742f71723be06bdb842c512851aca351e3e ./docs/RELEASE-v1.5.9.md -85160313c5dd89faea2e7404f090a6cef13a9b33758124dfc4d748a119375c32 ./docs/RELEASE-v1.6.0.md -be749a09ddbcd4cf427316c6fe531f138e9bfaa6c104f4c11a08f7555231e8b2 ./docs/UNIFIED-GRAPH.md -77d91565660789c621b05bd69fecfa3c2ef736f93855eca1f58b017e61c76f90 ./docs/VALIDATION-v1.5.0.md -3ea1953b2b4eb8f966310a65a12a681fb44e4eb3857b843312e235759aacfce5 ./docs/VALIDATION.md -be4810451750abb676eee0edcc6f164d86f372f32b185f6f730bf84538cef4a8 ./exports/knowledge-obsidian-snapshot.zip -dfa65e55e9ccf642ae5ef8ef91c62f4220b31ce4304ea4e1061f2e7d8c1b0fb4 ./exports/knowledge-obsidian-snapshot.zip.sha256 -99ffd5f497239a5e17b4e4bc79c7a1b5e1e41fdaf9b3f07beb7179050bbddcc4 ./go.work -5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a ./knowledge/01_active-directory.json -70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc ./knowledge/01_arbeitsplatzdrucker.json -96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 ./knowledge/01_fachanwendung-storung.json -521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 ./knowledge/01_festnetztelefon.json -88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 ./knowledge/01_hardwarebeschaffung.json -4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d ./knowledge/01_kennwort-zurucksetzen.json -5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 ./knowledge/01_lan.json -95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 ./knowledge/01_neue-it-anforderung.json -ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 ./knowledge/01_padagogisches-netzwerk.json -f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 ./knowledge/01_pc-und-notebook.json -ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc ./knowledge/01_sonstiges-und-unklare-zuordnung.json -5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 ./knowledge/01_verdachtige-e-mail-und-phishing.json -5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb ./knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json -ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 ./knowledge/02_fachanwendung-bedienung-und-beratung.json -0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 ./knowledge/02_gruppenrichtlinien.json -4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 ./knowledge/02_monitor-und-dockingstation.json -bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 ./knowledge/02_netzwerkdrucker.json -7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 ./knowledge/02_rufnummer-und-nebenstelle.json -efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 ./knowledge/02_schadsoftware-und-virenfund.json -2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 ./knowledge/02_schulverwaltungsnetz.json -830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 ./knowledge/02_softwarebeschaffung.json -fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 ./knowledge/02_wlan.json -26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 ./knowledge/03_ad-gruppen-und-gruppenrichtlinien.json -b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a ./knowledge/03_dateiablagen-und-netzlaufwerke.json -0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c ./knowledge/03_digitale-tafeln-und-prasentationstechnik.json -a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 ./knowledge/03_fachanwendung-berechtigung.json -f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 ./knowledge/03_internetzugang.json -5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 ./knowledge/03_lizenzbestellung.json -d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b ./knowledge/03_multifunktionsgerat-und-kopierer.json -c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 ./knowledge/03_rufgruppe-und-weiterleitung.json -b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f ./knowledge/03_sicherheitsvorfall.json -ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b ./knowledge/03_tastatur-maus-und-zubehor.json -7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 ./knowledge/04_anwendungsberechtigung.json -6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 ./knowledge/04_computerraume.json -36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 ./knowledge/04_fachanwendung-konfiguration.json -a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 ./knowledge/04_lizenzverwaltung.json -e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb ./knowledge/04_mobile-endgerate.json -adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 ./knowledge/04_mobilfunk.json -1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc ./knowledge/04_scanner.json -658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c ./knowledge/04_schwachstelle.json -5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 ./knowledge/04_serverbetrieb.json -ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d ./knowledge/04_standortanbindung.json -9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 ./knowledge/04_standorteroffnung-und-umzug.json -8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed ./knowledge/05_dienstliches-smartphone.json -47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 ./knowledge/05_digitalisierungsvorhaben.json -4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 ./knowledge/05_druckertreiber.json -74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 ./knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json -a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 ./knowledge/05_hypervisor.json -0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e ./knowledge/05_remotezugriff-und-vpn.json -9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 ./knowledge/05_schuler-und-lehrkraftekonten.json -2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 ./knowledge/05_softwareinstallation-am-arbeitsplatz.json -9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 ./knowledge/05_telekommunikationsvertrag.json -fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 ./knowledge/05_virenschutz.json -e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 ./knowledge/05_vpn.json -153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 ./knowledge/06_betriebssystem-am-arbeitsplatz.json -d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 ./knowledge/06_dns-und-dhcp.json -b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 ./knowledge/06_fachanwendung-bericht-und-auswertung.json -77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 ./knowledge/06_mobile-schulgerate.json -e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 ./knowledge/06_scan-to-mail-und-scan-to-folder.json -cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a ./knowledge/06_sicherheitsupdate.json -ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b ./knowledge/06_videokonferenz.json -e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c ./knowledge/06_virtuelle-maschinen.json -71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 ./knowledge/06_wartungs-und-supportvertrag.json -ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 ./knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json -2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a ./knowledge/07_datensicherung.json -46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e ./knowledge/07_fachanwendung-update-und-release.json -8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 ./knowledge/07_firewall-freischaltung.json -03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 ./knowledge/07_funktions-und-sammelpostfacher.json -a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f ./knowledge/07_geratewechsel-und-umzug.json -9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 ./knowledge/07_mobile-device-management-fur-schulen.json -48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 ./knowledge/07_projektunterstutzung.json -ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 ./knowledge/07_storage.json -2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 ./knowledge/07_telefonkonferenz-und-softphone.json -748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c ./knowledge/07_verbrauchsmaterial.json -7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b ./knowledge/08_datenbanken-plattformbetrieb.json -59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c ./knowledge/08_datenwiederherstellung.json -7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 ./knowledge/08_fachanwendung-neue-anforderung.json -f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab ./knowledge/08_hardware-neubeschaffung.json -b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 ./knowledge/08_netzwerksegment-und-vlan.json -1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f ./knowledge/08_neues-drucksystem.json -b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 ./knowledge/08_padagogische-lernplattformen.json -ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 ./knowledge/08_rechnung-und-kostenstelle.json -3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 ./knowledge/08_telekommunikationsbeschaffung.json -0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 ./knowledge/09_container-und-docker.json -4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 ./knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json -91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 ./knowledge/09_inventarisierung.json -d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb ./knowledge/09_protokollierung-und-auswertung.json -267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 ./knowledge/09_prufungs-und-klausursysteme.json -27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e ./knowledge/09_ruckgabe-und-aussonderung.json -eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 ./knowledge/10_haushalts-und-budgetplanung.json -523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a ./knowledge/10_kubernetes.json -1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 ./knowledge/10_microsoft-word.json -8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 ./knowledge/10_schulverwaltungsanwendungen.json -813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d ./knowledge/11_devops-und-automatisierung.json -213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a ./knowledge/11_microsoft-excel.json -a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de ./knowledge/12_microsoft-powerpoint.json -e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 ./knowledge/12_monitoring.json -2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c ./knowledge/13_outlook-client.json -cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b ./knowledge/14_office-vorlagen.json -9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d ./knowledge/15_office-add-ins-und-makros.json -7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 ./knowledge/16_office-aktivierung-und-lizenzierung.json -f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e ./knowledge/17_serienbriefe-und-dokumentfunktionen.json -5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 ./knowledge/example-vpn.json -49c48fb45c8387b020bb6a4bde2c81e544ac82b74403e453e28a85f987052978 ./mega-project.json -634bef89fe88e3b578b197095cb424ecbda9354211a69e86cd9964251eda047b ./patches/SHA256SUMS -47a6fa2c79bbba0c04af86dfa65d58529f492c698060fe586456c22a4eadb877 ./patches/glpi-agent-mega.diff -9b411c90d96a86c80f088ee4637046eeefaf31c62059d11aa1a999d6eb08b5b4 ./patches/glpi-knowledge-mega.diff -f0491de3cb6201f98ca6be8e865237adbba4772471f7fc7165d030e0c045fdb6 ./patches/neuroforge-mega.diff -576e75ca9191bbef7f136abde90c10a1c6bf2e36450a421fd3796e988bd2af00 ./patches/v1.1.0-to-v1.2.0.diff -7a5e0ae1d09b268d3ac62a51f92bfedf6771be26b909605a347f26d47d6fa8cb ./patches/v1.2.0-to-v1.3.0.diff -163a8a990aacd986bacb385494c2b2062ac10a9a8f0a456d19a6d1746c2fc500 ./patches/v1.3.0-to-v1.4.0.diff -654de4aac7cf318680872897c3de192afbafb61c7169acb8e7514116df86efde ./patches/v1.4.0-envfix-to-v1.4.1.diff -1ebc306bd6870389500c86cf07dcd677b5bd0448c762812c7e1c0f8732f36eb1 ./patches/v1.4.1-to-v1.4.2.diff -651c883facddfd0316f89e3cf3c24fd913371fcec2a5f447b8d105fa5dec463e ./patches/v1.4.2-to-v1.4.3.diff -d90be604acf6ecf5d821890d5864307853caf0e0678a3249eeaa1d29748a6ada ./patches/v1.4.3-to-v1.4.4.diff -01ba8cb6ea6ab0abd84295fb969fcfb93d841cd46ae1813fb0de9b23b2d5b22e ./patches/v1.4.4-to-v1.4.5.diff -617b434dcdc0dd0be5cdb15740661f0a513b330b99835364b508e21467f86ae4 ./patches/v1.4.5-to-v1.5.0.diff -b6bd8c6c04b5fca8d768279a5471c32e15dbcc57449b7d020f3e228f47adc310 ./patches/v1.5.0-to-v1.5.1.diff -b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 ./patches/v1.5.1-to-v1.5.2.diff -5c799db6701dce8019aeead5bccef6fc5fdc869e30785853eaae8d0eaf7ea43b ./patches/v1.5.2-to-v1.5.3.diff -27fd7fe12e9d774ced67cbcbf0f2e2e385736b48d01d655a1a9cf4b14b39bdcb ./patches/v1.5.3-to-v1.5.4.diff -7d0c4c85ee6cb89c6200cf35f23ea92c93ff5fdd9f2887c179c83fd5e5657281 ./patches/v1.5.4-to-v1.5.5.diff -405b41cbbd1ec48f316b1d935e67bc8d002fa1a3bd583fb813bf0875bd30a2e0 ./patches/v1.5.5-to-v1.5.6.diff -257a78b8f6613f92086a04bf53455f12e9bd1ab94775add31819ada465582163 ./patches/v1.5.6-to-v1.5.7.diff -53ab438b31a31590edbfe00eea6ba9d9ebff2b4c6255d7ac2056c86757f65c3a ./patches/v1.5.7-to-v1.5.8.diff -24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 ./patches/v1.5.8-to-v1.5.9.diff -e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 ./patches/v1.5.9-to-v1.6.0.diff -564817f8edabde0c4e4a1a427a3aa5418aae7bf12e9463044a7e6e0f13973657 ./platform/neuroforge/.env.example -39319b6f2058e4c8d6656a9cf01675374f81a04075b956b093a2befb5e05ada4 ./platform/neuroforge/.gitignore -189486a885c7fb78e0eb878d93cda0c70ca6d7ff9bfdfb3f5f32487cf03a9688 ./platform/neuroforge/BENCHMARK-v0.5.0.md -b9cfe0119f316b8143ebd33e9f66a8da36493a2c6c17acf2519ecba023223424 ./platform/neuroforge/BENCHMARK-v0.5.1.md -fc3aaadc0675ebb6dfeb42d9612a68c1905ac575e2c33299ca6e1522d8bdbeb9 ./platform/neuroforge/CHANGELOG-v0.7.0.md -9a1923b4c01a02d18b982aab3579ee7a976a95a6997dcc60647ae4283dd1e863 ./platform/neuroforge/CHANGELOG.md -2b286a4066da5b6bf7ce83c42ba7c97df6d4c6f8b71aa3f28d1ff824a7814391 ./platform/neuroforge/Dockerfile -9a9fd7708b61027e3677044ab1cbf7177119eac796df4d8786d39011da28729f ./platform/neuroforge/IMPLEMENTATION-NOTES-v0.6.0-dev.md -3146656e702607501cb348c77dadff608f9c152c4104647230848004e2692251 ./platform/neuroforge/MIGRATION-SQAR-VECTOR-JOURNAL.md -ee7f52ec20dcce87b751637229fe26c65d5cb0a926ac32915f46bf1b5eedad3d ./platform/neuroforge/MIGRATION-v0.2-to-v0.3.md -3095ca2e4512744ccfb2c3c71d80dfdbd42fe24e556a374061a76b80cbc33213 ./platform/neuroforge/MIGRATION-v0.3-to-v0.4.md -680eb77c8d645c3a2afa4e92e3062d1dadaa079425ed8c3f0d73393931354b59 ./platform/neuroforge/MIGRATION-v0.4-to-v0.5.md -0a21148446a057c773700fcfb026230fa771e970482bd6bdebb9cb9a3c11f4d9 ./platform/neuroforge/MIGRATION-v0.5.0-to-v0.5.1.md -59d223ec40991ef152f32b8bec26fa283bfa6318942e33a1a2169a85b361d9da ./platform/neuroforge/MIGRATION-v0.6-to-v0.7.md -4fc6e6f43fa70f4fa8a085225ecdc7ecd460bbcebca01dde70fed48a4c933c68 ./platform/neuroforge/MIGRATION-v0.7.2-to-v0.7.3.md -02bc0ac5316d063304b17bc51780f2fe55fbdfc44abbb151bac2397a642174ba ./platform/neuroforge/MIGRATION-v0.7.3-to-v0.8.0.md -9dc61428742d401d226d2eb8a74577b881309e67bb5c609d01b5152a18cb0bb1 ./platform/neuroforge/MIGRATION-v0.8.0-to-v0.8.1.md -7bbec66a7539fa9f65d8448e2c59c2f76797679f24d3b96691dc8c68e81d560f ./platform/neuroforge/MIGRATION-v0.8.1-to-v0.8.2.md -ffc6d8b40c9f77c7de1bd1d22c2161b5687b1a484fe6a8fcbe15ade1d2e5c9d3 ./platform/neuroforge/PRODUCTION.md -9f53f3a2d7212d47fed2cd88bf06bbbe2e1d37a3a2181afdb922148de4ab3eed ./platform/neuroforge/README.md -38bdb771c428636d18cb4640653ff7028715d5bfc5ca06d5ebc59f8ddcd095b2 ./platform/neuroforge/VALIDATION-v0.7.0.txt -73b7f0b287ae15f230a7ad2996ec305e787e7a8b096f7654fc69cf4daee12673 ./platform/neuroforge/VALIDATION-v0.7.3.txt -c3d34504b1b8a4ef73382df061c7b272a55c2a3a7a720435c6050254da3f22e0 ./platform/neuroforge/VALIDATION-v0.8.0.txt -0371dc02e4ac40b3197104f0aea41fb1b7be9b60caad4fc203eae1de1ac8191a ./platform/neuroforge/VALIDATION-v0.8.1.txt -b4896439112f9ca0f0a43d55d43cb3a2c4c802c568b65a96d04a3ca9d841c358 ./platform/neuroforge/VALIDATION-v0.8.2.txt -ceb9b2c03afb769df3b1e518520c4e1798e9a3313a1c5ffffcb054fef5d6d6fb ./platform/neuroforge/VERSION -0c5308f5a3d37ac23dce162fd5fab78ce598e41671db5dd50c9c4ae7f215d49f ./platform/neuroforge/cmd/bench/main.go -4916d533b1b9474f7ddf99c034778466b07a39a5aa61732660456156916a41c6 ./platform/neuroforge/cmd/server/main.go -9884e323599a328e16cfe08b52473cc9346b17c3f5969fce4425d20fd0f42a93 ./platform/neuroforge/cmd/worker/main.go -e3dacdaec3c629dd432f24218a50fa4de5e1698ac7f27e84b14f776b50beeb83 ./platform/neuroforge/deploy/learning-policy.example.json -3765a5faea1faeb72aad7878ff0159a56fa1b0ca1fc94a348a8c599e460b61dd ./platform/neuroforge/deploy/model-routing.example.json -dcb7f80239cf955941ede9bcdc3bda8f39d81db5e5b0e0962b373036b40a0df5 ./platform/neuroforge/deploy/neuroforge.service.example -72443642fd4c498554ad6e46db2a096c97530fba1ba20b842a478345fe54c46c ./platform/neuroforge/deploy/prometheus-alerts.yml -c30e5b2fd39e72894db22499259b6f97d225c5929a7f3472277853a348cab9be ./platform/neuroforge/deploy/prometheus.yml.example -dbbaa7fd4430b9cdb5302144b6d40f7ac4ab9f73e81da4ddb51e788dd373d95e ./platform/neuroforge/deploy/searxng/settings.yml.example -a8962d0a1adca3a80b91d1eeddeab36b91c1cf7d84f39c84bfcedb85ccf55a52 ./platform/neuroforge/docker-compose.yml -fc993dc95fa49802ecb62994e4140dff18a27438e8a4f3c6352229c79b041710 ./platform/neuroforge/go.mod -1dc1d58bf4761f2aac3f2436f78c5942bf3a5968ef88840693dd6042960f7b7f ./platform/neuroforge/internal/brain/brain.go -976288422c0c4116d8c98af8a9b164ac670f9caf03eddc2d5e2e48747456d3b4 ./platform/neuroforge/internal/brain/consolidation_test.go -cbf267375414612ce2d4a697606576f756ce3611df543ca8cb63b6c2fcc64e44 ./platform/neuroforge/internal/brain/distributed.go -0eb3cb86d1c235ac21527aae79de7c26ce204412814c5ea84dd9389e0b0fca2b ./platform/neuroforge/internal/brain/goal_progress.go -6a1e33b71d88f963f244a5a00ea45ee65bcff4882a562148d8ba925b8692e083 ./platform/neuroforge/internal/brain/goal_progress_test.go -bb4cec696bc7e5fc2b1f2fab0b5c01552f523a7dc2bfa1cb5be5e5374b304162 ./platform/neuroforge/internal/brain/orchestrator.go -03ba39168194068f59e4be837de1c152e0d10e316461b235c61704571cfc0e08 ./platform/neuroforge/internal/brain/orchestrator_test.go -359955653c647125559afd6dc3ebe69aa5ca19ff7e825ce801b7bc24e5fbcfcb ./platform/neuroforge/internal/brain/policy.go -27e87af473d2d71ba94ffb9bf7a70934776f8c23ce45496ca0998ad3000fc156 ./platform/neuroforge/internal/brain/policy_test.go -9022a797aa1d49af20ced5d8c3e0b903be740fbaa7251e8a80d43cd04d2f68fe ./platform/neuroforge/internal/brain/research_quality.go -0a3c8f7d149e814e091595982dbaa69467f6bf11eec631471d133a9b21585ae4 ./platform/neuroforge/internal/brain/research_trace.go -f90474aa355d3c4565e61662e863bceb089795af01802e9b228c1b6c545bb7d7 ./platform/neuroforge/internal/brain/staging.go -4d7715622739a43b043a1f08ae7165db6ee6bbc22f3ab94945f56f226dc4037d ./platform/neuroforge/internal/brain/staging_quality.go -d4fed8b68d31f6fbd1992bde76abd4e7b2f9d211e53bf8b234369a6a53692fe8 ./platform/neuroforge/internal/brain/v3.go -3ae13251512ecad1423a33ce09889f961130fefaed9342170b2d5cc6b3b51893 ./platform/neuroforge/internal/brain/v3_test.go -4bc58463b659bd7e51db4c7dbeba053de90fcb41f392a7d6e62a8cd84ddaa092 ./platform/neuroforge/internal/brain/v4.go -a9619d9571f6ab6363b36af9fdf1690f9f88e9333eb6f2773ae68580b19666d8 ./platform/neuroforge/internal/brain/v4_cluster_test.go -76319080d3faaf856e9fe5e1aac5e06be433d2f06cc6b152bac5f05dcc943fff ./platform/neuroforge/internal/brain/v5.go -816b725594ea5f6938999799394eaea9dd619addaeec5447d8e7707da4c28c69 ./platform/neuroforge/internal/brain/v5_cluster_test.go -cd8f1281e25ce42ae8918abbed16d4bf57ada9c3a3cc1212edd28f77ccc9328f ./platform/neuroforge/internal/brain/v6.go -312e7578b5800ccb06755c19d7ef7026a362443804ff78cb7a11a7443af2aab5 ./platform/neuroforge/internal/brain/v8.go -31420af7145686a2c7eb42fc18ca1299604afcb5942cfdfd132bef5c5f05d980 ./platform/neuroforge/internal/brain/v8_test.go -4345241b0108fb9f720df831f4878149689b8fb692d48641d3d4606ac7e89e30 ./platform/neuroforge/internal/core/types.go -65a8b8196343e7cfd9444ca314da4a83c81bc046d9478b8b93217d9cc68ba562 ./platform/neuroforge/internal/cost/cost.go -b9bb2934e01ed2bfb6b16e4139e10e51f1387112eb8654f7f692779a2fd2d273 ./platform/neuroforge/internal/cost/cost_test.go -fb66ce4ab760b979eacd4f7f17dedc41916f93c53ba582be572d250f99654695 ./platform/neuroforge/internal/httpapi/admin_app_auth_test.go -eb2b8c599e4e0838d920296391ecaacdba1578c4f3a6a0a9e8469fdba31217ed ./platform/neuroforge/internal/httpapi/goal_duplicate_test.go -d7e6d3cb1705da9a174f993d60927b91d89415a7135157629113e25476fccd57 ./platform/neuroforge/internal/httpapi/httpapi.go -680b7fdcb20ac76c9305dfb94e4ec7474526be7a44b56afb3db7e4a66c8e2c38 ./platform/neuroforge/internal/httpapi/index.html -20f4a6cc30d5ce84ceed4fbdfc9f5c57ab5f82274e0c54464817d571e0e881b0 ./platform/neuroforge/internal/httpapi/integration.go -c346dd0205548f7cdb00ea026f1afd82d59f87824eb983ebe84abfa7cb078974 ./platform/neuroforge/internal/httpapi/integration_api_test.go -f8cfcc7a2bc781394231c25e36e75c41563f5250a1387e5687880d2591efd93b ./platform/neuroforge/internal/httpapi/integration_graph.go -c289fe8748d28d4c1207b22924f7d1a7b2df25703d6df5bd1869acb325211b2e ./platform/neuroforge/internal/httpapi/integration_graph_test.go -29c365a797f1f162f4d7a5d32833123f7930b73ca3b649fe31e117453dcb9197 ./platform/neuroforge/internal/httpapi/knowledge.go -409dd8d7c5ca93cc82ce2b7bf17a12655c97dd27ad9767aa40619502830a0ab2 ./platform/neuroforge/internal/httpapi/knowledge_integration_test.go -cf285170ff175ce2bb223b4ffd87a35da604ec73b5fe386ac35402af1f9c3531 ./platform/neuroforge/internal/httpapi/knowledge_policy_test.go -b7ac0b25751ed312b0a39cc731396c0073d81a22dd4fbd6f4d75de18805618b4 ./platform/neuroforge/internal/httpapi/metrics.go -c40412c77dd8635bbd4bf9c6d7eb11b0d7902aee896bdd1ee99e5cdfb72c7453 ./platform/neuroforge/internal/httpapi/metrics_test.go -83033a18a319ffb162c7cdefb2efe108a47dba81095ac34662349e232f8c22da ./platform/neuroforge/internal/httpapi/model_routing_test.go -38a9388b6907975aec933697ad87ddeb9e7016a279523f2526db3810df70b006 ./platform/neuroforge/internal/httpapi/outcomes.go -0b0b111e6b47f38dd3ca331a0c1b79b3de95012820f1c04cbe37c79460c09e63 ./platform/neuroforge/internal/httpapi/outcomes_test.go -b3b1f5cfd6b0ec04341978898d72856af03a95b8922daf9864caeb27e37f1925 ./platform/neuroforge/internal/httpapi/research_live.go -b37f7ec3be867666999a9ec314139dd1bc4c74efb53edfa224b4fe8119858aac ./platform/neuroforge/internal/httpapi/research_live_test.go -4e65ab0a760369a6d6c4932c180ff6c67f0768d91e08ccf471f53c6ecd5d09be ./platform/neuroforge/internal/httpapi/status_dashboard_test.go -3eb47f1b666336efe952961c19a1f46a7475acff8fe4dd7a59ade000aa1b9466 ./platform/neuroforge/internal/httpapi/v3.go -b1587e066b56ef72f17162583698614d31c413e2ac7e75359b731c2e882a50ee ./platform/neuroforge/internal/httpapi/v4.go -1ef5cacb0652f2a6301c57f8987dd848fbe590abb73cab44899d9152ccce45d0 ./platform/neuroforge/internal/httpapi/v5.go -1d534726d7a75f21f7ad28a959f73a3402c021f7f4a1b554f0600cf3abf669d1 ./platform/neuroforge/internal/httpapi/v6.go -fdf937aae569dd48d09da63085fa2656299a3ab2f55c7fc7f718c46df39a2683 ./platform/neuroforge/internal/httpapi/v8.go -0c8c389b085e4546c3269b1bf0c539414f3603522ddfd0665261a6448f31b514 ./platform/neuroforge/internal/httpapi/v8_1_test.go -889b82a7f90be0590d0087ccac06aad330710c19f31c43b12c28480a94659be5 ./platform/neuroforge/internal/httpapi/v8_test.go -60fd100c9057c14d78f91d636f7f23aaf2767088c46b7670b4266ce22cbb5ae1 ./platform/neuroforge/internal/ingest/extract.go -7576df6ad6f8b692f14db739958fabe583cc5bb547c6068739fd47c45910d2ce ./platform/neuroforge/internal/ingest/extract_test.go -38a02bd5df61d1e7b257bcee8120380d38bf8c70ec329b6222f1092f74495629 ./platform/neuroforge/internal/provider/provider.go -f9cfbd0bab443faf39973e6fe1777d7137ae5dcf58fc1b0d6295099e1b3fadc9 ./platform/neuroforge/internal/provider/routing_test.go -baace266205a584a53463af76038e97771f088a38e42fe333bb548e43856e719 ./platform/neuroforge/internal/provider/runtime_test.go -024d4983420ac32fb0e29ff1cf145c45756af28076e937b07c272fcec28749dc ./platform/neuroforge/internal/research/searxng.go -e890897c662b070997c3b18134bafbee5bf9b3fa5220045ac09dc6e3dfd4ac41 ./platform/neuroforge/internal/research/searxng_test.go -fe35f46c0b6dee7283142dc818f484006fdde6d71906d5f5fb3bc05c424ddd4f ./platform/neuroforge/internal/store/batch.go -8e7a029a904ca1c22821c5e4871c72958f0d2645786871d75f3e00c186b00778 ./platform/neuroforge/internal/store/cluster.go -6637e541f61a58f624baca31d972095ea01a95491ddb30492d9ac055740213c5 ./platform/neuroforge/internal/store/diskann.go -1858ed8045b16282de815a6668ff6027c7a3a3795e38659c753c504d0f2a8383 ./platform/neuroforge/internal/store/diskann_test.go -366e3b2f29b4abb1997aceb9c2b934ebe8f4ee44605b54167c70b8ddad683c7c ./platform/neuroforge/internal/store/goal_duplicate_test.go -38f24189c76652f12da9eef7a617455dc923499e2f21cd3c29d94231dd0d7460 ./platform/neuroforge/internal/store/graph_index.go -bda98567153542e9119757ce5f8f6014334ac9f80ad79a26ff83fdfaccc1ca8e ./platform/neuroforge/internal/store/graph_multihop_test.go -d49925fda3cbe3ceeffde3369073bce5a5bc67a23defb6827e27cd4a7f4c59f5 ./platform/neuroforge/internal/store/index_segments.go -478efaeb260cb5e970af8b3bb99f232ef846f54c6e4ff9deb5499df6e008e1c1 ./platform/neuroforge/internal/store/index_segments_test.go -eef4aad176d7f6e740dff4ffd81f3dbacbcbe6646e724331ae010ab50db04b30 ./platform/neuroforge/internal/store/knowledge.go -e9aaedbf4d5e9a43f4a4a5dbe2e9e0f0257b952fb59e82bf729aa5e761955c1e ./platform/neuroforge/internal/store/knowledge_test.go -43de0f541fb47633b02b25686544d44887c17b47e8d01cb23f8e57567cebdcd7 ./platform/neuroforge/internal/store/mmap_linux.go -9ce033d691157037fc6df719b5ecd8fb29d6d881d1862e2d50dc0c6c573b5f66 ./platform/neuroforge/internal/store/mmap_linux_test.go -ef6135a9039fb45361e57107d3968272b4b365ac833e9e2e58b0875566bf5a07 ./platform/neuroforge/internal/store/mmap_other.go -fe98dca9f919cb52140a916de08e76b88bd0d08b6b83fc8b4e5220411998b0fc ./platform/neuroforge/internal/store/observability.go -781e3b00d4acd0679adf235a6addcf44992d5039adc4f6c6e3b071c7593cc968 ./platform/neuroforge/internal/store/orchestrator.go -f830512498457ccaade5bf1c55f1caea1504bb432e95c99f5b0437794fd1d2a4 ./platform/neuroforge/internal/store/orchestrator_test.go -66d612efe2462d76d79cc51b8b6835b5990d4a79a2dc036c26b040554357ae29 ./platform/neuroforge/internal/store/pagecache.go -5df97ac71adcea7627756a52d295d682900d3b812a977dbe9a4911e4c4118809 ./platform/neuroforge/internal/store/raftlog.go -fd6769bdcf1f7d22ced9ea426fd4dbc52b50b5483b4be42ac3b6adf637c926ab ./platform/neuroforge/internal/store/raftstate.go -f811ffd393ec7e72a3dd704eeebef7da1a78446169f451a44769bb586d7f0049 ./platform/neuroforge/internal/store/research_runs.go -c5c976d068317c688611bce153453133ebd7cf19b835955c77115b450793fe9b ./platform/neuroforge/internal/store/segment.go -ea968456538367ed536c212707209a173098ec65823152a0813422bc8071c952 ./platform/neuroforge/internal/store/segment_test.go -1d8071889b7a2be9faf029a1dd2981c69a9914798869bfe217f6db105340323e ./platform/neuroforge/internal/store/source_index.go -4d41586c15bfdd13139e19bfa102613f18ddc6120e4214a1f45dd9e2eaa195af ./platform/neuroforge/internal/store/sources.go -0ff464ef7fab01324d77ee4e406a8c15451b3333b75d4afa444be3c3c94f016b ./platform/neuroforge/internal/store/sources_test.go -61cd82b106e74f2aa0a7591d757a0872491a635dc1fccc78c66d41c052d0c9d2 ./platform/neuroforge/internal/store/sqar_vector.go -e17ceefd9d084102ca080a2978ef759eccee8e7973b2884ca1938d05f2faa6d0 ./platform/neuroforge/internal/store/store.go -f86b648e8b4845ab83e672ae3fcfb0bcb0dc1bf891abf4bd30d380304f0b259a ./platform/neuroforge/internal/store/tiering.go -2138e467981a3c814d1e88842dfbd470b76333898614543cafb4f93ea8b9beaa ./platform/neuroforge/internal/store/v3.go -091e4c9cacfc76786a76f6afc4eb2b697e5a641e611475b6dab97e99371360ea ./platform/neuroforge/internal/store/v3_test.go -7f0bca5419a895258b7921e940c3fd69cce04dedcb0b9d8f1127cd24b9c9e201 ./platform/neuroforge/internal/store/v5_test.go -b3a0115907719a8a7aa9e6b244eec55be71a2535ba20db65907987d5fb78dc77 ./platform/neuroforge/internal/store/vector_journal.go -bcc12eb91682f3c0a626f5baeea64cb3809e56f087b9f1178a6c2546f5e301b3 ./platform/neuroforge/internal/store/vector_journal_test.go -e8b9a9eadab426393b47d4a0a479272449a6557b4e60af9a413589784065544f ./platform/neuroforge/internal/store/wal.go -7f0d5667c4a0e5f87f5ae86a6eb246a7a448891ccdaa3501ec81933b0430794c ./platform/neuroforge/internal/vector/hnsw.go -4bfd79da525c6d7901db01bc09c823608b641970e40aff9fdaac33da15d4432f ./platform/neuroforge/internal/vector/hnsw_test.go -6eac7fabc0f8a3cab53c758502895576fae158e3d672f8c87ad58588523919c2 ./platform/neuroforge/internal/vector/pq.go -275b0dd3b8cc3589541e3cfb52a2f1a372e40446e2e0c9c902388fa832e4f0dd ./platform/neuroforge/internal/vector/pq_test.go -c854f70f4141344dc2c6fbec4419afe5dfe25fd1bc2da11e41781730a21c8834 ./platform/neuroforge/internal/vector/recall_test.go -32f55e82419e4b043d15682c82ade1c4bc9694bfa372f925c131c2611f48d744 ./platform/neuroforge/internal/vector/snapshot_test.go -c5463c525f5ea703ea1f6df74cabc30f937f3846b1f75f237adeee2700baa977 ./platform/neuroforge/internal/vector/vector.go -276cb34ddd9f87abdac17be110e2bf15135ab83ba66bf91663d59d61fbd81125 ./platform/neuroforge/neuroforge-v0.7.3-rtx4090-example.json -621c7116df7e26b52b53d8469c41ce416bddedeb680b8e030ba2de341e42a56e ./platform/neuroforge/openapi.yaml -02ebd40234435473fa5e4bdb03f035f22564049a6f43243d7f4e175c7239ab78 ./scripts/backup-data.sh -df684b1b487762248516aebb82742e5777395b504cbde42ebda85b999ae353be ./scripts/check-compose-env.py -6f1d21b7aea3265a088256801aa37a00931157523d8bd834aa4f473e99511f04 ./scripts/codebase-memory-ui.sh -5abbc6b60bca94fcb880abebae19c85c6a220c9eabb9865928b23ae86246705b ./scripts/export-obsidian.sh -01611fcaef13df301147ad539f38e96e09d32c8f0f17c0fc9a7e3dae38de6292 ./scripts/generate-secrets.sh -8b0a4be74d87cf42c98360819c6f797b9caaf10528e729159412daaaffba0771 ./scripts/go-live.sh -0d28cf37186f9faeff2a40e506e3a3e5b8fe3a399c9c426e369bd26c9021f7b3 ./scripts/preflight.sh -fe35fa5db6dabf06e6266e0de4c372604160180366d4c0a8042c7ee54b7c6dcd ./scripts/propose-draft.sh -1744be0629e845c8d94dc2a06e91ca2cc9ce5928db00300b7fb894445aeda9ff ./scripts/quality-replay.py -8b745865c41ae3fcc89280c861f78918ee947c1fae4973ca95d00a507fc3afcb ./scripts/release-gate.sh -76358a00fbc9698879d363dfca6624a13e7633181cec2ce05368cb01811b2580 ./scripts/research-up.sh -697891580c9713e6451c1e0f10509b009cfa3d3c5af4a1347f841e6f05339cea ./scripts/restore-data.sh -ddd353d613d17320d6d705aee459b5b290a5f8535e19b5b664e592d13aef8fc8 ./scripts/secret-scan.sh -a2c56eb668137e058cb78bb592ac359ef7e608cb9bedc7eaa930b3f4f66411ca ./scripts/status.sh -2970d5f5049323b14a3f3983c2f4eb5392d745b4fea34742e3cf56e52d740f17 ./scripts/validate.sh -17d7149607bf40ccc6f64a3c4cf3e4023a8325675918c18df0be309ea9731a36 ./services/agent/.dockerignore -d1d8c18c6f7c7c6f0b2d72f421837d010afaafc931596ae48442a826e50f0bc6 ./services/agent/.env.example -236713daf159ff0a8067e80a442ae3404fa28a5251ae6f24782f263bcfc17005 ./services/agent/.gitea/workflows/registry.yml -9ee46f2156ee805c4ff40e418b004ead5cfbc3a9b6df3968a1b772fe1b313cdc ./services/agent/.gitignore -4c893d6049499eb70ca755a333e97f0a726dd212e4ded4a9d4acabf0a25d9bd8 ./services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT.md -9339ce57dfc58d4731ea02c8d0ab9f2835e5966a29c071062fc6a2effd17da2c ./services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT_OLLAMA_POOL.md -81c91a44c5d3b7eca3051d8cee35f231ef283563da7a636c56c98b61da60218b ./services/agent/Dockerfile -2ec93f81187ebf92665a6a15a6672b2f7dfce23bfea1546f2d4188f49de5dce0 ./services/agent/EMERGENCY-HOTFIX-TICKETVERARBEITUNG.md -2bce6e10780004bc12042a416b8a05b417130db2d730d01a95d9d86a564e01cd ./services/agent/ESCALATION.md -bc344ff4486bd38356fab0d2da46590d9f738ece0f712d043597012f215bae49 ./services/agent/HOTFIX-GLPI-KB-AUTO-REPLY.md -3f51b802804e9004d17cb74a7e0b8fc668537e75bc95e7344923e77d5f05f13b ./services/agent/HOTFIX-GLPI-KB-SIMPLE-AUTO-REPLY.md -65408599c64a4364f41fe0876f6531b861c352eaee4afabfeae52a5a72f28f6b ./services/agent/HOTFIX-GLPI-KB-UNCATEGORIZED-AUTO-REPLY.md -dacf2060df228b5c137fb5ac5c5f7377226f937aadaae4b01b662fc54abea11e ./services/agent/HOTFIX-POLL-DIAGNOSE.md -6b2188c579db7c47cf7be54328f2671a8fe7aa17f21556483f751fe2457db1c5 ./services/agent/HOTFIX-PRIORITAET.md -30a29a59cf3c409bf33e3adc96789b9281a19fa86f961c727dcdfab07ff6a327 ./services/agent/HOTFIX-TRIAGE-KONSISTENZ.md -c0e13c6703cf3feda57a9eaaa718e501280fbb526f466040eb685a27700b5797 ./services/agent/IMPLEMENTATION.md -1126322e2cc8d165adc4c792eeb195717de2bcc7b39be1ce77959d78e87ef685 ./services/agent/LICENSE -0ed7ee0e4846be09994dd5f3b49ffdca6b59177b9018b1947388949f0dd49a14 ./services/agent/Makefile -80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 ./services/agent/OLLAMA-POOL.md -80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 ./services/agent/OLLAMA_POOL_BETRIEB.md -a7a3f969150005ef062e7a77cf852371931bc2fa5ca4be01cb0e9eb07d35335c ./services/agent/README.md -aeac85d1adc92a8b34ffa5a95f7f19c2c6fb94742a7359a7fd452529d59ad87b ./services/agent/SECURITY.md -edb92cdf2d1863df53ea8045657f64e415f206d503c8da3b45ea96b7f7f0960d ./services/agent/UPGRADE.md -68cfcbf3bd8eb0c24f12e41cb3d95c1654b98fb7cd8b9606844d68e409428c23 ./services/agent/cmd/agent/main.go -98be0fd63dd4fde52342c5385da113c926ac29195aedb657c0ca0f98e1593fff ./services/agent/compose_local.yml -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/agent/data/.gitkeep -b74f901cca99eae4bed1b168f1f077a28bf1134a71c867d263c7834f6d896684 ./services/agent/deploy/glpi-ai-agent.service -e8327a246f048c70f3b2b426f5f942bf16c41e49ac385d49c296eb994dc0cfd8 ./services/agent/docker-compose.registry.yml -29134dbd1bb19a0fd87b242a184b989a70f3a4c784254d86e2a0f5fbdaaa0497 ./services/agent/docker-compose.yml -a0105475dc054977223fac36618b8cd8137c55be1d11fddcd24e9a4d3074c170 ./services/agent/glpi-ai-agent-neural-brain.patch -8a7805be0da45875d45a5e5b26780fe659f671edd2d6b72c0d7da9606bf15755 ./services/agent/go.mod -e34ac0033c53effcf654689641a15a92deae1a987e3d92e9921b7b030efab3d0 ./services/agent/internal/agent/agent.go -789e55accdbfbb99ef8179f5680aa340e67a055c71686533302558389dabfb8a ./services/agent/internal/agent/agent_test.go -a8eedc80f57b174fbad3850d787346f7b370236b4c2eef67905ef6b60a1ced64 ./services/agent/internal/agent/analysis_runs.go -46a8dfe5e80569aac48f9a098acb2c1089ea4ab9521ed6c92f53b020e0c76ccd ./services/agent/internal/agent/analysis_runs_test.go -05ce267b4827cc5b89332b72fa6ecbcd80bd3796b5143bc40398b932f5f45867 ./services/agent/internal/agent/candidates_test.go -76fd850dc9bedf73db32e9b2d49edfb04624709507e0b67fea1f88730e87720c ./services/agent/internal/agent/category_mapping_audit_test.go -d8aab6fa743e6ed8246ac6e20f993fc3642c635a1cb349c4fb6579b863a2bd63 ./services/agent/internal/agent/escalation.go -c4bf8fcb4ba6eb7962ec55cacff806a8a99561d134ab5238abd012763a2f9c74 ./services/agent/internal/agent/escalation_actions.go -471cf47af1220f50fce99ea41c59adf257cd29417ce23fafb4728f0037d33842 ./services/agent/internal/agent/escalation_actions_test.go -7a65f676bacd5a7b879862e4c1a157632f541a570a71bd8b57864e603bb4cd60 ./services/agent/internal/agent/outcome_learning_test.go -5175e4fa9c394c0a8df94c29c69c5f896b3227a7d8de67d214aea4634ba0fad8 ./services/agent/internal/agent/policy.go -5434e1c4dc61eee33d6dceaff69156a5b9ab4a9fbb555cd056c8cf0444601606 ./services/agent/internal/agent/policy_test.go -6b70a29176602e499935a85ac8831dbe36bf75fddb130a56cda376e124ecb7db ./services/agent/internal/agent/status_reply.go -6f95bd3c85b42f1b2b098f05828c0b9e3dbb2e57f1f89b3dc4d1806453e0c4ce ./services/agent/internal/agent/status_reply_test.go -9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 ./services/agent/internal/brainactivity/client.go -cbb14913455ead81f9fe85c3dc185a29431e28271f8babe840759bc93417c0a3 ./services/agent/internal/config/config.go -fa1717032ad536bff867a07d3fdf0ae2e0a9d5d7637a27b43274832d51322eb6 ./services/agent/internal/config/config_test.go -6fe7335b0e66b60d5f6f27583b1ac11a0948719cfa657d51e77de0b337aab5d5 ./services/agent/internal/contextdata/collector.go -12e9eb6fd567b5d4173a63fa2b89be913b6d1c97e17b69048f46d119e88dce26 ./services/agent/internal/contextdata/collector_test.go -5eee0e130a9cb8452a07e9f5d4c76048ad2eb62aa411ea61c9dc6066fadff934 ./services/agent/internal/glpi/client.go -6499b2e19547d7d874dbaa64f0c905643ffe4d822329c6cd710df0a30a3a9755 ./services/agent/internal/glpi/client_test.go -247324ba41f3cd991a3582ed270d0b0d6c4c92c36f11e7742c20d3e59b982928 ./services/agent/internal/glpikb/sync.go -9836e2bf9153df638ead57d92c152b87f831e5a3696bcda4b1f650ff38e9ac70 ./services/agent/internal/glpikb/sync_test.go -787ab6b53623aa36ef6628f4eada11dcb4ed3011b34effd691bf07ba5d8f214d ./services/agent/internal/knowledge/category_mapping.go -d3a9909672ea420db87be29ce669f3cd973125ff15bb46ca90a54c3aa98d9e73 ./services/agent/internal/knowledge/neuroforge_backend.go -7bcd33bfcfb99fb06762fe75f4646308428957ed7eca26a266891ab580c3a929 ./services/agent/internal/knowledge/persistent_index.go -df1ac214ebb88bf3ce7f68112129cf9ffc300dd725380c38661b85b04908ecdf ./services/agent/internal/knowledge/store.go -63e47d86bcc89178fdc0f91e07ecb30bd01ee4b84bf29bfc660debbf26f8a40b ./services/agent/internal/knowledge/store_test.go -807996228b38e1cd97ec2516cb1ecfc4bd945b638fcde465a0991051df2f1a00 ./services/agent/internal/learning/outcomes.go -2917b3dab63783af4c92c48f269f097dc94b989008a5451f46d6c1b309ccdc9b ./services/agent/internal/learning/outcomes_test.go -cd8c7379d6ca50fed77a7eafb6ab34b12b9ce2fe56025da534f54e6db246430d ./services/agent/internal/learning/store.go -c540f2e554d66f36d79d2ce32c74a18b27b2940e96a40e89eae10abccd0bd2a5 ./services/agent/internal/learning/store_test.go -5030ec3f51830f7decfb24c27f9b243cb64f97302a6a51df096bc957f24b3fae ./services/agent/internal/metrics/metrics.go -809c3f2833ed2093bf1f9e4466eb3d855f2a84d827cb21deb698dd0e7b63b403 ./services/agent/internal/model/model.go -975d5846fa38bf2478fa3ab9696660740c8d9a0549ce8d4f3443d4ca917c4aba ./services/agent/internal/model/reason_codes.go -821850d649c7661f1665dc6be030ebeeacb60e4e007e1e84891d4b779b9a5d0d ./services/agent/internal/model/reason_codes_test.go -c89248224adf72720289215684b916eb209387c1d38b757d64804a8d3148011c ./services/agent/internal/obsidian/export.go -b75ffec53e9a94505ee57ecb9361bffbb6805cfd06ea05a7bf68b6b28b268c09 ./services/agent/internal/obsidian/export_test.go -f4e32c5653b5fc3e046f1536513d376a7c0ac11b392210a107e12bd0ee63e179 ./services/agent/internal/ollama/client.go -3522e7687e38db771dcf7e05b8dc52ad99a2fbc53d98dbdf07ded781d68b248e ./services/agent/internal/ollama/client_test.go -efbee1deb5b452da5ba8a9005cfc8b0f74dbdebddf8d251c71d63265c9ccfe9e ./services/agent/internal/ollama/pool.go -401db60148c8307516c091a56cf8acd21a95230c69fc50a6999bbb0292f2b06f ./services/agent/internal/ollama/pool_test.go -ab29c5808a06f6b21a0776355eeaa030f69be5eee251e7b1dbd7e00c71045c61 ./services/agent/internal/prioritysignals/signals.go -149bfefd67291ee288beed89a27235f58d3c33d87a1eff98cb2b7cc312fbd6b2 ./services/agent/internal/prioritysignals/signals_test.go -93f31d9e7062a03990f96bb950cae54a3fc24176a723a2f634312653a0692434 ./services/agent/internal/queue/queue.go -147146bf5fa222d54812d84ff799555fd4c2d57ccbf8337e960708524f0ecd86 ./services/agent/internal/queue/queue_test.go -171f346e674ac5ebfa562a55e47060a462a0460db86e741085e2ee85533d3193 ./services/agent/internal/state/store.go -592b00ad638b2e523d5e24d0d73286e547ee09c5027da4f6c13fe3885daed020 ./services/agent/internal/state/store_test.go -209c5ce33b17403289c897932ecfa14b1e8f036f89746715d41cca3c607cf405 ./services/agent/internal/uptimekuma/client.go -c849df7e346e1bd1e122037a4eafc30fe461add9b1876d7b9d63a02e33d831ad ./services/agent/internal/uptimekuma/client_test.go -5f0375f33c4c3f0804e58bb7b3a8699a450a05100274fbb19762d59f4d5d0034 ./services/agent/internal/web/control_graph.go -cb68a23bef549186d251766a3d3820b611defd55e8f7386b8b87490a78b9e11d ./services/agent/internal/web/control_graph_test.go -b4f7d72022d5009fce43b5879483de92dacfee2b3bf527306902187c3d26486b ./services/agent/internal/web/server.go -1f517bcb30f95831a06dc2fe0fd9cd633a0c42fc795f67e5ce53de7a2eebd2b1 ./services/agent/internal/web/server_test.go -21b32b0b863ed62ae352011d7dd85f0134bf9909c23ee80b1c7e3c3605ef8cc4 ./services/agent/internal/web/templates/category-mappings.html -d0561b87acd8fe8c0ab7f1f0bb82e1df840db8342f91798e8cb0a0408064abcc ./services/agent/internal/web/templates/dashboard.html -fb5a4e2e1846046032bb878f1e878eb792497e321df6a8ce16209c07a925fdcd ./services/agent/internal/web/templates/diagnostics.html -5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a ./services/agent/knowledge/01_active-directory.json -70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc ./services/agent/knowledge/01_arbeitsplatzdrucker.json -96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 ./services/agent/knowledge/01_fachanwendung-storung.json -521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 ./services/agent/knowledge/01_festnetztelefon.json -88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 ./services/agent/knowledge/01_hardwarebeschaffung.json -4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d ./services/agent/knowledge/01_kennwort-zurucksetzen.json -5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 ./services/agent/knowledge/01_lan.json -95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 ./services/agent/knowledge/01_neue-it-anforderung.json -ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 ./services/agent/knowledge/01_padagogisches-netzwerk.json -f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 ./services/agent/knowledge/01_pc-und-notebook.json -ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc ./services/agent/knowledge/01_sonstiges-und-unklare-zuordnung.json -5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 ./services/agent/knowledge/01_verdachtige-e-mail-und-phishing.json -5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb ./services/agent/knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json -ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 ./services/agent/knowledge/02_fachanwendung-bedienung-und-beratung.json -0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 ./services/agent/knowledge/02_gruppenrichtlinien.json -4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 ./services/agent/knowledge/02_monitor-und-dockingstation.json -bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 ./services/agent/knowledge/02_netzwerkdrucker.json -7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 ./services/agent/knowledge/02_rufnummer-und-nebenstelle.json -efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 ./services/agent/knowledge/02_schadsoftware-und-virenfund.json -2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 ./services/agent/knowledge/02_schulverwaltungsnetz.json -830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 ./services/agent/knowledge/02_softwarebeschaffung.json -fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 ./services/agent/knowledge/02_wlan.json -26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 ./services/agent/knowledge/03_ad-gruppen-und-gruppenrichtlinien.json -b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a ./services/agent/knowledge/03_dateiablagen-und-netzlaufwerke.json -0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c ./services/agent/knowledge/03_digitale-tafeln-und-prasentationstechnik.json -a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 ./services/agent/knowledge/03_fachanwendung-berechtigung.json -f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 ./services/agent/knowledge/03_internetzugang.json -5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 ./services/agent/knowledge/03_lizenzbestellung.json -d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b ./services/agent/knowledge/03_multifunktionsgerat-und-kopierer.json -c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 ./services/agent/knowledge/03_rufgruppe-und-weiterleitung.json -b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f ./services/agent/knowledge/03_sicherheitsvorfall.json -ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b ./services/agent/knowledge/03_tastatur-maus-und-zubehor.json -7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 ./services/agent/knowledge/04_anwendungsberechtigung.json -6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 ./services/agent/knowledge/04_computerraume.json -36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 ./services/agent/knowledge/04_fachanwendung-konfiguration.json -a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 ./services/agent/knowledge/04_lizenzverwaltung.json -e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb ./services/agent/knowledge/04_mobile-endgerate.json -adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 ./services/agent/knowledge/04_mobilfunk.json -1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc ./services/agent/knowledge/04_scanner.json -658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c ./services/agent/knowledge/04_schwachstelle.json -5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 ./services/agent/knowledge/04_serverbetrieb.json -ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d ./services/agent/knowledge/04_standortanbindung.json -9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 ./services/agent/knowledge/04_standorteroffnung-und-umzug.json -8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed ./services/agent/knowledge/05_dienstliches-smartphone.json -47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 ./services/agent/knowledge/05_digitalisierungsvorhaben.json -4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 ./services/agent/knowledge/05_druckertreiber.json -74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 ./services/agent/knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json -a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 ./services/agent/knowledge/05_hypervisor.json -0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e ./services/agent/knowledge/05_remotezugriff-und-vpn.json -9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 ./services/agent/knowledge/05_schuler-und-lehrkraftekonten.json -2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 ./services/agent/knowledge/05_softwareinstallation-am-arbeitsplatz.json -9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 ./services/agent/knowledge/05_telekommunikationsvertrag.json -fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 ./services/agent/knowledge/05_virenschutz.json -e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 ./services/agent/knowledge/05_vpn.json -153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 ./services/agent/knowledge/06_betriebssystem-am-arbeitsplatz.json -d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 ./services/agent/knowledge/06_dns-und-dhcp.json -b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 ./services/agent/knowledge/06_fachanwendung-bericht-und-auswertung.json -77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 ./services/agent/knowledge/06_mobile-schulgerate.json -e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 ./services/agent/knowledge/06_scan-to-mail-und-scan-to-folder.json -cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a ./services/agent/knowledge/06_sicherheitsupdate.json -ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b ./services/agent/knowledge/06_videokonferenz.json -e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c ./services/agent/knowledge/06_virtuelle-maschinen.json -71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 ./services/agent/knowledge/06_wartungs-und-supportvertrag.json -ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 ./services/agent/knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json -2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a ./services/agent/knowledge/07_datensicherung.json -46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e ./services/agent/knowledge/07_fachanwendung-update-und-release.json -8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 ./services/agent/knowledge/07_firewall-freischaltung.json -03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 ./services/agent/knowledge/07_funktions-und-sammelpostfacher.json -a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f ./services/agent/knowledge/07_geratewechsel-und-umzug.json -9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 ./services/agent/knowledge/07_mobile-device-management-fur-schulen.json -48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 ./services/agent/knowledge/07_projektunterstutzung.json -ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 ./services/agent/knowledge/07_storage.json -2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 ./services/agent/knowledge/07_telefonkonferenz-und-softphone.json -748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c ./services/agent/knowledge/07_verbrauchsmaterial.json -7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b ./services/agent/knowledge/08_datenbanken-plattformbetrieb.json -59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c ./services/agent/knowledge/08_datenwiederherstellung.json -7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 ./services/agent/knowledge/08_fachanwendung-neue-anforderung.json -f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab ./services/agent/knowledge/08_hardware-neubeschaffung.json -b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 ./services/agent/knowledge/08_netzwerksegment-und-vlan.json -1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f ./services/agent/knowledge/08_neues-drucksystem.json -b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 ./services/agent/knowledge/08_padagogische-lernplattformen.json -ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 ./services/agent/knowledge/08_rechnung-und-kostenstelle.json -3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 ./services/agent/knowledge/08_telekommunikationsbeschaffung.json -0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 ./services/agent/knowledge/09_container-und-docker.json -4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 ./services/agent/knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json -91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 ./services/agent/knowledge/09_inventarisierung.json -d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb ./services/agent/knowledge/09_protokollierung-und-auswertung.json -267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 ./services/agent/knowledge/09_prufungs-und-klausursysteme.json -27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e ./services/agent/knowledge/09_ruckgabe-und-aussonderung.json -eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 ./services/agent/knowledge/10_haushalts-und-budgetplanung.json -523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a ./services/agent/knowledge/10_kubernetes.json -1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 ./services/agent/knowledge/10_microsoft-word.json -8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 ./services/agent/knowledge/10_schulverwaltungsanwendungen.json -813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d ./services/agent/knowledge/11_devops-und-automatisierung.json -213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a ./services/agent/knowledge/11_microsoft-excel.json -a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de ./services/agent/knowledge/12_microsoft-powerpoint.json -e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 ./services/agent/knowledge/12_monitoring.json -2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c ./services/agent/knowledge/13_outlook-client.json -cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b ./services/agent/knowledge/14_office-vorlagen.json -9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d ./services/agent/knowledge/15_office-add-ins-und-makros.json -7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 ./services/agent/knowledge/16_office-aktivierung-und-lizenzierung.json -f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e ./services/agent/knowledge/17_serienbriefe-und-dokumentfunktionen.json -5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 ./services/agent/knowledge/example-vpn.json -7418ad39aa0899a85e69a9910d99ff1cb01757d5ae2edaa885c5ff7a815cb580 ./services/agent/knowledge-category-map.example.json -895f8400aaad550ff2b96262eaa54e954d603129682e97aa4964539eb19bd7c2 ./services/agent/run.ps1 -d1d4ab8145a7131c36df51d3a9fbdcdeb0cda3de134fe24c0e8d52370862f3b2 ./services/control/Dockerfile -f30ddf9251860d92717276483f7a2c2d7405f0516940ca62abc21d97ff3f3ad2 ./services/control/cmd/engineering-graph/main.go -b12ace89807a1cf558d009299f59abb9103820a46d11900942bbf812ac975868 ./services/control/engineering-graph.json -ba44c599b9faf861eca647614c4abd18548e2708233cf462b11eb277e96449b8 ./services/control/go.mod -da37393e58ff53847f26b1051f6d3a5270571d290b33d79360bae0dcb3513829 ./services/control/graph.go -e6a8021a44219219fe37ab5edc45d1908fc6579f653aa80c9966810d891ad27d ./services/control/graph_test.go -d373caddd318992fe25be3879f3606b81bf393d55a2cb8457d3984b8af499950 ./services/control/index.html -a42b3daf9b816503801f666e322668b3ad106ff8c1e1b83855e801e7db4eabe5 ./services/control/main.go -f0ec671cd3f00083d9b9d69c82ffcaa8efe08122837671e812f2a510f3c4e321 ./services/control/security_test.go -457fa6b4c81a62197eb1e76a5472cd571d5c69659110fcd9bc016582f1b5dfa1 ./services/knowledge/.dockerignore -e82cbdb6336e424c4b9d8db5d149016d606579292184582788a9bf295895cda4 ./services/knowledge/.env.example -236713daf159ff0a8067e80a442ae3404fa28a5251ae6f24782f263bcfc17005 ./services/knowledge/.gitea/workflows/registry.yml -e45bab225ede9bacef0b53f84fb0577235d9a81967ae2a53ab7c7f4d66bd4ccd ./services/knowledge/.gitignore -d3becb2b7565ba0b1d32bd8d3b085dca6adec2d2b153583c8c6855e0eea43b66 ./services/knowledge/CHANGELOG.md -d3c5e5636f96c641b0181fd91bb2cf4802529a7355ba0065cea49356057e402f ./services/knowledge/Dockerfile -31f5067cf8e45271081ff08bf0d9afffef74d300628ddc1bd1db6e87e6d4e922 ./services/knowledge/Makefile -d50e9892824dc9e2f86b7f4fbfdc81165a8c6b403e4b2b9f6fd5bd2f22c295f0 ./services/knowledge/QUALITY_REPORT.txt -bcafd8739157e677416bd4e7c3693eafb1572d72384830ab839dd4dcef74d3ee ./services/knowledge/README.md -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/backups/.gitkeep -cbaed4e619df99e83eef694b12b660757b749bf64c0a340143bab523a121c7f6 ./services/knowledge/cmd/server/app.go -ba6569476482d21ee6eb4852d6f1a8dbe052335f3be4669ef96298d0f5a5028b ./services/knowledge/cmd/server/app_test.go -fdaeb96aea39074522e75c2aeae8036ada253167d2e44bba156b10a7b8cf7c61 ./services/knowledge/cmd/server/main.go -1867f5c83786628441a1c9e3bb96d90d496f5cab24b6dd62a80376f256bfa497 ./services/knowledge/cmd/server/viewer/app.js -9537e946b22208a5a78f11a29a92c8f379805f51f3de3d3c74a7cd068d876418 ./services/knowledge/cmd/server/viewer/index.html -58f16f955f1287bc077933980e50eaae238ab2eb40599242abd820a31aca79f5 ./services/knowledge/cmd/server/viewer/style.css -0269039e6baffbf8ecc423fe021e91827924a9c47decbd0816257aa052702303 ./services/knowledge/cmd/server/web/app.js -7b1cdf4c71867e0563eb0eca460c60be34ab839099793e0ad9faf4a84b29c5e7 ./services/knowledge/cmd/server/web/index.html -c6e48f5098243eee4de816d515cd71d8f8904deeedb335966b1db320d574e2e1 ./services/knowledge/cmd/server/web/style.css -6e2430ead0897eba9ffb7b3df18e61136f54a8da6b4fb9e711490623fca31a12 ./services/knowledge/docker-compose.dual.yml -f5d6b8c7891ac8ab237423914f67bff5b91f7e3abd255622eeef26ec83feda37 ./services/knowledge/docker-compose.yml -5a49eb8794bdb76d14c29060ed6c4a9f0126d4daa2f2ae45b11b8d1e7ccaba45 ./services/knowledge/go.mod -06893c64974dac010903569e7554b1a245a8862538e3dc1b10375932eae6e5b9 ./services/knowledge/internal/aifallback/ollama.go -60727530c541fdce632fe071173c4846c3a4029929619caf9389f31f27e3c1b0 ./services/knowledge/internal/aifallback/ollama_test.go -9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 ./services/knowledge/internal/brainactivity/client.go -ff6d65a0a4648464a89c67e06f5c33a4ec87d7a41e84d894725e8ed582d64acb ./services/knowledge/internal/obsidian/export.go -e8e91a8ce16c1905c963ac57a156006e92323be80615637a7b1b477b89464012 ./services/knowledge/internal/obsidian/export_test.go -7fa9c38a719f2f46bd169438aec3c6e8595b047d63236d2a5e78db9cde6924f0 ./services/knowledge/internal/staging/staging.go -3c1f5726489a773536e430152b41944bd623ed0f88939688744611371462dd6c ./services/knowledge/internal/staging/staging_test.go -3bcb39124c72cb46efc7cf794254ab5821dbd7da57e7a20b1366862a71b874a9 ./services/knowledge/internal/store/store.go -c98bd048680978d06d34303db9729b130eb7cfc0750f15ee1a4e7abfe582dca1 ./services/knowledge/internal/store/store_test.go -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/knowledge/.gitkeep -81893d2cba93d7a30c5c728559e81a7b7e2eca87cae8f9e239aeefcaaf604d60 ./services/knowledge/run.ps1 -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/staging/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./staging/.gitkeep +27dc46be5cbb1b171deff7fbd2f28bff1be802dff403797535fd8968bb98c8eb .cbmignore +2b175ba266562ae5c6b9176e69287f1e4ad843f85da0ee0ca62a9ccc6fb0236e .env.example +e5ae1e5c6c25efc5dd362aec2056b6b354c7044a30f1a43704b157b4b512dee6 .env.subagent.example +4ff38172ec186fe954432741b4311a8a9cac0f89144fb9dca2ec3523e4321210 .gitea/workflows/ci.yml +2fe4f14b9dd2e20e50dc12866d163fdfae7f21267e99aaa00ec65523f0cbbd94 .gitea/workflows/release.yml +e1ff71187cc3411a85067b964264011db7bd109a585ef7b9ea5b08bda039d813 .gitignore +cdbdf1b5b66e81ea25cd6a7ebb707f4e638925202d6b3db10dc02cea1683d484 BUILD-AND-RELEASE.md +6fe6ff56b5370b68a99cc968ca63ce8863b22a001a9d714794abc19ee051b3b2 DISTRIBUTED-DEPLOYMENT-BUNDLE.md +ca915a34965137f3b709a862672d26077c17fb79f7e5c838cda8663ddfeac05a MIGRATION-FROM-MEGA.md +ccfc4c139345a69d05cd2b809b0d09b4332d98e5b4f79aace4db0b648e3ea814 Makefile +335d334606432df08ff1b85683161814ec0ddd8c6dcbd0ec1dfda0eba4c6ff12 README.md +4858caa52c0fb6cf302a1c581d07d448e5e90e0daa797e5819610fd6223bd348 RELEASE-NOTES-v1.1.0.md +01163462f46314f57660677fdef407c6c2884412ea850aab13a4f650e8c29f50 RELEASE-NOTES-v1.2.0.md +4da388ce660aa3b7a0d8075ec066a025b5437960973397360fcb9a5d4cb58c96 RELEASE-NOTES-v1.3.0.md +257d6d3ea1d22b8ab97adb5d35ca5ac806702d75cea66982dc711754040fc7a2 RELEASE-NOTES-v1.4.0.md +0723a0151b72d492899a5c5409e1fef37de1fe4c458c3053f2f7eccd4b6e70c5 RELEASE-NOTES-v1.4.1.md +58923d3292f11487526b8dc4d6c0992025c0f946df8435fde5c91ef4581d46fd RELEASE-NOTES-v1.4.2.md +548f3168f8f29add4f510c4c8048a74465f311dbc3ba2436e5b8db5cc898c552 RELEASE-NOTES-v1.4.3.md +61ceebe5a891388336795fda2fd0d1ad10373b4ebaae7d2e670f35766115c604 RELEASE-NOTES-v1.4.4.md +15a3defdd5bbf07ebaec1a2e8626347a027a3622f10afb508f7a21c9b0bdfa71 RELEASE-NOTES-v1.4.5.md +d10b1dbd87585d72144d816449800dc17b6d7f8faba2dfb4eca93ae3def2c631 RELEASE-NOTES-v1.5.0.md +d069259c99322966b525ab451defd6386ea182bdbdba58122e287df395e2396e RELEASE-NOTES-v1.6.0.md +cd02749b3f18cf635e2ee78f8895c50a397317ad8ccb416311f07a653126233d RELEASE-NOTES-v1.6.1.md +7f4e6dab2bca1c45677c958e71b18797efe3b4d36ebb0230cf995adc0f17623d RELEASE-NOTES-v1.6.2.md +a2d5e268fe70979caccd895156b3d33bd61a3cc7e660ab7a5b07066849b3e248 VERSION +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 backups/.gitkeep +8127e9db5e5e0af1d88770dc8fa60b381de45dbcc843262698cf9501409b4d58 deploy/searxng/settings.yml +4111b49c5e3eb1d1f455d7907b22460fb2ab990ab40888c2462f73a84f7f5731 deployments/README.md +dd91e3bec5c38d1b962e3d5e5511fbc4a298f0434a58f17cc41cb750b6ebc8d1 deployments/agent/.env +0df71e0cca461b82f4991368c6f53c11bcaf62fdbdb3fda0f3aad1555e1bad36 deployments/agent/docker-compose.build.yml +7de0310fc57f7459a176a2106406038207ef2907d9c307e0db106af93ea6bcad deployments/agent/docker-compose.yml +88be11f84b4db7533c5a2ecd1b53b01a27f86eea576592a29f46136fdc550c69 deployments/combined/.env +1cdd0d8f851d7f7d8533443d02496a9f368e4a4194faf39408ea67487b5f77d7 deployments/combined/docker-compose.build.yml +90068015004bc5d08f0ce1f4992a630147341e110fb606230ca603fa6f552b61 deployments/combined/docker-compose.yml +8e0f2dc98203b807798e3bb4bb0b88710f4a0838badbce099acf1ea8ea87b73f deployments/cpu-subagent/.env +fa1f4ebcf753f7d3ba7b49a1bf9b9738d2f9f3cf1b88b3ed4403a819de196b4a deployments/cpu-subagent/docker-compose.yml +dead361b179b6f761b160bd302f304b62941781cd542aba29eb5cfca8ec0497c deployments/cpu-subagent/preflight.sh +6f97ad0b9585efd18f604984987cfe67e4424cd6ae703ff9fd34aba4b56c22de deployments/gpu-subagent/.env +89dbaa1fa401a02ac9242450fa3119caf3d4f63e66d5b25af4cb100f1a8be351 deployments/gpu-subagent/docker-compose.yml +36d44f3bde10d228881273f04a309cbddba394c8bc559cb48f7d37ee8f2f4e3a deployments/gpu-subagent/preflight.sh +9614f2cee637b847ba1506335971652b6b6bf601254d3d9e098b4a9b54fffcde deployments/knowledge/.env +81013b617610a4903ac7e237c231d62e0aa5caa083d73a5a838316db31d9dc55 deployments/knowledge/docker-compose.build.yml +d5ddeb7e193e735b50422771ab580610de82a5099650c6153f9f092cfdbbbc97 deployments/knowledge/docker-compose.yml +05a2a5c066a5474988e50e45a84623060162e58f908eae09d83fbc36b4e3886e deployments/master/.env +ce3d84ef0ff7fc8df89223e29d746b8b6700f69969b2f3750a646f0b2cadd04b deployments/master/docker-compose.yml +0c2fc23f8f6deced618e025cc66fa5711349439814d8a2c6d0f347990b4632e1 deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json +083e25e212b70a4c0fb5cd8d2cd5a5735f8ae230809c7fac40187f785a8eb43d deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml +72305a9fded6b40fe740ec82dac495f006990e6e37de754d1fcd01d93da4834a deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml +0026163136fe3a51e140c73bdad1fa227bd2b38c9f6a69c391cd9d0647ad4e48 deployments/master/monitoring/prometheus/alerts.yml +aac90d7e75a79256f77d730ca80fc1824e86f4299192526910b6f0226cedcbb9 deployments/master/monitoring/prometheus/prometheus.yml.template +57fa3c3be7f476c791da0fd7e9985b38be82f1d9a816d13d75f645f29de57e86 deployments/master/preflight.sh +a3df591bb129f55e3860d36201936dd8ce2080c82dfe59094ad79387ce27557f deployments/ollama/.env +15a8680873d1dbde2b7deca0a30b786f49f4498f499269e9cd24c33e0b243333 deployments/ollama/docker-compose.yml +8d4943a3e023ed3b24312ff0b406298026b4d32aac6873b0c8488812b20dba4b deployments/ollama/pull-models.sh +750138d7d6a4ba4f8a0cbf572722b949f5ebc31d97ff0c33314fafa451ced3a1 docker-bake.hcl +58db2a87b703a5bbddc20b140907d187f8388e7db87b079ae9f6ffaac9c121fc docker-compose.dev.yml +91123a501c0cf14a57c8c7fe3742a366a4cac3ed8229225f797d00282db160fd docker-compose.subagent.yml +d8891ae37a17269f0e4a8eb7eb09c0824271bd537e9fde48f31015d7d9f3df75 docker-compose.yml +e4c516375e450c43b5193b1257880c02f2132b05a16e19ab1c96d57182fa070c docs/ARCHITECTURE.md +34e2c7ac5992389cc51b40f740c1e206f20bffc67bdf19cb59d94b5df67a7082 docs/BACKUP-RESTORE-v1.5.0.md +9467a3c0796a87bada0eab6ad191913e628b4abc05520cbb4b73fac76d334481 docs/CODEBASE-MEMORY-MCP.md +74cab9b3ce3f6ed2bb5f1de99e1fe5c6f311e90550d078e10e140b726674957e docs/CONTROL-CENTER.md +88955b0ea289ccc63cbd3404f5b00bb446b38270408dcee23a6103a698f1f76b docs/CONTROL-MATRIX.md +323402668da4d8e1d97a29c45db753834f6c3a16739eebebf7e016f06af160bf docs/CONTROLLED-AUTONOMY.md +51b4a8d7daff787f099b77bd5a33ebf8038c0812bd3056f0ee6afe17e103681d docs/ENV-MIGRATION-FROM-LEGACY.md +a19522149d097ede5751dd6b9db515146ac47d14aaa7a7b49515e495388d0e0e docs/ENVIRONMENT.md +9c49332c6d0e23cd7f82adf23f61bcbd795ad30401990ae99b23295b2ab4cd09 docs/GO-LIVE-v1.5.0.md +6a9862c2ffa95420a3f8d2c3901a46f00c77a38826913280bd0e367e3133498f docs/IMPLEMENTED.md +65ab928c3a0c283131e48322685d240507a085c1fb059d4c114465774473c335 docs/MASTER-SUBAGENT-ORCHESTRATOR.md +9c38807cc12fba6f94cadc4694996de58e6d2d5396e55fc0c4bd404bb13f53ad docs/MIGRATION-CUTOVER.md +a71d28353529906cee08cf90f35e0b0cf97bbc94ccfcef830145a2d561b21a9c docs/MIGRATION-MANIFEST.md +4330b3adefd8c40d174e1e878ff7cf075d99a44ec51251953d1a58d3a0fa8ca3 docs/MIGRATION-v1.1.0-to-v1.2.0.md +46672e5984cdcf5f31e88dccc273041aa277552cc9c5ecbf59a46b3de133da7b docs/MIGRATION-v1.2.0-to-v1.3.0.md +3738dc79be0598316dc397f1fcc71cedda67b604a3038b116c2ea001dc3105ce docs/MIGRATION-v1.3.0-to-v1.4.0.md +b285d050223844f5fd05014c2278199eaccd7525fbbf8a6dae724ed87ced0ee8 docs/MIGRATION-v1.4.4-to-v1.4.5.md +57858f4f2b6670106edf5a88ce705649d547c65c26407d6117040296122a0e26 docs/MIGRATION-v1.4.5-to-v1.5.0.md +c905084e03788887894e37c040492b8ce2386ecc25ac3ab2e10bfd4b0a7cd167 docs/MIGRATION-v1.5.0-to-v1.5.1.md +12619874bce206ed6e817685d2def62244d6bcceb6eeccbca9b0936fed74b8a2 docs/MIGRATION-v1.5.1-to-v1.5.2.md +44219c5fe32a0d6d2c32719e243793a54f3b7624cb45a497cda78c4a443774ae docs/MIGRATION-v1.5.2-to-v1.5.3.md +fd78b77e18277a8bb81cf6ad33e67c0e7fae3977dc1bbf74787b0cb0cb9a4b4e docs/MIGRATION-v1.5.3-to-v1.5.4.md +4e2e595c0c1b78271a971f6e039c48416114ad9916ad7afc5293c2cff4ec8f9d docs/MIGRATION-v1.5.4-to-v1.5.5.md +b38aaf9d352ad70a9701566fc24d73d26c50c407291fe1c38b08b1afad9435bc docs/MIGRATION-v1.5.5-to-v1.5.6.md +b57e66a589cff7331d547afc0e031accd1d576c46c013143f76311a84eacbcfe docs/MIGRATION-v1.5.6-to-v1.5.7.md +6c234e8bfcfa63f879f0748ccf9ca006595f5ee905b97d316f685154a4a5a2b9 docs/MIGRATION-v1.5.7-to-v1.5.8.md +91e8541a86f3b6b2080439b3177309b584f5d6c637baa286d7ad9d40afbbdba9 docs/MIGRATION-v1.5.8-to-v1.5.9.md +1d114256ef855284dd5a11b40682ce67ad27e17b41045625cae251a16531d0bd docs/MIGRATION-v1.5.9-to-v1.6.0.md +c23f60d75e790ab7317d6bc06c42ccbdb303bce1c11ace2410326257312058b9 docs/MIGRATION-v1.6.0-to-v1.6.1.md +a58ff9a9c9f0842716a7a532556850487d4aacc6032b69c0ffb69df8a4ffff9f docs/MIGRATION-v1.6.1-to-v1.6.2.md +2a01fb10a3e04eae1800a7c7e0aafc31e9bbb23584cea54d849b004716ef81b4 docs/OBSIDIAN-EXPORT.md +040010a807178d33797106e04716822f8d147d7d5833c1ba70865a1827f8484a docs/OPERATIONS.md +69ea49bc76690ac489aea908f4e784a14293e92d62478a240eabea53c5f90820 docs/QUALITY-REPLAY-example.json +bd2d3c43a09a89fefd0e844433064d7d7384a414179a754c6de94232d738fb04 docs/QUALITY-REPLAY.md +6d72b49b5b0ce93b94597cf5688403c468cd3b82f4d87470d80f108c5a7ec531 docs/RELEASE-v1.5.1.md +84b12cafd6320d781b99970cb48b7252da5aff94797d45af164f8a56dee94e5c docs/RELEASE-v1.5.2.md +f0ab4fa6b353059acf6b41a1e5a484ebd698684102724eddf8f8b91d7ff321a3 docs/RELEASE-v1.5.3.md +bc9067063b13788393fc3389ac60c0db50e35cc9d7d733311fca2fc6923834f1 docs/RELEASE-v1.5.4.md +677bf7eacb64adce080dcc1184dca0d7c088727b1c49242df14a7694eabbb593 docs/RELEASE-v1.5.5.md +4cde69f2875d5f97544fb85ff066eb671a40d9670302c81b2a9d9b9a95a9bee3 docs/RELEASE-v1.5.6.md +f7bbf78e76592762fe6e2a08eff9110a273962ae3145da287c5875355672db7f docs/RELEASE-v1.5.7.md +5757dd833f1f8b7b0a7b90b09324cb8989496ea6326d652d659a59f215a3172d docs/RELEASE-v1.5.8.md +245f942b72da17cb6c953720bd9c6742f71723be06bdb842c512851aca351e3e docs/RELEASE-v1.5.9.md +85160313c5dd89faea2e7404f090a6cef13a9b33758124dfc4d748a119375c32 docs/RELEASE-v1.6.0.md +be749a09ddbcd4cf427316c6fe531f138e9bfaa6c104f4c11a08f7555231e8b2 docs/UNIFIED-GRAPH.md +77d91565660789c621b05bd69fecfa3c2ef736f93855eca1f58b017e61c76f90 docs/VALIDATION-v1.5.0.md +3ea1953b2b4eb8f966310a65a12a681fb44e4eb3857b843312e235759aacfce5 docs/VALIDATION.md +be4810451750abb676eee0edcc6f164d86f372f32b185f6f730bf84538cef4a8 exports/knowledge-obsidian-snapshot.zip +dfa65e55e9ccf642ae5ef8ef91c62f4220b31ce4304ea4e1061f2e7d8c1b0fb4 exports/knowledge-obsidian-snapshot.zip.sha256 +99ffd5f497239a5e17b4e4bc79c7a1b5e1e41fdaf9b3f07beb7179050bbddcc4 go.work +5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a knowledge/01_active-directory.json +70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc knowledge/01_arbeitsplatzdrucker.json +96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 knowledge/01_fachanwendung-storung.json +521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 knowledge/01_festnetztelefon.json +88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 knowledge/01_hardwarebeschaffung.json +4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d knowledge/01_kennwort-zurucksetzen.json +5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 knowledge/01_lan.json +95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 knowledge/01_neue-it-anforderung.json +ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 knowledge/01_padagogisches-netzwerk.json +f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 knowledge/01_pc-und-notebook.json +ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc knowledge/01_sonstiges-und-unklare-zuordnung.json +5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 knowledge/01_verdachtige-e-mail-und-phishing.json +5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json +ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 knowledge/02_fachanwendung-bedienung-und-beratung.json +0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 knowledge/02_gruppenrichtlinien.json +4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 knowledge/02_monitor-und-dockingstation.json +bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 knowledge/02_netzwerkdrucker.json +7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 knowledge/02_rufnummer-und-nebenstelle.json +efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 knowledge/02_schadsoftware-und-virenfund.json +2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 knowledge/02_schulverwaltungsnetz.json +830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 knowledge/02_softwarebeschaffung.json +fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 knowledge/02_wlan.json +26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 knowledge/03_ad-gruppen-und-gruppenrichtlinien.json +b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a knowledge/03_dateiablagen-und-netzlaufwerke.json +0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c knowledge/03_digitale-tafeln-und-prasentationstechnik.json +a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 knowledge/03_fachanwendung-berechtigung.json +f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 knowledge/03_internetzugang.json +5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 knowledge/03_lizenzbestellung.json +d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b knowledge/03_multifunktionsgerat-und-kopierer.json +c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 knowledge/03_rufgruppe-und-weiterleitung.json +b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f knowledge/03_sicherheitsvorfall.json +ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b knowledge/03_tastatur-maus-und-zubehor.json +7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 knowledge/04_anwendungsberechtigung.json +6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 knowledge/04_computerraume.json +36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 knowledge/04_fachanwendung-konfiguration.json +a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 knowledge/04_lizenzverwaltung.json +e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb knowledge/04_mobile-endgerate.json +adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 knowledge/04_mobilfunk.json +1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc knowledge/04_scanner.json +658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c knowledge/04_schwachstelle.json +5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 knowledge/04_serverbetrieb.json +ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d knowledge/04_standortanbindung.json +9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 knowledge/04_standorteroffnung-und-umzug.json +8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed knowledge/05_dienstliches-smartphone.json +47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 knowledge/05_digitalisierungsvorhaben.json +4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 knowledge/05_druckertreiber.json +74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json +a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 knowledge/05_hypervisor.json +0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e knowledge/05_remotezugriff-und-vpn.json +9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 knowledge/05_schuler-und-lehrkraftekonten.json +2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 knowledge/05_softwareinstallation-am-arbeitsplatz.json +9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 knowledge/05_telekommunikationsvertrag.json +fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 knowledge/05_virenschutz.json +e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 knowledge/05_vpn.json +153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 knowledge/06_betriebssystem-am-arbeitsplatz.json +d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 knowledge/06_dns-und-dhcp.json +b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 knowledge/06_fachanwendung-bericht-und-auswertung.json +77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 knowledge/06_mobile-schulgerate.json +e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 knowledge/06_scan-to-mail-und-scan-to-folder.json +cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a knowledge/06_sicherheitsupdate.json +ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b knowledge/06_videokonferenz.json +e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c knowledge/06_virtuelle-maschinen.json +71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 knowledge/06_wartungs-und-supportvertrag.json +ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json +2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a knowledge/07_datensicherung.json +46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e knowledge/07_fachanwendung-update-und-release.json +8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 knowledge/07_firewall-freischaltung.json +03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 knowledge/07_funktions-und-sammelpostfacher.json +a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f knowledge/07_geratewechsel-und-umzug.json +9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 knowledge/07_mobile-device-management-fur-schulen.json +48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 knowledge/07_projektunterstutzung.json +ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 knowledge/07_storage.json +2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 knowledge/07_telefonkonferenz-und-softphone.json +748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c knowledge/07_verbrauchsmaterial.json +7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b knowledge/08_datenbanken-plattformbetrieb.json +59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c knowledge/08_datenwiederherstellung.json +7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 knowledge/08_fachanwendung-neue-anforderung.json +f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab knowledge/08_hardware-neubeschaffung.json +b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 knowledge/08_netzwerksegment-und-vlan.json +1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f knowledge/08_neues-drucksystem.json +b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 knowledge/08_padagogische-lernplattformen.json +ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 knowledge/08_rechnung-und-kostenstelle.json +3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 knowledge/08_telekommunikationsbeschaffung.json +0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 knowledge/09_container-und-docker.json +4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json +91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 knowledge/09_inventarisierung.json +d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb knowledge/09_protokollierung-und-auswertung.json +267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 knowledge/09_prufungs-und-klausursysteme.json +27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e knowledge/09_ruckgabe-und-aussonderung.json +eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 knowledge/10_haushalts-und-budgetplanung.json +523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a knowledge/10_kubernetes.json +1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 knowledge/10_microsoft-word.json +8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 knowledge/10_schulverwaltungsanwendungen.json +813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d knowledge/11_devops-und-automatisierung.json +213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a knowledge/11_microsoft-excel.json +a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de knowledge/12_microsoft-powerpoint.json +e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 knowledge/12_monitoring.json +2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c knowledge/13_outlook-client.json +cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b knowledge/14_office-vorlagen.json +9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d knowledge/15_office-add-ins-und-makros.json +7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 knowledge/16_office-aktivierung-und-lizenzierung.json +f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e knowledge/17_serienbriefe-und-dokumentfunktionen.json +5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 knowledge/example-vpn.json +49c48fb45c8387b020bb6a4bde2c81e544ac82b74403e453e28a85f987052978 mega-project.json +489b906fb8469562a110fcb2c47b425abed7f1fbf705da0ef51be2c8881ef12d patches/SHA256SUMS +47a6fa2c79bbba0c04af86dfa65d58529f492c698060fe586456c22a4eadb877 patches/glpi-agent-mega.diff +9b411c90d96a86c80f088ee4637046eeefaf31c62059d11aa1a999d6eb08b5b4 patches/glpi-knowledge-mega.diff +f0491de3cb6201f98ca6be8e865237adbba4772471f7fc7165d030e0c045fdb6 patches/neuroforge-mega.diff +576e75ca9191bbef7f136abde90c10a1c6bf2e36450a421fd3796e988bd2af00 patches/v1.1.0-to-v1.2.0.diff +7a5e0ae1d09b268d3ac62a51f92bfedf6771be26b909605a347f26d47d6fa8cb patches/v1.2.0-to-v1.3.0.diff +163a8a990aacd986bacb385494c2b2062ac10a9a8f0a456d19a6d1746c2fc500 patches/v1.3.0-to-v1.4.0.diff +654de4aac7cf318680872897c3de192afbafb61c7169acb8e7514116df86efde patches/v1.4.0-envfix-to-v1.4.1.diff +1ebc306bd6870389500c86cf07dcd677b5bd0448c762812c7e1c0f8732f36eb1 patches/v1.4.1-to-v1.4.2.diff +651c883facddfd0316f89e3cf3c24fd913371fcec2a5f447b8d105fa5dec463e patches/v1.4.2-to-v1.4.3.diff +d90be604acf6ecf5d821890d5864307853caf0e0678a3249eeaa1d29748a6ada patches/v1.4.3-to-v1.4.4.diff +01ba8cb6ea6ab0abd84295fb969fcfb93d841cd46ae1813fb0de9b23b2d5b22e patches/v1.4.4-to-v1.4.5.diff +617b434dcdc0dd0be5cdb15740661f0a513b330b99835364b508e21467f86ae4 patches/v1.4.5-to-v1.5.0.diff +b6bd8c6c04b5fca8d768279a5471c32e15dbcc57449b7d020f3e228f47adc310 patches/v1.5.0-to-v1.5.1.diff +b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 patches/v1.5.1-to-v1.5.2.diff +5c799db6701dce8019aeead5bccef6fc5fdc869e30785853eaae8d0eaf7ea43b patches/v1.5.2-to-v1.5.3.diff +27fd7fe12e9d774ced67cbcbf0f2e2e385736b48d01d655a1a9cf4b14b39bdcb patches/v1.5.3-to-v1.5.4.diff +7d0c4c85ee6cb89c6200cf35f23ea92c93ff5fdd9f2887c179c83fd5e5657281 patches/v1.5.4-to-v1.5.5.diff +405b41cbbd1ec48f316b1d935e67bc8d002fa1a3bd583fb813bf0875bd30a2e0 patches/v1.5.5-to-v1.5.6.diff +257a78b8f6613f92086a04bf53455f12e9bd1ab94775add31819ada465582163 patches/v1.5.6-to-v1.5.7.diff +53ab438b31a31590edbfe00eea6ba9d9ebff2b4c6255d7ac2056c86757f65c3a patches/v1.5.7-to-v1.5.8.diff +24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 patches/v1.5.8-to-v1.5.9.diff +e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 patches/v1.5.9-to-v1.6.0.diff +0cdce94c11a931e11c3c908f2fa3dfa733c5c602ac00f7f89e41b8c301574766 patches/v1.6.0-to-v1.6.1.diff +564817f8edabde0c4e4a1a427a3aa5418aae7bf12e9463044a7e6e0f13973657 platform/neuroforge/.env.example +39319b6f2058e4c8d6656a9cf01675374f81a04075b956b093a2befb5e05ada4 platform/neuroforge/.gitignore +189486a885c7fb78e0eb878d93cda0c70ca6d7ff9bfdfb3f5f32487cf03a9688 platform/neuroforge/BENCHMARK-v0.5.0.md +b9cfe0119f316b8143ebd33e9f66a8da36493a2c6c17acf2519ecba023223424 platform/neuroforge/BENCHMARK-v0.5.1.md +fc3aaadc0675ebb6dfeb42d9612a68c1905ac575e2c33299ca6e1522d8bdbeb9 platform/neuroforge/CHANGELOG-v0.7.0.md +a2908a0931e63244849016898c26eb023200bb9c1b1f7a8e62f5858000c94fd3 platform/neuroforge/CHANGELOG.md +2b286a4066da5b6bf7ce83c42ba7c97df6d4c6f8b71aa3f28d1ff824a7814391 platform/neuroforge/Dockerfile +9a9fd7708b61027e3677044ab1cbf7177119eac796df4d8786d39011da28729f platform/neuroforge/IMPLEMENTATION-NOTES-v0.6.0-dev.md +3146656e702607501cb348c77dadff608f9c152c4104647230848004e2692251 platform/neuroforge/MIGRATION-SQAR-VECTOR-JOURNAL.md +ee7f52ec20dcce87b751637229fe26c65d5cb0a926ac32915f46bf1b5eedad3d platform/neuroforge/MIGRATION-v0.2-to-v0.3.md +3095ca2e4512744ccfb2c3c71d80dfdbd42fe24e556a374061a76b80cbc33213 platform/neuroforge/MIGRATION-v0.3-to-v0.4.md +680eb77c8d645c3a2afa4e92e3062d1dadaa079425ed8c3f0d73393931354b59 platform/neuroforge/MIGRATION-v0.4-to-v0.5.md +0a21148446a057c773700fcfb026230fa771e970482bd6bdebb9cb9a3c11f4d9 platform/neuroforge/MIGRATION-v0.5.0-to-v0.5.1.md +59d223ec40991ef152f32b8bec26fa283bfa6318942e33a1a2169a85b361d9da platform/neuroforge/MIGRATION-v0.6-to-v0.7.md +4fc6e6f43fa70f4fa8a085225ecdc7ecd460bbcebca01dde70fed48a4c933c68 platform/neuroforge/MIGRATION-v0.7.2-to-v0.7.3.md +02bc0ac5316d063304b17bc51780f2fe55fbdfc44abbb151bac2397a642174ba platform/neuroforge/MIGRATION-v0.7.3-to-v0.8.0.md +9dc61428742d401d226d2eb8a74577b881309e67bb5c609d01b5152a18cb0bb1 platform/neuroforge/MIGRATION-v0.8.0-to-v0.8.1.md +7bbec66a7539fa9f65d8448e2c59c2f76797679f24d3b96691dc8c68e81d560f platform/neuroforge/MIGRATION-v0.8.1-to-v0.8.2.md +5b9714a45f9a5d31a536eca9b2461f408751882aa6909bd2bb6398b8d8907fbc platform/neuroforge/PRODUCTION.md +3d80eb1d37e9f6bd828d3d499ecc88e011a9f34b44d5fff5a89fd517a39907c7 platform/neuroforge/README.md +38bdb771c428636d18cb4640653ff7028715d5bfc5ca06d5ebc59f8ddcd095b2 platform/neuroforge/VALIDATION-v0.7.0.txt +73b7f0b287ae15f230a7ad2996ec305e787e7a8b096f7654fc69cf4daee12673 platform/neuroforge/VALIDATION-v0.7.3.txt +c3d34504b1b8a4ef73382df061c7b272a55c2a3a7a720435c6050254da3f22e0 platform/neuroforge/VALIDATION-v0.8.0.txt +0371dc02e4ac40b3197104f0aea41fb1b7be9b60caad4fc203eae1de1ac8191a platform/neuroforge/VALIDATION-v0.8.1.txt +b4896439112f9ca0f0a43d55d43cb3a2c4c802c568b65a96d04a3ca9d841c358 platform/neuroforge/VALIDATION-v0.8.2.txt +780363b8ef02ff7e83eb72709f6396b09dec21a26403bb44f58ea8d6757fe33d platform/neuroforge/VERSION +0c5308f5a3d37ac23dce162fd5fab78ce598e41671db5dd50c9c4ae7f215d49f platform/neuroforge/cmd/bench/main.go +6d315373348d82175d0666c709e4f0eec992f1349bdefabc968c0c2e8afe398a platform/neuroforge/cmd/server/main.go +3613379f9f9c3df373b6d686501e3e0290451cded5eae3ca307e5e14ddc829d7 platform/neuroforge/cmd/server/main_test.go +9884e323599a328e16cfe08b52473cc9346b17c3f5969fce4425d20fd0f42a93 platform/neuroforge/cmd/worker/main.go +e3dacdaec3c629dd432f24218a50fa4de5e1698ac7f27e84b14f776b50beeb83 platform/neuroforge/deploy/learning-policy.example.json +3765a5faea1faeb72aad7878ff0159a56fa1b0ca1fc94a348a8c599e460b61dd platform/neuroforge/deploy/model-routing.example.json +dcb7f80239cf955941ede9bcdc3bda8f39d81db5e5b0e0962b373036b40a0df5 platform/neuroforge/deploy/neuroforge.service.example +72443642fd4c498554ad6e46db2a096c97530fba1ba20b842a478345fe54c46c platform/neuroforge/deploy/prometheus-alerts.yml +c30e5b2fd39e72894db22499259b6f97d225c5929a7f3472277853a348cab9be platform/neuroforge/deploy/prometheus.yml.example +dbbaa7fd4430b9cdb5302144b6d40f7ac4ab9f73e81da4ddb51e788dd373d95e platform/neuroforge/deploy/searxng/settings.yml.example +a8962d0a1adca3a80b91d1eeddeab36b91c1cf7d84f39c84bfcedb85ccf55a52 platform/neuroforge/docker-compose.yml +fc993dc95fa49802ecb62994e4140dff18a27438e8a4f3c6352229c79b041710 platform/neuroforge/go.mod +1dc1d58bf4761f2aac3f2436f78c5942bf3a5968ef88840693dd6042960f7b7f platform/neuroforge/internal/brain/brain.go +976288422c0c4116d8c98af8a9b164ac670f9caf03eddc2d5e2e48747456d3b4 platform/neuroforge/internal/brain/consolidation_test.go +cbf267375414612ce2d4a697606576f756ce3611df543ca8cb63b6c2fcc64e44 platform/neuroforge/internal/brain/distributed.go +0eb3cb86d1c235ac21527aae79de7c26ce204412814c5ea84dd9389e0b0fca2b platform/neuroforge/internal/brain/goal_progress.go +6a1e33b71d88f963f244a5a00ea45ee65bcff4882a562148d8ba925b8692e083 platform/neuroforge/internal/brain/goal_progress_test.go +bb4cec696bc7e5fc2b1f2fab0b5c01552f523a7dc2bfa1cb5be5e5374b304162 platform/neuroforge/internal/brain/orchestrator.go +03ba39168194068f59e4be837de1c152e0d10e316461b235c61704571cfc0e08 platform/neuroforge/internal/brain/orchestrator_test.go +359955653c647125559afd6dc3ebe69aa5ca19ff7e825ce801b7bc24e5fbcfcb platform/neuroforge/internal/brain/policy.go +27e87af473d2d71ba94ffb9bf7a70934776f8c23ce45496ca0998ad3000fc156 platform/neuroforge/internal/brain/policy_test.go +9022a797aa1d49af20ced5d8c3e0b903be740fbaa7251e8a80d43cd04d2f68fe platform/neuroforge/internal/brain/research_quality.go +0a3c8f7d149e814e091595982dbaa69467f6bf11eec631471d133a9b21585ae4 platform/neuroforge/internal/brain/research_trace.go +f90474aa355d3c4565e61662e863bceb089795af01802e9b228c1b6c545bb7d7 platform/neuroforge/internal/brain/staging.go +4d7715622739a43b043a1f08ae7165db6ee6bbc22f3ab94945f56f226dc4037d platform/neuroforge/internal/brain/staging_quality.go +d4fed8b68d31f6fbd1992bde76abd4e7b2f9d211e53bf8b234369a6a53692fe8 platform/neuroforge/internal/brain/v3.go +3ae13251512ecad1423a33ce09889f961130fefaed9342170b2d5cc6b3b51893 platform/neuroforge/internal/brain/v3_test.go +4bc58463b659bd7e51db4c7dbeba053de90fcb41f392a7d6e62a8cd84ddaa092 platform/neuroforge/internal/brain/v4.go +a9619d9571f6ab6363b36af9fdf1690f9f88e9333eb6f2773ae68580b19666d8 platform/neuroforge/internal/brain/v4_cluster_test.go +76319080d3faaf856e9fe5e1aac5e06be433d2f06cc6b152bac5f05dcc943fff platform/neuroforge/internal/brain/v5.go +816b725594ea5f6938999799394eaea9dd619addaeec5447d8e7707da4c28c69 platform/neuroforge/internal/brain/v5_cluster_test.go +cd8f1281e25ce42ae8918abbed16d4bf57ada9c3a3cc1212edd28f77ccc9328f platform/neuroforge/internal/brain/v6.go +312e7578b5800ccb06755c19d7ef7026a362443804ff78cb7a11a7443af2aab5 platform/neuroforge/internal/brain/v8.go +31420af7145686a2c7eb42fc18ca1299604afcb5942cfdfd132bef5c5f05d980 platform/neuroforge/internal/brain/v8_test.go +e8e3defcef9ca18adb00494f85df463968cf3a9db37149d97ef02a7a2719db57 platform/neuroforge/internal/core/types.go +65a8b8196343e7cfd9444ca314da4a83c81bc046d9478b8b93217d9cc68ba562 platform/neuroforge/internal/cost/cost.go +b9bb2934e01ed2bfb6b16e4139e10e51f1387112eb8654f7f692779a2fd2d273 platform/neuroforge/internal/cost/cost_test.go +fb66ce4ab760b979eacd4f7f17dedc41916f93c53ba582be572d250f99654695 platform/neuroforge/internal/httpapi/admin_app_auth_test.go +eb2b8c599e4e0838d920296391ecaacdba1578c4f3a6a0a9e8469fdba31217ed platform/neuroforge/internal/httpapi/goal_duplicate_test.go +8f67172078339d35e61a812991dd3e48d9bb00febc6935a8be212d59d320c8c3 platform/neuroforge/internal/httpapi/httpapi.go +680b7fdcb20ac76c9305dfb94e4ec7474526be7a44b56afb3db7e4a66c8e2c38 platform/neuroforge/internal/httpapi/index.html +20f4a6cc30d5ce84ceed4fbdfc9f5c57ab5f82274e0c54464817d571e0e881b0 platform/neuroforge/internal/httpapi/integration.go +c346dd0205548f7cdb00ea026f1afd82d59f87824eb983ebe84abfa7cb078974 platform/neuroforge/internal/httpapi/integration_api_test.go +f8cfcc7a2bc781394231c25e36e75c41563f5250a1387e5687880d2591efd93b platform/neuroforge/internal/httpapi/integration_graph.go +c289fe8748d28d4c1207b22924f7d1a7b2df25703d6df5bd1869acb325211b2e platform/neuroforge/internal/httpapi/integration_graph_test.go +29c365a797f1f162f4d7a5d32833123f7930b73ca3b649fe31e117453dcb9197 platform/neuroforge/internal/httpapi/knowledge.go +409dd8d7c5ca93cc82ce2b7bf17a12655c97dd27ad9767aa40619502830a0ab2 platform/neuroforge/internal/httpapi/knowledge_integration_test.go +cf285170ff175ce2bb223b4ffd87a35da604ec73b5fe386ac35402af1f9c3531 platform/neuroforge/internal/httpapi/knowledge_policy_test.go +3bca3bf2f3d486892cee5d7e41920517f2df504ae729ddbce7d840556fe08625 platform/neuroforge/internal/httpapi/metrics.go +c40412c77dd8635bbd4bf9c6d7eb11b0d7902aee896bdd1ee99e5cdfb72c7453 platform/neuroforge/internal/httpapi/metrics_test.go +83033a18a319ffb162c7cdefb2efe108a47dba81095ac34662349e232f8c22da platform/neuroforge/internal/httpapi/model_routing_test.go +38a9388b6907975aec933697ad87ddeb9e7016a279523f2526db3810df70b006 platform/neuroforge/internal/httpapi/outcomes.go +0b0b111e6b47f38dd3ca331a0c1b79b3de95012820f1c04cbe37c79460c09e63 platform/neuroforge/internal/httpapi/outcomes_test.go +b3b1f5cfd6b0ec04341978898d72856af03a95b8922daf9864caeb27e37f1925 platform/neuroforge/internal/httpapi/research_live.go +b37f7ec3be867666999a9ec314139dd1bc4c74efb53edfa224b4fe8119858aac platform/neuroforge/internal/httpapi/research_live_test.go +4e65ab0a760369a6d6c4932c180ff6c67f0768d91e08ccf471f53c6ecd5d09be platform/neuroforge/internal/httpapi/status_dashboard_test.go +3eb47f1b666336efe952961c19a1f46a7475acff8fe4dd7a59ade000aa1b9466 platform/neuroforge/internal/httpapi/v3.go +b1587e066b56ef72f17162583698614d31c413e2ac7e75359b731c2e882a50ee platform/neuroforge/internal/httpapi/v4.go +1ef5cacb0652f2a6301c57f8987dd848fbe590abb73cab44899d9152ccce45d0 platform/neuroforge/internal/httpapi/v5.go +1d534726d7a75f21f7ad28a959f73a3402c021f7f4a1b554f0600cf3abf669d1 platform/neuroforge/internal/httpapi/v6.go +fdf937aae569dd48d09da63085fa2656299a3ab2f55c7fc7f718c46df39a2683 platform/neuroforge/internal/httpapi/v8.go +0c8c389b085e4546c3269b1bf0c539414f3603522ddfd0665261a6448f31b514 platform/neuroforge/internal/httpapi/v8_1_test.go +889b82a7f90be0590d0087ccac06aad330710c19f31c43b12c28480a94659be5 platform/neuroforge/internal/httpapi/v8_test.go +60fd100c9057c14d78f91d636f7f23aaf2767088c46b7670b4266ce22cbb5ae1 platform/neuroforge/internal/ingest/extract.go +7576df6ad6f8b692f14db739958fabe583cc5bb547c6068739fd47c45910d2ce platform/neuroforge/internal/ingest/extract_test.go +38a02bd5df61d1e7b257bcee8120380d38bf8c70ec329b6222f1092f74495629 platform/neuroforge/internal/provider/provider.go +f9cfbd0bab443faf39973e6fe1777d7137ae5dcf58fc1b0d6295099e1b3fadc9 platform/neuroforge/internal/provider/routing_test.go +baace266205a584a53463af76038e97771f088a38e42fe333bb548e43856e719 platform/neuroforge/internal/provider/runtime_test.go +2907860f1b4a30df0522f4808c58808f5b184b7b6a3733a959b4619c528379e3 platform/neuroforge/internal/research/searxng.go +e890897c662b070997c3b18134bafbee5bf9b3fa5220045ac09dc6e3dfd4ac41 platform/neuroforge/internal/research/searxng_test.go +fe35f46c0b6dee7283142dc818f484006fdde6d71906d5f5fb3bc05c424ddd4f platform/neuroforge/internal/store/batch.go +c3bd0bb04e951181c72976c246673f3b7c80e57b9f49fda47ce73702df01a5c4 platform/neuroforge/internal/store/checkpoint_order_test.go +8e7a029a904ca1c22821c5e4871c72958f0d2645786871d75f3e00c186b00778 platform/neuroforge/internal/store/cluster.go +6637e541f61a58f624baca31d972095ea01a95491ddb30492d9ac055740213c5 platform/neuroforge/internal/store/diskann.go +1858ed8045b16282de815a6668ff6027c7a3a3795e38659c753c504d0f2a8383 platform/neuroforge/internal/store/diskann_test.go +366e3b2f29b4abb1997aceb9c2b934ebe8f4ee44605b54167c70b8ddad683c7c platform/neuroforge/internal/store/goal_duplicate_test.go +38f24189c76652f12da9eef7a617455dc923499e2f21cd3c29d94231dd0d7460 platform/neuroforge/internal/store/graph_index.go +bda98567153542e9119757ce5f8f6014334ac9f80ad79a26ff83fdfaccc1ca8e platform/neuroforge/internal/store/graph_multihop_test.go +95640acc25c6d815f47a9fb390b71e58205d3d6118d73258031faad778418490 platform/neuroforge/internal/store/index_segments.go +478efaeb260cb5e970af8b3bb99f232ef846f54c6e4ff9deb5499df6e008e1c1 platform/neuroforge/internal/store/index_segments_test.go +eef4aad176d7f6e740dff4ffd81f3dbacbcbe6646e724331ae010ab50db04b30 platform/neuroforge/internal/store/knowledge.go +e9aaedbf4d5e9a43f4a4a5dbe2e9e0f0257b952fb59e82bf729aa5e761955c1e platform/neuroforge/internal/store/knowledge_test.go +43de0f541fb47633b02b25686544d44887c17b47e8d01cb23f8e57567cebdcd7 platform/neuroforge/internal/store/mmap_linux.go +9ce033d691157037fc6df719b5ecd8fb29d6d881d1862e2d50dc0c6c573b5f66 platform/neuroforge/internal/store/mmap_linux_test.go +ef6135a9039fb45361e57107d3968272b4b365ac833e9e2e58b0875566bf5a07 platform/neuroforge/internal/store/mmap_other.go +fe98dca9f919cb52140a916de08e76b88bd0d08b6b83fc8b4e5220411998b0fc platform/neuroforge/internal/store/observability.go +fcd87a843b6dfd3520185bdacf9d732303ff39befa31b513225618560eea1567 platform/neuroforge/internal/store/orchestrator.go +13ae3b3d7b797cd4d6d6775cda15975428ff4cec96738ae53594763ddd6a9ef0 platform/neuroforge/internal/store/orchestrator_test.go +66d612efe2462d76d79cc51b8b6835b5990d4a79a2dc036c26b040554357ae29 platform/neuroforge/internal/store/pagecache.go +5df97ac71adcea7627756a52d295d682900d3b812a977dbe9a4911e4c4118809 platform/neuroforge/internal/store/raftlog.go +fd6769bdcf1f7d22ced9ea426fd4dbc52b50b5483b4be42ac3b6adf637c926ab platform/neuroforge/internal/store/raftstate.go +fa892de65ab1c6ef8f4f811784cc3852854bf7e7de7e0f9c63a34363a6e8f28f platform/neuroforge/internal/store/recovery_hardening_test.go +f811ffd393ec7e72a3dd704eeebef7da1a78446169f451a44769bb586d7f0049 platform/neuroforge/internal/store/research_runs.go +c5c976d068317c688611bce153453133ebd7cf19b835955c77115b450793fe9b platform/neuroforge/internal/store/segment.go +ea968456538367ed536c212707209a173098ec65823152a0813422bc8071c952 platform/neuroforge/internal/store/segment_test.go +1d8071889b7a2be9faf029a1dd2981c69a9914798869bfe217f6db105340323e platform/neuroforge/internal/store/source_index.go +4d41586c15bfdd13139e19bfa102613f18ddc6120e4214a1f45dd9e2eaa195af platform/neuroforge/internal/store/sources.go +0ff464ef7fab01324d77ee4e406a8c15451b3333b75d4afa444be3c3c94f016b platform/neuroforge/internal/store/sources_test.go +61cd82b106e74f2aa0a7591d757a0872491a635dc1fccc78c66d41c052d0c9d2 platform/neuroforge/internal/store/sqar_vector.go +7dae4f397a98afb0fca66e59420a7799e69e8b704818d79b281dbca999ea8070 platform/neuroforge/internal/store/startup_compact.go +4ec1d8893f80f52980ff045a3a142dc9f161b3e061318755d2179950b7071956 platform/neuroforge/internal/store/startup_compact_test.go +b631e8b2fb6b5576653ea8a54672638e4e05e4ad509ea853f90b25ccec4ac5a4 platform/neuroforge/internal/store/store.go +f86b648e8b4845ab83e672ae3fcfb0bcb0dc1bf891abf4bd30d380304f0b259a platform/neuroforge/internal/store/tiering.go +2138e467981a3c814d1e88842dfbd470b76333898614543cafb4f93ea8b9beaa platform/neuroforge/internal/store/v3.go +091e4c9cacfc76786a76f6afc4eb2b697e5a641e611475b6dab97e99371360ea platform/neuroforge/internal/store/v3_test.go +7f0bca5419a895258b7921e940c3fd69cce04dedcb0b9d8f1127cd24b9c9e201 platform/neuroforge/internal/store/v5_test.go +b3a0115907719a8a7aa9e6b244eec55be71a2535ba20db65907987d5fb78dc77 platform/neuroforge/internal/store/vector_journal.go +bcc12eb91682f3c0a626f5baeea64cb3809e56f087b9f1178a6c2546f5e301b3 platform/neuroforge/internal/store/vector_journal_test.go +158125b5585b59fc1d2af7ab92c9c86ecffa2a7e3cd5147a19840ac103d348a4 platform/neuroforge/internal/store/wal.go +b369c68fbfb7bde75ba37c40948651789fe486f7c651ac245c8fdf8573c7ecf5 platform/neuroforge/internal/vector/hnsw.go +09ac0fa04dff1446893e846a95dc1131d6d1a7a54b77b48cdab8e46fc6124b7b platform/neuroforge/internal/vector/hnsw_delta_test.go +4bfd79da525c6d7901db01bc09c823608b641970e40aff9fdaac33da15d4432f platform/neuroforge/internal/vector/hnsw_test.go +6eac7fabc0f8a3cab53c758502895576fae158e3d672f8c87ad58588523919c2 platform/neuroforge/internal/vector/pq.go +275b0dd3b8cc3589541e3cfb52a2f1a372e40446e2e0c9c902388fa832e4f0dd platform/neuroforge/internal/vector/pq_test.go +c854f70f4141344dc2c6fbec4419afe5dfe25fd1bc2da11e41781730a21c8834 platform/neuroforge/internal/vector/recall_test.go +32f55e82419e4b043d15682c82ade1c4bc9694bfa372f925c131c2611f48d744 platform/neuroforge/internal/vector/snapshot_test.go +c5463c525f5ea703ea1f6df74cabc30f937f3846b1f75f237adeee2700baa977 platform/neuroforge/internal/vector/vector.go +276cb34ddd9f87abdac17be110e2bf15135ab83ba66bf91663d59d61fbd81125 platform/neuroforge/neuroforge-v0.7.3-rtx4090-example.json +6088a896352046ce9e04b1a3f8ea30aaa98e5681a4d393fa5b6bfd5288a3ee2e platform/neuroforge/openapi.yaml +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/agent-data/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/backups/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/knowledge/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/ollama/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/staging/.gitkeep +02ebd40234435473fa5e4bdb03f035f22564049a6f43243d7f4e175c7239ab78 scripts/backup-data.sh +df684b1b487762248516aebb82742e5777395b504cbde42ebda85b999ae353be scripts/check-compose-env.py +6f1d21b7aea3265a088256801aa37a00931157523d8bd834aa4f473e99511f04 scripts/codebase-memory-ui.sh +bfc2c31612e57904efefd44dd6fd2f66acdca03e7228b4a31da2a7fb2a5b28a3 scripts/create-network.sh +5abbc6b60bca94fcb880abebae19c85c6a220c9eabb9865928b23ae86246705b scripts/export-obsidian.sh +01611fcaef13df301147ad539f38e96e09d32c8f0f17c0fc9a7e3dae38de6292 scripts/generate-secrets.sh +8b0a4be74d87cf42c98360819c6f797b9caaf10528e729159412daaaffba0771 scripts/go-live.sh +3df6e58552fbe5e29f77a66c872339bb39a13329e8d63e34b3b2afb41585e84a scripts/health.sh +02cd54884b3ee16095a7c111579361b9af5920ec55349627423db96dadc63854 scripts/preflight-role.sh +0d28cf37186f9faeff2a40e506e3a3e5b8fe3a399c9c426e369bd26c9021f7b3 scripts/preflight.sh +fe35fa5db6dabf06e6266e0de4c372604160180366d4c0a8042c7ee54b7c6dcd scripts/propose-draft.sh +1744be0629e845c8d94dc2a06e91ca2cc9ce5928db00300b7fb894445aeda9ff scripts/quality-replay.py +8b745865c41ae3fcc89280c861f78918ee947c1fae4973ca95d00a507fc3afcb scripts/release-gate.sh +76358a00fbc9698879d363dfca6624a13e7633181cec2ce05368cb01811b2580 scripts/research-up.sh +697891580c9713e6451c1e0f10509b009cfa3d3c5af4a1347f841e6f05339cea scripts/restore-data.sh +e504b53162490545302b8835bc1b8f22625421a4e5d91449828837488fb82ab9 scripts/secret-scan.sh +a2c56eb668137e058cb78bb592ac359ef7e608cb9bedc7eaa930b3f4f66411ca scripts/status.sh +2970d5f5049323b14a3f3983c2f4eb5392d745b4fea34742e3cf56e52d740f17 scripts/validate.sh +cc0fc4433db0964ed172a82f2910e55c0f3dd6d59b1ec04fe0541b8fe5a912bb scripts/verify-package.sh +17d7149607bf40ccc6f64a3c4cf3e4023a8325675918c18df0be309ea9731a36 services/agent/.dockerignore +d1d8c18c6f7c7c6f0b2d72f421837d010afaafc931596ae48442a826e50f0bc6 services/agent/.env.example +9ee46f2156ee805c4ff40e418b004ead5cfbc3a9b6df3968a1b772fe1b313cdc services/agent/.gitignore +4c893d6049499eb70ca755a333e97f0a726dd212e4ded4a9d4acabf0a25d9bd8 services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT.md +9339ce57dfc58d4731ea02c8d0ab9f2835e5966a29c071062fc6a2effd17da2c services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT_OLLAMA_POOL.md +81c91a44c5d3b7eca3051d8cee35f231ef283563da7a636c56c98b61da60218b services/agent/Dockerfile +2ec93f81187ebf92665a6a15a6672b2f7dfce23bfea1546f2d4188f49de5dce0 services/agent/EMERGENCY-HOTFIX-TICKETVERARBEITUNG.md +2bce6e10780004bc12042a416b8a05b417130db2d730d01a95d9d86a564e01cd services/agent/ESCALATION.md +bc344ff4486bd38356fab0d2da46590d9f738ece0f712d043597012f215bae49 services/agent/HOTFIX-GLPI-KB-AUTO-REPLY.md +3f51b802804e9004d17cb74a7e0b8fc668537e75bc95e7344923e77d5f05f13b services/agent/HOTFIX-GLPI-KB-SIMPLE-AUTO-REPLY.md +65408599c64a4364f41fe0876f6531b861c352eaee4afabfeae52a5a72f28f6b services/agent/HOTFIX-GLPI-KB-UNCATEGORIZED-AUTO-REPLY.md +dacf2060df228b5c137fb5ac5c5f7377226f937aadaae4b01b662fc54abea11e services/agent/HOTFIX-POLL-DIAGNOSE.md +6b2188c579db7c47cf7be54328f2671a8fe7aa17f21556483f751fe2457db1c5 services/agent/HOTFIX-PRIORITAET.md +30a29a59cf3c409bf33e3adc96789b9281a19fa86f961c727dcdfab07ff6a327 services/agent/HOTFIX-TRIAGE-KONSISTENZ.md +c0e13c6703cf3feda57a9eaaa718e501280fbb526f466040eb685a27700b5797 services/agent/IMPLEMENTATION.md +1126322e2cc8d165adc4c792eeb195717de2bcc7b39be1ce77959d78e87ef685 services/agent/LICENSE +0ed7ee0e4846be09994dd5f3b49ffdca6b59177b9018b1947388949f0dd49a14 services/agent/Makefile +80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 services/agent/OLLAMA-POOL.md +80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 services/agent/OLLAMA_POOL_BETRIEB.md +a7a3f969150005ef062e7a77cf852371931bc2fa5ca4be01cb0e9eb07d35335c services/agent/README.md +aeac85d1adc92a8b34ffa5a95f7f19c2c6fb94742a7359a7fd452529d59ad87b services/agent/SECURITY.md +edb92cdf2d1863df53ea8045657f64e415f206d503c8da3b45ea96b7f7f0960d services/agent/UPGRADE.md +68cfcbf3bd8eb0c24f12e41cb3d95c1654b98fb7cd8b9606844d68e409428c23 services/agent/cmd/agent/main.go +98be0fd63dd4fde52342c5385da113c926ac29195aedb657c0ca0f98e1593fff services/agent/compose_local.yml +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/agent/data/.gitkeep +b74f901cca99eae4bed1b168f1f077a28bf1134a71c867d263c7834f6d896684 services/agent/deploy/glpi-ai-agent.service +e8327a246f048c70f3b2b426f5f942bf16c41e49ac385d49c296eb994dc0cfd8 services/agent/docker-compose.registry.yml +29134dbd1bb19a0fd87b242a184b989a70f3a4c784254d86e2a0f5fbdaaa0497 services/agent/docker-compose.yml +a0105475dc054977223fac36618b8cd8137c55be1d11fddcd24e9a4d3074c170 services/agent/glpi-ai-agent-neural-brain.patch +8a7805be0da45875d45a5e5b26780fe659f671edd2d6b72c0d7da9606bf15755 services/agent/go.mod +e34ac0033c53effcf654689641a15a92deae1a987e3d92e9921b7b030efab3d0 services/agent/internal/agent/agent.go +789e55accdbfbb99ef8179f5680aa340e67a055c71686533302558389dabfb8a services/agent/internal/agent/agent_test.go +a8eedc80f57b174fbad3850d787346f7b370236b4c2eef67905ef6b60a1ced64 services/agent/internal/agent/analysis_runs.go +46a8dfe5e80569aac48f9a098acb2c1089ea4ab9521ed6c92f53b020e0c76ccd services/agent/internal/agent/analysis_runs_test.go +05ce267b4827cc5b89332b72fa6ecbcd80bd3796b5143bc40398b932f5f45867 services/agent/internal/agent/candidates_test.go +76fd850dc9bedf73db32e9b2d49edfb04624709507e0b67fea1f88730e87720c services/agent/internal/agent/category_mapping_audit_test.go +d8aab6fa743e6ed8246ac6e20f993fc3642c635a1cb349c4fb6579b863a2bd63 services/agent/internal/agent/escalation.go +c4bf8fcb4ba6eb7962ec55cacff806a8a99561d134ab5238abd012763a2f9c74 services/agent/internal/agent/escalation_actions.go +471cf47af1220f50fce99ea41c59adf257cd29417ce23fafb4728f0037d33842 services/agent/internal/agent/escalation_actions_test.go +7a65f676bacd5a7b879862e4c1a157632f541a570a71bd8b57864e603bb4cd60 services/agent/internal/agent/outcome_learning_test.go +5175e4fa9c394c0a8df94c29c69c5f896b3227a7d8de67d214aea4634ba0fad8 services/agent/internal/agent/policy.go +5434e1c4dc61eee33d6dceaff69156a5b9ab4a9fbb555cd056c8cf0444601606 services/agent/internal/agent/policy_test.go +6b70a29176602e499935a85ac8831dbe36bf75fddb130a56cda376e124ecb7db services/agent/internal/agent/status_reply.go +6f95bd3c85b42f1b2b098f05828c0b9e3dbb2e57f1f89b3dc4d1806453e0c4ce services/agent/internal/agent/status_reply_test.go +9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 services/agent/internal/brainactivity/client.go +cbb14913455ead81f9fe85c3dc185a29431e28271f8babe840759bc93417c0a3 services/agent/internal/config/config.go +fa1717032ad536bff867a07d3fdf0ae2e0a9d5d7637a27b43274832d51322eb6 services/agent/internal/config/config_test.go +6fe7335b0e66b60d5f6f27583b1ac11a0948719cfa657d51e77de0b337aab5d5 services/agent/internal/contextdata/collector.go +12e9eb6fd567b5d4173a63fa2b89be913b6d1c97e17b69048f46d119e88dce26 services/agent/internal/contextdata/collector_test.go +5eee0e130a9cb8452a07e9f5d4c76048ad2eb62aa411ea61c9dc6066fadff934 services/agent/internal/glpi/client.go +6499b2e19547d7d874dbaa64f0c905643ffe4d822329c6cd710df0a30a3a9755 services/agent/internal/glpi/client_test.go +247324ba41f3cd991a3582ed270d0b0d6c4c92c36f11e7742c20d3e59b982928 services/agent/internal/glpikb/sync.go +9836e2bf9153df638ead57d92c152b87f831e5a3696bcda4b1f650ff38e9ac70 services/agent/internal/glpikb/sync_test.go +787ab6b53623aa36ef6628f4eada11dcb4ed3011b34effd691bf07ba5d8f214d services/agent/internal/knowledge/category_mapping.go +d3a9909672ea420db87be29ce669f3cd973125ff15bb46ca90a54c3aa98d9e73 services/agent/internal/knowledge/neuroforge_backend.go +7bcd33bfcfb99fb06762fe75f4646308428957ed7eca26a266891ab580c3a929 services/agent/internal/knowledge/persistent_index.go +df1ac214ebb88bf3ce7f68112129cf9ffc300dd725380c38661b85b04908ecdf services/agent/internal/knowledge/store.go +63e47d86bcc89178fdc0f91e07ecb30bd01ee4b84bf29bfc660debbf26f8a40b services/agent/internal/knowledge/store_test.go +807996228b38e1cd97ec2516cb1ecfc4bd945b638fcde465a0991051df2f1a00 services/agent/internal/learning/outcomes.go +2917b3dab63783af4c92c48f269f097dc94b989008a5451f46d6c1b309ccdc9b services/agent/internal/learning/outcomes_test.go +cd8c7379d6ca50fed77a7eafb6ab34b12b9ce2fe56025da534f54e6db246430d services/agent/internal/learning/store.go +c540f2e554d66f36d79d2ce32c74a18b27b2940e96a40e89eae10abccd0bd2a5 services/agent/internal/learning/store_test.go +5030ec3f51830f7decfb24c27f9b243cb64f97302a6a51df096bc957f24b3fae services/agent/internal/metrics/metrics.go +809c3f2833ed2093bf1f9e4466eb3d855f2a84d827cb21deb698dd0e7b63b403 services/agent/internal/model/model.go +975d5846fa38bf2478fa3ab9696660740c8d9a0549ce8d4f3443d4ca917c4aba services/agent/internal/model/reason_codes.go +821850d649c7661f1665dc6be030ebeeacb60e4e007e1e84891d4b779b9a5d0d services/agent/internal/model/reason_codes_test.go +c89248224adf72720289215684b916eb209387c1d38b757d64804a8d3148011c services/agent/internal/obsidian/export.go +b75ffec53e9a94505ee57ecb9361bffbb6805cfd06ea05a7bf68b6b28b268c09 services/agent/internal/obsidian/export_test.go +f4e32c5653b5fc3e046f1536513d376a7c0ac11b392210a107e12bd0ee63e179 services/agent/internal/ollama/client.go +3522e7687e38db771dcf7e05b8dc52ad99a2fbc53d98dbdf07ded781d68b248e services/agent/internal/ollama/client_test.go +efbee1deb5b452da5ba8a9005cfc8b0f74dbdebddf8d251c71d63265c9ccfe9e services/agent/internal/ollama/pool.go +401db60148c8307516c091a56cf8acd21a95230c69fc50a6999bbb0292f2b06f services/agent/internal/ollama/pool_test.go +ab29c5808a06f6b21a0776355eeaa030f69be5eee251e7b1dbd7e00c71045c61 services/agent/internal/prioritysignals/signals.go +149bfefd67291ee288beed89a27235f58d3c33d87a1eff98cb2b7cc312fbd6b2 services/agent/internal/prioritysignals/signals_test.go +93f31d9e7062a03990f96bb950cae54a3fc24176a723a2f634312653a0692434 services/agent/internal/queue/queue.go +147146bf5fa222d54812d84ff799555fd4c2d57ccbf8337e960708524f0ecd86 services/agent/internal/queue/queue_test.go +171f346e674ac5ebfa562a55e47060a462a0460db86e741085e2ee85533d3193 services/agent/internal/state/store.go +592b00ad638b2e523d5e24d0d73286e547ee09c5027da4f6c13fe3885daed020 services/agent/internal/state/store_test.go +209c5ce33b17403289c897932ecfa14b1e8f036f89746715d41cca3c607cf405 services/agent/internal/uptimekuma/client.go +c849df7e346e1bd1e122037a4eafc30fe461add9b1876d7b9d63a02e33d831ad services/agent/internal/uptimekuma/client_test.go +5f0375f33c4c3f0804e58bb7b3a8699a450a05100274fbb19762d59f4d5d0034 services/agent/internal/web/control_graph.go +cb68a23bef549186d251766a3d3820b611defd55e8f7386b8b87490a78b9e11d services/agent/internal/web/control_graph_test.go +b4f7d72022d5009fce43b5879483de92dacfee2b3bf527306902187c3d26486b services/agent/internal/web/server.go +1f517bcb30f95831a06dc2fe0fd9cd633a0c42fc795f67e5ce53de7a2eebd2b1 services/agent/internal/web/server_test.go +21b32b0b863ed62ae352011d7dd85f0134bf9909c23ee80b1c7e3c3605ef8cc4 services/agent/internal/web/templates/category-mappings.html +d0561b87acd8fe8c0ab7f1f0bb82e1df840db8342f91798e8cb0a0408064abcc services/agent/internal/web/templates/dashboard.html +fb5a4e2e1846046032bb878f1e878eb792497e321df6a8ce16209c07a925fdcd services/agent/internal/web/templates/diagnostics.html +5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a services/agent/knowledge/01_active-directory.json +70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc services/agent/knowledge/01_arbeitsplatzdrucker.json +96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 services/agent/knowledge/01_fachanwendung-storung.json +521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 services/agent/knowledge/01_festnetztelefon.json +88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 services/agent/knowledge/01_hardwarebeschaffung.json +4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d services/agent/knowledge/01_kennwort-zurucksetzen.json +5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 services/agent/knowledge/01_lan.json +95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 services/agent/knowledge/01_neue-it-anforderung.json +ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 services/agent/knowledge/01_padagogisches-netzwerk.json +f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 services/agent/knowledge/01_pc-und-notebook.json +ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc services/agent/knowledge/01_sonstiges-und-unklare-zuordnung.json +5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 services/agent/knowledge/01_verdachtige-e-mail-und-phishing.json +5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb services/agent/knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json +ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 services/agent/knowledge/02_fachanwendung-bedienung-und-beratung.json +0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 services/agent/knowledge/02_gruppenrichtlinien.json +4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 services/agent/knowledge/02_monitor-und-dockingstation.json +bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 services/agent/knowledge/02_netzwerkdrucker.json +7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 services/agent/knowledge/02_rufnummer-und-nebenstelle.json +efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 services/agent/knowledge/02_schadsoftware-und-virenfund.json +2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 services/agent/knowledge/02_schulverwaltungsnetz.json +830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 services/agent/knowledge/02_softwarebeschaffung.json +fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 services/agent/knowledge/02_wlan.json +26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 services/agent/knowledge/03_ad-gruppen-und-gruppenrichtlinien.json +b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a services/agent/knowledge/03_dateiablagen-und-netzlaufwerke.json +0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c services/agent/knowledge/03_digitale-tafeln-und-prasentationstechnik.json +a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 services/agent/knowledge/03_fachanwendung-berechtigung.json +f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 services/agent/knowledge/03_internetzugang.json +5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 services/agent/knowledge/03_lizenzbestellung.json +d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b services/agent/knowledge/03_multifunktionsgerat-und-kopierer.json +c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 services/agent/knowledge/03_rufgruppe-und-weiterleitung.json +b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f services/agent/knowledge/03_sicherheitsvorfall.json +ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b services/agent/knowledge/03_tastatur-maus-und-zubehor.json +7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 services/agent/knowledge/04_anwendungsberechtigung.json +6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 services/agent/knowledge/04_computerraume.json +36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 services/agent/knowledge/04_fachanwendung-konfiguration.json +a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 services/agent/knowledge/04_lizenzverwaltung.json +e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb services/agent/knowledge/04_mobile-endgerate.json +adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 services/agent/knowledge/04_mobilfunk.json +1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc services/agent/knowledge/04_scanner.json +658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c services/agent/knowledge/04_schwachstelle.json +5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 services/agent/knowledge/04_serverbetrieb.json +ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d services/agent/knowledge/04_standortanbindung.json +9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 services/agent/knowledge/04_standorteroffnung-und-umzug.json +8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed services/agent/knowledge/05_dienstliches-smartphone.json +47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 services/agent/knowledge/05_digitalisierungsvorhaben.json +4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 services/agent/knowledge/05_druckertreiber.json +74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 services/agent/knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json +a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 services/agent/knowledge/05_hypervisor.json +0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e services/agent/knowledge/05_remotezugriff-und-vpn.json +9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 services/agent/knowledge/05_schuler-und-lehrkraftekonten.json +2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 services/agent/knowledge/05_softwareinstallation-am-arbeitsplatz.json +9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 services/agent/knowledge/05_telekommunikationsvertrag.json +fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 services/agent/knowledge/05_virenschutz.json +e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 services/agent/knowledge/05_vpn.json +153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 services/agent/knowledge/06_betriebssystem-am-arbeitsplatz.json +d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 services/agent/knowledge/06_dns-und-dhcp.json +b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 services/agent/knowledge/06_fachanwendung-bericht-und-auswertung.json +77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 services/agent/knowledge/06_mobile-schulgerate.json +e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 services/agent/knowledge/06_scan-to-mail-und-scan-to-folder.json +cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a services/agent/knowledge/06_sicherheitsupdate.json +ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b services/agent/knowledge/06_videokonferenz.json +e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c services/agent/knowledge/06_virtuelle-maschinen.json +71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 services/agent/knowledge/06_wartungs-und-supportvertrag.json +ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 services/agent/knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json +2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a services/agent/knowledge/07_datensicherung.json +46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e services/agent/knowledge/07_fachanwendung-update-und-release.json +8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 services/agent/knowledge/07_firewall-freischaltung.json +03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 services/agent/knowledge/07_funktions-und-sammelpostfacher.json +a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f services/agent/knowledge/07_geratewechsel-und-umzug.json +9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 services/agent/knowledge/07_mobile-device-management-fur-schulen.json +48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 services/agent/knowledge/07_projektunterstutzung.json +ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 services/agent/knowledge/07_storage.json +2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 services/agent/knowledge/07_telefonkonferenz-und-softphone.json +748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c services/agent/knowledge/07_verbrauchsmaterial.json +7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b services/agent/knowledge/08_datenbanken-plattformbetrieb.json +59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c services/agent/knowledge/08_datenwiederherstellung.json +7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 services/agent/knowledge/08_fachanwendung-neue-anforderung.json +f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab services/agent/knowledge/08_hardware-neubeschaffung.json +b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 services/agent/knowledge/08_netzwerksegment-und-vlan.json +1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f services/agent/knowledge/08_neues-drucksystem.json +b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 services/agent/knowledge/08_padagogische-lernplattformen.json +ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 services/agent/knowledge/08_rechnung-und-kostenstelle.json +3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 services/agent/knowledge/08_telekommunikationsbeschaffung.json +0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 services/agent/knowledge/09_container-und-docker.json +4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 services/agent/knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json +91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 services/agent/knowledge/09_inventarisierung.json +d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb services/agent/knowledge/09_protokollierung-und-auswertung.json +267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 services/agent/knowledge/09_prufungs-und-klausursysteme.json +27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e services/agent/knowledge/09_ruckgabe-und-aussonderung.json +eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 services/agent/knowledge/10_haushalts-und-budgetplanung.json +523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a services/agent/knowledge/10_kubernetes.json +1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 services/agent/knowledge/10_microsoft-word.json +8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 services/agent/knowledge/10_schulverwaltungsanwendungen.json +813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d services/agent/knowledge/11_devops-und-automatisierung.json +213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a services/agent/knowledge/11_microsoft-excel.json +a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de services/agent/knowledge/12_microsoft-powerpoint.json +e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 services/agent/knowledge/12_monitoring.json +2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c services/agent/knowledge/13_outlook-client.json +cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b services/agent/knowledge/14_office-vorlagen.json +9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d services/agent/knowledge/15_office-add-ins-und-makros.json +7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 services/agent/knowledge/16_office-aktivierung-und-lizenzierung.json +f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e services/agent/knowledge/17_serienbriefe-und-dokumentfunktionen.json +5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 services/agent/knowledge/example-vpn.json +7418ad39aa0899a85e69a9910d99ff1cb01757d5ae2edaa885c5ff7a815cb580 services/agent/knowledge-category-map.example.json +895f8400aaad550ff2b96262eaa54e954d603129682e97aa4964539eb19bd7c2 services/agent/run.ps1 +d1d4ab8145a7131c36df51d3a9fbdcdeb0cda3de134fe24c0e8d52370862f3b2 services/control/Dockerfile +f30ddf9251860d92717276483f7a2c2d7405f0516940ca62abc21d97ff3f3ad2 services/control/cmd/engineering-graph/main.go +d989c1706c42eb841d9a719243246f5c04059489969188a2cfbe0f6ac03c4987 services/control/engineering-graph.json +ba44c599b9faf861eca647614c4abd18548e2708233cf462b11eb277e96449b8 services/control/go.mod +da37393e58ff53847f26b1051f6d3a5270571d290b33d79360bae0dcb3513829 services/control/graph.go +e6a8021a44219219fe37ab5edc45d1908fc6579f653aa80c9966810d891ad27d services/control/graph_test.go +d373caddd318992fe25be3879f3606b81bf393d55a2cb8457d3984b8af499950 services/control/index.html +a42b3daf9b816503801f666e322668b3ad106ff8c1e1b83855e801e7db4eabe5 services/control/main.go +f0ec671cd3f00083d9b9d69c82ffcaa8efe08122837671e812f2a510f3c4e321 services/control/security_test.go +457fa6b4c81a62197eb1e76a5472cd571d5c69659110fcd9bc016582f1b5dfa1 services/knowledge/.dockerignore +e82cbdb6336e424c4b9d8db5d149016d606579292184582788a9bf295895cda4 services/knowledge/.env.example +e45bab225ede9bacef0b53f84fb0577235d9a81967ae2a53ab7c7f4d66bd4ccd services/knowledge/.gitignore +d3becb2b7565ba0b1d32bd8d3b085dca6adec2d2b153583c8c6855e0eea43b66 services/knowledge/CHANGELOG.md +d3c5e5636f96c641b0181fd91bb2cf4802529a7355ba0065cea49356057e402f services/knowledge/Dockerfile +31f5067cf8e45271081ff08bf0d9afffef74d300628ddc1bd1db6e87e6d4e922 services/knowledge/Makefile +d50e9892824dc9e2f86b7f4fbfdc81165a8c6b403e4b2b9f6fd5bd2f22c295f0 services/knowledge/QUALITY_REPORT.txt +bcafd8739157e677416bd4e7c3693eafb1572d72384830ab839dd4dcef74d3ee services/knowledge/README.md +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/backups/.gitkeep +cbaed4e619df99e83eef694b12b660757b749bf64c0a340143bab523a121c7f6 services/knowledge/cmd/server/app.go +ba6569476482d21ee6eb4852d6f1a8dbe052335f3be4669ef96298d0f5a5028b services/knowledge/cmd/server/app_test.go +fdaeb96aea39074522e75c2aeae8036ada253167d2e44bba156b10a7b8cf7c61 services/knowledge/cmd/server/main.go +1867f5c83786628441a1c9e3bb96d90d496f5cab24b6dd62a80376f256bfa497 services/knowledge/cmd/server/viewer/app.js +9537e946b22208a5a78f11a29a92c8f379805f51f3de3d3c74a7cd068d876418 services/knowledge/cmd/server/viewer/index.html +58f16f955f1287bc077933980e50eaae238ab2eb40599242abd820a31aca79f5 services/knowledge/cmd/server/viewer/style.css +0269039e6baffbf8ecc423fe021e91827924a9c47decbd0816257aa052702303 services/knowledge/cmd/server/web/app.js +7b1cdf4c71867e0563eb0eca460c60be34ab839099793e0ad9faf4a84b29c5e7 services/knowledge/cmd/server/web/index.html +c6e48f5098243eee4de816d515cd71d8f8904deeedb335966b1db320d574e2e1 services/knowledge/cmd/server/web/style.css +6e2430ead0897eba9ffb7b3df18e61136f54a8da6b4fb9e711490623fca31a12 services/knowledge/docker-compose.dual.yml +f5d6b8c7891ac8ab237423914f67bff5b91f7e3abd255622eeef26ec83feda37 services/knowledge/docker-compose.yml +5a49eb8794bdb76d14c29060ed6c4a9f0126d4daa2f2ae45b11b8d1e7ccaba45 services/knowledge/go.mod +06893c64974dac010903569e7554b1a245a8862538e3dc1b10375932eae6e5b9 services/knowledge/internal/aifallback/ollama.go +60727530c541fdce632fe071173c4846c3a4029929619caf9389f31f27e3c1b0 services/knowledge/internal/aifallback/ollama_test.go +9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 services/knowledge/internal/brainactivity/client.go +ff6d65a0a4648464a89c67e06f5c33a4ec87d7a41e84d894725e8ed582d64acb services/knowledge/internal/obsidian/export.go +e8e91a8ce16c1905c963ac57a156006e92323be80615637a7b1b477b89464012 services/knowledge/internal/obsidian/export_test.go +7fa9c38a719f2f46bd169438aec3c6e8595b047d63236d2a5e78db9cde6924f0 services/knowledge/internal/staging/staging.go +3c1f5726489a773536e430152b41944bd623ed0f88939688744611371462dd6c services/knowledge/internal/staging/staging_test.go +3bcb39124c72cb46efc7cf794254ab5821dbd7da57e7a20b1366862a71b874a9 services/knowledge/internal/store/store.go +c98bd048680978d06d34303db9729b130eb7cfc0750f15ee1a4e7abfe582dca1 services/knowledge/internal/store/store_test.go +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/knowledge/.gitkeep +81893d2cba93d7a30c5c728559e81a7b7e2eca87cae8f9e239aeefcaaf604d60 services/knowledge/run.ps1 +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/staging/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 staging/.gitkeep diff --git a/MIGRATION-FROM-MEGA.md b/MIGRATION-FROM-MEGA.md new file mode 100644 index 0000000..30740ad --- /dev/null +++ b/MIGRATION-FROM-MEGA.md @@ -0,0 +1,33 @@ +# Migration: Mega v1.6.1 -> Agent + Knowledge Core + +Der Core-Modus verändert das Datenformat der Knowledge-JSONs nicht. + +## Variante A: Knowledge-Verzeichnis weiterverwenden + +Setze in `deployments/agent/.env` und `deployments/knowledge/.env` denselben `CORE_DATA_ROOT`, sodass beide auf denselben Hostpfad zeigen. + +## Variante B: Agent-Index übernehmen + +Der Agent speichert seinen persistenten lokalen Knowledge-Index unter `/app/data`. Der Core-Compose bind-mountet dafür `runtime/agent-data`. +Um einen bestehenden Named Volume zu übernehmen, zuerst dessen Namen ermitteln und dann offline kopieren. Niemals das laufende Volume gleichzeitig von zwei Agent-Instanzen beschreiben lassen. + +## Funktionale Änderungen gegenüber Mega + +Nicht verfügbar im Core-Modus: + +- NeuroForge Memories/Synapses/Graph +- autonome Research Goals +- Staging-Synthese durch NeuroForge +- CPU/GPU Subagent-Orchestrator +- Control Center + +Weiter verfügbar: + +- GLPI Polling/OAuth2 +- Agent WebUI und Diagnose +- lokale Knowledge-Indizierung und Embeddings +- Hybrid Retrieval/RAG +- Kategorien/Keywords/Source Policy +- lokale Human-in-the-loop Lernbeispiele +- optional GLPI-KB-Synchronisation +- Knowledge Editor/Search diff --git a/README.md b/README.md index 26f047c..5d1db75 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ -# GLPI NeuroForge Mega v1.6.0 +# GLPI NeuroForge Mega v1.6.2 -> Release: **v1.6.0** · Durable Master/Subagent Orchestration + Knowledge-Graph Convergence: CPU-/GPU-Arbeit wird capability-basiert, lease-gefenced und persistent geplant; importierte Memories werden bounded zu einem n:m-Synapsengraphen verknüpft und Retrieval kann mehrere Hops traversieren. +> Release: **v1.6.2** · kanonischer Vollrelease mit Full-Mega-, Distributed- und Standalone-Betriebsmodi. Die v1.6.1 Recovery/OOM-Härtung ist vollständig enthalten. + +v1.6.2 konsolidiert die zuvor getrennten Pakete wieder in **ein vollständiges Monorepo**: NeuroForge Master, CPU/GPU-Subagents, GLPI Agent, Knowledge, Control, Ollama, Research sowie eigenständig betreibbare Agent-/Knowledge-/Ollama-Core-Kits und Prometheus/Grafana-Beispiele. Ein kontrolliertes Monorepo aus **GLPI AI Agent**, **GLPI AI Knowledgebase** und **NeuroForge + SQAR**. Ziel ist nicht ein untrennbarer Monolith, sondern eine gemeinsame Plattform mit klaren Zuständigkeiten, getrennten Credentials und nachvollziehbaren Failure-Modi. +## Crash-/Recovery-Hardening (seit v1.6.1, in v1.6.2 enthalten) + +Die in v1.6.1 eingeführte Härtung behebt einen RAM-/Recovery-Fehler, der bei großen Graph-Backfills nach mehreren Stunden Laufzeit auftreten konnte. Erfolgreiche `vector.relink`-Jobs verwerfen ihre großen transienten Vector-Payloads unmittelbar nach dem autoritativen Master-Apply; ein Queue-Payload-Budget verhindert neue ungebremste Speicherbelegung. Beim ersten Start migriert NeuroForge alte v1.6.0-Checkpoints streaming, bevor `state.json` vollständig in den RAM geladen wird. + +HNSW-Deltas werden nicht mehr über einen vollständigen Deep-Copy aller Vektoren erzeugt. Checkpoints persistieren den Index vor `state.json`, sodass ein Crash während der Indexpersistenz den autoritativen Checkpoint nicht vor den verwendbaren Index ziehen kann. Bei Containerstarts mit explizitem `-listen` ist sofort eine Bootstrap-Liveness-/Startup-Seite erreichbar; Logs zeigen die aktuelle Recovery-Phase (`checkpoint.precompact`, `memory-segments.scan`, `wal.replay`, `hnsw.snapshot.load`, `hnsw.rebuild`, ...). + ## Research → Human-Review-Staging (v1.5.0) Autonome Research-Goals können ihre quellengebundene Evidenz jetzt tatsächlich in einen **KB-Staging-Entwurf** überführen. Die Bridge ist einseitig: NeuroForge darf ausschließlich `POST /api/integrations/staging` mit dem separaten `KB_INTEGRATION_TOKEN` verwenden; `auto_reply=false` wird serverseitig erzwungen und Produktivwissen bleibt menschlich freigabepflichtig. @@ -26,6 +34,22 @@ Lokale Standardrollen: Zusätzliche Hosts können mit `docker-compose.subagent.yml` angebunden werden. Das Control Center bleibt read-only und zeigt Master-, Worker-, Queue- und Graphzustand. Details: [`docs/MASTER-SUBAGENT-ORCHESTRATOR.md`](docs/MASTER-SUBAGENT-ORCHESTRATOR.md). + +## Deployment-Modi + +Das Repository enthält bewusst mehrere, voneinander entkoppelte Betriebsformen: + +- `docker-compose.yml`: vollständiger Mega-Stack mit lokalem CPU- und GPU-Worker. +- `deployments/master`: autoritativer Master ohne lokale Worker, für getrennte CPU-/GPU-Hosts. +- `deployments/cpu-subagent`: abgesetzter CPU-Worker für `cpu,vector.relink`. +- `deployments/gpu-subagent`: abgesetzter GPU-Worker plus Ollama für `gpu,model.chat,model.embed`. +- `deployments/agent`: GLPI Agent standalone mit lokalem Vector-Backend, ohne NeuroForge-Zwang. +- `deployments/knowledge`: Knowledge standalone. +- `deployments/ollama`: Ollama standalone. +- `deployments/combined`: Agent + Knowledge + Ollama ohne NeuroForge. + +Details: [`deployments/README.md`](deployments/README.md). + ## Unified Graph Explorer (v1.4.0) Das read-only Control Center visualisiert Runtime/Trust, Ticket-Evidence, Learning-Lineage, Research-Provenance, einen redigierten NeuroForge-Brain-Graph sowie einen reproduzierbaren Engineering-Graph aus Go-AST und Compose. Für Dateien/Symbole/Routen gibt es zusätzlich eine statische Change-Impact-/Blast-Radius-Sicht. 2D ist der operative Default; 3D ist ein optionaler, gebundener Explorer. diff --git a/RELEASE-NOTES-v1.6.1.md b/RELEASE-NOTES-v1.6.1.md new file mode 100644 index 0000000..bcd79a8 --- /dev/null +++ b/RELEASE-NOTES-v1.6.1.md @@ -0,0 +1,30 @@ +# GLPI NeuroForge Mega v1.6.1 + +## Production hotfix: Master crash / silent recovery on large Knowledge corpora + +v1.6.1 keeps the v1.6.0 Master/Subagent and n:m Knowledge-Graph architecture and hardens the NeuroForge master for long-running bulk imports/backfills. + +### Fixed + +- Completed `vector.relink` jobs no longer retain full target/candidate vectors after successful master apply. +- A one-time streaming startup migration compacts large v1.6.0 `state.json` checkpoints before full JSON unmarshal. +- Durable pending-job payload bytes are bounded by `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB` (default 128 MiB). +- HNSW segmented checkpoint deltas no longer deep-copy every vector/node on each checkpoint; only changed nodes are copied. +- Checkpoint ordering is index-first, then `state.json`, then WAL prune, improving crash recovery when index persistence is interrupted. +- Startup with explicit `-listen` exposes `/livez` and a minimal `/admin` recovery page before the store is fully open. +- Startup phases are logged, so segment/WAL/HNSW recovery is no longer silent. +- Existing v1.6.0 completed relink payloads are compacted on first v1.6.1 boot. +- Authoritative `state.json` and `secrets.json` are decoded directly from files instead of `ReadFile`+`Unmarshal`; corrupt or trailing JSON now fails closed with an explicit startup error instead of being silently ignored. +- Checkpoints/secrets/index metadata are JSON-streamed to atomic temporary files instead of allocating a second complete encoded byte slice; the final file and directory entry are synced before success. +- WAL recovery immediately drops transient payload/result blobs from already-completed `vector.relink` jobs, so a large surviving WAL cannot rebuild the same multi-GB terminal-job heap during restart. +- Fresh data directories no longer consume the one-time v1.6.1 compaction marker before a possible v1.6.0 restore. + +### Safer graph defaults + +- `NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24` +- `NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000` +- `NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16` +- `NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64` +- `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128` + +These are throughput/stability defaults; completed relink payload compaction is enforced independently of the values above. diff --git a/RELEASE-NOTES-v1.6.2.md b/RELEASE-NOTES-v1.6.2.md new file mode 100644 index 0000000..aa1c0ae --- /dev/null +++ b/RELEASE-NOTES-v1.6.2.md @@ -0,0 +1,38 @@ +# GLPI NeuroForge Mega v1.6.2 + +## Consolidated production release + +v1.6.2 is the canonical full repository release. It keeps the v1.6.1 runtime/data format and recovery/OOM hotfixes, and consolidates all previously split deployment variants into one coherent tree. + +### Included deployment modes + +- Full Mega stack with local CPU/GPU workers. +- Distributed Master with remote CPU and GPU subagents. +- Standalone GLPI Agent using a local vector backend. +- Standalone Knowledge service. +- Standalone Ollama. +- Combined Agent + Knowledge + Ollama core stack without NeuroForge. +- Prometheus/Grafana example monitoring on the distributed Master, including payload/queue/graph metrics and alerts. + +### Packaging / CI corrections + +- Restores the complete v1.6.1 NeuroForge recovery hardening and graph/orchestrator source tree. +- Includes complete `.env` files with safe placeholders for Master, CPU subagent, GPU subagent and standalone roles. +- Removes embedded real credentials from deployment examples. +- Replaces conflicting release workflows with one root CI workflow and one immutable tag release workflow. +- CI covers NeuroForge, Agent, Knowledge and Control plus all six project images. +- `docker-bake.hcl` builds NeuroForge server/worker, Agent, Agent data-init, Knowledge and Control. +- Removes nested/legacy workflow copies and stale duplicate compose files. +- Regenerates a single repository manifest for the exact final archive. + +### Runtime safety inherited from v1.6.1 + +- Streaming compaction of legacy terminal `vector.relink` payloads before normal state loading. +- Completed relink payload/result blobs are discarded after successful Master apply and during WAL recovery. +- Bounded pending durable job payload bytes. +- HNSW delta checkpoints avoid full graph/vector deep copies. +- Index-first checkpoint ordering and fail-closed authoritative JSON recovery. +- `/livez` and a bootstrap `/admin` page are available while store recovery is still running. +- Conservative graph backfill and job-retention defaults for large Knowledge corpora. + +There is no intentional persisted-data-format break from v1.6.1 to v1.6.2. diff --git a/VERSION b/VERSION index dc1e644..fdd3be6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.0 +1.6.2 diff --git a/deployments/README.md b/deployments/README.md index 298e25d..13b543c 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -1,16 +1,29 @@ -# Distributed Deployment Kits (v1.6.0) +# Deployment Kits — GLPI NeuroForge Mega v1.6.2 -This directory contains three independent deployment kits: +This repository intentionally supports both the complete NeuroForge platform and stripped standalone operation. -- `master/` - authoritative NeuroForge Master plus Agent, Knowledge, Control, optional SearXNG and optional Prometheus/Grafana. -- `cpu-subagent/` - CPU worker for `vector.relink` / graph convergence. -- `gpu-subagent/` - GPU worker plus local Ollama for `model.chat` and `model.embed`. +## Complete / distributed platform -The generated `NEUROFORGE_WORKER_TOKEN` is identical in all three `.env` files. Replace the RFC 5737 example IP addresses (`192.0.2.x`) with real reachable addresses before starting. +- `master/` — authoritative NeuroForge Master plus GLPI Agent, Knowledge, Control, optional SearXNG, Prometheus and Grafana. Remote CPU/GPU workers connect to this node. +- `cpu-subagent/` — disposable CPU worker with `cpu,vector.relink` capabilities. +- `gpu-subagent/` — disposable GPU worker plus Ollama with `gpu,model.chat,model.embed` capabilities. -Recommended order: -1. GPU subagent: `docker compose --profile monitoring up -d` -2. CPU subagent: `docker compose --profile monitoring up -d` -3. Master: fill GLPI credentials, then `docker compose --profile research --profile monitoring up -d` +The Master is the only authoritative owner of NeuroForge state. CPU/GPU workers use leases, heartbeats and fenced job completion. -Only the Master holds authoritative NeuroForge state. Workers are disposable execution nodes. +## Standalone core operation + +- `agent/` — GLPI Agent with local Knowledge vector backend; no NeuroForge, Control or Research dependency. +- `knowledge/` — standalone Knowledge editor/service. +- `ollama/` — standalone Ollama runtime. +- `combined/` — Agent + Knowledge + Ollama on one host, still without NeuroForge. + +The standalone Agent and Knowledge kits share `runtime/knowledge` by default. The Agent mounts it read-only; Knowledge mounts it read-write. + +## Release rules + +- Project images are pinned by `IMAGE_TAG=1.6.2`; production compose files do not require `latest`. +- Replace every `CHANGE_ME_...` placeholder before startup. +- Never use `docker compose down -v` during an in-place upgrade unless loss of persistent state is intended. +- For a v1.6.0/v1.6.1 NeuroForge data volume, keep the volume: v1.6.2 includes the v1.6.1 recovery/OOM hotfixes and startup compaction path. + +See each role's README/preflight and the root `README.md` for startup order. diff --git a/deployments/agent/docker-compose.build.yml b/deployments/agent/docker-compose.build.yml new file mode 100644 index 0000000..37248cf --- /dev/null +++ b/deployments/agent/docker-compose.build.yml @@ -0,0 +1,10 @@ +services: + agent-data-init: + image: glpi-ai-agent-data-init-local:1.6.2 + build: + context: ../../services/agent + target: data-init + agent: + image: glpi-ai-agent-local:1.6.2 + build: + context: ../../services/agent diff --git a/deployments/agent/docker-compose.yml b/deployments/agent/docker-compose.yml new file mode 100644 index 0000000..879e41c --- /dev/null +++ b/deployments/agent/docker-compose.yml @@ -0,0 +1,62 @@ +name: glpi-ai-agent-core +services: + agent-data-init: + image: ${AGENT_DATA_INIT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:-1.6.2}} + restart: "no" + user: "0:0" + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - CHOWN + - FOWNER + + agent: + image: ${AGENT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:-1.6.2}} + restart: unless-stopped + env_file: + - .env + environment: + HTTP_ADDR: :8080 + DATA_DIR: /app/data + KNOWLEDGE_DIR: /app/knowledge + KNOWLEDGE_VECTOR_BACKEND: local + OUTCOME_LEARNING_ENABLED: "false" + OUTCOME_RETRIEVAL_ENABLED: "false" + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + NEUROFORGE_URL: "" + NEUROFORGE_API_KEY: "" + CONTROL_READ_TOKEN: "" + ports: + - ${AGENT_BIND_IP:-127.0.0.1}:${AGENT_HOST_PORT:-9980}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/app/knowledge:ro + depends_on: + agent-data-init: + condition: service_completed_successfully + networks: + - core + read_only: true + tmpfs: + - /tmp:size=64m,mode=1777 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: ["CMD", "/app/glpi-ai-agent", "healthcheck"] + interval: 15s + timeout: 3s + retries: 8 + start_period: 10s + stop_grace_period: 20s + +networks: + core: + external: true + name: ${CORE_NETWORK:-glpi-ai-core} diff --git a/deployments/combined/compose.yml b/deployments/combined/compose.yml new file mode 100644 index 0000000..3563e79 --- /dev/null +++ b/deployments/combined/compose.yml @@ -0,0 +1,99 @@ +name: glpi-ai-core +services: + ollama: + image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2} + restart: unless-stopped + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/ollama:/root/.ollama + ports: + - ${OLLAMA_BIND_IP:-127.0.0.1}:${OLLAMA_HOST_PORT:-11434}:11434 + networks: + core: + aliases: [ollama] + security_opt: + - no-new-privileges:true + + agent-data-init: + image: ${AGENT_DATA_INIT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:-1.6.1}} + restart: "no" + user: "0:0" + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + security_opt: + - no-new-privileges:true + cap_drop: [ALL] + cap_add: [CHOWN, FOWNER] + + agent: + image: ${AGENT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:-1.6.1}} + restart: unless-stopped + env_file: [.env] + environment: + HTTP_ADDR: :8080 + DATA_DIR: /app/data + KNOWLEDGE_DIR: /app/knowledge + OLLAMA_URL: http://ollama:11434 + OLLAMA_URLS: http://ollama:11434 + KNOWLEDGE_VECTOR_BACKEND: local + OUTCOME_LEARNING_ENABLED: "false" + OUTCOME_RETRIEVAL_ENABLED: "false" + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + NEUROFORGE_URL: "" + NEUROFORGE_API_KEY: "" + CONTROL_READ_TOKEN: "" + ports: + - ${AGENT_BIND_IP:-127.0.0.1}:${AGENT_HOST_PORT:-9980}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/app/knowledge:ro + depends_on: + agent-data-init: + condition: service_completed_successfully + ollama: + condition: service_started + networks: [core] + read_only: true + tmpfs: [/tmp:size=64m,mode=1777] + security_opt: [no-new-privileges:true] + cap_drop: [ALL] + healthcheck: + test: ["CMD", "/app/glpi-ai-agent", "healthcheck"] + interval: 15s + timeout: 3s + retries: 8 + start_period: 10s + stop_grace_period: 20s + + knowledge: + image: ${KNOWLEDGE_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:-1.6.1}} + restart: unless-stopped + env_file: [.env] + environment: + APP_MODE: ${KB_APP_MODE:-editor} + DATA_DIR: /data/knowledge + BACKUP_DIR: /data/backups + STAGING_DIR: /data/staging + LISTEN_ADDR: :8080 + OLLAMA_BASE_URL: http://ollama:11434 + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + ports: + - ${KNOWLEDGE_BIND_IP:-127.0.0.1}:${KNOWLEDGE_HOST_PORT:-9981}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/data/knowledge:rw + - ${CORE_DATA_ROOT:-../../runtime}/staging:/data/staging:rw + - ${CORE_DATA_ROOT:-../../runtime}/backups:/data/backups:rw + depends_on: + ollama: + condition: service_started + networks: [core] + read_only: true + tmpfs: [/tmp:size=32m,mode=1777] + security_opt: [no-new-privileges:true] + cap_drop: [ALL] + stop_grace_period: 35s + +networks: + core: + name: ${CORE_NETWORK:-glpi-ai-core} diff --git a/deployments/combined/compose.yml.old b/deployments/combined/compose.yml.old new file mode 100644 index 0000000..3213639 --- /dev/null +++ b/deployments/combined/compose.yml.old @@ -0,0 +1,14 @@ +services: + agent-data-init: + image: glpi-ai-agent-data-init-local:1.6.1 + build: + context: ../../services/agent + target: data-init + agent: + image: glpi-ai-agent-local:1.6.1 + build: + context: ../../services/agent + knowledge: + image: glpi-ai-knowledge-local:1.6.1 + build: + context: ../../services/knowledge diff --git a/deployments/combined/docker-compose.build.yml b/deployments/combined/docker-compose.build.yml new file mode 100644 index 0000000..2a2e3ac --- /dev/null +++ b/deployments/combined/docker-compose.build.yml @@ -0,0 +1,14 @@ +services: + agent-data-init: + image: glpi-ai-agent-data-init-local:1.6.2 + build: + context: ../../services/agent + target: data-init + agent: + image: glpi-ai-agent-local:1.6.2 + build: + context: ../../services/agent + knowledge: + image: glpi-ai-knowledge-local:1.6.2 + build: + context: ../../services/knowledge diff --git a/deployments/combined/docker-compose.yml b/deployments/combined/docker-compose.yml new file mode 100644 index 0000000..1412e46 --- /dev/null +++ b/deployments/combined/docker-compose.yml @@ -0,0 +1,99 @@ +name: glpi-ai-core +services: + ollama: + image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2} + restart: unless-stopped + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/ollama:/root/.ollama + ports: + - ${OLLAMA_BIND_IP:-127.0.0.1}:${OLLAMA_HOST_PORT:-11434}:11434 + networks: + core: + aliases: [ollama] + security_opt: + - no-new-privileges:true + + agent-data-init: + image: ${AGENT_DATA_INIT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:-1.6.2}} + restart: "no" + user: "0:0" + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + security_opt: + - no-new-privileges:true + cap_drop: [ALL] + cap_add: [CHOWN, FOWNER] + + agent: + image: ${AGENT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:-1.6.2}} + restart: unless-stopped + env_file: [.env] + environment: + HTTP_ADDR: :8080 + DATA_DIR: /app/data + KNOWLEDGE_DIR: /app/knowledge + OLLAMA_URL: http://ollama:11434 + OLLAMA_URLS: http://ollama:11434 + KNOWLEDGE_VECTOR_BACKEND: local + OUTCOME_LEARNING_ENABLED: "false" + OUTCOME_RETRIEVAL_ENABLED: "false" + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + NEUROFORGE_URL: "" + NEUROFORGE_API_KEY: "" + CONTROL_READ_TOKEN: "" + ports: + - ${AGENT_BIND_IP:-127.0.0.1}:${AGENT_HOST_PORT:-9980}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/app/knowledge:ro + depends_on: + agent-data-init: + condition: service_completed_successfully + ollama: + condition: service_started + networks: [core] + read_only: true + tmpfs: [/tmp:size=64m,mode=1777] + security_opt: [no-new-privileges:true] + cap_drop: [ALL] + healthcheck: + test: ["CMD", "/app/glpi-ai-agent", "healthcheck"] + interval: 15s + timeout: 3s + retries: 8 + start_period: 10s + stop_grace_period: 20s + + knowledge: + image: ${KNOWLEDGE_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:-1.6.2}} + restart: unless-stopped + env_file: [.env] + environment: + APP_MODE: ${KB_APP_MODE:-editor} + DATA_DIR: /data/knowledge + BACKUP_DIR: /data/backups + STAGING_DIR: /data/staging + LISTEN_ADDR: :8080 + OLLAMA_BASE_URL: http://ollama:11434 + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + ports: + - ${KNOWLEDGE_BIND_IP:-127.0.0.1}:${KNOWLEDGE_HOST_PORT:-9981}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/data/knowledge:rw + - ${CORE_DATA_ROOT:-../../runtime}/staging:/data/staging:rw + - ${CORE_DATA_ROOT:-../../runtime}/backups:/data/backups:rw + depends_on: + ollama: + condition: service_started + networks: [core] + read_only: true + tmpfs: [/tmp:size=32m,mode=1777] + security_opt: [no-new-privileges:true] + cap_drop: [ALL] + stop_grace_period: 35s + +networks: + core: + name: ${CORE_NETWORK:-glpi-ai-core} diff --git a/deployments/cpu-subagent/preflight.sh b/deployments/cpu-subagent/preflight.sh index 90a0293..435c650 100755 --- a/deployments/cpu-subagent/preflight.sh +++ b/deployments/cpu-subagent/preflight.sh @@ -1,7 +1,11 @@ #!/usr/bin/env sh set -eu -for k in NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_CPU_WORKER_ID; do v=$(grep -E "^${k}=" .env | head -1 | cut -d= -f2- || true); [ -n "$v" ] || { echo "FEHLT: $k"; exit 1; }; echo "$v" | grep -q '192\.0\.2\.' && { echo "SETZEN: $k"; exit 1; } || true; done -command -v docker >/dev/null 2>&1 || { echo "FEHLT: docker"; exit 1; } -docker compose version >/dev/null +[ -f .env ] || { echo 'missing .env'; exit 1; } +for k in IMAGE_TAG NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_CPU_WORKER_ID; do + v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env) + [ -n "$v" ] || { echo "missing: $k"; exit 1; } +done +if grep -Eq '192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then echo 'placeholder/example values remain in .env'; exit 1; fi +[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; } docker compose --profile monitoring config >/dev/null -echo "CPU subagent preflight: OK" +echo 'cpu subagent preflight: OK' diff --git a/deployments/gpu-subagent/preflight.sh b/deployments/gpu-subagent/preflight.sh index 1337bb7..2061a06 100755 --- a/deployments/gpu-subagent/preflight.sh +++ b/deployments/gpu-subagent/preflight.sh @@ -1,8 +1,11 @@ #!/usr/bin/env sh set -eu -for k in NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_GPU_WORKER_ID OLLAMA_MODEL OLLAMA_EMBEDDING_MODEL; do v=$(grep -E "^${k}=" .env | head -1 | cut -d= -f2- || true); [ -n "$v" ] || { echo "FEHLT: $k"; exit 1; }; echo "$v" | grep -q '192\.0\.2\.' && { echo "SETZEN: $k"; exit 1; } || true; done -command -v docker >/dev/null 2>&1 || { echo "FEHLT: docker"; exit 1; } -docker compose version >/dev/null +[ -f .env ] || { echo 'missing .env'; exit 1; } +for k in IMAGE_TAG NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_GPU_WORKER_ID OLLAMA_MODEL OLLAMA_EMBEDDING_MODEL; do + v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env) + [ -n "$v" ] || { echo "missing: $k"; exit 1; } +done +if grep -Eq '192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then echo 'placeholder/example values remain in .env'; exit 1; fi +[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; } docker compose --profile monitoring config >/dev/null -command -v nvidia-smi >/dev/null 2>&1 || echo "WARNUNG: nvidia-smi nicht gefunden; NVIDIA Host/Toolkit prüfen." -echo "GPU subagent preflight: OK" +echo 'gpu subagent preflight: OK' diff --git a/deployments/knowledge/docker-compose.build.yml b/deployments/knowledge/docker-compose.build.yml new file mode 100644 index 0000000..82f3010 --- /dev/null +++ b/deployments/knowledge/docker-compose.build.yml @@ -0,0 +1,5 @@ +services: + knowledge: + image: glpi-ai-knowledge-local:1.6.2 + build: + context: ../../services/knowledge diff --git a/deployments/knowledge/docker-compose.yml b/deployments/knowledge/docker-compose.yml new file mode 100644 index 0000000..3b5d238 --- /dev/null +++ b/deployments/knowledge/docker-compose.yml @@ -0,0 +1,36 @@ +name: glpi-ai-knowledge-core +services: + knowledge: + image: ${KNOWLEDGE_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:-1.6.2}} + restart: unless-stopped + env_file: + - .env + environment: + APP_MODE: ${KB_APP_MODE:-editor} + DATA_DIR: /data/knowledge + BACKUP_DIR: /data/backups + STAGING_DIR: /data/staging + LISTEN_ADDR: :8080 + BRAIN_ACTIVITY_URL: "" + BRAIN_ACTIVITY_API_KEY: "" + ports: + - ${KNOWLEDGE_BIND_IP:-127.0.0.1}:${KNOWLEDGE_HOST_PORT:-9981}:8080 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/data/knowledge:rw + - ${CORE_DATA_ROOT:-../../runtime}/staging:/data/staging:rw + - ${CORE_DATA_ROOT:-../../runtime}/backups:/data/backups:rw + networks: + - core + read_only: true + tmpfs: + - /tmp:size=32m,mode=1777 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + stop_grace_period: 35s + +networks: + core: + external: true + name: ${CORE_NETWORK:-glpi-ai-core} diff --git a/deployments/master/docker-compose.yml b/deployments/master/docker-compose.yml index a3e00d2..a49cb8d 100644 --- a/deployments/master/docker-compose.yml +++ b/deployments/master/docker-compose.yml @@ -23,7 +23,7 @@ services: - ALL neuroforge: image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG - to an immutable release tag, for example 1.6.0} + to an immutable release tag, for example 1.6.2} command: - -data - /app/data @@ -57,14 +57,15 @@ services: NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS:-3} NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS:-15} NEUROFORGE_WORKER_MAX_QUEUED_JOBS: ${NEUROFORGE_WORKER_MAX_QUEUED_JOBS:-5000} + NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB: ${NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB:-128} NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS:-5} NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS:-5} - NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-168} - NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-20000} + NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-24} + NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-2000} NEUROFORGE_GRAPH_BACKFILL_ENABLED: ${NEUROFORGE_GRAPH_BACKFILL_ENABLED:-true} NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS: ${NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS:-10} - NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-64} - NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-256} + NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-16} + NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-64} NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE: ${NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE:-3} NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER: ${NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER:-6} NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES: ${NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES:-360} @@ -137,7 +138,7 @@ services: stop_grace_period: 35s agent-data-init: image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set - IMAGE_TAG to an immutable release tag, for example 1.6.0} + IMAGE_TAG to an immutable release tag, for example 1.6.2} restart: 'no' user: 0:0 volumes: @@ -151,7 +152,7 @@ services: - FOWNER agent: image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG - to an immutable release tag, for example 1.6.0} + to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: AI_CONTENT_LABEL_ENABLED: ${AI_CONTENT_LABEL_ENABLED:-} @@ -373,7 +374,7 @@ services: stop_grace_period: 20s knowledge: image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG - to an immutable release tag, for example 1.6.0} + to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: APP_MODE: ${KB_APP_MODE:-editor} @@ -414,7 +415,7 @@ services: stop_grace_period: 35s control: image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG - to an immutable release tag, for example 1.6.0} + to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: CONTROL_ADDR: :8070 @@ -543,7 +544,7 @@ services: - prometheus-secrets:/etc/prometheus-secrets:ro depends_on: neuroforge: - condition: service_healthy + condition: service_started prometheus-secrets-init: condition: service_completed_successfully prometheus-config-init: diff --git a/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json b/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json index 84c4a08..4b48074 100644 --- a/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json +++ b/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json @@ -9,7 +9,7 @@ ], "timezone": "browser", "schemaVersion": 41, - "version": 1, + "version": 2, "refresh": "15s", "time": { "from": "now-6h", @@ -1443,6 +1443,45 @@ "sort": "desc" } } + }, + { + "id": 26, + "title": "Durable Job Payload Bytes", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "gridPos": { + "x": 0, + "y": 56, + "w": 24, + "h": 8 + }, + "fieldConfig": { + "defaults": { + "unit": "bytes" + }, + "overrides": [] + }, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "expr": "neuroforge_job_payload_bytes", + "legendFormat": "{{state}}", + "refId": "A" + } + ] } ] } diff --git a/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml b/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml new file mode 100644 index 0000000..707ffdd --- /dev/null +++ b/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: NeuroForge + orgId: 1 + folder: NeuroForge + type: file + disableDeletion: true + updateIntervalSeconds: 30 + allowUiUpdates: false + options: + path: /var/lib/grafana/dashboards diff --git a/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml b/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml index 433ef5c..00f9915 100644 --- a/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml +++ b/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml @@ -1,4 +1,5 @@ apiVersion: 1 + datasources: - name: Prometheus uid: prometheus diff --git a/deployments/master/monitoring/prometheus/alerts.yml b/deployments/master/monitoring/prometheus/alerts.yml index 4b9cf81..4a8667d 100644 --- a/deployments/master/monitoring/prometheus/alerts.yml +++ b/deployments/master/monitoring/prometheus/alerts.yml @@ -56,3 +56,19 @@ groups: annotations: summary: "Knowledge graph remains highly isolated" description: "More than 80% of memories are still isolated after 30 minutes." + + - alert: NeuroForgePendingPayloadHigh + expr: neuroforge_job_payload_bytes{state="pending"} > 104857600 + for: 5m + labels: {severity: warning} + annotations: + summary: "NeuroForge pending job payload memory is high" + description: "Pending durable job payload/result bytes exceed 100 MiB for 5 minutes; backfill should remain below the v1.6.2 safety budget." + + - alert: NeuroForgeTerminalPayloadHigh + expr: neuroforge_job_payload_bytes{state="terminal"} > 67108864 + for: 10m + labels: {severity: warning} + annotations: + summary: "NeuroForge terminal job payload retention is high" + description: "Terminal job payload/result bytes exceed 64 MiB. Completed vector.relink jobs should be compacted automatically in v1.6.2." diff --git a/deployments/master/preflight.sh b/deployments/master/preflight.sh index e333617..cb00405 100755 --- a/deployments/master/preflight.sh +++ b/deployments/master/preflight.sh @@ -1,15 +1,14 @@ #!/usr/bin/env sh set -eu -bad=0 -check() { key="$1"; val=$(grep -E "^${key}=" .env | head -1 | cut -d= -f2- || true); if [ -z "$val" ] || echo "$val" | grep -Eq 'CHANGE_ME|example\.invalid|192\.0\.2\.'; then echo "SETZEN: $key"; bad=1; fi; } -for k in GLPI_URL GLPI_CLIENT_ID GLPI_CLIENT_SECRET GLPI_USERNAME GLPI_PASSWORD OLLAMA_BASE_URL OLLAMA_URLS; do check "$k"; done -if [ "$bad" -ne 0 ]; then echo "Preflight fehlgeschlagen: Platzhalter ersetzen."; exit 1; fi -command -v docker >/dev/null 2>&1 || { echo "FEHLT: docker"; exit 1; } -docker compose version >/dev/null +[ -f .env ] || { echo 'missing .env'; exit 1; } +required='IMAGE_TAG NEUROFORGE_ADMIN_TOKEN NEUROFORGE_APP_API_KEY NEUROFORGE_INTEGRATION_TOKEN NEUROFORGE_CONTROL_READ_TOKEN NEUROFORGE_WORKER_TOKEN NEUROFORGE_METRICS_TOKEN KB_INTEGRATION_TOKEN CONTROL_READ_TOKEN GLPI_URL GLPI_CLIENT_ID GLPI_CLIENT_SECRET GLPI_USERNAME GLPI_PASSWORD GRAFANA_ADMIN_PASSWORD' +for k in $required; do + v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env) + [ -n "$v" ] || { echo "missing: $k"; exit 1; } +done +if grep -Eq '(^|=)(https?://)?192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then + echo 'placeholder/example values remain in .env'; exit 1 +fi +[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; } docker compose --profile research --profile monitoring config >/dev/null -[ -f monitoring/prometheus/prometheus.yml.template ] -[ -f monitoring/prometheus/alerts.yml ] -[ -f monitoring/grafana/dashboards/neuroforge-master-subagents.json ] -mkdir -p knowledge staging backups -./monitoring/validate.sh -echo "Master preflight: OK" +echo 'master preflight: OK' diff --git a/deployments/ollama/docker-compose.yml b/deployments/ollama/docker-compose.yml new file mode 100644 index 0000000..735731f --- /dev/null +++ b/deployments/ollama/docker-compose.yml @@ -0,0 +1,20 @@ +name: glpi-ai-ollama-core +services: + ollama: + image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2} + restart: unless-stopped + ports: + - ${OLLAMA_BIND_IP:-127.0.0.1}:${OLLAMA_HOST_PORT:-11434}:11434 + volumes: + - ${CORE_DATA_ROOT:-../../runtime}/ollama:/root/.ollama + networks: + core: + aliases: + - ollama + security_opt: + - no-new-privileges:true + +networks: + core: + external: true + name: ${CORE_NETWORK:-glpi-ai-core} diff --git a/deployments/ollama/pull-models.sh b/deployments/ollama/pull-models.sh new file mode 100755 index 0000000..097e592 --- /dev/null +++ b/deployments/ollama/pull-models.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -eu +[ -f .env ] && set -a && . ./.env && set +a +: "${OLLAMA_MODEL:=gemma4}" +: "${OLLAMA_EMBEDDING_MODEL:=embeddinggemma}" +docker compose exec ollama ollama pull "$OLLAMA_MODEL" +docker compose exec ollama ollama pull "$OLLAMA_EMBEDDING_MODEL" +echo "Modelle vorhanden:" +docker compose exec ollama ollama list diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..b508934 --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,57 @@ +variable "IMAGE_TAG" { + default = "1.6.2" +} + +variable "REGISTRY" { + default = "git.send.nrw/sendnrw" +} + +group "default" { + targets = [ + "neuroforge", + "neuroforge-worker", + "agent", + "agent-data-init", + "knowledge", + "control", + ] +} + +target "neuroforge" { + context = "./platform/neuroforge" + dockerfile = "Dockerfile" + target = "server" + tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG}"] +} + +target "neuroforge-worker" { + context = "./platform/neuroforge" + dockerfile = "Dockerfile" + target = "worker" + tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG}"] +} + +target "agent" { + context = "./services/agent" + dockerfile = "Dockerfile" + tags = ["${REGISTRY}/glpi-neuroforge-mega-agent:${IMAGE_TAG}"] +} + +target "agent-data-init" { + context = "./services/agent" + dockerfile = "Dockerfile" + target = "data-init" + tags = ["${REGISTRY}/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG}"] +} + +target "knowledge" { + context = "./services/knowledge" + dockerfile = "Dockerfile" + tags = ["${REGISTRY}/glpi-neuroforge-mega-knowledge:${IMAGE_TAG}"] +} + +target "control" { + context = "./services/control" + dockerfile = "Dockerfile" + tags = ["${REGISTRY}/glpi-neuroforge-mega-control:${IMAGE_TAG}"] +} diff --git a/docker-compose.subagent.yml b/docker-compose.subagent.yml index 7f8bd35..5f923e8 100644 --- a/docker-compose.subagent.yml +++ b/docker-compose.subagent.yml @@ -7,7 +7,7 @@ name: neuroforge-subagents services: cpu-subagent: profiles: [cpu] - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1} command: - -server - ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL} @@ -29,7 +29,7 @@ services: gpu-subagent: profiles: [gpu] - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1} command: - -server - ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL} diff --git a/docker-compose.yml b/docker-compose.yml index df01540..e777fcd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,7 @@ services: cap_drop: - ALL neuroforge: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} command: - -data - /app/data @@ -61,14 +61,15 @@ services: NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS:-3} NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS:-15} NEUROFORGE_WORKER_MAX_QUEUED_JOBS: ${NEUROFORGE_WORKER_MAX_QUEUED_JOBS:-5000} + NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB: ${NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB:-128} NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS:-5} NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS:-5} - NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-168} - NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-20000} + NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-24} + NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-2000} NEUROFORGE_GRAPH_BACKFILL_ENABLED: ${NEUROFORGE_GRAPH_BACKFILL_ENABLED:-true} NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS: ${NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS:-10} - NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-64} - NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-256} + NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-16} + NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-64} NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE: ${NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE:-3} NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER: ${NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER:-6} NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES: ${NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES:-360} @@ -142,7 +143,7 @@ services: start_period: 15s stop_grace_period: 35s neuroforge-worker-cpu: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} command: - -server - http://neuroforge:8080 @@ -167,7 +168,7 @@ services: - ALL neuroforge-worker-gpu: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} command: - -server - http://neuroforge:8080 @@ -199,7 +200,7 @@ services: - ALL agent-data-init: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} restart: 'no' user: 0:0 volumes: @@ -212,7 +213,7 @@ services: - CHOWN - FOWNER agent: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: AI_CONTENT_LABEL_ENABLED: ${AI_CONTENT_LABEL_ENABLED:-} @@ -433,7 +434,7 @@ services: start_period: 10s stop_grace_period: 20s knowledge: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: APP_MODE: ${KB_APP_MODE:-editor} @@ -474,7 +475,7 @@ services: - ALL stop_grace_period: 35s control: - image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} + image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2} restart: unless-stopped environment: CONTROL_ADDR: :8070 diff --git a/docs/MIGRATION-v1.6.0-to-v1.6.1.md b/docs/MIGRATION-v1.6.0-to-v1.6.1.md new file mode 100644 index 0000000..4fae1d4 --- /dev/null +++ b/docs/MIGRATION-v1.6.0-to-v1.6.1.md @@ -0,0 +1,27 @@ +# Migration v1.6.0 → v1.6.1 + +1. **Kein Volume löschen.** `neuroforge-data` enthält den autoritativen Store. +2. Backup des NeuroForge-Volumes erstellen. +3. `IMAGE_TAG=1.6.1` setzen. +4. Für große Graph-Backfills die neuen sicheren Defaults übernehmen: + - `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128` + - `NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24` + - `NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000` + - `NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16` + - `NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64` +5. Nur NeuroForge Master zuerst neu erstellen. Beim ersten Start läuft ggf. `checkpoint.precompact`; diese Streaming-Migration entfernt historische abgeschlossene Relink-Vectorblobs aus dem v1.6.0-Checkpoint. +6. Während Recovery sind `/livez` und `/admin` bereits erreichbar; `/readyz` bleibt bis zum vollständigen Store-/Provider-Start 503. +7. Erst nach `/readyz=200` CPU-/GPU-Subagents und übrige Services normal starten. + +Erwartete Startup-Phasen im Log: + +`store.prepare → checkpoint.precompact → checkpoint.load → memory-segments.scan → wal.replay → jobs.compact → graph.restore → disk-ann.load → hnsw.snapshot.load [→ hnsw.rebuild] → checkpoint.write → store.ready` + +## Recovery-Verhalten in v1.6.1 + +- `state.json` und `secrets.json` sind autoritativ. Syntaxfehler, ein zweites angehängtes JSON-Objekt oder sonstige Dekodierfehler werden **nicht** mehr ignoriert; NeuroForge meldet den konkreten Startup-Fehler und bleibt nicht mit stillschweigend zurückgesetztem Zustand/Tokens in Betrieb. +- Große Checkpoints werden direkt aus der Datei dekodiert und beim Schreiben gestreamt. Dadurch entfällt die zusätzliche vollständige `[]byte`-Kopie des Checkpoints im RAM. +- Beim WAL-Replay werden bereits abgeschlossene `vector.relink`-Payloads sofort verworfen. `apply_wait`, `queued`, `claimed`, `retry_wait` und fehlgeschlagene/retrybare Jobs behalten ihre für Recovery/Retry benötigten Daten. +- Ein beschädigter `state.json`/`secrets.json` wird nicht automatisch überschrieben. Erst Backup/Restore bzw. gezielte Reparatur durchführen. + +Vor einem Upgrade bei einem bereits hängenden v1.6.0-Master das Volume **nicht** löschen. Ein Dateisystem-/Volume-Snapshot ist vorzuziehen. diff --git a/docs/MIGRATION-v1.6.1-to-v1.6.2.md b/docs/MIGRATION-v1.6.1-to-v1.6.2.md new file mode 100644 index 0000000..b855c96 --- /dev/null +++ b/docs/MIGRATION-v1.6.1-to-v1.6.2.md @@ -0,0 +1,12 @@ +# Migration v1.6.1 -> v1.6.2 + +v1.6.2 is primarily a packaging/deployment consolidation release. It retains the v1.6.1 NeuroForge recovery and persisted-state behavior. + +1. Back up the persistent data volumes/directories. +2. Set `IMAGE_TAG=1.6.2` in the selected deployment `.env`. +3. Keep existing `neuroforge-data`, Agent data and Knowledge files; do not delete volumes. +4. Replace deployment examples with the v1.6.2 variants, preserving real secrets locally. +5. Recreate project containers so changed environment/configuration is applied. +6. Check `/livez`, `/readyz`, Agent `/api/status`, worker status, graph convergence and Prometheus alerts. + +The distributed roles now live alongside the standalone Agent/Knowledge/Ollama roles in the same canonical repository. diff --git a/patches/SHA256SUMS b/patches/SHA256SUMS index c0f4e7b..d4d88c5 100644 --- a/patches/SHA256SUMS +++ b/patches/SHA256SUMS @@ -20,3 +20,4 @@ b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 v1.5.1-to-v1.5 53ab438b31a31590edbfe00eea6ba9d9ebff2b4c6255d7ac2056c86757f65c3a v1.5.7-to-v1.5.8.diff 24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 v1.5.8-to-v1.5.9.diff e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 v1.5.9-to-v1.6.0.diff +0cdce94c11a931e11c3c908f2fa3dfa733c5c602ac00f7f89e41b8c301574766 v1.6.0-to-v1.6.1.diff diff --git a/patches/v1.6.0-to-v1.6.1.diff b/patches/v1.6.0-to-v1.6.1.diff new file mode 100644 index 0000000..0c23c17 --- /dev/null +++ b/patches/v1.6.0-to-v1.6.1.diff @@ -0,0 +1,4198 @@ +diff --git a/.env.example b/.env.example +index d6ccda1..45a8d49 100644 +--- a/.env.example ++++ b/.env.example +@@ -29,7 +29,7 @@ + # 01. MEGA STACK - RELEASE / HOST PORTS / PFADE + ############################################################################### + # Immutable Registry-Tag der sechs Projekt-Images. "latest" ist produktiv verboten. +-IMAGE_TAG=1.6.0 ++IMAGE_TAG=1.6.1 + + CONTROL_HOST_PORT=8070 + AGENT_HOST_PORT=8080 +@@ -204,12 +204,13 @@ NEUROFORGE_WORKER_STALE_AFTER_SECONDS=60 + NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS=3 + NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS=15 + NEUROFORGE_WORKER_MAX_QUEUED_JOBS=5000 ++NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128 + # Worker-Ergebnisse, die Master-State verändern (z.B. vector.relink), werden + # zweiphasig persistiert und bei Master-Neustart idempotent weiter angewendet. + NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS=5 + NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS=5 +-NEUROFORGE_WORKER_JOB_RETENTION_HOURS=168 +-NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=20000 ++NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24 ++NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000 + # Lokale Compose-Subagents. Zusätzliche Remote-Subagents können mit + # docker-compose.subagent.yml auf anderen Hosts gestartet werden. + NEUROFORGE_CPU_WORKER_CONCURRENCY=2 +@@ -219,8 +220,8 @@ NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s + # n:m-Graphen verlinkt. Kein O(N²)-Queue-Burst: ANN-Kandidaten + bounded queue. + NEUROFORGE_GRAPH_BACKFILL_ENABLED=true + NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS=10 +-NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=64 +-NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=256 ++NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16 ++NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64 + NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE=3 + NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER=6 + NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES=360 +diff --git a/.env.subagent.example b/.env.subagent.example +index ff75be5..3276990 100644 +--- a/.env.subagent.example ++++ b/.env.subagent.example +@@ -1,5 +1,5 @@ +-# Remote NeuroForge subagent host (v1.6.0) +-IMAGE_TAG=1.6.0 ++# Remote NeuroForge subagent host (v1.6.1) ++IMAGE_TAG=1.6.1 + NEUROFORGE_MASTER_URL=https://neuroforge.internal.example + NEUROFORGE_WORKER_TOKEN=CHANGE_ME_WORKER_TOKEN_SHARED_WITH_MASTER + NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s +diff --git a/README.md b/README.md +index 26f047c..8145919 100644 +--- a/README.md ++++ b/README.md +@@ -1,10 +1,16 @@ +-# GLPI NeuroForge Mega v1.6.0 ++# GLPI NeuroForge Mega v1.6.1 + +-> Release: **v1.6.0** · Durable Master/Subagent Orchestration + Knowledge-Graph Convergence: CPU-/GPU-Arbeit wird capability-basiert, lease-gefenced und persistent geplant; importierte Memories werden bounded zu einem n:m-Synapsengraphen verknüpft und Retrieval kann mehrere Hops traversieren. ++> Release: **v1.6.1** · Crash-/Recovery-Hardening für große Knowledge-Korpora auf Basis der v1.6.0 Master/Subagent- und n:m-Graph-Architektur. + + Ein kontrolliertes Monorepo aus **GLPI AI Agent**, **GLPI AI Knowledgebase** und **NeuroForge + SQAR**. Ziel ist nicht ein untrennbarer Monolith, sondern eine gemeinsame Plattform mit klaren Zuständigkeiten, getrennten Credentials und nachvollziehbaren Failure-Modi. + + ++## Crash-/Recovery-Hardening (v1.6.1) ++ ++v1.6.1 behebt einen RAM-/Recovery-Fehler, der bei großen Graph-Backfills nach mehreren Stunden Laufzeit auftreten konnte. Erfolgreiche `vector.relink`-Jobs verwerfen ihre großen transienten Vector-Payloads unmittelbar nach dem autoritativen Master-Apply; ein Queue-Payload-Budget verhindert neue ungebremste Speicherbelegung. Beim ersten Start migriert NeuroForge alte v1.6.0-Checkpoints streaming, bevor `state.json` vollständig in den RAM geladen wird. ++ ++HNSW-Deltas werden nicht mehr über einen vollständigen Deep-Copy aller Vektoren erzeugt. Checkpoints persistieren den Index vor `state.json`, sodass ein Crash während der Indexpersistenz den autoritativen Checkpoint nicht vor den verwendbaren Index ziehen kann. Bei Containerstarts mit explizitem `-listen` ist sofort eine Bootstrap-Liveness-/Startup-Seite erreichbar; Logs zeigen die aktuelle Recovery-Phase (`checkpoint.precompact`, `memory-segments.scan`, `wal.replay`, `hnsw.snapshot.load`, `hnsw.rebuild`, ...). ++ + ## Research → Human-Review-Staging (v1.5.0) + + Autonome Research-Goals können ihre quellengebundene Evidenz jetzt tatsächlich in einen **KB-Staging-Entwurf** überführen. Die Bridge ist einseitig: NeuroForge darf ausschließlich `POST /api/integrations/staging` mit dem separaten `KB_INTEGRATION_TOKEN` verwenden; `auto_reply=false` wird serverseitig erzwungen und Produktivwissen bleibt menschlich freigabepflichtig. +diff --git a/RELEASE-NOTES-v1.6.1.md b/RELEASE-NOTES-v1.6.1.md +new file mode 100644 +index 0000000..bcd79a8 +--- /dev/null ++++ b/RELEASE-NOTES-v1.6.1.md +@@ -0,0 +1,30 @@ ++# GLPI NeuroForge Mega v1.6.1 ++ ++## Production hotfix: Master crash / silent recovery on large Knowledge corpora ++ ++v1.6.1 keeps the v1.6.0 Master/Subagent and n:m Knowledge-Graph architecture and hardens the NeuroForge master for long-running bulk imports/backfills. ++ ++### Fixed ++ ++- Completed `vector.relink` jobs no longer retain full target/candidate vectors after successful master apply. ++- A one-time streaming startup migration compacts large v1.6.0 `state.json` checkpoints before full JSON unmarshal. ++- Durable pending-job payload bytes are bounded by `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB` (default 128 MiB). ++- HNSW segmented checkpoint deltas no longer deep-copy every vector/node on each checkpoint; only changed nodes are copied. ++- Checkpoint ordering is index-first, then `state.json`, then WAL prune, improving crash recovery when index persistence is interrupted. ++- Startup with explicit `-listen` exposes `/livez` and a minimal `/admin` recovery page before the store is fully open. ++- Startup phases are logged, so segment/WAL/HNSW recovery is no longer silent. ++- Existing v1.6.0 completed relink payloads are compacted on first v1.6.1 boot. ++- Authoritative `state.json` and `secrets.json` are decoded directly from files instead of `ReadFile`+`Unmarshal`; corrupt or trailing JSON now fails closed with an explicit startup error instead of being silently ignored. ++- Checkpoints/secrets/index metadata are JSON-streamed to atomic temporary files instead of allocating a second complete encoded byte slice; the final file and directory entry are synced before success. ++- WAL recovery immediately drops transient payload/result blobs from already-completed `vector.relink` jobs, so a large surviving WAL cannot rebuild the same multi-GB terminal-job heap during restart. ++- Fresh data directories no longer consume the one-time v1.6.1 compaction marker before a possible v1.6.0 restore. ++ ++### Safer graph defaults ++ ++- `NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24` ++- `NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000` ++- `NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16` ++- `NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64` ++- `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128` ++ ++These are throughput/stability defaults; completed relink payload compaction is enforced independently of the values above. +diff --git a/VERSION b/VERSION +index dc1e644..9c6d629 100644 +--- a/VERSION ++++ b/VERSION +@@ -1 +1 @@ +-1.6.0 ++1.6.1 +diff --git a/docker-compose.subagent.yml b/docker-compose.subagent.yml +index 7f8bd35..5f923e8 100644 +--- a/docker-compose.subagent.yml ++++ b/docker-compose.subagent.yml +@@ -7,7 +7,7 @@ name: neuroforge-subagents + services: + cpu-subagent: + profiles: [cpu] +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1} + command: + - -server + - ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL} +@@ -29,7 +29,7 @@ services: + + gpu-subagent: + profiles: [gpu] +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1} + command: + - -server + - ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL} +diff --git a/docker-compose.yml b/docker-compose.yml +index df01540..61900b1 100644 +--- a/docker-compose.yml ++++ b/docker-compose.yml +@@ -33,7 +33,7 @@ services: + cap_drop: + - ALL + neuroforge: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + command: + - -data + - /app/data +@@ -61,14 +61,15 @@ services: + NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS:-3} + NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS:-15} + NEUROFORGE_WORKER_MAX_QUEUED_JOBS: ${NEUROFORGE_WORKER_MAX_QUEUED_JOBS:-5000} ++ NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB: ${NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB:-128} + NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS:-5} + NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS:-5} +- NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-168} +- NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-20000} ++ NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-24} ++ NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-2000} + NEUROFORGE_GRAPH_BACKFILL_ENABLED: ${NEUROFORGE_GRAPH_BACKFILL_ENABLED:-true} + NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS: ${NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS:-10} +- NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-64} +- NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-256} ++ NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-16} ++ NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-64} + NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE: ${NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE:-3} + NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER: ${NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER:-6} + NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES: ${NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES:-360} +@@ -142,7 +143,7 @@ services: + start_period: 15s + stop_grace_period: 35s + neuroforge-worker-cpu: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + command: + - -server + - http://neuroforge:8080 +@@ -167,7 +168,7 @@ services: + - ALL + + neuroforge-worker-gpu: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + command: + - -server + - http://neuroforge:8080 +@@ -199,7 +200,7 @@ services: + - ALL + + agent-data-init: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + restart: 'no' + user: 0:0 + volumes: +@@ -212,7 +213,7 @@ services: + - CHOWN + - FOWNER + agent: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + restart: unless-stopped + environment: + AI_CONTENT_LABEL_ENABLED: ${AI_CONTENT_LABEL_ENABLED:-} +@@ -433,7 +434,7 @@ services: + start_period: 10s + stop_grace_period: 20s + knowledge: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + restart: unless-stopped + environment: + APP_MODE: ${KB_APP_MODE:-editor} +@@ -474,7 +475,7 @@ services: + - ALL + stop_grace_period: 35s + control: +- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0} ++ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1} + restart: unless-stopped + environment: + CONTROL_ADDR: :8070 +diff --git a/docs/MIGRATION-v1.6.0-to-v1.6.1.md b/docs/MIGRATION-v1.6.0-to-v1.6.1.md +new file mode 100644 +index 0000000..4fae1d4 +--- /dev/null ++++ b/docs/MIGRATION-v1.6.0-to-v1.6.1.md +@@ -0,0 +1,27 @@ ++# Migration v1.6.0 → v1.6.1 ++ ++1. **Kein Volume löschen.** `neuroforge-data` enthält den autoritativen Store. ++2. Backup des NeuroForge-Volumes erstellen. ++3. `IMAGE_TAG=1.6.1` setzen. ++4. Für große Graph-Backfills die neuen sicheren Defaults übernehmen: ++ - `NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128` ++ - `NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24` ++ - `NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000` ++ - `NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16` ++ - `NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64` ++5. Nur NeuroForge Master zuerst neu erstellen. Beim ersten Start läuft ggf. `checkpoint.precompact`; diese Streaming-Migration entfernt historische abgeschlossene Relink-Vectorblobs aus dem v1.6.0-Checkpoint. ++6. Während Recovery sind `/livez` und `/admin` bereits erreichbar; `/readyz` bleibt bis zum vollständigen Store-/Provider-Start 503. ++7. Erst nach `/readyz=200` CPU-/GPU-Subagents und übrige Services normal starten. ++ ++Erwartete Startup-Phasen im Log: ++ ++`store.prepare → checkpoint.precompact → checkpoint.load → memory-segments.scan → wal.replay → jobs.compact → graph.restore → disk-ann.load → hnsw.snapshot.load [→ hnsw.rebuild] → checkpoint.write → store.ready` ++ ++## Recovery-Verhalten in v1.6.1 ++ ++- `state.json` und `secrets.json` sind autoritativ. Syntaxfehler, ein zweites angehängtes JSON-Objekt oder sonstige Dekodierfehler werden **nicht** mehr ignoriert; NeuroForge meldet den konkreten Startup-Fehler und bleibt nicht mit stillschweigend zurückgesetztem Zustand/Tokens in Betrieb. ++- Große Checkpoints werden direkt aus der Datei dekodiert und beim Schreiben gestreamt. Dadurch entfällt die zusätzliche vollständige `[]byte`-Kopie des Checkpoints im RAM. ++- Beim WAL-Replay werden bereits abgeschlossene `vector.relink`-Payloads sofort verworfen. `apply_wait`, `queued`, `claimed`, `retry_wait` und fehlgeschlagene/retrybare Jobs behalten ihre für Recovery/Retry benötigten Daten. ++- Ein beschädigter `state.json`/`secrets.json` wird nicht automatisch überschrieben. Erst Backup/Restore bzw. gezielte Reparatur durchführen. ++ ++Vor einem Upgrade bei einem bereits hängenden v1.6.0-Master das Volume **nicht** löschen. Ein Dateisystem-/Volume-Snapshot ist vorzuziehen. +diff --git a/platform/neuroforge/CHANGELOG.md b/platform/neuroforge/CHANGELOG.md +index 842d354..8fe1d5d 100644 +--- a/platform/neuroforge/CHANGELOG.md ++++ b/platform/neuroforge/CHANGELOG.md +@@ -1,4 +1,17 @@ + # Changelog ++- v1.6.1 hotfix: streaming authoritative checkpoint I/O avoids full raw/encoded `state.json` copies during load/save. ++- v1.6.1 hotfix: corrupt `state.json`/`secrets.json` fail closed instead of being silently ignored. ++- v1.6.1 hotfix: completed `vector.relink` blobs are compacted during WAL replay as well as checkpoint migration. ++ ++## v0.8.3 ++ ++- Crash-/Recovery-Hardening für große Knowledge-Korpora und Graph-Backfills. ++- Streaming-Migration entfernt historische `vector.relink`-Payloads vor dem vollständigen Checkpoint-Unmarshal. ++- Erfolgreiche Relink-Jobs verwerfen Target-/Candidate-Vektoren sofort nach Master-Apply. ++- Queue-Payload-Budget schützt den Master vor ungebremstem transientem Job-State. ++- HNSW-Deltas kopieren nur veränderte Nodes statt bei jedem Checkpoint den kompletten Index. ++- Checkpoint-Reihenfolge index-first verhindert `state.json`-Vorlauf nach Crash in der Indexpersistenz. ++- Bootstrap-HTTP und Startup-Phasenlogs machen lange Recovery sichtbar. + + ## v0.8.2 + +diff --git a/platform/neuroforge/PRODUCTION.md b/platform/neuroforge/PRODUCTION.md +index 919ac75..43694a4 100644 +--- a/platform/neuroforge/PRODUCTION.md ++++ b/platform/neuroforge/PRODUCTION.md +@@ -1,4 +1,4 @@ +-# NeuroForge v0.8.2 – Production Guide ++# NeuroForge v0.8.3 – Production Guide + + ## 1. Sicherheitsgrenze + +@@ -146,3 +146,7 @@ Research-Trace-Events sind **Observability/Audit**, nicht autoritative Knowledge + + Die `preview`-Felder im Research-Trace sind absichtlich gekürzt und enthalten keine vollständigen Dokumente. Für vollständige Inhalte den Source-/Memory-Inspector verwenden. + ++ ++### v1.6.1 Recovery / large graph backfill ++ ++For large graph backfills keep completed relink payloads compact and bound pending payload bytes. The master streams `state.json` on load/save and fails closed on corrupt authoritative state/secrets. Never delete or replace a corrupt checkpoint automatically; retain the volume for forensic recovery/restore. During startup with explicit `-listen`, `/livez` and the bootstrap `/admin` page expose the current recovery phase while `/readyz` remains 503. +diff --git a/platform/neuroforge/README.md b/platform/neuroforge/README.md +index 3785683..67af793 100644 +--- a/platform/neuroforge/README.md ++++ b/platform/neuroforge/README.md +@@ -1,8 +1,8 @@ +-# NeuroForge v0.8.2 ++# NeuroForge v0.8.3 + + NeuroForge ist eine persistente, assoziativ lernende KI-Schicht in Go. Ollama und optional OpenAI liefern Inferenz/Embeddings; NeuroForge besitzt den dauerhaften Wissenszustand: Vektoren, HNSW/Disk-PQ-Recall, Synapsen, Rewards, Provenance, Konflikte, Konsolidierung, Goals und Learning Cycles. + +-**v0.8.2 erweitert den Production-/Explainability-Stand um einen source-grounded Lernpfad, Dokument-/Text-Ingestion, SearXNG-Research und ein vollständig neu gestaltetes CSS/Vanilla-JS-Admin-UI mit responsive Knowledge-Graph und Level-of-Detail (LOD).** Wissen soll nicht nur gespeichert, sondern als Kette `Quelle → Evidence → Recall → Learning` nachvollziehbar sein. ++**v0.8.3 übernimmt den v0.8.2-Funktionsumfang und härtet große Bulk-/Graph-Workloads gegen Speicher- und Recovery-Spitzen. v0.8.2 erweitert den Production-/Explainability-Stand um einen source-grounded Lernpfad, Dokument-/Text-Ingestion, SearXNG-Research und ein vollständig neu gestaltetes CSS/Vanilla-JS-Admin-UI mit responsive Knowledge-Graph und Level-of-Detail (LOD).** Wissen soll nicht nur gespeichert, sondern als Kette `Quelle → Evidence → Recall → Learning` nachvollziehbar sein. + + + ## Neu in v0.8.2: Live Research pro Goal +diff --git a/platform/neuroforge/VERSION b/platform/neuroforge/VERSION +index 100435b..ee94dd8 100644 +--- a/platform/neuroforge/VERSION ++++ b/platform/neuroforge/VERSION +@@ -1 +1 @@ +-0.8.2 ++0.8.3 +diff --git a/platform/neuroforge/cmd/server/main.go b/platform/neuroforge/cmd/server/main.go +index 9886bcd..c48a86a 100644 +--- a/platform/neuroforge/cmd/server/main.go ++++ b/platform/neuroforge/cmd/server/main.go +@@ -6,12 +6,14 @@ import ( + "flag" + "fmt" + "log" ++ "net" + "net/http" + "os" + "os/signal" + "path/filepath" + "strconv" + "strings" ++ "sync" + "syscall" + "time" + +@@ -101,6 +103,59 @@ func maxIntMain(a, b int) int { + return b + } + ++type bootstrapHandler struct { ++ mu sync.RWMutex ++ phase string ++ handler http.Handler ++} ++ ++func newBootstrapHandler() *bootstrapHandler { ++ return &bootstrapHandler{phase: "process.start"} ++} ++ ++func (b *bootstrapHandler) SetPhase(phase string) { ++ b.mu.Lock() ++ b.phase = phase ++ b.mu.Unlock() ++} ++ ++func (b *bootstrapHandler) SetHandler(h http.Handler) { ++ b.mu.Lock() ++ b.handler = h ++ b.phase = "ready" ++ b.mu.Unlock() ++} ++ ++func (b *bootstrapHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ++ b.mu.RLock() ++ h := b.handler ++ phase := b.phase ++ b.mu.RUnlock() ++ if h != nil { ++ h.ServeHTTP(w, r) ++ return ++ } ++ w.Header().Set("Cache-Control", "no-store") ++ switch r.URL.Path { ++ case "/livez", "/healthz": ++ w.Header().Set("Content-Type", "application/json") ++ w.WriteHeader(http.StatusOK) ++ _, _ = fmt.Fprintf(w, `{"ok":true,"status":"starting","phase":%q}`, phase) ++ case "/readyz": ++ w.Header().Set("Content-Type", "application/json") ++ w.WriteHeader(http.StatusServiceUnavailable) ++ _, _ = fmt.Fprintf(w, `{"ok":false,"status":"starting","phase":%q}`, phase) ++ case "/", "/admin": ++ w.Header().Set("Content-Type", "text/html; charset=utf-8") ++ w.WriteHeader(http.StatusServiceUnavailable) ++ _, _ = fmt.Fprintf(w, `NeuroForge starting

NeuroForge startet

Recovery-/Startphase: %s

Die autoritativen Daten werden geladen. Diese Seite aktualisiert sich automatisch.

`, phase) ++ default: ++ w.Header().Set("Content-Type", "application/json") ++ w.WriteHeader(http.StatusServiceUnavailable) ++ _, _ = fmt.Fprintf(w, `{"error":"neuroforge is starting","phase":%q}`, phase) ++ } ++} ++ + func main() { + if err := run(); err != nil { + log.Printf("fatal: %v", err) +@@ -117,7 +172,55 @@ func run() (retErr error) { + return err + } + +- s, err := store.New(*data) ++ var boot *bootstrapHandler ++ var srv *http.Server ++ var errCh chan error ++ // Container deployments pass an explicit -listen address. Bind it before ++ // opening the potentially large store so liveness and a minimal startup UI ++ // remain reachable during WAL/index recovery instead of looking like a dead ++ // container with no logs. ++ if strings.TrimSpace(*listen) != "" { ++ boot = newBootstrapHandler() ++ d := core.DefaultConfig() ++ h := d.HTTP ++ srv = &http.Server{ ++ Addr: *listen, ++ Handler: boot, ++ ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, ++ ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, ++ WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, ++ IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, ++ MaxHeaderBytes: h.MaxHeaderBytes, ++ } ++ ln, err := net.Listen("tcp", *listen) ++ if err != nil { ++ return fmt.Errorf("bootstrap listen %s: %w", *listen, err) ++ } ++ errCh = make(chan error, 1) ++ go func() { ++ err := srv.Serve(ln) ++ if errors.Is(err, http.ErrServerClosed) { ++ err = nil ++ } ++ errCh <- err ++ }() ++ log.Printf("NeuroForge bootstrap listener active on %s", *listen) ++ defer func() { ++ if retErr != nil && srv != nil { ++ ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ++ defer cancel() ++ _ = srv.Shutdown(ctx) ++ } ++ }() ++ } ++ reportStartup := func(phase string) { ++ log.Printf("startup phase: %s", phase) ++ if boot != nil { ++ boot.SetPhase(phase) ++ } ++ } ++ ++ s, err := store.NewWithProgress(*data, reportStartup) + if err != nil { + return err + } +@@ -258,7 +361,7 @@ func run() (retErr error) { + // and environment-owned only when set, preserving admin-managed config otherwise. + workerEnv := []string{ + "NEUROFORGE_WORKER_LEASE_SECONDS", "NEUROFORGE_WORKER_HEARTBEAT_SECONDS", "NEUROFORGE_WORKER_STALE_AFTER_SECONDS", +- "NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS", "NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS", "NEUROFORGE_WORKER_MAX_QUEUED_JOBS", ++ "NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS", "NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS", "NEUROFORGE_WORKER_MAX_QUEUED_JOBS", "NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB", + "NEUROFORGE_WORKER_JOB_RETENTION_HOURS", "NEUROFORGE_WORKER_MAX_TERMINAL_JOBS", + "NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS", "NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS", + "NEUROFORGE_GRAPH_BACKFILL_ENABLED", "NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS", "NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE", +@@ -294,6 +397,9 @@ func run() (retErr error) { + if v, ok := envInt("NEUROFORGE_WORKER_MAX_QUEUED_JOBS"); ok { + cfg.Worker.MaxQueuedJobs = v + } ++ if v, ok := envInt("NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB"); ok { ++ cfg.Worker.MaxQueuedPayloadMB = v ++ } + if v, ok := envInt("NEUROFORGE_WORKER_JOB_RETENTION_HOURS"); ok { + cfg.Worker.JobRetentionHours = v + } +@@ -453,30 +559,33 @@ func run() (retErr error) { + } + + h := cfg.HTTP +- srv := &http.Server{ +- Addr: addr, +- Handler: api.Handler(), +- ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, +- ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, +- WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, +- IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, +- MaxHeaderBytes: h.MaxHeaderBytes, +- } +- log.Printf("NeuroForge v0.8.2 listening on %s", addr) ++ if srv == nil { ++ srv = &http.Server{ ++ Addr: addr, ++ Handler: api.Handler(), ++ ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, ++ ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, ++ WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, ++ IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, ++ MaxHeaderBytes: h.MaxHeaderBytes, ++ } ++ errCh = make(chan error, 1) ++ go func() { ++ err := srv.ListenAndServe() ++ if errors.Is(err, http.ErrServerClosed) { ++ err = nil ++ } ++ errCh <- err ++ }() ++ } else { ++ boot.SetHandler(api.Handler()) ++ } ++ log.Printf("NeuroForge v0.8.3 listening on %s", addr) + log.Printf("Admin dashboard: /admin · readiness: /readyz · metrics: /metrics") + if os.Getenv("NEUROFORGE_ADMIN_TOKEN") == "" { + log.Printf("Admin token is intentionally not printed; read it locally from %s or set NEUROFORGE_ADMIN_TOKEN", filepath.Join(*data, "secrets.json")) + } + +- errCh := make(chan error, 1) +- go func() { +- err := srv.ListenAndServe() +- if errors.Is(err, http.ErrServerClosed) { +- err = nil +- } +- errCh <- err +- }() +- + var serveErr error + select { + case serveErr = <-errCh: +diff --git a/platform/neuroforge/cmd/server/main_test.go b/platform/neuroforge/cmd/server/main_test.go +new file mode 100644 +index 0000000..c987a23 +--- /dev/null ++++ b/platform/neuroforge/cmd/server/main_test.go +@@ -0,0 +1,38 @@ ++package main ++ ++import ( ++ "net/http" ++ "net/http/httptest" ++ "strings" ++ "testing" ++) ++ ++func TestBootstrapHandlerExposesLivenessAndStartupUI(t *testing.T) { ++ b := newBootstrapHandler() ++ b.SetPhase("hnsw.rebuild") ++ ++ rr := httptest.NewRecorder() ++ b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/livez", nil)) ++ if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "hnsw.rebuild") { ++ t.Fatalf("livez=%d %s", rr.Code, rr.Body.String()) ++ } ++ ++ rr = httptest.NewRecorder() ++ b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/readyz", nil)) ++ if rr.Code != http.StatusServiceUnavailable { ++ t.Fatalf("readyz=%d", rr.Code) ++ } ++ ++ rr = httptest.NewRecorder() ++ b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/admin", nil)) ++ if rr.Code != http.StatusServiceUnavailable || !strings.Contains(rr.Body.String(), "hnsw.rebuild") { ++ t.Fatalf("admin=%d %s", rr.Code, rr.Body.String()) ++ } ++ ++ b.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) })) ++ rr = httptest.NewRecorder() ++ b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/admin", nil)) ++ if rr.Code != http.StatusNoContent { ++ t.Fatalf("delegated=%d", rr.Code) ++ } ++} +diff --git a/platform/neuroforge/internal/core/types.go b/platform/neuroforge/internal/core/types.go +index 78b379b..c75ef25 100644 +--- a/platform/neuroforge/internal/core/types.go ++++ b/platform/neuroforge/internal/core/types.go +@@ -398,6 +398,7 @@ type Config struct { + DefaultMaxAttempts int `json:"default_max_attempts"` + RetryBackoffSeconds int `json:"retry_backoff_seconds"` + MaxQueuedJobs int `json:"max_queued_jobs"` ++ MaxQueuedPayloadMB int `json:"max_queued_payload_mb"` + MasterApplyMaxAttempts int `json:"master_apply_max_attempts"` + MasterApplyBackoffSeconds int `json:"master_apply_backoff_seconds"` + JobRetentionHours int `json:"job_retention_hours"` +@@ -921,14 +922,15 @@ func DefaultConfig() Config { + c.Worker.DefaultMaxAttempts = 3 + c.Worker.RetryBackoffSeconds = 15 + c.Worker.MaxQueuedJobs = 5000 ++ c.Worker.MaxQueuedPayloadMB = 128 + c.Worker.MasterApplyMaxAttempts = 5 + c.Worker.MasterApplyBackoffSeconds = 5 +- c.Worker.JobRetentionHours = 168 +- c.Worker.MaxTerminalJobs = 20000 ++ c.Worker.JobRetentionHours = 24 ++ c.Worker.MaxTerminalJobs = 2000 + c.Worker.GraphBackfillEnabled = true + c.Worker.GraphBackfillIntervalS = 10 +- c.Worker.GraphBackfillBatchSize = 64 +- c.Worker.GraphBackfillMaxQueued = 512 ++ c.Worker.GraphBackfillBatchSize = 16 ++ c.Worker.GraphBackfillMaxQueued = 64 + c.Worker.GraphBackfillMinDegree = 3 + c.Worker.GraphCandidateMultiplier = 6 + c.Worker.GraphRetryAfterMinutes = 360 +diff --git a/platform/neuroforge/internal/httpapi/httpapi.go b/platform/neuroforge/internal/httpapi/httpapi.go +index 109e8ea..254eea6 100644 +--- a/platform/neuroforge/internal/httpapi/httpapi.go ++++ b/platform/neuroforge/internal/httpapi/httpapi.go +@@ -59,7 +59,7 @@ func (s *Server) routes() { + s.mux.HandleFunc("GET /healthz", s.livez) + s.mux.HandleFunc("GET /livez", s.livez) + s.mux.HandleFunc("GET /readyz", s.readyz) +- s.mux.HandleFunc("GET /version", func(w http.ResponseWriter, r *http.Request) { s.json(w, 200, map[string]any{"version": "0.8.2"}) }) ++ s.mux.HandleFunc("GET /version", func(w http.ResponseWriter, r *http.Request) { s.json(w, 200, map[string]any{"version": "0.8.3"}) }) + s.mux.Handle("POST /api/v1/chat", s.appAuth(http.HandlerFunc(s.chat))) + s.mux.Handle("POST /api/v1/learn", s.appAuth(http.HandlerFunc(s.learn))) + s.mux.Handle("POST /api/v1/search", s.appAuth(http.HandlerFunc(s.search))) +@@ -882,7 +882,7 @@ func (s *Server) requestLimits(next http.Handler) http.Handler { + } + + func (s *Server) livez(w http.ResponseWriter, r *http.Request) { +- s.json(w, http.StatusOK, map[string]any{"ok": true, "status": "alive", "time": time.Now().UTC(), "version": "0.8.2"}) ++ s.json(w, http.StatusOK, map[string]any{"ok": true, "status": "alive", "time": time.Now().UTC(), "version": "0.8.3"}) + } + + func configuredModelAvailable(models map[string]bool, configured string) bool { +diff --git a/platform/neuroforge/internal/httpapi/metrics.go b/platform/neuroforge/internal/httpapi/metrics.go +index d49bf82..3fbd67f 100644 +--- a/platform/neuroforge/internal/httpapi/metrics.go ++++ b/platform/neuroforge/internal/httpapi/metrics.go +@@ -276,6 +276,9 @@ func (s *Server) metricsEndpoint(w http.ResponseWriter, r *http.Request) { + for _, status := range statuses { + promSample(&b, "neuroforge_jobs", orch.JobsByStatus[status], "status", status) + } ++ promHeader(&b, "neuroforge_job_payload_bytes", "Durable orchestrator payload/result bytes retained in memory and checkpoints.", "gauge") ++ promSample(&b, "neuroforge_job_payload_bytes", orch.PendingPayloadBytes, "state", "pending") ++ promSample(&b, "neuroforge_job_payload_bytes", orch.TerminalPayloadBytes, "state", "terminal") + promHeader(&b, "neuroforge_jobs_by_resource", "Current durable orchestrator jobs by resource class.", "gauge") + for resource, n := range orch.JobsByResource { + promSample(&b, "neuroforge_jobs_by_resource", n, "resource", resource) +diff --git a/platform/neuroforge/internal/research/searxng.go b/platform/neuroforge/internal/research/searxng.go +index 0ed4bfc..f527010 100644 +--- a/platform/neuroforge/internal/research/searxng.go ++++ b/platform/neuroforge/internal/research/searxng.go +@@ -169,7 +169,7 @@ func FetchResource(ctx context.Context, cfg FetchConfig, rawURL string) (Resourc + req.Header.Set("Accept", "text/html,application/xhtml+xml,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/plain,text/markdown,text/csv,application/json;q=0.9,*/*;q=0.2") + ua := strings.TrimSpace(cfg.UserAgent) + if ua == "" { +- ua = "NeuroForge/0.8.2 research bot" ++ ua = "NeuroForge/0.8.3 research bot" + } + req.Header.Set("User-Agent", ua) + resp, err := client.Do(req) +diff --git a/platform/neuroforge/internal/store/checkpoint_order_test.go b/platform/neuroforge/internal/store/checkpoint_order_test.go +new file mode 100644 +index 0000000..455f27c +--- /dev/null ++++ b/platform/neuroforge/internal/store/checkpoint_order_test.go +@@ -0,0 +1,56 @@ ++package store ++ ++import ( ++ "encoding/json" ++ "os" ++ "path/filepath" ++ "testing" ++ ++ "neuroforge/internal/core" ++) ++ ++func readCheckpointRevision(t *testing.T, dir string) uint64 { ++ t.Helper() ++ b, err := os.ReadFile(filepath.Join(dir, "state.json")) ++ if err != nil { ++ t.Fatal(err) ++ } ++ var st core.PersistedState ++ if err := json.Unmarshal(b, &st); err != nil { ++ t.Fatal(err) ++ } ++ return st.Revision ++} ++ ++func TestCheckpointDoesNotAdvanceStateBeforeIndexSnapshotSucceeds(t *testing.T) { ++ dir := t.TempDir() ++ s, err := New(dir) ++ if err != nil { ++ t.Fatal(err) ++ } ++ cfg := s.Config() ++ cfg.Storage.CheckpointEvery = 1 ++ if err := s.UpdateConfig(cfg); err != nil { ++ t.Fatal(err) ++ } ++ before := readCheckpointRevision(t, dir) ++ ++ idxDir := filepath.Join(dir, "hnsw-index") ++ if err := os.RemoveAll(idxDir); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(idxDir, []byte("block directory creation"), 0600); err != nil { ++ t.Fatal(err) ++ } ++ m := &core.Memory{ID: "m1", Kind: core.MemorySemantic, MemoryType: core.MemorySemantic, Text: "x", Vector: []float32{1, 0, 0}, VectorDim: 3, Status: core.MemoryActive} ++ if err := s.AddMemory(m); err == nil { ++ t.Fatal("expected checkpoint/index failure") ++ } ++ after := readCheckpointRevision(t, dir) ++ if after != before { ++ t.Fatalf("state checkpoint advanced despite index failure: before=%d after=%d", before, after) ++ } ++ ++ _ = os.Remove(idxDir) ++ _ = s.Close() ++} +diff --git a/platform/neuroforge/internal/store/index_segments.go b/platform/neuroforge/internal/store/index_segments.go +index 63a59b0..910b562 100644 +--- a/platform/neuroforge/internal/store/index_segments.go ++++ b/platform/neuroforge/internal/store/index_segments.go +@@ -211,41 +211,28 @@ func (s *Store) writeSegmentedIndexSnapshotLocked() error { + return nil + } + +- current := s.currentSnapshotsLocked() ++ currentShadow := make(map[int]indexSnapshotShadow, len(s.indexes)) + delta := indexDeltaBundle{Revision: s.state.Revision, Dimensions: map[string]indexDimensionDelta{}} + dims := map[int]bool{} +- for dim := range current { ++ for dim := range s.indexes { + dims[dim] = true + } + for dim := range s.indexShadow { + dims[dim] = true + } + for dim := range dims { +- cur, curOK := current[dim] ++ idx, curOK := s.indexes[dim] + prev, prevOK := s.indexShadow[dim] + key := strconv.Itoa(dim) +- if !curOK { ++ if !curOK || idx == nil { + delta.Dimensions[key] = indexDimensionDelta{DeletedDimension: true} + continue + } +- d := indexDimensionDelta{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel} +- curIDs := map[string]bool{} +- for _, n := range cur.Nodes { +- curIDs[n.ID] = true +- h := hashSnapshotNode(n) +- if ph, ok := prev.Nodes[n.ID]; !prevOK || !ok || ph != h { +- d.Upserts = append(d.Upserts, n) +- } +- } +- if prevOK { +- for id := range prev.Nodes { +- if !curIDs[id] { +- d.Deletes = append(d.Deletes, id) +- } +- } +- } +- sort.Strings(d.Deletes) +- if !prevOK || len(d.Upserts) > 0 || len(d.Deletes) > 0 || prev.EntryID != cur.EntryID || prev.MaxLevel != cur.MaxLevel || prev.Config != cur.Config { ++ vprev := vector.HNSWShadow{Config: prev.Config, EntryID: prev.EntryID, MaxLevel: prev.MaxLevel, Nodes: prev.Nodes} ++ cur, upserts, deletes := idx.Delta(vprev) ++ currentShadow[dim] = indexSnapshotShadow{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel, Nodes: cur.Nodes} ++ d := indexDimensionDelta{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel, Upserts: upserts, Deletes: deletes} ++ if !prevOK || len(upserts) > 0 || len(deletes) > 0 || prev.EntryID != cur.EntryID || prev.MaxLevel != cur.MaxLevel || prev.Config != cur.Config { + delta.Dimensions[key] = d + } + } +@@ -258,7 +245,7 @@ func (s *Store) writeSegmentedIndexSnapshotLocked() error { + if err := writeAtomic(manifestPath, 0600, &manifest); err != nil { + return err + } +- s.indexShadow = buildIndexShadow(current) ++ s.indexShadow = currentShadow + s.indexSnapshotRevision = s.state.Revision + s.indexDeltaCount = len(manifest.Deltas) + return nil +diff --git a/platform/neuroforge/internal/store/orchestrator.go b/platform/neuroforge/internal/store/orchestrator.go +index f3b6368..2efe0f4 100644 +--- a/platform/neuroforge/internal/store/orchestrator.go ++++ b/platform/neuroforge/internal/store/orchestrator.go +@@ -43,18 +43,20 @@ type WorkerHeartbeat struct { + } + + type OrchestratorStatus struct { +- Workers []core.WorkerState `json:"workers"` +- JobsByStatus map[string]int `json:"jobs_by_status"` +- JobsByType map[string]int `json:"jobs_by_type"` +- JobsByResource map[string]int `json:"jobs_by_resource"` +- OldestQueued time.Time `json:"oldest_queued,omitempty"` +- Queued int `json:"queued"` +- Claimed int `json:"claimed"` +- Retrying int `json:"retrying"` +- Applying int `json:"applying"` +- Blocked int `json:"blocked"` +- Failed int `json:"failed"` +- Done int `json:"done"` ++ Workers []core.WorkerState `json:"workers"` ++ JobsByStatus map[string]int `json:"jobs_by_status"` ++ JobsByType map[string]int `json:"jobs_by_type"` ++ JobsByResource map[string]int `json:"jobs_by_resource"` ++ OldestQueued time.Time `json:"oldest_queued,omitempty"` ++ Queued int `json:"queued"` ++ Claimed int `json:"claimed"` ++ Retrying int `json:"retrying"` ++ Applying int `json:"applying"` ++ Blocked int `json:"blocked"` ++ Failed int `json:"failed"` ++ Done int `json:"done"` ++ PendingPayloadBytes int64 `json:"pending_payload_bytes"` ++ TerminalPayloadBytes int64 `json:"terminal_payload_bytes"` + } + + func normalizeCapabilities(in []string) []string { +@@ -104,15 +106,22 @@ func (s *Store) EnqueueJobSpec(spec JobSpec) (*core.Job, error) { + s.mu.Lock() + defer s.mu.Unlock() + cfg := s.state.Config.Worker +- if cfg.MaxQueuedJobs > 0 { +- pending := 0 +- for _, j := range s.state.Jobs { +- if j != nil && (j.Status == "queued" || j.Status == "claimed" || j.Status == "retry_wait" || j.Status == "blocked" || j.Status == "apply_wait") { +- pending++ +- } ++ pending := 0 ++ var pendingPayloadBytes int64 ++ for _, j := range s.state.Jobs { ++ if j == nil || (j.Status != "queued" && j.Status != "claimed" && j.Status != "retry_wait" && j.Status != "blocked" && j.Status != "apply_wait") { ++ continue + } +- if pending >= cfg.MaxQueuedJobs { +- return nil, fmt.Errorf("orchestrator queue full: %d >= %d", pending, cfg.MaxQueuedJobs) ++ pending++ ++ pendingPayloadBytes += int64(len(j.Payload) + len(j.Result)) ++ } ++ if cfg.MaxQueuedJobs > 0 && pending >= cfg.MaxQueuedJobs { ++ return nil, fmt.Errorf("orchestrator queue full: %d >= %d", pending, cfg.MaxQueuedJobs) ++ } ++ if cfg.MaxQueuedPayloadMB > 0 { ++ limit := int64(cfg.MaxQueuedPayloadMB) << 20 ++ if pendingPayloadBytes+int64(len(b)) > limit { ++ return nil, fmt.Errorf("orchestrator queued payload budget exceeded: %d + %d > %d bytes", pendingPayloadBytes, len(b), limit) + } + } + if key := strings.TrimSpace(spec.IdempotencyKey); key != "" { +@@ -603,6 +612,13 @@ func (s *Store) OrchestratorStatus() OrchestratorStatus { + if j == nil { + continue + } ++ payloadBytes := int64(len(j.Payload) + len(j.Result)) ++ switch j.Status { ++ case "queued", "claimed", "retry_wait", "blocked", "apply_wait": ++ out.PendingPayloadBytes += payloadBytes ++ case "done", "failed", "canceled": ++ out.TerminalPayloadBytes += payloadBytes ++ } + out.JobsByStatus[j.Status]++ + out.JobsByType[j.Type]++ + resource := j.ResourceClass +@@ -701,6 +717,15 @@ func (s *Store) FinishMasterApply(id string, applyErr error) (*core.Job, error) + j.ApplyError = "" + j.ApplyNextAttemptAt = time.Time{} + j.FinishedAt = now ++ // vector.relink payloads contain the target plus a bounded set of full ++ // candidate vectors. Once the authoritative master apply succeeded these ++ // blobs have no retry value and retaining thousands of them can consume ++ // gigabytes during a large graph backfill. Keep the durable audit metadata ++ // (type/idempotency/status/timestamps) but release the transient vectors. ++ if j.Type == "vector.relink" { ++ j.Payload = nil ++ j.Result = nil ++ } + } else { + j.ApplyError = strings.TrimSpace(applyErr.Error()) + maxAttempts := j.MaxApplyAttempts +@@ -732,6 +757,41 @@ func (s *Store) FinishMasterApply(id string, applyErr error) (*core.Job, error) + return &cp, s.commitLocked("job.upsert", cp) + } + ++// compactCompletedRelinkJobsLocked removes transient vector blobs from jobs ++// that already reached their durable terminal state. It is also used on boot to ++// migrate v1.6.0 checkpoints that may contain thousands of completed backfill ++// payloads. Caller must hold s.mu. ++func (s *Store) compactCompletedRelinkJobsLocked() int { ++ n := 0 ++ for _, j := range s.state.Jobs { ++ if j == nil || j.Type != "vector.relink" || j.Status != "done" { ++ continue ++ } ++ if len(j.Payload) == 0 && len(j.Result) == 0 { ++ continue ++ } ++ j.Payload = nil ++ j.Result = nil ++ n++ ++ } ++ return n ++} ++ ++func (s *Store) CompactCompletedRelinkJobs() (int, error) { ++ s.mu.Lock() ++ defer s.mu.Unlock() ++ n := s.compactCompletedRelinkJobsLocked() ++ if n == 0 { ++ return 0, nil ++ } ++ // One checkpoint is substantially cheaper than one WAL event per historical ++ // job and atomically rewrites state.json without the obsolete vector blobs. ++ if err := s.checkpointLocked(); err != nil { ++ return 0, err ++ } ++ return n, nil ++} ++ + func (s *Store) CancelJob(id, reason string) error { + s.mu.Lock() + defer s.mu.Unlock() +diff --git a/platform/neuroforge/internal/store/orchestrator_test.go b/platform/neuroforge/internal/store/orchestrator_test.go +index 16ca3a8..202bf8a 100644 +--- a/platform/neuroforge/internal/store/orchestrator_test.go ++++ b/platform/neuroforge/internal/store/orchestrator_test.go +@@ -292,3 +292,52 @@ func TestApplyWaitJobSurvivesRestart(t *testing.T) { + t.Fatalf("recovered master apply queue=%+v", pending) + } + } ++ ++func TestOrchestratorQueuedPayloadBudget(t *testing.T) { ++ s, err := New(t.TempDir()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer s.Close() ++ s.mu.Lock() ++ s.state.Config.Worker.MaxQueuedPayloadMB = 1 ++ s.mu.Unlock() ++ blob := strings.Repeat("x", 700<<10) ++ if _, err := s.EnqueueJobSpec(JobSpec{Type: "large", Payload: map[string]any{"blob": blob}, ResourceClass: "cpu"}); err != nil { ++ t.Fatal(err) ++ } ++ if _, err := s.EnqueueJobSpec(JobSpec{Type: "large2", Payload: map[string]any{"blob": blob}, ResourceClass: "cpu"}); err == nil || !strings.Contains(err.Error(), "payload budget") { ++ t.Fatalf("expected payload budget rejection, err=%v", err) ++ } ++} ++ ++func TestCompletedRelinkDropsTransientVectorBlobs(t *testing.T) { ++ s, err := New(t.TempDir()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer s.Close() ++ j, err := s.EnqueueJobSpec(JobSpec{Type: "vector.relink", Payload: map[string]any{"target": []float32{1, 2, 3}}, ResourceClass: "cpu", RequiredCapabilities: []string{"cpu", "vector.relink"}, RequiresMasterApply: true}) ++ if err != nil { ++ t.Fatal(err) ++ } ++ w := WorkerHeartbeat{ID: "cpu", ResourceClass: "cpu", Capabilities: []string{"cpu", "vector.relink"}, MaxConcurrency: 1} ++ claimed, err := s.ClaimJobForWorker(w, time.Minute) ++ if err != nil || claimed == nil || claimed.ID != j.ID { ++ t.Fatalf("claim=%+v err=%v", claimed, err) ++ } ++ waiting, err := s.CompleteJobLease(j.ID, w.ID, claimed.LeaseToken, json.RawMessage(`{"target_id":"x","neighbors":[]}`), "") ++ if err != nil { ++ t.Fatal(err) ++ } ++ if waiting.Status != "apply_wait" { ++ t.Fatalf("status=%s", waiting.Status) ++ } ++ done, err := s.FinishMasterApply(j.ID, nil) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if done.Status != "done" || len(done.Payload) != 0 || len(done.Result) != 0 { ++ t.Fatalf("done job retained transient blobs: %+v", done) ++ } ++} +diff --git a/platform/neuroforge/internal/store/recovery_hardening_test.go b/platform/neuroforge/internal/store/recovery_hardening_test.go +new file mode 100644 +index 0000000..59b69f0 +--- /dev/null ++++ b/platform/neuroforge/internal/store/recovery_hardening_test.go +@@ -0,0 +1,120 @@ ++package store ++ ++import ( ++ "bufio" ++ "encoding/json" ++ "os" ++ "path/filepath" ++ "strings" ++ "testing" ++ "time" ++ ++ "neuroforge/internal/core" ++) ++ ++func TestNewFailsClosedOnCorruptAuthoritativeState(t *testing.T) { ++ dir := t.TempDir() ++ if err := os.WriteFile(filepath.Join(dir, v161JobCompactionMarker), []byte("compacted=0\n"), 0600); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(filepath.Join(dir, "state.json"), []byte(`{"config":`), 0600); err != nil { ++ t.Fatal(err) ++ } ++ if _, err := New(dir); err == nil || !strings.Contains(err.Error(), "load authoritative state checkpoint") { ++ t.Fatalf("New error=%v, want authoritative state corruption failure", err) ++ } ++} ++ ++func TestNewFailsClosedOnCorruptAuthoritativeSecrets(t *testing.T) { ++ dir := t.TempDir() ++ st := core.PersistedState{ ++ Config: core.DefaultConfig(), ++ Memories: map[string]*core.Memory{}, ++ Synapses: map[string]*core.Synapse{}, ++ Jobs: map[string]*core.Job{}, ++ Goals: map[string]*core.Goal{}, ++ Sources: map[string]*core.KnowledgeSource{}, ++ ResearchRuns: map[string]*core.ResearchRun{}, ++ } ++ if err := writeAtomic(filepath.Join(dir, "state.json"), 0600, &st); err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(filepath.Join(dir, "secrets.json"), []byte(`{"admin_token":"unterminated}`), 0600); err != nil { ++ t.Fatal(err) ++ } ++ if _, err := New(dir); err == nil || !strings.Contains(err.Error(), "load authoritative secrets") { ++ t.Fatalf("New error=%v, want authoritative secrets corruption failure", err) ++ } ++} ++ ++func TestLoadJSONRejectsTrailingJSONValue(t *testing.T) { ++ dir := t.TempDir() ++ path := filepath.Join(dir, "state.json") ++ if err := os.WriteFile(path, []byte(`{"revision":1}{"revision":2}`), 0600); err != nil { ++ t.Fatal(err) ++ } ++ s := &Store{} ++ var st core.PersistedState ++ if err := s.loadJSON(path, &st); err == nil || !strings.Contains(err.Error(), "unexpected trailing JSON token") { ++ t.Fatalf("loadJSON error=%v, want trailing JSON rejection", err) ++ } ++} ++ ++func TestReplayWALCompactsCompletedRelinkPayloadImmediately(t *testing.T) { ++ dir := t.TempDir() ++ walDir := filepath.Join(dir, "wal") ++ if err := os.MkdirAll(walDir, 0700); err != nil { ++ t.Fatal(err) ++ } ++ job := core.Job{ ++ ID: "job_done_relink", ++ Type: "vector.relink", ++ Status: "done", ++ Payload: json.RawMessage(`{"target":{"vector":[1,2,3]},"candidates":[{"vector":[4,5,6]}]}`), ++ Result: json.RawMessage(`{"edges":[{"a":"a","b":"b","weight":0.9}]}`), ++ CreatedAt: time.Now().UTC(), ++ UpdatedAt: time.Now().UTC(), ++ } ++ data, err := json.Marshal(job) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ev := walEvent{Revision: 1, Time: time.Now().UTC(), Type: "job.upsert", Data: data} ++ f, err := os.OpenFile(filepath.Join(walDir, "wal-active.jsonl"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) ++ if err != nil { ++ t.Fatal(err) ++ } ++ bw := bufio.NewWriter(f) ++ if err := json.NewEncoder(bw).Encode(ev); err != nil { ++ t.Fatal(err) ++ } ++ if err := bw.Flush(); err != nil { ++ t.Fatal(err) ++ } ++ if err := f.Close(); err != nil { ++ t.Fatal(err) ++ } ++ ++ s, err := New(dir) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer s.Close() ++ got, ok := s.Job(job.ID) ++ if !ok { ++ t.Fatal("replayed job missing") ++ } ++ if len(got.Payload) != 0 || len(got.Result) != 0 { ++ t.Fatalf("replayed completed relink retained vector blobs: payload=%d result=%d", len(got.Payload), len(got.Result)) ++ } ++} ++ ++func TestFreshDirectoryDoesNotConsumeV161CompactionMarker(t *testing.T) { ++ dir := t.TempDir() ++ if n, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil || n != 0 { ++ t.Fatalf("compact fresh dir = %d, %v", n, err) ++ } ++ if _, err := os.Stat(filepath.Join(dir, v161JobCompactionMarker)); !os.IsNotExist(err) { ++ t.Fatalf("fresh directory unexpectedly created migration marker: %v", err) ++ } ++} +diff --git a/platform/neuroforge/internal/store/startup_compact.go b/platform/neuroforge/internal/store/startup_compact.go +new file mode 100644 +index 0000000..ea78fd8 +--- /dev/null ++++ b/platform/neuroforge/internal/store/startup_compact.go +@@ -0,0 +1,277 @@ ++package store ++ ++import ( ++ "bufio" ++ "encoding/json" ++ "errors" ++ "fmt" ++ "io" ++ "os" ++ "path/filepath" ++ ++ "neuroforge/internal/core" ++) ++ ++const v161JobCompactionMarker = ".migration-v1.6.1-terminal-relink-compaction" ++ ++// compactLegacyTerminalRelinkCheckpoint is a one-time, streaming migration for ++// v1.6.0 checkpoints. That release retained full target/candidate vectors in ++// completed vector.relink jobs. A large graph backfill can therefore make ++// state.json hundreds of MB or larger and cause OOM during the next startup. ++// ++// The migration deliberately runs before state.json is unmarshaled. It rewrites ++// JSON token-by-token and only materializes one Job at a time, so peak memory is ++// bounded by the largest single job instead of the whole checkpoint. ++func compactLegacyTerminalRelinkCheckpoint(dir string) (int, error) { ++ marker := filepath.Join(dir, v161JobCompactionMarker) ++ if _, err := os.Stat(marker); err == nil { ++ return 0, nil ++ } ++ path := filepath.Join(dir, "state.json") ++ in, err := os.Open(path) ++ if err != nil { ++ if errors.Is(err, os.ErrNotExist) { ++ // Fresh data directory. Do not create the migration marker yet: an ++ // operator may restore a v1.6.0 checkpoint into this directory before ++ // the next boot, and that restored checkpoint must still be compacted. ++ return 0, nil ++ } ++ return 0, err ++ } ++ defer in.Close() ++ ++ tmp := path + ".v161-compact.tmp" ++ out, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) ++ if err != nil { ++ return 0, err ++ } ++ ok := false ++ defer func() { ++ _ = out.Close() ++ if !ok { ++ _ = os.Remove(tmp) ++ } ++ }() ++ ++ dec := json.NewDecoder(bufio.NewReaderSize(in, 1<<20)) ++ dec.UseNumber() ++ bw := bufio.NewWriterSize(out, 1<<20) ++ compacted, err := rewriteCheckpointObject(dec, bw) ++ if err != nil { ++ return 0, err ++ } ++ if err := bw.Flush(); err != nil { ++ return 0, err ++ } ++ if err := out.Sync(); err != nil { ++ return 0, err ++ } ++ if err := out.Close(); err != nil { ++ return 0, err ++ } ++ if compacted > 0 { ++ if err := os.Rename(tmp, path); err != nil { ++ return 0, err ++ } ++ } else { ++ _ = os.Remove(tmp) ++ } ++ if err := os.WriteFile(marker, []byte(fmt.Sprintf("compacted=%d\n", compacted)), 0600); err != nil { ++ return 0, err ++ } ++ ok = true ++ return compacted, nil ++} ++ ++func rewriteCheckpointObject(dec *json.Decoder, w *bufio.Writer) (int, error) { ++ tok, err := dec.Token() ++ if err != nil { ++ return 0, err ++ } ++ if d, ok := tok.(json.Delim); !ok || d != '{' { ++ return 0, errors.New("state checkpoint must be a JSON object") ++ } ++ if err := w.WriteByte('{'); err != nil { ++ return 0, err ++ } ++ first := true ++ compacted := 0 ++ for dec.More() { ++ kt, err := dec.Token() ++ if err != nil { ++ return 0, err ++ } ++ key, ok := kt.(string) ++ if !ok { ++ return 0, errors.New("state checkpoint object key is not a string") ++ } ++ if !first { ++ if err := w.WriteByte(','); err != nil { ++ return 0, err ++ } ++ } ++ first = false ++ kb, _ := json.Marshal(key) ++ if _, err := w.Write(kb); err != nil { ++ return 0, err ++ } ++ if err := w.WriteByte(':'); err != nil { ++ return 0, err ++ } ++ if key == "jobs" { ++ n, err := rewriteJobsObject(dec, w) ++ if err != nil { ++ return 0, err ++ } ++ compacted += n ++ continue ++ } ++ if err := copyJSONValue(dec, w); err != nil { ++ return 0, err ++ } ++ } ++ if _, err := dec.Token(); err != nil { // closing } ++ return 0, err ++ } ++ if err := w.WriteByte('}'); err != nil { ++ return 0, err ++ } ++ if tok, err := dec.Token(); err != io.EOF { ++ if err == nil { ++ return 0, fmt.Errorf("unexpected trailing JSON token %v", tok) ++ } ++ return 0, err ++ } ++ return compacted, nil ++} ++ ++func rewriteJobsObject(dec *json.Decoder, w *bufio.Writer) (int, error) { ++ tok, err := dec.Token() ++ if err != nil { ++ return 0, err ++ } ++ if tok == nil { ++ _, err = w.WriteString("null") ++ return 0, err ++ } ++ if d, ok := tok.(json.Delim); !ok || d != '{' { ++ return 0, errors.New("jobs must be a JSON object") ++ } ++ if err := w.WriteByte('{'); err != nil { ++ return 0, err ++ } ++ first := true ++ compacted := 0 ++ for dec.More() { ++ kt, err := dec.Token() ++ if err != nil { ++ return 0, err ++ } ++ key := kt.(string) ++ var job core.Job ++ if err := dec.Decode(&job); err != nil { ++ return 0, err ++ } ++ if job.Type == "vector.relink" && job.Status == "done" { ++ if len(job.Payload) > 0 || len(job.Result) > 0 { ++ compacted++ ++ } ++ job.Payload = nil ++ job.Result = nil ++ } ++ if !first { ++ if err := w.WriteByte(','); err != nil { ++ return 0, err ++ } ++ } ++ first = false ++ kb, _ := json.Marshal(key) ++ jb, err := json.Marshal(job) ++ if err != nil { ++ return 0, err ++ } ++ if _, err := w.Write(kb); err != nil { ++ return 0, err ++ } ++ if err := w.WriteByte(':'); err != nil { ++ return 0, err ++ } ++ if _, err := w.Write(jb); err != nil { ++ return 0, err ++ } ++ } ++ if _, err := dec.Token(); err != nil { ++ return 0, err ++ } ++ return compacted, w.WriteByte('}') ++} ++ ++func copyJSONValue(dec *json.Decoder, w *bufio.Writer) error { ++ tok, err := dec.Token() ++ if err != nil { ++ return err ++ } ++ if d, ok := tok.(json.Delim); ok { ++ switch d { ++ case '{': ++ if err := w.WriteByte('{'); err != nil { ++ return err ++ } ++ first := true ++ for dec.More() { ++ kt, err := dec.Token() ++ if err != nil { ++ return err ++ } ++ if !first { ++ if err := w.WriteByte(','); err != nil { ++ return err ++ } ++ } ++ first = false ++ kb, _ := json.Marshal(kt.(string)) ++ if _, err := w.Write(kb); err != nil { ++ return err ++ } ++ if err := w.WriteByte(':'); err != nil { ++ return err ++ } ++ if err := copyJSONValue(dec, w); err != nil { ++ return err ++ } ++ } ++ if _, err := dec.Token(); err != nil { ++ return err ++ } ++ return w.WriteByte('}') ++ case '[': ++ if err := w.WriteByte('['); err != nil { ++ return err ++ } ++ first := true ++ for dec.More() { ++ if !first { ++ if err := w.WriteByte(','); err != nil { ++ return err ++ } ++ } ++ first = false ++ if err := copyJSONValue(dec, w); err != nil { ++ return err ++ } ++ } ++ if _, err := dec.Token(); err != nil { ++ return err ++ } ++ return w.WriteByte(']') ++ default: ++ return fmt.Errorf("unexpected JSON delimiter %q", d) ++ } ++ } ++ b, err := json.Marshal(tok) ++ if err != nil { ++ return err ++ } ++ _, err = w.Write(b) ++ return err ++} +diff --git a/platform/neuroforge/internal/store/startup_compact_test.go b/platform/neuroforge/internal/store/startup_compact_test.go +new file mode 100644 +index 0000000..b756846 +--- /dev/null ++++ b/platform/neuroforge/internal/store/startup_compact_test.go +@@ -0,0 +1,56 @@ ++package store ++ ++import ( ++ "encoding/json" ++ "os" ++ "path/filepath" ++ "testing" ++ ++ "neuroforge/internal/core" ++) ++ ++func TestCompactLegacyTerminalRelinkCheckpoint(t *testing.T) { ++ dir := t.TempDir() ++ st := core.PersistedState{Config: core.DefaultConfig(), Jobs: map[string]*core.Job{}, Synapses: map[string]*core.Synapse{}, Goals: map[string]*core.Goal{}} ++ st.Jobs["done"] = &core.Job{ID: "done", Type: "vector.relink", Status: "done", Payload: json.RawMessage(`{"target":[1,2,3]}`), Result: json.RawMessage(`{"ok":true}`)} ++ st.Jobs["queued"] = &core.Job{ID: "queued", Type: "vector.relink", Status: "queued", Payload: json.RawMessage(`{"target":[4,5,6]}`)} ++ st.Jobs["other"] = &core.Job{ID: "other", Type: "model.chat", Status: "done", Payload: json.RawMessage(`{"prompt":"keep"}`), Result: json.RawMessage(`{"text":"keep"}`)} ++ b, err := json.Marshal(st) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if err := os.WriteFile(filepath.Join(dir, "state.json"), b, 0600); err != nil { ++ t.Fatal(err) ++ } ++ ++ n, err := compactLegacyTerminalRelinkCheckpoint(dir) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if n != 1 { ++ t.Fatalf("compacted=%d want 1", n) ++ } ++ ++ var got core.PersistedState ++ bb, err := os.ReadFile(filepath.Join(dir, "state.json")) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if err := json.Unmarshal(bb, &got); err != nil { ++ t.Fatal(err) ++ } ++ cleared := func(b json.RawMessage) bool { return len(b) == 0 || string(b) == "null" } ++ if !cleared(got.Jobs["done"].Payload) || !cleared(got.Jobs["done"].Result) { ++ t.Fatalf("done relink blobs not cleared: payload=%q result=%q", got.Jobs["done"].Payload, got.Jobs["done"].Result) ++ } ++ if len(got.Jobs["queued"].Payload) == 0 { ++ t.Fatalf("queued relink payload must be retained") ++ } ++ if len(got.Jobs["other"].Payload) == 0 || len(got.Jobs["other"].Result) == 0 { ++ t.Fatalf("unrelated job blobs must be retained") ++ } ++ ++ if n2, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil || n2 != 0 { ++ t.Fatalf("second migration = %d, %v", n2, err) ++ } ++} +diff --git a/platform/neuroforge/internal/store/store.go b/platform/neuroforge/internal/store/store.go +index 8445572..0beccc6 100644 +--- a/platform/neuroforge/internal/store/store.go ++++ b/platform/neuroforge/internal/store/store.go +@@ -1,11 +1,13 @@ + package store + + import ( ++ "bufio" + "crypto/rand" + "encoding/hex" + "encoding/json" + "errors" + "fmt" ++ "io" + "math" + "net/url" + "os" +@@ -49,7 +51,19 @@ type Store struct { + synapseAdj map[string]map[string]*core.Synapse + } + ++type OpenProgressFunc func(phase string) ++ + func New(dir string) (*Store, error) { ++ return NewWithProgress(dir, nil) ++} ++ ++func NewWithProgress(dir string, report OpenProgressFunc) (*Store, error) { ++ progress := func(phase string) { ++ if report != nil { ++ report(phase) ++ } ++ } ++ progress("store.prepare") + if dir == "" { + dir = "./data" + } +@@ -58,8 +72,18 @@ func New(dir string) (*Store, error) { + } + s := &Store{dir: dir, indexes: map[int]*vector.HNSW{}, diskIndexes: map[int]*vector.PQIndex{}, provenanceSourceIDs: map[string]map[string]struct{}{}, workers: map[string]core.WorkerState{}, synapseAdj: map[string]map[string]*core.Synapse{}} + s.state = core.PersistedState{Config: core.DefaultConfig(), Memories: map[string]*core.Memory{}, Synapses: map[string]*core.Synapse{}, Jobs: map[string]*core.Job{}, Goals: map[string]*core.Goal{}, Sources: map[string]*core.KnowledgeSource{}, ResearchRuns: map[string]*core.ResearchRun{}} +- _ = s.loadJSON(filepath.Join(dir, "state.json"), &s.state) +- _ = s.loadJSON(filepath.Join(dir, "secrets.json"), &s.secrets) ++ progress("checkpoint.precompact") ++ if _, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil { ++ return nil, fmt.Errorf("compact legacy terminal relink jobs: %w", err) ++ } ++ progress("checkpoint.load") ++ if err := s.loadJSON(filepath.Join(dir, "state.json"), &s.state); err != nil && !errors.Is(err, os.ErrNotExist) { ++ return nil, fmt.Errorf("load authoritative state checkpoint: %w", err) ++ } ++ progress("secrets.load") ++ if err := s.loadJSON(filepath.Join(dir, "secrets.json"), &s.secrets); err != nil && !errors.Is(err, os.ErrNotExist) { ++ return nil, fmt.Errorf("load authoritative secrets: %w", err) ++ } + if s.state.Memories == nil { + s.state.Memories = map[string]*core.Memory{} + } +@@ -84,6 +108,7 @@ func New(dir string) (*Store, error) { + // v0.6 binary vector sidecar: rebuildable acceleration data used by the + // disk ANN builder. Corruption must never prevent the authoritative memory + // store from opening; move a bad cache aside and recreate it empty. ++ progress("vector-journal.open") + vjPath := filepath.Join(dir, "vector-journal.nfv") + vj, vjErr := openVectorJournal(vjPath, vectorJournalOptionsFromConfig(s.state.Config)) + if vjErr != nil { +@@ -95,6 +120,7 @@ func New(dir string) (*Store, error) { + } + + if s.state.Config.Storage.Segments.Enabled { ++ progress("memory-segments.scan") + seg, err := openSegmentStore(filepath.Join(dir, "memory-segments"), s.state.Config.Storage.Segments.MaxSegmentBytes, s.state.Config.Storage.Segments.MmapSealed) + if err != nil { + return nil, fmt.Errorf("open memory segments: %w", err) +@@ -110,9 +136,13 @@ func New(dir string) (*Store, error) { + s.state.Memories = meta + } + } ++ progress("wal.replay") + if err := s.replayWAL(); err != nil { + return nil, err + } ++ progress("jobs.compact") ++ _ = s.compactCompletedRelinkJobsLocked() ++ progress("graph.restore") + s.rebuildSynapseAdjLocked() + applyNewDefaults(&s.state.Config) + if s.state.Cluster.Term < s.state.Config.Cluster.Term { +@@ -198,19 +228,26 @@ func New(dir string) (*Store, error) { + if s.secrets.ClusterToken == "" { + s.secrets.ClusterToken = randomID(24) + } ++ progress("disk-ann.load") + _ = s.loadDiskANNLocked() ++ progress("hnsw.snapshot.load") + if !s.loadIndexSnapshotLocked() { ++ progress("hnsw.rebuild") + s.rebuildIndexesLocked() + } ++ progress("secrets.persist") + if err := s.persistSecretsLocked(); err != nil { + return nil, err + } ++ progress("checkpoint.write") + if err := s.checkpointLocked(); err != nil { + return nil, err + } + if s.state.Config.Storage.Tiering.Enabled && s.segments != nil { ++ progress("tiering.initialize") + s.tierMemoryBodiesLocked(time.Now().UTC()) + } ++ progress("store.ready") + return s, nil + } + +@@ -524,6 +561,9 @@ func applyNewDefaults(c *core.Config) { + if c.Worker.MaxQueuedJobs == 0 { + c.Worker.MaxQueuedJobs = d.Worker.MaxQueuedJobs + } ++ if c.Worker.MaxQueuedPayloadMB == 0 { ++ c.Worker.MaxQueuedPayloadMB = d.Worker.MaxQueuedPayloadMB ++ } + if c.Worker.MasterApplyMaxAttempts == 0 { + c.Worker.MasterApplyMaxAttempts = d.Worker.MasterApplyMaxAttempts + } +@@ -613,23 +653,80 @@ func inferMemoryType(kind string) string { + } + + func (s *Store) loadJSON(path string, v any) error { +- b, err := os.ReadFile(path) ++ f, err := os.Open(path) + if err != nil { + return err + } +- return json.Unmarshal(b, v) ++ defer f.Close() ++ ++ // Decode directly from the file instead of ReadFile+Unmarshal. Large ++ // checkpoints can contain millions of graph edges/jobs; keeping a second ++ // raw []byte copy of state.json during boot needlessly doubles peak memory. ++ dec := json.NewDecoder(bufio.NewReaderSize(f, 1<<20)) ++ if err := dec.Decode(v); err != nil { ++ return fmt.Errorf("decode %s: %w", filepath.Base(path), err) ++ } ++ // Reject a second JSON value/trailing non-whitespace. Silently accepting a ++ // partially corrupt authoritative checkpoint can create split-brain state. ++ if tok, err := dec.Token(); err != io.EOF { ++ if err == nil { ++ return fmt.Errorf("decode %s: unexpected trailing JSON token %v", filepath.Base(path), tok) ++ } ++ return fmt.Errorf("decode %s trailing data: %w", filepath.Base(path), err) ++ } ++ return nil + } + +-func writeAtomic(path string, perm os.FileMode, v any) error { +- b, err := json.MarshalIndent(v, "", " ") ++func writeAtomic(path string, perm os.FileMode, v any) (retErr error) { ++ // Stream JSON directly into the temporary file instead of MarshalIndent+ ++ // WriteFile. state.json and index manifests can be large; allocating the ++ // complete encoded checkpoint as another in-memory byte slice is avoidable. ++ tmp := path + ".tmp" ++ _ = os.Remove(tmp) ++ f, err := os.OpenFile(tmp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm) + if err != nil { + return err + } +- tmp := path + ".tmp" +- if err := os.WriteFile(tmp, b, perm); err != nil { ++ closed := false ++ defer func() { ++ if !closed { ++ _ = f.Close() ++ } ++ if retErr != nil { ++ _ = os.Remove(tmp) ++ } ++ }() ++ ++ bw := bufio.NewWriterSize(f, 1<<20) ++ enc := json.NewEncoder(bw) ++ enc.SetIndent("", " ") ++ if err := enc.Encode(v); err != nil { ++ return err ++ } ++ if err := bw.Flush(); err != nil { ++ return err ++ } ++ if err := f.Sync(); err != nil { ++ return err ++ } ++ if err := f.Close(); err != nil { ++ return err ++ } ++ closed = true ++ if err := os.Rename(tmp, path); err != nil { ++ return err ++ } ++ // Persist the directory entry as well. This matters for secrets/state after ++ // host power loss and is cheap compared with the checkpoint itself. ++ d, err := os.Open(filepath.Dir(path)) ++ if err != nil { ++ return err ++ } ++ if err := d.Sync(); err != nil { ++ _ = d.Close() + return err + } +- return os.Rename(tmp, path) ++ return d.Close() + } + + func (s *Store) persistLocked() error { +@@ -1609,7 +1706,7 @@ func (s *Store) validateConfigLocked(c core.Config) error { + if c.Worker.LeaseSeconds < 10 || c.Worker.LeaseSeconds > 3600 || c.Worker.HeartbeatSeconds < 2 || c.Worker.HeartbeatSeconds >= c.Worker.LeaseSeconds || c.Worker.StaleAfterSeconds < c.Worker.HeartbeatSeconds || c.Worker.StaleAfterSeconds > 7200 { + return errors.New("invalid worker lease/heartbeat/stale timing") + } +- if c.Worker.DefaultMaxAttempts < 1 || c.Worker.DefaultMaxAttempts > 20 || c.Worker.RetryBackoffSeconds < 1 || c.Worker.RetryBackoffSeconds > 3600 || c.Worker.MaxQueuedJobs < 16 || c.Worker.MaxQueuedJobs > 1000000 || c.Worker.MasterApplyMaxAttempts < 1 || c.Worker.MasterApplyMaxAttempts > 20 || c.Worker.MasterApplyBackoffSeconds < 1 || c.Worker.MasterApplyBackoffSeconds > 3600 || c.Worker.JobRetentionHours < 1 || c.Worker.JobRetentionHours > 8760 || c.Worker.MaxTerminalJobs < 100 || c.Worker.MaxTerminalJobs > 1000000 { ++ if c.Worker.DefaultMaxAttempts < 1 || c.Worker.DefaultMaxAttempts > 20 || c.Worker.RetryBackoffSeconds < 1 || c.Worker.RetryBackoffSeconds > 3600 || c.Worker.MaxQueuedJobs < 16 || c.Worker.MaxQueuedJobs > 1000000 || c.Worker.MaxQueuedPayloadMB < 16 || c.Worker.MaxQueuedPayloadMB > 65536 || c.Worker.MasterApplyMaxAttempts < 1 || c.Worker.MasterApplyMaxAttempts > 20 || c.Worker.MasterApplyBackoffSeconds < 1 || c.Worker.MasterApplyBackoffSeconds > 3600 || c.Worker.JobRetentionHours < 1 || c.Worker.JobRetentionHours > 8760 || c.Worker.MaxTerminalJobs < 100 || c.Worker.MaxTerminalJobs > 1000000 { + return errors.New("invalid worker retry/queue/retention configuration") + } + if c.Worker.GraphBackfillIntervalS < 2 || c.Worker.GraphBackfillIntervalS > 3600 || c.Worker.GraphBackfillBatchSize < 1 || c.Worker.GraphBackfillBatchSize > 4096 || c.Worker.GraphBackfillMaxQueued < 1 || c.Worker.GraphBackfillMaxQueued > c.Worker.MaxQueuedJobs || c.Worker.GraphBackfillMinDegree < 1 || c.Worker.GraphBackfillMinDegree > 100 || c.Worker.GraphCandidateMultiplier < 2 || c.Worker.GraphCandidateMultiplier > 64 || c.Worker.GraphRetryAfterMinutes < 1 || c.Worker.GraphRetryAfterMinutes > 43200 { +diff --git a/platform/neuroforge/internal/store/wal.go b/platform/neuroforge/internal/store/wal.go +index afab3bc..4eac292 100644 +--- a/platform/neuroforge/internal/store/wal.go ++++ b/platform/neuroforge/internal/store/wal.go +@@ -232,6 +232,14 @@ func (s *Store) applyWALEvent(ev walEvent) error { + if err := json.Unmarshal(ev.Data, &x); err != nil { + return err + } ++ // v1.6.0 could leave a large WAL containing completed vector.relink ++ // jobs with full target/candidate vectors. Compact each terminal event as ++ // it is replayed so recovery memory remains bounded by one WAL record ++ // instead of accumulating every historical vector payload in state. ++ if x.Type == "vector.relink" && x.Status == "done" { ++ x.Payload = nil ++ x.Result = nil ++ } + s.state.Jobs[x.ID] = &x + case "job.delete": + var ids []string +@@ -332,14 +340,20 @@ func (s *Store) checkpointLocked() error { + // Keep state.json O(non-memory-state) instead of O(memory-count). + checkpoint.Memories = nil + } +- if err := writeAtomic(filepath.Join(s.dir, "state.json"), 0600, &checkpoint); err != nil { +- return err +- } ++ // Persist acceleration state before the authoritative non-memory checkpoint. ++ // If the process dies after the index snapshot but before state.json, the WAL ++ // remains intact; boot replays it to the same revision and can immediately use ++ // the already-written index. The previous order could advance state.json first, ++ // then die during a large HNSW snapshot and force a full synchronous rebuild on ++ // every restart. + if s.state.Config.Storage.IndexSnapshot && s.state.Config.Brain.Index.Enabled { + if err := s.writeIndexSnapshotLocked(); err != nil { + return err + } + } ++ if err := writeAtomic(filepath.Join(s.dir, "state.json"), 0600, &checkpoint); err != nil { ++ return err ++ } + // The checkpoint and (when enabled) memory segments now cover every WAL + // event through state.Revision. Prune those already-checkpointed log files + // only after all checkpoint artifacts succeeded, otherwise long bulk +diff --git a/platform/neuroforge/internal/vector/hnsw.go b/platform/neuroforge/internal/vector/hnsw.go +index a8ad362..8def7a1 100644 +--- a/platform/neuroforge/internal/vector/hnsw.go ++++ b/platform/neuroforge/internal/vector/hnsw.go +@@ -679,6 +679,50 @@ type HNSWShadow struct { + Nodes map[string][32]byte + } + ++func (h *HNSW) nodeFingerprintLocked(n *hnswNode) [32]byte { ++ hash := sha256.New() ++ writeHashString(hash, n.ID) ++ writeHashU32(hash, uint32(n.Level)) ++ writeHashU32(hash, uint32(len(n.Vector))) ++ var b [4]byte ++ for _, x := range n.Vector { ++ binary.LittleEndian.PutUint32(b[:], math.Float32bits(x)) ++ _, _ = hash.Write(b[:]) ++ } ++ for level := 0; level <= n.Level; level++ { ++ writeHashU32(hash, uint32(level)) ++ edges := n.Neighbors[level] ++ writeHashU32(hash, uint32(len(edges))) ++ for _, edge := range edges { ++ idx := int(edge.idx) ++ if idx >= 0 && idx < len(h.nodes) { ++ writeHashString(hash, h.nodes[idx].ID) ++ } ++ } ++ } ++ var sum [32]byte ++ copy(sum[:], hash.Sum(nil)) ++ return sum ++} ++ ++func (h *HNSW) snapshotNodeLocked(n *hnswNode) HNSWSnapshotNode { ++ cn := HNSWSnapshotNode{ID: n.ID, Vector: append([]float32(nil), n.Vector...), Level: n.Level, Neighbors: map[int][]string{}} ++ for level, ids := range n.Neighbors { ++ if len(ids) == 0 { ++ continue ++ } ++ refs := make([]string, 0, len(ids)) ++ for _, edge := range ids { ++ idx := int(edge.idx) ++ if idx >= 0 && idx < len(h.nodes) { ++ refs = append(refs, h.nodes[idx].ID) ++ } ++ } ++ cn.Neighbors[level] = refs ++ } ++ return cn ++} ++ + func (h *HNSW) Shadow() HNSWShadow { + h.mu.RLock() + defer h.mu.RUnlock() +@@ -688,31 +732,39 @@ func (h *HNSW) Shadow() HNSWShadow { + } + out := HNSWShadow{Config: h.cfg, EntryID: entryID, MaxLevel: h.maxLevel, Nodes: make(map[string][32]byte, len(h.nodes))} + for _, n := range h.nodes { +- hash := sha256.New() +- writeHashString(hash, n.ID) +- writeHashU32(hash, uint32(n.Level)) +- writeHashU32(hash, uint32(len(n.Vector))) +- var b [4]byte +- for _, x := range n.Vector { +- binary.LittleEndian.PutUint32(b[:], math.Float32bits(x)) +- _, _ = hash.Write(b[:]) ++ out.Nodes[n.ID] = h.nodeFingerprintLocked(n) ++ } ++ return out ++} ++ ++// Delta returns only nodes whose vector/graph representation changed since prev, ++// plus a compact hash shadow for the current graph. Unlike Snapshot(), this does ++// not deep-copy every vector on each checkpoint, which keeps bulk-ingest memory ++// bounded as the HNSW grows. ++func (h *HNSW) Delta(prev HNSWShadow) (HNSWShadow, []HNSWSnapshotNode, []string) { ++ h.mu.RLock() ++ defer h.mu.RUnlock() ++ entryID := "" ++ if h.entry >= 0 && h.entry < len(h.nodes) { ++ entryID = h.nodes[h.entry].ID ++ } ++ cur := HNSWShadow{Config: h.cfg, EntryID: entryID, MaxLevel: h.maxLevel, Nodes: make(map[string][32]byte, len(h.nodes))} ++ upserts := make([]HNSWSnapshotNode, 0) ++ for _, n := range h.nodes { ++ fp := h.nodeFingerprintLocked(n) ++ cur.Nodes[n.ID] = fp ++ if old, ok := prev.Nodes[n.ID]; !ok || old != fp { ++ upserts = append(upserts, h.snapshotNodeLocked(n)) + } +- for level := 0; level <= n.Level; level++ { +- writeHashU32(hash, uint32(level)) +- edges := n.Neighbors[level] +- writeHashU32(hash, uint32(len(edges))) +- for _, edge := range edges { +- idx := int(edge.idx) +- if idx >= 0 && idx < len(h.nodes) { +- writeHashString(hash, h.nodes[idx].ID) +- } +- } ++ } ++ deletes := make([]string, 0) ++ for id := range prev.Nodes { ++ if _, ok := cur.Nodes[id]; !ok { ++ deletes = append(deletes, id) + } +- var sum [32]byte +- copy(sum[:], hash.Sum(nil)) +- out.Nodes[n.ID] = sum + } +- return out ++ sort.Strings(deletes) ++ return cur, upserts, deletes + } + + func writeHashString(w io.Writer, s string) { +diff --git a/platform/neuroforge/internal/vector/hnsw_delta_test.go b/platform/neuroforge/internal/vector/hnsw_delta_test.go +new file mode 100644 +index 0000000..7e678ce +--- /dev/null ++++ b/platform/neuroforge/internal/vector/hnsw_delta_test.go +@@ -0,0 +1,38 @@ ++package vector ++ ++import "testing" ++ ++func TestHNSWDeltaDoesNotSnapshotUnchangedVectors(t *testing.T) { ++ h := NewHNSW(HNSWConfig{M: 4, EfConstruction: 16, EfSearch: 8}) ++ h.Add("a", []float32{1, 0, 0}) ++ h.Add("b", []float32{0.9, 0.1, 0}) ++ base := h.Shadow() ++ cur, upserts, deletes := h.Delta(base) ++ if len(upserts) != 0 || len(deletes) != 0 { ++ t.Fatalf("unchanged delta upserts=%d deletes=%d", len(upserts), len(deletes)) ++ } ++ if len(cur.Nodes) != 2 { ++ t.Fatalf("shadow nodes=%d", len(cur.Nodes)) ++ } ++ ++ h.Add("c", []float32{0.8, 0.2, 0}) ++ cur2, upserts, deletes := h.Delta(base) ++ if len(upserts) == 0 { ++ t.Fatalf("expected changed/new nodes") ++ } ++ if len(deletes) != 0 { ++ t.Fatalf("unexpected deletes: %v", deletes) ++ } ++ if len(cur2.Nodes) != 3 { ++ t.Fatalf("shadow nodes=%d", len(cur2.Nodes)) ++ } ++ foundC := false ++ for _, n := range upserts { ++ if n.ID == "c" { ++ foundC = true ++ } ++ } ++ if !foundC { ++ t.Fatalf("new node c missing from delta") ++ } ++} +diff --git a/platform/neuroforge/openapi.yaml b/platform/neuroforge/openapi.yaml +index afbe818..155728a 100644 +--- a/platform/neuroforge/openapi.yaml ++++ b/platform/neuroforge/openapi.yaml +@@ -1,7 +1,7 @@ + openapi: 3.1.0 + info: + title: NeuroForge API +- version: 0.8.2 ++ version: 0.8.3 + description: REST API for NeuroForge associative learning, explainable vector recall, + provenance, document/text ingestion, SearXNG-backed autonomous research, source-grounded + goal cycles, responsive knowledge graph, model routing, cost controls, Prometheus +diff --git a/services/control/engineering-graph.json b/services/control/engineering-graph.json +index 3665df0..e7c5396 100644 +--- a/services/control/engineering-graph.json ++++ b/services/control/engineering-graph.json +@@ -562,6 +562,16 @@ + "path": "platform/neuroforge/internal/store/sqar_vector.go" + } + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "kind": "file", ++ "label": "startup_compact.go", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "path": "platform/neuroforge/internal/store/startup_compact.go" ++ } ++ }, + { + "id": "file:platform/neuroforge/internal/store/store.go", + "kind": "file", +@@ -9742,6 +9752,42 @@ + "path": "platform/neuroforge/cmd/bench/main.go" + } + }, ++ { ++ "id": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", ++ "kind": "function", ++ "label": "bootstrapHandler.ServeHTTP", ++ "group": "engineering", ++ "community": "neuroforge/cmd/server", ++ "meta": { ++ "exported": true, ++ "line": 129, ++ "path": "platform/neuroforge/cmd/server/main.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", ++ "kind": "function", ++ "label": "bootstrapHandler.SetHandler", ++ "group": "engineering", ++ "community": "neuroforge/cmd/server", ++ "meta": { ++ "exported": true, ++ "line": 122, ++ "path": "platform/neuroforge/cmd/server/main.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", ++ "kind": "function", ++ "label": "bootstrapHandler.SetPhase", ++ "group": "engineering", ++ "community": "neuroforge/cmd/server", ++ "meta": { ++ "exported": true, ++ "line": 116, ++ "path": "platform/neuroforge/cmd/server/main.go" ++ } ++ }, + { + "id": "function:neuroforge/cmd/server:envBool", + "kind": "function", +@@ -9750,7 +9796,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 26, ++ "line": 28, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9762,7 +9808,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 50, ++ "line": 52, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9774,7 +9820,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 38, ++ "line": 40, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9786,7 +9832,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 514, ++ "line": 623, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9798,7 +9844,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 104, ++ "line": 159, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9810,7 +9856,19 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 97, ++ "line": 99, ++ "path": "platform/neuroforge/cmd/server/main.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/cmd/server:newBootstrapHandler", ++ "kind": "function", ++ "label": "newBootstrapHandler", ++ "group": "engineering", ++ "community": "neuroforge/cmd/server", ++ "meta": { ++ "exported": false, ++ "line": 112, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9822,7 +9880,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 111, ++ "line": 166, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9834,7 +9892,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 62, ++ "line": 64, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -9846,7 +9904,7 @@ + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, +- "line": 77, ++ "line": 79, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, +@@ -12066,7 +12124,7 @@ + "community": "neuroforge/internal/core", + "meta": { + "exported": true, +- "line": 740, ++ "line": 741, + "path": "platform/neuroforge/internal/core/types.go" + } + }, +@@ -14562,7 +14620,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 52, ++ "line": 56, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -14574,7 +14632,19 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 650, ++ "line": 747, ++ "path": "platform/neuroforge/internal/store/store.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewWithProgress", ++ "kind": "function", ++ "label": "NewWithProgress", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": true, ++ "line": 60, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -14886,7 +14956,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 863, ++ "line": 960, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -14910,7 +14980,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1312, ++ "line": 1409, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -14934,7 +15004,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 735, ++ "line": 795, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -14946,7 +15016,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 389, ++ "line": 398, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -14958,7 +15028,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 393, ++ "line": 402, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -14970,7 +15040,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 652, ++ "line": 749, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15034,6 +15104,18 @@ + "path": "platform/neuroforge/internal/store/cluster.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", ++ "kind": "function", ++ "label": "Store.CompactCompletedRelinkJobs", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": true, ++ "line": 780, ++ "path": "platform/neuroforge/internal/store/orchestrator.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/store:Store.CompactIndexSegments", + "kind": "function", +@@ -15042,7 +15124,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 439, ++ "line": 426, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -15054,7 +15136,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 668, ++ "line": 765, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15066,7 +15148,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 436, ++ "line": 445, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15078,7 +15160,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 440, ++ "line": 449, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15090,7 +15172,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 689, ++ "line": 786, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15114,7 +15196,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1157, ++ "line": 1254, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15126,7 +15208,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1109, ++ "line": 1206, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15162,7 +15244,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1357, ++ "line": 1454, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15210,7 +15292,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 91, ++ "line": 93, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15222,7 +15304,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 95, ++ "line": 97, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15234,7 +15316,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1378, ++ "line": 1475, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15246,7 +15328,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 686, ++ "line": 702, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15270,7 +15352,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 397, ++ "line": 411, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -15294,7 +15376,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 929, ++ "line": 1026, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15390,7 +15472,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 585, ++ "line": 594, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15402,7 +15484,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 294, ++ "line": 303, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15414,7 +15496,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 431, ++ "line": 418, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -15426,7 +15508,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 477, ++ "line": 464, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -15438,7 +15520,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 829, ++ "line": 889, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15450,7 +15532,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 499, ++ "line": 508, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15462,7 +15544,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 512, ++ "line": 521, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15534,7 +15616,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1252, ++ "line": 1349, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15546,7 +15628,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1210, ++ "line": 1307, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15570,7 +15652,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1229, ++ "line": 1326, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15618,7 +15700,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 598, ++ "line": 607, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15654,7 +15736,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 636, ++ "line": 652, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15666,7 +15748,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 654, ++ "line": 670, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15690,7 +15772,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 759, ++ "line": 819, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15738,7 +15820,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1344, ++ "line": 1441, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15762,7 +15844,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 284, ++ "line": 293, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15774,7 +15856,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1098, ++ "line": 1195, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15834,7 +15916,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 538, ++ "line": 547, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -15870,7 +15952,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 986, ++ "line": 1083, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15882,7 +15964,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1709, ++ "line": 1806, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15894,7 +15976,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1718, ++ "line": 1815, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15906,7 +15988,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 746, ++ "line": 843, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15918,7 +16000,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 680, ++ "line": 777, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15942,7 +16024,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1185, ++ "line": 1282, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -15954,7 +16036,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1196, ++ "line": 1293, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16002,7 +16084,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1265, ++ "line": 1362, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16038,7 +16120,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1242, ++ "line": 1339, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16074,7 +16156,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1133, ++ "line": 1230, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16098,7 +16180,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 690, ++ "line": 787, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16110,7 +16192,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1258, ++ "line": 1355, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16122,7 +16204,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 753, ++ "line": 850, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16182,7 +16264,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1328, ++ "line": 1425, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16194,7 +16276,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 1396, ++ "line": 1493, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16218,7 +16300,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 403, ++ "line": 417, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -16230,7 +16312,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": true, +- "line": 559, ++ "line": 568, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -16302,7 +16384,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 323, ++ "line": 331, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -16342,6 +16424,18 @@ + "path": "platform/neuroforge/internal/store/wal.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", ++ "kind": "function", ++ "label": "Store.compactCompletedRelinkJobsLocked", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": false, ++ "line": 764, ++ "path": "platform/neuroforge/internal/store/orchestrator.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/store:Store.currentSnapshotsLocked", + "kind": "function", +@@ -16374,7 +16468,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 214, ++ "line": 223, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -16410,7 +16504,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 940, ++ "line": 1037, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16434,7 +16528,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 370, ++ "line": 357, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -16506,7 +16600,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 383, ++ "line": 397, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -16518,7 +16612,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 615, ++ "line": 655, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16530,7 +16624,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 396, ++ "line": 383, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -16542,7 +16636,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 299, ++ "line": 286, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -16566,7 +16660,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 771, ++ "line": 868, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16578,7 +16672,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 325, ++ "line": 334, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -16614,7 +16708,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 635, ++ "line": 732, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16626,7 +16720,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 638, ++ "line": 735, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16638,7 +16732,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 354, ++ "line": 368, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -16650,7 +16744,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 776, ++ "line": 873, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16662,7 +16756,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 813, ++ "line": 910, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16698,7 +16792,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 244, ++ "line": 253, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -16746,7 +16840,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 992, ++ "line": 1089, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16830,7 +16924,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 1402, ++ "line": 1499, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -16866,7 +16960,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 451, ++ "line": 438, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -16878,7 +16972,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 376, ++ "line": 390, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -16890,7 +16984,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 388, ++ "line": 375, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -17070,7 +17164,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 267, ++ "line": 254, + "path": "platform/neuroforge/internal/store/index_segments.go" + } + }, +@@ -17082,7 +17176,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 217, ++ "line": 254, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17130,7 +17224,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 75, ++ "line": 77, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -17166,7 +17260,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 965, ++ "line": 1062, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17202,7 +17296,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 763, ++ "line": 860, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17218,6 +17312,30 @@ + "path": "platform/neuroforge/internal/store/raftlog.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "kind": "function", ++ "label": "compactLegacyTerminalRelinkCheckpoint", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": false, ++ "line": 25, ++ "path": "platform/neuroforge/internal/store/startup_compact.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/internal/store:copyJSONValue", ++ "kind": "function", ++ "label": "copyJSONValue", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": false, ++ "line": 209, ++ "path": "platform/neuroforge/internal/store/startup_compact.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/store:decodeVectorPayload", + "kind": "function", +@@ -17262,7 +17380,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 1091, ++ "line": 1188, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17382,7 +17500,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 602, ++ "line": 642, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17454,7 +17572,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 380, ++ "line": 389, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -17514,7 +17632,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 393, ++ "line": 407, + "path": "platform/neuroforge/internal/store/wal.go" + } + }, +@@ -17538,7 +17656,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 563, ++ "line": 603, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17574,7 +17692,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 60, ++ "line": 62, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -17670,7 +17788,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 1102, ++ "line": 1199, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17754,7 +17872,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 642, ++ "line": 739, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -17782,6 +17900,30 @@ + "path": "platform/neuroforge/internal/store/sqar_vector.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "kind": "function", ++ "label": "rewriteCheckpointObject", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": false, ++ "line": 86, ++ "path": "platform/neuroforge/internal/store/startup_compact.go" ++ } ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteJobsObject", ++ "kind": "function", ++ "label": "rewriteJobsObject", ++ "group": "engineering", ++ "community": "neuroforge/internal/store", ++ "meta": { ++ "exported": false, ++ "line": 148, ++ "path": "platform/neuroforge/internal/store/startup_compact.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/store:sameClusterMemory", + "kind": "function", +@@ -17886,7 +18028,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 235, ++ "line": 244, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, +@@ -17898,7 +18040,7 @@ + "community": "neuroforge/internal/store", + "meta": { + "exported": false, +- "line": 623, ++ "line": 680, + "path": "platform/neuroforge/internal/store/store.go" + } + }, +@@ -18010,6 +18152,18 @@ + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.Delta", ++ "kind": "function", ++ "label": "HNSW.Delta", ++ "group": "engineering", ++ "community": "neuroforge/internal/vector", ++ "meta": { ++ "exported": true, ++ "line": 744, ++ "path": "platform/neuroforge/internal/vector/hnsw.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/vector:HNSW.Len", + "kind": "function", +@@ -18042,7 +18196,7 @@ + "community": "neuroforge/internal/vector", + "meta": { + "exported": true, +- "line": 682, ++ "line": 726, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, +@@ -18066,7 +18220,7 @@ + "community": "neuroforge/internal/vector", + "meta": { + "exported": true, +- "line": 733, ++ "line": 785, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, +@@ -18106,6 +18260,18 @@ + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "kind": "function", ++ "label": "HNSW.nodeFingerprintLocked", ++ "group": "engineering", ++ "community": "neuroforge/internal/vector", ++ "meta": { ++ "exported": false, ++ "line": 682, ++ "path": "platform/neuroforge/internal/vector/hnsw.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/vector:HNSW.pruneLocked", + "kind": "function", +@@ -18130,6 +18296,18 @@ + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", ++ "kind": "function", ++ "label": "HNSW.snapshotNodeLocked", ++ "group": "engineering", ++ "community": "neuroforge/internal/vector", ++ "meta": { ++ "exported": false, ++ "line": 708, ++ "path": "platform/neuroforge/internal/vector/hnsw.go" ++ } ++ }, + { + "id": "function:neuroforge/internal/vector:NewHNSW", + "kind": "function", +@@ -18270,7 +18448,7 @@ + "community": "neuroforge/internal/vector", + "meta": { + "exported": true, +- "line": 785, ++ "line": 837, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, +@@ -18678,7 +18856,7 @@ + "community": "neuroforge/internal/vector", + "meta": { + "exported": false, +- "line": 718, ++ "line": 770, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, +@@ -18690,7 +18868,7 @@ + "community": "neuroforge/internal/vector", + "meta": { + "exported": false, +- "line": 722, ++ "line": 774, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, +@@ -20924,7 +21102,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20935,7 +21113,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20946,7 +21124,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20957,7 +21135,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20968,7 +21146,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20979,7 +21157,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -20990,7 +21168,7 @@ + "community": "compose", + "status": "configured", + "meta": { +- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" ++ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}" + } + }, + { +@@ -21329,6 +21507,24 @@ + "to": "package:time", + "kind": "imports" + }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.ServeHTTP:defines", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetHandler:defines", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetPhase:defines", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:envBool:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", +@@ -21365,6 +21561,12 @@ + "to": "function:neuroforge/cmd/server:maxIntMain", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:newBootstrapHandler:defines", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "function:neuroforge/cmd/server:newBootstrapHandler", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:run:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", +@@ -21419,6 +21621,12 @@ + "to": "package:net/http", + "kind": "imports" + }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:net:imports", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "package:net", ++ "kind": "imports" ++ }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:neuroforge/internal/brain:imports", + "from": "file:platform/neuroforge/cmd/server/main.go", +@@ -21485,6 +21693,12 @@ + "to": "package:strings", + "kind": "imports" + }, ++ { ++ "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:sync:imports", ++ "from": "file:platform/neuroforge/cmd/server/main.go", ++ "to": "package:sync", ++ "kind": "imports" ++ }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:syscall:imports", + "from": "file:platform/neuroforge/cmd/server/main.go", +@@ -26135,6 +26349,12 @@ + "to": "function:neuroforge/internal/store:Store.ClaimJobForWorker", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.CompactCompletedRelinkJobs:defines", ++ "from": "file:platform/neuroforge/internal/store/orchestrator.go", ++ "to": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.CompleteJob:defines", + "from": "file:platform/neuroforge/internal/store/orchestrator.go", +@@ -26237,6 +26457,12 @@ + "to": "function:neuroforge/internal/store:Store.WorkersSnapshot", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:defines", ++ "from": "file:platform/neuroforge/internal/store/orchestrator.go", ++ "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.dependencyStateLocked:defines", + "from": "file:platform/neuroforge/internal/store/orchestrator.go", +@@ -27125,6 +27351,78 @@ + "to": "package:io", + "kind": "imports" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint:defines", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:copyJSONValue:defines", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "function:neuroforge/internal/store:copyJSONValue", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:rewriteCheckpointObject:defines", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:rewriteJobsObject:defines", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "function:neuroforge/internal/store:rewriteJobsObject", ++ "kind": "defines" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:bufio:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:bufio", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:encoding/json:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:encoding/json", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:errors:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:errors", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:fmt:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:fmt", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:io:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:io", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:neuroforge/internal/core:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:neuroforge/internal/core", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:os:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:os", ++ "kind": "imports" ++ }, ++ { ++ "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:path/filepath:imports", ++ "from": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "to": "package:path/filepath", ++ "kind": "imports" ++ }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:New:defines", + "from": "file:platform/neuroforge/internal/store/store.go", +@@ -27137,6 +27435,12 @@ + "to": "function:neuroforge/internal/store:NewID", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:NewWithProgress:defines", ++ "from": "file:platform/neuroforge/internal/store/store.go", ++ "to": "function:neuroforge/internal/store:NewWithProgress", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:Store.AddMemory:defines", + "from": "file:platform/neuroforge/internal/store/store.go", +@@ -27425,6 +27729,12 @@ + "to": "function:neuroforge/internal/store:writeAtomic", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:bufio:imports", ++ "from": "file:platform/neuroforge/internal/store/store.go", ++ "to": "package:bufio", ++ "kind": "imports" ++ }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:crypto/rand:imports", + "from": "file:platform/neuroforge/internal/store/store.go", +@@ -27455,6 +27765,12 @@ + "to": "package:fmt", + "kind": "imports" + }, ++ { ++ "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:io:imports", ++ "from": "file:platform/neuroforge/internal/store/store.go", ++ "to": "package:io", ++ "kind": "imports" ++ }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:math:imports", + "from": "file:platform/neuroforge/internal/store/store.go", +@@ -28109,6 +28425,12 @@ + "to": "function:neuroforge/internal/vector:HNSW.AddBatch", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.Delta:defines", ++ "from": "file:platform/neuroforge/internal/vector/hnsw.go", ++ "to": "function:neuroforge/internal/vector:HNSW.Delta", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.Len:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", +@@ -28157,6 +28479,12 @@ + "to": "function:neuroforge/internal/vector:HNSW.levelForID", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:defines", ++ "from": "file:platform/neuroforge/internal/vector/hnsw.go", ++ "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.pruneLocked:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", +@@ -28169,6 +28497,12 @@ + "to": "function:neuroforge/internal/vector:HNSW.searchLayerLocked", + "kind": "defines" + }, ++ { ++ "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.snapshotNodeLocked:defines", ++ "from": "file:platform/neuroforge/internal/vector/hnsw.go", ++ "to": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", ++ "kind": "defines" ++ }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:NewHNSW:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", +@@ -49274,6 +49608,13 @@ + "kind": "calls_package", + "label": "Sqrt" + }, ++ { ++ "id": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", ++ "to": "package:fmt", ++ "kind": "calls_package", ++ "label": "Fprintf" ++ }, + { + "id": "function:neuroforge/cmd/server:envBool-\u003epackage:os:calls_package", + "from": "function:neuroforge/cmd/server:envBool", +@@ -49364,6 +49705,18 @@ + "kind": "calls_package", + "label": "Exit" + }, ++ { ++ "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetHandler:calls", ++ "from": "function:neuroforge/cmd/server:run", ++ "to": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetPhase:calls", ++ "from": "function:neuroforge/cmd/server:run", ++ "to": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", ++ "kind": "calls" ++ }, + { + "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:envBool:calls", + "from": "function:neuroforge/cmd/server:run", +@@ -49394,6 +49747,12 @@ + "to": "function:neuroforge/cmd/server:maxIntMain", + "kind": "calls" + }, ++ { ++ "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:newBootstrapHandler:calls", ++ "from": "function:neuroforge/cmd/server:run", ++ "to": "function:neuroforge/cmd/server:newBootstrapHandler", ++ "kind": "calls" ++ }, + { + "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:validateManagedSecretsFromEnv:calls", + "from": "function:neuroforge/cmd/server:run", +@@ -49405,7 +49764,7 @@ + "from": "function:neuroforge/cmd/server:run", + "to": "package:context", + "kind": "calls_package", +- "label": "Background" ++ "label": "WithTimeout" + }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:errors:calls_package", +@@ -49435,6 +49794,13 @@ + "kind": "calls_package", + "label": "Printf" + }, ++ { ++ "id": "function:neuroforge/cmd/server:run-\u003epackage:net:calls_package", ++ "from": "function:neuroforge/cmd/server:run", ++ "to": "package:net", ++ "kind": "calls_package", ++ "label": "Listen" ++ }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/brain:calls_package", + "from": "function:neuroforge/cmd/server:run", +@@ -49442,6 +49808,13 @@ + "kind": "calls_package", + "label": "New" + }, ++ { ++ "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/core:calls_package", ++ "from": "function:neuroforge/cmd/server:run", ++ "to": "package:neuroforge/internal/core", ++ "kind": "calls_package", ++ "label": "DefaultConfig" ++ }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/cost:calls_package", + "from": "function:neuroforge/cmd/server:run", +@@ -49468,7 +49841,7 @@ + "from": "function:neuroforge/cmd/server:run", + "to": "package:neuroforge/internal/store", + "kind": "calls_package", +- "label": "New" ++ "label": "NewWithProgress" + }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:os/signal:calls_package", +@@ -57100,192 +57473,216 @@ + "label": "Base" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.ConsumeMetadata:calls", ++ "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:NewWithProgress:calls", + "from": "function:neuroforge/internal/store:New", ++ "to": "function:neuroforge/internal/store:NewWithProgress", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewID-\u003efunction:neuroforge/internal/store:randomID:calls", ++ "from": "function:neuroforge/internal/store:NewID", ++ "to": "function:neuroforge/internal/store:randomID", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:New:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", ++ "to": "function:neuroforge/internal/store:New", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.ConsumeMetadata:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.ConsumeMetadata", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.HasRecords:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.HasRecords:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.HasRecords", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.Rebuild:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.Rebuild:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.Rebuild", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.checkpointLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.initHotTrackerLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", ++ "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.initHotTrackerLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.initHotTrackerLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.initializeClusterRoleLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.initializeClusterRoleLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.initializeClusterRoleLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadDiskANNLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadDiskANNLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadDiskANNLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadIndexSnapshotLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadIndexSnapshotLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadIndexSnapshotLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadJSON", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.persistSecretsLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.persistSecretsLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.persistSecretsLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildIndexesLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildIndexesLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildIndexesLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildSynapseAdjLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildSynapseAdjLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildSynapseAdjLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.replayWAL:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.replayWAL:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.replayWAL", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.tierMemoryBodiesLocked:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.tierMemoryBodiesLocked:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.tierMemoryBodiesLocked", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:applyNewDefaults:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:applyNewDefaults:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:applyNewDefaults", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:migrateMemories:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", ++ "to": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:migrateMemories:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:migrateMemories", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:newMemoryPageCache:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:newMemoryPageCache:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:newMemoryPageCache", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:openSegmentStore:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:openSegmentStore:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:openSegmentStore", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:openVectorJournal:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:openVectorJournal:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:openVectorJournal", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:randomID:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:randomID:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:randomID", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:vectorJournalOptionsFromConfig:calls", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:vectorJournalOptionsFromConfig:calls", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:vectorJournalOptionsFromConfig", + "kind": "calls" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:errors:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:errors:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:errors", + "kind": "calls_package", +- "label": "New" ++ "label": "Is" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:fmt:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:fmt", + "kind": "calls_package", +- "label": "Sprint" ++ "label": "Errorf" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:neuroforge/internal/core:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:neuroforge/internal/core:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:neuroforge/internal/core", + "kind": "calls_package", + "label": "DefaultConfig" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:os:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:os:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:os", + "kind": "calls_package", + "label": "MkdirAll" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:path/filepath:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:path/filepath:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:path/filepath", + "kind": "calls_package", + "label": "Join" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:strings:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:strings:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:strings", + "kind": "calls_package", + "label": "TrimSpace" + }, + { +- "id": "function:neuroforge/internal/store:New-\u003epackage:time:calls_package", +- "from": "function:neuroforge/internal/store:New", ++ "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:time:calls_package", ++ "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:time", + "kind": "calls_package", + "label": "Now" + }, +- { +- "id": "function:neuroforge/internal/store:NewID-\u003efunction:neuroforge/internal/store:randomID:calls", +- "from": "function:neuroforge/internal/store:NewID", +- "to": "function:neuroforge/internal/store:randomID", +- "kind": "calls" +- }, + { + "id": "function:neuroforge/internal/store:SegmentStore.AppendDelete-\u003efunction:neuroforge/internal/store:ClusterLog.append:calls", + "from": "function:neuroforge/internal/store:SegmentStore.AppendDelete", +@@ -58166,6 +58563,18 @@ + "kind": "calls_package", + "label": "Now" + }, ++ { ++ "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", ++ "from": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", ++ "to": "function:neuroforge/internal/store:Store.checkpointLocked", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:calls", ++ "from": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", ++ "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", ++ "kind": "calls" ++ }, + { + "id": "function:neuroforge/internal/store:Store.CompactIndexSegments-\u003efunction:neuroforge/internal/store:Store.IndexSnapshotStatusUnlocked:calls", + "from": "function:neuroforge/internal/store:Store.CompactIndexSegments", +@@ -60943,19 +61352,46 @@ + "to": "function:neuroforge/internal/store:indexMode", + "kind": "calls" + }, ++ { ++ "id": "function:neuroforge/internal/store:Store.loadJSON-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", ++ "from": "function:neuroforge/internal/store:Store.loadJSON", ++ "to": "function:neuroforge/internal/store:ClusterLog.Close", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:bufio:calls_package", ++ "from": "function:neuroforge/internal/store:Store.loadJSON", ++ "to": "package:bufio", ++ "kind": "calls_package", ++ "label": "NewReaderSize" ++ }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "package:encoding/json", + "kind": "calls_package", +- "label": "Unmarshal" ++ "label": "NewDecoder" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/internal/store:Store.loadJSON", ++ "to": "package:fmt", ++ "kind": "calls_package", ++ "label": "Errorf" + }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:os:calls_package", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "package:os", + "kind": "calls_package", +- "label": "ReadFile" ++ "label": "Open" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:path/filepath:calls_package", ++ "from": "function:neuroforge/internal/store:Store.loadJSON", ++ "to": "package:path/filepath", ++ "kind": "calls_package", ++ "label": "Base" + }, + { + "id": "function:neuroforge/internal/store:Store.loadLegacyIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", +@@ -61731,12 +62167,6 @@ + "to": "function:neuroforge/internal/store:ClusterLog.append", + "kind": "calls" + }, +- { +- "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.currentSnapshotsLocked:calls", +- "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +- "to": "function:neuroforge/internal/store:Store.currentSnapshotsLocked", +- "kind": "calls" +- }, + { + "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", + "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +@@ -61755,24 +62185,12 @@ + "to": "function:neuroforge/internal/store:Store.writeLegacyIndexSnapshotLocked", + "kind": "calls" + }, +- { +- "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:buildIndexShadow:calls", +- "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +- "to": "function:neuroforge/internal/store:buildIndexShadow", +- "kind": "calls" +- }, + { + "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:cleanupOldIndexBases:calls", + "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", + "to": "function:neuroforge/internal/store:cleanupOldIndexBases", + "kind": "calls" + }, +- { +- "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:hashSnapshotNode:calls", +- "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +- "to": "function:neuroforge/internal/store:hashSnapshotNode", +- "kind": "calls" +- }, + { + "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:shadowFromHNSW:calls", + "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +@@ -61806,13 +62224,6 @@ + "kind": "calls_package", + "label": "Join" + }, +- { +- "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003epackage:sort:calls_package", +- "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +- "to": "package:sort", +- "kind": "calls_package", +- "label": "Strings" +- }, + { + "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003epackage:strconv:calls_package", + "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", +@@ -62285,6 +62696,74 @@ + "kind": "calls_package", + "label": "Sprintf" + }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "function:neuroforge/internal/store:ClusterLog.Close", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003efunction:neuroforge/internal/store:rewriteCheckpointObject:calls", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:bufio:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:bufio", ++ "kind": "calls_package", ++ "label": "NewReaderSize" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:encoding/json:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:encoding/json", ++ "kind": "calls_package", ++ "label": "NewDecoder" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:errors:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:errors", ++ "kind": "calls_package", ++ "label": "Is" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:fmt", ++ "kind": "calls_package", ++ "label": "Sprintf" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:os:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:os", ++ "kind": "calls_package", ++ "label": "Stat" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:path/filepath:calls_package", ++ "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", ++ "to": "package:path/filepath", ++ "kind": "calls_package", ++ "label": "Join" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:copyJSONValue-\u003epackage:encoding/json:calls_package", ++ "from": "function:neuroforge/internal/store:copyJSONValue", ++ "to": "package:encoding/json", ++ "kind": "calls_package", ++ "label": "Marshal" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:copyJSONValue-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/internal/store:copyJSONValue", ++ "to": "package:fmt", ++ "kind": "calls_package", ++ "label": "Errorf" ++ }, + { + "id": "function:neuroforge/internal/store:decodeVectorPayload-\u003efunction:neuroforge/internal/store:ClusterLog.append:calls", + "from": "function:neuroforge/internal/store:decodeVectorPayload", +@@ -62715,6 +63194,51 @@ + "to": "function:neuroforge/internal/store:vectorPredictorValue", + "kind": "calls" + }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:New:calls", ++ "from": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "to": "function:neuroforge/internal/store:New", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:copyJSONValue:calls", ++ "from": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "to": "function:neuroforge/internal/store:copyJSONValue", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:rewriteJobsObject:calls", ++ "from": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "to": "function:neuroforge/internal/store:rewriteJobsObject", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003epackage:encoding/json:calls_package", ++ "from": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "to": "package:encoding/json", ++ "kind": "calls_package", ++ "label": "Marshal" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003epackage:fmt:calls_package", ++ "from": "function:neuroforge/internal/store:rewriteCheckpointObject", ++ "to": "package:fmt", ++ "kind": "calls_package", ++ "label": "Errorf" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteJobsObject-\u003efunction:neuroforge/internal/store:New:calls", ++ "from": "function:neuroforge/internal/store:rewriteJobsObject", ++ "to": "function:neuroforge/internal/store:New", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:rewriteJobsObject-\u003epackage:encoding/json:calls_package", ++ "from": "function:neuroforge/internal/store:rewriteJobsObject", ++ "to": "package:encoding/json", ++ "kind": "calls_package", ++ "label": "Marshal" ++ }, + { + "id": "function:neuroforge/internal/store:segmentName-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:segmentName", +@@ -62821,19 +63345,39 @@ + "kind": "calls_package", + "label": "ToLower" + }, ++ { ++ "id": "function:neuroforge/internal/store:writeAtomic-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", ++ "from": "function:neuroforge/internal/store:writeAtomic", ++ "to": "function:neuroforge/internal/store:ClusterLog.Close", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:bufio:calls_package", ++ "from": "function:neuroforge/internal/store:writeAtomic", ++ "to": "package:bufio", ++ "kind": "calls_package", ++ "label": "NewWriterSize" ++ }, + { + "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:writeAtomic", + "to": "package:encoding/json", + "kind": "calls_package", +- "label": "MarshalIndent" ++ "label": "NewEncoder" + }, + { + "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:os:calls_package", + "from": "function:neuroforge/internal/store:writeAtomic", + "to": "package:os", + "kind": "calls_package", +- "label": "WriteFile" ++ "label": "Remove" ++ }, ++ { ++ "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:path/filepath:calls_package", ++ "from": "function:neuroforge/internal/store:writeAtomic", ++ "to": "package:path/filepath", ++ "kind": "calls_package", ++ "label": "Dir" + }, + { + "id": "function:neuroforge/internal/store:writeHNSWAtomic-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", +@@ -63028,6 +63572,25 @@ + "to": "function:neuroforge/internal/vector:normalizeCopy", + "kind": "calls" + }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:calls", ++ "from": "function:neuroforge/internal/vector:HNSW.Delta", ++ "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003efunction:neuroforge/internal/vector:HNSW.snapshotNodeLocked:calls", ++ "from": "function:neuroforge/internal/vector:HNSW.Delta", ++ "to": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003epackage:sort:calls_package", ++ "from": "function:neuroforge/internal/vector:HNSW.Delta", ++ "to": "package:sort", ++ "kind": "calls_package", ++ "label": "Strings" ++ }, + { + "id": "function:neuroforge/internal/vector:HNSW.Search-\u003efunction:neuroforge/internal/vector:HNSW.Add:calls", + "from": "function:neuroforge/internal/vector:HNSW.Search", +@@ -63121,31 +63684,11 @@ + "label": "Slice" + }, + { +- "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:writeHashString:calls", ++ "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:calls", + "from": "function:neuroforge/internal/vector:HNSW.Shadow", +- "to": "function:neuroforge/internal/vector:writeHashString", ++ "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "kind": "calls" + }, +- { +- "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:writeHashU32:calls", +- "from": "function:neuroforge/internal/vector:HNSW.Shadow", +- "to": "function:neuroforge/internal/vector:writeHashU32", +- "kind": "calls" +- }, +- { +- "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003epackage:crypto/sha256:calls_package", +- "from": "function:neuroforge/internal/vector:HNSW.Shadow", +- "to": "package:crypto/sha256", +- "kind": "calls_package", +- "label": "New" +- }, +- { +- "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003epackage:math:calls_package", +- "from": "function:neuroforge/internal/vector:HNSW.Shadow", +- "to": "package:math", +- "kind": "calls_package", +- "label": "Float32bits" +- }, + { + "id": "function:neuroforge/internal/vector:HNSW.Snapshot-\u003epackage:sort:calls_package", + "from": "function:neuroforge/internal/vector:HNSW.Snapshot", +@@ -63230,6 +63773,32 @@ + "kind": "calls_package", + "label": "Log" + }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003efunction:neuroforge/internal/vector:writeHashString:calls", ++ "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "to": "function:neuroforge/internal/vector:writeHashString", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003efunction:neuroforge/internal/vector:writeHashU32:calls", ++ "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "to": "function:neuroforge/internal/vector:writeHashU32", ++ "kind": "calls" ++ }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003epackage:crypto/sha256:calls_package", ++ "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "to": "package:crypto/sha256", ++ "kind": "calls_package", ++ "label": "New" ++ }, ++ { ++ "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003epackage:math:calls_package", ++ "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", ++ "to": "package:math", ++ "kind": "calls_package", ++ "label": "Float32bits" ++ }, + { + "id": "function:neuroforge/internal/vector:HNSW.searchLayerLocked-\u003efunction:neuroforge/internal/vector:dotNormalized:calls", + "from": "function:neuroforge/internal/vector:HNSW.searchLayerLocked", +@@ -65188,6 +65757,12 @@ + "to": "file:platform/neuroforge/internal/store/sqar_vector.go", + "kind": "contains_file" + }, ++ { ++ "id": "package:neuroforge/internal/store-\u003efile:platform/neuroforge/internal/store/startup_compact.go:contains_file", ++ "from": "package:neuroforge/internal/store", ++ "to": "file:platform/neuroforge/internal/store/startup_compact.go", ++ "kind": "contains_file" ++ }, + { + "id": "package:neuroforge/internal/store-\u003efile:platform/neuroforge/internal/store/store.go:contains_file", + "from": "package:neuroforge/internal/store", +@@ -66288,10 +66863,10 @@ + } + ], + "meta": { +- "edges": 7210, ++ "edges": 7273, + "format_version": 1, + "generator": "go-ast+compose", + "modules": 4, +- "nodes": 1830 ++ "nodes": 1845 + } + } diff --git a/platform/neuroforge/CHANGELOG.md b/platform/neuroforge/CHANGELOG.md index 842d354..8fe1d5d 100644 --- a/platform/neuroforge/CHANGELOG.md +++ b/platform/neuroforge/CHANGELOG.md @@ -1,4 +1,17 @@ # Changelog +- v1.6.1 hotfix: streaming authoritative checkpoint I/O avoids full raw/encoded `state.json` copies during load/save. +- v1.6.1 hotfix: corrupt `state.json`/`secrets.json` fail closed instead of being silently ignored. +- v1.6.1 hotfix: completed `vector.relink` blobs are compacted during WAL replay as well as checkpoint migration. + +## v0.8.3 + +- Crash-/Recovery-Hardening für große Knowledge-Korpora und Graph-Backfills. +- Streaming-Migration entfernt historische `vector.relink`-Payloads vor dem vollständigen Checkpoint-Unmarshal. +- Erfolgreiche Relink-Jobs verwerfen Target-/Candidate-Vektoren sofort nach Master-Apply. +- Queue-Payload-Budget schützt den Master vor ungebremstem transientem Job-State. +- HNSW-Deltas kopieren nur veränderte Nodes statt bei jedem Checkpoint den kompletten Index. +- Checkpoint-Reihenfolge index-first verhindert `state.json`-Vorlauf nach Crash in der Indexpersistenz. +- Bootstrap-HTTP und Startup-Phasenlogs machen lange Recovery sichtbar. ## v0.8.2 diff --git a/platform/neuroforge/PRODUCTION.md b/platform/neuroforge/PRODUCTION.md index 919ac75..43694a4 100644 --- a/platform/neuroforge/PRODUCTION.md +++ b/platform/neuroforge/PRODUCTION.md @@ -1,4 +1,4 @@ -# NeuroForge v0.8.2 – Production Guide +# NeuroForge v0.8.3 – Production Guide ## 1. Sicherheitsgrenze @@ -146,3 +146,7 @@ Research-Trace-Events sind **Observability/Audit**, nicht autoritative Knowledge Die `preview`-Felder im Research-Trace sind absichtlich gekürzt und enthalten keine vollständigen Dokumente. Für vollständige Inhalte den Source-/Memory-Inspector verwenden. + +### v1.6.1 Recovery / large graph backfill + +For large graph backfills keep completed relink payloads compact and bound pending payload bytes. The master streams `state.json` on load/save and fails closed on corrupt authoritative state/secrets. Never delete or replace a corrupt checkpoint automatically; retain the volume for forensic recovery/restore. During startup with explicit `-listen`, `/livez` and the bootstrap `/admin` page expose the current recovery phase while `/readyz` remains 503. diff --git a/platform/neuroforge/README.md b/platform/neuroforge/README.md index 3785683..67af793 100644 --- a/platform/neuroforge/README.md +++ b/platform/neuroforge/README.md @@ -1,8 +1,8 @@ -# NeuroForge v0.8.2 +# NeuroForge v0.8.3 NeuroForge ist eine persistente, assoziativ lernende KI-Schicht in Go. Ollama und optional OpenAI liefern Inferenz/Embeddings; NeuroForge besitzt den dauerhaften Wissenszustand: Vektoren, HNSW/Disk-PQ-Recall, Synapsen, Rewards, Provenance, Konflikte, Konsolidierung, Goals und Learning Cycles. -**v0.8.2 erweitert den Production-/Explainability-Stand um einen source-grounded Lernpfad, Dokument-/Text-Ingestion, SearXNG-Research und ein vollständig neu gestaltetes CSS/Vanilla-JS-Admin-UI mit responsive Knowledge-Graph und Level-of-Detail (LOD).** Wissen soll nicht nur gespeichert, sondern als Kette `Quelle → Evidence → Recall → Learning` nachvollziehbar sein. +**v0.8.3 übernimmt den v0.8.2-Funktionsumfang und härtet große Bulk-/Graph-Workloads gegen Speicher- und Recovery-Spitzen. v0.8.2 erweitert den Production-/Explainability-Stand um einen source-grounded Lernpfad, Dokument-/Text-Ingestion, SearXNG-Research und ein vollständig neu gestaltetes CSS/Vanilla-JS-Admin-UI mit responsive Knowledge-Graph und Level-of-Detail (LOD).** Wissen soll nicht nur gespeichert, sondern als Kette `Quelle → Evidence → Recall → Learning` nachvollziehbar sein. ## Neu in v0.8.2: Live Research pro Goal diff --git a/platform/neuroforge/VERSION b/platform/neuroforge/VERSION index 100435b..ee94dd8 100644 --- a/platform/neuroforge/VERSION +++ b/platform/neuroforge/VERSION @@ -1 +1 @@ -0.8.2 +0.8.3 diff --git a/platform/neuroforge/cmd/server/main.go b/platform/neuroforge/cmd/server/main.go index 9886bcd..c48a86a 100644 --- a/platform/neuroforge/cmd/server/main.go +++ b/platform/neuroforge/cmd/server/main.go @@ -6,12 +6,14 @@ import ( "flag" "fmt" "log" + "net" "net/http" "os" "os/signal" "path/filepath" "strconv" "strings" + "sync" "syscall" "time" @@ -101,6 +103,59 @@ func maxIntMain(a, b int) int { return b } +type bootstrapHandler struct { + mu sync.RWMutex + phase string + handler http.Handler +} + +func newBootstrapHandler() *bootstrapHandler { + return &bootstrapHandler{phase: "process.start"} +} + +func (b *bootstrapHandler) SetPhase(phase string) { + b.mu.Lock() + b.phase = phase + b.mu.Unlock() +} + +func (b *bootstrapHandler) SetHandler(h http.Handler) { + b.mu.Lock() + b.handler = h + b.phase = "ready" + b.mu.Unlock() +} + +func (b *bootstrapHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + b.mu.RLock() + h := b.handler + phase := b.phase + b.mu.RUnlock() + if h != nil { + h.ServeHTTP(w, r) + return + } + w.Header().Set("Cache-Control", "no-store") + switch r.URL.Path { + case "/livez", "/healthz": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintf(w, `{"ok":true,"status":"starting","phase":%q}`, phase) + case "/readyz": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusServiceUnavailable) + _, _ = fmt.Fprintf(w, `{"ok":false,"status":"starting","phase":%q}`, phase) + case "/", "/admin": + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusServiceUnavailable) + _, _ = fmt.Fprintf(w, `NeuroForge starting

NeuroForge startet

Recovery-/Startphase: %s

Die autoritativen Daten werden geladen. Diese Seite aktualisiert sich automatisch.

`, phase) + default: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusServiceUnavailable) + _, _ = fmt.Fprintf(w, `{"error":"neuroforge is starting","phase":%q}`, phase) + } +} + func main() { if err := run(); err != nil { log.Printf("fatal: %v", err) @@ -117,7 +172,55 @@ func run() (retErr error) { return err } - s, err := store.New(*data) + var boot *bootstrapHandler + var srv *http.Server + var errCh chan error + // Container deployments pass an explicit -listen address. Bind it before + // opening the potentially large store so liveness and a minimal startup UI + // remain reachable during WAL/index recovery instead of looking like a dead + // container with no logs. + if strings.TrimSpace(*listen) != "" { + boot = newBootstrapHandler() + d := core.DefaultConfig() + h := d.HTTP + srv = &http.Server{ + Addr: *listen, + Handler: boot, + ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, + ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, + WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, + IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, + MaxHeaderBytes: h.MaxHeaderBytes, + } + ln, err := net.Listen("tcp", *listen) + if err != nil { + return fmt.Errorf("bootstrap listen %s: %w", *listen, err) + } + errCh = make(chan error, 1) + go func() { + err := srv.Serve(ln) + if errors.Is(err, http.ErrServerClosed) { + err = nil + } + errCh <- err + }() + log.Printf("NeuroForge bootstrap listener active on %s", *listen) + defer func() { + if retErr != nil && srv != nil { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + _ = srv.Shutdown(ctx) + } + }() + } + reportStartup := func(phase string) { + log.Printf("startup phase: %s", phase) + if boot != nil { + boot.SetPhase(phase) + } + } + + s, err := store.NewWithProgress(*data, reportStartup) if err != nil { return err } @@ -258,7 +361,7 @@ func run() (retErr error) { // and environment-owned only when set, preserving admin-managed config otherwise. workerEnv := []string{ "NEUROFORGE_WORKER_LEASE_SECONDS", "NEUROFORGE_WORKER_HEARTBEAT_SECONDS", "NEUROFORGE_WORKER_STALE_AFTER_SECONDS", - "NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS", "NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS", "NEUROFORGE_WORKER_MAX_QUEUED_JOBS", + "NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS", "NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS", "NEUROFORGE_WORKER_MAX_QUEUED_JOBS", "NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB", "NEUROFORGE_WORKER_JOB_RETENTION_HOURS", "NEUROFORGE_WORKER_MAX_TERMINAL_JOBS", "NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS", "NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS", "NEUROFORGE_GRAPH_BACKFILL_ENABLED", "NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS", "NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE", @@ -294,6 +397,9 @@ func run() (retErr error) { if v, ok := envInt("NEUROFORGE_WORKER_MAX_QUEUED_JOBS"); ok { cfg.Worker.MaxQueuedJobs = v } + if v, ok := envInt("NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB"); ok { + cfg.Worker.MaxQueuedPayloadMB = v + } if v, ok := envInt("NEUROFORGE_WORKER_JOB_RETENTION_HOURS"); ok { cfg.Worker.JobRetentionHours = v } @@ -453,30 +559,33 @@ func run() (retErr error) { } h := cfg.HTTP - srv := &http.Server{ - Addr: addr, - Handler: api.Handler(), - ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, - ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, - WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, - IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, - MaxHeaderBytes: h.MaxHeaderBytes, + if srv == nil { + srv = &http.Server{ + Addr: addr, + Handler: api.Handler(), + ReadHeaderTimeout: time.Duration(h.ReadHeaderTimeoutSeconds) * time.Second, + ReadTimeout: time.Duration(h.ReadTimeoutSeconds) * time.Second, + WriteTimeout: time.Duration(h.WriteTimeoutSeconds) * time.Second, + IdleTimeout: time.Duration(h.IdleTimeoutSeconds) * time.Second, + MaxHeaderBytes: h.MaxHeaderBytes, + } + errCh = make(chan error, 1) + go func() { + err := srv.ListenAndServe() + if errors.Is(err, http.ErrServerClosed) { + err = nil + } + errCh <- err + }() + } else { + boot.SetHandler(api.Handler()) } - log.Printf("NeuroForge v0.8.2 listening on %s", addr) + log.Printf("NeuroForge v0.8.3 listening on %s", addr) log.Printf("Admin dashboard: /admin · readiness: /readyz · metrics: /metrics") if os.Getenv("NEUROFORGE_ADMIN_TOKEN") == "" { log.Printf("Admin token is intentionally not printed; read it locally from %s or set NEUROFORGE_ADMIN_TOKEN", filepath.Join(*data, "secrets.json")) } - errCh := make(chan error, 1) - go func() { - err := srv.ListenAndServe() - if errors.Is(err, http.ErrServerClosed) { - err = nil - } - errCh <- err - }() - var serveErr error select { case serveErr = <-errCh: diff --git a/platform/neuroforge/cmd/server/main_test.go b/platform/neuroforge/cmd/server/main_test.go new file mode 100644 index 0000000..c987a23 --- /dev/null +++ b/platform/neuroforge/cmd/server/main_test.go @@ -0,0 +1,38 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestBootstrapHandlerExposesLivenessAndStartupUI(t *testing.T) { + b := newBootstrapHandler() + b.SetPhase("hnsw.rebuild") + + rr := httptest.NewRecorder() + b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/livez", nil)) + if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "hnsw.rebuild") { + t.Fatalf("livez=%d %s", rr.Code, rr.Body.String()) + } + + rr = httptest.NewRecorder() + b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/readyz", nil)) + if rr.Code != http.StatusServiceUnavailable { + t.Fatalf("readyz=%d", rr.Code) + } + + rr = httptest.NewRecorder() + b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/admin", nil)) + if rr.Code != http.StatusServiceUnavailable || !strings.Contains(rr.Body.String(), "hnsw.rebuild") { + t.Fatalf("admin=%d %s", rr.Code, rr.Body.String()) + } + + b.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) })) + rr = httptest.NewRecorder() + b.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/admin", nil)) + if rr.Code != http.StatusNoContent { + t.Fatalf("delegated=%d", rr.Code) + } +} diff --git a/platform/neuroforge/internal/core/types.go b/platform/neuroforge/internal/core/types.go index 78b379b..c75ef25 100644 --- a/platform/neuroforge/internal/core/types.go +++ b/platform/neuroforge/internal/core/types.go @@ -398,6 +398,7 @@ type Config struct { DefaultMaxAttempts int `json:"default_max_attempts"` RetryBackoffSeconds int `json:"retry_backoff_seconds"` MaxQueuedJobs int `json:"max_queued_jobs"` + MaxQueuedPayloadMB int `json:"max_queued_payload_mb"` MasterApplyMaxAttempts int `json:"master_apply_max_attempts"` MasterApplyBackoffSeconds int `json:"master_apply_backoff_seconds"` JobRetentionHours int `json:"job_retention_hours"` @@ -921,14 +922,15 @@ func DefaultConfig() Config { c.Worker.DefaultMaxAttempts = 3 c.Worker.RetryBackoffSeconds = 15 c.Worker.MaxQueuedJobs = 5000 + c.Worker.MaxQueuedPayloadMB = 128 c.Worker.MasterApplyMaxAttempts = 5 c.Worker.MasterApplyBackoffSeconds = 5 - c.Worker.JobRetentionHours = 168 - c.Worker.MaxTerminalJobs = 20000 + c.Worker.JobRetentionHours = 24 + c.Worker.MaxTerminalJobs = 2000 c.Worker.GraphBackfillEnabled = true c.Worker.GraphBackfillIntervalS = 10 - c.Worker.GraphBackfillBatchSize = 64 - c.Worker.GraphBackfillMaxQueued = 512 + c.Worker.GraphBackfillBatchSize = 16 + c.Worker.GraphBackfillMaxQueued = 64 c.Worker.GraphBackfillMinDegree = 3 c.Worker.GraphCandidateMultiplier = 6 c.Worker.GraphRetryAfterMinutes = 360 diff --git a/platform/neuroforge/internal/httpapi/httpapi.go b/platform/neuroforge/internal/httpapi/httpapi.go index 109e8ea..254eea6 100644 --- a/platform/neuroforge/internal/httpapi/httpapi.go +++ b/platform/neuroforge/internal/httpapi/httpapi.go @@ -59,7 +59,7 @@ func (s *Server) routes() { s.mux.HandleFunc("GET /healthz", s.livez) s.mux.HandleFunc("GET /livez", s.livez) s.mux.HandleFunc("GET /readyz", s.readyz) - s.mux.HandleFunc("GET /version", func(w http.ResponseWriter, r *http.Request) { s.json(w, 200, map[string]any{"version": "0.8.2"}) }) + s.mux.HandleFunc("GET /version", func(w http.ResponseWriter, r *http.Request) { s.json(w, 200, map[string]any{"version": "0.8.3"}) }) s.mux.Handle("POST /api/v1/chat", s.appAuth(http.HandlerFunc(s.chat))) s.mux.Handle("POST /api/v1/learn", s.appAuth(http.HandlerFunc(s.learn))) s.mux.Handle("POST /api/v1/search", s.appAuth(http.HandlerFunc(s.search))) @@ -882,7 +882,7 @@ func (s *Server) requestLimits(next http.Handler) http.Handler { } func (s *Server) livez(w http.ResponseWriter, r *http.Request) { - s.json(w, http.StatusOK, map[string]any{"ok": true, "status": "alive", "time": time.Now().UTC(), "version": "0.8.2"}) + s.json(w, http.StatusOK, map[string]any{"ok": true, "status": "alive", "time": time.Now().UTC(), "version": "0.8.3"}) } func configuredModelAvailable(models map[string]bool, configured string) bool { diff --git a/platform/neuroforge/internal/httpapi/metrics.go b/platform/neuroforge/internal/httpapi/metrics.go index d49bf82..3fbd67f 100644 --- a/platform/neuroforge/internal/httpapi/metrics.go +++ b/platform/neuroforge/internal/httpapi/metrics.go @@ -276,6 +276,9 @@ func (s *Server) metricsEndpoint(w http.ResponseWriter, r *http.Request) { for _, status := range statuses { promSample(&b, "neuroforge_jobs", orch.JobsByStatus[status], "status", status) } + promHeader(&b, "neuroforge_job_payload_bytes", "Durable orchestrator payload/result bytes retained in memory and checkpoints.", "gauge") + promSample(&b, "neuroforge_job_payload_bytes", orch.PendingPayloadBytes, "state", "pending") + promSample(&b, "neuroforge_job_payload_bytes", orch.TerminalPayloadBytes, "state", "terminal") promHeader(&b, "neuroforge_jobs_by_resource", "Current durable orchestrator jobs by resource class.", "gauge") for resource, n := range orch.JobsByResource { promSample(&b, "neuroforge_jobs_by_resource", n, "resource", resource) diff --git a/platform/neuroforge/internal/research/searxng.go b/platform/neuroforge/internal/research/searxng.go index 0ed4bfc..f527010 100644 --- a/platform/neuroforge/internal/research/searxng.go +++ b/platform/neuroforge/internal/research/searxng.go @@ -169,7 +169,7 @@ func FetchResource(ctx context.Context, cfg FetchConfig, rawURL string) (Resourc req.Header.Set("Accept", "text/html,application/xhtml+xml,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/plain,text/markdown,text/csv,application/json;q=0.9,*/*;q=0.2") ua := strings.TrimSpace(cfg.UserAgent) if ua == "" { - ua = "NeuroForge/0.8.2 research bot" + ua = "NeuroForge/0.8.3 research bot" } req.Header.Set("User-Agent", ua) resp, err := client.Do(req) diff --git a/platform/neuroforge/internal/store/checkpoint_order_test.go b/platform/neuroforge/internal/store/checkpoint_order_test.go new file mode 100644 index 0000000..455f27c --- /dev/null +++ b/platform/neuroforge/internal/store/checkpoint_order_test.go @@ -0,0 +1,56 @@ +package store + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "neuroforge/internal/core" +) + +func readCheckpointRevision(t *testing.T, dir string) uint64 { + t.Helper() + b, err := os.ReadFile(filepath.Join(dir, "state.json")) + if err != nil { + t.Fatal(err) + } + var st core.PersistedState + if err := json.Unmarshal(b, &st); err != nil { + t.Fatal(err) + } + return st.Revision +} + +func TestCheckpointDoesNotAdvanceStateBeforeIndexSnapshotSucceeds(t *testing.T) { + dir := t.TempDir() + s, err := New(dir) + if err != nil { + t.Fatal(err) + } + cfg := s.Config() + cfg.Storage.CheckpointEvery = 1 + if err := s.UpdateConfig(cfg); err != nil { + t.Fatal(err) + } + before := readCheckpointRevision(t, dir) + + idxDir := filepath.Join(dir, "hnsw-index") + if err := os.RemoveAll(idxDir); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(idxDir, []byte("block directory creation"), 0600); err != nil { + t.Fatal(err) + } + m := &core.Memory{ID: "m1", Kind: core.MemorySemantic, MemoryType: core.MemorySemantic, Text: "x", Vector: []float32{1, 0, 0}, VectorDim: 3, Status: core.MemoryActive} + if err := s.AddMemory(m); err == nil { + t.Fatal("expected checkpoint/index failure") + } + after := readCheckpointRevision(t, dir) + if after != before { + t.Fatalf("state checkpoint advanced despite index failure: before=%d after=%d", before, after) + } + + _ = os.Remove(idxDir) + _ = s.Close() +} diff --git a/platform/neuroforge/internal/store/index_segments.go b/platform/neuroforge/internal/store/index_segments.go index 63a59b0..910b562 100644 --- a/platform/neuroforge/internal/store/index_segments.go +++ b/platform/neuroforge/internal/store/index_segments.go @@ -211,41 +211,28 @@ func (s *Store) writeSegmentedIndexSnapshotLocked() error { return nil } - current := s.currentSnapshotsLocked() + currentShadow := make(map[int]indexSnapshotShadow, len(s.indexes)) delta := indexDeltaBundle{Revision: s.state.Revision, Dimensions: map[string]indexDimensionDelta{}} dims := map[int]bool{} - for dim := range current { + for dim := range s.indexes { dims[dim] = true } for dim := range s.indexShadow { dims[dim] = true } for dim := range dims { - cur, curOK := current[dim] + idx, curOK := s.indexes[dim] prev, prevOK := s.indexShadow[dim] key := strconv.Itoa(dim) - if !curOK { + if !curOK || idx == nil { delta.Dimensions[key] = indexDimensionDelta{DeletedDimension: true} continue } - d := indexDimensionDelta{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel} - curIDs := map[string]bool{} - for _, n := range cur.Nodes { - curIDs[n.ID] = true - h := hashSnapshotNode(n) - if ph, ok := prev.Nodes[n.ID]; !prevOK || !ok || ph != h { - d.Upserts = append(d.Upserts, n) - } - } - if prevOK { - for id := range prev.Nodes { - if !curIDs[id] { - d.Deletes = append(d.Deletes, id) - } - } - } - sort.Strings(d.Deletes) - if !prevOK || len(d.Upserts) > 0 || len(d.Deletes) > 0 || prev.EntryID != cur.EntryID || prev.MaxLevel != cur.MaxLevel || prev.Config != cur.Config { + vprev := vector.HNSWShadow{Config: prev.Config, EntryID: prev.EntryID, MaxLevel: prev.MaxLevel, Nodes: prev.Nodes} + cur, upserts, deletes := idx.Delta(vprev) + currentShadow[dim] = indexSnapshotShadow{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel, Nodes: cur.Nodes} + d := indexDimensionDelta{Config: cur.Config, EntryID: cur.EntryID, MaxLevel: cur.MaxLevel, Upserts: upserts, Deletes: deletes} + if !prevOK || len(upserts) > 0 || len(deletes) > 0 || prev.EntryID != cur.EntryID || prev.MaxLevel != cur.MaxLevel || prev.Config != cur.Config { delta.Dimensions[key] = d } } @@ -258,7 +245,7 @@ func (s *Store) writeSegmentedIndexSnapshotLocked() error { if err := writeAtomic(manifestPath, 0600, &manifest); err != nil { return err } - s.indexShadow = buildIndexShadow(current) + s.indexShadow = currentShadow s.indexSnapshotRevision = s.state.Revision s.indexDeltaCount = len(manifest.Deltas) return nil diff --git a/platform/neuroforge/internal/store/orchestrator.go b/platform/neuroforge/internal/store/orchestrator.go index f3b6368..2efe0f4 100644 --- a/platform/neuroforge/internal/store/orchestrator.go +++ b/platform/neuroforge/internal/store/orchestrator.go @@ -43,18 +43,20 @@ type WorkerHeartbeat struct { } type OrchestratorStatus struct { - Workers []core.WorkerState `json:"workers"` - JobsByStatus map[string]int `json:"jobs_by_status"` - JobsByType map[string]int `json:"jobs_by_type"` - JobsByResource map[string]int `json:"jobs_by_resource"` - OldestQueued time.Time `json:"oldest_queued,omitempty"` - Queued int `json:"queued"` - Claimed int `json:"claimed"` - Retrying int `json:"retrying"` - Applying int `json:"applying"` - Blocked int `json:"blocked"` - Failed int `json:"failed"` - Done int `json:"done"` + Workers []core.WorkerState `json:"workers"` + JobsByStatus map[string]int `json:"jobs_by_status"` + JobsByType map[string]int `json:"jobs_by_type"` + JobsByResource map[string]int `json:"jobs_by_resource"` + OldestQueued time.Time `json:"oldest_queued,omitempty"` + Queued int `json:"queued"` + Claimed int `json:"claimed"` + Retrying int `json:"retrying"` + Applying int `json:"applying"` + Blocked int `json:"blocked"` + Failed int `json:"failed"` + Done int `json:"done"` + PendingPayloadBytes int64 `json:"pending_payload_bytes"` + TerminalPayloadBytes int64 `json:"terminal_payload_bytes"` } func normalizeCapabilities(in []string) []string { @@ -104,15 +106,22 @@ func (s *Store) EnqueueJobSpec(spec JobSpec) (*core.Job, error) { s.mu.Lock() defer s.mu.Unlock() cfg := s.state.Config.Worker - if cfg.MaxQueuedJobs > 0 { - pending := 0 - for _, j := range s.state.Jobs { - if j != nil && (j.Status == "queued" || j.Status == "claimed" || j.Status == "retry_wait" || j.Status == "blocked" || j.Status == "apply_wait") { - pending++ - } + pending := 0 + var pendingPayloadBytes int64 + for _, j := range s.state.Jobs { + if j == nil || (j.Status != "queued" && j.Status != "claimed" && j.Status != "retry_wait" && j.Status != "blocked" && j.Status != "apply_wait") { + continue } - if pending >= cfg.MaxQueuedJobs { - return nil, fmt.Errorf("orchestrator queue full: %d >= %d", pending, cfg.MaxQueuedJobs) + pending++ + pendingPayloadBytes += int64(len(j.Payload) + len(j.Result)) + } + if cfg.MaxQueuedJobs > 0 && pending >= cfg.MaxQueuedJobs { + return nil, fmt.Errorf("orchestrator queue full: %d >= %d", pending, cfg.MaxQueuedJobs) + } + if cfg.MaxQueuedPayloadMB > 0 { + limit := int64(cfg.MaxQueuedPayloadMB) << 20 + if pendingPayloadBytes+int64(len(b)) > limit { + return nil, fmt.Errorf("orchestrator queued payload budget exceeded: %d + %d > %d bytes", pendingPayloadBytes, len(b), limit) } } if key := strings.TrimSpace(spec.IdempotencyKey); key != "" { @@ -603,6 +612,13 @@ func (s *Store) OrchestratorStatus() OrchestratorStatus { if j == nil { continue } + payloadBytes := int64(len(j.Payload) + len(j.Result)) + switch j.Status { + case "queued", "claimed", "retry_wait", "blocked", "apply_wait": + out.PendingPayloadBytes += payloadBytes + case "done", "failed", "canceled": + out.TerminalPayloadBytes += payloadBytes + } out.JobsByStatus[j.Status]++ out.JobsByType[j.Type]++ resource := j.ResourceClass @@ -701,6 +717,15 @@ func (s *Store) FinishMasterApply(id string, applyErr error) (*core.Job, error) j.ApplyError = "" j.ApplyNextAttemptAt = time.Time{} j.FinishedAt = now + // vector.relink payloads contain the target plus a bounded set of full + // candidate vectors. Once the authoritative master apply succeeded these + // blobs have no retry value and retaining thousands of them can consume + // gigabytes during a large graph backfill. Keep the durable audit metadata + // (type/idempotency/status/timestamps) but release the transient vectors. + if j.Type == "vector.relink" { + j.Payload = nil + j.Result = nil + } } else { j.ApplyError = strings.TrimSpace(applyErr.Error()) maxAttempts := j.MaxApplyAttempts @@ -732,6 +757,41 @@ func (s *Store) FinishMasterApply(id string, applyErr error) (*core.Job, error) return &cp, s.commitLocked("job.upsert", cp) } +// compactCompletedRelinkJobsLocked removes transient vector blobs from jobs +// that already reached their durable terminal state. It is also used on boot to +// migrate v1.6.0 checkpoints that may contain thousands of completed backfill +// payloads. Caller must hold s.mu. +func (s *Store) compactCompletedRelinkJobsLocked() int { + n := 0 + for _, j := range s.state.Jobs { + if j == nil || j.Type != "vector.relink" || j.Status != "done" { + continue + } + if len(j.Payload) == 0 && len(j.Result) == 0 { + continue + } + j.Payload = nil + j.Result = nil + n++ + } + return n +} + +func (s *Store) CompactCompletedRelinkJobs() (int, error) { + s.mu.Lock() + defer s.mu.Unlock() + n := s.compactCompletedRelinkJobsLocked() + if n == 0 { + return 0, nil + } + // One checkpoint is substantially cheaper than one WAL event per historical + // job and atomically rewrites state.json without the obsolete vector blobs. + if err := s.checkpointLocked(); err != nil { + return 0, err + } + return n, nil +} + func (s *Store) CancelJob(id, reason string) error { s.mu.Lock() defer s.mu.Unlock() diff --git a/platform/neuroforge/internal/store/orchestrator_test.go b/platform/neuroforge/internal/store/orchestrator_test.go index 16ca3a8..202bf8a 100644 --- a/platform/neuroforge/internal/store/orchestrator_test.go +++ b/platform/neuroforge/internal/store/orchestrator_test.go @@ -292,3 +292,52 @@ func TestApplyWaitJobSurvivesRestart(t *testing.T) { t.Fatalf("recovered master apply queue=%+v", pending) } } + +func TestOrchestratorQueuedPayloadBudget(t *testing.T) { + s, err := New(t.TempDir()) + if err != nil { + t.Fatal(err) + } + defer s.Close() + s.mu.Lock() + s.state.Config.Worker.MaxQueuedPayloadMB = 1 + s.mu.Unlock() + blob := strings.Repeat("x", 700<<10) + if _, err := s.EnqueueJobSpec(JobSpec{Type: "large", Payload: map[string]any{"blob": blob}, ResourceClass: "cpu"}); err != nil { + t.Fatal(err) + } + if _, err := s.EnqueueJobSpec(JobSpec{Type: "large2", Payload: map[string]any{"blob": blob}, ResourceClass: "cpu"}); err == nil || !strings.Contains(err.Error(), "payload budget") { + t.Fatalf("expected payload budget rejection, err=%v", err) + } +} + +func TestCompletedRelinkDropsTransientVectorBlobs(t *testing.T) { + s, err := New(t.TempDir()) + if err != nil { + t.Fatal(err) + } + defer s.Close() + j, err := s.EnqueueJobSpec(JobSpec{Type: "vector.relink", Payload: map[string]any{"target": []float32{1, 2, 3}}, ResourceClass: "cpu", RequiredCapabilities: []string{"cpu", "vector.relink"}, RequiresMasterApply: true}) + if err != nil { + t.Fatal(err) + } + w := WorkerHeartbeat{ID: "cpu", ResourceClass: "cpu", Capabilities: []string{"cpu", "vector.relink"}, MaxConcurrency: 1} + claimed, err := s.ClaimJobForWorker(w, time.Minute) + if err != nil || claimed == nil || claimed.ID != j.ID { + t.Fatalf("claim=%+v err=%v", claimed, err) + } + waiting, err := s.CompleteJobLease(j.ID, w.ID, claimed.LeaseToken, json.RawMessage(`{"target_id":"x","neighbors":[]}`), "") + if err != nil { + t.Fatal(err) + } + if waiting.Status != "apply_wait" { + t.Fatalf("status=%s", waiting.Status) + } + done, err := s.FinishMasterApply(j.ID, nil) + if err != nil { + t.Fatal(err) + } + if done.Status != "done" || len(done.Payload) != 0 || len(done.Result) != 0 { + t.Fatalf("done job retained transient blobs: %+v", done) + } +} diff --git a/platform/neuroforge/internal/store/recovery_hardening_test.go b/platform/neuroforge/internal/store/recovery_hardening_test.go new file mode 100644 index 0000000..59b69f0 --- /dev/null +++ b/platform/neuroforge/internal/store/recovery_hardening_test.go @@ -0,0 +1,120 @@ +package store + +import ( + "bufio" + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "neuroforge/internal/core" +) + +func TestNewFailsClosedOnCorruptAuthoritativeState(t *testing.T) { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, v161JobCompactionMarker), []byte("compacted=0\n"), 0600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "state.json"), []byte(`{"config":`), 0600); err != nil { + t.Fatal(err) + } + if _, err := New(dir); err == nil || !strings.Contains(err.Error(), "load authoritative state checkpoint") { + t.Fatalf("New error=%v, want authoritative state corruption failure", err) + } +} + +func TestNewFailsClosedOnCorruptAuthoritativeSecrets(t *testing.T) { + dir := t.TempDir() + st := core.PersistedState{ + Config: core.DefaultConfig(), + Memories: map[string]*core.Memory{}, + Synapses: map[string]*core.Synapse{}, + Jobs: map[string]*core.Job{}, + Goals: map[string]*core.Goal{}, + Sources: map[string]*core.KnowledgeSource{}, + ResearchRuns: map[string]*core.ResearchRun{}, + } + if err := writeAtomic(filepath.Join(dir, "state.json"), 0600, &st); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "secrets.json"), []byte(`{"admin_token":"unterminated}`), 0600); err != nil { + t.Fatal(err) + } + if _, err := New(dir); err == nil || !strings.Contains(err.Error(), "load authoritative secrets") { + t.Fatalf("New error=%v, want authoritative secrets corruption failure", err) + } +} + +func TestLoadJSONRejectsTrailingJSONValue(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "state.json") + if err := os.WriteFile(path, []byte(`{"revision":1}{"revision":2}`), 0600); err != nil { + t.Fatal(err) + } + s := &Store{} + var st core.PersistedState + if err := s.loadJSON(path, &st); err == nil || !strings.Contains(err.Error(), "unexpected trailing JSON token") { + t.Fatalf("loadJSON error=%v, want trailing JSON rejection", err) + } +} + +func TestReplayWALCompactsCompletedRelinkPayloadImmediately(t *testing.T) { + dir := t.TempDir() + walDir := filepath.Join(dir, "wal") + if err := os.MkdirAll(walDir, 0700); err != nil { + t.Fatal(err) + } + job := core.Job{ + ID: "job_done_relink", + Type: "vector.relink", + Status: "done", + Payload: json.RawMessage(`{"target":{"vector":[1,2,3]},"candidates":[{"vector":[4,5,6]}]}`), + Result: json.RawMessage(`{"edges":[{"a":"a","b":"b","weight":0.9}]}`), + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + } + data, err := json.Marshal(job) + if err != nil { + t.Fatal(err) + } + ev := walEvent{Revision: 1, Time: time.Now().UTC(), Type: "job.upsert", Data: data} + f, err := os.OpenFile(filepath.Join(walDir, "wal-active.jsonl"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + t.Fatal(err) + } + bw := bufio.NewWriter(f) + if err := json.NewEncoder(bw).Encode(ev); err != nil { + t.Fatal(err) + } + if err := bw.Flush(); err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + s, err := New(dir) + if err != nil { + t.Fatal(err) + } + defer s.Close() + got, ok := s.Job(job.ID) + if !ok { + t.Fatal("replayed job missing") + } + if len(got.Payload) != 0 || len(got.Result) != 0 { + t.Fatalf("replayed completed relink retained vector blobs: payload=%d result=%d", len(got.Payload), len(got.Result)) + } +} + +func TestFreshDirectoryDoesNotConsumeV161CompactionMarker(t *testing.T) { + dir := t.TempDir() + if n, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil || n != 0 { + t.Fatalf("compact fresh dir = %d, %v", n, err) + } + if _, err := os.Stat(filepath.Join(dir, v161JobCompactionMarker)); !os.IsNotExist(err) { + t.Fatalf("fresh directory unexpectedly created migration marker: %v", err) + } +} diff --git a/platform/neuroforge/internal/store/startup_compact.go b/platform/neuroforge/internal/store/startup_compact.go new file mode 100644 index 0000000..ea78fd8 --- /dev/null +++ b/platform/neuroforge/internal/store/startup_compact.go @@ -0,0 +1,277 @@ +package store + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + + "neuroforge/internal/core" +) + +const v161JobCompactionMarker = ".migration-v1.6.1-terminal-relink-compaction" + +// compactLegacyTerminalRelinkCheckpoint is a one-time, streaming migration for +// v1.6.0 checkpoints. That release retained full target/candidate vectors in +// completed vector.relink jobs. A large graph backfill can therefore make +// state.json hundreds of MB or larger and cause OOM during the next startup. +// +// The migration deliberately runs before state.json is unmarshaled. It rewrites +// JSON token-by-token and only materializes one Job at a time, so peak memory is +// bounded by the largest single job instead of the whole checkpoint. +func compactLegacyTerminalRelinkCheckpoint(dir string) (int, error) { + marker := filepath.Join(dir, v161JobCompactionMarker) + if _, err := os.Stat(marker); err == nil { + return 0, nil + } + path := filepath.Join(dir, "state.json") + in, err := os.Open(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // Fresh data directory. Do not create the migration marker yet: an + // operator may restore a v1.6.0 checkpoint into this directory before + // the next boot, and that restored checkpoint must still be compacted. + return 0, nil + } + return 0, err + } + defer in.Close() + + tmp := path + ".v161-compact.tmp" + out, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + if err != nil { + return 0, err + } + ok := false + defer func() { + _ = out.Close() + if !ok { + _ = os.Remove(tmp) + } + }() + + dec := json.NewDecoder(bufio.NewReaderSize(in, 1<<20)) + dec.UseNumber() + bw := bufio.NewWriterSize(out, 1<<20) + compacted, err := rewriteCheckpointObject(dec, bw) + if err != nil { + return 0, err + } + if err := bw.Flush(); err != nil { + return 0, err + } + if err := out.Sync(); err != nil { + return 0, err + } + if err := out.Close(); err != nil { + return 0, err + } + if compacted > 0 { + if err := os.Rename(tmp, path); err != nil { + return 0, err + } + } else { + _ = os.Remove(tmp) + } + if err := os.WriteFile(marker, []byte(fmt.Sprintf("compacted=%d\n", compacted)), 0600); err != nil { + return 0, err + } + ok = true + return compacted, nil +} + +func rewriteCheckpointObject(dec *json.Decoder, w *bufio.Writer) (int, error) { + tok, err := dec.Token() + if err != nil { + return 0, err + } + if d, ok := tok.(json.Delim); !ok || d != '{' { + return 0, errors.New("state checkpoint must be a JSON object") + } + if err := w.WriteByte('{'); err != nil { + return 0, err + } + first := true + compacted := 0 + for dec.More() { + kt, err := dec.Token() + if err != nil { + return 0, err + } + key, ok := kt.(string) + if !ok { + return 0, errors.New("state checkpoint object key is not a string") + } + if !first { + if err := w.WriteByte(','); err != nil { + return 0, err + } + } + first = false + kb, _ := json.Marshal(key) + if _, err := w.Write(kb); err != nil { + return 0, err + } + if err := w.WriteByte(':'); err != nil { + return 0, err + } + if key == "jobs" { + n, err := rewriteJobsObject(dec, w) + if err != nil { + return 0, err + } + compacted += n + continue + } + if err := copyJSONValue(dec, w); err != nil { + return 0, err + } + } + if _, err := dec.Token(); err != nil { // closing } + return 0, err + } + if err := w.WriteByte('}'); err != nil { + return 0, err + } + if tok, err := dec.Token(); err != io.EOF { + if err == nil { + return 0, fmt.Errorf("unexpected trailing JSON token %v", tok) + } + return 0, err + } + return compacted, nil +} + +func rewriteJobsObject(dec *json.Decoder, w *bufio.Writer) (int, error) { + tok, err := dec.Token() + if err != nil { + return 0, err + } + if tok == nil { + _, err = w.WriteString("null") + return 0, err + } + if d, ok := tok.(json.Delim); !ok || d != '{' { + return 0, errors.New("jobs must be a JSON object") + } + if err := w.WriteByte('{'); err != nil { + return 0, err + } + first := true + compacted := 0 + for dec.More() { + kt, err := dec.Token() + if err != nil { + return 0, err + } + key := kt.(string) + var job core.Job + if err := dec.Decode(&job); err != nil { + return 0, err + } + if job.Type == "vector.relink" && job.Status == "done" { + if len(job.Payload) > 0 || len(job.Result) > 0 { + compacted++ + } + job.Payload = nil + job.Result = nil + } + if !first { + if err := w.WriteByte(','); err != nil { + return 0, err + } + } + first = false + kb, _ := json.Marshal(key) + jb, err := json.Marshal(job) + if err != nil { + return 0, err + } + if _, err := w.Write(kb); err != nil { + return 0, err + } + if err := w.WriteByte(':'); err != nil { + return 0, err + } + if _, err := w.Write(jb); err != nil { + return 0, err + } + } + if _, err := dec.Token(); err != nil { + return 0, err + } + return compacted, w.WriteByte('}') +} + +func copyJSONValue(dec *json.Decoder, w *bufio.Writer) error { + tok, err := dec.Token() + if err != nil { + return err + } + if d, ok := tok.(json.Delim); ok { + switch d { + case '{': + if err := w.WriteByte('{'); err != nil { + return err + } + first := true + for dec.More() { + kt, err := dec.Token() + if err != nil { + return err + } + if !first { + if err := w.WriteByte(','); err != nil { + return err + } + } + first = false + kb, _ := json.Marshal(kt.(string)) + if _, err := w.Write(kb); err != nil { + return err + } + if err := w.WriteByte(':'); err != nil { + return err + } + if err := copyJSONValue(dec, w); err != nil { + return err + } + } + if _, err := dec.Token(); err != nil { + return err + } + return w.WriteByte('}') + case '[': + if err := w.WriteByte('['); err != nil { + return err + } + first := true + for dec.More() { + if !first { + if err := w.WriteByte(','); err != nil { + return err + } + } + first = false + if err := copyJSONValue(dec, w); err != nil { + return err + } + } + if _, err := dec.Token(); err != nil { + return err + } + return w.WriteByte(']') + default: + return fmt.Errorf("unexpected JSON delimiter %q", d) + } + } + b, err := json.Marshal(tok) + if err != nil { + return err + } + _, err = w.Write(b) + return err +} diff --git a/platform/neuroforge/internal/store/startup_compact_test.go b/platform/neuroforge/internal/store/startup_compact_test.go new file mode 100644 index 0000000..b756846 --- /dev/null +++ b/platform/neuroforge/internal/store/startup_compact_test.go @@ -0,0 +1,56 @@ +package store + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "neuroforge/internal/core" +) + +func TestCompactLegacyTerminalRelinkCheckpoint(t *testing.T) { + dir := t.TempDir() + st := core.PersistedState{Config: core.DefaultConfig(), Jobs: map[string]*core.Job{}, Synapses: map[string]*core.Synapse{}, Goals: map[string]*core.Goal{}} + st.Jobs["done"] = &core.Job{ID: "done", Type: "vector.relink", Status: "done", Payload: json.RawMessage(`{"target":[1,2,3]}`), Result: json.RawMessage(`{"ok":true}`)} + st.Jobs["queued"] = &core.Job{ID: "queued", Type: "vector.relink", Status: "queued", Payload: json.RawMessage(`{"target":[4,5,6]}`)} + st.Jobs["other"] = &core.Job{ID: "other", Type: "model.chat", Status: "done", Payload: json.RawMessage(`{"prompt":"keep"}`), Result: json.RawMessage(`{"text":"keep"}`)} + b, err := json.Marshal(st) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "state.json"), b, 0600); err != nil { + t.Fatal(err) + } + + n, err := compactLegacyTerminalRelinkCheckpoint(dir) + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Fatalf("compacted=%d want 1", n) + } + + var got core.PersistedState + bb, err := os.ReadFile(filepath.Join(dir, "state.json")) + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(bb, &got); err != nil { + t.Fatal(err) + } + cleared := func(b json.RawMessage) bool { return len(b) == 0 || string(b) == "null" } + if !cleared(got.Jobs["done"].Payload) || !cleared(got.Jobs["done"].Result) { + t.Fatalf("done relink blobs not cleared: payload=%q result=%q", got.Jobs["done"].Payload, got.Jobs["done"].Result) + } + if len(got.Jobs["queued"].Payload) == 0 { + t.Fatalf("queued relink payload must be retained") + } + if len(got.Jobs["other"].Payload) == 0 || len(got.Jobs["other"].Result) == 0 { + t.Fatalf("unrelated job blobs must be retained") + } + + if n2, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil || n2 != 0 { + t.Fatalf("second migration = %d, %v", n2, err) + } +} diff --git a/platform/neuroforge/internal/store/store.go b/platform/neuroforge/internal/store/store.go index 8445572..0beccc6 100644 --- a/platform/neuroforge/internal/store/store.go +++ b/platform/neuroforge/internal/store/store.go @@ -1,11 +1,13 @@ package store import ( + "bufio" "crypto/rand" "encoding/hex" "encoding/json" "errors" "fmt" + "io" "math" "net/url" "os" @@ -49,7 +51,19 @@ type Store struct { synapseAdj map[string]map[string]*core.Synapse } +type OpenProgressFunc func(phase string) + func New(dir string) (*Store, error) { + return NewWithProgress(dir, nil) +} + +func NewWithProgress(dir string, report OpenProgressFunc) (*Store, error) { + progress := func(phase string) { + if report != nil { + report(phase) + } + } + progress("store.prepare") if dir == "" { dir = "./data" } @@ -58,8 +72,18 @@ func New(dir string) (*Store, error) { } s := &Store{dir: dir, indexes: map[int]*vector.HNSW{}, diskIndexes: map[int]*vector.PQIndex{}, provenanceSourceIDs: map[string]map[string]struct{}{}, workers: map[string]core.WorkerState{}, synapseAdj: map[string]map[string]*core.Synapse{}} s.state = core.PersistedState{Config: core.DefaultConfig(), Memories: map[string]*core.Memory{}, Synapses: map[string]*core.Synapse{}, Jobs: map[string]*core.Job{}, Goals: map[string]*core.Goal{}, Sources: map[string]*core.KnowledgeSource{}, ResearchRuns: map[string]*core.ResearchRun{}} - _ = s.loadJSON(filepath.Join(dir, "state.json"), &s.state) - _ = s.loadJSON(filepath.Join(dir, "secrets.json"), &s.secrets) + progress("checkpoint.precompact") + if _, err := compactLegacyTerminalRelinkCheckpoint(dir); err != nil { + return nil, fmt.Errorf("compact legacy terminal relink jobs: %w", err) + } + progress("checkpoint.load") + if err := s.loadJSON(filepath.Join(dir, "state.json"), &s.state); err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("load authoritative state checkpoint: %w", err) + } + progress("secrets.load") + if err := s.loadJSON(filepath.Join(dir, "secrets.json"), &s.secrets); err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("load authoritative secrets: %w", err) + } if s.state.Memories == nil { s.state.Memories = map[string]*core.Memory{} } @@ -84,6 +108,7 @@ func New(dir string) (*Store, error) { // v0.6 binary vector sidecar: rebuildable acceleration data used by the // disk ANN builder. Corruption must never prevent the authoritative memory // store from opening; move a bad cache aside and recreate it empty. + progress("vector-journal.open") vjPath := filepath.Join(dir, "vector-journal.nfv") vj, vjErr := openVectorJournal(vjPath, vectorJournalOptionsFromConfig(s.state.Config)) if vjErr != nil { @@ -95,6 +120,7 @@ func New(dir string) (*Store, error) { } if s.state.Config.Storage.Segments.Enabled { + progress("memory-segments.scan") seg, err := openSegmentStore(filepath.Join(dir, "memory-segments"), s.state.Config.Storage.Segments.MaxSegmentBytes, s.state.Config.Storage.Segments.MmapSealed) if err != nil { return nil, fmt.Errorf("open memory segments: %w", err) @@ -110,9 +136,13 @@ func New(dir string) (*Store, error) { s.state.Memories = meta } } + progress("wal.replay") if err := s.replayWAL(); err != nil { return nil, err } + progress("jobs.compact") + _ = s.compactCompletedRelinkJobsLocked() + progress("graph.restore") s.rebuildSynapseAdjLocked() applyNewDefaults(&s.state.Config) if s.state.Cluster.Term < s.state.Config.Cluster.Term { @@ -198,19 +228,26 @@ func New(dir string) (*Store, error) { if s.secrets.ClusterToken == "" { s.secrets.ClusterToken = randomID(24) } + progress("disk-ann.load") _ = s.loadDiskANNLocked() + progress("hnsw.snapshot.load") if !s.loadIndexSnapshotLocked() { + progress("hnsw.rebuild") s.rebuildIndexesLocked() } + progress("secrets.persist") if err := s.persistSecretsLocked(); err != nil { return nil, err } + progress("checkpoint.write") if err := s.checkpointLocked(); err != nil { return nil, err } if s.state.Config.Storage.Tiering.Enabled && s.segments != nil { + progress("tiering.initialize") s.tierMemoryBodiesLocked(time.Now().UTC()) } + progress("store.ready") return s, nil } @@ -524,6 +561,9 @@ func applyNewDefaults(c *core.Config) { if c.Worker.MaxQueuedJobs == 0 { c.Worker.MaxQueuedJobs = d.Worker.MaxQueuedJobs } + if c.Worker.MaxQueuedPayloadMB == 0 { + c.Worker.MaxQueuedPayloadMB = d.Worker.MaxQueuedPayloadMB + } if c.Worker.MasterApplyMaxAttempts == 0 { c.Worker.MasterApplyMaxAttempts = d.Worker.MasterApplyMaxAttempts } @@ -613,23 +653,80 @@ func inferMemoryType(kind string) string { } func (s *Store) loadJSON(path string, v any) error { - b, err := os.ReadFile(path) + f, err := os.Open(path) if err != nil { return err } - return json.Unmarshal(b, v) + defer f.Close() + + // Decode directly from the file instead of ReadFile+Unmarshal. Large + // checkpoints can contain millions of graph edges/jobs; keeping a second + // raw []byte copy of state.json during boot needlessly doubles peak memory. + dec := json.NewDecoder(bufio.NewReaderSize(f, 1<<20)) + if err := dec.Decode(v); err != nil { + return fmt.Errorf("decode %s: %w", filepath.Base(path), err) + } + // Reject a second JSON value/trailing non-whitespace. Silently accepting a + // partially corrupt authoritative checkpoint can create split-brain state. + if tok, err := dec.Token(); err != io.EOF { + if err == nil { + return fmt.Errorf("decode %s: unexpected trailing JSON token %v", filepath.Base(path), tok) + } + return fmt.Errorf("decode %s trailing data: %w", filepath.Base(path), err) + } + return nil } -func writeAtomic(path string, perm os.FileMode, v any) error { - b, err := json.MarshalIndent(v, "", " ") +func writeAtomic(path string, perm os.FileMode, v any) (retErr error) { + // Stream JSON directly into the temporary file instead of MarshalIndent+ + // WriteFile. state.json and index manifests can be large; allocating the + // complete encoded checkpoint as another in-memory byte slice is avoidable. + tmp := path + ".tmp" + _ = os.Remove(tmp) + f, err := os.OpenFile(tmp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm) if err != nil { return err } - tmp := path + ".tmp" - if err := os.WriteFile(tmp, b, perm); err != nil { + closed := false + defer func() { + if !closed { + _ = f.Close() + } + if retErr != nil { + _ = os.Remove(tmp) + } + }() + + bw := bufio.NewWriterSize(f, 1<<20) + enc := json.NewEncoder(bw) + enc.SetIndent("", " ") + if err := enc.Encode(v); err != nil { return err } - return os.Rename(tmp, path) + if err := bw.Flush(); err != nil { + return err + } + if err := f.Sync(); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + closed = true + if err := os.Rename(tmp, path); err != nil { + return err + } + // Persist the directory entry as well. This matters for secrets/state after + // host power loss and is cheap compared with the checkpoint itself. + d, err := os.Open(filepath.Dir(path)) + if err != nil { + return err + } + if err := d.Sync(); err != nil { + _ = d.Close() + return err + } + return d.Close() } func (s *Store) persistLocked() error { @@ -1609,7 +1706,7 @@ func (s *Store) validateConfigLocked(c core.Config) error { if c.Worker.LeaseSeconds < 10 || c.Worker.LeaseSeconds > 3600 || c.Worker.HeartbeatSeconds < 2 || c.Worker.HeartbeatSeconds >= c.Worker.LeaseSeconds || c.Worker.StaleAfterSeconds < c.Worker.HeartbeatSeconds || c.Worker.StaleAfterSeconds > 7200 { return errors.New("invalid worker lease/heartbeat/stale timing") } - if c.Worker.DefaultMaxAttempts < 1 || c.Worker.DefaultMaxAttempts > 20 || c.Worker.RetryBackoffSeconds < 1 || c.Worker.RetryBackoffSeconds > 3600 || c.Worker.MaxQueuedJobs < 16 || c.Worker.MaxQueuedJobs > 1000000 || c.Worker.MasterApplyMaxAttempts < 1 || c.Worker.MasterApplyMaxAttempts > 20 || c.Worker.MasterApplyBackoffSeconds < 1 || c.Worker.MasterApplyBackoffSeconds > 3600 || c.Worker.JobRetentionHours < 1 || c.Worker.JobRetentionHours > 8760 || c.Worker.MaxTerminalJobs < 100 || c.Worker.MaxTerminalJobs > 1000000 { + if c.Worker.DefaultMaxAttempts < 1 || c.Worker.DefaultMaxAttempts > 20 || c.Worker.RetryBackoffSeconds < 1 || c.Worker.RetryBackoffSeconds > 3600 || c.Worker.MaxQueuedJobs < 16 || c.Worker.MaxQueuedJobs > 1000000 || c.Worker.MaxQueuedPayloadMB < 16 || c.Worker.MaxQueuedPayloadMB > 65536 || c.Worker.MasterApplyMaxAttempts < 1 || c.Worker.MasterApplyMaxAttempts > 20 || c.Worker.MasterApplyBackoffSeconds < 1 || c.Worker.MasterApplyBackoffSeconds > 3600 || c.Worker.JobRetentionHours < 1 || c.Worker.JobRetentionHours > 8760 || c.Worker.MaxTerminalJobs < 100 || c.Worker.MaxTerminalJobs > 1000000 { return errors.New("invalid worker retry/queue/retention configuration") } if c.Worker.GraphBackfillIntervalS < 2 || c.Worker.GraphBackfillIntervalS > 3600 || c.Worker.GraphBackfillBatchSize < 1 || c.Worker.GraphBackfillBatchSize > 4096 || c.Worker.GraphBackfillMaxQueued < 1 || c.Worker.GraphBackfillMaxQueued > c.Worker.MaxQueuedJobs || c.Worker.GraphBackfillMinDegree < 1 || c.Worker.GraphBackfillMinDegree > 100 || c.Worker.GraphCandidateMultiplier < 2 || c.Worker.GraphCandidateMultiplier > 64 || c.Worker.GraphRetryAfterMinutes < 1 || c.Worker.GraphRetryAfterMinutes > 43200 { diff --git a/platform/neuroforge/internal/store/wal.go b/platform/neuroforge/internal/store/wal.go index afab3bc..4eac292 100644 --- a/platform/neuroforge/internal/store/wal.go +++ b/platform/neuroforge/internal/store/wal.go @@ -232,6 +232,14 @@ func (s *Store) applyWALEvent(ev walEvent) error { if err := json.Unmarshal(ev.Data, &x); err != nil { return err } + // v1.6.0 could leave a large WAL containing completed vector.relink + // jobs with full target/candidate vectors. Compact each terminal event as + // it is replayed so recovery memory remains bounded by one WAL record + // instead of accumulating every historical vector payload in state. + if x.Type == "vector.relink" && x.Status == "done" { + x.Payload = nil + x.Result = nil + } s.state.Jobs[x.ID] = &x case "job.delete": var ids []string @@ -332,14 +340,20 @@ func (s *Store) checkpointLocked() error { // Keep state.json O(non-memory-state) instead of O(memory-count). checkpoint.Memories = nil } - if err := writeAtomic(filepath.Join(s.dir, "state.json"), 0600, &checkpoint); err != nil { - return err - } + // Persist acceleration state before the authoritative non-memory checkpoint. + // If the process dies after the index snapshot but before state.json, the WAL + // remains intact; boot replays it to the same revision and can immediately use + // the already-written index. The previous order could advance state.json first, + // then die during a large HNSW snapshot and force a full synchronous rebuild on + // every restart. if s.state.Config.Storage.IndexSnapshot && s.state.Config.Brain.Index.Enabled { if err := s.writeIndexSnapshotLocked(); err != nil { return err } } + if err := writeAtomic(filepath.Join(s.dir, "state.json"), 0600, &checkpoint); err != nil { + return err + } // The checkpoint and (when enabled) memory segments now cover every WAL // event through state.Revision. Prune those already-checkpointed log files // only after all checkpoint artifacts succeeded, otherwise long bulk diff --git a/platform/neuroforge/internal/vector/hnsw.go b/platform/neuroforge/internal/vector/hnsw.go index a8ad362..8def7a1 100644 --- a/platform/neuroforge/internal/vector/hnsw.go +++ b/platform/neuroforge/internal/vector/hnsw.go @@ -679,6 +679,50 @@ type HNSWShadow struct { Nodes map[string][32]byte } +func (h *HNSW) nodeFingerprintLocked(n *hnswNode) [32]byte { + hash := sha256.New() + writeHashString(hash, n.ID) + writeHashU32(hash, uint32(n.Level)) + writeHashU32(hash, uint32(len(n.Vector))) + var b [4]byte + for _, x := range n.Vector { + binary.LittleEndian.PutUint32(b[:], math.Float32bits(x)) + _, _ = hash.Write(b[:]) + } + for level := 0; level <= n.Level; level++ { + writeHashU32(hash, uint32(level)) + edges := n.Neighbors[level] + writeHashU32(hash, uint32(len(edges))) + for _, edge := range edges { + idx := int(edge.idx) + if idx >= 0 && idx < len(h.nodes) { + writeHashString(hash, h.nodes[idx].ID) + } + } + } + var sum [32]byte + copy(sum[:], hash.Sum(nil)) + return sum +} + +func (h *HNSW) snapshotNodeLocked(n *hnswNode) HNSWSnapshotNode { + cn := HNSWSnapshotNode{ID: n.ID, Vector: append([]float32(nil), n.Vector...), Level: n.Level, Neighbors: map[int][]string{}} + for level, ids := range n.Neighbors { + if len(ids) == 0 { + continue + } + refs := make([]string, 0, len(ids)) + for _, edge := range ids { + idx := int(edge.idx) + if idx >= 0 && idx < len(h.nodes) { + refs = append(refs, h.nodes[idx].ID) + } + } + cn.Neighbors[level] = refs + } + return cn +} + func (h *HNSW) Shadow() HNSWShadow { h.mu.RLock() defer h.mu.RUnlock() @@ -688,33 +732,41 @@ func (h *HNSW) Shadow() HNSWShadow { } out := HNSWShadow{Config: h.cfg, EntryID: entryID, MaxLevel: h.maxLevel, Nodes: make(map[string][32]byte, len(h.nodes))} for _, n := range h.nodes { - hash := sha256.New() - writeHashString(hash, n.ID) - writeHashU32(hash, uint32(n.Level)) - writeHashU32(hash, uint32(len(n.Vector))) - var b [4]byte - for _, x := range n.Vector { - binary.LittleEndian.PutUint32(b[:], math.Float32bits(x)) - _, _ = hash.Write(b[:]) - } - for level := 0; level <= n.Level; level++ { - writeHashU32(hash, uint32(level)) - edges := n.Neighbors[level] - writeHashU32(hash, uint32(len(edges))) - for _, edge := range edges { - idx := int(edge.idx) - if idx >= 0 && idx < len(h.nodes) { - writeHashString(hash, h.nodes[idx].ID) - } - } - } - var sum [32]byte - copy(sum[:], hash.Sum(nil)) - out.Nodes[n.ID] = sum + out.Nodes[n.ID] = h.nodeFingerprintLocked(n) } return out } +// Delta returns only nodes whose vector/graph representation changed since prev, +// plus a compact hash shadow for the current graph. Unlike Snapshot(), this does +// not deep-copy every vector on each checkpoint, which keeps bulk-ingest memory +// bounded as the HNSW grows. +func (h *HNSW) Delta(prev HNSWShadow) (HNSWShadow, []HNSWSnapshotNode, []string) { + h.mu.RLock() + defer h.mu.RUnlock() + entryID := "" + if h.entry >= 0 && h.entry < len(h.nodes) { + entryID = h.nodes[h.entry].ID + } + cur := HNSWShadow{Config: h.cfg, EntryID: entryID, MaxLevel: h.maxLevel, Nodes: make(map[string][32]byte, len(h.nodes))} + upserts := make([]HNSWSnapshotNode, 0) + for _, n := range h.nodes { + fp := h.nodeFingerprintLocked(n) + cur.Nodes[n.ID] = fp + if old, ok := prev.Nodes[n.ID]; !ok || old != fp { + upserts = append(upserts, h.snapshotNodeLocked(n)) + } + } + deletes := make([]string, 0) + for id := range prev.Nodes { + if _, ok := cur.Nodes[id]; !ok { + deletes = append(deletes, id) + } + } + sort.Strings(deletes) + return cur, upserts, deletes +} + func writeHashString(w io.Writer, s string) { writeHashU32(w, uint32(len(s))) _, _ = io.WriteString(w, s) diff --git a/platform/neuroforge/internal/vector/hnsw_delta_test.go b/platform/neuroforge/internal/vector/hnsw_delta_test.go new file mode 100644 index 0000000..7e678ce --- /dev/null +++ b/platform/neuroforge/internal/vector/hnsw_delta_test.go @@ -0,0 +1,38 @@ +package vector + +import "testing" + +func TestHNSWDeltaDoesNotSnapshotUnchangedVectors(t *testing.T) { + h := NewHNSW(HNSWConfig{M: 4, EfConstruction: 16, EfSearch: 8}) + h.Add("a", []float32{1, 0, 0}) + h.Add("b", []float32{0.9, 0.1, 0}) + base := h.Shadow() + cur, upserts, deletes := h.Delta(base) + if len(upserts) != 0 || len(deletes) != 0 { + t.Fatalf("unchanged delta upserts=%d deletes=%d", len(upserts), len(deletes)) + } + if len(cur.Nodes) != 2 { + t.Fatalf("shadow nodes=%d", len(cur.Nodes)) + } + + h.Add("c", []float32{0.8, 0.2, 0}) + cur2, upserts, deletes := h.Delta(base) + if len(upserts) == 0 { + t.Fatalf("expected changed/new nodes") + } + if len(deletes) != 0 { + t.Fatalf("unexpected deletes: %v", deletes) + } + if len(cur2.Nodes) != 3 { + t.Fatalf("shadow nodes=%d", len(cur2.Nodes)) + } + foundC := false + for _, n := range upserts { + if n.ID == "c" { + foundC = true + } + } + if !foundC { + t.Fatalf("new node c missing from delta") + } +} diff --git a/platform/neuroforge/openapi.yaml b/platform/neuroforge/openapi.yaml index afbe818..155728a 100644 --- a/platform/neuroforge/openapi.yaml +++ b/platform/neuroforge/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: NeuroForge API - version: 0.8.2 + version: 0.8.3 description: REST API for NeuroForge associative learning, explainable vector recall, provenance, document/text ingestion, SearXNG-backed autonomous research, source-grounded goal cycles, responsive knowledge graph, model routing, cost controls, Prometheus diff --git a/runtime/agent-data/.gitkeep b/runtime/agent-data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/runtime/backups/.gitkeep b/runtime/backups/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/runtime/knowledge/.gitkeep b/runtime/knowledge/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/runtime/ollama/.gitkeep b/runtime/ollama/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/runtime/staging/.gitkeep b/runtime/staging/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/backup-data.sh b/scripts/backup-data.sh old mode 100644 new mode 100755 diff --git a/scripts/check-compose-env.py b/scripts/check-compose-env.py old mode 100644 new mode 100755 diff --git a/scripts/codebase-memory-ui.sh b/scripts/codebase-memory-ui.sh old mode 100644 new mode 100755 diff --git a/scripts/create-network.sh b/scripts/create-network.sh new file mode 100755 index 0000000..28d743e --- /dev/null +++ b/scripts/create-network.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -eu +NETWORK="${CORE_NETWORK:-glpi-ai-core}" +if docker network inspect "$NETWORK" >/dev/null 2>&1; then + echo "Docker-Netzwerk $NETWORK existiert bereits." +else + docker network create "$NETWORK" >/dev/null + echo "Docker-Netzwerk $NETWORK wurde angelegt." +fi diff --git a/scripts/export-obsidian.sh b/scripts/export-obsidian.sh old mode 100644 new mode 100755 diff --git a/scripts/generate-secrets.sh b/scripts/generate-secrets.sh old mode 100644 new mode 100755 diff --git a/scripts/go-live.sh b/scripts/go-live.sh old mode 100644 new mode 100755 diff --git a/scripts/health.sh b/scripts/health.sh new file mode 100755 index 0000000..fefa361 --- /dev/null +++ b/scripts/health.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +set -eu +AGENT_URL="${AGENT_URL:-http://127.0.0.1:9980}" +KNOWLEDGE_URL="${KNOWLEDGE_URL:-http://127.0.0.1:9981}" +printf 'Agent /healthz: ' +curl -fsS "$AGENT_URL/healthz" || true +echo +printf 'Agent /readyz: ' +curl -fsS "$AGENT_URL/readyz" || true +echo +printf 'Knowledge /api/health: ' +curl -fsS "$KNOWLEDGE_URL/api/health" || true +echo diff --git a/scripts/preflight-role.sh b/scripts/preflight-role.sh new file mode 100755 index 0000000..ef560ff --- /dev/null +++ b/scripts/preflight-role.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env sh +set -eu +ROLE="${1:-}" +case "$ROLE" in agent|knowledge|ollama|combined) ;; *) echo "Usage: $0 agent|knowledge|ollama|combined" >&2; exit 2;; esac +DIR="$(CDPATH= cd -- "$(dirname -- "$0")/../deployments/$ROLE" && pwd)" +ENVFILE="$DIR/.env" +[ -f "$ENVFILE" ] || { echo "Fehlt: $ENVFILE" >&2; exit 1; } +if grep -Eq '^[A-Z0-9_]+=CHANGE_ME' "$ENVFILE"; then + echo "Hinweis: CHANGE_ME-Werte vorhanden:" + grep -E '^[A-Z0-9_]+=CHANGE_ME' "$ENVFILE" | cut -d= -f1 + if [ "$ROLE" = agent ] || [ "$ROLE" = combined ]; then exit 1; fi +fi +mkdir -p "$DIR/../../runtime/agent-data" "$DIR/../../runtime/knowledge" "$DIR/../../runtime/backups" "$DIR/../../runtime/staging" "$DIR/../../runtime/ollama" +if command -v docker >/dev/null 2>&1; then + (cd "$DIR" && docker compose config >/dev/null) + echo "docker compose config: OK ($ROLE)" +else + echo "Docker CLI nicht vorhanden; YAML wurde beim Release statisch geprüft." +fi diff --git a/scripts/preflight.sh b/scripts/preflight.sh old mode 100644 new mode 100755 diff --git a/scripts/propose-draft.sh b/scripts/propose-draft.sh old mode 100644 new mode 100755 diff --git a/scripts/quality-replay.py b/scripts/quality-replay.py old mode 100644 new mode 100755 diff --git a/scripts/release-gate.sh b/scripts/release-gate.sh old mode 100644 new mode 100755 diff --git a/scripts/research-up.sh b/scripts/research-up.sh old mode 100644 new mode 100755 diff --git a/scripts/restore-data.sh b/scripts/restore-data.sh old mode 100644 new mode 100755 diff --git a/scripts/secret-scan.sh b/scripts/secret-scan.sh old mode 100644 new mode 100755 index f16be47..c60c531 --- a/scripts/secret-scan.sh +++ b/scripts/secret-scan.sh @@ -23,7 +23,11 @@ fi # Private keys and common live-token shapes must not be committed. Placeholders in # templates/docs are intentionally allowed. -if grep -E '(^|/)\.env$|\.pem$|\.p12$|\.pfx$|(^|/)id_rsa$|(^|/)id_ed25519$' "$FILES" >/dev/null; then +# Distributed deployment bundles intentionally contain three complete `.env` +# templates. They must remain placeholder-only; every other private env/key file +# is still forbidden. +if grep -E '(^|/)\.env$|\.pem$|\.p12$|\.pfx$|(^|/)id_rsa$|(^|/)id_ed25519$' "$FILES" \ + | grep -Ev '^deployments/(master|cpu-subagent|gpu-subagent|agent|knowledge|ollama|combined)/\.env$' >/dev/null; then bad "private environment/key material found" fi @@ -47,6 +51,30 @@ else fi fi +# Checked-in deployment .env files are templates, never live configuration. +for envf in \ + deployments/master/.env \ + deployments/cpu-subagent/.env \ + deployments/gpu-subagent/.env \ + deployments/agent/.env \ + deployments/knowledge/.env \ + deployments/ollama/.env \ + deployments/combined/.env; do + [ -f "$envf" ] || continue + if awk -F= ' + /^[[:space:]]*#/ || NF < 2 { next } + { + key=$1; sub(/^[[:space:]]+/, "", key); sub(/[[:space:]]+$/, "", key) + val=$0; sub(/^[^=]*=/, "", val) + if (key ~ /(TOKEN|PASSWORD|SECRET|CLIENT_ID|CLIENT_SECRET|API_KEY)$/ && val != "" && val !~ /^CHANGE_ME/) { + print FILENAME ":" NR ": live-looking secret in " key > "/dev/stderr"; + bad=1 + } + } + END { exit bad ? 1 : 0 } + ' "$envf"; then :; else bad "deployment template contains a non-placeholder secret: $envf"; fi +done + # Reject accidental binary blobs outside explicitly expected assets. while IFS= read -r f; do [ -f "$f" ] || continue diff --git a/scripts/status.sh b/scripts/status.sh old mode 100644 new mode 100755 diff --git a/scripts/validate.sh b/scripts/validate.sh old mode 100644 new mode 100755 diff --git a/scripts/verify-package.sh b/scripts/verify-package.sh new file mode 100755 index 0000000..8070ced --- /dev/null +++ b/scripts/verify-package.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env sh +set -eu +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +cd "$ROOT" + +for f in \ + services/agent/Dockerfile \ + services/knowledge/Dockerfile \ + .gitea/workflows/ci.yml \ + .gitea/workflows/release.yml \ + docker-bake.hcl; do + test -f "$f" || { echo "missing: $f" >&2; exit 1; } +done + +grep -q '^FROM ' services/agent/Dockerfile +grep -q 'AS data-init' services/agent/Dockerfile +grep -q '^FROM ' services/knowledge/Dockerfile + +echo "OK: Dockerfiles and repository workflows are present." diff --git a/services/control/engineering-graph.json b/services/control/engineering-graph.json index 3665df0..9502d59 100644 --- a/services/control/engineering-graph.json +++ b/services/control/engineering-graph.json @@ -562,6 +562,16 @@ "path": "platform/neuroforge/internal/store/sqar_vector.go" } }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go", + "kind": "file", + "label": "startup_compact.go", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "path": "platform/neuroforge/internal/store/startup_compact.go" + } + }, { "id": "file:platform/neuroforge/internal/store/store.go", "kind": "file", @@ -9742,6 +9752,42 @@ "path": "platform/neuroforge/cmd/bench/main.go" } }, + { + "id": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", + "kind": "function", + "label": "bootstrapHandler.ServeHTTP", + "group": "engineering", + "community": "neuroforge/cmd/server", + "meta": { + "exported": true, + "line": 129, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, + { + "id": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", + "kind": "function", + "label": "bootstrapHandler.SetHandler", + "group": "engineering", + "community": "neuroforge/cmd/server", + "meta": { + "exported": true, + "line": 122, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, + { + "id": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", + "kind": "function", + "label": "bootstrapHandler.SetPhase", + "group": "engineering", + "community": "neuroforge/cmd/server", + "meta": { + "exported": true, + "line": 116, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, { "id": "function:neuroforge/cmd/server:envBool", "kind": "function", @@ -9750,7 +9796,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 26, + "line": 28, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9762,7 +9808,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 50, + "line": 52, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9774,7 +9820,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 38, + "line": 40, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9786,7 +9832,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 514, + "line": 623, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9798,7 +9844,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 104, + "line": 159, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9810,7 +9856,19 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 97, + "line": 99, + "path": "platform/neuroforge/cmd/server/main.go" + } + }, + { + "id": "function:neuroforge/cmd/server:newBootstrapHandler", + "kind": "function", + "label": "newBootstrapHandler", + "group": "engineering", + "community": "neuroforge/cmd/server", + "meta": { + "exported": false, + "line": 112, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9822,7 +9880,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 111, + "line": 166, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9834,7 +9892,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 62, + "line": 64, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -9846,7 +9904,7 @@ "community": "neuroforge/cmd/server", "meta": { "exported": false, - "line": 77, + "line": 79, "path": "platform/neuroforge/cmd/server/main.go" } }, @@ -12066,7 +12124,7 @@ "community": "neuroforge/internal/core", "meta": { "exported": true, - "line": 740, + "line": 741, "path": "platform/neuroforge/internal/core/types.go" } }, @@ -14562,7 +14620,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 52, + "line": 56, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -14574,7 +14632,19 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 650, + "line": 747, + "path": "platform/neuroforge/internal/store/store.go" + } + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress", + "kind": "function", + "label": "NewWithProgress", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": true, + "line": 60, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -14886,7 +14956,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 863, + "line": 960, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -14910,7 +14980,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1312, + "line": 1409, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -14934,7 +15004,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 735, + "line": 795, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -14946,7 +15016,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 389, + "line": 398, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -14958,7 +15028,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 393, + "line": 402, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -14970,7 +15040,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 652, + "line": 749, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15034,6 +15104,18 @@ "path": "platform/neuroforge/internal/store/cluster.go" } }, + { + "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", + "kind": "function", + "label": "Store.CompactCompletedRelinkJobs", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": true, + "line": 780, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, { "id": "function:neuroforge/internal/store:Store.CompactIndexSegments", "kind": "function", @@ -15042,7 +15124,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 439, + "line": 426, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -15054,7 +15136,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 668, + "line": 765, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15066,7 +15148,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 436, + "line": 445, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15078,7 +15160,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 440, + "line": 449, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15090,7 +15172,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 689, + "line": 786, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15114,7 +15196,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1157, + "line": 1254, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15126,7 +15208,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1109, + "line": 1206, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15162,7 +15244,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1357, + "line": 1454, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15210,7 +15292,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 91, + "line": 93, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15222,7 +15304,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 95, + "line": 97, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15234,7 +15316,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1378, + "line": 1475, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15246,7 +15328,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 686, + "line": 702, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15270,7 +15352,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 397, + "line": 411, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -15294,7 +15376,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 929, + "line": 1026, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15390,7 +15472,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 585, + "line": 594, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15402,7 +15484,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 294, + "line": 303, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15414,7 +15496,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 431, + "line": 418, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -15426,7 +15508,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 477, + "line": 464, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -15438,7 +15520,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 829, + "line": 889, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15450,7 +15532,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 499, + "line": 508, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15462,7 +15544,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 512, + "line": 521, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15534,7 +15616,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1252, + "line": 1349, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15546,7 +15628,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1210, + "line": 1307, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15570,7 +15652,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1229, + "line": 1326, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15618,7 +15700,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 598, + "line": 607, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15654,7 +15736,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 636, + "line": 652, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15666,7 +15748,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 654, + "line": 670, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15690,7 +15772,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 759, + "line": 819, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15738,7 +15820,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1344, + "line": 1441, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15762,7 +15844,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 284, + "line": 293, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15774,7 +15856,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1098, + "line": 1195, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15834,7 +15916,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 538, + "line": 547, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -15870,7 +15952,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 986, + "line": 1083, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15882,7 +15964,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1709, + "line": 1806, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15894,7 +15976,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1718, + "line": 1815, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15906,7 +15988,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 746, + "line": 843, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15918,7 +16000,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 680, + "line": 777, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15942,7 +16024,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1185, + "line": 1282, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -15954,7 +16036,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1196, + "line": 1293, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16002,7 +16084,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1265, + "line": 1362, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16038,7 +16120,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1242, + "line": 1339, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16074,7 +16156,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1133, + "line": 1230, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16098,7 +16180,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 690, + "line": 787, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16110,7 +16192,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1258, + "line": 1355, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16122,7 +16204,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 753, + "line": 850, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16182,7 +16264,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1328, + "line": 1425, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16194,7 +16276,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 1396, + "line": 1493, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16218,7 +16300,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 403, + "line": 417, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -16230,7 +16312,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": true, - "line": 559, + "line": 568, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -16302,7 +16384,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 323, + "line": 331, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -16342,6 +16424,18 @@ "path": "platform/neuroforge/internal/store/wal.go" } }, + { + "id": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", + "kind": "function", + "label": "Store.compactCompletedRelinkJobsLocked", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": false, + "line": 764, + "path": "platform/neuroforge/internal/store/orchestrator.go" + } + }, { "id": "function:neuroforge/internal/store:Store.currentSnapshotsLocked", "kind": "function", @@ -16374,7 +16468,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 214, + "line": 223, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -16410,7 +16504,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 940, + "line": 1037, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16434,7 +16528,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 370, + "line": 357, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -16506,7 +16600,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 383, + "line": 397, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -16518,7 +16612,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 615, + "line": 655, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16530,7 +16624,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 396, + "line": 383, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -16542,7 +16636,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 299, + "line": 286, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -16566,7 +16660,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 771, + "line": 868, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16578,7 +16672,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 325, + "line": 334, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -16614,7 +16708,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 635, + "line": 732, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16626,7 +16720,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 638, + "line": 735, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16638,7 +16732,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 354, + "line": 368, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -16650,7 +16744,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 776, + "line": 873, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16662,7 +16756,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 813, + "line": 910, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16698,7 +16792,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 244, + "line": 253, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -16746,7 +16840,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 992, + "line": 1089, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16830,7 +16924,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 1402, + "line": 1499, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -16866,7 +16960,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 451, + "line": 438, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -16878,7 +16972,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 376, + "line": 390, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -16890,7 +16984,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 388, + "line": 375, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -17070,7 +17164,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 267, + "line": 254, "path": "platform/neuroforge/internal/store/index_segments.go" } }, @@ -17082,7 +17176,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 217, + "line": 254, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17130,7 +17224,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 75, + "line": 77, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -17166,7 +17260,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 965, + "line": 1062, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17202,7 +17296,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 763, + "line": 860, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17218,6 +17312,30 @@ "path": "platform/neuroforge/internal/store/raftlog.go" } }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "kind": "function", + "label": "compactLegacyTerminalRelinkCheckpoint", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": false, + "line": 25, + "path": "platform/neuroforge/internal/store/startup_compact.go" + } + }, + { + "id": "function:neuroforge/internal/store:copyJSONValue", + "kind": "function", + "label": "copyJSONValue", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": false, + "line": 209, + "path": "platform/neuroforge/internal/store/startup_compact.go" + } + }, { "id": "function:neuroforge/internal/store:decodeVectorPayload", "kind": "function", @@ -17262,7 +17380,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 1091, + "line": 1188, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17382,7 +17500,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 602, + "line": 642, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17454,7 +17572,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 380, + "line": 389, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -17514,7 +17632,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 393, + "line": 407, "path": "platform/neuroforge/internal/store/wal.go" } }, @@ -17538,7 +17656,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 563, + "line": 603, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17574,7 +17692,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 60, + "line": 62, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -17670,7 +17788,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 1102, + "line": 1199, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17754,7 +17872,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 642, + "line": 739, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -17782,6 +17900,30 @@ "path": "platform/neuroforge/internal/store/sqar_vector.go" } }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject", + "kind": "function", + "label": "rewriteCheckpointObject", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": false, + "line": 86, + "path": "platform/neuroforge/internal/store/startup_compact.go" + } + }, + { + "id": "function:neuroforge/internal/store:rewriteJobsObject", + "kind": "function", + "label": "rewriteJobsObject", + "group": "engineering", + "community": "neuroforge/internal/store", + "meta": { + "exported": false, + "line": 148, + "path": "platform/neuroforge/internal/store/startup_compact.go" + } + }, { "id": "function:neuroforge/internal/store:sameClusterMemory", "kind": "function", @@ -17886,7 +18028,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 235, + "line": 244, "path": "platform/neuroforge/internal/store/orchestrator.go" } }, @@ -17898,7 +18040,7 @@ "community": "neuroforge/internal/store", "meta": { "exported": false, - "line": 623, + "line": 680, "path": "platform/neuroforge/internal/store/store.go" } }, @@ -18010,6 +18152,18 @@ "path": "platform/neuroforge/internal/vector/hnsw.go" } }, + { + "id": "function:neuroforge/internal/vector:HNSW.Delta", + "kind": "function", + "label": "HNSW.Delta", + "group": "engineering", + "community": "neuroforge/internal/vector", + "meta": { + "exported": true, + "line": 744, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, { "id": "function:neuroforge/internal/vector:HNSW.Len", "kind": "function", @@ -18042,7 +18196,7 @@ "community": "neuroforge/internal/vector", "meta": { "exported": true, - "line": 682, + "line": 726, "path": "platform/neuroforge/internal/vector/hnsw.go" } }, @@ -18066,7 +18220,7 @@ "community": "neuroforge/internal/vector", "meta": { "exported": true, - "line": 733, + "line": 785, "path": "platform/neuroforge/internal/vector/hnsw.go" } }, @@ -18106,6 +18260,18 @@ "path": "platform/neuroforge/internal/vector/hnsw.go" } }, + { + "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "kind": "function", + "label": "HNSW.nodeFingerprintLocked", + "group": "engineering", + "community": "neuroforge/internal/vector", + "meta": { + "exported": false, + "line": 682, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, { "id": "function:neuroforge/internal/vector:HNSW.pruneLocked", "kind": "function", @@ -18130,6 +18296,18 @@ "path": "platform/neuroforge/internal/vector/hnsw.go" } }, + { + "id": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", + "kind": "function", + "label": "HNSW.snapshotNodeLocked", + "group": "engineering", + "community": "neuroforge/internal/vector", + "meta": { + "exported": false, + "line": 708, + "path": "platform/neuroforge/internal/vector/hnsw.go" + } + }, { "id": "function:neuroforge/internal/vector:NewHNSW", "kind": "function", @@ -18270,7 +18448,7 @@ "community": "neuroforge/internal/vector", "meta": { "exported": true, - "line": 785, + "line": 837, "path": "platform/neuroforge/internal/vector/hnsw.go" } }, @@ -18678,7 +18856,7 @@ "community": "neuroforge/internal/vector", "meta": { "exported": false, - "line": 718, + "line": 770, "path": "platform/neuroforge/internal/vector/hnsw.go" } }, @@ -18690,7 +18868,7 @@ "community": "neuroforge/internal/vector", "meta": { "exported": false, - "line": 722, + "line": 774, "path": "platform/neuroforge/internal/vector/hnsw.go" } }, @@ -20924,7 +21102,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20935,7 +21113,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20946,7 +21124,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20957,7 +21135,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20968,7 +21146,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20979,7 +21157,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -20990,7 +21168,7 @@ "community": "compose", "status": "configured", "meta": { - "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.0}" + "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}" } }, { @@ -21329,6 +21507,24 @@ "to": "package:time", "kind": "imports" }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.ServeHTTP:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetHandler:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetPhase:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", + "kind": "defines" + }, { "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:envBool:defines", "from": "file:platform/neuroforge/cmd/server/main.go", @@ -21365,6 +21561,12 @@ "to": "function:neuroforge/cmd/server:maxIntMain", "kind": "defines" }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:newBootstrapHandler:defines", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "function:neuroforge/cmd/server:newBootstrapHandler", + "kind": "defines" + }, { "id": "file:platform/neuroforge/cmd/server/main.go-\u003efunction:neuroforge/cmd/server:run:defines", "from": "file:platform/neuroforge/cmd/server/main.go", @@ -21419,6 +21621,12 @@ "to": "package:net/http", "kind": "imports" }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:net:imports", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "package:net", + "kind": "imports" + }, { "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:neuroforge/internal/brain:imports", "from": "file:platform/neuroforge/cmd/server/main.go", @@ -21485,6 +21693,12 @@ "to": "package:strings", "kind": "imports" }, + { + "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:sync:imports", + "from": "file:platform/neuroforge/cmd/server/main.go", + "to": "package:sync", + "kind": "imports" + }, { "id": "file:platform/neuroforge/cmd/server/main.go-\u003epackage:syscall:imports", "from": "file:platform/neuroforge/cmd/server/main.go", @@ -26135,6 +26349,12 @@ "to": "function:neuroforge/internal/store:Store.ClaimJobForWorker", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.CompactCompletedRelinkJobs:defines", + "from": "file:platform/neuroforge/internal/store/orchestrator.go", + "to": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.CompleteJob:defines", "from": "file:platform/neuroforge/internal/store/orchestrator.go", @@ -26237,6 +26457,12 @@ "to": "function:neuroforge/internal/store:Store.WorkersSnapshot", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:defines", + "from": "file:platform/neuroforge/internal/store/orchestrator.go", + "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/store/orchestrator.go-\u003efunction:neuroforge/internal/store:Store.dependencyStateLocked:defines", "from": "file:platform/neuroforge/internal/store/orchestrator.go", @@ -27125,6 +27351,78 @@ "to": "package:io", "kind": "imports" }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint:defines", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:copyJSONValue:defines", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "function:neuroforge/internal/store:copyJSONValue", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:rewriteCheckpointObject:defines", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "function:neuroforge/internal/store:rewriteCheckpointObject", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003efunction:neuroforge/internal/store:rewriteJobsObject:defines", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "function:neuroforge/internal/store:rewriteJobsObject", + "kind": "defines" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:bufio:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:bufio", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:encoding/json:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:encoding/json", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:errors:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:errors", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:fmt:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:fmt", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:io:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:io", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:neuroforge/internal/core:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:neuroforge/internal/core", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:os:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:os", + "kind": "imports" + }, + { + "id": "file:platform/neuroforge/internal/store/startup_compact.go-\u003epackage:path/filepath:imports", + "from": "file:platform/neuroforge/internal/store/startup_compact.go", + "to": "package:path/filepath", + "kind": "imports" + }, { "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:New:defines", "from": "file:platform/neuroforge/internal/store/store.go", @@ -27137,6 +27435,12 @@ "to": "function:neuroforge/internal/store:NewID", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:NewWithProgress:defines", + "from": "file:platform/neuroforge/internal/store/store.go", + "to": "function:neuroforge/internal/store:NewWithProgress", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/store/store.go-\u003efunction:neuroforge/internal/store:Store.AddMemory:defines", "from": "file:platform/neuroforge/internal/store/store.go", @@ -27425,6 +27729,12 @@ "to": "function:neuroforge/internal/store:writeAtomic", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:bufio:imports", + "from": "file:platform/neuroforge/internal/store/store.go", + "to": "package:bufio", + "kind": "imports" + }, { "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:crypto/rand:imports", "from": "file:platform/neuroforge/internal/store/store.go", @@ -27455,6 +27765,12 @@ "to": "package:fmt", "kind": "imports" }, + { + "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:io:imports", + "from": "file:platform/neuroforge/internal/store/store.go", + "to": "package:io", + "kind": "imports" + }, { "id": "file:platform/neuroforge/internal/store/store.go-\u003epackage:math:imports", "from": "file:platform/neuroforge/internal/store/store.go", @@ -28109,6 +28425,12 @@ "to": "function:neuroforge/internal/vector:HNSW.AddBatch", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.Delta:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", + "to": "function:neuroforge/internal/vector:HNSW.Delta", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.Len:defines", "from": "file:platform/neuroforge/internal/vector/hnsw.go", @@ -28157,6 +28479,12 @@ "to": "function:neuroforge/internal/vector:HNSW.levelForID", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", + "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.pruneLocked:defines", "from": "file:platform/neuroforge/internal/vector/hnsw.go", @@ -28169,6 +28497,12 @@ "to": "function:neuroforge/internal/vector:HNSW.searchLayerLocked", "kind": "defines" }, + { + "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:HNSW.snapshotNodeLocked:defines", + "from": "file:platform/neuroforge/internal/vector/hnsw.go", + "to": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", + "kind": "defines" + }, { "id": "file:platform/neuroforge/internal/vector/hnsw.go-\u003efunction:neuroforge/internal/vector:NewHNSW:defines", "from": "file:platform/neuroforge/internal/vector/hnsw.go", @@ -49274,6 +49608,13 @@ "kind": "calls_package", "label": "Sqrt" }, + { + "id": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/cmd/server:bootstrapHandler.ServeHTTP", + "to": "package:fmt", + "kind": "calls_package", + "label": "Fprintf" + }, { "id": "function:neuroforge/cmd/server:envBool-\u003epackage:os:calls_package", "from": "function:neuroforge/cmd/server:envBool", @@ -49364,6 +49705,18 @@ "kind": "calls_package", "label": "Exit" }, + { + "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetHandler:calls", + "from": "function:neuroforge/cmd/server:run", + "to": "function:neuroforge/cmd/server:bootstrapHandler.SetHandler", + "kind": "calls" + }, + { + "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:bootstrapHandler.SetPhase:calls", + "from": "function:neuroforge/cmd/server:run", + "to": "function:neuroforge/cmd/server:bootstrapHandler.SetPhase", + "kind": "calls" + }, { "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:envBool:calls", "from": "function:neuroforge/cmd/server:run", @@ -49394,6 +49747,12 @@ "to": "function:neuroforge/cmd/server:maxIntMain", "kind": "calls" }, + { + "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:newBootstrapHandler:calls", + "from": "function:neuroforge/cmd/server:run", + "to": "function:neuroforge/cmd/server:newBootstrapHandler", + "kind": "calls" + }, { "id": "function:neuroforge/cmd/server:run-\u003efunction:neuroforge/cmd/server:validateManagedSecretsFromEnv:calls", "from": "function:neuroforge/cmd/server:run", @@ -49405,7 +49764,7 @@ "from": "function:neuroforge/cmd/server:run", "to": "package:context", "kind": "calls_package", - "label": "Background" + "label": "WithTimeout" }, { "id": "function:neuroforge/cmd/server:run-\u003epackage:errors:calls_package", @@ -49435,6 +49794,13 @@ "kind": "calls_package", "label": "Printf" }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:net:calls_package", + "from": "function:neuroforge/cmd/server:run", + "to": "package:net", + "kind": "calls_package", + "label": "Listen" + }, { "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/brain:calls_package", "from": "function:neuroforge/cmd/server:run", @@ -49442,6 +49808,13 @@ "kind": "calls_package", "label": "New" }, + { + "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/core:calls_package", + "from": "function:neuroforge/cmd/server:run", + "to": "package:neuroforge/internal/core", + "kind": "calls_package", + "label": "DefaultConfig" + }, { "id": "function:neuroforge/cmd/server:run-\u003epackage:neuroforge/internal/cost:calls_package", "from": "function:neuroforge/cmd/server:run", @@ -49468,7 +49841,7 @@ "from": "function:neuroforge/cmd/server:run", "to": "package:neuroforge/internal/store", "kind": "calls_package", - "label": "New" + "label": "NewWithProgress" }, { "id": "function:neuroforge/cmd/server:run-\u003epackage:os/signal:calls_package", @@ -57100,192 +57473,216 @@ "label": "Base" }, { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.ConsumeMetadata:calls", + "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:NewWithProgress:calls", "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:SegmentStore.ConsumeMetadata", + "to": "function:neuroforge/internal/store:NewWithProgress", "kind": "calls" }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.HasRecords:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:SegmentStore.HasRecords", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:SegmentStore.Rebuild:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:SegmentStore.Rebuild", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.checkpointLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.initHotTrackerLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.initHotTrackerLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.initializeClusterRoleLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.initializeClusterRoleLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadDiskANNLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.loadDiskANNLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadIndexSnapshotLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.loadIndexSnapshotLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.loadJSON", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.persistSecretsLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.persistSecretsLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildIndexesLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.rebuildIndexesLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.rebuildSynapseAdjLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.rebuildSynapseAdjLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.replayWAL:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.replayWAL", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:Store.tierMemoryBodiesLocked:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:Store.tierMemoryBodiesLocked", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:applyNewDefaults:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:applyNewDefaults", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:migrateMemories:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:migrateMemories", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:newMemoryPageCache:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:newMemoryPageCache", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:openSegmentStore:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:openSegmentStore", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:openVectorJournal:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:openVectorJournal", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:randomID:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:randomID", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003efunction:neuroforge/internal/store:vectorJournalOptionsFromConfig:calls", - "from": "function:neuroforge/internal/store:New", - "to": "function:neuroforge/internal/store:vectorJournalOptionsFromConfig", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:errors:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:errors", - "kind": "calls_package", - "label": "New" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:fmt:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:fmt", - "kind": "calls_package", - "label": "Sprint" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:neuroforge/internal/core:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:neuroforge/internal/core", - "kind": "calls_package", - "label": "DefaultConfig" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:os:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:os", - "kind": "calls_package", - "label": "MkdirAll" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:path/filepath:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:path/filepath", - "kind": "calls_package", - "label": "Join" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:strings:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:strings", - "kind": "calls_package", - "label": "TrimSpace" - }, - { - "id": "function:neuroforge/internal/store:New-\u003epackage:time:calls_package", - "from": "function:neuroforge/internal/store:New", - "to": "package:time", - "kind": "calls_package", - "label": "Now" - }, { "id": "function:neuroforge/internal/store:NewID-\u003efunction:neuroforge/internal/store:randomID:calls", "from": "function:neuroforge/internal/store:NewID", "to": "function:neuroforge/internal/store:randomID", "kind": "calls" }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:New:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:New", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.ConsumeMetadata:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.ConsumeMetadata", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.HasRecords:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.HasRecords", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:SegmentStore.Rebuild:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:SegmentStore.Rebuild", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.checkpointLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.initHotTrackerLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.initHotTrackerLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.initializeClusterRoleLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.initializeClusterRoleLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadDiskANNLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadDiskANNLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadIndexSnapshotLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadIndexSnapshotLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.loadJSON", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.persistSecretsLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.persistSecretsLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildIndexesLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildIndexesLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildProvenanceSourceIndexLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.rebuildSynapseAdjLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.rebuildSynapseAdjLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.replayWAL:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.replayWAL", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:Store.tierMemoryBodiesLocked:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:Store.tierMemoryBodiesLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:applyNewDefaults:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:applyNewDefaults", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:migrateMemories:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:migrateMemories", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:newMemoryPageCache:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:newMemoryPageCache", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:openSegmentStore:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:openSegmentStore", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:openVectorJournal:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:openVectorJournal", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:randomID:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:randomID", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003efunction:neuroforge/internal/store:vectorJournalOptionsFromConfig:calls", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "function:neuroforge/internal/store:vectorJournalOptionsFromConfig", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:errors:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:errors", + "kind": "calls_package", + "label": "Is" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:fmt", + "kind": "calls_package", + "label": "Errorf" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:neuroforge/internal/core:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:neuroforge/internal/core", + "kind": "calls_package", + "label": "DefaultConfig" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:os:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:os", + "kind": "calls_package", + "label": "MkdirAll" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:path/filepath:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:path/filepath", + "kind": "calls_package", + "label": "Join" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:strings:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:strings", + "kind": "calls_package", + "label": "TrimSpace" + }, + { + "id": "function:neuroforge/internal/store:NewWithProgress-\u003epackage:time:calls_package", + "from": "function:neuroforge/internal/store:NewWithProgress", + "to": "package:time", + "kind": "calls_package", + "label": "Now" + }, { "id": "function:neuroforge/internal/store:SegmentStore.AppendDelete-\u003efunction:neuroforge/internal/store:ClusterLog.append:calls", "from": "function:neuroforge/internal/store:SegmentStore.AppendDelete", @@ -58166,6 +58563,18 @@ "kind": "calls_package", "label": "Now" }, + { + "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs-\u003efunction:neuroforge/internal/store:Store.checkpointLocked:calls", + "from": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", + "to": "function:neuroforge/internal/store:Store.checkpointLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs-\u003efunction:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked:calls", + "from": "function:neuroforge/internal/store:Store.CompactCompletedRelinkJobs", + "to": "function:neuroforge/internal/store:Store.compactCompletedRelinkJobsLocked", + "kind": "calls" + }, { "id": "function:neuroforge/internal/store:Store.CompactIndexSegments-\u003efunction:neuroforge/internal/store:Store.IndexSnapshotStatusUnlocked:calls", "from": "function:neuroforge/internal/store:Store.CompactIndexSegments", @@ -60943,19 +61352,46 @@ "to": "function:neuroforge/internal/store:indexMode", "kind": "calls" }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "function:neuroforge/internal/store:ClusterLog.Close", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:bufio:calls_package", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "package:bufio", + "kind": "calls_package", + "label": "NewReaderSize" + }, { "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:encoding/json:calls_package", "from": "function:neuroforge/internal/store:Store.loadJSON", "to": "package:encoding/json", "kind": "calls_package", - "label": "Unmarshal" + "label": "NewDecoder" + }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "package:fmt", + "kind": "calls_package", + "label": "Errorf" }, { "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:os:calls_package", "from": "function:neuroforge/internal/store:Store.loadJSON", "to": "package:os", "kind": "calls_package", - "label": "ReadFile" + "label": "Open" + }, + { + "id": "function:neuroforge/internal/store:Store.loadJSON-\u003epackage:path/filepath:calls_package", + "from": "function:neuroforge/internal/store:Store.loadJSON", + "to": "package:path/filepath", + "kind": "calls_package", + "label": "Base" }, { "id": "function:neuroforge/internal/store:Store.loadLegacyIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", @@ -61731,12 +62167,6 @@ "to": "function:neuroforge/internal/store:ClusterLog.append", "kind": "calls" }, - { - "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.currentSnapshotsLocked:calls", - "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", - "to": "function:neuroforge/internal/store:Store.currentSnapshotsLocked", - "kind": "calls" - }, { "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:Store.loadJSON:calls", "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", @@ -61755,24 +62185,12 @@ "to": "function:neuroforge/internal/store:Store.writeLegacyIndexSnapshotLocked", "kind": "calls" }, - { - "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:buildIndexShadow:calls", - "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", - "to": "function:neuroforge/internal/store:buildIndexShadow", - "kind": "calls" - }, { "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:cleanupOldIndexBases:calls", "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", "to": "function:neuroforge/internal/store:cleanupOldIndexBases", "kind": "calls" }, - { - "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:hashSnapshotNode:calls", - "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", - "to": "function:neuroforge/internal/store:hashSnapshotNode", - "kind": "calls" - }, { "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003efunction:neuroforge/internal/store:shadowFromHNSW:calls", "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", @@ -61806,13 +62224,6 @@ "kind": "calls_package", "label": "Join" }, - { - "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003epackage:sort:calls_package", - "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", - "to": "package:sort", - "kind": "calls_package", - "label": "Strings" - }, { "id": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked-\u003epackage:strconv:calls_package", "from": "function:neuroforge/internal/store:Store.writeSegmentedIndexSnapshotLocked", @@ -62285,6 +62696,74 @@ "kind": "calls_package", "label": "Sprintf" }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "function:neuroforge/internal/store:ClusterLog.Close", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003efunction:neuroforge/internal/store:rewriteCheckpointObject:calls", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "function:neuroforge/internal/store:rewriteCheckpointObject", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:bufio:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:bufio", + "kind": "calls_package", + "label": "NewReaderSize" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:encoding/json", + "kind": "calls_package", + "label": "NewDecoder" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:errors:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:errors", + "kind": "calls_package", + "label": "Is" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:fmt", + "kind": "calls_package", + "label": "Sprintf" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:os:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:os", + "kind": "calls_package", + "label": "Stat" + }, + { + "id": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint-\u003epackage:path/filepath:calls_package", + "from": "function:neuroforge/internal/store:compactLegacyTerminalRelinkCheckpoint", + "to": "package:path/filepath", + "kind": "calls_package", + "label": "Join" + }, + { + "id": "function:neuroforge/internal/store:copyJSONValue-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:copyJSONValue", + "to": "package:encoding/json", + "kind": "calls_package", + "label": "Marshal" + }, + { + "id": "function:neuroforge/internal/store:copyJSONValue-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:copyJSONValue", + "to": "package:fmt", + "kind": "calls_package", + "label": "Errorf" + }, { "id": "function:neuroforge/internal/store:decodeVectorPayload-\u003efunction:neuroforge/internal/store:ClusterLog.append:calls", "from": "function:neuroforge/internal/store:decodeVectorPayload", @@ -62715,6 +63194,51 @@ "to": "function:neuroforge/internal/store:vectorPredictorValue", "kind": "calls" }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:New:calls", + "from": "function:neuroforge/internal/store:rewriteCheckpointObject", + "to": "function:neuroforge/internal/store:New", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:copyJSONValue:calls", + "from": "function:neuroforge/internal/store:rewriteCheckpointObject", + "to": "function:neuroforge/internal/store:copyJSONValue", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003efunction:neuroforge/internal/store:rewriteJobsObject:calls", + "from": "function:neuroforge/internal/store:rewriteCheckpointObject", + "to": "function:neuroforge/internal/store:rewriteJobsObject", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:rewriteCheckpointObject", + "to": "package:encoding/json", + "kind": "calls_package", + "label": "Marshal" + }, + { + "id": "function:neuroforge/internal/store:rewriteCheckpointObject-\u003epackage:fmt:calls_package", + "from": "function:neuroforge/internal/store:rewriteCheckpointObject", + "to": "package:fmt", + "kind": "calls_package", + "label": "Errorf" + }, + { + "id": "function:neuroforge/internal/store:rewriteJobsObject-\u003efunction:neuroforge/internal/store:New:calls", + "from": "function:neuroforge/internal/store:rewriteJobsObject", + "to": "function:neuroforge/internal/store:New", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:rewriteJobsObject-\u003epackage:encoding/json:calls_package", + "from": "function:neuroforge/internal/store:rewriteJobsObject", + "to": "package:encoding/json", + "kind": "calls_package", + "label": "Marshal" + }, { "id": "function:neuroforge/internal/store:segmentName-\u003epackage:fmt:calls_package", "from": "function:neuroforge/internal/store:segmentName", @@ -62821,19 +63345,39 @@ "kind": "calls_package", "label": "ToLower" }, + { + "id": "function:neuroforge/internal/store:writeAtomic-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", + "from": "function:neuroforge/internal/store:writeAtomic", + "to": "function:neuroforge/internal/store:ClusterLog.Close", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:bufio:calls_package", + "from": "function:neuroforge/internal/store:writeAtomic", + "to": "package:bufio", + "kind": "calls_package", + "label": "NewWriterSize" + }, { "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:encoding/json:calls_package", "from": "function:neuroforge/internal/store:writeAtomic", "to": "package:encoding/json", "kind": "calls_package", - "label": "MarshalIndent" + "label": "NewEncoder" }, { "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:os:calls_package", "from": "function:neuroforge/internal/store:writeAtomic", "to": "package:os", "kind": "calls_package", - "label": "WriteFile" + "label": "Remove" + }, + { + "id": "function:neuroforge/internal/store:writeAtomic-\u003epackage:path/filepath:calls_package", + "from": "function:neuroforge/internal/store:writeAtomic", + "to": "package:path/filepath", + "kind": "calls_package", + "label": "Dir" }, { "id": "function:neuroforge/internal/store:writeHNSWAtomic-\u003efunction:neuroforge/internal/store:ClusterLog.Close:calls", @@ -63028,6 +63572,25 @@ "to": "function:neuroforge/internal/vector:normalizeCopy", "kind": "calls" }, + { + "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:calls", + "from": "function:neuroforge/internal/vector:HNSW.Delta", + "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003efunction:neuroforge/internal/vector:HNSW.snapshotNodeLocked:calls", + "from": "function:neuroforge/internal/vector:HNSW.Delta", + "to": "function:neuroforge/internal/vector:HNSW.snapshotNodeLocked", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/vector:HNSW.Delta-\u003epackage:sort:calls_package", + "from": "function:neuroforge/internal/vector:HNSW.Delta", + "to": "package:sort", + "kind": "calls_package", + "label": "Strings" + }, { "id": "function:neuroforge/internal/vector:HNSW.Search-\u003efunction:neuroforge/internal/vector:HNSW.Add:calls", "from": "function:neuroforge/internal/vector:HNSW.Search", @@ -63121,31 +63684,11 @@ "label": "Slice" }, { - "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:writeHashString:calls", + "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:HNSW.nodeFingerprintLocked:calls", "from": "function:neuroforge/internal/vector:HNSW.Shadow", - "to": "function:neuroforge/internal/vector:writeHashString", + "to": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", "kind": "calls" }, - { - "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003efunction:neuroforge/internal/vector:writeHashU32:calls", - "from": "function:neuroforge/internal/vector:HNSW.Shadow", - "to": "function:neuroforge/internal/vector:writeHashU32", - "kind": "calls" - }, - { - "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003epackage:crypto/sha256:calls_package", - "from": "function:neuroforge/internal/vector:HNSW.Shadow", - "to": "package:crypto/sha256", - "kind": "calls_package", - "label": "New" - }, - { - "id": "function:neuroforge/internal/vector:HNSW.Shadow-\u003epackage:math:calls_package", - "from": "function:neuroforge/internal/vector:HNSW.Shadow", - "to": "package:math", - "kind": "calls_package", - "label": "Float32bits" - }, { "id": "function:neuroforge/internal/vector:HNSW.Snapshot-\u003epackage:sort:calls_package", "from": "function:neuroforge/internal/vector:HNSW.Snapshot", @@ -63230,6 +63773,32 @@ "kind": "calls_package", "label": "Log" }, + { + "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003efunction:neuroforge/internal/vector:writeHashString:calls", + "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "to": "function:neuroforge/internal/vector:writeHashString", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003efunction:neuroforge/internal/vector:writeHashU32:calls", + "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "to": "function:neuroforge/internal/vector:writeHashU32", + "kind": "calls" + }, + { + "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003epackage:crypto/sha256:calls_package", + "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "to": "package:crypto/sha256", + "kind": "calls_package", + "label": "New" + }, + { + "id": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked-\u003epackage:math:calls_package", + "from": "function:neuroforge/internal/vector:HNSW.nodeFingerprintLocked", + "to": "package:math", + "kind": "calls_package", + "label": "Float32bits" + }, { "id": "function:neuroforge/internal/vector:HNSW.searchLayerLocked-\u003efunction:neuroforge/internal/vector:dotNormalized:calls", "from": "function:neuroforge/internal/vector:HNSW.searchLayerLocked", @@ -65188,6 +65757,12 @@ "to": "file:platform/neuroforge/internal/store/sqar_vector.go", "kind": "contains_file" }, + { + "id": "package:neuroforge/internal/store-\u003efile:platform/neuroforge/internal/store/startup_compact.go:contains_file", + "from": "package:neuroforge/internal/store", + "to": "file:platform/neuroforge/internal/store/startup_compact.go", + "kind": "contains_file" + }, { "id": "package:neuroforge/internal/store-\u003efile:platform/neuroforge/internal/store/store.go:contains_file", "from": "package:neuroforge/internal/store", @@ -66288,10 +66863,10 @@ } ], "meta": { - "edges": 7210, + "edges": 7273, "format_version": 1, "generator": "go-ast+compose", "modules": 4, - "nodes": 1830 + "nodes": 1845 } }