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
package/bin/wydcode.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { spawnSync } = require('node:child_process');
5
+ const path = require('node:path');
6
+
7
+ const packageRoot = path.resolve(__dirname, '..');
8
+ const cliName = path.basename(process.argv[1] || '').includes('wydcode') ? 'wydcode' : 'socra-harness';
9
+ const candidates = [
10
+ process.env.SOCRA_PYTHON,
11
+ 'python3.12',
12
+ 'python3.11',
13
+ 'python3',
14
+ ].filter(Boolean);
15
+
16
+ function run(candidate, args, options = {}) {
17
+ return spawnSync(candidate, args, {
18
+ cwd: process.cwd(),
19
+ env: {
20
+ ...process.env,
21
+ SOCRA_CLI_NAME: process.env.SOCRA_CLI_NAME || cliName,
22
+ SOCRA_CLI_NAME: 'wydcode',
23
+ PYTHONPATH: [packageRoot, process.env.PYTHONPATH].filter(Boolean).join(path.delimiter),
24
+ },
25
+ encoding: 'utf8',
26
+ ...options,
27
+ });
28
+ }
29
+
30
+ let selected = null;
31
+ let lastError = '';
32
+
33
+ for (const candidate of candidates) {
34
+ const probe = run(candidate, [
35
+ '-c',
36
+ 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 42)',
37
+ ]);
38
+ if (probe.status === 0) {
39
+ selected = candidate;
40
+ break;
41
+ }
42
+ if (probe.error) {
43
+ lastError = String(probe.error.message || probe.error);
44
+ } else if (probe.status === 42) {
45
+ lastError = `${candidate} is older than Python 3.11`;
46
+ } else if (probe.stderr) {
47
+ lastError = probe.stderr.trim();
48
+ }
49
+ }
50
+
51
+ if (!selected) {
52
+ console.error(`${cliName} requires Python 3.11+.`);
53
+ console.error('Install Python 3.11+ or set SOCRA_PYTHON=/path/to/python.');
54
+ if (lastError) {
55
+ console.error(`Last probe: ${lastError}`);
56
+ }
57
+ process.exit(1);
58
+ }
59
+
60
+ const result = run(selected, ['-m', 'socra_harness.cli', ...process.argv.slice(2)], {
61
+ stdio: 'inherit',
62
+ encoding: undefined,
63
+ });
64
+
65
+ if (result.error) {
66
+ console.error(`Failed to run ${cliName} with ${selected}: ${result.error.message}`);
67
+ process.exit(1);
68
+ }
69
+
70
+ if (result.signal) {
71
+ process.kill(process.pid, result.signal);
72
+ }
73
+
74
+ process.exit(typeof result.status === 'number' ? result.status : 1);
@@ -0,0 +1,12 @@
1
+ {
2
+ "approach": "Preserve the arithmetic contract while correcting the failing branch.",
3
+ "invariants_to_preserve": [
4
+ "Inputs remain immutable and ordinary numeric addition remains unchanged."
5
+ ],
6
+ "checks_to_run": [
7
+ "Exercise positive, negative, and zero operands through the public API."
8
+ ],
9
+ "known_nonmatches": [
10
+ "Do not apply this behavior to multiplication or string concatenation failures."
11
+ ]
12
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "card_id": "math-add-terminal-assertion-v1",
3
+ "repository_identity": "github.com/mumpix/wydcode",
4
+ "owning_path": "src/math.js",
5
+ "owning_symbol_or_api": "add",
6
+ "invariant": {
7
+ "error_or_assertion_class": "AssertionError",
8
+ "operation": "add two numeric operands",
9
+ "expected_relation": "result equals the arithmetic sum"
10
+ }
11
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "status": "passed",
3
+ "probe_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
4
+ "hidden_feedback_used": false,
5
+ "description": "Replace probe_sha256 with the SHA-256 of the real held-out result artifact."
6
+ }
@@ -0,0 +1,29 @@
1
+ # Example: Ornith HumanEval with wydcode
2
+
3
+ ```bash
4
+ MODEL_PATH="$HOME/models/ornith-1.0-35b-Q6_K.gguf"
5
+ LLAMA_CLI="$HOME/src/llama.cpp/build/bin/llama-cli"
6
+ TASKS_PATH="$HOME/benchmarks/humaneval-openai-full-164.jsonl"
7
+
8
+ wydcode eval humaneval \
9
+ --name ornith \
10
+ --model "$MODEL_PATH" \
11
+ --llama-cli "$LLAMA_CLI" \
12
+ --tasks "$TASKS_PATH" \
13
+ --mode socra \
14
+ --gpu-layers 999 \
15
+ --no-cpu-fallback
16
+ ```
17
+
18
+ Known local result from the corrected harness:
19
+
20
+ - `145/164`
21
+ - pass@1 `0.8841`
22
+ - GPU generations `164/164`
23
+ - CPU fallback `0/164`
24
+
25
+ Open the terminal UI in another shell:
26
+
27
+ ```bash
28
+ wydcode tui --state-dir .socra
29
+ ```
@@ -0,0 +1,187 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from socra_harness.job.events import JobEventLog, read_events
10
+ from socra_harness.job.lock import JobLock
11
+ from socra_harness.job.manager import (
12
+ CREATOR_PROMPT_LAYOUT,
13
+ build_creator_messages,
14
+ call_creator_model,
15
+ declared_file_set,
16
+ endpoint_health,
17
+ latest_passed_artifacts_by_path,
18
+ load_plan,
19
+ parse_creator_json,
20
+ read_declared_file_context,
21
+ sha256_file,
22
+ sha256_text,
23
+ validate_creator_declared_files,
24
+ )
25
+ from socra_harness.job.planner import materialize_plan_from_events
26
+ from socra_harness.job.state import replay
27
+
28
+
29
+ def elapsed_ms(start: float) -> int:
30
+ return int(round((time.monotonic() - start) * 1000))
31
+
32
+
33
+ def write_json(path: Path, payload: dict[str, Any]) -> None:
34
+ path.parent.mkdir(parents=True, exist_ok=True)
35
+ path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
36
+
37
+
38
+ def main() -> int:
39
+ parser = argparse.ArgumentParser(description="Run a large-context JSON-contract diagnostic against a Socra job.")
40
+ parser.add_argument("--project", required=True, type=Path)
41
+ parser.add_argument("--job-id", required=True)
42
+ parser.add_argument("--diagnostic-id", default="large-context-json-v1")
43
+ parser.add_argument(
44
+ "--declared-file",
45
+ action="append",
46
+ dest="declared_files",
47
+ default=[],
48
+ help="Relative file to include. Repeatable. Defaults to the server decomposition stress shape.",
49
+ )
50
+ args = parser.parse_args()
51
+
52
+ project = args.project.expanduser().resolve()
53
+ job_dir = project / ".socra" / "jobs" / args.job_id
54
+ declared_files = args.declared_files or [
55
+ "server.js",
56
+ "server/admin/artifactReaders.js",
57
+ "server/admin/walAdapter.js",
58
+ "server/admin/geneRouteAdapter.js",
59
+ ]
60
+
61
+ with JobLock(job_dir / ".lock"):
62
+ events = read_events(job_dir / "events.jsonl")
63
+ state = replay(events)
64
+ plan = materialize_plan_from_events(events, load_plan(job_dir))
65
+ creator = state.creator_config or {}
66
+ log = JobEventLog(job_dir / "events.jsonl", state.job_id or args.job_id)
67
+
68
+ health = endpoint_health(str(creator.get("endpoint") or "http://127.0.0.1:11439/v1"))
69
+ expected_model = str(creator.get("expected_served_model_id") or Path(str(creator.get("model") or "")).name)
70
+ if expected_model:
71
+ served_ids = [str(item) for item in health.get("model_ids") or []]
72
+ health["expected_served_model_id"] = expected_model
73
+ health["model_identity_ok"] = expected_model in served_ids
74
+ health["served_model_ids"] = served_ids
75
+
76
+ task = {
77
+ "id": args.diagnostic_id,
78
+ "description": (
79
+ "Diagnostic only: test whether the current Creator configuration can return valid JSON "
80
+ "for a large multi-file context. Preserve existing behavior and return full file contents."
81
+ ),
82
+ "acceptance": [
83
+ "response is valid Creator JSON",
84
+ "all returned paths stay within declared files",
85
+ "finish_reason indicates a complete response",
86
+ ],
87
+ "risk_tier": 0,
88
+ "declared_files": declared_files,
89
+ }
90
+
91
+ effective_sources = latest_passed_artifacts_by_path(job_dir, plan)
92
+ file_context = read_declared_file_context(project, task, effective_sources)
93
+ context_sources = [
94
+ {
95
+ "path": item.get("path"),
96
+ "source": item.get("source"),
97
+ "source_task_id": item.get("source_task_id"),
98
+ "sandbox_path": item.get("sandbox_path"),
99
+ "sha256": item.get("sha256"),
100
+ "exists": item.get("exists"),
101
+ "content_chars": len(str(item.get("content") or "")),
102
+ }
103
+ for item in file_context
104
+ ]
105
+
106
+ artifact_dir = job_dir / "artifacts" / "diagnostics" / args.diagnostic_id
107
+ artifact_dir.mkdir(parents=True, exist_ok=True)
108
+ messages = build_creator_messages(job_dir, project, task, file_context, retry_feedback=[])
109
+ request_path = artifact_dir / "creator-request.json"
110
+ write_json(request_path, {"messages": messages})
111
+
112
+ start_event = log.append(
113
+ "large_context_json_diagnostic_started",
114
+ {
115
+ "diagnostic_id": args.diagnostic_id,
116
+ "declared_files": declared_files,
117
+ "context_sources": context_sources,
118
+ "prompt_layout": CREATOR_PROMPT_LAYOUT,
119
+ "request_path": str(request_path.relative_to(job_dir)),
120
+ "request_sha256": sha256_file(request_path),
121
+ "endpoint_health": health,
122
+ },
123
+ )
124
+
125
+ response: dict[str, Any] | None = None
126
+ parse_ok = False
127
+ violations: list[str] = []
128
+ parse_error = ""
129
+ creator_call_start = time.monotonic()
130
+ try:
131
+ if not health.get("ok") or not health.get("model_identity_ok"):
132
+ raise RuntimeError("diagnostic_endpoint_unavailable_or_model_mismatch")
133
+ response = call_creator_model(creator, messages, json_mode=True)
134
+ payload = parse_creator_json(str(response.get("content") or ""))
135
+ violations = validate_creator_declared_files(payload, declared_file_set(task))
136
+ parse_ok = not violations
137
+ except Exception as exc: # noqa: BLE001 - diagnostic records failures instead of raising.
138
+ parse_error = str(exc)
139
+ creator_call_ms = elapsed_ms(creator_call_start)
140
+
141
+ response_path = artifact_dir / "creator-response.json"
142
+ write_json(response_path, response or {"error": parse_error})
143
+
144
+ finish_reason = response.get("finish_reason") if response else None
145
+ if parse_ok and finish_reason == "stop":
146
+ verdict = "pass_large_context_json_current_config"
147
+ elif finish_reason == "length":
148
+ verdict = "fail_probable_truncation"
149
+ elif finish_reason == "stop":
150
+ verdict = "fail_large_context_json_contract_despite_complete_response"
151
+ else:
152
+ verdict = "fail_incomplete_or_unclassified_response"
153
+
154
+ result = {
155
+ "diagnostic_id": args.diagnostic_id,
156
+ "started_event_seq": start_event.seq,
157
+ "declared_files": declared_files,
158
+ "context_sources": context_sources,
159
+ "prompt_layout": CREATOR_PROMPT_LAYOUT,
160
+ "request_path": str(request_path.relative_to(job_dir)),
161
+ "request_sha256": sha256_file(request_path),
162
+ "response_path": str(response_path.relative_to(job_dir)),
163
+ "response_sha256": sha256_file(response_path),
164
+ "creator_call_ms": creator_call_ms,
165
+ "request_model": response.get("request_model") if response else None,
166
+ "response_model": response.get("response_model") if response else None,
167
+ "content_sha256": sha256_text(str(response.get("content") or "")) if response else None,
168
+ "content_chars": len(str(response.get("content") or "")) if response else 0,
169
+ "reasoning_chars": len(str(response.get("reasoning_content") or "")) if response else 0,
170
+ "finish_reason": finish_reason,
171
+ "usage": response.get("usage") if response and isinstance(response.get("usage"), dict) else {},
172
+ "parse_ok": parse_ok,
173
+ "declared_file_violations": violations,
174
+ "parse_error": parse_error,
175
+ "verdict": verdict,
176
+ }
177
+ result_path = artifact_dir / "result.json"
178
+ write_json(result_path, result)
179
+ log.append("large_context_json_diagnostic_completed", {**result, "result_path": str(result_path.relative_to(job_dir)), "result_sha256": sha256_file(result_path)})
180
+
181
+ print(json.dumps(result, indent=2))
182
+ return 0 if parse_ok else 2
183
+
184
+
185
+ if __name__ == "__main__":
186
+ raise SystemExit(main())
187
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "socra-failing-node-fixture",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "node --test"
8
+ }
9
+ }
@@ -0,0 +1,3 @@
1
+ export function add(a, b) {
2
+ return a - b;
3
+ }
@@ -0,0 +1,8 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { add } from "../src/math.js";
5
+
6
+ test("add returns the arithmetic sum", () => {
7
+ assert.equal(add(2, 3), 5);
8
+ });
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "wydcode",
3
+ "version": "0.4.3",
4
+ "description": "Local-first long-running bug fixer: staged AI patches behind hash-bound human review.",
5
+ "license": "BUSL-1.1",
6
+ "author": "Mumpix",
7
+ "type": "commonjs",
8
+ "bin": {
9
+ "wydcode": "bin/wydcode.js",
10
+ "socra-harness": "bin/wydcode.js"
11
+ },
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "files": [
16
+ "bin/wydcode.js",
17
+ "SOCRA_SOURCE.md",
18
+ "examples/*.json",
19
+ "examples/*.md",
20
+ "examples/*.py",
21
+ "fixtures",
22
+ "socra_harness/**/*.py",
23
+ "socra_harness/**/*.cjs",
24
+ "README.md",
25
+ "SECURITY.md",
26
+ "NOTICE",
27
+ "TRADEMARKS.md",
28
+ "pyproject.toml",
29
+ "LICENSE"
30
+ ],
31
+ "keywords": [
32
+ "coding-agent",
33
+ "bug-fixer",
34
+ "local-first",
35
+ "code-review",
36
+ "ai",
37
+ "automation",
38
+ "socra",
39
+ "mumpix"
40
+ ],
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/mumpix/wydcode.git"
44
+ },
45
+ "homepage": "https://github.com/mumpix/wydcode",
46
+ "bugs": {
47
+ "url": "https://github.com/mumpix/wydcode/issues"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "dependencies": {
53
+ "mumpix": "^1.1.3"
54
+ }
55
+ }
package/pyproject.toml ADDED
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "wydcode"
7
+ version = "0.4.3"
8
+ description = "Local-first long-running bug fixer with hash-bound human review."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ authors = [{name = "Mumpix"}]
12
+ license = {text = "BUSL-1.1"}
13
+ dependencies = []
14
+
15
+ [project.scripts]
16
+ wydcode = "socra_harness.cli:main"
17
+ socra-harness = "socra_harness.cli:main"
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["."]
21
+ include = ["socra_harness*"]
22
+
23
+ [tool.setuptools.package-data]
24
+ socra_harness = ["*.cjs"]
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.4.3"
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Any
5
+
6
+ from .job.events import JobEvent
7
+
8
+
9
+ class AttemptFailure(str, Enum):
10
+ LENGTH_DEATH = "length_death"
11
+ MALFORMED_OUTPUT = "malformed_output"
12
+ GATE_FAILED = "gate_failed"
13
+ ENDPOINT_ERROR = "endpoint_error"
14
+ INTERRUPTED = "interrupted"
15
+
16
+
17
+ IN_PROGRESS_EVENTS = {
18
+ "task_started",
19
+ "creator_request_written",
20
+ "creator_call_started",
21
+ "creator_output_received",
22
+ "patch_written",
23
+ "gate_started",
24
+ }
25
+
26
+ TERMINAL_EVENTS = {
27
+ "task_passed",
28
+ "task_failed",
29
+ "task_blocked",
30
+ "job_paused",
31
+ "job_completed",
32
+ "job_failed",
33
+ "job_interrupted",
34
+ }
35
+
36
+
37
+ def attempt_failure_payload(kind: AttemptFailure, **extra: Any) -> dict[str, Any]:
38
+ payload = {"failure_type": kind.value}
39
+ payload.update(extra)
40
+ return payload
41
+
42
+
43
+ def classify_creator_finish(finish_reason: str | None) -> AttemptFailure | None:
44
+ if finish_reason == "length":
45
+ return AttemptFailure.LENGTH_DEATH
46
+ return None
47
+
48
+
49
+ def detect_interrupted_attempt(events: list[JobEvent]) -> dict[str, Any] | None:
50
+ task_id: str | None = None
51
+ start_seq: int | None = None
52
+ last_in_progress: JobEvent | None = None
53
+
54
+ for event in events:
55
+ if event.type == "task_started":
56
+ task_id = str(event.payload.get("task_id") or "")
57
+ start_seq = event.seq
58
+ last_in_progress = event
59
+ elif task_id and event.type in IN_PROGRESS_EVENTS:
60
+ last_in_progress = event
61
+ elif event.type in TERMINAL_EVENTS:
62
+ event_task = str(event.payload.get("task_id") or "")
63
+ if not event_task or event_task == task_id:
64
+ task_id = None
65
+ start_seq = None
66
+ last_in_progress = None
67
+
68
+ if not task_id or not start_seq or last_in_progress is None:
69
+ return None
70
+ return {
71
+ "failure_type": AttemptFailure.INTERRUPTED.value,
72
+ "task_id": task_id,
73
+ "started_seq": start_seq,
74
+ "last_event_type": last_in_progress.type,
75
+ "last_event_seq": last_in_progress.seq,
76
+ }