Repository source: docs/plans/deadlus_post_response_and_content_assessment_outline_v1.md
Deadlus Post Response And Content Assessment Outline V1
This document brainstorms a post-write feature for asking the Deadlus Labs API to:
generate an engaging response to a newly created thread or comment
assess the new post for unwanted behavior such as trolling, bad-faith argumentation, aggression, harassment, spam, or escalation risk
The key product constraint is that the post creation path must stay fast. The first page after submit should render normally, then trigger a separate backend request after load.
Goals
Keep
create_threadandcreate_replyfast and reliable.Analyze newly written posts only after the successful page load path has started.
Use the backend as the only caller of the Deadlus Labs API, so API keys and prompts never reach the browser.
Produce one structured result per post with both engagement and moderation fields.
Make the operation idempotent, so reloads, duplicate browser events, and retries do not create duplicate analysis work.
Give the UI a clean path to show useful output later without making the initial V1 depend on a polished moderation dashboard.
Non-Goals For V1
Do not block post publishing on Deadlus Labs analysis.
Do not auto-delete, auto-hide, or auto-punish users from the first iteration.
Do not auto-publish generated replies without a separate explicit product decision.
Do not expose raw prompts, private API diagnostics, or full moderation reasoning to public pages.
Do not require a full queue worker if the current deployment model cannot support one yet.
Current Code Context
The current PHP app has these relevant flows:
POST /compose/threadwrites a thread and redirects to/threads/<thread-id>.POST /compose/replywrites a reply and redirects to/threads/<thread-id>.POST /api/create_threadandPOST /api/create_replyreturn plain text withpost_id,thread_id, andcommit_sha.Public read pages are rendered from the SQLite read model and static artifacts where possible.
Existing scripts are deferred from
templates/layout.php, making a post-load JavaScript trigger consistent with the current frontend shape.
Recommended Shape
Use a two-step post-write flow:
The normal write request creates the post and redirects or returns quickly.
The landing page includes enough metadata to identify the newly created post.
A small deferred browser script runs after load and calls the backend, for example
POST /api/analyze_post.The backend validates the post id, checks whether analysis already exists, and either returns the cached result or calls Deadlus Labs.
The backend stores the result and returns a compact status payload.
This keeps the expensive AI request out of the initial write and first render while still allowing analysis to start immediately after the user lands on the new content.
Triggering Strategy
Form Redirects
For compose form submissions, add a newly created post marker to the redirect target:
thread creation:
/threads/<thread-id>?created_post_id=<post-id>&__v=<commit-sha>reply creation:
/threads/<thread-id>?created_post_id=<post-id>&__v=<commit-sha>
The thread page can then render an inert marker such as:
<main data-created-post-id="reply-123">
or the post card can carry:
<article data-post-id="reply-123" data-newly-created-post="1">
The frontend script should only auto-trigger analysis for the explicitly newly created post, not every post on the thread page.
API Writes
For clients using POST /api/create_thread or POST /api/create_reply, return the same post_id already present today. API clients can opt into analysis by calling:
POST /api/analyze_post?post_id=<post-id>
This keeps API write behavior backward compatible.
Browser Timing
The browser should trigger the request after the page is usable:
prefer
requestIdleCallbackwhen availablefall back to
setTimeout(..., 0)orDOMContentLoadeduse
navigator.sendBeacononly if the endpoint is fire-and-forgetuse
fetchif the UI needs a returned status or result
V1 should probably use fetch, because it is easier to test and gives a clear success or failure state.
Backend API Contract
Proposed endpoint:
POST /api/analyze_post
Inputs:
post_id: requiredoptional
force=1: moderator-only future feature to refresh stale or failed analysis
Response examples:
status=ok
post_id=reply-123
analysis_status=complete
moderation_severity=low
or JSON if this feature becomes the first JSON-first internal API:
{
"status": "ok",
"post_id": "reply-123",
"analysis_status": "complete",
"moderation": {
"severity": "low",
"labels": ["good-faith-disagreement"],
"confidence": 0.74
},
"engagement": {
"suggested_response": "That framing makes sense. One angle worth separating is whether the disagreement is about the goal or the implementation."
}
}
Recommendation: use JSON for this endpoint even if older APIs are plain text. This result is structured enough that plain text will become awkward quickly.
Deadlus Labs Request Shape
The backend should send Deadlus Labs a bounded context object:
post id
post kind:
threadorreplythread id
parent id when available
subject for threads
body text
board tags
author display label or stable author id if needed
limited parent context for replies, such as the parent body and root subject
Avoid sending the entire thread in V1 unless response quality clearly requires it. Start with the post plus parent context to control latency, cost, and data exposure.
Deadlus Labs Output Schema
Ask for a strict structured response:
{
"engagement": {
"suggested_response": "string",
"response_style": "curious|clarifying|supportive|challenging|deescalating",
"response_should_be_public": true
},
"moderation": {
"severity": "none|low|medium|high|critical",
"labels": ["trolling", "bad_faith", "aggression"],
"confidence": 0.0,
"summary": "short moderator-facing summary",
"recommended_action": "none|watch|review|hide_pending_review|escalate"
},
"quality": {
"discussion_value": "low|medium|high",
"good_faith_likelihood": 0.0,
"needs_human_review": false
}
}
Keep labels stable and machine-readable. Human-facing copy can evolve separately.
Moderation Taxonomy
Initial labels:
trolling: provocative behavior mainly intended to derail or upsetbad_faith: repeated misrepresentation, refusal to engage with the point, or strategic ambiguityaggression: hostile tone, insults, intimidation, or contemptharassment: targeted abuse toward a person or groupthreat: credible or implied threat of harmspam: promotional, repetitive, irrelevant, or automated-looking contentlow_effort: content unlikely to sustain useful discussionoff_topic: unrelated to thread or board contextescalation_risk: likely to trigger a hostile back-and-forth
Severity should be separate from labels. A post can be aggressive but low severity, or spam with medium severity.
Storage Options
Option A: SQLite-Only Analysis Table
Store analysis in the local read-model database or a sibling operational database.
Pros:
fastest to implement
keeps moderation metadata private by default
avoids committing model judgments into the public canonical repository
Cons:
analysis is not part of repository replication
rebuilds need either a retained operational database or re-analysis
Recommended for V1.
Option B: Canonical Analysis Records
Write analysis records under something like records/post-analyses/.
Pros:
portable with the repository
auditable as part of the append-only content history
Cons:
moderation judgments may become public
generated text and scores can age poorly
harder to delete sensitive model output
Better for later only if the product intentionally wants portable, public analysis.
Option C: Hybrid
Store public-safe engagement metadata canonically and private moderation details operationally.
Pros:
supports federation or static display of approved assistant responses
keeps sensitive moderation details private
Cons:
more moving parts
requires a clear boundary between public and private fields
Good future direction after V1 proves useful.
Idempotency And Caching
Use post_id plus a content hash as the idempotency key:
if analysis exists for the same
post_idand hash, return itif analysis exists but the post text changed in a future edit system, create a new version
if an analysis is currently running, return
analysis_status=pendingif the Deadlus Labs request fails, store a short failure state with retry timing
Suggested fields:
post_idcontent_hashstatus:pending,complete,failedrequested_atcompleted_atproviderprovider_modelprovider_request_idmoderation_jsonengagement_jsonfailure_coderetry_after
UI Possibilities
V1 can be quiet:
trigger the analysis in the background
do not show anything to ordinary users unless the result is immediately useful
expose failures only in the console or a hidden debug surface
Moderator-facing later:
show posts with
mediumor higher severity in a review queueshow labels and recommended action
show the suggested de-escalating response as a draft, not as an automatic post
Author-facing later:
after submit, show a private "possible reply starter" or "suggested follow-up"
for risky posts, show a non-blocking prompt such as "This may read as aggressive. Consider revising."
Public-facing later:
allow a moderator or trusted user to publish a generated engaging response
mark generated responses clearly if they are ever posted as content
Safety And Product Rules
Generated responses should never impersonate another user.
Generated responses should not be auto-posted in V1.
For hostile content, generated responses should favor de-escalation, clarification, and boundary-setting.
For high-severity moderation results, do not generate a provocative public reply. Return a safe moderator summary instead.
Do not present model judgments as final truth. Treat them as triage signals.
Keep prompts and provider diagnostics server-side.
Security, Privacy, And Abuse Controls
Store
DEADLUS_API_KEYin environment configuration.Add a backend timeout and short circuit if the provider is slow.
Rate limit by IP, identity, and post id.
Cap post and context length before sending to Deadlus Labs.
Strip or minimize secrets if future post bodies can contain private data.
Log provider errors without logging full post bodies by default.
Do not let arbitrary users analyze arbitrary historical posts in a tight loop.
Suggested Implementation Slices
Slice 1: Backend Skeleton And Storage
Goal:
add an idempotent
POST /api/analyze_postendpoint with local storage
Expected changes:
add route handling in
Application::handle()add a small analysis repository or service class
add SQLite table creation or migration path
return fake deterministic analysis while the Deadlus client is stubbed
Verification:
missing post returns
404duplicate calls for the same post return one stored analysis
endpoint does not affect normal post creation latency
Slice 2: Post-Load Browser Trigger
Goal:
trigger analysis only for the newly created post after the thread page loads
Expected changes:
redirect compose submissions with
created_post_idrender a data attribute for the created post
add a deferred
post_analysis.jscall
POST /api/analyze_postafter load
Verification:
creating a thread triggers one analysis request
creating a reply triggers one analysis request
reloading without
created_post_iddoes not analyze every visible postduplicate browser events do not create duplicate rows
Slice 3: Real Deadlus Labs Client
Goal:
replace the fake analyzer with the real provider call
Expected changes:
add a
DeadlusLabsClientread endpoint, API key, timeout, and model from environment
enforce strict output parsing and fallback behavior
store provider request id and model metadata
Verification:
provider timeout returns a stored failure state
malformed provider JSON is handled safely
successful provider response stores structured moderation and engagement fields
Slice 4: Moderator Review Surface
Goal:
make the moderation assessment useful without auto-enforcement
Expected changes:
add a simple pending-review page filtered by severity
show post link, labels, severity, confidence, and recommended action
hide or restrict sensitive analysis fields if identity permissions are not ready
Verification:
medium and high severity posts appear in review
low severity posts do not create noise by default
generated response is displayed as a draft suggestion only
Slice 5: Product Refinement
Goal:
decide how generated engaging responses should be used
Options:
private moderator draft
private author suggestion
public assistant-authored reply after approval
no visible response, use only to help moderators de-escalate
The safest default is private moderator draft.
Open Questions
Should the generated response be for the original author, for moderators, or for a public assistant persona?
Should moderation metadata stay private forever, or should some low-risk fields become canonical records?
Does Deadlus Labs support strict JSON schemas, request ids, and moderation-specific models?
What latency and timeout budget is acceptable for the post-load request?
Who is allowed to see analysis results before a permissions model exists?
Should high-severity analysis create a notification, a dashboard item, or only stored metadata in V1?
Acceptance Criteria
The first production-ready slice is complete when:
post creation and reply creation still complete without waiting on Deadlus Labs
the landing page triggers exactly one backend analysis request for the newly created post
the backend endpoint is idempotent by
post_idand content hashprovider failures do not affect post visibility or page rendering
analysis results are stored server-side with structured moderation and engagement fields
tests cover endpoint validation, idempotency, duplicate trigger behavior, and failure handling
Recommended V1 Decision
Start with:
JSON
POST /api/analyze_postSQLite-only private analysis storage
post-load trigger scoped to
created_post_idreal Deadlus Labs call behind an environment flag
no auto-posting and no auto-moderation action
That gives the product immediate learning value while keeping the forum write path fast and avoiding irreversible moderation or public-content decisions.