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

@@ -286,6 +286,12 @@ class Database:
self._conn.close()
self._conn = None
def __enter__(self):
return self
def __exit__(self, *exc):
self.close()
# --- Drafts ---
def upsert_draft(self, draft: Draft) -> None:
@@ -343,8 +349,16 @@ class Database:
).fetchall()
return [self._row_to_draft(r) for r in rows]
def count_drafts(self) -> int:
return self.conn.execute("SELECT COUNT(*) FROM drafts").fetchone()[0]
def count_drafts(self, include_false_positives: bool = True) -> int:
if include_false_positives:
return self.conn.execute("SELECT COUNT(*) FROM drafts").fetchone()[0]
return self.conn.execute(
"""SELECT COUNT(*) FROM drafts d
WHERE NOT EXISTS (
SELECT 1 FROM ratings r
WHERE r.draft_name = d.name AND r.false_positive = 1
)"""
).fetchone()[0]
def search_drafts(self, query: str, limit: int = 50) -> list[Draft]:
rows = self.conn.execute(
@@ -408,13 +422,17 @@ class Database:
).fetchall()
return [self._row_to_draft(r) for r in rows]
def drafts_with_ratings(self, limit: int = 200) -> list[tuple[Draft, Rating]]:
def drafts_with_ratings(
self, limit: int = 200, include_false_positives: bool = False,
) -> list[tuple[Draft, Rating]]:
fp_clause = "" if include_false_positives else "WHERE COALESCE(r.false_positive, 0) = 0"
rows = self.conn.execute(
"""SELECT d.*, r.novelty, r.maturity, r.overlap, r.momentum, r.relevance,
f"""SELECT d.*, r.novelty, r.maturity, r.overlap, r.momentum, r.relevance,
r.summary, r.novelty_note, r.maturity_note, r.overlap_note,
r.momentum_note, r.relevance_note, r.categories as r_categories, r.rated_at
FROM drafts d
JOIN ratings r ON d.name = r.draft_name
{fp_clause}
ORDER BY (r.novelty * 0.30 + r.relevance * 0.25 + r.maturity * 0.20
+ r.momentum * 0.15 + (6 - r.overlap) * 0.10) DESC
LIMIT ?""",