Built a Flask REST service that scores submitted text for AI provenance by combining a model signal and a style signal under an explicit weighted rule, returning a per-signal breakdown instead of a single opaque verdict.
Documented what each signal measures and, critically, what it cannot see, then used those blind spots to construct an adversarial attack set that deliberately evaded the scorer.
Hardened the service against the attack set and added request rate limiting, re-running the full suite before and after to quantify which evasions the fix actually closed.
Designed an appeal workflow allowing flagged submissions to be re-reviewed with recorded justification, keeping human judgment in the loop for contested decisions.
Wrote a 32-test pytestregression suite covering scoring edge cases, malformed payloads, and appeal state transitions, so rubric changes could not silently alter prior outcomes.
Structured scoring as configurable weights so evaluation criteria could be re-tuned without rewriting the service.
The bullets above are reported in the full source resume ↗. Repository findings and any differences are identified separately below.
01 / SYSTEM ARCHITECTURE
How it fits together.
The Flask submit route validates text, generates a content ID, evaluates a Groq signal and a deterministic stylometric signal, then returns the combined score with both explanations. Separate JSON stores hold mutable content decisions and append-only audit events. An appeal changes the stored status to under_review and records the creator’s reasoning for human review.
Two documented local examples without a Groq key; the model signal is fixed at its neutral 0.5 fallback.
Combined scores were 0.66 for a uniform paragraph and 0.40 for a casual paragraph, a 0.26 gap from stylometry alone. Both remained uncertain; this is not a detector-accuracy benchmark.