Skip to content

Commit 5fc262d

Browse files
committed
v78: recover structured stream collection, verify configured dashboard bind and record native probe failures
1 parent f600374 commit 5fc262d

10 files changed

Lines changed: 408 additions & 24 deletions

‎SYNTA_V78.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Synta v78: recover dashboard collection and diagnose native probes
2+
3+
## Supplied host evidence
4+
5+
v77 installed successfully on Debian. All five inference variants were blocked with an undifferentiated JSON parser error, so no n-gram promotion or speedup was established. The mission supervisor restarted its process.
6+
7+
The dashboard service was listening on `192.168.0.68:8765`, while the v77 readiness checker queried `127.0.0.1:8765`. The dashboard collector thread also exited with `TypeError` when concatenating a dictionary stream fragment. The supplied screenshots show an initial/stale snapshot with missing mission data, not evidence that the saved goals, weights, ledgers or archives were deleted. A historical trade series remains visible.
8+
9+
## Changes
10+
11+
- Activity redaction can decode a complete JSON string into a sanitized dictionary or list. Agent views now render both text and structured fragments, including returned traces. Secrets remain redacted. Request boundaries and bounded stream previews remain intact.
12+
- The dashboard collector catches malformed-data `TypeError` and retains an explicitly stale snapshot for the next cycle rather than losing its refresh thread.
13+
- Readiness uses the configured private dashboard origin in `research/remote-access/config.json`, or the existing systemd service bind if that configuration is absent. It does not rebind the server or guess a new address. Both the expected HTML renderer and a fresh successful `/api/status` collector snapshot are checked. An initial page or stale snapshot is insufficient.
14+
- An unavailable mission-running field displays `unknown`, rather than `stopped`. Missing trade timestamps display `timestamp unavailable`, rather than `Invalid Date`.
15+
- The structured benchmark prompt now requests a native JSON schema. Completed probe responses are written to per-variant `*-probes.jsonl` files before correctness checks. Non-JSON HTTP responses and non-JSON model content get different errors with a bounded preview. Incorrect, empty and truncated results remain blocked. This fixes the probe interface and diagnosis; the previous receipts do not identify which JSON parser failed, and no host benchmark speedup is claimed.
16+
- The targeted updater installs v78 and refreshes the actual copied host launcher `bin/serve-v100-dashboard.py`, preserving its backup. Earlier package-only updates could leave that launcher old. Mission and training services are not restarted; profiles, goals, balances, weights, Windows policy and archives are unchanged.
17+
18+
## Install
19+
20+
Run `tools/update-synta-v78.sh` with this release's exact SHA on an installed v77-v78 system. It replaces the package and the copied dashboard launcher, restarts only the dashboard and existing remote gateway, then checks private-address HTML and fresh collector data. On failure it prints dashboard service diagnostics. Reload the browser once and inspect that the collected timestamp advances.
21+
22+
This update deliberately does not launch another exclusive GPU benchmark while the mission or training may be running. The fixed benchmark can be rerun in a separately controlled idle period after the dashboard recovery result is known. Windows has not supplied a native benchmark result; production Windows n-gram transport is not enabled by this update.
23+
24+
## Exact locations
25+
26+
| Fix | Files |
27+
| --- | --- |
28+
| Structured stream fragments | `rlm/v100/live_status.py` |
29+
| Collector recovery | `tools/serve-v100-dashboard.py` |
30+
| Configured bind and fresh-snapshot checks | `rlm/v100/dashboard_health.py` |
31+
| Unknown mission state and timestamp labels | `rlm/v100/dashboard_layout.py` |
32+
| Structured inference probes and response evidence | `rlm/v100/inference_benchmark.py` |
33+
| Targeted installer | `tools/update-synta-v78.sh` |
34+
| Regression fixtures | `tests/test_v100_dashboard_recovery.py`, `tests/test_v100_strategy_recovery.py` |
35+
36+
Host model performance and Tailscale phone routing still need operator verification. These fixes do not establish profitable strategies or accepted learning updates.
37+
38+
## Validation
39+
40+
Full local suite: 1305 passed, 65 skipped, one existing PEFT Conv1D warning. Ruff, whitespace checks, Bash syntax and embedded updater Python parsing passed. Regression fixtures reproduce the actual redacting journal producing structured stream values, collector recovery after a TypeError, private LAN readiness with initial/stale/fresh snapshots, constrained JSON probe requests and preserved failed model responses. Host deployment, fresh collector output and native GPU timing still require operator results.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "rlms"
33
authors = [
44
{name = "Alex Zhang", email = "altzhang@mit.edu"},
55
]
6-
version = "0.1.3+v100.77"
6+
version = "0.1.3+v100.78"
77
description = "Recursive Language Models."
88
readme = "README.md"
99
license = "MIT"

