Spaces:
Running
Running
github-actions[bot] commited on
Commit ·
f018656
1
Parent(s): 2575dda
Deploy 58acaca
Browse filesThe bench header names the construct, not the prompt
Source: https://github.com/WINTER4000/turingDNA/commit/58acacaad14a5c8f4ad07cddf1b41699cd81d8c9
- dee/static/app.js +31 -0
- dee/static/cockpit.js +16 -1
- dee/static/index.html +6 -6
- tests/test_bench_header.py +65 -0
dee/static/app.js
CHANGED
|
@@ -9847,6 +9847,29 @@ function runOracle(opts){
|
|
| 9847 |
function setMeta() {
|
| 9848 |
metaEl.textContent = `${plasmid.length.toLocaleString()} bp · ${plasmid.topology} · GC ${plasmid.gc}%`
|
| 9849 |
+ ((plasmid.features || []).length ? ` · ${plasmid.features.length} features` : '');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9850 |
}
|
| 9851 |
|
| 9852 |
function _renderPlasmidMeta() {
|
|
@@ -10988,6 +11011,14 @@ function runOracle(opts){
|
|
| 10988 |
/* What Turing reads and drives. The agent could already paint a construct
|
| 10989 |
but had no idea a second one was open, so it could not answer "switch to
|
| 10990 |
the other vector" — the most ordinary thing to ask once tabs exist. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10991 |
window.TDPlasmid.tabs = function () {
|
| 10992 |
return plasmidTabs ? plasmidTabs.list() : [];
|
| 10993 |
};
|
|
|
|
| 9847 |
function setMeta() {
|
| 9848 |
metaEl.textContent = `${plasmid.length.toLocaleString()} bp · ${plasmid.topology} · GC ${plasmid.gc}%`
|
| 9849 |
+ ((plasmid.features || []).length ? ` · ${plasmid.features.length} features` : '');
|
| 9850 |
+
syncBenchSpecimen();
|
| 9851 |
+
}
|
| 9852 |
+
|
| 9853 |
+
/* The bench header names what is on the bench.
|
| 9854 |
+
|
| 9855 |
+
It did not. Opening the bench from a live run labelled the specimen chip
|
| 9856 |
+
with the RUN's title — the sentence the scientist had just typed — and
|
| 9857 |
+
the sub-line with the literal words "live run", so a 19,895 bp pCambia
|
| 9858 |
+
showed up in its own header as "add a cds of fugfp upstream LUX-G and
|
| 9859 |
+
annotate it". The construct's identity was on screen twice (the map
|
| 9860 |
+
centre, the name field) and wrong in the one place that is supposed to
|
| 9861 |
+
be the answer to "what am I working on".
|
| 9862 |
+
|
| 9863 |
+
Called from setMeta, which every render path already goes through, so
|
| 9864 |
+
the header tracks tab switches and edits without another hook. */
|
| 9865 |
+
function syncBenchSpecimen() {
|
| 9866 |
+
if (!plasmid) return;
|
| 9867 |
+
if (document.body.getAttribute('data-bench') !== 'open') return;
|
| 9868 |
+
if (!window.TDBench || !window.TDBench.update) return;
|
| 9869 |
+
window.TDBench.update({
|
| 9870 |
+
name: plasmid.name || 'Construct',
|
| 9871 |
+
sub: `${(plasmid.length || 0).toLocaleString()} bp · ${plasmid.topology || ''}`.trim(),
|
| 9872 |
+
});
|
| 9873 |
}
|
| 9874 |
|
| 9875 |
function _renderPlasmidMeta() {
|
|
|
|
| 11011 |
/* What Turing reads and drives. The agent could already paint a construct
|
| 11012 |
but had no idea a second one was open, so it could not answer "switch to
|
| 11013 |
the other vector" — the most ordinary thing to ask once tabs exist. */
|
| 11014 |
+
/* The identity of what the editor is holding, for whoever has to name it
|
| 11015 |
+
before a render has happened — the Cockpit opens the bench from a tool
|
| 11016 |
+
result and needs the construct, not the conversation, in the header. */
|
| 11017 |
+
window.TDPlasmid.construct = function () {
|
| 11018 |
+
if (!plasmid) return null;
|
| 11019 |
+
return { name: plasmid.name || '', length: plasmid.length || 0,
|
| 11020 |
+
topology: plasmid.topology || '' };
|
| 11021 |
+
};
|
| 11022 |
window.TDPlasmid.tabs = function () {
|
| 11023 |
return plasmidTabs ? plasmidTabs.list() : [];
|
| 11024 |
};
|
dee/static/cockpit.js
CHANGED
|
@@ -1729,8 +1729,23 @@
|
|
| 1729 |
try {
|
| 1730 |
if (window.TDBench && window.TDBench.openConstruct &&
|
| 1731 |
document.body.getAttribute("data-bench") !== "open") {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1732 |
window.TDBench.openConstruct({
|
| 1733 |
-
name: currentTitle() || "Construct",
|
|
|
|
|
|
|
|
|
|
| 1734 |
phaseIdx: 0, route: view,
|
| 1735 |
});
|
| 1736 |
return;
|
|
|
|
| 1729 |
try {
|
| 1730 |
if (window.TDBench && window.TDBench.openConstruct &&
|
| 1731 |
document.body.getAttribute("data-bench") !== "open") {
|
| 1732 |
+
/* Name the bench after the CONSTRUCT, not the sentence that
|
| 1733 |
+
started the run. This used to pass currentTitle() — the
|
| 1734 |
+
user's own prompt — plus the literal sub-line "live run",
|
| 1735 |
+
so opening the bench off a plasmid tool put "add a cds of
|
| 1736 |
+
fugfp upstream LUX-G and annotate it" in the header of a
|
| 1737 |
+
19,895 bp pCambia. The run's title belongs to the run; it
|
| 1738 |
+
is already on the transcript. */
|
| 1739 |
+
var c = null;
|
| 1740 |
+
try {
|
| 1741 |
+
c = (window.TDPlasmid && window.TDPlasmid.construct)
|
| 1742 |
+
? window.TDPlasmid.construct() : null;
|
| 1743 |
+
} catch (e) { c = null; }
|
| 1744 |
window.TDBench.openConstruct({
|
| 1745 |
+
name: (c && c.name) || currentTitle() || "Construct",
|
| 1746 |
+
sub: c ? (Number(c.length || 0).toLocaleString() + " bp · "
|
| 1747 |
+
+ c.topology).trim()
|
| 1748 |
+
: "live run",
|
| 1749 |
phaseIdx: 0, route: view,
|
| 1750 |
});
|
| 1751 |
return;
|
dee/static/index.html
CHANGED
|
@@ -112,7 +112,7 @@
|
|
| 112 |
<!-- ?v= query bumps invalidate browser + iframe asset caches when app.css /
|
| 113 |
app.js change. Bump these numbers whenever you ship a frontend update —
|
| 114 |
without them, users keep getting the stale file for up to a week. -->
|
| 115 |
-
<link rel="stylesheet" href="/static/app.css?v=
|
| 116 |
<!-- The work catalog + the draggable rail. Kept out of app.css so two new
|
| 117 |
self-contained surfaces stay reviewable; every colour is an app.css
|
| 118 |
token, so both themes work with nothing added. -->
|
|
@@ -3140,12 +3140,12 @@
|
|
| 3140 |
<!-- Cloning reference data must load before app.js so the Designer
|
| 3141 |
can read VECTORS / ENZYMES / CLONING_METHODS / TAGS / LINKERS. -->
|
| 3142 |
<script src="/static/cloning_db.js?v=20260530-ui-polish" defer></script>
|
| 3143 |
-
<script src="/static/context.js?v=
|
| 3144 |
-
<script src="/static/lineage.js?v=
|
| 3145 |
<!-- Before app.js: it mounts a strip at init. Both are `defer`, so they
|
| 3146 |
run in document order. -->
|
| 3147 |
-
<script src="/static/tabs.js?v=
|
| 3148 |
-
<script src="/static/app.js?v=
|
| 3149 |
<!-- The decision trace, BEFORE cockpit.js: applyEvent calls TDTrace.push
|
| 3150 |
on the very first event, and both are `defer`, so document order is
|
| 3151 |
load order. Loading it after would drop the opening events of a
|
|
@@ -3154,7 +3154,7 @@
|
|
| 3154 |
<!-- THE COCKPIT — the persistent orchestrator rail. Loads after app.js so
|
| 3155 |
TDBench/TDStructure exist when a tool result asks the workspace to
|
| 3156 |
render something. This is the only conversation surface in the app. -->
|
| 3157 |
-
<script src="/static/cockpit.js?v=
|
| 3158 |
<!-- structcard before catalog: the catalog calls TDStructCard.observe as
|
| 3159 |
soon as it paints. Both are defer, so document order is load order. -->
|
| 3160 |
<script src="/static/structcard.js?v=20260811-buildC" defer></script>
|
|
|
|
| 112 |
<!-- ?v= query bumps invalidate browser + iframe asset caches when app.css /
|
| 113 |
app.js change. Bump these numbers whenever you ship a frontend update —
|
| 114 |
without them, users keep getting the stale file for up to a week. -->
|
| 115 |
+
<link rel="stylesheet" href="/static/app.css?v=20260829-specimen" />
|
| 116 |
<!-- The work catalog + the draggable rail. Kept out of app.css so two new
|
| 117 |
self-contained surfaces stay reviewable; every colour is an app.css
|
| 118 |
token, so both themes work with nothing added. -->
|
|
|
|
| 3140 |
<!-- Cloning reference data must load before app.js so the Designer
|
| 3141 |
can read VECTORS / ENZYMES / CLONING_METHODS / TAGS / LINKERS. -->
|
| 3142 |
<script src="/static/cloning_db.js?v=20260530-ui-polish" defer></script>
|
| 3143 |
+
<script src="/static/context.js?v=20260829-specimen" defer></script>
|
| 3144 |
+
<script src="/static/lineage.js?v=20260829-specimen" defer></script>
|
| 3145 |
<!-- Before app.js: it mounts a strip at init. Both are `defer`, so they
|
| 3146 |
run in document order. -->
|
| 3147 |
+
<script src="/static/tabs.js?v=20260829-specimen" defer></script>
|
| 3148 |
+
<script src="/static/app.js?v=20260829-specimen" defer></script>
|
| 3149 |
<!-- The decision trace, BEFORE cockpit.js: applyEvent calls TDTrace.push
|
| 3150 |
on the very first event, and both are `defer`, so document order is
|
| 3151 |
load order. Loading it after would drop the opening events of a
|
|
|
|
| 3154 |
<!-- THE COCKPIT — the persistent orchestrator rail. Loads after app.js so
|
| 3155 |
TDBench/TDStructure exist when a tool result asks the workspace to
|
| 3156 |
render something. This is the only conversation surface in the app. -->
|
| 3157 |
+
<script src="/static/cockpit.js?v=20260829-specimen" defer></script>
|
| 3158 |
<!-- structcard before catalog: the catalog calls TDStructCard.observe as
|
| 3159 |
soon as it paints. Both are defer, so document order is load order. -->
|
| 3160 |
<script src="/static/structcard.js?v=20260811-buildC" defer></script>
|
tests/test_bench_header.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The bench header names the construct, not the sentence that started the run.
|
| 2 |
+
|
| 3 |
+
A wiring assertion because the failure is silent and cosmetic-looking: the
|
| 4 |
+
header still renders, still styled, just describing the wrong thing. Opening
|
| 5 |
+
the bench from a live run labelled the specimen chip with the RUN's title, so
|
| 6 |
+
a 19,895 bp pCambia announced itself as "add a cds of fugfp upstream LUX-G
|
| 7 |
+
and annotate it" with "live run" underneath — while its real name sat in the
|
| 8 |
+
map centre two inches below.
|
| 9 |
+
"""
|
| 10 |
+
import re
|
| 11 |
+
|
| 12 |
+
_APP_JS = "dee/static/app.js"
|
| 13 |
+
_COCKPIT_JS = "dee/static/cockpit.js"
|
| 14 |
+
_INDEX = "dee/static/index.html"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _read(p):
|
| 18 |
+
with open(p, encoding="utf-8") as fh:
|
| 19 |
+
return fh.read()
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_the_cockpit_opens_the_bench_with_the_construct_not_the_prompt():
|
| 23 |
+
js = _read(_COCKPIT_JS)
|
| 24 |
+
m = re.search(r"function gotoView\(view\)\s*\{(.*?)\n \}", js, re.S)
|
| 25 |
+
assert m, "gotoView moved — re-point this guard"
|
| 26 |
+
body = m.group(1)
|
| 27 |
+
assert "TDPlasmid.construct" in body, \
|
| 28 |
+
"gotoView must ask the editor what is loaded before naming the bench"
|
| 29 |
+
# the run's own title is the FALLBACK, never the first choice
|
| 30 |
+
name_line = re.search(r"name:\s*(.+)", body).group(1)
|
| 31 |
+
assert name_line.index("c.name") < name_line.index("currentTitle"), \
|
| 32 |
+
"the construct's name must win over the run title"
|
| 33 |
+
assert '"live run"' in body and "c ?" in body, \
|
| 34 |
+
"'live run' must only be the no-construct fallback"
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_the_editor_exposes_what_it_is_holding():
|
| 38 |
+
js = _read(_APP_JS)
|
| 39 |
+
assert re.search(r"window\.TDPlasmid\.construct\s*=\s*function", js), \
|
| 40 |
+
"the Cockpit reads this to label the bench before any render runs"
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_every_render_path_refreshes_the_header():
|
| 44 |
+
"""setMeta is the choke point every render already goes through, so the
|
| 45 |
+
header follows tab switches and edits without a second hook."""
|
| 46 |
+
js = _read(_APP_JS)
|
| 47 |
+
m = re.search(r"function setMeta\(\)\s*\{(.*?)\n \}", js, re.S)
|
| 48 |
+
assert m, "setMeta moved — re-point this guard"
|
| 49 |
+
assert "syncBenchSpecimen()" in m.group(1)
|
| 50 |
+
sync = re.search(r"function syncBenchSpecimen\(\)\s*\{(.*?)\n \}", js, re.S)
|
| 51 |
+
assert sync, "syncBenchSpecimen missing"
|
| 52 |
+
b = sync.group(1)
|
| 53 |
+
assert "plasmid.name" in b and "plasmid.length" in b and "plasmid.topology" in b
|
| 54 |
+
assert 'data-bench' in b, "must not touch the header outside bench mode"
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_the_cache_bust_token_moved_with_the_js():
|
| 58 |
+
"""/static/* is served max-age=31536000; the ?v= token is the ONLY
|
| 59 |
+
invalidation. Shipping app.js without bumping it ships nothing."""
|
| 60 |
+
html = _read(_INDEX)
|
| 61 |
+
for asset in ("app.js", "cockpit.js"):
|
| 62 |
+
m = re.search(re.escape("/static/" + asset) + r"\?v=([0-9A-Za-z-]+)", html)
|
| 63 |
+
assert m, asset + " lost its cache-bust token"
|
| 64 |
+
assert m.group(1) != "20260828-anchor", \
|
| 65 |
+
asset + " still carries the pre-fix token"
|