time2 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. time2-2.5.0.data/data/share/time2/docs/AI_INSIGHTS.md +188 -0
  2. time2-2.5.0.data/data/share/time2/docs/CODE_WALKTHROUGH.md +222 -0
  3. time2-2.5.0.data/data/share/time2/docs/DEVELOPER_GUIDE.md +738 -0
  4. time2-2.5.0.data/data/share/time2/docs/MANUAL.md +570 -0
  5. time2-2.5.0.data/data/share/time2/docs/MODULES.md +167 -0
  6. time2-2.5.0.data/data/share/time2/docs/PACKAGING.md +189 -0
  7. time2-2.5.0.data/data/share/time2/docs/TAILSCALE_PRIVATE_ACCESS.md +157 -0
  8. time2-2.5.0.data/data/share/time2/examples/time2_data.example.json +5 -0
  9. time2-2.5.0.data/data/share/time2/examples/time2_settings.example.json +85 -0
  10. time2-2.5.0.data/data/share/time2/time2_e2_component/index.html +575 -0
  11. time2-2.5.0.data/data/share/time2/time2_matrix_component/index.html +427 -0
  12. time2-2.5.0.data/data/share/time2/time2_task_list_component/index.html +191 -0
  13. time2-2.5.0.dist-info/METADATA +447 -0
  14. time2-2.5.0.dist-info/RECORD +38 -0
  15. time2-2.5.0.dist-info/WHEEL +5 -0
  16. time2-2.5.0.dist-info/entry_points.txt +2 -0
  17. time2-2.5.0.dist-info/licenses/LICENSE +21 -0
  18. time2-2.5.0.dist-info/top_level.txt +5 -0
  19. time2_ai_cache_worker.py +76 -0
  20. time2_app/__init__.py +14 -0
  21. time2_app/ai.py +1391 -0
  22. time2_app/app.py +885 -0
  23. time2_app/budget.py +261 -0
  24. time2_app/chat.py +512 -0
  25. time2_app/cli.py +307 -0
  26. time2_app/components.py +47 -0
  27. time2_app/config.py +106 -0
  28. time2_app/models.py +610 -0
  29. time2_app/reports.py +384 -0
  30. time2_app/storage.py +373 -0
  31. time2_app/streamlit_entry.py +11 -0
  32. time2_app/tailscale_utils.py +148 -0
  33. time2_app/transcription.py +62 -0
  34. time2_app/utils.py +869 -0
  35. time2_app/views.py +5422 -0
  36. time2_jupyter_launcher.py +69 -0
  37. time2_streamlit_app.py +12 -0
  38. time2_tailscale_jupyter_launcher.py +310 -0
