Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/dashboard_refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ on:
paths:
- "registry.yaml"
- "campaigns.yaml"
- "sampler_candidates.json"
- "tasks/**"
- "migration.yaml"
- "insight/**"
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:

- name: Install tools
run: |
python -m pip install --quiet ruff pytest pyyaml
python -m pip install --quiet ruff pytest pyyaml playwright
curl -sSfL https://github.com/lycheeverse/lychee/releases/latest/download/lychee-x86_64-unknown-linux-gnu.tar.gz \
| tar -xz -C /usr/local/bin --strip-components=1 lychee-x86_64-unknown-linux-gnu/lychee

Expand All @@ -74,6 +74,13 @@ jobs:
PYAUTO_BRAIN: ${{ github.workspace }}/_brain
run: bin/pyauto-insight check --offline --mind _mind

- name: Chromium setup navigation and responsive layout
env:
PYAUTO_BRAIN: ${{ github.workspace }}/_brain
run: |
python -m playwright install --with-deps chromium
python tests/browser_setup.py

- name: lychee (markdown link-rot)
env:
# github.com serves 503 to unauthenticated page fetches (rate limit);
Expand Down
28 changes: 28 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,31 @@ and state; links continue to point into project repositories.
Before shipping: Ruff, formatting, full pytest and offline contract validation.
Integration CI validates Brain's feed contract. Actual sender/receiver success and
receipt revision are deployment evidence, distinct from mocked workflow tests.

## Setup browser and candidate handoff

Version 2 captures expose project → dataset family → model setup navigation.
Each setup opens a new tab with a stable `?view=setup&instance=…&setup=…`
address. The headline shows only the producer-selected, validator-qualified
reference baseline. Missing acceptance remains explicit; historical runtime is
never promoted into an expected runtime. Sampler records show initialization,
hardware, compilation/cache state and measured clock/work units separately.
Baseline manifests, archived results, comparison refusals and provenance remain
available in disclosures. Unmapped records remain reachable from the project.
Without JavaScript, setup content remains accessible as ordinary disclosures.
Images, when declared, resolve at the captured producer revision.

`sampler_candidates.json` is a manually reviewed public handoff from literature
curation. Each entry records primary paper and official code URLs, review date,
expected uses and limitations, separately stated integration/benchmark status,
and an editable investigation prompt. Update it after an on-demand literature
review; verify source claims and installed search coverage before changing status.
Do not copy private notes or infer measured superiority from a paper. Candidate
prompts propose work through the sampler pipeline; they do not authorize compute.
The candidate file participates in the dashboard input digest. Regenerate the
board after changing it and run the offline check.

The required Chromium navigation check (`python tests/browser_setup.py`, after
installing Playwright and Chromium) covers new-tab routing, mobile width,
unknown routes, history, editable clipboard payloads and the no-JavaScript
fallback alongside the full Python suite.
11,681 changes: 11,679 additions & 2 deletions dashboard.html

Large diffs are not rendered by default.

2,494 changes: 2,492 additions & 2 deletions dashboard.md

Large diffs are not rendered by default.

37 changes: 34 additions & 3 deletions insight/board.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from datetime import UTC, datetime
from pathlib import Path

from insight import ORGAN_ROOT, campaigns, summary
from insight import ORGAN_ROOT, campaigns, candidates, setup_browser, summary
from insight.theme import CSS, JS, theme

PAGES_URL = "https://pyautolabs.github.io/PyAutoInsight/"
Expand Down Expand Up @@ -89,7 +89,9 @@ def input_marker(views, data):
]
return (
"<!-- insight-inputs:"
+ hashlib.sha256(json.dumps([body, data], sort_keys=True).encode()).hexdigest()
+ hashlib.sha256(
json.dumps([body, data, candidates.load()], sort_keys=True).encode()
).hexdigest()
+ " -->"
)

