Skip to content

Commit 5ec2492

Browse files
committed
[fix.lab.sigma.D8+] make FEP review an exact recoverable scientific workflow
Pose, campaign, network, and RunSet decisions previously survived identity edits, lost durable receipts, or depended on ambiguous visual state. The standalone FEP workspace now binds every transition to content-addressed scientific provenance, fails closed on unresolved chemistry and receptor policy, restores interrupted operations exactly once, and remains operable by keyboard and at high zoom. The production build adds only the standalone FEP entry; it does not absorb the concurrent AppShell redesign or the unrelated time-tunnel demos.
1 parent 2c39ca8 commit 5ec2492

29 files changed

Lines changed: 4981 additions & 3 deletions

‎scripts/build.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const Apps = [
3030
{ kind: 'app', name: 'viewer', themes: ['light', 'dark', 'blue'], entryRoot: './src/apps.reference.viewer-demos.vendored-readonly/viewer' },
3131
{ kind: 'app', name: 'docking-viewer', entryRoot: './src/apps.reference.viewer-demos.vendored-readonly/docking-viewer' },
3232
{ kind: 'app', name: 'mesoscale-explorer', entryRoot: './src/apps.reference.viewer-demos.vendored-readonly/mesoscale-explorer' },
33-
{ kind: 'app', name: 'dirac', filename: 'dirac.js', entryRoot: './src/app.frontend.facets.molstar-rdkit.editable', staticDirs: ['assets/rdkit', 'fonts'], staticFiles: ['fonts.css','fascia-tokens.css','theme-fascia.css','theme-fascia.js','workspace-shell.css','voice-composer.css','addons.css','palette.js', ['../../node_modules/frappe-gantt/dist/frappe-gantt.css', 'frappe-gantt.css']], auxiliaryEntries: [{ entry: 'voice-composer-demo.ts', outfile: 'voice-composer-demo/voice-composer-demo.js', staticFiles: [['voice-composer-demo.html', 'voice-composer-demo/index.html'], ['voice-composer.css', 'voice-composer-demo/voice-composer.css']] }] },
33+
{ kind: 'app', name: 'dirac', filename: 'dirac.js', entryRoot: './src/app.frontend.facets.molstar-rdkit.editable', staticDirs: ['assets/rdkit', 'fonts'], staticFiles: ['fonts.css','fascia-tokens.css','theme-fascia.css','theme-fascia.js','workspace-shell.css','voice-composer.css','addons.css','palette.js', ['../../node_modules/frappe-gantt/dist/frappe-gantt.css', 'frappe-gantt.css']], auxiliaryEntries: [{ entry: 'voice-composer-demo.ts', outfile: 'voice-composer-demo/voice-composer-demo.js', staticFiles: [['voice-composer-demo.html', 'voice-composer-demo/index.html'], ['voice-composer.css', 'voice-composer-demo/voice-composer.css']] }, { entry: 'fep-workbench/fep-workbench.ts', outfile: 'time-tunnel/fep-workbench.js', staticFiles: [['fep-workbench/fep-workbench.html', 'time-tunnel/fep-workbench.html'], ['fep-workbench/fep-workbench.css', 'time-tunnel/fep-workbench.css']] }] },
3434
{ kind: 'app', name: 'mvs-stories', globalName: 'mvsStories', filename: 'mvs-stories.js', entryRoot: './src/apps.reference.viewer-demos.vendored-readonly/mvs-stories' },
3535

3636
// Examples

‎src/app.frontend.facets.molstar-rdkit.editable/fep-workbench/fep-workbench.css‎

Lines changed: 318 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width,initial-scale=1">
6+
<base href="/">
7+
<title>DIRAC · FEP Lab</title>
8+
<link rel="stylesheet" href="time-tunnel/fep-workbench.css">
9+
</head>
10+
<body>
11+
<div id="fep-lab"></div>
12+
<script src="time-tunnel/fep-workbench.js"></script>
13+
</body>
14+
</html>

‎src/app.frontend.facets.molstar-rdkit.editable/fep-workbench/fep-workbench.ts‎

Lines changed: 1120 additions & 0 deletions
Large diffs are not rendered by default.

‎src/app.frontend.facets.molstar-rdkit.editable/fep-workbench/molecule-sketcher.ts‎

Lines changed: 114 additions & 0 deletions
Large diffs are not rendered by default.

‎src/app.frontend.facets.molstar-rdkit.editable/fep-workbench/pose-reviewer.ts‎

Lines changed: 99 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { readFileSync, readdirSync, statSync } from 'node:fs';
3+
import { resolve } from 'node:path';
4+
5+
const facadePath = resolve(__dirname, 'fep-workbench.ts');
6+
const facade = readFileSync(facadePath, 'utf8');
7+
const sources = readdirSync(__dirname)
8+
.filter(name => name.endsWith('.ts') && !name.endsWith('.spec.ts'))
9+
.map(name => ({ name, source: readFileSync(resolve(__dirname, name), 'utf8') }));
10+
11+
const owners: Readonly<Record<string, string>> = {
12+
decideWorkbenchBoot: 'workbench-boot.ts',
13+
setSafeText: 'workbench-dom.ts',
14+
escapeHtml: 'workbench-dom.ts',
15+
safeElement: 'workbench-dom.ts',
16+
preparationReceiptFrom: 'workbench-receipts.ts',
17+
preparationRequestKey: 'workbench-receipts.ts',
18+
runReceiptFromData: 'workbench-receipts.ts',
19+
operationBindingFromReceipt: 'workbench-receipts.ts',
20+
preparationPolicyGate: 'workbench-view-model.ts',
21+
runAggregateViewFrom: 'workbench-view-model.ts',
22+
aggregatePanelViewFrom: 'workbench-view-model.ts',
23+
runHistoryViewFrom: 'workbench-view-model.ts',
24+
submitPreparationExactlyOnce: 'workbench-preparation.ts',
25+
preparationResultMatchesOpenCampaign: 'workbench-preparation.ts',
26+
};
27+
28+
describe('FEP controller architecture ratchets', () => {
29+
it('keeps the facade below the accepted monolith ceiling', () => {
30+
expect(statSync(facadePath).size).toBeLessThan(200_000);
31+
expect(facade.split(/\r?\n/).length).toBeLessThanOrEqual(1_200);
32+
});
33+
34+
it.each(Object.entries(owners))('%s has one module owner and no facade definition', (symbol, owner) => {
35+
const definition = new RegExp(`(?:export\\s+)?function\\s+${symbol}\\b`);
36+
expect(sources.filter(file => definition.test(file.source)).map(file => file.name)).toEqual([owner]);
37+
expect(definition.test(facade)).toBe(false);
38+
});
39+
40+
it('keeps receipt parsing and obsolete mock routes out of the facade', () => {
41+
expect(facade).not.toMatch(/JSON\.parse[^\n]*(?:pending_prepare|pending_planner|active_run)/);
42+
expect(facade).not.toContain('/time-tunnel/#/free-energy');
43+
expect(facade).not.toContain('/time-tunnel/field-workbench.html');
44+
});
45+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { decideWorkbenchBoot } from './workbench-boot';
3+
4+
describe('serialized FEP workbench boot reducer',()=>{
5+
const decide=(overrides:Partial<Parameters<typeof decideWorkbenchBoot>[0]>)=>decideWorkbenchBoot({hasRunReceipt:false,hasLegacyRunId:false,hasPlannerReceipt:false,hasPreparationReceipt:false,blankRequested:false,...overrides});
6+
it.each([
7+
[{hasRunReceipt:true},'reconcile-run'],
8+
[{hasLegacyRunId:true},'reconcile-run'],
9+
[{hasRunReceipt:true,blankRequested:true},'reconcile-run'],
10+
[{blankRequested:true,hasPlannerReceipt:true,hasPreparationReceipt:true},'new-campaign'],
11+
[{hasPlannerReceipt:true,hasPreparationReceipt:true},'resume-planner'],
12+
[{hasPreparationReceipt:true},'resume-preparation'],
13+
[{},'load-network'],
14+
] as const)('chooses one authoritative lane for %j', (input, expected) => {
15+
expect(decide(input)).toBe(expected);
16+
});
17+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export type WorkbenchBootAction = 'reconcile-run' | 'new-campaign'
2+
| 'resume-planner' | 'resume-preparation' | 'load-network';
3+
4+
export type WorkbenchBootState = Readonly<{
5+
hasRunReceipt: boolean;
6+
hasLegacyRunId: boolean;
7+
hasPlannerReceipt: boolean;
8+
hasPreparationReceipt: boolean;
9+
blankRequested: boolean;
10+
}>;
11+
12+
/**
13+
* Reduce all durable startup evidence to one authoritative recovery lane.
14+
* Keeping this decision pure prevents boot from racing independent restore
15+
* promises whose completion order would otherwise become application state.
16+
*/
17+
export function decideWorkbenchBoot(input: WorkbenchBootState): WorkbenchBootAction {
18+
if (input.hasRunReceipt || input.hasLegacyRunId) return 'reconcile-run';
19+
if (input.blankRequested) return 'new-campaign';
20+
if (input.hasPlannerReceipt) return 'resume-planner';
21+
if (input.hasPreparationReceipt) return 'resume-preparation';
22+
return 'load-network';
23+
}

0 commit comments

Comments
 (0)