@@ -0,0 +1,738 @@
1
+ # Developer Guide For time-2.0.5
2
+
3
+ This guide explains how the code is organized, how data moves through the app,
4
+ and where to begin when building the next version.
5
+
6
+ It is intentionally written for a future developer who may not remember the
7
+ conversation that created this app.
8
+
9
+ ## Design Goals
10
+
11
+ time-2.0.5 is built around these priorities:
12
+
13
+ - Local-first data ownership.
14
+ - Simple Python code that can be run from JupyterLab.
15
+ - Dataclasses and JSON instead of a database for this version.
16
+ - Clear history instead of disappearing tasks.
17
+ - Attention-friendly workflows: visible progress, reschedule reasons, mind states,
18
+ spontaneous work, reserved backlog tasks, audio capture, transcripts, and
19
+ AI-ready summaries.
20
+ - Optional heavy dependencies: Whisper and llama-cpp are loaded only when used.
21
+ - Optional LLM provider choice: local llama.cpp or an OpenAI-compatible API.
22
+
23
+ ## Runtime Shape
24
+
25
+ ```mermaid
26
+ flowchart TD
27
+ User["User in browser/Jupyter iframe"] --> Streamlit["Streamlit app"]
28
+ Streamlit --> App["time2_app/app.py"]
29
+ App --> Views["time2_app/views.py"]
30
+ Views --> Models["time2_app/models.py"]
31
+ Views --> Storage["time2_app/storage.py"]
32
+ Views --> Budget["time2_app/budget.py"]
33
+ Views --> AI["time2_app/ai.py"]
34
+ Views --> Whisper["time2_app/transcription.py"]
35
+ Views --> Tailscale["time2_app/tailscale_utils.py"]
36
+ Storage --> JSON["Local JSON files"]
37
+ Budget --> BudgetJSON["Monthly budget JSON files"]
38
+ AI --> Cache["AI cache JSON files"]
39
+ Whisper --> Audio["Local audio files"]
40
+ ```
41
+
42
+ ## Source File Explanation
43
+
44
+ ### `time2_streamlit_app.py`
45
+
46
+ Tiny entrypoint used by Streamlit and JupyterLab.
47
+
48
+ It imports `run_app()` from `time2_app.app` and calls it. Keep this file small
49
+ so the launch command remains stable:
50
+
51
+ ```bash
52
+ python -m streamlit run time2_streamlit_app.py
53
+ ```
54
+
55
+ ### `time2_app/streamlit_entry.py`
56
+
57
+ Installed-package Streamlit entrypoint.
58
+
59
+ The `time2` CLI points Streamlit at this file after the package is installed
60
+ from a wheel. Keep it tiny; it should only import and call `run_app()`.
61
+
62
+ ### `time2_app/cli.py`
63
+
64
+ Command-line interface exposed as:
65
+
66
+ ```bash
67
+ time2
68
+ ```
69
+
70
+ Responsibilities:
71
+
72
+ - `time2 run`: start the Streamlit app from an installed package.
73
+ - `time2 doctor`: check required and optional Python imports.
74
+ - `time2 install-optionals`: ask before installing Whisper/LLM extras.
75
+ - `time2 tailscale`: install/check/login/serve helpers for Tailscale.
76
+
77
+ The CLI should never install optional packages or system software without an
78
+ explicit yes/no confirmation.
79
+
80
+ ### `pyproject.toml` and `MANIFEST.in`
81
+
82
+ Package metadata and source distribution rules.
83
+
84
+ Important packaging details:
85
+
86
+ - `project.scripts` defines the `time2` command.
87
+ - Optional extras are `whisper`, `llm`, and `all`.
88
+ - Streamlit component HTML is included as wheel data under `share/time2/`.
89
+ - `MANIFEST.in` keeps docs, examples, launchers, and component HTML in source
90
+ distributions.
91
+
92
+ ### `time2_jupyter_launcher.py`
93
+
94
+ Portable launcher for JupyterLab.
95
+
96
+ Responsibilities:
97
+
98
+ - Find the app folder.
99
+ - Start Streamlit in a subprocess.
100
+ - Use port `8503`.
101
+ - Open the app in a browser.
102
+ - Display it in a Jupyter iframe.
103
+
104
+ This file should not contain a developer-specific absolute path.
105
+
106
+ ### `time2_jupyter_launcher.ipynb`
107
+
108
+ Notebook wrapper around the launcher. It exists because `%run` can hit
109
+ permission/path issues in some JupyterLab setups. The notebook is the friendlier
110
+ launch surface.
111
+
112
+ ### `time2_tailscale_jupyter_launcher.py`
113
+
114
+ JupyterLab-only private-access launcher.
115
+
116
+ Responsibilities:
117
+
118
+ - Start or restart the local Streamlit process.
119
+ - Find the Tailscale CLI in common macOS/Jupyter locations.
120
+ - Run `tailscale serve --bg http://127.0.0.1:8503`.
121
+ - Print the private tailnet HTTPS URL/status.
122
+ - Display the local app in a Jupyter iframe.
123
+
124
+ This file assumes Tailscale is already installed and signed in on the home
125
+ laptop.
126
+
127
+ ### `time2_tailscale_jupyter_launcher.ipynb`
128
+
129
+ Notebook wrapper for the Tailscale launcher.
130
+
131
+ It has cells for:
132
+
133
+ - Starting the app and Tailscale Serve.
134
+ - Showing Tailscale Serve status again.
135
+ - Resetting Tailscale Serve and stopping the Streamlit subprocess.
136
+
137
+ ### `time2_ai_cache_worker.py`
138
+
139
+ Optional background script.
140
+
141
+ Responsibilities:
142
+
143
+ - Load the same settings/data as the app.
144
+ - Refresh deterministic AI cache files.
145
+ - Optionally refresh LLM cache files.
146
+ - Run once or loop at an interval.
147
+
148
+ This is separate from Streamlit because Streamlit reruns are not a true
149
+ scheduler.
150
+
151
+ ## Package Modules
152
+
153
+ ### `time2_app/config.py`
154
+
155
+ Independent constants and defaults.
156
+
157
+ Contains:
158
+
159
+ - `APP_TITLE`
160
+ - `DATA_FILE`
161
+ - `SETTINGS_FILE`
162
+ - `DEFAULT_SETTINGS`
163
+ - `OPTION_FIELDS`
164
+
165
+ Add new setting defaults here first. Then normalize them in `utils.py`.
166
+
167
+ ### `time2_app/utils.py`
168
+
169
+ General helper layer.
170
+
171
+ Contains:
172
+
173
+ - ID generation.
174
+ - Current timestamps.
175
+ - Date/time parsing and formatting.
176
+ - Deadline/urgency math.
177
+ - Energy labels.
178
+ - Settings load/save.
179
+ - Settings normalization.
180
+ - Theme CSS.
181
+ - Project and daily routine normalization.
182
+ - Option helpers for Streamlit selectboxes.
183
+
184
+ Keep this module free of Streamlit tab workflows. Small UI-independent helpers
185
+ belong here.
186
+
187
+ ### `time2_app/models.py`
188
+
189
+ Core app meaning.
190
+
191
+ Defines:
192
+
193
+ - `Time2Comment`
194
+ - `Time2MindLog`
195
+ - `Time2AudioLog`
196
+ - `Time2Session`
197
+ - `Time2Reschedule`
198
+ - `Time2Item`
199
+ - `Time2Board`
200
+
201
+ Important model behavior:
202
+
203
+ - `Time2Item.start_session()`
204
+ - `Time2Item.stop_session()`
205
+ - `Time2Item.mark_done()`
206
+ - `Time2Item.undo_done()`
207
+ - `Time2Item.cancel()`
208
+ - `Time2Item.reschedule()`
209
+ - `Time2Item.sync_matrix_scores()`
210
+ - `Time2Board.stats()`
211
+ - `Time2Board.load()`
212
+ - `Time2Board.save()`
213
+
214
+ When adding fields, update:
215
+
216
+ 1. Dataclass field.
217
+ 2. `from_dict()` for backward compatibility.
218
+ 3. Any table/rendering code in `views.py`.
219
+ 4. Example data/settings if needed.
220
+
221
+ Reserved tasks use `Time2Item.reserved`. They are intentional unscheduled
222
+ backlog items. `sync_matrix_scores()` clears reserved mode once a planned date
223
+ or deadline exists, so the task returns to the normal color scheme.
224
+
225
+ ### `time2_app/storage.py`
226
+
227
+ Disk and folder behavior.
228
+
229
+ Contains:
230
+
231
+ - Path resolution.
232
+ - Data folder handling.
233
+ - First-run folder creation.
234
+ - Main board JSON read/write support.
235
+ - Per-task file saving.
236
+ - Per-mind-log file saving.
237
+ - Audio file saving.
238
+ - Backup writing.
239
+
240
+ Important functions:
241
+
242
+ - `data_storage_folder(settings)`
243
+ - `data_file_path(settings)`
244
+ - `task_storage_folder(settings)`
245
+ - `mind_storage_folder(settings)`
246
+ - `audio_storage_folder(settings)`
247
+ - `backup_storage_folder(settings)`
248
+ - `ensure_data_folder_structure(settings)`
249
+ - `store_audio_upload(...)`
250
+ - `write_backup(...)`
251
+
252
+ Relative paths resolve from the app working directory. Absolute paths are used
253
+ directly.
254
+
255
+ ### `time2_app/budget.py`
256
+
257
+ Budget data layer.
258
+
259
+ Defines:
260
+
261
+ - `BudgetEntry`
262
+ - `DEFAULT_BUDGET_CATEGORIES`
263
+ - `BUDGET_KINDS`
264
+ - `BUDGET_CLASSES`
265
+
266
+ Responsibilities:
267
+
268
+ - Normalize budget categories.
269
+ - Decide budget month from a custom month-end day.
270
+ - Read/write monthly budget files.
271
+ - Add budget entries.
272
+ - Build budget reports.
273
+
274
+ Budget files live under:
275
+
276
+ ```text
277
+ time2_budget/budget_YYYY_MM.json
278
+ ```
279
+
280
+ Budget entries can contain:
281
+
282
+ - Item.
283
+ - Amount.
284
+ - Category.
285
+ - Kind: income, expense, savings.
286
+ - Date.
287
+ - Bank/account.
288
+ - Note.
289
+ - Audio log metadata.
290
+ - Transcript status/text.
291
+
292
+ ### `time2_app/ai.py`
293
+
294
+ AI and deterministic analytics layer.
295
+
296
+ Responsibilities:
297
+
298
+ - Build daily/range reports.
299
+ - Calculate period scores.
300
+ - Calculate data availability.
301
+ - Build attention flags.
302
+ - Build next-action candidates.
303
+ - Create compact prompts for local or API LLM calls.
304
+ - Load and call `llama-cpp-python` only when requested.
305
+ - Call OpenAI-compatible `/chat/completions` APIs through the standard library.
306
+ - Write/read AI cache files.
307
+
308
+ This module should not edit tasks. It should read data and produce analysis.
309
+
310
+ ### `time2_app/transcription.py`
311
+
312
+ Optional Whisper integration.
313
+
314
+ Responsibilities:
315
+
316
+ - Keep supported language mapping.
317
+ - Load Whisper lazily.
318
+ - Transcribe a stored audio file.
319
+
320
+ The lazy import matters. The app should still run when `openai-whisper` is not
321
+ installed.
322
+
323
+ Supported UI languages:
324
+
325
+ - Auto detect
326
+ - English
327
+ - German
328
+ - Spanish
329
+ - Bengali
330
+ - Hindi
331
+
332
+ `Auto detect` should remain the default. When it is selected,
333
+ `transcribe_audio_file()` should call Whisper without a forced `language`
334
+ keyword and then store Whisper's detected language code/name.
335
+
336
+ ### `time2_app/tailscale_utils.py`
337
+
338
+ Optional Tailscale integration shared by the CLI and Settings screen.
339
+
340
+ Responsibilities:
341
+
342
+ - Detect the local `tailscale` command.
343
+ - Report download links and package-manager install commands.
344
+ - Ask callers to decide before install/sign-in/share actions.
345
+ - Start `tailscale up` for sign-in.
346
+ - Start `tailscale serve` for private tailnet access.
347
+ - Parse private `https://...ts.net` URLs from Serve status output.
348
+
349
+ This module should not import Streamlit. It should stay usable from both the
350
+ CLI and the app UI.
351
+
352
+ ### `time2_app/components.py`
353
+
354
+ Registers custom Streamlit components.
355
+
356
+ Components:
357
+
358
+ - `time2_matrix_component`
359
+ - `time2_e2_component`
360
+ - `time2_task_list_component`
361
+
362
+ In a source checkout, browser-side component files live beside the Python
363
+ package:
364
+
365
+ ```text
366
+ time2_matrix_component/index.html
367
+ time2_e2_component/index.html
368
+ time2_task_list_component/index.html
369
+ ```
370
+
371
+ In an installed wheel, `pyproject.toml` installs those files under
372
+ `share/time2/`, and `components.py` checks both locations.
373
+
374
+ ### `time2_app/views.py`
375
+
376
+ Most Streamlit UI code lives here.
377
+
378
+ Responsibilities:
379
+
380
+ - First-run setup screen.
381
+ - AI Insights tab UI.
382
+ - Planning tab.
383
+ - Deadlines tab.
384
+ - Matrix tab.
385
+ - E2 Plot tab.
386
+ - Mind Log tab.
387
+ - Budget tab.
388
+ - Settings tab.
389
+ - Task list rendering.
390
+ - Universal task editor.
391
+ - Audio upload/record/transcribe controls.
392
+ - Transcript display.
393
+ - Folder/file pickers.
394
+
395
+ Important functions:
396
+
397
+ - `save_board(board, settings=None, force_backup=False)`
398
+ - `render_first_run_setup(settings)`
399
+ - `render_planning_tab(board, settings)`
400
+ - `render_deadlines_tab(board, settings)`
401
+ - `render_eisenhower_tab(board, settings)`
402
+ - `render_e2_plot_tab(board, settings)`
403
+ - `render_mind_log_tab(board, settings)`
404
+ - `render_budget_tab(settings)`
405
+ - `render_settings_tab(settings, board)`
406
+ - `show_item_controls(board, item, settings, key_prefix="item", show_signal=False)`
407
+ - `save_audio_upload_log(...)`
408
+ - `transcribe_existing_audio_log(...)`
409
+
410
+ The universal task editor is especially important. Anything added to
411
+ `show_item_controls()` appears wherever a task is editable:
412
+
413
+ - Planning
414
+ - Pending
415
+ - Victory Board
416
+ - Cancelled
417
+ - Deadlines
418
+ - Matrix
419
+ - E2 Plot
420
+
421
+ ### `time2_app/app.py`
422
+
423
+ Top-level app composition.
424
+
425
+ Responsibilities:
426
+
427
+ - Set page config.
428
+ - Load settings.
429
+ - Apply theme.
430
+ - Show first-run setup if settings do not exist.
431
+ - Load board.
432
+ - Show top metrics.
433
+ - Create the 16 tabs.
434
+ - Call view functions.
435
+ - Handle the Data tab import/restore workflow.
436
+
437
+ Add new tabs here, but implement the tab body in `views.py`.
438
+
439
+ The time-2.0.5 tabs added after AI Insights are:
440
+
441
+ - `AI Chat`: saved local/API conversations over selected data context.
442
+ - `Reports`: PDF/HTML/TXT/JSON exports for tasks, ranges, dashboard, mind logs,
443
+ budgets, chats, and raw snapshots.
444
+
445
+ ## Data Flow
446
+
447
+ ### Task Creation
448
+
449
+ 1. User creates task in `Add`.
450
+ 2. `app.py` collects form values.
451
+ 3. `Time2Item` is created.
452
+ 4. Optional task audio is saved through `save_audio_upload_log()`.
453
+ 5. `Time2Board.add()` adds the task.
454
+ 6. `save_board()` writes:
455
+ - Main board JSON.
456
+ - Per-task files.
457
+ - Per-mind-log files.
458
+ - Backup if due.
459
+
460
+ ### Task Editing
461
+
462
+ 1. User clicks a task card.
463
+ 2. Custom task-list component sends selected ID.
464
+ 3. `show_item_controls()` renders the universal editor.
465
+ 4. User edits sessions, reschedules, deadlines, comments, details, audio, or status.
466
+ 5. `save_board()` persists changes.
467
+
468
+ ### Audio And Transcript
469
+
470
+ 1. User uploads or records audio.
471
+ 2. `store_audio_upload()` writes the file to `time2_audio/`.
472
+ 3. A `Time2AudioLog` is created.
473
+ 4. If `Transcribe` is on, `transcribe_audio_file()` runs Whisper.
474
+ 5. Transcript status/text are stored on the audio log.
475
+ 6. For mind logs and budget entries, transcript text is copied into the entry
476
+ for easier direct display.
477
+
478
+ ### Budget Entry
479
+
480
+ 1. User opens `Budget`.
481
+ 2. Date determines budget year/month using `budget_period_for_date()`.
482
+ 3. User adds income, expense, or savings.
483
+ 4. Optional audio/transcript is saved.
484
+ 5. `BudgetEntry` is written into the monthly JSON file.
485
+ 6. `budget_report()` recalculates totals and category rows.
486
+
487
+ ### AI Insights
488
+
489
+ 1. User selects today, single day, or range.
490
+ 2. `build_period_report()` gathers task, session, mind-log, project, audio, and
491
+ reschedule evidence.
492
+ 3. `score_period()` uses Settings-backed score weights and thresholds.
493
+ 4. `build_ai_insight_modules()` adds the deterministic attention support detectors.
494
+ 5. Deterministic scores/flags/modules are shown.
495
+ 6. Optional local or API LLM receives the compact report.
496
+ 7. AI cache can save common deterministic reports. LLM cache is local-model only
497
+ in time-2.0.5.
498
+
499
+ ## Storage Files
500
+
501
+ ### Main board JSON
502
+
503
+ ```text
504
+ time2_data.json
505
+ ```
506
+
507
+ Contains:
508
+
509
+ - `items`
510
+ - `mind_logs`
511
+ - `audio_logs`
512
+
513
+ ### Task files
514
+
515
+ ```text
516
+ time2_tasks/
517
+ index.json
518
+ item_xxxxxxxx.json
519
+ ```
520
+
521
+ Used when per-task file storage is enabled.
522
+
523
+ ### Mind log files
524
+
525
+ ```text
526
+ time2_mind_logs/
527
+ index.json
528
+ mind_xxxxxxxx.json
529
+ ```
530
+
531
+ Used when per-mind-log file storage is enabled.
532
+
533
+ ### Audio files
534
+
535
+ ```text
536
+ time2_audio/
537
+ audio_xxxxxxxx_original_name.wav
538
+ ```
539
+
540
+ The main JSON stores metadata and transcript text.
541
+
542
+ ### Budget files
543
+
544
+ ```text
545
+ time2_budget/
546
+ budget_2026_07.json
547
+ ```
548
+
549
+ Each month file stores budget entries for that budget period.
550
+
551
+ ### Backups
552
+
553
+ ```text
554
+ time2_backups/
555
+ time2_backup_YYYYMMDD_HHMMSS.json
556
+ ```
557
+
558
+ Backup payload:
559
+
560
+ - `created_at`
561
+ - `board`
562
+ - `settings`
563
+
564
+ ### AI cache
565
+
566
+ ```text
567
+ time2_ai_cache/
568
+ ```
569
+
570
+ Stores deterministic and optional LLM report payloads.
571
+
572
+ ## Settings Flow
573
+
574
+ When adding settings:
575
+
576
+ 1. Add default in `DEFAULT_SETTINGS` in `config.py`.
577
+ 2. Normalize in `load_settings()` in `utils.py`.
578
+ 3. Add controls in `render_settings_tab()` or a helper in `views.py`.
579
+ 4. Update `examples/time2_settings.example.json`.
580
+ 5. Update docs.
581
+
582
+ ## Compatibility Rules
583
+
584
+ The app should continue to open old JSON files.
585
+
586
+ When adding fields:
587
+
588
+ - Always use `.get()` with defaults in `from_dict()`.
589
+ - Do not assume old data has new keys.
590
+ - Do not delete unknown runtime data.
591
+ - Prefer adding fields rather than reshaping existing fields.
592
+
593
+ ## Testing Checklist
594
+
595
+ Run this after code changes:
596
+
597
+ ```bash
598
+ python -m py_compile time2_streamlit_app.py time2_jupyter_launcher.py time2_tailscale_jupyter_launcher.py time2_ai_cache_worker.py time2_app/*.py tests/time2_regression_checks.py
599
+ python tests/time2_regression_checks.py
600
+ ```
601
+
602
+ Run a Streamlit AppTest smoke test:
603
+
604
+ ```bash
605
+ python -c "from streamlit.testing.v1 import AppTest; at=AppTest.from_file('time2_streamlit_app.py', default_timeout=30); at.run(timeout=30); print([tab.label for tab in at.tabs]); print('exceptions', len(at.exception)); [print(e.value) for e in at.exception]"
606
+ ```
607
+
608
+ Expected full tab list:
609
+
610
+ ```text
611
+ Add
612
+ Planning
613
+ Deadlines
614
+ Matrix
615
+ E2 Plot
616
+ Mind Log
617
+ Pending
618
+ Victory Board
619
+ Cancelled
620
+ Dashboard
621
+ Budget
622
+ AI Insights
623
+ AI Chat
624
+ Reports
625
+ Settings
626
+ Data
627
+ ```
628
+
629
+ Recommended extra checks:
630
+
631
+ - Fresh unzip with no `time2_settings.json` shows First-time setup.
632
+ - Seeded settings opens all tabs.
633
+ - `time2 doctor` runs after editable install.
634
+ - `time2 run --open-browser` starts the app from the installed package entry.
635
+ - Reserved task can be added without planned date/deadline and turns non-reserved
636
+ when a planned date or deadline is saved.
637
+ - Task with attached audio opens in E2 without nested expander error.
638
+ - Task transcript is visible.
639
+ - Task comment audio transcript is visible under the same task.
640
+ - Mind transcript is visible.
641
+ - Budget transcript is visible.
642
+ - AI Chat works with local provider settings and API provider settings.
643
+ - Settings private-access section detects Tailscale or shows the install link.
644
+ - Zip contains no `__pycache__` or `.pyc`.
645
+ - Zip contains no private local paths.
646
+
647
+ ## Packaging Checklist
648
+
649
+ For pip package publishing, see `docs/PACKAGING.md`.
650
+
651
+ Build package artifacts:
652
+
653
+ ```bash
654
+ python -m pip install --upgrade build twine
655
+ python -m build
656
+ python -m twine check dist/*
657
+ ```
658
+
659
+ Then install the wheel in a clean environment and verify:
660
+
661
+ ```bash
662
+ python -m pip install dist/time2_local-2.0.5-py3-none-any.whl
663
+ time2 doctor
664
+ time2 run --open-browser
665
+ ```
666
+
667
+ Do not include runtime/private data in any release zip, wheel, source
668
+ distribution, or repository upload.
669
+
670
+ ## Next Version Starting Points
671
+
672
+ ### Local accounts
673
+
674
+ Files to touch:
675
+
676
+ - `models.py`: add user model or owner fields.
677
+ - `storage.py`: save/load user auth data.
678
+ - `views.py`: login/settings UI.
679
+ - `app.py`: gate the app after settings load.
680
+
681
+ Use password hashing such as Argon2id or bcrypt.
682
+
683
+ ### SQLite migration
684
+
685
+ Files to add/touch:
686
+
687
+ - New `database.py`.
688
+ - `models.py` for conversion helpers.
689
+ - `storage.py` for import/export compatibility.
690
+ - `views.py` Data tab for migration controls.
691
+
692
+ Keep JSON export/import.
693
+
694
+ ### Transcript search
695
+
696
+ Files to touch:
697
+
698
+ - `models.py`: ensure transcript fields are indexed in summaries.
699
+ - `views.py`: add search UI in Dashboard or new Search tab.
700
+ - `ai.py`: include transcript snippets in evidence payloads.
701
+
702
+ ### Budget editing
703
+
704
+ Files to touch:
705
+
706
+ - `budget.py`: update/delete entry helpers.
707
+ - `views.py`: add edit controls inside Budget tab.
708
+ - Docs/manual.
709
+
710
+ ### Calendar integration
711
+
712
+ Files to add/touch:
713
+
714
+ - New `calendar_export.py`.
715
+ - `views.py`: export/import controls.
716
+ - `models.py`: stable event/task mapping.
717
+
718
+ ## Known Design Tradeoffs
719
+
720
+ - JSON storage is easy to inspect but not ideal for simultaneous multi-user
721
+ editing.
722
+ - Whisper runs synchronously in the app, so long audio can block the UI.
723
+ - Local LLM generation can be slow on laptops.
724
+ - Streamlit is excellent for fast local tools but not a full multi-user web app
725
+ framework without extra care.
726
+ - The app has no login yet. Tailscale is the current private access layer.
727
+
728
+ ## Commenting Policy
729
+
730
+ The source code uses:
731
+
732
+ - Module-level dependency notes.
733
+ - Clear function names.
734
+ - Focused comments before non-obvious blocks.
735
+
736
+ It intentionally does not comment every single line. Line-by-line comments make
737
+ the code harder to maintain. This guide is the detailed explanation layer for
738
+ future development.