devcouncil 0.1.0 → 0.2.0
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.
- package/LICENSE +201 -201
- package/README.md +197 -494
- package/package.json +9 -2
- package/pyproject.toml +62 -27
- package/src/devcouncil/__main__.py +4 -4
- package/src/devcouncil/app/__init__.py +28 -28
- package/src/devcouncil/app/config.py +297 -108
- package/src/devcouncil/app/errors.py +23 -23
- package/src/devcouncil/app/events.py +44 -44
- package/src/devcouncil/app/orchestrator.py +67 -67
- package/src/devcouncil/app/project_status.py +29 -0
- package/src/devcouncil/app/run_context.py +39 -39
- package/src/devcouncil/app/state_machine.py +108 -108
- package/src/devcouncil/artifacts/__init__.py +1 -1
- package/src/devcouncil/artifacts/coverage.py +96 -96
- package/src/devcouncil/artifacts/graph.py +163 -143
- package/src/devcouncil/artifacts/migrations.py +20 -20
- package/src/devcouncil/artifacts/schemas.py +23 -23
- package/src/devcouncil/artifacts/serializer.py +21 -21
- package/src/devcouncil/artifacts/validators.py +27 -27
- package/src/devcouncil/assets/__init__.py +1 -0
- package/src/devcouncil/assets/devcouncil-logo.svg +60 -0
- package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
- package/src/devcouncil/cli/commands/agents.py +292 -0
- package/src/devcouncil/cli/commands/artifacts.py +54 -48
- package/src/devcouncil/cli/commands/ast.py +22 -0
- package/src/devcouncil/cli/commands/baseline.py +35 -32
- package/src/devcouncil/cli/commands/check.py +209 -0
- package/src/devcouncil/cli/commands/config.py +115 -54
- package/src/devcouncil/cli/commands/cost.py +57 -0
- package/src/devcouncil/cli/commands/dashboard.py +31 -0
- package/src/devcouncil/cli/commands/doctor.py +291 -47
- package/src/devcouncil/cli/commands/evidence.py +48 -0
- package/src/devcouncil/cli/commands/go.py +656 -0
- package/src/devcouncil/cli/commands/handoff.py +69 -0
- package/src/devcouncil/cli/commands/hook.py +209 -33
- package/src/devcouncil/cli/commands/init.py +204 -57
- package/src/devcouncil/cli/commands/integrate.py +1171 -76
- package/src/devcouncil/cli/commands/lsp.py +20 -0
- package/src/devcouncil/cli/commands/map.py +96 -22
- package/src/devcouncil/cli/commands/plan.py +422 -210
- package/src/devcouncil/cli/commands/prompt.py +48 -34
- package/src/devcouncil/cli/commands/repair.py +89 -69
- package/src/devcouncil/cli/commands/report.py +120 -54
- package/src/devcouncil/cli/commands/reset_demo_state.py +33 -28
- package/src/devcouncil/cli/commands/rollback.py +55 -54
- package/src/devcouncil/cli/commands/run.py +285 -220
- package/src/devcouncil/cli/commands/runs.py +223 -0
- package/src/devcouncil/cli/commands/scaffold.py +32 -0
- package/src/devcouncil/cli/commands/semantic.py +47 -0
- package/src/devcouncil/cli/commands/setup.py +300 -20
- package/src/devcouncil/cli/commands/shell.py +73 -0
- package/src/devcouncil/cli/commands/show.py +76 -57
- package/src/devcouncil/cli/commands/skills.py +88 -0
- package/src/devcouncil/cli/commands/status.py +141 -105
- package/src/devcouncil/cli/commands/tasks.py +55 -41
- package/src/devcouncil/cli/commands/trace.py +49 -4
- package/src/devcouncil/cli/commands/verify.py +293 -128
- package/src/devcouncil/cli/commands/version.py +20 -20
- package/src/devcouncil/cli/commands/watch.py +574 -0
- package/src/devcouncil/cli/commands/watch_fs.py +40 -0
- package/src/devcouncil/cli/main.py +92 -25
- package/src/devcouncil/council/prompts/arbiter.md +19 -19
- package/src/devcouncil/council/prompts/critic_a.md +10 -10
- package/src/devcouncil/council/prompts/critic_b.md +10 -10
- package/src/devcouncil/council/prompts/implementation_reviewer.md +16 -16
- package/src/devcouncil/council/prompts/planner_a.md +16 -16
- package/src/devcouncil/council/prompts/planner_b.md +16 -16
- package/src/devcouncil/council/prompts/rebuttal.md +10 -10
- package/src/devcouncil/council/prompts/spec_writer.md +12 -12
- package/src/devcouncil/domain/assumption.py +17 -17
- package/src/devcouncil/domain/critique.py +32 -32
- package/src/devcouncil/domain/evidence.py +47 -27
- package/src/devcouncil/domain/gap.py +52 -26
- package/src/devcouncil/domain/requirement.py +22 -22
- package/src/devcouncil/domain/task.py +55 -26
- package/src/devcouncil/execution/__init__.py +1 -1
- package/src/devcouncil/execution/checkpoints.py +246 -0
- package/src/devcouncil/execution/context_builder.py +54 -54
- package/src/devcouncil/execution/executor.py +15 -15
- package/src/devcouncil/execution/fs_watcher.py +180 -0
- package/src/devcouncil/execution/handoff.py +102 -0
- package/src/devcouncil/execution/hook_policy.py +186 -77
- package/src/devcouncil/execution/patch.py +77 -28
- package/src/devcouncil/execution/permissions.py +52 -59
- package/src/devcouncil/execution/policy_engine.py +343 -0
- package/src/devcouncil/execution/prompt_builder.py +650 -38
- package/src/devcouncil/execution/shell_session.py +225 -0
- package/src/devcouncil/execution/task_runner.py +68 -64
- package/src/devcouncil/executors/__init__.py +1 -1
- package/src/devcouncil/executors/agent_registry.py +575 -0
- package/src/devcouncil/executors/coding_cli.py +736 -0
- package/src/devcouncil/executors/mini_swe.py +63 -63
- package/src/devcouncil/executors/native/agent.py +186 -85
- package/src/devcouncil/executors/openhands.py +56 -56
- package/src/devcouncil/gating/__init__.py +1 -1
- package/src/devcouncil/gating/checks/clean_git.py +52 -45
- package/src/devcouncil/gating/checks/planned_files_check.py +32 -32
- package/src/devcouncil/gating/checks/requirement_coverage.py +26 -26
- package/src/devcouncil/gating/checks/secret_scan_check.py +53 -34
- package/src/devcouncil/gating/policy.py +315 -167
- package/src/devcouncil/hardware.py +184 -0
- package/src/devcouncil/indexing/__init__.py +1 -1
- package/src/devcouncil/indexing/ast_matcher.py +168 -0
- package/src/devcouncil/indexing/graph_index.py +48 -48
- package/src/devcouncil/indexing/lsp.py +161 -0
- package/src/devcouncil/indexing/repo_mapper.py +1455 -204
- package/src/devcouncil/indexing/semantic_index.py +205 -0
- package/src/devcouncil/integrations/actions.py +146 -0
- package/src/devcouncil/integrations/check.py +423 -0
- package/src/devcouncil/integrations/github.py +35 -35
- package/src/devcouncil/integrations/github_intent.py +142 -0
- package/src/devcouncil/integrations/gitnexus.py +62 -27
- package/src/devcouncil/integrations/graphify.py +34 -34
- package/src/devcouncil/integrations/mcp/server.py +2072 -96
- package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +24 -0
- package/src/devcouncil/integrations/pr_comments.py +62 -0
- package/src/devcouncil/live/__init__.py +2 -0
- package/src/devcouncil/live/cards.py +349 -0
- package/src/devcouncil/live/models.py +63 -0
- package/src/devcouncil/live/repair_prompt.py +83 -0
- package/src/devcouncil/live/reviewer.py +70 -0
- package/src/devcouncil/live/signals.py +135 -0
- package/src/devcouncil/live/summary.py +34 -0
- package/src/devcouncil/live/tasks.py +18 -0
- package/src/devcouncil/live/transcripts.py +141 -0
- package/src/devcouncil/llm/__init__.py +1 -1
- package/src/devcouncil/llm/cache.py +42 -38
- package/src/devcouncil/llm/model_defaults.yaml +44 -0
- package/src/devcouncil/llm/provider.py +627 -125
- package/src/devcouncil/llm/router.py +303 -118
- package/src/devcouncil/optimization/__init__.py +1 -0
- package/src/devcouncil/optimization/gepa_agent.py +318 -0
- package/src/devcouncil/planning/__init__.py +1 -1
- package/src/devcouncil/planning/arbiter_service.py +57 -57
- package/src/devcouncil/planning/correction_manifest.py +303 -0
- package/src/devcouncil/planning/critique_service.py +71 -66
- package/src/devcouncil/planning/plan_service.py +60 -46
- package/src/devcouncil/planning/prompt_enhancer_service.py +167 -0
- package/src/devcouncil/planning/repair_service.py +39 -39
- package/src/devcouncil/planning/spec_service.py +70 -44
- package/src/devcouncil/repo/ci_scaffold.py +157 -0
- package/src/devcouncil/repo/gitignore.py +123 -0
- package/src/devcouncil/repo/sca.py +374 -0
- package/src/devcouncil/reporting/github_check.py +32 -32
- package/src/devcouncil/reporting/json_report.py +30 -17
- package/src/devcouncil/reporting/markdown_report.py +83 -46
- package/src/devcouncil/reporting/report_builder.py +14 -14
- package/src/devcouncil/skills/__init__.py +19 -0
- package/src/devcouncil/skills/library/README.md +46 -0
- package/src/devcouncil/skills/library/ai-training.md +50 -0
- package/src/devcouncil/skills/library/android.md +50 -0
- package/src/devcouncil/skills/library/backend.md +52 -0
- package/src/devcouncil/skills/library/core-engineering.md +95 -0
- package/src/devcouncil/skills/library/data-engineering.md +47 -0
- package/src/devcouncil/skills/library/desktop.md +46 -0
- package/src/devcouncil/skills/library/devops.md +48 -0
- package/src/devcouncil/skills/library/game-dev.md +46 -0
- package/src/devcouncil/skills/library/ios.md +48 -0
- package/src/devcouncil/skills/library/mobile-cross-platform.md +46 -0
- package/src/devcouncil/skills/library/security.md +48 -0
- package/src/devcouncil/skills/library/systems.md +48 -0
- package/src/devcouncil/skills/library/web.md +47 -0
- package/src/devcouncil/skills/library/windows.md +47 -0
- package/src/devcouncil/skills/registry.py +330 -0
- package/src/devcouncil/storage/db.py +147 -66
- package/src/devcouncil/storage/models.py +204 -83
- package/src/devcouncil/storage/native.py +557 -0
- package/src/devcouncil/storage/repositories.py +388 -249
- package/src/devcouncil/telemetry/cost.py +140 -34
- package/src/devcouncil/telemetry/model_pricing.yaml +48 -0
- package/src/devcouncil/telemetry/pricing.py +28 -0
- package/src/devcouncil/telemetry/traces.py +62 -7
- package/src/devcouncil/telemetry/tracker.py +52 -49
- package/src/devcouncil/ui/__init__.py +1 -0
- package/src/devcouncil/ui/dashboard.py +423 -0
- package/src/devcouncil/utils/__init__.py +1 -1
- package/src/devcouncil/utils/redaction.py +147 -141
- package/src/devcouncil/utils/subprocess_env.py +69 -0
- package/src/devcouncil/verification/__init__.py +1 -1
- package/src/devcouncil/verification/acceptance_compiler.py +125 -0
- package/src/devcouncil/verification/ad_hoc_check.py +129 -0
- package/src/devcouncil/verification/diff_coverage.py +353 -0
- package/src/devcouncil/verification/implementation_reviewer.py +55 -55
- package/src/devcouncil/verification/next_actions.py +189 -0
- package/src/devcouncil/verification/sandbox.py +178 -0
- package/src/devcouncil/verification/test_resolver.py +91 -0
- package/src/devcouncil/verification/verifier.py +1342 -307
- package/uv.lock +205 -64
- package/src/devcouncil/indexing/symbol_index.py +0 -0
|
@@ -1,66 +1,147 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
from sqlmodel import SQLModel, create_engine, Session
|
|
6
|
+
from sqlalchemy import inspect, text
|
|
7
|
+
from sqlalchemy.exc import OperationalError
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from devcouncil.storage.models import SchemaVersionModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# v3: per-task indexes on the task/audit tables (task_id, lease status).
|
|
15
|
+
# v4: machine-routable gap columns (file, line, suggested_command,
|
|
16
|
+
# acceptance_criterion_id) so the repair contract survives a reload.
|
|
17
|
+
# v5: partial unique index enforcing one ACTIVE lease per task (single-writer).
|
|
18
|
+
SCHEMA_VERSION = 5
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Database:
|
|
22
|
+
def __init__(self, db_path: Path):
|
|
23
|
+
self.db_path = db_path
|
|
24
|
+
self.engine = create_engine(f"sqlite:///{db_path}")
|
|
25
|
+
|
|
26
|
+
def create_db_and_tables(self):
|
|
27
|
+
self._create_tables()
|
|
28
|
+
self.ensure_schema_version()
|
|
29
|
+
|
|
30
|
+
def ensure_schema_version(self):
|
|
31
|
+
self._create_tables()
|
|
32
|
+
with Session(self.engine) as session:
|
|
33
|
+
current = session.get(SchemaVersionModel, "singleton")
|
|
34
|
+
if current is None:
|
|
35
|
+
session.add(SchemaVersionModel(id="singleton", version=SCHEMA_VERSION))
|
|
36
|
+
session.commit()
|
|
37
|
+
return
|
|
38
|
+
if current.version > SCHEMA_VERSION:
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"Unsupported DevCouncil schema version {current.version}; "
|
|
41
|
+
f"expected {SCHEMA_VERSION}."
|
|
42
|
+
)
|
|
43
|
+
if current.version < SCHEMA_VERSION:
|
|
44
|
+
self._create_tables()
|
|
45
|
+
current.version = SCHEMA_VERSION
|
|
46
|
+
session.add(current)
|
|
47
|
+
session.commit()
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
def _create_tables(self):
|
|
51
|
+
try:
|
|
52
|
+
SQLModel.metadata.create_all(self.engine)
|
|
53
|
+
except OperationalError as exc:
|
|
54
|
+
if "already exists" not in str(exc):
|
|
55
|
+
raise
|
|
56
|
+
self._create_missing_columns()
|
|
57
|
+
self._dedup_active_leases()
|
|
58
|
+
self._create_missing_indexes()
|
|
59
|
+
|
|
60
|
+
def _dedup_active_leases(self):
|
|
61
|
+
# The partial unique index (ux_task_leases_active) can only be created if no task
|
|
62
|
+
# already has two ACTIVE leases. Older databases predate the constraint, so
|
|
63
|
+
# collapse any duplicates first — keep the newest active lease per task, mark the
|
|
64
|
+
# rest stale — making index creation succeed and restoring single-writer state.
|
|
65
|
+
inspector = inspect(self.engine)
|
|
66
|
+
if "task_leases" not in inspector.get_table_names():
|
|
67
|
+
return
|
|
68
|
+
with self.engine.begin() as conn:
|
|
69
|
+
rows = conn.execute(
|
|
70
|
+
text("SELECT id, task_id, created_at FROM task_leases WHERE status = 'active'")
|
|
71
|
+
).fetchall()
|
|
72
|
+
by_task = defaultdict(list)
|
|
73
|
+
for row in rows:
|
|
74
|
+
by_task[row.task_id].append(((row.created_at or ""), row.id))
|
|
75
|
+
stale_ids = []
|
|
76
|
+
for items in by_task.values():
|
|
77
|
+
if len(items) <= 1:
|
|
78
|
+
continue
|
|
79
|
+
items.sort() # ascending by (created_at, id); keep the last = newest
|
|
80
|
+
stale_ids.extend(item[1] for item in items[:-1])
|
|
81
|
+
if stale_ids:
|
|
82
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
83
|
+
for lease_id in stale_ids:
|
|
84
|
+
conn.execute(
|
|
85
|
+
text("UPDATE task_leases SET status = 'stale', released_at = :ts WHERE id = :id"),
|
|
86
|
+
{"ts": now, "id": lease_id},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def _create_missing_columns(self):
|
|
90
|
+
# create_all never alters an existing table, so columns added to a model
|
|
91
|
+
# later (e.g. the v4 gap routing columns) are missing on databases created by
|
|
92
|
+
# an older version — and a SELECT of the model would then fail. Add any
|
|
93
|
+
# missing column as a nullable ADD COLUMN (the only shape SQLite can add in
|
|
94
|
+
# place without a table rewrite); non-nullable additions are intentionally
|
|
95
|
+
# skipped because existing rows could not satisfy them.
|
|
96
|
+
inspector = inspect(self.engine)
|
|
97
|
+
existing_tables = set(inspector.get_table_names())
|
|
98
|
+
for table in SQLModel.metadata.sorted_tables:
|
|
99
|
+
if table.name not in existing_tables:
|
|
100
|
+
continue
|
|
101
|
+
existing_cols = {col["name"] for col in inspector.get_columns(table.name)}
|
|
102
|
+
for column in table.columns:
|
|
103
|
+
if column.name in existing_cols or not column.nullable:
|
|
104
|
+
continue
|
|
105
|
+
ddl_type = column.type.compile(self.engine.dialect)
|
|
106
|
+
stmt = text(f'ALTER TABLE "{table.name}" ADD COLUMN "{column.name}" {ddl_type}')
|
|
107
|
+
try:
|
|
108
|
+
with self.engine.begin() as conn:
|
|
109
|
+
conn.execute(stmt)
|
|
110
|
+
except OperationalError as exc:
|
|
111
|
+
if "duplicate column name" not in str(exc).lower():
|
|
112
|
+
raise
|
|
113
|
+
|
|
114
|
+
def _create_missing_indexes(self):
|
|
115
|
+
# create_all skips tables that already exist, so indexes added to the
|
|
116
|
+
# model definitions later never materialize on existing databases.
|
|
117
|
+
for table in SQLModel.metadata.sorted_tables:
|
|
118
|
+
for index in table.indexes:
|
|
119
|
+
try:
|
|
120
|
+
index.create(self.engine, checkfirst=True)
|
|
121
|
+
except OperationalError as exc:
|
|
122
|
+
if "already exists" not in str(exc):
|
|
123
|
+
raise
|
|
124
|
+
|
|
125
|
+
@contextmanager
|
|
126
|
+
def get_session(self):
|
|
127
|
+
"""Yield a session with automatic commit/rollback/close."""
|
|
128
|
+
session = Session(self.engine)
|
|
129
|
+
try:
|
|
130
|
+
yield session
|
|
131
|
+
session.commit()
|
|
132
|
+
except Exception:
|
|
133
|
+
session.rollback()
|
|
134
|
+
raise
|
|
135
|
+
finally:
|
|
136
|
+
session.close()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_db(project_root: Path = Path(".")) -> Optional[Database]:
|
|
140
|
+
dev_dir = project_root / ".devcouncil"
|
|
141
|
+
if not dev_dir.exists():
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
db_path = dev_dir / "state.sqlite"
|
|
145
|
+
db = Database(db_path)
|
|
146
|
+
db.ensure_schema_version()
|
|
147
|
+
return db
|
|
@@ -1,83 +1,204 @@
|
|
|
1
|
-
from sqlmodel import SQLModel, Field
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
from sqlmodel import SQLModel, Field
|
|
2
|
+
from sqlalchemy import Index, text
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SchemaVersionModel(SQLModel, table=True):
|
|
7
|
+
__tablename__ = "schema_version"
|
|
8
|
+
id: str = Field(primary_key=True, default="singleton")
|
|
9
|
+
version: int
|
|
10
|
+
|
|
11
|
+
class RequirementModel(SQLModel, table=True):
|
|
12
|
+
__tablename__ = "requirements"
|
|
13
|
+
id: str = Field(primary_key=True)
|
|
14
|
+
title: str
|
|
15
|
+
description: str
|
|
16
|
+
priority: str
|
|
17
|
+
source: str
|
|
18
|
+
# Store complex lists as JSON strings for simplicity in SQLite
|
|
19
|
+
acceptance_criteria_json: str = Field(default="[]")
|
|
20
|
+
|
|
21
|
+
class AssumptionModel(SQLModel, table=True):
|
|
22
|
+
__tablename__ = "assumptions"
|
|
23
|
+
id: str = Field(primary_key=True)
|
|
24
|
+
statement: str
|
|
25
|
+
confidence: str
|
|
26
|
+
impact: str
|
|
27
|
+
reversible: bool
|
|
28
|
+
requires_user_confirmation: bool
|
|
29
|
+
linked_requirement_ids_json: str = Field(default="[]")
|
|
30
|
+
status: str = "open"
|
|
31
|
+
|
|
32
|
+
class TaskModel(SQLModel, table=True):
|
|
33
|
+
__tablename__ = "tasks"
|
|
34
|
+
id: str = Field(primary_key=True)
|
|
35
|
+
title: str
|
|
36
|
+
description: str
|
|
37
|
+
requirement_ids_json: str = Field(default="[]")
|
|
38
|
+
acceptance_criterion_ids_json: str = Field(default="[]")
|
|
39
|
+
planned_files_json: str = Field(default="[]")
|
|
40
|
+
expected_tests_json: str = Field(default="[]")
|
|
41
|
+
allowed_commands_json: str = Field(default="[]")
|
|
42
|
+
forbidden_changes_json: str = Field(default="[]")
|
|
43
|
+
status: str = "planned"
|
|
44
|
+
|
|
45
|
+
class EvidenceModel(SQLModel, table=True):
|
|
46
|
+
__tablename__ = "evidence"
|
|
47
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
48
|
+
type: str # "command", "diff", "test"
|
|
49
|
+
task_id: Optional[str] = None
|
|
50
|
+
requirement_id: Optional[str] = None
|
|
51
|
+
acceptance_criterion_id: Optional[str] = None
|
|
52
|
+
data_json: str
|
|
53
|
+
|
|
54
|
+
class GapModel(SQLModel, table=True):
|
|
55
|
+
__tablename__ = "gaps"
|
|
56
|
+
id: str = Field(primary_key=True)
|
|
57
|
+
severity: str
|
|
58
|
+
gap_type: str
|
|
59
|
+
requirement_id: Optional[str] = None
|
|
60
|
+
task_id: Optional[str] = None
|
|
61
|
+
description: str
|
|
62
|
+
evidence_json: str = Field(default="[]")
|
|
63
|
+
recommended_fix: str
|
|
64
|
+
blocking: bool
|
|
65
|
+
# Machine-routable repair hints. Persisted (v4) so the typed next-actions
|
|
66
|
+
# contract survives a reload — a reconnecting/crashed agent can read outstanding
|
|
67
|
+
# gaps without the heuristic reconstruction the loader used to fall back to.
|
|
68
|
+
file: Optional[str] = None
|
|
69
|
+
line: Optional[int] = None
|
|
70
|
+
suggested_command: Optional[str] = None
|
|
71
|
+
acceptance_criterion_id: Optional[str] = None
|
|
72
|
+
|
|
73
|
+
class CritiqueFindingModel(SQLModel, table=True):
|
|
74
|
+
__tablename__ = "critique_findings"
|
|
75
|
+
id: str = Field(primary_key=True)
|
|
76
|
+
source_agent: str
|
|
77
|
+
target_plan_id: str
|
|
78
|
+
severity: str
|
|
79
|
+
finding_type: str
|
|
80
|
+
claim: str
|
|
81
|
+
linked_requirement_id: Optional[str] = None
|
|
82
|
+
suggested_requirement: Optional[str] = None
|
|
83
|
+
suggested_task: Optional[str] = None
|
|
84
|
+
falsifiable_check: str
|
|
85
|
+
status: str = "open"
|
|
86
|
+
|
|
87
|
+
class ProjectStateModel(SQLModel, table=True):
|
|
88
|
+
__tablename__ = "project_state"
|
|
89
|
+
id: str = Field(primary_key=True, default="singleton")
|
|
90
|
+
current_phase: str
|
|
91
|
+
history_json: str = Field(default="[]")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TaskLeaseModel(SQLModel, table=True):
|
|
95
|
+
__tablename__ = "task_leases"
|
|
96
|
+
# Enforce the single-writer guarantee at the DB level: at most one ACTIVE lease per
|
|
97
|
+
# task. A partial unique index turns a concurrent double-checkout race into an
|
|
98
|
+
# IntegrityError (surfaced as lease_conflict) instead of two live leases.
|
|
99
|
+
__table_args__ = (
|
|
100
|
+
Index(
|
|
101
|
+
"ux_task_leases_active",
|
|
102
|
+
"task_id",
|
|
103
|
+
unique=True,
|
|
104
|
+
sqlite_where=text("status = 'active'"),
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
id: str = Field(primary_key=True)
|
|
108
|
+
task_id: str = Field(index=True)
|
|
109
|
+
owner: str
|
|
110
|
+
agent: Optional[str] = None
|
|
111
|
+
client_id: Optional[str] = None
|
|
112
|
+
run_id: Optional[str] = None
|
|
113
|
+
branch: Optional[str] = None
|
|
114
|
+
lease_token: str
|
|
115
|
+
status: str = Field(default="active", index=True)
|
|
116
|
+
created_at: str
|
|
117
|
+
expires_at: Optional[str] = None
|
|
118
|
+
released_at: Optional[str] = None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class ShellSessionModel(SQLModel, table=True):
|
|
122
|
+
__tablename__ = "shell_sessions"
|
|
123
|
+
id: str = Field(primary_key=True)
|
|
124
|
+
task_id: str = Field(index=True)
|
|
125
|
+
lease_id: Optional[str] = None
|
|
126
|
+
shell: str
|
|
127
|
+
cwd: str
|
|
128
|
+
status: str
|
|
129
|
+
started_at: str
|
|
130
|
+
ended_at: Optional[str] = None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ShellCommandEventModel(SQLModel, table=True):
|
|
134
|
+
__tablename__ = "shell_command_events"
|
|
135
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
136
|
+
task_id: str = Field(index=True)
|
|
137
|
+
session_id: Optional[str] = None
|
|
138
|
+
command: str
|
|
139
|
+
status: str
|
|
140
|
+
exit_code: Optional[int] = None
|
|
141
|
+
reason: str = ""
|
|
142
|
+
stdout_path: str = ""
|
|
143
|
+
stderr_path: str = ""
|
|
144
|
+
created_at: str
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class FileChangeEventModel(SQLModel, table=True):
|
|
148
|
+
__tablename__ = "file_change_events"
|
|
149
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
150
|
+
task_id: Optional[str] = Field(default=None, index=True)
|
|
151
|
+
lease_id: Optional[str] = None
|
|
152
|
+
session_id: Optional[str] = None
|
|
153
|
+
path: str
|
|
154
|
+
operation: str
|
|
155
|
+
allowed: bool
|
|
156
|
+
reason: str = ""
|
|
157
|
+
created_at: str
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class SemanticDiffModel(SQLModel, table=True):
|
|
161
|
+
__tablename__ = "semantic_diffs"
|
|
162
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
163
|
+
task_id: str = Field(index=True)
|
|
164
|
+
before_snapshot_path: str
|
|
165
|
+
after_snapshot_path: str
|
|
166
|
+
classifications_json: str = "[]"
|
|
167
|
+
summary: str = ""
|
|
168
|
+
created_at: str
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class AgentHandoffModel(SQLModel, table=True):
|
|
172
|
+
__tablename__ = "agent_handoffs"
|
|
173
|
+
id: str = Field(primary_key=True)
|
|
174
|
+
task_id: str = Field(index=True)
|
|
175
|
+
from_agent: str
|
|
176
|
+
to_agent: str
|
|
177
|
+
run_id: str
|
|
178
|
+
manifest_path: str
|
|
179
|
+
status: str
|
|
180
|
+
created_at: str
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class CorrectionManifestModel(SQLModel, table=True):
|
|
184
|
+
__tablename__ = "correction_manifests"
|
|
185
|
+
id: str = Field(primary_key=True)
|
|
186
|
+
task_id: str = Field(index=True)
|
|
187
|
+
run_id: Optional[str] = None
|
|
188
|
+
manifest_path: str
|
|
189
|
+
retry_budget: int = 3
|
|
190
|
+
attempt: int = 0
|
|
191
|
+
status: str
|
|
192
|
+
created_at: str
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class VerificationRunModel(SQLModel, table=True):
|
|
196
|
+
__tablename__ = "verification_runs"
|
|
197
|
+
id: str = Field(primary_key=True)
|
|
198
|
+
task_id: str = Field(index=True)
|
|
199
|
+
sandbox: str
|
|
200
|
+
environment_json: str = "{}"
|
|
201
|
+
commands_json: str = "[]"
|
|
202
|
+
status: str
|
|
203
|
+
started_at: str
|
|
204
|
+
finished_at: Optional[str] = None
|