
HiddenSteps — una piattaforma local-first di intelligence personale per i flussi di lavoro
Questo è il complemento onesto, allo stato attuale, della mappa dei moduli target di docs/design/02-system-architecture.md. Indica cosa è effettivamente costruito, cosa è verificato contro un backend reale rispetto a un mock e cosa manca ancora davvero — non cosa è pianificato (quello è in docs/roadmap/01-implementation-roadmap.md).
Esegui cargo build --workspace && cargo test --workspace && cargo clippy --workspace --all-targets -- -D warnings dalla radice del repository. Al momento della stesura: 12 crate, 193 test superati, zero warning clippy, cargo fmt --check pulito — 183 nelle 11 crate che non richiedono display o servizi esterni, più 10 in hiddensteps-observation che richiedono un display X11 attivo (verificato dove presente; vedi quella riga). Quattro test sono contrassegnati con #[ignore] di proposito (vedi sotto) e non sono conteggiati né come fallimenti né come parte dei 193.
Inoltre, fuori da crates/ (non fa parte del workspace root — vedi sotto il perché):
hiddensteps-event-store con similarità coseno calcolata in Rust, al posto della tabella virtuale sqlite-vec di ADR-0007 (vedi il commento in cima a event-store/src/schema.sql) — caricare un'estensione SQLite nativa non era verificabile in questo ambiente, e ADR-0007 stesso nota che a volumi realistici di singolo utente, il comportamento di sqlite-vec è una ricerca esatta a forza bruta. Stessa semantica, nessun rischio di estensione nativa.hiddensteps-observation (src/macos/, src/windows/) sono sorgenti reali e complete contro API di piattaforma stabili da lungo tempo (CGWindowListCopyWindowInfo; GetForegroundWindow/GetWindowTextW/QueryFullProcessImageNameW) scritte senza una toolchain macOS/Windows disponibile — ed entrambi ora compilano puliti, verificato dalla matrice di job di . Il modulo macOS ha prima richiesto una correzione reale (i parametri generici di default non tipizzati di non soddisfacevano il trait bound di per una chiave — corretto tipizzandolo esplicitamente come ); Windows ha compilato pulito al primo tentativo.crates/crates/* è il workspace del Cargo.toml root ed è completamente compilabile/testabile in questo ambiente di sviluppo Linux con zero dipendenze di sistema oltre a quelle che cargo scarica. apps/desktop/src-tauri richiede webkit2gtk-4.1 (Linux) anche solo per compilare, che questo ambiente non può installare (nessun sudo senza password, nessun percorso nix/gestore di pacchetti funzionante — confermato per tentativo diretto). Tenerlo fuori dal workspace significa che cargo build --workspace rimane 100% verde qui invece di essere permanentemente rosso su una crate che nessuno può sistemare in questo sandbox. Richiede anche una propria tabella [workspace] vuota nel suo Cargo.toml per lo stesso motivo — altrimenti Cargo prova comunque ad agganciarlo a questo workspace antenato e fallisce con "current package believes it's in a workspace when it's not." apps/desktop/ui non ha questo vincolo ed è verificato nello stesso modo del core Rust. Entrambi i pezzi sono comunque verificati end-to-end — solo dalla CI invece che da questo sandbox.
#[ignore]hiddensteps-security::keyring_store::tests::set_get_delete_round_trip_against_the_real_vault — richiede un vault di credenziali OS reale/una sessione desktop.hiddensteps-observation::linux::shortcuts::tests::grabs_and_ungrabs_a_real_shortcut — esegue una XGrabKey reale a livello di sessione, che sarebbe di disturbo se eseguita automaticamente in un ambiente condiviso.tests/ollama_live.rs di hiddensteps-llm-provider (2 test) — richiede un'istanza Ollama reale in esecuzione. Entrambi sono stati effettivamente eseguiti durante lo sviluppo contro un'istanza locale reale qwen3:0.6b (modello di ragionamento ibrido da 0.6B parametri) e superati in ~2 secondi complessivi; sovrascrivi il modello/URL tramite le variabili d'ambiente HIDDENSTEPS_TEST_OLLAMA_MODEL/HIDDENSTEPS_TEST_OLLAMA_URL per una configurazione diversa.Tutti e quattro sono test reali, non vestigiali — docs/roadmap/03-testing-strategy.md §2 traccia esattamente questa distinzione tra la logica che appartiene dietro un mock in CI e l'integrazione OS/sessione/servizio esterno che appartiene a una verifica manuale e deliberata. Esegui uno qualsiasi di essi con cargo test -p <crate> -- --ignored (aggiungi <test name> per eseguirne solo uno) su una macchina dove farlo è appropriato.
| Crate | Implementa | Come verificato |
|---|
hiddensteps-domain | Tipi fondamentali: PrivacyLevel/PrivacyState, EventSummary/SignalType, Pattern, Recommendation, AuditEntry e CapturedSignal — un tipo che strutturalmente non può essere persistito (nessun Serialize), applicando la regola dei dati grezzi di ADR-0006 a livello di tipo | Test unitari: round-trip/ordinamento dei livelli, gating TTL della Deep Mode |
hiddensteps-security | SecretStore (ADR-0008): vault di sistema reale (KeyringSecretStore) + implementazioni in-memory (di test); generazione della chiave master CSPRNG (restituita in un wrapper zeroize::Zeroizing così la chiave viene azzerata al drop invece di rimanere nella memoria liberata); derivazione della passphrase Argon2id per la Portable Mode (PassphraseKey azzera la chiave derivata al drop, conservando il salt non segreto). Allo stesso modo hiddensteps-event-store conserva il testo SQL PRAGMA key/rekey contenente la chiave in Zeroizing | Test unitari contro lo store in-memory e il KDF; il round trip con il vault reale è contrassegnato con #[ignore] (vedi sotto) |
hiddensteps-event-store | SqlCipherEventStore (ADR-0003): lo schema completo da docs/design/07-database-schema.md, CRUD per stato della privacy, eventi, registro di audit, pattern, collegamenti pattern↔evento, embedding dei pattern (vedi nota sotto), raccomandazioni, configurazione del provider LLM e impostazioni generiche, più delete_all_data (transazionale; esegue anche un rekey per un "cancella tutto" che sopravvive a un riavvio)/export_data/count_rows (diagnostica)/delete_expired_events (la pulizia TTL della Deep Mode, chiamata dal loop periodico di raccomandazioni di apps/desktop/src-tauri — ttl_expires_at veniva persistito dalla v0.1.0 ma prima d'ora nulla eliminava una riga oltre quella scadenza); applicazione delle foreign key (PRAGMA foreign_keys = ON) così che l'ON DELETE CASCADE di schema.sql sui collegamenti pattern↔evento venga effettivamente eseguito | 33 test contro un file SQLCipher reale: la chiave sbagliata non apre il file, la stessa chiave riapre correttamente, delete-all pulisce ogni tabella incluse le più recenti, il round trip di rekey funziona, la pulizia TTL lascia intatti gli eventi non scaduti, il delete a cascata non lascia collegamenti pattern↔evento orfani |
hiddensteps-redaction | Il motore di redazione (docs/design/05-privacy-model.md §4): rilevatori regex+Luhn per chiavi API/token/chiavi PEM/email/SSN/carte di credito, un rilevatore di segreti ambigui basato sull'entropia e la politica di eliminazione in caso di incertezza | 30 test, inclusi input deliberatamente avversari (segreti incorporati in testo, non-segreti molto vicini come gli SHA di git, SSN senza trattini o con spazi, numeri di carta con cifre di padding, token ad alta entropia tutti nella stessa maiuscola/minuscola) |
hiddensteps-pipeline | La pipeline degli eventi (ADR-0006): Classifica → Redigi → Riassumi, gating per livello di privacy in base al tipo di segnale, assegnazione TTL della Deep Mode | 8 test che coprono eliminazioni innescate dalla redazione, eliminazioni per gating di livello e riassunzione riuscita |
hiddensteps-observation | ObservationSource (ADR-0005) + Linux: ActiveWindowSource (X11 GetInputFocus), FileOperationSource (inotify tramite notify), ClipboardMetadataSource (selezione X11, solo metadati), GlobalShortcutSource (X11 XGrabKey). Più i file sorgente per macOS/Windows (vedi sotto) | 10 test su 11 eseguiti contro backend reali in questo ambiente — un display X11 attivo (il DISPLAY=:0 di WSLg) e inotify reale, non mock. 1 test (la cattura reale di GlobalShortcutSource) è contrassegnato con #[ignore] di proposito |
hiddensteps-llm-provider | LlmProvider (ADR-0004): client Ollama (con un campo di richiesta think: Option<bool> per i modelli di ragionamento ibrido), un client compatibile con il wire di OpenAI (copre OpenAI/Azure/OpenRouter/Together/Groq/DeepSeek/LocalAI), un client Anthropic Messages e auto-rilevamento del runtime locale. Ogni client imposta un timeout di richiesta (build_http_client) così che un remoto bloccato non possa bloccare una chiamata per sempre; Ollama inoltra max_tokens come suo options.num_predict annidato | 19 test contro server mock wiremock (incluso un controllo reale che il timeout scatti e che Ollama invii effettivamente num_predict), più 2 test di integrazione con Ollama reale (tests/ollama_live.rs, contrassegnati con #[ignore] — vedi sotto) che hanno trovato e corretto un problema reale: lo stesso prompt impiegava oltre due minuti contro un vero modello locale di ragionamento ibrido con think lasciato al default, e pochi secondi con think: Some(false) |
hiddensteps-patterns | Rilevamento dei pattern (matching di sequenze n-gram a finestra scorrevole) + Grafo del flusso di lavoro (grafo di transizione con pesi sugli archi) — Livello 1 di ADR-0010 | 16 test, incluso un analogo diretto dell'esempio "observed 31 times" dello stesso PROMPT.md e un test di regressione che verifica che finestre sovrapposte su una ripetizione continua non vengano conteggiate due volte |
hiddensteps-recommendations | Il Livello 2 del motore di raccomandazioni (ADR-0010): sintesi LLM con un contratto di prompt JSON strutturato, un validatore di contraddizioni narrative e un loop di retry — fondamentalmente, i campi numerici (estimated_time_saved_minutes) non vengono mai parsati dall'output dell'LLM, ma solo calcolati dal Livello 1 | 23 test, inclusi retry su JSON malformato, retry per contraddizione narrativa (che coprono i numeri scritti in lettere e ogni campo controllato dall'LLM, non solo why) ed estrazione JSON consapevole delle stringhe, contro un provider di test scriptato |
hiddensteps-privacy-engine | Il gate di invio al cloud (docs/design/03-data-flow-diagrams.md §5) e il versionamento del consenso (docs/design/05-privacy-model.md §5); PrivacyGatedProvider incapsula qualsiasi LlmProvider così che il gate non possa essere aggirato dal normale percorso di chiamata | 13 test, incluso che il contenuto di Livello 4 sia bloccato anche con ogni consenso concesso |
hiddensteps-plugin-host | L'host per plugin WASM (ADR-0009): enumerazione chiusa delle capability, validazione del manifest, una sandbox basata su wasmtime che collega solo le funzioni host delle capability concesse, più fuel metering e un ResourceLimiter di memoria che limita CPU/memoria di un'istanza di plugin indipendentemente dalle capability che possiede — i due assi che la sezione Denial-of-Service di docs/research/06-threat-model.md indica come non affrontabili dalla sola applicazione delle capability (un modulo senza capability può comunque fare loop o crescere in memoria per sempre). instantiate_from_manifest è il punto di ingresso sicuro: forza la validazione del manifest (la regola Livello-4-richiesto-per-lo-screenshot) e rifiuta di concedere qualsiasi cosa non dichiarata dal manifest, prima che qualsiasi capability raggiunga il linker — la semplice slice di capability instantiate non ha alcun collegamento con un manifest | 20 test, inclusi tentativi reali di fuga dalle capability: moduli WAT scritti a mano e compilati al momento del test, che dimostrano che l'import di una capability non concessa è genuinamente irrisolto (l'istanziazione fallisce), non semplicemente inutilizzato; più un modulo con loop infinito reale e un modulo con memory.grow illimitato che generano trap invece di bloccarsi/esaurire la memoria |
hiddensteps-enterprise-policy | Schema della policy (docs/design/05-privacy-model.md §6) con esattamente due manopole (livello minimo di privacy, allowlist dei provider) — non esiste alcun campo per qualsiasi altra cosa una policy potrebbe voler limitare. Caricato da un file enterprise-policy.json nella directory dati dell'app se presente (un meccanismo reale, seppur provvisorio — il connettore completo PolicyLoader descritto in docs/design/08-plugin-architecture.md non è costruito), persistito tramite la tabella enterprise_policy di hiddensteps-event-store e effettivamente applicato nei comandi set_privacy_level/set_ai_provider di apps/desktop/src-tauri — i due punti di mutazione da cui una scelta di livello/provider viene mai scritta | 6 test, incluso il parsing di un file di policy massimamente avversario con cinque chiavi extra escluse by-design e la conferma che nessuna di esse sopravvive al parsing |
| Posizione | Implementa | Come verificato |
|---|
../apps/desktop/ui | UI React/TypeScript: OnboardingWizard (tutte le 8 schermate, docs/ux/02), PrivacyDashboard (docs/ux/03), RecommendationCard (docs/ux/04), SettingsPage, DiagnosticsPage, collegati insieme in App.tsx — che comunicano con il core solo tramite un tauriBridge.ts tipizzato | 50 test tramite vitest + @testing-library/react contro rendering jsdom reale, inclusi il gating dei passi del wizard di onboarding (nessun avanzamento oltre la validazione senza un controllo riuscito, nessun avvio dell'osservazione senza aver spuntato il consenso), l'esposizione degli errori in ogni punto di chiamata di mutazione, il banner di nuovo consenso, la traccia delle prove della raccomandazione e un gate di accessibilità axe-core; tsc -b typecheck pulito |
../apps/desktop/src-tauri | La shell Tauri: ~21 comandi IPC (docs/design/09-api-specification.md) che collegano insieme ogni crate sopra, più un loop background reale cattura→pipeline→store→evento-UI per Linux | Compila pulito su Linux, macOS e Windows in CI — non in questo sandbox di sviluppo nello specifico (vedi ../apps/desktop/README.md per il perché), ma l'avvertenza "non verificato" che c'era qui è sparita: la prima esecuzione reale della CI ha trovato e corretto 3 bug reali (una derive Serialize mancante, un feature flag Cargo mancante, un file icona generato mancante) che nessuna quantità di revisione locale avrebbe individuato |
core.github/workflows/ci.ymlCFDictionaryfind&CFStringCFDictionary<CFString, CFType>hiddensteps-observation/src/lib.rs — il primo richiede un artefatto separato di estensione browser che questo repository non contiene; il secondo (GlobalShortcutSource) è implementato ma mai avviato automaticamente, perché catturare una combinazione di tasti a livello di sessione in un sandbox di sviluppo condiviso sarebbe attivamente di disturbo.LlmProvider sia configurato; è stato testato contro un provider sostitutivo scriptato (asserzioni reali sulla logica di retry/validazione). hiddensteps-llm-provider stesso ora ha una copertura con Ollama reale (vedi sotto); eseguire il contratto di prompt del sintetizzatore contro un modello reale end-to-end (piuttosto che il client HTTP sottostante) è il passo naturale successivo, non ancora fatto.get_diagnostics (la shell Tauri) riporta conteggi reali di eventi/pattern/raccomandazioni/registro di audit e la dimensione reale dei file su disco, ma non l'uso di GPU/CPU/memoria, lo stato dei permessi OS per l'osservazione o lo stato degli aggiornamenti — la lista Self-Diagnostics completa di PROMPT.md. Ogni componente UI che visualizza questo lo dice esplicitamente invece di mostrare un "OK." inventato.