Skip to content
Hussein Jarrar edited this page Sep 12, 2026 · 2 revisions

v0.22.0

36 changes · 11 Bugfixes, 3 New Features, 3 New UI Features, 19 Chores · 110 points.

Bugfix

  • RADD-869 Docs truth pass: security-relevant docstring lies auth comments items mcp views · 2 pts · 3631d3c0 What is wrong (report 04, research/audit-2026-08/04-misleading-docs-comments.md): docstrings/comments that misdescribe security-relevant behavior. merge_users (auth/service.py:693) promises "account deactivated (kept for audit)" while the code hard-DELETES the source row — echoed in delete_user (:955). The…
  • RADD-880 page-print proof asserts on a selector that exists nowhere (vacuous pass) pages · 1 pts · a3ab7c33 web/scripts/page-print-proof.mjs:136 checks !!document.querySelector("[data-page-tree]") to prove the print view excludes the page tree — but data-page-tree is set nowhere in web/src, so the assertion passes unconditionally. Exactly the vacuous-check class CLAUDE.md warns is worse than no test.
  • RADD-879 Editor diff-review CSS styles class names the decoration plugin never emits editor · 1 pts · 46fb7a7a editor.css:184-194 styles .milkdown-diff-insert/-block-insert/-delete/-block-delete, but the forked decoration plugin emits milkdown-diff-added/-added-block/-removed (diff/decoration-plugin.ts:239,314,354-356). The styled names never appear; the emitted names are styled nowhere — non-textblock deletions…
  • RADD-875 Issue-rail state chip ships a drifted second copy of the workflow palette items · 2 pts · 0591fd82 IssueProperties.tsx:22-35 hardcodes CATEGORY_CHIP hexes: in_progress #eab308 (YELLOW) where the design system's traffic-light reading is green (--chart-progress), done green where the system says slate, triage/backlog also differ — and the map is theme-blind. index.css declares --chart-* as "ONE source…
  • RADD-878 _rebalance_ranks issues one UPDATE per item, instance-wide, inside a drag request items · 2 pts · 23fec0f4 items/service/queries.py:289-300 — when a rank midpoint collapses during drag-to-rank, _rebalance_ranks selects EVERY item id in the instance (not even project-scoped) and issues one UPDATE per row. Against the 503k-item perf dataset that is 503k statements in one interactive transaction, lock-contending every…
  • RADD-877 CommitBeforeSendMiddleware buffers entire file downloads in RAM attachments backup · 3 pts · 28f2e7ef radd/middleware.py:29-53 buffers every non-SSE response's body messages in a Python list until the app (including session teardown) finishes — so GET /backups/{name}/download holds a potentially multi-GB artifact fully in memory before the first byte leaves, and every concurrent attachment proxy holds its full…
  • RADD-872 sweep_expired_grants never runs — the kernel TaskSpec registry has zero readers access kernel · 2 pts · 3687c5a3 access/__init__.py:19 registers sweep_expired_grants as the codebase's only TaskSpec, but nothing anywhere reads registries.tasks — the sweep has never executed. Not a security hole (RADD-820 filters expired grants at resolution), but expired rows accumulate forever and the whole TaskSpec pipeline is a dead…
  • RADD-874 group.synced is a phantom automation trigger — offered in the catalog, never emitted automations groups · 1 pts · 2164e4a8 groups/__init__.py:16 registers GroupEvent.SYNCED ("Directory group synced") into the event/trigger catalog, but no code path emits it — the sync emits only MISSING/RESTORED (groups/service.py:348). Any automation built on the trigger can never fire; the catalog is lying to the rule builder.
  • RADD-873 Plugin enable/disable state silently discarded on any boot DB error pluginmgr · 1 pts · ed1c9df0 pluginmgr/boot.py:26-34 wraps the boot-time read of installed_plugins in except Exception: return {} with no logging. The docstring justifies the missing-table case, but the catch swallows every failure — transient DB outage, bad URL, permission error — and answers "no overrides": a disabled plugin silently…
  • RADD-876 field_writability runs a dead effective_permissions query on a hot path fields · 1 pts · cb481be5 fields/router.py:137perms = await authz.effective_permissions(...) whose result is never used (ruff F841, hand-verified). GET /fields/writable is called by useItemWritability across the SPA (spec 96), so every issue open pays a wasted permission-resolution round trip.
  • RADD-871 Issue page's linked-pages section 404s: SPA calls /items/{id}/docs, route is /items/{id}/pages items pages · 1 pts · 2dfdb9ab apiItemDocsPath (web/src/lib/constants/api-paths.ts:195) builds /items/{id}/docs, but the backend route is GET /items/{item_id}/pages (pages/router.py:463) since the RADD-701 docs→pages rename. itemPagesQuery is consumed by ItemPagesSection.tsx and routes/item-detail.tsx, so the issue page's linked-pages…

