231 lines
29 KiB
Markdown
231 lines
29 KiB
Markdown
|
|
# DevPlace: gap analysis against the Apple App Store requirement register
|
|||
|
|
|
|||
|
|
Author: retoor <retoor@molodetz.nl>
|
|||
|
|
|
|||
|
|
Stage two of `apple.md`. Input is the requirement register in [`applecomp.md`](applecomp.md) §8. Output is the exhaustive list of changes DevPlace needs to make an iOS client of this platform publishable. The implementation design is [`appleimpl.md`](appleimpl.md).
|
|||
|
|
|
|||
|
|
Every verdict below is backed by a file reference read during the traversal. No verdict is inferred from documentation; documentation was only used to locate code.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Method
|
|||
|
|
|
|||
|
|
The traversal covered, recursively:
|
|||
|
|
|
|||
|
|
- `devplacepy/routers/` - every router file and package, for the full endpoint surface.
|
|||
|
|
- `devplacepy/models.py`, `devplacepy/schemas/` - every input form and output schema.
|
|||
|
|
- `devplacepy/database/` - `schema.py` (column ensure blocks), `soft_delete.py` (`SOFT_DELETE_TABLES`), the batch helpers.
|
|||
|
|
- `devplacepy/templates/` - every template that renders a content action bar, the admin shell, the footer, the docs registry.
|
|||
|
|
- `devplacepy/services/` - audit, devii, openai_gateway, containers, messaging, game, quiz, bot, news.
|
|||
|
|
- `devplacepy/content.py`, `devplacepy/responses.py`, `devplacepy/templating.py` - the shared predicates and response choke points.
|
|||
|
|
- `devplacepy/main.py` - middleware stack and router mounts.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Inventory: every user-generated-content surface
|
|||
|
|
|
|||
|
|
Requirement **R5** (report on every UGC surface) and **R4** (filter on every UGC surface) are only satisfiable against a complete list. This is that list, derived from `SOFT_DELETE_TABLES` in `devplacepy/database/soft_delete.py:7` cross-checked against the routers that write each table.
|
|||
|
|
|
|||
|
|
| # | Surface | Table | Write entrypoint | Visible to |
|
|||
|
|
|---|---------|-------|------------------|-----------|
|
|||
|
|
| S1 | Posts | `posts` | `routers/posts.py` via `content.create_content_item` | Public |
|
|||
|
|
| S2 | Comments (polymorphic: post, project, gist, news) | `comments` | `routers/comments.py` via `content.create_comment_record` | Public |
|
|||
|
|
| S3 | Gists | `gists` | `routers/gists.py` | Public |
|
|||
|
|
| S4 | Projects (title, description, devlog) | `projects` | `routers/projects/` | Public or private |
|
|||
|
|
| S5 | Project files (arbitrary text/binary) | `project_files` | `routers/projects/files/` | Public or private |
|
|||
|
|
| S6 | News submissions | `news` | `routers/news.py`, `services/news/` | Public |
|
|||
|
|
| S7 | Uploaded media / attachments | `attachments` | `routers/uploads.py`, `attachments.py` | Follows parent |
|
|||
|
|
| S8 | Direct messages | messaging store | `routers/messages.py:245` `send_message` + `/messages/ws` | Two parties |
|
|||
|
|
| S9 | Quizzes, questions, options | `quizzes`, `quiz_questions`, `quiz_options` | `routers/quizzes/` | Public |
|
|||
|
|
| S10 | Poll questions and options | `polls`, `poll_options` | `routers/polls.py` | Public |
|
|||
|
|
| S11 | Awards (user-issued citations) | `awards` | `routers/awards.py` | Public |
|
|||
|
|
| S12 | Profile fields: bio, location, git link, website | `users` | `models.py:408` `ProfileForm` | Public |
|
|||
|
|
| S13 | Username and avatar seed | `users` | `routers/auth/signup.py`, `routers/profile/avatar.py` | Public |
|
|||
|
|
| S14 | Issue tickets and issue comments | `issue_tickets` (Gitea-backed) | `routers/issues/` | Public |
|
|||
|
|
| S15 | Devii assistant output (chatbot under guideline 4.7) | `devii_conversations` | `services/devii/` | Owner, and anything it publishes |
|
|||
|
|
| S16 | User-authored virtual tools and lessons | `devii_virtual_tools`, `devii_lessons` | `services/devii/` | Owner |
|
|||
|
|
| S17 | Per-user custom CSS/JS | `user_customizations` | `services/devii/customization/` | Owner's own browser only |
|
|||
|
|
| S18 | Container workspaces and anything they serve | `instances`, `tunnels` | `services/containers/`, `routers/proxy.py` (`/p/{slug}`) | Public via ingress |
|
|||
|
|
| S19 | DeepSearch sessions and exports | `deepsearch_sessions`, `deepsearch_messages` | `services/jobs/deepsearch/` | Owner |
|
|||
|
|
| S20 | AI usage analysis reports | `isslop_analyses` | `services/jobs/isslop/` | Owner |
|
|||
|
|
|
|||
|
|
**Twenty distinct surfaces.** Sixteen of them (S1-S14, S18, and S15's published output) are visible to at least one other person and therefore fall inside guideline 1.2's scope. This breadth is the single defining constraint of the implementation: any design that requires per-surface bespoke code will be incomplete on the day it ships and will decay afterwards.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Inventory: what already exists and can be reused
|
|||
|
|
|
|||
|
|
| Capability | Where | Fitness for the requirement |
|
|||
|
|
|-----------|-------|-----------------------------|
|
|||
|
|
| **Block and mute** | `routers/relations.py` (`/block/{username}`, `/mute/{username}`, and the `unblock`/`unmute` inverses), `user_relations` table, `_drop_blocked` in `database/comments.py` | Satisfies **R9** functionally. Reachability from content is a gap (see G9). |
|
|||
|
|
| **Soft delete across the board** | `database/soft_delete.py`, `SOFT_DELETE_TABLES` (44 tables) | Every content removal is already reversible and auditable, which is exactly what **P2** and DSA statements of reasons need. |
|
|||
|
|
| **Admin Trash** | `routers/admin/trash.py`, `/admin/trash`, restore/purge by event | Moderator undo path already exists. |
|
|||
|
|
| **Append-only audit log** | `services/audit/`, 288 keys in `events.md`, `/admin/audit-log` | The evidence substrate for **P1**, **P2** and the 24-hour SLA proof. |
|
|||
|
|
| **Account deactivation** | `users.is_active`, admin toggle at `routers/admin/users.py:179`, devrant `DELETE /api/users/me` at `routers/devrant/auth.py:189` | **Not** account deletion. Apple explicitly rejects deactivation-only. See G12. |
|
|||
|
|
| **Admin seniority guard** | `_is_senior_admin` in `routers/admin/users.py` | Reusable for moderator-action authorization. |
|
|||
|
|
| **Workspace moderation flags** | `services/containers/workspace/flags.py` - `raise_flag`, `clear_flag`, `set_status`, `list_flags`, statuses `open/acknowledged/resolved/dismissed`, severities `info/warn/critical`, soft-deletable `workspace_flags` table | **The closest existing analogue to a report queue.** It is instance-scoped, machine-raised and admin-resolved. Its state machine, severity ladder and audit shape are the correct precedent to generalise from. |
|
|||
|
|
| **Per-user AI opt-in** | `users.ai_correction_enabled` (default `0`) and `users.ai_modifier_enabled` (default `1`), `routers/profile/ai_correction.py`, `routers/profile/ai_modifier.py` | Establishes the pattern for a consent flag on the user row. Partially serves **R15** but is feature-scoped, not consent-scoped, and one of the two defaults to on. |
|
|||
|
|
| **Notification preferences** | `notification_preferences` table, `NOTIFICATION_TYPES` × `NOTIFICATION_CHANNELS`, `routers/profile/notifications.py` | Push is already per-type, per-channel and user-controlled - **R17** is close to satisfied. |
|
|||
|
|
| **Polymorphic target pattern** | `(target_type, target_uid)` on `comments`, `votes`, `reactions`, `bookmarks`; `resolve_target_redirect()` in `comments.py`; `database/ranking.py` `VOTABLE_TARGETS`/`STAR_TARGETS`; `database/content.py` `resolve_object_url` | **The load-bearing reuse.** A report is structurally identical to a vote: one row keyed on `(target_type, target_uid)` plus an actor. Reporting must be built on this exact pattern, not beside it. |
|
|||
|
|
| **Devii action catalog** | `services/devii/actions/catalog/`, `CONFIRM_REQUIRED` in `dispatcher.py` | Every new route gets its agent face here, per the root `CLAUDE.md` four-faces rule. |
|
|||
|
|
| **Docs prose registry** | `routers/docs/pages.py` `DOCS_PAGES`, e.g. the existing `block-and-mute` and admin-only `media-moderation` pages | The publication channel for terms, community guidelines and privacy policy, with role gating already implemented. |
|
|||
|
|
| **Site settings** | `site_settings`, `get_setting`/`get_int_setting`, `/admin/settings` | Where the moderation SLA, minimum age and filter aggressiveness belong - live-editable, no restart. |
|
|||
|
|
| **AI gateway** | `services/openai_gateway/`, `/openai/v1/*`, per-user cost attribution | Single choke point through which **every** third-party AI call passes. **R15**'s consent gate has exactly one correct insertion point because of this. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. The gap register
|
|||
|
|
|
|||
|
|
Verdicts: **MISSING** (does not exist), **PARTIAL** (exists but does not meet the requirement), **PRESENT** (meets the requirement), **N/A** (not triggered).
|
|||
|
|
|
|||
|
|
### 4.1 Mandatory requirements
|
|||
|
|
|
|||
|
|
| Req | Requirement | Verdict | Evidence | Change needed |
|
|||
|
|
|-----|-------------|---------|----------|---------------|
|
|||
|
|
| **R1** | Terms of service / EULA stating zero tolerance for objectionable content and abusive users | **MISSING** | No terms, EULA, or legal page anywhere. Grep for `terms`/`eula`/`privacy polic` across `templates/` and `routers/` returns only four unrelated docs pages (bots and Code Farm prose). `_footer_links.html` links Docs, Swagger, OpenAPI, Issue Report only. | Author the document; publish it as a first-class page; link it from the footer, the signup form and account settings. |
|
|||
|
|
| **R2** | Recorded affirmative acceptance at account creation, re-acceptance on material change | **MISSING** | `routers/auth/signup.py` collects username, email, password, confirm only. `models.py:51` `SignupForm` has four fields. No acceptance column on `users` (`database/schema.py:1823`ff enumerates every ensured column; none is terms-related). | Add a required acceptance control to signup; persist the accepted document version and timestamp; force re-acceptance when the version changes. |
|
|||
|
|
| **R3** | Community guidelines enumerating prohibited content per 1.1.1-1.1.7 | **MISSING** | No such document. | Author and publish; reference from the terms and from every report dialog. |
|
|||
|
|
| **R4** | Automated filtering of objectionable material at post time on every surface | **MISSING** | No content filter exists. The only `blocklist` occurrences in the codebase are the bot **quality** gate (`TRIVIAL_GIST_TERMS`, `GENERIC_COMMENT_PHRASES`) documented in `templates/docs/bots-content.html:38` - these judge whether generated content is *interesting*, not whether user content is *objectionable*, and they run only on bot output. | Introduce a filter that runs on every user-authored text at the single creation choke point, with an admin-tunable severity, that can block, hold for review, or flag. |
|
|||
|
|
| **R5** | Report mechanism on every UGC surface | **MISSING** | No report route, table, template, schema, or Devii action exists. `routers/relations.py` provides block/mute only. `services/containers/workspace/flags.py` flags *workspaces*, machine-raised, and is not reachable by a member for content. | Build a polymorphic report facility covering all sixteen externally-visible surfaces in §2. |
|
|||
|
|
| **R6** | Moderation queue with triage, decision and enforcement | **MISSING** | `/admin` sidebar (`templates/admin_base.html:11`-`59`) has Users, News, Media, Trash, Services, Gateway, Containers, Workspaces, Devii tasks, Bots, Game, AI usage, Statistics, Audit log, Backups, Notifications, Settings. There is no moderation section. `/admin/media` handles only *already soft-deleted* media. | Add a moderation queue as a first-class admin section, in the established `admin_section` pattern. |
|
|||
|
|
| **R7** | Published 24-hour response commitment, and a mechanism that evidences it | **MISSING** | No SLA is published or measured. | Publish the commitment in the terms and the report confirmation; measure age-of-oldest-open-report; surface it to admins and alert on breach. |
|
|||
|
|
| **R8** | Ejection of offending users as a first-class enforcement action | **PARTIAL** | `users.is_active` toggled at `routers/admin/users.py:179`. It is a bare on/off with no reason, no duration, no linkage to a report, and no notice to the user. `routers/devrant/auth.py:189` sets the same flag as "delete account". | Promote to a suspension/ban action carrying reason, scope, duration and a link to the report that caused it, and generating a statement of reasons (**P3**). |
|
|||
|
|
| **R9** | Block abusive users | **PARTIAL** | Fully implemented at `routers/relations.py:87`-`104` with enforcement in `database/comments.py` `_drop_blocked`. The gap is discoverability: the action is only reachable from a profile page. `templates/_post_card.html:32`ff and `templates/_comment.html:27`ff action bars offer Reply/Edit/Delete/React/Share and no Block. | Surface block from the content action bar alongside report; verify DM enforcement. |
|
|||
|
|
| **R10** | Published contact information reachable inside the app | **PARTIAL** | `_footer_links.html` links `/issues` ("Issue Report"), which is a Gitea-backed bug tracker requiring an account, not a contact route. No postal address, no email, no phone. | Publish a contact page carrying the DSA-mandated address, email and phone, linked from the footer and from settings. |
|
|||
|
|
| **R11** | Privacy policy meeting 5.1.1(i)'s three content requirements, in-app | **MISSING** | No privacy policy exists. | Author to the three-point spec; publish; link in-app and supply the URL to App Store Connect. |
|
|||
|
|
| **R12** | In-app account deletion of the account record and associated personal data | **MISSING** | The only account-removal path in the product is `DELETE /api/users/me` (`routers/devrant/auth.py:189`) which sets `is_active = False` and revokes tokens - **deactivation**, which Apple's account-deletion support page names as explicitly insufficient. There is no route under `/profile` or `/auth` for deletion. | Build a real, self-service, reauthenticated deletion that removes the account record and the associated personal data, discoverable in account settings. |
|
|||
|
|
| **R13** | Declared-age gate at account creation, plus age-based access restriction | **MISSING** | No birthdate, age or date-of-birth field exists anywhere: grep across `models.py` and `database/` returns nothing. `SignupForm` has no age field. | Collect a declared age at signup, store the derived age band (not the raw birthdate, per 5.1.4 data minimization), enforce a minimum age, and gate age-exceeding content on it. |
|
|||
|
|
| **R14** | Content age labelling; mature content hidden by default | **MISSING** | No maturity flag on any content table. | Add a maturity classification produced by the filter and settable by the author, and hide flagged content behind an explicit, age-gated opt-in. |
|
|||
|
|
| **R15** | Explicit consent before user content reaches third-party AI, with disclosure | **PARTIAL** | Two per-feature toggles exist: `users.ai_correction_enabled` defaults to `0` (opt-in, compliant in shape) and `users.ai_modifier_enabled` defaults to `1` (**opt-out - non-compliant**), both at `database/schema.py:1832`-`1841`. Neither is framed as consent to third-party processing, neither names the provider, and neither covers the other AI paths: Devii (`services/devii/`), DeepSearch, SEO metadata generation, the AI usage analyzer, issue enhancement (`services/gitea/enhance.py`), news import, and bots. All of these route through `/openai/v1/*` (`services/openai_gateway/`). | Introduce one explicit, named, versioned third-party-AI consent, defaulting to off, enforced at the gateway choke point, with the per-feature toggles kept as preferences subordinate to it. |
|
|||
|
|
| **R16** | Easily accessible consent withdrawal | **MISSING** | No consent record exists, therefore nothing to withdraw. | Consent record with a withdraw action in account settings, and a downstream effect that is real (processing stops). |
|
|||
|
|
| **R17** | Push optional, marketing push opt-in, in-app opt-out | **PRESENT** | `notification_preferences` per type per channel (`database/notifications.py`), user-editable at `routers/profile/notifications.py:17`. Push registration is explicit at `routers/push.py:32`. Nothing in the app requires push to function. | Verify no notification type is marketing-by-default; document the position for review notes. |
|
|||
|
|
| **R18** | DMCA / IP notice-and-takedown channel | **MISSING** | None. | Add an intellectual-property report reason to the report facility and a public notice-and-takedown page describing the counter-notice path. |
|
|||
|
|
| **R19** | Demo account with pre-seeded content and complete review notes | **MISSING** | No provisioning path for a review account exists; `registration_open` (`site_settings`) can close signup entirely, which would leave a reviewer unable to create an account. | Provide a stable demo account with visible content from other authors, so report and block can both be exercised. Write the review notes. |
|
|||
|
|
| **R20** | Age-rating questionnaire answered from the real feature set | **BLOCKED BY R4/R5/R6/R13** | The questionnaire asks whether the app has moderation systems, content filtering, reporting tools, blocking functionality and parental controls. Today four of five answers are "no". | Answers become truthful only once R4, R5, R6 and R13 ship. |
|
|||
|
|
| **R21** | App privacy details declared, including third-party AI processing | **BLOCKED BY R15** | Nothing to declare against until the AI data flow is disclosed and consented. | Declare Contact Info, User Content, Identifiers, Usage Data, Diagnostics, all Linked to You, none Used to Track You. |
|
|||
|
|
| **R22** | EU trader status with address, phone, email | **MISSING (metadata)** | The same contact data R10 needs. | Declare in App Store Connect; keep identical to the in-app contact page. |
|
|||
|
|
| **R23** | IPv6-only reachability | **UNVERIFIED** | `docker-compose.yml` and `nginx/nginx.conf.template` were not confirmed to bind IPv6; uvicorn defaults are IPv4. | Verify and, if needed, fix listen directives for the app, nginx, the WebSocket routes and the container ingress. |
|
|||
|
|
| **R24** | Remote code execution positioned under the 2.5.2 educational exception | **PARTIAL** | Substantively compliant already: containers execute **remotely** (`services/containers/`), the browser IDE makes source completely viewable and editable (`routers/projects/files/`), and nothing alters the client binary. What is missing is the **positioning**: no documentation states this, and the review notes do not exist. | Document the architecture for App Review; make the "code runs on our servers, never on your device" statement explicit in the product and the docs. |
|
|||
|
|
| **R25** | Native client materially beyond a web wrapper | **OUT OF SCOPE (client)** | The iOS binary is not in this repository. | The backend obligation is to expose every safety control as a JSON API so the native client can implement them natively rather than embedding web views. Covered by the four-faces rule. |
|
|||
|
|
|
|||
|
|
### 4.2 Conditional requirements
|
|||
|
|
|
|||
|
|
| Req | Trigger present? | Verdict | Evidence |
|
|||
|
|
|-----|------------------|---------|----------|
|
|||
|
|
| **C1** Sign in with Apple or equivalent | **No** | **N/A - must stay N/A** | Auth is exclusively DevPlace's own system: session cookie, `X-API-KEY`, Bearer, HTTP Basic, all resolved in `get_current_user`. `routers/auth/` has no OAuth provider. Guideline 4.8 exempts apps that exclusively use their own account system. **Adding any social login later immediately creates the Sign in with Apple obligation.** |
|
|||
|
|
| **C2** IAP for digital goods | **No** | **N/A - must stay N/A** | No payment processor anywhere: no Stripe, PayPal or checkout integration in the codebase. The Code Farm economy (`services/game/`) is earn-only; Stars and Era awards are not purchasable. AI quota is administered, not sold (`devplace gateway quota set`). **Any future sale of coins, credits, quota or boosts inside the app triggers mandatory IAP.** |
|
|||
|
|
| **C3** Loot-box odds disclosure | **No** | **N/A** | Randomized game rewards are not purchasable with real money. |
|
|||
|
|
| **C4** Contest rules stating Apple is not a sponsor | **Borderline** | **PARTIAL** | Code Farm Eras (`devplace game era start/end`) rank players and award Stars. As long as awards are cosmetic/status only and nothing of monetary value is given, 5.3 is not engaged. Any real prize engages it. Document the position. |
|
|||
|
|
| **C5** Index of offered software with universal links | **Yes** | **MISSING** | Users can publish workspaces reachable via the ingress proxy `/p/{slug}` (`routers/proxy.py`) and other users can open them. Guideline 4.7.4 requires an index of that software with universal links. No such index exists. |
|
|||
|
|
| **C6** Ad reporting control | **No** | **N/A** | No advertising anywhere in the codebase. |
|
|||
|
|
| **C7** App Tracking Transparency | **No** | **N/A** | No cross-app or cross-site tracking; no third-party analytics SDK. |
|
|||
|
|
| **C8** Recording indicator and consent | **Yes** | **MISSING** | Presence tracking (`services/presence.py`, `last_seen`), the live view relay (`services/live_view_relay.py`), Devii terminal sessions and the audit log all make a record of user activity. Guideline 2.5.14 requires explicit consent **and** a clear indication. Presence is currently silent and unconditional. |
|
|||
|
|
| **C9** Per-instance consent before sharing data with user software | **Yes** | **MISSING** | Container workspaces and Devii virtual tools can receive platform data. 4.7.3 requires explicit user consent **in each instance**. |
|
|||
|
|
|
|||
|
|
### 4.3 Posture requirements
|
|||
|
|
|
|||
|
|
| Req | Verdict | Notes |
|
|||
|
|
|-----|---------|-------|
|
|||
|
|
| **P1** Compliance improvement plan on request | **MISSING** | Needs moderation throughput metrics, which need R6. |
|
|||
|
|
| **P2** Moderation decisions retained as an audit trail | **PARTIAL** | The audit log already records every state change and never raises into the caller (`services/audit/`). Moderation event keys do not yet exist in `events.md`. |
|
|||
|
|
| **P3** Statement of reasons to the actioned user | **MISSING** | Content is soft-deleted silently. The notification system (`utils/notifications.py`, `create_notification`) is the right delivery channel and already exists. |
|
|||
|
|
| **P4** Privacy labels kept in step with features | **MISSING** | Process obligation; needs a documented owner and a checklist entry in the feature workflow. |
|
|||
|
|
| **P5** Accurate "What's New" | **MISSING** | Process obligation on the client release. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. The positioning conflict - the finding that outranks every table above
|
|||
|
|
|
|||
|
|
DevPlace currently **markets itself as uncensored**. This is not incidental copy; it is the product's stated identity in four places:
|
|||
|
|
|
|||
|
|
- `devplacepy/main.py:744` - the site description: *"Share what you're building in an open, uncensored environment."*
|
|||
|
|
- `devplacepy/templates/base.html:9` - the default `meta description`, on every page.
|
|||
|
|
- `devplacepy/templates/landing.html:120` - the landing hero paragraph, and at `landing.html:134` a feature card headed **"No Censorship"**.
|
|||
|
|
- `devplacepy/database/schema.py:280` - the default `site_tagline` site setting, echoed in `templates/admin_settings.html:24`.
|
|||
|
|
|
|||
|
|
Guideline 1.2 requires a **method for filtering objectionable material** and makes removal of violating content the developer's explicit responsibility. An App Review reviewer who opens the landing page - which they will, because it is the Support/Marketing URL - reads a promise that the platform does not moderate. That single sentence is sufficient grounds for a 1.2 rejection **regardless of how good the implementation is**, because it is a public statement that the required controls are not exercised.
|
|||
|
|
|
|||
|
|
There is no technical fix for this. The positioning must change to something that is both true and compatible: the platform is **open and uncensored in the sense that it does not editorialise developer opinion**, while enforcing a floor of prohibited categories. The four sites above must be reworded in step, and the wording must match the terms of service and community guidelines exactly, because a mismatch between marketing and policy is itself a 2.3.1 problem.
|
|||
|
|
|
|||
|
|
This is flagged as a decision for the lord, not an assumption: it changes the product's public voice.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Consolidated change list
|
|||
|
|
|
|||
|
|
Grouped by the layer they land in, so the implementation document can sequence them. Nothing here is designed yet; this is scope, not solution.
|
|||
|
|
|
|||
|
|
### 6.1 Data layer
|
|||
|
|
|
|||
|
|
1. A polymorphic **reports** store keyed on `(target_type, target_uid)`, soft-deletable, with a state machine.
|
|||
|
|
2. **Moderation decision** records linked to reports, retained for the audit trail.
|
|||
|
|
3. **Enforcement** records: suspension/ban with reason, scope, duration, originating report.
|
|||
|
|
4. `users` columns: terms-acceptance version and timestamp; declared age band; third-party-AI consent version, timestamp and state; activity-recording consent.
|
|||
|
|
5. A **maturity** classification on content, produced by the filter and adjustable by the author.
|
|||
|
|
6. New `site_settings` keys: moderation SLA hours, minimum age, filter mode and thresholds, contact details, current policy document versions.
|
|||
|
|
7. New soft-delete table registrations and indexes for all of the above.
|
|||
|
|
|
|||
|
|
### 6.2 Server layer
|
|||
|
|
|
|||
|
|
8. Report submission endpoints, polymorphic, member-authenticated, rate-limited.
|
|||
|
|
9. Report listing and decision endpoints for moderators, with the seniority guard.
|
|||
|
|
10. Enforcement endpoints (suspend, ban, lift) replacing the bare `is_active` toggle.
|
|||
|
|
11. Account **deletion** endpoint with reauthentication and a real data-removal cascade.
|
|||
|
|
12. Terms acceptance endpoint plus a gate that forces re-acceptance on version change.
|
|||
|
|
13. AI consent endpoints, and enforcement at the `/openai/v1/*` gateway choke point.
|
|||
|
|
14. Age declaration at signup, and an age predicate applied at every read of maturity-flagged content.
|
|||
|
|
15. The content filter, invoked at the single creation choke point that already exists in `content.py`.
|
|||
|
|
16. Public legal pages: terms, community guidelines, privacy policy, contact, notice-and-takedown.
|
|||
|
|
17. A published index of user-offered software with universal links (4.7.4).
|
|||
|
|
18. A presence/activity-recording consent and indicator (2.5.14).
|
|||
|
|
|
|||
|
|
### 6.3 View layer
|
|||
|
|
|
|||
|
|
19. Report and Block controls in **every** content action bar - `_post_card.html`, `_comment.html`, and the detail templates for gists, projects, news, quizzes, media, messages and profiles.
|
|||
|
|
20. A report dialog reusing the existing modal system, with reasons mapped to the 1.1.x categories.
|
|||
|
|
21. Signup form: terms acceptance and age declaration.
|
|||
|
|
22. Account settings: delete account, withdraw consent, view acceptances.
|
|||
|
|
23. Admin moderation section in the `admin_base.html` sidebar with the queue, SLA indicator and decision UI.
|
|||
|
|
24. Footer links to terms, privacy, community guidelines and contact.
|
|||
|
|
25. Maturity interstitial for age-exceeding content, hidden by default.
|
|||
|
|
|
|||
|
|
### 6.4 Agent, docs, SEO layer
|
|||
|
|
|
|||
|
|
26. Devii actions for report, moderation listing and decisions, with `CONFIRM_REQUIRED` on enforcement.
|
|||
|
|
27. `docs_api` entries for every new endpoint.
|
|||
|
|
28. `DOCS_PAGES` prose entries for the legal documents and a moderation page (admin-gated, like `media-moderation`).
|
|||
|
|
29. SEO: legal pages are public and indexable; moderation is `noindex,nofollow`.
|
|||
|
|
30. New audit event keys in `events.md` and `category_for`.
|
|||
|
|
|
|||
|
|
### 6.5 Positioning and process
|
|||
|
|
|
|||
|
|
31. Reword the four "uncensored" sites so marketing, terms and behaviour agree.
|
|||
|
|
32. Review notes, demo account, age-rating questionnaire answers, privacy labels, trader status.
|
|||
|
|
33. IPv6 verification across app, nginx, WebSockets and container ingress.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Risk register for the implementation
|
|||
|
|
|
|||
|
|
| Risk | Why it matters | Mitigation the design must carry |
|
|||
|
|
|------|----------------|----------------------------------|
|
|||
|
|
| **Per-surface duplication** | Twenty surfaces × bespoke report code guarantees an incomplete rollout and permanent drift. | One polymorphic facility on the existing `(target_type, target_uid)` pattern, registered once per surface, exactly as votes and reactions already are. |
|
|||
|
|
| **Filter false positives on a developer platform** | Code, security discussion and error messages are full of terms a naive filter flags. Blocking legitimate posts destroys the product. | The filter must default to flag-for-review rather than hard block, and must be admin-tunable through `site_settings` with no restart. |
|
|||
|
|
| **Silent failure** | The root `CLAUDE.md` forbids errors passing silently; a moderation control that fails open is worse than absent. | Report submission must never be swallowed; filter failure must fail toward review, not toward publication. |
|
|||
|
|
| **Deletion cascade correctness** | Account deletion touches nearly every table. A partial cascade leaves orphaned personal data and breaks the 5.1.1(v) promise. | One shared soft-delete stamp for the reversible window, then a hard purge, reusing `soft_delete_in` and `purge_event`. |
|
|||
|
|
| **Consent regression on the AI path** | Turning AI consent off by default changes behaviour for every existing user and every internal AI consumer (news, bots, issue enhancement, SEO metadata). | Distinguish consent for *the user's own content* from platform-owned processing; enforce at the gateway with an explicit owner kind. |
|
|||
|
|
| **Test suite scale** | ~2882 tests run serially. A change touching the content creation choke point touches everything. | Land the data and server layers first, run the full suite at each stage. |
|
|||
|
|
| **Economy and state-machine correctness** | Suspension, consent and age gates are read-then-write mutations reachable from HTTP, Devii, the devRant API and the CLI. | The root `CLAUDE.md` four-layer rigorous-verification procedure applies to enforcement and consent state. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Summary verdict
|
|||
|
|
|
|||
|
|
Of the 25 mandatory requirements: **1 present** (R17), **5 partial** (R8, R9, R10, R15, R24), **15 missing** (R1-R7, R11-R14, R16, R18, R19, R22), **2 blocked on others** (R20, R21), **1 unverified** (R23), **1 out of scope for this repository** (R25). The six categories partition all 25.
|
|||
|
|
|
|||
|
|
Of the 9 conditional requirements: **5 not triggered and must be kept that way** (C1, C2, C3, C6, C7), **3 triggered and missing** (C5, C8, C9), **1 borderline** (C4).
|
|||
|
|
|
|||
|
|
Of the 5 posture requirements: **1 partial** (P2), **4 missing**.
|
|||
|
|
|
|||
|
|
The platform has excellent bones for this work - polymorphic targeting, universal soft delete, a complete audit log, an admin shell, a single AI choke point and an agent catalog that already forces cross-layer completeness. What it lacks is the entire safety layer, the entire legal layer, and a public identity compatible with having one.
|