velune-cli 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- velune/__init__.py +5 -0
- velune/__main__.py +6 -0
- velune/cli/__init__.py +5 -0
- velune/cli/app.py +208 -0
- velune/cli/autocomplete.py +80 -0
- velune/cli/banner.py +60 -0
- velune/cli/commands/__init__.py +32 -0
- velune/cli/commands/ask.py +175 -0
- velune/cli/commands/base.py +16 -0
- velune/cli/commands/chat.py +228 -0
- velune/cli/commands/config.py +224 -0
- velune/cli/commands/daemon.py +88 -0
- velune/cli/commands/doctor.py +721 -0
- velune/cli/commands/init.py +170 -0
- velune/cli/commands/mcp.py +82 -0
- velune/cli/commands/memory.py +293 -0
- velune/cli/commands/models.py +683 -0
- velune/cli/commands/preflight.py +95 -0
- velune/cli/commands/run.py +270 -0
- velune/cli/commands/setup.py +184 -0
- velune/cli/commands/workspace.py +249 -0
- velune/cli/context.py +36 -0
- velune/cli/councilmodel_ui.py +199 -0
- velune/cli/display/council_view.py +254 -0
- velune/cli/display/memory_view.py +126 -0
- velune/cli/display/panels.py +35 -0
- velune/cli/display/progress.py +25 -0
- velune/cli/display/themes.py +25 -0
- velune/cli/main.py +15 -0
- velune/cli/model_selector.py +51 -0
- velune/cli/modes.py +86 -0
- velune/cli/pull_ui.py +123 -0
- velune/cli/registry.py +80 -0
- velune/cli/rendering/__init__.py +5 -0
- velune/cli/rendering/error_panel.py +79 -0
- velune/cli/rendering/markdown.py +63 -0
- velune/cli/repl.py +1855 -0
- velune/cli/session_manager.py +71 -0
- velune/cli/slash_commands.py +37 -0
- velune/cli/theme.py +8 -0
- velune/cognition/__init__.py +23 -0
- velune/cognition/agents/__init__.py +7 -0
- velune/cognition/agents/coder.py +209 -0
- velune/cognition/agents/planner.py +156 -0
- velune/cognition/agents/reviewer.py +195 -0
- velune/cognition/arbitrator.py +220 -0
- velune/cognition/architecture.py +415 -0
- velune/cognition/budget.py +65 -0
- velune/cognition/council/__init__.py +47 -0
- velune/cognition/council/base.py +217 -0
- velune/cognition/council/challenger.py +74 -0
- velune/cognition/council/coder.py +79 -0
- velune/cognition/council/critic_agent.py +43 -0
- velune/cognition/council/critic_configs.py +111 -0
- velune/cognition/council/critics.py +41 -0
- velune/cognition/council/debate.py +46 -0
- velune/cognition/council/factory.py +140 -0
- velune/cognition/council/messages.py +56 -0
- velune/cognition/council/planner.py +124 -0
- velune/cognition/council/reviewer.py +74 -0
- velune/cognition/council/synthesizer.py +67 -0
- velune/cognition/council/tiers.py +188 -0
- velune/cognition/council_orchestrator.py +282 -0
- velune/cognition/firewall.py +354 -0
- velune/cognition/module.py +46 -0
- velune/cognition/orchestrator.py +1205 -0
- velune/cognition/personality.py +238 -0
- velune/cognition/state.py +104 -0
- velune/cognition/style_resolver.py +64 -0
- velune/cognition/verification.py +205 -0
- velune/context/__init__.py +28 -0
- velune/context/assembler.py +240 -0
- velune/context/budget.py +97 -0
- velune/context/extractive.py +95 -0
- velune/context/prompt_adaptation.py +480 -0
- velune/context/sections.py +99 -0
- velune/context/token_counter.py +134 -0
- velune/context/utilization.py +33 -0
- velune/context/window.py +63 -0
- velune/core/__init__.py +89 -0
- velune/core/background.py +5 -0
- velune/core/config/__init__.py +37 -0
- velune/core/errors/__init__.py +90 -0
- velune/core/errors/catalog.py +188 -0
- velune/core/errors/execution.py +31 -0
- velune/core/errors/memory.py +25 -0
- velune/core/errors/orchestration.py +31 -0
- velune/core/errors/provider.py +37 -0
- velune/core/event_loop.py +35 -0
- velune/core/logging.py +83 -0
- velune/core/paths.py +165 -0
- velune/core/runtime.py +113 -0
- velune/core/startup_profiler.py +56 -0
- velune/core/task_registry.py +117 -0
- velune/core/trace.py +83 -0
- velune/core/types/__init__.py +48 -0
- velune/core/types/agent.py +53 -0
- velune/core/types/context.py +42 -0
- velune/core/types/inference.py +38 -0
- velune/core/types/memory.py +42 -0
- velune/core/types/model.py +70 -0
- velune/core/types/provider.py +62 -0
- velune/core/types/repository.py +38 -0
- velune/core/types/task.py +61 -0
- velune/core/types/workspace.py +28 -0
- velune/daemon/client.py +13 -0
- velune/daemon/server.py +127 -0
- velune/daemon/transport.py +179 -0
- velune/events.py +204 -0
- velune/execution/__init__.py +22 -0
- velune/execution/benchmarker.py +315 -0
- velune/execution/cancellation.py +53 -0
- velune/execution/checkpointer.py +130 -0
- velune/execution/command_spec.py +165 -0
- velune/execution/diff_preview.py +197 -0
- velune/execution/executor.py +181 -0
- velune/execution/module.py +18 -0
- velune/execution/multi_diff.py +67 -0
- velune/execution/path_guard.py +74 -0
- velune/execution/planner.py +91 -0
- velune/execution/rollback.py +89 -0
- velune/execution/sandbox.py +268 -0
- velune/execution/validator.py +115 -0
- velune/hardware/__init__.py +1 -0
- velune/hardware/detector.py +192 -0
- velune/kernel/__init__.py +55 -0
- velune/kernel/bootstrap.py +125 -0
- velune/kernel/config.py +426 -0
- velune/kernel/entrypoint.py +78 -0
- velune/kernel/health.py +54 -0
- velune/kernel/lifecycle.py +143 -0
- velune/kernel/module.py +17 -0
- velune/kernel/modules.py +23 -0
- velune/kernel/registry.py +96 -0
- velune/kernel/schemas.py +28 -0
- velune/main.py +9 -0
- velune/mcp/__init__.py +9 -0
- velune/mcp/client.py +115 -0
- velune/mcp/config.py +19 -0
- velune/mcp/server.py +624 -0
- velune/memory/__init__.py +32 -0
- velune/memory/compaction.py +506 -0
- velune/memory/embedding_pipeline.py +241 -0
- velune/memory/lifecycle.py +680 -0
- velune/memory/module.py +218 -0
- velune/memory/prioritizer.py +67 -0
- velune/memory/storage/episodic_schema.sql +53 -0
- velune/memory/storage/lancedb_store.py +282 -0
- velune/memory/storage/sqlite_manager.py +369 -0
- velune/memory/storage/sqlite_pool.py +149 -0
- velune/memory/tiers/episodic.py +588 -0
- velune/memory/tiers/graph.py +378 -0
- velune/memory/tiers/lineage.py +416 -0
- velune/memory/tiers/semantic.py +475 -0
- velune/memory/tiers/working.py +168 -0
- velune/memory/vitality.py +132 -0
- velune/models/__init__.py +15 -0
- velune/models/family.py +76 -0
- velune/models/module.py +20 -0
- velune/models/probes.py +192 -0
- velune/models/profile_cache.py +84 -0
- velune/models/profiler.py +108 -0
- velune/models/registry.py +251 -0
- velune/models/scorer.py +233 -0
- velune/models/specializations.py +205 -0
- velune/orchestration/__init__.py +19 -0
- velune/orchestration/engine.py +239 -0
- velune/orchestration/module.py +15 -0
- velune/orchestration/role_assignments.py +82 -0
- velune/orchestration/schemas.py +98 -0
- velune/plugins/__init__.py +20 -0
- velune/plugins/hooks.py +50 -0
- velune/plugins/loader.py +161 -0
- velune/plugins/registry.py +56 -0
- velune/plugins/schemas.py +21 -0
- velune/providers/__init__.py +23 -0
- velune/providers/adapters/anthropic.py +257 -0
- velune/providers/adapters/fireworks.py +115 -0
- velune/providers/adapters/google.py +234 -0
- velune/providers/adapters/groq.py +151 -0
- velune/providers/adapters/huggingface.py +210 -0
- velune/providers/adapters/llamacpp.py +208 -0
- velune/providers/adapters/lmstudio.py +175 -0
- velune/providers/adapters/ollama.py +233 -0
- velune/providers/adapters/openai.py +213 -0
- velune/providers/adapters/openrouter.py +81 -0
- velune/providers/adapters/together.py +134 -0
- velune/providers/adapters/xai.py +60 -0
- velune/providers/base.py +86 -0
- velune/providers/benchmarker.py +138 -0
- velune/providers/discovery/__init__.py +33 -0
- velune/providers/discovery/anthropic.py +79 -0
- velune/providers/discovery/benchmarks.py +44 -0
- velune/providers/discovery/classifier.py +69 -0
- velune/providers/discovery/fireworks.py +95 -0
- velune/providers/discovery/gguf.py +88 -0
- velune/providers/discovery/google.py +95 -0
- velune/providers/discovery/gpu.py +117 -0
- velune/providers/discovery/groq.py +21 -0
- velune/providers/discovery/huggingface.py +67 -0
- velune/providers/discovery/lmstudio.py +80 -0
- velune/providers/discovery/ollama.py +162 -0
- velune/providers/discovery/openai.py +96 -0
- velune/providers/discovery/openrouter.py +113 -0
- velune/providers/discovery/scanner.py +115 -0
- velune/providers/discovery/together.py +114 -0
- velune/providers/discovery/xai.py +57 -0
- velune/providers/health.py +67 -0
- velune/providers/health_monitor.py +169 -0
- velune/providers/keystore.py +142 -0
- velune/providers/local_paths.py +49 -0
- velune/providers/local_resolver.py +229 -0
- velune/providers/module.py +51 -0
- velune/providers/ollama_manager.py +193 -0
- velune/providers/registry.py +220 -0
- velune/providers/router.py +255 -0
- velune/providers/task_classifier.py +288 -0
- velune/py.typed +0 -0
- velune/repository/__init__.py +33 -0
- velune/repository/analyzer.py +127 -0
- velune/repository/ast_parser.py +822 -0
- velune/repository/blast_radius.py +298 -0
- velune/repository/boundary_classifier.py +295 -0
- velune/repository/cognition.py +316 -0
- velune/repository/grapher.py +179 -0
- velune/repository/import_graph.py +263 -0
- velune/repository/incremental_indexer.py +275 -0
- velune/repository/index_state.py +96 -0
- velune/repository/indexer.py +243 -0
- velune/repository/module.py +17 -0
- velune/repository/parser.py +474 -0
- velune/repository/project_type.py +300 -0
- velune/repository/rename_journal.py +287 -0
- velune/repository/scanner.py +193 -0
- velune/repository/schemas.py +102 -0
- velune/repository/symbol_registry.py +365 -0
- velune/repository/tracker.py +252 -0
- velune/retrieval/__init__.py +27 -0
- velune/retrieval/cache.py +110 -0
- velune/retrieval/fast_path.py +391 -0
- velune/retrieval/graph.py +124 -0
- velune/retrieval/hybrid.py +271 -0
- velune/retrieval/keyword.py +131 -0
- velune/retrieval/module.py +26 -0
- velune/retrieval/pipeline.py +303 -0
- velune/retrieval/reranker.py +102 -0
- velune/retrieval/schemas.py +59 -0
- velune/retrieval/slow_path.py +364 -0
- velune/retrieval/vector.py +203 -0
- velune/telemetry/__init__.py +59 -0
- velune/telemetry/cognition.py +267 -0
- velune/telemetry/cost_estimator.py +92 -0
- velune/telemetry/debug.py +304 -0
- velune/telemetry/doctor.py +244 -0
- velune/telemetry/logging.py +286 -0
- velune/telemetry/spans.py +277 -0
- velune/telemetry/token_tracker.py +140 -0
- velune/telemetry/usage_tracker.py +340 -0
- velune/tools/__init__.py +41 -0
- velune/tools/base/registry.py +87 -0
- velune/tools/base/tool.py +63 -0
- velune/tools/code/navigate.py +116 -0
- velune/tools/code/search.py +123 -0
- velune/tools/filesystem/read.py +75 -0
- velune/tools/filesystem/search.py +136 -0
- velune/tools/filesystem/write.py +163 -0
- velune/tools/git/history.py +177 -0
- velune/tools/git/operations.py +122 -0
- velune/tools/git/state.py +121 -0
- velune/tools/module.py +81 -0
- velune/tools/terminal/execute.py +72 -0
- velune/tools/terminal/history.py +47 -0
- velune/tools/web/fetch.py +55 -0
- velune/tools/web/validator.py +122 -0
- velune_cli-0.9.0.dist-info/METADATA +518 -0
- velune_cli-0.9.0.dist-info/RECORD +279 -0
- velune_cli-0.9.0.dist-info/WHEEL +4 -0
- velune_cli-0.9.0.dist-info/entry_points.txt +2 -0
- velune_cli-0.9.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""Decision Lineage and Failed Experiment Memory Tier.
|
|
2
|
+
|
|
3
|
+
SQLite-backed persistent store for long-running cognitive continuity and
|
|
4
|
+
failed-approach blocking.
|
|
5
|
+
|
|
6
|
+
All I/O is async and routed through
|
|
7
|
+
:class:`~velune.memory.storage.sqlite_pool.SQLiteConnectionPool` so
|
|
8
|
+
concurrent coroutines never cause WAL write contention.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import time
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from velune.memory.storage.sqlite_pool import SQLiteConnectionPool
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("velune.memory.tiers.lineage")
|
|
21
|
+
|
|
22
|
+
_SCHEMA_SQL = """
|
|
23
|
+
CREATE TABLE IF NOT EXISTS decision_nodes (
|
|
24
|
+
id TEXT PRIMARY KEY,
|
|
25
|
+
timestamp REAL NOT NULL,
|
|
26
|
+
target_subsystem TEXT NOT NULL,
|
|
27
|
+
rationale TEXT NOT NULL,
|
|
28
|
+
architectural_impact REAL DEFAULT 0.0,
|
|
29
|
+
consequences TEXT
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS design_alternatives (
|
|
33
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
|
+
decision_id TEXT NOT NULL,
|
|
35
|
+
option_name TEXT NOT NULL,
|
|
36
|
+
tradeoffs TEXT,
|
|
37
|
+
rejected_reason TEXT,
|
|
38
|
+
FOREIGN KEY(decision_id) REFERENCES decision_nodes(id) ON DELETE CASCADE
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
CREATE TABLE IF NOT EXISTS failed_experiments (
|
|
42
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43
|
+
timestamp REAL NOT NULL,
|
|
44
|
+
target_subsystem TEXT NOT NULL,
|
|
45
|
+
patch TEXT NOT NULL,
|
|
46
|
+
error_type TEXT NOT NULL,
|
|
47
|
+
error_message TEXT NOT NULL
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
CREATE TABLE IF NOT EXISTS repo_personality_styles (
|
|
51
|
+
subsystem TEXT PRIMARY KEY,
|
|
52
|
+
naming_conventions TEXT NOT NULL,
|
|
53
|
+
type_hinting_strictness REAL NOT NULL,
|
|
54
|
+
preferred_constructs TEXT NOT NULL,
|
|
55
|
+
class_vs_functional TEXT NOT NULL,
|
|
56
|
+
docstring_style TEXT NOT NULL,
|
|
57
|
+
updated_at REAL NOT NULL
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS repository_evolution_timeline (
|
|
61
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
|
+
timestamp REAL NOT NULL,
|
|
63
|
+
subsystem TEXT NOT NULL,
|
|
64
|
+
lcom_average REAL NOT NULL,
|
|
65
|
+
coupling_ratio REAL NOT NULL,
|
|
66
|
+
debt_items_count INTEGER NOT NULL,
|
|
67
|
+
major_milestone TEXT,
|
|
68
|
+
rationales_summary TEXT NOT NULL
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_subsystem ON decision_nodes(target_subsystem);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_failures_subsystem ON failed_experiments(target_subsystem);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_personality_subsystem ON repo_personality_styles(subsystem);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_evolution_subsystem ON repository_evolution_timeline(subsystem);
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class LineageMemoryTier:
|
|
79
|
+
"""Persistent storage tier for architectural decisions (DLS) and failed experiments (FEL).
|
|
80
|
+
|
|
81
|
+
All reads and writes are async and serialised through an
|
|
82
|
+
:class:`~velune.memory.storage.sqlite_pool.SQLiteConnectionPool` to
|
|
83
|
+
prevent file-locking contention.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, pool: SQLiteConnectionPool) -> None:
|
|
87
|
+
self._pool = pool
|
|
88
|
+
|
|
89
|
+
# ------------------------------------------------------------------
|
|
90
|
+
# Lifecycle
|
|
91
|
+
# ------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
async def initialize(self) -> None:
|
|
94
|
+
"""Create tables and indexes if they do not yet exist."""
|
|
95
|
+
await self._init_db()
|
|
96
|
+
|
|
97
|
+
async def _init_db(self) -> None:
|
|
98
|
+
async with self._pool.write() as conn:
|
|
99
|
+
for stmt in _SCHEMA_SQL.split(";"):
|
|
100
|
+
stmt = stmt.strip()
|
|
101
|
+
if stmt:
|
|
102
|
+
await conn.execute(stmt)
|
|
103
|
+
logger.debug("LineageMemoryTier schema initialised.")
|
|
104
|
+
|
|
105
|
+
# ------------------------------------------------------------------
|
|
106
|
+
# Write operations
|
|
107
|
+
# ------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
async def log_decision(
|
|
110
|
+
self,
|
|
111
|
+
decision_id: str,
|
|
112
|
+
target_subsystem: str,
|
|
113
|
+
rationale: str,
|
|
114
|
+
architectural_impact: float = 0.0,
|
|
115
|
+
consequences: str | None = None,
|
|
116
|
+
alternatives: list[dict[str, Any]] | None = None,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Log an approved architectural decision and its design trade-offs."""
|
|
119
|
+
decision_sql = """
|
|
120
|
+
INSERT INTO decision_nodes
|
|
121
|
+
(id, timestamp, target_subsystem, rationale, architectural_impact, consequences)
|
|
122
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
123
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
124
|
+
target_subsystem=excluded.target_subsystem,
|
|
125
|
+
rationale=excluded.rationale,
|
|
126
|
+
architectural_impact=excluded.architectural_impact,
|
|
127
|
+
consequences=excluded.consequences
|
|
128
|
+
"""
|
|
129
|
+
params = (
|
|
130
|
+
decision_id,
|
|
131
|
+
time.time(),
|
|
132
|
+
target_subsystem,
|
|
133
|
+
rationale,
|
|
134
|
+
architectural_impact,
|
|
135
|
+
consequences or "",
|
|
136
|
+
)
|
|
137
|
+
try:
|
|
138
|
+
async with self._pool.write() as conn:
|
|
139
|
+
await conn.execute(decision_sql, params)
|
|
140
|
+
if alternatives:
|
|
141
|
+
await conn.execute(
|
|
142
|
+
"DELETE FROM design_alternatives WHERE decision_id = ?",
|
|
143
|
+
(decision_id,),
|
|
144
|
+
)
|
|
145
|
+
for alt in alternatives:
|
|
146
|
+
await conn.execute(
|
|
147
|
+
"""
|
|
148
|
+
INSERT INTO design_alternatives
|
|
149
|
+
(decision_id, option_name, tradeoffs, rejected_reason)
|
|
150
|
+
VALUES (?, ?, ?, ?)
|
|
151
|
+
""",
|
|
152
|
+
(
|
|
153
|
+
decision_id,
|
|
154
|
+
alt.get("option_name", "Option"),
|
|
155
|
+
json.dumps(alt.get("tradeoffs", {})),
|
|
156
|
+
alt.get("rejected_reason", ""),
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
except Exception as exc:
|
|
160
|
+
logger.error("Checkpoint save failed: %s", exc)
|
|
161
|
+
raise
|
|
162
|
+
|
|
163
|
+
async def log_failed_experiment(
|
|
164
|
+
self,
|
|
165
|
+
target_subsystem: str,
|
|
166
|
+
patch: str,
|
|
167
|
+
error_type: str,
|
|
168
|
+
error_message: str,
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Log a failed implementation experiment (FEL) to prevent repeating it."""
|
|
171
|
+
try:
|
|
172
|
+
async with self._pool.write() as conn:
|
|
173
|
+
await conn.execute(
|
|
174
|
+
"""
|
|
175
|
+
INSERT INTO failed_experiments
|
|
176
|
+
(timestamp, target_subsystem, patch, error_type, error_message)
|
|
177
|
+
VALUES (?, ?, ?, ?, ?)
|
|
178
|
+
""",
|
|
179
|
+
(time.time(), target_subsystem, patch, error_type, error_message),
|
|
180
|
+
)
|
|
181
|
+
except Exception as exc:
|
|
182
|
+
logger.error("Failed to log experiment: %s", exc)
|
|
183
|
+
|
|
184
|
+
async def save_personality_style(
|
|
185
|
+
self,
|
|
186
|
+
subsystem: str,
|
|
187
|
+
naming_conventions: dict,
|
|
188
|
+
type_hinting_strictness: float,
|
|
189
|
+
preferred_constructs: list,
|
|
190
|
+
class_vs_functional: str,
|
|
191
|
+
docstring_style: str,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Persist a subsystem's repository personality and style profile."""
|
|
194
|
+
try:
|
|
195
|
+
async with self._pool.write() as conn:
|
|
196
|
+
await conn.execute(
|
|
197
|
+
"""
|
|
198
|
+
INSERT INTO repo_personality_styles (
|
|
199
|
+
subsystem, naming_conventions, type_hinting_strictness,
|
|
200
|
+
preferred_constructs, class_vs_functional, docstring_style,
|
|
201
|
+
updated_at
|
|
202
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
203
|
+
ON CONFLICT(subsystem) DO UPDATE SET
|
|
204
|
+
naming_conventions=excluded.naming_conventions,
|
|
205
|
+
type_hinting_strictness=excluded.type_hinting_strictness,
|
|
206
|
+
preferred_constructs=excluded.preferred_constructs,
|
|
207
|
+
class_vs_functional=excluded.class_vs_functional,
|
|
208
|
+
docstring_style=excluded.docstring_style,
|
|
209
|
+
updated_at=excluded.updated_at
|
|
210
|
+
""",
|
|
211
|
+
(
|
|
212
|
+
subsystem,
|
|
213
|
+
json.dumps(naming_conventions),
|
|
214
|
+
type_hinting_strictness,
|
|
215
|
+
json.dumps(preferred_constructs),
|
|
216
|
+
class_vs_functional,
|
|
217
|
+
docstring_style,
|
|
218
|
+
time.time(),
|
|
219
|
+
),
|
|
220
|
+
)
|
|
221
|
+
except Exception as exc:
|
|
222
|
+
logger.error("Failed to save personality style: %s", exc)
|
|
223
|
+
|
|
224
|
+
async def log_monthly_snapshot(
|
|
225
|
+
self,
|
|
226
|
+
subsystem: str,
|
|
227
|
+
lcom_average: float,
|
|
228
|
+
coupling_ratio: float,
|
|
229
|
+
debt_items_count: int,
|
|
230
|
+
milestone: str | None = None,
|
|
231
|
+
rationale_summary: str = "",
|
|
232
|
+
) -> None:
|
|
233
|
+
"""Persist a monthly architecture snapshot for the given subsystem."""
|
|
234
|
+
try:
|
|
235
|
+
async with self._pool.write() as conn:
|
|
236
|
+
await conn.execute(
|
|
237
|
+
"""
|
|
238
|
+
INSERT INTO repository_evolution_timeline
|
|
239
|
+
(timestamp, subsystem, lcom_average, coupling_ratio,
|
|
240
|
+
debt_items_count, major_milestone, rationales_summary)
|
|
241
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
242
|
+
""",
|
|
243
|
+
(
|
|
244
|
+
time.time(),
|
|
245
|
+
subsystem,
|
|
246
|
+
round(lcom_average, 4),
|
|
247
|
+
round(coupling_ratio, 4),
|
|
248
|
+
int(debt_items_count),
|
|
249
|
+
milestone or "",
|
|
250
|
+
rationale_summary,
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
except Exception as exc:
|
|
254
|
+
logger.error("Failed to log monthly snapshot: %s", exc)
|
|
255
|
+
|
|
256
|
+
# ------------------------------------------------------------------
|
|
257
|
+
# Read operations
|
|
258
|
+
# ------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
async def get_subsystem_decisions(self, subsystem: str) -> list[dict[str, Any]]:
|
|
261
|
+
"""Fetch all decisions matching a target subsystem."""
|
|
262
|
+
try:
|
|
263
|
+
async with self._pool.read() as conn:
|
|
264
|
+
cursor = await conn.execute(
|
|
265
|
+
"""
|
|
266
|
+
SELECT id, timestamp, target_subsystem, rationale,
|
|
267
|
+
architectural_impact, consequences
|
|
268
|
+
FROM decision_nodes
|
|
269
|
+
WHERE target_subsystem LIKE ?
|
|
270
|
+
ORDER BY timestamp DESC
|
|
271
|
+
""",
|
|
272
|
+
(f"%{subsystem}%",),
|
|
273
|
+
)
|
|
274
|
+
rows = await cursor.fetchall()
|
|
275
|
+
|
|
276
|
+
decisions = []
|
|
277
|
+
for r in rows:
|
|
278
|
+
dec = dict(r)
|
|
279
|
+
async with self._pool.read() as conn2:
|
|
280
|
+
cur2 = await conn2.execute(
|
|
281
|
+
"SELECT option_name, tradeoffs, rejected_reason"
|
|
282
|
+
" FROM design_alternatives WHERE decision_id = ?",
|
|
283
|
+
(dec["id"],),
|
|
284
|
+
)
|
|
285
|
+
alt_rows = await cur2.fetchall()
|
|
286
|
+
dec["alternatives"] = []
|
|
287
|
+
for ar in alt_rows:
|
|
288
|
+
alt = dict(ar)
|
|
289
|
+
try:
|
|
290
|
+
alt["tradeoffs"] = json.loads(alt["tradeoffs"])
|
|
291
|
+
except Exception:
|
|
292
|
+
alt["tradeoffs"] = {}
|
|
293
|
+
dec["alternatives"].append(alt)
|
|
294
|
+
decisions.append(dec)
|
|
295
|
+
return decisions
|
|
296
|
+
except Exception as exc:
|
|
297
|
+
logger.error("Failed to query subsystem decisions: %s", exc)
|
|
298
|
+
return []
|
|
299
|
+
|
|
300
|
+
async def get_failed_experiments(self, subsystem: str) -> list[dict[str, Any]]:
|
|
301
|
+
"""Fetch all failed experiment records matching a target subsystem."""
|
|
302
|
+
try:
|
|
303
|
+
async with self._pool.read() as conn:
|
|
304
|
+
cursor = await conn.execute(
|
|
305
|
+
"""
|
|
306
|
+
SELECT id, timestamp, target_subsystem, patch,
|
|
307
|
+
error_type, error_message
|
|
308
|
+
FROM failed_experiments
|
|
309
|
+
WHERE target_subsystem LIKE ?
|
|
310
|
+
ORDER BY timestamp DESC
|
|
311
|
+
""",
|
|
312
|
+
(f"%{subsystem}%",),
|
|
313
|
+
)
|
|
314
|
+
rows = await cursor.fetchall()
|
|
315
|
+
return [dict(r) for r in rows]
|
|
316
|
+
except Exception as exc:
|
|
317
|
+
logger.error("Failed to query failed experiments: %s", exc)
|
|
318
|
+
return []
|
|
319
|
+
|
|
320
|
+
async def get_personality_style(self, subsystem: str) -> dict[str, Any] | None:
|
|
321
|
+
"""Query the style profile for a subsystem."""
|
|
322
|
+
try:
|
|
323
|
+
async with self._pool.read() as conn:
|
|
324
|
+
cursor = await conn.execute(
|
|
325
|
+
"""
|
|
326
|
+
SELECT subsystem, naming_conventions, type_hinting_strictness,
|
|
327
|
+
preferred_constructs, class_vs_functional,
|
|
328
|
+
docstring_style, updated_at
|
|
329
|
+
FROM repo_personality_styles
|
|
330
|
+
WHERE subsystem = ?
|
|
331
|
+
""",
|
|
332
|
+
(subsystem,),
|
|
333
|
+
)
|
|
334
|
+
row = await cursor.fetchone()
|
|
335
|
+
if row:
|
|
336
|
+
res = dict(row)
|
|
337
|
+
try:
|
|
338
|
+
res["naming_conventions"] = json.loads(res["naming_conventions"])
|
|
339
|
+
except Exception:
|
|
340
|
+
res["naming_conventions"] = {}
|
|
341
|
+
try:
|
|
342
|
+
res["preferred_constructs"] = json.loads(res["preferred_constructs"])
|
|
343
|
+
except Exception:
|
|
344
|
+
res["preferred_constructs"] = []
|
|
345
|
+
return res
|
|
346
|
+
return None
|
|
347
|
+
except Exception as exc:
|
|
348
|
+
logger.error("Failed to query repository personality style: %s", exc)
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
async def get_evolution_timeline(self, subsystem: str, limit: int = 50) -> list[dict[str, Any]]:
|
|
352
|
+
"""Fetch the evolution snapshots for a subsystem, most recent first."""
|
|
353
|
+
try:
|
|
354
|
+
async with self._pool.read() as conn:
|
|
355
|
+
cursor = await conn.execute(
|
|
356
|
+
"""
|
|
357
|
+
SELECT id, timestamp, subsystem, lcom_average, coupling_ratio,
|
|
358
|
+
debt_items_count, major_milestone, rationales_summary
|
|
359
|
+
FROM repository_evolution_timeline
|
|
360
|
+
WHERE subsystem LIKE ?
|
|
361
|
+
ORDER BY timestamp DESC
|
|
362
|
+
LIMIT ?
|
|
363
|
+
""",
|
|
364
|
+
(f"%{subsystem}%", limit),
|
|
365
|
+
)
|
|
366
|
+
rows = await cursor.fetchall()
|
|
367
|
+
return [dict(r) for r in rows]
|
|
368
|
+
except Exception as exc:
|
|
369
|
+
logger.error("Failed to query evolution timeline: %s", exc)
|
|
370
|
+
return []
|
|
371
|
+
|
|
372
|
+
async def query_continuity_warnings(
|
|
373
|
+
self,
|
|
374
|
+
prompt: str,
|
|
375
|
+
repo_context: str,
|
|
376
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
377
|
+
"""Match task keywords against stored decisions and failed experiments."""
|
|
378
|
+
combined_text = (prompt + " " + repo_context).lower()
|
|
379
|
+
subsystem_keys = [
|
|
380
|
+
"database",
|
|
381
|
+
"db",
|
|
382
|
+
"concurrency",
|
|
383
|
+
"lock",
|
|
384
|
+
"thread",
|
|
385
|
+
"async",
|
|
386
|
+
"cache",
|
|
387
|
+
"sandbox",
|
|
388
|
+
"security",
|
|
389
|
+
"telemetry",
|
|
390
|
+
"model",
|
|
391
|
+
"routing",
|
|
392
|
+
"memory",
|
|
393
|
+
"graph",
|
|
394
|
+
"watcher",
|
|
395
|
+
"file",
|
|
396
|
+
"lifecycle",
|
|
397
|
+
"executor",
|
|
398
|
+
"bus",
|
|
399
|
+
"registry",
|
|
400
|
+
]
|
|
401
|
+
|
|
402
|
+
matched_decisions: list[dict[str, Any]] = []
|
|
403
|
+
matched_failures: list[dict[str, Any]] = []
|
|
404
|
+
|
|
405
|
+
for key in subsystem_keys:
|
|
406
|
+
if key in combined_text:
|
|
407
|
+
matched_decisions.extend(await self.get_subsystem_decisions(key))
|
|
408
|
+
matched_failures.extend(await self.get_failed_experiments(key))
|
|
409
|
+
|
|
410
|
+
unique_decisions = {d["id"]: d for d in matched_decisions}.values()
|
|
411
|
+
decisions_list = sorted(unique_decisions, key=lambda x: x["timestamp"], reverse=True)[:3]
|
|
412
|
+
|
|
413
|
+
unique_failures = {f["id"]: f for f in matched_failures}.values()
|
|
414
|
+
failures_list = sorted(unique_failures, key=lambda x: x["timestamp"], reverse=True)[:3]
|
|
415
|
+
|
|
416
|
+
return list(decisions_list), list(failures_list)
|