New Feature

  • RADD-884 Wire the server search/pagination that already exists: audit log, users table, inbox audit auth notify · 2 pts · 73824a8d three surfaces where the server is already able but the SPA never asks: the audit log has limit+offset (audit/router.py:28) but the page hardcodes 200 rows and offers no text filter; Settings → Users has server q but no pagination, rendering all 3,088 rows unfiltered; the Inbox caps at 100 with server limit/offset…
  • RADD-883 Server-backed q/limit/offset for the big collections; Select flips to server mode at scale auth fields groups labels projects teams · 5 pts · 99d94ac1 client filtering fixes findability but a 2,293-row /teams payload is the real risk at 10×; /users/directory ships 3,088 rows to every picker.
  • RADD-882 useListFilter + ListSearchInput: client-side filter for every settings list fields groups labels pages releases teams · 3 pts · 8b7dc8f2 27 of 47 audited list surfaces have no filter (report 08 table). Worst: Settings → Teams 2,293 rows, Labels 2,016, Fields rail 319 — all fetched fully, so client-side filtering is correct and needs no API change.

New UI Feature

  • RADD-901 Kit growth: IconButton, ErrorText, Callout, Popover; useKeyedRows; Modal focus trap; SubjectPicker rebuild automations settings views · 5 pts · e88ec5c5 Per report 03 §2/§3/§5/§6: IconButton kills the 34-copy class string; ErrorText kills 109 ad-hoc error paragraphs; Callout kills the 9 amber panels; Popover (backdrop + positioning + dismiss-stack) so DisplayMenu/WipLimitMenu/BreadcrumbCrumb stop rebuilding it; the 46 bordered button lookalikes fold into…
  • RADD-901 Kit growth: IconButton, ErrorText, Callout, Popover; useKeyedRows; Modal focus trap; SubjectPicker rebuild automations settings views · 5 pts · c6efba4d Per report 03 §2/§3/§5/§6: IconButton kills the 34-copy class string; ErrorText kills 109 ad-hoc error paragraphs; Callout kills the 9 amber panels; Popover (backdrop + positioning + dismiss-stack) so DisplayMenu/WipLimitMenu/BreadcrumbCrumb stop rebuilding it; the 46 bordered button lookalikes fold into…
  • RADD-881 Select/SelectField grow a searchable mode; TokenMultiSelect caps rendered matches views · 3 pts · b93a0c21 the kit Select (Select.tsx:65) only does first-letter jump type-ahead — over the ~1,031-option user directory that is effectively no search. Seven pickers ship it at that scale (issue rail assignee/reporter, New Item, bulk bar, timesheet person, project + team Add member, Jira user mapper), plus…

