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>
90 lines
5.1 KiB
HTML
90 lines
5.1 KiB
HTML
{% extends "base.html" %}
|
|
{% set active_page = "gaps" %}
|
|
|
|
{% block title %}Gap Explorer — IETF Draft Analyzer{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mb-6">
|
|
<h1 class="text-2xl font-bold text-white">Gap Explorer</h1>
|
|
<p class="text-slate-400 text-sm mt-1">{{ gaps | length }} identified gaps in AI/agent standards coverage. Gaps are identified by Claude AI analyzing the full corpus of drafts to find areas where important problems lack adequate proposals. Severity reflects urgency: <span class="text-red-400">critical</span> = blocking issue with no draft addressing it, <span class="text-orange-400">high</span> = partially addressed but incomplete, <span class="text-yellow-400">medium</span> = some coverage exists but more work needed, <span class="text-green-400">low</span> = minor gap or niche concern.</p>
|
|
</div>
|
|
|
|
<!-- Action bar -->
|
|
<div class="mb-6 flex flex-wrap gap-3">
|
|
<a href="/gaps/demo" class="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium rounded-lg transition">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
|
View Demo Draft
|
|
</a>
|
|
{% if generated_drafts %}
|
|
<span class="inline-flex items-center px-3 py-2 bg-slate-800 text-slate-400 text-sm rounded-lg">
|
|
{{ generated_drafts | length }} draft{{ 's' if generated_drafts | length != 1 }} already generated
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Severity overview -->
|
|
{% set ns = namespace(critical=0, high=0, medium=0, low=0) %}
|
|
{% for gap in gaps %}
|
|
{% if gap.severity == 'critical' %}{% set ns.critical = ns.critical + 1 %}
|
|
{% elif gap.severity == 'high' %}{% set ns.high = ns.high + 1 %}
|
|
{% elif gap.severity == 'medium' %}{% set ns.medium = ns.medium + 1 %}
|
|
{% else %}{% set ns.low = ns.low + 1 %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
|
|
<div class="stat-card rounded-xl border border-slate-800 p-4">
|
|
<div class="text-3xl font-bold text-slate-200">{{ gaps | length }}</div>
|
|
<div class="text-xs text-slate-400 mt-1">Total Gaps</div>
|
|
</div>
|
|
<div class="stat-card rounded-xl border border-red-500/30 p-4">
|
|
<div class="text-3xl font-bold text-red-400">{{ ns.critical }}</div>
|
|
<div class="text-xs text-red-400/70 mt-1">Critical</div>
|
|
</div>
|
|
<div class="stat-card rounded-xl border border-orange-500/30 p-4">
|
|
<div class="text-3xl font-bold text-orange-400">{{ ns.high }}</div>
|
|
<div class="text-xs text-orange-400/70 mt-1">High</div>
|
|
</div>
|
|
<div class="stat-card rounded-xl border border-yellow-500/30 p-4">
|
|
<div class="text-3xl font-bold text-yellow-400">{{ ns.medium }}</div>
|
|
<div class="text-xs text-yellow-400/70 mt-1">Medium</div>
|
|
</div>
|
|
<div class="stat-card rounded-xl border border-green-500/30 p-4">
|
|
<div class="text-3xl font-bold text-green-400">{{ ns.low }}</div>
|
|
<div class="text-xs text-green-400/70 mt-1">Low</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gap cards sorted by severity (critical first) -->
|
|
<div class="space-y-4" id="gapList">
|
|
{% for gap in gaps %}
|
|
<a href="/gaps/{{ gap.id }}" class="block bg-slate-900 rounded-xl border
|
|
{% if gap.severity == 'critical' %}border-red-500/40 hover:border-red-500/60
|
|
{% elif gap.severity == 'high' %}border-orange-500/30 hover:border-orange-500/50
|
|
{% elif gap.severity == 'medium' %}border-yellow-500/20 hover:border-yellow-500/40
|
|
{% else %}border-slate-800 hover:border-slate-700{% endif %}
|
|
p-5 transition group">
|
|
<div class="flex items-start justify-between gap-3 mb-3">
|
|
<h2 class="text-base font-semibold text-white group-hover:text-blue-400 transition">{{ gap.topic }}</h2>
|
|
<div class="flex items-center gap-2 shrink-0">
|
|
<span class="px-2.5 py-0.5 rounded-full text-xs font-semibold whitespace-nowrap
|
|
{% if gap.severity == 'critical' %}bg-red-500/20 text-red-400 ring-1 ring-red-500/30
|
|
{% elif gap.severity == 'high' %}bg-orange-500/20 text-orange-400 ring-1 ring-orange-500/30
|
|
{% elif gap.severity == 'medium' %}bg-yellow-500/20 text-yellow-400 ring-1 ring-yellow-500/30
|
|
{% else %}bg-green-500/20 text-green-400 ring-1 ring-green-500/30{% endif %}">
|
|
{{ gap.severity | upper }}
|
|
</span>
|
|
<svg class="w-4 h-4 text-slate-600 group-hover:text-blue-400 transition" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
|
</div>
|
|
</div>
|
|
|
|
{% if gap.category %}
|
|
<span class="inline-block px-2 py-0.5 rounded text-[10px] bg-slate-800 text-slate-400 mb-3 font-medium">{{ gap.category }}</span>
|
|
{% endif %}
|
|
|
|
<p class="text-sm text-slate-400 leading-relaxed">{{ gap.description }}</p>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endblock %}
|