tally/.kiro/specs/calculator/tasks.md

22 KiB

Tasks: Tally - Cross-Platform Calculator

References


Phase 1: Project Scaffolding & Engine Foundation

Task 1.1: Initialize workspace build structure [DONE]

  • Create .mise.toml declaring Zig version (pin to specific release)
  • Create top-level build.zig with module declarations for engine, cli, tui
  • Add Android cross-compilation targets to build.zig (-Dtarget=aarch64-linux-android, -Dtarget=x86_64-linux-android)
  • Create engine/build.zig producing static lib and shared lib targets
  • Create cli/build.zig linking engine
  • Create tui/build.zig linking engine + libvaxis dependency
  • Add .gitignore, README.md (minimal), LICENSE
  • Verify: mise install provisions Zig, zig build compiles without errors on empty main files

NOTE: Actual structure diverged from spec - single binary at src/main.zig (CLI + TUI combined), engine as static lib + shared lib. No separate cli/ or tui/ build files. kcov-based coverage wired in via build/Coverage.zig.

Task 1.2: Implement core types module [DONE]

  • Create engine/src/types.zig
  • Define Value union, Integer struct, BitWidth, Signedness, Endianness enums
  • Define MultiBaseResult struct
  • Define CalcError error set and ErrorInfo struct
  • Define Mode enum (standard, programmer, financial)
  • Verify: compiles, types are importable from other engine modules

Task 1.3: Implement tokenizer [DONE]

  • Create engine/src/tokenizer.zig
  • Token types: numbers (dec, hex 0x, oct 0o, bin 0b), operators, parens, identifiers, comma, semicolon, EOF
  • Handle implicit multiplication detection (emit synthetic * token)
  • Handle multi-character operators: **, <<, >>, >>>, rol, ror
  • Support _ and , as digit separators in number literals (e.g., 1_000_000, 1,000,000)
  • Verify: unit tests for tokenizing expressions in all bases, edge cases (negative numbers, adjacent tokens for implicit mult)

NOTE: Implicit multiplication handled in parser rather than tokenizer. Comma-as-separator only consumed when followed by a digit (avoids eating function arg commas).

Task 1.4: Implement Pratt parser [DONE]

  • Create engine/src/ast.zig with AST node definitions
  • Create engine/src/parser.zig
  • Implement Pratt parser with precedence table from design doc
  • Accept Mode parameter to resolve ^ ambiguity (power vs XOR)
  • Handle function calls: name(arg1, arg2, ...)
  • Handle variable assignment: X = expr
  • Handle implicit multiplication
  • Verify: unit tests parsing complex expressions, operator precedence, all bases, function calls, mode-dependent ^

Task 1.5: Implement standard mode evaluator [DONE]

  • Create engine/src/evaluator.zig
  • Environment struct with variables, history, config (in evaluator.zig, not separate file)
  • Evaluate AST to f64 for standard mode
  • Implement built-in functions: sin, cos, tan, asin, acos, atan, log, ln, sqrt, cbrt, abs, ceil, floor, round, factorial
  • Implement constants: pi, e, tau
  • Implement variable storage and Ans
  • Implement history recording
  • Verify: unit tests for arithmetic, precedence, functions, variables, history recall

Task 1.6: Implement programmer mode evaluator [DONE]

  • Create engine/src/programmer.zig
  • All operations on Integer (u64 storage, masked to bit_width)
  • Implement bitwise operators: AND, OR, XOR, NOT, shifts, rotates
  • Implement base conversion output (produce MultiBaseResult)
  • Implement bit_pattern array generation
  • Handle overflow/truncation per configured bit_width
  • Signed vs unsigned interpretation
  • Verify: unit tests for bitwise ops at all widths, base conversions, overflow behavior, sign extension

