Files
bbs-md/.planning/phases/01-safety-foundation/01-03-SUMMARY.md
T
ruohki d9db04fb01 docs(01-03): complete app event loop and Phase 1 wiring plan
- 01-03-SUMMARY.md: documents App struct, event loop, double-Ctrl+C state machine, main.rs pipeline
- STATE.md: Phase 1 complete, 3/3 plans done, Phase 2 next, 01-03 decisions recorded
- ROADMAP.md: Phase 1 marked Complete (3 plans / 3 summaries)
- REQUIREMENTS.md: SHEL-01 marked complete
2026-02-28 21:20:02 +01:00

134 lines
6.2 KiB
Markdown

---
phase: 01-safety-foundation
plan: 03
subsystem: app
tags: [rust, ratatui, crossterm, tui, event-loop, signal-polling, double-ctrl-c, login-shell, goodbye-message]
# Dependency graph
requires:
- phase: 01-01
provides: "Config struct, load_config(), detect_login_shell(), parse_cli(), terminal.rs scaffold"
- phase: 01-02
provides: "init_terminal(), restore_terminal(), install_panic_hook(), SignalFlags, register_signals()"
provides:
- "src/app.rs: App struct, run_event_loop(), handle_key(), draw(), show_goodbye()"
- "src/app.rs: ShutdownReason enum (UserQuit, Signal)"
- "src/app.rs: DOUBLE_PRESS_WINDOW const = 2 seconds"
- "src/main.rs: complete startup -> event loop -> shutdown pipeline"
- "Complete runnable Phase 1 application: cargo run produces working BBS TUI"
affects:
- "02-content-rendering (event loop receives content model in Phase 2)"
- "all future phases (event loop is the central integration point)"
# Tech tracking
tech-stack:
added: []
patterns:
- "Signal polling before draw: check SignalFlags.should_terminate() first each loop iteration"
- "Double-press window: ctrl_c_pressed_at Option<Instant> compared with elapsed() < DOUBLE_PRESS_WINDOW"
- "Login shell suppression: is_login_shell flag disables 'q' key shortcut in handle_key()"
- "ratatui::crossterm re-export path: all event polling uses ratatui::crossterm::event"
- "BrokenPipe silent exit: event loop propagates io::Error; main catches ErrorKind::BrokenPipe"
- "Goodbye after restore: restore_terminal() called first, then show_goodbye() prints to stdout"
key-files:
created:
- src/app.rs
modified:
- src/main.rs
key-decisions:
- "show_goodbye() called AFTER restore_terminal() — terminal must be in cooked mode before println! is safe"
- "app_config stored in App struct even unused in Phase 1 — avoids dead_code suppression anti-patterns and prepares Phase 2 access"
- "quit prompt cleared on any non-Ctrl+C key — pressing Enter/arrow/letter dismisses the double-press window"
patterns-established:
- "Signal-first event loop: signals.should_terminate() is always the first check before draw"
- "Layered shutdown: restore_terminal() unconditional, then match shutdown_reason for post-restore actions"
- "Login shell mode: is_login_shell propagated from main -> App::new, controls key behavior throughout"
requirements-completed: [SHEL-01]
# Metrics
duration: 2min
completed: 2026-02-28
---
# Phase 1 Plan 03: App Event Loop and Wiring Summary
**Double-press Ctrl+C state machine with login-shell 'q' suppression, signal-polling event loop, BBS goodbye message, and complete startup-to-shutdown pipeline wired in main.rs**
## Performance
- **Duration:** 2 min
- **Started:** 2026-02-28T20:15:54Z
- **Completed:** 2026-02-28T20:17:54Z
- **Tasks:** 2
- **Files modified:** 2
## Accomplishments
- App struct implements double-press Ctrl+C with a 2-second window using Option<Instant> elapsed comparison
- Login shell mode suppresses the 'q' key shortcut — only double Ctrl+C exits, enforcing SHEL-01
- Event loop checks SignalFlags.should_terminate() before each draw — fast path for SSH disconnect (SIGHUP/SIGTERM)
- Phase 1 placeholder TUI renders a centered BBS-MD block with quit prompt in yellow when Ctrl+C is first pressed
- show_goodbye() prints BBS-style "CARRIER LOST" message and sleeps 500ms after terminal is restored
- main.rs wires the full pipeline: config load -> panic hook -> signal registration -> terminal init -> event loop -> restore -> goodbye
## Task Commits
Each task was committed atomically:
1. **Task 1: Implement App struct and event loop with exit behavior** - `bad8fba` (feat)
2. **Task 2: Wire complete startup-to-shutdown pipeline in main.rs** - `771c9a8` (feat)
**Plan metadata:** (recorded after final commit)
## Files Created/Modified
- `src/app.rs` - App struct with run_event_loop(), handle_key(), draw(); ShutdownReason enum; show_goodbye(); DOUBLE_PRESS_WINDOW const
- `src/main.rs` - Complete startup-to-shutdown pipeline with all four modules declared and wired
## Decisions Made
- Called `restore_terminal()` unconditionally before the `match shutdown_reason` block. This ensures terminal is always in cooked mode before `show_goodbye()` prints to stdout, and before any `eprintln!()` on unexpected I/O errors.
- Stored `app_config` in the `App` struct (via `App::new(is_login_shell, app_config)`) rather than using `let _app_config`. Phase 2 will read `config.vault_path` from inside the event loop, so putting it on App now avoids a refactor later.
- Quit prompt is cleared on any non-Ctrl+C key press. This means pressing Enter, arrow keys, or typing any character will dismiss the "Press Ctrl+C again" prompt — matches natural user expectations.
## Deviations from Plan
None - plan executed exactly as written.
The only note: the plan listed bare `crossterm::event` in the imports example. Per the 01-02 deviation (crossterm is a transitive dep only), all imports use `ratatui::crossterm::event` instead. This was the established pattern from Plan 02 and applied automatically without needing a new deviation entry.
## Issues Encountered
None. Build compiled cleanly on first attempt. All key verification checks passed:
- No `unwrap()` in terminal.rs or app.rs
- No `LeaveAlternateScreen` in executable code (only in doc comments)
- DOUBLE_PRESS_WINDOW constant present and used in event loop
- All key link patterns confirmed (should_terminate, restore_terminal, run_event_loop, load_config, init_terminal)
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 1 is complete: all safety requirements (LIFE-01 through LIFE-04, CONF-01, SHEL-01, SHEL-02) are implemented
- The event loop in app.rs is the integration point for Phase 2 content rendering — add content model to App struct and call draw functions from draw()
- `app_config.vault_path` is already accessible inside App for Phase 2 vault loading
- Blocker noted for Phase 2: verify ratatui 0.30 Widget trait signature (changed between 0.28 and 0.29) before building BBS widgets
---
*Phase: 01-safety-foundation*
*Completed: 2026-02-28*
## Self-Check: PASSED
- src/app.rs: FOUND
- src/main.rs: FOUND
- 01-03-SUMMARY.md: FOUND
- Commit bad8fba (Task 1): FOUND
- Commit 771c9a8 (Task 2): FOUND