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
This commit is contained in:
2026-02-28 21:20:02 +01:00
parent 771c9a8561
commit d9db04fb01
4 changed files with 155 additions and 19 deletions
+2 -2
View File
@@ -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 |
+3 -3
View File
@@ -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 | - |
+17 -14
View File
@@ -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)
@@ -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<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