Task 1.7: Implement number display formatter [DONE]

  • Create engine/src/formatter.zig
  • Define FormattedValue struct with display (human-readable) and raw (clipboard) strings
  • Decimal formatting: comma-separated groups of 3 (4,294,967,295)
  • Scientific notation only when value > 10^15 or < 10^-15 or > 15 significant digits
  • Hex formatting: underscore per 16-bit word for value view (0xFFFF_FFFF), space per byte for memory view (FF FF FF FF)
  • Binary formatting: space per nibble (1111 1111 1111 1111)
  • Octal formatting: underscore per 3-digit group (0o37_777_777_777)
  • All formatters produce both display and raw variants
  • Respect bit width (don't show leading zeros beyond the configured width in short-form contexts)
  • Verify: unit tests for all bases at all bit widths, edge cases (0, max values, negative signed values), scientific notation threshold

Phase 2: Struct Layout & Financial Engines

Task 2.1: Implement struct DSL tokenizer and parser

  • Create engine/src/struct_layout.zig
  • Tokenize: struct, {, }, ;, type names, identifiers, [, ], numbers (for arrays)
  • Parse into a list of FieldDef structs: { name, type, array_count? }
  • Support types: u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, bool, ptr
  • Verify: unit tests parsing valid structs, error messages for malformed input

Task 2.2: Implement ABI profile interface and x86-64 System V profile

  • Define AbiProfile struct with function pointers/vtable (field_layout, struct_alignment, tail_padding, pointer_size)
  • Implement x86_64_sysv profile:
    • u8/i8/bool -> size 1, align 1
    • u16/i16 -> size 2, align 2
    • u32/i32/f32 -> size 4, align 4
    • u64/i64/f64/ptr -> size 8, align 8
    • Struct alignment = max of field alignments
    • Tail padding to struct alignment boundary
  • Implement packed profile (all alignment = 1, no padding)
  • Verify: unit tests matching pahole output for known structs

Task 2.3: Implement layout computation

  • Implement compute_layout(fields, abi) -> StructLayoutResult
  • Generate per-field: offset, size, alignment, padding_before
  • Generate byte-level map ([]ByteInfo) for visualization
  • Compute totals: size, alignment, total padding bytes, padding percentage
  • Compute "packed comparison" size
  • Verify: unit tests with structs of varying complexity, compare against known C compiler layouts

Task 2.4: Implement endianness display

  • Given a StructLayoutResult and a set of field values (optional), show byte ordering
  • Little-endian (default): LSB at lowest address
  • Big-endian: MSB at lowest address
  • This affects the byte-map visualization labels, not the layout computation itself
  • Verify: unit test showing a u32 value's bytes in both endiannesses

Task 2.5: Implement financial module

  • Create engine/src/financial.zig
  • Implement CAGR: (end/start)^(1/n) - 1
  • Implement compound interest (future value): PV * (1 + r/m)^(m*t)
  • Implement present value: FV / (1 + r/m)^(m*t)
  • Implement TVM solver:
    • Closed-form for FV, PV, PMT, N
    • Newton-Raphson for I/Y (rate) with convergence check, max 1000 iterations, tolerance 1e-10
  • Return step-by-step formula string alongside numeric result
  • Verify: unit tests against known financial calculation results (textbook examples)

Task 2.6: Implement unit conversion engine

  • Create engine/src/units.zig
  • Define UnitCategory enum and UnitDef struct (name, aliases, category, conversion factors)
  • Implement conversion tables for all categories: length, mass, temperature, time, digital storage, speed, area, volume, energy, pressure, data rate, angle
  • Implement convert(value: f64, from: UnitDef, to: UnitDef) !f64
  • Temperature special case: formula-based conversion (C/F/K with offsets)
  • Implement unit name resolution: given a string, find the matching UnitDef (supports aliases)
  • Verify: unit tests for all conversion categories, round-trip accuracy, unknown unit error handling

Phase 3: Engine Public API & C ABI

Task 3.1: Implement engine public API

  • Create engine/src/engine.zig
  • pub fn evaluate(env: *Environment, input: []const u8) !Value
  • pub fn computeStructLayout(definition: []const u8, abi: AbiProfile, endian: Endianness) !StructLayoutResult
  • pub fn computeCagr(start: f64, end: f64, periods: f64) !f64
  • pub fn solveTvm(params: TvmParams) !TvmResult
  • pub fn convert(value: f64, from_unit: []const u8, to_unit: []const u8) !ConvertResult
  • pub fn formatMultiBase(integer: Integer) MultiBaseResult
  • Verify: integration tests exercising the full pipeline (string in -> result out)

Task 3.2: Implement C API

  • Create engine/src/c_api.zig
  • Define CalcString (ptr + len) and CalcResult (json ptr/len + error ptr/len) extern structs
  • export fn tally_eval(expr_ptr, expr_len, mode, config_ptr, config_len) CalcResult
  • export fn tally_struct_layout(def_ptr, def_len, abi_ptr, abi_len, endian) CalcResult
  • export fn tally_convert(value, from_ptr, from_len, to_ptr, to_len) CalcResult
  • export fn tally_result_free(result) void
  • export fn tally_version(out_len) [*]const u8
  • No null-terminated strings - all inputs are (ptr, len) pairs
  • JSON serialization for all result types
  • Input length validation (reject > max expression length)
  • Verify: build shared library, write a C test program that calls the API with known inputs and validates JSON output

Task 3.3: Cross-compilation verification

  • Verify zig build produces binaries for:
    • x86_64-linux-gnu
    • aarch64-linux-gnu
    • x86_64-macos
    • aarch64-macos
    • x86_64-windows-gnu
  • Verify zig build produces .so for Android targets:
    • aarch64-linux-android
    • x86_64-linux-android
  • All targets invocable from the same build.zig via -Dtarget=<triple>
  • Verify mise-provisioned Zig handles all targets without additional toolchain installs
  • Document any target-specific issues or workarounds needed

Phase 4: CLI Frontend

Task 4.1: Implement CLI argument parsing [DONE]

  • Single binary at src/main.zig (not separate cli/)
  • Parse args: positional expression (multi-arg joined with spaces), -p/--programmer, --help, --version
  • Subcommands (struct, cagr, tvm, convert) NOT YET IMPLEMENTED
  • Verify: unit tests for arg parsing; --help prints usage

NOTE: Multi-arg joining implemented (tally 2 + 2 works same as tally "2+2"). Subcommands deferred until Phase 2 engine work is done.

Task 4.2: Implement CLI output formatting [DONE]

  • Standard mode: print result with comma formatting
  • Programmer mode: print multi-base table (dec signed, dec unsigned, hex, bin)
  • Struct mode: NOT YET IMPLEMENTED
  • Financial mode: NOT YET IMPLEMENTED
  • --json flag: NOT YET IMPLEMENTED
  • Verify: unit tests for evaluate function output

Task 4.3: CLI integration and error handling [DONE]

  • Wire arg parser -> engine calls -> output formatter
  • Map CalcError to user-friendly stderr messages
  • Set exit codes: 0 success, 1 error
  • Verify: unit tests for error paths

Phase 5: TUI Frontend

Task 5.1: Set up TUI skeleton [DONE]

  • Single binary at src/main.zig + src/tui.zig (not separate tui/)
  • libvaxis vxfw.App with custom Widget (eventHandler + drawFn)
  • No args launches TUI; with args runs CLI
  • Molokai color theme
  • Verify: TUI launches, responds to Ctrl-C (quit)

Task 5.2: Implement standard mode TUI view [DONE]

  • Expression input via vxfw.TextField (gap buffer)
  • Result display in scrollable history panel
  • History navigation with up/down arrows (saves/restores current input)
  • Ctrl-L clears history
  • ? shows help overlay (keybindings, functions, constants, operators)
  • Variables persist across expressions (assignment + Ans)
  • Verify: can type expressions, see results, browse history

Task 5.3: Implement programmer mode TUI view - expression & base display [PARTIAL]

  • Programmer mode accessible via Tab key
  • Multi-base result display (dec signed, unsigned, hex, oct, bin)
  • Bit grid with navigation (arrows) and toggling (Space)
  • Width toggle (Ctrl-W cycles 8->16->32->64)
  • Ctrl-B toggles focus between input and bit grid
  • Expression evaluation through programmer engine
  • DONE: basic passive display + expression input + bit grid toggling
  • NOT DONE: editable fields (see Task 5.3.1)

Task 5.3.1: Programmer mode editable fields rework [NOT STARTED]

  • Replace passive display + input prompt with navigable editable fields
  • Each base representation (DEC signed, DEC unsigned, HEX, OCT, BIN) is an editable text field
  • Up/Down arrows move focus between fields (bit grid, dec, hex, oct, bin, expression)
  • Typing in any field parses the value in that base and updates all other fields
  • Bit grid is one of the navigable fields (not a separate mode toggled with Ctrl-B)
  • Expression field at the bottom for full programmer-mode expressions
  • Remove Ctrl-B keybinding (field navigation replaces it)
  • HEX field accepts/displays space-separated bytes (e.g. "FF 00 AB CD")
  • Verify: can arrow between fields, edit hex and see decimal update, toggle bits and see all fields update, evaluate expression and see result in all fields

Task 5.4: Implement programmer mode TUI view - bit grid [DONE - merged into 5.3]

  • Bit grid widget: 8 bits per group, groups separated by space
  • Arrow key navigation (left/right moves cursor across bits, up/down between rows for 32/64-bit)
  • Space/Enter toggles the bit under cursor
  • Bit changes immediately reflected in all base displays
  • Typing a value in the expression input updates the grid
  • Toggling bits updates the expression/result displays
  • Verify: can navigate grid, toggle bits, see values change in real-time

Task 5.5: Implement struct visualizer TUI view

  • Create tui/src/views/struct_viz.zig
  • Text input for struct definition (multi-line mini editor or single-line with parsing)
  • Live-updating field table (offset, size, align, padding)
  • Memory map visualization (byte grid with color-coded fields and padding)
  • ABI toggle (a key), endian toggle (e key)
  • "Packed comparison" shown at bottom
  • Transition: from programmer mode, press a key (e.g., S) to enter struct sub-view, Esc to return
  • Verify: can enter struct definition, see layout update, toggle ABI/endian

Task 5.6: Implement financial mode TUI view

  • Create tui/src/views/financial.zig
  • Form-style interface: labeled input fields for each financial function
  • Sub-mode selector: CAGR, Compound Interest, TVM
  • For TVM: 5 input fields, leave one empty to solve
  • Result display with formula breakdown
  • Verify: can input values, compute results, see formula steps

Task 5.7: Implement unit conversion TUI view

  • Create tui/src/views/convert.zig
  • Category selector (navigable list or hotkeys)
  • From-value input field, from-unit selector
  • To-unit selector, live-updating result
  • Quick swap (from ↔ to) key
  • Show conversion factor used
  • Verify: can select category, units, enter value, see live result update

Task 5.8: TUI polish and keybindings

  • Implement ? for help overlay showing all keybindings
  • Implement vi-style input mode (optional: Esc for normal mode, i for insert) - configurable
  • Mouse support: click on bit grid to toggle, click on tabs to switch modes
  • Color theme (sensible defaults, respects terminal capabilities)
  • Verify: help overlay works, mouse interactions work, looks reasonable in 80x24 terminal

Phase 6: Android App

Task 6.1: Set up Android project structure

  • Create android/ directory with Gradle project (Kotlin DSL)
  • Configure for Compose, minimum SDK 26 (Android 8.0), Material 3
  • Create JNI bridge class TallyEngine.kt with external fun declarations
  • Set up jniLibs/ directory structure for arm64-v8a, x86_64
  • Native .so files produced by zig build -Dtarget=aarch64-linux-android (etc.) - Gradle does not invoke Zig, it consumes prebuilt artifacts
  • Set up bottom navigation bar (Standard, Programmer, Financial, Convert)
  • Verify: zig build -Dtarget=aarch64-linux-android produces libtally.so; Gradle project builds and empty app launches with nav bar

Task 6.2: Implement JNI bridge

  • Kotlin side: TallyEngine.evaluate(expr, mode, config): String (returns JSON)
  • Kotlin side: TallyEngine.structLayout(definition, abi, endian): String
  • Kotlin side: TallyEngine.convert(value, fromUnit, toUnit): String
  • JSON deserialization into Kotlin data classes (Value, MultiBaseResult, StructLayoutResult, ConvertResult, etc.)
  • Error handling: parse error JSON, surface to UI
  • Verify: unit tests calling native functions with known inputs, validating deserialized results

Task 6.3: Implement standard calculator screen

  • Compose UI: expression display at top (scrollable, natural format), live result preview
  • Button grid: digits, operators, functions (swipeable function row for sin/cos/log/etc.)
  • History peek below expression (last 2-3 results), pull-down for full history
  • ViewModel: manages expression state, calls engine, stores history
  • Long-press = to copy result, tap history items to recall
  • Verify: can perform calculations, see results, scroll history

Task 6.4: Implement programmer mode screen

  • Compose UI: segmented control for bit width (8/16/32/64)
  • Value display cards: DEC, HEX, OCT, BIN, signed/unsigned (tappable to switch primary input base)
  • Bit grid: custom Canvas composable, tappable cells with bit index labels, haptic on toggle
  • Operator button row: AND, OR, XOR, NOT, <<, >>, ROL, ROR
  • Hex digit buttons (A-F) + numeric keypad + base prefix buttons (0x, 0b)
  • All displays update instantly on bit toggle or expression entry
  • FAB/toolbar button navigating to struct layout sub-screen
  • Verify: bit toggling works, base displays update, expressions evaluate

Task 6.5: Implement struct visualizer screen

  • Navigation: accessed from programmer mode via toolbar button
  • Form-based struct builder: type dropdown + name field per row, add/remove/reorder fields
  • Toggle for raw DSL text input (for power users / copy-paste from code)
  • Memory map: custom Canvas composable with color-coded byte bar, tappable segments highlighting field info
  • Summary card: total size, alignment, padding bytes/percentage, packed comparison
  • ABI dropdown selector, endian dropdown selector
  • Verify: can add fields visually, see layout update, toggle between form and raw DSL

Task 6.6: Implement financial calculator screen

  • Compose UI: scrollable chip row for function selection (CAGR, TVM, Compound Interest, Present Value)
  • Card-based input form adapting to selected function
  • For TVM: 5 labeled input fields, user leaves one blank to indicate solve target
  • Compute button, result card with animated appearance
  • Expandable formula breakdown section
  • Long-press result to copy
  • Verify: all financial calculations produce correct results, formula display works

Task 6.7: Implement unit conversion screen

  • Compose UI: scrollable category chip row at top
  • Large editable number input (top), from-unit selector dropdown
  • Swap button (animated rotation) between from/to
  • Live result display (bottom), to-unit selector dropdown
  • Numeric keypad (no expression input needed - just digits, decimal, sign)
  • Unit selectors open searchable bottom sheets
  • Conversion updates live as user types (no "calculate" button)
  • Verify: live conversion works, category switching filters units, swap works

Task 6.8: Android polish

  • Material 3 theming (dynamic color on Android 12+)
  • Tablet layout: two-pane (input left, results/visualization right)
  • Landscape orientation: adapted layouts (wider bit grid, side-by-side panels)
  • Haptic feedback on bit toggle and button presses
  • Screen rotation state preservation
  • App icon, splash screen
  • Verify: looks correct on phone and tablet, handles rotation, haptics fire

Phase 7: Testing & Release

Task 7.1: Comprehensive engine test suite [IN PROGRESS]

  • Target: >= 80% line coverage on engine code -- ACHIEVED: 97.71%
  • 187 unit tests passing across engine + CLI modules
  • Property-based tests for parser (roundtrip: parse -> format -> parse)
  • Known-answer tests for all financial functions (textbook values)
  • Unit conversion round-trip tests (convert A->B->A, verify precision)
  • Struct layout verification against pahole output for 20+ test structs
  • Fuzz testing for parser (random input shouldn't crash, should return error)
  • Edge cases: max values, min values, NaN, infinity, division by zero
  • Formatter tests: all bases, all bit widths, scientific notation threshold boundaries
  • Coverage measured via zig build coverage (kcov-based)
  • Verify: coverage report shows >= 80% on engine modules

Task 7.2: Integration tests

  • CLI end-to-end tests (run binary, check stdout/stderr/exit code)
  • TUI smoke tests (script key sequences, verify screen output via libvaxis test mode if available)
  • Android instrumented tests for JNI bridge

Task 7.3: CI/CD setup

  • Forgejo Actions workflow (at git.lerch.org/lobo/tally):
    • Use mise to provision toolchains (same .mise.toml as local dev)
    • Build + test on Linux, macOS, Windows
    • Enforce >= 80% engine coverage (fail CI if below)
    • Cross-compile all targets
    • Build Android .so files
  • Release workflow: produce platform binaries as Forgejo release artifacts
  • Android: produce signed APK/AAB

Task 7.4: Documentation

  • README.md: project overview, build instructions, usage examples
  • CONTRIBUTING.md: dev setup, architecture overview, how to add an ABI profile
  • CLI --help text finalized
  • Struct DSL syntax reference (in README or separate doc)

Dependency Graph

Phase 1 (foundation) --> Phase 2 (struct + financial) --> Phase 3 (API)
                                                              |
                                              +---------------+---------------+
                                              v               v               v
                                         Phase 4 (CLI)   Phase 5 (TUI)   Phase 6 (Android)
                                              |               |               |
                                              +---------------+---------------+
                                                              v
                                                     Phase 7 (Testing & Release)

Phases 4, 5, and 6 can be worked on in parallel once Phase 3 is complete.


Estimated Effort

Phase Effort Notes
Phase 1 2-3 days Core parsing + eval is the most algorithmic work
Phase 2 2-3 days Struct layout, financial formulas, unit conversion tables
Phase 3 1 day Thin wrappers + cross-compile verification
Phase 4 1 day Straightforward CLI, most logic in engine
Phase 5 3-4 days TUI widgets, bit grid interaction, struct viz, convert view
Phase 6 4-5 days Android project setup, Compose UI (5 screens), JNI, polish
Phase 7 2 days Testing infra, CI, docs
Total ~15-19 days For a single developer working focused