Skip to content

Self-Improvement System

VITA is a self-improving personal health operating system. This document describes the proposal development, scoring, and agent-assisted refinement workflow that enables continuous system enhancement.


Overview

The Self-Improvement System generates, evaluates, and refines feature proposals for VITA. It operates through:

  1. /improve β€” manual, on-demand: list, view, develop N new proposals via parallel agents, and re-score
  2. /sleep β€” the autonomous nightly Stepwise flow that reflects on the day's data, revises and reranks proposals, conditionally generates new ones, and autonomously builds one small improvement
  3. Web kanban board for visual proposal management
  4. Conversational agents that auto-spawn to respond to user comments on a proposal

The key innovation is the agent-per-comment model: when a user adds a comment to any proposal via the web UI, a background Claude agent spawns to read the proposal, address the feedback, and reply. This creates a conversational refinement loop without requiring manual /improve runs.


Architecture

                                   +-----------------------+
                                   |     Web Kanban UI     |
                                   |  /proposals route     |
                                   +-----------+-----------+
                                               |
              +-------------+      +-----------v-----------+
              |   /improve  |      |    FastAPI Router     |
              |   /sleep    +----->+  proposals.py         |
              | CLI commands|      +-----------+-----------+
              +-------------+                  |
                                   +-----------v-----------+
                                   |      Services         |
                                   |  - ProposalsService   |
                                   |  - ProposalStateService
                                   |  - ClaudeRunnerService|
                                   +-----------+-----------+
                                               |
                    +-------------+------------+-----------+-------------+
                    |             |                        |             |
             +------v------+ +----v----+           +-------v------+ +----v----+
             | proposal-   | |proposals|           |proposal-     | |Claude   |
             | state.json  | |/*.md    |           |scores.json   | |CLI      |
             +-------------+ +---------+           +--------------+ +---------+

Key Components

Component Location Purpose
ProposalsService api/src/services/proposals.py Parse markdown proposals, extract summaries
ProposalStateService api/src/services/proposal_state.py Kanban lanes, comments, agent lifecycle
ClaudeRunnerService api/src/services/claude_runner.py Spawn/manage Claude CLI sessions
ImproveCycleService api/src/services/improve_cycle.py Batch revision via API (legacy)
Proposals Router api/src/routers/proposals.py REST endpoints + agent orchestration

The Comment-Triggered Agent Model

This is the core interaction pattern for proposal refinement:

User adds comment          API receives POST            Agent spawns
in Web UI                  /{filename}/comments         in background
     |                            |                          |
     v                            v                          v
+----------+              +---------------+           +------------+
| "Add     |   HTTP POST  | 1. Validate   |  asyncio  | Claude CLI |
| Telegram |------------->| 2. Store      |---------->| with prompt|
| support" |              | 3. Cancel old |           | and tools  |
+----------+              | 4. Start new  |           +-----+------+
                          +---------------+                 |
                                                           v
                              Agent response         +------------+
                              via POST /comments     | 1. Read    |
                              with author=system     |    proposal|
                                    ^                | 2. Search  |
                                    |                |    codebase|
                                    +----------------| 3. Edit    |
                                                     | 4. Reply   |
                                                     +------------+

Agent Lifecycle States

                start_agent()
                     |
                     v
+----------+    +---------+    update_agent_session()    +----------+
| inactive |    | pending |--------------------------->  | running  |
+----------+    +---------+                              +----+-----+
     ^                                                        |
     |                                                        |
     +------------------cancel_agent()------------------------+
     |                                                        |
     +---------------clear_agent_if_session()-----------------+
                           (guards against stale callbacks)

The session-guarded cleanup is critical: when a user posts a new comment while an agent is running, the old agent is killed and a new one spawns. The clear_agent_if_session(session_id) method prevents race conditions where a stale agent's completion callback might clear the new agent's state.

Agent Prompt Template

When an agent spawns, it receives this prompt (simplified):

# Proposal Agent - Run {run_id}

You are helping refine a VITA improvement proposal.

## Proposal
File: plans/proposals/{filename}

## Recent Comments
[last 10 comments with timestamps and authors]

## Instructions
1. Read the proposal first
2. Understand the context
3. Address the latest comment:
   - If question -> answer it
   - If feedback -> incorporate into plan
   - If investigation request -> search codebase
4. You can search with Glob/Grep/Read, edit the proposal, or close it
5. MUST add a response comment when done

Trigger Points

Command Mode Description
/improve Interactive List proposals, choose to view or develop new ones
/improve view View Open proposal-rankings.html in browser
/improve develop N Develop Spawn N sub-agents to generate unique proposals
/improve score Score Re-score all proposals, regenerate rankings
/sleep Autonomous Nightly Stepwise flow: gather β†’ reflect β†’ revise β†’ conditionally generate β†’ build (see /sleep section)

When to Use Each Mode

Mode Frequency Trigger
Web comments Real-time Any time you have feedback on a specific proposal
/improve develop N On demand When seeking new ideas
/improve score After manual edits Re-evaluate changed proposals
/sleep Weekly After significant new data accumulation

Kanban Board

Lanes

Lane Meaning Visual Style
triage New proposals awaiting review Gray/slate
ready Approved, ready to implement Green/emerald
needs_work Requires changes before approval Amber/orange
maybe_later Deprioritized for now Blue/sky
done Completed or abandoned Purple/violet

Proposal Cards Display

Cards show: - Title (links to full detail view) - Priority badge (P0-P4 color-coded) - Effort badge (hours, day, days, week, weeks) - Score (weighted rubric score if calculated) - Domain/type tags - Abstract (first ~500 chars of Problem Statement) - Comment thread with user/system distinction - Agent status indicator (spinner when processing)

Drag-and-Drop

Cards can be dragged between lanes. Position within lane is preserved. State updates are atomic (temp file + rename pattern).


REST API

Base path: /api/v1/proposals

Core Endpoints

Method Endpoint Description
GET / List all proposals (optional ?status= filter)
GET /{filename} Get single proposal with full markdown content
GET /state Get kanban board state (lanes, comments, processing)
PUT /move Move proposal to target lane at position
PUT /reorder Reorder proposals within a lane
POST /create Create stub proposal from one-line idea
DELETE /{filename} Delete proposal (file + state)

Agent/Comment Endpoints

Method Endpoint Description
GET /{filename}/comments Get comments for proposal
POST /{filename}/comments Add comment (triggers agent if author=user)
POST /{filename}/close Move to done lane (for agent use)
GET /processing-status Get agent status (active, proposal, session_id)
POST /improve Start scoring cycle via Claude CLI

Example: Add Comment and Trigger Agent

The API listens on port 33800 (./run api). Port 33801 is the Vite web UI, 33802 is the public read-only API β€” they are not the proposals API.

# Add a user comment (triggers agent automatically)
curl -X POST http://localhost:33800/api/v1/proposals/2026-01-01-token-dashboard.md/comments \
  -H "Content-Type: application/json" \
  -d '{"text": "Can you add a weekly view?", "author": "user"}'

# Response: updated KanbanState with new comment
# Agent spawns in background, will add "system" comment when done

# Check if agent is still running
curl http://localhost:33800/api/v1/proposals/processing-status
# {"active": true, "started_at": "...", "proposal": "2026-01-01-token-dashboard.md", "session_id": "abc123"}

Data Structures

Proposal State (proposal-state.json)

{
  "schema_version": 1,
  "updated_at": "2026-01-04T20:18:16.512744",
  "lanes": {
    "triage": ["2026-01-02-layered-data-architecture.md", ...],
    "ready": ["2026-01-01-token-usage-dashboard.md"],
    "needs_work": [...],
    "maybe_later": [],
    "done": ["vdb-web-ui.md"]
  },
  "comments": {
    "2026-01-01-proactive-gap-filler.md": [
      {
        "id": "e52ac5dc",
        "ts": "2026-01-02T04:10:30.347554",
        "author": "user",
        "text": "hit me up on telegram with these questions",
        "processed": false
      },
      {
        "id": "59331726",
        "ts": "2026-01-04T20:18:07.555911",
        "author": "system",
        "text": "Acknowledged! The proposal agent is working...",
        "processed": true
      }
    ]
  },
  "processing": {
    "active": false,
    "started_at": null,
    "proposal": null,
    "session_id": null
  }
}

Processing State Fields

Field Type Purpose
active bool Agent is currently running
started_at ISO-8601 When agent started (for staleness detection)
proposal string Filename being processed
session_id string Claude session ID for kill/guard operations

The session_id starts as "pending" when the agent is spawning, then updates to the real session ID once the Claude process starts. This prevents race conditions in the completion callback.

Comment Fields

Field Type Purpose
id string 8-char hex for reference
ts ISO-8601 Creation timestamp
author "user" or "system" Who wrote it
text string Comment content
processed bool Has this been addressed by an agent?

Scoring

Evaluation Rubric

Proposals are scored using an 8-criterion weighted rubric:

Criterion Weight Description
user_impact 0.20 How significantly improves health outcomes?
vita_alignment 0.15 Aligns with proactive, self-improving, data-centric philosophy?
data_leverage 0.15 Uses existing Garmin/check-in/identity data?
feasibility 0.15 Realistic implementation?
novelty 0.10 Unique compared to other proposals?
immediate_relevance 0.10 Relevant to current goals?
specification_quality 0.10 Well-specified with clear UX?
risk_awareness 0.05 Acknowledges and mitigates risks?

Score Calculation

# Base score (0-10 range, weighted average)
base_score = sum(score[criterion] * weight[criterion] for each criterion)

# Bonuses
+1 if enhances other proposals (synergy)
+1 if Phase 1 delivers in <1 day (quick_win)
+1 if uses identity/observation data (identity_integration)

# Penalties
-2 if duplicates another proposal
-1 if tries to do too much (scope_creep)

total_score = base_score + bonuses - penalties

Error Handling

Stale Agent Cleanup

Agents are auto-cleared after 5 minutes of being "active":

if processing.get("active") and processing.get("started_at"):
    started = datetime.fromisoformat(processing["started_at"])
    elapsed_minutes = (datetime.now() - started).total_seconds() / 60
    if elapsed_minutes > 5:
        self.set_processing_state(active=False)
        return default_processing

Session-Guarded Cleanup

The clear_agent_if_session(session_id) method prevents stale callbacks:

def clear_agent_if_session(self, session_id: str) -> bool:
    """Clear agent state only if session_id matches current.
    Prevents stale callbacks from clearing newer agent state.
    """
    state = self._read_state()
    current_session = state.get("processing", {}).get("session_id")
    if current_session != session_id:
        return False  # Stale callback, ignore
    # Session matches, safe to clear
    state["processing"] = {...cleared...}
    self._write_state(state)
    return True

Orphaned Proposal Sync

On every get_kanban_state() call: 1. New .md files in plans/proposals/ are added to top of triage 2. Deleted files are removed from all lanes 3. Comments for deleted files are cleaned up

Atomic Writes

All state file writes use temp file + rename:

tmp_path = self.state_file.with_suffix(".tmp")
with open(tmp_path, "w") as f:
    json.dump(state, f, indent=2)
tmp_path.rename(self.state_file)  # Atomic on POSIX

File Locations

File Purpose
plans/proposals/*.md Individual proposal markdown files
plans/proposals/*.html Rendered HTML versions (generated)
plans/proposals/archived/ Archived (auto- or manually-retired) proposals
plans/proposal-rankings.html Master rankings dashboard
plans/proposal-state.json Kanban state (lanes, comments, processing)
plans/proposal-scores.json Cached scores (mtime-based invalidation)
plans/PROPOSAL_SCHEMA.md Template for new proposals
plans/EVALUATION_RUBRIC.json Scoring criteria and weights
scripts/render_proposals.py Render proposal markdown to styled HTML
meta/FEATURE_INTENT.md Aspirational feature goals (referenced/audited by /sleep)
api/src/routers/proposals.py REST API endpoints
api/src/services/proposals.py Proposal parsing service
api/src/services/proposal_state.py Kanban state + agent lifecycle
api/src/services/claude_runner.py Claude CLI session management
api/src/services/improve_cycle.py Batch revision via API (ImproveCycleService)
web/src/components/kanban/ React kanban board components
flows/sleep/FLOW.yaml /sleep flow orchestration (Stepwise)
flows/sleep/prompts/*.md Agent step prompts (reflect, prompt_review, revise_proposals, build, web_discover)
scripts/sleep/preprocess.py Builds the data/sleep/staging/* files
scripts/sleep/repair_corrupted_revisions.py Decodes JSON-escaped revision blocks back to markdown
scripts/executive/detect_stale_systemic_issues.py Identity-state staleness antibody
scripts/sleep/feature_intent_gaps.py Feature-intent gap-watch antibody
data/sleep/staging/ Inter-step staging files (proposal index, antibody reports, …)
.claude/skills/improve/ /improve skill (subcommands + develop workflow)

Known Limitations

Current Gaps

  1. No score persistence after scoring - Scores are recalculated each /improve score run rather than cached per-proposal with change detection

  2. No duplicate detection - Sub-agents are told not to duplicate but there's no automated similarity check before creation

  3. Single agent per proposal - Only one agent can process a proposal at a time; rapid comments queue behind each other

  4. Comments not marked processed - The agent model doesn't currently mark user comments as "processed" after responding

  5. No agent streaming to UI - Agent output goes to server logs, not streamed to the web UI (planned)

  6. No proposal dependencies - No way to mark "build X before Y" relationships

  7. Limited auto-archival - Partly addressed: plans/proposals/archived/ exists and preprocess.py auto-archives proposals with 5+ consecutive archive recommendations. Proposals not hitting that threshold still accumulate in plans/proposals/.

Improvement Opportunities

  1. Stream agent output to UI - Use SSE endpoint (/stream?session_id=...) to show agent thinking in real-time

  2. Embeddings-based duplicate detection - Compare new proposal embeddings against existing before writing

  3. Incremental scoring - Only re-score proposals modified since last scoring run

  4. Proposal dependencies graph - Allow explicit "depends on" relationships and visualize in rankings

  5. Comment threading - Allow replies to comments, not just flat list

  6. Version history - Track proposal revisions with diffs

  7. Agent memory - Let agents remember context from previous runs on the same proposal

  8. Multi-agent coordination - Allow parallel agents on different proposals with file locking


/sleep: the Nightly Reflection Flow

/sleep is the autonomous half of the self-improvement system. It is not a single batch script β€” it is a multi-step Stepwise flow defined at flows/sleep/FLOW.yaml, run nightly (~00:30 PT). It superseded the executive scheduler's old _run_sleep orchestration. The flow entrypoint is the YAML; the agent steps are driven by prompt files in flows/sleep/prompts/*.md.

A reader reimplementing this should treat flows/sleep/FLOW.yaml as the orchestration contract and the prompt files as the agent instructions.

Flow Steps

Steps are a mix of deterministic Python scripts (run:) and agent steps (executor: agent, with a prompt_file). Several script steps run in parallel after preprocess; the four agent steps share one Claude session (session: sleep) so context built in reflect carries forward.

Step Kind What it does
stepwise_snapshot script Snapshot stepwise.db for crash recovery (keeps last 10)
preprocess script Build the compact staging files under data/sleep/staging/ (proposal index, meetings, commitments, codebase graph, antibody reports)
commitment_drain_staging script Stage overdue/untriaged commitment-drain candidates (never mutates commitments.json)
heartbeat_archive_dead script Auto-dismiss heartbeat topics with β‰₯8 nudges and zero engagement
view_freshness_coverage script Audit memory/views/* against the liveness contract; write a staleness report
trigger_suppression_refresh script Auto-suppress proactive triggers with β‰₯10 sends and 0 responses
data_source_health script Aggregate source_state across all sync surfaces; surface auth/parse errors
web_discover agent Browse X/HN via Camofox for overnight discoveries (~15 min, cost-capped)
git_snapshot script Pre-sleep git snapshot for rollback safety
reflect agent Data gathering + pattern analysis + meetings + commitments + forge + sentinels (phases 2–3)
prompt_review agent Audit system-prompt health β€” voice drift, tool discipline, identity anchoring
engagement_aggregator script Aggregate per-voice composer engagement (last 7 days)
voice_drift_audit script Blind voice-classification audit (last 7 days)
revise_proposals agent Revise proposals, rerank, update work context, conditionally generate new (phases 4–6)
build agent Autonomously implement one small improvement (phase 7), only if threshold met
finalize script Git commit all changes + write the overnight summary JSON for the briefing

Note: The four agent steps (web_discover, reflect, prompt_review, revise_proposals, build) are where the LLM does work; the rest are deterministic. Frontier models in use are claude-opus-4-8 / claude-fable-5 β€” see Model Guidance for routing.

Staging Architecture (the data-passing contract)

/sleep steps don't pass data in memory β€” they communicate through files. This is the actual contract a reimplementer must honor:

File Written by Consumed by Contents
data/sleep/staging/proposal-index.json preprocess.py revise_proposals Per-proposal metadata + skip_revision flags, skip_revision_count, auto_archive_triggered
data/sleep/staging/meeting-summaries.json preprocess.py reflect Pre-extracted meeting commitments/outcomes
data/sleep/staging/commitment-triage.json preprocess.py reflect Pre-computed overdue/stale triage
data/sleep/staging/view-freshness-{date}.json view_freshness_coverage.py reflect Per-view staleness report
data/sleep/staging/identity-state-staleness.json preprocess.py (antibody) reflect Staleness audit of identity-state.md (see below)
data/sleep/staging/feature-intent-gaps.json preprocess.py (antibody) reflect Feature commits not reflected in FEATURE_INTENT.md
data/sleep/staging/homeostasis.json preprocess.py reflect Manifesto equilibrium metrics (engagement, ack rates, prediction calibration)
.stepwise/sleep-reflection.json reflect revise_proposals, finalize Structured reflection summary (health, patterns, forge, board, FLL) with real values

The reflect agent's only durable output is .stepwise/sleep-reflection.json. The revise_proposals step reads it back as the night's analysis input.

Data Sources the reflect Step Reads

Per flows/sleep/prompts/reflect.md, the reflect agent loads:

Category Sources
Profile & identity profile/identity.json, profile/communication.json, data/identity/observations.jsonl (last 10–20)
Health data/garmin/ (last 7 days), plus a live sleep-risk assessment and an implicit-checkin generator
Goals goals/active/*.json + training-occurrence progress (via scripts.scheduling)
Session memory data/assistant/{capture.json,commitments.json,learnings.jsonl}
Stream captures data/stream-captures/* (next-session questions + today's captures)
Board action items via scripts.board.action_items.scope_for_sleep
Working notes data/scratch/{daily,weekly}.md
Memory memory/views/ listing + staleness
FLL the Feedback Learning Loop via scripts.proactive.fll_learning (engagement trends, sleep summary)
Calendar data/calendar/events.json (last 7 / next 3 days) for travel cross-reference

Note: reflect is told not to read plans/proposals/*.md directly β€” the revise_proposals step handles proposals via the staging index. Older docs that listed data/checkins/ (30 days), data/briefs/*.json, meta/improvement-queue.json, and data/proactive/conversations.jsonl are stale; the current reflect step does not read those.

FLL vs VDB: "FLL" (Feedback Learning Loop, scripts.proactive.fll_learning) is the current name. Older docs call this "VDB feedback" β€” prefer FLL.

Proposal Revision Mechanics

revise_proposals reads proposal-index.json, then revises 5–10 proposals in place. Two behaviors matter for a reimplementer:

  • skip_revision: Proposals with a terminal status, or 5+ consecutive revision sections recommending archive, are flagged skip_revision: true by preprocess.py and skipped. The auto_archive_triggered list records proposals auto-archived at the 5-consecutive-archive-recommendation threshold.
  • Revision-append corruption guard: Each revision is appended as a ## Sleep Cycle Revision (YYYY-MM-DD) block. It must be raw markdown with real newlines β€” a json.dumps()-escaped one-liner is invisible to the preprocess.py parser (which requires a real newline after the date) and silently drops the revision from last_revision_date and the archive-recommendation count. If corrupted blocks appear, scripts/sleep/repair_corrupted_revisions.py decodes them back to markdown.

Significance Threshold (Conditional Generation)

In revise_proposals (Phase 6), new proposals are only generated if 2+ of these criteria are met:

  • New data source added since last /sleep
  • Goal completed or abandoned
  • 3+ related captures suggesting unmet need
  • 3+ learnings integrated into CLAUDE.md/skills
  • FLL feedback pattern with no matching proposal
  • Identity observation contradicting existing proposals
  • Commitment repeatedly failed (systemic issue)
  • Telegram pattern without a matching view
  • View with 0 access (consider replacement)
  • Board meeting feedback pattern
  • Objective chronically deferred (3+ times)

If the threshold isn't met, generation is skipped and the step reports "No significant new patterns detected."

Immune-System Antibodies

/sleep runs deterministic self-audits that exist because shipped mitigations have died silently in the past. They are report-only β€” they write staging JSON and never auto-edit the files they audit; acting on the findings is the reflect agent's job.

Antibody Script Output Detects
Identity-state staleness scripts/executive/detect_stale_systemic_issues.py data/sleep/staging/identity-state-staleness.json Systemic-issues rows contradicted by shipped proposals; dated section headers >14d old; dead file pointers in CLAUDE.md/ORIENTATION.md; ambient byte-budget bloat; and a semantic-claim grader that checks prose against its live primary source. A live-check exception now emits semantic_unverified; only the benign no-source-data case stays silent.
Feature-intent gap watch scripts/sleep/feature_intent_gaps.py data/sleep/staging/feature-intent-gaps.json Feature commits since the last FEATURE_INTENT.md update β€” any commit whose <area>: subject prefix is not a maintenance verb (fix/chore/docs/…) and that touches a real feature surface (scripts/, flows/, api/, web/, ui/, rails/, .claude/skills/)
Completion-claim audit scripts/audit/completion_claim_audit.py data/sleep/staging/completion-claim-audit.json Recent exec-loop β€œdone/verified/durable” claims without same-cycle primary-source backing. Hedge markers such as β€œbehaviorally unverified” or β€œout of scope” are annotated only as advisory context; they never remove a candidate or lower the unbacked count.

Both are invoked by scripts/sleep/preprocess.py. They can also be run standalone:

python scripts/executive/detect_stale_systemic_issues.py
python scripts/sleep/feature_intent_gaps.py

Example: Agent Conversation Flow

Here's a real example of the comment-triggered agent model in action:

User posts comment:

"hit me up on telegram with these questions"

Agent spawns, reads proposal, searches codebase for Telegram integration, then responds:

Acknowledged! I've updated the proposal to use Telegram as the primary outreach
channel. Key changes:
- Replaced in-app notifications with Telegram messages
- Added schedule for morning/evening prompts
- Integrated with existing proactive queue system

The proposal now aligns with VITA's existing Telegram infrastructure.

User follows up:

"what about rate limiting so it's not annoying?"

Previous agent is killed, new agent spawns, addresses the new feedback:

Good point! Added rate limiting section:
- Max 3 prompts per day
- Snooze option per prompt type
- Smart backoff if user ignores 2+ in a row
- User can set quiet hours in preferences

Also added these as success metrics to track.

This back-and-forth continues until the proposal is refined enough to move to "ready" lane.


Quality Checklist for Proposals

Good proposals:

  1. Solve a real problem - Not a solution looking for a problem
  2. Fit the VITA philosophy - Proactive, data-centric, self-improving
  3. Are scoped - Clear boundaries, not trying to do everything
  4. Have measurable outcomes - Can verify if it worked
  5. Consider alternatives - Shows you've thought through options
  6. Identify risks honestly - Not overly optimistic
  7. Are actionable - Could start implementing tomorrow

Where to go next

  • Executive Loop β€” the OODA loop that /sleep replaced the old _run_sleep orchestration from
  • Dev Tooling β€” Stepwise, the flow runner behind flows/sleep/FLOW.yaml
  • Homeostasis β€” the prediction/equilibrium metrics surfaced in data/sleep/staging/homeostasis.json
  • Model Guidance β€” frontier model roster and routing for the agent steps

meta/FEATURE_INTENT.md (in the repo) is the aspirational-direction document audited by the feature-intent gap watch.