‎rlm/v100/dashboard_health.py‎

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,99 @@
1-
"""Bounded dashboard startup checks after asynchronous systemd restart."""
1+
"""Bounded renderer and collector readiness at the configured private bind."""
22

3+
import ipaddress
4+
import json
5+
import math
6+
import re
7+
import subprocess
38
import time
9+
from pathlib import Path
10+
from urllib.parse import urlsplit
411

512
import requests
613

714

8-
def wait(seconds: int = 60) -> dict:
15+
def endpoint(root: Path | None) -> str:
16+
if root is None:
17+
return "http://127.0.0.1:8765/"
18+
config = root / "research/remote-access/config.json"
19+
if config.is_file():
20+
origin = json.loads(config.read_text())["dashboard"]
21+
else:
22+
command = subprocess.check_output(
23+
[
24+
"systemctl",
25+
"--user",
26+
"show",
27+
"v100-dashboard.service",
28+
"--property=ExecStart",
29+
"--value",
30+
],
31+
text=True,
32+
timeout=5,
33+
)
34+
found = re.search(r"--bind(?:=|\s+)([0-9.]+)(?:\s|;|$)", command)
35+
if found is None:
36+
raise ValueError("Cannot identify the owned dashboard bind; no address guessed")
37+
origin = "http://" + found.group(1) + ":8765"
38+
parsed = urlsplit(origin)
39+
address = ipaddress.ip_address(parsed.hostname or "")
40+
if (
41+
parsed.scheme != "http"
42+
or address.version != 4
43+
or not address.is_private
44+
or address.is_unspecified
45+
or address.is_multicast
46+
or parsed.port != 8765
47+
or parsed.username
48+
or parsed.password
49+
or parsed.query
50+
or parsed.fragment
51+
or parsed.path not in ("", "/")
52+
):
53+
raise ValueError(
54+
"Dashboard readiness requires the configured private IPv4 origin on port 8765"
55+
)
56+
return origin.rstrip("/") + "/"
57+
58+
59+
def wait(seconds: int = 60, *, root: Path | None = None) -> dict:
960
if type(seconds) is not int or not 1 <= seconds <= 90:
1061
raise ValueError("Dashboard readiness budget must be 1..90 seconds")
62+
url = endpoint(root)
1163
deadline = time.monotonic() + seconds
1264
detail = "No HTTP attempt completed"
1365
while time.monotonic() < deadline:
1466
try:
67+
response = requests.get(url, timeout=min(2, max(0.1, deadline - time.monotonic())))
68+
response.raise_for_status()
69+
if "mission-navigation" not in response.text or "Trade explorer" not in response.text:
70+
raise ValueError("HTTP response is not the expected host dashboard renderer")
1571
response = requests.get(
16-
"http://127.0.0.1:8765/", timeout=min(2, max(0.1, deadline - time.monotonic()))
72+
url + "api/status", timeout=min(2, max(0.1, deadline - time.monotonic()))
1773
)
1874
response.raise_for_status()
19-
if "mission-navigation" not in response.text or "Trade explorer" not in response.text:
20-
detail = "HTTP response is not the expected host dashboard renderer"
21-
else:
22-
return {
23-
"dashboard_ready": True,
24-
"url": "http://127.0.0.1:8765/",
25-
"scope": "HTTP renderer readiness only; not proof of model progress or phone routing",
26-
}
27-
except requests.RequestException as error:
75+
data = response.json()
76+
if not isinstance(data, dict):
77+
raise ValueError("Dashboard status must be a JSON object")
78+
collected = data.get("collected_at")
79+
if (
80+
not isinstance(data.get("mission"), dict)
81+
or type(collected) not in (int, float)
82+
or not math.isfinite(collected)
83+
or not -5 <= time.time() - collected <= 30
84+
or data.get("report_freshness", {}).get("source")
85+
== "previous snapshot after collector error"
86+
):
87+
raise ValueError(
88+
"Dashboard HTTP is available but its collector has no fresh successful snapshot"
89+
)
90+
return {
91+
"dashboard_ready": True,
92+
"collector_ready": True,
93+
"url": url,
94+
"scope": "Fresh local collector snapshot and renderer; not proof of model learning or phone routing",
95+
}
96+
except (requests.RequestException, ValueError, TypeError) as error:
2897
detail = str(error)[:300]
2998
time.sleep(min(1, max(0, deadline - time.monotonic())))
3099
raise RuntimeError("Dashboard did not become ready within the budget: " + detail)

