Repository navigation
Extension API: a documented facade app_logic::ext with accessors instead of AppState fields #878
Copy link
Copy link
Labels
app-shelldocumentationImprovements or additions to documentationImprovements or additions to documentationenhancementNew feature or requestNew feature or request
Milestone
Description
Goal
Extension crates that plug into an embedding app through app_shell::Wiring use one documented, narrow module of app-logic (app_logic::ext) instead of internal modules and public AppState fields. Internal refactorings of the core then no longer break extensions silently, and the core knows which of its items are extension API.
Context
crates/app-logic/src/lib.rsdeclares 52pub mod; nothing says which items are meant for extensions.AppState(crates/app-logic/src/state.rs:51) has 28 public fields.- Extensions built on the
Wiring(crates/app-shell/src/wiring.rs:30-76) need, from these modules:session_extension::{SessionExtension, ServerScope, scope_for_id, rebuild_server_redactors, rebuild_redactors_for_sessions},system_prompt::PromptLanguagechat_content_extension::{ChatContentExtension, ResolvedBlock, …},orchestration::accept_extension_contentai_usage::{AiUsageObserver, AiUsagePurpose, AiUsageRecord}backup::{Snapshot, ChangeSet, Change, HostKeyRow, KNOWN_SECTIONS, apply_changes, start_import_snapshot, BackupOpError, OpenSessions},host_key_store::FileHostKeyStoredto::{is_local, DocumentFormat},error::CommandErrorevents::{EventEmitter, ConnectionStatus, emit_connection_status_changed}- and they read the
AppStatefieldsprofile_store,sessions,backup_store,file_host_keysafterapp.try_state::<AppState>()(e.g. to rebuild the redactors of every server with an open session: loop overstate.sessions.snapshot()+rebuild_server_redactors).
app-shellalready re-exports its extension items at the crate root (Wiring,DbExtension,BackgroundService,ServiceContext,RunningService);app-logichas no equivalent.app-shellitself uses theAppStatefields directly, so they cannot becomepub(crate)in this step.
Requirements
- New module
crates/app-logic/src/ext.rs(pub mod ext;inlib.rs) with a//!doc: it is the supported surface for extension crates; items in it change only together with a changelog entry and the spec; everything outside it may change without notice. extre-exports (pub use) all items listed under Context, grouped by topic (session extension, chat content, AI usage, backup/snapshot, events, errors, helpers), plusstate::AppStateitself as a type (extensions need it forapp.try_state::<AppState>(); its fields are not part of the facade).- Accessors so extensions need no
AppStatefield access, as functions inexttaking&AppState:profile_store(&AppState) -> &dyn ProfileStorebackup_store(&AppState) -> &SqliteBackupStore(or the narrowest type the backup functions accept)file_host_keys(&AppState) -> &FileHostKeyStoreopen_session_server_ids(&AppState) -> Vec<ServerId>(sorted, deduplicated)rebuild_redactors_for_open_sessions(&AppState)– rebuilds the redactor of every server that has an open session (the loop extensions write themselves today).
ext::test_supportre-exportscrate::test_supportunder the samecfg(any(test, feature = "test-support"))gate, nothing more.- The doc comment on
AppStatepoints extension authors toext; the fields staypubforapp-shell. - Additive only: no existing path is removed or renamed; no behaviour change.
- Spec 0038 gets a section "Extension API (
app_logic::ext)" that lists the facade by topic and states the stability rule;docs/architecture.mdnamesapp_logic::extnext to theapp-shellroot re-exports. - The public session close path from One public path to close a session completely, used by disconnect, backup import and embedding app crates #816 (
app-shell, blocks this issue) is the one way for extensions to close sessions; the spec section names it next to theapp_logic::extitems.
Acceptance Criteria
-
app_logic::extexists, has a//!doc with the stability rule and re-exports every item listed under Context. - The five accessors exist;
open_session_server_idsreturns each server once even with two open sessions on it (unit test);rebuild_redactors_for_open_sessionsrebuilds the redactor of each such server and of no other (unit test, e.g. with a recording session extension and two servers of which one has no open session). - New integration test
crates/app-logic/tests/ext_facade.rsthat imports only fromapp_logic::ext(andssh_manager_core), implements aSessionExtension, aChatContentExtensionand anAiUsageObserver, and calls every accessor on a testAppState; it compiles and passes. - No existing public path of
app-logicis removed (the workspace and every existing test build unchanged). - Spec 0038 and
docs/architecture.mddescribe the facade as above. - Relevant automated tests pass (
cargo test --workspace,cargo clippy --workspace --all-targets -- -D warnings).
Technical Notes
- Pure additive API work in
crates/app-logic(lib.rs, newext.rs,state.rsdoc comment, possibly a small helper insession_extension.rs), plus docs. rebuild_redactors_for_open_sessionstouches redaction: it must only call the existingrebuild_server_redactorspath (literal secrets only extend the redactor, never weaken it). See.claude/skills/smart-ssh-coder/references/security-invariants.md(Redactor).- Do not move items into
ext; re-export them, so existing paths keep compiling. - The local file handle / URI opener of
app-shellis covered by Export the local file handle and URI opener from app-shell as public API #791, not here.
Spec impact: update 0038
Open questions
- Accessor style: free functions in
ext(recommended – keepsAppStateunchanged and the facade in one file) or inherent methods onAppState(names would collide with the public fields).
Related Work
- Export the local file handle and URI opener from app-shell as public API #791 (local file handle and URI opener as public API of
app-shell) - One public path to close a session completely, used by disconnect, backup import and embedding app crates #816 (one public path to close a session; blocks this issue)
Metadata
Metadata
Assignees
Labels
app-shelldocumentationImprovements or additions to documentationImprovements or additions to documentationenhancementNew feature or requestNew feature or request
Type
Fields
Priority
P2 - Normal
Activity
feat(app-logic): add documented extension facade app_logic::ext (#878)
smart-ssh-coder commentedon Oct 27, 2026
Implementation ready for review. PR #986. Added app_logic::ext facade (re-exports, five accessors, test_support), docs in Spec 0038 and architecture.md, unit tests with counter-proof and integration test ext_facade; CI green on PR #986.
smart-ssh-reviewer commentedon Oct 27, 2026
Review passed – moving on to merge. Approved. app_logic::ext facade, accessors, spec 0038 and architecture docs match the issue; tests and CI green; counter-proof confirmed. Non-blocking follow-up: cover the production rebuild_redactors_for_open_sessions loop with a real AppState.
feat(app-logic): add documented extension facade app_logic::ext (#878) (
smart-ssh-coordinator commentedon Oct 27, 2026
Merged and branch deleted: PR #986 (squash) in develop
smart-ssh-planner commentedon Oct 27, 2026
Follow-ups filed: #989 Test the production rebuild_redactors_for_open_sessions loop with a real AppState.