Add full shell, replaceable Omics adapter and bilingual interaction

Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
Codex
2026-09-13 14:26:39 +02:00
parent 2d1b714ebe
commit d8a29bfbdd
207 changed files with 8570 additions and 2164 deletions
@@ -0,0 +1,101 @@
# ADR 0021 — Shell separati e adapter sostituibile per il portale
- Stato: accettato
- Data: 2026-09-13
## Decisione
ThothII espone due modalità di installazione, selezionate da `shell.mode`:
- `embedded` (default): ThothII è ospitato da Omics Portal. Non renderizza alcun header e
riceve dal portale lingua, tema e fullscreen. L'accesso resta verificato dal server.
- `full`: ThothII è autonomo. Renderizza il proprio header, con selettore lingua, tema,
fullscreen e nome utente. Il click sul nome apre il logout. Non mostra mai la rotellina o
altri comandi amministrativi del portale. Mantiene un rail vuoto a sinistra di almeno 20 px.
Il fatto che la shell sia `full` è distinto dallo stato `fullscreen`: la prima decide quale
contenitore viene renderizzato, il secondo indica se è attiva la Fullscreen API del browser.
L'icona passa da “entra in fullscreen” a “torna alla modalità normale”; anche `Esc` aggiorna lo
stato visualizzato.
La configurazione installata resta semplice e retrocompatibile. Sul Mac di sviluppo il profilo
locale userà:
```yaml
shell:
mode: full
defaultLocale: en
```
Il deploy sul server userà invece:
```yaml
shell:
mode: embedded
adapter: omics-portal
```
`defaultLocale` indica la lingua iniziale della shell full; in embedded la fonte autorevole resta
il portale.
L'adapter è l'unico confine tra ThothII e il portale. La sua interfaccia pubblica è volutamente
profonda e minima: consegna solo snapshot dello stato, senza esporre comandi, token, identità o
dettagli di trasporto.
```ts
export type HostShellState = {
locale: string; // BCP-47, inizialmente it/en
theme: "light" | "dark";
fullscreen: boolean;
};
export interface PortalAdapter {
subscribe(
onState: (state: HostShellState) => void,
onError: (error: Error) => void,
): () => void;
}
```
`OmicsPortalAdapter` è l'implementazione corrente. Un adapter per un altro portale potrà
sostituirlo senza modificare shell, i18n o workflow. In `full` l'adapter non viene istanziato:
lo stato è gestito internamente dalla shell.
Per l'integrazione oggi operativa, che monta la SPA direttamente nel DOM di Omics Portal,
l'adapter legge la lingua effettiva dal selettore Omics, osserva l'attributo del tema e ascolta
il fullscreen del documento. Selettori e osservatori restano privati dell'implementazione Omics.
La lingua segue il normale ricaricamento Django; tema e fullscreen cambiano nella pagina aperta.
La revisione approvata del 2026-09-13 elimina il precedente handshake a eventi: non servono
messaggi personalizzati, versioni di trasporto o timeout di avvio. Un futuro adapter potrà usare
un diverso trasporto senza modificare l'interfaccia applicativa.
Se `shell` o l'adapter embedded sono omessi, si usa `embedded` con `omics-portal`. Questo default
supporta il documento Omics esistente; nomi adapter sconosciuti o dati host mancanti producono
un errore esplicito, senza attivare la shell full.
## Confini che restano invariati
L'identità e l'autorizzazione del backend non vengono ricostruite nel browser. In embedded,
Omics Portal continua a gestire login e logout e la catena server-side `auth_request` continua a
fornire i principal header già previsti. Lo stato UI non dichiara l'utente autenticato: il modulo
di accesso usa la verifica backend esistente anche alla riconnessione e al ritorno alla pagina.
Un rifiuto su una singola operazione non equivale automaticamente alla perdita dell'accesso.
La lingua UI e la lingua di interazione con il modello restano separate dalla lingua del
workspace; il relativo contratto è in [ADR 0022](0022-separate-ui-locale-from-session-interaction-language.md).
## Alternative scartate
- Duplicare l'header di Omics in embedded: crea due fonti di stato e incompatibilità visive.
- Spargere controlli `if embedded/full` nei componenti: lega ogni pagina al portale.
- Trasmettere utente o token nel bridge: aumenta superficie e accoppia UI e autenticazione.
- Introdurre un protocollo completo request/response: non aggiunge funzionalità richiesta.
- Usare `profile` per distinguere le shell: `profile` descrive la topologia dell'installazione,
non la sua presentazione.
## Conseguenze
La soluzione richiede un adapter nel frontend che osserva il documento condiviso e mantiene la
logica di shell locale a ThothII. Il backend non necessita di un nuovo protocollo di autenticazione
o di una nuova sessione browser. Un nuovo portale deve soddisfare anche il contratto server di
identità fidata: la sola sostituzione della classe UI non sostituisce quel contratto.
@@ -0,0 +1,51 @@
# Separate UI locale from session interaction language
status: accepted
ThothII distinguishes three language concepts:
- `workspace.language` remains the language of workspace-owned documents, descriptions and Evidence;
- `ui_locale` controls deterministic ThothII chrome such as labels, form help, placeholders, errors,
accessibility text and review-widget chrome;
- `interaction_language` is persisted in a session and controls model-generated questions,
explanations and reviewer proposals.
The initial locale catalog supports Italian and English and uses extensible BCP-47 language tags.
Missing deterministic translations fall back to English. The selected UI locale supplies the default
interaction language when a new session is created. A resumed session always uses its persisted
interaction language; changing the host or full-shell UI locale must not silently rewrite an existing
session or make its model output switch language mid-workflow.
The distinction is required because the current workspace contract already uses `language` for
content and the PSD workspace is Italian. Reusing that field for a browser preference would make a
visual choice mutate domain content semantics. The model receives the session interaction language
through the session/Pi workflow context. SQL, identifiers, database values and other technical
artifacts remain governed by their existing contracts and are not translated as UI strings.
In `full`, the local shell owns `ui_locale` and supplies it when starting a new session. In
`embedded`, the host adapter is authoritative for `ui_locale`; ThothII applies host changes to
deterministic UI immediately while preserving the interaction language of any active session.
We considered using only `workspace.language`, using only a global browser locale, and translating
the model output after generation. The first conflates domain content with UI preference; the second
cannot preserve a session's language or follow the host portal; and the third would be unsafe for
structured reviewer decisions and would not control the model's reasoning or proposal language.
## Considered Options
- One mutable `language` field for workspace, UI and session was rejected because the fields have
different owners and lifecycles.
- Client-only translation of reviewer choices was rejected because choices can be generated by the
model and must be requested in the intended language.
- An English-only deterministic chrome was rejected because embedded and full installations must
follow the selected host/user language.
## Consequences
- Session creation and the persisted manifest gain an explicit interaction-language value.
- Legacy manifests without that value use the workspace language, pinned idempotently on first
resume; the browser locale must not determine this compatibility value.
- Resume must read that value from the manifest and must not accept a new locale as an override.
- The workflow prompt contract and deterministic reviewer-widget builders need a locale-aware input.
- Frontend strings need a catalog and stable keys; backend events should expose stable codes where
the frontend is responsible for localization.