Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/rune-mcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
// Spawn model: Claude Code launches one instance per session via stdio.
// Lifecycle: starting → waiting_for_vault → active ↔ dormant.
// Tools: 8 MCP tools (capture, recall, batch_capture, capture_history,
// delete_capture, vault_status, diagnostics, reload_pipelines).
//
// delete_capture, vault_status, diagnostics, reload_pipelines).
//
// Wiring: Deps holds a State manager + 3 services. Adapter clients (vault /
// envector / embedder) are populated on the services by the boot loop after
Expand Down
2 changes: 1 addition & 1 deletion internal/adapters/config/dormant.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func SaveToPath(cfg *Config, path string) error {
// - "not_configured" — config.json missing, fresh install
// - "vault_unconfigured" — config exists but Vault.Endpoint/Token empty
// - "user_deactivated" — already-dormant config picked up by boot
// (idempotent path, just refreshes timestamp)
// (idempotent path, just refreshes timestamp)
func MarkDormant(reason string) error {
cfg, err := Load()
if err != nil {
Expand Down
10 changes: 6 additions & 4 deletions internal/adapters/config/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// Python: agents/common/config.py (365 LoC) — Go reduced from 7 sections to 3.
//
// Dropped sections (per scope SOT — docs/v04/overview/architecture.md):
// envector / embedding / llm / scribe / retriever — moved to Vault bundle
// (memory only) or external embedder process.
//
// envector / embedding / llm / scribe / retriever — moved to Vault bundle
// (memory only) or external embedder process.
package config

import (
Expand Down Expand Up @@ -33,8 +34,9 @@ type VaultConfig struct {
}

// FilePerms — per rune-mcp.md §Config:
// ~/.rune/ 0700
// ~/.rune/config.json 0600
//
// ~/.rune/ 0700
// ~/.rune/config.json 0600
const (
DirPerm = 0700
FilePerm = 0600
Expand Down
4 changes: 2 additions & 2 deletions internal/adapters/embedder/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ type fakeRuned struct {
infoFn func(*runedv1.InfoRequest) (*runedv1.InfoResponse, error)
healthFn func(*runedv1.HealthRequest) (*runedv1.HealthResponse, error)

infoCalls int32 // atomic — Info should be invoked exactly once across the lifetime of an infoCache
embedCalls int32 // atomic — used by retry test to count attempts
infoCalls int32 // atomic — Info should be invoked exactly once across the lifetime of an infoCache
embedCalls int32 // atomic — used by retry test to count attempts
embedBatchCalls int32 // atomic — used by batch-split test
}

Expand Down
4 changes: 2 additions & 2 deletions internal/adapters/envector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type Client interface {
Insert(ctx context.Context, req InsertRequest) (*InsertResult, error)
Score(ctx context.Context, vec []float32) ([][]byte, error)
GetMetadata(ctx context.Context, refs []MetadataRef, fields []string) ([]MetadataEntry, error)
OpenIndex(ctx context.Context) error // opens (or creates) the server-side index
OpenIndex(ctx context.Context) error // opens (or creates) the server-side index
GetIndexList(ctx context.Context) ([]string, error) // used by diagnostics + warmup
Close() error
}
Expand All @@ -65,7 +65,7 @@ type ClientConfig struct {
}

type client struct {
sdk *envector.Client
sdk *envector.Client
keys *envector.Keys
idx *envector.Index
cfg ClientConfig
Expand Down
44 changes: 22 additions & 22 deletions internal/adapters/envector/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,45 @@ func TestMapSDKError_Nil(t *testing.T) {

func TestMapSDKError_SDKSentinels(t *testing.T) {
tests := []struct {
name string
err error
wantCode string
name string
err error
wantCode string
wantRetry bool
}{
{
name: "ErrKeysNotForEncrypt",
err: envector.ErrKeysNotForEncrypt,
wantCode: "DECRYPTOR_UNAVAILABLE",
name: "ErrKeysNotForEncrypt",
err: envector.ErrKeysNotForEncrypt,
wantCode: "DECRYPTOR_UNAVAILABLE",
wantRetry: false,
},
{
name: "ErrKeysNotForDecrypt",
err: envector.ErrKeysNotForDecrypt,
wantCode: "DECRYPTOR_UNAVAILABLE",
name: "ErrKeysNotForDecrypt",
err: envector.ErrKeysNotForDecrypt,
wantCode: "DECRYPTOR_UNAVAILABLE",
wantRetry: false,
},
{
name: "ErrKeysNotForRegister",
err: envector.ErrKeysNotForRegister,
wantCode: "KEY_NOT_FOR_REGISTER",
name: "ErrKeysNotForRegister",
err: envector.ErrKeysNotForRegister,
wantCode: "KEY_NOT_FOR_REGISTER",
wantRetry: false,
},
{
name: "ErrClientClosed",
err: envector.ErrClientClosed,
wantCode: "ENVECTOR_CONNECTION_LOST",
name: "ErrClientClosed",
err: envector.ErrClientClosed,
wantCode: "ENVECTOR_CONNECTION_LOST",
wantRetry: true,
},
{
name: "ErrKeysNotFound",
err: envector.ErrKeysNotFound,
wantCode: "ENVECTOR_KEYS_NOT_FOUND",
name: "ErrKeysNotFound",
err: envector.ErrKeysNotFound,
wantCode: "ENVECTOR_KEYS_NOT_FOUND",
wantRetry: false,
},
{
name: "ErrKeysRequired",
err: envector.ErrKeysRequired,
wantCode: "ENVECTOR_KEYS_REQUIRED",
name: "ErrKeysRequired",
err: envector.ErrKeysRequired,
wantCode: "ENVECTOR_KEYS_REQUIRED",
wantRetry: false,
},
}
Expand Down Expand Up @@ -142,7 +142,7 @@ func TestMapSDKError_GenericError(t *testing.T) {
}
}

//--- Error types ---//
// --- Error types ---//
func TestError_ErrorString(t *testing.T) {
tests := []struct {
name string
Expand Down
20 changes: 10 additions & 10 deletions internal/domain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import "errors"

// Code enum — 8 codes.
const (
CodeInternal = "INTERNAL_ERROR"
CodeVaultConnection = "VAULT_CONNECTION_ERROR"
CodeVaultDecryption = "VAULT_DECRYPTION_ERROR"
CodeEnvectorConnection = "ENVECTOR_CONNECTION_ERROR"
CodeEnvectorInsert = "ENVECTOR_INSERT_ERROR"
CodePipelineNotReady = "PIPELINE_NOT_READY"
CodeInvalidInput = "INVALID_INPUT"
CodeEmbedderUnreachable = "EMBEDDER_UNREACHABLE" // Go-specific (D30)
CodeEmptyEmbedText = "EMPTY_EMBED_TEXT" // D5 — dedicated code for missing embed text
CodeExtractionMissing = "EXTRACTION_MISSING" // D14 — agent must provide pre_extraction
CodeInternal = "INTERNAL_ERROR"
CodeVaultConnection = "VAULT_CONNECTION_ERROR"
CodeVaultDecryption = "VAULT_DECRYPTION_ERROR"
CodeEnvectorConnection = "ENVECTOR_CONNECTION_ERROR"
CodeEnvectorInsert = "ENVECTOR_INSERT_ERROR"
CodePipelineNotReady = "PIPELINE_NOT_READY"
CodeInvalidInput = "INVALID_INPUT"
CodeEmbedderUnreachable = "EMBEDDER_UNREACHABLE" // Go-specific (D30)
CodeEmptyEmbedText = "EMPTY_EMBED_TEXT" // D5 — dedicated code for missing embed text
CodeExtractionMissing = "EXTRACTION_MISSING" // D14 — agent must provide pre_extraction
)

// RuneError — MCP error response body (Python make_error equivalent).
Expand Down
1 change: 0 additions & 1 deletion internal/domain/extraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func (r *ExtractionResult) IsBundle() bool {
return r.GroupType == "bundle" && len(r.Phases) > 1
}


// ParseExtractionFromAgent builds Detection + ExtractionResult from the flat
// CaptureRequest.Extracted dict sent by the agent. Wire → internal conversion.
//
Expand Down
6 changes: 3 additions & 3 deletions internal/domain/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ type Payload struct {
// DecisionRecord — §3. Python: decision_record.py:L166-213.
// envector.Insert metadata의 decrypted payload.
type DecisionRecord struct {
SchemaVersion string `json:"schema_version"` // fixed "2.1"
ID string `json:"id"`
Type string `json:"type"` // fixed "decision_record"
SchemaVersion string `json:"schema_version"` // fixed "2.1"
ID string `json:"id"`
Type string `json:"type"` // fixed "decision_record"

Domain Domain `json:"domain"`
Sensitivity Sensitivity `json:"sensitivity"`
Expand Down
2 changes: 1 addition & 1 deletion internal/lifecycle/boot.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ const (
// - vault endpoint/token empty → terminal Dormant (await /rune:configure)
// - vault dial / GetAgentManifest → state=WaitingForVault, exp backoff retry
// - keymanager / embedder / envector init → exp backoff retry (might be
// transient — daemon down, etc.)
// transient — daemon down, etc.)
// - other config error (parse fail) → exp backoff retry (user might be editing)
// - ctx cancellation → return immediately
//
Expand Down
6 changes: 3 additions & 3 deletions internal/mcp/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// owns Deps injection + state-aware response shaping.
//
// Spec:
// docs/v04/spec/components/rune-mcp.md (MCP server 구현)
// docs/v04/spec/flows/{capture,recall,lifecycle}.md
//
// docs/v04/spec/components/rune-mcp.md (MCP server 구현)
// docs/v04/spec/flows/{capture,recall,lifecycle}.md
//
// SDK: github.com/modelcontextprotocol/go-sdk v1.5.0+ (D2). Stdio transport.
// Input schema is auto-inferred from the Go input struct (jsonschema tags
Expand Down Expand Up @@ -200,4 +201,3 @@ func isValidToolName(name string) bool {
}
return true
}

2 changes: 1 addition & 1 deletion internal/policy/record_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// MAX_INPUT_CHARS — Python L227. Truncate cleanText before extraction.
const MaxInputChars = 12_000

// QuotePatterns — 4 regex (Python L72-77): double "", single '', Japanese 「」,
// QuotePatterns — 4 regex (Python L72-77): double "", single ”, Japanese 「」,
// French «». Min 10 chars.
var QuotePatterns = []*regexp.Regexp{
regexp.MustCompile(`"([^"]{10,})"`),
Expand Down
1 change: 0 additions & 1 deletion internal/policy/rerank.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var TimeRanges = map[domain.TimeScope]time.Duration{
//
// BIT-IDENTICAL REQUIREMENT: Python timedelta.days is integer floor.
// Go Hours()/24 is float — must math.Floor to match.
//
func ApplyRecencyWeighting(hits []domain.SearchHit, now time.Time) []domain.SearchHit {
for i := range hits {
r := &hits[i]
Expand Down
7 changes: 4 additions & 3 deletions internal/service/capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
// delegate to these services; business logic lives here, not in handlers.
//
// Spec:
// docs/v04/spec/flows/capture.md (7-phase)
// docs/v04/spec/flows/recall.md (7-phase)
// docs/v04/spec/flows/lifecycle.md (6 tools)
//
// docs/v04/spec/flows/capture.md (7-phase)
// docs/v04/spec/flows/recall.md (7-phase)
// docs/v04/spec/flows/lifecycle.md (6 tools)
package service

import (
Expand Down
27 changes: 14 additions & 13 deletions internal/service/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ type DiagnosticsResult struct {

// EnvInfo — OS, Go runtime version, cwd.
type EnvInfo struct {
OS string `json:"os"`
Runtime string `json:"runtime"`
CWD string `json:"cwd"`
GOArch string `json:"goarch"`
OS string `json:"os"`
Runtime string `json:"runtime"`
CWD string `json:"cwd"`
GOArch string `json:"goarch"`
}

// VaultInfo — subset exposed in diagnostics.
Expand Down Expand Up @@ -168,10 +168,10 @@ func (s *LifecycleService) Diagnostics(ctx context.Context) *DiagnosticsResult {
// Environment
cwd, _ := os.Getwd()
r.Environment = EnvInfo{
OS: runtime.GOOS,
Runtime: runtime.Version(),
CWD: cwd,
GOArch: runtime.GOARCH,
OS: runtime.GOOS,
Runtime: runtime.Version(),
CWD: cwd,
GOArch: runtime.GOARCH,
}

// Config state
Expand Down Expand Up @@ -490,11 +490,12 @@ const WarmupTimeout = 60 * time.Second
//
// TODO: currently a no-op for state recovery — only envector warmup probe runs.
// Full re-init requires:
// 1. internal/lifecycle/boot.go::RunBootLoop body (Vault.GetAgentManifest + bundle setup
// + envector.NewClient + state=Active transition)
// 2. wiring here to re-trigger boot logic on call (state.SetState(Starting) +
// RunBootLoop re-invoke, or a shared _init_pipelines helper called from both
// startup and this function)
// 1. internal/lifecycle/boot.go::RunBootLoop body (Vault.GetAgentManifest + bundle setup
// + envector.NewClient + state=Active transition)
// 2. wiring here to re-trigger boot logic on call (state.SetState(Starting) +
// RunBootLoop re-invoke, or a shared _init_pipelines helper called from both
// startup and this function)
//
// Until both land, /rune:activate cannot recover from dormant or trigger first-time
// pipeline init.
func (s *LifecycleService) ReloadPipelines(ctx context.Context) (*ReloadPipelinesResult, error) {
Expand Down
6 changes: 3 additions & 3 deletions internal/service/recall.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,9 +635,9 @@ func calculateConfidence(results []domain.SearchHit) float64 {
}

certaintyWeights := map[string]float64{
"supported": 1.0,
"partially_supported": 0.6,
"unknown": 0.3,
"supported": 1.0,
"partially_supported": 0.6,
"unknown": 0.3,
}

totalScore := 0.0
Expand Down
Loading