From d9db04fb01afd29aff92345482f4fa0cbe86c0ed Mon Sep 17 00:00:00 2001 From: ruohki Date: Sat, 28 Feb 2026 21:20:02 +0100 Subject: [PATCH] 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 --- .planning/REQUIREMENTS.md | 4 +- .planning/ROADMAP.md | 6 +- .planning/STATE.md | 31 ++-- .../01-safety-foundation/01-03-SUMMARY.md | 133 ++++++++++++++++++ 4 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 .planning/phases/01-safety-foundation/01-03-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index c9bf6f1..0eed129 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -57,7 +57,7 @@ Requirements for initial release. Each maps to roadmap phases. ### Shell Integration -- [ ] **SHEL-01**: App exits cleanly with q or Ctrl+C, restoring terminal state +- [x] **SHEL-01**: App exits cleanly with q or Ctrl+C, restoring terminal state - [x] **SHEL-02**: App handles being launched as a login shell gracefully ## v2 Requirements @@ -103,7 +103,7 @@ Which phases cover which requirements. Updated during roadmap creation. | LIFE-03 | Phase 1 | Complete | | LIFE-04 | Phase 1 | Complete | | CONF-01 | Phase 1 | Complete (01-01) | -| SHEL-01 | Phase 1 | Pending | +| SHEL-01 | Phase 1 | Complete | | SHEL-02 | Phase 1 | Complete (01-01) | | REND-01 | Phase 2 | Pending | | REND-02 | Phase 2 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 1159ff3..147f8c0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -12,7 +12,7 @@ Four phases take this project from a bare Rust scaffold to a fully functional re Decimal phases appear between their surrounding integers in numeric order. -- [ ] **Phase 1: Safety Foundation** - Process lifecycle safety, shell integration, and configuration +- [x] **Phase 1: Safety Foundation** - Process lifecycle safety, shell integration, and configuration (completed 2026-02-28) - [ ] **Phase 2: Vault Core and Rendering** - Markdown parsing pipeline and full content display - [ ] **Phase 3: Navigation and Links** - Link following, back/forward history, link cycling - [ ] **Phase 4: BBS Polish and Live Content** - Retro aesthetic, filesystem watching, directory listing @@ -29,7 +29,7 @@ Decimal phases appear between their surrounding integers in numeric order. 3. The app reads vault path and theme settings from bbs.toml and applies them on launch 4. The app exits cleanly when the user presses q or Ctrl+C, restoring the terminal 5. The app starts correctly when invoked as a login shell (argv[0] may have a leading dash) -**Plans:** 3 plans +**Plans:** 3/3 plans complete Plans: - [x] 01-01-PLAN.md — Config loading, CLI parsing, and login shell detection - [ ] 01-02-PLAN.md — Terminal init/restore, panic hook, and signal handling @@ -77,7 +77,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Safety Foundation | 1/3 | Executing | - | +| 1. Safety Foundation | 3/3 | Complete | 2026-02-28 | | 2. Vault Core and Rendering | 0/TBD | Not started | - | | 3. Navigation and Links | 0/TBD | Not started | - | | 4. BBS Polish and Live Content | 0/TBD | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 56f099e..3754e74 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,32 +5,32 @@ See: .planning/PROJECT.md (updated 2026-02-28) **Core value:** Users can connect via SSH and seamlessly browse a vault of linked markdown documents with retro BBS aesthetics -**Current focus:** Phase 1 - Safety Foundation +**Current focus:** Phase 1 complete — Phase 2 next (Content Rendering) ## Current Position -Phase: 1 of 4 (Safety Foundation) -Plan: 2 of 3 in current phase -Status: Executing — Plan 02 complete, Plan 03 next -Last activity: 2026-02-28 — Plan 02 complete (terminal safety primitives) +Phase: 1 of 4 (Safety Foundation) — COMPLETE +Plan: 3 of 3 in current phase (all plans done) +Status: Phase 1 complete — Phase 2 ready to start +Last activity: 2026-02-28 — Plan 03 complete (event loop + main.rs wiring) -Progress: [██░░░░░░░░] 17% +Progress: [███░░░░░░░] 25% ## Performance Metrics **Velocity:** -- Total plans completed: 2 -- Average duration: 3 min -- Total execution time: 0.08 hours +- Total plans completed: 3 +- Average duration: 2.3 min +- Total execution time: 0.12 hours **By Phase:** | Phase | Plans | Total | Avg/Plan | |-------|-------|-------|----------| -| 01-safety-foundation | 2 | 5 min | 2.5 min | +| 01-safety-foundation | 3 | 7 min | 2.3 min | **Recent Trend:** -- Last 5 plans: 2 min, 3 min +- Last 5 plans: 2 min, 3 min, 2 min - Trend: Stable *Updated after each plan completion* @@ -51,10 +51,13 @@ Recent decisions affecting current work: - 01-02: Use ratatui::crossterm re-export path — crossterm is transitive dep, not direct dep in Cargo.toml - 01-02: SIGINT not registered via signal-hook — crossterm key events handle double-press Ctrl+C in Plan 03 - 01-02: restore_terminal() skips ratatui::restore() — that would call LeaveAlternateScreen (never entered) +- 01-03: show_goodbye() called after restore_terminal() — terminal must be in cooked mode before println! is safe +- 01-03: app_config stored in App struct for Phase 2 access — vault_path accessible inside event loop +- 01-03: Quit prompt cleared on any non-Ctrl+C key — pressing any key dismisses double-press window ### Pending Todos -None yet. +None. ### Blockers/Concerns @@ -65,5 +68,5 @@ None yet. ## Session Continuity Last session: 2026-02-28 -Stopped at: Completed 01-02-PLAN.md — terminal safety primitives committed -Resume file: .planning/phases/01-safety-foundation/01-03-PLAN.md +Stopped at: Completed 01-03-PLAN.md — Phase 1 complete, app event loop and main.rs pipeline committed +Resume file: .planning/phases/02-content-rendering/ (next phase) diff --git a/.planning/phases/01-safety-foundation/01-03-SUMMARY.md b/.planning/phases/01-safety-foundation/01-03-SUMMARY.md new file mode 100644 index 0000000..1959a16 --- /dev/null +++ b/.planning/phases/01-safety-foundation/01-03-SUMMARY.md @@ -0,0 +1,133 @@ +--- +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 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 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