534 lines
23 KiB
Markdown
Raw Normal View History

Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
# Audit event catalogue
retoor <retoor@molodetz.nl>
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
Every state-changing action in DevPlace records one append-only row through `devplacepy/services/audit/record.py` (`record` for a request-scoped actor, `record_system` for a background one). This file is the authoritative catalogue of the 331 event keys currently emitted, grouped by the category `services/audit/categories.py` `category_for` resolves them to.
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
## Rules
- An event key is `<domain>.<noun>.<verb>` (or `<domain>.<verb>` when the domain is the noun). The domain is the first segment and MUST have an entry in `CATEGORY_BY_PREFIX`; an unmapped domain resolves to category `other` and is a bug.
- `target_type` names the entity acted upon, not the event (`gateway_quota`, not `gateway_quota_reset`).
- Every row carries a result: `success`, `failure`, `denied`. A refused action is recorded with `denied`, never dropped.
- Actor kinds: `user`, `guest`, `system`, `cli`, `service`. Origins: `web`, `api`, `devii`, `cli`, `service`, `scheduler`, `devrant`.
- The recorder never raises into its caller; a failed audit write is logged and swallowed so it can never break the action it describes.
- Some families are built from a variable at the call site (`vote.{target_type}.{direction}`, `comment.create.{target_type}`, `quiz.question.{action}`, `profile.customization.{scope}.toggle`, and the `create`/`edit`/`delete` verbs the shared `content.py` helpers emit per content type). Every member of those families is listed below individually.
- Adding an event means all three of: the key in this file, a `CATEGORY_BY_PREFIX` entry for a new domain, and the recorder call at the mutation point.
## Categories
| Category | Keys |
|---|---|
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `account` | 16 |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `admin` | 21 |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `ai` | 11 |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `attachment` | 5 |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `auth` | 8 |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `backup` | 2 |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `cli` | 43 |
| `container` | 36 |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `content` | 37 |
| `database` | 4 |
| `devii` | 18 |
| `email` | 6 |
| `engagement` | 23 |
| `game` | 6 |
| `ingress` | 1 |
| `message` | 2 |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `moderation` | 12 |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `news` | 9 |
| `notification` | 4 |
| `project` | 11 |
| `project_files` | 14 |
| `pubsub` | 1 |
| `push` | 2 |
| `reward` | 4 |
| `security` | 3 |
| `service` | 5 |
| `social` | 10 |
| `telegram` | 5 |
| `tools` | 12 |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
**Total: 331 keys.**
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
## Account and profile (`account`)
| Event key | Recorded in |
|---|---|
| `profile.ai_correction` | `routers/profile/ai_correction.py` |
| `profile.ai_modifier` | `routers/profile/ai_modifier.py` |
| `profile.api_key.regenerate` | `routers/profile/index.py` |
| `profile.avatar.regenerate` | `routers/profile/avatar.py` |
| `profile.customization.global.toggle` | `routers/profile/customization.py` |
| `profile.customization.pagetype.toggle` | `routers/profile/customization.py` |
| `profile.interactions` | `routers/profile/interactions.py`, `services/devii/actions/dispatcher.py` |
| `profile.notification.reset` | `routers/profile/notifications.py` |
| `profile.notification.toggle` | `routers/profile/notifications.py` |
| `profile.update` | `routers/devrant/auth.py`, `routers/profile/index.py` |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `account.delete.purge` | `services/moderation/deletion.py` |
| `account.delete.request` | `routers/devrant/auth.py`, `routers/profile/delete.py` |
| `consent.grant` | `routers/profile/consent.py` |
| `profile.mature_content` | `routers/profile/consent.py` |
| `consent.withdraw` | `routers/profile/consent.py` |
| `terms.accept` | `routers/auth/terms.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
## Administration (`admin`)
| Event key | Recorded in |
|---|---|
| `admin.ai_quota.reset_all` | `routers/admin/aiquota.py` |
| `admin.ai_quota.reset_guests` | `routers/admin/aiquota.py` |
| `admin.backup.delete` | `routers/admin/backups.py` |
| `admin.backup.run` | `routers/admin/backups.py` |
| `admin.backup_schedule.create` | `routers/admin/backups.py` |
| `admin.backup_schedule.delete` | `routers/admin/backups.py` |
| `admin.backup_schedule.toggle` | `routers/admin/backups.py` |
| `admin.backup_schedule.update` | `routers/admin/backups.py` |
| `admin.devii_task.delete` | `routers/admin/devii_tasks.py` |
| `admin.devii_task.disable` | `routers/admin/devii_tasks.py` |
| `admin.game.era_end` | `routers/admin/game.py` |
| `admin.game.era_start` | `routers/admin/game.py` |
| `admin.notification.default` | `routers/admin/notifications.py` |
| `admin.setting.update` | `docs_api/groups/admin.py`, `routers/admin/settings.py` |
| `admin.trash.purge` | `routers/admin/trash.py` |
| `admin.trash.restore` | `routers/admin/trash.py` |
| `admin.user.active.disable` | `routers/admin/users.py` |
| `admin.user.active.enable` | `routers/admin/users.py` |
| `admin.user.ai_quota.reset` | `routers/admin/users.py` |
| `admin.user.password.reset` | `routers/admin/users.py` |
| `admin.user.role.change` | `routers/admin/users.py` |
## AI gateway (`ai`)
| Event key | Recorded in |
|---|---|
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `ai.consent.denied` | `services/openai_gateway/service.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `ai.clippy.chat` | `routers/devii.py` |
| `ai.gateway.call` | `services/openai_gateway/gateway.py`, `services/openai_gateway/usage.py` |
| `ai.quota.exceeded` | `routers/devii.py`, `services/openai_gateway/service.py` |
| `gateway.model.delete` | `routers/admin/gateway_configs.py` |
| `gateway.model.update` | `routers/admin/gateway_configs.py` |
| `gateway.provider.delete` | `routers/admin/gateway_configs.py` |
| `gateway.provider.update` | `routers/admin/gateway_configs.py` |
| `gateway.quota.reset` | `cli/gateway.py`, `routers/admin/gateway_configs.py` |
| `gateway.quota_rule.delete` | `cli/gateway.py`, `routers/admin/gateway_configs.py` |
| `gateway.quota_rule.update` | `cli/gateway.py`, `routers/admin/gateway_configs.py` |
## Attachments (`attachment`)
| Event key | Recorded in |
|---|---|
| `attachment.delete` | `routers/admin/media.py`, `routers/media.py` |
| `attachment.rename` | `routers/uploads.py` |
| `attachment.restore` | `routers/media.py` |
| `attachment.upload` | `routers/uploads.py` |
| `attachment.upload_url` | `routers/uploads.py` |
## Authentication (`auth`)
| Event key | Recorded in |
|---|---|
| `auth.login.failure` | `routers/auth/login.py`, `routers/devrant/auth.py` |
| `auth.login.success` | `routers/auth/login.py`, `routers/devrant/auth.py` |
| `auth.logout` | `routers/auth/logout.py` |
| `auth.password.forgot_request` | `routers/auth/forgotpassword.py` |
| `auth.password.reset_complete` | `routers/auth/resetpassword.py` |
| `auth.signup` | `routers/auth/signup.py`, `routers/devrant/auth.py` |
| `auth.token.failure` | `routers/auth/token.py` |
| `auth.token.issued` | `routers/auth/token.py` |
## Backups (`backup`)
| Event key | Recorded in |
|---|---|
| `job.backup.complete` | `services/backup/service.py` |
| `job.backup.failed` | `services/backup/service.py` |
## Command line (`cli`)
| Event key | Recorded in |
|---|---|
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `cli.accounts.prune` | `cli/accounts.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `cli.apikey.backfill` | `cli/apikeys.py` |
| `cli.apikey.reset` | `cli/apikeys.py` |
| `cli.attachments.prune` | `cli/attachments.py` |
| `cli.backups.clear` | `cli/backups.py` |
| `cli.backups.prune` | `cli/backups.py` |
| `cli.backups.run` | `cli/backups.py` |
| `cli.containers.gc_workspaces` | `cli/containers.py` |
| `cli.containers.prune` | `cli/containers.py` |
| `cli.containers.prune_builds` | `cli/containers.py` |
| `cli.containers.reconcile` | `cli/containers.py` |
| `cli.deepsearch.clear` | `cli/jobs.py` |
| `cli.deepsearch.prune` | `cli/jobs.py` |
| `cli.devii.lessons.clear` | `cli/devii.py` |
| `cli.devii.lessons.prune` | `cli/devii.py` |
| `cli.devii.quota.reset` | `cli/devii.py` |
| `cli.devii.task.disable` | `cli/devii.py` |
| `cli.devii.task.prune` | `cli/devii.py` |
| `cli.emoji.sync` | `cli/migrate.py` |
| `cli.forks.clear` | `cli/jobs.py` |
| `cli.forks.prune` | `cli/jobs.py` |
| `cli.game.era.end` | `cli/game.py` |
| `cli.game.era.start` | `cli/game.py` |
| `cli.game.market.prune` | `cli/game.py` |
| `cli.game.steals.prune` | `cli/game.py` |
| `cli.isslop.analyze` | `cli/jobs.py` |
| `cli.isslop.clear` | `cli/jobs.py` |
| `cli.isslop.prune` | `cli/jobs.py` |
| `cli.messaging.prune_tickets` | `cli/messaging.py` |
| `cli.news.clear` | `cli/news.py` |
| `cli.news.sanitize` | `cli/news.py` |
| `cli.quiz.prune` | `cli/quiz.py` |
| `cli.role.set` | `cli/roles.py` |
| `cli.seo.clear` | `cli/jobs.py` |
| `cli.seo.prune` | `cli/jobs.py` |
| `cli.seo_meta.clear` | `cli/jobs.py` |
| `cli.seo_meta.prune` | `cli/jobs.py` |
| `cli.token.issue` | `cli/tokens.py` |
| `cli.token.prune` | `cli/tokens.py` |
| `cli.token.revoke` | `cli/tokens.py` |
| `cli.token.revoke_all` | `cli/tokens.py` |
| `cli.zips.clear` | `cli/jobs.py` |
| `cli.zips.prune` | `cli/jobs.py` |
## Containers (`container`)
| Event key | Recorded in |
|---|---|
| `container.instance.configure` | `routers/admin/containers.py`, `services/devii/actions/dispatcher.py` |
| `container.instance.create` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` |
| `container.instance.delete` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` |
| `container.instance.exec` | `routers/projects/containers/instances.py`, `services/devii/actions/dispatcher.py` |
| `container.instance.shell.close` | `routers/projects/containers/instances.py` |
| `container.instance.shell.open` | `routers/projects/containers/instances.py` |
| `container.instance.start` | `docs_api/groups/admin.py` |
| `container.instance.status` | `services/containers/service.py` |
| `container.instance.sync` | `routers/admin/containers.py`, `routers/projects/containers/instances.py` |
| `container.reconcile.action` | `services/containers/service.py` |
2026-08-07 10:53:08 +02:00
| `container.tunnel.cert.failure` | `services/containers/workspace_service.py` |
| `container.tunnel.cert.issue` | `services/containers/workspace_service.py` |
| `container.tunnel.create` | `routers/projects/containers/workspace.py` |
| `container.tunnel.delete` | `routers/projects/containers/workspace.py` |
| `container.tunnel.failure` | `services/containers/workspace_service.py` |
| `container.tunnel.suspend` | `services/containers/workspace_service.py` |
| `container.workspace.create` | `routers/projects/containers/workspace.py` |
| `container.workspace.delete` | `routers/projects/containers/workspace.py` |
| `container.workspace.flag.dismiss` | `routers/admin/workspaces.py` |
| `container.workspace.flag.raise` | `services/containers/workspace_service.py` |
| `container.workspace.flag.resolve` | `routers/admin/workspaces.py` |
| `container.workspace.idle.stop` | `services/containers/workspace_service.py` |
| `container.workspace.idle.warn` | `services/containers/workspace_service.py` |
| `container.workspace.open` | `routers/projects/containers/workspace.py` |
| `container.workspace.purge` | `services/containers/workspace_service.py` |
| `container.workspace.quota.block` | `routers/projects/containers/workspace.py` |
| `container.workspace.quota.warn` | `services/containers/workspace_service.py` |
| `container.workspace.restore` | `routers/admin/workspaces.py` |
| `container.workspace.resume` | `routers/projects/containers/workspace.py` |
| `container.workspace.retention.warn` | `services/containers/workspace_service.py` |
| `container.workspace.settings.update` | `routers/admin/workspaces.py` |
| `container.workspace.stop` | `routers/projects/containers/workspace.py` |
| `container.workspace.suspend` | `routers/admin/workspaces.py` |
| `container.workspace.unsuspend` | `routers/admin/workspaces.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `container.schedule.create` | `routers/projects/containers/schedules.py`, `services/devii/actions/dispatcher.py` |
| `container.schedule.delete` | `routers/projects/containers/schedules.py` |
## Content (`content`)
| Event key | Recorded in |
|---|---|
| `comment.create.gist` | `content.py` |
| `comment.create.issue` | `content.py` |
| `comment.create.news` | `content.py` |
| `comment.create.post` | `content.py` |
| `comment.create.project` | `content.py` |
| `comment.create.quiz` | `content.py` |
| `comment.delete` | `content.py`, `routers/comments.py` |
| `comment.edit` | `content.py`, `routers/comments.py` |
| `gist.create` | `content.py` |
| `gist.delete` | `content.py` |
| `gist.edit` | `content.py` |
| `issue.attachment.add` | `routers/issues/attachments.py` |
| `issue.attachment.delete` | `routers/issues/attachments.py` |
| `issue.comment` | `routers/issues/comment.py` |
| `issue.create` | `services/jobs/issue_create_service.py` |
| `issue.create.request` | `routers/issues/create.py` |
| `issue.planning.generate` | `services/jobs/planning_service.py` |
| `issue.planning.request` | `routers/issues/planning.py` |
| `issue.status` | `routers/issues/status.py` |
| `issue.sync.reply` | `services/gitea/service.py` |
| `issue.sync.status` | `services/gitea/service.py` |
| `post.create` | `content.py` |
| `post.delete` | `content.py` |
| `post.edit` | `content.py` |
| `quiz.attempt.answer` | `routers/quizzes/attempts.py` |
| `quiz.attempt.finish` | `routers/quizzes/attempts.py` |
| `quiz.attempt.start` | `routers/quizzes/attempts.py` |
| `quiz.create` | `content.py` |
| `quiz.delete` | `content.py` |
| `quiz.edit` | `content.py` |
| `quiz.grade.failed` | `routers/quizzes/attempts.py` |
| `quiz.import` | `routers/quizzes/index.py` |
| `quiz.publish` | `routers/quizzes/index.py` |
| `quiz.question.create` | `routers/quizzes/questions.py` |
| `quiz.question.delete` | `routers/quizzes/questions.py` |
| `quiz.question.edit` | `routers/quizzes/questions.py` |
| `quiz.question.reorder` | `routers/quizzes/questions.py` |
## Database API (`database`)
| Event key | Recorded in |
|---|---|
| `database.access.denied` | `routers/dbapi/_shared.py` |
| `database.nl.design` | `routers/dbapi/nl.py` |
| `database.query` | `routers/dbapi/query.py` |
| `database.query.async` | `routers/dbapi/query.py` |
## Devii assistant (`devii`)
| Event key | Recorded in |
|---|---|
| `devii.behavior.update` | `services/devii/actions/dispatcher.py` |
| `devii.customization.css.set` | `services/devii/actions/dispatcher.py` |
| `devii.customization.js.set` | `services/devii/actions/dispatcher.py` |
| `devii.customization.reset` | `services/devii/actions/dispatcher.py` |
| `devii.lesson.forget` | `services/devii/actions/dispatcher.py` |
| `devii.lesson.reflect` | `services/devii/actions/dispatcher.py` |
| `devii.notification.reset` | `services/devii/actions/dispatcher.py` |
| `devii.notification.set` | `services/devii/actions/dispatcher.py` |
| `devii.task.blocked` | `services/devii/tasks/scheduler.py` |
| `devii.task.create` | `services/devii/actions/dispatcher.py` |
| `devii.task.deferred` | `services/devii/tasks/scheduler.py` |
| `devii.task.delete` | `services/devii/actions/dispatcher.py` |
| `devii.task.execute` | `services/devii/tasks/scheduler.py` |
| `devii.task.update` | `services/devii/actions/dispatcher.py` |
| `devii.tool.create` | `services/devii/actions/dispatcher.py` |
| `devii.tool.delete` | `services/devii/actions/dispatcher.py` |
| `devii.tool.update` | `services/devii/actions/dispatcher.py` |
| `devii.turn` | `services/devii/session/core.py` |
## Email (`email`)
| Event key | Recorded in |
|---|---|
| `email.account.delete` | `services/devii/actions/dispatcher.py` |
| `email.account.set` | `services/devii/actions/dispatcher.py` |
| `email.message.delete` | `services/devii/actions/dispatcher.py` |
| `email.message.flag` | `services/devii/actions/dispatcher.py` |
| `email.message.move` | `services/devii/actions/dispatcher.py` |
| `email.send` | `services/devii/actions/dispatcher.py` |
## Engagement (`engagement`)
| Event key | Recorded in |
|---|---|
| `bookmark.add` | `content.py`, `routers/bookmarks.py` |
| `bookmark.remove` | `content.py`, `routers/bookmarks.py` |
| `poll.create` | `routers/posts.py` |
| `poll.vote.cast` | `routers/polls.py` |
| `poll.vote.change` | `routers/polls.py` |
| `poll.vote.clear` | `routers/polls.py` |
| `reaction.add` | `routers/reactions.py` |
| `reaction.remove` | `routers/reactions.py` |
| `vote.comment.clear` | `content.py` |
| `vote.comment.down` | `content.py` |
| `vote.comment.up` | `content.py` |
| `vote.gist.clear` | `content.py` |
| `vote.gist.down` | `content.py` |
| `vote.gist.up` | `content.py` |
| `vote.post.clear` | `content.py` |
| `vote.post.down` | `content.py` |
| `vote.post.up` | `content.py` |
| `vote.project.clear` | `content.py` |
| `vote.project.down` | `content.py` |
| `vote.project.up` | `content.py` |
| `vote.quiz.clear` | `content.py` |
| `vote.quiz.down` | `content.py` |
| `vote.quiz.up` | `content.py` |
## Code Farm (`game`)
| Event key | Recorded in |
|---|---|
| `game.cosmetic.buy` | `routers/game/index.py` |
| `game.defense.downgrade` | `routers/game/index.py` |
| `game.defense.upgrade` | `routers/game/index.py` |
| `game.grant.claim` | `routers/game/index.py` |
| `game.infrastructure.buy` | `routers/game/index.py` |
| `game.prestige` | `routers/game/index.py` |
## Container ingress (`ingress`)
| Event key | Recorded in |
|---|---|
| `proxy.access` | `routers/proxy.py` |
## Direct messages (`message`)
| Event key | Recorded in |
|---|---|
| `message.read_on_view` | `routers/messages.py` |
| `message.send` | `services/messaging/persist.py` |
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
## Moderation (`moderation`)
| Event key | Recorded in |
|---|---|
| `filter.block` | `services/moderation/screening.py` |
| `filter.maturity` | `services/moderation/screening.py` |
| `filter.review` | `services/moderation/screening.py` |
| `moderation.ban` | `routers/admin/moderation.py`, `routers/admin/users.py` |
| `moderation.lift` | `routers/admin/moderation.py`, `routers/admin/users.py` |
| `moderation.remove` | `routers/admin/moderation.py` |
| `moderation.restore` | `routers/admin/moderation.py` |
| `moderation.suspend` | `routers/admin/moderation.py`, `routers/admin/users.py` |
| `moderation.warn` | `routers/admin/moderation.py` |
| `report.create` | `routers/reports.py` |
| `report.decide` | `routers/admin/moderation.py` |
| `report.status` | `routers/admin/moderation.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
## News (`news`)
| Event key | Recorded in |
|---|---|
| `news.delete` | `routers/admin/news.py` |
| `news.featured.toggle` | `routers/admin/news.py` |
| `news.landing.toggle` | `routers/admin/news.py` |
| `news.publish.toggle` | `routers/admin/news.py` |
| `news.service.draft` | `services/news/service.py` |
| `news.service.ingest` | `services/news/service.py` |
| `news.service.landing` | `services/news/service.py` |
| `news.service.publish` | `services/news/service.py` |
| `news.service.reject` | `services/news/service.py` |
## Notifications (`notification`)
| Event key | Recorded in |
|---|---|
| `notification.create` | `utils/notifications.py` |
| `notification.open` | `routers/notifications.py` |
| `notification.read.all` | `routers/devrant/notifs.py`, `routers/notifications.py` |
| `notification.read.one` | `routers/notifications.py` |
## Projects (`project`)
| Event key | Recorded in |
|---|---|
| `project.create` | `content.py` |
| `project.delete` | `content.py` |
| `project.edit` | `content.py` |
| `project.fork.complete` | `services/jobs/fork_service.py` |
| `project.fork.failed` | `services/jobs/fork_service.py` |
| `project.fork.request` | `routers/projects/index.py` |
| `project.readonly.disable` | `routers/projects/index.py` |
| `project.readonly.enable` | `routers/projects/index.py` |
| `project.visibility.private` | `routers/projects/index.py` |
| `project.visibility.public` | `routers/projects/index.py` |
| `project.zip.request` | `routers/projects/index.py` |
## Project files (`project_files`)
| Event key | Recorded in |
|---|---|
| `dir.create` | `routers/projects/files.py` |
| `file.append` | `routers/projects/files.py` |
| `file.delete` | `routers/projects/files.py` |
| `file.delete_lines` | `routers/projects/files.py` |
| `file.insert_lines` | `routers/projects/files.py` |
| `file.move` | `routers/projects/files.py` |
| `file.replace_lines` | `routers/projects/files.py` |
| `file.upload` | `routers/projects/files.py` |
| `file.write` | `routers/projects/files.py` |
| `file.write.create` | `routers/projects/files.py` |
| `file.write.overwrite` | `routers/projects/files.py` |
| `files.zip.request` | `routers/projects/files.py` |
| `job.zip.complete` | `services/jobs/zip_service.py` |
| `job.zip.failed` | `services/jobs/zip_service.py` |
## Pub/sub (`pubsub`)
| Event key | Recorded in |
|---|---|
| `pubsub.publish` | `routers/pubsub.py` |
## Web push (`push`)
| Event key | Recorded in |
|---|---|
| `push.subscribe` | `routers/push.py` |
| `push.update` | `routers/push.py` |
Both carry `metadata.provider` (`webpush`, `apns`, ...) plus `created`; `metadata.endpoint_host` is set for endpoint-based providers and `null` for token-based ones.
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
## Gamification (`reward`)
| Event key | Recorded in |
|---|---|
| `reward.badge.award` | `utils/badges.py` |
| `reward.level.up` | `utils/rewards.py` |
| `reward.streak.milestone` | `utils/rewards.py` |
| `reward.xp.grant` | `utils/rewards.py` |
## Security (`security`)
| Event key | Recorded in |
|---|---|
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
| `security.authz.denied` | `routers/admin/backups.py`, `routers/profile/delete.py`, `services/devii/actions/dispatcher.py`, `utils/guards.py` |
Fix circular import, primary-admin NULL trap, and add gateway quota reset Restores a working import graph and closes two data-correctness bugs, plus adds a reset for the AI gateway's rolling 24h spend. Circular import: database/__init__ -> engagement -> content -> utils -> database made the package unimportable. get_project_devlog moves out of database/engagement.py into content.py, where enrich_items already lives. Primary administrator: _can_hold_primary_admin read is_active with bool(row.get("is_active")), so an admin row whose is_active column is SQL NULL (any row predating the column) was treated as deactivated and skipped. Every other site defaults an unknown is_active to active; this one now does too. Profile JSON: xp_next_level and xp_progress_pct were computed but only put on the top-level context, never on profile_user, so they serialised as null even though UserOut declares them and the API docs document them as embedded there. Gateway quota reset: a cap previously lifted only with the passage of time. quota.reset upserts a watermark row into gateway_quota_resets, scoped by the same three nullable dimensions as a quota rule, and spent_24h sums from max(24h cutoff, watermark). No ledger row is deleted, so the cost analytics on /admin/ai-usage stay intact. Reaches every surface: POST /admin/gateway/quota-resets, a per-rule Reset spend button, the Devii tool gateway_quota_reset (confirm-gated), devplace gateway quota reset, and the API docs. Admin's Reset all quotas now stamps a global gateway watermark too, which is what a caller stuck on "AI gateway daily quota exceeded" needed. Startup: _backfill_gamification swept every xp=0 user on every boot in every worker and could never converge, since a user with no content earns no XP. It now intersects pending users with _milestone_candidates(). db.tables is a live reflection, so it is hoisted out of the loops that probed it per row. Docker: the dependency layer now depends on pyproject.toml only, so a source edit no longer reinstalls every dependency and re-downloads Chromium. Adds start_interval so the healthcheck probes during the start period, and a docker-reload target, since docker-up does not restart an unchanged container. Adds events.md, the audit event catalogue that README, CLAUDE.md, the quiz docs and the tooling all referenced but which never existed: 288 keys across 28 categories, including the families built from a variable at the call site. Test fixes: both devlog helpers dated post 0 as the newest while the tests assumed post 2 was; a profile login posted username= to a form that takes email=; a devlog assertion matched six buttons under strict mode; and the primary-admin tests seeded founders newer than the back-dated fixture admin, so they only passed without the api tier. Full suite: 2989 passed, 1 skipped.
2026-07-27 11:17:48 +02:00
| `security.maintenance.block` | `main.py` |
| `security.rate_limit.block` | `main.py` |
## Background services (`service`)
| Event key | Recorded in |
|---|---|
| `service.config.update` | `routers/admin/services.py` |
| `service.logs.clear` | `routers/admin/services.py` |
| `service.run_now` | `routers/admin/services.py` |
| `service.start` | `routers/admin/services.py` |
| `service.stop` | `routers/admin/services.py` |
## Social graph (`social`)
| Event key | Recorded in |
|---|---|
| `award.complete` | `services/jobs/award_service.py` |
| `award.failed` | `services/jobs/award_service.py` |
| `award.give` | `routers/profile/award.py` |
| `award.revoke` | `routers/admin/awards.py` |
| `follow.follow` | `routers/follow.py` |
| `follow.unfollow` | `routers/follow.py` |
| `relation.block` | `routers/relations.py` |
| `relation.mute` | `routers/relations.py` |
| `relation.unblock` | `routers/relations.py` |
| `relation.unmute` | `routers/relations.py` |
## Telegram (`telegram`)
| Event key | Recorded in |
|---|---|
| `telegram.pair.failure` | `services/telegram/bridge.py` |
| `telegram.pair.request` | `routers/profile/telegram.py` |
| `telegram.pair.success` | `services/telegram/bridge.py` |
| `telegram.send` | `services/devii/actions/dispatcher.py` |
| `telegram.unpair` | `routers/profile/telegram.py` |
## Developer tools (`tools`)
| Event key | Recorded in |
|---|---|
| `deepsearch.chat` | `routers/tools/deepsearch.py` |
| `deepsearch.run.complete` | `services/jobs/deepsearch/service.py` |
| `deepsearch.run.failed` | `services/jobs/deepsearch/service.py` |
| `deepsearch.run.request` | `routers/tools/deepsearch.py` |
| `isslop.run.complete` | `services/jobs/isslop/service.py` |
| `isslop.run.failed` | `services/jobs/isslop/service.py` |
| `isslop.run.request` | `routers/tools/isslop.py` |
| `seo.meta.failed` | `services/jobs/seo_meta_service.py` |
| `seo.meta.generate` | `services/jobs/seo_meta_service.py` |
| `seo.run.complete` | `services/jobs/seo/service.py` |
| `seo.run.failed` | `services/jobs/seo/service.py` |
| `seo.run.request` | `routers/tools/seo.py` |
## Mapped domains with no recorder
The `backup` prefix has a `CATEGORY_BY_PREFIX` entry but no call site emits a bare `backup.*` key - backup activity is recorded as `admin.backup*` (operator actions) and `job.backup.*` (worker outcomes), both of which resolve to the `backup` category through their own prefixes. Keep the mapping: it is what routes `job.backup.*` correctly.