Fix remaining critical, high, and medium issues from 4-perspective review

Critical fixes:
- Fix rating clamp range 1-10 → 1-5 (actual scale)
- Add `ietf ideas convergence` command (SequenceMatcher at 0.75 threshold)
- Fix "628 cross-org ideas" → 130 (verified from current DB) across 8 files

Security fixes:
- Sanitize FTS5 query input (strip special chars + boolean operators)
- Add rate limiting (10 req/min/IP) on Claude-calling endpoints
- Change <path:name> → <string:name> on draft routes

Codebase fixes:
- Add Database context manager (__enter__/__exit__)
- Wire false_positive filtering into queries (exclude by default in web UI)
- Fix Post 3 arithmetic ("~300" → "~409" distinct proposals)

Content & licensing:
- Add MIT LICENSE file
- Add IPR/FRAND notes (BCP 79, RFC 8179) to Posts 03 and 07
- Qualify "4:1 safety ratio" with monthly variation in 6 remaining files
- Add "Data as of March 2026" freeze-date headers to all 10 blog posts
- Hedge causal language in Post 04

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 12:47:47 +01:00
parent f1a0b0264c
commit e7527ad68e
40 changed files with 1005 additions and 169 deletions

View File

@@ -3,22 +3,26 @@
{% block title %}Ratings — IETF Draft Analyzer{% endblock %}
{% block extra_head %}<script src="/static/js/plotly.min.js"></script>{% endblock %}
{% block content %}
<div class="mb-6">
<h1 class="text-2xl font-bold text-white">Rating Analytics</h1>
<p class="text-slate-400 text-sm mt-1">Distribution and analysis of AI-generated ratings</p>
<p class="text-slate-400 text-sm mt-1">Distribution and analysis of AI-generated ratings across five dimensions. Each draft is rated 15 on novelty, maturity, overlap, momentum, and relevance by Claude AI, then combined into a weighted composite score.</p>
</div>
<!-- Score Distribution -->
<div class="bg-slate-900 rounded-xl border border-slate-800 p-5 mb-6">
<h2 class="text-sm font-semibold text-slate-300 mb-3">Composite Score Distribution</h2>
<h2 class="text-sm font-semibold text-slate-300 mb-1">Composite Score Distribution</h2>
<p class="text-xs text-slate-500 mb-3">The composite score is a weighted average of all five dimensions (each 20%). Scores range from 1.0 (low) to 5.0 (high). Most drafts cluster in the 2.03.5 range.</p>
<div id="scoreHist" style="height: 300px;"></div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- Dimension Box Plots -->
<div class="bg-slate-900 rounded-xl border border-slate-800 p-5">
<h2 class="text-sm font-semibold text-slate-300 mb-3">Score Distributions by Dimension</h2>
<h2 class="text-sm font-semibold text-slate-300 mb-1">Score Distributions by Dimension</h2>
<p class="text-xs text-slate-500 mb-3"><b>Novelty</b>: originality of ideas. <b>Maturity</b>: completeness and specification detail. <b>Overlap</b>: redundancy with other drafts (high = more unique). <b>Momentum</b>: adoption likelihood and community traction. <b>Relevance</b>: importance to AI agent infrastructure.</p>
<div id="dimDist" style="height: 350px;"></div>
</div>
<!-- Category Radar -->
@@ -30,7 +34,8 @@
<!-- Scatter: novelty vs maturity -->
<div class="bg-slate-900 rounded-xl border border-slate-800 p-5 mb-6">
<h2 class="text-sm font-semibold text-slate-300 mb-3">Novelty vs Maturity (bubble = relevance)</h2>
<h2 class="text-sm font-semibold text-slate-300 mb-1">Novelty vs Maturity (bubble = relevance)</h2>
<p class="text-xs text-slate-500 mb-3">Each dot is a rated draft. Drafts in the top-right corner are both novel and mature — prime candidates for standardization. Bubble size reflects relevance. Click a point to view the draft.</p>
<div id="scatter" style="height: 450px;"></div>
</div>
@@ -38,6 +43,7 @@
<div class="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
<div class="p-4 border-b border-slate-800">
<h2 class="text-sm font-semibold text-slate-300">Top 20 Drafts by Composite Score</h2>
<p class="text-xs text-slate-500 mt-1">Highest-rated drafts across all dimensions. Green (4+) = strong, amber (3) = moderate, grey (&lt;3) = needs improvement.</p>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
@@ -166,6 +172,7 @@ document.getElementById('scatter').on('plotly_click', function(data) {
momentum: dist.momentum[i],
overlap: dist.overlap[i],
category: dist.categories[i],
source: (dist.sources || [])[i] || 'ietf',
}));
drafts.sort((a, b) => b.score - a.score);
@@ -185,12 +192,13 @@ document.getElementById('scatter').on('plotly_click', function(data) {
top20.forEach((d, i) => {
const shortName = d.name.replace('draft-', '').substring(0, 40);
const sourceBadge = d.source !== 'ietf' ? ` <span style="display:inline-block;padding:1px 5px;border-radius:4px;font-size:0.55rem;font-weight:600;background:rgba(168,85,247,0.15);color:#c084fc;border:1px solid rgba(168,85,247,0.3);vertical-align:middle">${d.source.toUpperCase()}</span>` : '';
const row = document.createElement('tr');
row.className = 'hover:bg-slate-800/50 transition';
row.innerHTML = `
<td class="px-4 py-3 text-slate-500 font-mono text-xs">${i + 1}</td>
<td class="px-4 py-3">
<a href="/drafts/${d.name}" class="text-blue-400 hover:text-blue-300 transition text-xs font-mono">${shortName}</a>
<a href="/drafts/${d.name}" class="text-blue-400 hover:text-blue-300 transition text-xs font-mono">${shortName}</a>${sourceBadge}
</td>
<td class="px-4 py-3 text-center">
<span class="score-badge ${scoreClass(d.score)}">${d.score.toFixed(2)}</span>