mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-05 20:31:54 +00:00
Remove superseded Nostr implementation
This commit is contained in:
1 parent
87001555ea
commit
056fab8af4
802 files changed
+43
-104020
No files matched your search
@@ -1,54 +0,0 @@
|
||||
# Agent Coverage Gaps
|
||||
|
||||
This document identifies areas of the TORCH repository that lack specific agent coverage and recommends new agents to fill these gaps.
|
||||
|
||||
## Summary of Identified Gaps
|
||||
|
||||
| Gap | Evidence | Proposed Agent |
|
||||
| :--- | :--- | :--- |
|
||||
| **Database Migrations** | `migrations/` directory and SQL files present. | `migration-agent` |
|
||||
| **E2E Testing** | `test/` integration/e2e files (Node.js `--test`). | `e2e-agent` |
|
||||
| **Memory Subsystem** | `scripts/memory/`, `.scheduler-memory/` exist. | `memory-health-agent` |
|
||||
| **Relay Stability** | `src/relay-health.mjs` and known relay flakiness. | `relay-health-agent` |
|
||||
| **Skills Management** | `skills/` directory and `torch-basics.md`. | `skills-agent` |
|
||||
| **Landing Page** | `landing/` directory exists. | `landing-page-agent` |
|
||||
| **Prompt Contracts** | `scripts/validate-prompt-contract.mjs` and recurring violations. | `prompt-contract-agent` |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Recommendations
|
||||
|
||||
### 1. `migration-agent`
|
||||
- **Gap**: No agent monitors or audits the `migrations/` directory.
|
||||
- **Mission**: Audit pending migrations, ensure schema consistency, validate rollback scripts, and ensure migrations follow project naming conventions.
|
||||
- **Priority**: Medium
|
||||
|
||||
### 2. `e2e-agent`
|
||||
- **Gap**: E2E/integration tests exist (e.g., `test/scheduler-preflight-lock.e2e.test.mjs` via Node.js `--test`), but no agent is dedicated to scenario maintenance.
|
||||
- **Mission**: Maintain E2E scenarios, fix flaky tests, and ensure the dashboard and scheduler flow remain functional across protocol changes.
|
||||
- **Priority**: High (given the "Scenario-First" mandate in `AGENTS.md`)
|
||||
|
||||
### 3. `memory-health-agent`
|
||||
- **Gap**: The memory subsystem (ingest, retrieve, prune) is a core part of TORCH but lacks a dedicated quality auditor.
|
||||
- **Mission**: Audit memory retrieval relevance, ensure embeddings are healthy, and manage long-term memory pruning policies.
|
||||
- **Priority**: High
|
||||
|
||||
### 4. `relay-health-agent`
|
||||
- **Gap**: Relay instability is a recurring issue (`KNOWN_ISSUES.md`).
|
||||
- **Mission**: Continuously monitor relay performance, automatically rotate unhealthy relays in `torch-config.json`, and manage relay quarantine states.
|
||||
- **Priority**: High
|
||||
|
||||
### 5. `skills-agent`
|
||||
- **Gap**: The `skills/` directory contains agent guidance that needs periodic auditing.
|
||||
- **Mission**: Audit and update agent skills, ensuring they reflect current protocol standards and tool availability.
|
||||
- **Priority**: Low
|
||||
|
||||
### 6. `landing-page-agent`
|
||||
- **Gap**: The `landing/` page is a separate entity from the dashboard and needs maintenance.
|
||||
- **Mission**: Update the landing page with the latest release information, ensure the "Offline Bundle" links work, and maintain site aesthetic.
|
||||
- **Priority**: Low
|
||||
|
||||
### 7. `prompt-contract-agent`
|
||||
- **Gap**: Recurring prompt contract violations despite automated scripts.
|
||||
- **Mission**: Specifically focused on ensuring all prompts in `src/prompts/` follow the latest shared agent run contract and metadata standards.
|
||||
- **Priority**: Medium
|
||||
@@ -1320,6 +1320,15 @@ TORCH SHOULD be treated as the product name rather than retaining the legacy
|
||||
Exit gate: the owner approves the product boundary and legacy preservation
|
||||
plan.
|
||||
|
||||
Implementation checkpoint (2026-09-27): the former Nostr product is preserved
|
||||
at `legacy/nostr-torch`, the development-network experiment is preserved at
|
||||
`legacy/development-network-v2`, and active development occurs on
|
||||
`rewrite/portable-agent-fleet`. Once the replacement kernel and its acceptance
|
||||
suite were established, Nostr-era runtime code, dashboards, generated output,
|
||||
task logs, fixtures, and tests were removed from the rewrite branch rather
|
||||
than carried as a misleading second product. The legacy branches remain the
|
||||
recovery and history boundary.
|
||||
|
||||
### Stage 1: Portable kernel
|
||||
|
||||
- configuration schema and loader;
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# TORCH Evolution: The Node-Based Future
|
||||
|
||||
## 1. The Core Concept: From "Roster" to "Workflow"
|
||||
|
||||
Currently, TORCH is a **Round-Robin Scheduler**. It loops through a static list of agents (`daily`/`weekly`). This is great for maintenance but limits complex, multi-step tasks.
|
||||
|
||||
The "Bigger" vision is to evolve TORCH into a **Workflow Orchestrator** (similar to Airflow, Prefect, or ComfyUI, but for Autonomous Agents).
|
||||
|
||||
### The Shift
|
||||
* **Old**: `roster.json` (List) -> `Agent A`, `Agent B`, `Agent C`.
|
||||
* **New**: `workflows/refactor.json` (Graph) -> `Trigger` -> `Planner` -> `[Coder, Writer]` -> `Reviewer`.
|
||||
|
||||
## 2. The Node-Based UI
|
||||
|
||||
You mentioned a "concept for a node based evolution". This is the right move.
|
||||
* **Recommendation**: Build a web-based **Workflow Editor** using **React Flow** or **Svelte Flow**.
|
||||
* **How it works**:
|
||||
1. **Drag & Drop Agents**: Drag a "Claude Node" or "Jules Node" onto the canvas.
|
||||
2. **Connect Context**: Draw lines to show data flow. Output of "Planner" becomes context for "Coder".
|
||||
3. **Visual Debugging**: Watch nodes light up Green/Red as the scheduler executes them in real-time.
|
||||
|
||||
## 3. Tool Integrations
|
||||
|
||||
Here is where the tools you mentioned fit in:
|
||||
|
||||
### A. OpenClaw (The Interface / Gateway)
|
||||
* **Role**: **The Conversational Trigger & Notification Layer**.
|
||||
* **Why**: OpenClaw excels at connecting to Discord/Slack/Telegram.
|
||||
* **Integration**:
|
||||
* Don't make OpenClaw the *scheduler*. Make it the *interface*.
|
||||
* You text OpenClaw: *"Run the 'Deep Refactor' workflow on `src/auth`."*
|
||||
* OpenClaw triggers the TORCH backend and pipes updates back to your chat.
|
||||
|
||||
### B. ClaudeCode CLI & Codex CLI (The Workers)
|
||||
* **Role**: **Execution Nodes**.
|
||||
* **Why**: Your local machine (Jules) can't be the only brain forever.
|
||||
* **Integration**:
|
||||
* Create specific "Nodes" in your graph for these tools.
|
||||
* **"Claude Node"**: Wraps the `claude` CLI. Best for complex reasoning/architecture.
|
||||
* **"Codex Node"**: Wraps the `codex` CLI. Best for pure code generation/completion.
|
||||
* **"Local Node"**: Runs local models or scripts (what you do now).
|
||||
|
||||
## 4. The Roadmap (How to get there)
|
||||
|
||||
You don't need to do this alone. Here is a phased approach:
|
||||
|
||||
**Phase 1: The Graph Data Structure (Backend)**
|
||||
* Stop thinking in `roster.json`. Start thinking in `DAGs` (Directed Acyclic Graphs).
|
||||
* Create a schema for a "Workflow" (e.g., inputs, steps, outputs).
|
||||
* *Action*: I can help you draft this `workflow.schema.json`.
|
||||
|
||||
**Phase 2: The "Headless" Runner**
|
||||
* Modify `run-scheduler-cycle.mjs` to accept a `--workflow` argument instead of just looping a roster.
|
||||
* It executes the graph: runs Node A, passes output to Node B.
|
||||
|
||||
**Phase 3: The UI (The "Face")**
|
||||
* Build a simple React/Next.js app that reads these JSON workflow files and renders them with React Flow.
|
||||
* Allow editing and saving back to JSON.
|
||||
|
||||
**Phase 4: The Integrations**
|
||||
* Add "Nodes" for OpenClaw (notifications) and External CLIs (heavy lifting).
|
||||
|
||||
## 5. Summary Opinion
|
||||
Taking TORCH "node-based" transforms it from a **Janitor** (cleaning up code daily) into a **Factory** (building features on demand).
|
||||
|
||||
* **Tools**: Use **React Flow** for UI. Use **OpenClaw** for ChatOps.
|
||||
* **Strategy**: Keep the "Core" (locking/state) but replace the "Scheduler" (loop) with a "Runner" (graph).
|
||||
@@ -1,56 +0,0 @@
|
||||
# Agent Handoffs
|
||||
|
||||
This directory stores reusable context so future agents can work faster without repeating discovery.
|
||||
|
||||
## Directory purpose
|
||||
|
||||
- `learnings/`: successful patterns, proven fixes, and repeatable implementation guidance.
|
||||
- `incidents/`: failures, root causes, mitigations, and prevention guidance.
|
||||
|
||||
## When to write a note
|
||||
|
||||
Create or update a note when your work reveals knowledge likely useful in future sessions.
|
||||
|
||||
Examples:
|
||||
|
||||
- A reliable fix pattern that can be reused.
|
||||
- A recurring failure mode and its mitigation.
|
||||
- A validation approach that prevented regressions.
|
||||
|
||||
If the issue is still active/reproducible, also update `KNOWN_ISSUES.md`.
|
||||
|
||||
## Naming convention
|
||||
|
||||
Use:
|
||||
|
||||
- `YYYY-MM-DD-short-topic.md`
|
||||
|
||||
Examples:
|
||||
|
||||
- `2026-02-14-validation-before-summary.md`
|
||||
- `2026-02-14-missing-baseline-context.md`
|
||||
|
||||
## Required note structure
|
||||
|
||||
Each learning/incident note should include:
|
||||
|
||||
1. Context
|
||||
2. Observation
|
||||
3. Action taken
|
||||
4. Validation performed
|
||||
5. Recommendation for next agents
|
||||
|
||||
## Writing guidelines
|
||||
|
||||
- Keep entries short and actionable.
|
||||
- Prefer bullets over long narrative.
|
||||
- Include exact commands when useful for reproduction.
|
||||
- Avoid secrets, credentials, and identifying data.
|
||||
- Distinguish facts from assumptions.
|
||||
|
||||
## Maintenance workflow
|
||||
|
||||
1. Search for an existing related note.
|
||||
2. Update it if it already covers your case.
|
||||
3. Otherwise, add a new note using the naming convention.
|
||||
4. Cross-link related notes when helpful.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Missing Baseline Context
|
||||
|
||||
- **Context:** Agent started editing without reviewing baseline policy files first.
|
||||
- **Observation:** Resulting patch required rework to match repository standards.
|
||||
- **Action taken:** Re-read policy docs, reverted conflicting edits, and reapplied changes correctly.
|
||||
- **Validation performed:** Compared final output to policy docs and reran checks.
|
||||
- **Recommendation for next agents:** Read `AGENTS.md` and `CLAUDE.md` first on every run to avoid avoidable churn.
|
||||
@@ -1,111 +0,0 @@
|
||||
# Hermit "text file busy" (ETXTBSY) blocks all Torch commands on Goose Desktop
|
||||
|
||||
## Context
|
||||
|
||||
- **Date discovered:** 2026-02-15
|
||||
- **Platform:** Goose Desktop (Linux), any version shipping `/usr/lib/goose/resources/bin/node-setup-common.sh`
|
||||
- **Impact:** 100% of Torch lock/check/complete commands fail. Daily and weekly scheduler runs cannot proceed.
|
||||
- **Severity:** Critical — completely blocks all agent scheduling.
|
||||
|
||||
## Observation
|
||||
|
||||
Every `node` or `npx` invocation on Goose Desktop runs through a Bash wrapper (`/usr/lib/goose/resources/bin/node`) that sources `node-setup-common.sh`. This setup script:
|
||||
|
||||
1. Downloads the hermit binary (~20 MB ELF) directly into `~/.config/goose/mcp-hermit/bin/hermit`
|
||||
2. Runs `hermit init` — which **overwrites its own binary** in-place (replaces the ELF with a ~1.5 KB shell stub)
|
||||
3. On Linux, writing to a binary that is currently executing produces `ETXTBSY` ("text file busy")
|
||||
|
||||
The error is **100% reproducible** on clean state. Once hermit is downloaded, `hermit init` always fails because the running process holds the binary open.
|
||||
|
||||
### Secondary bug: exit code swallowing
|
||||
|
||||
The Goose `node` wrapper also swallows non-zero exit codes:
|
||||
|
||||
```bash
|
||||
node "$@" || log "Failed to execute 'node' with arguments: $*"
|
||||
```
|
||||
|
||||
This means `torch-lock lock` returning exit 3 (lock denied) appears as exit 0 to the caller, breaking the scheduler's lock-race retry logic.
|
||||
|
||||
## Root cause analysis
|
||||
|
||||
```
|
||||
/usr/lib/goose/resources/bin/node (bash wrapper)
|
||||
→ sources node-setup-common.sh
|
||||
→ downloads hermit ELF binary to ~/.config/goose/mcp-hermit/bin/hermit
|
||||
→ runs: hermit init
|
||||
→ hermit init tries to overwrite ~/.config/goose/mcp-hermit/bin/hermit
|
||||
→ ETXTBSY because that's the binary currently executing
|
||||
→ ERR trap fires → script exits 1
|
||||
```
|
||||
|
||||
## Fix applied (local workaround)
|
||||
|
||||
Patched `node-setup-common.sh` at `/home/user/.local/goose-fix/bin/node-setup-common.sh` with three changes:
|
||||
|
||||
### 1. Atomic download (prevents partial writes / concurrent corruption)
|
||||
```bash
|
||||
HERMIT_TMP=$(mktemp ~/.config/goose/mcp-hermit/bin/.hermit-download.XXXXXX)
|
||||
curl ... | gzip -dc > "$HERMIT_TMP"
|
||||
chmod +x "$HERMIT_TMP"
|
||||
mv -f "$HERMIT_TMP" "$HERMIT_BIN" # atomic rename
|
||||
sync
|
||||
```
|
||||
|
||||
### 2. Copy-for-init (prevents ETXTBSY)
|
||||
```bash
|
||||
HERMIT_INIT_COPY=$(mktemp /tmp/.hermit-init-copy.XXXXXX)
|
||||
cp "$HERMIT_BIN" "$HERMIT_INIT_COPY"
|
||||
chmod +x "$HERMIT_INIT_COPY"
|
||||
"$HERMIT_INIT_COPY" init # executing binary ≠ write target → no ETXTBSY
|
||||
rm -f "$HERMIT_INIT_COPY"
|
||||
```
|
||||
|
||||
### 3. flock for concurrency (prevents download races)
|
||||
```bash
|
||||
exec 9>"$HERMIT_LOCK"
|
||||
flock -w 30 9
|
||||
# ... download + init ...
|
||||
flock -u 9
|
||||
```
|
||||
|
||||
### 4. Save/restore working directory
|
||||
```bash
|
||||
_GOOSE_ORIG_CWD="$(pwd)"
|
||||
cd ~/.config/goose/mcp-hermit
|
||||
# ... setup ...
|
||||
cd "$_GOOSE_ORIG_CWD" # restore so caller's relative paths work
|
||||
```
|
||||
|
||||
## Validation performed
|
||||
|
||||
```bash
|
||||
# Clean state test (from scratch):
|
||||
rm -rf ~/.config/goose/mcp-hermit
|
||||
PATH=/home/user/.local/goose-fix/bin:$PATH node bin/torch-lock.mjs check --cadence daily
|
||||
# → Exit 0, full JSON output with lock state
|
||||
# → hermit downloaded, initialized, node installed, lock check completed
|
||||
```
|
||||
|
||||
## What needs to happen upstream
|
||||
|
||||
### Goose repo PR (permanent fix)
|
||||
1. Apply the patch from `docs/agent-handoffs/incidents/goose-hermit-etxtbsy-fix.patch` to `/usr/lib/goose/resources/bin/node-setup-common.sh`
|
||||
2. Fix exit code swallowing in `/usr/lib/goose/resources/bin/node` and `npx`:
|
||||
```bash
|
||||
# Before (swallows exit code):
|
||||
node "$@" || log "Failed to execute 'node' with arguments: $*"
|
||||
# After (preserves exit code):
|
||||
node "$@"
|
||||
```
|
||||
3. File: <https://github.com/block/goose> — component: `resources/bin/node-setup-common.sh`
|
||||
|
||||
### Torch repo (defensive improvement, optional)
|
||||
- Add `HERMIT_BIN_PATH` env var support so operators can point to a pre-initialized hermit
|
||||
- Add diagnostics to lock preflight failure messages (hermit stderr, `fuser` output, suggested fixes)
|
||||
|
||||
## Recommendation for next agents
|
||||
|
||||
- Until the upstream Goose fix ships, use `PATH=/home/user/.local/goose-fix/bin:$PATH` before any `node`/`npm` commands
|
||||
- If hermit gets stuck again: `rm -rf ~/.config/goose/mcp-hermit` and retry
|
||||
- The exit code swallowing bug means schedulers must parse stdout for `LOCK_STATUS=denied` rather than relying on exit codes
|
||||
@@ -1,23 +0,0 @@
|
||||
# Incident: `npm test` Timeout in Sandbox Environment
|
||||
|
||||
## Context
|
||||
Running `npm test` as part of the `onboarding-audit-agent` daily check.
|
||||
|
||||
## Observation
|
||||
`npm test` timed out after 401 seconds.
|
||||
The command runs: `npm run validate:scheduler && npm run test:integration:e2e && node --test test/*.test.mjs tests/*.test.js`.
|
||||
- `validate:scheduler` passed (200ms).
|
||||
- `test:integration:e2e` passed (692ms).
|
||||
- `node --test test/*.test.mjs tests/*.test.js` timed out.
|
||||
Individual runs of many test files passed quickly.
|
||||
|
||||
## Action taken
|
||||
Recorded the timeout in `artifacts/onboarding-audit-agent/report.md`.
|
||||
Verified individual tests passed.
|
||||
|
||||
## Validation performed
|
||||
Ran individual tests (`lock-ops.test.mjs`, `lock-acquisition-resilience.integration.test.mjs`, etc.) and they passed.
|
||||
|
||||
## Recommendation for next agents
|
||||
Investigate why running the full suite causes a timeout. It might be resource contention or a specific test that hangs only in the full suite context.
|
||||
Workaround: Run specific test subsets or increase timeout if possible.
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
# Codex environment test failures: scheduler platform assertion and memory telemetry capture
|
||||
|
||||
## Context
|
||||
- Date: 2026-02-20
|
||||
- Agent: `docs-code-investigator`
|
||||
- Validation stage for documentation-only updates (`src/services/memory/index.js`).
|
||||
|
||||
## Observation
|
||||
- `npm test` fails in `test/scheduler-preflight-lock.e2e.test.mjs`.
|
||||
- Failure detail: first e2e assertion expects frontmatter platform to be `unknown`, but scheduler log in this environment reports `platform: codex`.
|
||||
- Targeted memory test bundle (`node --test test/memory-*.test.mjs test/memory.test.js`) fails in `test/memory-telemetry.test.mjs`.
|
||||
- Direct run (`node test/memory-telemetry.test.mjs`) shows fixture child process exits with code `0` but both `stdout` and `stderr` are empty, failing assertions that expect `[PASS]` and `TORCH-MEMORY` output.
|
||||
|
||||
## Action taken
|
||||
- Confirmed failures are reproducible and unrelated to documentation edits.
|
||||
- Captured reproductions with direct command runs to reveal assertion details.
|
||||
- Updated `KNOWN_ISSUES.md` with active issue entries for both failures.
|
||||
|
||||
## Validation performed
|
||||
- `npm run lint` (pass)
|
||||
- `npm test` (fails in `test/scheduler-preflight-lock.e2e.test.mjs`)
|
||||
- `node --test test/scheduler-preflight-lock.e2e.test.mjs`
|
||||
- `node test/scheduler-preflight-lock.e2e.test.mjs`
|
||||
- `node --test test/memory-*.test.mjs test/memory.test.js`
|
||||
- `node test/memory-telemetry.test.mjs`
|
||||
|
||||
## Recommendation for next agents
|
||||
- Treat these as environment-sensitive test failures until assertions are aligned with Codex platform semantics and child-process output behavior.
|
||||
- If updating tests, follow spec-correction protocol and preserve strict behavioral intent.
|
||||
- Keep `KNOWN_ISSUES.md` dates/status current after remediation.
|
||||
@@ -1,26 +0,0 @@
|
||||
# content-audit-agent: no upload/contribution surface to audit
|
||||
|
||||
## Context
|
||||
- Date: 2026-02-20
|
||||
- Agent: `content-audit-agent`
|
||||
- Prompt scope: verify upload/contribution user docs against runtime behavior.
|
||||
|
||||
## Observation
|
||||
- `docs/` contains TORCH scheduler/lock documentation, not upload/contribution-media product docs.
|
||||
- Repository codebase has no upload UI/API/storage/moderation implementation paths to validate.
|
||||
- Keyword scans for upload/resumability/moderation claims return prompt text and unrelated matches only.
|
||||
|
||||
## Action taken
|
||||
- Completed required startup and memory retrieval.
|
||||
- Produced canonical run artifacts under `src/context/`, `src/todo/`, `src/decisions/`, `src/test_logs/`.
|
||||
- Generated reproducible audit evidence in `artifacts/docs-audit/2026-02-20/`.
|
||||
- Added active issue entry in `KNOWN_ISSUES.md`.
|
||||
|
||||
## Validation performed
|
||||
- `rg --hidden --files docs | sort`
|
||||
- `rg --hidden --line-number -i "upload|contribut|media|mime|multipart|resum|chunk|thumbnail|transcod|moderat|attribution|license" docs`
|
||||
- `rg --hidden --line-number -i "upload|multipart|mime|thumbnail|transcod|resumable|presign|signed url|moderat|attribution|drag.?drop|content-type" src docs test`
|
||||
|
||||
## Recommendation for next agents
|
||||
- Treat this prompt as a scope-mismatch no-op until repository adds upload/contribution features or prompt scope is revised.
|
||||
- If scope is changed, target actual TORCH user docs contracts (lock lifecycle, scheduler flow, dashboard behavior) instead of upload-media behavior.
|
||||
@@ -1,35 +0,0 @@
|
||||
# Incident: pr-review-agent verify-run-artifacts failure
|
||||
|
||||
**Date:** 2026-02-19T05:06:16Z
|
||||
**Agent:** pr-review-agent
|
||||
**Platform:** linux
|
||||
**Incident ID:** PR-REVIEW-ARTIFACT-METADATA-2026-02-19
|
||||
|
||||
## Context
|
||||
|
||||
The `pr-review-agent` ran on 2026-02-19 (weekly cadence) on a linux platform. The scheduler's validate step (`node scripts/agent/verify-run-artifacts.mjs`) exited with code 1 while `npm run lint` passed (exit 0).
|
||||
|
||||
## Observation
|
||||
|
||||
- **failure_reason:** `Validation failed (Verify: 1, Lint: 0)`
|
||||
- The `verify-run-artifacts.mjs` script checks four artifact directories: `src/context/`, `src/todo/`, `src/decisions/`, `src/test_logs/`.
|
||||
- Each artifact must include YAML frontmatter with keys: `agent:`, `cadence:`, and `session:` or `run-start:`.
|
||||
- The agent either did not write any artifacts for the run window, or wrote files that lacked the required metadata fields.
|
||||
|
||||
## Action taken
|
||||
|
||||
- Documented this failure as an incident note.
|
||||
- No code changes made — the verify script and prompt are correct.
|
||||
- The prompt already instructs artifact writing (`Run artifacts (required)`) but does not specify the required metadata format.
|
||||
|
||||
## Validation performed
|
||||
|
||||
- Reviewed `verify-run-artifacts.mjs` source — the metadata validation logic (`validateArtifactMetadata`) is correct and checks for `agent:`, `cadence:`, and `session:`/`run-start:` keys.
|
||||
- No structural defects found in the verify script.
|
||||
|
||||
## Recommendation for next agents
|
||||
|
||||
1. When writing run artifacts, always include YAML frontmatter with `agent:`, `cadence:`, and `run-start:` (or `session:`) fields.
|
||||
2. Artifact content must also reference the active agent name and prompt file path.
|
||||
3. See `scripts/agent/verify-run-artifacts.mjs` lines 190–237 for the exact validation logic.
|
||||
4. Consider updating agent prompts to include an explicit artifact metadata template (a task for `prompt-maintenance-agent`).
|
||||
@@ -1,33 +0,0 @@
|
||||
# Sandbox test permission failures: local socket bind and shell spawn blocked
|
||||
|
||||
## Context
|
||||
- Date: 2026-02-20
|
||||
- Agent: `known-issues-agent`
|
||||
- Scope: daily `KNOWN_ISSUES.md` verification in Codex sandbox.
|
||||
|
||||
## Observation
|
||||
- `npm test` fails in this environment for tests that require local socket listeners or shell-backed child processes.
|
||||
- Repro signals:
|
||||
- `node test/dashboard-auth.test.mjs` => `listen EPERM: operation not permitted 127.0.0.1`
|
||||
- `node test/nostr-lock.test.mjs` => `listen EPERM: operation not permitted 0.0.0.0`
|
||||
- `node test/ops.test.mjs` => `spawnSync /bin/sh EPERM`
|
||||
- Additional child-process stdio assertion failures remain in `test/memory-telemetry.test.mjs` (empty `stdout`/`stderr`).
|
||||
|
||||
## Action taken
|
||||
- Reproduced failures with targeted commands and captured exact failing signatures.
|
||||
- Updated `KNOWN_ISSUES.md` with an active sandbox-permissions entry and refreshed verification dates/status for all related entries.
|
||||
- Did not modify tests to relax assertions or bypass behavior checks.
|
||||
|
||||
## Validation performed
|
||||
- `npm test`
|
||||
- `node --test test/scheduler-preflight-lock.e2e.test.mjs`
|
||||
- `node test/memory-telemetry.test.mjs`
|
||||
- `node test/dashboard-auth.test.mjs`
|
||||
- `node test/nostr-lock.test.mjs`
|
||||
- `node test/ops.test.mjs`
|
||||
- `npm run lock:health -- --cadence daily`
|
||||
|
||||
## Recommendation for next agents
|
||||
- Treat these failures as environment constraints unless running in a runtime that permits local bind/listen and shell execution.
|
||||
- For CI portability, consider medium-scope refactors to isolate privileged test paths behind explicit environment guards, without weakening assertions.
|
||||
- Open upstream/environment issue tracking from a network-enabled environment if GitHub issue creation is required.
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
---
|
||||
agent: onboarding-audit-agent
|
||||
cadence: daily
|
||||
run-start: 2026-02-15T01:03:31Z
|
||||
prompt: src/prompts/daily/onboarding-audit-agent.md
|
||||
---
|
||||
|
||||
# Incident: PROMPT-CONTRACT-FAILURES
|
||||
|
||||
**Date:** 2026-02-15T01:03:31Z
|
||||
**Agent:** onboarding-audit-agent
|
||||
**Incident ID:** PROMPT-CONTRACT-FAILURES
|
||||
|
||||
## Description
|
||||
The `npm test` command fails during onboarding validation due to multiple prompt files violating the strict prompt contract enforced by `scripts/validate-prompt-contract.mjs`.
|
||||
|
||||
## Impact
|
||||
New developers cannot run `npm test` cleanly after checking out the repository.
|
||||
|
||||
## Remediation
|
||||
Update all agent prompts in `src/prompts/` to strictly follow the `Scheduler Flow` contract regarding completion commands and log file naming.
|
||||
@@ -1,166 +0,0 @@
|
||||
--- /usr/lib/goose/resources/bin/node-setup-common.sh 2025-10-28 11:13:27.000000000 -0400
|
||||
+++ /home/user/.local/goose-fix/bin/node-setup-common.sh 2026-02-15 13:17:19.900379075 -0500
|
||||
@@ -2,68 +2,107 @@
|
||||
|
||||
# Common setup script for node and npx
|
||||
# This script sets up hermit and node.js environment
|
||||
+#
|
||||
+# PATCHED: Fixes "text file busy" (ETXTBSY) during hermit init.
|
||||
+# Root cause: `hermit init` overwrites its own binary (replaces the ~20MB ELF
|
||||
+# with a ~1.5KB shell stub). On Linux, you cannot write to a binary that is
|
||||
+# currently being executed, so `hermit init` fails with ETXTBSY when the
|
||||
+# downloaded hermit binary is the one running the init command.
|
||||
+#
|
||||
+# Fix: Copy the hermit binary to a temp location, run init from the copy
|
||||
+# (so the executing binary differs from the write target), then clean up.
|
||||
+# Also adds flock to prevent concurrent downloads.
|
||||
+#
|
||||
+# See: docs/agent-handoffs/incidents/2026-02-15-hermit-text-file-busy.md
|
||||
|
||||
-# Enable strict mode to exit on errors and unset variables
|
||||
set -euo pipefail
|
||||
|
||||
-# Set log file
|
||||
LOG_FILE="/tmp/mcp.log"
|
||||
-
|
||||
-# Clear the log file at the start
|
||||
> "$LOG_FILE"
|
||||
|
||||
-# Function for logging
|
||||
log() {
|
||||
local MESSAGE="$1"
|
||||
echo "$(date +'%Y-%m-%d %H:%M:%S') - $MESSAGE" | tee -a "$LOG_FILE" >&2
|
||||
}
|
||||
|
||||
-# Trap errors and log them before exiting
|
||||
trap 'log "An error occurred. Exiting with status $?."' ERR
|
||||
|
||||
-log "Starting node setup (common)."
|
||||
+log "Starting node setup (common) [patched]."
|
||||
|
||||
-# Ensure ~/.config/goose/mcp-hermit/bin exists
|
||||
-log "Creating directory ~/.config/goose/mcp-hermit/bin if it does not exist."
|
||||
mkdir -p ~/.config/goose/mcp-hermit/bin
|
||||
-
|
||||
-# Change to the ~/.config/goose/mcp-hermit directory
|
||||
+# Save the original working directory — hermit init requires cd but we must
|
||||
+# restore it so that the caller's relative paths still work.
|
||||
+_GOOSE_ORIG_CWD="$(pwd)"
|
||||
log "Changing to directory ~/.config/goose/mcp-hermit."
|
||||
cd ~/.config/goose/mcp-hermit
|
||||
|
||||
+# ── Hermit install with flock + atomic write + copy-for-init ──────────
|
||||
+HERMIT_BIN=~/.config/goose/mcp-hermit/bin/hermit
|
||||
+HERMIT_LOCK=~/.config/goose/mcp-hermit/install.lock
|
||||
+
|
||||
+# Acquire an exclusive file lock so concurrent node/npx invocations
|
||||
+# do not race to download or init hermit simultaneously.
|
||||
+exec 9>"$HERMIT_LOCK"
|
||||
+if ! flock -w 30 9; then
|
||||
+ log "WARNING: Could not acquire hermit install lock within 30s; proceeding anyway."
|
||||
+fi
|
||||
+
|
||||
+NEEDS_INIT=0
|
||||
|
||||
-# Check if hermit binary exists and download if not
|
||||
-if [ ! -f ~/.config/goose/mcp-hermit/bin/hermit ]; then
|
||||
- log "Hermit binary not found. Downloading hermit binary."
|
||||
+if [ ! -f "$HERMIT_BIN" ]; then
|
||||
+ log "Hermit binary not found. Downloading hermit binary (atomic)."
|
||||
+ HERMIT_TMP=$(mktemp ~/.config/goose/mcp-hermit/bin/.hermit-download.XXXXXX)
|
||||
curl -fsSL "https://github.com/cashapp/hermit/releases/download/stable/hermit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/').gz" \
|
||||
- | gzip -dc > ~/.config/goose/mcp-hermit/bin/hermit && chmod +x ~/.config/goose/mcp-hermit/bin/hermit
|
||||
- log "Hermit binary downloaded and made executable."
|
||||
+ | gzip -dc > "$HERMIT_TMP"
|
||||
+ chmod +x "$HERMIT_TMP"
|
||||
+ mv -f "$HERMIT_TMP" "$HERMIT_BIN"
|
||||
+ sync
|
||||
+ log "Hermit binary downloaded and made executable (atomic)."
|
||||
+ NEEDS_INIT=1
|
||||
+elif file "$HERMIT_BIN" 2>/dev/null | grep -q "ELF"; then
|
||||
+ # Binary exists but is still the full ELF (init hasn't run yet)
|
||||
+ log "Hermit binary exists but is uninitialized ELF. Will run init."
|
||||
+ NEEDS_INIT=1
|
||||
+fi
|
||||
+
|
||||
+if [ "$NEEDS_INIT" -eq 1 ]; then
|
||||
+ log "Initializing hermit (copy-for-init to avoid ETXTBSY)."
|
||||
+ # hermit init overwrites its own binary, which fails with ETXTBSY
|
||||
+ # if the running binary IS the binary being overwritten.
|
||||
+ # Fix: copy to temp location, run init from the copy.
|
||||
+ HERMIT_INIT_COPY=$(mktemp /tmp/.hermit-init-copy.XXXXXX)
|
||||
+ cp "$HERMIT_BIN" "$HERMIT_INIT_COPY"
|
||||
+ chmod +x "$HERMIT_INIT_COPY"
|
||||
+
|
||||
+ if "$HERMIT_INIT_COPY" init >> "$LOG_FILE" 2>&1; then
|
||||
+ log "hermit init succeeded (via copy)."
|
||||
+ else
|
||||
+ log "ERROR: hermit init failed even with copy-for-init. Check $LOG_FILE for details."
|
||||
+ rm -f "$HERMIT_INIT_COPY"
|
||||
+ flock -u 9
|
||||
+ exit 1
|
||||
+ fi
|
||||
+ rm -f "$HERMIT_INIT_COPY"
|
||||
else
|
||||
- log "Hermit binary already exists. Skipping download."
|
||||
+ log "Hermit already initialized. Skipping init."
|
||||
fi
|
||||
|
||||
+# Release the install lock
|
||||
+flock -u 9
|
||||
|
||||
log "setting hermit cache to be local for MCP servers"
|
||||
mkdir -p ~/.config/goose/mcp-hermit/cache
|
||||
export HERMIT_STATE_DIR=~/.config/goose/mcp-hermit/cache
|
||||
|
||||
-
|
||||
-# Update PATH
|
||||
export PATH=~/.config/goose/mcp-hermit/bin:$PATH
|
||||
log "Updated PATH to include ~/.config/goose/mcp-hermit/bin."
|
||||
|
||||
-
|
||||
-# Verify hermit installation
|
||||
log "Checking for hermit in PATH."
|
||||
which hermit >> "$LOG_FILE"
|
||||
|
||||
-# Initialize hermit
|
||||
-log "Initializing hermit."
|
||||
-hermit init >> "$LOG_FILE"
|
||||
-
|
||||
# Install Node.js using hermit
|
||||
log "Installing Node.js with hermit."
|
||||
-hermit install node >> "$LOG_FILE"
|
||||
+hermit install node >> "$LOG_FILE" 2>&1
|
||||
|
||||
# Verify installations
|
||||
log "Verifying installation locations:"
|
||||
@@ -71,15 +110,12 @@
|
||||
log "node: $(which node)"
|
||||
log "npx: $(which npx)"
|
||||
|
||||
-
|
||||
log "Checking for GOOSE_NPM_REGISTRY and GOOSE_NPM_CERT environment variables for custom npm registry setup..."
|
||||
-# Check if GOOSE_NPM_REGISTRY is set and accessible
|
||||
if [ -n "${GOOSE_NPM_REGISTRY:-}" ] && curl -s --head --fail "$GOOSE_NPM_REGISTRY" > /dev/null; then
|
||||
log "Checking custom goose registry availability: $GOOSE_NPM_REGISTRY"
|
||||
log "$GOOSE_NPM_REGISTRY is accessible. Using it for npm registry."
|
||||
export NPM_CONFIG_REGISTRY="$GOOSE_NPM_REGISTRY"
|
||||
|
||||
- # Check if GOOSE_NPM_CERT is set and accessible
|
||||
if [ -n "${GOOSE_NPM_CERT:-}" ] && curl -s --head --fail "$GOOSE_NPM_CERT" > /dev/null; then
|
||||
log "Downloading certificate from: $GOOSE_NPM_CERT"
|
||||
curl -sSL -o ~/.config/goose/mcp-hermit/cert.pem "$GOOSE_NPM_CERT"
|
||||
@@ -92,10 +128,12 @@
|
||||
else
|
||||
log "GOOSE_NPM_CERT is either not set or not accessible. Skipping certificate setup."
|
||||
fi
|
||||
-
|
||||
else
|
||||
log "GOOSE_NPM_REGISTRY is either not set or not accessible. Falling back to default npm registry."
|
||||
export NPM_CONFIG_REGISTRY="https://registry.npmjs.org/"
|
||||
fi
|
||||
|
||||
+# Restore the original working directory so callers' relative paths work
|
||||
+cd "$_GOOSE_ORIG_CWD"
|
||||
+
|
||||
log "Node setup (common) completed successfully."
|
||||
@@ -1,23 +0,0 @@
|
||||
# Single Distribution Path: GitHub Tarball
|
||||
|
||||
## Context
|
||||
- Installation docs previously mixed npm-style wording with GitHub source installs.
|
||||
- This caused confusion because the package is not published to the npm registry.
|
||||
|
||||
## Observation
|
||||
- `README.md` and `landing/index.html` are the two primary user-facing install surfaces.
|
||||
- Any npm-registry wording in either location risks reintroducing broken install expectations.
|
||||
|
||||
## Action taken
|
||||
- Standardized both surfaces to one install path:
|
||||
- `npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz`
|
||||
- Added explicit README guidance that TORCH is distributed via GitHub tarballs and is not on npm registry.
|
||||
- Updated CLI examples to `npx --no-install` so examples only use locally installed binaries.
|
||||
|
||||
## Validation performed
|
||||
- Searched docs/UI sources for install phrasing and npm-registry implications.
|
||||
- Verified updated install command appears consistently in README and landing page.
|
||||
|
||||
## Recommendation for next agents
|
||||
- Treat GitHub tarball install as the canonical distribution path unless a real npm publish workflow is added.
|
||||
- If distribution changes in the future, update `README.md`, `landing/index.html`, and this learning note in the same PR.
|
||||
@@ -1,19 +0,0 @@
|
||||
# PR Review Agent Limitations in Sandbox
|
||||
|
||||
## Context
|
||||
The `pr-review-agent` is designed to enumerate open PRs using the GitHub API and review them mechanically.
|
||||
|
||||
## Observation
|
||||
In the current execution environment (sandbox), there is no authenticated access to the GitHub API (e.g., via `gh` CLI or `GITHUB_TOKEN`). As a result, the `curl` request to list PRs returns a 404 or requires authentication that is not present.
|
||||
|
||||
## Action Taken
|
||||
- Documented the limitation in the task log.
|
||||
- Fell back to running repository-wide checks (lint, test) on the current HEAD to provide some value (baseline health check).
|
||||
- Created an artifact `artifacts/pr-review/pr-check.txt` capturing the API response.
|
||||
|
||||
## Validation Performed
|
||||
- Confirmed that `npm run lint` and `npm test` still run successfully and provide useful feedback.
|
||||
|
||||
## Recommendation for next agents
|
||||
- Future runs of `pr-review-agent` in this environment should expect PR enumeration to fail.
|
||||
- Consider implementing a "local branch" review mode if multiple branches are available in the sandbox, or skip PR review if no token is found.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Secure Dashboard Binding
|
||||
|
||||
- **Context:** Hardening the local dashboard server.
|
||||
- **Observation:** Default `server.listen(port)` in Node.js binds to all interfaces (`0.0.0.0`), exposing the tool to the local network.
|
||||
- **Action taken:** Explicitly added `'127.0.0.1'` as the hostname argument to `server.listen`.
|
||||
- **Validation performed:** Verified code change with `grep`.
|
||||
- **Recommendation for next agents:** Always specify `'127.0.0.1'` for servers intended only for local use.
|
||||
@@ -1,7 +0,0 @@
|
||||
# Validation Before Summary
|
||||
|
||||
- **Context:** Multi-file documentation update.
|
||||
- **Observation:** Running quick checks before writing summaries catches avoidable mistakes.
|
||||
- **Action taken:** Ran status and diff checks before finalizing.
|
||||
- **Validation performed:** Confirmed expected files changed and no accidental edits were included.
|
||||
- **Recommendation for next agents:** Always run at least one verification command after edits and before committing.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Relay health preflight job before scheduler lock acquisition
|
||||
|
||||
## Context
|
||||
- Scheduler runs were intermittently failing during lock acquisition when relay connectivity degraded.
|
||||
- Existing preflight checks only validated lock query behavior and did not preserve health history for trend analysis.
|
||||
|
||||
## Observation
|
||||
- A dedicated relay probe that checks websocket reachability plus publish/read behavior per relay provides a clearer readiness signal before lock acquisition.
|
||||
- Persisting each probe result enables windowed success-rate alerts and detection of prolonged all-relay outages.
|
||||
|
||||
## Action taken
|
||||
- Added `torch-lock health` / `npm run lock:health` to run relay websocket + publish/read probes per relay.
|
||||
- Integrated scheduler preflight to call `lock:health` immediately before lock acquisition.
|
||||
- Updated scheduler behavior to defer early (status `_deferred`) with reason `All relays unhealthy preflight` and incident metadata when no relay is healthy.
|
||||
- Stored probe history in `task-logs/relay-health/<cadence>.jsonl` and evaluated alert thresholds:
|
||||
- all relays down for more than N minutes,
|
||||
- success rate below X% over a rolling time window.
|
||||
|
||||
## Validation performed
|
||||
- Ran targeted unit tests for relay health threshold evaluation.
|
||||
- Ran scheduler flow parity checks to ensure preflight remains before lock acquisition in required ordering.
|
||||
|
||||
## Recommendation for next agents
|
||||
- Keep `lock:health` enabled for scheduler preflight in automation.
|
||||
- Review `task-logs/relay-health/<cadence>.jsonl` during incidents to distinguish transient single-relay flakiness from full relay outages.
|
||||
- Tune threshold defaults (`--all-relays-down-minutes`, `--min-success-rate`, `--window-minutes`) per environment reliability goals.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Docs Agent: README/TORCH.md Synchronization Pattern
|
||||
|
||||
- **Context:** docs-agent run 2026-02-20. Audited README.md, CONTRIBUTING.md, AGENTS.md against package.json, src/, and TORCH.md.
|
||||
- **Observation:** `TORCH.md` is the authoritative configuration and env-var reference. `README.md` had drifted and was missing 4 env vars and 8 `torch-config.json` keys present in TORCH.md and verified in `src/torch-config.mjs`.
|
||||
- **Action taken:** Added missing items to README.md's "Environment variables" and "Configuration" sections. No changes to CONTRIBUTING.md or AGENTS.md — both were accurate.
|
||||
- **Validation performed:** Grep confirmed all 4 missing env vars are used in `src/torch-config.mjs`. All referenced file paths verified to exist via Glob.
|
||||
- **Recommendation for next agents:**
|
||||
1. When updating env vars or torch-config keys in TORCH.md or `src/torch-config.mjs`, also update README.md's matching sections.
|
||||
2. CONTRIBUTING.md is minimal and accurate — only update if new scripts are added to package.json.
|
||||
3. AGENTS.md is a policy-only file — do not edit it for accuracy; open an issue if policy appears wrong.
|
||||
4. The 4 env vars most often missed in README: `NOSTR_LOCK_PUBLISH_TIMEOUT_MS`, `NOSTR_LOCK_MIN_SUCCESSFUL_PUBLISHES`, `NOSTR_LOCK_RELAY_FALLBACKS`, `NOSTR_LOCK_MIN_ACTIVE_RELAY_POOL`.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Docs Alignment Agent: Claims Tracing Pattern
|
||||
|
||||
- **Context:** docs-alignment-agent run 2026-02-20. Daily pass auditing docs/lib-overview.md, docs/lock-ops-overview.md, docs/memory-index-overview.md, docs/removal.md, docs/architecture/system-overview.md, docs/AGENT_COVERAGE_GAPS.md, src/constants.mjs USAGE_TEXT.
|
||||
- **Observation:** Four confirmed doc/code divergences found and fixed:
|
||||
1. `docs/lib-overview.md` claimed RACE_CHECK_DELAY_MS = 2000ms; actual is 1500ms (src/constants.mjs:2).
|
||||
2. `src/constants.mjs` USAGE_TEXT said `NOSTR_LOCK_QUERY_TIMEOUT_MS` default is 15000; actual default is 30000 (`DEFAULT_QUERY_TIMEOUT_MS`). The 15000 value matches `DEFAULT_PUBLISH_TIMEOUT_MS` — likely copy-paste confusion between the two timeouts.
|
||||
3. `docs/removal.md` env var cleanup list was missing 4 vars: `NOSTR_LOCK_PUBLISH_TIMEOUT_MS`, `NOSTR_LOCK_MIN_SUCCESSFUL_PUBLISHES`, `NOSTR_LOCK_RELAY_FALLBACKS`, `NOSTR_LOCK_MIN_ACTIVE_RELAY_POOL`.
|
||||
4. `docs/AGENT_COVERAGE_GAPS.md` claimed "playwright dependency" — Playwright is not in package.json; E2E tests use Node.js built-in `--test` runner.
|
||||
- **Action taken:** Fixed all four issues with minimal, targeted edits.
|
||||
- **Validation performed:** Grep confirmed exact corrected values in all edited files post-edit.
|
||||
- **Recommendation for next agents:**
|
||||
1. When auditing docs, always trace numeric constants (timeouts, delays, counts) to `src/constants.mjs` — these drift most often.
|
||||
2. The USAGE_TEXT in `src/constants.mjs` is the CLI `--help` string — verify its defaults match the exported constants above it.
|
||||
3. `docs/removal.md` env var list must stay in sync with `src/torch-config.mjs` — the four new relay-reliability vars are the most commonly missed.
|
||||
4. When AGENT_COVERAGE_GAPS.md mentions technology dependencies, verify against `package.json` before trusting the claim.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Learning: Scheduler Cycle File Documentation Patterns
|
||||
|
||||
- **Context:** docs-code-investigator run 2026-02-20. Documented `scripts/agent/run-scheduler-cycle.mjs` (926 lines) with JSDoc, inline comments, and `docs/scheduler-cycle-overview.md`.
|
||||
- **Observation:** The file's logic precisely mirrors the 19 numbered steps in `src/prompts/scheduler-flow.md`. The top-of-file comment already referenced scheduler-flow.md but didn't enumerate the steps — adding them makes the code immediately navigable.
|
||||
- **Action taken:** Added module-level JSDoc with 19-step flow, JSDoc to 15 internal functions, targeted inline comments, and a full overview doc.
|
||||
- **Validation performed:** Static read of full 926-line file. Call graph traced to scheduler-utils.mjs, scheduler-lock.mjs, src/utils.mjs, roster.json, and test files.
|
||||
- **Recommendation for next agents:**
|
||||
1. When documenting scheduler-related files, always cross-reference `src/prompts/scheduler-flow.md` — it is the spec; the code implements it.
|
||||
2. The most non-obvious invariant: lock:complete is called ONLY after all validation gates pass. Document this explicitly when touching the scheduler.
|
||||
3. Three test files cover scheduler behavior: run-scheduler-cycle-memory-policy.test.mjs, scheduler-time-window.test.mjs, scheduler-preflight-lock.e2e.test.mjs. Run all three when changing scheduler logic.
|
||||
4. `task-logs/<cadence>/.scheduler-run-state.json` is day-scoped (resets at midnight UTC). Do not treat it as persistent cross-day storage.
|
||||
@@ -1,47 +0,0 @@
|
||||
# TORCH Distribution Strategy: Hybrid "Drop-in"
|
||||
|
||||
## Goal
|
||||
To make TORCH the universal "operating system for AI agents" across all languages and environments, we must remove the dependency on Node.js/NPM for end users while maintaining a robust development experience for contributors.
|
||||
|
||||
## The Problem
|
||||
Currently, TORCH is distributed as a tarball that requires `npm install`. This assumes:
|
||||
1. The user has Node.js installed.
|
||||
2. The user is comfortable with `package.json` and `node_modules`.
|
||||
3. The project is a JavaScript/TypeScript project.
|
||||
|
||||
For Python, Go, or Rust developers, this is friction. A "drop-in" tool should work without language-specific prerequisites.
|
||||
|
||||
## The Solution: Hybrid Distribution
|
||||
|
||||
We will support three primary distribution methods to cover all use cases:
|
||||
|
||||
### 1. The Official NPM Package (Current)
|
||||
**Target Audience:** Node.js / TypeScript developers.
|
||||
**Experience:** `npm install torch-lock` (or similar).
|
||||
**Pros:** seamless integration with existing JS toolchains, easy updates via dependabot/renovate.
|
||||
|
||||
### 2. Standalone Binary CLI (New)
|
||||
**Target Audience:** Python, Rust, Go, and non-Node developers.
|
||||
**Experience:** Download a single binary (`torch`) from GitHub Releases and add it to `$PATH`.
|
||||
**Implementation:**
|
||||
- Use Node.js Single Executable Applications (SEA) or `pkg` to compile the JS source into binaries for Linux, macOS, and Windows.
|
||||
- **Zero Dependencies:** No `npm`, no `node_modules`.
|
||||
- **Fast:** Instant startup, no install process.
|
||||
|
||||
### 3. Hosted Dashboard (New)
|
||||
**Target Audience:** All users (especially those using the binary).
|
||||
**Experience:** A static web app hosted at a public URL (e.g., `dashboard.torch.sh` or GitHub Pages).
|
||||
**Workflow:**
|
||||
1. User runs `torch lock ...` locally.
|
||||
2. CLI prints: `View active locks: https://dashboard.torch.sh/?hashtag=my-project-hash`
|
||||
3. User clicks the link to see the dashboard.
|
||||
**Features:**
|
||||
- **Stateless:** The dashboard is a static HTML/JS file.
|
||||
- **Client-side Logic:** It connects directly to Nostr relays from the browser.
|
||||
- **URL Configuration:** Configuration (hashtag, namespace, relays) is passed via URL query parameters, making links shareable.
|
||||
- **Local Persistence:** Settings are saved to `localStorage` for returning visitors.
|
||||
|
||||
## Benefits
|
||||
- **Universal Access:** Removes the Node.js barrier to entry.
|
||||
- **True "Drop-in":** The binary + hosted dashboard requires *zero* local setup beyond downloading one file.
|
||||
- **Maintainability:** The core logic remains in JavaScript/Node.js, shared across all three distribution methods.
|
||||
@@ -1,157 +0,0 @@
|
||||
# TORCH System Architecture
|
||||
|
||||
## High-Level Overview
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Agents["AI Agent Platforms"]
|
||||
Jules["Jules"]
|
||||
Claude["Claude Code"]
|
||||
Codex["Codex"]
|
||||
Goose["Goose"]
|
||||
end
|
||||
|
||||
subgraph TORCH["TORCH CLI / Scheduler"]
|
||||
CLI["bin/torch-lock.mjs"]
|
||||
Lib["src/lib.mjs<br/>Command Dispatch"]
|
||||
LockOps["src/lock-ops.mjs<br/>Lock Publisher / Query"]
|
||||
Health["src/relay-health.mjs<br/>Relay Health Monitor"]
|
||||
Scheduler["scripts/agent/<br/>run-scheduler-cycle.mjs"]
|
||||
end
|
||||
|
||||
subgraph Nostr["Nostr Network"]
|
||||
R1["Relay 1<br/>relay.damus.io"]
|
||||
R2["Relay 2<br/>nos.lol"]
|
||||
R3["Relay 3<br/>relay.primal.net"]
|
||||
end
|
||||
|
||||
subgraph Dashboard["Dashboard"]
|
||||
Server["src/dashboard.mjs<br/>HTTP Server"]
|
||||
UI["dashboard/index.html<br/>+ app.js"]
|
||||
end
|
||||
|
||||
subgraph Memory["Memory Subsystem"]
|
||||
Ingestor["Ingestor"]
|
||||
Retriever["Retriever"]
|
||||
Pruner["Pruner"]
|
||||
Store["memory-store.json"]
|
||||
end
|
||||
|
||||
Agents -->|"torch-lock lock/check/complete"| CLI
|
||||
CLI --> Lib
|
||||
Lib --> LockOps
|
||||
LockOps -->|"NIP-01 Events<br/>(kind 30078)"| R1 & R2 & R3
|
||||
Health -->|"Probe & Track"| R1 & R2 & R3
|
||||
Scheduler -->|"Orchestrate"| CLI
|
||||
|
||||
Server -->|"Serve Static"| UI
|
||||
UI -->|"WebSocket SUB"| R1 & R2 & R3
|
||||
|
||||
Lib --> Ingestor
|
||||
Lib --> Retriever
|
||||
Ingestor --> Store
|
||||
Retriever --> Store
|
||||
Pruner --> Store
|
||||
```
|
||||
|
||||
## Lock Lifecycle
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Agent
|
||||
participant TORCH as TORCH CLI
|
||||
participant Relays as Nostr Relays
|
||||
|
||||
Agent->>TORCH: torch-lock check --cadence daily
|
||||
TORCH->>Relays: Query active locks (REQ filter)
|
||||
Relays-->>TORCH: Existing lock events
|
||||
TORCH-->>Agent: Status: available / locked
|
||||
|
||||
Agent->>TORCH: torch-lock lock --agent my-agent
|
||||
TORCH->>TORCH: Generate NIP-01 event (kind 30078)
|
||||
TORCH->>Relays: Publish lock event (fanout)
|
||||
Relays-->>TORCH: ACK from N relays
|
||||
TORCH->>Relays: Race-check query (verify no conflicts)
|
||||
Relays-->>TORCH: Confirm lock acquired
|
||||
TORCH-->>Agent: Lock acquired (exit 0)
|
||||
|
||||
Note over Agent: Agent performs work...
|
||||
|
||||
Agent->>TORCH: torch-lock complete --agent my-agent
|
||||
TORCH->>Relays: Publish completion event
|
||||
TORCH-->>Agent: Completed (exit 0)
|
||||
```
|
||||
|
||||
## Scheduler Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start["Scheduler Cycle Start"] --> LoadRoster["Load Roster<br/>(roster.json)"]
|
||||
LoadRoster --> Filter["Filter by cadence<br/>(daily/weekly)"]
|
||||
Filter --> CheckPaused{"Agent<br/>paused?"}
|
||||
CheckPaused -->|Yes| Skip["Skip agent"]
|
||||
CheckPaused -->|No| CheckCompleted{"Already<br/>completed<br/>today?"}
|
||||
CheckCompleted -->|Yes| Skip
|
||||
CheckCompleted -->|No| CheckLock{"Lock<br/>available?"}
|
||||
CheckLock -->|No| Skip
|
||||
CheckLock -->|Yes| Acquire["Acquire Lock"]
|
||||
Acquire --> RunAgent["Execute Agent Prompt"]
|
||||
RunAgent --> Complete["Publish Completion"]
|
||||
Complete --> LogResult["Write Task Log"]
|
||||
Skip --> LogResult
|
||||
LogResult --> Next{"More<br/>agents?"}
|
||||
Next -->|Yes| CheckPaused
|
||||
Next -->|No| End["Cycle Complete"]
|
||||
```
|
||||
|
||||
## Memory Subsystem
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Events["Runtime Events"] --> Ingestor
|
||||
Ingestor --> |"Chunk + Summarize"| Store["Memory Store<br/>(JSON file)"]
|
||||
Store --> Retriever
|
||||
Retriever --> |"Rank: semantic +<br/>importance + recency"| Results["Ranked Memories"]
|
||||
|
||||
Scheduler["Maintenance<br/>Scheduler"] --> Pruner
|
||||
Pruner --> |"TTL / merge /<br/>archive / delete"| Store
|
||||
|
||||
subgraph Jobs["Scheduled Jobs"]
|
||||
J1["Ingest (1-5 min)"]
|
||||
J2["Consolidate (1 hr)"]
|
||||
J3["Prune (daily)"]
|
||||
J4["Deep Merge (weekly)"]
|
||||
end
|
||||
Scheduler --> Jobs
|
||||
```
|
||||
|
||||
## Key Data Flows
|
||||
|
||||
| Flow | Protocol | Event Kind | Key Fields |
|
||||
|------|----------|-----------|------------|
|
||||
| Lock acquire | NIP-01 publish | 30078 | d-tag, expiration, agent, cadence, status |
|
||||
| Lock query | NIP-01 REQ | 30078 | #t filter on hashtag |
|
||||
| Lock complete | NIP-01 publish | 30078 | status: "completed" |
|
||||
| Dashboard feed | NIP-01 WebSocket | 30078 | Real-time SUB with since filter |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
TORCH/
|
||||
├── bin/ # CLI entry point
|
||||
├── src/
|
||||
│ ├── lib.mjs # Command dispatch
|
||||
│ ├── lock-ops.mjs # Nostr relay operations
|
||||
│ ├── relay-health.mjs # Relay health monitoring
|
||||
│ ├── dashboard.mjs # Dashboard HTTP server
|
||||
│ ├── ops.mjs # Install/update operations
|
||||
│ ├── services/
|
||||
│ │ ├── memory/ # Memory subsystem
|
||||
│ │ └── governance/ # Governance service
|
||||
│ └── prompts/ # Agent prompt definitions
|
||||
├── dashboard/ # Dashboard frontend
|
||||
├── scripts/agent/ # Scheduler and agent scripts
|
||||
├── test/ # Test suite
|
||||
├── docs/ # Architecture documentation
|
||||
└── migrations/ # Database schemas (future)
|
||||
```
|
||||
@@ -1,45 +0,0 @@
|
||||
# Dashboard App Overview
|
||||
|
||||
## What this module does
|
||||
The `dashboard/app.js` module is the main entry point for the frontend UI logic of the TORCH web dashboard. It manages the configuration loading cascading from URL queries, `localStorage`, and `torch-config.json` default settings, and maintains live WebSocket connections to Nostr relays. Incoming `LOCK_EVENT_KIND` events are parsed, de-duplicated, and pushed to the UI where they render as lock cards with remaining TTL status via a periodic interval.
|
||||
|
||||
## Main Execution Flow
|
||||
1. **Config Setup**: `bootstrap()` loads settings via `loadTorchConfigFile()`, resolves preferences using `parseDashboardConfig()`, and populates global constants like `RELAYS` and `HASHTAG`.
|
||||
2. **Relay Setup**: `init()` loops over the configured `RELAYS` and calls `connectToRelay()` for each.
|
||||
3. **Data Subscription**: `connectToRelay()` opens a WebSocket, emitting a NIP-01 `REQ` for lock events tagged with the namespace over the past 7 days.
|
||||
4. **Data Intake**: Incoming `EVENT` messages are parsed via `parseLockEvent()`, de-duplicated based on `dTag`, stored in `lockStore`, and logged into the `rawLog` DOM container.
|
||||
5. **UI Rendering**: The receipt of events triggers `scheduleRender()`, which delegates to `renderLocks()` to build DOM nodes using `createLockCard()` for all active/filtered locks.
|
||||
6. **Background Loop**: A 30s interval re-renders the DOM to decrement remaining lock TTL and update the CSS widths of progress bars.
|
||||
|
||||
## Usage Snippet
|
||||
```js
|
||||
// Within dashboard/index.html
|
||||
<script src="app.js" type="module"></script>
|
||||
```
|
||||
*(The module invokes `bootstrap()` as its final statement).*
|
||||
|
||||
## Public API / Exports
|
||||
*(Note: As an entrypoint module assigned via a direct `<script>` import to `window`, it does not export standard ES modules to other files. However, internally these serve as the public API hooks for HTML interactive handlers).*
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `fetchDocs()` | Asynchronously fetches `TORCH.md` and renders parsed markdown using marked and DOMPurify. |
|
||||
| `loadTorchConfigFile()` | Asynchronously fetches and parses the local `torch-config.json` repository config file. |
|
||||
| `parseDashboardConfig()`| Consolidates configs spanning URL search query overrides, localstorage, and standard file fallback values. |
|
||||
| `connectToRelay()` | Opens and manages a resilient WebSocket connection with auto-reconnect logic and event handling. |
|
||||
| `init()` | Iterates configured relays initiating the primary loop connection handlers. |
|
||||
| `bootstrap()` | Primary async entrypoint which fetches configuration before setting DOM default filters and calling `init()`. |
|
||||
|
||||
## Invariants and Edge Cases
|
||||
- **Duplicate Locks**: Handled using `dTag`. The latest `created_at` timestamp is preferred when determining state.
|
||||
- **Relay Instability**: Dropped connections (`ws.close`) fire an automatic reconnect timeout loop after 5000ms.
|
||||
- **Security Check**: Text content mapping to HTML utilizes `DOMPurify.sanitize()` when `fetchDocs()` is rendered to prevent XSS.
|
||||
- **Background Throttling**: The global scope validates visibility via `!document.hidden` and `isAlive()` before polling or rendering frames to conserve resources.
|
||||
|
||||
## Why it works this way
|
||||
Configuration runs dynamically to ensure that static builds generated using `npm run dashboard:serve` do not hard-fix user settings but allow live overrides using URL hash routing or LocalStorage variables. Events are debounced behind a `requestAnimationFrame` wrapper in `scheduleRender()` to avoid locking the DOM when heavily queried relays blast historical events.
|
||||
|
||||
## When to change
|
||||
- **Config format evolution:** Update `loadTorchConfigFile()` if the parent directory structure, or schema definition of `torch-config.json` (`nostrLock` keys) change significantly.
|
||||
- **Adding new Event Kinds**: Modify the websocket subscription filter (`kinds: [LOCK_EVENT_KIND]`) in `connectToRelay` if the dashboard expands visualization beyond raw agent locks.
|
||||
- **Adding layout themes:** Update standard DOM manipulation styles in `createLockCard` if UI library changes.
|
||||
@@ -1,58 +0,0 @@
|
||||
# src/lib.mjs: Core Logic Overview
|
||||
|
||||
## Summary
|
||||
`src/lib.mjs` is the orchestration engine for Torch. It implements the high-level commands (`check`, `lock`, `list`, `complete`) that coordinate between local configuration, the file system (task logs), and Nostr relays (distributed locks).
|
||||
|
||||
It is designed to be:
|
||||
- **Stateless**: No persistent local database; relies on Nostr events and flat files.
|
||||
- **Race-Condition Safe**: Uses a commit-wait-reveal pattern to handle concurrent agent starts.
|
||||
- **Dependency Injected**: Nearly all external side-effects (relays, time, FS) can be mocked.
|
||||
|
||||
## Public API
|
||||
|
||||
### `cmdCheck(cadence, deps)`
|
||||
Aggregates the current state of the world for a given cadence.
|
||||
- **Inputs**: Cadence ('daily'/'weekly').
|
||||
- **Outputs**: JSON object with lists of `locked`, `completed`, `paused`, and `available` agents.
|
||||
- **Side Effects**: Queries relays, reads `task-logs/`, reads `torch-config.json`.
|
||||
|
||||
### `cmdLock(agent, cadence, dryRun, deps)`
|
||||
Attempts to claim a task.
|
||||
- **Returns**: `{ status: 'ok', eventId: '...' }`
|
||||
- **Throws**: `ExitError(3)` if the lock is denied (already active, completed, or race lost).
|
||||
|
||||
### `cmdComplete(agent, cadence, dryRun, deps)`
|
||||
Finalizes a task.
|
||||
- **Action**: Publishes a permanent event (no expiration) with `status: 'completed'`.
|
||||
- **Constraint**: Requires an existing active lock for the agent.
|
||||
|
||||
### `cmdList(cadence, deps)`
|
||||
Displays a CLI-friendly table of active locks.
|
||||
|
||||
## Locking Flow (Sequence)
|
||||
|
||||
1. **Check**: Query relays for any existing valid lock for `(agent, cadence, date)`.
|
||||
- If found and `status=completed`: **Abort** (Task done).
|
||||
- If found and `status=started` (and not expired): **Abort** (Agent busy).
|
||||
2. **Generate**: Create a new ephemeral Nostr keypair.
|
||||
3. **Build**: Create a signed event (Kind 30078) with:
|
||||
- Tags: `d` (identifier), `expiration`, `t` (search tags).
|
||||
- Content: JSON payload with metadata.
|
||||
4. **Publish**: Send to all configured relays.
|
||||
5. **Race Check**:
|
||||
- Wait `RACE_CHECK_DELAY_MS` (default 1500ms).
|
||||
- Query relays again.
|
||||
- If another valid event exists with an *earlier* `created_at`: **Abort** (Race lost).
|
||||
- Otherwise: **Success**.
|
||||
|
||||
## Key Invariants
|
||||
|
||||
- **Uniqueness**: Only one valid lock per (agent, cadence, date) should exist at a time.
|
||||
- **Expiration**: All locks must have an `expiration` tag (NIP-40) to prevent zombies, except completion events.
|
||||
- **Completion**: A completion event is permanent (no expiration) and effectively "freezes" the slot for that date.
|
||||
|
||||
## When to Change
|
||||
|
||||
- **Protocol Upgrades**: If changing the Nostr event structure or tags.
|
||||
- **Concurrency Tuning**: Adjusting `RACE_CHECK_DELAY_MS` or retry logic.
|
||||
- **New Commands**: Adding new lifecycle states (e.g., `failed` or `paused` via CLI).
|
||||
@@ -1,57 +0,0 @@
|
||||
# Lock Operations (`src/lock-ops.mjs`) Overview
|
||||
|
||||
This module implements the core distributed locking mechanism for the Torch agent system using the Nostr protocol. It ensures that agents operating on different machines or environments can coordinate their activities without race conditions.
|
||||
|
||||
## Architecture
|
||||
|
||||
The module is built around two main components:
|
||||
|
||||
1. **`RelayHealthManager`**: A singleton (accessible via `defaultHealthManager`) that tracks the health of Nostr relays. It records success/failure outcomes, latency, and timeouts. It uses this data to score and rank relays, ensuring that operations prioritize healthy relays and quarantine failing ones.
|
||||
2. **`LockPublisher`**: A class that orchestrates the publication of lock events. It uses the `RelayHealthManager` to select the best relays and implements a robust retry mechanism with exponential backoff.
|
||||
|
||||
## Key Flows
|
||||
|
||||
### 1. Acquiring a Lock (`publishLock`)
|
||||
|
||||
The process of acquiring a lock involves publishing a specific Nostr event (Kind 30078 with a `d` tag) to a quorum of relays.
|
||||
|
||||
1. **Initialization**: `publishLock` initializes a `LockPublisher` with the target relays and the lock event.
|
||||
2. **Primary Phase**: The publisher attempts to send the event to the configured *primary relays*.
|
||||
- Relays are prioritized by health score.
|
||||
3. **Fallback Phase** (if needed): If the number of successful publishes is below the required quorum (`minSuccesses`), the publisher attempts *fallback relays*.
|
||||
4. **Retry Loop**:
|
||||
- If quorum is still not met, the system analyzes the failures.
|
||||
- If failures are *transient* (e.g., timeout, network error) and the retry budget permits, the system waits (exponential backoff) and retries the process.
|
||||
- If failures are *permanent* (e.g., auth error) or the retry budget is exhausted, the operation fails.
|
||||
5. **Completion**: Returns the event on success or throws an error on failure.
|
||||
|
||||
### 2. Checking Locks (`queryLocks`)
|
||||
|
||||
Before claiming a task, agents query for existing locks.
|
||||
|
||||
1. **Prioritization**: The function asks `RelayHealthManager` to rank the provided relays.
|
||||
2. **Query**: It sends a request for active lock events to the top-ranked relays.
|
||||
3. **Fallback**: If the query fails (e.g., all primary relays time out), it automatically retries with fallback relays.
|
||||
4. **Filtering**: Results are filtered to exclude expired locks.
|
||||
|
||||
## Public API
|
||||
|
||||
### `publishLock(relays, event, deps)`
|
||||
Publishes a lock event to the network.
|
||||
- **relays**: Array of relay URLs.
|
||||
- **event**: The Nostr event object.
|
||||
- **deps**: Dependencies (config, logger, etc.).
|
||||
|
||||
### `queryLocks(relays, cadence, dateStr, namespace, deps)`
|
||||
Finds active locks for a specific scope.
|
||||
- **cadence**: 'daily' | 'weekly'.
|
||||
- **dateStr**: 'YYYY-MM-DD'.
|
||||
|
||||
### `parseLockEvent(event)`
|
||||
Utility to parse a raw Nostr event into a structured lock object, handling JSON parsing of content and tag extraction.
|
||||
|
||||
## Failure Handling
|
||||
|
||||
- **Transient Errors**: Network timeouts, connection resets, 503s. These trigger retries.
|
||||
- **Permanent Errors**: TLS errors, protocol violations. These abort the retry loop immediately.
|
||||
- **Quarantine**: Relays that fail repeatedly are temporarily "quarantined" (ignored) by the `RelayHealthManager` to prevent slowing down operations, but are slowly reintroduced to check for recovery.
|
||||
@@ -1,83 +0,0 @@
|
||||
# Memory Events Contract
|
||||
|
||||
This contract defines the merge-safe memory pipeline used by `torch-lock memory`.
|
||||
|
||||
## Source of Truth
|
||||
|
||||
- Append-only event files:
|
||||
- `memory/events/YYYY/MM/DD/<timestamp>_<id>.json`
|
||||
- Event schema:
|
||||
- `memory/schema/memory-event.schema.json`
|
||||
- Generated artifact:
|
||||
- `memory_update.md`
|
||||
|
||||
## Commands
|
||||
|
||||
### `torch-lock memory add`
|
||||
|
||||
Writes exactly one new event file.
|
||||
|
||||
Required flags:
|
||||
- `--agent`
|
||||
- `--topic`
|
||||
- one of `--message` or `--message-file`
|
||||
|
||||
Optional flags:
|
||||
- `--created-at` (RFC3339 UTC, default: now)
|
||||
- `--id` (UUIDv7, default: generated)
|
||||
- `--run-id`
|
||||
- `--tags` (comma-separated)
|
||||
- `--source`
|
||||
- `--project` (defaults to `TORCH`; non-`TORCH` is rejected by schema)
|
||||
- `--events-dir` (default: `memory/events`)
|
||||
- `--schema-path` (default: `memory/schema/memory-event.schema.json`)
|
||||
|
||||
Behavior:
|
||||
- Validates event against schema contract before writing.
|
||||
- Enforces append-only naming: `<timestamp>_<id>.json`.
|
||||
- Fails if target file already exists.
|
||||
|
||||
### `torch-lock memory build`
|
||||
|
||||
Builds deterministic markdown from all events.
|
||||
|
||||
Optional flags:
|
||||
- `--events-dir` (default: `memory/events`)
|
||||
- `--schema-path` (default: `memory/schema/memory-event.schema.json`)
|
||||
- `--output` (default: `memory_update.md`)
|
||||
|
||||
Behavior:
|
||||
- Loads and validates all event JSON files.
|
||||
- Enforces filename/date/id contract.
|
||||
- Fails on duplicate ids.
|
||||
- Sorts by `(created_at ASC, id ASC, relative_path ASC)`.
|
||||
- Emits auto-generated header:
|
||||
- `<!-- AUTO-GENERATED: DO NOT EDIT. Source: memory/events -->`
|
||||
- Normalizes output to LF newlines and one trailing newline.
|
||||
|
||||
### `torch-lock memory verify`
|
||||
|
||||
Verification gate for CI and branch safety.
|
||||
|
||||
Optional flags:
|
||||
- `--events-dir` (default: `memory/events`)
|
||||
- `--schema-path` (default: `memory/schema/memory-event.schema.json`)
|
||||
- `--output` (default: `memory_update.md`)
|
||||
|
||||
Behavior:
|
||||
- Re-validates all events + path contract.
|
||||
- Re-renders twice (determinism guard).
|
||||
- Fails if output file is missing header.
|
||||
- Fails if output file bytes differ from deterministic render (no-diff gate).
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: success
|
||||
- `1`: usage error (missing/invalid CLI arguments)
|
||||
- `4`: memory contract violation
|
||||
- schema invalid
|
||||
- filename/date mismatch
|
||||
- duplicate event id
|
||||
- deterministic render mismatch
|
||||
- generated file missing header
|
||||
- generated file out of date
|
||||
@@ -1,132 +0,0 @@
|
||||
# Memory Service Index Overview (`src/services/memory/index.js`)
|
||||
|
||||
## What this module does
|
||||
`src/services/memory/index.js` is the orchestration layer for TORCH memory ingestion, retrieval, pruning, and admin operations.
|
||||
|
||||
It coordinates lower-level modules (`ingestor`, `retriever`, `pruner`, `cache`, `scheduler`, feature flags), persists a process-local `Map` store to `.scheduler-memory/memory-store.json`, and exposes APIs used by CLI commands and scheduler memory workflows.
|
||||
|
||||
## Where it fits
|
||||
- Primary callers:
|
||||
- CLI entrypoints in `src/lib.mjs:28` and command handling in `src/lib.mjs:640`.
|
||||
- Scheduler memory pipelines in `scripts/memory/retrieve.mjs:1` and `scripts/memory/store.mjs:1`.
|
||||
- Prompt context formatter via memory service interface in `src/services/memory/formatter.js:173`.
|
||||
- Side effects:
|
||||
- Filesystem read/write (`.scheduler-memory/memory-store.json`).
|
||||
- In-process cache invalidation and stats mutation.
|
||||
- Telemetry/metrics emission hooks.
|
||||
|
||||
## Typical sequence (ingest -> retrieve -> prune)
|
||||
1. Caller sends raw events to `ingestEvents(events, options)`.
|
||||
2. `ingestMemoryWindow` validates/transforms/summarizes and writes through repository.
|
||||
3. Service clears retrieval cache to avoid stale ranked results.
|
||||
4. Caller queries `getRelevantMemories({ agent_id, query, ... })`.
|
||||
5. Service returns cache hit or ranks records, updates `last_seen`, and caches result.
|
||||
6. Optional `runPruneCycle` removes stale unpinned records (or reports candidates in dry-run mode).
|
||||
|
||||
## Public API summary
|
||||
- `ingestEvents(events, options)`
|
||||
- Stores event-derived memory records.
|
||||
- Observable outputs: persisted records + telemetry/metrics.
|
||||
- `getRelevantMemories(params)`
|
||||
- Returns ranked top-k memory records for query context.
|
||||
- Observable outputs: ranked records + telemetry/metrics + updated `last_seen`.
|
||||
- `runPruneCycle(options)`
|
||||
- Executes prune in `on` mode, or returns skip/dry-run metadata for `off`/`dry-run` modes.
|
||||
- `pinMemory(id, options)` / `unpinMemory(id, options)`
|
||||
- Toggle `pinned` state for prune protection and retrieval weighting.
|
||||
- `markMemoryMerged(id, mergedInto, options)`
|
||||
- Marks record as archived into another id.
|
||||
- `listMemories(filters, options)`
|
||||
- Lists filtered memories sorted by `last_seen` descending.
|
||||
- `inspectMemory(id, options)`
|
||||
- Returns one memory by id.
|
||||
- `triggerPruneDryRun(options)`
|
||||
- Returns prune candidates without deleting.
|
||||
- `memoryStats(options)`
|
||||
- Returns aggregate counts/rates and ingest throughput estimate.
|
||||
|
||||
## Main execution paths
|
||||
- Ingest path:
|
||||
- Input: validated event list.
|
||||
- Output: stored `MemoryRecord[]`.
|
||||
- Side effects: repository inserts, cache clear, ingest metrics.
|
||||
- Retrieval path:
|
||||
- Input: `{agent_id, query, tags, timeframe, k}`.
|
||||
- Output: ranked `MemoryRecord[]`.
|
||||
- Side effects: cache hit/miss telemetry, usage timestamp updates, retrieval metrics.
|
||||
- Prune path:
|
||||
- Input: retention window + prune mode.
|
||||
- Output: pruned records (or dry-run candidates / off-mode no-op).
|
||||
- Side effects: deletes from store, save to disk, cache clear, prune metrics.
|
||||
- Admin path:
|
||||
- Input: id/filter operations from CLI/admin surfaces.
|
||||
- Output: direct record/admin summaries.
|
||||
- Side effects: pin/unpin/merge mutate store and clear cache.
|
||||
|
||||
## Assumptions and invariants
|
||||
- `memoryStore` is process-local and shared by all exports in this module.
|
||||
- All state mutations that influence retrieval semantics clear cache.
|
||||
- Save operations are coalesced (`currentSavePromise` + `pendingSavePromise`) to avoid overlapping writes.
|
||||
- Repository injection objects must implement the methods expected by each API path (`listMemories`, `setPinned`, `markMerged`, etc.).
|
||||
- Telemetry payloads are sanitized by `toSafeTelemetryPayload` to avoid high-risk raw content fields.
|
||||
|
||||
## Edge cases and error paths
|
||||
- JSON store load failures are caught and logged; module falls back to empty `Map`.
|
||||
- Save failures are caught/logged in `performSave`; callers are not failed by persistence errors from that path.
|
||||
- Feature flag can force ingest skip (`isMemoryIngestEnabled`), returning `[]` with telemetry.
|
||||
- Prune mode edge handling:
|
||||
- `off`: no deletion, skip telemetry event.
|
||||
- `dry-run`: returns candidates only.
|
||||
- Several APIs propagate repository/ranker exceptions directly (no catch wrapper).
|
||||
- No explicit retry/backoff inside this module; retry strategy is expected in callers if needed.
|
||||
|
||||
## Performance and concurrency considerations
|
||||
- Retrieval caching is keyed by serialized query params (`JSON.stringify`), reducing repeated ranking cost.
|
||||
- Throughput sample arrays are capped at 10k entries to avoid unbounded growth.
|
||||
- Save coalescing collapses concurrent write bursts to at most one active write plus one queued write.
|
||||
- Process-level in-memory state means multi-process deployments can drift unless coordinated externally.
|
||||
|
||||
## Security considerations
|
||||
- ⚠️ SECURITY: This module handles user/agent-provided memory content and telemetry emission.
|
||||
- `toSafeTelemetryPayload` intentionally strips `content`, `summary`, `query`, and `raw` fields to reduce accidental leakage.
|
||||
- Filesystem persistence path is under working directory `.scheduler-memory`; callers should treat it as sensitive operational data.
|
||||
- No cryptographic/signing logic exists in this file; avoid introducing any here without security review.
|
||||
|
||||
## Related files and call graph
|
||||
- Direct dependencies used by this module:
|
||||
- `src/services/memory/cache.js:1`
|
||||
- `src/services/memory/ingestor.js:1`
|
||||
- `src/services/memory/retriever.js:1`
|
||||
- `src/services/memory/pruner.js:1`
|
||||
- `src/services/memory/scheduler.js:1`
|
||||
- `src/services/memory/feature-flags.js:1`
|
||||
- Inbound usage:
|
||||
- `src/lib.mjs:28`
|
||||
- `scripts/memory/retrieve.mjs:1`
|
||||
- `scripts/memory/store.mjs:1`
|
||||
- `test/memory-admin.test.mjs:15`
|
||||
- `test/memory-ingest.test.mjs:3`
|
||||
- `test/memory-run-prune-cycle.test.mjs:64`
|
||||
|
||||
## Why it works this way
|
||||
- This design prioritizes deterministic scheduler-local operation with minimal dependencies: JSON-backed persistence + in-process cache + injectable repository hooks for tests.
|
||||
- The module centralizes memory lifecycle events so scheduler contracts can call stable entrypoints (`ingestEvents`, `getRelevantMemories`) with marker verification.
|
||||
|
||||
## When to change
|
||||
- Extract persistence if multi-process consistency or transactional guarantees are required.
|
||||
- Split telemetry/stats into dedicated module if eventing complexity grows.
|
||||
- Replace JSON store if memory volume makes full-file rewrites too expensive.
|
||||
- Refactor if cache keying/ranking inputs require versioned schema or backward-compat layers.
|
||||
|
||||
## Tests validating behavior
|
||||
- `test/memory-ingest.test.mjs`
|
||||
- `test/memory-index.test.mjs`
|
||||
- `test/memory-admin.test.mjs`
|
||||
- `test/memory-run-prune-cycle.test.mjs`
|
||||
- `test/memory-stats.test.mjs`
|
||||
|
||||
Run checks:
|
||||
```bash
|
||||
npm run lint
|
||||
npm test
|
||||
```
|
||||
@@ -1,95 +0,0 @@
|
||||
# Memory Service Overview (`src/services/memory/index.js`)
|
||||
|
||||
This document provides a high-level overview of the `src/services/memory/index.js` module, which orchestrates memory ingestion, retrieval, pruning, and persistence for the TORCH agent system.
|
||||
|
||||
## What this module does
|
||||
|
||||
`src/services/memory/index.js` acts as the **central controller** for the agent memory system. It coordinates:
|
||||
|
||||
1. **Ingestion**: Validating raw events, generating summaries, and persisting them.
|
||||
2. **Retrieval**: Fetching relevant memories based on semantic similarity (or simple filtering) and updating usage stats.
|
||||
3. **Pruning**: Identifying and removing stale, unpinned memories to keep the store manageable.
|
||||
4. **Persistence**: Managing an in-memory store backed by a JSON file (`.scheduler-memory/memory-store.json`), with serialized writes to prevent corruption.
|
||||
5. **Caching**: Providing a short-lived cache for retrieval queries to improve performance within a single run.
|
||||
|
||||
It serves as the main API surface for agents and the scheduler to interact with long-term memory.
|
||||
|
||||
## Key Flows
|
||||
|
||||
### 1. Ingestion Flow (`ingestEvents`)
|
||||
|
||||
1. **Validate**: Checks input events against schema.
|
||||
2. **Ingest**: Calls `ingestMemoryWindow` (in `ingestor.js`) to process events.
|
||||
3. **Persist**: Writes new records to the in-memory store and triggers a save.
|
||||
4. **Invalidate**: Clears the retrieval cache (`cache.clear()`) because new data might change query results.
|
||||
5. **Telemetry**: Emits metrics and telemetry events.
|
||||
|
||||
### 2. Retrieval Flow (`getRelevantMemories`)
|
||||
|
||||
1. **Check Cache**: Returns cached results if the exact query params match.
|
||||
2. **List/Filter**: Fetches all memories or a filtered subset from the store.
|
||||
3. **Rank**: Calls `filterAndRankMemories` (in `retriever.js`) to score relevance (e.g., using embeddings or keyword matching).
|
||||
4. **Update Usage**: Updates `last_seen` timestamp for retrieved memories (async, fire-and-forget).
|
||||
5. **Cache**: Stores the ranked result in the retrieval cache.
|
||||
6. **Return**: Returns the top-k memories.
|
||||
|
||||
### 3. Pruning Flow (`runPruneCycle`)
|
||||
|
||||
1. **Identify Candidates**: Calls `listPruneCandidates` (in `pruner.js`) or scans the store for unpinned memories older than `retentionMs`.
|
||||
2. **Check Mode**:
|
||||
* `off`: Does nothing.
|
||||
* `dry-run`: Returns candidates without deleting.
|
||||
* `on` (default): Deletes candidates from the store.
|
||||
3. **Persist**: Saves the updated store if changes occurred.
|
||||
4. **Invalidate**: Clears the retrieval cache.
|
||||
|
||||
## Public API
|
||||
|
||||
| Function | Description |
|
||||
| :--- | :--- |
|
||||
| `ingestEvents(events, options)` | Ingests raw agent events, creates memory records, and persists them. |
|
||||
| `getRelevantMemories(params)` | Retrieves relevant memories for a query, updating usage stats. |
|
||||
| `runPruneCycle(options)` | Prunes stale, unpinned memories based on retention policy. |
|
||||
| `listMemories(filters, options)` | Lists memories with optional filtering (tags, agent_id, type) and pagination. |
|
||||
| `inspectMemory(id, options)` | Retrieves a single memory record by ID. |
|
||||
| `pinMemory(id, options)` | Pins a memory to prevent it from being pruned. |
|
||||
| `unpinMemory(id, options)` | Unpins a memory, allowing it to be pruned if it becomes stale. |
|
||||
| `markMemoryMerged(id, dest, options)` | Marks a memory as merged into another, effectively archiving it. |
|
||||
| `triggerPruneDryRun(options)` | Simulates a prune cycle to preview what would be deleted. |
|
||||
| `memoryStats(options)` | Returns aggregate statistics (counts, rates, estimated size) for observability. |
|
||||
|
||||
## Usage Example
|
||||
|
||||
```javascript
|
||||
import { ingestEvents, getRelevantMemories } from './src/services/memory/index.js';
|
||||
|
||||
// 1. Ingest a new observation
|
||||
await ingestEvents([{
|
||||
agent_id: 'test-agent',
|
||||
content: 'The api/v1/users endpoint requires Bearer auth.',
|
||||
timestamp: Date.now(),
|
||||
tags: ['api', 'auth']
|
||||
}]);
|
||||
|
||||
// 2. Retrieve relevant context later
|
||||
const memories = await getRelevantMemories({
|
||||
agent_id: 'test-agent',
|
||||
query: 'how to authenticate user api',
|
||||
k: 5
|
||||
});
|
||||
|
||||
console.log(memories);
|
||||
```
|
||||
|
||||
## Key Invariants & Assumptions
|
||||
|
||||
* **Process-Local Truth**: The `memoryStore` Map is the source of truth for the running process. Multi-process coordination (locking) is handled at the scheduler level, not here.
|
||||
* **Serialized Writes**: `saveMemoryStore` uses a promise queue (`currentSavePromise`, `pendingSavePromise`) to ensure writes to `memory-store.json` do not overlap or race, preventing data corruption.
|
||||
* **Cache Invalidation**: Any mutation (ingest, prune, pin, merge) **must** clear the retrieval cache (`cache.clear()`) to ensure consistency.
|
||||
* **Fire-and-Forget Usage Updates**: `updateMemoryUsage` is often called without awaiting its save operation to minimize latency on the retrieval path.
|
||||
|
||||
## When to Change
|
||||
|
||||
* **Schema Updates**: If the memory record structure changes (e.g., new fields), update `ingestor.js` and `schema.js`, and ensure `index.js` passes the new fields correctly.
|
||||
* **Persistence Strategy**: If moving from a JSON file to a real database (SQLite, Postgres), this file would need significant refactoring to replace the in-memory `Map` and `fs` calls with DB queries.
|
||||
* **Concurrency**: If multiple agents run in parallel processes writing to the *same* memory file without a coordinator, the file-based locking/serialization here is insufficient. You would need a real DB or a dedicated memory server process.
|
||||
@@ -1,58 +0,0 @@
|
||||
# Ops Module Overview (`src/ops.mjs`)
|
||||
|
||||
## What this module does
|
||||
This module implements the core CLI operations for the Torch agent system: `init` and `update`. It handles the scaffolding of the agent environment, including directory structure creation, asset copying, configuration generation, and upgrades.
|
||||
|
||||
## Initialization Flow (`cmdInit`)
|
||||
1. **Resolve Configuration**:
|
||||
- Queries user for install directory, namespace, hashtag, and relays.
|
||||
- Validates inputs (prevents path injection).
|
||||
2. **Ensure Directories**:
|
||||
- Creates `torch/prompts/`, `src/proposals/`, `.torch/prompt-history/`.
|
||||
3. **Install Assets**:
|
||||
- Copies app directories (`src`, `bin`, `dashboard`, etc.).
|
||||
- Copies static files (`META_PROMPTS.md`, scheduler docs).
|
||||
- Copies prompts (`daily/`, `weekly/`).
|
||||
4. **Configure**:
|
||||
- Generates `torch-config.json`.
|
||||
- Updates `.gitignore` to exclude `node_modules`.
|
||||
- Generates `TORCH_DASHBOARD.md`.
|
||||
5. **Inject Scripts**:
|
||||
- Adds `torch:*` scripts to the host `package.json` if installed in a subdirectory.
|
||||
|
||||
## Update Flow (`cmdUpdate`)
|
||||
1. **Detect Installation**:
|
||||
- Finds the existing `torch` directory.
|
||||
2. **Backup**:
|
||||
- Creates a timestamped backup in `torch/_backups/`.
|
||||
3. **Update App**:
|
||||
- Overwrites application code (`src`, `bin`, etc.).
|
||||
- Overwrites static files.
|
||||
4. **Update Prompts**:
|
||||
- Adds new prompts from the package.
|
||||
- Preserves existing prompts (unless `--force` is used).
|
||||
5. **Preserve Roster**:
|
||||
- `roster.json` is preserved to avoid resetting rotation (unless `--force` is used).
|
||||
|
||||
## Public API
|
||||
|
||||
### `cmdInit(force, cwd, mockAnswers)`
|
||||
Initializes a new Torch environment.
|
||||
- `force`: Boolean, overwrite existing directory if not empty.
|
||||
- `cwd`: Current working directory.
|
||||
- `mockAnswers`: Optional object for testing (bypasses interactive prompts).
|
||||
|
||||
### `cmdUpdate(force, cwd)`
|
||||
Updates an existing Torch environment.
|
||||
- `force`: Boolean, overwrite modified prompts/roster.
|
||||
- `cwd`: Current working directory.
|
||||
|
||||
## Key Invariants
|
||||
- **Safe Installation**: `validateInstallDir` ensures the target directory name is safe (alphanumeric + safe chars).
|
||||
- **Non-Destructive Defaults**: `init` fails if directory is not empty (unless forced). `update` preserves user data (prompts, roster, config) by default.
|
||||
- **Backup First**: `update` always creates a full backup before modifying files.
|
||||
|
||||
## When to change
|
||||
- When adding new CLI commands or workflows.
|
||||
- When changing the directory structure or asset layout.
|
||||
- When updating the default configuration logic or `torch-config.json` schema.
|
||||
@@ -1,290 +0,0 @@
|
||||
# Prompt Versioning and State Backup
|
||||
|
||||
This document describes how TORCH manages agent prompt versions and runtime state backups — two distinct but complementary systems.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
| Concern | Mechanism | Storage location |
|
||||
|---------|-----------|-----------------|
|
||||
| Prompt change control | Governance proposals | `src/proposals/` |
|
||||
| Prompt version archive | Applied-proposal archive | `.torch/prompt-history/` |
|
||||
| Prompt rollback | `torch-lock rollback` | Reads `.torch/prompt-history/`, falls back to git |
|
||||
| State snapshot | `torch-lock backup` | `.torch/backups/<timestamp>/` |
|
||||
|
||||
---
|
||||
|
||||
## 1. Prompt Versioning
|
||||
|
||||
### How it works
|
||||
|
||||
Every agent prompt lives in `src/prompts/daily/<agent>.md` or `src/prompts/weekly/<agent>.md`. These are the canonical sources of truth.
|
||||
|
||||
When a change is proposed and applied through the governance system, TORCH automatically archives the **old** content before overwriting the file. The archive is stored in:
|
||||
|
||||
```
|
||||
.torch/prompt-history/<subpath>/<basename>_<timestamp>_<sha256>.md
|
||||
```
|
||||
|
||||
A sidecar metadata file is written alongside each archive:
|
||||
|
||||
```
|
||||
.torch/prompt-history/<subpath>/<basename>_<timestamp>_<sha256>.meta.json
|
||||
```
|
||||
|
||||
The sidecar records who made the change, why, and when — making the archive self-documenting.
|
||||
|
||||
### Filename format
|
||||
|
||||
```
|
||||
audit-agent_2026-01-15T00-00-00-000Z_a3f5b19c....md
|
||||
│ │ │
|
||||
│ │ └─ SHA-256 of old content
|
||||
│ └─ ISO timestamp (colons/dots replaced with dashes)
|
||||
└─ Prompt base name
|
||||
```
|
||||
|
||||
This naming scheme ensures versions sort lexicographically by time with no reliance on filesystem mtime.
|
||||
|
||||
---
|
||||
|
||||
## 2. Proposal Workflow (Create → Apply → Rollback)
|
||||
|
||||
### Step 1 — Create a proposal
|
||||
|
||||
```bash
|
||||
torch-lock proposal create \
|
||||
--agent <agent-name> \
|
||||
--target src/prompts/daily/<agent>.md \
|
||||
--content /path/to/new-content.md \
|
||||
--reason "Add missing EXIT CRITERIA section"
|
||||
```
|
||||
|
||||
This writes the proposal to `src/proposals/<timestamp>_<agent>/`:
|
||||
- `meta.json` — author, target, reason, status
|
||||
- `new.md` — proposed new content
|
||||
- `change.diff` — git diff against current file
|
||||
|
||||
The target path must be within `src/prompts/daily/` or `src/prompts/weekly/`.
|
||||
|
||||
### Step 2 — List pending proposals
|
||||
|
||||
```bash
|
||||
torch-lock proposal list
|
||||
# or filter by status:
|
||||
torch-lock proposal list --status pending
|
||||
```
|
||||
|
||||
### Step 3 — Inspect a proposal
|
||||
|
||||
```bash
|
||||
torch-lock proposal show --id <proposal-id>
|
||||
```
|
||||
|
||||
### Step 4 — Apply the proposal
|
||||
|
||||
```bash
|
||||
torch-lock proposal apply --id <proposal-id>
|
||||
```
|
||||
|
||||
Before writing the new content, TORCH:
|
||||
1. Validates the proposal passes all invariant checks (required contract headers)
|
||||
2. Archives the **current** file content to `.torch/prompt-history/`
|
||||
3. Writes the new content to the target path
|
||||
4. Attempts a `git add` + `git commit` automatically
|
||||
|
||||
A proposal can only be applied when its status is `pending`.
|
||||
|
||||
### Step 5 — Reject a proposal (optional alternative)
|
||||
|
||||
```bash
|
||||
torch-lock proposal reject --id <proposal-id> --reason "Does not preserve EXIT CRITERIA pattern"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Listing Available Versions
|
||||
|
||||
To see what archived versions exist for a prompt:
|
||||
|
||||
```bash
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md --list
|
||||
```
|
||||
|
||||
Output (JSON array, newest first):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"filename": "audit-agent_2026-02-20T12-00-00-000Z_a3f5b19c.md",
|
||||
"archivedAt": "2026-02-20T12:00:00.000Z",
|
||||
"hash": "a3f5b19c...",
|
||||
"proposalId": "2026-02-20T12-00-00-000Z_prompt-fixer-agent",
|
||||
"author": "prompt-fixer-agent",
|
||||
"reason": "Add missing EXIT CRITERIA section"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Rolling Back a Prompt
|
||||
|
||||
### Restore the most recent version
|
||||
|
||||
```bash
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md
|
||||
# same as:
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md --strategy latest
|
||||
```
|
||||
|
||||
### Restore a specific version by hash fragment
|
||||
|
||||
```bash
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md --strategy a3f5b19c
|
||||
```
|
||||
|
||||
Any unique fragment of the hash or timestamp in the filename works as a strategy value.
|
||||
|
||||
### Fallback to git
|
||||
|
||||
If no local archive exists, TORCH falls back to git:
|
||||
|
||||
```bash
|
||||
# Restores the HEAD version from git (useful if .torch/ was lost)
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md --strategy latest
|
||||
```
|
||||
|
||||
Or specify an explicit git commit SHA:
|
||||
|
||||
```bash
|
||||
torch-lock rollback --target src/prompts/daily/audit-agent.md --strategy <git-sha>
|
||||
```
|
||||
|
||||
The fallback uses `git checkout <commit> -- <target>`, which overwrites the file in the working tree.
|
||||
|
||||
---
|
||||
|
||||
## 5. State Backup
|
||||
|
||||
Agent runtime state lives outside of git in two places:
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `.scheduler-memory/memory-store.json` | Agent long-term memory |
|
||||
| `task-logs/daily/.scheduler-run-state.json` | Scheduler lock deferral state |
|
||||
|
||||
### Create a snapshot
|
||||
|
||||
```bash
|
||||
torch-lock backup
|
||||
```
|
||||
|
||||
This writes a timestamped directory under `.torch/backups/`:
|
||||
|
||||
```
|
||||
.torch/backups/
|
||||
└── 2026-02-20T12-00-00-000Z/
|
||||
├── backup-manifest.json
|
||||
├── .scheduler-memory__memory-store.json
|
||||
└── task-logs__daily__.scheduler-run-state.json
|
||||
```
|
||||
|
||||
`backup-manifest.json` records what was captured, what was skipped (if a file was missing), the git commit at backup time, and the ISO timestamp.
|
||||
|
||||
### Write to a custom directory
|
||||
|
||||
```bash
|
||||
torch-lock backup --output /path/to/my-backup-dir
|
||||
```
|
||||
|
||||
### List existing backups
|
||||
|
||||
```bash
|
||||
torch-lock backup --list
|
||||
```
|
||||
|
||||
Output (JSON array, newest first):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "2026-02-20T12-00-00-000Z",
|
||||
"createdAt": "2026-02-20T12:00:00.000Z",
|
||||
"gitCommit": "a3f5b19...",
|
||||
"captured": [
|
||||
".scheduler-memory/memory-store.json",
|
||||
"task-logs/daily/.scheduler-run-state.json"
|
||||
],
|
||||
"backupDir": "/path/to/TORCH/.torch/backups/2026-02-20T12-00-00-000Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Restore from a backup
|
||||
|
||||
Restoration is a manual copy. From your project root:
|
||||
|
||||
```bash
|
||||
# Restore memory store
|
||||
cp .torch/backups/<id>/.scheduler-memory__memory-store.json .scheduler-memory/memory-store.json
|
||||
|
||||
# Restore scheduler run state
|
||||
cp .torch/backups/<id>/task-logs__daily__.scheduler-run-state.json task-logs/daily/.scheduler-run-state.json
|
||||
```
|
||||
|
||||
There is intentionally no automated restore command — restoring memory state is a significant operation that should be a deliberate human decision.
|
||||
|
||||
### Recommended backup cadence
|
||||
|
||||
The `backup-agent` (weekly) can be configured to run `torch-lock backup` as part of the weekly cycle. Alternatively, add it to your CI pipeline or cron schedule. Backups are cheap (two small JSON files) and idempotent.
|
||||
|
||||
---
|
||||
|
||||
## 6. What Is and Is Not Versioned
|
||||
|
||||
| Artifact | Version tracked | How |
|
||||
|----------|----------------|-----|
|
||||
| `src/prompts/daily/*.md` | Yes | Proposal archive + git history |
|
||||
| `src/prompts/weekly/*.md` | Yes | Proposal archive + git history |
|
||||
| `.scheduler-memory/memory-store.json` | Snapshot only | `torch-lock backup` |
|
||||
| `task-logs/daily/.scheduler-run-state.json` | Snapshot only | `torch-lock backup` |
|
||||
| `src/proposals/*/` | Permanent record | On-disk (not archived further) |
|
||||
| `torch-config.json` | Git history | Standard git |
|
||||
|
||||
---
|
||||
|
||||
## 7. Storage Locations Reference
|
||||
|
||||
```
|
||||
.torch/
|
||||
├── prompt-history/ # Prompt archive (created on first proposal apply)
|
||||
│ └── src/prompts/daily/
|
||||
│ ├── audit-agent_<ts>_<hash>.md # Archived prompt content
|
||||
│ └── audit-agent_<ts>_<hash>.meta.json # Archive metadata
|
||||
└── backups/ # State snapshots
|
||||
└── <timestamp>/
|
||||
├── backup-manifest.json
|
||||
├── .scheduler-memory__memory-store.json
|
||||
└── task-logs__daily__.scheduler-run-state.json
|
||||
|
||||
src/proposals/
|
||||
└── <timestamp>_<agent>/ # One directory per proposal
|
||||
├── meta.json
|
||||
├── new.md
|
||||
└── change.diff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Removing TORCH
|
||||
|
||||
The `.torch/` directory (prompt history + backups) and `src/proposals/` are removed by `torch-lock remove`. See [docs/removal.md](removal.md) for the full removal procedure.
|
||||
|
||||
If you want to preserve the prompt history or state backups before removing TORCH, copy `.torch/` to a safe location first:
|
||||
|
||||
```bash
|
||||
cp -r .torch/ ~/torch-archive-$(date +%Y%m%d)/
|
||||
torch-lock remove
|
||||
```
|
||||
@@ -1,306 +0,0 @@
|
||||
# Proposal: Node-Based Prompt Engineering Editor
|
||||
|
||||
## Context
|
||||
|
||||
TORCH orchestrates 44 agent prompts (23 daily, 21 weekly) via a round-robin scheduler backed by Nostr relay locks. The prompts follow a consistent Markdown convention — shared contract header, identity line, mission statement, authority hierarchy, scope, constraints, workflow — but have no machine-readable metadata beyond what the three validation scripts check (`validate-prompt-contract.mjs`, `validate-scheduler-roster.mjs`, `validate-scheduler-flow-parity.mjs`).
|
||||
|
||||
Today, understanding how agents relate to each other requires reading all 44 Markdown files. There is no way to answer questions like "which agents produce security reports?", "which agents are read-only?", or "what implicit dependencies exist between agents?" without manual inspection.
|
||||
|
||||
The scheduler itself is a flat round-robin over `roster.json`. The current daily/weekly cadence model is just one possible execution topology. If prompts were modularized with structured metadata, the same agents could be composed into arbitrary flows — for example, a security-focused DAG where `deps-security-agent` runs before `fuzz-agent`, which gates `smoke-agent`.
|
||||
|
||||
## Proposal
|
||||
|
||||
Build a **node-based prompt engineering editor** in three incremental phases:
|
||||
|
||||
1. **Metadata extraction** — Parse all prompts into a machine-readable graph model (JSON).
|
||||
2. **Structured frontmatter** — Add YAML frontmatter to all prompts, creating a schema-validated metadata layer.
|
||||
3. **Interactive visualization** — A new dashboard page that renders the agent fleet as a directed graph using Cytoscape.js.
|
||||
|
||||
Each phase delivers value independently. The visualization is read-only; a future Phase 3+ would add editing capabilities through the existing governance proposal system.
|
||||
|
||||
## Architecture Constraints
|
||||
|
||||
These constraints are derived from the current codebase and should be respected:
|
||||
|
||||
- **No new frontend framework.** TORCH is a CLI toolkit with a vanilla JS dashboard. The CSP already allows `cdn.jsdelivr.net`. Use Cytoscape.js via CDN, not React Flow + Vite.
|
||||
- **No new runtime dependencies.** Extraction and validation scripts use only Node.js built-ins.
|
||||
- **Existing validators must not break.** `validate-prompt-contract.mjs` uses `content.includes()` to find tokens. `src/services/governance/index.js` uses regex on file content for `Shared contract (required):` and `Required startup + artifacts + memory + issue capture`. YAML frontmatter prepended before the shared contract blockquote is transparent to both.
|
||||
- **Governance model preserved.** Future editing features must route through `createProposal()` in `src/services/governance/index.js`, not write files directly.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Metadata Extraction & Graph Model
|
||||
|
||||
**Goal:** Parse all 44 prompts into `artifacts/prompt-graph/graph.json` without modifying any existing files.
|
||||
|
||||
### New Files
|
||||
|
||||
```
|
||||
scripts/prompt-graph/
|
||||
├── extract-metadata.mjs # Regex extraction from prompt Markdown
|
||||
├── build-graph.mjs # Combines metadata + roster.json + torch-config.json → graph.json
|
||||
├── graph-model.schema.json # JSON Schema for the graph output format
|
||||
└── validate-graph.mjs # Checks graph integrity (all roster agents have nodes, etc.)
|
||||
```
|
||||
|
||||
### Extraction Patterns
|
||||
|
||||
The following regex patterns extract metadata from the current prompt format:
|
||||
|
||||
```javascript
|
||||
// Identity — "You are: **governance-agent**, responsible for..."
|
||||
/You are:\s*\*\*([^*]+)\*\*,?\s*(.+?)\.?\s*$/m
|
||||
|
||||
// Mission — "Mission: Ensure that all prompt changes..."
|
||||
/(?:Your\s+(?:single-purpose\s+)?)?[Mm]ission:\s*\*?\*?(.+?)(?:\*\*)?$/m
|
||||
|
||||
// Report output directories — "reports/audit/", "reports/security/"
|
||||
/reports\/([a-z-]+)\//g
|
||||
|
||||
// Scope directories — between "In scope:" and "Out of scope:" or next divider
|
||||
// (Section-based extraction, not a single regex)
|
||||
```
|
||||
|
||||
These patterns work against the current prompt format. They are heuristic — some prompts use slight variations ("Your single-purpose mission:", `===` vs `───` dividers). The extraction script should log warnings for prompts that don't match expected patterns, requiring manual review.
|
||||
|
||||
### Graph Model
|
||||
|
||||
**Node types:**
|
||||
|
||||
| Type | Represents | Example ID |
|
||||
|------|-----------|------------|
|
||||
| `scheduler` | Daily or weekly scheduler | `scheduler:daily` |
|
||||
| `agent` | An agent prompt | `agent:governance-agent` |
|
||||
| `artifact` | An output directory | `artifact:reports/audit/` |
|
||||
| `policy` | A shared policy file | `policy:AGENTS.md` |
|
||||
|
||||
**Edge types:**
|
||||
|
||||
| Type | Meaning | Example |
|
||||
|------|---------|---------|
|
||||
| `schedules` | Scheduler runs this agent (from roster order) | `scheduler:daily` → `agent:audit-agent` |
|
||||
| `produces` | Agent writes to this directory | `agent:audit-agent` → `artifact:reports/audit/` |
|
||||
| `reads` | Agent reads this policy file | `agent:audit-agent` → `policy:AGENTS.md` |
|
||||
| `depends_on` | Agent B consumes Agent A's output | `agent:prompt-fixer-agent` → `agent:prompt-safety-agent` |
|
||||
|
||||
**Example node in `graph.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "agent:governance-agent",
|
||||
"type": "agent",
|
||||
"label": "governance-agent",
|
||||
"properties": {
|
||||
"cadence": "daily",
|
||||
"category": "governance",
|
||||
"mode": "read-write",
|
||||
"role": "responsible for reviewing and applying prompt change proposals",
|
||||
"mission_summary": "Ensure that all prompt changes follow the established governance rules",
|
||||
"prompt_file": "src/prompts/daily/governance-agent.md",
|
||||
"outputs": ["src/decisions/"],
|
||||
"scope_dirs": ["src/proposals/", "src/prompts/daily/", "src/prompts/weekly/"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### npm Script
|
||||
|
||||
```json
|
||||
"graph:build": "node scripts/prompt-graph/build-graph.mjs"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: YAML Frontmatter Schema & Migration
|
||||
|
||||
**Goal:** Add structured YAML frontmatter to all 44 prompt files, creating a schema-validated metadata layer that the graph builder, validators, and future editor can consume.
|
||||
|
||||
### Frontmatter Schema
|
||||
|
||||
```yaml
|
||||
---
|
||||
agent: governance-agent # required, must match roster.json entry
|
||||
cadence: daily # required, enum: daily | weekly
|
||||
category: governance # required, see enum below
|
||||
mode: read-write # required, enum: read-only | read-write
|
||||
role: "prompt change reviewer" # optional, extracted from "You are:" line
|
||||
mission_summary: "Review and apply prompt change proposals" # optional, ≤200 chars
|
||||
outputs: # optional, directories this agent produces
|
||||
- src/decisions/
|
||||
scope_dirs: # optional, directories in scope
|
||||
- src/proposals/
|
||||
depends_on: [] # optional, other agent IDs whose output this agent reads
|
||||
---
|
||||
```
|
||||
|
||||
**Category enum:** `audit`, `security`, `docs`, `performance`, `governance`, `maintenance`, `testing`, `refactor`, `synthesis`, `infrastructure`, `debug`, `research`, `proposal`
|
||||
|
||||
### What belongs in frontmatter vs. the prompt body
|
||||
|
||||
Frontmatter captures **machine-queryable classification metadata**: category, mode, cadence, outputs, dependencies. The prompt body retains the **behavioral specification**: the actual instructions, constraints, workflow steps, and failure modes that the agent executes. Frontmatter should not duplicate prose from the body. The `mission_summary` field is the one overlap — it should be a short (≤200 char) summary, not a copy of the Mission section.
|
||||
|
||||
### Compatibility
|
||||
|
||||
The frontmatter is prepended *before* the existing shared contract blockquote. This is safe because:
|
||||
|
||||
- `validate-prompt-contract.mjs` uses `content.includes()` — the `## Required startup` heading and all 11 contract tokens remain in the file body unchanged.
|
||||
- `src/services/governance/index.js` `validateProposal()` uses `regex.test(newContent)` — both required patterns (`Shared contract (required):`, `Required startup + artifacts + memory + issue capture`) still match.
|
||||
- `validate-scheduler-roster.mjs` checks file existence by name, not content.
|
||||
|
||||
### New Files
|
||||
|
||||
```
|
||||
scripts/prompt-graph/
|
||||
├── frontmatter.schema.json # JSON Schema for validation
|
||||
├── migrate-add-frontmatter.mjs # One-time migration: extract metadata, prepend YAML
|
||||
└── validate-frontmatter.mjs # Validate frontmatter against schema, cross-check roster
|
||||
```
|
||||
|
||||
### Safety Gate
|
||||
|
||||
The migration script must run all existing validators after modifying files:
|
||||
|
||||
```bash
|
||||
node scripts/prompt-graph/migrate-add-frontmatter.mjs
|
||||
npm run validate:scheduler # must still pass
|
||||
```
|
||||
|
||||
### Drift Prevention
|
||||
|
||||
The `validate-frontmatter.mjs` script should cross-check the `agent` field against the `You are: **<name>**` line in the body. For other fields (like `mission_summary`), drift is expected since the frontmatter is a summary. The `prompt-maintenance-agent` (weekly) could be extended to detect frontmatter staleness.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Interactive Graph Visualization
|
||||
|
||||
**Goal:** A new dashboard page that renders the agent DAG as an interactive, read-only graph.
|
||||
|
||||
### New Files
|
||||
|
||||
```
|
||||
dashboard/
|
||||
├── graph.html # Graph visualization page
|
||||
└── graphUtils.js # Graph data loading, Cytoscape config, styling, interaction handlers
|
||||
```
|
||||
|
||||
### CDN Dependencies
|
||||
|
||||
All already permitted by the CSP (`script-src ... https://cdn.jsdelivr.net`):
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cytoscape.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dagre.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/cytoscape-dagre.js"></script>
|
||||
```
|
||||
|
||||
### Dashboard Server Change
|
||||
|
||||
In `src/dashboard.mjs`, add to the `allowedPaths` array (line 154):
|
||||
|
||||
```javascript
|
||||
path.join(packageRoot, 'artifacts', 'prompt-graph'),
|
||||
```
|
||||
|
||||
This allows the graph page to fetch `graph.json` from same-origin.
|
||||
|
||||
### Layout
|
||||
|
||||
- **DAG layout** (dagre): Schedulers at top → Agents in middle → Artifacts at bottom
|
||||
- **Left-right flow** for readability with 44+ nodes
|
||||
|
||||
### Visual Encoding
|
||||
|
||||
Node colors by category, using existing CSS custom properties from `dashboard/styles.css`:
|
||||
|
||||
| Category | Color token | Hex |
|
||||
|----------|------------|-----|
|
||||
| audit | `--color-accent` | blue |
|
||||
| security | `--color-danger` | red |
|
||||
| docs | `--color-info` | cyan |
|
||||
| performance | `--color-warning` | yellow |
|
||||
| governance | `--color-success` | green |
|
||||
| testing | `--color-info` | light blue |
|
||||
| maintenance | `--color-text-muted` | gray |
|
||||
|
||||
Edge styles by type: `schedules` = solid, `produces` = dashed, `reads` = dotted, `depends_on` = bold.
|
||||
|
||||
### Interaction
|
||||
|
||||
- **Click node** → Side panel shows: agent name, role, mission summary, category/cadence/mode badges, output dirs, scope dirs, dependencies. Prompt Markdown rendered via the already-loaded `marked.min.js`.
|
||||
- **Cadence filter** → Show daily / weekly / all.
|
||||
- **Category filter** → Multiselect checkboxes.
|
||||
- **Toggle layers** → Show/hide artifact nodes, policy nodes.
|
||||
- **Search** → Filter nodes by name substring.
|
||||
|
||||
### Navigation
|
||||
|
||||
Add a link from the main dashboard (`dashboard/index.html`) to the graph page.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3+ (Future): Interactive Editing & Custom Flows
|
||||
|
||||
This phase is out of scope for the initial implementation but documented here for roadmap context.
|
||||
|
||||
### Prompt Editing via Governance
|
||||
|
||||
- Side-panel form editing of frontmatter fields.
|
||||
- Drag-to-connect `depends_on` edges.
|
||||
- Every edit creates a governance proposal via `createProposal()` (`src/services/governance/index.js`) rather than writing files directly.
|
||||
- The governance-agent reviews and applies proposals during its normal run cycle.
|
||||
|
||||
This requires new API endpoints in `src/dashboard.mjs`:
|
||||
|
||||
```
|
||||
GET /api/graph → returns graph.json
|
||||
GET /api/prompt/:agent → returns prompt file content
|
||||
POST /api/proposals → creates a governance proposal
|
||||
GET /api/proposals → lists pending proposals
|
||||
```
|
||||
|
||||
### Custom Flow Configurations
|
||||
|
||||
The current scheduler runs flat round-robin over `roster.json`. Custom flows would evolve the roster format:
|
||||
|
||||
```json
|
||||
{
|
||||
"flows": {
|
||||
"daily-standard": {
|
||||
"type": "round-robin",
|
||||
"agents": ["audit-agent", "ci-health-agent", "..."],
|
||||
"trigger": { "schedule": "daily" }
|
||||
},
|
||||
"security-scan": {
|
||||
"type": "dag",
|
||||
"nodes": [
|
||||
{ "agent": "deps-security-agent" },
|
||||
{ "agent": "fuzz-agent", "depends_on": ["deps-security-agent"] }
|
||||
],
|
||||
"trigger": { "schedule": "weekly" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This requires significant changes to `scripts/agent/run-scheduler-cycle.mjs` (currently assumes flat round-robin), the lock model (currently one lock per agent per cadence per day), and the task-log format. The graph model and frontmatter from Phases 0-2 lay the groundwork but don't enable this directly.
|
||||
|
||||
### Simulation / Dry-Run
|
||||
|
||||
A "simulate" button that walks the DAG, checks preflight files exist, validates `graph.json` integrity, and reports expected outputs without executing any agents. Useful for validating flow configurations before deploying them.
|
||||
|
||||
---
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Severity | Mitigation |
|
||||
|------|----------|------------|
|
||||
| Frontmatter breaks existing validators | High | Migration script runs all validators as post-check. Verified that `content.includes()` and regex patterns are unaffected by prepended YAML. |
|
||||
| Frontmatter drifts from prompt body | Medium | `validate-frontmatter.mjs` cross-checks `agent` field against `You are: **<name>**` line. Weekly `prompt-maintenance-agent` can detect staleness. |
|
||||
| Extraction regex misses edge cases | Medium | 44 prompts use slight variations. Script logs warnings for non-matching prompts; manual review required. |
|
||||
| `graph.json` becomes stale after prompt edits | Medium | Add `graph:build` to CI or the `validate:scheduler` script chain. |
|
||||
| Cytoscape.js CDN unavailable | Low | Same pattern as existing `marked.min.js` CDN usage. Pin exact versions. |
|
||||
| 44-file frontmatter migration is a large diff | Medium | Migration script is idempotent. Review via `git diff` before committing. |
|
||||
|
||||
## Relationship to Other Proposals
|
||||
|
||||
This proposal complements [Prompt Packages](./prompt-packages.md). Prompt packages define *which* agents are installed for a given project type. The node-based editor would visualize the installed package as a graph and allow users to see and eventually customize the flow for their specific project. The frontmatter schema should be designed to be compatible with the package metadata format once that proposal is implemented.
|
||||
@@ -1,88 +0,0 @@
|
||||
# Roadmap: Project-Type Specific Prompt Packages
|
||||
|
||||
## Context
|
||||
|
||||
Torch currently provides a comprehensive set of agents for repository maintenance, but many of them are implicitly tailored to the Torch repository itself (e.g., specific folder structures like `/workspace/TORCH`, specific tools). As Torch aims to be a generalized tool for *any* project, it needs a way to provide relevant agents without forcing every project to use the same structure or stack.
|
||||
|
||||
## Proposal
|
||||
|
||||
Implement **Project-Type Specific Prompt Packages**. During initialization (`torch init`), users should be able to select the type of project they are building, and Torch will install a curated set of agent prompts relevant to that project type.
|
||||
|
||||
### Goals
|
||||
|
||||
1. **Relevance:** Only install agents that make sense for the project (e.g., don't install a React component auditor for a Python backend).
|
||||
2. **Reduced Noise:** Avoid cluttering the `src/prompts/` directory with irrelevant files.
|
||||
3. **Faster Onboarding:** Provide immediate value by giving users agents that "understand" their stack out of the box.
|
||||
|
||||
### Proposed Packages
|
||||
|
||||
We can categorize projects into "Archetypes" or "Packages":
|
||||
|
||||
#### 1. The "Base" Package (Core)
|
||||
* **Description:** Agents every project needs.
|
||||
* **Included Agents:**
|
||||
* `log-fixer-agent`: Monitor task logs for failures.
|
||||
* `prompt-maintenance-agent`: Ensure prompts are valid.
|
||||
* `todo-triage-agent`: Manage TODOs.
|
||||
* `changelog-agent`: Generate changelogs based on commits.
|
||||
|
||||
#### 2. The "Node.js Backend" Package
|
||||
* **Description:** For server-side JavaScript/TypeScript applications.
|
||||
* **Included Agents:**
|
||||
* `deps-security-agent`: Check `npm audit`.
|
||||
* `api-schema-validator-agent`: Validate OpenAPI/Swagger specs.
|
||||
* `express-route-auditor-agent`: Check for common Express.js pitfalls.
|
||||
* `db-migration-agent`: Check for pending database migrations (e.g., Knex, Prisma).
|
||||
|
||||
#### 3. The "React Frontend" Package
|
||||
* **Description:** For client-side React applications.
|
||||
* **Included Agents:**
|
||||
* `component-audit-agent`: Check for large components, prop drilling.
|
||||
* `accessibility-agent`: Check for ARIA attributes, image alts.
|
||||
* `unused-component-agent`: Find components not imported anywhere.
|
||||
* `bundle-size-agent`: Monitor build artifacts size.
|
||||
|
||||
#### 4. The "Library / Tool" Package
|
||||
* **Description:** For NPM packages or CLI tools intended for distribution.
|
||||
* **Included Agents:**
|
||||
* `semantic-version-agent`: Ensure versioning follows SemVer.
|
||||
* `api-docs-agent`: Generate documentation from JSDoc/TSDoc.
|
||||
* `compatibility-agent`: Check `engines` field in `package.json`.
|
||||
|
||||
#### 5. The "Python / Django" Package (Future)
|
||||
* **Description:** Expanding beyond Node.js.
|
||||
* **Included Agents:**
|
||||
* `pip-audit-agent`: Check Python dependencies.
|
||||
* `django-migration-agent`: Check Django migrations.
|
||||
* `flake8-agent`: Python linting.
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
1. **Directory Structure:** Organise `src/prompts/` into subdirectories for packages, or use a central registry.
|
||||
```
|
||||
src/prompts/
|
||||
├── core/
|
||||
├── node-backend/
|
||||
├── react-frontend/
|
||||
└── library/
|
||||
```
|
||||
2. **CLI Update:** Update `torch init` to ask:
|
||||
> "What type of project is this?"
|
||||
> [ ] Generic / Custom
|
||||
> [ ] Node.js Backend
|
||||
> [ ] React Frontend
|
||||
> [ ] Library / Tool
|
||||
3. **Configuration:** Store the selected package(s) in `torch-config.json`.
|
||||
```json
|
||||
{
|
||||
"projectType": ["node-backend", "react-frontend"],
|
||||
"installedPackages": ["core", "node-backend"]
|
||||
}
|
||||
```
|
||||
4. **Dynamic Roster:** The `roster.json` could be dynamically generated based on installed packages, or simply include all installed agents.
|
||||
|
||||
### Success Metrics
|
||||
|
||||
* Reduction in users deleting/ignoring irrelevant agents.
|
||||
* Increase in successful agent runs (due to better alignment with project structure).
|
||||
* Positive user feedback on "magic" setup experience.
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
# Removing TORCH from a Project
|
||||
|
||||
This guide covers how to completely remove TORCH from a project repository that installed it via the standard installation process (`npm install <torch-tarball> && npx torch-lock init`).
|
||||
|
||||
## Quick Removal (Recommended)
|
||||
|
||||
Run the built-in removal command from your project root:
|
||||
|
||||
```bash
|
||||
npx torch-lock remove
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
1. Detect your TORCH installation (whether installed to `torch/` or to the project root)
|
||||
2. Show a list of all artifacts that will be removed
|
||||
3. Ask for confirmation before deleting anything
|
||||
4. Remove all TORCH files, directories, configuration, and the npm package
|
||||
|
||||
To skip the confirmation prompt:
|
||||
|
||||
```bash
|
||||
npx torch-lock remove --force
|
||||
```
|
||||
|
||||
## What Gets Removed
|
||||
|
||||
The `remove` command cleans up everything that `torch-lock init` created, plus any runtime artifacts:
|
||||
|
||||
| Artifact | Description |
|
||||
|---|---|
|
||||
| `torch/` | The main TORCH install directory (contains src, bin, dashboard, scripts, prompts, etc.) |
|
||||
| `torch-config.json` | TORCH configuration file at the project root |
|
||||
| `.torch/` | Hidden directory for prompt history tracking |
|
||||
| `.scheduler-memory/` | Runtime memory store used by the memory subsystem |
|
||||
| `task-logs/` | Scheduler run logs (daily/weekly cycle logs, relay health history) |
|
||||
| `src/proposals/` | Governance proposals directory |
|
||||
| `torch:*` scripts in `package.json` | Convenience scripts injected into the host project's package.json |
|
||||
| `node_modules/torch-lock/` | The installed npm package |
|
||||
|
||||
## Manual Removal
|
||||
|
||||
If you prefer to remove TORCH manually, or if the `remove` command is unavailable (e.g., the package was already uninstalled), follow these steps from your project root:
|
||||
|
||||
### 1. Remove the TORCH directory
|
||||
|
||||
```bash
|
||||
rm -rf torch/
|
||||
```
|
||||
|
||||
### 2. Remove TORCH configuration
|
||||
|
||||
```bash
|
||||
rm -f torch-config.json
|
||||
```
|
||||
|
||||
### 3. Remove hidden directories
|
||||
|
||||
```bash
|
||||
rm -rf .torch/
|
||||
rm -rf .scheduler-memory/
|
||||
```
|
||||
|
||||
### 4. Remove runtime logs
|
||||
|
||||
```bash
|
||||
rm -rf task-logs/
|
||||
```
|
||||
|
||||
### 5. Remove governance proposals (if empty/unused)
|
||||
|
||||
```bash
|
||||
# Only remove if this directory was created by TORCH and contains no user content
|
||||
rm -rf src/proposals/
|
||||
# Remove src/ too if it's now empty
|
||||
rmdir src/ 2>/dev/null
|
||||
```
|
||||
|
||||
### 6. Clean up package.json scripts
|
||||
|
||||
Remove these keys from the `"scripts"` section of your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"torch:dashboard": "...",
|
||||
"torch:check": "...",
|
||||
"torch:lock": "...",
|
||||
"torch:health": "...",
|
||||
"torch:memory:list": "...",
|
||||
"torch:memory:inspect": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Uninstall the npm package
|
||||
|
||||
```bash
|
||||
npm uninstall torch-lock
|
||||
```
|
||||
|
||||
### 8. Clean up environment variables (if set)
|
||||
|
||||
If you configured TORCH environment variables in your shell profile, CI, or `.env` files, remove them:
|
||||
|
||||
- `NOSTR_LOCK_NAMESPACE`
|
||||
- `NOSTR_LOCK_HASHTAG`
|
||||
- `NOSTR_LOCK_RELAYS`
|
||||
- `NOSTR_LOCK_TTL`
|
||||
- `NOSTR_LOCK_QUERY_TIMEOUT_MS`
|
||||
- `NOSTR_LOCK_PUBLISH_TIMEOUT_MS`
|
||||
- `NOSTR_LOCK_MIN_SUCCESSFUL_PUBLISHES`
|
||||
- `NOSTR_LOCK_RELAY_FALLBACKS`
|
||||
- `NOSTR_LOCK_MIN_ACTIVE_RELAY_POOL`
|
||||
- `NOSTR_LOCK_DAILY_ROSTER`
|
||||
- `NOSTR_LOCK_WEEKLY_ROSTER`
|
||||
- `TORCH_CONFIG_PATH`
|
||||
- `TORCH_DASHBOARD_AUTH`
|
||||
- `TORCH_MEMORY_ENABLED`
|
||||
- `TORCH_MEMORY_INGEST_ENABLED`
|
||||
- `TORCH_MEMORY_RETRIEVAL_ENABLED`
|
||||
- `TORCH_MEMORY_PRUNE_ENABLED`
|
||||
- `AGENT_PLATFORM`
|
||||
|
||||
### 9. Optional: Remove generated reports
|
||||
|
||||
If you ran lock reliability reports, you may also want to remove:
|
||||
|
||||
```bash
|
||||
rm -rf artifacts/lock-reliability/
|
||||
```
|
||||
|
||||
## Verifying Complete Removal
|
||||
|
||||
After removal, verify no TORCH artifacts remain:
|
||||
|
||||
```bash
|
||||
# Should return no results
|
||||
ls torch/ 2>/dev/null
|
||||
ls .torch/ 2>/dev/null
|
||||
ls .scheduler-memory/ 2>/dev/null
|
||||
ls torch-config.json 2>/dev/null
|
||||
|
||||
# Should not contain torch-lock
|
||||
npm ls torch-lock 2>/dev/null
|
||||
|
||||
# Should not contain torch:* scripts
|
||||
grep '"torch:' package.json
|
||||
```
|
||||
|
||||
## Re-installing TORCH
|
||||
|
||||
If you want to re-install TORCH later:
|
||||
|
||||
```bash
|
||||
npm install https://github.com/PR0M3TH3AN/TORCH/archive/<commit-sha>.tar.gz --force && npx torch-lock init
|
||||
```
|
||||
@@ -1,262 +0,0 @@
|
||||
# Scheduler Cycle Overview (`scripts/agent/run-scheduler-cycle.mjs`)
|
||||
|
||||
## What this module does
|
||||
|
||||
`run-scheduler-cycle.mjs` is the main scheduler entry point. Given a cadence
|
||||
(`daily` or `weekly`), it selects the next eligible agent from the roster,
|
||||
acquires a distributed Nostr lock to prevent duplicate execution across agents,
|
||||
runs the selected agent's prompt via a configured handoff command, verifies that
|
||||
required run artifacts and memory evidence were produced, then publishes task
|
||||
completion.
|
||||
|
||||
This script owns the full agent lifecycle for one slot per invocation:
|
||||
lock acquisition → prompt execution → artifact/memory verification → lock:complete.
|
||||
Spawned agents must **not** call `lock:complete` themselves — the scheduler does it
|
||||
after all validation passes.
|
||||
|
||||
## Where it fits
|
||||
|
||||
```
|
||||
npm run scheduler:daily
|
||||
└─ scripts/agent/run-scheduler-cycle.mjs daily
|
||||
├─ src/prompts/roster.json (agent list)
|
||||
├─ torch-config.json (handoff command, memory policy, etc.)
|
||||
├─ scripts/agent/scheduler-utils.mjs
|
||||
├─ scripts/agent/scheduler-lock.mjs
|
||||
├─ npm run lock:check:daily (relay query)
|
||||
├─ npm run lock:lock (relay publish)
|
||||
├─ <handoffCommand> (spawns the agent)
|
||||
├─ scripts/agent/verify-run-artifacts.mjs
|
||||
└─ npm run lock:complete (relay publish, permanent)
|
||||
```
|
||||
|
||||
## Typical call flow
|
||||
|
||||
```
|
||||
1. Parse args → cadence='daily', platform='codex', model=null
|
||||
|
||||
2. Load roster from src/prompts/roster.json → ['audit-agent', 'docs-agent', ...]
|
||||
|
||||
3. Read AGENTS.md → print to stdout (best-effort; non-fatal if missing)
|
||||
|
||||
4. [LOOP] Read run-state → { run_date: '2026-02-20', lock_deferral: null }
|
||||
|
||||
5. [LOOP] Lock health preflight (if enabled):
|
||||
npm run lock:health -- --cadence daily
|
||||
→ non-zero: write _deferred.md or _failed.md, exit
|
||||
|
||||
6. [LOOP] npm run lock:check:daily -- --json --quiet
|
||||
→ exclusion set: { locked: ['ci-health-agent'], paused: [], completed: [] }
|
||||
|
||||
7. [LOOP] Time-window guard: exclude agents that ran within 24h window
|
||||
|
||||
8. [LOOP] Round-robin select:
|
||||
roster = ['audit-agent', 'ci-health-agent', 'docs-agent', ...]
|
||||
previous = 'ci-health-agent' (from latest log)
|
||||
excluded = { 'ci-health-agent' }
|
||||
→ selected = 'docs-agent'
|
||||
|
||||
9. [LOOP] npm run lock:lock -- --agent docs-agent --cadence daily
|
||||
→ exit 0: lock acquired (continue)
|
||||
→ exit 3: race lost (restart loop at step 4)
|
||||
→ exit 2: backend error → defer (non-strict) or fail (strict)
|
||||
|
||||
10. [LOOP] Validate src/prompts/daily/docs-agent.md
|
||||
→ not readable → _failed.md (prompt_parse_error), exit 1
|
||||
→ bad first line → _failed.md (prompt_schema_error), exit 1
|
||||
|
||||
11. [LOOP] Clear deferral state. Run memory retrieve command (if configured).
|
||||
|
||||
12. [LOOP] Run handoff command (e.g. `claude --prompt "$SCHEDULER_PROMPT_PATH"`)
|
||||
→ non-zero → _failed.md (execution_error), exit non-zero
|
||||
→ missing → _failed.md, exit 1
|
||||
|
||||
13. [LOOP] Run memory store command (if configured).
|
||||
|
||||
14. [LOOP] Verify memory evidence (markers/artifacts):
|
||||
→ mode='required' + missing → _failed.md, exit 1
|
||||
→ mode='optional' + missing → console.warn, continue
|
||||
|
||||
15. [LOOP] node scripts/agent/verify-run-artifacts.mjs --since <runStart>
|
||||
→ non-zero → _failed.md (prompt_schema_error), exit non-zero
|
||||
|
||||
16. [LOOP] npm run lint (or configured validationCommands)
|
||||
→ non-zero → _failed.md (execution_error), exit non-zero
|
||||
|
||||
17. [LOOP] npm run lock:complete -- --agent docs-agent --cadence daily
|
||||
→ non-zero → _failed.md, exit non-zero
|
||||
|
||||
18. [LOOP] Write _completed.md task log. Print run summary. Exit 0.
|
||||
```
|
||||
|
||||
## Public API summary
|
||||
|
||||
This is a CLI script — it has no exported functions. The entry point is:
|
||||
|
||||
```bash
|
||||
node scripts/agent/run-scheduler-cycle.mjs <daily|weekly> [--platform <p>] [--model <m>]
|
||||
# or via npm:
|
||||
npm run scheduler:daily
|
||||
npm run scheduler:weekly
|
||||
```
|
||||
|
||||
**Environment variables:**
|
||||
|
||||
| Variable | Source | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `AGENT_PLATFORM` | env | Platform identifier (codex, claude, linux) |
|
||||
| `AGENT_MODEL` | env | Model identifier forwarded to lock events |
|
||||
| `SCHEDULER_LOCK_MAX_RETRIES` | env | Override lock:lock retry count (default: 2) |
|
||||
| `SCHEDULER_LOCK_BACKOFF_MS` | env | Override base backoff ms (default: 250) |
|
||||
| `SCHEDULER_LOCK_JITTER_MS` | env | Override jitter ms (default: 75) |
|
||||
| `SCHEDULER_LOCK_HEALTH_PREFLIGHT` | env | Enable/disable relay health preflight |
|
||||
| `SCHEDULER_SKIP_LOCK_HEALTH_PREFLIGHT` | env | Skip preflight even if enabled |
|
||||
| `SCHEDULER_STRICT_LOCK` | env | Override strict_lock policy |
|
||||
| `SCHEDULER_DEGRADED_LOCK_RETRY_WINDOW_MS` | env | Override deferral window ms |
|
||||
| `SCHEDULER_MAX_DEFERRALS` | env | Override max deferral count |
|
||||
|
||||
**Variables injected into child processes:**
|
||||
|
||||
| Variable | Value | Purpose |
|
||||
|----------|-------|---------|
|
||||
| `SCHEDULER_AGENT` | selected agent name | Used by handoff/memory commands |
|
||||
| `SCHEDULER_CADENCE` | `daily`\|`weekly` | Used by handoff/memory commands |
|
||||
| `SCHEDULER_PROMPT_PATH` | absolute path | Path to prompt file |
|
||||
| `SCHEDULER_MEMORY_FILE` | absolute path | Agent writes learnings here |
|
||||
|
||||
## Key internal functions
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `parseArgs(argv)` | Parses cadence, platform, model from CLI args |
|
||||
| `getSchedulerConfig(cadence, opts)` | Merges torch-config.json + env overrides |
|
||||
| `readRunState(cadence)` / `writeRunState(path, state)` | Deferral state persistence (day-scoped) |
|
||||
| `getLatestFile(logDir)` | Finds most recent valid task log for previous-agent detection |
|
||||
| `selectNextAgent({roster, excludedSet, previousAgent, firstPrompt})` | Round-robin with exclusion |
|
||||
| `validatePromptFile(promptPath)` | Reads and validates prompt format |
|
||||
| `verifyMemoryStep({name, markers, artifacts, outputText, sinceMs})` | Evidence check |
|
||||
| `writeLog({cadence, agent, status, ...})` | Writes YAML-frontmatter task log file |
|
||||
| `exitWithSummary(code, data)` | Prints summary + calls process.exit |
|
||||
|
||||
## Key invariants
|
||||
|
||||
1. **lock:complete gate**: `npm run lock:complete` is called only after memory
|
||||
verification (step 14), artifact verification (step 15), and all validation
|
||||
commands (step 16) pass. If any gate fails, the run ends without publishing
|
||||
completion.
|
||||
|
||||
2. **Completed log gate**: `_completed.md` task log is written only after
|
||||
lock:complete exits 0 (step 17). Earlier failures always write `_failed.md`
|
||||
or `_deferred.md`.
|
||||
|
||||
3. **Race resolution**: Lock exit code 3 (race lost) causes the scheduler to
|
||||
restart the loop at step 4, re-checking what agents are available. This is
|
||||
the normal race resolution path — not an error.
|
||||
|
||||
4. **Time-window guard**: Agents that appear in a recent local log (within 24h
|
||||
for daily, 7d for weekly) are added to the exclusion set regardless of relay
|
||||
state. This handles the cross-midnight edge case where the relay date-scope
|
||||
has rolled over.
|
||||
|
||||
5. **Cycle saturation**: When all roster agents are in the time-window exclusion
|
||||
set, the scheduler exits 0 with a "cycle complete" reason — not treated as
|
||||
an error by cron/CI.
|
||||
|
||||
6. **Deferral state is day-scoped**: `task-logs/<cadence>/.scheduler-run-state.json`
|
||||
is reset whenever `run_date` changes, so deferral budgets do not carry across days.
|
||||
|
||||
## Edge cases & error paths
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| No roster entries | `process.exit(1)` before loop |
|
||||
| Roster fully excluded (not cycle-saturated) | `_failed.md` + exit 1 |
|
||||
| All relays unhealthy (preflight) | `_deferred.md` + exit 0 |
|
||||
| Lock backend error (strict mode) | `_failed.md` + exit 2 |
|
||||
| Lock backend error (non-strict, within window/budget) | `_deferred.md` + exit 0; deferral state persisted |
|
||||
| Lock backend error (non-strict, budget exhausted) | `_failed.md` + exit 2 |
|
||||
| Prompt file missing or unreadable | `_failed.md (prompt_parse_error)` + exit 1 |
|
||||
| Prompt first line not `#` or `>` | `_failed.md (prompt_schema_error)` + exit 1 |
|
||||
| handoffCommand missing (non-interactive mode) | `_failed.md (execution_error)` + exit 1 |
|
||||
| handoffCommand exits non-zero | `_failed.md (execution_error)` + exit non-zero |
|
||||
| Memory evidence missing (required mode) | `_failed.md (prompt_schema_error)` + exit 1 |
|
||||
| Memory evidence missing (optional mode) | console.warn, continue |
|
||||
| Artifact verification fails | `_failed.md (prompt_schema_error)` + exit non-zero |
|
||||
| Validation command exits non-zero | `_failed.md (execution_error)` + exit non-zero |
|
||||
| lock:complete exits non-zero | `_failed.md` + exit non-zero |
|
||||
|
||||
## Security considerations
|
||||
|
||||
- ⚠️ Environment variables injected into child processes (`SCHEDULER_AGENT`,
|
||||
`SCHEDULER_CADENCE`, `SCHEDULER_PROMPT_PATH`, `SCHEDULER_MEMORY_FILE`) are derived
|
||||
from the roster and config — not from user input. No sanitization issue here, but
|
||||
ensure `torch-config.json` is not writable by untrusted parties.
|
||||
- The handoff command is executed via `bash -lc` with a login shell. The command
|
||||
string comes from `torch-config.json`. Do not allow untrusted config sources.
|
||||
- No cryptographic or signing logic in this file.
|
||||
|
||||
## Performance & concurrency
|
||||
|
||||
- The loop is single-threaded and sequential; no parallel lock attempts.
|
||||
- Each iteration makes several network calls (lock:check, lock:lock, lock:complete)
|
||||
that may take seconds each. Total wall-clock time per cycle depends heavily on
|
||||
relay latency and handoff command duration.
|
||||
- `getLatestFile()` reads all log filenames and file contents synchronously in
|
||||
memory — acceptable for typical log counts (< 1000 files) but worth monitoring
|
||||
if logs accumulate without rotation.
|
||||
|
||||
## Related files and call graph
|
||||
|
||||
| File | Relationship |
|
||||
|------|-------------|
|
||||
| `scripts/agent/scheduler-utils.mjs` | Utility functions (runCommand, readJson, parsers, etc.) |
|
||||
| `scripts/agent/scheduler-lock.mjs` | Lock acquisition, error classification, preflight |
|
||||
| `scripts/agent/verify-run-artifacts.mjs` | Run artifact validation (called as subprocess) |
|
||||
| `scripts/agent/scheduler-utils.mjs:buildRecentlyRunExclusionSet` | Time-window guard |
|
||||
| `src/utils.mjs:detectPlatform` | Auto-detects agent platform |
|
||||
| `src/prompts/roster.json` | Agent roster (read at startup) |
|
||||
| `src/prompts/scheduler-flow.md` | Authoritative numbered steps (implemented by this script) |
|
||||
| `torch-config.json` | Handoff command, memory policy, lock policy |
|
||||
| `task-logs/<cadence>/` | Task logs written and read here |
|
||||
| `task-logs/<cadence>/.scheduler-run-state.json` | Deferral state (day-scoped) |
|
||||
| `memory-updates/` | Memory update files (written path injected as env var) |
|
||||
|
||||
## Why it works this way
|
||||
|
||||
- **Infinite loop with restart on race loss**: Distributed lock systems inherently
|
||||
have races; the loop lets the scheduler immediately try the next available agent
|
||||
without requiring the caller to re-invoke the script.
|
||||
- **Day-scoped run-state**: Deferral tracking needs to persist across multiple rapid
|
||||
scheduler invocations on the same day (e.g., cron every 5 minutes) but should
|
||||
reset daily so a failed relay doesn't block the next day's run.
|
||||
- **lock:complete gated behind all validation**: Ensures a completion event on the
|
||||
relay (which prevents future agents from re-running the same slot) is only
|
||||
published after the run is genuinely finished and verified. This prevents
|
||||
"green-but-worthless" completions.
|
||||
- **Subprocess model for handoff**: Agents run in their own process with
|
||||
environment variables injected by the scheduler. This isolates agent crashes
|
||||
and allows the scheduler to check exit codes cleanly.
|
||||
|
||||
## When to change
|
||||
|
||||
- **New validation gate**: Add a step between artifact check and lock:complete.
|
||||
Maintain the gate ordering: memory evidence → artifact check → validation → lock:complete.
|
||||
- **New failure category**: Add to the `FAILURE_CATEGORY` constant and document
|
||||
in `src/prompts/scheduler-flow.md`.
|
||||
- **New config option**: Add parsing in `getSchedulerConfig()` with an env var
|
||||
override following the existing pattern.
|
||||
- **Log format change**: Update `writeLog()` — also update `verify-run-artifacts.mjs`
|
||||
and `scheduler-utils.mjs:parseFrontmatterAgent` to match.
|
||||
|
||||
## Tests that validate behavior
|
||||
|
||||
- `test/run-scheduler-cycle-memory-policy.test.mjs` — memory policy enforcement
|
||||
- `test/scheduler-time-window.test.mjs` — time-window guard behavior
|
||||
- `test/scheduler-preflight-lock.e2e.test.mjs` — preflight + lock flow
|
||||
|
||||
```bash
|
||||
# Run all scheduler tests:
|
||||
node --test test/run-scheduler-cycle-memory-policy.test.mjs test/scheduler-time-window.test.mjs
|
||||
# Or full suite (may be slow in sandbox):
|
||||
npm test
|
||||
```
|
||||
@@ -1,50 +0,0 @@
|
||||
# `src/lock-ops.mjs` Overview
|
||||
|
||||
This module implements the core logic for distributed locking using Nostr relays. It handles querying for existing locks, publishing new lock events, and managing relay health and fallbacks.
|
||||
|
||||
## High-Level Summary
|
||||
The `lock-ops` module provides the mechanism for agents to coordinate and avoid race conditions. It uses ephemeral Nostr events (Kind `30078` parameterized replaceable events, though currently using `app_data`) to represent locks. It includes robust retry logic, relay health scoring, and fallback mechanisms to ensure reliability even when some relays are slow or offline.
|
||||
|
||||
## Public Surface
|
||||
|
||||
### `queryLocks(relays, cadence, dateStr, namespace, deps)`
|
||||
Queries the specified relays for active locks matching the cadence and date.
|
||||
- **relays**: Array of relay URLs.
|
||||
- **cadence**: 'daily' or 'weekly'.
|
||||
- **dateStr**: Date string (YYYY-MM-DD).
|
||||
- **namespace**: Application namespace (e.g., 'torch').
|
||||
- **Returns**: Array of active lock objects.
|
||||
|
||||
### `publishLock(relays, event, deps)`
|
||||
Publishes a lock event to the specified relays, ensuring a quorum is met.
|
||||
- **relays**: Array of relay URLs.
|
||||
- **event**: The Nostr event object to publish.
|
||||
- **Returns**: The published event if successful, throws error otherwise.
|
||||
|
||||
### `parseLockEvent(event)`
|
||||
Parses a raw Nostr event into a structured lock object.
|
||||
- **event**: Raw Nostr event.
|
||||
- **Returns**: Structured lock object with `agent`, `cadence`, `expiresAt`, etc.
|
||||
|
||||
## Key Flows
|
||||
|
||||
1. **Lock Acquisition**:
|
||||
- `queryLocks` is called to check for existing locks.
|
||||
- If no valid lock exists, a new lock event is created (external to this module).
|
||||
- `publishLock` is called to broadcast the event.
|
||||
- Race check (reading back) confirms ownership (handled by caller, `src/lib.mjs`).
|
||||
|
||||
2. **Relay Health & Fallback**:
|
||||
- Relays are scored based on latency and success rate.
|
||||
- Failed attempts penalize the relay score.
|
||||
- If primary relays fail, fallback relays are used.
|
||||
|
||||
## Invariants
|
||||
- Locks must have an expiration time.
|
||||
- A lock is only valid if it is the latest event for the `d` tag.
|
||||
- Publishing requires a minimum number of successful relays (quorum).
|
||||
|
||||
## When to Change
|
||||
- If the relay selection or scoring algorithm needs tuning.
|
||||
- If the underlying Nostr event structure changes.
|
||||
- If new telemetry or logging requirements arise.
|
||||
@@ -1,95 +0,0 @@
|
||||
# TORCH Style Guide
|
||||
|
||||
This document outlines the design system, color palette, and typography used in the TORCH project (`dashboard/` and `landing/`).
|
||||
|
||||
## Color Palette
|
||||
|
||||
The project uses a dark theme with high-contrast text and vibrant accents.
|
||||
|
||||
### Core Colors
|
||||
|
||||
| Name | CSS Variable | Hex | Usage |
|
||||
|:---------------|:-------------------|:----------|:-------------------------------------------|
|
||||
| **Surface** | `--surface` | `#0b1020` | Main background color. |
|
||||
| **Surface Alt**| `--surface-alt` | `#151b2f` | Card backgrounds, headers, secondary areas.|
|
||||
| **Text** | `--text` | `#dbe4ff` | Primary body text (slightly blue-tinted). |
|
||||
| **Text Strong**| `--text-strong` | `#ffffff` | Headings, emphasized text. |
|
||||
| **Muted** | `--muted` | `#9aa6c6` | Secondary text, metadata, descriptions. |
|
||||
| **Border** | `--border` | `#2a3456` | Borders for cards, inputs, dividers. |
|
||||
|
||||
### Brand & Functional Colors
|
||||
|
||||
| Name | CSS Variable | Hex | Usage |
|
||||
|:---------------|:-------------------|:----------|:-------------------------------------------|
|
||||
| **Accent** | `--accent` | `#4f8cff` | Primary buttons, active states, links. |
|
||||
| **Accent Strong**| `--accent-strong`| `#2f6be5` | Hover states for accent elements. |
|
||||
| **Info** | `--info` | `#46b7ff` | Informational messages, icons. |
|
||||
| **Info Strong**| `--info-strong` | `#8ad4ff` | Stronger info emphasis. |
|
||||
| **Success** | `--success` | `#35c47c` | Success messages, "Active" statuses. |
|
||||
| **Warning** | `--warning` | `#f5b84b` | Warning alerts. |
|
||||
| **Danger** | `--danger` | `#ff6b6b` | Error messages, destructive actions. |
|
||||
|
||||
### Auxiliary Colors
|
||||
|
||||
| Name | CSS Variable | Hex |
|
||||
|:---------------|:-------------------|:----------|
|
||||
| **Purple** | `--purple` | `#a855f7` |
|
||||
| **Pink** | `--pink` | `#ec4899` |
|
||||
| **Teal** | `--teal` | `#2dd4bf` |
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Families
|
||||
|
||||
* **Primary**: `Inter`, `system-ui`, `-apple-system`, `Segoe UI`, `Roboto`, `sans-serif`
|
||||
* **Monospace**: `ui-monospace`, `SFMono-Regular`, `Menlo`, `Monaco`, `Consolas`, `monospace`
|
||||
|
||||
### Hierarchy
|
||||
|
||||
* **Headings**: Bold (`700`), `Text Strong`.
|
||||
* **Body**: Regular (`400`), `Text`.
|
||||
* **Small/Meta**: Small size, `Muted`.
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
|
||||
* **Primary CTA**:
|
||||
* Background: `var(--accent)`
|
||||
* Text: `white`
|
||||
* Border Radius: `9999px` (Pill shape)
|
||||
* Hover: `var(--accent-strong)`, slight transform up.
|
||||
* **Secondary CTA**:
|
||||
* Background: Transparent or low opacity white.
|
||||
* Border: `var(--border)`
|
||||
* Text: `var(--text)`
|
||||
* Hover: Lighter background, `Text Strong`.
|
||||
|
||||
### Cards
|
||||
|
||||
* Background: `var(--surface-alt)`
|
||||
* Border: `1px solid var(--border)`
|
||||
* Radius: `12px` or `1rem`
|
||||
* Hover: Border color changes to `var(--accent)`, slight lift (`transform: translateY(-5px)`).
|
||||
|
||||
### Status Badges
|
||||
|
||||
* Small, pill-shaped (`border-radius: 99px`).
|
||||
* Uppercase, bold, small font size.
|
||||
* Active: Background `var(--accent)`, Text `var(--surface)`.
|
||||
* Disabled/Inactive: Background `rgba(255,255,255,0.1)`, Text `var(--muted)`.
|
||||
|
||||
## Layout
|
||||
|
||||
* **Container**: Max-width `1200px` centered.
|
||||
* **Grid**: Responsive grids using `grid-template-columns: repeat(auto-fit, minmax(..., 1fr))`.
|
||||
* **Spacing**: Uses `rem` units (e.g., `1rem`, `2rem`, `4rem`).
|
||||
|
||||
## Accessibility Guidelines
|
||||
|
||||
* **Contrast**: Ensure text passes WCAG AA standards against the dark background.
|
||||
* **Interactive Elements**:
|
||||
* Must have `role="button"` or be `<button>`/`<a>`.
|
||||
* Must be keyboard accessible (`tabindex="0"`).
|
||||
* Must have visible focus states (`focus:ring`).
|
||||
* **Images**: All images must have `alt` text. Decorative images should use `aria-hidden="true"`.
|
||||
Reference in new issue
Block a user