--- name: epupp-dev description: 'Epupp architecture, development patterns, and system contracts. Use when: writing or modifying Epupp source code, implementing features, fixing bugs, debugging extension behavior, working with Uniflow actions or effects, understanding message flows, injection or connection lifecycle, testing strategy, build pipeline, reviewing Epupp PRs, planning implementations, running tests or builds, REPL-driven development, or any coding task in the Epupp workspace. Contains architecture overview, Uniflow contracts, source file map, testing patterns, delegation, and development principles. References hold deep state contracts and temporal sequences. IMPORTANT: Also load when PLANNING Epupp changes, not only at implementation time.' --- # Epupp Development Shared knowledge base for developing the Epupp browser extension. Loaded by any agent or mode doing real Epupp work. ## Architecture λ architecture_layers. editor_or_AI → nREPL_client → babashka_relay(browser-nrepl, ports_12345/12346) → background_service_worker(chrome.runtime) → content_bridge(ISOLATED_world, chrome.runtime ↔ postMessage) → ws_bridge(MAIN_world, postMessage ↔ WebSocket) → page_context(Scittle_REPL ↔ DOM) | six_layers | each_boundary ≡ trust_boundary ∧ serialization_point | ¬direct_editor_to_page | always_relayed λ build_pipeline. src/*.cljs → squint_compiler → extension/*.mjs(ESM) → esbuild → build/*.js(IIFE) | config: squint.edn | bb_squint-compile ≡ compile_check | bb_watch ≡ continuous | build/*.css ∧ build/*.html copied_from extension/* | format: IIFE | ¬ESM_in_browser_extension | define: EXTENSION_CONFIG from config/*.edn | separate_bundles: background.js popup.js panel.js content-bridge.js ws-bridge.js trigger-scittle.js userscript-loader.js λ page_runtime. scittle ≡ SCI_in_browser | true_clojure_keywords | userscripts_run_in_page_context | full_DOM_access | async_via ^:async ∧ await | ¬top_level_await | libraries_injected_via :epupp/inject | bundled_scittle_plugins λ injection_model. ensure_scittle! → check_first → inject_only_if_missing | ensure_bridge! → check_first → inject_only_if_missing | duplicate_injection → ¬error | ¬duplicate_state | check_scittle_fn → {hasScittle, hasWsBridge} | both_must_be_true λ content_bridge_security. ISOLATED_world: content_bridge.cljs | ¬page_access | chrome.runtime_access | validates_message_source ∧ type via message_registry | MAIN_world: ws_bridge.cljs | page_access | ¬chrome.runtime_access | trust_boundary: ISOLATED ≡ trusted | MAIN ≡ untrusted ## Principles λ epistemology. assumptions ≡ enemy | benchmark > estimate | measure > guess | verify_before_stating_fix_locations | mark("needs investigation") when_uncertain | failure_to_read_docs ≡ #1_cause_of_mistakes λ data_oriented. what_would_rich_hickey_do | data > objects | fn > class | immutable_core > mutable_shell | transform(data) > mutate(state) | REPL_first: test_functions ∧ explore_data ∧ validate_assumptions_before_coding | simple > easy | separate(data, behavior) | clear > clever λ clojure. definition_order_matters | ¬forward_declares | almost_always ≡ poor_structure | plan_test_strategy: unit(structural ∧ contracts ∧ invariants) ∧ e2e(integration ∧ real_flows) | unit ∧ e2e ≡ complementary_tools | ¬sequential_phases λ api_stability. user_facing_API ≡ commitment: manifest_keys ∧ epupp.fs ∧ REPL_behaviors | preserve_existing | break_only_when(cost_compat > cost_users) | prefer_clean_solutions | ¬contorted_shims | ¬special_case_branching ## Uniflow (compact) λ uniflow_sap. event_loop ≡ SINGLE_ACCESS_POINT for !state | actions: pure_fn(state, uf-data, [action & args]) → {:uf/db :uf/fxs :uf/dxs} | effects: receive(params_from_action) | ¬read(@!state) | ¬transitive_atom_access | ¬swap! ∧ ¬reset! outside_event_loop | ref: dev/docs/architecture/uniflow.md | full_contracts: references/state-contracts.md λ action_naming. actions: :domain/ax.verb-noun | effects: :domain/fx.verb-noun | state: :domain/key-name | messages: "kebab-case-string" ## REPLs and Babashka λ available_repls. bb → babashka_REPL | scripting ∧ automation squint → squint_REPL | test_pure_functions_in_Node.js scittle-dev-repl → scittle_Dev_REPL | test_scittle_code_in_browser_like_env | develop_solutions_incrementally_in_appropriate_REPL λ babashka_utilities. prefer_babashka_builtins > python ∧ shell ∧ external_tools | http_server: bb_test:server ∨ babashka.http-server | ¬python_-m_http.server | file_ops: babashka.fs | ¬shell(cp mv rm find) | process: babashka.process | ¬raw_shell_scripts | http_client: babashka.http-client | ¬curl | ¬wget ## Testing λ testing_pattern. unit_tests: vitest | src/test/*.cljs → test/*.cljs | test_pure_actions: call_handle-action → assert_return_shape | test_data_transforms: call_fn → assert_output | ¬test_effects_in_unit_tests | effects_tested_in_e2e e2e_tests: playwright_in_docker | e2e/*.cljs | test_full_flows: injection ∧ connection ∧ eval ∧ userscripts | bb_test:e2e → docker_build → playwright_run λ truth_hierarchy. browser_page > e2e_test > unit_test > source > docs > assumption | browser_page ≡ ground_truth | where_scittle_actually_runs | ¬trust(green_tests_alone) → verify_in(real_browser) λ debug_approach. 1_context: gather(failing_env ⊗ working_env) | what_data_differs 2_trace: data_flow(input → transform → output) | find_divergence_point 3_fix: root_cause(data_flow) | ¬symptom_patch | six_layer_trace: page_scittle → ws_bridge → content_bridge → background → bb_relay → editor | find_break_point ## Delegation λ subagents. commit → summary_of_task(bigger_picture) | expert_git_agent research → context ∧ what_to_know ∧ report_structure edit → Clojure_editor | files ∧ linenumbers ∧ code ∧ instructions epupp-elaborator → user_prompt ∧ file_context ∧ session_context → refined_prompt | delegation ≡ intelligent | protect_context_window ∧ ensure_quality ## Documentation λ docs_by_task. read_before_starting_work | use(read_file): | understanding_system → architecture_overview → detailed_docs | unit_tests → dev/docs/testing-unit.md | UI(popup/panel) → reagami skill ∧ dev/docs/ui.md | state/events → dev/docs/architecture/uniflow.md ∧ state-management.md | messaging → dev/docs/architecture/message-protocol.md | injection/REPL → dev/docs/architecture/injection-flows.md | library_deps → dev/docs/architecture/library-namespaces.md | userscripts → dev/docs/userscripts-architecture.md | build/release → dev/docs/dev.md | finding_source → dev/docs/architecture/components.md | return_to_index_when_scope_changes λ docs_reference. consult_when_relevant: | README.md → user_facing_overview | dev/docs/architecture/security.md → trust_boundaries ∧ CSP | dev/docs/architecture/build-pipeline.md → build_config_injection λ docs_sync. epupp_repo ≡ source_of_truth | synced_files ≡ generated_artifacts | README.md → my-epupp-hq/docs/epupp-README.md | docs/repl-fs-sync.md → my-epupp-hq/docs/repl-fs-sync.md | docs/connecting-to-epupp.md → my-epupp-hq/docs/connecting-to-epupp.md | ¬edit(synced_copies) | treat_as_generated | ∀doc_updates: edit_here(epupp/) | human_runs(bb_docs-sync)_when_ready ## Source File Map λ source_file_map. src/background.cljs → service_worker | message_routing ∧ init src/popup.cljs → popup_ui | connection_controls ∧ script_list src/panel.cljs → devtools_panel | code_editor ∧ eval src/content_bridge.cljs → ISOLATED_world | message_validation ∧ relay src/ws_bridge.cljs → MAIN_world | WebSocket_relay ∧ page_comms src/trigger_scittle.cljs → page_injection | Scittle_loader src/userscript_loader.cljs → content_script | auto_injection src/event_handler.cljs → uniflow_engine | dispatch ∧ execute src/storage.cljs → chrome.storage_mirror | persist ∧ load src/config.cljs → build_config | dev/prod/test src/manifest_parser.cljs → EDN_manifest_parsing src/script_utils.cljs → script_normalization ∧ ID_generation ∧ library_classification src/scittle_libs.cljs → library_collection ∧ injection src/bg_ws.cljs → background_WebSocket_management src/bg_inject.cljs → content_script_injection src/dep_resolver.cljs → dependency_resolution | pure_resolver ∧ cycle_detection ∧ ext-dep_classification src/ext_dep.cljs → ext_dep_URL_validation ∧ async_fetch_cache src/bg_fs_dispatch.cljs → FS_message_routing ∧ optional_:uf/dxs_dispatch src/popup_actions.cljs → popup_uniflow_actions src/panel_actions.cljs → panel_uniflow_actions src/reagami.cljs → minimal_UI_rendering_library src/background_actions.cljs → background_uniflow_actions | re-resolution src/background_actions/*.cljs → background_action_modules ## Deep References Load as needed per task: | Reference | When to load | |-----------|-------------| | [state-contracts.md](references/state-contracts.md) | Adding/modifying actions, effects, state shapes | | [message-protocol.md](references/message-protocol.md) | Debugging message relay, adding messages | | [temporal-sequences.md](references/temporal-sequences.md) | Debugging connection/injection flows | | [decision-rules.md](references/decision-rules.md) | Architecture decisions, "why does X happen" |