Skip to content

F007: Record Decision Endpoint

FieldValue
Feature IDF007
StatusImplemented
PriorityP1
Depends OnF001 (Server Infrastructure)
BlocksNone
Decisionfddb416c

Summary

Add cstp.recordDecision JSON-RPC method to allow remote agents to create and store decisions via the CSTP API, with automatic indexing to ChromaDB for semantic search.

Goals

  1. Create decisions via API (no CLI required)
  2. Auto-generate decision ID and timestamps
  3. Auto-index to ChromaDB for immediate searchability
  4. Support full decision schema (reasons, K-lines, pre-decision protocol)
  5. Return decision ID and file path

Non-Goals

  • Decision updates (future F008)
  • Decision deletion (future F009)
  • Batch recording (future optimization)

API Specification

Method

cstp.recordDecision

Request

json
{
  "jsonrpc": "2.0",
  "method": "cstp.recordDecision",
  "params": {
    "decision": "Use PostgreSQL for agent memory storage",
    "confidence": 0.85,
    "category": "architecture",
    "stakes": "high",
    "context": "Choosing database for long-term agent memory",
    "reasons": [
      {"type": "analysis", "text": "ACID compliance needed for consistency", "strength": 0.9},
      {"type": "pattern", "text": "Similar to prior successful database choice", "strength": 0.7}
    ],
    "kpiIndicators": ["latency", "consistency"],
    "mentalState": "deliberate",
    "reviewIn": "30d",
    "tags": ["database", "infrastructure"],
    "preDecision": {
      "queryRun": true,
      "similarFound": 2,
      "guardrailsChecked": true,
      "guardrailsPassed": true
    }
  },
  "id": 1
}

Parameters

FieldTypeRequiredDescription
decisionstringThe decision statement
confidencefloatConfidence level (0.0-1.0)
categorystringCategory: architecture, process, integration, tooling, security
stakesstringStakes level: low, medium, high, critical (default: medium)
contextstringBackground/situation being decided
reasonsarrayArray of reason objects
reasons[].typestringReason type: authority, analogy, analysis, pattern, intuition
reasons[].textstringReason explanation
reasons[].strengthfloatReason strength (0.0-1.0, default: 0.8)
kpiIndicatorsarrayKPIs affected by this decision
mentalStatestringState: deliberate, reactive, exploratory, habitual, pressured
reviewInstringReview reminder: 7d, 2w, 1m, etc.
tagsarrayAdditional tags for categorization
preDecisionobjectPre-decision protocol tracking
preDecision.queryRunboolWhether similar decisions were queried
preDecision.similarFoundintNumber of similar decisions found
preDecision.guardrailsCheckedboolWhether guardrails were checked
preDecision.guardrailsPassedboolWhether guardrails passed

Response (Success)

json
{
  "jsonrpc": "2.0",
  "result": {
    "success": true,
    "id": "fddb416c",
    "path": "decisions/2026/02/2026-02-05-decision-fddb416c.yaml",
    "indexed": true,
    "timestamp": "2026-02-05T00:45:00Z"
  },
  "id": 1
}

Response (Error)

json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "field": "confidence",
      "error": "Must be between 0.0 and 1.0"
    }
  },
  "id": 1
}

Implementation Plan

Phase 1: Core Service (~2h)

1.1 Create decision_service.py

a2a/cstp/decision_service.py

Functions:

  • generate_decision_id() — UUID-based short ID
  • create_decision_yaml(params) — Build YAML content
  • write_decision_file(id, yaml) — Write to decisions/YYYY/MM/
  • index_decision(path) — Add to ChromaDB
  • record_decision(params) — Main orchestrator

1.2 Add models

python
# a2a/cstp/models.py

@dataclass
class Reason:
    type: str  # authority, analogy, analysis, pattern, intuition
    text: str
    strength: float = 0.8

@dataclass
class PreDecisionProtocol:
    query_run: bool = False
    similar_found: int = 0
    guardrails_checked: bool = False
    guardrails_passed: bool = False

@dataclass
class RecordDecisionRequest:
    decision: str
    confidence: float
    category: str
    stakes: str = "medium"
    context: str | None = None
    reasons: list[Reason] = field(default_factory=list)
    kpi_indicators: list[str] = field(default_factory=list)
    mental_state: str | None = None
    review_in: str | None = None
    tags: list[str] = field(default_factory=list)
    pre_decision: PreDecisionProtocol | None = None

@dataclass
class RecordDecisionResponse:
    success: bool
    id: str
    path: str
    indexed: bool
    timestamp: str

Phase 2: Dispatcher Integration (~1h)

2.1 Update dispatcher.py

python
async def _handle_record_decision(self, params: dict) -> dict:
    request = RecordDecisionRequest.from_dict(params)
    result = await record_decision(request)
    return result.to_dict()

2.2 Register method

python
self._methods["cstp.recordDecision"] = self._handle_record_decision

Phase 3: Configuration (~30m)

3.1 Environment variables

bash
# Path where decisions are stored
DECISIONS_PATH=/app/decisions

# ChromaDB collection for indexing
CHROMA_COLLECTION=decisions_gemini

3.2 Update config.py

python
@dataclass
class DecisionsConfig:
    path: Path = Path("decisions")
    chroma_collection: str = "decisions_gemini"

Phase 4: Tests (~1h)

4.1 Unit tests

tests/test_decision_service.py
tests/test_f007_record_decision.py

Test cases:

  • Valid decision creation
  • Missing required fields
  • Invalid confidence range
  • Invalid category
  • Reason validation
  • File path generation
  • YAML format verification

4.2 Integration tests

  • End-to-end API call
  • ChromaDB indexing verification
  • Concurrent recording

Phase 5: Documentation (~30m)

  • Update README with new endpoint
  • Update docs/CSTP-v0.7.0-DESIGN.md
  • Add examples to docs/DOCKER.md

Files to Create/Modify

FileActionDescription
a2a/cstp/decision_service.pyCreateCore recording logic
a2a/cstp/models.pyModifyAdd request/response models
a2a/cstp/dispatcher.pyModifyRegister new method
a2a/config.pyModifyAdd decisions config
tests/test_decision_service.pyCreateUnit tests
tests/test_f007_record_decision.pyCreateIntegration tests
README.mdModifyDocument new endpoint

Security Considerations

  1. Input validation — Sanitize all string inputs
  2. Path traversal — Ensure decision paths stay within decisions/
  3. Rate limiting — Consider adding rate limits (future)
  4. Agent attribution — Record which agent created the decision

Error Codes

CodeMessageCause
-32602Invalid paramsMissing/invalid field
-32603Internal errorFile write or indexing failed
-32001Storage errorCannot write to decisions path
-32002Indexing errorChromaDB indexing failed

Acceptance Criteria

  1. cstp.recordDecision creates valid YAML file
  2. ✅ Decision immediately searchable via cstp.queryDecisions
  3. ✅ All required fields validated
  4. ✅ Optional fields handled correctly
  5. ✅ Error responses include helpful details
  6. ✅ Agent ID from auth recorded in decision
  7. ✅ File path follows YYYY/MM/ structure

Estimated Effort

PhaseTime
Core Service2h
Dispatcher Integration1h
Configuration30m
Tests1h
Documentation30m
Total~5h

Future Enhancements

  • F008: updateDecision — Add outcome, review decision
  • F009: deleteDecision — Remove decision (with audit)
  • F010: listDecisions — Paginated listing with filters
  • Batch recording for bulk imports
  • Webhooks for decision events

Released under the Apache 2.0 License.