Outcome: done
Changed: tests/conftest.py:293-300, tests/e2e/post.py:5,74-157, tests/e2e/posts/index.py:17,396-418, tests/e2e/notifications/index.py:313,799, tests/e2e/leaderboard.py:42
Verified by: python -m pytest tests/e2e/post.py tests/e2e/posts/index.py tests/e2e/notifications/index.py tests/e2e/leaderboard.py -> 68 passed (verify() passed); make test full suite -> 3344 passed, 1 skipped, 10 failed (9x tests/api/projects/workspace.py: docker binary absent in this environment, same as prior sibling run; 1x tests/e2e/game/farm.py::test_victim_notification_names_the_raider_and_the_amount, passes standalone and 9/9 in its file, game code has no vote markup)
Findings: New vote_glyph helper (tests/conftest.py:293) reads getComputedStyle(el,'::before').content and normalizes CSS escapes; glyph assertions cover post upvote (+ -> U+2295), post downvote (U+2212 -> U+2296), and comment upvote (+ -> U+2295), each also asserting unvoted glyph absence and toggle-off restoration to the unvoted glyph. New test fails against pre-change code: on 72e11db, test_post_upvote_voted_state_persists fails with "assert 'none' == '+'" (no ::before rule existed; buttons held literal text). Fixed all three has_text="+" vote locators (notifications/index.py:313,799, leaderboard.py:42) to class-based button.post-action-btn.vote-up; the three tests now pass. test_comment_voted_state_persists was made deterministic by wrapping the click in expect_response (pre-existing reload race); it now passes reliably
Typosaurus-Run: c36faeceb55d45b6bf38a45eec4f47ab
Typosaurus-Node: 9b6a5ec294a2472e800fbfeed775b27b
Typosaurus-Agent: @sveta
Refs: #162
DeviiTerminal bound [data-devii-open] click listeners only after the
async /devii/session fetch resolved, silently dropping early clicks.
Switch to a single delegated document listener bound in the
constructor, matching the ModalManager/dp-lightbox pattern.
The steal-confirm and comment-vote e2e tests asserted DOM state right
after a click with no wait for the triggering POST to land, racing the
server under CI load. Wrap those clicks in page.expect_response.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add `cmd_isslop_prune`, `cmd_isslop_clear`, `cmd_isslop_analyze` CLI commands for AI usage analysis job management
- Register `/game` router with `index` and `farm` endpoints for Code Farm idle game
- Add `politics` to allowed TOPICS constant replacing `signals`
- Introduce `ISSLOP_DIR`, `ISSLOP_WORKSPACES_DIR`, `ISSLOP_RUNS_DIR`, `ISSLOP_MEDIA_DIR` config paths
- Add `clear_user_stars` and `clear_user_projects_cache` calls on vote and project create/delete
- Update `make prod` to use `nproc` workers via `DEVPLACE_WEB_WORKERS` env var
- Convert `database.py` and `utils.py` to packages for modular structure
- Add `devplace apikey` and `devplace token` CLI subcommands for API key and access token management
- Invalidate `_hot_settings_value` and `_hot_settings_at` in `_patch_limits` so stale
cached limits from prior tests do not persist after monkey-patching `get_int_setting`.
- Add a polling retry loop (up to 5 seconds) in `test_maintenance_mode_blocks_guests`
to handle eventual consistency of the maintenance-mode flag propagation.
- Replace brittle hardcoded `@bob_test` assertion in mention test with dynamic
`data-username` attribute extraction from the active dropdown item.
- Replace `asyncio.run` calls in bot unit tests with shared `run_async` helper from
`tests.conftest` to avoid event-loop conflicts in the test suite.
- Change `insert` to `upsert` with `["uid"]` conflict target in Gitea test fixture
to prevent duplicate-key errors on repeated test runs.
- Replace `asyncio.new_event_loop().run_until_complete` in Telegram worker tests
with the same shared `run_async` helper for consistency.
- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications
- Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py`
- Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views
- Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES`
- Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
- Add comprehensive documentation for backend content rendering in AGENTS.md, detailing the new `render_content` and `render_title` Jinja globals built on mistune with media processing, emoji shortcodes, and XSS protection
- Document the `X-Response-Time` header and bottom-left render time indicator in README.md
- Update bot token pricing documentation to clarify fallback vs gateway cost headers
- Add `email_accounts` to soft-delete tables and `idx_users_role` composite index in database schema
- Implement `telegram_pairings` and `telegram_links` table creation with column migration and indexes
- Add `/profile/{username}/telegram` endpoint to docs API with request/unpair actions
- Register `TelegramService` in main.py lifespan and add `response_timing` middleware emitting `X-Response-Time` header
- Introduce `TelegramPairForm` model and `guard_public_host_sync` synchronous host validation function
- Add `account_api_key` field to `BotState` and `_adopt_account_api_key()` method that fetches the bot's own `users.api_key` from its profile JSON after auth, switching `LLMClient` to use it instead of the shared bootstrap key, routing gateway spend through the bot's user identity
- Replace `interleave_by_author` with a simpler greedy algorithm that picks the first row whose owner differs from the last picked, removing the multi-queue priority logic
- Extend `_ai_quota` response with `unlimited`, `requests`, and `last_used` fields sourced from new `user_spend_24h()` analytics helper
- Pass `recent_post_titles` from `BotState` into `LLMClient.generate_post()` and add a `distinct_rule` prompt instructing the model to avoid repeating framing, examples, or opinions from the bot's last six posts
- Remove early-return dedup check in `ArticleRegistry.admit` that skipped articles already held by the same bot owner, allowing re-admission under a different category
- Render bot username as a clickable link with avatar in the admin bots table
Split the monolithic test directory into three tiers (unit, api, e2e) with a path-mirroring directory structure. Added corresponding Makefile targets (test-unit, test-api, test-e2e) and updated all documentation references (CLAUDE.md, README.md, testing-cicd.html, testing-framework.html, testing-make.html) to reflect the new layout and naming conventions.