Chore

  • RADD-892 Auth aggregation inversion + kernel entity sockets + project_purge hook auth jiraimport kernel projects · 8 pts · d3eeeb8a What is wrong (report 01 §1/§2/§4): auth imports six feature modules it outranks (nav-facts from timelogging/forms, PageSpace scope labels, groups/teams subjects) — the aggregation inversion; kernel/entities.py hard-imports auth/projects/events at runtime; jiraimport/rollback.py deletes seven modules' tables by…
  • RADD-891 Setting keys become SettingSpec contributions; settings keeps the cascade mechanism kernel settings · 5 pts · d560ca97 settings/types.py::SettingKey hardcodes AI (7), CSAT, release (2), timesheet (4), LDAP (12), workflow and estimation keys — the settings module knows every feature's tunables; the file calls itself a registry but is an enum (report 01 §5). The kernel plugin fields for this (settings_keys) exist and are never read…
  • RADD-890 Permission atoms become RaddPlugin contributions; auth keeps only its own auth kernel · 8 pts · f249d484 auth/types.py::Permission centrally enumerates ~150 atoms for every module while the kernel permissions registry exists and milestones proves contribution works — two parallel systems, and every new module edits auth (report 01 §5, critical).
  • RADD-902 Split the oversized security-critical files along their own section markers ai auth automations views · 5 pts · 275fe9c1 Per report 02 file-size table + report 03: 65 backend / 64 frontend files exceed the ~300-line rule. This issue takes the ones whose seams the reports verified: auth/authz.py 1099 (decision core / seam / batched / explain), auth/service.py 1087 (users / sessions / tokens / lifecycle.py for the merge-delete block),…
  • RADD-897 One clock, tunables into config.py, db pool settings kernel · 2 pts · 179c66e8 Per report 02 systemic fixes 2/6: radd/clock.py::utcnow() (naive-UTC, documented as the schema convention) replaces the 15 private _utcnow/_now helpers and ~15 inline copies; normalize timezone.utcUTC (attachments/movejob.py). Tunables sweep: sso/forgejo httpx timeouts, the stray BATCH/MAX_* module…
  • RADD-895 Backcompat removal, server side: alias routes, dual-shape parsers, wire mirrors, one-shot migrations ai attachments auth comments fields forgejo ldap reporting sso teams views · 3 pts · 8a9923d2 Per 06-backcompat-hacks.md and the no-backcompat-until-V1 rule: delete the pre-102 attachment alias routes (A1), the Forgejo env-state merge fallback + env-keyed capability check (A2/A3 — capability counts connection rows instead), the UserSource.UNKNOWN lazy upgrade via one throwaway migration deriving source…
  • RADD-900 Semantic status color tier; charts and roadmap consume tokens instead of hex dashboards reporting · 3 pts · a06793fa Per report 03 §1 + systemic fix 1: charts still draw the retired indigo accent (BURNUP_SCOPE_COLOR/CHART_ACCENT_COLOR #818cf8) and stock-zinc grid/axis hexes, theme-blind; roadmap connectors same; cycle pills use shades absent from the light remap (blue-200 on white ≈1.4:1); kind/priority icon shades unmapped; the…
  • RADD-893 Backend dead-code sweep (~1,100 lines): RADD-828 cluster, spec-90 schemas, kernel dead seams, 12 routes auth backup forms jiraimport kernel pages sso · 3 pts · b7f5ae23 Per 05-dead-code-backend.md, the CERTAIN/LIKELY deletions: the RADD-828 cluster (if-False branch, deflect_public, Form.public_token + drop migration), spec-90 wizard schemas + ImportStage enum, the sso identity surface, 37 dead functions, the never-wired kernel machinery EXCEPT TaskSpec (which RADD-872 wires…
  • RADD-898 Enum-bypass fixes + exception narrowing + the lints that keep both fixed ai attachments auth automations mcp pluginmgr workflow · 3 pts · 4d2dfca7 Per report 02 §1/§2: use the enums that exist (RuleType in the routing engine, ApproverKind in guards, InstanceRole in roles_router, ItemEvent.DELETED in comments/attachments parents, StorageHostType in backup); new enums where the vocabulary lives in a comment (automations PlanKind, embedder EmbedTaskKind, pluginmgr…
  • RADD-889 MCP core tools become McpToolSpec contributions in their owner modules; pages_bridge dies items mcp pages releases timelogging · 5 pts · a843098f the RADD-640 McpToolSpec registry has exactly one client (milestones); the 19+ core tools are hardcoded in mcp/tools.py importing ten modules, and pages is special-cased through an importlib reflection bridge (pages_bridge.py) that duck-types signatures at runtime (report 01 §5).
  • RADD-896 Backcompat removal, frontend: pre-101 tolerance, legacy queue path, legacy routes decision editor views · 1 pts · c8890350 Per 06-backcompat-hacks.md (d) + 05-dead-code-frontend.md §3: make features required in the AI status type (the SPA and backend ship in one image — no pre-101 pairing exists), delete ViewList's unreachable legacy queue render path (QueueRowMeta + prop threading; confirm no swimlane reuse), fix applyRankChain's…
  • RADD-894 Frontend dead-code sweep (~660 lines + 5 assets): query factories, meta constants, types, CSS jiraimport pages views · 2 pts · 50a10999 Per 05-dead-code-frontend.md: the query-factory + queryKeys + api-path chain (~170 lines, one refactor's residue), six meta.ts constants, the misc dead exports (useMoveItem, EpicProgressBlock, plugin-loader diagnostics, …), 16 dead types incl. the pre-spec-100 jira-import block, dead zinc tokens + the stale…
  • RADD-870 Docs truth pass: doc-file drift (CLAUDE.md, modules.md, contributing, README, PLAN) docs · 3 pts · eabc0dfd What is wrong (report 04): CLAUDE.md's status preamble is 12 releases stale (claims 0.9.0; newest tag v0.21.0), cites a /health endpoint that does not exist, a --workspace main import flag that argparse rejects (also in README), "Tests 1335" (now 1570). docs/modules.md: pgvector listed as an unbuilt gap (shipped…
  • RADD-899 Snapshot[T] TTL helper replaces the four module-level write-through caches ai attachments sso · 2 pts · daf6a603 Per report 02 §7: sso registry _snapshot, ai _role_snapshot, attachments hosts _default_snapshot + clients _cache, and the SSO metadata cache all go stale under >1 web replica (admin edit in worker A, worker B serves old state until restart). One Snapshot[T] helper (value + loaded-at + ttl + async refresh,…
  • RADD-887 Model-import burn-down: access read seams, ldap→groups.service, forms portal, notify, search, misc access ai approvals forms ldap notify pages search · 5 pts · 14d858a2 the report-01 §3e/§3f reaches: four access-framework adopters query AccessGrant directly (dashboards/fields/views/pluginmgr); ldap/groupsync.py writes groups tables raw; forms/requests.py composes five modules' tables; notify imports a comments-internal visibility helper; search/semantic.py reads…
  • RADD-888 SLQ relational builtins move into owner SlqFieldSpec contributions cycles items itemtypes releases teams workflow · 5 pts · 485d4598 spec 97 built SlqFieldSpec so "items never learns those modules exist", then left the builtin catalog hardcoded — items/slq/builtins.py:12-16 imports cycles/releases/itemtypes/teams/workflow models while logged_by/commented_by go through the registry (report 01 §3a).
  • RADD-886 events.service re-exports Event; the 13 consumer imports of events.models die events · 1 pts · 16cf0aef the Event row IS the consumer contract, but it lives in events/models.py, so 13 consumers import another module's models (report 01 §3c list).
  • RADD-885 CI ratchet: models-import whitelist + imports⊆depends_on, with weak_depends on RaddPlugin kernel · 3 pts · a5ee9fa6 the written spine exception (User, Project) is fiction — 78 non-spine X.models imports, 30 modules importing undeclared deps, cycles hidden behind "# deferred: loads after" comments (report 01 §3/§6 tables).
  • RADD-863 Deep-scan audit 2026-08: nine-category codebase report docs · 3 pts · 14c26b4d What: Full deep scan of the codebase (~76k lines Python / ~78k TS) across nine categories: kernel/plugin architecture violations, backend + frontend bad practices, misleading docs/comments, dead code (both sides), backcompat hacks, missing features, and the long-lists-without-filters sweep Hussein flagged.

Changes from v0.21.0 to v0.22.0.


Mirrored from project.radd-hq.com on 2026-09-12. Documentation is written there; this copy is regenerated by scripts/publish_wiki.py and hand edits do not survive it.

Clone this wiki locally