| Age | Commit message (Collapse) | Author |
|
The test database file should not be tracked in git. Only the
production database (.tasks/tasks.jsonl) should be committed.
Amp-Thread-ID:
https://ampcode.com/threads/T-4e6225cf-3e78-4538-963c-5377bbbccee8
Co-authored-by: Amp <amp@ampcode.com>
|
|
Amp-Thread-ID:
https://ampcode.com/threads/T-4e6225cf-3e78-4538-963c-5377bbbccee8
Co-authored-by: Amp <amp@ampcode.com>
|
|
- Implement box-drawing characters (├──, └──, │) for
task tree visualization - Fix 'task create' flag ordering by using
[options] in docopt (same as 'task list') - Document TASK_TEST_MODE
environment variable in AGENTS.md Testing section - Add test case for
multi-flag ordering on 'task create' - Clean up test tasks polluted
in production database
All 29 tests passing.
Amp-Thread-ID:
https://ampcode.com/threads/T-4e6225cf-3e78-4538-963c-5377bbbccee8
Co-authored-by: Amp <amp@ampcode.com>
|
|
- Changed 'task list' usage from explicit flags to [options] -
Allows flags to be specified in any order (fixes t-10KNtTF) - All
combinations now work: --namespace --status, --status --namespace, etc.
- Updated AGENTS.md examples to use correct status argument format -
All tests passing (28/28)
|
|
|
|
- Implemented 'task show <id>' command with human-readable output -
Shows all task fields: title, type, status, priority, timestamps -
Displays dependencies with their types - Supports --json flag for
programmatic use - Added CLI tests for show command - Includes priority
descriptions (Critical/High/Medium/Low/Backlog)
|
|
|
|
|
|
|
|
- Fix get_usage() to count from user_episodes table instead of
episodes.user_id
- Now correctly tracks when episodes are added to user's feed -
Handles shared/existing episodes properly (count against the user
who added them)
- Add comprehensive test suite for usage limits (TestUsageLimits):
- test_usage_counts_episodes_added_to_feed -
test_usage_counts_existing_episodes_correctly -
test_free_tier_limit_enforcement (10 articles) -
test_can_submit_blocks_at_limit - test_paid_tier_unlimited
- Billing.can_submit() now properly enforces 10 article limit
for free tier - Usage tracking via user_episodes.added_at ensures
accurate billing
Completes t-144eKR1
|
|
- Change primary feed URLs to use .rss extension (more semantically
correct)
- /feed/{token}.xml -> /feed/{token}.rss - /public.rss stays the same
- Add .xml aliases for backwards compatibility
- /feed/{token}.xml redirects to .rss handler - /public.xml redirects
to .rss handler
- Add tests to verify both extensions work and return identical
content - Update all references in UI and tests to use .rss
This provides consistency (.rss everywhere) while maintaining backwards
compatibility for existing feed subscribers using .xml URLs.
Amp-Thread-ID:
https://ampcode.com/threads/T-6d73d458-3d80-44e5-865f-358a69e5b2bf
Co-authored-by: Amp <amp@ampcode.com>
|
|
- Fix dashboard-updates endpoint to return Response with both
components
concatenated as HTML strings, preventing episodes from disappearing
after HTMX innerHTML swap
- Add viewing_own_feed flag to EpisodeList to hide 'In your feed'
button
when users are viewing their own feed on homepage
- Add test coverage for admin adding user episodes to own feed -
Add test coverage for admin adding user episodes to public feed
Amp-Thread-ID:
https://ampcode.com/threads/T-6d73d458-3d80-44e5-865f-358a69e5b2bf
Co-authored-by: Amp <amp@ampcode.com>
|
|
When viewing the public feed (or any episode list), logged-in users
now see: - 'Add to my feed' button if episode is not in their feed -
'In your feed' button (disabled) if they already have it
Implementation: - Added user_button logic to EpisodeList component
- Checks Core.Database.user_has_episode() for each episode - Shows
blue outline button for add action - Shows gray disabled button when
already added - Buttons displayed in flex row with admin buttons
Updated add_episode_to_feed endpoint: - Changed to redirect back
to referer page after adding - Uses HX-Redirect to reload the page
showing updated state - This allows the button to change from 'Add
to my feed' to 'In your feed'
All tests passing (48 tests)
|
|
Changed logger format from %d to %s for episode_id to handle cases
where the ID might be passed as a string from route parameters.
Error was: Message: 'Tracked %s event for episode %d (user: %s)'
Arguments: ('added', '2', 1)
Using %s is more flexible and works with both int and str types.
|
|
Privacy improvements: - Removed email address from episode page
signup banner - Changed 'This episode was created by <email>' to
'This episode was created using PodcastItLater' - Protects user
privacy while still showing the signup prompt
Admin UI improvements: - Removed floating Public/Private toggle
button (was confusing) - Added '+ Add to public feed' button at
bottom of episode cards - Button only visible to admin users - Shows
'Added to public feed' with checkmark when already public - Shows '+
Add to public feed' with plus icon when private - Clearer and more
actionable UX for managing public feed content
All tests passing (48 tests)
|
|
Issue #1: demo@example.com admin status - Confirmed demo@example.com
IS in ADMIN_EMAILS (working as intended) - Both Core.py and UI.py
have demo@example.com in whitelist
Issue #2: /public page auto-refresh causing disappearing articles -
Created PublicFeedPage component without auto-refresh - Separated
from HomePage which has dashboard auto-updates - /public route now
uses PublicFeedPage instead of HomePage
Issue #3: Homepage missing public feed for logged-out users - Updated
HomePage to show public feed when user is not logged in - Shows
login form with public episodes below - Includes marketing message:
'Sign up to create your own personal feed!' - Public episodes are
fetched and displayed prominently
Additional improvements: - Added 'Public Feed' link to navbar for
easy access - PublicFeedPage shows RSS feed link for public.rss -
Clear separation between user dashboard (auto-refresh) and public feed
(static)
All tests passing (48 tests)
|
|
- Added admin@example.com to ADMIN_EMAILS in Core.py and UI.py - This
provides a demo admin account for testing - Public/Private toggle
badge already checks for admin status correctly - All tests passing
|
|
- Added Core.Database.get_metrics_summary() for aggregate stats -
Added Core.Database.get_episode_metric_events() for raw event data -
Created MetricsDashboard component with summary cards and top episodes
tables - Added /admin/metrics route with admin authentication -
Added metrics link to admin dropdown menu - Added comprehensive tests
for metrics functionality - Fixed type errors in Admin.py by adding
MetricCardAttrs - All tests passing (48 tests total in Web.py) -
Completed epic t-ga8V8O (24/24 tasks)
|
|
- Enhanced Worker.py to extract publication date and author from
articles - Added intro TTS with metadata (title, author, publication
date) - Added outro TTS with attribution - Combined intro, pauses,
content, and outro in Worker.py - Added comprehensive tests for public
feed, deduplication, metrics, and intro/outro
All tests passing (Worker: 30 tests, Web: 43 tests)
Tasks completed: - t-gcNemK: Extract metadata in Worker.py - t-gcPraJ:
Add intro TTS generation - t-gcRCzw: Add outro TTS generation -
t-gcTPQn: Combine audio segments - t-gcW6zN: Tests for public feed
- t-gdlWtu: Tests for deduplication - t-gdoeYo: Tests for metrics
tracking - t-gdqsl7: Tests for audio intro/outro
|
|
- Added POST /episode/{id}/track endpoint to track play/download
events - Added JavaScript to audio player to track first play event -
JavaScript sends fetch request to tracking endpoint on play - Tracks
user_id if logged in, otherwise anonymous - Added main() function to
Episode.py for test compatibility
Tasks completed: t-gcdFSb, t-gcfTnG
|
|
The issue was that bild --plan outputs progress indicators before the
JSON, causing jq to fail parsing. Fixed by: - Using grep to extract
only lines starting with '{' (the JSON output) - This filters out
progress lines like '[…] target' - Restored typecheck.sh to use
repl.sh for proper environment setup
Now typecheck.sh correctly provisions the environment via repl.sh
instead of trying to use bild or raw python.
|
|
- Episode pages now show 'Add to feed' button for logged-in users
who don't have the episode - Added POST /episode/{id}/add-to-feed
endpoint - Tracks 'added' metric when user adds episode to their feed -
Added Database.track_episode_metric() function for metrics tracking -
Fixed typecheck.sh to use bild instead of broken repl.sh approach
Tasks completed: t-gc9aud, t-gcbqDl
|
|
- Add toggle button to episode cards (admins only) - Button shows
globe icon for public, lock icon for private - Toggle endpoint at
POST /admin/episode/{id}/toggle-public - Updates episode is_public
status and redirects to home
Tasks completed: t-gcbqDl, t-gc6Vrk (partially - admin toggle complete)
|
|
- test_session_persistence: Check for navbar links instead of removed
'Logged in as:' text - test_delete_action: Add referer header to
trigger HX-Redirect response
|
|
When tests create episodes directly, they must also add them to
the user_episodes junction table for them to appear in user feeds.
This maintains the new many-to-many relationship architecture.
Test suite now has same 2 pre-existing failures as before (unrelated
to this work): - test_session_persistence (expects 'Logged in as:'
text that doesn't exist) - test_delete_action (expects HX-Redirect
header from admin delete)
All new functionality tests pass.
|
|
- Add /public route showing public episodes - Add /public.rss RSS
feed for public episodes - Update home page to show public feed when
not logged in - Use user_episodes junction table instead of user_id
filtering - All tests passing
|
|
- Check for existing episodes by URL hash before processing - Reuse
existing episodes when user submits duplicate URL - Add episode
to user's feed if they don't have it - Track 'added' metrics when
episode added to feed - Worker now creates user_episodes link and
tracks metrics - Show appropriate messages for already-in-feed vs
newly-added episodes
|
|
- Add is_public column to episodes table - Add user_episodes
junction table for many-to-many relationship - Add episode_metrics
table for tracking engagement - Add original_url_hash column for
deduplication - Add Core.py functions for public episodes (mark_public,
get_public_episodes) - Add Core.py functions for user_episodes
(add_episode_to_user, user_has_episode, get_user_episodes) -
Add Core.py functions for metrics tracking (track_episode_event,
get_episode_metrics) - Add URL normalization and hashing utilities -
All tests passing
|
|
Maintain compatibility with old sequential integer episode URLs by
redirecting them to the new sqid-based URLs with a 301 permanent
redirect.
- Add /episode/{episode_id:int} route for legacy integer IDs - Redirect
with 301 status to indicate permanent move to new URL - Add test to
verify redirect behavior - Mark route as deprecated in documentation
This allows existing shared links to continue working while encouraging
adoption of the new non-sequential URLs. The legacy route can be
removed after a deprecation period.
Amp-Thread-ID:
https://ampcode.com/threads/T-cc5d29f0-454e-4864-8d7e-1ad69a42afa9
Co-authored-by: Amp <amp@ampcode.com>
|
|
Replace sequential integer IDs with sqids in episode URLs for better
privacy and security. Episode IDs are no longer easily guessable.
- Add sqids dependency to Web.py - Create encode_episode_id() and
decode_episode_id() helper functions - Update /episode/{episode_sqid}
route to accept and decode sqids - Update EpisodeList to generate
sqid-based links - Update RSS feed to use sqids in episode URLs -
Update EpisodeDetailPage to accept and use sqids for share URLs -
Update all tests to use sqids
Episode URLs now look like /episode/AbCd1234 instead of /episode/1
Database still uses integer IDs internally for efficiency.
Amp-Thread-ID:
https://ampcode.com/threads/T-cc5d29f0-454e-4864-8d7e-1ad69a42afa9
Co-authored-by: Amp <amp@ampcode.com>
|
|
- Add page_title and meta_tags parameters to PageLayout - Generate
episode-specific page titles: '<Episode Title> - PodcastItLater' -
Add Open Graph meta tags for better link previews:
- og:title, og:type, og:url - og:description with duration and
author - og:audio with MP3 URL - og:site_name
- Add Twitter Card tags for Twitter sharing - Include article:author
when available
This improves how episode links appear when shared in messaging apps,
social media, and other platforms that support Open Graph protocol.
Amp-Thread-ID:
https://ampcode.com/threads/T-cc5d29f0-454e-4864-8d7e-1ad69a42afa9
Co-authored-by: Amp <amp@ampcode.com>
|
|
The bild --plan command now outputs progress indicators before JSON.
Updated run.sh to use 'tail -1' to extract only the JSON line and
fixed the file path check to use the full path.
Amp-Thread-ID:
https://ampcode.com/threads/T-cc5d29f0-454e-4864-8d7e-1ad69a42afa9
Co-authored-by: Amp <amp@ampcode.com>
|
|
- Create new Episode.py module with episode-specific components
- EpisodePlayer: HTML5 audio player - ShareButton: Clipboard copy
with Bootstrap input-group pattern - SignupBanner: Promotional banner
for non-authenticated users - EpisodeDetailPage: Full page layout
- Update Web.py to add /episode/<id> route (public, no auth required)
- Make episode titles clickable in EpisodeList component - Add
Database.get_episode_by_id() method for efficient queries - Update
RSS feed and share buttons to use Bootstrap input-group pattern -
Add comprehensive test suite for episode detail pages
All episode pages are publicly accessible and include: - Media player
to listen to episodes - Share button with URL copying - Links to
original articles - Creator attribution banner for non-logged-in users
Amp-Thread-ID:
https://ampcode.com/threads/T-cc5d29f0-454e-4864-8d7e-1ad69a42afa9
Co-authored-by: Amp <amp@ampcode.com>
|
|
Problem: Still getting segfaults ("free(): invalid pointer") even
with ncurses calls protected. The mutex only covered ANSI calls, but
IORef reads and IO.hPutStr operations were happening outside the lock.
Root cause: Race conditions in concurrent terminal output: 1. Multiple
threads reading namespaceLines IORef concurrently 2. Interleaved
IO.hPutStr calls corrupting output buffer 3. Rainbow.hPutChunks not
being thread-safe
Solution: Expand terminalLock scope to cover entire output operations:
- Move IORef reads inside the lock - Protect all IO.hPutStr and
Rainbow.hPutChunks calls - Lock both SingleLine and MultiLine modes -
Ensure atomicity from IORef read through all I/O to flush
This makes each updateLine/updateLineState call atomic, preventing
any interleaving of terminal operations between threads.
Changes: - updateLine SingleLine: wrap entire output in withMVar
terminalLock - updateLine MultiLine: move nsMap read inside lock -
updateLineState SingleLine: wrap entire output in withMVar terminalLock
- updateLineState MultiLine: move nsMap read inside lock
Tested: 10/10 successful runs of `bild --time 0 **/*` without
segfaults.
|
|
Clarify that the mutex protects against ncurses thread-safety issues
that cause segfaults during concurrent builds.
This commit also forces a new Nix derivation hash to ensure the
mutex fix is actually used (previous builds were cached with the
old version).
|
|
Problem: Intermittent segfaults when running `bild --time 0 **/*` with
many concurrent builds. Core dumps showed crashes in libncursesw's
free() function during terminal cleanup.
Root cause: ANSI.getTerminalSize and other ANSI terminal library
calls are not thread-safe. With mapConcurrentlyBounded running up to
8 analyses concurrently, multiple threads were calling ANSI functions
simultaneously, causing memory corruption in the ncurses library.
Solution: Add global MVar terminalLock to serialize all ANSI terminal
operations. Wrap all ANSI function calls (cursor movement, line
clearing, etc.) with withMVar terminalLock.
Changes: - Add terminalLock :: MVar () in Omni/Log/Concurrent.hs -
Wrap all ANSI calls in withMVar terminalLock:
- initializeLines: cursor column, clear line - updateLine: cursor
up/down, column set, clear line - updateLineState: cursor up/down,
column set, clear line - withLineManager: cursor up
Tested: 5 consecutive runs of `bild --time 0 **/*` complete without
segfaults (previously failed 1-2 out of 3 runs).
|
|
Problem: Calling bild.run inside NixOS configs triggered IFD during
OS evaluation. ANSI escape codes from bild broke JSON parsing in Nix
sandbox, causing build failures.
Root cause: bild.run uses IFD (Import From Derivation) which runs
bild --plan during Nix evaluation. When this happened inside NixOS
service definitions, it ran recursively and bild output ANSI codes
that corrupted the JSON analysis output.
Solution: Two-phase architecture + NO_COLOR support
1. Biz/Packages.nix: Pre-builds all packages outside NixOS context
2. Biz.nix: Accepts packages as function argument (default:
Packages.nix) 3. Omni/Bild.nix: Sets NO_COLOR=1 in analysis
derivation 4. Omni/Log/Terminal.hs: Respects NO_COLOR env var
5. Omni/Log/Terminal.hs: Skip getTerminalSize when NO_COLOR set
to avoid
escape code output
6. Omni/Log/Concurrent.hs: Skip line initialization without ANSI
support
Now NixOS builds succeed: - Package IFD happens once at top level -
No recursive builds during service evaluation - Clean JSON output
from bild --plan in Nix sandbox - NixOS configs reference pre-analyzed
packages
Changes: - Add Biz/Packages.nix - standalone package builder - Update
Biz.nix to accept packages argument - Update Biz/Dragons/Analysis.nix
to use Packages.nix - Remove Biz/Targets.nix (replaced by
Packages.nix) - Add NO_COLOR support throughout logging stack -
Fix ANSI.getTerminalSize outputting escape codes when NO_COLOR set
Amp-Thread-ID:
https://ampcode.com/threads/T-bc0f6fc7-46bf-4aa2-892e-dd62e7251d4b
Co-authored-by: Amp <amp@ampcode.com>
|
|
Problem: Calling bild.run inside NixOS configs triggers recursive
builds during OS image creation, causing slow IFD evaluations that
worsen as complexity grows.
Solution: Create Biz/Targets.nix that pre-declares all buildable
targets as an attribute set. NixOS configs now import and reference
these targets directly, eliminating recursive builds during evaluation.
Changes: - Add Biz/Targets.nix exposing storybook, podcastitlater-web,
podcastitlater-worker, dragons-analysis
- Update Biz.nix to import targets and reference them - Update
Biz/Dragons/Analysis.nix to use targets pattern
Benefits: - All bild.run calls happen once at top level during targets
evaluation - NixOS service configs reference pre-built derivations -
Scalable: adding targets doesn't slow individual builds - Explicit:
clear what gets built for each OS
Amp-Thread-ID:
https://ampcode.com/threads/T-bc0f6fc7-46bf-4aa2-892e-dd62e7251d4b
Co-authored-by: Amp <amp@ampcode.com>
|
|
ANSI.getTerminalSize queries the terminal by writing escape codes and
reading the response from stdin. When stdin is unavailable or at EOF
(common in non-interactive contexts), this causes "hWaitForInput:
end of file" errors.
Additionally, Conduit.streamingProcess returns a stdin handle that
was never being closed, which could cause child processes to block
waiting for input.
Fixes: - Catch IOException in detectTerminal when getTerminalSize
fails - Close subprocess stdin handle immediately after spawn -
Fallback to default terminal size (80x24) when detection fails
This unblocks all bild commands that were failing with stdin errors.
|
|
- Remove BILD_OUTPUT_MODE environment variable - Simplify to 2
modes: MultiLine (≥80 cols) and SingleLine (<80) - Use existing
--loud flag to disable concurrent UI entirely - When --loud: show
all compiler output line-by-line (for debugging) - When not --loud:
adaptive concurrent UI based on terminal width
This is simpler and uses the existing --loud flag instead of adding
new configuration. The --loud flag was already meant for debugging,
so it makes sense to use it to show all output.
Note: Omni/Bild.nix updated to include new Omni/Log/Terminal.hs module.
Blocked by: stdin bug (see _/llm/STDIN_BUG.md) - cannot test build yet.
|
|
- Add Omni/Log/Terminal module to detect terminal capabilities -
Implement 3 output modes: RichMultiLine (≥80 cols), SingleLine
(40-79),
SimpleFallback (<40 or dumb terminals)
- Terminal width auto-detection via System.Console.ANSI.getTerminalSize
- Text truncation with ellipsis to prevent line wrapping - Manual
override via BILD_OUTPUT_MODE env var (simple|single|rich|auto)
Fixes UI corruption on small terminals (mobile SSH, narrow windows).
Wide terminals keep existing multi-line behavior but with truncation.
Design doc: _/llm/CONCURRENT_LOG_DESIGN.md
|
|
When building per-module derivations, copying dependency .hi files
was failing silently because: 1. First dep copies read-only Alpha.hi
from nix store 2. Second dep also has Alpha.hi (transitive dep) and
tries to overwrite 3. cp fails with permission denied but error is
hidden by '|| true'
Fixed by: - Add -f flag to cp to force overwrite - Run chmod -R +w
after each dependency copy - This allows later deps to overwrite
shared files like Alpha.hi
Tested: Biz/Que/Host.hs now builds successfully with per-module
derivations
|
|
Set dontPatchShebangs=true for per-module link derivation to avoid
unbound variable error in Nix's patch-shebangs.sh script.
The script declares 'local update' but never initializes it, causing
bash set -u to fail. Our ELF binaries don't need shebangs patched
anyway.
Tested: All Haskell targets now build successfully including
Omni/Lint.hs which uses makeWrapper with rundeps.
|
|
- Fix nixBuild to propagate exit code from realise step
- Previously used >> which discarded exit code - Now checks realise
result before running symlink - Fixes false success checkmarks on
build failures
- Fix per-module Nix derivations
- Use cp -rL instead of tar (src is directory not tarball) - Add
coreutils and findutils to pkgs - Copy deps to . and use -i. for
GHC - Use find with --parents to preserve module hierarchy - Set
dontStrip=true to avoid fixup script errors
- Tested: Example.hs, Task.hs, Dragons.hs, Bild.hs all build - Known
issue: makeWrapper fixup scripts have unbound vars (Nix bug)
|
|
When using custom builder, unpackPhase function is not available.
Use tar xzf to manually extract the source archive.
|
|
When using custom builder, standard phases don't run automatically.
Call unpackPhase explicitly and cd to 'source' directory where files
are unpacked.
Fixes 'sourceRoot: unbound variable' error in module compilation.
|
|
The haskell.ghcWith package has setup hooks that override buildPhase
even when explicitly set. Solution: use custom builder = stdenv.shell
with args instead of relying on mkDerivation phases.
Changes: - Module derivations: Use custom builder with single -c script
- Combines unpack, build, and install into one script - Explicitly
call ghc with full path to avoid hook interference - Remove unused
objectPaths computation (now using ghc --make with source)
This fixes builds for Omni/Lint.hs, Omni/Task.hs, and all other
Haskell targets with complex dependency graphs.
|
|
GHC --make was treating -i /nix/store/.../hidir paths as compilation targets.
Solution: Copy all .hi files to local directory and use -i. only.
Also make hsGraph optional with 'or null' for backward compatibility with
old bild binaries during bootstrap.
Amp-Thread-ID: https://ampcode.com/threads/T-fe68faaf-1c1d-4c43-a377-1cf5e6cffb3a
Co-authored-by: Amp <amp@ampcode.com>
|
|
GHC --make interprets arguments before flags as targets. Moving the
entry point source file to the beginning prevents -i paths from being
treated as compilation targets.
This fixes the 'is not a module name or a source file' error.
|
|
This is the core architecture transformation from Phase 3 of the
performance plan. Each Haskell module is now built as a separate
Nix derivation, enabling true incremental builds where only changed
modules and their dependents are rebuilt.
Implementation: - buildHsModuleGraph: Analyzes transitive module
dependencies and builds DAG - TH detection: Falls back to monolithic
build if Template Haskell detected - SCC cycle detection: Falls
back if import cycles found - Per-module Nix builder: Each module ->
separate derivation with .hi and .o - Module dependencies: Copy .hi
files to build dir, use -i flags for imports - Final link: Use ghc
--make with entry point source + -i paths to .hi files - Entry point
fix: Explicitly analyze entry point module separately from deps
Architecture: - Module compilation: ghc -c with -i paths to dependency
.hi files - Source filtering: Each module derivation includes only
its source file - Dependency DAG: Expressed as recursive Nix attrset
with lib.fix - Link phase: ghc --make with entry source file + all .hi
search paths - Fallback: Monolithic ghc --make when hsGraph is null
(TH/cycles)
Performance characteristics: - Change one module -> rebuild only
that + dependents + relink - Nix handles DAG scheduling and caching
automatically - Parallel module compilation (Nix orchestrates) -
Content-addressed caching across machines
Testing: - Added test_buildHsModuleGraph unit test - Verified with
Omni/Bild/Example.hs (4 modules) - Tested incremental rebuild triggers
correct subset
This completes Phase 2 and Phase 3 core optimizations from the plan.
|