wydcode 0.4.3

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.
Files changed (41) hide show
  1. package/LICENSE +100 -0
  2. package/NOTICE +5 -0
  3. package/README.md +434 -0
  4. package/SECURITY.md +64 -0
  5. package/SOCRA_SOURCE.md +36 -0
  6. package/TRADEMARKS.md +12 -0
  7. package/bin/wydcode.js +74 -0
  8. package/examples/behavior-preference-card.json +12 -0
  9. package/examples/exact-recurrence-card.json +11 -0
  10. package/examples/heldout-gene-evidence.json +6 -0
  11. package/examples/humaneval-ornith.md +29 -0
  12. package/examples/large_context_json_diagnostic.py +187 -0
  13. package/fixtures/failing-node-repo/package.json +9 -0
  14. package/fixtures/failing-node-repo/src/math.js +3 -0
  15. package/fixtures/failing-node-repo/test/math.test.js +8 -0
  16. package/package.json +55 -0
  17. package/pyproject.toml +24 -0
  18. package/socra_harness/__init__.py +3 -0
  19. package/socra_harness/attempts.py +76 -0
  20. package/socra_harness/central_traces.py +627 -0
  21. package/socra_harness/cli.py +265 -0
  22. package/socra_harness/eval/__init__.py +1 -0
  23. package/socra_harness/eval/humaneval.py +388 -0
  24. package/socra_harness/events.py +39 -0
  25. package/socra_harness/init.py +206 -0
  26. package/socra_harness/job/__init__.py +2 -0
  27. package/socra_harness/job/events.py +132 -0
  28. package/socra_harness/job/lock.py +90 -0
  29. package/socra_harness/job/manager.py +3785 -0
  30. package/socra_harness/job/planner.py +253 -0
  31. package/socra_harness/job/state.py +106 -0
  32. package/socra_harness/mumpix_trace_bridge.cjs +32 -0
  33. package/socra_harness/product.py +533 -0
  34. package/socra_harness/recurrence.py +263 -0
  35. package/socra_harness/scan/__init__.py +1 -0
  36. package/socra_harness/scan/repo.py +85 -0
  37. package/socra_harness/serve/__init__.py +1 -0
  38. package/socra_harness/serve/local_proxy.py +293 -0
  39. package/socra_harness/start.py +331 -0
  40. package/socra_harness/tui/__init__.py +1 -0
  41. package/socra_harness/tui/app.py +1114 -0