‎rlm/v100/dashboard_layout.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
function render(data){if(latestInference&&inferenceStamp(latestInference)>=inferenceStamp(data.live))data=mergeInference(data,latestInference);latestSnapshot=data;const m=data.mission||{},r=data.report||{},ev=(m.state?.phase==='official-public-baseline'?r.mission_evidence?.official_benchmark:null)||m.evaluation||{},learn=m.learning||{},gpu=data.gpu||{};
109109
if(data.layout?.active_sha256&&layoutVersion!==data.layout.active_sha256){pendingLayout=true;$('reload').textContent='New layout available - load when ready'}$('layout').textContent='Master HTML: /workspace/dashboard/index.html | '+(data.layout?.state||'waiting')+(data.layout?.error?' | '+data.layout.error:'');
110110
$('fresh').textContent='Collected: '+new Date(data.collected_at*1000).toLocaleString()+' | Live snapshots every 5 s; aggregate audit runs separately | Run: '+fmt(m.run)+(data.report_freshness?.state==='stale'?' | STALE REPORT: '+fmt(data.report_freshness.updated_at):'')+' | Aggregate audit: '+fmt(data.report_collection?.state);
111-
workView(data);learningPanel(data);incomePanel(data);sourcesPanel(data);insightsView(data);archivePanel();$('warning').textContent=(data.errors||[]).join('\n');$('cards').replaceChildren();card('Mission',m.running?'running':'stopped');card('Phase',m.state?.phase);card('Server startup',m.server_startup?.stage);card('Learning cycles',learn.completed_cycles);card('Accepted weight updates',r.accepted_weight_updates_this_run);card('Confirmed optimizer updates',r.mission_evidence?.optimizer_updates_observed);card('GPU utilization',gpu.utilization===undefined?null:gpu.utilization+'%');
111+
workView(data);learningPanel(data);incomePanel(data);sourcesPanel(data);insightsView(data);archivePanel();$('warning').textContent=(data.errors||[]).join('\n');$('cards').replaceChildren();card('Mission',typeof m.running==='boolean'?(m.running?'running':'stopped'):'unknown');card('Phase',m.state?.phase);card('Server startup',m.server_startup?.stage);card('Learning cycles',learn.completed_cycles);card('Accepted weight updates',r.accepted_weight_updates_this_run);card('Confirmed optimizer updates',r.mission_evidence?.optimizer_updates_observed);card('GPU utilization',gpu.utilization===undefined?null:gpu.utilization+'%');
112112
$('evaluation').textContent=ev.total?`${ev.completed}/${ev.total} completed, ${ev.passed===undefined?'official grading':ev.passed+' passed'} (${ev.state})`:'No current evaluation counter';$('progress').max=ev.total||1;$('progress').value=ev.completed||0;$('case').textContent=ev.current_case?('Current case: '+ev.current_case):'';
113113
const ledger=r.live_paper||r.paper||{};$('paper').textContent=Object.entries(ledger.branches||{}).map(([name,b])=>`${name} | Equity ${fmt(b.equity)} ${ledger.currency||''} | Net P&L ${fmt(b.net_pnl_before_personal_tax)} | Costs ${fmt(b.modeled_costs)} | Resolved fills ${fmt(b.resolved_fills)}`).join('\n\n')||(r.paper_display_status||'Paper ledger status unavailable');if(ledger.updated_at)$('paper').textContent+='\n\nSaved report: '+ledger.updated_at+' | period '+(ledger.period||'live recorded state')+' | '+(ledger.scope||'');$('blockers').textContent=(r.paper_blockers||[]).join('\n')||(Array.isArray(r.paper_blockers)?'No blockers in recorded snapshot':'Paper blockers unavailable');$('workers').textContent=JSON.stringify({drones:r.drones,external_compute:r.external_compute,desktop:r.desktop,official_benchmark:r.official_benchmark},null,2);$('log').textContent=data.controller_log||'Log unavailable';$('evidence').textContent=JSON.stringify({report_freshness:data.report_freshness,report_collection:data.report_collection,last_cycle:learn.last_cycle,evidence:r.mission_evidence,collection_errors:data.errors},null,2);
114114
let audit=$('backtest-audit');if(!audit){audit=document.createElement('pre');audit.id='backtest-audit';const disclosure=document.createElement('details'),label=document.createElement('summary');disclosure.dataset.key='backtest-audit';label.textContent='Historical replay audit and evidence';disclosure.append(label,audit);$('chart').after(disclosure)}audit.textContent=r.backtest_audit?JSON.stringify(r.backtest_audit,null,2):'No recorded source replay audit. Drawdown is peak-to-trough loss, not standard deviation. Loss alone is not a data glitch.';
@@ -153,7 +153,7 @@
153153
if(!row){get('trade-summary').textContent='No recorded trade series available.';get('trade-scope').textContent=tradeData.scope||'';return}
154154
get('trade-hover').textContent='Hover a valuation point or select an execution to inspect its costs.';
155155
get('trade-scope').textContent=row.scope+(row.cost_assumptions?` Fee: ${row.cost_assumptions.fee_bps_per_leg} bps (${row.cost_assumptions.fee_bps_per_leg/100}%) per leg; slippage: ${row.cost_assumptions.slippage_bps_per_leg} bps (${row.cost_assumptions.slippage_bps_per_leg/100}%) per leg. Assumptions, not certified exchange rates.`:'');
156-
const initial=row.initial_capital;get('trade-summary').textContent=`Net P&L: ${Number(row.net_pnl).toFixed(4)} ${row.currency} | Net return: ${(row.net_pnl/initial*100).toFixed(2)}% | Modeled costs: ${Number(row.total_costs).toFixed(4)} | Recorded executions in view archive: ${row.executions.length} | Collected ${new Date(tradeData.updated*1000).toLocaleTimeString()}`;
156+
const initial=row.initial_capital;get('trade-summary').textContent=`Net P&L: ${Number(row.net_pnl).toFixed(4)} ${row.currency} | Net return: ${(row.net_pnl/initial*100).toFixed(2)}% | Modeled costs: ${Number(row.total_costs).toFixed(4)} | Recorded executions in view archive: ${row.executions.length} | Collected ${Number.isFinite(tradeData.updated)?new Date(tradeData.updated*1000).toLocaleTimeString():'timestamp unavailable'}`;
157157
if(row.diagnostics)get('trade-summary').textContent+=` | Same signals without costs: ${(row.diagnostics.same_signals_without_costs_return*100).toFixed(2)}% | Cost drag (return difference): ${(row.diagnostics.cost_drag_return_difference*100).toFixed(2)} percentage points | ${row.diagnostics.state}`;
158158
const all=row.points||[],begin=Math.floor(all.length*tradeStart/100),end=Math.max(begin+1,Math.ceil(all.length*tradeEnd/100)),points=all.slice(begin,end);
159159
const value=p=>tradeMetric==='frictionless'?p.frictionless_equity:tradeMetric==='spot'?p.spot_equity:tradeMetric==='return'?(p.equity/initial-1)*100:tradeMetric==='price'?p.price:p.equity;

0 commit comments

Comments
 (0)