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:
@@ -3,6 +3,8 @@
|
||||
|
||||
{% block title %}Timeline — 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">Timeline Animation</h1>
|
||||
@@ -53,6 +55,22 @@ const points = animData.points;
|
||||
const months = animData.months;
|
||||
const catMonthly = animData.category_monthly;
|
||||
|
||||
const MONTH_NAMES = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||
function fmtMonth(ym) {
|
||||
if (!ym) return ym;
|
||||
let y, m;
|
||||
if (ym.includes('-')) {
|
||||
[y, m] = ym.split('-');
|
||||
} else if (ym.length >= 6) {
|
||||
y = ym.slice(0, 4);
|
||||
m = ym.slice(4, 6);
|
||||
} else {
|
||||
return ym;
|
||||
}
|
||||
const mi = parseInt(m, 10) - 1;
|
||||
return (MONTH_NAMES[mi] || m) + ' ' + y;
|
||||
}
|
||||
|
||||
if (points.length > 0 && months.length > 0) {
|
||||
|
||||
// --- Stat cards ---
|
||||
@@ -64,7 +82,7 @@ if (points.length > 0 && months.length > 0) {
|
||||
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 to-blue-400"></div>
|
||||
<div class="text-3xl font-bold text-blue-400">${months.length}</div>
|
||||
<div class="text-xs text-slate-400 mt-1 uppercase tracking-wider">Months Span</div>
|
||||
<div class="text-xs text-slate-500 mt-0.5">${firstMonth} to ${lastMonth}</div>
|
||||
<div class="text-xs text-slate-500 mt-0.5">${fmtMonth(firstMonth)} – ${fmtMonth(lastMonth)}</div>
|
||||
</div>
|
||||
<div class="stat-card rounded-xl border border-slate-800 p-5 relative overflow-hidden">
|
||||
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-emerald-500 to-emerald-400"></div>
|
||||
@@ -130,7 +148,7 @@ if (points.length > 0 && months.length > 0) {
|
||||
// Slider steps
|
||||
const sliderSteps = months.map(month => ({
|
||||
method: 'animate',
|
||||
label: month,
|
||||
label: fmtMonth(month),
|
||||
args: [[month], { frame: { duration: 500, redraw: true }, transition: { duration: 300 }, mode: 'immediate' }],
|
||||
}));
|
||||
|
||||
@@ -182,12 +200,12 @@ if (points.length > 0 && months.length > 0) {
|
||||
|
||||
// Update badge on animation frame
|
||||
const badge = document.querySelector('#monthBadge span');
|
||||
badge.textContent = `Month: ${months[0]} (${firstCount} drafts)`;
|
||||
badge.textContent = `${fmtMonth(months[0])} — ${firstCount} drafts`;
|
||||
|
||||
document.getElementById('tsneAnim').on('plotly_animatingframe', function(ev) {
|
||||
const month = ev.name;
|
||||
const cumCount = points.filter(p => p.month <= month).length;
|
||||
badge.textContent = `Month: ${month} (${cumCount} drafts)`;
|
||||
badge.textContent = `${fmtMonth(month)} — ${cumCount} drafts`;
|
||||
});
|
||||
|
||||
// Click to navigate
|
||||
@@ -211,8 +229,9 @@ if (points.length > 0 && months.length > 0) {
|
||||
return totalB - totalA;
|
||||
});
|
||||
|
||||
const monthLabels = months.map(fmtMonth);
|
||||
const areaTraces = areaCatList.map((cat, i) => ({
|
||||
x: months,
|
||||
x: monthLabels,
|
||||
y: months.map(m => (catMonthly[m] || {})[cat] || 0),
|
||||
name: cat,
|
||||
type: 'scatter',
|
||||
|
||||
Reference in New Issue
Block a user