Repository source: docs/plans/php_unicode_risk_detection_slices_v1.md
PHP Unicode Risk Detection Slices V1
This document outlines an atomic implementation plan for detecting suspicious or hard-to-inspect Unicode in authored content after Unicode prose support is introduced.
Goal
Add transparent Unicode risk detection for authored text without turning advisory analysis into the write gate.
The companion acceptance plan is docs/plans/php_unicode_authored_text_feature_flag_slices_v1.md (source unavailable). That plan defines what the server accepts. This plan defines how the system detects, explains, stores, and surfaces Unicode text that may deserve human attention.
Why This Should Be Separate
Unicode acceptance and Unicode detection have different jobs:
acceptance is deterministic and must be enforced before canonical write
detection is advisory, explainable, and allowed to evolve as tooling improves
acceptance should be stable enough that users know what can be posted
detection can combine deterministic scanners, Unicode metadata, and LLM analysis
detection output belongs in operational analysis state, not in canonical post records
Keeping these separate avoids using an LLM as a write-time authority and keeps rollback understandable.
Product Decision
The detection system should:
run only after a post exists or during explicit analysis
never be the sole reason a write is rejected
use deterministic scanners for objective Unicode facts
use an LLM only for advisory interpretation, summarization, and review priority
store results in SQLite analysis or a sibling operational table
show results only in existing approved-viewer analysis/moderation surfaces
keep canonical records unchanged
Detection Categories
V1 should distinguish these categories:
unsafe_rejected: characters that the write path would reject under the active Unicode policymixed_script: multiple scripts in the same prose fieldconfusable_identifier_like_text: text that looks like handles, domains, paths, commands, hashes, or identifiers and contains confusable charactersdirectionality_risk: right-to-left text, bidirectional layout concerns, or script ordering that may confuse visual inspectioninvisible_or_spacing_risk: unusual spacing, separators, or characters close to invisible behaviornormalization_risk: input that changed under normalization or contains combining-mark patterns worth surfacingllm_review_note: model-generated explanation or recommended human review priority
The names above are operational labels, not canonical record fields.
Slice 1: Deterministic Unicode Inspection Helper
Focus:
add a local scanner that produces objective Unicode facts for subject and body text
Checklist:
create a small helper, tentatively
UnicodeRiskInspectorinspect post
subjectandbodyseparatelyemit structured facts:
normalized form used for analysis
scripts present
code point counts by broad class
suspicious code points, if any
whether text changed under NFC normalization
whether right-to-left scripts are present
whether multiple scripts are present
keep output bounded by summarizing repeated findings
avoid adding a dependency unless the current PHP runtime lacks a required Unicode capability
Expected outcome:
the app can explain Unicode text properties without calling an LLM
analysis can be tested deterministically
Components touched:
likely a new helper under
src/ForumRewrite/Analysis/orsrc/ForumRewrite/Support/focused helper tests
Slice 2: Add Operational Storage For Unicode Risk Results
Focus:
persist detection results without changing canonical records
Checklist:
decide whether Unicode risk data is stored inside existing
post_analysesJSON or in a sibling tableprefer a sibling table if the data should refresh independently from LLM post analysis
include:
post_idcontent_hashschema_versionstatusdeterministic facts JSON
LLM review JSON, nullable
timestamps
make writes idempotent for the same
post_idandcontent_hashensure failed LLM review does not erase deterministic facts
Expected outcome:
Unicode risk findings are cached and tied to exact post content
operational data can be rebuilt or discarded without altering canonical history
Components touched:
src/ForumRewrite/Analysis/*read-model or analysis-store migration code
tests around save, hydrate, and idempotency
Slice 3: Integrate Deterministic Detection Into Post Analysis
Focus:
run Unicode inspection where the app already performs post analysis
Checklist:
add deterministic Unicode facts to the analysis context or result
ensure
/api/analyze_postcomputes and stores deterministic facts before any LLM callreturn Unicode risk details only to viewers who can already see post analysis
keep anonymous responses unchanged except for existing high-level status fields
make detection work in stub analysis mode
do not trigger automatic agent replies based only on Unicode risk findings
Expected outcome:
approved viewers can inspect Unicode risk data through the existing analysis path
detection does not create a second write-time moderation system
Components touched:
src/ForumRewrite/Analysis/PostAnalysisService.phpsrc/ForumRewrite/Analysis/SqlitePostAnalysisStore.phpor sibling storesrc/ForumRewrite/Application.phptests/WriteApiSmokeTest.phptests/DedalusPostAnalyzerTest.phpif analysis schema changes
Slice 4: Add LLM-Assisted Unicode Risk Review
Focus:
use the model to explain and prioritize deterministic findings, not to discover basic facts from scratch
Checklist:
extend the post-analysis prompt or add a dedicated Unicode-risk prompt section
pass deterministic Unicode facts into the model context
ask the model for a bounded JSON object:
review_priority:none,low,medium,highsummary: short human-readable explanationconcerns: list of stable labelsrecommended_action:none,watch,human_reviewconfidence: numeric 0-1
require the model to cite only facts present in the deterministic scan or visible post text
reject or sanitize malformed model output
keep LLM review unavailable when provider config is missing
keep deterministic findings available when LLM review fails
Expected outcome:
LLM output helps humans understand why a post may be confusing
deterministic scanner remains the source of objective truth
Components touched:
prompts/dedalus_post_analysis_system.txtor a new prompt filesrc/ForumRewrite/Analysis/DedalusPostAnalyzer.phpsrc/ForumRewrite/Analysis/StubPostAnalyzer.phpdecoder and schema tests
Slice 5: Surface Findings In Approved Analysis UI
Focus:
make findings visible to inspectors without alarming normal readers
Checklist:
add a compact Unicode risk section to the existing post-analysis details panel
show:
risk level
scripts detected
short explanation
any blocked/suspicious code point names when available
LLM review summary when present
keep the normal public thread/post UI unchanged
avoid showing raw invisible characters in a way that can affect layout
prefer escaped code point notation such as
U+202Efor suspicious characters
Expected outcome:
approved users and inspectors can review Unicode risk clearly
public readers are not shown internal analysis metadata
Components touched:
templates/partials/post_card.phppublic/assets/post_analysis.jsif dynamic analysis rendering needs updatessmoke tests for approved and anonymous rendering
Slice 6: Add Backfill And Recheck Tooling
Focus:
allow existing posts to be inspected after the feature ships
Checklist:
add a script or command to scan all current posts for Unicode risk
support rechecking only posts whose content hash changed or whose risk schema version is stale
print a concise summary:
scanned count
changed count
high/medium/low finding counts
provider failures if LLM review is enabled
allow deterministic-only mode for offline or no-provider environments
keep the tool read-only with respect to canonical records
Expected outcome:
operators can audit existing content after enabling Unicode prose
detection can be refreshed as the policy evolves
Components touched:
scripts/analysis store
runbook docs
Slice 7: Regression And Abuse-Case Coverage
Focus:
prove detection catches the cases it is meant to surface
Checklist:
add deterministic scanner tests for:
plain ASCII text
plain Cyrillic text
mixed Latin and Cyrillic prose
right-to-left script text
confusable identifier-like text
unusual combining-mark patterns
unsafe characters that should already be rejected by write validation
add LLM decoder tests for expected Unicode review shape
add store hydration tests
add
/api/analyze_posttests confirming:anonymous viewers do not receive details
approved viewers receive Unicode risk details
deterministic findings persist even if LLM analysis is unavailable
Expected outcome:
detection behavior is repeatable and inspectable
LLM failure modes do not break deterministic risk reporting
Components touched:
new helper tests
tests/DedalusPostAnalyzerTest.phptests/WriteApiSmokeTest.php
Guardrails
Do not use an LLM to decide whether a canonical write is valid.
Do not block posts solely because the LLM assigns risk.
Do not expose Unicode risk details to anonymous viewers in V1.
Do not store LLM analysis in canonical records.
Do not treat mixed-script prose as automatically malicious.
Do not let suspicious characters render raw inside inspector UI if they can affect layout.
Do not hide deterministic facts behind model prose.
Do not make agent replies respond publicly to Unicode risk findings in V1.
Open Policy Questions
These should be resolved before Slice 4:
Should Unicode risk review be part of the existing post-analysis model call or a separate optional model call? Recommendation: start inside existing post analysis to avoid another provider path, but keep the stored schema separable.
Should mixed-script prose be a low-priority signal by default? Recommendation: yes, unless identifier-like text is present.
Should LLM review run automatically for all Unicode posts or only when deterministic signals are non-empty? Recommendation: only when deterministic signals are non-empty in V1.
Should approved viewers see exact code point names for all non-ASCII characters? Recommendation: no, show exact names only for suspicious or rejected classes.
Should the backfill tool call the LLM by default? Recommendation: no, deterministic-only by default with an explicit provider-enabled option.
Recommended Order
add deterministic Unicode inspection
add operational storage
integrate deterministic results into post analysis
add LLM-assisted review using deterministic facts as input
surface findings to approved viewers
add backfill and recheck tooling
add regression and abuse-case coverage
Acceptance Criteria
This work is complete when:
Unicode risk detection is stored outside canonical records
deterministic scanner output is available without provider configuration
LLM review is advisory and cannot block writes
approved viewers can inspect Unicode risk details
anonymous viewers do not receive internal Unicode risk details
detection handles plain Cyrillic without treating it as inherently malicious
mixed-script, confusable, directionality, spacing, and normalization risks are represented as distinct findings
backfill can scan existing posts without modifying canonical data
Summary
Detection should make Unicode support more transparent, not more brittle.
The intended result is:
deterministic rules enforce the hard safety boundary
deterministic inspection explains Unicode facts
LLM review helps humans prioritize ambiguous cases
canonical records remain unchanged
Unicode prose support remains usable for legitimate non-Latin text