Expand Down Expand Up @@ -135,6 +137,9 @@ def _detail(s, now=None):
parts.append(
'<p class="warn">Local preview, not a published capture. No receipt written.</p>'
)
if d.get("version") == 2:
nav, _ = setup_browser.render(s, now)
return "".join(parts) + nav
if not d:
return "".join(parts)
parts += [
Expand Down Expand Up @@ -256,18 +261,21 @@ def _captured_at(views):
def render_html(views, now=None, campaign_data=None):
data = campaign_data if campaign_data is not None else campaigns.load()
shared = theme()
browser_css, browser_js = setup_browser.assets()
return shared.section_layout(
'<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">'
"<title>PyAutoInsight dashboard</title><style>"
+ shared.css("insight")
+ CSS
+ browser_css
+ "pre{white-space:pre-wrap;overflow-wrap:anywhere}details{margin:1rem 0}dd{margin-left:1rem}</style></head><body>"
+ shared.hero(
"insight",
"Inference dashboard",
navigation=[
{"href": "#campaigns", "label": "Active campaigns"},
{"href": "#evidence", "label": "Inference evidence"},
{"href": "#sampler-candidates", "label": "Sampler candidates"},
*(
{
"href": "#" + v.instance.instance,
Expand All @@ -277,7 +285,7 @@ def render_html(views, now=None, campaign_data=None):
),
],
)
+ "<main>"
+ '<main><p id="setup-route-status" role="status"></p><div id="inference-home">'
+ input_marker(views, data)
+ campaigns.render_html(
data,
Expand All @@ -289,9 +297,14 @@ def render_html(views, now=None, campaign_data=None):
)
+ "<p>Execution completion is separate from convergence and scientific acceptance. Cortex retains scientific conclusions. Evidence remains in project storage.</p>"
+ "".join(_detail(s, now) for s in views)
+ candidates.render(candidates.load())
+ '</div><div id="inference-pages">'
+ "".join(setup_browser.render(s, now)[1] for s in views)
+ "</div>"
+ "</main><script>"
+ shared.JS
+ JS
+ browser_js
+ "</script></body></html>\n"
)

Expand Down Expand Up @@ -321,6 +334,13 @@ def render_markdown(views, now=None, campaign_data=None):
out.append("Cached: original evidence and capture times retained.")
if not s.doc:
continue
if s.doc.get("version") == 2:
for setup in s.doc["setups"]:
out.append(
f"- [{md(setup['dataset_family'])} / {md(setup['model_family'])} / {md(setup['label'])}]"
f"({PAGES_URL}{setup_browser.route(s.instance.instance, setup['id'])})"
)
out += ["", "<details><summary>Full captured evidence and provenance</summary>", ""]
out += [
"",
"Coverage: " + _dump(s.doc["coverage"]),
Expand Down Expand Up @@ -363,6 +383,17 @@ def render_markdown(views, now=None, campaign_data=None):
f"Comparison {md(c['id'])}: "
+ md(summary.comparison_refusals(s.doc, c) or c["protocol"])
)
out += ["", "</details>", ""]
out += [
"## Sampler candidates",
"",
f"[Browse candidates and copy investigation prompts]({PAGES_URL}#sampler-candidates)",
"",
]
for candidate in candidates.load():
out.append(
f"- [{md(candidate['name'])}]({candidate['paper_url']}): {md(candidate['integration_status'])}. {md(candidate['benchmark_status'])}."
)
return "\n".join(out) + "\n"


Expand Down
78 changes: 78 additions & 0 deletions insight/candidates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Curated public literature handoff; expectations are not benchmark evidence."""

import json
from datetime import date
from urllib.parse import urlparse

from insight import ORGAN_ROOT
from insight.setup_browser import disclosure, e


def load(path=None):
data = json.loads((path or ORGAN_ROOT / "sampler_candidates.json").read_text())
if not isinstance(data, list):
raise ValueError("sampler candidates must be a list")
ids = set()
for row in data:
if not isinstance(row, dict):
raise ValueError("sampler candidate must be object")
for key in (
"id",
"name",
"family",
"integration_status",
"benchmark_status",
"investigation_prompt",
"reviewed_at",
):
if not isinstance(row.get(key), str) or not row[key].strip():
raise ValueError(f"candidate needs {key}")
if row["id"] in ids:
raise ValueError("duplicate sampler candidate id")
ids.add(row["id"])
for key in ("paper_url", "code_url"):
value = row.get(key)
url = urlparse(value) if isinstance(value, str) else None
if not url or url.scheme != "https" or not url.netloc or url.username or url.password:
raise ValueError(f"candidate needs public HTTPS {key}")
for key in ("expected_uses", "limitations"):
if (
not isinstance(row.get(key), list)
or not row[key]
or not all(isinstance(v, str) and v.strip() for v in row[key])
):
raise ValueError(f"candidate needs {key} list")
date.fromisoformat(row["reviewed_at"])
return data


def render(data):
out = [
'<h2 id="sampler-candidates">Sampler candidates</h2>',
"<p>Literature expectations, not measured PyAuto recommendations. Review a candidate and copy its investigation prompt.</p>",
]
for i, row in enumerate(data):
body = f"<p>{e(row['family'])} · reviewed {e(row['reviewed_at'])}</p>"
body += f'<p><a href="{e(row["paper_url"])}" target="_blank" rel="noopener">Paper ↗</a> · <a href="{e(row["code_url"])}" target="_blank" rel="noopener">Official code ↗</a></p>'
for label, key in (("Expected uses", "expected_uses"), ("Limitations", "limitations")):
body += (
f"<h3>{label}</h3><ul>" + "".join(f"<li>{e(v)}</li>" for v in row[key]) + "</ul>"
)
body += f"<p>Integration: {e(row['integration_status'])}</p><p>Benchmark evidence: {e(row['benchmark_status'])}</p>"
prompt = (
row["investigation_prompt"]
+ "\n\nUse the existing sampler pipeline and setup catalogue. Preserve the baseline-prepared model and priors. Distinguish cold/warm/resume starts from compilation/cache state. Propose a bounded implementation or benchmark plan before executing compute. Paper: "
+ row["paper_url"]
+ "\nCode: "
+ row["code_url"]
)
body += '<div data-candidate><button type="button" data-candidate-copy aria-label="Copy investigation prompt">⧉ Copy investigation prompt</button><span role="status" aria-live="polite"></span>'
body += (
disclosure(
"Investigation prompt",
f'<label for="candidate-{i}">Editable prompt</label><textarea class="candidate-prompt" id="candidate-{i}">{e(prompt)}</textarea>',
)
+ "</div>"
)
out.append(disclosure(row["name"], body))
return "".join(out)
2 changes: 1 addition & 1 deletion insight/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def common(p, reading=True):
args = parser.parse_args(argv)
try:
return args.func(args)
except registry.RegistryError as exc:
except (registry.RegistryError, ValueError) as exc:
print(f"pyauto-insight: {exc}", file=sys.stderr)
return 1

Expand Down
13 changes: 13 additions & 0 deletions insight/setup_browser.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.setup-choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:.75rem;margin:1rem 0}
.setup-choice{display:block;padding:1rem;border:1px solid var(--line);border-radius:.6rem;background:var(--card);text-decoration:none}
.setup-choice:hover,.setup-choice:focus-visible{border-color:var(--accent);outline:2px solid var(--accent);outline-offset:2px}
.setup-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,12rem),1fr));gap:.75rem;margin:1rem 0}
.setup-metrics>div{padding:1rem;background:var(--card);border:1px solid var(--line);border-radius:.6rem;min-width:0}
.setup-metrics dt{font-size:.85rem;color:var(--muted)}.setup-metrics dd{margin:.35rem 0 0!important;font-size:1.15rem;font-weight:600;overflow-wrap:anywhere}
.setup-page>summary{font-weight:600}.setup-page[open]{border:0}.setup-view .setup-page>summary{display:none}
.setup-image{max-width:100%;height:auto;max-height:28rem;object-fit:contain;border-radius:.6rem}
.setup-view .board-nav,.setup-view [data-orchestration-panel]{display:none}
[hidden]{display:none!important}.setup-page pre{max-height:32rem;overflow:auto;font-size:.85rem}
.setup-page details{padding:.75rem;border:1px solid var(--line);border-radius:.5rem}.setup-page summary{cursor:pointer}
.candidate-prompt{width:100%;box-sizing:border-box;min-height:8rem;font:inherit;padding:.75rem}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
33 changes: 33 additions & 0 deletions insight/setup_browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(() => {
function route() {
const query = new URLSearchParams(location.search);
const pages = [...document.querySelectorAll('.setup-page')];
const selected = pages.find(p => p.dataset.instance === query.get('instance') && p.dataset.setup === query.get('setup'));
const detail = query.get('view') === 'setup' && !!selected;
document.body.classList.toggle('setup-view', detail);
document.getElementById('inference-home').hidden = detail;
document.getElementById('inference-pages').hidden = !detail;
for (const page of pages) {
page.hidden = page !== selected;
page.open = page === selected;
}
document.getElementById('setup-route-status').textContent = query.get('view') === 'setup' && !selected ? 'This setup is unavailable in the captured evidence. Choose an available setup below.' : '';
if (detail) document.title = selected.querySelector('h1').textContent + ' · PyAutoInsight';
}
route();
addEventListener('popstate', route);
document.querySelectorAll('[data-candidate-copy]').forEach(button => button.addEventListener('click', async () => {
const panel = button.closest('[data-candidate]');
const text = panel.querySelector('textarea');
const status = panel.querySelector('[role="status"]');
try {
if (!navigator.clipboard) throw new Error('Clipboard unavailable');
await navigator.clipboard.writeText(text.value);
status.textContent = 'Prompt copied';
} catch (_) {
text.closest('details').open = true;
text.focus(); text.select();
status.textContent = 'Select and copy the prompt below';
}
}));
})();
Loading
Loading