@@ -0,0 +1,253 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from .events import JobEvent
9
+
10
+
11
+ def canonical_task_hash(tasks: list[dict[str, Any]]) -> str:
12
+ immutable_tasks = []
13
+ for task in tasks:
14
+ immutable = {
15
+ "id": task.get("id"),
16
+ "description": task.get("description"),
17
+ "acceptance": task.get("acceptance", []),
18
+ "risk_tier": task.get("risk_tier"),
19
+ "depends_on": task.get("depends_on", []),
20
+ "declared_files": task.get("declared_files", []),
21
+ }
22
+ if "recurrence" in task:
23
+ immutable["recurrence"] = task.get("recurrence")
24
+ if "retrieval_mode" in task:
25
+ immutable["retrieval_mode"] = task.get("retrieval_mode")
26
+ immutable_tasks.append(immutable)
27
+ payload = json.dumps(immutable_tasks, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
28
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
29
+
30
+
31
+ def task_event_payload(task: dict[str, Any]) -> dict[str, Any]:
32
+ required = {
33
+ "id",
34
+ "description",
35
+ "acceptance",
36
+ "risk_tier",
37
+ "status",
38
+ "depends_on",
39
+ "declared_files",
40
+ "attempt_count",
41
+ "produced_artifacts",
42
+ }
43
+ missing = required - set(task)
44
+ if missing:
45
+ raise ValueError(f"task missing required fields: {sorted(missing)}")
46
+ return dict(task)
47
+
48
+
49
+ def validate_task_graph(tasks: list[dict[str, Any]]) -> None:
50
+ ids: set[str] = set()
51
+ for task in tasks:
52
+ payload = task_event_payload(task)
53
+ task_id = str(payload.get("id"))
54
+ if task_id in ids:
55
+ raise ValueError(f"duplicate task id: {task_id}")
56
+ ids.add(task_id)
57
+ for task in tasks:
58
+ task_id = str(task.get("id"))
59
+ for dep in task.get("depends_on", []):
60
+ if str(dep) not in ids:
61
+ raise ValueError(f"task {task_id} depends on unknown task: {dep}")
62
+
63
+
64
+ def materialize_plan_from_events(events: list[JobEvent], fallback_plan: dict[str, Any] | None = None) -> dict[str, Any]:
65
+ goal = ""
66
+ tasks: list[dict[str, Any]] = []
67
+ committed: dict[str, Any] | None = None
68
+ revision = 0
69
+ by_id: dict[str, dict[str, Any]] = {}
70
+ task_statuses: dict[str, str] = {}
71
+ task_attempts: dict[str, int] = {}
72
+ task_artifacts: dict[str, list[dict[str, Any]]] = {}
73
+
74
+ for event in events:
75
+ if event.type == "job_created":
76
+ goal = str(event.payload.get("goal") or goal)
77
+ elif event.type == "plan_revised":
78
+ tasks = []
79
+ by_id = {}
80
+ committed = None
81
+ revision += 1
82
+ elif event.type == "task_added":
83
+ task = task_event_payload(event.payload)
84
+ task_id = str(task["id"])
85
+ if task_id in task_statuses:
86
+ task["status"] = task_statuses[task_id]
87
+ if task_id in task_attempts:
88
+ task["attempt_count"] = task_attempts[task_id]
89
+ if task_id in task_artifacts:
90
+ task["produced_artifacts"] = task_artifacts[task_id]
91
+ tasks.append(task)
92
+ by_id[task_id] = task
93
+ elif event.type == "plan_committed":
94
+ committed = dict(event.payload)
95
+ revision = int(committed.get("revision") or revision or 1)
96
+ elif event.type == "task_started":
97
+ task_id = str(event.payload.get("task_id") or "")
98
+ task_attempts[task_id] = task_attempts.get(task_id, 0) + 1
99
+ task = by_id.get(task_id)
100
+ if task:
101
+ task["status"] = "running"
102
+ task["attempt_count"] = task_attempts[task_id]
103
+ elif event.type == "task_artifacts":
104
+ task_id = str(event.payload.get("task_id") or "")
105
+ task_artifacts[task_id] = list(event.payload.get("artifacts") or [])
106
+ task = by_id.get(task_id)
107
+ if task:
108
+ task["produced_artifacts"] = task_artifacts[task_id]
109
+ elif event.type == "task_passed":
110
+ task_id = str(event.payload.get("task_id") or "")
111
+ task_statuses[task_id] = "passed"
112
+ task = by_id.get(task_id)
113
+ if task:
114
+ task["status"] = "passed"
115
+ elif event.type in {"task_failed", "declared_file_violation"}:
116
+ task_id = str(event.payload.get("task_id") or "")
117
+ task_statuses[task_id] = "failed"
118
+ task = by_id.get(task_id)
119
+ if task:
120
+ task["status"] = "failed"
121
+ elif event.type == "task_retry_scheduled":
122
+ task_id = str(event.payload.get("task_id") or "")
123
+ task_statuses[task_id] = "pending"
124
+ task = by_id.get(task_id)
125
+ if task:
126
+ task["status"] = "pending"
127
+ elif event.type == "task_blocked":
128
+ task_id = str(event.payload.get("task_id") or "")
129
+ task_statuses[task_id] = "blocked"
130
+ task = by_id.get(task_id)
131
+ if task:
132
+ task["status"] = "blocked"
133
+
134
+ if not tasks:
135
+ return fallback_plan or {"version": 1, "goal": goal, "tasks": []}
136
+
137
+ plan = {
138
+ "version": 1,
139
+ "goal": goal,
140
+ "revision": revision or 1,
141
+ "source": "task_added_events",
142
+ "tasks": tasks,
143
+ "task_count": len(tasks),
144
+ "task_hash": canonical_task_hash(tasks),
145
+ }
146
+ if committed:
147
+ plan["committed"] = committed
148
+ return plan
149
+
150
+
151
+ def ready_task_ids(plan: dict[str, Any]) -> list[str]:
152
+ tasks = plan.get("tasks") if isinstance(plan.get("tasks"), list) else []
153
+ by_id = {str(task.get("id")): task for task in tasks}
154
+ passed = {task_id for task_id, task in by_id.items() if task.get("status") in {"passed", "skipped"}}
155
+ ready: list[str] = []
156
+ for task in tasks:
157
+ if task.get("status") != "pending":
158
+ continue
159
+ deps = [str(dep) for dep in task.get("depends_on", [])]
160
+ if all(dep in passed for dep in deps):
161
+ ready.append(str(task.get("id")))
162
+ return ready
163
+
164
+
165
+ def mumpix_dashboard_tasks() -> list[dict[str, Any]]:
166
+ def task(
167
+ idx: int,
168
+ description: str,
169
+ acceptance: list[str],
170
+ risk: int,
171
+ files: list[str],
172
+ deps: list[str] | None = None,
173
+ ) -> dict[str, Any]:
174
+ return {
175
+ "id": f"task-{idx:03d}",
176
+ "description": description,
177
+ "acceptance": acceptance,
178
+ "risk_tier": risk,
179
+ "status": "pending",
180
+ "depends_on": deps or [],
181
+ "declared_files": files,
182
+ "attempt_count": 0,
183
+ "produced_artifacts": [],
184
+ }
185
+
186
+ return [
187
+ task(1, "Inventory existing Mumpix frontend routes, APIs, and data files.", ["Inventory report lists usable API endpoints", "Declared dashboard data sources are named"], 0, ["App.jsx", "server.js", "mumpixdb-core.js", "package.json"]),
188
+ task(2, "Define dashboard navigation and information architecture.", ["Dashboard sections cover WAL, genes/routes, benchmarks, and status", "Navigation labels are fixed before UI work"], 0, ["App.jsx"], ["task-001"]),
189
+ task(3, "Define local admin API contract and artifact discovery plan.", ["Contract includes WAL/events, gene/route, benchmark, and health payloads", "Plan lists local globs, excludes large/vendor paths, and avoids hosted dependencies"], 0, ["server.js"], ["task-001"]),
190
+ task(4, "Document dashboard milestone gates and preview command.", ["Runner command is identified", "Milestone acceptance checks are written"], 0, ["README.md", "package.json"], ["task-001"]),
191
+ task(5, "Add server utility for safe local artifact reads.", ["Malformed files return structured errors", "Reads stay inside local project/data roots"], 2, ["server.js"], ["task-003"]),
192
+ task(6, "Add WAL/event artifact adapter.", ["Adapter reads .wal/.ndjson event files", "Payload normalizes timestamp, key, type, and summary fields"], 2, ["server.js"], ["task-005"]),
193
+ task(7, "Add gene and route artifact adapter.", ["Adapter finds likely gene/route files", "Payload includes id, family, status, and source path"], 2, ["server.js"], ["task-005"]),
194
+ task(
195
+ 8,
196
+ "Create standalone safe artifact reader module.",
197
+ [
198
+ "server/admin/artifactReaders.js provides safe local artifact read helpers",
199
+ "Existing safe-read behavior from task 005 is retained",
200
+ ],
201
+ 2,
202
+ ["server/admin/artifactReaders.js"],
203
+ ["task-007"],
204
+ ),
205
+ task(36, "Extract WAL/event artifact adapter into a module.", ["Adapter reads .wal/.ndjson event files", "Payload normalizes timestamp, key, type, and summary fields"], 2, ["server/admin/walAdapter.js", "server/admin/artifactReaders.js"], ["task-008"]),
206
+ task(37, "Extract gene and route artifact adapter into a module.", ["Adapter finds likely gene/route files", "Payload includes id, family, status, and source path"], 2, ["server/admin/geneRouteAdapter.js", "server/admin/artifactReaders.js"], ["task-008"]),
207
+ task(9, "Add benchmark/result artifact adapter.", ["Adapter finds benchmark JSON/markdown summaries", "Payload includes name, score, date, and source path"], 2, ["server/admin/benchmarkAdapter.js", "server/admin/artifactReaders.js"], ["task-008"]),
208
+ task(10, "Expose admin health endpoint.", ["Endpoint reports local-only mode and artifact counts", "Endpoint has deterministic empty-state payload"], 2, ["server.js", "server/admin/artifactReaders.js"], ["task-008"]),
209
+ task(11, "Expose admin WAL/events endpoint.", ["Endpoint returns paginated normalized events", "Endpoint handles missing data without crashing"], 2, ["server.js", "server/admin/walAdapter.js"], ["task-036"]),
210
+ task(12, "Expose admin genes/routes endpoint.", ["Endpoint returns normalized gene/route rows", "Endpoint exposes source file path for inspection"], 2, ["server.js", "server/admin/geneRouteAdapter.js"], ["task-037"]),
211
+ task(35, "Expose admin benchmark/results endpoint.", ["Endpoint returns normalized benchmark cards", "Endpoint handles mixed JSON/markdown inputs"], 2, ["server.js", "server/admin/benchmarkAdapter.js"], ["task-009"]),
212
+ task(13, "Create dashboard data client helpers.", ["Frontend helpers fetch admin endpoints", "Errors are represented as UI-friendly objects"], 2, ["App.jsx"], ["task-010", "task-011", "task-012", "task-035"]),
213
+ task(14, "Create App.jsx dashboard decomposition boundary.", ["Dashboard additions have a named boundary inside or beside the existing monolith", "Future UI tasks do not require understanding the whole 1371-line file at once"], 2, ["App.jsx"], ["task-002", "task-013"]),
214
+ task(15, "Create dashboard shell layout.", ["Shell has header, nav, and main content region", "Existing manual memory UI remains reachable or is intentionally replaced"], 2, ["App.jsx", "styles.css"], ["task-014"]),
215
+ task(16, "Add overview cards for local health and artifact counts.", ["Cards show WAL, gene/route, benchmark, and error counts", "Empty state is visible"], 2, ["App.jsx"], ["task-015"]),
216
+ task(17, "Add WAL/event timeline panel.", ["Timeline lists recent events with readable summaries", "Filtering by type/source exists"], 2, ["App.jsx", "styles.css"], ["task-010", "task-015"]),
217
+ task(18, "Add gene inspector panel.", ["Inspector lists genes/routes and key metadata", "Selecting a row shows detail"], 2, ["App.jsx", "styles.css"], ["task-011", "task-015"]),
218
+ task(19, "Add route-fire timeline panel.", ["Panel visualizes route/gene activation-like rows where available", "No data state explains what is missing"], 2, ["App.jsx", "styles.css"], ["task-011", "task-015"]),
219
+ task(20, "Add benchmark viewer panel.", ["Viewer shows benchmark cards and detail", "Scores and source paths are visible"], 2, ["App.jsx", "styles.css"], ["task-035", "task-015"]),
220
+ task(21, "Add local file/source detail drawer.", ["Drawer shows selected artifact metadata", "Dangerous raw rendering is escaped"], 2, ["App.jsx", "styles.css"], ["task-017", "task-018", "task-020"]),
221
+ task(22, "Add loading and error state components.", ["All admin panels have loading and error states", "Retry action is available"], 2, ["App.jsx", "styles.css"], ["task-015"]),
222
+ task(23, "Add lightweight chart primitives and overview metric bars without new dependency.", ["Bar/timeline visuals render from CSS/React", "Overview values match fetched artifact counts"], 2, ["App.jsx", "styles.css"], ["task-016"]),
223
+ task(24, "Wire chart primitives into benchmark viewer.", ["Benchmarks display compact score visuals", "Missing scores do not break layout"], 2, ["App.jsx", "styles.css"], ["task-020", "task-023"]),
224
+ task(25, "Add live-tail polling for admin data.", ["Refresh interval can be started/stopped", "Polling does not duplicate rows"], 2, ["App.jsx"], ["task-017", "task-018", "task-020"]),
225
+ task(26, "Add WebSocket live-tail integration if existing server broadcast supports it.", ["Uses existing WebSocket server only", "Falls back to polling when unavailable"], 3, ["server.js", "App.jsx"], ["task-025"]),
226
+ task(27, "Add dashboard search across events, genes, routes, and benchmarks.", ["Search filters all visible panels", "Search handles empty query and special characters"], 2, ["App.jsx"], ["task-017", "task-018", "task-020"]),
227
+ task(28, "Run frontend syntax and build gate.", ["Runner records syntax/build notes", "Build artifacts are not treated as source truth"], 3, ["package.json", "vite.config.js"], ["task-015", "task-022"]),
228
+ task(29, "Run backend API smoke checks in sandbox.", ["Health endpoint responds", "Admin endpoints return JSON"], 3, ["server.js"], ["task-010", "task-011", "task-012", "task-035"]),
229
+ task(30, "Preview dashboard and record smoke evidence.", ["Preview page loads", "At least three dashboard sections render"], 3, ["App.jsx", "styles.css"], ["task-028", "task-029"]),
230
+ task(31, "Write dashboard usage docs.", ["README explains running dashboard", "Docs mention local-only artifact inspection"], 2, ["README.md"], ["task-030"]),
231
+ task(32, "Prepare atomic promotion patch bundle.", ["Patch bundle lists all changed files", "Rollback manifest exists"], 5, [], ["task-030", "task-031"]),
232
+ task(33, "Run final promotion gate.", ["Build and smoke checks pass after promotion candidate", "No hosted calls are required"], 5, ["package.json"], ["task-032"]),
233
+ task(34, "Write stress-test outcome report.", ["Report compares actual task count and interventions to prereg", "Report states allowed and blocked claims"], 2, ["README.md"], ["task-033"]),
234
+ ]
235
+
236
+
237
+ def planner_tasks_for_goal(goal: str, project: Path) -> list[dict[str, Any]]:
238
+ text = f"{goal} {project}".lower()
239
+ if "mumpix" in text and "dashboard" in text:
240
+ return mumpix_dashboard_tasks()
241
+ return [
242
+ {
243
+ "id": "task-001",
244
+ "description": "Inventory the project and produce a concrete task DAG.",
245
+ "acceptance": ["Project files are inventoried", "Next task graph can be generated from evidence"],
246
+ "risk_tier": 0,
247
+ "status": "pending",
248
+ "depends_on": [],
249
+ "declared_files": [],
250
+ "attempt_count": 0,
251
+ "produced_artifacts": [],
252
+ }
253
+ ]
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from .events import JobEvent, read_events
9
+
10
+
11
+ @dataclasses.dataclass
12
+ class JobState:
13
+ job_id: str
14
+ goal: str
15
+ phase: str
16
+ project: str
17
+ active_task_id: str | None
18
+ completed_tasks: list[str]
19
+ blocked_tasks: list[str]
20
+ last_error: str | None
21
+ creator_config: dict[str, Any] | None
22
+ last_seq: int
23
+
24
+
25
+ def initial_state() -> JobState:
26
+ return JobState(
27
+ job_id="",
28
+ goal="",
29
+ phase="created",
30
+ project="",
31
+ active_task_id=None,
32
+ completed_tasks=[],
33
+ blocked_tasks=[],
34
+ last_error=None,
35
+ creator_config=None,
36
+ last_seq=0,
37
+ )
38
+
39
+
40
+ def apply_event(state: JobState, event: JobEvent) -> JobState:
41
+ state.last_seq = event.seq
42
+ state.job_id = event.job_id
43
+ payload = event.payload
44
+ if event.type == "job_created":
45
+ state.goal = str(payload.get("goal", state.goal))
46
+ state.project = str(payload.get("project", state.project))
47
+ state.phase = "planning"
48
+ elif event.type == "plan_written":
49
+ state.phase = "paused"
50
+ elif event.type == "phase_changed":
51
+ state.phase = str(payload.get("phase", state.phase))
52
+ elif event.type == "task_started":
53
+ state.phase = "executing"
54
+ state.active_task_id = str(payload.get("task_id") or "")
55
+ elif event.type == "creator_configured":
56
+ state.creator_config = dict(payload)
57
+ elif event.type == "task_passed":
58
+ task_id = str(payload.get("task_id") or "")
59
+ if task_id and task_id not in state.completed_tasks:
60
+ state.completed_tasks.append(task_id)
61
+ if state.active_task_id == task_id:
62
+ state.active_task_id = None
63
+ state.phase = "paused"
64
+ elif event.type == "task_failed":
65
+ task_id = str(payload.get("task_id") or "")
66
+ if state.active_task_id == task_id:
67
+ state.active_task_id = None
68
+ state.last_error = str(payload.get("error") or "")
69
+ state.phase = "paused"
70
+ elif event.type == "task_retry_scheduled":
71
+ state.phase = "paused"
72
+ state.active_task_id = None
73
+ elif event.type == "task_blocked":
74
+ task_id = str(payload.get("task_id") or "")
75
+ if task_id and task_id not in state.blocked_tasks:
76
+ state.blocked_tasks.append(task_id)
77
+ state.last_error = str(payload.get("error") or "")
78
+ state.phase = "blocked"
79
+ elif event.type == "job_paused":
80
+ state.phase = "paused"
81
+ elif event.type == "job_completed":
82
+ state.phase = "done"
83
+ state.active_task_id = None
84
+ elif event.type == "job_failed":
85
+ state.phase = "failed"
86
+ state.last_error = str(payload.get("error") or "")
87
+ return state
88
+
89
+
90
+ def replay(events: list[JobEvent]) -> JobState:
91
+ state = initial_state()
92
+ for event in events:
93
+ apply_event(state, event)
94
+ return state
95
+
96
+
97
+ def replay_path(path: Path) -> JobState:
98
+ return replay(read_events(path))
99
+
100
+
101
+ def state_to_json(state: JobState) -> dict[str, Any]:
102
+ return dataclasses.asdict(state)
103
+
104
+
105
+ def write_state_snapshot(path: Path, state: JobState) -> None:
106
+ path.write_text(json.dumps(state_to_json(state), indent=2) + "\n", encoding="utf-8")
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { Mumpix } = require("mumpix");
6
+
7
+ async function main() {
8
+ const dbPath = process.argv[2];
9
+ if (!dbPath) throw new Error("mumpix trace bridge requires a database path");
10
+ process.env.MUMPIX_LICENSE_WATERMARK_PATH ||= path.join(path.dirname(dbPath), ".mumpix-license-watermark.json");
11
+ const input = fs.readFileSync(0, "utf8");
12
+ const event = JSON.parse(input);
13
+ const payload = event.payload || {};
14
+ const project = payload.project || {};
15
+ const db = await Mumpix.open(dbPath, { consistency: "eventual", telemetry: false });
16
+ try {
17
+ const result = await db.remember(JSON.stringify(event), {
18
+ workspace: "wydcode-cross-project-traces",
19
+ repo: project.repository_name || project.project_scope_id || "unknown",
20
+ source: "wydcode:validated-fix-trace",
21
+ ts: Date.parse(event.timestamp) || Date.now(),
22
+ });
23
+ process.stdout.write(JSON.stringify({ ok: true, record_id: String(result.id), consistency: result.consistency }));
24
+ } finally {
25
+ await db.close();
26
+ }
27
+ }
28
+
29
+ main().catch((error) => {
30
+ process.stderr.write(String(error && error.stack ? error.stack : error));
31
+ process.exitCode = 1;
32
+ });